tclCmdMZ.cGo to the documentation of this file.00001 /* 00002 * tclCmdMZ.c -- 00003 * 00004 * This file contains the top-level command routines for most of the Tcl 00005 * built-in commands whose names begin with the letters M to Z. It 00006 * contains only commands in the generic core (i.e. those that don't 00007 * depend much upon UNIX facilities). 00008 * 00009 * Copyright (c) 1987-1993 The Regents of the University of California. 00010 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 00011 * Copyright (c) 1998-2000 Scriptics Corporation. 00012 * Copyright (c) 2002 ActiveState Corporation. 00013 * Copyright (c) 2003 Donal K. Fellows. 00014 * 00015 * See the file "license.terms" for information on usage and redistribution of 00016 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00017 * 00018 * RCS: @(#) $Id: tclCmdMZ.c,v 1.163 2007/12/13 15:23:15 dgp Exp $ 00019 */ 00020 00021 #include "tclInt.h" 00022 #include "tclRegexp.h" 00023 00024 static int UniCharIsAscii(int character); 00025 00026 /* 00027 *---------------------------------------------------------------------- 00028 * 00029 * Tcl_PwdObjCmd -- 00030 * 00031 * This procedure is invoked to process the "pwd" Tcl command. See the 00032 * user documentation for details on what it does. 00033 * 00034 * Results: 00035 * A standard Tcl result. 00036 * 00037 * Side effects: 00038 * See the user documentation. 00039 * 00040 *---------------------------------------------------------------------- 00041 */ 00042 00043 int 00044 Tcl_PwdObjCmd( 00045 ClientData dummy, /* Not used. */ 00046 Tcl_Interp *interp, /* Current interpreter. */ 00047 int objc, /* Number of arguments. */ 00048 Tcl_Obj *CONST objv[]) /* Argument objects. */ 00049 { 00050 Tcl_Obj *retVal; 00051 00052 if (objc != 1) { 00053 Tcl_WrongNumArgs(interp, 1, objv, NULL); 00054 return TCL_ERROR; 00055 } 00056 00057 retVal = Tcl_FSGetCwd(interp); 00058 if (retVal == NULL) { 00059 return TCL_ERROR; 00060 } 00061 Tcl_SetObjResult(interp, retVal); 00062 Tcl_DecrRefCount(retVal); 00063 return TCL_OK; 00064 } 00065 00066 /* 00067 *---------------------------------------------------------------------- 00068 * 00069 * Tcl_RegexpObjCmd -- 00070 * 00071 * This procedure is invoked to process the "regexp" Tcl command. See 00072 * the user documentation for details on what it does. 00073 * 00074 * Results: 00075 * A standard Tcl result. 00076 * 00077 * Side effects: 00078 * See the user documentation. 00079 * 00080 *---------------------------------------------------------------------- 00081 */ 00082 00083 int 00084 Tcl_RegexpObjCmd( 00085 ClientData dummy, /* Not used. */ 00086 Tcl_Interp *interp, /* Current interpreter. */ 00087 int objc, /* Number of arguments. */ 00088 Tcl_Obj *CONST objv[]) /* Argument objects. */ 00089 { 00090 int i, indices, match, about, offset, all, doinline, numMatchesSaved; 00091 int cflags, eflags, stringLength; 00092 Tcl_RegExp regExpr; 00093 Tcl_Obj *objPtr, *startIndex = NULL, *resultPtr = NULL; 00094 Tcl_RegExpInfo info; 00095 static CONST char *options[] = { 00096 "-all", "-about", "-indices", "-inline", 00097 "-expanded", "-line", "-linestop", "-lineanchor", 00098 "-nocase", "-start", "--", NULL 00099 }; 00100 enum options { 00101 REGEXP_ALL, REGEXP_ABOUT, REGEXP_INDICES, REGEXP_INLINE, 00102 REGEXP_EXPANDED,REGEXP_LINE, REGEXP_LINESTOP,REGEXP_LINEANCHOR, 00103 REGEXP_NOCASE, REGEXP_START, REGEXP_LAST 00104 }; 00105 00106 indices = 0; 00107 about = 0; 00108 cflags = TCL_REG_ADVANCED; 00109 eflags = 0; 00110 offset = 0; 00111 all = 0; 00112 doinline = 0; 00113 00114 for (i = 1; i < objc; i++) { 00115 char *name; 00116 int index; 00117 00118 name = TclGetString(objv[i]); 00119 if (name[0] != '-') { 00120 break; 00121 } 00122 if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT, 00123 &index) != TCL_OK) { 00124 goto optionError; 00125 } 00126 switch ((enum options) index) { 00127 case REGEXP_ALL: 00128 all = 1; 00129 break; 00130 case REGEXP_INDICES: 00131 indices = 1; 00132 break; 00133 case REGEXP_INLINE: 00134 doinline = 1; 00135 break; 00136 case REGEXP_NOCASE: 00137 cflags |= TCL_REG_NOCASE; 00138 break; 00139 case REGEXP_ABOUT: 00140 about = 1; 00141 break; 00142 case REGEXP_EXPANDED: 00143 cflags |= TCL_REG_EXPANDED; 00144 break; 00145 case REGEXP_LINE: 00146 cflags |= TCL_REG_NEWLINE; 00147 break; 00148 case REGEXP_LINESTOP: 00149 cflags |= TCL_REG_NLSTOP; 00150 break; 00151 case REGEXP_LINEANCHOR: 00152 cflags |= TCL_REG_NLANCH; 00153 break; 00154 case REGEXP_START: { 00155 int temp; 00156 if (++i >= objc) { 00157 goto endOfForLoop; 00158 } 00159 if (TclGetIntForIndexM(interp, objv[i], 0, &temp) != TCL_OK) { 00160 goto optionError; 00161 } 00162 if (startIndex) { 00163 Tcl_DecrRefCount(startIndex); 00164 } 00165 startIndex = objv[i]; 00166 Tcl_IncrRefCount(startIndex); 00167 break; 00168 } 00169 case REGEXP_LAST: 00170 i++; 00171 goto endOfForLoop; 00172 } 00173 } 00174 00175 endOfForLoop: 00176 if ((objc - i) < (2 - about)) { 00177 Tcl_WrongNumArgs(interp, 1, objv, 00178 "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?"); 00179 goto optionError; 00180 } 00181 objc -= i; 00182 objv += i; 00183 00184 /* 00185 * Check if the user requested -inline, but specified match variables; a 00186 * no-no. 00187 */ 00188 00189 if (doinline && ((objc - 2) != 0)) { 00190 Tcl_AppendResult(interp, "regexp match variables not allowed" 00191 " when using -inline", NULL); 00192 goto optionError; 00193 } 00194 00195 /* 00196 * Handle the odd about case separately. 00197 */ 00198 00199 if (about) { 00200 regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags); 00201 if ((regExpr == NULL) || (TclRegAbout(interp, regExpr) < 0)) { 00202 optionError: 00203 if (startIndex) { 00204 Tcl_DecrRefCount(startIndex); 00205 } 00206 return TCL_ERROR; 00207 } 00208 return TCL_OK; 00209 } 00210 00211 /* 00212 * Get the length of the string that we are matching against so we can do 00213 * the termination test for -all matches. Do this before getting the 00214 * regexp to avoid shimmering problems. 00215 */ 00216 00217 objPtr = objv[1]; 00218 stringLength = Tcl_GetCharLength(objPtr); 00219 00220 if (startIndex) { 00221 TclGetIntForIndexM(NULL, startIndex, stringLength, &offset); 00222 Tcl_DecrRefCount(startIndex); 00223 if (offset < 0) { 00224 offset = 0; 00225 } 00226 } 00227 00228 regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags); 00229 if (regExpr == NULL) { 00230 return TCL_ERROR; 00231 } 00232 00233 if (offset > 0) { 00234 /* 00235 * Add flag if using offset (string is part of a larger string), so 00236 * that "^" won't match. 00237 */ 00238 00239 eflags |= TCL_REG_NOTBOL; 00240 } 00241 00242 objc -= 2; 00243 objv += 2; 00244 00245 if (doinline) { 00246 /* 00247 * Save all the subexpressions, as we will return them as a list 00248 */ 00249 00250 numMatchesSaved = -1; 00251 } else { 00252 /* 00253 * Save only enough subexpressions for matches we want to keep, expect 00254 * in the case of -all, where we need to keep at least one to know 00255 * where to move the offset. 00256 */ 00257 00258 numMatchesSaved = (objc == 0) ? all : objc; 00259 } 00260 00261 /* 00262 * The following loop is to handle multiple matches within the same source 00263 * string; each iteration handles one match. If "-all" hasn't been 00264 * specified then the loop body only gets executed once. We terminate the 00265 * loop when the starting offset is past the end of the string. 00266 */ 00267 00268 while (1) { 00269 match = Tcl_RegExpExecObj(interp, regExpr, objPtr, 00270 offset /* offset */, numMatchesSaved, eflags 00271 | ((offset > 0 && 00272 (Tcl_GetUniChar(objPtr,offset-1) != (Tcl_UniChar)'\n')) 00273 ? TCL_REG_NOTBOL : 0)); 00274 00275 if (match < 0) { 00276 return TCL_ERROR; 00277 } 00278 00279 if (match == 0) { 00280 /* 00281 * We want to set the value of the intepreter result only when 00282 * this is the first time through the loop. 00283 */ 00284 00285 if (all <= 1) { 00286 /* 00287 * If inlining, the interpreter's object result remains an 00288 * empty list, otherwise set it to an integer object w/ value 00289 * 0. 00290 */ 00291 00292 if (!doinline) { 00293 Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); 00294 } 00295 return TCL_OK; 00296 } 00297 break; 00298 } 00299 00300 /* 00301 * If additional variable names have been specified, return index 00302 * information in those variables. 00303 */ 00304 00305 Tcl_RegExpGetInfo(regExpr, &info); 00306 if (doinline) { 00307 /* 00308 * It's the number of substitutions, plus one for the matchVar at 00309 * index 0 00310 */ 00311 00312 objc = info.nsubs + 1; 00313 if (all <= 1) { 00314 resultPtr = Tcl_NewObj(); 00315 } 00316 } 00317 for (i = 0; i < objc; i++) { 00318 Tcl_Obj *newPtr; 00319 00320 if (indices) { 00321 int start, end; 00322 Tcl_Obj *objs[2]; 00323 00324 /* 00325 * Only adjust the match area if there was a match for that 00326 * area. (Scriptics Bug 4391/SF Bug #219232) 00327 */ 00328 00329 if (i <= info.nsubs && info.matches[i].start >= 0) { 00330 start = offset + info.matches[i].start; 00331 end = offset + info.matches[i].end; 00332 00333 /* 00334 * Adjust index so it refers to the last character in the 00335 * match instead of the first character after the match. 00336 */ 00337 00338 if (end >= offset) { 00339 end--; 00340 } 00341 } else { 00342 start = -1; 00343 end = -1; 00344 } 00345 00346 objs[0] = Tcl_NewLongObj(start); 00347 objs[1] = Tcl_NewLongObj(end); 00348 00349 newPtr = Tcl_NewListObj(2, objs); 00350 } else { 00351 if (i <= info.nsubs) { 00352 newPtr = Tcl_GetRange(objPtr, 00353 offset + info.matches[i].start, 00354 offset + info.matches[i].end - 1); 00355 } else { 00356 newPtr = Tcl_NewObj(); 00357 } 00358 } 00359 if (doinline) { 00360 if (Tcl_ListObjAppendElement(interp, resultPtr, newPtr) 00361 != TCL_OK) { 00362 Tcl_DecrRefCount(newPtr); 00363 Tcl_DecrRefCount(resultPtr); 00364 return TCL_ERROR; 00365 } 00366 } else { 00367 Tcl_Obj *valuePtr; 00368 valuePtr = Tcl_ObjSetVar2(interp, objv[i], NULL, newPtr, 0); 00369 if (valuePtr == NULL) { 00370 Tcl_AppendResult(interp, "couldn't set variable \"", 00371 TclGetString(objv[i]), "\"", NULL); 00372 return TCL_ERROR; 00373 } 00374 } 00375 } 00376 00377 if (all == 0) { 00378 break; 00379 } 00380 00381 /* 00382 * Adjust the offset to the character just after the last one in the 00383 * matchVar and increment all to count how many times we are making a 00384 * match. We always increment the offset by at least one to prevent 00385 * endless looping (as in the case: regexp -all {a*} a). Otherwise, 00386 * when we match the NULL string at the end of the input string, we 00387 * will loop indefinately (because the length of the match is 0, so 00388 * offset never changes). 00389 */ 00390 00391 if (info.matches[0].end == 0) { 00392 offset++; 00393 } 00394 offset += info.matches[0].end; 00395 all++; 00396 eflags |= TCL_REG_NOTBOL; 00397 if (offset >= stringLength) { 00398 break; 00399 } 00400 } 00401 00402 /* 00403 * Set the interpreter's object result to an integer object with value 1 00404 * if -all wasn't specified, otherwise it's all-1 (the number of times 00405 * through the while - 1). 00406 */ 00407 00408 if (doinline) { 00409 Tcl_SetObjResult(interp, resultPtr); 00410 } else { 00411 Tcl_SetObjResult(interp, Tcl_NewIntObj(all ? all-1 : 1)); 00412 } 00413 return TCL_OK; 00414 } 00415 00416 /* 00417 *---------------------------------------------------------------------- 00418 * 00419 * Tcl_RegsubObjCmd -- 00420 * 00421 * This procedure is invoked to process the "regsub" Tcl command. See the 00422 * user documentation for details on what it does. 00423 * 00424 * Results: 00425 * A standard Tcl result. 00426 * 00427 * Side effects: 00428 * See the user documentation. 00429 * 00430 *---------------------------------------------------------------------- 00431 */ 00432 00433 int 00434 Tcl_RegsubObjCmd( 00435 ClientData dummy, /* Not used. */ 00436 Tcl_Interp *interp, /* Current interpreter. */ 00437 int objc, /* Number of arguments. */ 00438 Tcl_Obj *CONST objv[]) /* Argument objects. */ 00439 { 00440 int idx, result, cflags, all, wlen, wsublen, numMatches, offset; 00441 int start, end, subStart, subEnd, match; 00442 Tcl_RegExp regExpr; 00443 Tcl_RegExpInfo info; 00444 Tcl_Obj *resultPtr, *subPtr, *objPtr, *startIndex = NULL; 00445 Tcl_UniChar ch, *wsrc, *wfirstChar, *wstring, *wsubspec, *wend; 00446 00447 static CONST char *options[] = { 00448 "-all", "-nocase", "-expanded", 00449 "-line", "-linestop", "-lineanchor", "-start", 00450 "--", NULL 00451 }; 00452 enum options { 00453 REGSUB_ALL, REGSUB_NOCASE, REGSUB_EXPANDED, 00454 REGSUB_LINE, REGSUB_LINESTOP, REGSUB_LINEANCHOR, REGSUB_START, 00455 REGSUB_LAST 00456 }; 00457 00458 cflags = TCL_REG_ADVANCED; 00459 all = 0; 00460 offset = 0; 00461 resultPtr = NULL; 00462 00463 for (idx = 1; idx < objc; idx++) { 00464 char *name; 00465 int index; 00466 00467 name = TclGetString(objv[idx]); 00468 if (name[0] != '-') { 00469 break; 00470 } 00471 if (Tcl_GetIndexFromObj(interp, objv[idx], options, "switch", 00472 TCL_EXACT, &index) != TCL_OK) { 00473 goto optionError; 00474 } 00475 switch ((enum options) index) { 00476 case REGSUB_ALL: 00477 all = 1; 00478 break; 00479 case REGSUB_NOCASE: 00480 cflags |= TCL_REG_NOCASE; 00481 break; 00482 case REGSUB_EXPANDED: 00483 cflags |= TCL_REG_EXPANDED; 00484 break; 00485 case REGSUB_LINE: 00486 cflags |= TCL_REG_NEWLINE; 00487 break; 00488 case REGSUB_LINESTOP: 00489 cflags |= TCL_REG_NLSTOP; 00490 break; 00491 case REGSUB_LINEANCHOR: 00492 cflags |= TCL_REG_NLANCH; 00493 break; 00494 case REGSUB_START: { 00495 int temp; 00496 if (++idx >= objc) { 00497 goto endOfForLoop; 00498 } 00499 if (TclGetIntForIndexM(interp, objv[idx], 0, &temp) != TCL_OK) { 00500 goto optionError; 00501 } 00502 if (startIndex) { 00503 Tcl_DecrRefCount(startIndex); 00504 } 00505 startIndex = objv[idx]; 00506 Tcl_IncrRefCount(startIndex); 00507 break; 00508 } 00509 case REGSUB_LAST: 00510 idx++; 00511 goto endOfForLoop; 00512 } 00513 } 00514 00515 endOfForLoop: 00516 if (objc-idx < 3 || objc-idx > 4) { 00517 Tcl_WrongNumArgs(interp, 1, objv, 00518 "?switches? exp string subSpec ?varName?"); 00519 optionError: 00520 if (startIndex) { 00521 Tcl_DecrRefCount(startIndex); 00522 } 00523 return TCL_ERROR; 00524 } 00525 00526 objc -= idx; 00527 objv += idx; 00528 00529 if (startIndex) { 00530 int stringLength = Tcl_GetCharLength(objv[1]); 00531 00532 TclGetIntForIndexM(NULL, startIndex, stringLength, &offset); 00533 Tcl_DecrRefCount(startIndex); 00534 if (offset < 0) { 00535 offset = 0; 00536 } 00537 } 00538 00539 if (all && (offset == 0) 00540 && (strpbrk(TclGetString(objv[2]), "&\\") == NULL) 00541 && (strpbrk(TclGetString(objv[0]), "*+?{}()[].\\|^$") == NULL)) { 00542 /* 00543 * This is a simple one pair string map situation. We make use of a 00544 * slightly modified version of the one pair STR_MAP code. 00545 */ 00546 00547 int slen, nocase; 00548 int (*strCmpFn)(CONST Tcl_UniChar*,CONST Tcl_UniChar*,unsigned long); 00549 Tcl_UniChar *p, wsrclc; 00550 00551 numMatches = 0; 00552 nocase = (cflags & TCL_REG_NOCASE); 00553 strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp; 00554 00555 wsrc = Tcl_GetUnicodeFromObj(objv[0], &slen); 00556 wstring = Tcl_GetUnicodeFromObj(objv[1], &wlen); 00557 wsubspec = Tcl_GetUnicodeFromObj(objv[2], &wsublen); 00558 wend = wstring + wlen - (slen ? slen - 1 : 0); 00559 result = TCL_OK; 00560 00561 if (slen == 0) { 00562 /* 00563 * regsub behavior for "" matches between each character. 'string 00564 * map' skips the "" case. 00565 */ 00566 00567 if (wstring < wend) { 00568 resultPtr = Tcl_NewUnicodeObj(wstring, 0); 00569 Tcl_IncrRefCount(resultPtr); 00570 for (; wstring < wend; wstring++) { 00571 Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen); 00572 Tcl_AppendUnicodeToObj(resultPtr, wstring, 1); 00573 numMatches++; 00574 } 00575 wlen = 0; 00576 } 00577 } else { 00578 wsrclc = Tcl_UniCharToLower(*wsrc); 00579 for (p = wfirstChar = wstring; wstring < wend; wstring++) { 00580 if ((*wstring == *wsrc || 00581 (nocase && Tcl_UniCharToLower(*wstring)==wsrclc)) && 00582 (slen==1 || (strCmpFn(wstring, wsrc, 00583 (unsigned long) slen) == 0))) { 00584 if (numMatches == 0) { 00585 resultPtr = Tcl_NewUnicodeObj(wstring, 0); 00586 Tcl_IncrRefCount(resultPtr); 00587 } 00588 if (p != wstring) { 00589 Tcl_AppendUnicodeToObj(resultPtr, p, wstring - p); 00590 p = wstring + slen; 00591 } else { 00592 p += slen; 00593 } 00594 wstring = p - 1; 00595 00596 Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen); 00597 numMatches++; 00598 } 00599 } 00600 if (numMatches) { 00601 wlen = wfirstChar + wlen - p; 00602 wstring = p; 00603 } 00604 } 00605 objPtr = NULL; 00606 subPtr = NULL; 00607 goto regsubDone; 00608 } 00609 00610 regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags); 00611 if (regExpr == NULL) { 00612 return TCL_ERROR; 00613 } 00614 00615 /* 00616 * Make sure to avoid problems where the objects are shared. This can 00617 * cause RegExpObj <> UnicodeObj shimmering that causes data corruption. 00618 * [Bug #461322] 00619 */ 00620 00621 if (objv[1] == objv[0]) { 00622 objPtr = Tcl_DuplicateObj(objv[1]); 00623 } else { 00624 objPtr = objv[1]; 00625 } 00626 wstring = Tcl_GetUnicodeFromObj(objPtr, &wlen); 00627 if (objv[2] == objv[0]) { 00628 subPtr = Tcl_DuplicateObj(objv[2]); 00629 } else { 00630 subPtr = objv[2]; 00631 } 00632 wsubspec = Tcl_GetUnicodeFromObj(subPtr, &wsublen); 00633 00634 result = TCL_OK; 00635 00636 /* 00637 * The following loop is to handle multiple matches within the same source 00638 * string; each iteration handles one match and its corresponding 00639 * substitution. If "-all" hasn't been specified then the loop body only 00640 * gets executed once. We must use 'offset <= wlen' in particular for the 00641 * case where the regexp pattern can match the empty string - this is 00642 * useful when doing, say, 'regsub -- ^ $str ...' when $str might be 00643 * empty. 00644 */ 00645 00646 numMatches = 0; 00647 for ( ; offset <= wlen; ) { 00648 00649 /* 00650 * The flags argument is set if string is part of a larger string, so 00651 * that "^" won't match. 00652 */ 00653 00654 match = Tcl_RegExpExecObj(interp, regExpr, objPtr, offset, 00655 10 /* matches */, ((offset > 0 && 00656 (wstring[offset-1] != (Tcl_UniChar)'\n')) 00657 ? TCL_REG_NOTBOL : 0)); 00658 00659 if (match < 0) { 00660 result = TCL_ERROR; 00661 goto done; 00662 } 00663 if (match == 0) { 00664 break; 00665 } 00666 if (numMatches == 0) { 00667 resultPtr = Tcl_NewUnicodeObj(wstring, 0); 00668 Tcl_IncrRefCount(resultPtr); 00669 if (offset > 0) { 00670 /* 00671 * Copy the initial portion of the string in if an offset was 00672 * specified. 00673 */ 00674 00675 Tcl_AppendUnicodeToObj(resultPtr, wstring, offset); 00676 } 00677 } 00678 numMatches++; 00679 00680 /* 00681 * Copy the portion of the source string before the match to the 00682 * result variable. 00683 */ 00684 00685 Tcl_RegExpGetInfo(regExpr, &info); 00686 start = info.matches[0].start; 00687 end = info.matches[0].end; 00688 Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, start); 00689 00690 /* 00691 * Append the subSpec argument to the variable, making appropriate 00692 * substitutions. This code is a bit hairy because of the backslash 00693 * conventions and because the code saves up ranges of characters in 00694 * subSpec to reduce the number of calls to Tcl_SetVar. 00695 */ 00696 00697 wsrc = wfirstChar = wsubspec; 00698 wend = wsubspec + wsublen; 00699 for (ch = *wsrc; wsrc != wend; wsrc++, ch = *wsrc) { 00700 if (ch == '&') { 00701 idx = 0; 00702 } else if (ch == '\\') { 00703 ch = wsrc[1]; 00704 if ((ch >= '0') && (ch <= '9')) { 00705 idx = ch - '0'; 00706 } else if ((ch == '\\') || (ch == '&')) { 00707 *wsrc = ch; 00708 Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, 00709 wsrc - wfirstChar + 1); 00710 *wsrc = '\\'; 00711 wfirstChar = wsrc + 2; 00712 wsrc++; 00713 continue; 00714 } else { 00715 continue; 00716 } 00717 } else { 00718 continue; 00719 } 00720 00721 if (wfirstChar != wsrc) { 00722 Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, 00723 wsrc - wfirstChar); 00724 } 00725 00726 if (idx <= info.nsubs) { 00727 subStart = info.matches[idx].start; 00728 subEnd = info.matches[idx].end; 00729 if ((subStart >= 0) && (subEnd >= 0)) { 00730 Tcl_AppendUnicodeToObj(resultPtr, 00731 wstring + offset + subStart, subEnd - subStart); 00732 } 00733 } 00734 00735 if (*wsrc == '\\') { 00736 wsrc++; 00737 } 00738 wfirstChar = wsrc + 1; 00739 } 00740 00741 if (wfirstChar != wsrc) { 00742 Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, wsrc - wfirstChar); 00743 } 00744 00745 if (end == 0) { 00746 /* 00747 * Always consume at least one character of the input string in 00748 * order to prevent infinite loops. 00749 */ 00750 00751 if (offset < wlen) { 00752 Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1); 00753 } 00754 offset++; 00755 } else { 00756 offset += end; 00757 if (start == end) { 00758 /* 00759 * We matched an empty string, which means we must go forward 00760 * one more step so we don't match again at the same spot. 00761 */ 00762 00763 if (offset < wlen) { 00764 Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1); 00765 } 00766 offset++; 00767 } 00768 } 00769 if (!all) { 00770 break; 00771 } 00772 } 00773 00774 /* 00775 * Copy the portion of the source string after the last match to the 00776 * result variable. 00777 */ 00778 00779 regsubDone: 00780 if (numMatches == 0) { 00781 /* 00782 * On zero matches, just ignore the offset, since it shouldn't matter 00783 * to us in this case, and the user may have skewed it. 00784 */ 00785 00786 resultPtr = objv[1]; 00787 Tcl_IncrRefCount(resultPtr); 00788 } else if (offset < wlen) { 00789 Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset); 00790 } 00791 if (objc == 4) { 00792 if (Tcl_ObjSetVar2(interp, objv[3], NULL, resultPtr, 0) == NULL) { 00793 Tcl_AppendResult(interp, "couldn't set variable \"", 00794 TclGetString(objv[3]), "\"", NULL); 00795 result = TCL_ERROR; 00796 } else { 00797 /* 00798 * Set the interpreter's object result to an integer object 00799 * holding the number of matches. 00800 */ 00801 00802 Tcl_SetObjResult(interp, Tcl_NewIntObj(numMatches)); 00803 } 00804 } else { 00805 /* 00806 * No varname supplied, so just return the modified string. 00807 */ 00808 00809 Tcl_SetObjResult(interp, resultPtr); 00810 } 00811 00812 done: 00813 if (objPtr && (objv[1] == objv[0])) { 00814 Tcl_DecrRefCount(objPtr); 00815 } 00816 if (subPtr && (objv[2] == objv[0])) { 00817 Tcl_DecrRefCount(subPtr); 00818 } 00819 if (resultPtr) { 00820 Tcl_DecrRefCount(resultPtr); 00821 } 00822 return result; 00823 } 00824 00825 /* 00826 *---------------------------------------------------------------------- 00827 * 00828 * Tcl_RenameObjCmd -- 00829 * 00830 * This procedure is invoked to process the "rename" Tcl command. See the 00831 * user documentation for details on what it does. 00832 * 00833 * Results: 00834 * A standard Tcl object result. 00835 * 00836 * Side effects: 00837 * See the user documentation. 00838 * 00839 *---------------------------------------------------------------------- 00840 */ 00841 00842 int 00843 Tcl_RenameObjCmd( 00844 ClientData dummy, /* Arbitrary value passed to the command. */ 00845 Tcl_Interp *interp, /* Current interpreter. */ 00846 int objc, /* Number of arguments. */ 00847 Tcl_Obj *CONST objv[]) /* Argument objects. */ 00848 { 00849 char *oldName, *newName; 00850 00851 if (objc != 3) { 00852 Tcl_WrongNumArgs(interp, 1, objv, "oldName newName"); 00853 return TCL_ERROR; 00854 } 00855 00856 oldName = TclGetString(objv[1]); 00857 newName = TclGetString(objv[2]); 00858 return TclRenameCommand(interp, oldName, newName); 00859 } 00860 00861 /* 00862 *---------------------------------------------------------------------- 00863 * 00864 * Tcl_ReturnObjCmd -- 00865 * 00866 * This object-based procedure is invoked to process the "return" Tcl 00867 * command. See the user documentation for details on what it does. 00868 * 00869 * Results: 00870 * A standard Tcl object result. 00871 * 00872 * Side effects: 00873 * See the user documentation. 00874 * 00875 *---------------------------------------------------------------------- 00876 */ 00877 00878 int 00879 Tcl_ReturnObjCmd( 00880 ClientData dummy, /* Not used. */ 00881 Tcl_Interp *interp, /* Current interpreter. */ 00882 int objc, /* Number of arguments. */ 00883 Tcl_Obj *CONST objv[]) /* Argument objects. */ 00884 { 00885 int code, level; 00886 Tcl_Obj *returnOpts; 00887 00888 /* 00889 * General syntax: [return ?-option value ...? ?result?] 00890 * An even number of words means an explicit result argument is present. 00891 */ 00892 00893 int explicitResult = (0 == (objc % 2)); 00894 int numOptionWords = objc - 1 - explicitResult; 00895 00896 if (TCL_ERROR == TclMergeReturnOptions(interp, numOptionWords, objv+1, 00897 &returnOpts, &code, &level)) { 00898 return TCL_ERROR; 00899 } 00900 00901 code = TclProcessReturn(interp, code, level, returnOpts); 00902 if (explicitResult) { 00903 Tcl_SetObjResult(interp, objv[objc-1]); 00904 } 00905 return code; 00906 } 00907 00908 /* 00909 *---------------------------------------------------------------------- 00910 * 00911 * Tcl_SourceObjCmd -- 00912 * 00913 * This procedure is invoked to process the "source" Tcl command. See the 00914 * user documentation for details on what it does. 00915 * 00916 * Results: 00917 * A standard Tcl object result. 00918 * 00919 * Side effects: 00920 * See the user documentation. 00921 * 00922 *---------------------------------------------------------------------- 00923 */ 00924 00925 int 00926 Tcl_SourceObjCmd( 00927 ClientData dummy, /* Not used. */ 00928 Tcl_Interp *interp, /* Current interpreter. */ 00929 int objc, /* Number of arguments. */ 00930 Tcl_Obj *CONST objv[]) /* Argument objects. */ 00931 { 00932 CONST char *encodingName = NULL; 00933 Tcl_Obj *fileName; 00934 00935 if (objc != 2 && objc !=4) { 00936 Tcl_WrongNumArgs(interp, 1, objv, "?-encoding name? fileName"); 00937 return TCL_ERROR; 00938 } 00939 00940 fileName = objv[objc-1]; 00941 00942 if (objc == 4) { 00943 static CONST char *options[] = { 00944 "-encoding", NULL 00945 }; 00946 int index; 00947 00948 if (TCL_ERROR == Tcl_GetIndexFromObj(interp, objv[1], options, 00949 "option", TCL_EXACT, &index)) { 00950 return TCL_ERROR; 00951 } 00952 encodingName = TclGetString(objv[2]); 00953 } 00954 00955 return Tcl_FSEvalFileEx(interp, fileName, encodingName); 00956 } 00957 00958 /* 00959 *---------------------------------------------------------------------- 00960 * 00961 * Tcl_SplitObjCmd -- 00962 * 00963 * This procedure is invoked to process the "split" Tcl command. See the 00964 * user documentation for details on what it does. 00965 * 00966 * Results: 00967 * A standard Tcl result. 00968 * 00969 * Side effects: 00970 * See the user documentation. 00971 * 00972 *---------------------------------------------------------------------- 00973 */ 00974 00975 int 00976 Tcl_SplitObjCmd( 00977 ClientData dummy, /* Not used. */ 00978 Tcl_Interp *interp, /* Current interpreter. */ 00979 int objc, /* Number of arguments. */ 00980 Tcl_Obj *CONST objv[]) /* Argument objects. */ 00981 { 00982 Tcl_UniChar ch; 00983 int len; 00984 char *splitChars, *stringPtr, *end; 00985 int splitCharLen, stringLen; 00986 Tcl_Obj *listPtr, *objPtr; 00987 00988 if (objc == 2) { 00989 splitChars = " \n\t\r"; 00990 splitCharLen = 4; 00991 } else if (objc == 3) { 00992 splitChars = TclGetStringFromObj(objv[2], &splitCharLen); 00993 } else { 00994 Tcl_WrongNumArgs(interp, 1, objv, "string ?splitChars?"); 00995 return TCL_ERROR; 00996 } 00997 00998 stringPtr = TclGetStringFromObj(objv[1], &stringLen); 00999 end = stringPtr + stringLen; 01000 listPtr = Tcl_NewObj(); 01001 01002 if (stringLen == 0) { 01003 /* 01004 * Do nothing. 01005 */ 01006 } else if (splitCharLen == 0) { 01007 Tcl_HashTable charReuseTable; 01008 Tcl_HashEntry *hPtr; 01009 int isNew; 01010 01011 /* 01012 * Handle the special case of splitting on every character. 01013 * 01014 * Uses a hash table to ensure that each kind of character has only 01015 * one Tcl_Obj instance (multiply-referenced) in the final list. This 01016 * is a *major* win when splitting on a long string (especially in the 01017 * megabyte range!) - DKF 01018 */ 01019 01020 Tcl_InitHashTable(&charReuseTable, TCL_ONE_WORD_KEYS); 01021 01022 for ( ; stringPtr < end; stringPtr += len) { 01023 len = TclUtfToUniChar(stringPtr, &ch); 01024 01025 /* 01026 * Assume Tcl_UniChar is an integral type... 01027 */ 01028 01029 hPtr = Tcl_CreateHashEntry(&charReuseTable, (char*)0+ch, &isNew); 01030 if (isNew) { 01031 TclNewStringObj(objPtr, stringPtr, len); 01032 01033 /* 01034 * Don't need to fiddle with refcount... 01035 */ 01036 01037 Tcl_SetHashValue(hPtr, (ClientData) objPtr); 01038 } else { 01039 objPtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); 01040 } 01041 Tcl_ListObjAppendElement(NULL, listPtr, objPtr); 01042 } 01043 Tcl_DeleteHashTable(&charReuseTable); 01044 01045 } else if (splitCharLen == 1) { 01046 char *p; 01047 01048 /* 01049 * Handle the special case of splitting on a single character. This is 01050 * only true for the one-char ASCII case, as one unicode char is > 1 01051 * byte in length. 01052 */ 01053 01054 while (*stringPtr && (p=strchr(stringPtr,(int)*splitChars)) != NULL) { 01055 objPtr = Tcl_NewStringObj(stringPtr, p - stringPtr); 01056 Tcl_ListObjAppendElement(NULL, listPtr, objPtr); 01057 stringPtr = p + 1; 01058 } 01059 TclNewStringObj(objPtr, stringPtr, end - stringPtr); 01060 Tcl_ListObjAppendElement(NULL, listPtr, objPtr); 01061 } else { 01062 char *element, *p, *splitEnd; 01063 int splitLen; 01064 Tcl_UniChar splitChar; 01065 01066 /* 01067 * Normal case: split on any of a given set of characters. Discard 01068 * instances of the split characters. 01069 */ 01070 01071 splitEnd = splitChars + splitCharLen; 01072 01073 for (element = stringPtr; stringPtr < end; stringPtr += len) { 01074 len = TclUtfToUniChar(stringPtr, &ch); 01075 for (p = splitChars; p < splitEnd; p += splitLen) { 01076 splitLen = TclUtfToUniChar(p, &splitChar); 01077 if (ch == splitChar) { 01078 TclNewStringObj(objPtr, element, stringPtr - element); 01079 Tcl_ListObjAppendElement(NULL, listPtr, objPtr); 01080 element = stringPtr + len; 01081 break; 01082 } 01083 } 01084 } 01085 01086 TclNewStringObj(objPtr, element, stringPtr - element); 01087 Tcl_ListObjAppendElement(NULL, listPtr, objPtr); 01088 } 01089 Tcl_SetObjResult(interp, listPtr); 01090 return TCL_OK; 01091 } 01092 01093 /* 01094 *---------------------------------------------------------------------- 01095 * 01096 * StringFirstCmd -- 01097 * 01098 * This procedure is invoked to process the "string first" Tcl command. 01099 * See the user documentation for details on what it does. Note that this 01100 * command only functions correctly on properly formed Tcl UTF strings. 01101 * 01102 * Results: 01103 * A standard Tcl result. 01104 * 01105 * Side effects: 01106 * See the user documentation. 01107 * 01108 *---------------------------------------------------------------------- 01109 */ 01110 01111 static int 01112 StringFirstCmd( 01113 ClientData dummy, /* Not used. */ 01114 Tcl_Interp *interp, /* Current interpreter. */ 01115 int objc, /* Number of arguments. */ 01116 Tcl_Obj *const objv[]) /* Argument objects. */ 01117 { 01118 Tcl_UniChar *ustring1, *ustring2; 01119 int match, start, length1, length2; 01120 01121 if (objc < 3 || objc > 4) { 01122 Tcl_WrongNumArgs(interp, 1, objv, 01123 "needleString haystackString ?startIndex?"); 01124 return TCL_ERROR; 01125 } 01126 01127 /* 01128 * We are searching string2 for the sequence string1. 01129 */ 01130 01131 match = -1; 01132 start = 0; 01133 length2 = -1; 01134 01135 ustring1 = Tcl_GetUnicodeFromObj(objv[1], &length1); 01136 ustring2 = Tcl_GetUnicodeFromObj(objv[2], &length2); 01137 01138 if (objc == 4) { 01139 /* 01140 * If a startIndex is specified, we will need to fast forward to that 01141 * point in the string before we think about a match. 01142 */ 01143 01144 if (TclGetIntForIndexM(interp, objv[3], length2-1, &start) != TCL_OK){ 01145 return TCL_ERROR; 01146 } 01147 01148 /* 01149 * Reread to prevent shimmering problems. 01150 */ 01151 01152 ustring1 = Tcl_GetUnicodeFromObj(objv[1], &length1); 01153 ustring2 = Tcl_GetUnicodeFromObj(objv[2], &length2); 01154 01155 if (start >= length2) { 01156 goto str_first_done; 01157 } else if (start > 0) { 01158 ustring2 += start; 01159 length2 -= start; 01160 } else if (start < 0) { 01161 /* 01162 * Invalid start index mapped to string start; Bug #423581 01163 */ 01164 01165 start = 0; 01166 } 01167 } 01168 01169 if (length1 > 0) { 01170 register Tcl_UniChar *p, *end; 01171 01172 end = ustring2 + length2 - length1 + 1; 01173 for (p = ustring2; p < end; p++) { 01174 /* 01175 * Scan forward to find the first character. 01176 */ 01177 01178 if ((*p == *ustring1) && (TclUniCharNcmp(ustring1, p, 01179 (unsigned long) length1) == 0)) { 01180 match = p - ustring2; 01181 break; 01182 } 01183 } 01184 } 01185 01186 /* 01187 * Compute the character index of the matching string by counting the 01188 * number of characters before the match. 01189 */ 01190 01191 if ((match != -1) && (objc == 4)) { 01192 match += start; 01193 } 01194 01195 str_first_done: 01196 Tcl_SetObjResult(interp, Tcl_NewIntObj(match)); 01197 return TCL_OK; 01198 } 01199 01200 /* 01201 *---------------------------------------------------------------------- 01202 * 01203 * StringLastCmd -- 01204 * 01205 * This procedure is invoked to process the "string last" Tcl command. 01206 * See the user documentation for details on what it does. Note that this 01207 * command only functions correctly on properly formed Tcl UTF strings. 01208 * 01209 * Results: 01210 * A standard Tcl result. 01211 * 01212 * Side effects: 01213 * See the user documentation. 01214 * 01215 *---------------------------------------------------------------------- 01216 */ 01217 01218 static int 01219 StringLastCmd( 01220 ClientData dummy, /* Not used. */ 01221 Tcl_Interp *interp, /* Current interpreter. */ 01222 int objc, /* Number of arguments. */ 01223 Tcl_Obj *const objv[]) /* Argument objects. */ 01224 { 01225 Tcl_UniChar *ustring1, *ustring2, *p; 01226 int match, start, length1, length2; 01227 01228 if (objc < 3 || objc > 4) { 01229 Tcl_WrongNumArgs(interp, 1, objv, 01230 "needleString haystackString ?startIndex?"); 01231 return TCL_ERROR; 01232 } 01233 01234 /* 01235 * We are searching string2 for the sequence string1. 01236 */ 01237 01238 match = -1; 01239 start = 0; 01240 length2 = -1; 01241 01242 ustring1 = Tcl_GetUnicodeFromObj(objv[1], &length1); 01243 ustring2 = Tcl_GetUnicodeFromObj(objv[2], &length2); 01244 01245 if (objc == 4) { 01246 /* 01247 * If a startIndex is specified, we will need to restrict the string 01248 * range to that char index in the string 01249 */ 01250 01251 if (TclGetIntForIndexM(interp, objv[3], length2-1, &start) != TCL_OK){ 01252 return TCL_ERROR; 01253 } 01254 01255 /* 01256 * Reread to prevent shimmering problems. 01257 */ 01258 01259 ustring1 = Tcl_GetUnicodeFromObj(objv[1], &length1); 01260 ustring2 = Tcl_GetUnicodeFromObj(objv[2], &length2); 01261 01262 if (start < 0) { 01263 goto str_last_done; 01264 } else if (start < length2) { 01265 p = ustring2 + start + 1 - length1; 01266 } else { 01267 p = ustring2 + length2 - length1; 01268 } 01269 } else { 01270 p = ustring2 + length2 - length1; 01271 } 01272 01273 if (length1 > 0) { 01274 for (; p >= ustring2; p--) { 01275 /* 01276 * Scan backwards to find the first character. 01277 */ 01278 01279 if ((*p == *ustring1) && !memcmp(ustring1, p, 01280 sizeof(Tcl_UniChar) * (size_t)length1)) { 01281 match = p - ustring2; 01282 break; 01283 } 01284 } 01285 } 01286 01287 str_last_done: 01288 Tcl_SetObjResult(interp, Tcl_NewIntObj(match)); 01289 return TCL_OK; 01290 } 01291 01292 /* 01293 *---------------------------------------------------------------------- 01294 * 01295 * StringIndexCmd -- 01296 * 01297 * This procedure is invoked to process the "string index" Tcl command. 01298 * See the user documentation for details on what it does. Note that this 01299 * command only functions correctly on properly formed Tcl UTF strings. 01300 * 01301 * Results: 01302 * A standard Tcl result. 01303 * 01304 * Side effects: 01305 * See the user documentation. 01306 * 01307 *---------------------------------------------------------------------- 01308 */ 01309 01310 static int 01311 StringIndexCmd( 01312 ClientData dummy, /* Not used. */ 01313 Tcl_Interp *interp, /* Current interpreter. */ 01314 int objc, /* Number of arguments. */ 01315 Tcl_Obj *const objv[]) /* Argument objects. */ 01316 { 01317 int length, index; 01318 01319 if (objc != 3) { 01320 Tcl_WrongNumArgs(interp, 1, objv, "string charIndex"); 01321 return TCL_ERROR; 01322 } 01323 01324 /* 01325 * If we have a ByteArray object, avoid indexing in the Utf string since 01326 * the byte array contains one byte per character. Otherwise, use the 01327 * Unicode string rep to get the index'th char. 01328 */ 01329 01330 if (objv[1]->typePtr == &tclByteArrayType) { 01331 const unsigned char *string = 01332 Tcl_GetByteArrayFromObj(objv[1], &length); 01333 01334 if (TclGetIntForIndexM(interp, objv[2], length-1, &index) != TCL_OK){ 01335 return TCL_ERROR; 01336 } 01337 string = Tcl_GetByteArrayFromObj(objv[1], &length); 01338 if ((index >= 0) && (index < length)) { 01339 Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(string + index, 1)); 01340 } 01341 } else { 01342 /* 01343 * Get Unicode char length to calulate what 'end' means. 01344 */ 01345 01346 length = Tcl_GetCharLength(objv[1]); 01347 01348 if (TclGetIntForIndexM(interp, objv[2], length-1, &index) != TCL_OK){ 01349 return TCL_ERROR; 01350 } 01351 if ((index >= 0) && (index < length)) { 01352 char buf[TCL_UTF_MAX]; 01353 Tcl_UniChar ch; 01354 01355 ch = Tcl_GetUniChar(objv[1], index); 01356 length = Tcl_UniCharToUtf(ch, buf); 01357 Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, length)); 01358 } 01359 } 01360 return TCL_OK; 01361 } 01362 01363 /* 01364 *---------------------------------------------------------------------- 01365 * 01366 * StringIsCmd -- 01367 * 01368 * This procedure is invoked to process the "string is" Tcl command. See 01369 * the user documentation for details on what it does. Note that this 01370 * command only functions correctly on properly formed Tcl UTF strings. 01371 * 01372 * Results: 01373 * A standard Tcl result. 01374 * 01375 * Side effects: 01376 * See the user documentation. 01377 * 01378 *---------------------------------------------------------------------- 01379 */ 01380 01381 static int 01382 StringIsCmd( 01383 ClientData dummy, /* Not used. */ 01384 Tcl_Interp *interp, /* Current interpreter. */ 01385 int objc, /* Number of arguments. */ 01386 Tcl_Obj *const objv[]) /* Argument objects. */ 01387 { 01388 const char *string1, *string2, *end, *stop; 01389 Tcl_UniChar ch; 01390 int (*chcomp)(int) = NULL; /* The UniChar comparison function. */ 01391 int i, failat = 0, result = 1, strict = 0, index, length1, length2; 01392 Tcl_Obj *objPtr, *failVarObj = NULL; 01393 Tcl_WideInt w; 01394 01395 static const char *isOptions[] = { 01396 "alnum", "alpha", "ascii", "control", 01397 "boolean", "digit", "double", "false", 01398 "graph", "integer", "list", "lower", 01399 "print", "punct", "space", "true", 01400 "upper", "wideinteger", "wordchar", "xdigit", 01401 NULL 01402 }; 01403 enum isOptions { 01404 STR_IS_ALNUM, STR_IS_ALPHA, STR_IS_ASCII, STR_IS_CONTROL, 01405 STR_IS_BOOL, STR_IS_DIGIT, STR_IS_DOUBLE, STR_IS_FALSE, 01406 STR_IS_GRAPH, STR_IS_INT, STR_IS_LIST, STR_IS_LOWER, 01407 STR_IS_PRINT, STR_IS_PUNCT, STR_IS_SPACE, STR_IS_TRUE, 01408 STR_IS_UPPER, STR_IS_WIDE, STR_IS_WORD, STR_IS_XDIGIT 01409 }; 01410 01411 if (objc < 3 || objc > 6) { 01412 Tcl_WrongNumArgs(interp, 1, objv, 01413 "class ?-strict? ?-failindex var? str"); 01414 return TCL_ERROR; 01415 } 01416 if (Tcl_GetIndexFromObj(interp, objv[1], isOptions, "class", 0, 01417 &index) != TCL_OK) { 01418 return TCL_ERROR; 01419 } 01420 01421 if (objc != 3) { 01422 for (i = 2; i < objc-1; i++) { 01423 string2 = TclGetStringFromObj(objv[i], &length2); 01424 if ((length2 > 1) && 01425 strncmp(string2, "-strict", (size_t) length2) == 0) { 01426 strict = 1; 01427 } else if ((length2 > 1) && 01428 strncmp(string2, "-failindex", (size_t)length2) == 0){ 01429 if (i+1 >= objc-1) { 01430 Tcl_WrongNumArgs(interp, 2, objv, 01431 "?-strict? ?-failindex var? str"); 01432 return TCL_ERROR; 01433 } 01434 failVarObj = objv[++i]; 01435 } else { 01436 Tcl_AppendResult(interp, "bad option \"", string2, 01437 "\": must be -strict or -failindex", NULL); 01438 return TCL_ERROR; 01439 } 01440 } 01441 } 01442 01443 /* 01444 * We get the objPtr so that we can short-cut for some classes by checking 01445 * the object type (int and double), but we need the string otherwise, 01446 * because we don't want any conversion of type occuring (as, for example, 01447 * Tcl_Get*FromObj would do). 01448 */ 01449 01450 objPtr = objv[objc-1]; 01451 string1 = TclGetStringFromObj(objPtr, &length1); 01452 if (length1 == 0 && index != STR_IS_LIST) { 01453 if (strict) { 01454 result = 0; 01455 } 01456 goto str_is_done; 01457 } 01458 end = string1 + length1; 01459 01460 /* 01461 * When entering here, result == 1 and failat == 0. 01462 */ 01463 01464 switch ((enum isOptions) index) { 01465 case STR_IS_ALNUM: 01466 chcomp = Tcl_UniCharIsAlnum; 01467 break; 01468 case STR_IS_ALPHA: 01469 chcomp = Tcl_UniCharIsAlpha; 01470 break; 01471 case STR_IS_ASCII: 01472 chcomp = UniCharIsAscii; 01473 break; 01474 case STR_IS_BOOL: 01475 case STR_IS_TRUE: 01476 case STR_IS_FALSE: 01477 if (TCL_OK != Tcl_ConvertToType(NULL, objPtr, &tclBooleanType)) { 01478 result = 0; 01479 } else if (((index == STR_IS_TRUE) && 01480 objPtr->internalRep.longValue == 0) 01481 || ((index == STR_IS_FALSE) && 01482 objPtr->internalRep.longValue != 0)) { 01483 result = 0; 01484 } 01485 break; 01486 case STR_IS_CONTROL: 01487 chcomp = Tcl_UniCharIsControl; 01488 break; 01489 case STR_IS_DIGIT: 01490 chcomp = Tcl_UniCharIsDigit; 01491 break; 01492 case STR_IS_DOUBLE: { 01493 /* TODO */ 01494 if ((objPtr->typePtr == &tclDoubleType) || 01495 (objPtr->typePtr == &tclIntType) || 01496 #ifndef NO_WIDE_TYPE 01497 (objPtr->typePtr == &tclWideIntType) || 01498 #endif 01499 (objPtr->typePtr == &tclBignumType)) { 01500 break; 01501 } 01502 if (TclParseNumber(NULL, objPtr, NULL, NULL, -1, 01503 (const char **) &stop, 0) != TCL_OK) { 01504 result = 0; 01505 failat = 0; 01506 } else { 01507 failat = stop - string1; 01508 if (stop < end) { 01509 result = 0; 01510 TclFreeIntRep(objPtr); 01511 objPtr->typePtr = NULL; 01512 } 01513 } 01514 break; 01515 } 01516 case STR_IS_GRAPH: 01517 chcomp = Tcl_UniCharIsGraph; 01518 break; 01519 case STR_IS_INT: 01520 if (TCL_OK == TclGetIntFromObj(NULL, objPtr, &i)) { 01521 break; 01522 } 01523 goto failedIntParse; 01524 case STR_IS_WIDE: 01525 if (TCL_OK == Tcl_GetWideIntFromObj(NULL, objPtr, &w)) { 01526 break; 01527 } 01528 01529 failedIntParse: 01530 result = 0; 01531 01532 if (failVarObj == NULL) { 01533 /* 01534 * Don't bother computing the failure point if we're not going to 01535 * return it. 01536 */ 01537 01538 break; 01539 } 01540 if (TclParseNumber(NULL, objPtr, NULL, NULL, -1, 01541 (const char **) &stop, TCL_PARSE_INTEGER_ONLY) == TCL_OK) { 01542 if (stop == end) { 01543 /* 01544 * Entire string parses as an integer, but rejected by 01545 * Tcl_Get(Wide)IntFromObj() so we must have overflowed the 01546 * target type, and our convention is to return failure at 01547 * index -1 in that situation. 01548 */ 01549 01550 failat = -1; 01551 } else { 01552 /* 01553 * Some prefix parsed as an integer, but not the whole string, 01554 * so return failure index as the point where parsing stopped. 01555 * Clear out the internal rep, since keeping it would leave 01556 * *objPtr in an inconsistent state. 01557 */ 01558 01559 failat = stop - string1; 01560 TclFreeIntRep(objPtr); 01561 objPtr->typePtr = NULL; 01562 } 01563 } else { 01564 /* 01565 * No prefix is a valid integer. Fail at beginning. 01566 */ 01567 01568 failat = 0; 01569 } 01570 break; 01571 case STR_IS_LIST: 01572 /* 01573 * We ignore the strictness here, since empty strings are always 01574 * well-formed lists. 01575 */ 01576 01577 if (TCL_OK == TclListObjLength(NULL, objPtr, &length2)) { 01578 break; 01579 } 01580 01581 if (failVarObj != NULL) { 01582 /* 01583 * Need to figure out where the list parsing failed, which is 01584 * fairly expensive. This is adapted from the core of 01585 * SetListFromAny(). 01586 */ 01587 01588 const char *elemStart, *nextElem, *limit; 01589 int lenRemain, elemSize, hasBrace; 01590 register const char *p; 01591 01592 limit = string1 + length1; 01593 failat = -1; 01594 for (p=string1, lenRemain=length1; lenRemain > 0; 01595 p=nextElem, lenRemain=limit-nextElem) { 01596 if (TCL_ERROR == TclFindElement(NULL, p, lenRemain, 01597 &elemStart, &nextElem, &elemSize, &hasBrace)) { 01598 Tcl_Obj *tmpStr; 01599 01600 /* 01601 * This is the simplest way of getting the number of 01602 * characters parsed. Note that this is not the same as 01603 * the number of bytes when parsing strings with non-ASCII 01604 * characters in them. 01605 * 01606 * Skip leading spaces first. This is only really an issue 01607 * if it is the first "element" that has the failure. 01608 */ 01609 01610 while (isspace(UCHAR(*p))) { /* INTL: ? */ 01611 p++; 01612 } 01613 TclNewStringObj(tmpStr, string1, p-string1); 01614 failat = Tcl_GetCharLength(tmpStr); 01615 TclDecrRefCount(tmpStr); 01616 break; 01617 } 01618 } 01619 } 01620 result = 0; 01621 break; 01622 case STR_IS_LOWER: 01623 chcomp = Tcl_UniCharIsLower; 01624 break; 01625 case STR_IS_PRINT: 01626 chcomp = Tcl_UniCharIsPrint; 01627 break; 01628 case STR_IS_PUNCT: 01629 chcomp = Tcl_UniCharIsPunct; 01630 break; 01631 case STR_IS_SPACE: 01632 chcomp = Tcl_UniCharIsSpace; 01633 break; 01634 case STR_IS_UPPER: 01635 chcomp = Tcl_UniCharIsUpper; 01636 break; 01637 case STR_IS_WORD: 01638 chcomp = Tcl_UniCharIsWordChar; 01639 break; 01640 case STR_IS_XDIGIT: 01641 for (; string1 < end; string1++, failat++) { 01642 /* INTL: We assume unicode is bad for this class. */ 01643 if ((*((unsigned char *)string1) >= 0xC0) || 01644 !isxdigit(*(unsigned char *)string1)) { 01645 result = 0; 01646 break; 01647 } 01648 } 01649 break; 01650 } 01651 if (chcomp != NULL) { 01652 for (; string1 < end; string1 += length2, failat++) { 01653 length2 = TclUtfToUniChar(string1, &ch); 01654 if (!chcomp(ch)) { 01655 result = 0; 01656 break; 01657 } 01658 } 01659 } 01660 01661 /* 01662 * Only set the failVarObj when we will return 0 and we have indicated a 01663 * valid fail index (>= 0). 01664 */ 01665 01666 str_is_done: 01667 if ((result == 0) && (failVarObj != NULL) && 01668 Tcl_ObjSetVar2(interp, failVarObj, NULL, Tcl_NewIntObj(failat), 01669 TCL_LEAVE_ERR_MSG) == NULL) { 01670 return TCL_ERROR; 01671 } 01672 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); 01673 return TCL_OK; 01674 } 01675 01676 static int 01677 UniCharIsAscii( 01678 int character) 01679 { 01680 return (character >= 0) && (character < 0x80); 01681 } 01682 01683 /* 01684 *---------------------------------------------------------------------- 01685 * 01686 * StringMapCmd -- 01687 * 01688 * This procedure is invoked to process the "string map" Tcl command. See 01689 * the user documentation for details on what it does. Note that this 01690 * command only functions correctly on properly formed Tcl UTF strings. 01691 * 01692 * Results: 01693 * A standard Tcl result. 01694 * 01695 * Side effects: 01696 * See the user documentation. 01697 * 01698 *---------------------------------------------------------------------- 01699 */ 01700 01701 static int 01702 StringMapCmd( 01703 ClientData dummy, /* Not used. */ 01704 Tcl_Interp *interp, /* Current interpreter. */ 01705 int objc, /* Number of arguments. */ 01706 Tcl_Obj *const objv[]) /* Argument objects. */ 01707 { 01708 int length1, length2, mapElemc, index; 01709 int nocase = 0, mapWithDict = 0, copySource = 0; 01710 Tcl_Obj **mapElemv, *sourceObj, *resultPtr; 01711 Tcl_UniChar *ustring1, *ustring2, *p, *end; 01712 int (*strCmpFn)(const Tcl_UniChar*, const Tcl_UniChar*, unsigned long); 01713 01714 if (objc < 3 || objc > 4) { 01715 Tcl_WrongNumArgs(interp, 1, objv, "?-nocase? charMap string"); 01716 return TCL_ERROR; 01717 } 01718 01719 if (objc == 4) { 01720 const char *string = TclGetStringFromObj(objv[1], &length2); 01721 01722 if ((length2 > 1) && 01723 strncmp(string, "-nocase", (size_t) length2) == 0) { 01724 nocase = 1; 01725 } else { 01726 Tcl_AppendResult(interp, "bad option \"", string, 01727 "\": must be -nocase", NULL); 01728 return TCL_ERROR; 01729 } 01730 } 01731 01732 /* 01733 * This test is tricky, but has to be that way or you get other strange 01734 * inconsistencies (see test string-10.20 for illustration why!) 01735 */ 01736 01737 if (objv[objc-2]->typePtr == &tclDictType && objv[objc-2]->bytes == NULL){ 01738 int i, done; 01739 Tcl_DictSearch search; 01740 01741 /* 01742 * We know the type exactly, so all dict operations will succeed for 01743 * sure. This shortens this code quite a bit. 01744 */ 01745 01746 Tcl_DictObjSize(interp, objv[objc-2], &mapElemc); 01747 if (mapElemc == 0) { 01748 /* 01749 * Empty charMap, just return whatever string was given. 01750 */ 01751 01752 Tcl_SetObjResult(interp, objv[objc-1]); 01753 return TCL_OK; 01754 } 01755 01756 mapElemc *= 2; 01757 mapWithDict = 1; 01758 01759 /* 01760 * Copy the dictionary out into an array; that's the easiest way to 01761 * adapt this code... 01762 */ 01763 01764 mapElemv = (Tcl_Obj **) 01765 TclStackAlloc(interp, sizeof(Tcl_Obj *) * mapElemc); 01766 Tcl_DictObjFirst(interp, objv[objc-2], &search, mapElemv+0, 01767 mapElemv+1, &done); 01768 for (i=2 ; i<mapElemc ; i+=2) { 01769 Tcl_DictObjNext(&search, mapElemv+i, mapElemv+i+1, &done); 01770 } 01771 Tcl_DictObjDone(&search); 01772 } else { 01773 if (TclListObjGetElements(interp, objv[objc-2], &mapElemc, 01774 &mapElemv) != TCL_OK) { 01775 return TCL_ERROR; 01776 } 01777 if (mapElemc == 0) { 01778 /* 01779 * empty charMap, just return whatever string was given. 01780 */ 01781 01782 Tcl_SetObjResult(interp, objv[objc-1]); 01783 return TCL_OK; 01784 } else if (mapElemc & 1) { 01785 /* 01786 * The charMap must be an even number of key/value items. 01787 */ 01788 01789 Tcl_SetObjResult(interp, 01790 Tcl_NewStringObj("char map list unbalanced", -1)); 01791 return TCL_ERROR; 01792 } 01793 } 01794 01795 /* 01796 * Take a copy of the source string object if it is the same as the map 01797 * string to cut out nasty sharing crashes. [Bug 1018562] 01798 */ 01799 01800 if (objv[objc-2] == objv[objc-1]) { 01801 sourceObj = Tcl_DuplicateObj(objv[objc-1]); 01802 copySource = 1; 01803 } else { 01804 sourceObj = objv[objc-1]; 01805 } 01806 ustring1 = Tcl_GetUnicodeFromObj(sourceObj, &length1); 01807 if (length1 == 0) { 01808 /* 01809 * Empty input string, just stop now. 01810 */ 01811 01812 goto done; 01813 } 01814 end = ustring1 + length1; 01815 01816 strCmpFn = (nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp); 01817 01818 /* 01819 * Force result to be Unicode 01820 */ 01821 01822 resultPtr = Tcl_NewUnicodeObj(ustring1, 0); 01823 01824 if (mapElemc == 2) { 01825 /* 01826 * Special case for one map pair which avoids the extra for loop and 01827 * extra calls to get Unicode data. The algorithm is otherwise 01828 * identical to the multi-pair case. This will be >30% faster on 01829 * larger strings. 01830 */ 01831 01832 int mapLen; 01833 Tcl_UniChar *mapString, u2lc; 01834 01835 ustring2 = Tcl_GetUnicodeFromObj(mapElemv[0], &length2); 01836 p = ustring1; 01837 if ((length2 > length1) || (length2 == 0)) { 01838 /* 01839 * Match string is either longer than input or empty. 01840 */ 01841 01842 ustring1 = end; 01843 } else { 01844 mapString = Tcl_GetUnicodeFromObj(mapElemv[1], &mapLen); 01845 u2lc = (nocase ? Tcl_UniCharToLower(*ustring2) : 0); 01846 for (; ustring1 < end; ustring1++) { 01847 if (((*ustring1 == *ustring2) || 01848 (nocase&&Tcl_UniCharToLower(*ustring1)==u2lc)) && 01849 (length2==1 || strCmpFn(ustring1, ustring2, 01850 (unsigned long) length2) == 0)) { 01851 if (p != ustring1) { 01852 Tcl_AppendUnicodeToObj(resultPtr, p, ustring1-p); 01853 p = ustring1 + length2; 01854 } else { 01855 p += length2; 01856 } 01857 ustring1 = p - 1; 01858 01859 Tcl_AppendUnicodeToObj(resultPtr, mapString, mapLen); 01860 } 01861 } 01862 } 01863 } else { 01864 Tcl_UniChar **mapStrings, *u2lc = NULL; 01865 int *mapLens; 01866 01867 /* 01868 * Precompute pointers to the unicode string and length. This saves us 01869 * repeated function calls later, significantly speeding up the 01870 * algorithm. We only need the lowercase first char in the nocase 01871 * case. 01872 */ 01873 01874 mapStrings = (Tcl_UniChar **) TclStackAlloc(interp, 01875 mapElemc * 2 * sizeof(Tcl_UniChar *)); 01876 mapLens = (int *) TclStackAlloc(interp, mapElemc * 2 * sizeof(int)); 01877 if (nocase) { 01878 u2lc = (Tcl_UniChar *) TclStackAlloc(interp, 01879 mapElemc * sizeof(Tcl_UniChar)); 01880 } 01881 for (index = 0; index < mapElemc; index++) { 01882 mapStrings[index] = Tcl_GetUnicodeFromObj(mapElemv[index], 01883 mapLens+index); 01884 if (nocase && ((index % 2) == 0)) { 01885 u2lc[index/2] = Tcl_UniCharToLower(*mapStrings[index]); 01886 } 01887 } 01888 for (p = ustring1; ustring1 < end; ustring1++) { 01889 for (index = 0; index < mapElemc; index += 2) { 01890 /* 01891 * Get the key string to match on. 01892 */ 01893 01894 ustring2 = mapStrings[index]; 01895 length2 = mapLens[index]; 01896 if ((length2 > 0) && ((*ustring1 == *ustring2) || (nocase && 01897 (Tcl_UniCharToLower(*ustring1) == u2lc[index/2]))) && 01898 /* Restrict max compare length. */ 01899 (end-ustring1 >= length2) && ((length2 == 1) || 01900 !strCmpFn(ustring2, ustring1, (unsigned) length2))) { 01901 if (p != ustring1) { 01902 /* 01903 * Put the skipped chars onto the result first. 01904 */ 01905 01906 Tcl_AppendUnicodeToObj(resultPtr, p, ustring1-p); 01907 p = ustring1 + length2; 01908 } else { 01909 p += length2; 01910 } 01911 01912 /* 01913 * Adjust len to be full length of matched string. 01914 */ 01915 01916 ustring1 = p - 1; 01917 01918 /* 01919 * Append the map value to the unicode string. 01920 */ 01921 01922 Tcl_AppendUnicodeToObj(resultPtr, 01923 mapStrings[index+1], mapLens[index+1]); 01924 break; 01925 } 01926 } 01927 } 01928 if (nocase) { 01929 TclStackFree(interp, u2lc); 01930 } 01931 TclStackFree(interp, mapLens); 01932 TclStackFree(interp, mapStrings); 01933 } 01934 if (p != ustring1) { 01935 /* 01936 * Put the rest of the unmapped chars onto result. 01937 */ 01938 01939 Tcl_AppendUnicodeToObj(resultPtr, p, ustring1 - p); 01940 } 01941 Tcl_SetObjResult(interp, resultPtr); 01942 done: 01943 if (mapWithDict) { 01944 TclStackFree(interp, mapElemv); 01945 } 01946 if (copySource) { 01947 Tcl_DecrRefCount(sourceObj); 01948 } 01949 return TCL_OK; 01950 } 01951 01952 /* 01953 *---------------------------------------------------------------------- 01954 * 01955 * StringMatchCmd -- 01956 * 01957 * This procedure is invoked to process the "string match" Tcl command. 01958 * See the user documentation for details on what it does. Note that this 01959 * command only functions correctly on properly formed Tcl UTF strings. 01960 * 01961 * Results: 01962 * A standard Tcl result. 01963 * 01964 * Side effects: 01965 * See the user documentation. 01966 * 01967 *---------------------------------------------------------------------- 01968 */ 01969 01970 static int 01971 StringMatchCmd( 01972 ClientData dummy, /* Not used. */ 01973 Tcl_Interp *interp, /* Current interpreter. */ 01974 int objc, /* Number of arguments. */ 01975 Tcl_Obj *const objv[]) /* Argument objects. */ 01976 { 01977 int nocase = 0; 01978 01979 if (objc < 3 || objc > 4) { 01980 Tcl_WrongNumArgs(interp, 1, objv, "?-nocase? pattern string"); 01981 return TCL_ERROR; 01982 } 01983 01984 if (objc == 4) { 01985 int length; 01986 const char *string = TclGetStringFromObj(objv[1], &length); 01987 01988 if ((length > 1) && 01989 strncmp(string, "-nocase", (size_t) length) == 0) { 01990 nocase = TCL_MATCH_NOCASE; 01991 } else { 01992 Tcl_AppendResult(interp, "bad option \"", string, 01993 "\": must be -nocase", NULL); 01994 return TCL_ERROR; 01995 } 01996 } 01997 Tcl_SetObjResult(interp, Tcl_NewBooleanObj( 01998 TclStringMatchObj(objv[objc-1], objv[objc-2], nocase))); 01999 return TCL_OK; 02000 } 02001 02002 /* 02003 *---------------------------------------------------------------------- 02004 * 02005 * StringRangeCmd -- 02006 * 02007 * This procedure is invoked to process the "string range" Tcl command. 02008 * See the user documentation for details on what it does. Note that this 02009 * command only functions correctly on properly formed Tcl UTF strings. 02010 * 02011 * Results: 02012 * A standard Tcl result. 02013 * 02014 * Side effects: 02015 * See the user documentation. 02016 * 02017 *---------------------------------------------------------------------- 02018 */ 02019 02020 static int 02021 StringRangeCmd( 02022 ClientData dummy, /* Not used. */ 02023 Tcl_Interp *interp, /* Current interpreter. */ 02024 int objc, /* Number of arguments. */ 02025 Tcl_Obj *const objv[]) /* Argument objects. */ 02026 { 02027 const unsigned char *string; 02028 int length, first, last; 02029 02030 if (objc != 4) { 02031 Tcl_WrongNumArgs(interp, 1, objv, "string first last"); 02032 return TCL_ERROR; 02033 } 02034 02035 /* 02036 * If we have a ByteArray object, avoid indexing in the Utf string since 02037 * the byte array contains one byte per character. Otherwise, use the 02038 * Unicode string rep to get the range. 02039 */ 02040 02041 if (objv[1]->typePtr == &tclByteArrayType) { 02042 string = Tcl_GetByteArrayFromObj(objv[1], &length); 02043 length--; 02044 } else { 02045 /* 02046 * Get the length in actual characters. 02047 */ 02048 02049 string = NULL; 02050 length = Tcl_GetCharLength(objv[1]) - 1; 02051 } 02052 02053 if (TclGetIntForIndexM(interp, objv[2], length, &first) != TCL_OK || 02054 TclGetIntForIndexM(interp, objv[3], length, &last) != TCL_OK) { 02055 return TCL_ERROR; 02056 } 02057 02058 if (first < 0) { 02059 first = 0; 02060 } 02061 if (last >= length) { 02062 last = length; 02063 } 02064 if (last >= first) { 02065 if (string != NULL) { 02066 /* 02067 * Reread the string to prevent shimmering nasties. 02068 */ 02069 02070 string = Tcl_GetByteArrayFromObj(objv[1], &length); 02071 Tcl_SetObjResult(interp, 02072 Tcl_NewByteArrayObj(string+first, last - first + 1)); 02073 } else { 02074 Tcl_SetObjResult(interp, Tcl_GetRange(objv[1], first, last)); 02075 } 02076 } 02077 return TCL_OK; 02078 } 02079 02080 /* 02081 *---------------------------------------------------------------------- 02082 * 02083 * StringReptCmd -- 02084 * 02085 * This procedure is invoked to process the "string repeat" Tcl command. 02086 * See the user documentation for details on what it does. Note that this 02087 * command only functions correctly on properly formed Tcl UTF strings. 02088 * 02089 * Results: 02090 * A standard Tcl result. 02091 * 02092 * Side effects: 02093 * See the user documentation. 02094 * 02095 *---------------------------------------------------------------------- 02096 */ 02097 02098 static int 02099 StringReptCmd( 02100 ClientData dummy, /* Not used. */ 02101 Tcl_Interp *interp, /* Current interpreter. */ 02102 int objc, /* Number of arguments. */ 02103 Tcl_Obj *const objv[]) /* Argument objects. */ 02104 { 02105 const char *string1; 02106 char *string2; 02107 int count, index, length1, length2; 02108 Tcl_Obj *resultPtr; 02109 02110 if (objc != 3) { 02111 Tcl_WrongNumArgs(interp, 1, objv, "string count"); 02112 return TCL_ERROR; 02113 } 02114 02115 if (TclGetIntFromObj(interp, objv[2], &count) != TCL_OK) { 02116 return TCL_ERROR; 02117 } 02118 02119 /* 02120 * Check for cases that allow us to skip copying stuff. 02121 */ 02122 02123 if (count == 1) { 02124 Tcl_SetObjResult(interp, objv[1]); 02125 goto done; 02126 } else if (count < 1) { 02127 goto done; 02128 } 02129 string1 = TclGetStringFromObj(objv[1], &length1); 02130 if (length1 <= 0) { 02131 goto done; 02132 } 02133 02134 /* 02135 * Only build up a string that has data. Instead of building it up with 02136 * repeated appends, we just allocate the necessary space once and copy 02137 * the string value in. Check for overflow with back-division. [Bug 02138 * #714106] 02139 */ 02140 02141 length2 = length1 * count + 1; 02142 if ((length2-1) / count != length1) { 02143 Tcl_SetObjResult(interp, Tcl_ObjPrintf( 02144 "string size overflow, must be less than %d", INT_MAX)); 02145 return TCL_ERROR; 02146 } 02147 02148 /* 02149 * Include space for the NUL. 02150 */ 02151 02152 string2 = attemptckalloc((size_t) length2); 02153 if (string2 == NULL) { 02154 /* 02155 * Alloc failed. Note that in this case we try to do an error message 02156 * since this is a case that's most likely when the alloc is large and 02157 * that's easy to do with this API. Note that if we fail allocating a 02158 * short string, this will likely keel over too (and fatally). 02159 */ 02160 02161 Tcl_SetObjResult(interp, Tcl_ObjPrintf( 02162 "string size overflow, out of memory allocating %d bytes", 02163 length2)); 02164 return TCL_ERROR; 02165 } 02166 for (index = 0; index < count; index++) { 02167 memcpy(string2 + (length1 * index), string1, (size_t) length1); 02168 } 02169 string2[length2-1] = '\0'; 02170 02171 /* 02172 * We have to directly assign this instead of using Tcl_SetStringObj (and 02173 * indirectly TclInitStringRep) because that makes another copy of the 02174 * data. 02175 */ 02176 02177 TclNewObj(resultPtr); 02178 resultPtr->bytes = string2; 02179 resultPtr->length = length2-1; 02180 Tcl_SetObjResult(interp, resultPtr); 02181 02182 done: 02183 return TCL_OK; 02184 } 02185 02186 /* 02187 *---------------------------------------------------------------------- 02188 * 02189 * StringRplcCmd -- 02190 * 02191 * This procedure is invoked to process the "string replace" Tcl command. 02192 * See the user documentation for details on what it does. Note that this 02193 * command only functions correctly on properly formed Tcl UTF strings. 02194 * 02195 * Results: 02196 * A standard Tcl result. 02197 * 02198 * Side effects: 02199 * See the user documentation. 02200 * 02201 *---------------------------------------------------------------------- 02202 */ 02203 02204 static int 02205 StringRplcCmd( 02206 ClientData dummy, /* Not used. */ 02207 Tcl_Interp *interp, /* Current interpreter. */ 02208 int objc, /* Number of arguments. */ 02209 Tcl_Obj *const objv[]) /* Argument objects. */ 02210 { 02211 Tcl_UniChar *ustring; 02212 int first, last, length; 02213 02214 if (objc < 4 || objc > 5) { 02215 Tcl_WrongNumArgs(interp, 1, objv, "string first last ?string?"); 02216 return TCL_ERROR; 02217 } 02218 02219 ustring = Tcl_GetUnicodeFromObj(objv[1], &length); 02220 length--; 02221 02222 if (TclGetIntForIndexM(interp, objv[2], length, &first) != TCL_OK || 02223 TclGetIntForIndexM(interp, objv[3], length, &last) != TCL_OK){ 02224 return TCL_ERROR; 02225 } 02226 02227 if ((last < first) || (last < 0) || (first > length)) { 02228 Tcl_SetObjResult(interp, objv[1]); 02229 } else { 02230 Tcl_Obj *resultPtr; 02231 02232 ustring = Tcl_GetUnicodeFromObj(objv[1], &length); 02233 length--; 02234 02235 if (first < 0) { 02236 first = 0; 02237 } 02238 02239 resultPtr = Tcl_NewUnicodeObj(ustring, first); 02240 if (objc == 5) { 02241 Tcl_AppendObjToObj(resultPtr, objv[4]); 02242 } 02243 if (last < length) { 02244 Tcl_AppendUnicodeToObj(resultPtr, ustring + last + 1, 02245 length - last); 02246 } 02247 Tcl_SetObjResult(interp, resultPtr); 02248 } 02249 return TCL_OK; 02250 } 02251 02252 /* 02253 *---------------------------------------------------------------------- 02254 * 02255 * StringRevCmd -- 02256 * 02257 * This procedure is invoked to process the "string reverse" Tcl command. 02258 * See the user documentation for details on what it does. Note that this 02259 * command only functions correctly on properly formed Tcl UTF strings. 02260 * 02261 * Results: 02262 * A standard Tcl result. 02263 * 02264 * Side effects: 02265 * See the user documentation. 02266 * 02267 *---------------------------------------------------------------------- 02268 */ 02269 02270 static int 02271 StringRevCmd( 02272 ClientData dummy, /* Not used. */ 02273 Tcl_Interp *interp, /* Current interpreter. */ 02274 int objc, /* Number of arguments. */ 02275 Tcl_Obj *const objv[]) /* Argument objects. */ 02276 { 02277 if (objc != 2) { 02278 Tcl_WrongNumArgs(interp, 1, objv, "string"); 02279 return TCL_ERROR; 02280 } 02281 02282 Tcl_SetObjResult(interp, TclStringObjReverse(objv[1])); 02283 return TCL_OK; 02284 } 02285 02286 /* 02287 *---------------------------------------------------------------------- 02288 * 02289 * StringStartCmd -- 02290 * 02291 * This procedure is invoked to process the "string wordstart" Tcl 02292 * command. See the user documentation for details on what it does. Note 02293 * that this command only functions correctly on properly formed Tcl UTF 02294 * strings. 02295 * 02296 * Results: 02297 * A standard Tcl result. 02298 * 02299 * Side effects: 02300 * See the user documentation. 02301 * 02302 *---------------------------------------------------------------------- 02303 */ 02304 02305 static int 02306 StringStartCmd( 02307 ClientData dummy, /* Not used. */ 02308 Tcl_Interp *interp, /* Current interpreter. */ 02309 int objc, /* Number of arguments. */ 02310 Tcl_Obj *const objv[]) /* Argument objects. */ 02311 { 02312 Tcl_UniChar ch; 02313 const char *p, *string; 02314 int cur, index, length, numChars; 02315 02316 if (objc != 3) { 02317 Tcl_WrongNumArgs(interp, 1, objv, "string index"); 02318 return TCL_ERROR; 02319 } 02320 02321 string = TclGetStringFromObj(objv[1], &length); 02322 numChars = Tcl_NumUtfChars(string, length); 02323 if (TclGetIntForIndexM(interp, objv[2], numChars-1, &index) != TCL_OK) { 02324 return TCL_ERROR; 02325 } 02326 string = TclGetStringFromObj(objv[1], &length); 02327 if (index >= numChars) { 02328 index = numChars - 1; 02329 } 02330 cur = 0; 02331 if (index > 0) { 02332 p = Tcl_UtfAtIndex(string, index); 02333 for (cur = index; cur >= 0; cur--) { 02334 TclUtfToUniChar(p, &ch); 02335 if (!Tcl_UniCharIsWordChar(ch)) { 02336 break; 02337 } 02338 p = Tcl_UtfPrev(p, string); 02339 } 02340 if (cur != index) { 02341 cur += 1; 02342 } 02343 } 02344 Tcl_SetObjResult(interp, Tcl_NewIntObj(cur)); 02345 return TCL_OK; 02346 } 02347 02348 /* 02349 *---------------------------------------------------------------------- 02350 * 02351 * StringEndCmd -- 02352 * 02353 * This procedure is invoked to process the "string wordend" Tcl command. 02354 * See the user documentation for details on what it does. Note that this 02355 * command only functions correctly on properly formed Tcl UTF strings. 02356 * 02357 * Results: 02358 * A standard Tcl result. 02359 * 02360 * Side effects: 02361 * See the user documentation. 02362 * 02363 *---------------------------------------------------------------------- 02364 */ 02365 02366 static int 02367 StringEndCmd( 02368 ClientData dummy, /* Not used. */ 02369 Tcl_Interp *interp, /* Current interpreter. */ 02370 int objc, /* Number of arguments. */ 02371 Tcl_Obj *const objv[]) /* Argument objects. */ 02372 { 02373 Tcl_UniChar ch; 02374 const char *p, *end, *string; 02375 int cur, index, length, numChars; 02376 02377 if (objc != 3) { 02378 Tcl_WrongNumArgs(interp, 1, objv, "string index"); 02379 return TCL_ERROR; 02380 } 02381 02382 string = TclGetStringFromObj(objv[1], &length); 02383 numChars = Tcl_NumUtfChars(string, length); 02384 if (TclGetIntForIndexM(interp, objv[2], numChars-1, &index) != TCL_OK) { 02385 return TCL_ERROR; 02386 } 02387 string = TclGetStringFromObj(objv[1], &length); 02388 if (index < 0) { 02389 index = 0; 02390 } 02391 if (index < numChars) { 02392 p = Tcl_UtfAtIndex(string, index); 02393 end = string+length; 02394 for (cur = index; p < end; cur++) { 02395 p += TclUtfToUniChar(p, &ch); 02396 if (!Tcl_UniCharIsWordChar(ch)) { 02397 break; 02398 } 02399 } 02400 if (cur == index) { 02401 cur++; 02402 } 02403 } else { 02404 cur = numChars; 02405 } 02406 Tcl_SetObjResult(interp, Tcl_NewIntObj(cur)); 02407 return TCL_OK; 02408 } 02409 02410 /* 02411 *---------------------------------------------------------------------- 02412 * 02413 * StringEqualCmd -- 02414 * 02415 * This procedure is invoked to process the "string equal" Tcl command. 02416 * See the user documentation for details on what it does. Note that this 02417 * command only functions correctly on properly formed Tcl UTF strings. 02418 * 02419 * Results: 02420 * A standard Tcl result. 02421 * 02422 * Side effects: 02423 * See the user documentation. 02424 * 02425 *---------------------------------------------------------------------- 02426 */ 02427 02428 static int 02429 StringEqualCmd( 02430 ClientData dummy, /* Not used. */ 02431 Tcl_Interp *interp, /* Current interpreter. */ 02432 int objc, /* Number of arguments. */ 02433 Tcl_Obj *const objv[]) /* Argument objects. */ 02434 { 02435 /* 02436 * Remember to keep code here in some sync with the byte-compiled versions 02437 * in tclExecute.c (INST_STR_EQ, INST_STR_NEQ and INST_STR_CMP as well as 02438 * the expr string comparison in INST_EQ/INST_NEQ/INST_LT/...). 02439 */ 02440 02441 char *string1, *string2; 02442 int length1, length2, i, match, length, nocase = 0, reqlength = -1; 02443 typedef int (*strCmpFn_t)(const char *, const char *, unsigned int); 02444 strCmpFn_t strCmpFn; 02445 02446 if (objc < 3 || objc > 6) { 02447 str_cmp_args: 02448 Tcl_WrongNumArgs(interp, 1, objv, 02449 "?-nocase? ?-length int? string1 string2"); 02450 return TCL_ERROR; 02451 } 02452 02453 for (i = 1; i < objc-2; i++) { 02454 string2 = TclGetStringFromObj(objv[i], &length2); 02455 if ((length2 > 1) && !strncmp(string2, "-nocase", (size_t)length2)) { 02456 nocase = 1; 02457 } else if ((length2 > 1) 02458 && !strncmp(string2, "-length", (size_t)length2)) { 02459 if (i+1 >= objc-2) { 02460 goto str_cmp_args; 02461 } 02462 ++i; 02463 if (TclGetIntFromObj(interp, objv[i], &reqlength) != TCL_OK) { 02464 return TCL_ERROR; 02465 } 02466 } else { 02467 Tcl_AppendResult(interp, "bad option \"", string2, 02468 "\": must be -nocase or -length", NULL); 02469 return TCL_ERROR; 02470 } 02471 } 02472 02473 /* 02474 * From now on, we only access the two objects at the end of the argument 02475 * array. 02476 */ 02477 02478 objv += objc-2; 02479 02480 if ((reqlength == 0) || (objv[0] == objv[1])) { 02481 /* 02482 * Always match at 0 chars of if it is the same obj. 02483 */ 02484 02485 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); 02486 return TCL_OK; 02487 } 02488 02489 if (!nocase && objv[0]->typePtr == &tclByteArrayType && 02490 objv[1]->typePtr == &tclByteArrayType) { 02491 /* 02492 * Use binary versions of comparisons since that won't cause undue 02493 * type conversions and it is much faster. Only do this if we're 02494 * case-sensitive (which is all that really makes sense with byte 02495 * arrays anyway, and we have no memcasecmp() for some reason... :^) 02496 */ 02497 02498 string1 = (char *) Tcl_GetByteArrayFromObj(objv[0], &length1); 02499 string2 = (char *) Tcl_GetByteArrayFromObj(objv[1], &length2); 02500 strCmpFn = (strCmpFn_t) memcmp; 02501 } else if ((objv[0]->typePtr == &tclStringType) 02502 && (objv[1]->typePtr == &tclStringType)) { 02503 /* 02504 * Do a unicode-specific comparison if both of the args are of String 02505 * type. In benchmark testing this proved the most efficient check 02506 * between the unicode and string comparison operations. 02507 */ 02508 02509 string1 = (char *) Tcl_GetUnicodeFromObj(objv[0], &length1); 02510 string2 = (char *) Tcl_GetUnicodeFromObj(objv[1], &length2); 02511 strCmpFn = (strCmpFn_t) 02512 (nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp); 02513 } else { 02514 /* 02515 * As a catch-all we will work with UTF-8. We cannot use memcmp() as 02516 * that is unsafe with any string containing NUL (\xC0\x80 in Tcl's 02517 * utf rep). We can use the more efficient TclpUtfNcmp2 if we are 02518 * case-sensitive and no specific length was requested. 02519 */ 02520 02521 string1 = (char *) TclGetStringFromObj(objv[0], &length1); 02522 string2 = (char *) TclGetStringFromObj(objv[1], &length2); 02523 if ((reqlength < 0) && !nocase) { 02524 strCmpFn = (strCmpFn_t) TclpUtfNcmp2; 02525 } else { 02526 length1 = Tcl_NumUtfChars(string1, length1); 02527 length2 = Tcl_NumUtfChars(string2, length2); 02528 strCmpFn = (strCmpFn_t) (nocase ? Tcl_UtfNcasecmp : Tcl_UtfNcmp); 02529 } 02530 } 02531 02532 if ((reqlength < 0) && (length1 != length2)) { 02533 match = 1; /* This will be reversed below. */ 02534 } else { 02535 length = (length1 < length2) ? length1 : length2; 02536 if (reqlength > 0 && reqlength < length) { 02537 length = reqlength; 02538 } else if (reqlength < 0) { 02539 /* 02540 * The requested length is negative, so we ignore it by setting it 02541 * to length + 1 so we correct the match var. 02542 */ 02543 02544 reqlength = length + 1; 02545 } 02546 02547 match = strCmpFn(string1, string2, (unsigned) length); 02548 if ((match == 0) && (reqlength > length)) { 02549 match = length1 - length2; 02550 } 02551 } 02552 02553 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(match ? 0 : 1)); 02554 return TCL_OK; 02555 } 02556 02557 /* 02558 *---------------------------------------------------------------------- 02559 * 02560 * StringCmpCmd -- 02561 * 02562 * This procedure is invoked to process the "string compare" Tcl command. 02563 * See the user documentation for details on what it does. Note that this 02564 * command only functions correctly on properly formed Tcl UTF strings. 02565 * 02566 * Results: 02567 * A standard Tcl result. 02568 * 02569 * Side effects: 02570 * See the user documentation. 02571 * 02572 *---------------------------------------------------------------------- 02573 */ 02574 02575 static int 02576 StringCmpCmd( 02577 ClientData dummy, /* Not used. */ 02578 Tcl_Interp *interp, /* Current interpreter. */ 02579 int objc, /* Number of arguments. */ 02580 Tcl_Obj *const objv[]) /* Argument objects. */ 02581 { 02582 /* 02583 * Remember to keep code here in some sync with the byte-compiled versions 02584 * in tclExecute.c (INST_STR_EQ, INST_STR_NEQ and INST_STR_CMP as well as 02585 * the expr string comparison in INST_EQ/INST_NEQ/INST_LT/...). 02586 */ 02587 02588 char *string1, *string2; 02589 int length1, length2, i, match, length, nocase = 0, reqlength = -1; 02590 typedef int (*strCmpFn_t)(const char *, const char *, unsigned int); 02591 strCmpFn_t strCmpFn; 02592 02593 if (objc < 3 || objc > 6) { 02594 str_cmp_args: 02595 Tcl_WrongNumArgs(interp, 1, objv, 02596 "?-nocase? ?-length int? string1 string2"); 02597 return TCL_ERROR; 02598 } 02599 02600 for (i = 1; i < objc-2; i++) { 02601 string2 = TclGetStringFromObj(objv[i], &length2); 02602 if ((length2 > 1) && !strncmp(string2, "-nocase", (size_t)length2)) { 02603 nocase = 1; 02604 } else if ((length2 > 1) 02605 && !strncmp(string2, "-length", (size_t)length2)) { 02606 if (i+1 >= objc-2) { 02607 goto str_cmp_args; 02608 } 02609 ++i; 02610 if (TclGetIntFromObj(interp, objv[i], &reqlength) != TCL_OK) { 02611 return TCL_ERROR; 02612 } 02613 } else { 02614 Tcl_AppendResult(interp, "bad option \"", string2, 02615 "\": must be -nocase or -length", NULL); 02616 return TCL_ERROR; 02617 } 02618 } 02619 02620 /* 02621 * From now on, we only access the two objects at the end of the argument 02622 * array. 02623 */ 02624 02625 objv += objc-2; 02626 02627 if ((reqlength == 0) || (objv[0] == objv[1])) { 02628 /* 02629 * Always match at 0 chars of if it is the same obj. 02630 */ 02631 02632 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); 02633 return TCL_OK; 02634 } 02635 02636 if (!nocase && objv[0]->typePtr == &tclByteArrayType && 02637 objv[1]->typePtr == &tclByteArrayType) { 02638 /* 02639 * Use binary versions of comparisons since that won't cause undue 02640 * type conversions and it is much faster. Only do this if we're 02641 * case-sensitive (which is all that really makes sense with byte 02642 * arrays anyway, and we have no memcasecmp() for some reason... :^) 02643 */ 02644 02645 string1 = (char *) Tcl_GetByteArrayFromObj(objv[0], &length1); 02646 string2 = (char *) Tcl_GetByteArrayFromObj(objv[1], &length2); 02647 strCmpFn = (strCmpFn_t) memcmp; 02648 } else if ((objv[0]->typePtr == &tclStringType) 02649 && (objv[1]->typePtr == &tclStringType)) { 02650 /* 02651 * Do a unicode-specific comparison if both of the args are of String 02652 * type. In benchmark testing this proved the most efficient check 02653 * between the unicode and string comparison operations. 02654 */ 02655 02656 string1 = (char *) Tcl_GetUnicodeFromObj(objv[0], &length1); 02657 string2 = (char *) Tcl_GetUnicodeFromObj(objv[1], &length2); 02658 strCmpFn = (strCmpFn_t) 02659 (nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp); 02660 } else { 02661 /* 02662 * As a catch-all we will work with UTF-8. We cannot use memcmp() as 02663 * that is unsafe with any string containing NUL (\xC0\x80 in Tcl's 02664 * utf rep). We can use the more efficient TclpUtfNcmp2 if we are 02665 * case-sensitive and no specific length was requested. 02666 */ 02667 02668 string1 = (char *) TclGetStringFromObj(objv[0], &length1); 02669 string2 = (char *) TclGetStringFromObj(objv[1], &length2); 02670 if ((reqlength < 0) && !nocase) { 02671 strCmpFn = (strCmpFn_t) TclpUtfNcmp2; 02672 } else { 02673 length1 = Tcl_NumUtfChars(string1, length1); 02674 length2 = Tcl_NumUtfChars(string2, length2); 02675 strCmpFn = (strCmpFn_t) (nocase ? Tcl_UtfNcasecmp : Tcl_UtfNcmp); 02676 } 02677 } 02678 02679 length = (length1 < length2) ? length1 : length2; 02680 if (reqlength > 0 && reqlength < length) { 02681 length = reqlength; 02682 } else if (reqlength < 0) { 02683 /* 02684 * The requested length is negative, so we ignore it by setting it to 02685 * length + 1 so we correct the match var. 02686 */ 02687 02688 reqlength = length + 1; 02689 } 02690 02691 match = strCmpFn(string1, string2, (unsigned) length); 02692 if ((match == 0) && (reqlength > length)) { 02693 match = length1 - length2; 02694 } 02695 02696 Tcl_SetObjResult(interp, 02697 Tcl_NewIntObj((match > 0) ? 1 : (match < 0) ? -1 : 0)); 02698 return TCL_OK; 02699 } 02700 02701 /* 02702 *---------------------------------------------------------------------- 02703 * 02704 * StringBytesCmd -- 02705 * 02706 * This procedure is invoked to process the "string bytelength" Tcl 02707 * command. See the user documentation for details on what it does. Note 02708 * that this command only functions correctly on properly formed Tcl UTF 02709 * strings. 02710 * 02711 * Results: 02712 * A standard Tcl result. 02713 * 02714 * Side effects: 02715 * See the user documentation. 02716 * 02717 *---------------------------------------------------------------------- 02718 */ 02719 02720 static int 02721 StringBytesCmd( 02722 ClientData dummy, /* Not used. */ 02723 Tcl_Interp *interp, /* Current interpreter. */ 02724 int objc, /* Number of arguments. */ 02725 Tcl_Obj *const objv[]) /* Argument objects. */ 02726 { 02727 int length; 02728 02729 if (objc != 2) { 02730 Tcl_WrongNumArgs(interp, 1, objv, "string"); 02731 return TCL_ERROR; 02732 } 02733 02734 (void) TclGetStringFromObj(objv[1], &length); 02735 Tcl_SetObjResult(interp, Tcl_NewIntObj(length)); 02736 return TCL_OK; 02737 } 02738 02739 /* 02740 *---------------------------------------------------------------------- 02741 * 02742 * StringLenCmd -- 02743 * 02744 * This procedure is invoked to process the "string length" Tcl command. 02745 * See the user documentation for details on what it does. Note that this 02746 * command only functions correctly on properly formed Tcl UTF strings. 02747 * 02748 * Results: 02749 * A standard Tcl result. 02750 * 02751 * Side effects: 02752 * See the user documentation. 02753 * 02754 *---------------------------------------------------------------------- 02755 */ 02756 02757 static int 02758 StringLenCmd( 02759 ClientData dummy, /* Not used. */ 02760 Tcl_Interp *interp, /* Current interpreter. */ 02761 int objc, /* Number of arguments. */ 02762 Tcl_Obj *const objv[]) /* Argument objects. */ 02763 { 02764 int length; 02765 02766 if (objc != 2) { 02767 Tcl_WrongNumArgs(interp, 1, objv, "string"); 02768 return TCL_ERROR; 02769 } 02770 02771 /* 02772 * If we have a ByteArray object, avoid recomputing the string since the 02773 * byte array contains one byte per character. Otherwise, use the Unicode 02774 * string rep to calculate the length. 02775 */ 02776 02777 if (objv[1]->typePtr == &tclByteArrayType) { 02778 (void) Tcl_GetByteArrayFromObj(objv[1], &length); 02779 } else { 02780 length = Tcl_GetCharLength(objv[1]); 02781 } 02782 Tcl_SetObjResult(interp, Tcl_NewIntObj(length)); 02783 return TCL_OK; 02784 } 02785 02786 /* 02787 *---------------------------------------------------------------------- 02788 * 02789 * StringLowerCmd -- 02790 * 02791 * This procedure is invoked to process the "string tolower" Tcl command. 02792 * See the user documentation for details on what it does. Note that this 02793 * command only functions correctly on properly formed Tcl UTF strings. 02794 * 02795 * Results: 02796 * A standard Tcl result. 02797 * 02798 * Side effects: 02799 * See the user documentation. 02800 * 02801 *---------------------------------------------------------------------- 02802 */ 02803 02804 static int 02805 StringLowerCmd( 02806 ClientData dummy, /* Not used. */ 02807 Tcl_Interp *interp, /* Current interpreter. */ 02808 int objc, /* Number of arguments. */ 02809 Tcl_Obj *const objv[]) /* Argument objects. */ 02810 { 02811 int length1, length2; 02812 char *string1, *string2; 02813 02814 if (objc < 2 || objc > 4) { 02815 Tcl_WrongNumArgs(interp, 1, objv, "string ?first? ?last?"); 02816 return TCL_ERROR; 02817 } 02818 02819 string1 = TclGetStringFromObj(objv[1], &length1); 02820 02821 if (objc == 2) { 02822 Tcl_Obj *resultPtr = Tcl_NewStringObj(string1, length1); 02823 02824 length1 = Tcl_UtfToLower(TclGetString(resultPtr)); 02825 Tcl_SetObjLength(resultPtr, length1); 02826 Tcl_SetObjResult(interp, resultPtr); 02827 } else { 02828 int first, last; 02829 const char *start, *end; 02830 Tcl_Obj *resultPtr; 02831 02832 length1 = Tcl_NumUtfChars(string1, length1) - 1; 02833 if (TclGetIntForIndexM(interp,objv[2],length1, &first) != TCL_OK) { 02834 return TCL_ERROR; 02835 } 02836 if (first < 0) { 02837 first = 0; 02838 } 02839 last = first; 02840 02841 if ((objc == 4) && (TclGetIntForIndexM(interp, objv[3], length1, 02842 &last) != TCL_OK)) { 02843 return TCL_ERROR; 02844 } 02845 02846 if (last >= length1) { 02847 last = length1; 02848 } 02849 if (last < first) { 02850 Tcl_SetObjResult(interp, objv[1]); 02851 return TCL_OK; 02852 } 02853 02854 string1 = TclGetStringFromObj(objv[1], &length1); 02855 start = Tcl_UtfAtIndex(string1, first); 02856 end = Tcl_UtfAtIndex(start, last - first + 1); 02857 resultPtr = Tcl_NewStringObj(string1, end - string1); 02858 string2 = TclGetString(resultPtr) + (start - string1); 02859 02860 length2 = Tcl_UtfToLower(string2); 02861 Tcl_SetObjLength(resultPtr, length2 + (start - string1)); 02862 02863 Tcl_AppendToObj(resultPtr, end, -1); 02864 Tcl_SetObjResult(interp, resultPtr); 02865 } 02866 02867 return TCL_OK; 02868 } 02869 02870 /* 02871 *---------------------------------------------------------------------- 02872 * 02873 * StringUpperCmd -- 02874 * 02875 * This procedure is invoked to process the "string toupper" Tcl command. 02876 * See the user documentation for details on what it does. Note that this 02877 * command only functions correctly on properly formed Tcl UTF strings. 02878 * 02879 * Results: 02880 * A standard Tcl result. 02881 * 02882 * Side effects: 02883 * See the user documentation. 02884 * 02885 *---------------------------------------------------------------------- 02886 */ 02887 02888 static int 02889 StringUpperCmd( 02890 ClientData dummy, /* Not used. */ 02891 Tcl_Interp *interp, /* Current interpreter. */ 02892 int objc, /* Number of arguments. */ 02893 Tcl_Obj *const objv[]) /* Argument objects. */ 02894 { 02895 int length1, length2; 02896 char *string1, *string2; 02897 02898 if (objc < 2 || objc > 4) { 02899 Tcl_WrongNumArgs(interp, 1, objv, "string ?first? ?last?"); 02900 return TCL_ERROR; 02901 } 02902 02903 string1 = TclGetStringFromObj(objv[1], &length1); 02904 02905 if (objc == 2) { 02906 Tcl_Obj *resultPtr = Tcl_NewStringObj(string1, length1); 02907 02908 length1 = Tcl_UtfToUpper(TclGetString(resultPtr)); 02909 Tcl_SetObjLength(resultPtr, length1); 02910 Tcl_SetObjResult(interp, resultPtr); 02911 } else { 02912 int first, last; 02913 const char *start, *end; 02914 Tcl_Obj *resultPtr; 02915 02916 length1 = Tcl_NumUtfChars(string1, length1) - 1; 02917 if (TclGetIntForIndexM(interp,objv[2],length1, &first) != TCL_OK) { 02918 return TCL_ERROR; 02919 } 02920 if (first < 0) { 02921 first = 0; 02922 } 02923 last = first; 02924 02925 if ((objc == 4) && (TclGetIntForIndexM(interp, objv[3], length1, 02926 &last) != TCL_OK)) { 02927 return TCL_ERROR; 02928 } 02929 02930 if (last >= length1) { 02931 last = length1; 02932 } 02933 if (last < first) { 02934 Tcl_SetObjResult(interp, objv[1]); 02935 return TCL_OK; 02936 } 02937 02938 string1 = TclGetStringFromObj(objv[1], &length1); 02939 start = Tcl_UtfAtIndex(string1, first); 02940 end = Tcl_UtfAtIndex(start, last - first + 1); 02941 resultPtr = Tcl_NewStringObj(string1, end - string1); 02942 string2 = TclGetString(resultPtr) + (start - string1); 02943 02944 length2 = Tcl_UtfToUpper(string2); 02945 Tcl_SetObjLength(resultPtr, length2 + (start - string1)); 02946 02947 Tcl_AppendToObj(resultPtr, end, -1); 02948 Tcl_SetObjResult(interp, resultPtr); 02949 } 02950 02951 return TCL_OK; 02952 } 02953 02954 /* 02955 *---------------------------------------------------------------------- 02956 * 02957 * StringTitleCmd -- 02958 * 02959 * This procedure is invoked to process the "string totitle" Tcl command. 02960 * See the user documentation for details on what it does. Note that this 02961 * command only functions correctly on properly formed Tcl UTF strings. 02962 * 02963 * Results: 02964 * A standard Tcl result. 02965 * 02966 * Side effects: 02967 * See the user documentation. 02968 * 02969 *---------------------------------------------------------------------- 02970 */ 02971 02972 static int 02973 StringTitleCmd( 02974 ClientData dummy, /* Not used. */ 02975 Tcl_Interp *interp, /* Current interpreter. */ 02976 int objc, /* Number of arguments. */ 02977 Tcl_Obj *const objv[]) /* Argument objects. */ 02978 { 02979 int length1, length2; 02980 char *string1, *string2; 02981 02982 if (objc < 2 || objc > 4) { 02983 Tcl_WrongNumArgs(interp, 1, objv, "string ?first? ?last?"); 02984 return TCL_ERROR; 02985 } 02986 02987 string1 = TclGetStringFromObj(objv[1], &length1); 02988 02989 if (objc == 2) { 02990 Tcl_Obj *resultPtr = Tcl_NewStringObj(string1, length1); 02991 02992 length1 = Tcl_UtfToTitle(TclGetString(resultPtr)); 02993 Tcl_SetObjLength(resultPtr, length1); 02994 Tcl_SetObjResult(interp, resultPtr); 02995 } else { 02996 int first, last; 02997 const char *start, *end; 02998 Tcl_Obj *resultPtr; 02999 03000 length1 = Tcl_NumUtfChars(string1, length1) - 1; 03001 if (TclGetIntForIndexM(interp,objv[2],length1, &first) != TCL_OK) { 03002 return TCL_ERROR; 03003 } 03004 if (first < 0) { 03005 first = 0; 03006 } 03007 last = first; 03008 03009 if ((objc == 4) && (TclGetIntForIndexM(interp, objv[3], length1, 03010 &last) != TCL_OK)) { 03011 return TCL_ERROR; 03012 } 03013 03014 if (last >= length1) { 03015 last = length1; 03016 } 03017 if (last < first) { 03018 Tcl_SetObjResult(interp, objv[1]); 03019 return TCL_OK; 03020 } 03021 03022 string1 = TclGetStringFromObj(objv[1], &length1); 03023 start = Tcl_UtfAtIndex(string1, first); 03024 end = Tcl_UtfAtIndex(start, last - first + 1); 03025 resultPtr = Tcl_NewStringObj(string1, end - string1); 03026 string2 = TclGetString(resultPtr) + (start - string1); 03027 03028 length2 = Tcl_UtfToTitle(string2); 03029 Tcl_SetObjLength(resultPtr, length2 + (start - string1)); 03030 03031 Tcl_AppendToObj(resultPtr, end, -1); 03032 Tcl_SetObjResult(interp, resultPtr); 03033 } 03034 03035 return TCL_OK; 03036 } 03037 03038 /* 03039 *---------------------------------------------------------------------- 03040 * 03041 * StringTrimCmd -- 03042 * 03043 * This procedure is invoked to process the "string trim" Tcl command. 03044 * See the user documentation for details on what it does. Note that this 03045 * command only functions correctly on properly formed Tcl UTF strings. 03046 * 03047 * Results: 03048 * A standard Tcl result. 03049 * 03050 * Side effects: 03051 * See the user documentation. 03052 * 03053 *---------------------------------------------------------------------- 03054 */ 03055 03056 static int 03057 StringTrimCmd( 03058 ClientData dummy, /* Not used. */ 03059 Tcl_Interp *interp, /* Current interpreter. */ 03060 int objc, /* Number of arguments. */ 03061 Tcl_Obj *const objv[]) /* Argument objects. */ 03062 { 03063 Tcl_UniChar ch, trim; 03064 register const char *p, *end; 03065 const char *check, *checkEnd, *string1, *string2; 03066 int offset, length1, length2; 03067 03068 if (objc == 3) { 03069 string2 = TclGetStringFromObj(objv[2], &length2); 03070 } else if (objc == 2) { 03071 string2 = " \t\n\r"; 03072 length2 = strlen(string2); 03073 } else { 03074 Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?"); 03075 return TCL_ERROR; 03076 } 03077 string1 = TclGetStringFromObj(objv[1], &length1); 03078 checkEnd = string2 + length2; 03079 03080 /* 03081 * The outer loop iterates over the string. The inner loop iterates over 03082 * the trim characters. The loops terminate as soon as a non-trim 03083 * character is discovered and string1 is left pointing at the first 03084 * non-trim character. 03085 */ 03086 03087 end = string1 + length1; 03088 for (p = string1; p < end; p += offset) { 03089 offset = TclUtfToUniChar(p, &ch); 03090 03091 for (check = string2; ; ) { 03092 if (check >= checkEnd) { 03093 p = end; 03094 break; 03095 } 03096 check += TclUtfToUniChar(check, &trim); 03097 if (ch == trim) { 03098 length1 -= offset; 03099 string1 += offset; 03100 break; 03101 } 03102 } 03103 } 03104 03105 /* 03106 * The outer loop iterates over the string. The inner loop iterates over 03107 * the trim characters. The loops terminate as soon as a non-trim 03108 * character is discovered and length1 marks the last non-trim character. 03109 */ 03110 03111 end = string1; 03112 for (p = string1 + length1; p > end; ) { 03113 p = Tcl_UtfPrev(p, string1); 03114 offset = TclUtfToUniChar(p, &ch); 03115 check = string2; 03116 while (1) { 03117 if (check >= checkEnd) { 03118 p = end; 03119 break; 03120 } 03121 check += TclUtfToUniChar(check, &trim); 03122 if (ch == trim) { 03123 length1 -= offset; 03124 break; 03125 } 03126 } 03127 } 03128 03129 Tcl_SetObjResult(interp, Tcl_NewStringObj(string1, length1)); 03130 return TCL_OK; 03131 } 03132 03133 /* 03134 *---------------------------------------------------------------------- 03135 * 03136 * StringTrimLCmd -- 03137 * 03138 * This procedure is invoked to process the "string trimleft" Tcl 03139 * command. See the user documentation for details on what it does. Note 03140 * that this command only functions correctly on properly formed Tcl UTF 03141 * strings. 03142 * 03143 * Results: 03144 * A standard Tcl result. 03145 * 03146 * Side effects: 03147 * See the user documentation. 03148 * 03149 *---------------------------------------------------------------------- 03150 */ 03151 03152 static int 03153 StringTrimLCmd( 03154 ClientData dummy, /* Not used. */ 03155 Tcl_Interp *interp, /* Current interpreter. */ 03156 int objc, /* Number of arguments. */ 03157 Tcl_Obj *const objv[]) /* Argument objects. */ 03158 { 03159 Tcl_UniChar ch, trim; 03160 register const char *p, *end; 03161 const char *check, *checkEnd, *string1, *string2; 03162 int offset, length1, length2; 03163 03164 if (objc == 3) { 03165 string2 = TclGetStringFromObj(objv[2], &length2); 03166 } else if (objc == 2) { 03167 string2 = " \t\n\r"; 03168 length2 = strlen(string2); 03169 } else { 03170 Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?"); 03171 return TCL_ERROR; 03172 } 03173 string1 = TclGetStringFromObj(objv[1], &length1); 03174 checkEnd = string2 + length2; 03175 03176 /* 03177 * The outer loop iterates over the string. The inner loop iterates over 03178 * the trim characters. The loops terminate as soon as a non-trim 03179 * character is discovered and string1 is left pointing at the first 03180 * non-trim character. 03181 */ 03182 03183 end = string1 + length1; 03184 for (p = string1; p < end; p += offset) { 03185 offset = TclUtfToUniChar(p, &ch); 03186 03187 for (check = string2; ; ) { 03188 if (check >= checkEnd) { 03189 p = end; 03190 break; 03191 } 03192 check += TclUtfToUniChar(check, &trim); 03193 if (ch == trim) { 03194 length1 -= offset; 03195 string1 += offset; 03196 break; 03197 } 03198 } 03199 } 03200 03201 Tcl_SetObjResult(interp, Tcl_NewStringObj(string1, length1)); 03202 return TCL_OK; 03203 } 03204 03205 /* 03206 *---------------------------------------------------------------------- 03207 * 03208 * StringTrimRCmd -- 03209 * 03210 * This procedure is invoked to process the "string trimright" Tcl 03211 * command. See the user documentation for details on what it does. Note 03212 * that this command only functions correctly on properly formed Tcl UTF 03213 * strings. 03214 * 03215 * Results: 03216 * A standard Tcl result. 03217 * 03218 * Side effects: 03219 * See the user documentation. 03220 * 03221 *---------------------------------------------------------------------- 03222 */ 03223 03224 static int 03225 StringTrimRCmd( 03226 ClientData dummy, /* Not used. */ 03227 Tcl_Interp *interp, /* Current interpreter. */ 03228 int objc, /* Number of arguments. */ 03229 Tcl_Obj *const objv[]) /* Argument objects. */ 03230 { 03231 Tcl_UniChar ch, trim; 03232 register const char *p, *end; 03233 const char *check, *checkEnd, *string1, *string2; 03234 int offset, length1, length2; 03235 03236 if (objc == 3) { 03237 string2 = TclGetStringFromObj(objv[2], &length2); 03238 } else if (objc == 2) { 03239 string2 = " \t\n\r"; 03240 length2 = strlen(string2); 03241 } else { 03242 Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?"); 03243 return TCL_ERROR; 03244 } 03245 string1 = TclGetStringFromObj(objv[1], &length1); 03246 checkEnd = string2 + length2; 03247 03248 /* 03249 * The outer loop iterates over the string. The inner loop iterates over 03250 * the trim characters. The loops terminate as soon as a non-trim 03251 * character is discovered and length1 marks the last non-trim character. 03252 */ 03253 03254 end = string1; 03255 for (p = string1 + length1; p > end; ) { 03256 p = Tcl_UtfPrev(p, string1); 03257 offset = TclUtfToUniChar(p, &ch); 03258 check = string2; 03259 while (1) { 03260 if (check >= checkEnd) { 03261 p = end; 03262 break; 03263 } 03264 check += TclUtfToUniChar(check, &trim); 03265 if (ch == trim) { 03266 length1 -= offset; 03267 break; 03268 } 03269 } 03270 } 03271 03272 Tcl_SetObjResult(interp, Tcl_NewStringObj(string1, length1)); 03273 return TCL_OK; 03274 } 03275 03276 /* 03277 *---------------------------------------------------------------------- 03278 * 03279 * TclInitStringCmd -- 03280 * 03281 * This procedure creates the "string" Tcl command. See the user 03282 * documentation for details on what it does. Note that this command only 03283 * functions correctly on properly formed Tcl UTF strings. 03284 * 03285 * Also note that the primary methods here (equal, compare, match, ...) 03286 * have bytecode equivalents. You will find the code for those in 03287 * tclExecute.c. The code here will only be used in the non-bc case (like 03288 * in an 'eval'). 03289 * 03290 * Results: 03291 * A standard Tcl result. 03292 * 03293 * Side effects: 03294 * See the user documentation. 03295 * 03296 *---------------------------------------------------------------------- 03297 */ 03298 03299 Tcl_Command 03300 TclInitStringCmd( 03301 Tcl_Interp *interp) /* Current interpreter. */ 03302 { 03303 static const EnsembleImplMap stringImplMap[] = { 03304 {"bytelength", StringBytesCmd, NULL}, 03305 {"compare", StringCmpCmd, TclCompileStringCmpCmd}, 03306 {"equal", StringEqualCmd, TclCompileStringEqualCmd}, 03307 {"first", StringFirstCmd, NULL}, 03308 {"index", StringIndexCmd, TclCompileStringIndexCmd}, 03309 {"is", StringIsCmd, NULL}, 03310 {"last", StringLastCmd, NULL}, 03311 {"length", StringLenCmd, TclCompileStringLenCmd}, 03312 {"map", StringMapCmd, NULL}, 03313 {"match", StringMatchCmd, TclCompileStringMatchCmd}, 03314 {"range", StringRangeCmd, NULL}, 03315 {"repeat", StringReptCmd, NULL}, 03316 {"replace", StringRplcCmd, NULL}, 03317 {"reverse", StringRevCmd, NULL}, 03318 {"tolower", StringLowerCmd, NULL}, 03319 {"toupper", StringUpperCmd, NULL}, 03320 {"totitle", StringTitleCmd, NULL}, 03321 {"trim", StringTrimCmd, NULL}, 03322 {"trimleft", StringTrimLCmd, NULL}, 03323 {"trimright", StringTrimRCmd, NULL}, 03324 {"wordend", StringEndCmd, NULL}, 03325 {"wordstart", StringStartCmd, NULL}, 03326 {NULL} 03327 }; 03328 03329 return TclMakeEnsemble(interp, "string", stringImplMap); 03330 } 03331 03332 /* 03333 *---------------------------------------------------------------------- 03334 * 03335 * Tcl_SubstObjCmd -- 03336 * 03337 * This procedure is invoked to process the "subst" Tcl command. See the 03338 * user documentation for details on what it does. This command relies on 03339 * Tcl_SubstObj() for its implementation. 03340 * 03341 * Results: 03342 * A standard Tcl result. 03343 * 03344 * Side effects: 03345 * See the user documentation. 03346 * 03347 *---------------------------------------------------------------------- 03348 */ 03349 03350 int 03351 Tcl_SubstObjCmd( 03352 ClientData dummy, /* Not used. */ 03353 Tcl_Interp *interp, /* Current interpreter. */ 03354 int objc, /* Number of arguments. */ 03355 Tcl_Obj *CONST objv[]) /* Argument objects. */ 03356 { 03357 static CONST char *substOptions[] = { 03358 "-nobackslashes", "-nocommands", "-novariables", NULL 03359 }; 03360 enum substOptions { 03361 SUBST_NOBACKSLASHES, SUBST_NOCOMMANDS, SUBST_NOVARS 03362 }; 03363 Tcl_Obj *resultPtr; 03364 int flags, i; 03365 03366 /* 03367 * Parse command-line options. 03368 */ 03369 03370 flags = TCL_SUBST_ALL; 03371 for (i = 1; i < (objc-1); i++) { 03372 int optionIndex; 03373 03374 if (Tcl_GetIndexFromObj(interp, objv[i], substOptions, "switch", 0, 03375 &optionIndex) != TCL_OK) { 03376 return TCL_ERROR; 03377 } 03378 switch (optionIndex) { 03379 case SUBST_NOBACKSLASHES: 03380 flags &= ~TCL_SUBST_BACKSLASHES; 03381 break; 03382 case SUBST_NOCOMMANDS: 03383 flags &= ~TCL_SUBST_COMMANDS; 03384 break; 03385 case SUBST_NOVARS: 03386 flags &= ~TCL_SUBST_VARIABLES; 03387 break; 03388 default: 03389 Tcl_Panic("Tcl_SubstObjCmd: bad option index to SubstOptions"); 03390 } 03391 } 03392 if (i != objc-1) { 03393 Tcl_WrongNumArgs(interp, 1, objv, 03394 "?-nobackslashes? ?-nocommands? ?-novariables? string"); 03395 return TCL_ERROR; 03396 } 03397 03398 /* 03399 * Perform the substitution. 03400 */ 03401 03402 resultPtr = Tcl_SubstObj(interp, objv[i], flags); 03403 03404 if (resultPtr == NULL) { 03405 return TCL_ERROR; 03406 } 03407 Tcl_SetObjResult(interp, resultPtr); 03408 return TCL_OK; 03409 } 03410 03411 /* 03412 *---------------------------------------------------------------------- 03413 * 03414 * Tcl_SwitchObjCmd -- 03415 * 03416 * This object-based procedure is invoked to process the "switch" Tcl 03417 * command. See the user documentation for details on what it does. 03418 * 03419 * Results: 03420 * A standard Tcl object result. 03421 * 03422 * Side effects: 03423 * See the user documentation. 03424 * 03425 *---------------------------------------------------------------------- 03426 */ 03427 03428 int 03429 Tcl_SwitchObjCmd( 03430 ClientData dummy, /* Not used. */ 03431 Tcl_Interp *interp, /* Current interpreter. */ 03432 int objc, /* Number of arguments. */ 03433 Tcl_Obj *CONST objv[]) /* Argument objects. */ 03434 { 03435 int i,j, index, mode, foundmode, result, splitObjs, numMatchesSaved; 03436 int noCase, patternLength; 03437 char *pattern; 03438 Tcl_Obj *stringObj, *indexVarObj, *matchVarObj; 03439 Tcl_Obj *CONST *savedObjv = objv; 03440 Tcl_RegExp regExpr = NULL; 03441 Interp *iPtr = (Interp *) interp; 03442 int pc = 0; 03443 int bidx = 0; /* Index of body argument. */ 03444 Tcl_Obj *blist = NULL; /* List obj which is the body */ 03445 CmdFrame *ctxPtr; /* Copy of the topmost cmdframe, to allow us 03446 * to mess with the line information */ 03447 03448 /* 03449 * If you add options that make -e and -g not unique prefixes of -exact or 03450 * -glob, you *must* fix TclCompileSwitchCmd's option parser as well. 03451 */ 03452 03453 static CONST char *options[] = { 03454 "-exact", "-glob", "-indexvar", "-matchvar", "-nocase", "-regexp", 03455 "--", NULL 03456 }; 03457 enum options { 03458 OPT_EXACT, OPT_GLOB, OPT_INDEXV, OPT_MATCHV, OPT_NOCASE, OPT_REGEXP, 03459 OPT_LAST 03460 }; 03461 typedef int (*strCmpFn_t)(const char *, const char *); 03462 strCmpFn_t strCmpFn = strcmp; 03463 03464 mode = OPT_EXACT; 03465 foundmode = 0; 03466 indexVarObj = NULL; 03467 matchVarObj = NULL; 03468 numMatchesSaved = 0; 03469 noCase = 0; 03470 for (i = 1; i < objc-2; i++) { 03471 if (TclGetString(objv[i])[0] != '-') { 03472 break; 03473 } 03474 if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, 03475 &index) != TCL_OK) { 03476 return TCL_ERROR; 03477 } 03478 switch ((enum options) index) { 03479 /* 03480 * General options. 03481 */ 03482 03483 case OPT_LAST: 03484 i++; 03485 goto finishedOptions; 03486 case OPT_NOCASE: 03487 strCmpFn = strcasecmp; 03488 noCase = 1; 03489 break; 03490 03491 /* 03492 * Handle the different switch mode options. 03493 */ 03494 03495 default: 03496 if (foundmode) { 03497 /* 03498 * Mode already set via -exact, -glob, or -regexp. 03499 */ 03500 03501 Tcl_AppendResult(interp, "bad option \"", 03502 TclGetString(objv[i]), "\": ", options[mode], 03503 " option already found", NULL); 03504 return TCL_ERROR; 03505 } else { 03506 foundmode = 1; 03507 mode = index; 03508 break; 03509 } 03510 03511 /* 03512 * Check for TIP#75 options specifying the variables to write 03513 * regexp information into. 03514 */ 03515 03516 case OPT_INDEXV: 03517 i++; 03518 if (i >= objc-2) { 03519 Tcl_AppendResult(interp, "missing variable name argument to ", 03520 "-indexvar", " option", NULL); 03521 return TCL_ERROR; 03522 } 03523 indexVarObj = objv[i]; 03524 numMatchesSaved = -1; 03525 break; 03526 case OPT_MATCHV: 03527 i++; 03528 if (i >= objc-2) { 03529 Tcl_AppendResult(interp, "missing variable name argument to ", 03530 "-matchvar", " option", NULL); 03531 return TCL_ERROR; 03532 } 03533 matchVarObj = objv[i]; 03534 numMatchesSaved = -1; 03535 break; 03536 } 03537 } 03538 03539 finishedOptions: 03540 if (objc - i < 2) { 03541 Tcl_WrongNumArgs(interp, 1, objv, 03542 "?switches? string pattern body ... ?default body?"); 03543 return TCL_ERROR; 03544 } 03545 if (indexVarObj != NULL && mode != OPT_REGEXP) { 03546 Tcl_AppendResult(interp, 03547 "-indexvar option requires -regexp option", NULL); 03548 return TCL_ERROR; 03549 } 03550 if (matchVarObj != NULL && mode != OPT_REGEXP) { 03551 Tcl_AppendResult(interp, 03552 "-matchvar option requires -regexp option", NULL); 03553 return TCL_ERROR; 03554 } 03555 03556 stringObj = objv[i]; 03557 objc -= i + 1; 03558 objv += i + 1; 03559 bidx = i + 1; /* First after the match string. */ 03560 03561 /* 03562 * If all of the pattern/command pairs are lumped into a single argument, 03563 * split them out again. 03564 * 03565 * TIP #280: Determine the lines the words in the list start at, based on 03566 * the same data for the list word itself. The cmdFramePtr line 03567 * information is manipulated directly. 03568 */ 03569 03570 splitObjs = 0; 03571 if (objc == 1) { 03572 Tcl_Obj **listv; 03573 blist = objv[0]; 03574 03575 if (TclListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK){ 03576 return TCL_ERROR; 03577 } 03578 03579 /* 03580 * Ensure that the list is non-empty. 03581 */ 03582 03583 if (objc < 1) { 03584 Tcl_WrongNumArgs(interp, 1, savedObjv, 03585 "?switches? string {pattern body ... ?default body?}"); 03586 return TCL_ERROR; 03587 } 03588 objv = listv; 03589 splitObjs = 1; 03590 } 03591 03592 /* 03593 * Complain if there is an odd number of words in the list of patterns and 03594 * bodies. 03595 */ 03596 03597 if (objc % 2) { 03598 Tcl_ResetResult(interp); 03599 Tcl_AppendResult(interp, "extra switch pattern with no body", NULL); 03600 03601 /* 03602 * Check if this can be due to a badly placed comment in the switch 03603 * block. 03604 * 03605 * The following is an heuristic to detect the infamous "comment in 03606 * switch" error: just check if a pattern begins with '#'. 03607 */ 03608 03609 if (splitObjs) { 03610 for (i=0 ; i<objc ; i+=2) { 03611 if (TclGetString(objv[i])[0] == '#') { 03612 Tcl_AppendResult(interp, ", this may be due to a " 03613 "comment incorrectly placed outside of a " 03614 "switch body - see the \"switch\" " 03615 "documentation", NULL); 03616 break; 03617 } 03618 } 03619 } 03620 03621 return TCL_ERROR; 03622 } 03623 03624 /* 03625 * Complain if the last body is a continuation. Note that this check 03626 * assumes that the list is non-empty! 03627 */ 03628 03629 if (strcmp(TclGetString(objv[objc-1]), "-") == 0) { 03630 Tcl_ResetResult(interp); 03631 Tcl_AppendResult(interp, "no body specified for pattern \"", 03632 TclGetString(objv[objc-2]), "\"", NULL); 03633 return TCL_ERROR; 03634 } 03635 03636 for (i = 0; i < objc; i += 2) { 03637 /* 03638 * See if the pattern matches the string. 03639 */ 03640 03641 pattern = TclGetStringFromObj(objv[i], &patternLength); 03642 03643 if ((i == objc - 2) && (*pattern == 'd') 03644 && (strcmp(pattern, "default") == 0)) { 03645 Tcl_Obj *emptyObj = NULL; 03646 03647 /* 03648 * If either indexVarObj or matchVarObj are non-NULL, we're in 03649 * REGEXP mode but have reached the default clause anyway. TIP#75 03650 * specifies that we set the variables to empty lists (== empty 03651 * objects) in that case. 03652 */ 03653 03654 if (indexVarObj != NULL) { 03655 TclNewObj(emptyObj); 03656 if (Tcl_ObjSetVar2(interp, indexVarObj, NULL, emptyObj, 03657 TCL_LEAVE_ERR_MSG) == NULL) { 03658 return TCL_ERROR; 03659 } 03660 } 03661 if (matchVarObj != NULL) { 03662 if (emptyObj == NULL) { 03663 TclNewObj(emptyObj); 03664 } 03665 if (Tcl_ObjSetVar2(interp, matchVarObj, NULL, emptyObj, 03666 TCL_LEAVE_ERR_MSG) == NULL) { 03667 return TCL_ERROR; 03668 } 03669 } 03670 goto matchFound; 03671 } else { 03672 switch (mode) { 03673 case OPT_EXACT: 03674 if (strCmpFn(TclGetString(stringObj), pattern) == 0) { 03675 goto matchFound; 03676 } 03677 break; 03678 case OPT_GLOB: 03679 if (Tcl_StringCaseMatch(TclGetString(stringObj), pattern, 03680 noCase)) { 03681 goto matchFound; 03682 } 03683 break; 03684 case OPT_REGEXP: 03685 regExpr = Tcl_GetRegExpFromObj(interp, objv[i], 03686 TCL_REG_ADVANCED | (noCase ? TCL_REG_NOCASE : 0)); 03687 if (regExpr == NULL) { 03688 return TCL_ERROR; 03689 } else { 03690 int matched = Tcl_RegExpExecObj(interp, regExpr, 03691 stringObj, 0, numMatchesSaved, 0); 03692 03693 if (matched < 0) { 03694 return TCL_ERROR; 03695 } else if (matched) { 03696 goto matchFoundRegexp; 03697 } 03698 } 03699 break; 03700 } 03701 } 03702 } 03703 return TCL_OK; 03704 03705 matchFoundRegexp: 03706 /* 03707 * We are operating in REGEXP mode and we need to store information about 03708 * what we matched in some user-nominated arrays. So build the lists of 03709 * values and indices to write here. [TIP#75] 03710 */ 03711 03712 if (numMatchesSaved) { 03713 Tcl_RegExpInfo info; 03714 Tcl_Obj *matchesObj, *indicesObj = NULL; 03715 03716 Tcl_RegExpGetInfo(regExpr, &info); 03717 if (matchVarObj != NULL) { 03718 TclNewObj(matchesObj); 03719 } else { 03720 matchesObj = NULL; 03721 } 03722 if (indexVarObj != NULL) { 03723 TclNewObj(indicesObj); 03724 } 03725 03726 for (j=0 ; j<=info.nsubs ; j++) { 03727 if (indexVarObj != NULL) { 03728 Tcl_Obj *rangeObjAry[2]; 03729 03730 rangeObjAry[0] = Tcl_NewLongObj(info.matches[j].start); 03731 rangeObjAry[1] = Tcl_NewLongObj(info.matches[j].end); 03732 03733 /* 03734 * Never fails; the object is always clean at this point. 03735 */ 03736 03737 Tcl_ListObjAppendElement(NULL, indicesObj, 03738 Tcl_NewListObj(2, rangeObjAry)); 03739 } 03740 03741 if (matchVarObj != NULL) { 03742 Tcl_Obj *substringObj; 03743 03744 substringObj = Tcl_GetRange(stringObj, 03745 info.matches[j].start, info.matches[j].end-1); 03746 03747 /* 03748 * Never fails; the object is always clean at this point. 03749 */ 03750 03751 Tcl_ListObjAppendElement(NULL, matchesObj, substringObj); 03752 } 03753 } 03754 03755 if (indexVarObj != NULL) { 03756 if (Tcl_ObjSetVar2(interp, indexVarObj, NULL, indicesObj, 03757 TCL_LEAVE_ERR_MSG) == NULL) { 03758 /* 03759 * Careful! Check to see if we have allocated the list of 03760 * matched strings; if so (but there was an error assigning 03761 * the indices list) we have a potential memory leak because 03762 * the match list has not been written to a variable. Except 03763 * that we'll clean that up right now. 03764 */ 03765 03766 if (matchesObj != NULL) { 03767 Tcl_DecrRefCount(matchesObj); 03768 } 03769 return TCL_ERROR; 03770 } 03771 } 03772 if (matchVarObj != NULL) { 03773 if (Tcl_ObjSetVar2(interp, matchVarObj, NULL, matchesObj, 03774 TCL_LEAVE_ERR_MSG) == NULL) { 03775 /* 03776 * Unlike above, if indicesObj is non-NULL at this point, it 03777 * will have been written to a variable already and will hence 03778 * not be leaked. 03779 */ 03780 03781 return TCL_ERROR; 03782 } 03783 } 03784 } 03785 03786 /* 03787 * We've got a match. Find a body to execute, skipping bodies that are 03788 * "-". 03789 */ 03790 03791 matchFound: 03792 ctxPtr = (CmdFrame *) TclStackAlloc(interp, sizeof(CmdFrame)); 03793 *ctxPtr = *iPtr->cmdFramePtr; 03794 03795 if (splitObjs) { 03796 /* 03797 * We have to perform the GetSrc and other type dependent handling of 03798 * the frame here because we are munging with the line numbers, 03799 * something the other commands like if, etc. are not doing. Them are 03800 * fine with simply passing the CmdFrame through and having the 03801 * special handling done in 'info frame', or the bc compiler 03802 */ 03803 03804 if (ctxPtr->type == TCL_LOCATION_BC) { 03805 /* 03806 * Type BC => ctxPtr->data.eval.path is not used. 03807 * ctxPtr->data.tebc.codePtr is used instead. 03808 */ 03809 03810 TclGetSrcInfoForPc(ctxPtr); 03811 pc = 1; 03812 03813 /* 03814 * The line information in the cmdFrame is now a copy we do not 03815 * own. 03816 */ 03817 } 03818 03819 if (ctxPtr->type == TCL_LOCATION_SOURCE && ctxPtr->line[bidx] >= 0) { 03820 int bline = ctxPtr->line[bidx]; 03821 03822 ctxPtr->line = (int *) ckalloc(objc * sizeof(int)); 03823 ctxPtr->nline = objc; 03824 TclListLines(TclGetString(blist), bline, objc, ctxPtr->line); 03825 } else { 03826 /* 03827 * This is either a dynamic code word, when all elements are 03828 * relative to themselves, or something else less expected and 03829 * where we have no information. The result is the same in both 03830 * cases; tell the code to come that it doesn't know where it is, 03831 * which triggers reversion to the old behavior. 03832 */ 03833 03834 int k; 03835 03836 ctxPtr->line = (int *) ckalloc(objc * sizeof(int)); 03837 ctxPtr->nline = objc; 03838 for (k=0; k < objc; k++) { 03839 ctxPtr->line[k] = -1; 03840 } 03841 } 03842 } 03843 03844 for (j = i + 1; ; j += 2) { 03845 if (j >= objc) { 03846 /* 03847 * This shouldn't happen since we've checked that the last body is 03848 * not a continuation... 03849 */ 03850 03851 Tcl_Panic("fall-out when searching for body to match pattern"); 03852 } 03853 if (strcmp(TclGetString(objv[j]), "-") != 0) { 03854 break; 03855 } 03856 } 03857 03858 /* 03859 * TIP #280: Make invoking context available to switch branch. 03860 */ 03861 03862 result = TclEvalObjEx(interp, objv[j], 0, ctxPtr, j); 03863 if (splitObjs) { 03864 ckfree((char *) ctxPtr->line); 03865 if (pc && (ctxPtr->type == TCL_LOCATION_SOURCE)) { 03866 /* 03867 * Death of SrcInfo reference. 03868 */ 03869 03870 Tcl_DecrRefCount(ctxPtr->data.eval.path); 03871 } 03872 } 03873 03874 /* 03875 * Generate an error message if necessary. 03876 */ 03877 03878 if (result == TCL_ERROR) { 03879 int limit = 50; 03880 int overflow = (patternLength > limit); 03881 03882 Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( 03883 "\n (\"%.*s%s\" arm line %d)", 03884 (overflow ? limit : patternLength), pattern, 03885 (overflow ? "..." : ""), interp->errorLine)); 03886 } 03887 TclStackFree(interp, ctxPtr); 03888 return result; 03889 } 03890 03891 /* 03892 *---------------------------------------------------------------------- 03893 * 03894 * Tcl_TimeObjCmd -- 03895 * 03896 * This object-based procedure is invoked to process the "time" Tcl 03897 * command. See the user documentation for details on what it does. 03898 * 03899 * Results: 03900 * A standard Tcl object result. 03901 * 03902 * Side effects: 03903 * See the user documentation. 03904 * 03905 *---------------------------------------------------------------------- 03906 */ 03907 03908 int 03909 Tcl_TimeObjCmd( 03910 ClientData dummy, /* Not used. */ 03911 Tcl_Interp *interp, /* Current interpreter. */ 03912 int objc, /* Number of arguments. */ 03913 Tcl_Obj *CONST objv[]) /* Argument objects. */ 03914 { 03915 register Tcl_Obj *objPtr; 03916 Tcl_Obj *objs[4]; 03917 register int i, result; 03918 int count; 03919 double totalMicroSec; 03920 #ifndef TCL_WIDE_CLICKS 03921 Tcl_Time start, stop; 03922 #else 03923 Tcl_WideInt start, stop; 03924 #endif 03925 03926 if (objc == 2) { 03927 count = 1; 03928 } else if (objc == 3) { 03929 result = TclGetIntFromObj(interp, objv[2], &count); 03930 if (result != TCL_OK) { 03931 return result; 03932 } 03933 } else { 03934 Tcl_WrongNumArgs(interp, 1, objv, "command ?count?"); 03935 return TCL_ERROR; 03936 } 03937 03938 objPtr = objv[1]; 03939 i = count; 03940 #ifndef TCL_WIDE_CLICKS 03941 Tcl_GetTime(&start); 03942 #else 03943 start = TclpGetWideClicks(); 03944 #endif 03945 while (i-- > 0) { 03946 result = Tcl_EvalObjEx(interp, objPtr, 0); 03947 if (result != TCL_OK) { 03948 return result; 03949 } 03950 } 03951 #ifndef TCL_WIDE_CLICKS 03952 Tcl_GetTime(&stop); 03953 totalMicroSec = ((double) (stop.sec - start.sec)) * 1.0e6 03954 + (stop.usec - start.usec); 03955 #else 03956 stop = TclpGetWideClicks(); 03957 totalMicroSec = ((double) TclpWideClicksToNanoseconds(stop - start))/1.0e3; 03958 #endif 03959 03960 if (count <= 1) { 03961 /* 03962 * Use int obj since we know time is not fractional. [Bug 1202178] 03963 */ 03964 03965 objs[0] = Tcl_NewIntObj((count <= 0) ? 0 : (int) totalMicroSec); 03966 } else { 03967 objs[0] = Tcl_NewDoubleObj(totalMicroSec/count); 03968 } 03969 03970 /* 03971 * Construct the result as a list because many programs have always parsed 03972 * as such (extracting the first element, typically). 03973 */ 03974 03975 TclNewLiteralStringObj(objs[1], "microseconds"); 03976 TclNewLiteralStringObj(objs[2], "per"); 03977 TclNewLiteralStringObj(objs[3], "iteration"); 03978 Tcl_SetObjResult(interp, Tcl_NewListObj(4, objs)); 03979 03980 return TCL_OK; 03981 } 03982 03983 /* 03984 *---------------------------------------------------------------------- 03985 * 03986 * Tcl_WhileObjCmd -- 03987 * 03988 * This procedure is invoked to process the "while" Tcl command. See the 03989 * user documentation for details on what it does. 03990 * 03991 * With the bytecode compiler, this procedure is only called when a 03992 * command name is computed at runtime, and is "while" or the name to 03993 * which "while" was renamed: e.g., "set z while; $z {$i<100} {}" 03994 * 03995 * Results: 03996 * A standard Tcl result. 03997 * 03998 * Side effects: 03999 * See the user documentation. 04000 * 04001 *---------------------------------------------------------------------- 04002 */ 04003 04004 int 04005 Tcl_WhileObjCmd( 04006 ClientData dummy, /* Not used. */ 04007 Tcl_Interp *interp, /* Current interpreter. */ 04008 int objc, /* Number of arguments. */ 04009 Tcl_Obj *CONST objv[]) /* Argument objects. */ 04010 { 04011 int result, value; 04012 Interp *iPtr = (Interp *) interp; 04013 04014 if (objc != 3) { 04015 Tcl_WrongNumArgs(interp, 1, objv, "test command"); 04016 return TCL_ERROR; 04017 } 04018 04019 while (1) { 04020 result = Tcl_ExprBooleanObj(interp, objv[1], &value); 04021 if (result != TCL_OK) { 04022 return result; 04023 } 04024 if (!value) { 04025 break; 04026 } 04027 04028 /* TIP #280. */ 04029 result = TclEvalObjEx(interp, objv[2], 0, iPtr->cmdFramePtr, 2); 04030 if ((result != TCL_OK) && (result != TCL_CONTINUE)) { 04031 if (result == TCL_ERROR) { 04032 Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( 04033 "\n (\"while\" body line %d)", interp->errorLine)); 04034 } 04035 break; 04036 } 04037 } 04038 if (result == TCL_BREAK) { 04039 result = TCL_OK; 04040 } 04041 if (result == TCL_OK) { 04042 Tcl_ResetResult(interp); 04043 } 04044 return result; 04045 } 04046 04047 /* 04048 *---------------------------------------------------------------------- 04049 * 04050 * TclListLines -- 04051 * 04052 * ??? 04053 * 04054 * Results: 04055 * Filled in array of line numbers? 04056 * 04057 * Side effects: 04058 * None. 04059 * 04060 *---------------------------------------------------------------------- 04061 */ 04062 04063 void 04064 TclListLines( 04065 CONST char *listStr, /* Pointer to string with list structure. 04066 * Assumed to be valid. Assumed to contain n 04067 * elements. */ 04068 int line, /* Line the list as a whole starts on. */ 04069 int n, /* #elements in lines */ 04070 int *lines) /* Array of line numbers, to fill. */ 04071 { 04072 int i, length = strlen(listStr); 04073 CONST char *element = NULL, *next = NULL; 04074 04075 for (i = 0; i < n; i++) { 04076 TclFindElement(NULL, listStr, length, &element, &next, NULL, NULL); 04077 04078 TclAdvanceLines(&line, listStr, element); 04079 /* Leading whitespace */ 04080 lines[i] = line; 04081 length -= (next - listStr); 04082 TclAdvanceLines(&line, element, next); 04083 /* Element */ 04084 listStr = next; 04085 04086 if (*element == 0) { 04087 /* ASSERT i == n */ 04088 break; 04089 } 04090 } 04091 } 04092 04093 /* 04094 * Local Variables: 04095 * mode: c 04096 * c-basic-offset: 4 04097 * fill-column: 78 04098 * End: 04099 */
Generated on Wed Mar 12 12:18:13 2008 by 1.5.1 |