tclCmdMZ.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:139k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclCmdMZ.c --
  3.  *
  4.  * This file contains the top-level command routines for most of
  5.  * the Tcl built-in commands whose names begin with the letters
  6.  * M to Z.  It contains only commands in the generic core (i.e.
  7.  * those that don't depend much upon UNIX facilities).
  8.  *
  9.  * Copyright (c) 1987-1993 The Regents of the University of California.
  10.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  11.  * Copyright (c) 1998-2000 Scriptics Corporation.
  12.  * Copyright (c) 2002 ActiveState Corporation.
  13.  *
  14.  * See the file "license.terms" for information on usage and redistribution
  15.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  16.  *
  17.  * RCS: @(#) $Id: tclCmdMZ.c,v 1.82.2.29 2007/06/27 17:29:22 dgp Exp $
  18.  */
  19. #include "tclInt.h"
  20. #include "tclPort.h"
  21. #include "tclRegexp.h"
  22. #include "tclCompile.h"
  23. /*
  24.  * Structures used to hold information about variable traces:
  25.  */
  26. typedef struct {
  27.     int flags; /* Operations for which Tcl command is
  28.  * to be invoked. */
  29.     size_t length; /* Number of non-NULL chars. in command. */
  30.     char command[4]; /* Space for Tcl command to invoke.  Actual
  31.  * size will be as large as necessary to
  32.  * hold command.  This field must be the
  33.  * last in the structure, so that it can
  34.  * be larger than 4 bytes. */
  35. } TraceVarInfo;
  36. typedef struct {
  37.     VarTrace trace;
  38.     TraceVarInfo tvar;
  39. } CompoundVarTrace;
  40. /*
  41.  * Structure used to hold information about command traces:
  42.  */
  43. typedef struct {
  44.     int flags; /* Operations for which Tcl command is
  45.  * to be invoked. */
  46.     size_t length; /* Number of non-NULL chars. in command. */
  47.     Tcl_Trace stepTrace;        /* Used for execution traces, when tracing
  48.                                  * inside the given command */
  49.     int startLevel;             /* Used for bookkeeping with step execution
  50.                                  * traces, store the level at which the step
  51.                                  * trace was invoked */
  52.     char *startCmd;             /* Used for bookkeeping with step execution
  53.                                  * traces, store the command name which invoked
  54.                                  * step trace */
  55.     int curFlags;               /* Trace flags for the current command */
  56.     int curCode;                /* Return code for the current command */
  57.     int refCount;               /* Used to ensure this structure is
  58.                                  * not deleted too early.  Keeps track
  59.                                  * of how many pieces of code have
  60.                                  * a pointer to this structure. */
  61.     char command[4]; /* Space for Tcl command to invoke.  Actual
  62.  * size will be as large as necessary to
  63.  * hold command.  This field must be the
  64.  * last in the structure, so that it can
  65.  * be larger than 4 bytes. */
  66. } TraceCommandInfo;
  67. /* 
  68.  * Used by command execution traces.  Note that we assume in the code
  69.  * that the first two defines are exactly 4 times the
  70.  * 'TCL_TRACE_ENTER_EXEC' and 'TCL_TRACE_LEAVE_EXEC' constants.
  71.  * 
  72.  * TCL_TRACE_ENTER_DURING_EXEC  - Trace each command inside the command
  73.  *                                currently being traced, before execution.
  74.  * TCL_TRACE_LEAVE_DURING_EXEC  - Trace each command inside the command
  75.  *                                currently being traced, after execution.
  76.  * TCL_TRACE_ANY_EXEC           - OR'd combination of all EXEC flags.
  77.  * TCL_TRACE_EXEC_IN_PROGRESS   - The callback procedure on this trace
  78.  *                                is currently executing.  Therefore we
  79.  *                                don't let further traces execute.
  80.  * TCL_TRACE_EXEC_DIRECT        - This execution trace is triggered directly
  81.  *                                by the command being traced, not because
  82.  *                                of an internal trace.
  83.  * The flags 'TCL_TRACE_DESTROYED' and 'TCL_INTERP_DESTROYED' may also
  84.  * be used in command execution traces.
  85.  */
  86. #define TCL_TRACE_ENTER_DURING_EXEC 4
  87. #define TCL_TRACE_LEAVE_DURING_EXEC 8
  88. #define TCL_TRACE_ANY_EXEC              15
  89. #define TCL_TRACE_EXEC_IN_PROGRESS      0x10
  90. #define TCL_TRACE_EXEC_DIRECT           0x20
  91. /*
  92.  * Forward declarations for procedures defined in this file:
  93.  */
  94. typedef int (Tcl_TraceTypeObjCmd) _ANSI_ARGS_((Tcl_Interp *interp,
  95. int optionIndex, int objc, Tcl_Obj *CONST objv[]));
  96. Tcl_TraceTypeObjCmd TclTraceVariableObjCmd;
  97. Tcl_TraceTypeObjCmd TclTraceCommandObjCmd;
  98. Tcl_TraceTypeObjCmd TclTraceExecutionObjCmd;
  99. /* 
  100.  * Each subcommand has a number of 'types' to which it can apply.
  101.  * Currently 'execution', 'command' and 'variable' are the only
  102.  * types supported.  These three arrays MUST be kept in sync!
  103.  * In the future we may provide an API to add to the list of
  104.  * supported trace types.
  105.  */
  106. static CONST char *traceTypeOptions[] = {
  107.     "execution", "command", "variable", (char*) NULL
  108. };
  109. static Tcl_TraceTypeObjCmd* traceSubCmds[] = {
  110.     TclTraceExecutionObjCmd,
  111.     TclTraceCommandObjCmd,
  112.     TclTraceVariableObjCmd,
  113. };
  114. /*
  115.  * Declarations for local procedures to this file:
  116.  */
  117. static int              CallTraceProcedure _ANSI_ARGS_((Tcl_Interp *interp,
  118.                             Trace *tracePtr, Command *cmdPtr,
  119.                             CONST char *command, int numChars,
  120.                             int objc, Tcl_Obj *CONST objv[]));
  121. static char * TraceVarProc _ANSI_ARGS_((ClientData clientData,
  122.     Tcl_Interp *interp, CONST char *name1, 
  123.                             CONST char *name2, int flags));
  124. static void TraceCommandProc _ANSI_ARGS_((ClientData clientData,
  125.     Tcl_Interp *interp, CONST char *oldName,
  126.                             CONST char *newName, int flags));
  127. static Tcl_CmdObjTraceProc TraceExecutionProc;
  128. #ifdef TCL_TIP280
  129. static void             ListLines _ANSI_ARGS_((CONST char* listStr, int line,
  130.        int n, int* lines));
  131. #endif
  132. /*
  133.  *----------------------------------------------------------------------
  134.  *
  135.  * Tcl_PwdObjCmd --
  136.  *
  137.  * This procedure is invoked to process the "pwd" Tcl command.
  138.  * See the user documentation for details on what it does.
  139.  *
  140.  * Results:
  141.  * A standard Tcl result.
  142.  *
  143.  * Side effects:
  144.  * See the user documentation.
  145.  *
  146.  *----------------------------------------------------------------------
  147.  */
  148. /* ARGSUSED */
  149. int
  150. Tcl_PwdObjCmd(dummy, interp, objc, objv)
  151.     ClientData dummy; /* Not used. */
  152.     Tcl_Interp *interp; /* Current interpreter. */
  153.     int objc; /* Number of arguments. */
  154.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  155. {
  156.     Tcl_Obj *retVal;
  157.     if (objc != 1) {
  158. Tcl_WrongNumArgs(interp, 1, objv, NULL);
  159. return TCL_ERROR;
  160.     }
  161.     retVal = Tcl_FSGetCwd(interp);
  162.     if (retVal == NULL) {
  163. return TCL_ERROR;
  164.     }
  165.     Tcl_SetObjResult(interp, retVal);
  166.     Tcl_DecrRefCount(retVal);
  167.     return TCL_OK;
  168. }
  169. /*
  170.  *----------------------------------------------------------------------
  171.  *
  172.  * Tcl_RegexpObjCmd --
  173.  *
  174.  * This procedure is invoked to process the "regexp" Tcl command.
  175.  * See the user documentation for details on what it does.
  176.  *
  177.  * Results:
  178.  * A standard Tcl result.
  179.  *
  180.  * Side effects:
  181.  * See the user documentation.
  182.  *
  183.  *----------------------------------------------------------------------
  184.  */
  185. /* ARGSUSED */
  186. int
  187. Tcl_RegexpObjCmd(dummy, interp, objc, objv)
  188.     ClientData dummy; /* Not used. */
  189.     Tcl_Interp *interp; /* Current interpreter. */
  190.     int objc; /* Number of arguments. */
  191.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  192. {
  193.     int i, indices, match, about, offset, all, doinline, numMatchesSaved;
  194.     int cflags, eflags, stringLength;
  195.     Tcl_RegExp regExpr;
  196.     Tcl_Obj *objPtr, *resultPtr;
  197.     Tcl_RegExpInfo info;
  198.     static CONST char *options[] = {
  199. "-all", "-about", "-indices", "-inline",
  200. "-expanded", "-line", "-linestop", "-lineanchor",
  201. "-nocase", "-start", "--", (char *) NULL
  202.     };
  203.     enum options {
  204. REGEXP_ALL, REGEXP_ABOUT, REGEXP_INDICES, REGEXP_INLINE,
  205. REGEXP_EXPANDED,REGEXP_LINE, REGEXP_LINESTOP,REGEXP_LINEANCHOR,
  206. REGEXP_NOCASE, REGEXP_START, REGEXP_LAST
  207.     };
  208.     indices = 0;
  209.     about = 0;
  210.     cflags = TCL_REG_ADVANCED;
  211.     eflags = 0;
  212.     offset = 0;
  213.     all = 0;
  214.     doinline = 0;
  215.     
  216.     for (i = 1; i < objc; i++) {
  217. char *name;
  218. int index;
  219. name = Tcl_GetString(objv[i]);
  220. if (name[0] != '-') {
  221.     break;
  222. }
  223. if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,
  224. &index) != TCL_OK) {
  225.     return TCL_ERROR;
  226. }
  227. switch ((enum options) index) {
  228.     case REGEXP_ALL: {
  229. all = 1;
  230. break;
  231.     }
  232.     case REGEXP_INDICES: {
  233. indices = 1;
  234. break;
  235.     }
  236.     case REGEXP_INLINE: {
  237. doinline = 1;
  238. break;
  239.     }
  240.     case REGEXP_NOCASE: {
  241. cflags |= TCL_REG_NOCASE;
  242. break;
  243.     }
  244.     case REGEXP_ABOUT: {
  245. about = 1;
  246. break;
  247.     }
  248.     case REGEXP_EXPANDED: {
  249. cflags |= TCL_REG_EXPANDED;
  250. break;
  251.     }
  252.     case REGEXP_LINE: {
  253. cflags |= TCL_REG_NEWLINE;
  254. break;
  255.     }
  256.     case REGEXP_LINESTOP: {
  257. cflags |= TCL_REG_NLSTOP;
  258. break;
  259.     }
  260.     case REGEXP_LINEANCHOR: {
  261. cflags |= TCL_REG_NLANCH;
  262. break;
  263.     }
  264.     case REGEXP_START: {
  265. if (++i >= objc) {
  266.     goto endOfForLoop;
  267. }
  268. if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {
  269.     return TCL_ERROR;
  270. }
  271. if (offset < 0) {
  272.     offset = 0;
  273. }
  274. break;
  275.     }
  276.     case REGEXP_LAST: {
  277. i++;
  278. goto endOfForLoop;
  279.     }
  280. }
  281.     }
  282.     endOfForLoop:
  283.     if ((objc - i) < (2 - about)) {
  284. Tcl_WrongNumArgs(interp, 1, objv, 
  285.   "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
  286. return TCL_ERROR;
  287.     }
  288.     objc -= i;
  289.     objv += i;
  290.     if (doinline && ((objc - 2) != 0)) {
  291. /*
  292.  * User requested -inline, but specified match variables - a no-no.
  293.  */
  294. Tcl_AppendResult(interp, "regexp match variables not allowed",
  295. " when using -inline", (char *) NULL);
  296. return TCL_ERROR;
  297.     }
  298.     /*
  299.      * Handle the odd about case separately.
  300.      */
  301.     if (about) {
  302. regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
  303. if ((regExpr == NULL) || (TclRegAbout(interp, regExpr) < 0)) {
  304.     return TCL_ERROR;
  305. }
  306. return TCL_OK;
  307.     }
  308.     /*
  309.      * Get the length of the string that we are matching against so
  310.      * we can do the termination test for -all matches.  Do this before
  311.      * getting the regexp to avoid shimmering problems.
  312.      */
  313.     objPtr = objv[1];
  314.     stringLength = Tcl_GetCharLength(objPtr);
  315.     regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
  316.     if (regExpr == NULL) {
  317. return TCL_ERROR;
  318.     }
  319.     if (offset > 0) {
  320. /*
  321.  * Add flag if using offset (string is part of a larger string),
  322.  * so that "^" won't match.
  323.  */
  324. eflags |= TCL_REG_NOTBOL;
  325.     }
  326.     objc -= 2;
  327.     objv += 2;
  328.     resultPtr = Tcl_GetObjResult(interp);
  329.     if (doinline) {
  330. /*
  331.  * Save all the subexpressions, as we will return them as a list
  332.  */
  333. numMatchesSaved = -1;
  334.     } else {
  335. /*
  336.  * Save only enough subexpressions for matches we want to keep,
  337.  * expect in the case of -all, where we need to keep at least
  338.  * one to know where to move the offset.
  339.  */
  340. numMatchesSaved = (objc == 0) ? all : objc;
  341.     }
  342.     /*
  343.      * The following loop is to handle multiple matches within the
  344.      * same source string;  each iteration handles one match.  If "-all"
  345.      * hasn't been specified then the loop body only gets executed once.
  346.      * We terminate the loop when the starting offset is past the end of the
  347.      * string.
  348.      */
  349.     while (1) {
  350. match = Tcl_RegExpExecObj(interp, regExpr, objPtr,
  351. offset /* offset */, numMatchesSaved, eflags 
  352. | ((offset > 0 &&
  353.    (Tcl_GetUniChar(objPtr,offset-1) != (Tcl_UniChar)'n'))
  354.    ? TCL_REG_NOTBOL : 0));
  355. if (match < 0) {
  356.     return TCL_ERROR;
  357. }
  358. if (match == 0) {
  359.     /*
  360.      * We want to set the value of the intepreter result only when
  361.      * this is the first time through the loop.
  362.      */
  363.     if (all <= 1) {
  364. /*
  365.  * If inlining, set the interpreter's object result to an
  366.  * empty list, otherwise set it to an integer object w/
  367.  * value 0.
  368.  */
  369. if (doinline) {
  370.     Tcl_SetListObj(resultPtr, 0, NULL);
  371. } else {
  372.     Tcl_SetIntObj(resultPtr, 0);
  373. }
  374. return TCL_OK;
  375.     }
  376.     break;
  377. }
  378. /*
  379.  * If additional variable names have been specified, return
  380.  * index information in those variables.
  381.  */
  382. Tcl_RegExpGetInfo(regExpr, &info);
  383. if (doinline) {
  384.     /*
  385.      * It's the number of substitutions, plus one for the matchVar
  386.      * at index 0
  387.      */
  388.     objc = info.nsubs + 1;
  389. }
  390. for (i = 0; i < objc; i++) {
  391.     Tcl_Obj *newPtr;
  392.     if (indices) {
  393. int start, end;
  394. Tcl_Obj *objs[2];
  395. /*
  396.  * Only adjust the match area if there was a match for
  397.  * that area.  (Scriptics Bug 4391/SF Bug #219232)
  398.  */
  399. if (i <= info.nsubs && info.matches[i].start >= 0) {
  400.     start = offset + info.matches[i].start;
  401.     end   = offset + info.matches[i].end;
  402.     /*
  403.      * Adjust index so it refers to the last character in the
  404.      * match instead of the first character after the match.
  405.      */
  406.     if (end >= offset) {
  407. end--;
  408.     }
  409. } else {
  410.     start = -1;
  411.     end   = -1;
  412. }
  413. objs[0] = Tcl_NewLongObj(start);
  414. objs[1] = Tcl_NewLongObj(end);
  415. newPtr = Tcl_NewListObj(2, objs);
  416.     } else {
  417. if (i <= info.nsubs) {
  418.     newPtr = Tcl_GetRange(objPtr,
  419.     offset + info.matches[i].start,
  420.     offset + info.matches[i].end - 1);
  421. } else {
  422.     newPtr = Tcl_NewObj();
  423. }
  424.     }
  425.     if (doinline) {
  426. if (Tcl_ListObjAppendElement(interp, resultPtr, newPtr)
  427. != TCL_OK) {
  428.     Tcl_DecrRefCount(newPtr);
  429.     return TCL_ERROR;
  430. }
  431.     } else {
  432. Tcl_Obj *valuePtr;
  433. Tcl_IncrRefCount(newPtr);
  434. valuePtr = Tcl_ObjSetVar2(interp, objv[i], NULL, newPtr, 0);
  435. Tcl_DecrRefCount(newPtr);
  436. if (valuePtr == NULL) {
  437.     Tcl_AppendResult(interp, "couldn't set variable "",
  438.     Tcl_GetString(objv[i]), """, (char *) NULL);
  439.     return TCL_ERROR;
  440. }
  441.     }
  442. }
  443. if (all == 0) {
  444.     break;
  445. }
  446. /*
  447.  * Adjust the offset to the character just after the last one
  448.  * in the matchVar and increment all to count how many times
  449.  * we are making a match.  We always increment the offset by at least
  450.  * one to prevent endless looping (as in the case:
  451.  * regexp -all {a*} a).  Otherwise, when we match the NULL string at
  452.  * the end of the input string, we will loop indefinately (because the
  453.  * length of the match is 0, so offset never changes).
  454.  */
  455. if (info.matches[0].end == 0) {
  456.     offset++;
  457. }
  458. offset += info.matches[0].end;
  459. all++;
  460. eflags |= TCL_REG_NOTBOL;
  461. if (offset >= stringLength) {
  462.     break;
  463. }
  464.     }
  465.     /*
  466.      * Set the interpreter's object result to an integer object
  467.      * with value 1 if -all wasn't specified, otherwise it's all-1
  468.      * (the number of times through the while - 1).
  469.      * Get the resultPtr again as the Tcl_ObjSetVar2 above may have
  470.      * cause the result to change. [Patch #558324] (watson).
  471.      */
  472.     if (!doinline) {
  473. resultPtr = Tcl_GetObjResult(interp);
  474. Tcl_SetIntObj(resultPtr, (all ? all-1 : 1));
  475.     }
  476.     return TCL_OK;
  477. }
  478. /*
  479.  *----------------------------------------------------------------------
  480.  *
  481.  * Tcl_RegsubObjCmd --
  482.  *
  483.  * This procedure is invoked to process the "regsub" Tcl command.
  484.  * See the user documentation for details on what it does.
  485.  *
  486.  * Results:
  487.  * A standard Tcl result.
  488.  *
  489.  * Side effects:
  490.  * See the user documentation.
  491.  *
  492.  *----------------------------------------------------------------------
  493.  */
  494. /* ARGSUSED */
  495. int
  496. Tcl_RegsubObjCmd(dummy, interp, objc, objv)
  497.     ClientData dummy; /* Not used. */
  498.     Tcl_Interp *interp; /* Current interpreter. */
  499.     int objc; /* Number of arguments. */
  500.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  501. {
  502.     int idx, result, cflags, all, wlen, wsublen, numMatches, offset;
  503.     int start, end, subStart, subEnd, match;
  504.     Tcl_RegExp regExpr;
  505.     Tcl_RegExpInfo info;
  506.     Tcl_Obj *resultPtr, *subPtr, *objPtr;
  507.     Tcl_UniChar ch, *wsrc, *wfirstChar, *wstring, *wsubspec, *wend;
  508.     static CONST char *options[] = {
  509. "-all", "-nocase", "-expanded",
  510. "-line", "-linestop", "-lineanchor", "-start",
  511. "--", NULL
  512.     };
  513.     enum options {
  514. REGSUB_ALL, REGSUB_NOCASE, REGSUB_EXPANDED,
  515. REGSUB_LINE, REGSUB_LINESTOP, REGSUB_LINEANCHOR, REGSUB_START,
  516. REGSUB_LAST
  517.     };
  518.     cflags = TCL_REG_ADVANCED;
  519.     all = 0;
  520.     offset = 0;
  521.     resultPtr = NULL;
  522.     for (idx = 1; idx < objc; idx++) {
  523. char *name;
  524. int index;
  525. name = Tcl_GetString(objv[idx]);
  526. if (name[0] != '-') {
  527.     break;
  528. }
  529. if (Tcl_GetIndexFromObj(interp, objv[idx], options, "switch",
  530. TCL_EXACT, &index) != TCL_OK) {
  531.     return TCL_ERROR;
  532. }
  533. switch ((enum options) index) {
  534.     case REGSUB_ALL: {
  535. all = 1;
  536. break;
  537.     }
  538.     case REGSUB_NOCASE: {
  539. cflags |= TCL_REG_NOCASE;
  540. break;
  541.     }
  542.     case REGSUB_EXPANDED: {
  543. cflags |= TCL_REG_EXPANDED;
  544. break;
  545.     }
  546.     case REGSUB_LINE: {
  547. cflags |= TCL_REG_NEWLINE;
  548. break;
  549.     }
  550.     case REGSUB_LINESTOP: {
  551. cflags |= TCL_REG_NLSTOP;
  552. break;
  553.     }
  554.     case REGSUB_LINEANCHOR: {
  555. cflags |= TCL_REG_NLANCH;
  556. break;
  557.     }
  558.     case REGSUB_START: {
  559. if (++idx >= objc) {
  560.     goto endOfForLoop;
  561. }
  562. if (Tcl_GetIntFromObj(interp, objv[idx], &offset) != TCL_OK) {
  563.     return TCL_ERROR;
  564. }
  565. if (offset < 0) {
  566.     offset = 0;
  567. }
  568. break;
  569.     }
  570.     case REGSUB_LAST: {
  571. idx++;
  572. goto endOfForLoop;
  573.     }
  574. }
  575.     }
  576.     endOfForLoop:
  577.     if (objc-idx < 3 || objc-idx > 4) {
  578. Tcl_WrongNumArgs(interp, 1, objv,
  579. "?switches? exp string subSpec ?varName?");
  580. return TCL_ERROR;
  581.     }
  582.     objc -= idx;
  583.     objv += idx;
  584.     if (all && (offset == 0)
  585.     && (strpbrk(Tcl_GetString(objv[2]), "&\") == NULL)
  586.     && (strpbrk(Tcl_GetString(objv[0]), "*+?{}()[].\|^$") == NULL)) {
  587. /*
  588.  * This is a simple one pair string map situation.  We make use of
  589.  * a slightly modified version of the one pair STR_MAP code.
  590.  */
  591. int slen, nocase;
  592. int (*strCmpFn)_ANSI_ARGS_((CONST Tcl_UniChar *, CONST Tcl_UniChar *,
  593. unsigned long));
  594. Tcl_UniChar *p, wsrclc;
  595. numMatches = 0;
  596. nocase     = (cflags & TCL_REG_NOCASE);
  597. strCmpFn   = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
  598. wsrc     = Tcl_GetUnicodeFromObj(objv[0], &slen);
  599. wstring  = Tcl_GetUnicodeFromObj(objv[1], &wlen);
  600. wsubspec = Tcl_GetUnicodeFromObj(objv[2], &wsublen);
  601. wend     = wstring + wlen - (slen ? slen - 1 : 0);
  602. result   = TCL_OK;
  603. if (slen == 0) {
  604.     /*
  605.      * regsub behavior for "" matches between each character.
  606.      * 'string map' skips the "" case.
  607.      */
  608.     if (wstring < wend) {
  609. resultPtr = Tcl_NewUnicodeObj(wstring, 0);
  610. Tcl_IncrRefCount(resultPtr);
  611. for (; wstring < wend; wstring++) {
  612.     Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen);
  613.     Tcl_AppendUnicodeToObj(resultPtr, wstring, 1);
  614.     numMatches++;
  615. }
  616. wlen = 0;
  617.     }
  618. } else {
  619.     wsrclc = Tcl_UniCharToLower(*wsrc);
  620.     for (p = wfirstChar = wstring; wstring < wend; wstring++) {
  621. if (((*wstring == *wsrc) ||
  622. (nocase && (Tcl_UniCharToLower(*wstring) ==
  623. wsrclc))) &&
  624. ((slen == 1) || (strCmpFn(wstring, wsrc,
  625. (unsigned long) slen) == 0))) {
  626.     if (numMatches == 0) {
  627. resultPtr = Tcl_NewUnicodeObj(wstring, 0);
  628. Tcl_IncrRefCount(resultPtr);
  629.     }
  630.     if (p != wstring) {
  631. Tcl_AppendUnicodeToObj(resultPtr, p, wstring - p);
  632. p = wstring + slen;
  633.     } else {
  634. p += slen;
  635.     }
  636.     wstring = p - 1;
  637.     Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen);
  638.     numMatches++;
  639. }
  640.     }
  641.     if (numMatches) {
  642. wlen    = wfirstChar + wlen - p;
  643. wstring = p;
  644.     }
  645. }
  646. objPtr = NULL;
  647. subPtr = NULL;
  648. goto regsubDone;
  649.     }
  650.     regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
  651.     if (regExpr == NULL) {
  652. return TCL_ERROR;
  653.     }
  654.     /*
  655.      * Make sure to avoid problems where the objects are shared.  This
  656.      * can cause RegExpObj <> UnicodeObj shimmering that causes data
  657.      * corruption.  [Bug #461322]
  658.      */
  659.     if (objv[1] == objv[0]) {
  660. objPtr = Tcl_DuplicateObj(objv[1]);
  661.     } else {
  662. objPtr = objv[1];
  663.     }
  664.     wstring = Tcl_GetUnicodeFromObj(objPtr, &wlen);
  665.     if (objv[2] == objv[0]) {
  666. subPtr = Tcl_DuplicateObj(objv[2]);
  667.     } else {
  668. subPtr = objv[2];
  669.     }
  670.     wsubspec = Tcl_GetUnicodeFromObj(subPtr, &wsublen);
  671.     result = TCL_OK;
  672.     /*
  673.      * The following loop is to handle multiple matches within the
  674.      * same source string;  each iteration handles one match and its
  675.      * corresponding substitution.  If "-all" hasn't been specified
  676.      * then the loop body only gets executed once.  We must use
  677.      * 'offset <= wlen' in particular for the case where the regexp
  678.      * pattern can match the empty string - this is useful when
  679.      * doing, say, 'regsub -- ^ $str ...' when $str might be empty.
  680.      */
  681.     numMatches = 0;
  682.     for ( ; offset <= wlen; ) {
  683. /*
  684.  * The flags argument is set if string is part of a larger string,
  685.  * so that "^" won't match.
  686.  */
  687. match = Tcl_RegExpExecObj(interp, regExpr, objPtr, offset,
  688. 10 /* matches */, ((offset > 0 &&
  689.    (wstring[offset-1] != (Tcl_UniChar)'n'))
  690.    ? TCL_REG_NOTBOL : 0));
  691. if (match < 0) {
  692.     result = TCL_ERROR;
  693.     goto done;
  694. }
  695. if (match == 0) {
  696.     break;
  697. }
  698. if (numMatches == 0) {
  699.     resultPtr = Tcl_NewUnicodeObj(wstring, 0);
  700.     Tcl_IncrRefCount(resultPtr);
  701.     if (offset > 0) {
  702. /*
  703.  * Copy the initial portion of the string in if an offset
  704.  * was specified.
  705.  */
  706. Tcl_AppendUnicodeToObj(resultPtr, wstring, offset);
  707.     }
  708. }
  709. numMatches++;
  710. /*
  711.  * Copy the portion of the source string before the match to the
  712.  * result variable.
  713.  */
  714. Tcl_RegExpGetInfo(regExpr, &info);
  715. start = info.matches[0].start;
  716. end = info.matches[0].end;
  717. Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, start);
  718. /*
  719.  * Append the subSpec argument to the variable, making appropriate
  720.  * substitutions.  This code is a bit hairy because of the backslash
  721.  * conventions and because the code saves up ranges of characters in
  722.  * subSpec to reduce the number of calls to Tcl_SetVar.
  723.  */
  724. wsrc = wfirstChar = wsubspec;
  725. wend = wsubspec + wsublen;
  726. for (ch = *wsrc; wsrc != wend; wsrc++, ch = *wsrc) {
  727.     if (ch == '&') {
  728. idx = 0;
  729.     } else if (ch == '\') {
  730. ch = wsrc[1];
  731. if ((ch >= '0') && (ch <= '9')) {
  732.     idx = ch - '0';
  733. } else if ((ch == '\') || (ch == '&')) {
  734.     *wsrc = ch;
  735.     Tcl_AppendUnicodeToObj(resultPtr, wfirstChar,
  736.     wsrc - wfirstChar + 1);
  737.     *wsrc = '\';
  738.     wfirstChar = wsrc + 2;
  739.     wsrc++;
  740.     continue;
  741. } else {
  742.     continue;
  743. }
  744.     } else {
  745. continue;
  746.     }
  747.     if (wfirstChar != wsrc) {
  748. Tcl_AppendUnicodeToObj(resultPtr, wfirstChar,
  749. wsrc - wfirstChar);
  750.     }
  751.     if (idx <= info.nsubs) {
  752. subStart = info.matches[idx].start;
  753. subEnd = info.matches[idx].end;
  754. if ((subStart >= 0) && (subEnd >= 0)) {
  755.     Tcl_AppendUnicodeToObj(resultPtr,
  756.     wstring + offset + subStart, subEnd - subStart);
  757. }
  758.     }
  759.     if (*wsrc == '\') {
  760. wsrc++;
  761.     }
  762.     wfirstChar = wsrc + 1;
  763. }
  764. if (wfirstChar != wsrc) {
  765.     Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, wsrc - wfirstChar);
  766. }
  767. if (end == 0) {
  768.     /*
  769.      * Always consume at least one character of the input string
  770.      * in order to prevent infinite loops.
  771.      */
  772.     if (offset < wlen) {
  773. Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
  774.     }
  775.     offset++;
  776. } else {
  777.     offset += end;
  778.     if (start == end) {
  779. /*
  780.  * We matched an empty string, which means we must go 
  781.  * forward one more step so we don't match again at the
  782.  * same spot.
  783.  */
  784. if (offset < wlen) {
  785.     Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
  786. }
  787. offset++;
  788.     }
  789. }
  790. if (!all) {
  791.     break;
  792. }
  793.     }
  794.     /*
  795.      * Copy the portion of the source string after the last match to the
  796.      * result variable.
  797.      */
  798.     regsubDone:
  799.     if (numMatches == 0) {
  800. /*
  801.  * On zero matches, just ignore the offset, since it shouldn't
  802.  * matter to us in this case, and the user may have skewed it.
  803.  */
  804. resultPtr = objv[1];
  805. Tcl_IncrRefCount(resultPtr);
  806.     } else if (offset < wlen) {
  807. Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset);
  808.     }
  809.     if (objc == 4) {
  810. if (Tcl_ObjSetVar2(interp, objv[3], NULL, resultPtr, 0) == NULL) {
  811.     Tcl_AppendResult(interp, "couldn't set variable "",
  812.     Tcl_GetString(objv[3]), """, (char *) NULL);
  813.     result = TCL_ERROR;
  814. } else {
  815.     /*
  816.      * Set the interpreter's object result to an integer object
  817.      * holding the number of matches. 
  818.      */
  819.     Tcl_SetIntObj(Tcl_GetObjResult(interp), numMatches);
  820. }
  821.     } else {
  822. /*
  823.  * No varname supplied, so just return the modified string.
  824.  */
  825. Tcl_SetObjResult(interp, resultPtr);
  826.     }
  827.     done:
  828.     if (objPtr && (objv[1] == objv[0])) { Tcl_DecrRefCount(objPtr); }
  829.     if (subPtr && (objv[2] == objv[0])) { Tcl_DecrRefCount(subPtr); }
  830.     if (resultPtr) { Tcl_DecrRefCount(resultPtr); }
  831.     return result;
  832. }
  833. /*
  834.  *----------------------------------------------------------------------
  835.  *
  836.  * Tcl_RenameObjCmd --
  837.  *
  838.  * This procedure is invoked to process the "rename" Tcl command.
  839.  * See the user documentation for details on what it does.
  840.  *
  841.  * Results:
  842.  * A standard Tcl object result.
  843.  *
  844.  * Side effects:
  845.  * See the user documentation.
  846.  *
  847.  *----------------------------------------------------------------------
  848.  */
  849. /* ARGSUSED */
  850. int
  851. Tcl_RenameObjCmd(dummy, interp, objc, objv)
  852.     ClientData dummy; /* Arbitrary value passed to the command. */
  853.     Tcl_Interp *interp; /* Current interpreter. */
  854.     int objc; /* Number of arguments. */
  855.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  856. {
  857.     char *oldName, *newName;
  858.     
  859.     if (objc != 3) {
  860. Tcl_WrongNumArgs(interp, 1, objv, "oldName newName");
  861. return TCL_ERROR;
  862.     }
  863.     oldName = Tcl_GetString(objv[1]);
  864.     newName = Tcl_GetString(objv[2]);
  865.     return TclRenameCommand(interp, oldName, newName);
  866. }
  867. /*
  868.  *----------------------------------------------------------------------
  869.  *
  870.  * Tcl_ReturnObjCmd --
  871.  *
  872.  * This object-based procedure is invoked to process the "return" Tcl
  873.  * command. See the user documentation for details on what it does.
  874.  *
  875.  * Results:
  876.  * A standard Tcl object result.
  877.  *
  878.  * Side effects:
  879.  * See the user documentation.
  880.  *
  881.  *----------------------------------------------------------------------
  882.  */
  883. /* ARGSUSED */
  884. int
  885. Tcl_ReturnObjCmd(dummy, interp, objc, objv)
  886.     ClientData dummy; /* Not used. */
  887.     Tcl_Interp *interp; /* Current interpreter. */
  888.     int objc; /* Number of arguments. */
  889.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  890. {
  891.     Interp *iPtr = (Interp *) interp;
  892.     int optionLen, argLen, code, result;
  893.     if (iPtr->errorInfo != NULL) {
  894. ckfree(iPtr->errorInfo);
  895. iPtr->errorInfo = NULL;
  896.     }
  897.     if (iPtr->errorCode != NULL) {
  898. ckfree(iPtr->errorCode);
  899. iPtr->errorCode = NULL;
  900.     }
  901.     code = TCL_OK;
  902.     
  903.     for (objv++, objc--;  objc > 1;  objv += 2, objc -= 2) {
  904. char *option = Tcl_GetStringFromObj(objv[0], &optionLen);
  905. char *arg = Tcl_GetStringFromObj(objv[1], &argLen);
  906.     
  907. if (strcmp(option, "-code") == 0) {
  908.     register int c = arg[0];
  909.     if ((c == 'o') && (strcmp(arg, "ok") == 0)) {
  910. code = TCL_OK;
  911.     } else if ((c == 'e') && (strcmp(arg, "error") == 0)) {
  912. code = TCL_ERROR;
  913.     } else if ((c == 'r') && (strcmp(arg, "return") == 0)) {
  914. code = TCL_RETURN;
  915.     } else if ((c == 'b') && (strcmp(arg, "break") == 0)) {
  916. code = TCL_BREAK;
  917.     } else if ((c == 'c') && (strcmp(arg, "continue") == 0)) {
  918. code = TCL_CONTINUE;
  919.     } else {
  920. result = Tcl_GetIntFromObj((Tcl_Interp *) NULL, objv[1],
  921.         &code);
  922. if (result != TCL_OK) {
  923.     Tcl_ResetResult(interp);
  924.     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  925.     "bad completion code "",
  926.     Tcl_GetString(objv[1]),
  927.     "": must be ok, error, return, break, ",
  928.     "continue, or an integer", (char *) NULL);
  929.     return result;
  930. }
  931.     }
  932. } else if (strcmp(option, "-errorinfo") == 0) {
  933.     iPtr->errorInfo =
  934. (char *) ckalloc((unsigned) (strlen(arg) + 1));
  935.     strcpy(iPtr->errorInfo, arg);
  936. } else if (strcmp(option, "-errorcode") == 0) {
  937.     iPtr->errorCode =
  938. (char *) ckalloc((unsigned) (strlen(arg) + 1));
  939.     strcpy(iPtr->errorCode, arg);
  940. } else {
  941.     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  942.     "bad option "", option,
  943.     "": must be -code, -errorcode, or -errorinfo",
  944.     (char *) NULL);
  945.     return TCL_ERROR;
  946. }
  947.     }
  948.     
  949.     if (objc == 1) {
  950. /*
  951.  * Set the interpreter's object result. An inline version of
  952.  * Tcl_SetObjResult.
  953.  */
  954. Tcl_SetObjResult(interp, objv[0]);
  955.     }
  956.     iPtr->returnCode = code;
  957.     return TCL_RETURN;
  958. }
  959. /*
  960.  *----------------------------------------------------------------------
  961.  *
  962.  * Tcl_SourceObjCmd --
  963.  *
  964.  * This procedure is invoked to process the "source" Tcl command.
  965.  * See the user documentation for details on what it does.
  966.  *
  967.  * Results:
  968.  * A standard Tcl object result.
  969.  *
  970.  * Side effects:
  971.  * See the user documentation.
  972.  *
  973.  *----------------------------------------------------------------------
  974.  */
  975. /* ARGSUSED */
  976. int
  977. Tcl_SourceObjCmd(dummy, interp, objc, objv)
  978.     ClientData dummy; /* Not used. */
  979.     Tcl_Interp *interp; /* Current interpreter. */
  980.     int objc; /* Number of arguments. */
  981.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  982. {
  983.     if (objc != 2) {
  984. Tcl_WrongNumArgs(interp, 1, objv, "fileName");
  985. return TCL_ERROR;
  986.     }
  987.     return Tcl_FSEvalFile(interp, objv[1]);
  988. }
  989. /*
  990.  *----------------------------------------------------------------------
  991.  *
  992.  * Tcl_SplitObjCmd --
  993.  *
  994.  * This procedure is invoked to process the "split" Tcl command.
  995.  * See the user documentation for details on what it does.
  996.  *
  997.  * Results:
  998.  * A standard Tcl result.
  999.  *
  1000.  * Side effects:
  1001.  * See the user documentation.
  1002.  *
  1003.  *----------------------------------------------------------------------
  1004.  */
  1005. /* ARGSUSED */
  1006. int
  1007. Tcl_SplitObjCmd(dummy, interp, objc, objv)
  1008.     ClientData dummy; /* Not used. */
  1009.     Tcl_Interp *interp; /* Current interpreter. */
  1010.     int objc; /* Number of arguments. */
  1011.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  1012. {
  1013.     Tcl_UniChar ch;
  1014.     int len;
  1015.     char *splitChars, *string, *end;
  1016.     int splitCharLen, stringLen;
  1017.     Tcl_Obj *listPtr, *objPtr;
  1018.     if (objc == 2) {
  1019. splitChars = " ntr";
  1020. splitCharLen = 4;
  1021.     } else if (objc == 3) {
  1022. splitChars = Tcl_GetStringFromObj(objv[2], &splitCharLen);
  1023.     } else {
  1024. Tcl_WrongNumArgs(interp, 1, objv, "string ?splitChars?");
  1025. return TCL_ERROR;
  1026.     }
  1027.     string = Tcl_GetStringFromObj(objv[1], &stringLen);
  1028.     end = string + stringLen;
  1029.     listPtr = Tcl_GetObjResult(interp);
  1030.     
  1031.     if (stringLen == 0) {
  1032. /*
  1033.  * Do nothing.
  1034.  */
  1035.     } else if (splitCharLen == 0) {
  1036. Tcl_HashTable charReuseTable;
  1037. Tcl_HashEntry *hPtr;
  1038. int isNew;
  1039. /*
  1040.  * Handle the special case of splitting on every character.
  1041.  *
  1042.  * Uses a hash table to ensure that each kind of character has
  1043.  * only one Tcl_Obj instance (multiply-referenced) in the
  1044.  * final list.  This is a *major* win when splitting on a long
  1045.  * string (especially in the megabyte range!) - DKF
  1046.  */
  1047. Tcl_InitHashTable(&charReuseTable, TCL_ONE_WORD_KEYS);
  1048. for ( ; string < end; string += len) {
  1049.     len = TclUtfToUniChar(string, &ch);
  1050.     /* Assume Tcl_UniChar is an integral type... */
  1051.     hPtr = Tcl_CreateHashEntry(&charReuseTable, (char*)0 + ch, &isNew);
  1052.     if (isNew) {
  1053. objPtr = Tcl_NewStringObj(string, len);
  1054. /* Don't need to fiddle with refcount... */
  1055. Tcl_SetHashValue(hPtr, (ClientData) objPtr);
  1056.     } else {
  1057. objPtr = (Tcl_Obj*) Tcl_GetHashValue(hPtr);
  1058.     }
  1059.     Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1060. }
  1061. Tcl_DeleteHashTable(&charReuseTable);
  1062.     } else if (splitCharLen == 1) {
  1063. char *p;
  1064. /*
  1065.  * Handle the special case of splitting on a single character.
  1066.  * This is only true for the one-char ASCII case, as one unicode
  1067.  * char is > 1 byte in length.
  1068.  */
  1069. while (*string && (p = strchr(string, (int) *splitChars)) != NULL) {
  1070.     objPtr = Tcl_NewStringObj(string, p - string);
  1071.     Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1072.     string = p + 1;
  1073. }
  1074. objPtr = Tcl_NewStringObj(string, end - string);
  1075. Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1076.     } else {
  1077. char *element, *p, *splitEnd;
  1078. int splitLen;
  1079. Tcl_UniChar splitChar;
  1080. /*
  1081.  * Normal case: split on any of a given set of characters.
  1082.  * Discard instances of the split characters.
  1083.  */
  1084. splitEnd = splitChars + splitCharLen;
  1085. for (element = string; string < end; string += len) {
  1086.     len = TclUtfToUniChar(string, &ch);
  1087.     for (p = splitChars; p < splitEnd; p += splitLen) {
  1088. splitLen = TclUtfToUniChar(p, &splitChar);
  1089. if (ch == splitChar) {
  1090.     objPtr = Tcl_NewStringObj(element, string - element);
  1091.     Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1092.     element = string + len;
  1093.     break;
  1094. }
  1095.     }
  1096. }
  1097. objPtr = Tcl_NewStringObj(element, string - element);
  1098. Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1099.     }
  1100.     return TCL_OK;
  1101. }
  1102. /*
  1103.  *----------------------------------------------------------------------
  1104.  *
  1105.  * Tcl_StringObjCmd --
  1106.  *
  1107.  * This procedure is invoked to process the "string" Tcl command.
  1108.  * See the user documentation for details on what it does.  Note
  1109.  * that this command only functions correctly on properly formed
  1110.  * Tcl UTF strings.
  1111.  *
  1112.  * Note that the primary methods here (equal, compare, match, ...)
  1113.  * have bytecode equivalents.  You will find the code for those in
  1114.  * tclExecute.c.  The code here will only be used in the non-bc
  1115.  * case (like in an 'eval').
  1116.  *
  1117.  * Results:
  1118.  * A standard Tcl result.
  1119.  *
  1120.  * Side effects:
  1121.  * See the user documentation.
  1122.  *
  1123.  *----------------------------------------------------------------------
  1124.  */
  1125. /* ARGSUSED */
  1126. int
  1127. Tcl_StringObjCmd(dummy, interp, objc, objv)
  1128.     ClientData dummy; /* Not used. */
  1129.     Tcl_Interp *interp; /* Current interpreter. */
  1130.     int objc; /* Number of arguments. */
  1131.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  1132. {
  1133.     int index, left, right;
  1134.     Tcl_Obj *resultPtr;
  1135.     char *string1, *string2;
  1136.     int length1, length2;
  1137.     static CONST char *options[] = {
  1138. "bytelength", "compare", "equal", "first",
  1139. "index", "is", "last", "length",
  1140. "map", "match", "range", "repeat",
  1141. "replace", "tolower", "toupper", "totitle",
  1142. "trim", "trimleft", "trimright",
  1143. "wordend", "wordstart", (char *) NULL
  1144.     };
  1145.     enum options {
  1146. STR_BYTELENGTH, STR_COMPARE, STR_EQUAL, STR_FIRST,
  1147. STR_INDEX, STR_IS, STR_LAST, STR_LENGTH,
  1148. STR_MAP, STR_MATCH, STR_RANGE, STR_REPEAT,
  1149. STR_REPLACE, STR_TOLOWER, STR_TOUPPER, STR_TOTITLE,
  1150. STR_TRIM, STR_TRIMLEFT, STR_TRIMRIGHT,
  1151. STR_WORDEND, STR_WORDSTART
  1152.     };   
  1153.     if (objc < 2) {
  1154.         Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
  1155. return TCL_ERROR;
  1156.     }
  1157.     
  1158.     if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0,
  1159.     &index) != TCL_OK) {
  1160. return TCL_ERROR;
  1161.     }
  1162.     resultPtr = Tcl_GetObjResult(interp);
  1163.     switch ((enum options) index) {
  1164. case STR_EQUAL:
  1165. case STR_COMPARE: {
  1166.     /*
  1167.      * Remember to keep code here in some sync with the
  1168.      * byte-compiled versions in tclExecute.c (INST_STR_EQ,
  1169.      * INST_STR_NEQ and INST_STR_CMP as well as the expr string
  1170.      * comparison in INST_EQ/INST_NEQ/INST_LT/...).
  1171.      */
  1172.     int i, match, length, nocase = 0, reqlength = -1;
  1173.     int (*strCmpFn)();
  1174.     if (objc < 4 || objc > 7) {
  1175.     str_cmp_args:
  1176.         Tcl_WrongNumArgs(interp, 2, objv,
  1177.  "?-nocase? ?-length int? string1 string2");
  1178. return TCL_ERROR;
  1179.     }
  1180.     for (i = 2; i < objc-2; i++) {
  1181. string2 = Tcl_GetStringFromObj(objv[i], &length2);
  1182. if ((length2 > 1)
  1183. && strncmp(string2, "-nocase", (size_t)length2) == 0) {
  1184.     nocase = 1;
  1185. } else if ((length2 > 1)
  1186. && strncmp(string2, "-length", (size_t)length2) == 0) {
  1187.     if (i+1 >= objc-2) {
  1188. goto str_cmp_args;
  1189.     }
  1190.     if (Tcl_GetIntFromObj(interp, objv[++i],
  1191.     &reqlength) != TCL_OK) {
  1192. return TCL_ERROR;
  1193.     }
  1194. } else {
  1195.     Tcl_AppendStringsToObj(resultPtr, "bad option "",
  1196.     string2, "": must be -nocase or -length",
  1197.     (char *) NULL);
  1198.     return TCL_ERROR;
  1199. }
  1200.     }
  1201.     /*
  1202.      * From now on, we only access the two objects at the end
  1203.      * of the argument array.
  1204.      */
  1205.     objv += objc-2;
  1206.     if ((reqlength == 0) || (objv[0] == objv[1])) {
  1207. /*
  1208.  * Alway match at 0 chars of if it is the same obj.
  1209.  */
  1210. Tcl_SetBooleanObj(resultPtr,
  1211. ((enum options) index == STR_EQUAL));
  1212. break;
  1213.     } else if (!nocase && objv[0]->typePtr == &tclByteArrayType &&
  1214.     objv[1]->typePtr == &tclByteArrayType) {
  1215. /*
  1216.  * Use binary versions of comparisons since that won't
  1217.  * cause undue type conversions and it is much faster.
  1218.  * Only do this if we're case-sensitive (which is all
  1219.  * that really makes sense with byte arrays anyway, and
  1220.  * we have no memcasecmp() for some reason... :^)
  1221.  */
  1222. string1 = (char*) Tcl_GetByteArrayFromObj(objv[0], &length1);
  1223. string2 = (char*) Tcl_GetByteArrayFromObj(objv[1], &length2);
  1224. strCmpFn = memcmp;
  1225.     } else if ((objv[0]->typePtr == &tclStringType)
  1226.     && (objv[1]->typePtr == &tclStringType)) {
  1227. /*
  1228.  * Do a unicode-specific comparison if both of the args
  1229.  * are of String type.  In benchmark testing this proved
  1230.  * the most efficient check between the unicode and
  1231.  * string comparison operations.
  1232.  */
  1233. string1 = (char*) Tcl_GetUnicodeFromObj(objv[0], &length1);
  1234. string2 = (char*) Tcl_GetUnicodeFromObj(objv[1], &length2);
  1235. strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
  1236.     } else {
  1237. /*
  1238.  * As a catch-all we will work with UTF-8.  We cannot use
  1239.  * memcmp() as that is unsafe with any string containing
  1240.  * NULL (xC0x80 in Tcl's utf rep).  We can use the more
  1241.  * efficient TclpUtfNcmp2 if we are case-sensitive and no
  1242.  * specific length was requested.
  1243.  */
  1244. string1 = (char*) Tcl_GetStringFromObj(objv[0], &length1);
  1245. string2 = (char*) Tcl_GetStringFromObj(objv[1], &length2);
  1246. if ((reqlength < 0) && !nocase) {
  1247.     strCmpFn = TclpUtfNcmp2;
  1248. } else {
  1249.     length1 = Tcl_NumUtfChars(string1, length1);
  1250.     length2 = Tcl_NumUtfChars(string2, length2);
  1251.     strCmpFn = nocase ? Tcl_UtfNcasecmp : Tcl_UtfNcmp;
  1252. }
  1253.     }
  1254.     if (((enum options) index == STR_EQUAL)
  1255.     && (reqlength < 0) && (length1 != length2)) {
  1256. match = 1; /* this will be reversed below */
  1257.     } else {
  1258. length = (length1 < length2) ? length1 : length2;
  1259. if (reqlength > 0 && reqlength < length) {
  1260.     length = reqlength;
  1261. } else if (reqlength < 0) {
  1262.     /*
  1263.      * The requested length is negative, so we ignore it by
  1264.      * setting it to length + 1 so we correct the match var.
  1265.      */
  1266.     reqlength = length + 1;
  1267. }
  1268. match = strCmpFn(string1, string2, (unsigned) length);
  1269. if ((match == 0) && (reqlength > length)) {
  1270.     match = length1 - length2;
  1271. }
  1272.     }
  1273.     if ((enum options) index == STR_EQUAL) {
  1274. Tcl_SetBooleanObj(resultPtr, (match) ? 0 : 1);
  1275.     } else {
  1276. Tcl_SetIntObj(resultPtr, ((match > 0) ? 1 :
  1277.   (match < 0) ? -1 : 0));
  1278.     }
  1279.     break;
  1280. }
  1281. case STR_FIRST: {
  1282.     Tcl_UniChar *ustring1, *ustring2;
  1283.     int match, start;
  1284.     if (objc < 4 || objc > 5) {
  1285.         Tcl_WrongNumArgs(interp, 2, objv,
  1286.  "subString string ?startIndex?");
  1287. return TCL_ERROR;
  1288.     }
  1289.     /*
  1290.      * We are searching string2 for the sequence string1.
  1291.      */
  1292.     match = -1;
  1293.     start = 0;
  1294.     length2 = -1;
  1295.     ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
  1296.     ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);
  1297.     if (objc == 5) {
  1298. /*
  1299.  * If a startIndex is specified, we will need to fast
  1300.  * forward to that point in the string before we think
  1301.  * about a match
  1302.  */
  1303. if (TclGetIntForIndex(interp, objv[4], length2 - 1,
  1304. &start) != TCL_OK) {
  1305.     return TCL_ERROR;
  1306. }
  1307. if (start >= length2) {
  1308.     goto str_first_done;
  1309. } else if (start > 0) {
  1310.     ustring2 += start;
  1311.     length2  -= start;
  1312. } else if (start < 0) {
  1313.     /*
  1314.      * Invalid start index mapped to string start;
  1315.      * Bug #423581
  1316.      */
  1317.     start = 0;
  1318. }
  1319.     }
  1320.     if (length1 > 0) {
  1321. register Tcl_UniChar *p, *end;
  1322. end = ustring2 + length2 - length1 + 1;
  1323. for (p = ustring2;  p < end;  p++) {
  1324.     /*
  1325.      * Scan forward to find the first character.
  1326.      */
  1327.     if ((*p == *ustring1) &&
  1328.     (TclUniCharNcmp(ustring1, p,
  1329.     (unsigned long) length1) == 0)) {
  1330. match = p - ustring2;
  1331. break;
  1332.     }
  1333. }
  1334.     }
  1335.     /*
  1336.      * Compute the character index of the matching string by
  1337.      * counting the number of characters before the match.
  1338.      */
  1339.     if ((match != -1) && (objc == 5)) {
  1340. match += start;
  1341.     }
  1342.     str_first_done:
  1343.     Tcl_SetIntObj(resultPtr, match);
  1344.     break;
  1345. }
  1346. case STR_INDEX: {
  1347.     if (objc != 4) {
  1348.         Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");
  1349. return TCL_ERROR;
  1350.     }
  1351.     /*
  1352.      * If we have a ByteArray object, avoid indexing in the
  1353.      * Utf string since the byte array contains one byte per
  1354.      * character.  Otherwise, use the Unicode string rep to
  1355.      * get the index'th char.
  1356.      */
  1357.     if (objv[2]->typePtr == &tclByteArrayType) {
  1358. string1 = (char *) Tcl_GetByteArrayFromObj(objv[2], &length1);
  1359. if (TclGetIntForIndex(interp, objv[3], length1 - 1,
  1360. &index) != TCL_OK) {
  1361.     return TCL_ERROR;
  1362. }
  1363. if ((index >= 0) && (index < length1)) {
  1364.     Tcl_SetByteArrayObj(resultPtr,
  1365.     (unsigned char *)(&string1[index]), 1);
  1366. }
  1367.     } else {
  1368. /*
  1369.  * Get Unicode char length to calulate what 'end' means.
  1370.  */
  1371. length1 = Tcl_GetCharLength(objv[2]);
  1372. if (TclGetIntForIndex(interp, objv[3], length1 - 1,
  1373. &index) != TCL_OK) {
  1374.     return TCL_ERROR;
  1375. }
  1376. if ((index >= 0) && (index < length1)) {
  1377.     char buf[TCL_UTF_MAX];
  1378.     Tcl_UniChar ch;
  1379.     ch      = Tcl_GetUniChar(objv[2], index);
  1380.     length1 = Tcl_UniCharToUtf(ch, buf);
  1381.     Tcl_SetStringObj(resultPtr, buf, length1);
  1382. }
  1383.     }
  1384.     break;
  1385. }
  1386. case STR_IS: {
  1387.     char *end;
  1388.     Tcl_UniChar ch;
  1389.             /*
  1390.      * The UniChar comparison function
  1391.      */
  1392.     int (*chcomp)_ANSI_ARGS_((int)) = NULL; 
  1393.     int i, failat = 0, result = 1, strict = 0;
  1394.     Tcl_Obj *objPtr, *failVarObj = NULL;
  1395.     static CONST char *isOptions[] = {
  1396. "alnum", "alpha", "ascii", "control",
  1397. "boolean", "digit", "double", "false",
  1398. "graph", "integer", "lower", "print",
  1399. "punct", "space", "true", "upper",
  1400. "wordchar", "xdigit", (char *) NULL
  1401.     };
  1402.     enum isOptions {
  1403. STR_IS_ALNUM, STR_IS_ALPHA, STR_IS_ASCII, STR_IS_CONTROL,
  1404. STR_IS_BOOL, STR_IS_DIGIT, STR_IS_DOUBLE, STR_IS_FALSE,
  1405. STR_IS_GRAPH, STR_IS_INT, STR_IS_LOWER, STR_IS_PRINT,
  1406. STR_IS_PUNCT, STR_IS_SPACE, STR_IS_TRUE, STR_IS_UPPER,
  1407. STR_IS_WORD, STR_IS_XDIGIT
  1408.     };
  1409.     if (objc < 4 || objc > 7) {
  1410. Tcl_WrongNumArgs(interp, 2, objv,
  1411.  "class ?-strict? ?-failindex var? str");
  1412. return TCL_ERROR;
  1413.     }
  1414.     if (Tcl_GetIndexFromObj(interp, objv[2], isOptions, "class", 0,
  1415.     &index) != TCL_OK) {
  1416. return TCL_ERROR;
  1417.     }
  1418.     if (objc != 4) {
  1419. for (i = 3; i < objc-1; i++) {
  1420.     string2 = Tcl_GetStringFromObj(objv[i], &length2);
  1421.     if ((length2 > 1) &&
  1422. strncmp(string2, "-strict", (size_t) length2) == 0) {
  1423. strict = 1;
  1424.     } else if ((length2 > 1) &&
  1425.     strncmp(string2, "-failindex",
  1426.     (size_t) length2) == 0) {
  1427. if (i+1 >= objc-1) {
  1428.     Tcl_WrongNumArgs(interp, 3, objv,
  1429.      "?-strict? ?-failindex var? str");
  1430.     return TCL_ERROR;
  1431. }
  1432. failVarObj = objv[++i];
  1433.     } else {
  1434. Tcl_AppendStringsToObj(resultPtr, "bad option "",
  1435. string2, "": must be -strict or -failindex",
  1436. (char *) NULL);
  1437. return TCL_ERROR;
  1438.     }
  1439. }
  1440.     }
  1441.     /*
  1442.      * We get the objPtr so that we can short-cut for some classes
  1443.      * by checking the object type (int and double), but we need
  1444.      * the string otherwise, because we don't want any conversion
  1445.      * of type occuring (as, for example, Tcl_Get*FromObj would do
  1446.      */
  1447.     objPtr = objv[objc-1];
  1448.     string1 = Tcl_GetStringFromObj(objPtr, &length1);
  1449.     if (length1 == 0) {
  1450. if (strict) {
  1451.     result = 0;
  1452. }
  1453. goto str_is_done;
  1454.     }
  1455.     end = string1 + length1;
  1456.     /*
  1457.      * When entering here, result == 1 and failat == 0
  1458.      */
  1459.     switch ((enum isOptions) index) {
  1460. case STR_IS_ALNUM:
  1461.     chcomp = Tcl_UniCharIsAlnum;
  1462.     break;
  1463. case STR_IS_ALPHA:
  1464.     chcomp = Tcl_UniCharIsAlpha;
  1465.     break;
  1466. case STR_IS_ASCII:
  1467.     for (; string1 < end; string1++, failat++) {
  1468. /*
  1469.  * This is a valid check in unicode, because all
  1470.  * bytes < 0xC0 are single byte chars (but isascii
  1471.  * limits that def'n to 0x80).
  1472.  */
  1473. if (*((unsigned char *)string1) >= 0x80) {
  1474.     result = 0;
  1475.     break;
  1476. }
  1477.     }
  1478.     break;
  1479. case STR_IS_BOOL:
  1480. case STR_IS_TRUE:
  1481. case STR_IS_FALSE:
  1482.     /* Optimizers, beware Bug 1187123 ! */
  1483.     if ((Tcl_GetBoolean(NULL, string1, &i)
  1484. == TCL_ERROR) ||
  1485.        (((enum isOptions) index == STR_IS_TRUE) &&
  1486. i == 0) ||
  1487.        (((enum isOptions) index == STR_IS_FALSE) &&
  1488. i != 0)) {
  1489. result = 0;
  1490.     }
  1491.     break;
  1492. case STR_IS_CONTROL:
  1493.     chcomp = Tcl_UniCharIsControl;
  1494.     break;
  1495. case STR_IS_DIGIT:
  1496.     chcomp = Tcl_UniCharIsDigit;
  1497.     break;
  1498. case STR_IS_DOUBLE: {
  1499.     char *stop;
  1500.     if ((objPtr->typePtr == &tclDoubleType) ||
  1501. (objPtr->typePtr == &tclIntType)) {
  1502. break;
  1503.     }
  1504.     /*
  1505.      * This is adapted from Tcl_GetDouble
  1506.      *
  1507.      * The danger in this function is that
  1508.      * "12345678901234567890" is an acceptable 'double',
  1509.      * but will later be interp'd as an int by something
  1510.      * like [expr].  Therefore, we check to see if it looks
  1511.      * like an int, and if so we do a range check on it.
  1512.      * If strtoul gets to the end, we know we either
  1513.      * received an acceptable int, or over/underflow
  1514.      */
  1515.     if (TclLooksLikeInt(string1, length1)) {
  1516. errno = 0;
  1517. #ifdef TCL_WIDE_INT_IS_LONG
  1518. strtoul(string1, &stop, 0); /* INTL: Tcl source. */
  1519. #else
  1520. strtoull(string1, &stop, 0); /* INTL: Tcl source. */
  1521. #endif
  1522. if (stop == end) {
  1523.     if (errno == ERANGE) {
  1524. result = 0;
  1525. failat = -1;
  1526.     }
  1527.     break;
  1528. }
  1529.     }
  1530.     errno = 0;
  1531.     strtod(string1, &stop); /* INTL: Tcl source. */
  1532.     if (errno == ERANGE) {
  1533. /*
  1534.  * if (errno == ERANGE), then it was an over/underflow
  1535.  * problem, but in this method, we only want to know
  1536.  * yes or no, so bad flow returns 0 (false) and sets
  1537.  * the failVarObj to the string length.
  1538.  */
  1539. result = 0;
  1540. failat = -1;
  1541.     } else if (stop == string1) {
  1542. /*
  1543.  * In this case, nothing like a number was found
  1544.  */
  1545. result = 0;
  1546. failat = 0;
  1547.     } else {
  1548. /*
  1549.  * Assume we sucked up one char per byte
  1550.  * and then we go onto SPACE, since we are
  1551.  * allowed trailing whitespace
  1552.  */
  1553. failat = stop - string1;
  1554. string1 = stop;
  1555. chcomp = Tcl_UniCharIsSpace;
  1556.     }
  1557.     break;
  1558. }
  1559. case STR_IS_GRAPH:
  1560.     chcomp = Tcl_UniCharIsGraph;
  1561.     break;
  1562. case STR_IS_INT: {
  1563.     char *stop;
  1564.     long int l = 0;
  1565.     if (TCL_OK == Tcl_GetIntFromObj(NULL, objPtr, &i)) {
  1566. break;
  1567.     }
  1568.     /*
  1569.      * Like STR_IS_DOUBLE, but we use strtoul.
  1570.      * Since Tcl_GetIntFromObj already failed,
  1571.      * we set result to 0.
  1572.      */
  1573.     result = 0;
  1574.     errno = 0;
  1575.     l = strtol(string1, &stop, 0); /* INTL: Tcl source. */
  1576.     if ((errno == ERANGE) || (l > INT_MAX) || (l < INT_MIN)) {
  1577. /*
  1578.  * if (errno == ERANGE), then it was an over/underflow
  1579.  * problem, but in this method, we only want to know
  1580.  * yes or no, so bad flow returns 0 (false) and sets
  1581.  * the failVarObj to the string length.
  1582.  */
  1583. failat = -1;
  1584.     } else if (stop == string1) {
  1585. /*
  1586.  * In this case, nothing like a number was found
  1587.  */
  1588. failat = 0;
  1589.     } else {
  1590. /*
  1591.  * Assume we sucked up one char per byte
  1592.  * and then we go onto SPACE, since we are
  1593.  * allowed trailing whitespace
  1594.  */
  1595. failat = stop - string1;
  1596. string1 = stop;
  1597. chcomp = Tcl_UniCharIsSpace;
  1598.     }
  1599.     break;
  1600. }
  1601. case STR_IS_LOWER:
  1602.     chcomp = Tcl_UniCharIsLower;
  1603.     break;
  1604. case STR_IS_PRINT:
  1605.     chcomp = Tcl_UniCharIsPrint;
  1606.     break;
  1607. case STR_IS_PUNCT:
  1608.     chcomp = Tcl_UniCharIsPunct;
  1609.     break;
  1610. case STR_IS_SPACE:
  1611.     chcomp = Tcl_UniCharIsSpace;
  1612.     break;
  1613. case STR_IS_UPPER:
  1614.     chcomp = Tcl_UniCharIsUpper;
  1615.     break;
  1616. case STR_IS_WORD:
  1617.     chcomp = Tcl_UniCharIsWordChar;
  1618.     break;
  1619. case STR_IS_XDIGIT: {
  1620.     for (; string1 < end; string1++, failat++) {
  1621. /* INTL: We assume unicode is bad for this class */
  1622. if ((*((unsigned char *)string1) >= 0xC0) ||
  1623.     !isxdigit(*(unsigned char *)string1)) {
  1624.     result = 0;
  1625.     break;
  1626. }
  1627.     }
  1628.     break;
  1629. }
  1630.     }
  1631.     if (chcomp != NULL) {
  1632. for (; string1 < end; string1 += length2, failat++) {
  1633.     length2 = TclUtfToUniChar(string1, &ch);
  1634.     if (!chcomp(ch)) {
  1635. result = 0;
  1636. break;
  1637.     }
  1638. }
  1639.     }
  1640. str_is_done:
  1641.     /*
  1642.      * Only set the failVarObj when we will return 0
  1643.      * and we have indicated a valid fail index (>= 0)
  1644.      */
  1645.     if ((result == 0) && (failVarObj != NULL)) {
  1646. Tcl_Obj *resPtr, *tmpPtr = Tcl_NewIntObj(failat);
  1647. Tcl_IncrRefCount(tmpPtr);
  1648. resPtr = Tcl_ObjSetVar2(interp, failVarObj, NULL, tmpPtr,
  1649. TCL_LEAVE_ERR_MSG);
  1650. Tcl_DecrRefCount(tmpPtr);
  1651. if (resPtr == NULL) {
  1652.     return TCL_ERROR;
  1653. }
  1654.     }
  1655.     Tcl_SetBooleanObj(resultPtr, result);
  1656.     break;
  1657. }
  1658. case STR_LAST: {
  1659.     Tcl_UniChar *ustring1, *ustring2, *p;
  1660.     int match, start;
  1661.     if (objc < 4 || objc > 5) {
  1662.         Tcl_WrongNumArgs(interp, 2, objv,
  1663.  "subString string ?startIndex?");
  1664. return TCL_ERROR;
  1665.     }
  1666.     /*
  1667.      * We are searching string2 for the sequence string1.
  1668.      */
  1669.     match = -1;
  1670.     start = 0;
  1671.     length2 = -1;
  1672.     ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
  1673.     ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);
  1674.     if (objc == 5) {
  1675. /*
  1676.  * If a startIndex is specified, we will need to restrict
  1677.  * the string range to that char index in the string
  1678.  */
  1679. if (TclGetIntForIndex(interp, objv[4], length2 - 1,
  1680. &start) != TCL_OK) {
  1681.     return TCL_ERROR;
  1682. }
  1683. if (start < 0) {
  1684.     goto str_last_done;
  1685. } else if (start < length2) {
  1686.     p = ustring2 + start + 1 - length1;
  1687. } else {
  1688.     p = ustring2 + length2 - length1;
  1689. }
  1690.     } else {
  1691. p = ustring2 + length2 - length1;
  1692.     }
  1693.     if (length1 > 0) {
  1694. for (; p >= ustring2;  p--) {
  1695.     /*
  1696.      * Scan backwards to find the first character.
  1697.      */
  1698.     if ((*p == *ustring1) &&
  1699.     (memcmp((char *) ustring1, (char *) p, (size_t)
  1700.     (length1 * sizeof(Tcl_UniChar))) == 0)) {
  1701. match = p - ustring2;
  1702. break;
  1703.     }
  1704. }
  1705.     }
  1706.     str_last_done:
  1707.     Tcl_SetIntObj(resultPtr, match);
  1708.     break;
  1709. }
  1710. case STR_BYTELENGTH:
  1711. case STR_LENGTH: {
  1712.     if (objc != 3) {
  1713.         Tcl_WrongNumArgs(interp, 2, objv, "string");
  1714. return TCL_ERROR;
  1715.     }
  1716.     if ((enum options) index == STR_BYTELENGTH) {
  1717. (void) Tcl_GetStringFromObj(objv[2], &length1);
  1718.     } else {
  1719. /*
  1720.  * If we have a ByteArray object, avoid recomputing the
  1721.  * string since the byte array contains one byte per
  1722.  * character.  Otherwise, use the Unicode string rep to
  1723.  * calculate the length.
  1724.  */
  1725. if (objv[2]->typePtr == &tclByteArrayType) {
  1726.     (void) Tcl_GetByteArrayFromObj(objv[2], &length1);
  1727. } else {
  1728.     length1 = Tcl_GetCharLength(objv[2]);
  1729. }
  1730.     }
  1731.     Tcl_SetIntObj(resultPtr, length1);
  1732.     break;
  1733. }
  1734. case STR_MAP: {
  1735.     int mapElemc, nocase = 0, copySource = 0;
  1736.     Tcl_Obj **mapElemv, *sourceObj;
  1737.     Tcl_UniChar *ustring1, *ustring2, *p, *end;
  1738.     int (*strCmpFn)_ANSI_ARGS_((CONST Tcl_UniChar*,
  1739. CONST Tcl_UniChar*, unsigned long));
  1740.     if (objc < 4 || objc > 5) {
  1741.         Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? charMap string");
  1742. return TCL_ERROR;
  1743.     }
  1744.     if (objc == 5) {
  1745. string2 = Tcl_GetStringFromObj(objv[2], &length2);
  1746. if ((length2 > 1) &&
  1747.     strncmp(string2, "-nocase", (size_t) length2) == 0) {
  1748.     nocase = 1;
  1749. } else {
  1750.     Tcl_AppendStringsToObj(resultPtr, "bad option "",
  1751.    string2, "": must be -nocase",
  1752.    (char *) NULL);
  1753.     return TCL_ERROR;
  1754. }
  1755.     }
  1756.     if (Tcl_ListObjGetElements(interp, objv[objc-2], &mapElemc,
  1757.        &mapElemv) != TCL_OK) {
  1758. return TCL_ERROR;
  1759.     }
  1760.     if (mapElemc == 0) {
  1761. /*
  1762.  * empty charMap, just return whatever string was given
  1763.  */
  1764. Tcl_SetObjResult(interp, objv[objc-1]);
  1765. return TCL_OK;
  1766.     } else if (mapElemc & 1) {
  1767. /*
  1768.  * The charMap must be an even number of key/value items
  1769.  */
  1770. Tcl_SetStringObj(resultPtr, "char map list unbalanced", -1);
  1771. return TCL_ERROR;
  1772.     }
  1773.     /*
  1774.      * Take a copy of the source string object if it is the
  1775.      * same as the map string to cut out nasty sharing
  1776.      * crashes. [Bug 1018562]
  1777.      */
  1778.     if (objv[objc-2] == objv[objc-1]) {
  1779. sourceObj = Tcl_DuplicateObj(objv[objc-1]);
  1780. copySource = 1;
  1781.     } else {
  1782. sourceObj = objv[objc-1];
  1783.     }
  1784.     ustring1 = Tcl_GetUnicodeFromObj(sourceObj, &length1);
  1785.     if (length1 == 0) {
  1786. /*
  1787.  * Empty input string, just stop now
  1788.  */
  1789. if (copySource) {
  1790.     Tcl_DecrRefCount(sourceObj);
  1791. }
  1792. break;
  1793.     }
  1794.     end = ustring1 + length1;
  1795.     strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
  1796.     /*
  1797.      * Force result to be Unicode
  1798.      */
  1799.     Tcl_SetUnicodeObj(resultPtr, ustring1, 0);
  1800.     if (mapElemc == 2) {
  1801. /*
  1802.  * Special case for one map pair which avoids the extra
  1803.  * for loop and extra calls to get Unicode data.  The
  1804.  * algorithm is otherwise identical to the multi-pair case.
  1805.  * This will be >30% faster on larger strings.
  1806.  */
  1807. int mapLen;
  1808. Tcl_UniChar *mapString, u2lc;
  1809. ustring2 = Tcl_GetUnicodeFromObj(mapElemv[0], &length2);
  1810. p = ustring1;
  1811. if ((length2 > length1) || (length2 == 0)) {
  1812.     /* match string is either longer than input or empty */
  1813.     ustring1 = end;
  1814. } else {
  1815.     mapString = Tcl_GetUnicodeFromObj(mapElemv[1], &mapLen);
  1816.     u2lc = (nocase ? Tcl_UniCharToLower(*ustring2) : 0);
  1817.     for (; ustring1 < end; ustring1++) {
  1818. if (((*ustring1 == *ustring2) ||
  1819. (nocase && (Tcl_UniCharToLower(*ustring1) ==
  1820. u2lc))) &&
  1821. ((length2 == 1) || strCmpFn(ustring1, ustring2,
  1822. (unsigned long) length2) == 0)) {
  1823.     if (p != ustring1) {
  1824. Tcl_AppendUnicodeToObj(resultPtr, p,
  1825. ustring1 - p);
  1826. p = ustring1 + length2;
  1827.     } else {
  1828. p += length2;
  1829.     }
  1830.     ustring1 = p - 1;
  1831.     Tcl_AppendUnicodeToObj(resultPtr, mapString,
  1832.     mapLen);
  1833. }
  1834.     }
  1835. }
  1836.     } else {
  1837. Tcl_UniChar **mapStrings, *u2lc = NULL;
  1838. int *mapLens;
  1839. /*
  1840.  * Precompute pointers to the unicode string and length.
  1841.  * This saves us repeated function calls later,
  1842.  * significantly speeding up the algorithm.  We only need
  1843.  * the lowercase first char in the nocase case.
  1844.  */
  1845. mapStrings = (Tcl_UniChar **) ckalloc((mapElemc * 2)
  1846. * sizeof(Tcl_UniChar *));
  1847. mapLens = (int *) ckalloc((mapElemc * 2) * sizeof(int));
  1848. if (nocase) {
  1849.     u2lc = (Tcl_UniChar *)
  1850. ckalloc((mapElemc) * sizeof(Tcl_UniChar));
  1851. }
  1852. for (index = 0; index < mapElemc; index++) {
  1853.     mapStrings[index] = Tcl_GetUnicodeFromObj(mapElemv[index],
  1854.     &(mapLens[index]));
  1855.     if (nocase && ((index % 2) == 0)) {
  1856. u2lc[index/2] = Tcl_UniCharToLower(*mapStrings[index]);
  1857.     }
  1858. }
  1859. for (p = ustring1; ustring1 < end; ustring1++) {
  1860.     for (index = 0; index < mapElemc; index += 2) {
  1861. /*
  1862.  * Get the key string to match on.
  1863.  */
  1864. ustring2 = mapStrings[index];
  1865. length2  = mapLens[index];
  1866. if ((length2 > 0) && ((*ustring1 == *ustring2) ||
  1867. (nocase && (Tcl_UniCharToLower(*ustring1) ==
  1868. u2lc[index/2]))) &&
  1869. /* restrict max compare length */
  1870. ((end - ustring1) >= length2) &&
  1871. ((length2 == 1) || strCmpFn(ustring2, ustring1,
  1872. (unsigned long) length2) == 0)) {
  1873.     if (p != ustring1) {
  1874. /*
  1875.  * Put the skipped chars onto the result first
  1876.  */
  1877. Tcl_AppendUnicodeToObj(resultPtr, p,
  1878. ustring1 - p);
  1879. p = ustring1 + length2;
  1880.     } else {
  1881. p += length2;
  1882.     }
  1883.     /*
  1884.      * Adjust len to be full length of matched string
  1885.      */
  1886.     ustring1 = p - 1;
  1887.     /*
  1888.      * Append the map value to the unicode string
  1889.      */
  1890.     Tcl_AppendUnicodeToObj(resultPtr,
  1891.     mapStrings[index+1], mapLens[index+1]);
  1892.     break;
  1893. }
  1894.     }
  1895. }
  1896. ckfree((char *) mapStrings);
  1897. ckfree((char *) mapLens);
  1898. if (nocase) {
  1899.     ckfree((char *) u2lc);
  1900. }
  1901.     }
  1902.     if (p != ustring1) {
  1903. /*
  1904.  * Put the rest of the unmapped chars onto result
  1905.  */
  1906. Tcl_AppendUnicodeToObj(resultPtr, p, ustring1 - p);
  1907.     }
  1908.     if (copySource) {
  1909. Tcl_DecrRefCount(sourceObj);
  1910.     }
  1911.     break;
  1912. }
  1913. case STR_MATCH: {
  1914.     Tcl_UniChar *ustring1, *ustring2;
  1915.     int nocase = 0;
  1916.     if (objc < 4 || objc > 5) {
  1917.         Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? pattern string");
  1918. return TCL_ERROR;
  1919.     }
  1920.     if (objc == 5) {
  1921. string2 = Tcl_GetStringFromObj(objv[2], &length2);
  1922. if ((length2 > 1) &&
  1923.     strncmp(string2, "-nocase", (size_t) length2) == 0) {
  1924.     nocase = 1;
  1925. } else {
  1926.     Tcl_AppendStringsToObj(resultPtr, "bad option "",
  1927.    string2, "": must be -nocase",
  1928.    (char *) NULL);
  1929.     return TCL_ERROR;
  1930. }
  1931.     }
  1932.     ustring1 = Tcl_GetUnicodeFromObj(objv[objc-1], &length1);
  1933.     ustring2 = Tcl_GetUnicodeFromObj(objv[objc-2], &length2);
  1934.     Tcl_SetBooleanObj(resultPtr, TclUniCharMatch(ustring1, length1,
  1935.     ustring2, length2, nocase));
  1936.     break;
  1937. }
  1938. case STR_RANGE: {
  1939.     int first, last;
  1940.     if (objc != 5) {
  1941.         Tcl_WrongNumArgs(interp, 2, objv, "string first last");
  1942. return TCL_ERROR;
  1943.     }
  1944.     /*
  1945.      * If we have a ByteArray object, avoid indexing in the
  1946.      * Utf string since the byte array contains one byte per
  1947.      * character.  Otherwise, use the Unicode string rep to
  1948.      * get the range.
  1949.      */
  1950.     if (objv[2]->typePtr == &tclByteArrayType) {
  1951. string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);
  1952. length1--;
  1953.     } else {
  1954. /*
  1955.  * Get the length in actual characters.
  1956.  */
  1957. string1 = NULL;
  1958. length1 = Tcl_GetCharLength(objv[2]) - 1;
  1959.     }
  1960.     if ((TclGetIntForIndex(interp, objv[3], length1, &first) != TCL_OK)
  1961.     || (TclGetIntForIndex(interp, objv[4], length1,
  1962.     &last) != TCL_OK)) {
  1963. return TCL_ERROR;
  1964.     }
  1965.     if (first < 0) {
  1966. first = 0;
  1967.     }
  1968.     if (last >= length1) {
  1969. last = length1;
  1970.     }
  1971.     if (last >= first) {
  1972. if (string1 != NULL) {
  1973.     int numBytes = last - first + 1;
  1974.     resultPtr = Tcl_NewByteArrayObj(
  1975. (unsigned char *) &string1[first], numBytes);
  1976.     Tcl_SetObjResult(interp, resultPtr);
  1977. } else {
  1978.     Tcl_SetObjResult(interp,
  1979.     Tcl_GetRange(objv[2], first, last));
  1980. }
  1981.     }
  1982.     break;
  1983. }
  1984. case STR_REPEAT: {
  1985.     int count;
  1986.     if (objc != 4) {
  1987. Tcl_WrongNumArgs(interp, 2, objv, "string count");
  1988. return TCL_ERROR;
  1989.     }
  1990.     if (Tcl_GetIntFromObj(interp, objv[3], &count) != TCL_OK) {
  1991. return TCL_ERROR;
  1992.     }
  1993.     if (count == 1) {
  1994. Tcl_SetObjResult(interp, objv[2]);
  1995.     } else if (count > 1) {
  1996. string1 = Tcl_GetStringFromObj(objv[2], &length1);
  1997. if (length1 > 0) {
  1998.     /*
  1999.      * Only build up a string that has data.  Instead of
  2000.      * building it up with repeated appends, we just allocate
  2001.      * the necessary space once and copy the string value in.
  2002.      * Check for overflow with back-division. [Bug #714106]
  2003.      */
  2004.     length2 = length1 * count;
  2005.     if ((length2 / count) != length1) {
  2006. char buf[TCL_INTEGER_SPACE+1];
  2007. sprintf(buf, "%d", INT_MAX);
  2008. Tcl_AppendStringsToObj(resultPtr,
  2009. "string size overflow, must be less than ",
  2010. buf, (char *) NULL);
  2011. return TCL_ERROR;
  2012.     }
  2013.     /*
  2014.      * Include space for the NULL
  2015.      */
  2016.     string2 = (char *) ckalloc((size_t) length2+1);
  2017.     for (index = 0; index < count; index++) {
  2018. memcpy(string2 + (length1 * index), string1,
  2019. (size_t) length1);
  2020.     }
  2021.     string2[length2] = '';
  2022.     /*
  2023.      * We have to directly assign this instead of using
  2024.      * Tcl_SetStringObj (and indirectly TclInitStringRep)
  2025.      * because that makes another copy of the data.
  2026.      */
  2027.     resultPtr = Tcl_NewObj();
  2028.     resultPtr->bytes = string2;
  2029.     resultPtr->length = length2;
  2030.     Tcl_SetObjResult(interp, resultPtr);
  2031. }
  2032.     }
  2033.     break;
  2034. }
  2035. case STR_REPLACE: {
  2036.     Tcl_UniChar *ustring1;
  2037.     int first, last;
  2038.     if (objc < 5 || objc > 6) {
  2039.         Tcl_WrongNumArgs(interp, 2, objv,
  2040.  "string first last ?string?");
  2041. return TCL_ERROR;
  2042.     }
  2043.     ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
  2044.     length1--;
  2045.     if ((TclGetIntForIndex(interp, objv[3], length1, &first) != TCL_OK)
  2046.     || (TclGetIntForIndex(interp, objv[4], length1,
  2047.     &last) != TCL_OK)) {
  2048. return TCL_ERROR;
  2049.     }
  2050.     if ((last < first) || (last < 0) || (first > length1)) {
  2051. Tcl_SetObjResult(interp, objv[2]);
  2052.     } else {
  2053. if (first < 0) {
  2054.     first = 0;
  2055. }
  2056. Tcl_SetUnicodeObj(resultPtr, ustring1, first);
  2057. if (objc == 6) {
  2058.     Tcl_AppendObjToObj(resultPtr, objv[5]);
  2059. }
  2060. if (last < length1) {
  2061.     Tcl_AppendUnicodeToObj(resultPtr, ustring1 + last + 1,
  2062.     length1 - last);
  2063. }
  2064.     }
  2065.     break;
  2066. }
  2067. case STR_TOLOWER:
  2068. case STR_TOUPPER:
  2069. case STR_TOTITLE:
  2070.     if (objc < 3 || objc > 5) {
  2071.         Tcl_WrongNumArgs(interp, 2, objv, "string ?first? ?last?");
  2072. return TCL_ERROR;
  2073.     }
  2074.     string1 = Tcl_GetStringFromObj(objv[2], &length1);
  2075.     if (objc == 3) {
  2076. /*
  2077.  * Since the result object is not a shared object, it is
  2078.  * safe to copy the string into the result and do the
  2079.  * conversion in place.  The conversion may change the length
  2080.  * of the string, so reset the length after conversion.
  2081.  */
  2082. Tcl_SetStringObj(resultPtr, string1, length1);
  2083. if ((enum options) index == STR_TOLOWER) {
  2084.     length1 = Tcl_UtfToLower(Tcl_GetString(resultPtr));
  2085. } else if ((enum options) index == STR_TOUPPER) {
  2086.     length1 = Tcl_UtfToUpper(Tcl_GetString(resultPtr));
  2087. } else {
  2088.     length1 = Tcl_UtfToTitle(Tcl_GetString(resultPtr));
  2089. }
  2090. Tcl_SetObjLength(resultPtr, length1);
  2091.     } else {
  2092. int first, last;
  2093. CONST char *start, *end;
  2094. length1 = Tcl_NumUtfChars(string1, length1) - 1;
  2095. if (TclGetIntForIndex(interp, objv[3], length1,
  2096.       &first) != TCL_OK) {
  2097.     return TCL_ERROR;
  2098. }
  2099. if (first < 0) {
  2100.     first = 0;
  2101. }
  2102. last = first;
  2103. if ((objc == 5) && (TclGetIntForIndex(interp, objv[4], length1,
  2104.       &last) != TCL_OK)) {
  2105.     return TCL_ERROR;
  2106. }
  2107. if (last >= length1) {
  2108.     last = length1;
  2109. }
  2110. if (last < first) {
  2111.     Tcl_SetObjResult(interp, objv[2]);
  2112.     break;
  2113. }
  2114. start = Tcl_UtfAtIndex(string1, first);
  2115. end = Tcl_UtfAtIndex(start, last - first + 1);
  2116. length2 = end-start;
  2117. string2 = ckalloc((size_t) length2+1);
  2118. memcpy(string2, start, (size_t) length2);
  2119. string2[length2] = '';
  2120. if ((enum options) index == STR_TOLOWER) {
  2121.     length2 = Tcl_UtfToLower(string2);
  2122. } else if ((enum options) index == STR_TOUPPER) {
  2123.     length2 = Tcl_UtfToUpper(string2);
  2124. } else {
  2125.     length2 = Tcl_UtfToTitle(string2);
  2126. }
  2127. Tcl_SetStringObj(resultPtr, string1, start - string1);
  2128. Tcl_AppendToObj(resultPtr, string2, length2);
  2129. Tcl_AppendToObj(resultPtr, end, -1);
  2130. ckfree(string2);
  2131.     }
  2132.     break;
  2133. case STR_TRIM: {
  2134.     Tcl_UniChar ch, trim;
  2135.     register CONST char *p, *end;
  2136.     char *check, *checkEnd;
  2137.     int offset;
  2138.     left = 1;
  2139.     right = 1;
  2140.     dotrim:
  2141.     if (objc == 4) {
  2142. string2 = Tcl_GetStringFromObj(objv[3], &length2);
  2143.     } else if (objc == 3) {
  2144. string2 = " tnr";
  2145. length2 = strlen(string2);
  2146.     } else {
  2147.         Tcl_WrongNumArgs(interp, 2, objv, "string ?chars?");
  2148. return TCL_ERROR;
  2149.     }
  2150.     string1 = Tcl_GetStringFromObj(objv[2], &length1);
  2151.     checkEnd = string2 + length2;
  2152.     if (left) {
  2153. end = string1 + length1;
  2154. /*
  2155.  * The outer loop iterates over the string.  The inner
  2156.  * loop iterates over the trim characters.  The loops
  2157.  * terminate as soon as a non-trim character is discovered
  2158.  * and string1 is left pointing at the first non-trim
  2159.  * character.
  2160.  */
  2161. for (p = string1; p < end; p += offset) {
  2162.     offset = TclUtfToUniChar(p, &ch);
  2163.     
  2164.     for (check = string2; ; ) {
  2165. if (check >= checkEnd) {
  2166.     p = end;
  2167.     break;
  2168. }
  2169. check += TclUtfToUniChar(check, &trim);
  2170. if (ch == trim) {
  2171.     length1 -= offset;
  2172.     string1 += offset;
  2173.     break;
  2174. }
  2175.     }
  2176. }
  2177.     }
  2178.     if (right) {
  2179.         end = string1;
  2180. /*
  2181.  * The outer loop iterates over the string.  The inner
  2182.  * loop iterates over the trim characters.  The loops
  2183.  * terminate as soon as a non-trim character is discovered
  2184.  * and length1 marks the last non-trim character.
  2185.  */
  2186. for (p = string1 + length1; p > end; ) {
  2187.     p = Tcl_UtfPrev(p, string1);
  2188.     offset = TclUtfToUniChar(p, &ch);
  2189.     for (check = string2; ; ) {
  2190.         if (check >= checkEnd) {
  2191.     p = end;
  2192.     break;
  2193. }
  2194. check += TclUtfToUniChar(check, &trim);
  2195. if (ch == trim) {
  2196.     length1 -= offset;
  2197.     break;
  2198. }
  2199.     }
  2200. }
  2201.     }
  2202.     Tcl_SetStringObj(resultPtr, string1, length1);
  2203.     break;
  2204. }
  2205. case STR_TRIMLEFT: {
  2206.     left = 1;
  2207.     right = 0;
  2208.     goto dotrim;
  2209. }
  2210. case STR_TRIMRIGHT: {
  2211.     left = 0;
  2212.     right = 1;
  2213.     goto dotrim;
  2214. }
  2215. case STR_WORDEND: {
  2216.     int cur;
  2217.     Tcl_UniChar ch;
  2218.     CONST char *p, *end;
  2219.     int numChars;
  2220.     
  2221.     if (objc != 4) {
  2222.         Tcl_WrongNumArgs(interp, 2, objv, "string index");
  2223. return TCL_ERROR;
  2224.     }
  2225.     string1 = Tcl_GetStringFromObj(objv[2], &length1);
  2226.     numChars = Tcl_NumUtfChars(string1, length1);
  2227.     if (TclGetIntForIndex(interp, objv[3], numChars-1,
  2228.   &index) != TCL_OK) {
  2229. return TCL_ERROR;
  2230.     }
  2231.     if (index < 0) {
  2232. index = 0;
  2233.     }
  2234.     if (index < numChars) {
  2235. p = Tcl_UtfAtIndex(string1, index);
  2236. end = string1+length1;
  2237. for (cur = index; p < end; cur++) {
  2238.     p += TclUtfToUniChar(p, &ch);
  2239.     if (!Tcl_UniCharIsWordChar(ch)) {
  2240. break;
  2241.     }
  2242. }
  2243. if (cur == index) {
  2244.     cur++;
  2245. }
  2246.     } else {
  2247. cur = numChars;
  2248.     }
  2249.     Tcl_SetIntObj(resultPtr, cur);
  2250.     break;
  2251. }
  2252. case STR_WORDSTART: {
  2253.     int cur;
  2254.     Tcl_UniChar ch;
  2255.     CONST char *p;
  2256.     int numChars;
  2257.     
  2258.     if (objc != 4) {
  2259.         Tcl_WrongNumArgs(interp, 2, objv, "string index");
  2260. return TCL_ERROR;
  2261.     }
  2262.     string1 = Tcl_GetStringFromObj(objv[2], &length1);
  2263.     numChars = Tcl_NumUtfChars(string1, length1);
  2264.     if (TclGetIntForIndex(interp, objv[3], numChars-1,
  2265.   &index) != TCL_OK) {
  2266. return TCL_ERROR;
  2267.     }
  2268.     if (index >= numChars) {
  2269. index = numChars - 1;
  2270.     }
  2271.     cur = 0;
  2272.     if (index > 0) {
  2273. p = Tcl_UtfAtIndex(string1, index);
  2274.         for (cur = index; cur >= 0; cur--) {
  2275.     TclUtfToUniChar(p, &ch);
  2276.     if (!Tcl_UniCharIsWordChar(ch)) {
  2277. break;
  2278.     }
  2279.     p = Tcl_UtfPrev(p, string1);
  2280. }
  2281. if (cur != index) {
  2282.     cur += 1;
  2283. }
  2284.     }
  2285.     Tcl_SetIntObj(resultPtr, cur);
  2286.     break;
  2287. }
  2288.     }
  2289.     return TCL_OK;
  2290. }
  2291. /*
  2292.  *----------------------------------------------------------------------
  2293.  *
  2294.  * Tcl_SubstObjCmd --
  2295.  *
  2296.  * This procedure is invoked to process the "subst" Tcl command.
  2297.  * See the user documentation for details on what it does.  This
  2298.  * command relies on Tcl_SubstObj() for its implementation.
  2299.  *
  2300.  * Results:
  2301.  * A standard Tcl result.
  2302.  *
  2303.  * Side effects:
  2304.  * See the user documentation.
  2305.  *
  2306.  *----------------------------------------------------------------------
  2307.  */
  2308. /* ARGSUSED */
  2309. int
  2310. Tcl_SubstObjCmd(dummy, interp, objc, objv)
  2311.     ClientData dummy; /* Not used. */
  2312.     Tcl_Interp *interp; /* Current interpreter. */
  2313.     int objc; /* Number of arguments. */
  2314.     Tcl_Obj *CONST objv[];        /* Argument objects. */
  2315. {
  2316.     static CONST char *substOptions[] = {
  2317. "-nobackslashes", "-nocommands", "-novariables", (char *) NULL
  2318.     };
  2319.     enum substOptions {
  2320. SUBST_NOBACKSLASHES,      SUBST_NOCOMMANDS,       SUBST_NOVARS
  2321.     };
  2322.     Tcl_Obj *resultPtr;
  2323.     int optionIndex, flags, i;
  2324.     /*
  2325.      * Parse command-line options.
  2326.      */
  2327.     flags = TCL_SUBST_ALL;
  2328.     for (i = 1; i < (objc-1); i++) {
  2329. if (Tcl_GetIndexFromObj(interp, objv[i], substOptions,
  2330. "switch", 0, &optionIndex) != TCL_OK) {
  2331.     return TCL_ERROR;
  2332. }
  2333. switch (optionIndex) {
  2334.     case SUBST_NOBACKSLASHES: {
  2335. flags &= ~TCL_SUBST_BACKSLASHES;
  2336. break;
  2337.     }