/[dtapublic]/projs/dtats/trunk/shared_source/c_tcl_base_7_5_w_mods/tclcmdmz.c
ViewVC logotype

Diff of /projs/dtats/trunk/shared_source/c_tcl_base_7_5_w_mods/tclcmdmz.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 66 by dashley, Sun Oct 30 21:57:38 2016 UTC revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC
# Line 1  Line 1 
1  /* $Header$ */  /* $Header$ */
2  /*  /*
3   * tclCmdMZ.c --   * tclCmdMZ.c --
4   *   *
5   *      This file contains the top-level command routines for most of   *      This file contains the top-level command routines for most of
6   *      the Tcl built-in commands whose names begin with the letters   *      the Tcl built-in commands whose names begin with the letters
7   *      M to Z.  It contains only commands in the generic core (i.e.   *      M to Z.  It contains only commands in the generic core (i.e.
8   *      those that don't depend much upon UNIX facilities).   *      those that don't depend much upon UNIX facilities).
9   *   *
10   * Copyright (c) 1987-1993 The Regents of the University of California.   * Copyright (c) 1987-1993 The Regents of the University of California.
11   * Copyright (c) 1994-1997 Sun Microsystems, Inc.   * Copyright (c) 1994-1997 Sun Microsystems, Inc.
12   * Copyright (c) 1998-1999 by Scriptics Corporation.   * Copyright (c) 1998-1999 by Scriptics Corporation.
13   *   *
14   * See the file "license.terms" for information on usage and redistribution   * See the file "license.terms" for information on usage and redistribution
15   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16   *   *
17   * RCS: @(#) $Id: tclcmdmz.c,v 1.1.1.1 2001/06/13 04:35:16 dtashley Exp $   * RCS: @(#) $Id: tclcmdmz.c,v 1.1.1.1 2001/06/13 04:35:16 dtashley Exp $
18   */   */
19    
20  #include "tclInt.h"  #include "tclInt.h"
21  #include "tclPort.h"  #include "tclPort.h"
22  #include "tclCompile.h"  #include "tclCompile.h"
23  #include "tclRegexp.h"  #include "tclRegexp.h"
24    
25  /*  /*
26   * Flag values used by Tcl_ScanObjCmd.   * Flag values used by Tcl_ScanObjCmd.
27   */   */
28    
29  #define SCAN_NOSKIP     0x1               /* Don't skip blanks. */  #define SCAN_NOSKIP     0x1               /* Don't skip blanks. */
30  #define SCAN_SUPPRESS   0x2               /* Suppress assignment. */  #define SCAN_SUPPRESS   0x2               /* Suppress assignment. */
31  #define SCAN_UNSIGNED   0x4               /* Read an unsigned value. */  #define SCAN_UNSIGNED   0x4               /* Read an unsigned value. */
32  #define SCAN_WIDTH      0x8               /* A width value was supplied. */  #define SCAN_WIDTH      0x8               /* A width value was supplied. */
33    
34  #define SCAN_SIGNOK     0x10              /* A +/- character is allowed. */  #define SCAN_SIGNOK     0x10              /* A +/- character is allowed. */
35  #define SCAN_NODIGITS   0x20              /* No digits have been scanned. */  #define SCAN_NODIGITS   0x20              /* No digits have been scanned. */
36  #define SCAN_NOZERO     0x40              /* No zero digits have been scanned. */  #define SCAN_NOZERO     0x40              /* No zero digits have been scanned. */
37  #define SCAN_XOK        0x80              /* An 'x' is allowed. */  #define SCAN_XOK        0x80              /* An 'x' is allowed. */
38  #define SCAN_PTOK       0x100             /* Decimal point is allowed. */  #define SCAN_PTOK       0x100             /* Decimal point is allowed. */
39  #define SCAN_EXPOK      0x200             /* An exponent is allowed. */  #define SCAN_EXPOK      0x200             /* An exponent is allowed. */
40    
41  /*  /*
42   * Structure used to hold information about variable traces:   * Structure used to hold information about variable traces:
43   */   */
44    
45  typedef struct {  typedef struct {
46      int flags;                  /* Operations for which Tcl command is      int flags;                  /* Operations for which Tcl command is
47                                   * to be invoked. */                                   * to be invoked. */
48      char *errMsg;               /* Error message returned from Tcl command,      char *errMsg;               /* Error message returned from Tcl command,
49                                   * or NULL.  Malloc'ed. */                                   * or NULL.  Malloc'ed. */
50      size_t length;              /* Number of non-NULL chars. in command. */      size_t length;              /* Number of non-NULL chars. in command. */
51      char command[4];            /* Space for Tcl command to invoke.  Actual      char command[4];            /* Space for Tcl command to invoke.  Actual
52                                   * size will be as large as necessary to                                   * size will be as large as necessary to
53                                   * hold command.  This field must be the                                   * hold command.  This field must be the
54                                   * last in the structure, so that it can                                   * last in the structure, so that it can
55                                   * be larger than 4 bytes. */                                   * be larger than 4 bytes. */
56  } TraceVarInfo;  } TraceVarInfo;
57    
58  /*  /*
59   * Forward declarations for procedures defined in this file:   * Forward declarations for procedures defined in this file:
60   */   */
61    
62  static char *           TraceVarProc _ANSI_ARGS_((ClientData clientData,  static char *           TraceVarProc _ANSI_ARGS_((ClientData clientData,
63                              Tcl_Interp *interp, char *name1, char *name2,                              Tcl_Interp *interp, char *name1, char *name2,
64                              int flags));                              int flags));
65    
66  /*  /*
67   *----------------------------------------------------------------------   *----------------------------------------------------------------------
68   *   *
69   * Tcl_PwdObjCmd --   * Tcl_PwdObjCmd --
70   *   *
71   *      This procedure is invoked to process the "pwd" Tcl command.   *      This procedure is invoked to process the "pwd" Tcl command.
72   *      See the user documentation for details on what it does.   *      See the user documentation for details on what it does.
73   *   *
74   * Results:   * Results:
75   *      A standard Tcl result.   *      A standard Tcl result.
76   *   *
77   * Side effects:   * Side effects:
78   *      See the user documentation.   *      See the user documentation.
79   *   *
80   *----------------------------------------------------------------------   *----------------------------------------------------------------------
81   */   */
82    
83          /* ARGSUSED */          /* ARGSUSED */
84  int  int
85  Tcl_PwdObjCmd(dummy, interp, objc, objv)  Tcl_PwdObjCmd(dummy, interp, objc, objv)
86      ClientData dummy;                   /* Not used. */      ClientData dummy;                   /* Not used. */
87      Tcl_Interp *interp;                 /* Current interpreter. */      Tcl_Interp *interp;                 /* Current interpreter. */
88      int objc;                           /* Number of arguments. */      int objc;                           /* Number of arguments. */
89      Tcl_Obj *CONST objv[];              /* Argument objects. */      Tcl_Obj *CONST objv[];              /* Argument objects. */
90  {  {
91      Tcl_DString ds;      Tcl_DString ds;
92    
93      if (objc != 1) {      if (objc != 1) {
94          Tcl_WrongNumArgs(interp, 1, objv, NULL);          Tcl_WrongNumArgs(interp, 1, objv, NULL);
95          return TCL_ERROR;          return TCL_ERROR;
96      }      }
97    
98      if (Tcl_GetCwd(interp, &ds) == NULL) {      if (Tcl_GetCwd(interp, &ds) == NULL) {
99          return TCL_ERROR;          return TCL_ERROR;
100      }      }
101      Tcl_DStringResult(interp, &ds);      Tcl_DStringResult(interp, &ds);
102      return TCL_OK;      return TCL_OK;
103  }  }
104    
105  /*  /*
106   *----------------------------------------------------------------------   *----------------------------------------------------------------------
107   *   *
108   * Tcl_RegexpObjCmd --   * Tcl_RegexpObjCmd --
109   *   *
110   *      This procedure is invoked to process the "regexp" Tcl command.   *      This procedure is invoked to process the "regexp" Tcl command.
111   *      See the user documentation for details on what it does.   *      See the user documentation for details on what it does.
112   *   *
113   * Results:   * Results:
114   *      A standard Tcl result.   *      A standard Tcl result.
115   *   *
116   * Side effects:   * Side effects:
117   *      See the user documentation.   *      See the user documentation.
118   *   *
119   *----------------------------------------------------------------------   *----------------------------------------------------------------------
120   */   */
121    
122          /* ARGSUSED */          /* ARGSUSED */
123  int  int
124  Tcl_RegexpObjCmd(dummy, interp, objc, objv)  Tcl_RegexpObjCmd(dummy, interp, objc, objv)
125      ClientData dummy;                   /* Not used. */      ClientData dummy;                   /* Not used. */
126      Tcl_Interp *interp;                 /* Current interpreter. */      Tcl_Interp *interp;                 /* Current interpreter. */
127      int objc;                           /* Number of arguments. */      int objc;                           /* Number of arguments. */
128      Tcl_Obj *CONST objv[];              /* Argument objects. */      Tcl_Obj *CONST objv[];              /* Argument objects. */
129  {  {
130      int i, indices, match, about, offset, all, doinline, numMatchesSaved;      int i, indices, match, about, offset, all, doinline, numMatchesSaved;
131      int cflags, eflags, stringLength;      int cflags, eflags, stringLength;
132      Tcl_RegExp regExpr;      Tcl_RegExp regExpr;
133      Tcl_Obj *objPtr, *resultPtr;      Tcl_Obj *objPtr, *resultPtr;
134      Tcl_RegExpInfo info;      Tcl_RegExpInfo info;
135      static char *options[] = {      static char *options[] = {
136          "-all",         "-about",       "-indices",     "-inline",          "-all",         "-about",       "-indices",     "-inline",
137          "-expanded",    "-line",        "-linestop",    "-lineanchor",          "-expanded",    "-line",        "-linestop",    "-lineanchor",
138          "-nocase",      "-start",       "--",           (char *) NULL          "-nocase",      "-start",       "--",           (char *) NULL
139      };      };
140      enum options {      enum options {
141          REGEXP_ALL,     REGEXP_ABOUT,   REGEXP_INDICES, REGEXP_INLINE,          REGEXP_ALL,     REGEXP_ABOUT,   REGEXP_INDICES, REGEXP_INLINE,
142          REGEXP_EXPANDED,REGEXP_LINE,    REGEXP_LINESTOP,REGEXP_LINEANCHOR,          REGEXP_EXPANDED,REGEXP_LINE,    REGEXP_LINESTOP,REGEXP_LINEANCHOR,
143          REGEXP_NOCASE,  REGEXP_START,   REGEXP_LAST          REGEXP_NOCASE,  REGEXP_START,   REGEXP_LAST
144      };      };
145    
146      indices     = 0;      indices     = 0;
147      about       = 0;      about       = 0;
148      cflags      = TCL_REG_ADVANCED;      cflags      = TCL_REG_ADVANCED;
149      eflags      = 0;      eflags      = 0;
150      offset      = 0;      offset      = 0;
151      all         = 0;      all         = 0;
152      doinline    = 0;      doinline    = 0;
153            
154      for (i = 1; i < objc; i++) {      for (i = 1; i < objc; i++) {
155          char *name;          char *name;
156          int index;          int index;
157    
158          name = Tcl_GetString(objv[i]);          name = Tcl_GetString(objv[i]);
159          if (name[0] != '-') {          if (name[0] != '-') {
160              break;              break;
161          }          }
162          if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,          if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,
163                  &index) != TCL_OK) {                  &index) != TCL_OK) {
164              return TCL_ERROR;              return TCL_ERROR;
165          }          }
166          switch ((enum options) index) {          switch ((enum options) index) {
167              case REGEXP_ALL: {              case REGEXP_ALL: {
168                  all = 1;                  all = 1;
169                  break;                  break;
170              }              }
171              case REGEXP_INDICES: {              case REGEXP_INDICES: {
172                  indices = 1;                  indices = 1;
173                  break;                  break;
174              }              }
175              case REGEXP_INLINE: {              case REGEXP_INLINE: {
176                  doinline = 1;                  doinline = 1;
177                  break;                  break;
178              }              }
179              case REGEXP_NOCASE: {              case REGEXP_NOCASE: {
180                  cflags |= TCL_REG_NOCASE;                  cflags |= TCL_REG_NOCASE;
181                  break;                  break;
182              }              }
183              case REGEXP_ABOUT: {              case REGEXP_ABOUT: {
184                  about = 1;                  about = 1;
185                  break;                  break;
186              }              }
187              case REGEXP_EXPANDED: {              case REGEXP_EXPANDED: {
188                  cflags |= TCL_REG_EXPANDED;                  cflags |= TCL_REG_EXPANDED;
189                  break;                  break;
190              }              }
191              case REGEXP_LINE: {              case REGEXP_LINE: {
192                  cflags |= TCL_REG_NEWLINE;                  cflags |= TCL_REG_NEWLINE;
193                  break;                  break;
194              }              }
195              case REGEXP_LINESTOP: {              case REGEXP_LINESTOP: {
196                  cflags |= TCL_REG_NLSTOP;                  cflags |= TCL_REG_NLSTOP;
197                  break;                  break;
198              }              }
199              case REGEXP_LINEANCHOR: {              case REGEXP_LINEANCHOR: {
200                  cflags |= TCL_REG_NLANCH;                  cflags |= TCL_REG_NLANCH;
201                  break;                  break;
202              }              }
203              case REGEXP_START: {              case REGEXP_START: {
204                  if (++i >= objc) {                  if (++i >= objc) {
205                      goto endOfForLoop;                      goto endOfForLoop;
206                  }                  }
207                  if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {                  if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {
208                      return TCL_ERROR;                      return TCL_ERROR;
209                  }                  }
210                  if (offset < 0) {                  if (offset < 0) {
211                      offset = 0;                      offset = 0;
212                  }                  }
213                  break;                  break;
214              }              }
215              case REGEXP_LAST: {              case REGEXP_LAST: {
216                  i++;                  i++;
217                  goto endOfForLoop;                  goto endOfForLoop;
218              }              }
219          }          }
220      }      }
221    
222      endOfForLoop:      endOfForLoop:
223      if ((objc - i) < (2 - about)) {      if ((objc - i) < (2 - about)) {
224          Tcl_WrongNumArgs(interp, 1, objv, "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");          Tcl_WrongNumArgs(interp, 1, objv, "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
225          return TCL_ERROR;          return TCL_ERROR;
226      }      }
227      objc -= i;      objc -= i;
228      objv += i;      objv += i;
229    
230      if (doinline && ((objc - 2) != 0)) {      if (doinline && ((objc - 2) != 0)) {
231          /*          /*
232           * User requested -inline, but specified match variables - a no-no.           * User requested -inline, but specified match variables - a no-no.
233           */           */
234          Tcl_AppendResult(interp, "regexp match variables not allowed",          Tcl_AppendResult(interp, "regexp match variables not allowed",
235                  " when using -inline", (char *) NULL);                  " when using -inline", (char *) NULL);
236          return TCL_ERROR;          return TCL_ERROR;
237      }      }
238    
239      regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);      regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
240      if (regExpr == NULL) {      if (regExpr == NULL) {
241          return TCL_ERROR;          return TCL_ERROR;
242      }      }
243      objPtr = objv[1];      objPtr = objv[1];
244    
245      if (about) {      if (about) {
246          if (TclRegAbout(interp, regExpr) < 0) {          if (TclRegAbout(interp, regExpr) < 0) {
247              return TCL_ERROR;              return TCL_ERROR;
248          }          }
249          return TCL_OK;          return TCL_OK;
250      }      }
251    
252      if (offset > 0) {      if (offset > 0) {
253          /*          /*
254           * Add flag if using offset (string is part of a larger string),           * Add flag if using offset (string is part of a larger string),
255           * so that "^" won't match.           * so that "^" won't match.
256           */           */
257          eflags |= TCL_REG_NOTBOL;          eflags |= TCL_REG_NOTBOL;
258      }      }
259    
260      objc -= 2;      objc -= 2;
261      objv += 2;      objv += 2;
262      resultPtr = Tcl_GetObjResult(interp);      resultPtr = Tcl_GetObjResult(interp);
263    
264      if (doinline) {      if (doinline) {
265          /*          /*
266           * Save all the subexpressions, as we will return them as a list           * Save all the subexpressions, as we will return them as a list
267           */           */
268          numMatchesSaved = -1;          numMatchesSaved = -1;
269      } else {      } else {
270          /*          /*
271           * Save only enough subexpressions for matches we want to keep,           * Save only enough subexpressions for matches we want to keep,
272           * expect in the case of -all, where we need to keep at least           * expect in the case of -all, where we need to keep at least
273           * one to know where to move the offset.           * one to know where to move the offset.
274           */           */
275          numMatchesSaved = (objc == 0) ? all : objc;          numMatchesSaved = (objc == 0) ? all : objc;
276      }      }
277    
278      /*      /*
279       * Get the length of the string that we are matching against so       * Get the length of the string that we are matching against so
280       * we can do the termination test for -all matches.       * we can do the termination test for -all matches.
281       */       */
282      stringLength = Tcl_GetCharLength(objPtr);      stringLength = Tcl_GetCharLength(objPtr);
283            
284      /*      /*
285       * The following loop is to handle multiple matches within the       * The following loop is to handle multiple matches within the
286       * same source string;  each iteration handles one match.  If "-all"       * same source string;  each iteration handles one match.  If "-all"
287       * hasn't been specified then the loop body only gets executed once.       * hasn't been specified then the loop body only gets executed once.
288       * We terminate the loop when the starting offset is past the end of the       * We terminate the loop when the starting offset is past the end of the
289       * string.       * string.
290       */       */
291    
292      while (1) {      while (1) {
293          match = Tcl_RegExpExecObj(interp, regExpr, objPtr,          match = Tcl_RegExpExecObj(interp, regExpr, objPtr,
294                  offset /* offset */, numMatchesSaved, eflags);                  offset /* offset */, numMatchesSaved, eflags);
295    
296          if (match < 0) {          if (match < 0) {
297              return TCL_ERROR;              return TCL_ERROR;
298          }          }
299    
300          if (match == 0) {          if (match == 0) {
301              /*              /*
302               * We want to set the value of the intepreter result only when               * We want to set the value of the intepreter result only when
303               * this is the first time through the loop.               * this is the first time through the loop.
304               */               */
305              if (all <= 1) {              if (all <= 1) {
306                  /*                  /*
307                   * If inlining, set the interpreter's object result to an                   * If inlining, set the interpreter's object result to an
308                   * empty list, otherwise set it to an integer object w/                   * empty list, otherwise set it to an integer object w/
309                   * value 0.                   * value 0.
310                   */                   */
311                  if (doinline) {                  if (doinline) {
312                      Tcl_SetListObj(resultPtr, 0, NULL);                      Tcl_SetListObj(resultPtr, 0, NULL);
313                  } else {                  } else {
314                      Tcl_SetIntObj(resultPtr, 0);                      Tcl_SetIntObj(resultPtr, 0);
315                  }                  }
316                  return TCL_OK;                  return TCL_OK;
317              }              }
318              break;              break;
319          }          }
320    
321          /*          /*
322           * If additional variable names have been specified, return           * If additional variable names have been specified, return
323           * index information in those variables.           * index information in those variables.
324           */           */
325    
326          Tcl_RegExpGetInfo(regExpr, &info);          Tcl_RegExpGetInfo(regExpr, &info);
327          if (doinline) {          if (doinline) {
328              /*              /*
329               * It's the number of substitutions, plus one for the matchVar               * It's the number of substitutions, plus one for the matchVar
330               * at index 0               * at index 0
331               */               */
332              objc = info.nsubs + 1;              objc = info.nsubs + 1;
333          }          }
334          for (i = 0; i < objc; i++) {          for (i = 0; i < objc; i++) {
335              Tcl_Obj *newPtr;              Tcl_Obj *newPtr;
336    
337              if (indices) {              if (indices) {
338                  int start, end;                  int start, end;
339                  Tcl_Obj *objs[2];                  Tcl_Obj *objs[2];
340    
341                  if (i <= info.nsubs) {                  if (i <= info.nsubs) {
342                      start = offset + info.matches[i].start;                      start = offset + info.matches[i].start;
343                      end   = offset + info.matches[i].end;                      end   = offset + info.matches[i].end;
344    
345                      /*                      /*
346                       * Adjust index so it refers to the last character in the                       * Adjust index so it refers to the last character in the
347                       * match instead of the first character after the match.                       * match instead of the first character after the match.
348                       */                       */
349    
350                      if (end >= offset) {                      if (end >= offset) {
351                          end--;                          end--;
352                      }                      }
353                  } else {                  } else {
354                      start = -1;                      start = -1;
355                      end   = -1;                      end   = -1;
356                  }                  }
357    
358                  objs[0] = Tcl_NewLongObj(start);                  objs[0] = Tcl_NewLongObj(start);
359                  objs[1] = Tcl_NewLongObj(end);                  objs[1] = Tcl_NewLongObj(end);
360    
361                  newPtr = Tcl_NewListObj(2, objs);                  newPtr = Tcl_NewListObj(2, objs);
362              } else {              } else {
363                  if (i <= info.nsubs) {                  if (i <= info.nsubs) {
364                      newPtr = Tcl_GetRange(objPtr,                      newPtr = Tcl_GetRange(objPtr,
365                              offset + info.matches[i].start,                              offset + info.matches[i].start,
366                              offset + info.matches[i].end - 1);                              offset + info.matches[i].end - 1);
367                  } else {                  } else {
368                      newPtr = Tcl_NewObj();                      newPtr = Tcl_NewObj();
369                  }                  }
370              }              }
371              if (doinline) {              if (doinline) {
372                  if (Tcl_ListObjAppendElement(interp, resultPtr, newPtr)                  if (Tcl_ListObjAppendElement(interp, resultPtr, newPtr)
373                          != TCL_OK) {                          != TCL_OK) {
374                      Tcl_DecrRefCount(newPtr);                      Tcl_DecrRefCount(newPtr);
375                      return TCL_ERROR;                      return TCL_ERROR;
376                  }                  }
377              } else {              } else {
378                  Tcl_Obj *valuePtr;                  Tcl_Obj *valuePtr;
379                  valuePtr = Tcl_ObjSetVar2(interp, objv[i], NULL, newPtr, 0);                  valuePtr = Tcl_ObjSetVar2(interp, objv[i], NULL, newPtr, 0);
380                  if (valuePtr == NULL) {                  if (valuePtr == NULL) {
381                      Tcl_DecrRefCount(newPtr);                      Tcl_DecrRefCount(newPtr);
382                      Tcl_AppendResult(interp, "couldn't set variable \"",                      Tcl_AppendResult(interp, "couldn't set variable \"",
383                              Tcl_GetString(objv[i]), "\"", (char *) NULL);                              Tcl_GetString(objv[i]), "\"", (char *) NULL);
384                      return TCL_ERROR;                      return TCL_ERROR;
385                  }                  }
386              }              }
387          }          }
388    
389          if (all == 0) {          if (all == 0) {
390              break;              break;
391          }          }
392          /*          /*
393           * Adjust the offset to the character just after the last one           * Adjust the offset to the character just after the last one
394           * in the matchVar and increment all to count how many times           * in the matchVar and increment all to count how many times
395           * we are making a match.  We always increment the offset by at least           * we are making a match.  We always increment the offset by at least
396           * one to prevent endless looping (as in the case:           * one to prevent endless looping (as in the case:
397           * regexp -all {a*} a).  Otherwise, when we match the NULL string at           * regexp -all {a*} a).  Otherwise, when we match the NULL string at
398           * the end of the input string, we will loop indefinately (because the           * the end of the input string, we will loop indefinately (because the
399           * length of the match is 0, so offset never changes).           * length of the match is 0, so offset never changes).
400           */           */
401          if (info.matches[0].end == 0) {          if (info.matches[0].end == 0) {
402              offset++;              offset++;
403          }          }
404          offset += info.matches[0].end;          offset += info.matches[0].end;
405          all++;          all++;
406          if (offset >= stringLength) {          if (offset >= stringLength) {
407              break;              break;
408          }          }
409      }      }
410    
411      /*      /*
412       * Set the interpreter's object result to an integer object       * Set the interpreter's object result to an integer object
413       * with value 1 if -all wasn't specified, otherwise it's all-1       * with value 1 if -all wasn't specified, otherwise it's all-1
414       * (the number of times through the while - 1).       * (the number of times through the while - 1).
415       */       */
416    
417      if (!doinline) {      if (!doinline) {
418          Tcl_SetIntObj(resultPtr, (all ? all-1 : 1));          Tcl_SetIntObj(resultPtr, (all ? all-1 : 1));
419      }      }
420      return TCL_OK;      return TCL_OK;
421  }  }
422    
423  /*  /*
424   *----------------------------------------------------------------------   *----------------------------------------------------------------------
425   *   *
426   * Tcl_RegsubObjCmd --   * Tcl_RegsubObjCmd --
427   *   *
428   *      This procedure is invoked to process the "regsub" Tcl command.   *      This procedure is invoked to process the "regsub" Tcl command.
429   *      See the user documentation for details on what it does.   *      See the user documentation for details on what it does.
430   *   *
431   * Results:   * Results:
432   *      A standard Tcl result.   *      A standard Tcl result.
433   *   *
434   * Side effects:   * Side effects:
435   *      See the user documentation.   *      See the user documentation.
436   *   *
437   *----------------------------------------------------------------------   *----------------------------------------------------------------------
438   */   */
439    
440          /* ARGSUSED */          /* ARGSUSED */
441  int  int
442  Tcl_RegsubObjCmd(dummy, interp, objc, objv)  Tcl_RegsubObjCmd(dummy, interp, objc, objv)
443      ClientData dummy;                   /* Not used. */      ClientData dummy;                   /* Not used. */
444      Tcl_Interp *interp;                 /* Current interpreter. */      Tcl_Interp *interp;                 /* Current interpreter. */
445      int objc;                           /* Number of arguments. */      int objc;                           /* Number of arguments. */
446      Tcl_Obj *CONST objv[];              /* Argument objects. */      Tcl_Obj *CONST objv[];              /* Argument objects. */
447  {  {
448      int i, result, cflags, all, wlen, numMatches, offset;      int i, result, cflags, all, wlen, numMatches, offset;
449      Tcl_RegExp regExpr;      Tcl_RegExp regExpr;
450      Tcl_Obj *resultPtr, *varPtr, *objPtr;      Tcl_Obj *resultPtr, *varPtr, *objPtr;
451      Tcl_UniChar *wstring;      Tcl_UniChar *wstring;
452      char *subspec;      char *subspec;
453    
454      static char *options[] = {      static char *options[] = {
455          "-all",         "-nocase",      "-expanded",          "-all",         "-nocase",      "-expanded",
456          "-line",        "-linestop",    "-lineanchor",  "-start",          "-line",        "-linestop",    "-lineanchor",  "-start",
457          "--",           NULL          "--",           NULL
458      };      };
459      enum options {      enum options {
460          REGSUB_ALL,     REGSUB_NOCASE,  REGSUB_EXPANDED,          REGSUB_ALL,     REGSUB_NOCASE,  REGSUB_EXPANDED,
461          REGSUB_LINE,    REGSUB_LINESTOP, REGSUB_LINEANCHOR,     REGSUB_START,          REGSUB_LINE,    REGSUB_LINESTOP, REGSUB_LINEANCHOR,     REGSUB_START,
462          REGSUB_LAST          REGSUB_LAST
463      };      };
464    
465      cflags = TCL_REG_ADVANCED;      cflags = TCL_REG_ADVANCED;
466      all = 0;      all = 0;
467      offset = 0;      offset = 0;
468    
469      for (i = 1; i < objc; i++) {      for (i = 1; i < objc; i++) {
470          char *name;          char *name;
471          int index;          int index;
472                    
473          name = Tcl_GetString(objv[i]);          name = Tcl_GetString(objv[i]);
474          if (name[0] != '-') {          if (name[0] != '-') {
475              break;              break;
476          }          }
477          if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,          if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,
478                  &index) != TCL_OK) {                  &index) != TCL_OK) {
479              return TCL_ERROR;              return TCL_ERROR;
480          }          }
481          switch ((enum options) index) {          switch ((enum options) index) {
482              case REGSUB_ALL: {              case REGSUB_ALL: {
483                  all = 1;                  all = 1;
484                  break;                  break;
485              }              }
486              case REGSUB_NOCASE: {              case REGSUB_NOCASE: {
487                  cflags |= TCL_REG_NOCASE;                  cflags |= TCL_REG_NOCASE;
488                  break;                  break;
489              }              }
490              case REGSUB_EXPANDED: {              case REGSUB_EXPANDED: {
491                  cflags |= TCL_REG_EXPANDED;                  cflags |= TCL_REG_EXPANDED;
492                  break;                  break;
493              }              }
494              case REGSUB_LINE: {              case REGSUB_LINE: {
495                  cflags |= TCL_REG_NEWLINE;                  cflags |= TCL_REG_NEWLINE;
496                  break;                  break;
497              }              }
498              case REGSUB_LINESTOP: {              case REGSUB_LINESTOP: {
499                  cflags |= TCL_REG_NLSTOP;                  cflags |= TCL_REG_NLSTOP;
500                  break;                  break;
501              }              }
502              case REGSUB_LINEANCHOR: {              case REGSUB_LINEANCHOR: {
503                  cflags |= TCL_REG_NLANCH;                  cflags |= TCL_REG_NLANCH;
504                  break;                  break;
505              }              }
506              case REGSUB_START: {              case REGSUB_START: {
507                  if (++i >= objc) {                  if (++i >= objc) {
508                      goto endOfForLoop;                      goto endOfForLoop;
509                  }                  }
510                  if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {                  if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {
511                      return TCL_ERROR;                      return TCL_ERROR;
512                  }                  }
513                  if (offset < 0) {                  if (offset < 0) {
514                      offset = 0;                      offset = 0;
515                  }                  }
516                  break;                  break;
517              }              }
518              case REGSUB_LAST: {              case REGSUB_LAST: {
519                  i++;                  i++;
520                  goto endOfForLoop;                  goto endOfForLoop;
521              }              }
522          }          }
523      }      }
524      endOfForLoop:      endOfForLoop:
525      if (objc - i != 4) {      if (objc - i != 4) {
526          Tcl_WrongNumArgs(interp, 1, objv,          Tcl_WrongNumArgs(interp, 1, objv,
527                  "?switches? exp string subSpec varName");                  "?switches? exp string subSpec varName");
528          return TCL_ERROR;          return TCL_ERROR;
529      }      }
530    
531      objv += i;      objv += i;
532    
533      regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);      regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
534      if (regExpr == NULL) {      if (regExpr == NULL) {
535          return TCL_ERROR;          return TCL_ERROR;
536      }      }
537    
538      result = TCL_OK;      result = TCL_OK;
539      resultPtr = Tcl_NewObj();      resultPtr = Tcl_NewObj();
540      Tcl_IncrRefCount(resultPtr);      Tcl_IncrRefCount(resultPtr);
541    
542      objPtr = objv[1];      objPtr = objv[1];
543      wlen = Tcl_GetCharLength(objPtr);      wlen = Tcl_GetCharLength(objPtr);
544      wstring = Tcl_GetUnicode(objPtr);      wstring = Tcl_GetUnicode(objPtr);
545      subspec = Tcl_GetString(objv[2]);      subspec = Tcl_GetString(objv[2]);
546      varPtr = objv[3];      varPtr = objv[3];
547    
548      /*      /*
549       * The following loop is to handle multiple matches within the       * The following loop is to handle multiple matches within the
550       * same source string;  each iteration handles one match and its       * same source string;  each iteration handles one match and its
551       * corresponding substitution.  If "-all" hasn't been specified       * corresponding substitution.  If "-all" hasn't been specified
552       * then the loop body only gets executed once.       * then the loop body only gets executed once.
553       */       */
554    
555      numMatches = 0;      numMatches = 0;
556      for ( ; offset < wlen; ) {      for ( ; offset < wlen; ) {
557          int start, end, subStart, subEnd, match;          int start, end, subStart, subEnd, match;
558          char *src, *firstChar;          char *src, *firstChar;
559          char c;          char c;
560          Tcl_RegExpInfo info;          Tcl_RegExpInfo info;
561    
562          /*          /*
563           * The flags argument is set if string is part of a larger string,           * The flags argument is set if string is part of a larger string,
564           * so that "^" won't match.           * so that "^" won't match.
565           */           */
566    
567          match = Tcl_RegExpExecObj(interp, regExpr, objPtr, offset,          match = Tcl_RegExpExecObj(interp, regExpr, objPtr, offset,
568                  10 /* matches */, ((offset > 0) ? TCL_REG_NOTBOL : 0));                  10 /* matches */, ((offset > 0) ? TCL_REG_NOTBOL : 0));
569    
570          if (match < 0) {          if (match < 0) {
571              result = TCL_ERROR;              result = TCL_ERROR;
572              goto done;              goto done;
573          }          }
574          if (match == 0) {          if (match == 0) {
575              break;              break;
576          }          }
577          if ((numMatches == 0) && (offset > 0)) {          if ((numMatches == 0) && (offset > 0)) {
578              /* Copy the initial portion of the string in if an offset              /* Copy the initial portion of the string in if an offset
579               * was specified.               * was specified.
580               */               */
581              Tcl_AppendUnicodeToObj(resultPtr, wstring, offset);              Tcl_AppendUnicodeToObj(resultPtr, wstring, offset);
582          }          }
583          numMatches++;          numMatches++;
584    
585          /*          /*
586           * Copy the portion of the source string before the match to the           * Copy the portion of the source string before the match to the
587           * result variable.           * result variable.
588           */           */
589    
590          Tcl_RegExpGetInfo(regExpr, &info);          Tcl_RegExpGetInfo(regExpr, &info);
591          start = info.matches[0].start;          start = info.matches[0].start;
592          end = info.matches[0].end;          end = info.matches[0].end;
593          Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, start);          Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, start);
594    
595          /*          /*
596           * Append the subSpec argument to the variable, making appropriate           * Append the subSpec argument to the variable, making appropriate
597           * substitutions.  This code is a bit hairy because of the backslash           * substitutions.  This code is a bit hairy because of the backslash
598           * conventions and because the code saves up ranges of characters in           * conventions and because the code saves up ranges of characters in
599           * subSpec to reduce the number of calls to Tcl_SetVar.           * subSpec to reduce the number of calls to Tcl_SetVar.
600           */           */
601    
602          src = subspec;          src = subspec;
603          firstChar = subspec;          firstChar = subspec;
604          for (c = *src; c != '\0'; src++, c = *src) {          for (c = *src; c != '\0'; src++, c = *src) {
605              int index;              int index;
606            
607              if (c == '&') {              if (c == '&') {
608                  index = 0;                  index = 0;
609              } else if (c == '\\') {              } else if (c == '\\') {
610                  c = src[1];                  c = src[1];
611                  if ((c >= '0') && (c <= '9')) {                  if ((c >= '0') && (c <= '9')) {
612                      index = c - '0';                      index = c - '0';
613                  } else if ((c == '\\') || (c == '&')) {                  } else if ((c == '\\') || (c == '&')) {
614                      Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);                      Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);
615                      Tcl_AppendToObj(resultPtr, &c, 1);                      Tcl_AppendToObj(resultPtr, &c, 1);
616                      firstChar = src + 2;                      firstChar = src + 2;
617                      src++;                      src++;
618                      continue;                      continue;
619                  } else {                  } else {
620                      continue;                      continue;
621                  }                  }
622              } else {              } else {
623                  continue;                  continue;
624              }              }
625              if (firstChar != src) {              if (firstChar != src) {
626                  Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);                  Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);
627              }              }
628              if (index <= info.nsubs) {              if (index <= info.nsubs) {
629                  subStart = info.matches[index].start;                  subStart = info.matches[index].start;
630                  subEnd = info.matches[index].end;                  subEnd = info.matches[index].end;
631                  if ((subStart >= 0) && (subEnd >= 0)) {                  if ((subStart >= 0) && (subEnd >= 0)) {
632                      Tcl_AppendUnicodeToObj(resultPtr,                      Tcl_AppendUnicodeToObj(resultPtr,
633                              wstring + offset + subStart, subEnd - subStart);                              wstring + offset + subStart, subEnd - subStart);
634                  }                  }
635              }              }
636              if (*src == '\\') {              if (*src == '\\') {
637                  src++;                  src++;
638              }              }
639              firstChar = src + 1;              firstChar = src + 1;
640          }          }
641          if (firstChar != src) {          if (firstChar != src) {
642              Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);              Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);
643          }          }
644          if (end == 0) {          if (end == 0) {
645              /*              /*
646               * Always consume at least one character of the input string               * Always consume at least one character of the input string
647               * in order to prevent infinite loops.               * in order to prevent infinite loops.
648               */               */
649    
650              Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);              Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
651              offset++;              offset++;
652          }          }
653          offset += end;          offset += end;
654          if (!all) {          if (!all) {
655              break;              break;
656          }          }
657      }      }
658    
659      /*      /*
660       * Copy the portion of the source string after the last match to the       * Copy the portion of the source string after the last match to the
661       * result variable.       * result variable.
662       */       */
663    
664      if (numMatches == 0) {      if (numMatches == 0) {
665          /*          /*
666           * On zero matches, just ignore the offset, since it shouldn't           * On zero matches, just ignore the offset, since it shouldn't
667           * matter to us in this case, and the user may have skewed it.           * matter to us in this case, and the user may have skewed it.
668           */           */
669          Tcl_AppendUnicodeToObj(resultPtr, wstring, wlen);          Tcl_AppendUnicodeToObj(resultPtr, wstring, wlen);
670      } else if (offset < wlen) {      } else if (offset < wlen) {
671          Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset);          Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset);
672      }      }
673      if (Tcl_ObjSetVar2(interp, varPtr, NULL, resultPtr, 0) == NULL) {      if (Tcl_ObjSetVar2(interp, varPtr, NULL, resultPtr, 0) == NULL) {
674          Tcl_AppendResult(interp, "couldn't set variable \"",          Tcl_AppendResult(interp, "couldn't set variable \"",
675                  Tcl_GetString(varPtr), "\"", (char *) NULL);                  Tcl_GetString(varPtr), "\"", (char *) NULL);
676          result = TCL_ERROR;          result = TCL_ERROR;
677      } else {      } else {
678          /*          /*
679           * Set the interpreter's object result to an integer object holding the           * Set the interpreter's object result to an integer object holding the
680           * number of matches.           * number of matches.
681           */           */
682                    
683          Tcl_SetIntObj(Tcl_GetObjResult(interp), numMatches);          Tcl_SetIntObj(Tcl_GetObjResult(interp), numMatches);
684      }      }
685    
686      done:      done:
687      Tcl_DecrRefCount(resultPtr);      Tcl_DecrRefCount(resultPtr);
688      return result;      return result;
689  }  }
690    
691  /*  /*
692   *----------------------------------------------------------------------   *----------------------------------------------------------------------
693   *   *
694   * Tcl_RenameObjCmd --   * Tcl_RenameObjCmd --
695   *   *
696   *      This procedure is invoked to process the "rename" Tcl command.   *      This procedure is invoked to process the "rename" Tcl command.
697   *      See the user documentation for details on what it does.   *      See the user documentation for details on what it does.
698   *   *
699   * Results:   * Results:
700   *      A standard Tcl object result.   *      A standard Tcl object result.
701   *   *
702   * Side effects:   * Side effects:
703   *      See the user documentation.   *      See the user documentation.
704   *   *
705   *----------------------------------------------------------------------   *----------------------------------------------------------------------
706   */   */
707    
708          /* ARGSUSED */          /* ARGSUSED */
709  int  int
710  Tcl_RenameObjCmd(dummy, interp, objc, objv)  Tcl_RenameObjCmd(dummy, interp, objc, objv)
711      ClientData dummy;           /* Arbitrary value passed to the command. */      ClientData dummy;           /* Arbitrary value passed to the command. */
712      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
713      int objc;                   /* Number of arguments. */      int objc;                   /* Number of arguments. */
714      Tcl_Obj *CONST objv[];      /* Argument objects. */      Tcl_Obj *CONST objv[];      /* Argument objects. */
715  {  {
716      char *oldName, *newName;      char *oldName, *newName;
717            
718      if (objc != 3) {      if (objc != 3) {
719          Tcl_WrongNumArgs(interp, 1, objv, "oldName newName");          Tcl_WrongNumArgs(interp, 1, objv, "oldName newName");
720          return TCL_ERROR;          return TCL_ERROR;
721      }      }
722    
723      oldName = Tcl_GetString(objv[1]);      oldName = Tcl_GetString(objv[1]);
724      newName = Tcl_GetString(objv[2]);      newName = Tcl_GetString(objv[2]);
725      return TclRenameCommand(interp, oldName, newName);      return TclRenameCommand(interp, oldName, newName);
726  }  }
727    
728  /*  /*
729   *----------------------------------------------------------------------   *----------------------------------------------------------------------
730   *   *
731   * Tcl_ReturnObjCmd --   * Tcl_ReturnObjCmd --
732   *   *
733   *      This object-based procedure is invoked to process the "return" Tcl   *      This object-based procedure is invoked to process the "return" Tcl
734   *      command. See the user documentation for details on what it does.   *      command. See the user documentation for details on what it does.
735   *   *
736   * Results:   * Results:
737   *      A standard Tcl object result.   *      A standard Tcl object result.
738   *   *
739   * Side effects:   * Side effects:
740   *      See the user documentation.   *      See the user documentation.
741   *   *
742   *----------------------------------------------------------------------   *----------------------------------------------------------------------
743   */   */
744    
745          /* ARGSUSED */          /* ARGSUSED */
746  int  int
747  Tcl_ReturnObjCmd(dummy, interp, objc, objv)  Tcl_ReturnObjCmd(dummy, interp, objc, objv)
748      ClientData dummy;           /* Not used. */      ClientData dummy;           /* Not used. */
749      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
750      int objc;                   /* Number of arguments. */      int objc;                   /* Number of arguments. */
751      Tcl_Obj *CONST objv[];      /* Argument objects. */      Tcl_Obj *CONST objv[];      /* Argument objects. */
752  {  {
753      Interp *iPtr = (Interp *) interp;      Interp *iPtr = (Interp *) interp;
754      int optionLen, argLen, code, result;      int optionLen, argLen, code, result;
755    
756      if (iPtr->errorInfo != NULL) {      if (iPtr->errorInfo != NULL) {
757          ckfree(iPtr->errorInfo);          ckfree(iPtr->errorInfo);
758          iPtr->errorInfo = NULL;          iPtr->errorInfo = NULL;
759      }      }
760      if (iPtr->errorCode != NULL) {      if (iPtr->errorCode != NULL) {
761          ckfree(iPtr->errorCode);          ckfree(iPtr->errorCode);
762          iPtr->errorCode = NULL;          iPtr->errorCode = NULL;
763      }      }
764      code = TCL_OK;      code = TCL_OK;
765            
766      for (objv++, objc--;  objc > 1;  objv += 2, objc -= 2) {      for (objv++, objc--;  objc > 1;  objv += 2, objc -= 2) {
767          char *option = Tcl_GetStringFromObj(objv[0], &optionLen);          char *option = Tcl_GetStringFromObj(objv[0], &optionLen);
768          char *arg = Tcl_GetStringFromObj(objv[1], &argLen);          char *arg = Tcl_GetStringFromObj(objv[1], &argLen);
769                    
770          if (strcmp(option, "-code") == 0) {          if (strcmp(option, "-code") == 0) {
771              register int c = arg[0];              register int c = arg[0];
772              if ((c == 'o') && (strcmp(arg, "ok") == 0)) {              if ((c == 'o') && (strcmp(arg, "ok") == 0)) {
773                  code = TCL_OK;                  code = TCL_OK;
774              } else if ((c == 'e') && (strcmp(arg, "error") == 0)) {              } else if ((c == 'e') && (strcmp(arg, "error") == 0)) {
775                  code = TCL_ERROR;                  code = TCL_ERROR;
776              } else if ((c == 'r') && (strcmp(arg, "return") == 0)) {              } else if ((c == 'r') && (strcmp(arg, "return") == 0)) {
777                  code = TCL_RETURN;                  code = TCL_RETURN;
778              } else if ((c == 'b') && (strcmp(arg, "break") == 0)) {              } else if ((c == 'b') && (strcmp(arg, "break") == 0)) {
779                  code = TCL_BREAK;                  code = TCL_BREAK;
780              } else if ((c == 'c') && (strcmp(arg, "continue") == 0)) {              } else if ((c == 'c') && (strcmp(arg, "continue") == 0)) {
781                  code = TCL_CONTINUE;                  code = TCL_CONTINUE;
782              } else {              } else {
783                  result = Tcl_GetIntFromObj((Tcl_Interp *) NULL, objv[1],                  result = Tcl_GetIntFromObj((Tcl_Interp *) NULL, objv[1],
784                          &code);                          &code);
785                  if (result != TCL_OK) {                  if (result != TCL_OK) {
786                      Tcl_ResetResult(interp);                      Tcl_ResetResult(interp);
787                      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),                      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
788                              "bad completion code \"",                              "bad completion code \"",
789                              Tcl_GetString(objv[1]),                              Tcl_GetString(objv[1]),
790                              "\": must be ok, error, return, break, ",                              "\": must be ok, error, return, break, ",
791                              "continue, or an integer", (char *) NULL);                              "continue, or an integer", (char *) NULL);
792                      return result;                      return result;
793                  }                  }
794              }              }
795          } else if (strcmp(option, "-errorinfo") == 0) {          } else if (strcmp(option, "-errorinfo") == 0) {
796              iPtr->errorInfo =              iPtr->errorInfo =
797                  (char *) ckalloc((unsigned) (strlen(arg) + 1));                  (char *) ckalloc((unsigned) (strlen(arg) + 1));
798              strcpy(iPtr->errorInfo, arg);              strcpy(iPtr->errorInfo, arg);
799          } else if (strcmp(option, "-errorcode") == 0) {          } else if (strcmp(option, "-errorcode") == 0) {
800              iPtr->errorCode =              iPtr->errorCode =
801                  (char *) ckalloc((unsigned) (strlen(arg) + 1));                  (char *) ckalloc((unsigned) (strlen(arg) + 1));
802              strcpy(iPtr->errorCode, arg);              strcpy(iPtr->errorCode, arg);
803          } else {          } else {
804              Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),              Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
805                      "bad option \"", option,                      "bad option \"", option,
806                      "\": must be -code, -errorcode, or -errorinfo",                      "\": must be -code, -errorcode, or -errorinfo",
807                      (char *) NULL);                      (char *) NULL);
808              return TCL_ERROR;              return TCL_ERROR;
809          }          }
810      }      }
811            
812      if (objc == 1) {      if (objc == 1) {
813          /*          /*
814           * Set the interpreter's object result. An inline version of           * Set the interpreter's object result. An inline version of
815           * Tcl_SetObjResult.           * Tcl_SetObjResult.
816           */           */
817    
818          Tcl_SetObjResult(interp, objv[0]);          Tcl_SetObjResult(interp, objv[0]);
819      }      }
820      iPtr->returnCode = code;      iPtr->returnCode = code;
821      return TCL_RETURN;      return TCL_RETURN;
822  }  }
823    
824  /*  /*
825   *----------------------------------------------------------------------   *----------------------------------------------------------------------
826   *   *
827   * Tcl_SourceObjCmd --   * Tcl_SourceObjCmd --
828   *   *
829   *      This procedure is invoked to process the "source" Tcl command.   *      This procedure is invoked to process the "source" Tcl command.
830   *      See the user documentation for details on what it does.   *      See the user documentation for details on what it does.
831   *   *
832   * Results:   * Results:
833   *      A standard Tcl object result.   *      A standard Tcl object result.
834   *   *
835   * Side effects:   * Side effects:
836   *      See the user documentation.   *      See the user documentation.
837   *   *
838   *----------------------------------------------------------------------   *----------------------------------------------------------------------
839   */   */
840    
841          /* ARGSUSED */          /* ARGSUSED */
842  int  int
843  Tcl_SourceObjCmd(dummy, interp, objc, objv)  Tcl_SourceObjCmd(dummy, interp, objc, objv)
844      ClientData dummy;           /* Not used. */      ClientData dummy;           /* Not used. */
845      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
846      int objc;                   /* Number of arguments. */      int objc;                   /* Number of arguments. */
847      Tcl_Obj *CONST objv[];      /* Argument objects. */      Tcl_Obj *CONST objv[];      /* Argument objects. */
848  {  {
849      char *bytes;      char *bytes;
850      int result;      int result;
851            
852      if (objc != 2) {      if (objc != 2) {
853          Tcl_WrongNumArgs(interp, 1, objv, "fileName");          Tcl_WrongNumArgs(interp, 1, objv, "fileName");
854          return TCL_ERROR;          return TCL_ERROR;
855      }      }
856    
857      bytes = Tcl_GetString(objv[1]);      bytes = Tcl_GetString(objv[1]);
858      result = Tcl_EvalFile(interp, bytes);      result = Tcl_EvalFile(interp, bytes);
859      return result;      return result;
860  }  }
861    
862  /*  /*
863   *----------------------------------------------------------------------   *----------------------------------------------------------------------
864   *   *
865   * Tcl_SplitObjCmd --   * Tcl_SplitObjCmd --
866   *   *
867   *      This procedure is invoked to process the "split" Tcl command.   *      This procedure is invoked to process the "split" Tcl command.
868   *      See the user documentation for details on what it does.   *      See the user documentation for details on what it does.
869   *   *
870   * Results:   * Results:
871   *      A standard Tcl result.   *      A standard Tcl result.
872   *   *
873   * Side effects:   * Side effects:
874   *      See the user documentation.   *      See the user documentation.
875   *   *
876   *----------------------------------------------------------------------   *----------------------------------------------------------------------
877   */   */
878    
879          /* ARGSUSED */          /* ARGSUSED */
880  int  int
881  Tcl_SplitObjCmd(dummy, interp, objc, objv)  Tcl_SplitObjCmd(dummy, interp, objc, objv)
882      ClientData dummy;           /* Not used. */      ClientData dummy;           /* Not used. */
883      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
884      int objc;                   /* Number of arguments. */      int objc;                   /* Number of arguments. */
885      Tcl_Obj *CONST objv[];      /* Argument objects. */      Tcl_Obj *CONST objv[];      /* Argument objects. */
886  {  {
887      Tcl_UniChar ch;      Tcl_UniChar ch;
888      int len;      int len;
889      char *splitChars, *string, *end;      char *splitChars, *string, *end;
890      int splitCharLen, stringLen;      int splitCharLen, stringLen;
891      Tcl_Obj *listPtr, *objPtr;      Tcl_Obj *listPtr, *objPtr;
892    
893      if (objc == 2) {      if (objc == 2) {
894          splitChars = " \n\t\r";          splitChars = " \n\t\r";
895          splitCharLen = 4;          splitCharLen = 4;
896      } else if (objc == 3) {      } else if (objc == 3) {
897          splitChars = Tcl_GetStringFromObj(objv[2], &splitCharLen);          splitChars = Tcl_GetStringFromObj(objv[2], &splitCharLen);
898      } else {      } else {
899          Tcl_WrongNumArgs(interp, 1, objv, "string ?splitChars?");          Tcl_WrongNumArgs(interp, 1, objv, "string ?splitChars?");
900          return TCL_ERROR;          return TCL_ERROR;
901      }      }
902    
903      string = Tcl_GetStringFromObj(objv[1], &stringLen);      string = Tcl_GetStringFromObj(objv[1], &stringLen);
904      end = string + stringLen;      end = string + stringLen;
905      listPtr = Tcl_GetObjResult(interp);      listPtr = Tcl_GetObjResult(interp);
906            
907      if (stringLen == 0) {      if (stringLen == 0) {
908          /*          /*
909           * Do nothing.           * Do nothing.
910           */           */
911      } else if (splitCharLen == 0) {      } else if (splitCharLen == 0) {
912          /*          /*
913           * Handle the special case of splitting on every character.           * Handle the special case of splitting on every character.
914           */           */
915    
916          for ( ; string < end; string += len) {          for ( ; string < end; string += len) {
917              len = Tcl_UtfToUniChar(string, &ch);              len = Tcl_UtfToUniChar(string, &ch);
918              objPtr = Tcl_NewStringObj(string, len);              objPtr = Tcl_NewStringObj(string, len);
919              Tcl_ListObjAppendElement(NULL, listPtr, objPtr);              Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
920          }          }
921      } else {      } else {
922          char *element, *p, *splitEnd;          char *element, *p, *splitEnd;
923          int splitLen;          int splitLen;
924          Tcl_UniChar splitChar;          Tcl_UniChar splitChar;
925                    
926          /*          /*
927           * Normal case: split on any of a given set of characters.           * Normal case: split on any of a given set of characters.
928           * Discard instances of the split characters.           * Discard instances of the split characters.
929           */           */
930    
931          splitEnd = splitChars + splitCharLen;          splitEnd = splitChars + splitCharLen;
932    
933          for (element = string; string < end; string += len) {          for (element = string; string < end; string += len) {
934              len = Tcl_UtfToUniChar(string, &ch);              len = Tcl_UtfToUniChar(string, &ch);
935              for (p = splitChars; p < splitEnd; p += splitLen) {              for (p = splitChars; p < splitEnd; p += splitLen) {
936                  splitLen = Tcl_UtfToUniChar(p, &splitChar);                  splitLen = Tcl_UtfToUniChar(p, &splitChar);
937                  if (ch == splitChar) {                  if (ch == splitChar) {
938                      objPtr = Tcl_NewStringObj(element, string - element);                      objPtr = Tcl_NewStringObj(element, string - element);
939                      Tcl_ListObjAppendElement(NULL, listPtr, objPtr);                      Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
940                      element = string + len;                      element = string + len;
941                      break;                      break;
942                  }                  }
943              }              }
944          }          }
945          objPtr = Tcl_NewStringObj(element, string - element);          objPtr = Tcl_NewStringObj(element, string - element);
946          Tcl_ListObjAppendElement(NULL, listPtr, objPtr);          Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
947      }      }
948      return TCL_OK;      return TCL_OK;
949  }  }
950    
951  /*  /*
952   *----------------------------------------------------------------------   *----------------------------------------------------------------------
953   *   *
954   * Tcl_StringObjCmd --   * Tcl_StringObjCmd --
955   *   *
956   *      This procedure is invoked to process the "string" Tcl command.   *      This procedure is invoked to process the "string" Tcl command.
957   *      See the user documentation for details on what it does.  Note   *      See the user documentation for details on what it does.  Note
958   *      that this command only functions correctly on properly formed   *      that this command only functions correctly on properly formed
959   *      Tcl UTF strings.   *      Tcl UTF strings.
960   *   *
961   * Results:   * Results:
962   *      A standard Tcl result.   *      A standard Tcl result.
963   *   *
964   * Side effects:   * Side effects:
965   *      See the user documentation.   *      See the user documentation.
966   *   *
967   *----------------------------------------------------------------------   *----------------------------------------------------------------------
968   */   */
969    
970          /* ARGSUSED */          /* ARGSUSED */
971  int  int
972  Tcl_StringObjCmd(dummy, interp, objc, objv)  Tcl_StringObjCmd(dummy, interp, objc, objv)
973      ClientData dummy;           /* Not used. */      ClientData dummy;           /* Not used. */
974      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
975      int objc;                   /* Number of arguments. */      int objc;                   /* Number of arguments. */
976      Tcl_Obj *CONST objv[];      /* Argument objects. */      Tcl_Obj *CONST objv[];      /* Argument objects. */
977  {  {
978      int index, left, right;      int index, left, right;
979      Tcl_Obj *resultPtr;      Tcl_Obj *resultPtr;
980      char *string1, *string2;      char *string1, *string2;
981      int length1, length2;      int length1, length2;
982      static char *options[] = {      static char *options[] = {
983          "bytelength",   "compare",      "equal",        "first",          "bytelength",   "compare",      "equal",        "first",
984          "index",        "is",           "last",         "length",          "index",        "is",           "last",         "length",
985          "map",          "match",        "range",        "repeat",          "map",          "match",        "range",        "repeat",
986          "replace",      "tolower",      "toupper",      "totitle",          "replace",      "tolower",      "toupper",      "totitle",
987          "trim",         "trimleft",     "trimright",          "trim",         "trimleft",     "trimright",
988          "wordend",      "wordstart",    (char *) NULL          "wordend",      "wordstart",    (char *) NULL
989      };      };
990      enum options {      enum options {
991          STR_BYTELENGTH, STR_COMPARE,    STR_EQUAL,      STR_FIRST,          STR_BYTELENGTH, STR_COMPARE,    STR_EQUAL,      STR_FIRST,
992          STR_INDEX,      STR_IS,         STR_LAST,       STR_LENGTH,          STR_INDEX,      STR_IS,         STR_LAST,       STR_LENGTH,
993          STR_MAP,        STR_MATCH,      STR_RANGE,      STR_REPEAT,          STR_MAP,        STR_MATCH,      STR_RANGE,      STR_REPEAT,
994          STR_REPLACE,    STR_TOLOWER,    STR_TOUPPER,    STR_TOTITLE,          STR_REPLACE,    STR_TOLOWER,    STR_TOUPPER,    STR_TOTITLE,
995          STR_TRIM,       STR_TRIMLEFT,   STR_TRIMRIGHT,          STR_TRIM,       STR_TRIMLEFT,   STR_TRIMRIGHT,
996          STR_WORDEND,    STR_WORDSTART          STR_WORDEND,    STR_WORDSTART
997      };          };    
998    
999      if (objc < 2) {      if (objc < 2) {
1000          Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");          Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
1001          return TCL_ERROR;          return TCL_ERROR;
1002      }      }
1003            
1004      if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0,      if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0,
1005              &index) != TCL_OK) {              &index) != TCL_OK) {
1006          return TCL_ERROR;          return TCL_ERROR;
1007      }      }
1008    
1009      resultPtr = Tcl_GetObjResult(interp);      resultPtr = Tcl_GetObjResult(interp);
1010      switch ((enum options) index) {      switch ((enum options) index) {
1011          case STR_EQUAL:          case STR_EQUAL:
1012          case STR_COMPARE: {          case STR_COMPARE: {
1013              int i, match, length, nocase = 0, reqlength = -1;              int i, match, length, nocase = 0, reqlength = -1;
1014    
1015              if (objc < 4 || objc > 7) {              if (objc < 4 || objc > 7) {
1016              str_cmp_args:              str_cmp_args:
1017                  Tcl_WrongNumArgs(interp, 2, objv,                  Tcl_WrongNumArgs(interp, 2, objv,
1018                                   "?-nocase? ?-length int? string1 string2");                                   "?-nocase? ?-length int? string1 string2");
1019                  return TCL_ERROR;                  return TCL_ERROR;
1020              }              }
1021    
1022              for (i = 2; i < objc-2; i++) {              for (i = 2; i < objc-2; i++) {
1023                  string2 = Tcl_GetStringFromObj(objv[i], &length2);                  string2 = Tcl_GetStringFromObj(objv[i], &length2);
1024                  if ((length2 > 1)                  if ((length2 > 1)
1025                          && strncmp(string2, "-nocase", (size_t) length2) == 0) {                          && strncmp(string2, "-nocase", (size_t) length2) == 0) {
1026                      nocase = 1;                      nocase = 1;
1027                  } else if ((length2 > 1)                  } else if ((length2 > 1)
1028                          && strncmp(string2, "-length", (size_t) length2) == 0) {                          && strncmp(string2, "-length", (size_t) length2) == 0) {
1029                      if (i+1 >= objc-2) {                      if (i+1 >= objc-2) {
1030                          goto str_cmp_args;                          goto str_cmp_args;
1031                      }                      }
1032                      if (Tcl_GetIntFromObj(interp, objv[++i],                      if (Tcl_GetIntFromObj(interp, objv[++i],
1033                              &reqlength) != TCL_OK) {                              &reqlength) != TCL_OK) {
1034                          return TCL_ERROR;                          return TCL_ERROR;
1035                      }                      }
1036                  } else {                  } else {
1037                      Tcl_AppendStringsToObj(resultPtr, "bad option \"",                      Tcl_AppendStringsToObj(resultPtr, "bad option \"",
1038                              string2, "\": must be -nocase or -length",                              string2, "\": must be -nocase or -length",
1039                              (char *) NULL);                              (char *) NULL);
1040                      return TCL_ERROR;                      return TCL_ERROR;
1041                  }                  }
1042              }              }
1043    
1044              string1 = Tcl_GetStringFromObj(objv[objc-2], &length1);              string1 = Tcl_GetStringFromObj(objv[objc-2], &length1);
1045              string2 = Tcl_GetStringFromObj(objv[objc-1], &length2);              string2 = Tcl_GetStringFromObj(objv[objc-1], &length2);
1046              /*              /*
1047               * This is the min length IN BYTES of the two strings               * This is the min length IN BYTES of the two strings
1048               */               */
1049              length = (length1 < length2) ? length1 : length2;              length = (length1 < length2) ? length1 : length2;
1050    
1051              if (reqlength == 0) {              if (reqlength == 0) {
1052                  /*                  /*
1053                   * Anything matches at 0 chars, right?                   * Anything matches at 0 chars, right?
1054                   */                   */
1055    
1056                  match = 0;                  match = 0;
1057              } else if (nocase || ((reqlength > 0) && (reqlength <= length))) {              } else if (nocase || ((reqlength > 0) && (reqlength <= length))) {
1058                  /*                  /*
1059                   * with -nocase or -length we have to check true char length                   * with -nocase or -length we have to check true char length
1060                   * as it could be smaller than expected                   * as it could be smaller than expected
1061                   */                   */
1062    
1063                  length1 = Tcl_NumUtfChars(string1, length1);                  length1 = Tcl_NumUtfChars(string1, length1);
1064                  length2 = Tcl_NumUtfChars(string2, length2);                  length2 = Tcl_NumUtfChars(string2, length2);
1065                  length = (length1 < length2) ? length1 : length2;                  length = (length1 < length2) ? length1 : length2;
1066    
1067                  /*                  /*
1068                   * Do the reqlength check again, against 0 as well for                   * Do the reqlength check again, against 0 as well for
1069                   * the benfit of nocase                   * the benfit of nocase
1070                   */                   */
1071    
1072                  if ((reqlength > 0) && (reqlength < length)) {                  if ((reqlength > 0) && (reqlength < length)) {
1073                      length = reqlength;                      length = reqlength;
1074                  } else if (reqlength < 0) {                  } else if (reqlength < 0) {
1075                      /*                      /*
1076                       * The requested length is negative, so we ignore it by                       * The requested length is negative, so we ignore it by
1077                       * setting it to the longer of the two lengths.                       * setting it to the longer of the two lengths.
1078                       */                       */
1079    
1080                      reqlength = (length1 > length2) ? length1 : length2;                      reqlength = (length1 > length2) ? length1 : length2;
1081                  }                  }
1082                  if (nocase) {                  if (nocase) {
1083                      match = Tcl_UtfNcasecmp(string1, string2,                      match = Tcl_UtfNcasecmp(string1, string2,
1084                              (unsigned) length);                              (unsigned) length);
1085                  } else {                  } else {
1086                      match = Tcl_UtfNcmp(string1, string2, (unsigned) length);                      match = Tcl_UtfNcmp(string1, string2, (unsigned) length);
1087                  }                  }
1088                  if ((match == 0) && (reqlength > length)) {                  if ((match == 0) && (reqlength > length)) {
1089                      match = length1 - length2;                      match = length1 - length2;
1090                  }                  }
1091              } else {              } else {
1092                  match = memcmp(string1, string2, (unsigned) length);                  match = memcmp(string1, string2, (unsigned) length);
1093                  if (match == 0) {                  if (match == 0) {
1094                      match = length1 - length2;                      match = length1 - length2;
1095                  }                  }
1096              }              }
1097    
1098              if ((enum options) index == STR_EQUAL) {              if ((enum options) index == STR_EQUAL) {
1099                  Tcl_SetBooleanObj(resultPtr, (match) ? 0 : 1);                  Tcl_SetBooleanObj(resultPtr, (match) ? 0 : 1);
1100              } else {              } else {
1101                  Tcl_SetIntObj(resultPtr, ((match > 0) ? 1 :                  Tcl_SetIntObj(resultPtr, ((match > 0) ? 1 :
1102                                            (match < 0) ? -1 : 0));                                            (match < 0) ? -1 : 0));
1103              }              }
1104              break;              break;
1105          }          }
1106          case STR_FIRST: {          case STR_FIRST: {
1107              register char *p, *end;              register char *p, *end;
1108              int match, utflen, start;              int match, utflen, start;
1109    
1110              if (objc < 4 || objc > 5) {              if (objc < 4 || objc > 5) {
1111                  Tcl_WrongNumArgs(interp, 2, objv,                  Tcl_WrongNumArgs(interp, 2, objv,
1112                                   "string1 string2 ?startIndex?");                                   "string1 string2 ?startIndex?");
1113                  return TCL_ERROR;                  return TCL_ERROR;
1114              }              }
1115    
1116              /*              /*
1117               * This algorithm fails on improperly formed UTF strings.               * This algorithm fails on improperly formed UTF strings.
1118               * We are searching string2 for the sequence string1.               * We are searching string2 for the sequence string1.
1119               */               */
1120    
1121              match = -1;              match = -1;
1122              start = 0;              start = 0;
1123              utflen = -1;              utflen = -1;
1124              string1 = Tcl_GetStringFromObj(objv[2], &length1);              string1 = Tcl_GetStringFromObj(objv[2], &length1);
1125              string2 = Tcl_GetStringFromObj(objv[3], &length2);              string2 = Tcl_GetStringFromObj(objv[3], &length2);
1126    
1127              if (objc == 5) {              if (objc == 5) {
1128                  /*                  /*
1129                   * If a startIndex is specified, we will need to fast forward                   * If a startIndex is specified, we will need to fast forward
1130                   * to that point in the string before we think about a match                   * to that point in the string before we think about a match
1131                   */                   */
1132                  utflen = Tcl_NumUtfChars(string2, length2);                  utflen = Tcl_NumUtfChars(string2, length2);
1133                  if (TclGetIntForIndex(interp, objv[4], utflen-1,                  if (TclGetIntForIndex(interp, objv[4], utflen-1,
1134                                        &start) != TCL_OK) {                                        &start) != TCL_OK) {
1135                      return TCL_ERROR;                      return TCL_ERROR;
1136                  }                  }
1137                  if (start >= utflen) {                  if (start >= utflen) {
1138                      goto str_first_done;                      goto str_first_done;
1139                  } else if (start > 0) {                  } else if (start > 0) {
1140                      if (length2 == utflen) {                      if (length2 == utflen) {
1141                          /* no unicode chars */                          /* no unicode chars */
1142                          string2 += start;                          string2 += start;
1143                          length2 -= start;                          length2 -= start;
1144                      } else {                      } else {
1145                          char *s = Tcl_UtfAtIndex(string2, start);                          char *s = Tcl_UtfAtIndex(string2, start);
1146                          length2 -= s - string2;                          length2 -= s - string2;
1147                          string2 = s;                          string2 = s;
1148                      }                      }
1149                  }                  }
1150              }              }
1151    
1152              if (length1 > 0) {              if (length1 > 0) {
1153                  end = string2 + length2 - length1 + 1;                  end = string2 + length2 - length1 + 1;
1154                  for (p = string2;  p < end;  p++) {                  for (p = string2;  p < end;  p++) {
1155                      /*                      /*
1156                       * Scan forward to find the first character.                       * Scan forward to find the first character.
1157                       */                       */
1158    
1159                      p = memchr(p, *string1, (unsigned) (end - p));                      p = memchr(p, *string1, (unsigned) (end - p));
1160                      if (p == NULL) {                      if (p == NULL) {
1161                          break;                          break;
1162                      }                      }
1163                      if (memcmp(string1, p, (unsigned) length1) == 0) {                      if (memcmp(string1, p, (unsigned) length1) == 0) {
1164                          match = p - string2;                          match = p - string2;
1165                          break;                          break;
1166                      }                      }
1167                  }                  }
1168              }              }
1169    
1170              /*              /*
1171               * Compute the character index of the matching string by               * Compute the character index of the matching string by
1172               * counting the number of characters before the match.               * counting the number of characters before the match.
1173               */               */
1174          str_first_done:          str_first_done:
1175              if (match != -1) {              if (match != -1) {
1176                  if (objc == 4) {                  if (objc == 4) {
1177                      match = Tcl_NumUtfChars(string2, match);                      match = Tcl_NumUtfChars(string2, match);
1178                  } else if (length2 == utflen) {                  } else if (length2 == utflen) {
1179                      /* no unicode chars */                      /* no unicode chars */
1180                      match += start;                      match += start;
1181                  } else {                  } else {
1182                      match = start + Tcl_NumUtfChars(string2, match);                      match = start + Tcl_NumUtfChars(string2, match);
1183                  }                  }
1184              }              }
1185              Tcl_SetIntObj(resultPtr, match);              Tcl_SetIntObj(resultPtr, match);
1186              break;              break;
1187          }          }
1188          case STR_INDEX: {          case STR_INDEX: {
1189              char buf[TCL_UTF_MAX];              char buf[TCL_UTF_MAX];
1190              Tcl_UniChar unichar;              Tcl_UniChar unichar;
1191    
1192              if (objc != 4) {              if (objc != 4) {
1193                  Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");                  Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");
1194                  return TCL_ERROR;                  return TCL_ERROR;
1195              }              }
1196    
1197              /*              /*
1198               * If we have a ByteArray object, avoid indexing in the               * If we have a ByteArray object, avoid indexing in the
1199               * Utf string since the byte array contains one byte per               * Utf string since the byte array contains one byte per
1200               * character.  Otherwise, use the Unicode string rep to               * character.  Otherwise, use the Unicode string rep to
1201               * get the index'th char.               * get the index'th char.
1202               */               */
1203    
1204              if (objv[2]->typePtr == &tclByteArrayType) {              if (objv[2]->typePtr == &tclByteArrayType) {
1205    
1206                  string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);                  string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);
1207    
1208                  if (TclGetIntForIndex(interp, objv[3], length1 - 1,                  if (TclGetIntForIndex(interp, objv[3], length1 - 1,
1209                          &index) != TCL_OK) {                          &index) != TCL_OK) {
1210                      return TCL_ERROR;                      return TCL_ERROR;
1211                  }                  }
1212                  Tcl_SetByteArrayObj(resultPtr,                  Tcl_SetByteArrayObj(resultPtr,
1213                          (unsigned char *)(&string1[index]), 1);                          (unsigned char *)(&string1[index]), 1);
1214              } else {              } else {
1215                  string1 = Tcl_GetStringFromObj(objv[2], &length1);                  string1 = Tcl_GetStringFromObj(objv[2], &length1);
1216                                    
1217                  /*                  /*
1218                   * convert to Unicode internal rep to calulate what                   * convert to Unicode internal rep to calulate what
1219                   * 'end' really means.                   * 'end' really means.
1220                   */                   */
1221    
1222                  length2 = Tcl_GetCharLength(objv[2]);                  length2 = Tcl_GetCharLength(objv[2]);
1223            
1224                  if (TclGetIntForIndex(interp, objv[3], length2 - 1,                  if (TclGetIntForIndex(interp, objv[3], length2 - 1,
1225                          &index) != TCL_OK) {                          &index) != TCL_OK) {
1226                      return TCL_ERROR;                      return TCL_ERROR;
1227                  }                  }
1228                  if ((index >= 0) && (index < length2)) {                  if ((index >= 0) && (index < length2)) {
1229                      unichar = Tcl_GetUniChar(objv[2], index);                      unichar = Tcl_GetUniChar(objv[2], index);
1230                      length2 = Tcl_UniCharToUtf((int)unichar, buf);                      length2 = Tcl_UniCharToUtf((int)unichar, buf);
1231                      Tcl_SetStringObj(resultPtr, buf, length2);                      Tcl_SetStringObj(resultPtr, buf, length2);
1232                  }                  }
1233              }              }
1234              break;              break;
1235          }          }
1236          case STR_IS: {          case STR_IS: {
1237              char *end;              char *end;
1238              Tcl_UniChar ch;              Tcl_UniChar ch;
1239    
1240              /*              /*
1241               * The UniChar comparison function               * The UniChar comparison function
1242               */               */
1243    
1244              int (*chcomp)_ANSI_ARGS_((int)) = NULL;              int (*chcomp)_ANSI_ARGS_((int)) = NULL;
1245              int i, failat = 0, result = 1, strict = 0;              int i, failat = 0, result = 1, strict = 0;
1246              Tcl_Obj *objPtr, *failVarObj = NULL;              Tcl_Obj *objPtr, *failVarObj = NULL;
1247    
1248              static char *isOptions[] = {              static char *isOptions[] = {
1249                  "alnum",        "alpha",        "ascii",        "control",                  "alnum",        "alpha",        "ascii",        "control",
1250                  "boolean",      "digit",        "double",       "false",                  "boolean",      "digit",        "double",       "false",
1251                  "graph",        "integer",      "lower",        "print",                  "graph",        "integer",      "lower",        "print",
1252                  "punct",        "space",        "true",         "upper",                  "punct",        "space",        "true",         "upper",
1253                  "wordchar",     "xdigit",       (char *) NULL                  "wordchar",     "xdigit",       (char *) NULL
1254              };              };
1255              enum isOptions {              enum isOptions {
1256                  STR_IS_ALNUM,   STR_IS_ALPHA,   STR_IS_ASCII,   STR_IS_CONTROL,                  STR_IS_ALNUM,   STR_IS_ALPHA,   STR_IS_ASCII,   STR_IS_CONTROL,
1257                  STR_IS_BOOL,    STR_IS_DIGIT,   STR_IS_DOUBLE,  STR_IS_FALSE,                  STR_IS_BOOL,    STR_IS_DIGIT,   STR_IS_DOUBLE,  STR_IS_FALSE,
1258                  STR_IS_GRAPH,   STR_IS_INT,     STR_IS_LOWER,   STR_IS_PRINT,                  STR_IS_GRAPH,   STR_IS_INT,     STR_IS_LOWER,   STR_IS_PRINT,
1259                  STR_IS_PUNCT,   STR_IS_SPACE,   STR_IS_TRUE,    STR_IS_UPPER,                  STR_IS_PUNCT,   STR_IS_SPACE,   STR_IS_TRUE,    STR_IS_UPPER,
1260                  STR_IS_WORD,    STR_IS_XDIGIT                  STR_IS_WORD,    STR_IS_XDIGIT
1261              };              };
1262    
1263              if (objc < 4 || objc > 7) {              if (objc < 4 || objc > 7) {
1264                  Tcl_WrongNumArgs(interp, 2, objv,                  Tcl_WrongNumArgs(interp, 2, objv,
1265                                   "class ?-strict? ?-failindex var? str");                                   "class ?-strict? ?-failindex var? str");
1266                  return TCL_ERROR;                  return TCL_ERROR;
1267              }              }
1268              if (Tcl_GetIndexFromObj(interp, objv[2], isOptions, "class", 0,              if (Tcl_GetIndexFromObj(interp, objv[2], isOptions, "class", 0,
1269                                      &index) != TCL_OK) {                                      &index) != TCL_OK) {
1270                  return TCL_ERROR;                  return TCL_ERROR;
1271              }              }
1272              if (objc != 4) {              if (objc != 4) {
1273                  for (i = 3; i < objc-1; i++) {                  for (i = 3; i < objc-1; i++) {
1274                      string2 = Tcl_GetStringFromObj(objv[i], &length2);                      string2 = Tcl_GetStringFromObj(objv[i], &length2);
1275                      if ((length2 > 1) &&                      if ((length2 > 1) &&
1276                          strncmp(string2, "-strict", (size_t) length2) == 0) {                          strncmp(string2, "-strict", (size_t) length2) == 0) {
1277                          strict = 1;                          strict = 1;
1278                      } else if ((length2 > 1) &&                      } else if ((length2 > 1) &&
1279                                 strncmp(string2, "-failindex", (size_t) length2) == 0) {                                 strncmp(string2, "-failindex", (size_t) length2) == 0) {
1280                          if (i+1 >= objc-1) {                          if (i+1 >= objc-1) {
1281                              Tcl_WrongNumArgs(interp, 3, objv,                              Tcl_WrongNumArgs(interp, 3, objv,
1282                                               "?-strict? ?-failindex var? str");                                               "?-strict? ?-failindex var? str");
1283                              return TCL_ERROR;                              return TCL_ERROR;
1284                          }                          }
1285                          failVarObj = objv[++i];                          failVarObj = objv[++i];
1286                      } else {                      } else {
1287                          Tcl_AppendStringsToObj(resultPtr, "bad option \"",                          Tcl_AppendStringsToObj(resultPtr, "bad option \"",
1288                                  string2, "\": must be -strict or -failindex",                                  string2, "\": must be -strict or -failindex",
1289                                  (char *) NULL);                                  (char *) NULL);
1290                          return TCL_ERROR;                          return TCL_ERROR;
1291                      }                      }
1292                  }                  }
1293              }              }
1294    
1295              /*              /*
1296               * We get the objPtr so that we can short-cut for some classes               * We get the objPtr so that we can short-cut for some classes
1297               * by checking the object type (int and double), but we need               * by checking the object type (int and double), but we need
1298               * the string otherwise, because we don't want any conversion               * the string otherwise, because we don't want any conversion
1299               * of type occuring (as, for example, Tcl_Get*FromObj would do               * of type occuring (as, for example, Tcl_Get*FromObj would do
1300               */               */
1301              objPtr = objv[objc-1];              objPtr = objv[objc-1];
1302              string1 = Tcl_GetStringFromObj(objPtr, &length1);              string1 = Tcl_GetStringFromObj(objPtr, &length1);
1303              if (length1 == 0) {              if (length1 == 0) {
1304                  if (strict) {                  if (strict) {
1305                      result = 0;                      result = 0;
1306                  }                  }
1307                  goto str_is_done;                  goto str_is_done;
1308              }              }
1309              end = string1 + length1;              end = string1 + length1;
1310    
1311              /*              /*
1312               * When entering here, result == 1 and failat == 0               * When entering here, result == 1 and failat == 0
1313               */               */
1314              switch ((enum isOptions) index) {              switch ((enum isOptions) index) {
1315                  case STR_IS_ALNUM:                  case STR_IS_ALNUM:
1316                      chcomp = Tcl_UniCharIsAlnum;                      chcomp = Tcl_UniCharIsAlnum;
1317                      break;                      break;
1318                  case STR_IS_ALPHA:                  case STR_IS_ALPHA:
1319                      chcomp = Tcl_UniCharIsAlpha;                      chcomp = Tcl_UniCharIsAlpha;
1320                      break;                      break;
1321                  case STR_IS_ASCII:                  case STR_IS_ASCII:
1322                      for (; string1 < end; string1++, failat++) {                      for (; string1 < end; string1++, failat++) {
1323                          /*                          /*
1324                           * This is a valid check in unicode, because all                           * This is a valid check in unicode, because all
1325                           * bytes < 0xC0 are single byte chars (but isascii                           * bytes < 0xC0 are single byte chars (but isascii
1326                           * limits that def'n to 0x80).                           * limits that def'n to 0x80).
1327                           */                           */
1328                          if (*((unsigned char *)string1) >= 0x80) {                          if (*((unsigned char *)string1) >= 0x80) {
1329                              result = 0;                              result = 0;
1330                              break;                              break;
1331                          }                          }
1332                      }                      }
1333                      break;                      break;
1334                  case STR_IS_BOOL:                  case STR_IS_BOOL:
1335                  case STR_IS_TRUE:                  case STR_IS_TRUE:
1336                  case STR_IS_FALSE:                  case STR_IS_FALSE:
1337                      if (objPtr->typePtr == &tclBooleanType) {                      if (objPtr->typePtr == &tclBooleanType) {
1338                          if ((((enum isOptions) index == STR_IS_TRUE) &&                          if ((((enum isOptions) index == STR_IS_TRUE) &&
1339                               objPtr->internalRep.longValue == 0) ||                               objPtr->internalRep.longValue == 0) ||
1340                              (((enum isOptions) index == STR_IS_FALSE) &&                              (((enum isOptions) index == STR_IS_FALSE) &&
1341                               objPtr->internalRep.longValue != 0)) {                               objPtr->internalRep.longValue != 0)) {
1342                              result = 0;                              result = 0;
1343                          }                          }
1344                      } else if ((Tcl_GetBoolean(NULL, string1, &i)                      } else if ((Tcl_GetBoolean(NULL, string1, &i)
1345                                  == TCL_ERROR) ||                                  == TCL_ERROR) ||
1346                                 (((enum isOptions) index == STR_IS_TRUE) &&                                 (((enum isOptions) index == STR_IS_TRUE) &&
1347                                  i == 0) ||                                  i == 0) ||
1348                                 (((enum isOptions) index == STR_IS_FALSE) &&                                 (((enum isOptions) index == STR_IS_FALSE) &&
1349                                  i != 0)) {                                  i != 0)) {
1350                          result = 0;                          result = 0;
1351                      }                      }
1352                      break;                      break;
1353                  case STR_IS_CONTROL:                  case STR_IS_CONTROL:
1354                      chcomp = Tcl_UniCharIsControl;                      chcomp = Tcl_UniCharIsControl;
1355                      break;                      break;
1356                  case STR_IS_DIGIT:                  case STR_IS_DIGIT:
1357                      chcomp = Tcl_UniCharIsDigit;                      chcomp = Tcl_UniCharIsDigit;
1358                      break;                      break;
1359                  case STR_IS_DOUBLE: {                  case STR_IS_DOUBLE: {
1360                      char *stop;                      char *stop;
1361    
1362                      if ((objPtr->typePtr == &tclDoubleType) ||                      if ((objPtr->typePtr == &tclDoubleType) ||
1363                          (objPtr->typePtr == &tclIntType)) {                          (objPtr->typePtr == &tclIntType)) {
1364                          break;                          break;
1365                      }                      }
1366                      /*                      /*
1367                       * This is adapted from Tcl_GetDouble                       * This is adapted from Tcl_GetDouble
1368                       *                       *
1369                       * The danger in this function is that                       * The danger in this function is that
1370                       * "12345678901234567890" is an acceptable 'double',                       * "12345678901234567890" is an acceptable 'double',
1371                       * but will later be interp'd as an int by something                       * but will later be interp'd as an int by something
1372                       * like [expr].  Therefore, we check to see if it looks                       * like [expr].  Therefore, we check to see if it looks
1373                       * like an int, and if so we do a range check on it.                       * like an int, and if so we do a range check on it.
1374                       * If strtoul gets to the end, we know we either                       * If strtoul gets to the end, we know we either
1375                       * received an acceptable int, or over/underflow                       * received an acceptable int, or over/underflow
1376                       */                       */
1377                      if (TclLooksLikeInt(string1, length1)) {                      if (TclLooksLikeInt(string1, length1)) {
1378                          errno = 0;                          errno = 0;
1379                          strtoul(string1, &stop, 0);                          strtoul(string1, &stop, 0);
1380                          if (stop == end) {                          if (stop == end) {
1381                              if (errno == ERANGE) {                              if (errno == ERANGE) {
1382                                  result = 0;                                  result = 0;
1383                                  failat = -1;                                  failat = -1;
1384                              }                              }
1385                              break;                              break;
1386                          }                          }
1387                      }                      }
1388                      errno = 0;                      errno = 0;
1389                      strtod(string1, &stop); /* INTL: Tcl source. */                      strtod(string1, &stop); /* INTL: Tcl source. */
1390                      if (errno == ERANGE) {                      if (errno == ERANGE) {
1391                          /*                          /*
1392                           * if (errno == ERANGE), then it was an over/underflow                           * if (errno == ERANGE), then it was an over/underflow
1393                           * problem, but in this method, we only want to know                           * problem, but in this method, we only want to know
1394                           * yes or no, so bad flow returns 0 (false) and sets                           * yes or no, so bad flow returns 0 (false) and sets
1395                           * the failVarObj to the string length.                           * the failVarObj to the string length.
1396                           */                           */
1397                          result = 0;                          result = 0;
1398                          failat = -1;                          failat = -1;
1399                      } else if (stop == string1) {                      } else if (stop == string1) {
1400                          /*                          /*
1401                           * In this case, nothing like a number was found                           * In this case, nothing like a number was found
1402                           */                           */
1403                          result = 0;                          result = 0;
1404                          failat = 0;                          failat = 0;
1405                      } else {                      } else {
1406                          /*                          /*
1407                           * Assume we sucked up one char per byte                           * Assume we sucked up one char per byte
1408                           * and then we go onto SPACE, since we are                           * and then we go onto SPACE, since we are
1409                           * allowed trailing whitespace                           * allowed trailing whitespace
1410                           */                           */
1411                          failat = stop - string1;                          failat = stop - string1;
1412                          string1 = stop;                          string1 = stop;
1413                          chcomp = Tcl_UniCharIsSpace;                          chcomp = Tcl_UniCharIsSpace;
1414                      }                      }
1415                      break;                      break;
1416                  }                  }
1417                  case STR_IS_GRAPH:                  case STR_IS_GRAPH:
1418                      chcomp = Tcl_UniCharIsGraph;                      chcomp = Tcl_UniCharIsGraph;
1419                      break;                      break;
1420                  case STR_IS_INT: {                  case STR_IS_INT: {
1421                      char *stop;                      char *stop;
1422    
1423                      if ((objPtr->typePtr == &tclIntType) ||                      if ((objPtr->typePtr == &tclIntType) ||
1424                          (Tcl_GetInt(NULL, string1, &i) == TCL_OK)) {                          (Tcl_GetInt(NULL, string1, &i) == TCL_OK)) {
1425                          break;                          break;
1426                      }                      }
1427                      /*                      /*
1428                       * Like STR_IS_DOUBLE, but we use strtoul.                       * Like STR_IS_DOUBLE, but we use strtoul.
1429                       * Since Tcl_GetInt already failed, we set result to 0.                       * Since Tcl_GetInt already failed, we set result to 0.
1430                       */                       */
1431                      result = 0;                      result = 0;
1432                      errno = 0;                      errno = 0;
1433                      strtoul(string1, &stop, 0); /* INTL: Tcl source. */                      strtoul(string1, &stop, 0); /* INTL: Tcl source. */
1434                      if (errno == ERANGE) {                      if (errno == ERANGE) {
1435                          /*                          /*
1436                           * if (errno == ERANGE), then it was an over/underflow                           * if (errno == ERANGE), then it was an over/underflow
1437                           * problem, but in this method, we only want to know                           * problem, but in this method, we only want to know
1438                           * yes or no, so bad flow returns 0 (false) and sets                           * yes or no, so bad flow returns 0 (false) and sets
1439                           * the failVarObj to the string length.                           * the failVarObj to the string length.
1440                           */                           */
1441                          failat = -1;                          failat = -1;
1442                      } else if (stop == string1) {                      } else if (stop == string1) {
1443                          /*                          /*
1444                           * In this case, nothing like a number was found                           * In this case, nothing like a number was found
1445                           */                           */
1446                          failat = 0;                          failat = 0;
1447                      } else {                      } else {
1448                          /*                          /*
1449                           * Assume we sucked up one char per byte                           * Assume we sucked up one char per byte
1450                           * and then we go onto SPACE, since we are                           * and then we go onto SPACE, since we are
1451                           * allowed trailing whitespace                           * allowed trailing whitespace
1452                           */                           */
1453                          failat = stop - string1;                          failat = stop - string1;
1454                          string1 = stop;                          string1 = stop;
1455                          chcomp = Tcl_UniCharIsSpace;                          chcomp = Tcl_UniCharIsSpace;
1456                      }                      }
1457                      break;                      break;
1458                  }                  }
1459                  case STR_IS_LOWER:                  case STR_IS_LOWER:
1460                      chcomp = Tcl_UniCharIsLower;                      chcomp = Tcl_UniCharIsLower;
1461                      break;                      break;
1462                  case STR_IS_PRINT:                  case STR_IS_PRINT:
1463                      chcomp = Tcl_UniCharIsPrint;                      chcomp = Tcl_UniCharIsPrint;
1464                      break;                      break;
1465                  case STR_IS_PUNCT:                  case STR_IS_PUNCT:
1466                      chcomp = Tcl_UniCharIsPunct;                      chcomp = Tcl_UniCharIsPunct;
1467                      break;                      break;
1468                  case STR_IS_SPACE:                  case STR_IS_SPACE:
1469                      chcomp = Tcl_UniCharIsSpace;                      chcomp = Tcl_UniCharIsSpace;
1470                      break;                      break;
1471                  case STR_IS_UPPER:                  case STR_IS_UPPER:
1472                      chcomp = Tcl_UniCharIsUpper;                      chcomp = Tcl_UniCharIsUpper;
1473                      break;                      break;
1474                  case STR_IS_WORD:                  case STR_IS_WORD:
1475                      chcomp = Tcl_UniCharIsWordChar;                      chcomp = Tcl_UniCharIsWordChar;
1476                      break;                      break;
1477                  case STR_IS_XDIGIT: {                  case STR_IS_XDIGIT: {
1478                      for (; string1 < end; string1++, failat++) {                      for (; string1 < end; string1++, failat++) {
1479                          /* INTL: We assume unicode is bad for this class */                          /* INTL: We assume unicode is bad for this class */
1480                          if ((*((unsigned char *)string1) >= 0xC0) ||                          if ((*((unsigned char *)string1) >= 0xC0) ||
1481                              !isxdigit(*(unsigned char *)string1)) {                              !isxdigit(*(unsigned char *)string1)) {
1482                              result = 0;                              result = 0;
1483                              break;                              break;
1484                          }                          }
1485                      }                      }
1486                      break;                      break;
1487                  }                  }
1488              }              }
1489              if (chcomp != NULL) {              if (chcomp != NULL) {
1490                  for (; string1 < end; string1 += length2, failat++) {                  for (; string1 < end; string1 += length2, failat++) {
1491                      length2 = Tcl_UtfToUniChar(string1, &ch);                      length2 = Tcl_UtfToUniChar(string1, &ch);
1492                      if (!chcomp(ch)) {                      if (!chcomp(ch)) {
1493                          result = 0;                          result = 0;
1494                          break;                          break;
1495                      }                      }
1496                  }                  }
1497              }              }
1498          str_is_done:          str_is_done:
1499              /*              /*
1500               * Only set the failVarObj when we will return 0               * Only set the failVarObj when we will return 0
1501               * and we have indicated a valid fail index (>= 0)               * and we have indicated a valid fail index (>= 0)
1502               */               */
1503              if ((result == 0) && (failVarObj != NULL) &&              if ((result == 0) && (failVarObj != NULL) &&
1504                  Tcl_ObjSetVar2(interp, failVarObj, NULL, Tcl_NewIntObj(failat),                  Tcl_ObjSetVar2(interp, failVarObj, NULL, Tcl_NewIntObj(failat),
1505                                 TCL_LEAVE_ERR_MSG) == NULL) {                                 TCL_LEAVE_ERR_MSG) == NULL) {
1506                  return TCL_ERROR;                  return TCL_ERROR;
1507              }              }
1508              Tcl_SetBooleanObj(resultPtr, result);              Tcl_SetBooleanObj(resultPtr, result);
1509              break;              break;
1510          }          }
1511          case STR_LAST: {          case STR_LAST: {
1512              register char *p;              register char *p;
1513              int match, utflen, start;              int match, utflen, start;
1514    
1515              if (objc < 4 || objc > 5) {              if (objc < 4 || objc > 5) {
1516                  Tcl_WrongNumArgs(interp, 2, objv,                  Tcl_WrongNumArgs(interp, 2, objv,
1517                                   "string1 string2 ?startIndex?");                                   "string1 string2 ?startIndex?");
1518                  return TCL_ERROR;                  return TCL_ERROR;
1519              }              }
1520    
1521              /*              /*
1522               * This algorithm fails on improperly formed UTF strings.               * This algorithm fails on improperly formed UTF strings.
1523               */               */
1524    
1525              match = -1;              match = -1;
1526              start = 0;              start = 0;
1527              utflen = -1;              utflen = -1;
1528              string1 = Tcl_GetStringFromObj(objv[2], &length1);              string1 = Tcl_GetStringFromObj(objv[2], &length1);
1529              string2 = Tcl_GetStringFromObj(objv[3], &length2);              string2 = Tcl_GetStringFromObj(objv[3], &length2);
1530    
1531              if (objc == 5) {              if (objc == 5) {
1532                  /*                  /*
1533                   * If a startIndex is specified, we will need to restrict                   * If a startIndex is specified, we will need to restrict
1534                   * the string range to that char index in the string                   * the string range to that char index in the string
1535                   */                   */
1536                  utflen = Tcl_NumUtfChars(string2, length2);                  utflen = Tcl_NumUtfChars(string2, length2);
1537                  if (TclGetIntForIndex(interp, objv[4], utflen-1,                  if (TclGetIntForIndex(interp, objv[4], utflen-1,
1538                                        &start) != TCL_OK) {                                        &start) != TCL_OK) {
1539                      return TCL_ERROR;                      return TCL_ERROR;
1540                  }                  }
1541                  if (start < 0) {                  if (start < 0) {
1542                      goto str_last_done;                      goto str_last_done;
1543                  } else if (start < utflen) {                  } else if (start < utflen) {
1544                      if (length2 == utflen) {                      if (length2 == utflen) {
1545                          /* no unicode chars */                          /* no unicode chars */
1546                          p = string2 + start + 1 - length1;                          p = string2 + start + 1 - length1;
1547                      } else {                      } else {
1548                          p = Tcl_UtfAtIndex(string2, start+1) - length1;                          p = Tcl_UtfAtIndex(string2, start+1) - length1;
1549                      }                      }
1550                  } else {                  } else {
1551                      p = string2 + length2 - length1;                      p = string2 + length2 - length1;
1552                  }                  }
1553              } else {              } else {
1554                  p = string2 + length2 - length1;                  p = string2 + length2 - length1;
1555              }              }
1556    
1557              if (length1 > 0) {              if (length1 > 0) {
1558                  for (;  p >= string2;  p--) {                  for (;  p >= string2;  p--) {
1559                      /*                      /*
1560                       * Scan backwards to find the first character.                       * Scan backwards to find the first character.
1561                       */                       */
1562    
1563                      while ((p != string2) && (*p != *string1)) {                      while ((p != string2) && (*p != *string1)) {
1564                          p--;                          p--;
1565                      }                      }
1566                      if (memcmp(string1, p, (unsigned) length1) == 0) {                      if (memcmp(string1, p, (unsigned) length1) == 0) {
1567                          match = p - string2;                          match = p - string2;
1568                          break;                          break;
1569                      }                      }
1570                  }                  }
1571              }              }
1572    
1573              /*              /*
1574               * Compute the character index of the matching string by counting               * Compute the character index of the matching string by counting
1575               * the number of characters before the match.               * the number of characters before the match.
1576               */               */
1577          str_last_done:          str_last_done:
1578              if (match != -1) {              if (match != -1) {
1579                  if ((objc == 4) || (length2 != utflen)) {                  if ((objc == 4) || (length2 != utflen)) {
1580                      /* only check when we've got unicode chars */                      /* only check when we've got unicode chars */
1581                      match = Tcl_NumUtfChars(string2, match);                      match = Tcl_NumUtfChars(string2, match);
1582                  }                  }
1583              }              }
1584              Tcl_SetIntObj(resultPtr, match);              Tcl_SetIntObj(resultPtr, match);
1585              break;              break;
1586          }          }
1587          case STR_BYTELENGTH:          case STR_BYTELENGTH:
1588          case STR_LENGTH: {          case STR_LENGTH: {
1589              if (objc != 3) {              if (objc != 3) {
1590                  Tcl_WrongNumArgs(interp, 2, objv, "string");                  Tcl_WrongNumArgs(interp, 2, objv, "string");
1591                  return TCL_ERROR;                  return TCL_ERROR;
1592              }              }
1593    
1594              if ((enum options) index == STR_BYTELENGTH) {              if ((enum options) index == STR_BYTELENGTH) {
1595                  (void) Tcl_GetStringFromObj(objv[2], &length1);                  (void) Tcl_GetStringFromObj(objv[2], &length1);
1596                  Tcl_SetIntObj(resultPtr, length1);                  Tcl_SetIntObj(resultPtr, length1);
1597              } else {              } else {
1598                  /*                  /*
1599                   * If we have a ByteArray object, avoid recomputing the                   * If we have a ByteArray object, avoid recomputing the
1600                   * string since the byte array contains one byte per                   * string since the byte array contains one byte per
1601                   * character.  Otherwise, use the Unicode string rep to                   * character.  Otherwise, use the Unicode string rep to
1602                   * calculate the length.                   * calculate the length.
1603                   */                   */
1604    
1605                  if (objv[2]->typePtr == &tclByteArrayType) {                  if (objv[2]->typePtr == &tclByteArrayType) {
1606                      (void) Tcl_GetByteArrayFromObj(objv[2], &length1);                      (void) Tcl_GetByteArrayFromObj(objv[2], &length1);
1607                      Tcl_SetIntObj(resultPtr, length1);                      Tcl_SetIntObj(resultPtr, length1);
1608                  } else {                  } else {
1609                      Tcl_SetIntObj(resultPtr,                      Tcl_SetIntObj(resultPtr,
1610                              Tcl_GetCharLength(objv[2]));                              Tcl_GetCharLength(objv[2]));
1611                  }                  }
1612              }              }
1613              break;              break;
1614          }          }
1615          case STR_MAP: {          case STR_MAP: {
1616              int uselen, mapElemc, len, nocase = 0;              int uselen, mapElemc, len, nocase = 0;
1617              Tcl_Obj **mapElemv;              Tcl_Obj **mapElemv;
1618              char *end;              char *end;
1619              Tcl_UniChar ch;              Tcl_UniChar ch;
1620              int (*str_comp_fn)();              int (*str_comp_fn)();
1621    
1622              if (objc < 4 || objc > 5) {              if (objc < 4 || objc > 5) {
1623                  Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? charMap string");                  Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? charMap string");
1624                  return TCL_ERROR;                  return TCL_ERROR;
1625              }              }
1626    
1627              if (objc == 5) {              if (objc == 5) {
1628                  string2 = Tcl_GetStringFromObj(objv[2], &length2);                  string2 = Tcl_GetStringFromObj(objv[2], &length2);
1629                  if ((length2 > 1) &&                  if ((length2 > 1) &&
1630                      strncmp(string2, "-nocase", (size_t) length2) == 0) {                      strncmp(string2, "-nocase", (size_t) length2) == 0) {
1631                      nocase = 1;                      nocase = 1;
1632                  } else {                  } else {
1633                      Tcl_AppendStringsToObj(resultPtr, "bad option \"",                      Tcl_AppendStringsToObj(resultPtr, "bad option \"",
1634                                             string2, "\": must be -nocase",                                             string2, "\": must be -nocase",
1635                                             (char *) NULL);                                             (char *) NULL);
1636                      return TCL_ERROR;                      return TCL_ERROR;
1637                  }                  }
1638              }              }
1639    
1640              if (Tcl_ListObjGetElements(interp, objv[objc-2], &mapElemc,              if (Tcl_ListObjGetElements(interp, objv[objc-2], &mapElemc,
1641                                         &mapElemv) != TCL_OK) {                                         &mapElemv) != TCL_OK) {
1642                  return TCL_ERROR;                  return TCL_ERROR;
1643              }              }
1644              if (mapElemc == 0) {              if (mapElemc == 0) {
1645                  /*                  /*
1646                   * empty charMap, just return whatever string was given                   * empty charMap, just return whatever string was given
1647                   */                   */
1648                  Tcl_SetObjResult(interp, objv[objc-1]);                  Tcl_SetObjResult(interp, objv[objc-1]);
1649              } else if (mapElemc & 1) {              } else if (mapElemc & 1) {
1650                  /*                  /*
1651                   * The charMap must be an even number of key/value items                   * The charMap must be an even number of key/value items
1652                   */                   */
1653                  Tcl_SetStringObj(resultPtr, "char map list unbalanced", -1);                  Tcl_SetStringObj(resultPtr, "char map list unbalanced", -1);
1654                  return TCL_ERROR;                  return TCL_ERROR;
1655              }              }
1656              string1 = Tcl_GetStringFromObj(objv[objc-1], &length1);              string1 = Tcl_GetStringFromObj(objv[objc-1], &length1);
1657              if (length1 == 0) {              if (length1 == 0) {
1658                  break;                  break;
1659              }              }
1660              end = string1 + length1;              end = string1 + length1;
1661    
1662              if (nocase) {              if (nocase) {
1663                  length1 = Tcl_NumUtfChars(string1, length1);                  length1 = Tcl_NumUtfChars(string1, length1);
1664                  str_comp_fn = Tcl_UtfNcasecmp;                  str_comp_fn = Tcl_UtfNcasecmp;
1665              } else {              } else {
1666                  str_comp_fn = memcmp;                  str_comp_fn = memcmp;
1667              }              }
1668    
1669              for ( ; string1 < end; string1 += len) {              for ( ; string1 < end; string1 += len) {
1670                  len = Tcl_UtfToUniChar(string1, &ch);                  len = Tcl_UtfToUniChar(string1, &ch);
1671                  for (index = 0; index < mapElemc; index +=2) {                  for (index = 0; index < mapElemc; index +=2) {
1672                      /*                      /*
1673                       * Get the key string to match on                       * Get the key string to match on
1674                       */                       */
1675                      string2 = Tcl_GetStringFromObj(mapElemv[index],                      string2 = Tcl_GetStringFromObj(mapElemv[index],
1676                                                     &length2);                                                     &length2);
1677                      if (nocase) {                      if (nocase) {
1678                          uselen = Tcl_NumUtfChars(string2, length2);                          uselen = Tcl_NumUtfChars(string2, length2);
1679                      } else {                      } else {
1680                          uselen = length2;                          uselen = length2;
1681                      }                      }
1682                      if ((uselen > 0) && (uselen <= length1) &&                      if ((uselen > 0) && (uselen <= length1) &&
1683                          (str_comp_fn(string2, string1, uselen) == 0)) {                          (str_comp_fn(string2, string1, uselen) == 0)) {
1684                          /*                          /*
1685                           * Adjust len to be full length of matched string                           * Adjust len to be full length of matched string
1686                           * it has to be the BYTE length                           * it has to be the BYTE length
1687                           */                           */
1688                          len = length2;                          len = length2;
1689                          /*                          /*
1690                           * Change string2 and length2 to the map value                           * Change string2 and length2 to the map value
1691                           */                           */
1692                          string2 = Tcl_GetStringFromObj(mapElemv[index+1],                          string2 = Tcl_GetStringFromObj(mapElemv[index+1],
1693                                                         &length2);                                                         &length2);
1694                          Tcl_AppendToObj(resultPtr, string2, length2);                          Tcl_AppendToObj(resultPtr, string2, length2);
1695                          break;                          break;
1696                      }                      }
1697                  }                  }
1698                  if (index == mapElemc) {                  if (index == mapElemc) {
1699                      /*                      /*
1700                       * No match was found, put the char onto result                       * No match was found, put the char onto result
1701                       */                       */
1702                      Tcl_AppendToObj(resultPtr, string1, len);                      Tcl_AppendToObj(resultPtr, string1, len);
1703                  }                  }
1704                  /*                  /*
1705                   * in nocase, length1 is in chars                   * in nocase, length1 is in chars
1706                   * otherwise it is in bytes                   * otherwise it is in bytes
1707                   */                   */
1708                  if (nocase) {                  if (nocase) {
1709                      length1--;                      length1--;
1710                  } else {                  } else {
1711                      length1 -= len;                      length1 -= len;
1712                  }                  }
1713              }              }
1714              break;              break;
1715          }          }
1716          case STR_MATCH: {          case STR_MATCH: {
1717              int nocase = 0;              int nocase = 0;
1718    
1719              if (objc < 4 || objc > 5) {              if (objc < 4 || objc > 5) {
1720                  Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? pattern string");                  Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? pattern string");
1721                  return TCL_ERROR;                  return TCL_ERROR;
1722              }              }
1723    
1724              if (objc == 5) {              if (objc == 5) {
1725                  string2 = Tcl_GetStringFromObj(objv[2], &length2);                  string2 = Tcl_GetStringFromObj(objv[2], &length2);
1726                  if ((length2 > 1) &&                  if ((length2 > 1) &&
1727                      strncmp(string2, "-nocase", (size_t) length2) == 0) {                      strncmp(string2, "-nocase", (size_t) length2) == 0) {
1728                      nocase = 1;                      nocase = 1;
1729                  } else {                  } else {
1730                      Tcl_AppendStringsToObj(resultPtr, "bad option \"",                      Tcl_AppendStringsToObj(resultPtr, "bad option \"",
1731                                             string2, "\": must be -nocase",                                             string2, "\": must be -nocase",
1732                                             (char *) NULL);                                             (char *) NULL);
1733                      return TCL_ERROR;                      return TCL_ERROR;
1734                  }                  }
1735              }              }
1736    
1737              Tcl_SetBooleanObj(resultPtr,              Tcl_SetBooleanObj(resultPtr,
1738                                Tcl_StringCaseMatch(Tcl_GetString(objv[objc-1]),                                Tcl_StringCaseMatch(Tcl_GetString(objv[objc-1]),
1739                                                    Tcl_GetString(objv[objc-2]),                                                    Tcl_GetString(objv[objc-2]),
1740                                                    nocase));                                                    nocase));
1741              break;              break;
1742          }          }
1743          case STR_RANGE: {          case STR_RANGE: {
1744              int first, last;              int first, last;
1745    
1746              if (objc != 5) {              if (objc != 5) {
1747                  Tcl_WrongNumArgs(interp, 2, objv, "string first last");                  Tcl_WrongNumArgs(interp, 2, objv, "string first last");
1748                  return TCL_ERROR;                  return TCL_ERROR;
1749              }              }
1750    
1751              /*              /*
1752               * If we have a ByteArray object, avoid indexing in the               * If we have a ByteArray object, avoid indexing in the
1753               * Utf string since the byte array contains one byte per               * Utf string since the byte array contains one byte per
1754               * character.  Otherwise, use the Unicode string rep to               * character.  Otherwise, use the Unicode string rep to
1755               * get the range.               * get the range.
1756               */               */
1757    
1758              if (objv[2]->typePtr == &tclByteArrayType) {              if (objv[2]->typePtr == &tclByteArrayType) {
1759    
1760                  string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);                  string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);
1761    
1762                  if (TclGetIntForIndex(interp, objv[3], length1 - 1,                  if (TclGetIntForIndex(interp, objv[3], length1 - 1,
1763                          &first) != TCL_OK) {                          &first) != TCL_OK) {
1764                      return TCL_ERROR;                      return TCL_ERROR;
1765                  }                  }
1766                  if (TclGetIntForIndex(interp, objv[4], length1 - 1,                  if (TclGetIntForIndex(interp, objv[4], length1 - 1,
1767                          &last) != TCL_OK) {                          &last) != TCL_OK) {
1768                      return TCL_ERROR;                      return TCL_ERROR;
1769                  }                  }
1770                  if (first < 0) {                  if (first < 0) {
1771                      first = 0;                      first = 0;
1772                  }                  }
1773                  if (last >= length1 - 1) {                  if (last >= length1 - 1) {
1774                      last = length1 - 1;                      last = length1 - 1;
1775                  }                  }
1776                  if (last >= first) {                  if (last >= first) {
1777                      int numBytes = last - first + 1;                      int numBytes = last - first + 1;
1778                      resultPtr = Tcl_NewByteArrayObj(                      resultPtr = Tcl_NewByteArrayObj(
1779                                  (unsigned char *) &string1[first], numBytes);                                  (unsigned char *) &string1[first], numBytes);
1780                      Tcl_SetObjResult(interp, resultPtr);                      Tcl_SetObjResult(interp, resultPtr);
1781                  }                  }
1782              } else {              } else {
1783                  string1 = Tcl_GetStringFromObj(objv[2], &length1);                  string1 = Tcl_GetStringFromObj(objv[2], &length1);
1784                                    
1785                  /*                  /*
1786                   * Convert to Unicode internal rep to calulate length and                   * Convert to Unicode internal rep to calulate length and
1787                   * create a result object.                   * create a result object.
1788                   */                   */
1789    
1790                  length2 = Tcl_GetCharLength(objv[2]) - 1;                  length2 = Tcl_GetCharLength(objv[2]) - 1;
1791            
1792                  if (TclGetIntForIndex(interp, objv[3], length2,                  if (TclGetIntForIndex(interp, objv[3], length2,
1793                          &first) != TCL_OK) {                          &first) != TCL_OK) {
1794                      return TCL_ERROR;                      return TCL_ERROR;
1795                  }                  }
1796                  if (TclGetIntForIndex(interp, objv[4], length2,                  if (TclGetIntForIndex(interp, objv[4], length2,
1797                          &last) != TCL_OK) {                          &last) != TCL_OK) {
1798                      return TCL_ERROR;                      return TCL_ERROR;
1799                  }                  }
1800                  if (first < 0) {                  if (first < 0) {
1801                      first = 0;                      first = 0;
1802                  }                  }
1803                  if (last >= length2) {                  if (last >= length2) {
1804                      last = length2;                      last = length2;
1805                  }                  }
1806                  if (last >= first) {                  if (last >= first) {
1807                      resultPtr = Tcl_GetRange(objv[2], first, last);                      resultPtr = Tcl_GetRange(objv[2], first, last);
1808                      Tcl_SetObjResult(interp, resultPtr);                      Tcl_SetObjResult(interp, resultPtr);
1809                  }                  }
1810              }              }
1811              break;              break;
1812          }          }
1813          case STR_REPEAT: {          case STR_REPEAT: {
1814              int count;              int count;
1815    
1816              if (objc != 4) {              if (objc != 4) {
1817                  Tcl_WrongNumArgs(interp, 2, objv, "string count");                  Tcl_WrongNumArgs(interp, 2, objv, "string count");
1818                  return TCL_ERROR;                  return TCL_ERROR;
1819              }              }
1820    
1821              if (Tcl_GetIntFromObj(interp, objv[3], &count) != TCL_OK) {              if (Tcl_GetIntFromObj(interp, objv[3], &count) != TCL_OK) {
1822                  return TCL_ERROR;                  return TCL_ERROR;
1823              }              }
1824    
1825              string1 = Tcl_GetStringFromObj(objv[2], &length1);              string1 = Tcl_GetStringFromObj(objv[2], &length1);
1826              if (length1 > 0) {              if (length1 > 0) {
1827                  for (index = 0; index < count; index++) {                  for (index = 0; index < count; index++) {
1828                      Tcl_AppendToObj(resultPtr, string1, length1);                      Tcl_AppendToObj(resultPtr, string1, length1);
1829                  }                  }
1830              }              }
1831              break;              break;
1832          }          }
1833          case STR_REPLACE: {          case STR_REPLACE: {
1834              int first, last;              int first, last;
1835    
1836              if (objc < 5 || objc > 6) {              if (objc < 5 || objc > 6) {
1837                  Tcl_WrongNumArgs(interp, 2, objv,                  Tcl_WrongNumArgs(interp, 2, objv,
1838                                   "string first last ?string?");                                   "string first last ?string?");
1839                  return TCL_ERROR;                  return TCL_ERROR;
1840              }              }
1841    
1842              string1 = Tcl_GetStringFromObj(objv[2], &length1);              string1 = Tcl_GetStringFromObj(objv[2], &length1);
1843              length1 = Tcl_NumUtfChars(string1, length1) - 1;              length1 = Tcl_NumUtfChars(string1, length1) - 1;
1844              if (TclGetIntForIndex(interp, objv[3], length1,              if (TclGetIntForIndex(interp, objv[3], length1,
1845                                    &first) != TCL_OK) {                                    &first) != TCL_OK) {
1846                  return TCL_ERROR;                  return TCL_ERROR;
1847              }              }
1848              if (TclGetIntForIndex(interp, objv[4], length1,              if (TclGetIntForIndex(interp, objv[4], length1,
1849                      &last) != TCL_OK) {                      &last) != TCL_OK) {
1850                  return TCL_ERROR;                  return TCL_ERROR;
1851              }              }
1852              if ((last < first) || (first > length1) || (last < 0)) {              if ((last < first) || (first > length1) || (last < 0)) {
1853                  Tcl_SetObjResult(interp, objv[2]);                  Tcl_SetObjResult(interp, objv[2]);
1854              } else {              } else {
1855                  char *start, *end;                  char *start, *end;
1856    
1857                  if (first < 0) {                  if (first < 0) {
1858                      first = 0;                      first = 0;
1859                  }                  }
1860                  start = Tcl_UtfAtIndex(string1, first);                  start = Tcl_UtfAtIndex(string1, first);
1861                  end = Tcl_UtfAtIndex(start, ((last > length1) ? length1 : last)                  end = Tcl_UtfAtIndex(start, ((last > length1) ? length1 : last)
1862                                       - first + 1);                                       - first + 1);
1863                  Tcl_SetStringObj(resultPtr, string1, start - string1);                  Tcl_SetStringObj(resultPtr, string1, start - string1);
1864                  if (objc == 6) {                  if (objc == 6) {
1865                      Tcl_AppendObjToObj(resultPtr, objv[5]);                      Tcl_AppendObjToObj(resultPtr, objv[5]);
1866                  }                  }
1867                  if (last < length1) {                  if (last < length1) {
1868                      Tcl_AppendToObj(resultPtr, end, -1);                      Tcl_AppendToObj(resultPtr, end, -1);
1869                  }                  }
1870              }              }
1871              break;              break;
1872          }          }
1873          case STR_TOLOWER:          case STR_TOLOWER:
1874          case STR_TOUPPER:          case STR_TOUPPER:
1875          case STR_TOTITLE:          case STR_TOTITLE:
1876              if (objc < 3 || objc > 5) {              if (objc < 3 || objc > 5) {
1877                  Tcl_WrongNumArgs(interp, 2, objv, "string ?first? ?last?");                  Tcl_WrongNumArgs(interp, 2, objv, "string ?first? ?last?");
1878                  return TCL_ERROR;                  return TCL_ERROR;
1879              }              }
1880    
1881              string1 = Tcl_GetStringFromObj(objv[2], &length1);              string1 = Tcl_GetStringFromObj(objv[2], &length1);
1882    
1883              if (objc == 3) {              if (objc == 3) {
1884                  /*                  /*
1885                   * Since the result object is not a shared object, it is                   * Since the result object is not a shared object, it is
1886                   * safe to copy the string into the result and do the                   * safe to copy the string into the result and do the
1887                   * conversion in place.  The conversion may change the length                   * conversion in place.  The conversion may change the length
1888                   * of the string, so reset the length after conversion.                   * of the string, so reset the length after conversion.
1889                   */                   */
1890    
1891                  Tcl_SetStringObj(resultPtr, string1, length1);                  Tcl_SetStringObj(resultPtr, string1, length1);
1892                  if ((enum options) index == STR_TOLOWER) {                  if ((enum options) index == STR_TOLOWER) {
1893                      length1 = Tcl_UtfToLower(Tcl_GetString(resultPtr));                      length1 = Tcl_UtfToLower(Tcl_GetString(resultPtr));
1894                  } else if ((enum options) index == STR_TOUPPER) {                  } else if ((enum options) index == STR_TOUPPER) {
1895                      length1 = Tcl_UtfToUpper(Tcl_GetString(resultPtr));                      length1 = Tcl_UtfToUpper(Tcl_GetString(resultPtr));
1896                  } else {                  } else {
1897                      length1 = Tcl_UtfToTitle(Tcl_GetString(resultPtr));                      length1 = Tcl_UtfToTitle(Tcl_GetString(resultPtr));
1898                  }                  }
1899                  Tcl_SetObjLength(resultPtr, length1);                  Tcl_SetObjLength(resultPtr, length1);
1900              } else {              } else {
1901                  int first, last;                  int first, last;
1902                  char *start, *end;                  char *start, *end;
1903    
1904                  length1 = Tcl_NumUtfChars(string1, length1) - 1;                  length1 = Tcl_NumUtfChars(string1, length1) - 1;
1905                  if (TclGetIntForIndex(interp, objv[3], length1,                  if (TclGetIntForIndex(interp, objv[3], length1,
1906                                        &first) != TCL_OK) {                                        &first) != TCL_OK) {
1907                      return TCL_ERROR;                      return TCL_ERROR;
1908                  }                  }
1909                  if (first < 0) {                  if (first < 0) {
1910                      first = 0;                      first = 0;
1911                  }                  }
1912                  last = first;                  last = first;
1913                  if ((objc == 5) && (TclGetIntForIndex(interp, objv[4], length1,                  if ((objc == 5) && (TclGetIntForIndex(interp, objv[4], length1,
1914                                                        &last) != TCL_OK)) {                                                        &last) != TCL_OK)) {
1915                      return TCL_ERROR;                      return TCL_ERROR;
1916                  }                  }
1917                  if (last >= length1) {                  if (last >= length1) {
1918                      last = length1;                      last = length1;
1919                  }                  }
1920                  if (last < first) {                  if (last < first) {
1921                      Tcl_SetObjResult(interp, objv[2]);                      Tcl_SetObjResult(interp, objv[2]);
1922                      break;                      break;
1923                  }                  }
1924                  start = Tcl_UtfAtIndex(string1, first);                  start = Tcl_UtfAtIndex(string1, first);
1925                  end = Tcl_UtfAtIndex(start, last - first + 1);                  end = Tcl_UtfAtIndex(start, last - first + 1);
1926                  length2 = end-start;                  length2 = end-start;
1927                  string2 = ckalloc((size_t) length2+1);                  string2 = ckalloc((size_t) length2+1);
1928                  memcpy(string2, start, (size_t) length2);                  memcpy(string2, start, (size_t) length2);
1929                  string2[length2] = '\0';                  string2[length2] = '\0';
1930                  if ((enum options) index == STR_TOLOWER) {                  if ((enum options) index == STR_TOLOWER) {
1931                      length2 = Tcl_UtfToLower(string2);                      length2 = Tcl_UtfToLower(string2);
1932                  } else if ((enum options) index == STR_TOUPPER) {                  } else if ((enum options) index == STR_TOUPPER) {
1933                      length2 = Tcl_UtfToUpper(string2);                      length2 = Tcl_UtfToUpper(string2);
1934                  } else {                  } else {
1935                      length2 = Tcl_UtfToTitle(string2);                      length2 = Tcl_UtfToTitle(string2);
1936                  }                  }
1937                  Tcl_SetStringObj(resultPtr, string1, start - string1);                  Tcl_SetStringObj(resultPtr, string1, start - string1);
1938                  Tcl_AppendToObj(resultPtr, string2, length2);                  Tcl_AppendToObj(resultPtr, string2, length2);
1939                  Tcl_AppendToObj(resultPtr, end, -1);                  Tcl_AppendToObj(resultPtr, end, -1);
1940                  ckfree(string2);                  ckfree(string2);
1941              }              }
1942              break;              break;
1943    
1944          case STR_TRIM: {          case STR_TRIM: {
1945              Tcl_UniChar ch, trim;              Tcl_UniChar ch, trim;
1946              register char *p, *end;              register char *p, *end;
1947              char *check, *checkEnd;              char *check, *checkEnd;
1948              int offset;              int offset;
1949    
1950              left = 1;              left = 1;
1951              right = 1;              right = 1;
1952    
1953              dotrim:              dotrim:
1954              if (objc == 4) {              if (objc == 4) {
1955                  string2 = Tcl_GetStringFromObj(objv[3], &length2);                  string2 = Tcl_GetStringFromObj(objv[3], &length2);
1956              } else if (objc == 3) {              } else if (objc == 3) {
1957                  string2 = " \t\n\r";                  string2 = " \t\n\r";
1958                  length2 = strlen(string2);                  length2 = strlen(string2);
1959              } else {              } else {
1960                  Tcl_WrongNumArgs(interp, 2, objv, "string ?chars?");                  Tcl_WrongNumArgs(interp, 2, objv, "string ?chars?");
1961                  return TCL_ERROR;                  return TCL_ERROR;
1962              }              }
1963              string1 = Tcl_GetStringFromObj(objv[2], &length1);              string1 = Tcl_GetStringFromObj(objv[2], &length1);
1964              checkEnd = string2 + length2;              checkEnd = string2 + length2;
1965    
1966              if (left) {              if (left) {
1967                  end = string1 + length1;                  end = string1 + length1;
1968                  /*                  /*
1969                   * The outer loop iterates over the string.  The inner                   * The outer loop iterates over the string.  The inner
1970                   * loop iterates over the trim characters.  The loops                   * loop iterates over the trim characters.  The loops
1971                   * terminate as soon as a non-trim character is discovered                   * terminate as soon as a non-trim character is discovered
1972                   * and string1 is left pointing at the first non-trim                   * and string1 is left pointing at the first non-trim
1973                   * character.                   * character.
1974                   */                   */
1975    
1976                  for (p = string1; p < end; p += offset) {                  for (p = string1; p < end; p += offset) {
1977                      offset = Tcl_UtfToUniChar(p, &ch);                      offset = Tcl_UtfToUniChar(p, &ch);
1978                                            
1979                      for (check = string2; ; ) {                      for (check = string2; ; ) {
1980                          if (check >= checkEnd) {                          if (check >= checkEnd) {
1981                              p = end;                              p = end;
1982                              break;                              break;
1983                          }                          }
1984                          check += Tcl_UtfToUniChar(check, &trim);                          check += Tcl_UtfToUniChar(check, &trim);
1985                          if (ch == trim) {                          if (ch == trim) {
1986                              length1 -= offset;                              length1 -= offset;
1987                              string1 += offset;                              string1 += offset;
1988                              break;                              break;
1989                          }                          }
1990                      }                      }
1991                  }                  }
1992              }              }
1993              if (right) {              if (right) {
1994                  end = string1;                  end = string1;
1995    
1996                  /*                  /*
1997                   * The outer loop iterates over the string.  The inner                   * The outer loop iterates over the string.  The inner
1998                   * loop iterates over the trim characters.  The loops                   * loop iterates over the trim characters.  The loops
1999                   * terminate as soon as a non-trim character is discovered                   * terminate as soon as a non-trim character is discovered
2000                   * and length1 marks the last non-trim character.                   * and length1 marks the last non-trim character.
2001                   */                   */
2002    
2003                  for (p = string1 + length1; p > end; ) {                  for (p = string1 + length1; p > end; ) {
2004                      p = Tcl_UtfPrev(p, string1);                      p = Tcl_UtfPrev(p, string1);
2005                      offset = Tcl_UtfToUniChar(p, &ch);                      offset = Tcl_UtfToUniChar(p, &ch);
2006                      for (check = string2; ; ) {                      for (check = string2; ; ) {
2007                          if (check >= checkEnd) {                          if (check >= checkEnd) {
2008                              p = end;                              p = end;
2009                              break;                              break;
2010                          }                          }
2011                          check += Tcl_UtfToUniChar(check, &trim);                          check += Tcl_UtfToUniChar(check, &trim);
2012                          if (ch == trim) {                          if (ch == trim) {
2013                              length1 -= offset;                              length1 -= offset;
2014                              break;                              break;
2015                          }                          }
2016                      }                      }
2017                  }                  }
2018              }              }
2019              Tcl_SetStringObj(resultPtr, string1, length1);              Tcl_SetStringObj(resultPtr, string1, length1);
2020              break;              break;
2021          }          }
2022          case STR_TRIMLEFT: {          case STR_TRIMLEFT: {
2023              left = 1;              left = 1;
2024              right = 0;              right = 0;
2025              goto dotrim;              goto dotrim;
2026          }          }
2027          case STR_TRIMRIGHT: {          case STR_TRIMRIGHT: {
2028              left = 0;              left = 0;
2029              right = 1;              right = 1;
2030              goto dotrim;              goto dotrim;
2031          }          }
2032          case STR_WORDEND: {          case STR_WORDEND: {
2033              int cur;              int cur;
2034              Tcl_UniChar ch;              Tcl_UniChar ch;
2035              char *p, *end;              char *p, *end;
2036              int numChars;              int numChars;
2037                            
2038              if (objc != 4) {              if (objc != 4) {
2039                  Tcl_WrongNumArgs(interp, 2, objv, "string index");                  Tcl_WrongNumArgs(interp, 2, objv, "string index");
2040                  return TCL_ERROR;                  return TCL_ERROR;
2041              }              }
2042    
2043              string1 = Tcl_GetStringFromObj(objv[2], &length1);              string1 = Tcl_GetStringFromObj(objv[2], &length1);
2044              numChars = Tcl_NumUtfChars(string1, length1);              numChars = Tcl_NumUtfChars(string1, length1);
2045              if (TclGetIntForIndex(interp, objv[3], numChars-1,              if (TclGetIntForIndex(interp, objv[3], numChars-1,
2046                                    &index) != TCL_OK) {                                    &index) != TCL_OK) {
2047                  return TCL_ERROR;                  return TCL_ERROR;
2048              }              }
2049              if (index < 0) {              if (index < 0) {
2050                  index = 0;                  index = 0;
2051              }              }
2052              if (index < numChars) {              if (index < numChars) {
2053                  p = Tcl_UtfAtIndex(string1, index);                  p = Tcl_UtfAtIndex(string1, index);
2054                  end = string1+length1;                  end = string1+length1;
2055                  for (cur = index; p < end; cur++) {                  for (cur = index; p < end; cur++) {
2056                      p += Tcl_UtfToUniChar(p, &ch);                      p += Tcl_UtfToUniChar(p, &ch);
2057                      if (!Tcl_UniCharIsWordChar(ch)) {                      if (!Tcl_UniCharIsWordChar(ch)) {
2058                          break;                          break;
2059                      }                      }
2060                  }                  }
2061                  if (cur == index) {                  if (cur == index) {
2062                      cur++;                      cur++;
2063                  }                  }
2064              } else {              } else {
2065                  cur = numChars;                  cur = numChars;
2066              }              }
2067              Tcl_SetIntObj(resultPtr, cur);              Tcl_SetIntObj(resultPtr, cur);
2068              break;              break;
2069          }          }
2070          case STR_WORDSTART: {          case STR_WORDSTART: {
2071              int cur;              int cur;
2072              Tcl_UniChar ch;              Tcl_UniChar ch;
2073              char *p;              char *p;
2074              int numChars;              int numChars;
2075                            
2076              if (objc != 4) {              if (objc != 4) {
2077                  Tcl_WrongNumArgs(interp, 2, objv, "string index");                  Tcl_WrongNumArgs(interp, 2, objv, "string index");
2078                  return TCL_ERROR;                  return TCL_ERROR;
2079              }              }
2080    
2081              string1 = Tcl_GetStringFromObj(objv[2], &length1);              string1 = Tcl_GetStringFromObj(objv[2], &length1);
2082              numChars = Tcl_NumUtfChars(string1, length1);              numChars = Tcl_NumUtfChars(string1, length1);
2083              if (TclGetIntForIndex(interp, objv[3], numChars-1,              if (TclGetIntForIndex(interp, objv[3], numChars-1,
2084                                    &index) != TCL_OK) {                                    &index) != TCL_OK) {
2085                  return TCL_ERROR;                  return TCL_ERROR;
2086              }              }
2087              if (index >= numChars) {              if (index >= numChars) {
2088                  index = numChars - 1;                  index = numChars - 1;
2089              }              }
2090              cur = 0;              cur = 0;
2091              if (index > 0) {              if (index > 0) {
2092                  p = Tcl_UtfAtIndex(string1, index);                  p = Tcl_UtfAtIndex(string1, index);
2093                  for (cur = index; cur >= 0; cur--) {                  for (cur = index; cur >= 0; cur--) {
2094                      Tcl_UtfToUniChar(p, &ch);                      Tcl_UtfToUniChar(p, &ch);
2095                      if (!Tcl_UniCharIsWordChar(ch)) {                      if (!Tcl_UniCharIsWordChar(ch)) {
2096                          break;                          break;
2097                      }                      }
2098                      p = Tcl_UtfPrev(p, string1);                      p = Tcl_UtfPrev(p, string1);
2099                  }                  }
2100                  if (cur != index) {                  if (cur != index) {
2101                      cur += 1;                      cur += 1;
2102                  }                  }
2103              }              }
2104              Tcl_SetIntObj(resultPtr, cur);              Tcl_SetIntObj(resultPtr, cur);
2105              break;              break;
2106          }          }
2107      }      }
2108      return TCL_OK;      return TCL_OK;
2109  }  }
2110    
2111  /*  /*
2112   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2113   *   *
2114   * Tcl_SubstObjCmd --   * Tcl_SubstObjCmd --
2115   *   *
2116   *      This procedure is invoked to process the "subst" Tcl command.   *      This procedure is invoked to process the "subst" Tcl command.
2117   *      See the user documentation for details on what it does.  This   *      See the user documentation for details on what it does.  This
2118   *      command is an almost direct copy of an implementation by   *      command is an almost direct copy of an implementation by
2119   *      Andrew Payne.   *      Andrew Payne.
2120   *   *
2121   * Results:   * Results:
2122   *      A standard Tcl result.   *      A standard Tcl result.
2123   *   *
2124   * Side effects:   * Side effects:
2125   *      See the user documentation.   *      See the user documentation.
2126   *   *
2127   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2128   */   */
2129    
2130          /* ARGSUSED */          /* ARGSUSED */
2131  int  int
2132  Tcl_SubstObjCmd(dummy, interp, objc, objv)  Tcl_SubstObjCmd(dummy, interp, objc, objv)
2133      ClientData dummy;                   /* Not used. */      ClientData dummy;                   /* Not used. */
2134      Tcl_Interp *interp;                 /* Current interpreter. */      Tcl_Interp *interp;                 /* Current interpreter. */
2135      int objc;                           /* Number of arguments. */      int objc;                           /* Number of arguments. */
2136      Tcl_Obj *CONST objv[];              /* Argument objects. */      Tcl_Obj *CONST objv[];              /* Argument objects. */
2137  {  {
2138      static char *substOptions[] = {      static char *substOptions[] = {
2139          "-nobackslashes", "-nocommands", "-novariables", (char *) NULL          "-nobackslashes", "-nocommands", "-novariables", (char *) NULL
2140      };      };
2141      enum substOptions {      enum substOptions {
2142          SUBST_NOBACKSLASHES,      SUBST_NOCOMMANDS,       SUBST_NOVARS          SUBST_NOBACKSLASHES,      SUBST_NOCOMMANDS,       SUBST_NOVARS
2143      };      };
2144      Interp *iPtr = (Interp *) interp;      Interp *iPtr = (Interp *) interp;
2145      Tcl_DString result;      Tcl_DString result;
2146      char *p, *old, *value;      char *p, *old, *value;
2147      int optionIndex, code, count, doVars, doCmds, doBackslashes, i;      int optionIndex, code, count, doVars, doCmds, doBackslashes, i;
2148    
2149      /*      /*
2150       * Parse command-line options.       * Parse command-line options.
2151       */       */
2152    
2153      doVars = doCmds = doBackslashes = 1;      doVars = doCmds = doBackslashes = 1;
2154      for (i = 1; i < (objc-1); i++) {      for (i = 1; i < (objc-1); i++) {
2155          p = Tcl_GetString(objv[i]);          p = Tcl_GetString(objv[i]);
2156          if (*p != '-') {          if (*p != '-') {
2157              break;              break;
2158          }          }
2159          if (Tcl_GetIndexFromObj(interp, objv[i], substOptions,          if (Tcl_GetIndexFromObj(interp, objv[i], substOptions,
2160                  "switch", 0, &optionIndex) != TCL_OK) {                  "switch", 0, &optionIndex) != TCL_OK) {
2161    
2162              return TCL_ERROR;              return TCL_ERROR;
2163          }          }
2164          switch (optionIndex) {          switch (optionIndex) {
2165              case SUBST_NOBACKSLASHES: {              case SUBST_NOBACKSLASHES: {
2166                  doBackslashes = 0;                  doBackslashes = 0;
2167                  break;                  break;
2168              }              }
2169              case SUBST_NOCOMMANDS: {              case SUBST_NOCOMMANDS: {
2170                  doCmds = 0;                  doCmds = 0;
2171                  break;                  break;
2172              }              }
2173              case SUBST_NOVARS: {              case SUBST_NOVARS: {
2174                  doVars = 0;                  doVars = 0;
2175                  break;                  break;
2176              }              }
2177              default: {              default: {
2178                  panic("Tcl_SubstObjCmd: bad option index to SubstOptions");                  panic("Tcl_SubstObjCmd: bad option index to SubstOptions");
2179              }              }
2180          }          }
2181      }      }
2182      if (i != (objc-1)) {      if (i != (objc-1)) {
2183          Tcl_WrongNumArgs(interp, 1, objv,          Tcl_WrongNumArgs(interp, 1, objv,
2184                  "?-nobackslashes? ?-nocommands? ?-novariables? string");                  "?-nobackslashes? ?-nocommands? ?-novariables? string");
2185          return TCL_ERROR;          return TCL_ERROR;
2186      }      }
2187    
2188      /*      /*
2189       * Scan through the string one character at a time, performing       * Scan through the string one character at a time, performing
2190       * command, variable, and backslash substitutions.       * command, variable, and backslash substitutions.
2191       */       */
2192    
2193      Tcl_DStringInit(&result);      Tcl_DStringInit(&result);
2194      old = p = Tcl_GetString(objv[i]);      old = p = Tcl_GetString(objv[i]);
2195      while (*p != 0) {      while (*p != 0) {
2196          switch (*p) {          switch (*p) {
2197              case '\\':              case '\\':
2198                  if (doBackslashes) {                  if (doBackslashes) {
2199                      char buf[TCL_UTF_MAX];                      char buf[TCL_UTF_MAX];
2200    
2201                      if (p != old) {                      if (p != old) {
2202                          Tcl_DStringAppend(&result, old, p-old);                          Tcl_DStringAppend(&result, old, p-old);
2203                      }                      }
2204                      Tcl_DStringAppend(&result, buf,                      Tcl_DStringAppend(&result, buf,
2205                              Tcl_UtfBackslash(p, &count, buf));                              Tcl_UtfBackslash(p, &count, buf));
2206                      p += count;                      p += count;
2207                      old = p;                      old = p;
2208                  } else {                  } else {
2209                      p++;                      p++;
2210                  }                  }
2211                  break;                  break;
2212    
2213              case '$':              case '$':
2214                  if (doVars) {                  if (doVars) {
2215                      if (p != old) {                      if (p != old) {
2216                          Tcl_DStringAppend(&result, old, p-old);                          Tcl_DStringAppend(&result, old, p-old);
2217                      }                      }
2218                      value = Tcl_ParseVar(interp, p, &p);                      value = Tcl_ParseVar(interp, p, &p);
2219                      if (value == NULL) {                      if (value == NULL) {
2220                          Tcl_DStringFree(&result);                          Tcl_DStringFree(&result);
2221                          return TCL_ERROR;                          return TCL_ERROR;
2222                      }                      }
2223                      Tcl_DStringAppend(&result, value, -1);                      Tcl_DStringAppend(&result, value, -1);
2224                      old = p;                      old = p;
2225                  } else {                  } else {
2226                      p++;                      p++;
2227                  }                  }
2228                  break;                  break;
2229    
2230              case '[':              case '[':
2231                  if (doCmds) {                  if (doCmds) {
2232                      if (p != old) {                      if (p != old) {
2233                          Tcl_DStringAppend(&result, old, p-old);                          Tcl_DStringAppend(&result, old, p-old);
2234                      }                      }
2235                      iPtr->evalFlags = TCL_BRACKET_TERM;                      iPtr->evalFlags = TCL_BRACKET_TERM;
2236                      code = Tcl_Eval(interp, p+1);                      code = Tcl_Eval(interp, p+1);
2237                      if (code == TCL_ERROR) {                      if (code == TCL_ERROR) {
2238                          Tcl_DStringFree(&result);                          Tcl_DStringFree(&result);
2239                          return code;                          return code;
2240                      }                      }
2241                      old = p = (p+1 + iPtr->termOffset+1);                      old = p = (p+1 + iPtr->termOffset+1);
2242                      Tcl_DStringAppend(&result, iPtr->result, -1);                      Tcl_DStringAppend(&result, iPtr->result, -1);
2243                      Tcl_ResetResult(interp);                      Tcl_ResetResult(interp);
2244                  } else {                  } else {
2245                      p++;                      p++;
2246                  }                  }
2247                  break;                  break;
2248    
2249              default:              default:
2250                  p++;                  p++;
2251                  break;                  break;
2252          }          }
2253      }      }
2254      if (p != old) {      if (p != old) {
2255          Tcl_DStringAppend(&result, old, p-old);          Tcl_DStringAppend(&result, old, p-old);
2256      }      }
2257      Tcl_DStringResult(interp, &result);      Tcl_DStringResult(interp, &result);
2258      return TCL_OK;      return TCL_OK;
2259  }  }
2260    
2261  /*  /*
2262   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2263   *   *
2264   * Tcl_SwitchObjCmd --   * Tcl_SwitchObjCmd --
2265   *   *
2266   *      This object-based procedure is invoked to process the "switch" Tcl   *      This object-based procedure is invoked to process the "switch" Tcl
2267   *      command. See the user documentation for details on what it does.   *      command. See the user documentation for details on what it does.
2268   *   *
2269   * Results:   * Results:
2270   *      A standard Tcl object result.   *      A standard Tcl object result.
2271   *   *
2272   * Side effects:   * Side effects:
2273   *      See the user documentation.   *      See the user documentation.
2274   *   *
2275   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2276   */   */
2277    
2278          /* ARGSUSED */          /* ARGSUSED */
2279  int  int
2280  Tcl_SwitchObjCmd(dummy, interp, objc, objv)  Tcl_SwitchObjCmd(dummy, interp, objc, objv)
2281      ClientData dummy;           /* Not used. */      ClientData dummy;           /* Not used. */
2282      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
2283      int objc;                   /* Number of arguments. */      int objc;                   /* Number of arguments. */
2284      Tcl_Obj *CONST objv[];      /* Argument objects. */      Tcl_Obj *CONST objv[];      /* Argument objects. */
2285  {  {
2286      int i, j, index, mode, matched, result, splitObjs, seenComment;      int i, j, index, mode, matched, result, splitObjs, seenComment;
2287      char *string, *pattern;      char *string, *pattern;
2288      Tcl_Obj *stringObj;      Tcl_Obj *stringObj;
2289      static char *options[] = {      static char *options[] = {
2290          "-exact",       "-glob",        "-regexp",      "--",          "-exact",       "-glob",        "-regexp",      "--",
2291          NULL          NULL
2292      };      };
2293      enum options {      enum options {
2294          OPT_EXACT,      OPT_GLOB,       OPT_REGEXP,     OPT_LAST          OPT_EXACT,      OPT_GLOB,       OPT_REGEXP,     OPT_LAST
2295      };      };
2296    
2297      mode = OPT_EXACT;      mode = OPT_EXACT;
2298      for (i = 1; i < objc; i++) {      for (i = 1; i < objc; i++) {
2299          string = Tcl_GetString(objv[i]);          string = Tcl_GetString(objv[i]);
2300          if (string[0] != '-') {          if (string[0] != '-') {
2301              break;              break;
2302          }          }
2303          if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0,          if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0,
2304                  &index) != TCL_OK) {                  &index) != TCL_OK) {
2305              return TCL_ERROR;              return TCL_ERROR;
2306          }          }
2307          if (index == OPT_LAST) {          if (index == OPT_LAST) {
2308              i++;              i++;
2309              break;              break;
2310          }          }
2311          mode = index;          mode = index;
2312      }      }
2313    
2314      if (objc - i < 2) {      if (objc - i < 2) {
2315          Tcl_WrongNumArgs(interp, 1, objv,          Tcl_WrongNumArgs(interp, 1, objv,
2316                  "?switches? string pattern body ... ?default body?");                  "?switches? string pattern body ... ?default body?");
2317          return TCL_ERROR;          return TCL_ERROR;
2318      }      }
2319    
2320      stringObj = objv[i];      stringObj = objv[i];
2321      objc -= i + 1;      objc -= i + 1;
2322      objv += i + 1;      objv += i + 1;
2323    
2324      /*      /*
2325       * If all of the pattern/command pairs are lumped into a single       * If all of the pattern/command pairs are lumped into a single
2326       * argument, split them out again.       * argument, split them out again.
2327       */       */
2328    
2329      splitObjs = 0;      splitObjs = 0;
2330      if (objc == 1) {      if (objc == 1) {
2331          Tcl_Obj **listv;          Tcl_Obj **listv;
2332    
2333          if (Tcl_ListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK) {          if (Tcl_ListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK) {
2334              return TCL_ERROR;              return TCL_ERROR;
2335          }          }
2336          objv = listv;          objv = listv;
2337          splitObjs = 1;          splitObjs = 1;
2338      }      }
2339    
2340      seenComment = 0;      seenComment = 0;
2341      for (i = 0; i < objc; i += 2) {      for (i = 0; i < objc; i += 2) {
2342          if (i == objc - 1) {          if (i == objc - 1) {
2343              Tcl_ResetResult(interp);              Tcl_ResetResult(interp);
2344              Tcl_AppendToObj(Tcl_GetObjResult(interp),              Tcl_AppendToObj(Tcl_GetObjResult(interp),
2345                      "extra switch pattern with no body", -1);                      "extra switch pattern with no body", -1);
2346    
2347              /*              /*
2348               * Check if this can be due to a badly placed comment               * Check if this can be due to a badly placed comment
2349               * in the switch block               * in the switch block
2350               */               */
2351    
2352              if (splitObjs && seenComment) {              if (splitObjs && seenComment) {
2353                  Tcl_AppendToObj(Tcl_GetObjResult(interp),                  Tcl_AppendToObj(Tcl_GetObjResult(interp),
2354                          ", this may be due to a comment incorrectly placed outside of a switch body - see the \"switch\" documentation", -1);                          ", this may be due to a comment incorrectly placed outside of a switch body - see the \"switch\" documentation", -1);
2355              }              }
2356    
2357              return TCL_ERROR;              return TCL_ERROR;
2358          }          }
2359    
2360          /*          /*
2361           * See if the pattern matches the string.           * See if the pattern matches the string.
2362           */           */
2363    
2364          pattern = Tcl_GetString(objv[i]);          pattern = Tcl_GetString(objv[i]);
2365    
2366          /*          /*
2367           * The following is an heuristic to detect the infamous           * The following is an heuristic to detect the infamous
2368           * "comment in switch" error: just check if a pattern           * "comment in switch" error: just check if a pattern
2369           * begins with '#'.           * begins with '#'.
2370           */           */
2371    
2372          if (splitObjs && *pattern == '#') {          if (splitObjs && *pattern == '#') {
2373              seenComment = 1;              seenComment = 1;
2374          }          }
2375    
2376          matched = 0;          matched = 0;
2377          if ((i == objc - 2)          if ((i == objc - 2)
2378                  && (*pattern == 'd')                  && (*pattern == 'd')
2379                  && (strcmp(pattern, "default") == 0)) {                  && (strcmp(pattern, "default") == 0)) {
2380              matched = 1;              matched = 1;
2381          } else {          } else {
2382              switch (mode) {              switch (mode) {
2383                  case OPT_EXACT:                  case OPT_EXACT:
2384                      matched = (strcmp(Tcl_GetString(stringObj), pattern) == 0);                      matched = (strcmp(Tcl_GetString(stringObj), pattern) == 0);
2385                      break;                      break;
2386                  case OPT_GLOB:                  case OPT_GLOB:
2387                      matched = Tcl_StringMatch(Tcl_GetString(stringObj),                      matched = Tcl_StringMatch(Tcl_GetString(stringObj),
2388                              pattern);                              pattern);
2389                      break;                      break;
2390                  case OPT_REGEXP:                  case OPT_REGEXP:
2391                      matched = Tcl_RegExpMatchObj(interp, stringObj, objv[i]);                      matched = Tcl_RegExpMatchObj(interp, stringObj, objv[i]);
2392                      if (matched < 0) {                      if (matched < 0) {
2393                          return TCL_ERROR;                          return TCL_ERROR;
2394                      }                      }
2395                      break;                      break;
2396              }              }
2397          }          }
2398          if (matched == 0) {          if (matched == 0) {
2399              continue;              continue;
2400          }          }
2401    
2402          /*          /*
2403           * We've got a match. Find a body to execute, skipping bodies           * We've got a match. Find a body to execute, skipping bodies
2404           * that are "-".           * that are "-".
2405           */           */
2406    
2407          for (j = i + 1; ; j += 2) {          for (j = i + 1; ; j += 2) {
2408              if (j >= objc) {              if (j >= objc) {
2409                  Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),                  Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
2410                          "no body specified for pattern \"", pattern,                          "no body specified for pattern \"", pattern,
2411                          "\"", (char *) NULL);                          "\"", (char *) NULL);
2412                  return TCL_ERROR;                  return TCL_ERROR;
2413              }              }
2414              if (strcmp(Tcl_GetString(objv[j]), "-") != 0) {              if (strcmp(Tcl_GetString(objv[j]), "-") != 0) {
2415                  break;                  break;
2416              }              }
2417          }          }
2418          result = Tcl_EvalObjEx(interp, objv[j], 0);          result = Tcl_EvalObjEx(interp, objv[j], 0);
2419          if (result == TCL_ERROR) {          if (result == TCL_ERROR) {
2420              char msg[100 + TCL_INTEGER_SPACE];              char msg[100 + TCL_INTEGER_SPACE];
2421    
2422              sprintf(msg, "\n    (\"%.50s\" arm line %d)", pattern,              sprintf(msg, "\n    (\"%.50s\" arm line %d)", pattern,
2423                      interp->errorLine);                      interp->errorLine);
2424              Tcl_AddObjErrorInfo(interp, msg, -1);              Tcl_AddObjErrorInfo(interp, msg, -1);
2425          }          }
2426          return result;          return result;
2427      }      }
2428      return TCL_OK;      return TCL_OK;
2429  }  }
2430    
2431  /*  /*
2432   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2433   *   *
2434   * Tcl_TimeObjCmd --   * Tcl_TimeObjCmd --
2435   *   *
2436   *      This object-based procedure is invoked to process the "time" Tcl   *      This object-based procedure is invoked to process the "time" Tcl
2437   *      command.  See the user documentation for details on what it does.   *      command.  See the user documentation for details on what it does.
2438   *   *
2439   * Results:   * Results:
2440   *      A standard Tcl object result.   *      A standard Tcl object result.
2441   *   *
2442   * Side effects:   * Side effects:
2443   *      See the user documentation.   *      See the user documentation.
2444   *   *
2445   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2446   */   */
2447    
2448          /* ARGSUSED */          /* ARGSUSED */
2449  int  int
2450  Tcl_TimeObjCmd(dummy, interp, objc, objv)  Tcl_TimeObjCmd(dummy, interp, objc, objv)
2451      ClientData dummy;           /* Not used. */      ClientData dummy;           /* Not used. */
2452      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
2453      int objc;                   /* Number of arguments. */      int objc;                   /* Number of arguments. */
2454      Tcl_Obj *CONST objv[];      /* Argument objects. */      Tcl_Obj *CONST objv[];      /* Argument objects. */
2455  {  {
2456      register Tcl_Obj *objPtr;      register Tcl_Obj *objPtr;
2457      register int i, result;      register int i, result;
2458      int count;      int count;
2459      double totalMicroSec;      double totalMicroSec;
2460      Tcl_Time start, stop;      Tcl_Time start, stop;
2461      char buf[100];      char buf[100];
2462    
2463      if (objc == 2) {      if (objc == 2) {
2464          count = 1;          count = 1;
2465      } else if (objc == 3) {      } else if (objc == 3) {
2466          result = Tcl_GetIntFromObj(interp, objv[2], &count);          result = Tcl_GetIntFromObj(interp, objv[2], &count);
2467          if (result != TCL_OK) {          if (result != TCL_OK) {
2468              return result;              return result;
2469          }          }
2470      } else {      } else {
2471          Tcl_WrongNumArgs(interp, 1, objv, "command ?count?");          Tcl_WrongNumArgs(interp, 1, objv, "command ?count?");
2472          return TCL_ERROR;          return TCL_ERROR;
2473      }      }
2474            
2475      objPtr = objv[1];      objPtr = objv[1];
2476      i = count;      i = count;
2477      TclpGetTime(&start);      TclpGetTime(&start);
2478      while (i-- > 0) {      while (i-- > 0) {
2479          result = Tcl_EvalObjEx(interp, objPtr, 0);          result = Tcl_EvalObjEx(interp, objPtr, 0);
2480          if (result != TCL_OK) {          if (result != TCL_OK) {
2481              return result;              return result;
2482          }          }
2483      }      }
2484      TclpGetTime(&stop);      TclpGetTime(&stop);
2485            
2486      totalMicroSec =      totalMicroSec =
2487          (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);          (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
2488      sprintf(buf, "%.0f microseconds per iteration",      sprintf(buf, "%.0f microseconds per iteration",
2489          ((count <= 0) ? 0 : totalMicroSec/count));          ((count <= 0) ? 0 : totalMicroSec/count));
2490      Tcl_ResetResult(interp);      Tcl_ResetResult(interp);
2491      Tcl_AppendToObj(Tcl_GetObjResult(interp), buf, -1);      Tcl_AppendToObj(Tcl_GetObjResult(interp), buf, -1);
2492      return TCL_OK;      return TCL_OK;
2493  }  }
2494    
2495  /*  /*
2496   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2497   *   *
2498   * Tcl_TraceObjCmd --   * Tcl_TraceObjCmd --
2499   *   *
2500   *      This procedure is invoked to process the "trace" Tcl command.   *      This procedure is invoked to process the "trace" Tcl command.
2501   *      See the user documentation for details on what it does.   *      See the user documentation for details on what it does.
2502   *   *
2503   * Results:   * Results:
2504   *      A standard Tcl result.   *      A standard Tcl result.
2505   *   *
2506   * Side effects:   * Side effects:
2507   *      See the user documentation.   *      See the user documentation.
2508   *   *
2509   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2510   */   */
2511    
2512          /* ARGSUSED */          /* ARGSUSED */
2513  int  int
2514  Tcl_TraceObjCmd(dummy, interp, objc, objv)  Tcl_TraceObjCmd(dummy, interp, objc, objv)
2515      ClientData dummy;                   /* Not used. */      ClientData dummy;                   /* Not used. */
2516      Tcl_Interp *interp;                 /* Current interpreter. */      Tcl_Interp *interp;                 /* Current interpreter. */
2517      int objc;                           /* Number of arguments. */      int objc;                           /* Number of arguments. */
2518      Tcl_Obj *CONST objv[];              /* Argument objects. */      Tcl_Obj *CONST objv[];              /* Argument objects. */
2519  {  {
2520      int optionIndex, commandLength;      int optionIndex, commandLength;
2521      char *name, *rwuOps, *command, *p;      char *name, *rwuOps, *command, *p;
2522      size_t length;      size_t length;
2523      static char *traceOptions[] = {      static char *traceOptions[] = {
2524          "variable", "vdelete", "vinfo", (char *) NULL          "variable", "vdelete", "vinfo", (char *) NULL
2525      };      };
2526      enum traceOptions {      enum traceOptions {
2527          TRACE_VARIABLE,       TRACE_VDELETE,      TRACE_VINFO          TRACE_VARIABLE,       TRACE_VDELETE,      TRACE_VINFO
2528      };      };
2529    
2530      if (objc < 2) {      if (objc < 2) {
2531          Tcl_WrongNumArgs(interp, 1, objv, "option [arg arg ...]");          Tcl_WrongNumArgs(interp, 1, objv, "option [arg arg ...]");
2532          return TCL_ERROR;          return TCL_ERROR;
2533      }      }
2534    
2535      if (Tcl_GetIndexFromObj(interp, objv[1], traceOptions,      if (Tcl_GetIndexFromObj(interp, objv[1], traceOptions,
2536                  "option", 0, &optionIndex) != TCL_OK) {                  "option", 0, &optionIndex) != TCL_OK) {
2537          return TCL_ERROR;          return TCL_ERROR;
2538      }      }
2539      switch ((enum traceOptions) optionIndex) {      switch ((enum traceOptions) optionIndex) {
2540              case TRACE_VARIABLE: {              case TRACE_VARIABLE: {
2541                  int flags;                  int flags;
2542                  TraceVarInfo *tvarPtr;                  TraceVarInfo *tvarPtr;
2543                  if (objc != 5) {                  if (objc != 5) {
2544                      Tcl_WrongNumArgs(interp, 2, objv, "name ops command");                      Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
2545                      return TCL_ERROR;                      return TCL_ERROR;
2546                  }                  }
2547    
2548                  flags = 0;                  flags = 0;
2549                  rwuOps = Tcl_GetString(objv[3]);                  rwuOps = Tcl_GetString(objv[3]);
2550                  for (p = rwuOps; *p != 0; p++) {                  for (p = rwuOps; *p != 0; p++) {
2551                      if (*p == 'r') {                      if (*p == 'r') {
2552                          flags |= TCL_TRACE_READS;                          flags |= TCL_TRACE_READS;
2553                      } else if (*p == 'w') {                      } else if (*p == 'w') {
2554                          flags |= TCL_TRACE_WRITES;                          flags |= TCL_TRACE_WRITES;
2555                      } else if (*p == 'u') {                      } else if (*p == 'u') {
2556                          flags |= TCL_TRACE_UNSETS;                          flags |= TCL_TRACE_UNSETS;
2557                      } else {                      } else {
2558                          goto badOps;                          goto badOps;
2559                      }                      }
2560                  }                  }
2561                  if (flags == 0) {                  if (flags == 0) {
2562                      goto badOps;                      goto badOps;
2563                  }                  }
2564    
2565                  command = Tcl_GetStringFromObj(objv[4], &commandLength);                  command = Tcl_GetStringFromObj(objv[4], &commandLength);
2566                  length = (size_t) commandLength;                  length = (size_t) commandLength;
2567                  tvarPtr = (TraceVarInfo *) ckalloc((unsigned)                  tvarPtr = (TraceVarInfo *) ckalloc((unsigned)
2568                          (sizeof(TraceVarInfo) - sizeof(tvarPtr->command)                          (sizeof(TraceVarInfo) - sizeof(tvarPtr->command)
2569                                  + length + 1));                                  + length + 1));
2570                  tvarPtr->flags = flags;                  tvarPtr->flags = flags;
2571                  tvarPtr->errMsg = NULL;                  tvarPtr->errMsg = NULL;
2572                  tvarPtr->length = length;                  tvarPtr->length = length;
2573                  flags |= TCL_TRACE_UNSETS;                  flags |= TCL_TRACE_UNSETS;
2574                  strcpy(tvarPtr->command, command);                  strcpy(tvarPtr->command, command);
2575                  name = Tcl_GetString(objv[2]);                  name = Tcl_GetString(objv[2]);
2576                  if (Tcl_TraceVar(interp, name, flags, TraceVarProc,                  if (Tcl_TraceVar(interp, name, flags, TraceVarProc,
2577                          (ClientData) tvarPtr) != TCL_OK) {                          (ClientData) tvarPtr) != TCL_OK) {
2578                      ckfree((char *) tvarPtr);                      ckfree((char *) tvarPtr);
2579                      return TCL_ERROR;                      return TCL_ERROR;
2580                  }                  }
2581                  break;                  break;
2582              }              }
2583              case TRACE_VDELETE: {              case TRACE_VDELETE: {
2584                  int flags;                  int flags;
2585                  TraceVarInfo *tvarPtr;                  TraceVarInfo *tvarPtr;
2586                  ClientData clientData;                  ClientData clientData;
2587    
2588                  if (objc != 5) {                  if (objc != 5) {
2589                      Tcl_WrongNumArgs(interp, 2, objv, "name ops command");                      Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
2590                      return TCL_ERROR;                      return TCL_ERROR;
2591                  }                  }
2592    
2593                  flags = 0;                  flags = 0;
2594                  rwuOps = Tcl_GetString(objv[3]);                  rwuOps = Tcl_GetString(objv[3]);
2595                  for (p = rwuOps; *p != 0; p++) {                  for (p = rwuOps; *p != 0; p++) {
2596                      if (*p == 'r') {                      if (*p == 'r') {
2597                          flags |= TCL_TRACE_READS;                          flags |= TCL_TRACE_READS;
2598                      } else if (*p == 'w') {                      } else if (*p == 'w') {
2599                          flags |= TCL_TRACE_WRITES;                          flags |= TCL_TRACE_WRITES;
2600                      } else if (*p == 'u') {                      } else if (*p == 'u') {
2601                          flags |= TCL_TRACE_UNSETS;                          flags |= TCL_TRACE_UNSETS;
2602                      } else {                      } else {
2603                          goto badOps;                          goto badOps;
2604                      }                      }
2605                  }                  }
2606                  if (flags == 0) {                  if (flags == 0) {
2607                      goto badOps;                      goto badOps;
2608                  }                  }
2609    
2610                  /*                  /*
2611                   * Search through all of our traces on this variable to                   * Search through all of our traces on this variable to
2612                   * see if there's one with the given command.  If so, then                   * see if there's one with the given command.  If so, then
2613                   * delete the first one that matches.                   * delete the first one that matches.
2614                   */                   */
2615                                    
2616                  command = Tcl_GetStringFromObj(objv[4], &commandLength);                  command = Tcl_GetStringFromObj(objv[4], &commandLength);
2617                  length = (size_t) commandLength;                  length = (size_t) commandLength;
2618                  clientData = 0;                  clientData = 0;
2619                  name = Tcl_GetString(objv[2]);                  name = Tcl_GetString(objv[2]);
2620                  while ((clientData = Tcl_VarTraceInfo(interp, name, 0,                  while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
2621                          TraceVarProc, clientData)) != 0) {                          TraceVarProc, clientData)) != 0) {
2622                      tvarPtr = (TraceVarInfo *) clientData;                      tvarPtr = (TraceVarInfo *) clientData;
2623                      if ((tvarPtr->length == length) && (tvarPtr->flags == flags)                      if ((tvarPtr->length == length) && (tvarPtr->flags == flags)
2624                              && (strncmp(command, tvarPtr->command,                              && (strncmp(command, tvarPtr->command,
2625                                      (size_t) length) == 0)) {                                      (size_t) length) == 0)) {
2626                          Tcl_UntraceVar(interp, name, flags | TCL_TRACE_UNSETS,                          Tcl_UntraceVar(interp, name, flags | TCL_TRACE_UNSETS,
2627                                  TraceVarProc, clientData);                                  TraceVarProc, clientData);
2628                          if (tvarPtr->errMsg != NULL) {                          if (tvarPtr->errMsg != NULL) {
2629                              ckfree(tvarPtr->errMsg);                              ckfree(tvarPtr->errMsg);
2630                          }                          }
2631                          ckfree((char *) tvarPtr);                          ckfree((char *) tvarPtr);
2632                          break;                          break;
2633                      }                      }
2634                  }                  }
2635                  break;                  break;
2636              }              }
2637              case TRACE_VINFO: {              case TRACE_VINFO: {
2638                  ClientData clientData;                  ClientData clientData;
2639                  char ops[4];                  char ops[4];
2640                  Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;                  Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
2641    
2642                  if (objc != 3) {                  if (objc != 3) {
2643                      Tcl_WrongNumArgs(interp, 2, objv, "name");                      Tcl_WrongNumArgs(interp, 2, objv, "name");
2644                      return TCL_ERROR;                      return TCL_ERROR;
2645                  }                  }
2646                  resultListPtr = Tcl_GetObjResult(interp);                  resultListPtr = Tcl_GetObjResult(interp);
2647                  clientData = 0;                  clientData = 0;
2648                  name = Tcl_GetString(objv[2]);                  name = Tcl_GetString(objv[2]);
2649                  while ((clientData = Tcl_VarTraceInfo(interp, name, 0,                  while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
2650                          TraceVarProc, clientData)) != 0) {                          TraceVarProc, clientData)) != 0) {
2651    
2652                      TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;                      TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
2653    
2654                      pairObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);                      pairObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
2655                      p = ops;                      p = ops;
2656                      if (tvarPtr->flags & TCL_TRACE_READS) {                      if (tvarPtr->flags & TCL_TRACE_READS) {
2657                          *p = 'r';                          *p = 'r';
2658                          p++;                          p++;
2659                      }                      }
2660                      if (tvarPtr->flags & TCL_TRACE_WRITES) {                      if (tvarPtr->flags & TCL_TRACE_WRITES) {
2661                          *p = 'w';                          *p = 'w';
2662                          p++;                          p++;
2663                      }                      }
2664                      if (tvarPtr->flags & TCL_TRACE_UNSETS) {                      if (tvarPtr->flags & TCL_TRACE_UNSETS) {
2665                          *p = 'u';                          *p = 'u';
2666                          p++;                          p++;
2667                      }                      }
2668                      *p = '\0';                      *p = '\0';
2669    
2670                      /*                      /*
2671                       * Build a pair (2-item list) with the ops string as                       * Build a pair (2-item list) with the ops string as
2672                       * the first obj element and the tvarPtr->command string                       * the first obj element and the tvarPtr->command string
2673                       * as the second obj element.  Append the pair (as an                       * as the second obj element.  Append the pair (as an
2674                       * element) to the end of the result object list.                       * element) to the end of the result object list.
2675                       */                       */
2676    
2677                      elemObjPtr = Tcl_NewStringObj(ops, -1);                      elemObjPtr = Tcl_NewStringObj(ops, -1);
2678                      Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);                      Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
2679                      elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);                      elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
2680                      Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);                      Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
2681                      Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);                      Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
2682                  }                  }
2683                  Tcl_SetObjResult(interp, resultListPtr);                  Tcl_SetObjResult(interp, resultListPtr);
2684                  break;                  break;
2685              }              }
2686          default: {          default: {
2687                  panic("Tcl_TraceObjCmd: bad option index to TraceOptions");                  panic("Tcl_TraceObjCmd: bad option index to TraceOptions");
2688              }              }
2689      }      }
2690      return TCL_OK;      return TCL_OK;
2691    
2692      badOps:      badOps:
2693      Tcl_AppendResult(interp, "bad operations \"", rwuOps,      Tcl_AppendResult(interp, "bad operations \"", rwuOps,
2694              "\": should be one or more of rwu", (char *) NULL);              "\": should be one or more of rwu", (char *) NULL);
2695      return TCL_ERROR;      return TCL_ERROR;
2696  }  }
2697    
2698  /*  /*
2699   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2700   *   *
2701   * TraceVarProc --   * TraceVarProc --
2702   *   *
2703   *      This procedure is called to handle variable accesses that have   *      This procedure is called to handle variable accesses that have
2704   *      been traced using the "trace" command.   *      been traced using the "trace" command.
2705   *   *
2706   * Results:   * Results:
2707   *      Normally returns NULL.  If the trace command returns an error,   *      Normally returns NULL.  If the trace command returns an error,
2708   *      then this procedure returns an error string.   *      then this procedure returns an error string.
2709   *   *
2710   * Side effects:   * Side effects:
2711   *      Depends on the command associated with the trace.   *      Depends on the command associated with the trace.
2712   *   *
2713   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2714   */   */
2715    
2716          /* ARGSUSED */          /* ARGSUSED */
2717  static char *  static char *
2718  TraceVarProc(clientData, interp, name1, name2, flags)  TraceVarProc(clientData, interp, name1, name2, flags)
2719      ClientData clientData;      /* Information about the variable trace. */      ClientData clientData;      /* Information about the variable trace. */
2720      Tcl_Interp *interp;         /* Interpreter containing variable. */      Tcl_Interp *interp;         /* Interpreter containing variable. */
2721      char *name1;                /* Name of variable or array. */      char *name1;                /* Name of variable or array. */
2722      char *name2;                /* Name of element within array;  NULL means      char *name2;                /* Name of element within array;  NULL means
2723                                   * scalar variable is being referenced. */                                   * scalar variable is being referenced. */
2724      int flags;                  /* OR-ed bits giving operation and other      int flags;                  /* OR-ed bits giving operation and other
2725                                   * information. */                                   * information. */
2726  {  {
2727      Tcl_SavedResult state;      Tcl_SavedResult state;
2728      TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;      TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
2729      char *result;      char *result;
2730      int code;      int code;
2731      Tcl_DString cmd;      Tcl_DString cmd;
2732    
2733      result = NULL;      result = NULL;
2734      if (tvarPtr->errMsg != NULL) {      if (tvarPtr->errMsg != NULL) {
2735          ckfree(tvarPtr->errMsg);          ckfree(tvarPtr->errMsg);
2736          tvarPtr->errMsg = NULL;          tvarPtr->errMsg = NULL;
2737      }      }
2738      if ((tvarPtr->flags & flags) && !(flags & TCL_INTERP_DESTROYED)) {      if ((tvarPtr->flags & flags) && !(flags & TCL_INTERP_DESTROYED)) {
2739    
2740          /*          /*
2741           * Generate a command to execute by appending list elements           * Generate a command to execute by appending list elements
2742           * for the two variable names and the operation.  The five           * for the two variable names and the operation.  The five
2743           * extra characters are for three space, the opcode character,           * extra characters are for three space, the opcode character,
2744           * and the terminating null.           * and the terminating null.
2745           */           */
2746    
2747          if (name2 == NULL) {          if (name2 == NULL) {
2748              name2 = "";              name2 = "";
2749          }          }
2750          Tcl_DStringInit(&cmd);          Tcl_DStringInit(&cmd);
2751          Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);          Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
2752          Tcl_DStringAppendElement(&cmd, name1);          Tcl_DStringAppendElement(&cmd, name1);
2753          Tcl_DStringAppendElement(&cmd, name2);          Tcl_DStringAppendElement(&cmd, name2);
2754          if (flags & TCL_TRACE_READS) {          if (flags & TCL_TRACE_READS) {
2755              Tcl_DStringAppend(&cmd, " r", 2);              Tcl_DStringAppend(&cmd, " r", 2);
2756          } else if (flags & TCL_TRACE_WRITES) {          } else if (flags & TCL_TRACE_WRITES) {
2757              Tcl_DStringAppend(&cmd, " w", 2);              Tcl_DStringAppend(&cmd, " w", 2);
2758          } else if (flags & TCL_TRACE_UNSETS) {          } else if (flags & TCL_TRACE_UNSETS) {
2759              Tcl_DStringAppend(&cmd, " u", 2);              Tcl_DStringAppend(&cmd, " u", 2);
2760          }          }
2761    
2762          /*          /*
2763           * Execute the command.  Save the interp's result used for           * Execute the command.  Save the interp's result used for
2764           * the command. We discard any object result the command returns.           * the command. We discard any object result the command returns.
2765           */           */
2766    
2767          Tcl_SaveResult(interp, &state);          Tcl_SaveResult(interp, &state);
2768    
2769          code = Tcl_Eval(interp, Tcl_DStringValue(&cmd));          code = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
2770          if (code != TCL_OK) {        /* copy error msg to result */          if (code != TCL_OK) {        /* copy error msg to result */
2771              char *string;              char *string;
2772              int length;              int length;
2773                            
2774              string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length);              string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length);
2775              tvarPtr->errMsg = (char *) ckalloc((unsigned) (length + 1));              tvarPtr->errMsg = (char *) ckalloc((unsigned) (length + 1));
2776              memcpy(tvarPtr->errMsg, string, (size_t) (length + 1));              memcpy(tvarPtr->errMsg, string, (size_t) (length + 1));
2777              result = tvarPtr->errMsg;              result = tvarPtr->errMsg;
2778          }          }
2779    
2780          Tcl_RestoreResult(interp, &state);          Tcl_RestoreResult(interp, &state);
2781    
2782          Tcl_DStringFree(&cmd);          Tcl_DStringFree(&cmd);
2783      }      }
2784      if (flags & TCL_TRACE_DESTROYED) {      if (flags & TCL_TRACE_DESTROYED) {
2785          result = NULL;          result = NULL;
2786          if (tvarPtr->errMsg != NULL) {          if (tvarPtr->errMsg != NULL) {
2787              ckfree(tvarPtr->errMsg);              ckfree(tvarPtr->errMsg);
2788          }          }
2789          ckfree((char *) tvarPtr);          ckfree((char *) tvarPtr);
2790      }      }
2791      return result;      return result;
2792  }  }
2793    
2794  /*  /*
2795   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2796   *   *
2797   * Tcl_WhileObjCmd --   * Tcl_WhileObjCmd --
2798   *   *
2799   *      This procedure is invoked to process the "while" Tcl command.   *      This procedure is invoked to process the "while" Tcl command.
2800   *      See the user documentation for details on what it does.   *      See the user documentation for details on what it does.
2801   *   *
2802   *      With the bytecode compiler, this procedure is only called when   *      With the bytecode compiler, this procedure is only called when
2803   *      a command name is computed at runtime, and is "while" or the name   *      a command name is computed at runtime, and is "while" or the name
2804   *      to which "while" was renamed: e.g., "set z while; $z {$i<100} {}"   *      to which "while" was renamed: e.g., "set z while; $z {$i<100} {}"
2805   *   *
2806   * Results:   * Results:
2807   *      A standard Tcl result.   *      A standard Tcl result.
2808   *   *
2809   * Side effects:   * Side effects:
2810   *      See the user documentation.   *      See the user documentation.
2811   *   *
2812   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2813   */   */
2814    
2815          /* ARGSUSED */          /* ARGSUSED */
2816  int  int
2817  Tcl_WhileObjCmd(dummy, interp, objc, objv)  Tcl_WhileObjCmd(dummy, interp, objc, objv)
2818      ClientData dummy;                   /* Not used. */      ClientData dummy;                   /* Not used. */
2819      Tcl_Interp *interp;                 /* Current interpreter. */      Tcl_Interp *interp;                 /* Current interpreter. */
2820      int objc;                           /* Number of arguments. */      int objc;                           /* Number of arguments. */
2821      Tcl_Obj *CONST objv[];              /* Argument objects. */      Tcl_Obj *CONST objv[];              /* Argument objects. */
2822  {  {
2823      int result, value;      int result, value;
2824    
2825      if (objc != 3) {      if (objc != 3) {
2826          Tcl_WrongNumArgs(interp, 1, objv, "test command");          Tcl_WrongNumArgs(interp, 1, objv, "test command");
2827          return TCL_ERROR;          return TCL_ERROR;
2828      }      }
2829    
2830      while (1) {      while (1) {
2831          result = Tcl_ExprBooleanObj(interp, objv[1], &value);          result = Tcl_ExprBooleanObj(interp, objv[1], &value);
2832          if (result != TCL_OK) {          if (result != TCL_OK) {
2833              return result;              return result;
2834          }          }
2835          if (!value) {          if (!value) {
2836              break;              break;
2837          }          }
2838          result = Tcl_EvalObjEx(interp, objv[2], 0);          result = Tcl_EvalObjEx(interp, objv[2], 0);
2839          if ((result != TCL_OK) && (result != TCL_CONTINUE)) {          if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
2840              if (result == TCL_ERROR) {              if (result == TCL_ERROR) {
2841                  char msg[32 + TCL_INTEGER_SPACE];                  char msg[32 + TCL_INTEGER_SPACE];
2842    
2843                  sprintf(msg, "\n    (\"while\" body line %d)",                  sprintf(msg, "\n    (\"while\" body line %d)",
2844                          interp->errorLine);                          interp->errorLine);
2845                  Tcl_AddErrorInfo(interp, msg);                  Tcl_AddErrorInfo(interp, msg);
2846              }              }
2847              break;              break;
2848          }          }
2849      }      }
2850      if (result == TCL_BREAK) {      if (result == TCL_BREAK) {
2851          result = TCL_OK;          result = TCL_OK;
2852      }      }
2853      if (result == TCL_OK) {      if (result == TCL_OK) {
2854          Tcl_ResetResult(interp);          Tcl_ResetResult(interp);
2855      }      }
2856      return result;      return result;
2857  }  }
2858    
2859  /* End of tclcmdmz.c */  /* End of tclcmdmz.c */

Legend:
Removed from v.66  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25