tclCmdMZ.c

Go 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  doxygen 1.5.1