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

Diff of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclutil.c

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

revision 44 by dashley, Fri Oct 14 02:09:58 2016 UTC revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC
# Line 1  Line 1 
 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tcl_base/tclutil.c,v 1.1.1.1 2001/06/13 04:47:21 dtashley Exp $ */  
   
 /*  
  * tclUtil.c --  
  *  
  *      This file contains utility procedures that are used by many Tcl  
  *      commands.  
  *  
  * Copyright (c) 1987-1993 The Regents of the University of California.  
  * Copyright (c) 1994-1998 Sun Microsystems, Inc.  
  *  
  * See the file "license.terms" for information on usage and redistribution  
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.  
  *  
  *  RCS: @(#) $Id: tclutil.c,v 1.1.1.1 2001/06/13 04:47:21 dtashley Exp $  
  */  
   
 #include "tclInt.h"  
 #include "tclPort.h"  
   
 /*  
  * The following variable holds the full path name of the binary  
  * from which this application was executed, or NULL if it isn't  
  * know.  The value of the variable is set by the procedure  
  * Tcl_FindExecutable.  The storage space is dynamically allocated.  
  */  
   
 char *tclExecutableName = NULL;  
 char *tclNativeExecutableName = NULL;  
   
 /*  
  * The following values are used in the flags returned by Tcl_ScanElement  
  * and used by Tcl_ConvertElement.  The value TCL_DONT_USE_BRACES is also  
  * defined in tcl.h;  make sure its value doesn't overlap with any of the  
  * values below.  
  *  
  * TCL_DONT_USE_BRACES -        1 means the string mustn't be enclosed in  
  *                              braces (e.g. it contains unmatched braces,  
  *                              or ends in a backslash character, or user  
  *                              just doesn't want braces);  handle all  
  *                              special characters by adding backslashes.  
  * USE_BRACES -                 1 means the string contains a special  
  *                              character that can be handled simply by  
  *                              enclosing the entire argument in braces.  
  * BRACES_UNMATCHED -           1 means that braces aren't properly matched  
  *                              in the argument.  
  */  
   
 #define USE_BRACES              2  
 #define BRACES_UNMATCHED        4  
   
 /*  
  * The following values determine the precision used when converting  
  * floating-point values to strings.  This information is linked to all  
  * of the tcl_precision variables in all interpreters via the procedure  
  * TclPrecTraceProc.  
  */  
   
 static char precisionString[10] = "12";  
                                 /* The string value of all the tcl_precision  
                                  * variables. */  
 static char precisionFormat[10] = "%.12g";  
                                 /* The format string actually used in calls  
                                  * to sprintf. */  
 TCL_DECLARE_MUTEX(precisionMutex)  
   
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclFindElement --  
  *  
  *      Given a pointer into a Tcl list, locate the first (or next)  
  *      element in the list.  
  *  
  * Results:  
  *      The return value is normally TCL_OK, which means that the  
  *      element was successfully located.  If TCL_ERROR is returned  
  *      it means that list didn't have proper list structure;  
  *      the interp's result contains a more detailed error message.  
  *  
  *      If TCL_OK is returned, then *elementPtr will be set to point to the  
  *      first element of list, and *nextPtr will be set to point to the  
  *      character just after any white space following the last character  
  *      that's part of the element. If this is the last argument in the  
  *      list, then *nextPtr will point just after the last character in the  
  *      list (i.e., at the character at list+listLength). If sizePtr is  
  *      non-NULL, *sizePtr is filled in with the number of characters in the  
  *      element.  If the element is in braces, then *elementPtr will point  
  *      to the character after the opening brace and *sizePtr will not  
  *      include either of the braces. If there isn't an element in the list,  
  *      *sizePtr will be zero, and both *elementPtr and *termPtr will point  
  *      just after the last character in the list. Note: this procedure does  
  *      NOT collapse backslash sequences.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 TclFindElement(interp, list, listLength, elementPtr, nextPtr, sizePtr,  
                bracePtr)  
     Tcl_Interp *interp;         /* Interpreter to use for error reporting.  
                                  * If NULL, then no error message is left  
                                  * after errors. */  
     CONST char *list;           /* Points to the first byte of a string  
                                  * containing a Tcl list with zero or more  
                                  * elements (possibly in braces). */  
     int listLength;             /* Number of bytes in the list's string. */  
     CONST char **elementPtr;    /* Where to put address of first significant  
                                  * character in first element of list. */  
     CONST char **nextPtr;       /* Fill in with location of character just  
                                  * after all white space following end of  
                                  * argument (next arg or end of list). */  
     int *sizePtr;               /* If non-zero, fill in with size of  
                                  * element. */  
     int *bracePtr;              /* If non-zero, fill in with non-zero/zero  
                                  * to indicate that arg was/wasn't  
                                  * in braces. */  
 {  
     CONST char *p = list;  
     CONST char *elemStart;      /* Points to first byte of first element. */  
     CONST char *limit;          /* Points just after list's last byte. */  
     int openBraces = 0;         /* Brace nesting level during parse. */  
     int inQuotes = 0;  
     int size = 0;               /* lint. */  
     int numChars;  
     CONST char *p2;  
       
     /*  
      * Skim off leading white space and check for an opening brace or  
      * quote. We treat embedded NULLs in the list as bytes belonging to  
      * a list element.  
      */  
   
     limit = (list + listLength);  
     while ((p < limit) && (isspace(UCHAR(*p)))) { /* INTL: ISO space. */  
         p++;  
     }  
     if (p == limit) {           /* no element found */  
         elemStart = limit;  
         goto done;  
     }  
   
     if (*p == '{') {  
         openBraces = 1;  
         p++;  
     } else if (*p == '"') {  
         inQuotes = 1;  
         p++;  
     }  
     elemStart = p;  
     if (bracePtr != 0) {  
         *bracePtr = openBraces;  
     }  
   
     /*  
      * Find element's end (a space, close brace, or the end of the string).  
      */  
   
     while (p < limit) {  
         switch (*p) {  
   
             /*  
              * Open brace: don't treat specially unless the element is in  
              * braces. In this case, keep a nesting count.  
              */  
   
             case '{':  
                 if (openBraces != 0) {  
                     openBraces++;  
                 }  
                 break;  
   
             /*  
              * Close brace: if element is in braces, keep nesting count and  
              * quit when the last close brace is seen.  
              */  
   
             case '}':  
                 if (openBraces > 1) {  
                     openBraces--;  
                 } else if (openBraces == 1) {  
                     size = (p - elemStart);  
                     p++;  
                     if ((p >= limit)  
                             || isspace(UCHAR(*p))) { /* INTL: ISO space. */  
                         goto done;  
                     }  
   
                     /*  
                      * Garbage after the closing brace; return an error.  
                      */  
                       
                     if (interp != NULL) {  
                         char buf[100];  
                           
                         p2 = p;  
                         while ((p2 < limit)  
                                 && (!isspace(UCHAR(*p2))) /* INTL: ISO space. */  
                                 && (p2 < p+20)) {  
                             p2++;  
                         }  
                         sprintf(buf,  
                                 "list element in braces followed by \"%.*s\" instead of space",  
                                 (int) (p2-p), p);  
                         Tcl_SetResult(interp, buf, TCL_VOLATILE);  
                     }  
                     return TCL_ERROR;  
                 }  
                 break;  
   
             /*  
              * Backslash:  skip over everything up to the end of the  
              * backslash sequence.  
              */  
   
             case '\\': {  
                 Tcl_UtfBackslash(p, &numChars, NULL);  
                 p += (numChars - 1);  
                 break;  
             }  
   
             /*  
              * Space: ignore if element is in braces or quotes; otherwise  
              * terminate element.  
              */  
   
             case ' ':  
             case '\f':  
             case '\n':  
             case '\r':  
             case '\t':  
             case '\v':  
                 if ((openBraces == 0) && !inQuotes) {  
                     size = (p - elemStart);  
                     goto done;  
                 }  
                 break;  
   
             /*  
              * Double-quote: if element is in quotes then terminate it.  
              */  
   
             case '"':  
                 if (inQuotes) {  
                     size = (p - elemStart);  
                     p++;  
                     if ((p >= limit)  
                             || isspace(UCHAR(*p))) { /* INTL: ISO space */  
                         goto done;  
                     }  
   
                     /*  
                      * Garbage after the closing quote; return an error.  
                      */  
                       
                     if (interp != NULL) {  
                         char buf[100];  
                           
                         p2 = p;  
                         while ((p2 < limit)  
                                 && (!isspace(UCHAR(*p2))) /* INTL: ISO space */  
                                  && (p2 < p+20)) {  
                             p2++;  
                         }  
                         sprintf(buf,  
                                 "list element in quotes followed by \"%.*s\" %s",  
                                 (int) (p2-p), p, "instead of space");  
                         Tcl_SetResult(interp, buf, TCL_VOLATILE);  
                     }  
                     return TCL_ERROR;  
                 }  
                 break;  
         }  
         p++;  
     }  
   
   
     /*  
      * End of list: terminate element.  
      */  
   
     if (p == limit) {  
         if (openBraces != 0) {  
             if (interp != NULL) {  
                 Tcl_SetResult(interp, "unmatched open brace in list",  
                         TCL_STATIC);  
             }  
             return TCL_ERROR;  
         } else if (inQuotes) {  
             if (interp != NULL) {  
                 Tcl_SetResult(interp, "unmatched open quote in list",  
                         TCL_STATIC);  
             }  
             return TCL_ERROR;  
         }  
         size = (p - elemStart);  
     }  
   
     done:  
     while ((p < limit) && (isspace(UCHAR(*p)))) { /* INTL: ISO space. */  
         p++;  
     }  
     *elementPtr = elemStart;  
     *nextPtr = p;  
     if (sizePtr != 0) {  
         *sizePtr = size;  
     }  
     return TCL_OK;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclCopyAndCollapse --  
  *  
  *      Copy a string and eliminate any backslashes that aren't in braces.  
  *  
  * Results:  
  *      There is no return value. Count characters get copied from src to  
  *      dst. Along the way, if backslash sequences are found outside braces,  
  *      the backslashes are eliminated in the copy. After scanning count  
  *      chars from source, a null character is placed at the end of dst.  
  *      Returns the number of characters that got copied.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 TclCopyAndCollapse(count, src, dst)  
     int count;                  /* Number of characters to copy from src. */  
     CONST char *src;            /* Copy from here... */  
     char *dst;                  /* ... to here. */  
 {  
     register char c;  
     int numRead;  
     int newCount = 0;  
     int backslashCount;  
   
     for (c = *src;  count > 0;  src++, c = *src, count--) {  
         if (c == '\\') {  
             backslashCount = Tcl_UtfBackslash(src, &numRead, dst);  
             dst += backslashCount;  
             newCount += backslashCount;  
             src += numRead-1;  
             count -= numRead-1;  
         } else {  
             *dst = c;  
             dst++;  
             newCount++;  
         }  
     }  
     *dst = 0;  
     return newCount;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_SplitList --  
  *  
  *      Splits a list up into its constituent fields.  
  *  
  * Results  
  *      The return value is normally TCL_OK, which means that  
  *      the list was successfully split up.  If TCL_ERROR is  
  *      returned, it means that "list" didn't have proper list  
  *      structure;  the interp's result will contain a more detailed  
  *      error message.  
  *  
  *      *argvPtr will be filled in with the address of an array  
  *      whose elements point to the elements of list, in order.  
  *      *argcPtr will get filled in with the number of valid elements  
  *      in the array.  A single block of memory is dynamically allocated  
  *      to hold both the argv array and a copy of the list (with  
  *      backslashes and braces removed in the standard way).  
  *      The caller must eventually free this memory by calling free()  
  *      on *argvPtr.  Note:  *argvPtr and *argcPtr are only modified  
  *      if the procedure returns normally.  
  *  
  * Side effects:  
  *      Memory is allocated.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_SplitList(interp, list, argcPtr, argvPtr)  
     Tcl_Interp *interp;         /* Interpreter to use for error reporting.  
                                  * If NULL, no error message is left. */  
     CONST char *list;           /* Pointer to string with list structure. */  
     int *argcPtr;               /* Pointer to location to fill in with  
                                  * the number of elements in the list. */  
     char ***argvPtr;            /* Pointer to place to store pointer to  
                                  * array of pointers to list elements. */  
 {  
     char **argv;  
     CONST char *l;  
     char *p;  
     int length, size, i, result, elSize, brace;  
     CONST char *element;  
   
     /*  
      * Figure out how much space to allocate.  There must be enough  
      * space for both the array of pointers and also for a copy of  
      * the list.  To estimate the number of pointers needed, count  
      * the number of space characters in the list.  
      */  
   
     for (size = 1, l = list; *l != 0; l++) {  
         if (isspace(UCHAR(*l))) { /* INTL: ISO space. */  
             size++;  
         }  
     }  
     size++;                     /* Leave space for final NULL pointer. */  
     argv = (char **) ckalloc((unsigned)  
             ((size * sizeof(char *)) + (l - list) + 1));  
     length = strlen(list);  
     for (i = 0, p = ((char *) argv) + size*sizeof(char *);  
             *list != 0;  i++) {  
         CONST char *prevList = list;  
           
         result = TclFindElement(interp, list, length, &element,  
                                 &list, &elSize, &brace);  
         length -= (list - prevList);  
         if (result != TCL_OK) {  
             ckfree((char *) argv);  
             return result;  
         }  
         if (*element == 0) {  
             break;  
         }  
         if (i >= size) {  
             ckfree((char *) argv);  
             if (interp != NULL) {  
                 Tcl_SetResult(interp, "internal error in Tcl_SplitList",  
                         TCL_STATIC);  
             }  
             return TCL_ERROR;  
         }  
         argv[i] = p;  
         if (brace) {  
             memcpy((VOID *) p, (VOID *) element, (size_t) elSize);  
             p += elSize;  
             *p = 0;  
             p++;  
         } else {  
             TclCopyAndCollapse(elSize, element, p);  
             p += elSize+1;  
         }  
     }  
   
     argv[i] = NULL;  
     *argvPtr = argv;  
     *argcPtr = i;  
     return TCL_OK;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_ScanElement --  
  *  
  *      This procedure is a companion procedure to Tcl_ConvertElement.  
  *      It scans a string to see what needs to be done to it (e.g. add  
  *      backslashes or enclosing braces) to make the string into a  
  *      valid Tcl list element.  
  *  
  * Results:  
  *      The return value is an overestimate of the number of characters  
  *      that will be needed by Tcl_ConvertElement to produce a valid  
  *      list element from string.  The word at *flagPtr is filled in  
  *      with a value needed by Tcl_ConvertElement when doing the actual  
  *      conversion.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_ScanElement(string, flagPtr)  
     register CONST char *string; /* String to convert to list element. */  
     register int *flagPtr;       /* Where to store information to guide  
                                   * Tcl_ConvertCountedElement. */  
 {  
     return Tcl_ScanCountedElement(string, -1, flagPtr);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_ScanCountedElement --  
  *  
  *      This procedure is a companion procedure to  
  *      Tcl_ConvertCountedElement.  It scans a string to see what  
  *      needs to be done to it (e.g. add backslashes or enclosing  
  *      braces) to make the string into a valid Tcl list element.  
  *      If length is -1, then the string is scanned up to the first  
  *      null byte.  
  *  
  * Results:  
  *      The return value is an overestimate of the number of characters  
  *      that will be needed by Tcl_ConvertCountedElement to produce a  
  *      valid list element from string.  The word at *flagPtr is  
  *      filled in with a value needed by Tcl_ConvertCountedElement  
  *      when doing the actual conversion.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_ScanCountedElement(string, length, flagPtr)  
     CONST char *string;         /* String to convert to Tcl list element. */  
     int length;                 /* Number of bytes in string, or -1. */  
     int *flagPtr;               /* Where to store information to guide  
                                  * Tcl_ConvertElement. */  
 {  
     int flags, nestingLevel;  
     register CONST char *p, *lastChar;  
   
     /*  
      * This procedure and Tcl_ConvertElement together do two things:  
      *  
      * 1. They produce a proper list, one that will yield back the  
      * argument strings when evaluated or when disassembled with  
      * Tcl_SplitList.  This is the most important thing.  
      *  
      * 2. They try to produce legible output, which means minimizing the  
      * use of backslashes (using braces instead).  However, there are  
      * some situations where backslashes must be used (e.g. an element  
      * like "{abc": the leading brace will have to be backslashed.  
      * For each element, one of three things must be done:  
      *  
      * (a) Use the element as-is (it doesn't contain any special  
      * characters).  This is the most desirable option.  
      *  
      * (b) Enclose the element in braces, but leave the contents alone.  
      * This happens if the element contains embedded space, or if it  
      * contains characters with special interpretation ($, [, ;, or \),  
      * or if it starts with a brace or double-quote, or if there are  
      * no characters in the element.  
      *  
      * (c) Don't enclose the element in braces, but add backslashes to  
      * prevent special interpretation of special characters.  This is a  
      * last resort used when the argument would normally fall under case  
      * (b) but contains unmatched braces.  It also occurs if the last  
      * character of the argument is a backslash or if the element contains  
      * a backslash followed by newline.  
      *  
      * The procedure figures out how many bytes will be needed to store  
      * the result (actually, it overestimates). It also collects information  
      * about the element in the form of a flags word.  
      *  
      * Note: list elements produced by this procedure and  
      * Tcl_ConvertCountedElement must have the property that they can be  
      * enclosing in curly braces to make sub-lists.  This means, for  
      * example, that we must not leave unmatched curly braces in the  
      * resulting list element.  This property is necessary in order for  
      * procedures like Tcl_DStringStartSublist to work.  
      */  
   
     nestingLevel = 0;  
     flags = 0;  
     if (string == NULL) {  
         string = "";  
     }  
     if (length == -1) {  
         length = strlen(string);  
     }  
     lastChar = string + length;  
     p = string;  
     if ((p == lastChar) || (*p == '{') || (*p == '"')) {  
         flags |= USE_BRACES;  
     }  
     for ( ; p < lastChar; p++) {  
         switch (*p) {  
             case '{':  
                 nestingLevel++;  
                 break;  
             case '}':  
                 nestingLevel--;  
                 if (nestingLevel < 0) {  
                     flags |= TCL_DONT_USE_BRACES|BRACES_UNMATCHED;  
                 }  
                 break;  
             case '[':  
             case '$':  
             case ';':  
             case ' ':  
             case '\f':  
             case '\n':  
             case '\r':  
             case '\t':  
             case '\v':  
                 flags |= USE_BRACES;  
                 break;  
             case '\\':  
                 if ((p+1 == lastChar) || (p[1] == '\n')) {  
                     flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED;  
                 } else {  
                     int size;  
   
                     Tcl_UtfBackslash(p, &size, NULL);  
                     p += size-1;  
                     flags |= USE_BRACES;  
                 }  
                 break;  
         }  
     }  
     if (nestingLevel != 0) {  
         flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED;  
     }  
     *flagPtr = flags;  
   
     /*  
      * Allow enough space to backslash every character plus leave  
      * two spaces for braces.  
      */  
   
     return 2*(p-string) + 2;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_ConvertElement --  
  *  
  *      This is a companion procedure to Tcl_ScanElement.  Given  
  *      the information produced by Tcl_ScanElement, this procedure  
  *      converts a string to a list element equal to that string.  
  *  
  * Results:  
  *      Information is copied to *dst in the form of a list element  
  *      identical to src (i.e. if Tcl_SplitList is applied to dst it  
  *      will produce a string identical to src).  The return value is  
  *      a count of the number of characters copied (not including the  
  *      terminating NULL character).  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_ConvertElement(src, dst, flags)  
     register CONST char *src;   /* Source information for list element. */  
     register char *dst;         /* Place to put list-ified element. */  
     register int flags;         /* Flags produced by Tcl_ScanElement. */  
 {  
     return Tcl_ConvertCountedElement(src, -1, dst, flags);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_ConvertCountedElement --  
  *  
  *      This is a companion procedure to Tcl_ScanCountedElement.  Given  
  *      the information produced by Tcl_ScanCountedElement, this  
  *      procedure converts a string to a list element equal to that  
  *      string.  
  *  
  * Results:  
  *      Information is copied to *dst in the form of a list element  
  *      identical to src (i.e. if Tcl_SplitList is applied to dst it  
  *      will produce a string identical to src).  The return value is  
  *      a count of the number of characters copied (not including the  
  *      terminating NULL character).  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_ConvertCountedElement(src, length, dst, flags)  
     register CONST char *src;   /* Source information for list element. */  
     int length;                 /* Number of bytes in src, or -1. */  
     char *dst;                  /* Place to put list-ified element. */  
     int flags;                  /* Flags produced by Tcl_ScanElement. */  
 {  
     register char *p = dst;  
     register CONST char *lastChar;  
   
     /*  
      * See the comment block at the beginning of the Tcl_ScanElement  
      * code for details of how this works.  
      */  
   
     if (src && length == -1) {  
         length = strlen(src);  
     }  
     if ((src == NULL) || (length == 0)) {  
         p[0] = '{';  
         p[1] = '}';  
         p[2] = 0;  
         return 2;  
     }  
     lastChar = src + length;  
     if ((flags & USE_BRACES) && !(flags & TCL_DONT_USE_BRACES)) {  
         *p = '{';  
         p++;  
         for ( ; src != lastChar; src++, p++) {  
             *p = *src;  
         }  
         *p = '}';  
         p++;  
     } else {  
         if (*src == '{') {  
             /*  
              * Can't have a leading brace unless the whole element is  
              * enclosed in braces.  Add a backslash before the brace.  
              * Furthermore, this may destroy the balance between open  
              * and close braces, so set BRACES_UNMATCHED.  
              */  
   
             p[0] = '\\';  
             p[1] = '{';  
             p += 2;  
             src++;  
             flags |= BRACES_UNMATCHED;  
         }  
         for (; src != lastChar; src++) {  
             switch (*src) {  
                 case ']':  
                 case '[':  
                 case '$':  
                 case ';':  
                 case ' ':  
                 case '\\':  
                 case '"':  
                     *p = '\\';  
                     p++;  
                     break;  
                 case '{':  
                 case '}':  
                     /*  
                      * It may not seem necessary to backslash braces, but  
                      * it is.  The reason for this is that the resulting  
                      * list element may actually be an element of a sub-list  
                      * enclosed in braces (e.g. if Tcl_DStringStartSublist  
                      * has been invoked), so there may be a brace mismatch  
                      * if the braces aren't backslashed.  
                      */  
   
                     if (flags & BRACES_UNMATCHED) {  
                         *p = '\\';  
                         p++;  
                     }  
                     break;  
                 case '\f':  
                     *p = '\\';  
                     p++;  
                     *p = 'f';  
                     p++;  
                     continue;  
                 case '\n':  
                     *p = '\\';  
                     p++;  
                     *p = 'n';  
                     p++;  
                     continue;  
                 case '\r':  
                     *p = '\\';  
                     p++;  
                     *p = 'r';  
                     p++;  
                     continue;  
                 case '\t':  
                     *p = '\\';  
                     p++;  
                     *p = 't';  
                     p++;  
                     continue;  
                 case '\v':  
                     *p = '\\';  
                     p++;  
                     *p = 'v';  
                     p++;  
                     continue;  
             }  
             *p = *src;  
             p++;  
         }  
     }  
     *p = '\0';  
     return p-dst;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_Merge --  
  *  
  *      Given a collection of strings, merge them together into a  
  *      single string that has proper Tcl list structured (i.e.  
  *      Tcl_SplitList may be used to retrieve strings equal to the  
  *      original elements, and Tcl_Eval will parse the string back  
  *      into its original elements).  
  *  
  * Results:  
  *      The return value is the address of a dynamically-allocated  
  *      string containing the merged list.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 char *  
 Tcl_Merge(argc, argv)  
     int argc;                   /* How many strings to merge. */  
     char **argv;                /* Array of string values. */  
 {  
 #   define LOCAL_SIZE 20  
     int localFlags[LOCAL_SIZE], *flagPtr;  
     int numChars;  
     char *result;  
     char *dst;  
     int i;  
   
     /*  
      * Pass 1: estimate space, gather flags.  
      */  
   
     if (argc <= LOCAL_SIZE) {  
         flagPtr = localFlags;  
     } else {  
         flagPtr = (int *) ckalloc((unsigned) argc*sizeof(int));  
     }  
     numChars = 1;  
     for (i = 0; i < argc; i++) {  
         numChars += Tcl_ScanElement(argv[i], &flagPtr[i]) + 1;  
     }  
   
     /*  
      * Pass two: copy into the result area.  
      */  
   
     result = (char *) ckalloc((unsigned) numChars);  
     dst = result;  
     for (i = 0; i < argc; i++) {  
         numChars = Tcl_ConvertElement(argv[i], dst, flagPtr[i]);  
         dst += numChars;  
         *dst = ' ';  
         dst++;  
     }  
     if (dst == result) {  
         *dst = 0;  
     } else {  
         dst[-1] = 0;  
     }  
   
     if (flagPtr != localFlags) {  
         ckfree((char *) flagPtr);  
     }  
     return result;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_Backslash --  
  *  
  *      Figure out how to handle a backslash sequence.  
  *  
  * Results:  
  *      The return value is the character that should be substituted  
  *      in place of the backslash sequence that starts at src.  If  
  *      readPtr isn't NULL then it is filled in with a count of the  
  *      number of characters in the backslash sequence.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 char  
 Tcl_Backslash(src, readPtr)  
     CONST char *src;            /* Points to the backslash character of  
                                  * a backslash sequence. */  
     int *readPtr;               /* Fill in with number of characters read  
                                  * from src, unless NULL. */  
 {  
     char buf[TCL_UTF_MAX];  
     Tcl_UniChar ch;  
   
     Tcl_UtfBackslash(src, readPtr, buf);  
     Tcl_UtfToUniChar(buf, &ch);  
     return (char) ch;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_Concat --  
  *  
  *      Concatenate a set of strings into a single large string.  
  *  
  * Results:  
  *      The return value is dynamically-allocated string containing  
  *      a concatenation of all the strings in argv, with spaces between  
  *      the original argv elements.  
  *  
  * Side effects:  
  *      Memory is allocated for the result;  the caller is responsible  
  *      for freeing the memory.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 char *  
 Tcl_Concat(argc, argv)  
     int argc;                   /* Number of strings to concatenate. */  
     char **argv;                /* Array of strings to concatenate. */  
 {  
     int totalSize, i;  
     char *p;  
     char *result;  
   
     for (totalSize = 1, i = 0; i < argc; i++) {  
         totalSize += strlen(argv[i]) + 1;  
     }  
     result = (char *) ckalloc((unsigned) totalSize);  
     if (argc == 0) {  
         *result = '\0';  
         return result;  
     }  
     for (p = result, i = 0; i < argc; i++) {  
         char *element;  
         int length;  
   
         /*  
          * Clip white space off the front and back of the string  
          * to generate a neater result, and ignore any empty  
          * elements.  
          */  
   
         element = argv[i];  
         while (isspace(UCHAR(*element))) { /* INTL: ISO space. */  
             element++;  
         }  
         for (length = strlen(element);  
                 (length > 0)  
                 && (isspace(UCHAR(element[length-1]))) /* INTL: ISO space. */  
                 && ((length < 2) || (element[length-2] != '\\'));  
                 length--) {  
             /* Null loop body. */  
         }  
         if (length == 0) {  
             continue;  
         }  
         memcpy((VOID *) p, (VOID *) element, (size_t) length);  
         p += length;  
         *p = ' ';  
         p++;  
     }  
     if (p != result) {  
         p[-1] = 0;  
     } else {  
         *p = 0;  
     }  
     return result;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_ConcatObj --  
  *  
  *      Concatenate the strings from a set of objects into a single string  
  *      object with spaces between the original strings.  
  *  
  * Results:  
  *      The return value is a new string object containing a concatenation  
  *      of the strings in objv. Its ref count is zero.  
  *  
  * Side effects:  
  *      A new object is created.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 Tcl_Obj *  
 Tcl_ConcatObj(objc, objv)  
     int objc;                   /* Number of objects to concatenate. */  
     Tcl_Obj *CONST objv[];      /* Array of objects to concatenate. */  
 {  
     int allocSize, finalSize, length, elemLength, i;  
     char *p;  
     char *element;  
     char *concatStr;  
     Tcl_Obj *objPtr;  
   
     /*  
      * Check first to see if all the items are of list type.  If so,  
      * we will concat them together as lists, and return a list object.  
      * This is only valid when the lists have no current string  
      * representation, since we don't know what the original type was.  
      * An original string rep may have lost some whitespace info when  
      * converted which could be important.  
      */  
     for (i = 0;  i < objc;  i++) {  
         objPtr = objv[i];  
         if ((objPtr->typePtr != &tclListType) || (objPtr->bytes != NULL)) {  
             break;  
         }  
     }  
     if (i == objc) {  
         Tcl_Obj **listv;  
         int listc;  
   
         objPtr = Tcl_NewListObj(0, NULL);  
         for (i = 0;  i < objc;  i++) {  
             /*  
              * Tcl_ListObjAppendList could be used here, but this saves  
              * us a bit of type checking (since we've already done it)  
              * Use of INT_MAX tells us to always put the new stuff on  
              * the end.  It will be set right in Tcl_ListObjReplace.  
              */  
             Tcl_ListObjGetElements(NULL, objv[i], &listc, &listv);  
             Tcl_ListObjReplace(NULL, objPtr, INT_MAX, 0, listc, listv);  
         }  
         return objPtr;  
     }  
   
     allocSize = 0;  
     for (i = 0;  i < objc;  i++) {  
         objPtr = objv[i];  
         element = Tcl_GetStringFromObj(objPtr, &length);  
         if ((element != NULL) && (length > 0)) {  
             allocSize += (length + 1);  
         }  
     }  
     if (allocSize == 0) {  
         allocSize = 1;          /* enough for the NULL byte at end */  
     }  
   
     /*  
      * Allocate storage for the concatenated result. Note that allocSize  
      * is one more than the total number of characters, and so includes  
      * room for the terminating NULL byte.  
      */  
       
     concatStr = (char *) ckalloc((unsigned) allocSize);  
   
     /*  
      * Now concatenate the elements. Clip white space off the front and back  
      * to generate a neater result, and ignore any empty elements. Also put  
      * a null byte at the end.  
      */  
   
     finalSize = 0;  
     if (objc == 0) {  
         *concatStr = '\0';  
     } else {  
         p = concatStr;  
         for (i = 0;  i < objc;  i++) {  
             objPtr = objv[i];  
             element = Tcl_GetStringFromObj(objPtr, &elemLength);  
             while ((elemLength > 0)  
                     && (isspace(UCHAR(*element)))) { /* INTL: ISO space. */  
                  element++;  
                  elemLength--;  
             }  
   
             /*  
              * Trim trailing white space.  But, be careful not to trim  
              * a space character if it is preceded by a backslash: in  
              * this case it could be significant.  
              */  
   
             while ((elemLength > 0)  
                     && isspace(UCHAR(element[elemLength-1])) /* INTL: ISO space. */  
                     && ((elemLength < 2) || (element[elemLength-2] != '\\'))) {  
                 elemLength--;  
             }  
             if (elemLength == 0) {  
                  continue;      /* nothing left of this element */  
             }  
             memcpy((VOID *) p, (VOID *) element, (size_t) elemLength);  
             p += elemLength;  
             *p = ' ';  
             p++;  
             finalSize += (elemLength + 1);  
         }  
         if (p != concatStr) {  
             p[-1] = 0;  
             finalSize -= 1;     /* we overwrote the final ' ' */  
         } else {  
             *p = 0;  
         }  
     }  
       
     TclNewObj(objPtr);  
     objPtr->bytes  = concatStr;  
     objPtr->length = finalSize;  
     return objPtr;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_StringMatch --  
  *  
  *      See if a particular string matches a particular pattern.  
  *  
  * Results:  
  *      The return value is 1 if string matches pattern, and  
  *      0 otherwise.  The matching operation permits the following  
  *      special characters in the pattern: *?\[] (see the manual  
  *      entry for details on what these mean).  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_StringMatch(string, pattern)  
     CONST char *string;         /* String. */  
     CONST char *pattern;        /* Pattern, which may contain special  
                                  * characters. */  
 {  
     int p, s;  
     CONST char *pstart = pattern;  
       
     while (1) {  
         p = *pattern;  
         s = *string;  
           
         /*  
          * See if we're at the end of both the pattern and the string.  If  
          * so, we succeeded.  If we're at the end of the pattern but not at  
          * the end of the string, we failed.  
          */  
           
         if (p == '\0') {  
             if (s == '\0') {  
                 return 1;  
             } else {  
                 return 0;  
             }  
         }  
         if ((s == '\0') && (p != '*')) {  
             return 0;  
         }  
   
         /* Check for a "*" as the next pattern character.  It matches  
          * any substring.  We handle this by calling ourselves  
          * recursively for each postfix of string, until either we  
          * match or we reach the end of the string.  
          */  
           
         if (p == '*') {  
             pattern++;  
             if (*pattern == '\0') {  
                 return 1;  
             }  
             while (1) {  
                 if (Tcl_StringMatch(string, pattern)) {  
                     return 1;  
                 }  
                 if (*string == '\0') {  
                     return 0;  
                 }  
                 string++;  
             }  
         }  
   
         /* Check for a "?" as the next pattern character.  It matches  
          * any single character.  
          */  
   
         if (p == '?') {  
             Tcl_UniChar ch;  
               
             pattern++;  
             string += Tcl_UtfToUniChar(string, &ch);  
             continue;  
         }  
   
         /* Check for a "[" as the next pattern character.  It is followed  
          * by a list of characters that are acceptable, or by a range  
          * (two characters separated by "-").  
          */  
           
         if (p == '[') {  
             Tcl_UniChar ch, startChar, endChar;  
   
             pattern++;  
             string += Tcl_UtfToUniChar(string, &ch);  
   
             while (1) {  
                 if ((*pattern == ']') || (*pattern == '\0')) {  
                     return 0;  
                 }  
                 pattern += Tcl_UtfToUniChar(pattern, &startChar);  
                 if (*pattern == '-') {  
                     pattern++;  
                     if (*pattern == '\0') {  
                         return 0;  
                     }  
                     pattern += Tcl_UtfToUniChar(pattern, &endChar);  
                     if (((startChar <= ch) && (ch <= endChar))  
                             || ((endChar <= ch) && (ch <= startChar))) {  
                         /*  
                          * Matches ranges of form [a-z] or [z-a].  
                          */  
   
                         break;  
                     }  
                 } else if (startChar == ch) {  
                     break;  
                 }  
             }  
             while (*pattern != ']') {  
                 if (*pattern == '\0') {  
                     pattern = Tcl_UtfPrev(pattern, pstart);  
                     break;  
                 }  
                 pattern++;  
             }  
             pattern++;  
             continue;  
         }  
       
         /* If the next pattern character is '\', just strip off the '\'  
          * so we do exact matching on the character that follows.  
          */  
           
         if (p == '\\') {  
             pattern++;  
             p = *pattern;  
             if (p == '\0') {  
                 return 0;  
             }  
         }  
   
         /* There's no special character.  Just make sure that the next  
          * bytes of each string match.  
          */  
           
         if (s != p) {  
             return 0;  
         }  
         pattern++;  
         string++;  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_StringCaseMatch --  
  *  
  *      See if a particular string matches a particular pattern.  
  *      Allows case insensitivity.  
  *  
  * Results:  
  *      The return value is 1 if string matches pattern, and  
  *      0 otherwise.  The matching operation permits the following  
  *      special characters in the pattern: *?\[] (see the manual  
  *      entry for details on what these mean).  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_StringCaseMatch(string, pattern, nocase)  
     CONST char *string;         /* String. */  
     CONST char *pattern;        /* Pattern, which may contain special  
                                  * characters. */  
     int nocase;                 /* 0 for case sensitive, 1 for insensitive */  
 {  
     int p, s;  
     CONST char *pstart = pattern;  
     Tcl_UniChar ch1, ch2;  
       
     while (1) {  
         p = *pattern;  
         s = *string;  
           
         /*  
          * See if we're at the end of both the pattern and the string.  If  
          * so, we succeeded.  If we're at the end of the pattern but not at  
          * the end of the string, we failed.  
          */  
           
         if (p == '\0') {  
             return (s == '\0');  
         }  
         if ((s == '\0') && (p != '*')) {  
             return 0;  
         }  
   
         /* Check for a "*" as the next pattern character.  It matches  
          * any substring.  We handle this by calling ourselves  
          * recursively for each postfix of string, until either we  
          * match or we reach the end of the string.  
          */  
           
         if (p == '*') {  
             pattern++;  
             if (*pattern == '\0') {  
                 return 1;  
             }  
             while (1) {  
                 if (Tcl_StringCaseMatch(string, pattern, nocase)) {  
                     return 1;  
                 }  
                 if (*string == '\0') {  
                     return 0;  
                 }  
                 string++;  
             }  
         }  
   
         /* Check for a "?" as the next pattern character.  It matches  
          * any single character.  
          */  
   
         if (p == '?') {  
             pattern++;  
             string += Tcl_UtfToUniChar(string, &ch1);  
             continue;  
         }  
   
         /* Check for a "[" as the next pattern character.  It is followed  
          * by a list of characters that are acceptable, or by a range  
          * (two characters separated by "-").  
          */  
           
         if (p == '[') {  
             Tcl_UniChar startChar, endChar;  
   
             pattern++;  
             string += Tcl_UtfToUniChar(string, &ch1);  
             if (nocase) {  
                 ch1 = Tcl_UniCharToLower(ch1);  
             }  
             while (1) {  
                 if ((*pattern == ']') || (*pattern == '\0')) {  
                     return 0;  
                 }  
                 pattern += Tcl_UtfToUniChar(pattern, &startChar);  
                 if (nocase) {  
                     startChar = Tcl_UniCharToLower(startChar);  
                 }  
                 if (*pattern == '-') {  
                     pattern++;  
                     if (*pattern == '\0') {  
                         return 0;  
                     }  
                     pattern += Tcl_UtfToUniChar(pattern, &endChar);  
                     if (nocase) {  
                         endChar = Tcl_UniCharToLower(endChar);  
                     }  
                     if (((startChar <= ch1) && (ch1 <= endChar))  
                             || ((endChar <= ch1) && (ch1 <= startChar))) {  
                         /*  
                          * Matches ranges of form [a-z] or [z-a].  
                          */  
   
                         break;  
                     }  
                 } else if (startChar == ch1) {  
                     break;  
                 }  
             }  
             while (*pattern != ']') {  
                 if (*pattern == '\0') {  
                     pattern = Tcl_UtfPrev(pattern, pstart);  
                     break;  
                 }  
                 pattern++;  
             }  
             pattern++;  
             continue;  
         }  
       
         /* If the next pattern character is '\', just strip off the '\'  
          * so we do exact matching on the character that follows.  
          */  
           
         if (p == '\\') {  
             pattern++;  
             p = *pattern;  
             if (p == '\0') {  
                 return 0;  
             }  
         }  
   
         /* There's no special character.  Just make sure that the next  
          * bytes of each string match.  
          */  
           
         string  += Tcl_UtfToUniChar(string, &ch1);  
         pattern += Tcl_UtfToUniChar(pattern, &ch2);  
         if (nocase) {  
             if (Tcl_UniCharToLower(ch1) != Tcl_UniCharToLower(ch2)) {  
                 return 0;  
             }  
         } else if (ch1 != ch2) {  
             return 0;  
         }  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DStringInit --  
  *  
  *      Initializes a dynamic string, discarding any previous contents  
  *      of the string (Tcl_DStringFree should have been called already  
  *      if the dynamic string was previously in use).  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The dynamic string is initialized to be empty.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_DStringInit(dsPtr)  
     Tcl_DString *dsPtr;         /* Pointer to structure for dynamic string. */  
 {  
     dsPtr->string = dsPtr->staticSpace;  
     dsPtr->length = 0;  
     dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;  
     dsPtr->staticSpace[0] = '\0';  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DStringAppend --  
  *  
  *      Append more characters to the current value of a dynamic string.  
  *  
  * Results:  
  *      The return value is a pointer to the dynamic string's new value.  
  *  
  * Side effects:  
  *      Length bytes from string (or all of string if length is less  
  *      than zero) are added to the current value of the string. Memory  
  *      gets reallocated if needed to accomodate the string's new size.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 char *  
 Tcl_DStringAppend(dsPtr, string, length)  
     Tcl_DString *dsPtr;         /* Structure describing dynamic string. */  
     CONST char *string;         /* String to append.  If length is -1 then  
                                  * this must be null-terminated. */  
     int length;                 /* Number of characters from string to  
                                  * append.  If < 0, then append all of string,  
                                  * up to null at end. */  
 {  
     int newSize;  
     char *dst;  
     CONST char *end;  
   
     if (length < 0) {  
         length = strlen(string);  
     }  
     newSize = length + dsPtr->length;  
   
     /*  
      * Allocate a larger buffer for the string if the current one isn't  
      * large enough. Allocate extra space in the new buffer so that there  
      * will be room to grow before we have to allocate again.  
      */  
   
     if (newSize >= dsPtr->spaceAvl) {  
         dsPtr->spaceAvl = newSize * 2;  
         if (dsPtr->string == dsPtr->staticSpace) {  
             char *newString;  
   
             newString = (char *) ckalloc((unsigned) dsPtr->spaceAvl);  
             memcpy((VOID *) newString, (VOID *) dsPtr->string,  
                     (size_t) dsPtr->length);  
             dsPtr->string = newString;  
         } else {  
             dsPtr->string = (char *) ckrealloc((VOID *) dsPtr->string,  
                     (size_t) dsPtr->spaceAvl);  
         }  
     }  
   
     /*  
      * Copy the new string into the buffer at the end of the old  
      * one.  
      */  
   
     for (dst = dsPtr->string + dsPtr->length, end = string+length;  
             string < end; string++, dst++) {  
         *dst = *string;  
     }  
     *dst = '\0';  
     dsPtr->length += length;  
     return dsPtr->string;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DStringAppendElement --  
  *  
  *      Append a list element to the current value of a dynamic string.  
  *  
  * Results:  
  *      The return value is a pointer to the dynamic string's new value.  
  *  
  * Side effects:  
  *      String is reformatted as a list element and added to the current  
  *      value of the string.  Memory gets reallocated if needed to  
  *      accomodate the string's new size.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 char *  
 Tcl_DStringAppendElement(dsPtr, string)  
     Tcl_DString *dsPtr;         /* Structure describing dynamic string. */  
     CONST char *string;         /* String to append.  Must be  
                                  * null-terminated. */  
 {  
     int newSize, flags;  
     char *dst;  
   
     newSize = Tcl_ScanElement(string, &flags) + dsPtr->length + 1;  
   
     /*  
      * Allocate a larger buffer for the string if the current one isn't  
      * large enough.  Allocate extra space in the new buffer so that there  
      * will be room to grow before we have to allocate again.  
      * SPECIAL NOTE: must use memcpy, not strcpy, to copy the string  
      * to a larger buffer, since there may be embedded NULLs in the  
      * string in some cases.  
      */  
   
     if (newSize >= dsPtr->spaceAvl) {  
         dsPtr->spaceAvl = newSize * 2;  
         if (dsPtr->string == dsPtr->staticSpace) {  
             char *newString;  
   
             newString = (char *) ckalloc((unsigned) dsPtr->spaceAvl);  
             memcpy((VOID *) newString, (VOID *) dsPtr->string,  
                     (size_t) dsPtr->length);  
             dsPtr->string = newString;  
         } else {  
             dsPtr->string = (char *) ckrealloc((VOID *) dsPtr->string,  
                     (size_t) dsPtr->spaceAvl);  
         }  
     }  
   
     /*  
      * Convert the new string to a list element and copy it into the  
      * buffer at the end, with a space, if needed.  
      */  
   
     dst = dsPtr->string + dsPtr->length;  
     if (TclNeedSpace(dsPtr->string, dst)) {  
         *dst = ' ';  
         dst++;  
         dsPtr->length++;  
     }  
     dsPtr->length += Tcl_ConvertElement(string, dst, flags);  
     return dsPtr->string;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DStringSetLength --  
  *  
  *      Change the length of a dynamic string.  This can cause the  
  *      string to either grow or shrink, depending on the value of  
  *      length.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The length of dsPtr is changed to length and a null byte is  
  *      stored at that position in the string.  If length is larger  
  *      than the space allocated for dsPtr, then a panic occurs.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_DStringSetLength(dsPtr, length)  
     Tcl_DString *dsPtr;         /* Structure describing dynamic string. */  
     int length;                 /* New length for dynamic string. */  
 {  
     int newsize;  
   
     if (length < 0) {  
         length = 0;  
     }  
     if (length >= dsPtr->spaceAvl) {  
         /*  
          * There are two interesting cases here.  In the first case, the user  
          * may be trying to allocate a large buffer of a specific size.  It  
          * would be wasteful to overallocate that buffer, so we just allocate  
          * enough for the requested size plus the trailing null byte.  In the  
          * second case, we are growing the buffer incrementally, so we need  
          * behavior similar to Tcl_DStringAppend.  The requested length will  
          * usually be a small delta above the current spaceAvl, so we'll end up  
          * doubling the old size.  This won't grow the buffer quite as quickly,  
          * but it should be close enough.  
          */  
   
         newsize = dsPtr->spaceAvl * 2;  
         if (length < newsize) {  
             dsPtr->spaceAvl = newsize;  
         } else {  
             dsPtr->spaceAvl = length + 1;  
         }  
         if (dsPtr->string == dsPtr->staticSpace) {  
             char *newString;  
   
             newString = (char *) ckalloc((unsigned) dsPtr->spaceAvl);  
             memcpy((VOID *) newString, (VOID *) dsPtr->string,  
                     (size_t) dsPtr->length);  
             dsPtr->string = newString;  
         } else {  
             dsPtr->string = (char *) ckrealloc((VOID *) dsPtr->string,  
                     (size_t) dsPtr->spaceAvl);  
         }  
     }  
     dsPtr->length = length;  
     dsPtr->string[length] = 0;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DStringFree --  
  *  
  *      Frees up any memory allocated for the dynamic string and  
  *      reinitializes the string to an empty state.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The previous contents of the dynamic string are lost, and  
  *      the new value is an empty string.  
  *  
  *---------------------------------------------------------------------- */  
   
 void  
 Tcl_DStringFree(dsPtr)  
     Tcl_DString *dsPtr;         /* Structure describing dynamic string. */  
 {  
     if (dsPtr->string != dsPtr->staticSpace) {  
         ckfree(dsPtr->string);  
     }  
     dsPtr->string = dsPtr->staticSpace;  
     dsPtr->length = 0;  
     dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;  
     dsPtr->staticSpace[0] = '\0';  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DStringResult --  
  *  
  *      This procedure moves the value of a dynamic string into an  
  *      interpreter as its string result. Afterwards, the dynamic string  
  *      is reset to an empty string.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The string is "moved" to interp's result, and any existing  
  *      string result for interp is freed. dsPtr is reinitialized to  
  *      an empty string.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_DStringResult(interp, dsPtr)  
     Tcl_Interp *interp;         /* Interpreter whose result is to be reset. */  
     Tcl_DString *dsPtr;         /* Dynamic string that is to become the  
                                  * result of interp. */  
 {  
     Tcl_ResetResult(interp);  
       
     if (dsPtr->string != dsPtr->staticSpace) {  
         interp->result = dsPtr->string;  
         interp->freeProc = TCL_DYNAMIC;  
     } else if (dsPtr->length < TCL_RESULT_SIZE) {  
         interp->result = ((Interp *) interp)->resultSpace;  
         strcpy(interp->result, dsPtr->string);  
     } else {  
         Tcl_SetResult(interp, dsPtr->string, TCL_VOLATILE);  
     }  
       
     dsPtr->string = dsPtr->staticSpace;  
     dsPtr->length = 0;  
     dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;  
     dsPtr->staticSpace[0] = '\0';  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DStringGetResult --  
  *  
  *      This procedure moves an interpreter's result into a dynamic string.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The interpreter's string result is cleared, and the previous  
  *      contents of dsPtr are freed.  
  *  
  *      If the string result is empty, the object result is moved to the  
  *      string result, then the object result is reset.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_DStringGetResult(interp, dsPtr)  
     Tcl_Interp *interp;         /* Interpreter whose result is to be reset. */  
     Tcl_DString *dsPtr;         /* Dynamic string that is to become the  
                                  * result of interp. */  
 {  
     Interp *iPtr = (Interp *) interp;  
       
     if (dsPtr->string != dsPtr->staticSpace) {  
         ckfree(dsPtr->string);  
     }  
   
     /*  
      * If the string result is empty, move the object result to the  
      * string result, then reset the object result.  
      */  
   
     if (*(iPtr->result) == 0) {  
         Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)),  
                 TCL_VOLATILE);  
     }  
   
     dsPtr->length = strlen(iPtr->result);  
     if (iPtr->freeProc != NULL) {  
         if ((iPtr->freeProc == TCL_DYNAMIC)  
                 || (iPtr->freeProc == (Tcl_FreeProc *) free)) {  
             dsPtr->string = iPtr->result;  
             dsPtr->spaceAvl = dsPtr->length+1;  
         } else {  
             dsPtr->string = (char *) ckalloc((unsigned) (dsPtr->length+1));  
             strcpy(dsPtr->string, iPtr->result);  
             (*iPtr->freeProc)(iPtr->result);  
         }  
         dsPtr->spaceAvl = dsPtr->length+1;  
         iPtr->freeProc = NULL;  
     } else {  
         if (dsPtr->length < TCL_DSTRING_STATIC_SIZE) {  
             dsPtr->string = dsPtr->staticSpace;  
             dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;  
         } else {  
             dsPtr->string = (char *) ckalloc((unsigned) (dsPtr->length + 1));  
             dsPtr->spaceAvl = dsPtr->length + 1;  
         }  
         strcpy(dsPtr->string, iPtr->result);  
     }  
       
     iPtr->result = iPtr->resultSpace;  
     iPtr->resultSpace[0] = 0;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DStringStartSublist --  
  *  
  *      This procedure adds the necessary information to a dynamic  
  *      string (e.g. " {" to start a sublist.  Future element  
  *      appends will be in the sublist rather than the main list.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Characters get added to the dynamic string.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_DStringStartSublist(dsPtr)  
     Tcl_DString *dsPtr;                 /* Dynamic string. */  
 {  
     if (TclNeedSpace(dsPtr->string, dsPtr->string + dsPtr->length)) {  
         Tcl_DStringAppend(dsPtr, " {", -1);  
     } else {  
         Tcl_DStringAppend(dsPtr, "{", -1);  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DStringEndSublist --  
  *  
  *      This procedure adds the necessary characters to a dynamic  
  *      string to end a sublist (e.g. "}").  Future element appends  
  *      will be in the enclosing (sub)list rather than the current  
  *      sublist.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_DStringEndSublist(dsPtr)  
     Tcl_DString *dsPtr;                 /* Dynamic string. */  
 {  
     Tcl_DStringAppend(dsPtr, "}", -1);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_PrintDouble --  
  *  
  *      Given a floating-point value, this procedure converts it to  
  *      an ASCII string using.  
  *  
  * Results:  
  *      The ASCII equivalent of "value" is written at "dst".  It is  
  *      written using the current precision, and it is guaranteed to  
  *      contain a decimal point or exponent, so that it looks like  
  *      a floating-point value and not an integer.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_PrintDouble(interp, value, dst)  
     Tcl_Interp *interp;                 /* Interpreter whose tcl_precision  
                                          * variable used to be used to control  
                                          * printing.  It's ignored now. */  
     double value;                       /* Value to print as string. */  
     char *dst;                          /* Where to store converted value;  
                                          * must have at least TCL_DOUBLE_SPACE  
                                          * characters. */  
 {  
     char *p, c;  
     Tcl_UniChar ch;  
   
     Tcl_MutexLock(&precisionMutex);  
     sprintf(dst, precisionFormat, value);  
     Tcl_MutexUnlock(&precisionMutex);  
   
     /*  
      * If the ASCII result looks like an integer, add ".0" so that it  
      * doesn't look like an integer anymore.  This prevents floating-point  
      * values from being converted to integers unintentionally.  
      */  
   
     for (p = dst; *p != 0; ) {  
         p += Tcl_UtfToUniChar(p, &ch);  
         c = UCHAR(ch);  
         if ((c == '.') || isalpha(UCHAR(c))) {  /* INTL: ISO only. */  
             return;  
         }  
     }  
     p[0] = '.';  
     p[1] = '0';  
     p[2] = 0;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclPrecTraceProc --  
  *  
  *      This procedure is invoked whenever the variable "tcl_precision"  
  *      is written.  
  *  
  * Results:  
  *      Returns NULL if all went well, or an error message if the  
  *      new value for the variable doesn't make sense.  
  *  
  * Side effects:  
  *      If the new value doesn't make sense then this procedure  
  *      undoes the effect of the variable modification.  Otherwise  
  *      it modifies the format string that's used by Tcl_PrintDouble.  
  *  
  *----------------------------------------------------------------------  
  */  
   
         /* ARGSUSED */  
 char *  
 TclPrecTraceProc(clientData, interp, name1, name2, flags)  
     ClientData clientData;      /* Not used. */  
     Tcl_Interp *interp;         /* Interpreter containing variable. */  
     char *name1;                /* Name of variable. */  
     char *name2;                /* Second part of variable name. */  
     int flags;                  /* Information about what happened. */  
 {  
     char *value, *end;  
     int prec;  
   
     /*  
      * If the variable is unset, then recreate the trace.  
      */  
   
     if (flags & TCL_TRACE_UNSETS) {  
         if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {  
             Tcl_TraceVar2(interp, name1, name2,  
                     TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES  
                     |TCL_TRACE_UNSETS, TclPrecTraceProc, clientData);  
         }  
         return (char *) NULL;  
     }  
   
     /*  
      * When the variable is read, reset its value from our shared  
      * value.  This is needed in case the variable was modified in  
      * some other interpreter so that this interpreter's value is  
      * out of date.  
      */  
   
     Tcl_MutexLock(&precisionMutex);  
   
     if (flags & TCL_TRACE_READS) {  
         Tcl_SetVar2(interp, name1, name2, precisionString,  
                 flags & TCL_GLOBAL_ONLY);  
         Tcl_MutexUnlock(&precisionMutex);  
         return (char *) NULL;  
     }  
   
     /*  
      * The variable is being written.  Check the new value and disallow  
      * it if it isn't reasonable or if this is a safe interpreter (we  
      * don't want safe interpreters messing up the precision of other  
      * interpreters).  
      */  
   
     if (Tcl_IsSafe(interp)) {  
         Tcl_SetVar2(interp, name1, name2, precisionString,  
                 flags & TCL_GLOBAL_ONLY);  
         Tcl_MutexUnlock(&precisionMutex);  
         return "can't modify precision from a safe interpreter";  
     }  
     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);  
     if (value == NULL) {  
         value = "";  
     }  
     prec = strtoul(value, &end, 10);  
     if ((prec <= 0) || (prec > TCL_MAX_PREC) || (prec > 100) ||  
             (end == value) || (*end != 0)) {  
         Tcl_SetVar2(interp, name1, name2, precisionString,  
                 flags & TCL_GLOBAL_ONLY);  
         Tcl_MutexUnlock(&precisionMutex);  
         return "improper value for precision";  
     }  
     TclFormatInt(precisionString, prec);  
     sprintf(precisionFormat, "%%.%dg", prec);  
     Tcl_MutexUnlock(&precisionMutex);  
     return (char *) NULL;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclNeedSpace --  
  *  
  *      This procedure checks to see whether it is appropriate to  
  *      add a space before appending a new list element to an  
  *      existing string.  
  *  
  * Results:  
  *      The return value is 1 if a space is appropriate, 0 otherwise.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 TclNeedSpace(start, end)  
     char *start;                /* First character in string. */  
     char *end;                  /* End of string (place where space will  
                                  * be added, if appropriate). */  
 {  
     /*  
      * A space is needed unless either  
      * (a) we're at the start of the string, or  
      * (b) the trailing characters of the string consist of one or more  
      *     open curly braces preceded by a space or extending back to  
      *     the beginning of the string.  
      * (c) the trailing characters of the string consist of a space  
      *     preceded by a character other than backslash.  
      */  
   
     if (end == start) {  
         return 0;  
     }  
     end--;  
     if (*end != '{') {  
         if (isspace(UCHAR(*end)) /* INTL: ISO space. */  
                 && ((end == start) || (end[-1] != '\\'))) {  
             return 0;  
         }  
         return 1;  
     }  
     do {  
         if (end == start) {  
             return 0;  
         }  
         end--;  
     } while (*end == '{');  
     if (isspace(UCHAR(*end))) { /* INTL: ISO space. */  
         return 0;  
     }  
     return 1;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclFormatInt --  
  *  
  *      This procedure formats an integer into a sequence of decimal digit  
  *      characters in a buffer. If the integer is negative, a minus sign is  
  *      inserted at the start of the buffer. A null character is inserted at  
  *      the end of the formatted characters. It is the caller's  
  *      responsibility to ensure that enough storage is available. This  
  *      procedure has the effect of sprintf(buffer, "%d", n) but is faster.  
  *  
  * Results:  
  *      An integer representing the number of characters formatted, not  
  *      including the terminating \0.  
  *  
  * Side effects:  
  *      The formatted characters are written into the storage pointer to  
  *      by the "buffer" argument.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 TclFormatInt(buffer, n)  
     char *buffer;               /* Points to the storage into which the  
                                  * formatted characters are written. */  
     long n;                     /* The integer to format. */  
 {  
     long intVal;  
     int i;  
     int numFormatted, j;  
     char *digits = "0123456789";  
   
     /*  
      * Check first whether "n" is zero.  
      */  
   
     if (n == 0) {  
         buffer[0] = '0';  
         buffer[1] = 0;  
         return 1;  
     }  
   
     /*  
      * Check whether "n" is the maximum negative value. This is  
      * -2^(m-1) for an m-bit word, and has no positive equivalent;  
      * negating it produces the same value.  
      */  
   
     if (n == -n) {  
         sprintf(buffer, "%ld", n);  
         return strlen(buffer);  
     }  
   
     /*  
      * Generate the characters of the result backwards in the buffer.  
      */  
   
     intVal = (n < 0? -n : n);  
     i = 0;  
     buffer[0] = '\0';  
     do {  
         i++;  
         buffer[i] = digits[intVal % 10];  
         intVal = intVal/10;  
     } while (intVal > 0);  
     if (n < 0) {  
         i++;  
         buffer[i] = '-';  
     }  
     numFormatted = i;  
   
     /*  
      * Now reverse the characters.  
      */  
   
     for (j = 0;  j < i;  j++, i--) {  
         char tmp = buffer[i];  
         buffer[i] = buffer[j];  
         buffer[j] = tmp;  
     }  
     return numFormatted;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclLooksLikeInt --  
  *  
  *      This procedure decides whether the leading characters of a  
  *      string look like an integer or something else (such as a  
  *      floating-point number or string).  
  *  
  * Results:  
  *      The return value is 1 if the leading characters of p look  
  *      like a valid Tcl integer.  If they look like a floating-point  
  *      number (e.g. "e01" or "2.4"), or if they don't look like a  
  *      number at all, then 0 is returned.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 TclLooksLikeInt(bytes, length)  
     register char *bytes;       /* Points to first byte of the string. */  
     int length;                 /* Number of bytes in the string. If < 0  
                                  * bytes up to the first null byte are  
                                  * considered (if they may appear in an  
                                  * integer). */  
 {  
     register char *p, *end;  
   
     if (length < 0) {  
         length = (bytes? strlen(bytes) : 0);  
     }  
     end = (bytes + length);  
   
     p = bytes;  
     while ((p < end) && isspace(UCHAR(*p))) { /* INTL: ISO space. */  
         p++;  
     }  
     if (p == end) {  
         return 0;  
     }  
       
     if ((*p == '+') || (*p == '-')) {  
         p++;  
     }  
     if ((p == end) || !isdigit(UCHAR(*p))) { /* INTL: digit */  
         return 0;  
     }  
     p++;  
     while ((p < end) && isdigit(UCHAR(*p))) { /* INTL: digit */  
         p++;  
     }  
     if (p == end) {  
         return 1;  
     }  
     if ((*p != '.') && (*p != 'e') && (*p != 'E')) {  
         return 1;  
     }  
     return 0;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclGetIntForIndex --  
  *  
  *      This procedure returns an integer corresponding to the list index  
  *      held in a Tcl object. The Tcl object's value is expected to be  
  *      either an integer or a string of the form "end([+-]integer)?".  
  *  
  * Results:  
  *      The return value is normally TCL_OK, which means that the index was  
  *      successfully stored into the location referenced by "indexPtr".  If  
  *      the Tcl object referenced by "objPtr" has the value "end", the  
  *      value stored is "endValue". If "objPtr"s values is not of the form  
  *      "end([+-]integer)?" and  
  *      can not be converted to an integer, TCL_ERROR is returned and, if  
  *      "interp" is non-NULL, an error message is left in the interpreter's  
  *      result object.  
  *  
  * Side effects:  
  *      The object referenced by "objPtr" might be converted to an  
  *      integer object.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 TclGetIntForIndex(interp, objPtr, endValue, indexPtr)  
     Tcl_Interp *interp;         /* Interpreter to use for error reporting.  
                                  * If NULL, then no error message is left  
                                  * after errors. */  
     Tcl_Obj *objPtr;            /* Points to an object containing either  
                                  * "end" or an integer. */  
     int endValue;               /* The value to be stored at "indexPtr" if  
                                  * "objPtr" holds "end". */  
     int *indexPtr;              /* Location filled in with an integer  
                                  * representing an index. */  
 {  
     char *bytes;  
     int length, offset;  
   
     if (objPtr->typePtr == &tclIntType) {  
         *indexPtr = (int)objPtr->internalRep.longValue;  
         return TCL_OK;  
     }  
   
     bytes = Tcl_GetStringFromObj(objPtr, &length);  
   
     if ((*bytes != 'e') || (strncmp(bytes, "end",  
             (size_t)((length > 3) ? 3 : length)) != 0)) {  
         if (Tcl_GetIntFromObj(NULL, objPtr, &offset) != TCL_OK) {  
             goto intforindex_error;  
         }  
         *indexPtr = offset;  
         return TCL_OK;  
     }  
   
     if (length <= 3) {  
         *indexPtr = endValue;  
     } else if (bytes[3] == '-') {  
         /*  
          * This is our limited string expression evaluator  
          */  
         if (Tcl_GetInt(interp, bytes+3, &offset) != TCL_OK) {  
             return TCL_ERROR;  
         }  
         *indexPtr = endValue + offset;  
     } else {  
         intforindex_error:  
         if ((Interp *)interp != NULL) {  
             Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),  
                     "bad index \"", bytes,  
                     "\": must be integer or end?-integer?", (char *) NULL);  
             TclCheckBadOctal(interp, bytes);  
         }  
         return TCL_ERROR;  
     }  
     return TCL_OK;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclCheckBadOctal --  
  *  
  *      This procedure checks for a bad octal value and appends a  
  *      meaningful error to the interp's result.  
  *  
  * Results:  
  *      1 if the argument was a bad octal, else 0.  
  *  
  * Side effects:  
  *      The interpreter's result is modified.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 TclCheckBadOctal(interp, value)  
     Tcl_Interp *interp;         /* Interpreter to use for error reporting.  
                                  * If NULL, then no error message is left  
                                  * after errors. */  
     char *value;                /* String to check. */  
 {  
     register char *p = value;  
   
     /*  
      * A frequent mistake is invalid octal values due to an unwanted  
      * leading zero. Try to generate a meaningful error message.  
      */  
   
     while (isspace(UCHAR(*p))) {        /* INTL: ISO space. */  
         p++;  
     }  
     if (*p == '+' || *p == '-') {  
         p++;  
     }  
     if (*p == '0') {  
         while (isdigit(UCHAR(*p))) {    /* INTL: digit. */  
             p++;  
         }  
         while (isspace(UCHAR(*p))) {    /* INTL: ISO space. */  
             p++;  
         }  
         if (*p == '\0') {  
             /* Reached end of string */  
             if (interp != NULL) {  
                 Tcl_AppendResult(interp, " (looks like invalid octal number)",  
                         (char *) NULL);  
             }  
             return 1;  
         }  
     }  
     return 0;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_GetNameOfExecutable --  
  *  
  *      This procedure simply returns a pointer to the internal full  
  *      path name of the executable file as computed by  
  *      Tcl_FindExecutable.  This procedure call is the C API  
  *      equivalent to the "info nameofexecutable" command.  
  *  
  * Results:  
  *      A pointer to the internal string or NULL if the internal full  
  *      path name has not been computed or unknown.  
  *  
  * Side effects:  
  *      The object referenced by "objPtr" might be converted to an  
  *      integer object.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 CONST char *  
 Tcl_GetNameOfExecutable()  
 {  
     return (tclExecutableName);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_GetCwd --  
  *  
  *      This function replaces the library version of getcwd().  
  *  
  * Results:  
  *      The result is a pointer to a string specifying the current  
  *      directory, or NULL if the current directory could not be  
  *      determined.  If NULL is returned, an error message is left in the  
  *      interp's result.  Storage for the result string is allocated in  
  *      bufferPtr; the caller must call Tcl_DStringFree() when the result  
  *      is no longer needed.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 char *  
 Tcl_GetCwd(interp, cwdPtr)  
     Tcl_Interp *interp;  
     Tcl_DString *cwdPtr;  
 {  
     return TclpGetCwd(interp, cwdPtr);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_Chdir --  
  *  
  *      This function replaces the library version of chdir().  
  *  
  * Results:  
  *      See chdir() documentation.  
  *  
  * Side effects:  
  *      See chdir() documentation.    
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_Chdir(dirName)  
     CONST char *dirName;  
 {  
     return TclpChdir(dirName);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_Access --  
  *  
  *      This function replaces the library version of access().  
  *  
  * Results:  
  *      See access() documentation.  
  *  
  * Side effects:  
  *      See access() documentation.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_Access(path, mode)  
     CONST char *path;           /* Path of file to access (UTF-8). */  
     int mode;                   /* Permission setting. */  
 {  
     return TclAccess(path, mode);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_Stat --  
  *  
  *      This function replaces the library version of stat().  
  *  
  * Results:  
  *      See stat() documentation.  
  *  
  * Side effects:  
  *      See stat() documentation.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_Stat(path, bufPtr)  
     CONST char *path;           /* Path of file to stat (in UTF-8). */  
     struct stat *bufPtr;        /* Filled with results of stat call. */  
 {  
     return TclStat(path, bufPtr);  
 }  
   
   
 /* $History: tclutil.c $  
  *  
  * *****************  Version 1  *****************  
  * User: Dtashley     Date: 1/02/01    Time: 12:50a  
  * Created in $/IjuScripter, IjuConsole/Source/Tcl Base  
  * Initial check-in.  
  */  
   
 /* End of TCLUTIL.C */  
1    /* $Header$ */
2    /*
3     * tclUtil.c --
4     *
5     *      This file contains utility procedures that are used by many Tcl
6     *      commands.
7     *
8     * Copyright (c) 1987-1993 The Regents of the University of California.
9     * Copyright (c) 1994-1998 Sun Microsystems, Inc.
10     *
11     * See the file "license.terms" for information on usage and redistribution
12     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13     *
14     *  RCS: @(#) $Id: tclutil.c,v 1.1.1.1 2001/06/13 04:47:21 dtashley Exp $
15     */
16    
17    #include "tclInt.h"
18    #include "tclPort.h"
19    
20    /*
21     * The following variable holds the full path name of the binary
22     * from which this application was executed, or NULL if it isn't
23     * know.  The value of the variable is set by the procedure
24     * Tcl_FindExecutable.  The storage space is dynamically allocated.
25     */
26    
27    char *tclExecutableName = NULL;
28    char *tclNativeExecutableName = NULL;
29    
30    /*
31     * The following values are used in the flags returned by Tcl_ScanElement
32     * and used by Tcl_ConvertElement.  The value TCL_DONT_USE_BRACES is also
33     * defined in tcl.h;  make sure its value doesn't overlap with any of the
34     * values below.
35     *
36     * TCL_DONT_USE_BRACES -        1 means the string mustn't be enclosed in
37     *                              braces (e.g. it contains unmatched braces,
38     *                              or ends in a backslash character, or user
39     *                              just doesn't want braces);  handle all
40     *                              special characters by adding backslashes.
41     * USE_BRACES -                 1 means the string contains a special
42     *                              character that can be handled simply by
43     *                              enclosing the entire argument in braces.
44     * BRACES_UNMATCHED -           1 means that braces aren't properly matched
45     *                              in the argument.
46     */
47    
48    #define USE_BRACES              2
49    #define BRACES_UNMATCHED        4
50    
51    /*
52     * The following values determine the precision used when converting
53     * floating-point values to strings.  This information is linked to all
54     * of the tcl_precision variables in all interpreters via the procedure
55     * TclPrecTraceProc.
56     */
57    
58    static char precisionString[10] = "12";
59                                    /* The string value of all the tcl_precision
60                                     * variables. */
61    static char precisionFormat[10] = "%.12g";
62                                    /* The format string actually used in calls
63                                     * to sprintf. */
64    TCL_DECLARE_MUTEX(precisionMutex)
65    
66    
67    /*
68     *----------------------------------------------------------------------
69     *
70     * TclFindElement --
71     *
72     *      Given a pointer into a Tcl list, locate the first (or next)
73     *      element in the list.
74     *
75     * Results:
76     *      The return value is normally TCL_OK, which means that the
77     *      element was successfully located.  If TCL_ERROR is returned
78     *      it means that list didn't have proper list structure;
79     *      the interp's result contains a more detailed error message.
80     *
81     *      If TCL_OK is returned, then *elementPtr will be set to point to the
82     *      first element of list, and *nextPtr will be set to point to the
83     *      character just after any white space following the last character
84     *      that's part of the element. If this is the last argument in the
85     *      list, then *nextPtr will point just after the last character in the
86     *      list (i.e., at the character at list+listLength). If sizePtr is
87     *      non-NULL, *sizePtr is filled in with the number of characters in the
88     *      element.  If the element is in braces, then *elementPtr will point
89     *      to the character after the opening brace and *sizePtr will not
90     *      include either of the braces. If there isn't an element in the list,
91     *      *sizePtr will be zero, and both *elementPtr and *termPtr will point
92     *      just after the last character in the list. Note: this procedure does
93     *      NOT collapse backslash sequences.
94     *
95     * Side effects:
96     *      None.
97     *
98     *----------------------------------------------------------------------
99     */
100    
101    int
102    TclFindElement(interp, list, listLength, elementPtr, nextPtr, sizePtr,
103                   bracePtr)
104        Tcl_Interp *interp;         /* Interpreter to use for error reporting.
105                                     * If NULL, then no error message is left
106                                     * after errors. */
107        CONST char *list;           /* Points to the first byte of a string
108                                     * containing a Tcl list with zero or more
109                                     * elements (possibly in braces). */
110        int listLength;             /* Number of bytes in the list's string. */
111        CONST char **elementPtr;    /* Where to put address of first significant
112                                     * character in first element of list. */
113        CONST char **nextPtr;       /* Fill in with location of character just
114                                     * after all white space following end of
115                                     * argument (next arg or end of list). */
116        int *sizePtr;               /* If non-zero, fill in with size of
117                                     * element. */
118        int *bracePtr;              /* If non-zero, fill in with non-zero/zero
119                                     * to indicate that arg was/wasn't
120                                     * in braces. */
121    {
122        CONST char *p = list;
123        CONST char *elemStart;      /* Points to first byte of first element. */
124        CONST char *limit;          /* Points just after list's last byte. */
125        int openBraces = 0;         /* Brace nesting level during parse. */
126        int inQuotes = 0;
127        int size = 0;               /* lint. */
128        int numChars;
129        CONST char *p2;
130        
131        /*
132         * Skim off leading white space and check for an opening brace or
133         * quote. We treat embedded NULLs in the list as bytes belonging to
134         * a list element.
135         */
136    
137        limit = (list + listLength);
138        while ((p < limit) && (isspace(UCHAR(*p)))) { /* INTL: ISO space. */
139            p++;
140        }
141        if (p == limit) {           /* no element found */
142            elemStart = limit;
143            goto done;
144        }
145    
146        if (*p == '{') {
147            openBraces = 1;
148            p++;
149        } else if (*p == '"') {
150            inQuotes = 1;
151            p++;
152        }
153        elemStart = p;
154        if (bracePtr != 0) {
155            *bracePtr = openBraces;
156        }
157    
158        /*
159         * Find element's end (a space, close brace, or the end of the string).
160         */
161    
162        while (p < limit) {
163            switch (*p) {
164    
165                /*
166                 * Open brace: don't treat specially unless the element is in
167                 * braces. In this case, keep a nesting count.
168                 */
169    
170                case '{':
171                    if (openBraces != 0) {
172                        openBraces++;
173                    }
174                    break;
175    
176                /*
177                 * Close brace: if element is in braces, keep nesting count and
178                 * quit when the last close brace is seen.
179                 */
180    
181                case '}':
182                    if (openBraces > 1) {
183                        openBraces--;
184                    } else if (openBraces == 1) {
185                        size = (p - elemStart);
186                        p++;
187                        if ((p >= limit)
188                                || isspace(UCHAR(*p))) { /* INTL: ISO space. */
189                            goto done;
190                        }
191    
192                        /*
193                         * Garbage after the closing brace; return an error.
194                         */
195                        
196                        if (interp != NULL) {
197                            char buf[100];
198                            
199                            p2 = p;
200                            while ((p2 < limit)
201                                    && (!isspace(UCHAR(*p2))) /* INTL: ISO space. */
202                                    && (p2 < p+20)) {
203                                p2++;
204                            }
205                            sprintf(buf,
206                                    "list element in braces followed by \"%.*s\" instead of space",
207                                    (int) (p2-p), p);
208                            Tcl_SetResult(interp, buf, TCL_VOLATILE);
209                        }
210                        return TCL_ERROR;
211                    }
212                    break;
213    
214                /*
215                 * Backslash:  skip over everything up to the end of the
216                 * backslash sequence.
217                 */
218    
219                case '\\': {
220                    Tcl_UtfBackslash(p, &numChars, NULL);
221                    p += (numChars - 1);
222                    break;
223                }
224    
225                /*
226                 * Space: ignore if element is in braces or quotes; otherwise
227                 * terminate element.
228                 */
229    
230                case ' ':
231                case '\f':
232                case '\n':
233                case '\r':
234                case '\t':
235                case '\v':
236                    if ((openBraces == 0) && !inQuotes) {
237                        size = (p - elemStart);
238                        goto done;
239                    }
240                    break;
241    
242                /*
243                 * Double-quote: if element is in quotes then terminate it.
244                 */
245    
246                case '"':
247                    if (inQuotes) {
248                        size = (p - elemStart);
249                        p++;
250                        if ((p >= limit)
251                                || isspace(UCHAR(*p))) { /* INTL: ISO space */
252                            goto done;
253                        }
254    
255                        /*
256                         * Garbage after the closing quote; return an error.
257                         */
258                        
259                        if (interp != NULL) {
260                            char buf[100];
261                            
262                            p2 = p;
263                            while ((p2 < limit)
264                                    && (!isspace(UCHAR(*p2))) /* INTL: ISO space */
265                                     && (p2 < p+20)) {
266                                p2++;
267                            }
268                            sprintf(buf,
269                                    "list element in quotes followed by \"%.*s\" %s",
270                                    (int) (p2-p), p, "instead of space");
271                            Tcl_SetResult(interp, buf, TCL_VOLATILE);
272                        }
273                        return TCL_ERROR;
274                    }
275                    break;
276            }
277            p++;
278        }
279    
280    
281        /*
282         * End of list: terminate element.
283         */
284    
285        if (p == limit) {
286            if (openBraces != 0) {
287                if (interp != NULL) {
288                    Tcl_SetResult(interp, "unmatched open brace in list",
289                            TCL_STATIC);
290                }
291                return TCL_ERROR;
292            } else if (inQuotes) {
293                if (interp != NULL) {
294                    Tcl_SetResult(interp, "unmatched open quote in list",
295                            TCL_STATIC);
296                }
297                return TCL_ERROR;
298            }
299            size = (p - elemStart);
300        }
301    
302        done:
303        while ((p < limit) && (isspace(UCHAR(*p)))) { /* INTL: ISO space. */
304            p++;
305        }
306        *elementPtr = elemStart;
307        *nextPtr = p;
308        if (sizePtr != 0) {
309            *sizePtr = size;
310        }
311        return TCL_OK;
312    }
313    
314    /*
315     *----------------------------------------------------------------------
316     *
317     * TclCopyAndCollapse --
318     *
319     *      Copy a string and eliminate any backslashes that aren't in braces.
320     *
321     * Results:
322     *      There is no return value. Count characters get copied from src to
323     *      dst. Along the way, if backslash sequences are found outside braces,
324     *      the backslashes are eliminated in the copy. After scanning count
325     *      chars from source, a null character is placed at the end of dst.
326     *      Returns the number of characters that got copied.
327     *
328     * Side effects:
329     *      None.
330     *
331     *----------------------------------------------------------------------
332     */
333    
334    int
335    TclCopyAndCollapse(count, src, dst)
336        int count;                  /* Number of characters to copy from src. */
337        CONST char *src;            /* Copy from here... */
338        char *dst;                  /* ... to here. */
339    {
340        register char c;
341        int numRead;
342        int newCount = 0;
343        int backslashCount;
344    
345        for (c = *src;  count > 0;  src++, c = *src, count--) {
346            if (c == '\\') {
347                backslashCount = Tcl_UtfBackslash(src, &numRead, dst);
348                dst += backslashCount;
349                newCount += backslashCount;
350                src += numRead-1;
351                count -= numRead-1;
352            } else {
353                *dst = c;
354                dst++;
355                newCount++;
356            }
357        }
358        *dst = 0;
359        return newCount;
360    }
361    
362    /*
363     *----------------------------------------------------------------------
364     *
365     * Tcl_SplitList --
366     *
367     *      Splits a list up into its constituent fields.
368     *
369     * Results
370     *      The return value is normally TCL_OK, which means that
371     *      the list was successfully split up.  If TCL_ERROR is
372     *      returned, it means that "list" didn't have proper list
373     *      structure;  the interp's result will contain a more detailed
374     *      error message.
375     *
376     *      *argvPtr will be filled in with the address of an array
377     *      whose elements point to the elements of list, in order.
378     *      *argcPtr will get filled in with the number of valid elements
379     *      in the array.  A single block of memory is dynamically allocated
380     *      to hold both the argv array and a copy of the list (with
381     *      backslashes and braces removed in the standard way).
382     *      The caller must eventually free this memory by calling free()
383     *      on *argvPtr.  Note:  *argvPtr and *argcPtr are only modified
384     *      if the procedure returns normally.
385     *
386     * Side effects:
387     *      Memory is allocated.
388     *
389     *----------------------------------------------------------------------
390     */
391    
392    int
393    Tcl_SplitList(interp, list, argcPtr, argvPtr)
394        Tcl_Interp *interp;         /* Interpreter to use for error reporting.
395                                     * If NULL, no error message is left. */
396        CONST char *list;           /* Pointer to string with list structure. */
397        int *argcPtr;               /* Pointer to location to fill in with
398                                     * the number of elements in the list. */
399        char ***argvPtr;            /* Pointer to place to store pointer to
400                                     * array of pointers to list elements. */
401    {
402        char **argv;
403        CONST char *l;
404        char *p;
405        int length, size, i, result, elSize, brace;
406        CONST char *element;
407    
408        /*
409         * Figure out how much space to allocate.  There must be enough
410         * space for both the array of pointers and also for a copy of
411         * the list.  To estimate the number of pointers needed, count
412         * the number of space characters in the list.
413         */
414    
415        for (size = 1, l = list; *l != 0; l++) {
416            if (isspace(UCHAR(*l))) { /* INTL: ISO space. */
417                size++;
418            }
419        }
420        size++;                     /* Leave space for final NULL pointer. */
421        argv = (char **) ckalloc((unsigned)
422                ((size * sizeof(char *)) + (l - list) + 1));
423        length = strlen(list);
424        for (i = 0, p = ((char *) argv) + size*sizeof(char *);
425                *list != 0;  i++) {
426            CONST char *prevList = list;
427            
428            result = TclFindElement(interp, list, length, &element,
429                                    &list, &elSize, &brace);
430            length -= (list - prevList);
431            if (result != TCL_OK) {
432                ckfree((char *) argv);
433                return result;
434            }
435            if (*element == 0) {
436                break;
437            }
438            if (i >= size) {
439                ckfree((char *) argv);
440                if (interp != NULL) {
441                    Tcl_SetResult(interp, "internal error in Tcl_SplitList",
442                            TCL_STATIC);
443                }
444                return TCL_ERROR;
445            }
446            argv[i] = p;
447            if (brace) {
448                memcpy((VOID *) p, (VOID *) element, (size_t) elSize);
449                p += elSize;
450                *p = 0;
451                p++;
452            } else {
453                TclCopyAndCollapse(elSize, element, p);
454                p += elSize+1;
455            }
456        }
457    
458        argv[i] = NULL;
459        *argvPtr = argv;
460        *argcPtr = i;
461        return TCL_OK;
462    }
463    
464    /*
465     *----------------------------------------------------------------------
466     *
467     * Tcl_ScanElement --
468     *
469     *      This procedure is a companion procedure to Tcl_ConvertElement.
470     *      It scans a string to see what needs to be done to it (e.g. add
471     *      backslashes or enclosing braces) to make the string into a
472     *      valid Tcl list element.
473     *
474     * Results:
475     *      The return value is an overestimate of the number of characters
476     *      that will be needed by Tcl_ConvertElement to produce a valid
477     *      list element from string.  The word at *flagPtr is filled in
478     *      with a value needed by Tcl_ConvertElement when doing the actual
479     *      conversion.
480     *
481     * Side effects:
482     *      None.
483     *
484     *----------------------------------------------------------------------
485     */
486    
487    int
488    Tcl_ScanElement(string, flagPtr)
489        register CONST char *string; /* String to convert to list element. */
490        register int *flagPtr;       /* Where to store information to guide
491                                      * Tcl_ConvertCountedElement. */
492    {
493        return Tcl_ScanCountedElement(string, -1, flagPtr);
494    }
495    
496    /*
497     *----------------------------------------------------------------------
498     *
499     * Tcl_ScanCountedElement --
500     *
501     *      This procedure is a companion procedure to
502     *      Tcl_ConvertCountedElement.  It scans a string to see what
503     *      needs to be done to it (e.g. add backslashes or enclosing
504     *      braces) to make the string into a valid Tcl list element.
505     *      If length is -1, then the string is scanned up to the first
506     *      null byte.
507     *
508     * Results:
509     *      The return value is an overestimate of the number of characters
510     *      that will be needed by Tcl_ConvertCountedElement to produce a
511     *      valid list element from string.  The word at *flagPtr is
512     *      filled in with a value needed by Tcl_ConvertCountedElement
513     *      when doing the actual conversion.
514     *
515     * Side effects:
516     *      None.
517     *
518     *----------------------------------------------------------------------
519     */
520    
521    int
522    Tcl_ScanCountedElement(string, length, flagPtr)
523        CONST char *string;         /* String to convert to Tcl list element. */
524        int length;                 /* Number of bytes in string, or -1. */
525        int *flagPtr;               /* Where to store information to guide
526                                     * Tcl_ConvertElement. */
527    {
528        int flags, nestingLevel;
529        register CONST char *p, *lastChar;
530    
531        /*
532         * This procedure and Tcl_ConvertElement together do two things:
533         *
534         * 1. They produce a proper list, one that will yield back the
535         * argument strings when evaluated or when disassembled with
536         * Tcl_SplitList.  This is the most important thing.
537         *
538         * 2. They try to produce legible output, which means minimizing the
539         * use of backslashes (using braces instead).  However, there are
540         * some situations where backslashes must be used (e.g. an element
541         * like "{abc": the leading brace will have to be backslashed.
542         * For each element, one of three things must be done:
543         *
544         * (a) Use the element as-is (it doesn't contain any special
545         * characters).  This is the most desirable option.
546         *
547         * (b) Enclose the element in braces, but leave the contents alone.
548         * This happens if the element contains embedded space, or if it
549         * contains characters with special interpretation ($, [, ;, or \),
550         * or if it starts with a brace or double-quote, or if there are
551         * no characters in the element.
552         *
553         * (c) Don't enclose the element in braces, but add backslashes to
554         * prevent special interpretation of special characters.  This is a
555         * last resort used when the argument would normally fall under case
556         * (b) but contains unmatched braces.  It also occurs if the last
557         * character of the argument is a backslash or if the element contains
558         * a backslash followed by newline.
559         *
560         * The procedure figures out how many bytes will be needed to store
561         * the result (actually, it overestimates). It also collects information
562         * about the element in the form of a flags word.
563         *
564         * Note: list elements produced by this procedure and
565         * Tcl_ConvertCountedElement must have the property that they can be
566         * enclosing in curly braces to make sub-lists.  This means, for
567         * example, that we must not leave unmatched curly braces in the
568         * resulting list element.  This property is necessary in order for
569         * procedures like Tcl_DStringStartSublist to work.
570         */
571    
572        nestingLevel = 0;
573        flags = 0;
574        if (string == NULL) {
575            string = "";
576        }
577        if (length == -1) {
578            length = strlen(string);
579        }
580        lastChar = string + length;
581        p = string;
582        if ((p == lastChar) || (*p == '{') || (*p == '"')) {
583            flags |= USE_BRACES;
584        }
585        for ( ; p < lastChar; p++) {
586            switch (*p) {
587                case '{':
588                    nestingLevel++;
589                    break;
590                case '}':
591                    nestingLevel--;
592                    if (nestingLevel < 0) {
593                        flags |= TCL_DONT_USE_BRACES|BRACES_UNMATCHED;
594                    }
595                    break;
596                case '[':
597                case '$':
598                case ';':
599                case ' ':
600                case '\f':
601                case '\n':
602                case '\r':
603                case '\t':
604                case '\v':
605                    flags |= USE_BRACES;
606                    break;
607                case '\\':
608                    if ((p+1 == lastChar) || (p[1] == '\n')) {
609                        flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED;
610                    } else {
611                        int size;
612    
613                        Tcl_UtfBackslash(p, &size, NULL);
614                        p += size-1;
615                        flags |= USE_BRACES;
616                    }
617                    break;
618            }
619        }
620        if (nestingLevel != 0) {
621            flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED;
622        }
623        *flagPtr = flags;
624    
625        /*
626         * Allow enough space to backslash every character plus leave
627         * two spaces for braces.
628         */
629    
630        return 2*(p-string) + 2;
631    }
632    
633    /*
634     *----------------------------------------------------------------------
635     *
636     * Tcl_ConvertElement --
637     *
638     *      This is a companion procedure to Tcl_ScanElement.  Given
639     *      the information produced by Tcl_ScanElement, this procedure
640     *      converts a string to a list element equal to that string.
641     *
642     * Results:
643     *      Information is copied to *dst in the form of a list element
644     *      identical to src (i.e. if Tcl_SplitList is applied to dst it
645     *      will produce a string identical to src).  The return value is
646     *      a count of the number of characters copied (not including the
647     *      terminating NULL character).
648     *
649     * Side effects:
650     *      None.
651     *
652     *----------------------------------------------------------------------
653     */
654    
655    int
656    Tcl_ConvertElement(src, dst, flags)
657        register CONST char *src;   /* Source information for list element. */
658        register char *dst;         /* Place to put list-ified element. */
659        register int flags;         /* Flags produced by Tcl_ScanElement. */
660    {
661        return Tcl_ConvertCountedElement(src, -1, dst, flags);
662    }
663    
664    /*
665     *----------------------------------------------------------------------
666     *
667     * Tcl_ConvertCountedElement --
668     *
669     *      This is a companion procedure to Tcl_ScanCountedElement.  Given
670     *      the information produced by Tcl_ScanCountedElement, this
671     *      procedure converts a string to a list element equal to that
672     *      string.
673     *
674     * Results:
675     *      Information is copied to *dst in the form of a list element
676     *      identical to src (i.e. if Tcl_SplitList is applied to dst it
677     *      will produce a string identical to src).  The return value is
678     *      a count of the number of characters copied (not including the
679     *      terminating NULL character).
680     *
681     * Side effects:
682     *      None.
683     *
684     *----------------------------------------------------------------------
685     */
686    
687    int
688    Tcl_ConvertCountedElement(src, length, dst, flags)
689        register CONST char *src;   /* Source information for list element. */
690        int length;                 /* Number of bytes in src, or -1. */
691        char *dst;                  /* Place to put list-ified element. */
692        int flags;                  /* Flags produced by Tcl_ScanElement. */
693    {
694        register char *p = dst;
695        register CONST char *lastChar;
696    
697        /*
698         * See the comment block at the beginning of the Tcl_ScanElement
699         * code for details of how this works.
700         */
701    
702        if (src && length == -1) {
703            length = strlen(src);
704        }
705        if ((src == NULL) || (length == 0)) {
706            p[0] = '{';
707            p[1] = '}';
708            p[2] = 0;
709            return 2;
710        }
711        lastChar = src + length;
712        if ((flags & USE_BRACES) && !(flags & TCL_DONT_USE_BRACES)) {
713            *p = '{';
714            p++;
715            for ( ; src != lastChar; src++, p++) {
716                *p = *src;
717            }
718            *p = '}';
719            p++;
720        } else {
721            if (*src == '{') {
722                /*
723                 * Can't have a leading brace unless the whole element is
724                 * enclosed in braces.  Add a backslash before the brace.
725                 * Furthermore, this may destroy the balance between open
726                 * and close braces, so set BRACES_UNMATCHED.
727                 */
728    
729                p[0] = '\\';
730                p[1] = '{';
731                p += 2;
732                src++;
733                flags |= BRACES_UNMATCHED;
734            }
735            for (; src != lastChar; src++) {
736                switch (*src) {
737                    case ']':
738                    case '[':
739                    case '$':
740                    case ';':
741                    case ' ':
742                    case '\\':
743                    case '"':
744                        *p = '\\';
745                        p++;
746                        break;
747                    case '{':
748                    case '}':
749                        /*
750                         * It may not seem necessary to backslash braces, but
751                         * it is.  The reason for this is that the resulting
752                         * list element may actually be an element of a sub-list
753                         * enclosed in braces (e.g. if Tcl_DStringStartSublist
754                         * has been invoked), so there may be a brace mismatch
755                         * if the braces aren't backslashed.
756                         */
757    
758                        if (flags & BRACES_UNMATCHED) {
759                            *p = '\\';
760                            p++;
761                        }
762                        break;
763                    case '\f':
764                        *p = '\\';
765                        p++;
766                        *p = 'f';
767                        p++;
768                        continue;
769                    case '\n':
770                        *p = '\\';
771                        p++;
772                        *p = 'n';
773                        p++;
774                        continue;
775                    case '\r':
776                        *p = '\\';
777                        p++;
778                        *p = 'r';
779                        p++;
780                        continue;
781                    case '\t':
782                        *p = '\\';
783                        p++;
784                        *p = 't';
785                        p++;
786                        continue;
787                    case '\v':
788                        *p = '\\';
789                        p++;
790                        *p = 'v';
791                        p++;
792                        continue;
793                }
794                *p = *src;
795                p++;
796            }
797        }
798        *p = '\0';
799        return p-dst;
800    }
801    
802    /*
803     *----------------------------------------------------------------------
804     *
805     * Tcl_Merge --
806     *
807     *      Given a collection of strings, merge them together into a
808     *      single string that has proper Tcl list structured (i.e.
809     *      Tcl_SplitList may be used to retrieve strings equal to the
810     *      original elements, and Tcl_Eval will parse the string back
811     *      into its original elements).
812     *
813     * Results:
814     *      The return value is the address of a dynamically-allocated
815     *      string containing the merged list.
816     *
817     * Side effects:
818     *      None.
819     *
820     *----------------------------------------------------------------------
821     */
822    
823    char *
824    Tcl_Merge(argc, argv)
825        int argc;                   /* How many strings to merge. */
826        char **argv;                /* Array of string values. */
827    {
828    #   define LOCAL_SIZE 20
829        int localFlags[LOCAL_SIZE], *flagPtr;
830        int numChars;
831        char *result;
832        char *dst;
833        int i;
834    
835        /*
836         * Pass 1: estimate space, gather flags.
837         */
838    
839        if (argc <= LOCAL_SIZE) {
840            flagPtr = localFlags;
841        } else {
842            flagPtr = (int *) ckalloc((unsigned) argc*sizeof(int));
843        }
844        numChars = 1;
845        for (i = 0; i < argc; i++) {
846            numChars += Tcl_ScanElement(argv[i], &flagPtr[i]) + 1;
847        }
848    
849        /*
850         * Pass two: copy into the result area.
851         */
852    
853        result = (char *) ckalloc((unsigned) numChars);
854        dst = result;
855        for (i = 0; i < argc; i++) {
856            numChars = Tcl_ConvertElement(argv[i], dst, flagPtr[i]);
857            dst += numChars;
858            *dst = ' ';
859            dst++;
860        }
861        if (dst == result) {
862            *dst = 0;
863        } else {
864            dst[-1] = 0;
865        }
866    
867        if (flagPtr != localFlags) {
868            ckfree((char *) flagPtr);
869        }
870        return result;
871    }
872    
873    /*
874     *----------------------------------------------------------------------
875     *
876     * Tcl_Backslash --
877     *
878     *      Figure out how to handle a backslash sequence.
879     *
880     * Results:
881     *      The return value is the character that should be substituted
882     *      in place of the backslash sequence that starts at src.  If
883     *      readPtr isn't NULL then it is filled in with a count of the
884     *      number of characters in the backslash sequence.
885     *
886     * Side effects:
887     *      None.
888     *
889     *----------------------------------------------------------------------
890     */
891    
892    char
893    Tcl_Backslash(src, readPtr)
894        CONST char *src;            /* Points to the backslash character of
895                                     * a backslash sequence. */
896        int *readPtr;               /* Fill in with number of characters read
897                                     * from src, unless NULL. */
898    {
899        char buf[TCL_UTF_MAX];
900        Tcl_UniChar ch;
901    
902        Tcl_UtfBackslash(src, readPtr, buf);
903        Tcl_UtfToUniChar(buf, &ch);
904        return (char) ch;
905    }
906    
907    /*
908     *----------------------------------------------------------------------
909     *
910     * Tcl_Concat --
911     *
912     *      Concatenate a set of strings into a single large string.
913     *
914     * Results:
915     *      The return value is dynamically-allocated string containing
916     *      a concatenation of all the strings in argv, with spaces between
917     *      the original argv elements.
918     *
919     * Side effects:
920     *      Memory is allocated for the result;  the caller is responsible
921     *      for freeing the memory.
922     *
923     *----------------------------------------------------------------------
924     */
925    
926    char *
927    Tcl_Concat(argc, argv)
928        int argc;                   /* Number of strings to concatenate. */
929        char **argv;                /* Array of strings to concatenate. */
930    {
931        int totalSize, i;
932        char *p;
933        char *result;
934    
935        for (totalSize = 1, i = 0; i < argc; i++) {
936            totalSize += strlen(argv[i]) + 1;
937        }
938        result = (char *) ckalloc((unsigned) totalSize);
939        if (argc == 0) {
940            *result = '\0';
941            return result;
942        }
943        for (p = result, i = 0; i < argc; i++) {
944            char *element;
945            int length;
946    
947            /*
948             * Clip white space off the front and back of the string
949             * to generate a neater result, and ignore any empty
950             * elements.
951             */
952    
953            element = argv[i];
954            while (isspace(UCHAR(*element))) { /* INTL: ISO space. */
955                element++;
956            }
957            for (length = strlen(element);
958                    (length > 0)
959                    && (isspace(UCHAR(element[length-1]))) /* INTL: ISO space. */
960                    && ((length < 2) || (element[length-2] != '\\'));
961                    length--) {
962                /* Null loop body. */
963            }
964            if (length == 0) {
965                continue;
966            }
967            memcpy((VOID *) p, (VOID *) element, (size_t) length);
968            p += length;
969            *p = ' ';
970            p++;
971        }
972        if (p != result) {
973            p[-1] = 0;
974        } else {
975            *p = 0;
976        }
977        return result;
978    }
979    
980    /*
981     *----------------------------------------------------------------------
982     *
983     * Tcl_ConcatObj --
984     *
985     *      Concatenate the strings from a set of objects into a single string
986     *      object with spaces between the original strings.
987     *
988     * Results:
989     *      The return value is a new string object containing a concatenation
990     *      of the strings in objv. Its ref count is zero.
991     *
992     * Side effects:
993     *      A new object is created.
994     *
995     *----------------------------------------------------------------------
996     */
997    
998    Tcl_Obj *
999    Tcl_ConcatObj(objc, objv)
1000        int objc;                   /* Number of objects to concatenate. */
1001        Tcl_Obj *CONST objv[];      /* Array of objects to concatenate. */
1002    {
1003        int allocSize, finalSize, length, elemLength, i;
1004        char *p;
1005        char *element;
1006        char *concatStr;
1007        Tcl_Obj *objPtr;
1008    
1009        /*
1010         * Check first to see if all the items are of list type.  If so,
1011         * we will concat them together as lists, and return a list object.
1012         * This is only valid when the lists have no current string
1013         * representation, since we don't know what the original type was.
1014         * An original string rep may have lost some whitespace info when
1015         * converted which could be important.
1016         */
1017        for (i = 0;  i < objc;  i++) {
1018            objPtr = objv[i];
1019            if ((objPtr->typePtr != &tclListType) || (objPtr->bytes != NULL)) {
1020                break;
1021            }
1022        }
1023        if (i == objc) {
1024            Tcl_Obj **listv;
1025            int listc;
1026    
1027            objPtr = Tcl_NewListObj(0, NULL);
1028            for (i = 0;  i < objc;  i++) {
1029                /*
1030                 * Tcl_ListObjAppendList could be used here, but this saves
1031                 * us a bit of type checking (since we've already done it)
1032                 * Use of INT_MAX tells us to always put the new stuff on
1033                 * the end.  It will be set right in Tcl_ListObjReplace.
1034                 */
1035                Tcl_ListObjGetElements(NULL, objv[i], &listc, &listv);
1036                Tcl_ListObjReplace(NULL, objPtr, INT_MAX, 0, listc, listv);
1037            }
1038            return objPtr;
1039        }
1040    
1041        allocSize = 0;
1042        for (i = 0;  i < objc;  i++) {
1043            objPtr = objv[i];
1044            element = Tcl_GetStringFromObj(objPtr, &length);
1045            if ((element != NULL) && (length > 0)) {
1046                allocSize += (length + 1);
1047            }
1048        }
1049        if (allocSize == 0) {
1050            allocSize = 1;          /* enough for the NULL byte at end */
1051        }
1052    
1053        /*
1054         * Allocate storage for the concatenated result. Note that allocSize
1055         * is one more than the total number of characters, and so includes
1056         * room for the terminating NULL byte.
1057         */
1058        
1059        concatStr = (char *) ckalloc((unsigned) allocSize);
1060    
1061        /*
1062         * Now concatenate the elements. Clip white space off the front and back
1063         * to generate a neater result, and ignore any empty elements. Also put
1064         * a null byte at the end.
1065         */
1066    
1067        finalSize = 0;
1068        if (objc == 0) {
1069            *concatStr = '\0';
1070        } else {
1071            p = concatStr;
1072            for (i = 0;  i < objc;  i++) {
1073                objPtr = objv[i];
1074                element = Tcl_GetStringFromObj(objPtr, &elemLength);
1075                while ((elemLength > 0)
1076                        && (isspace(UCHAR(*element)))) { /* INTL: ISO space. */
1077                     element++;
1078                     elemLength--;
1079                }
1080    
1081                /*
1082                 * Trim trailing white space.  But, be careful not to trim
1083                 * a space character if it is preceded by a backslash: in
1084                 * this case it could be significant.
1085                 */
1086    
1087                while ((elemLength > 0)
1088                        && isspace(UCHAR(element[elemLength-1])) /* INTL: ISO space. */
1089                        && ((elemLength < 2) || (element[elemLength-2] != '\\'))) {
1090                    elemLength--;
1091                }
1092                if (elemLength == 0) {
1093                     continue;      /* nothing left of this element */
1094                }
1095                memcpy((VOID *) p, (VOID *) element, (size_t) elemLength);
1096                p += elemLength;
1097                *p = ' ';
1098                p++;
1099                finalSize += (elemLength + 1);
1100            }
1101            if (p != concatStr) {
1102                p[-1] = 0;
1103                finalSize -= 1;     /* we overwrote the final ' ' */
1104            } else {
1105                *p = 0;
1106            }
1107        }
1108        
1109        TclNewObj(objPtr);
1110        objPtr->bytes  = concatStr;
1111        objPtr->length = finalSize;
1112        return objPtr;
1113    }
1114    
1115    /*
1116     *----------------------------------------------------------------------
1117     *
1118     * Tcl_StringMatch --
1119     *
1120     *      See if a particular string matches a particular pattern.
1121     *
1122     * Results:
1123     *      The return value is 1 if string matches pattern, and
1124     *      0 otherwise.  The matching operation permits the following
1125     *      special characters in the pattern: *?\[] (see the manual
1126     *      entry for details on what these mean).
1127     *
1128     * Side effects:
1129     *      None.
1130     *
1131     *----------------------------------------------------------------------
1132     */
1133    
1134    int
1135    Tcl_StringMatch(string, pattern)
1136        CONST char *string;         /* String. */
1137        CONST char *pattern;        /* Pattern, which may contain special
1138                                     * characters. */
1139    {
1140        int p, s;
1141        CONST char *pstart = pattern;
1142        
1143        while (1) {
1144            p = *pattern;
1145            s = *string;
1146            
1147            /*
1148             * See if we're at the end of both the pattern and the string.  If
1149             * so, we succeeded.  If we're at the end of the pattern but not at
1150             * the end of the string, we failed.
1151             */
1152            
1153            if (p == '\0') {
1154                if (s == '\0') {
1155                    return 1;
1156                } else {
1157                    return 0;
1158                }
1159            }
1160            if ((s == '\0') && (p != '*')) {
1161                return 0;
1162            }
1163    
1164            /* Check for a "*" as the next pattern character.  It matches
1165             * any substring.  We handle this by calling ourselves
1166             * recursively for each postfix of string, until either we
1167             * match or we reach the end of the string.
1168             */
1169            
1170            if (p == '*') {
1171                pattern++;
1172                if (*pattern == '\0') {
1173                    return 1;
1174                }
1175                while (1) {
1176                    if (Tcl_StringMatch(string, pattern)) {
1177                        return 1;
1178                    }
1179                    if (*string == '\0') {
1180                        return 0;
1181                    }
1182                    string++;
1183                }
1184            }
1185    
1186            /* Check for a "?" as the next pattern character.  It matches
1187             * any single character.
1188             */
1189    
1190            if (p == '?') {
1191                Tcl_UniChar ch;
1192                
1193                pattern++;
1194                string += Tcl_UtfToUniChar(string, &ch);
1195                continue;
1196            }
1197    
1198            /* Check for a "[" as the next pattern character.  It is followed
1199             * by a list of characters that are acceptable, or by a range
1200             * (two characters separated by "-").
1201             */
1202            
1203            if (p == '[') {
1204                Tcl_UniChar ch, startChar, endChar;
1205    
1206                pattern++;
1207                string += Tcl_UtfToUniChar(string, &ch);
1208    
1209                while (1) {
1210                    if ((*pattern == ']') || (*pattern == '\0')) {
1211                        return 0;
1212                    }
1213                    pattern += Tcl_UtfToUniChar(pattern, &startChar);
1214                    if (*pattern == '-') {
1215                        pattern++;
1216                        if (*pattern == '\0') {
1217                            return 0;
1218                        }
1219                        pattern += Tcl_UtfToUniChar(pattern, &endChar);
1220                        if (((startChar <= ch) && (ch <= endChar))
1221                                || ((endChar <= ch) && (ch <= startChar))) {
1222                            /*
1223                             * Matches ranges of form [a-z] or [z-a].
1224                             */
1225    
1226                            break;
1227                        }
1228                    } else if (startChar == ch) {
1229                        break;
1230                    }
1231                }
1232                while (*pattern != ']') {
1233                    if (*pattern == '\0') {
1234                        pattern = Tcl_UtfPrev(pattern, pstart);
1235                        break;
1236                    }
1237                    pattern++;
1238                }
1239                pattern++;
1240                continue;
1241            }
1242        
1243            /* If the next pattern character is '\', just strip off the '\'
1244             * so we do exact matching on the character that follows.
1245             */
1246            
1247            if (p == '\\') {
1248                pattern++;
1249                p = *pattern;
1250                if (p == '\0') {
1251                    return 0;
1252                }
1253            }
1254    
1255            /* There's no special character.  Just make sure that the next
1256             * bytes of each string match.
1257             */
1258            
1259            if (s != p) {
1260                return 0;
1261            }
1262            pattern++;
1263            string++;
1264        }
1265    }
1266    
1267    /*
1268     *----------------------------------------------------------------------
1269     *
1270     * Tcl_StringCaseMatch --
1271     *
1272     *      See if a particular string matches a particular pattern.
1273     *      Allows case insensitivity.
1274     *
1275     * Results:
1276     *      The return value is 1 if string matches pattern, and
1277     *      0 otherwise.  The matching operation permits the following
1278     *      special characters in the pattern: *?\[] (see the manual
1279     *      entry for details on what these mean).
1280     *
1281     * Side effects:
1282     *      None.
1283     *
1284     *----------------------------------------------------------------------
1285     */
1286    
1287    int
1288    Tcl_StringCaseMatch(string, pattern, nocase)
1289        CONST char *string;         /* String. */
1290        CONST char *pattern;        /* Pattern, which may contain special
1291                                     * characters. */
1292        int nocase;                 /* 0 for case sensitive, 1 for insensitive */
1293    {
1294        int p, s;
1295        CONST char *pstart = pattern;
1296        Tcl_UniChar ch1, ch2;
1297        
1298        while (1) {
1299            p = *pattern;
1300            s = *string;
1301            
1302            /*
1303             * See if we're at the end of both the pattern and the string.  If
1304             * so, we succeeded.  If we're at the end of the pattern but not at
1305             * the end of the string, we failed.
1306             */
1307            
1308            if (p == '\0') {
1309                return (s == '\0');
1310            }
1311            if ((s == '\0') && (p != '*')) {
1312                return 0;
1313            }
1314    
1315            /* Check for a "*" as the next pattern character.  It matches
1316             * any substring.  We handle this by calling ourselves
1317             * recursively for each postfix of string, until either we
1318             * match or we reach the end of the string.
1319             */
1320            
1321            if (p == '*') {
1322                pattern++;
1323                if (*pattern == '\0') {
1324                    return 1;
1325                }
1326                while (1) {
1327                    if (Tcl_StringCaseMatch(string, pattern, nocase)) {
1328                        return 1;
1329                    }
1330                    if (*string == '\0') {
1331                        return 0;
1332                    }
1333                    string++;
1334                }
1335            }
1336    
1337            /* Check for a "?" as the next pattern character.  It matches
1338             * any single character.
1339             */
1340    
1341            if (p == '?') {
1342                pattern++;
1343                string += Tcl_UtfToUniChar(string, &ch1);
1344                continue;
1345            }
1346    
1347            /* Check for a "[" as the next pattern character.  It is followed
1348             * by a list of characters that are acceptable, or by a range
1349             * (two characters separated by "-").
1350             */
1351            
1352            if (p == '[') {
1353                Tcl_UniChar startChar, endChar;
1354    
1355                pattern++;
1356                string += Tcl_UtfToUniChar(string, &ch1);
1357                if (nocase) {
1358                    ch1 = Tcl_UniCharToLower(ch1);
1359                }
1360                while (1) {
1361                    if ((*pattern == ']') || (*pattern == '\0')) {
1362                        return 0;
1363                    }
1364                    pattern += Tcl_UtfToUniChar(pattern, &startChar);
1365                    if (nocase) {
1366                        startChar = Tcl_UniCharToLower(startChar);
1367                    }
1368                    if (*pattern == '-') {
1369                        pattern++;
1370                        if (*pattern == '\0') {
1371                            return 0;
1372                        }
1373                        pattern += Tcl_UtfToUniChar(pattern, &endChar);
1374                        if (nocase) {
1375                            endChar = Tcl_UniCharToLower(endChar);
1376                        }
1377                        if (((startChar <= ch1) && (ch1 <= endChar))
1378                                || ((endChar <= ch1) && (ch1 <= startChar))) {
1379                            /*
1380                             * Matches ranges of form [a-z] or [z-a].
1381                             */
1382    
1383                            break;
1384                        }
1385                    } else if (startChar == ch1) {
1386                        break;
1387                    }
1388                }
1389                while (*pattern != ']') {
1390                    if (*pattern == '\0') {
1391                        pattern = Tcl_UtfPrev(pattern, pstart);
1392                        break;
1393                    }
1394                    pattern++;
1395                }
1396                pattern++;
1397                continue;
1398            }
1399        
1400            /* If the next pattern character is '\', just strip off the '\'
1401             * so we do exact matching on the character that follows.
1402             */
1403            
1404            if (p == '\\') {
1405                pattern++;
1406                p = *pattern;
1407                if (p == '\0') {
1408                    return 0;
1409                }
1410            }
1411    
1412            /* There's no special character.  Just make sure that the next
1413             * bytes of each string match.
1414             */
1415            
1416            string  += Tcl_UtfToUniChar(string, &ch1);
1417            pattern += Tcl_UtfToUniChar(pattern, &ch2);
1418            if (nocase) {
1419                if (Tcl_UniCharToLower(ch1) != Tcl_UniCharToLower(ch2)) {
1420                    return 0;
1421                }
1422            } else if (ch1 != ch2) {
1423                return 0;
1424            }
1425        }
1426    }
1427    
1428    /*
1429     *----------------------------------------------------------------------
1430     *
1431     * Tcl_DStringInit --
1432     *
1433     *      Initializes a dynamic string, discarding any previous contents
1434     *      of the string (Tcl_DStringFree should have been called already
1435     *      if the dynamic string was previously in use).
1436     *
1437     * Results:
1438     *      None.
1439     *
1440     * Side effects:
1441     *      The dynamic string is initialized to be empty.
1442     *
1443     *----------------------------------------------------------------------
1444     */
1445    
1446    void
1447    Tcl_DStringInit(dsPtr)
1448        Tcl_DString *dsPtr;         /* Pointer to structure for dynamic string. */
1449    {
1450        dsPtr->string = dsPtr->staticSpace;
1451        dsPtr->length = 0;
1452        dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;
1453        dsPtr->staticSpace[0] = '\0';
1454    }
1455    
1456    /*
1457     *----------------------------------------------------------------------
1458     *
1459     * Tcl_DStringAppend --
1460     *
1461     *      Append more characters to the current value of a dynamic string.
1462     *
1463     * Results:
1464     *      The return value is a pointer to the dynamic string's new value.
1465     *
1466     * Side effects:
1467     *      Length bytes from string (or all of string if length is less
1468     *      than zero) are added to the current value of the string. Memory
1469     *      gets reallocated if needed to accomodate the string's new size.
1470     *
1471     *----------------------------------------------------------------------
1472     */
1473    
1474    char *
1475    Tcl_DStringAppend(dsPtr, string, length)
1476        Tcl_DString *dsPtr;         /* Structure describing dynamic string. */
1477        CONST char *string;         /* String to append.  If length is -1 then
1478                                     * this must be null-terminated. */
1479        int length;                 /* Number of characters from string to
1480                                     * append.  If < 0, then append all of string,
1481                                     * up to null at end. */
1482    {
1483        int newSize;
1484        char *dst;
1485        CONST char *end;
1486    
1487        if (length < 0) {
1488            length = strlen(string);
1489        }
1490        newSize = length + dsPtr->length;
1491    
1492        /*
1493         * Allocate a larger buffer for the string if the current one isn't
1494         * large enough. Allocate extra space in the new buffer so that there
1495         * will be room to grow before we have to allocate again.
1496         */
1497    
1498        if (newSize >= dsPtr->spaceAvl) {
1499            dsPtr->spaceAvl = newSize * 2;
1500            if (dsPtr->string == dsPtr->staticSpace) {
1501                char *newString;
1502    
1503                newString = (char *) ckalloc((unsigned) dsPtr->spaceAvl);
1504                memcpy((VOID *) newString, (VOID *) dsPtr->string,
1505                        (size_t) dsPtr->length);
1506                dsPtr->string = newString;
1507            } else {
1508                dsPtr->string = (char *) ckrealloc((VOID *) dsPtr->string,
1509                        (size_t) dsPtr->spaceAvl);
1510            }
1511        }
1512    
1513        /*
1514         * Copy the new string into the buffer at the end of the old
1515         * one.
1516         */
1517    
1518        for (dst = dsPtr->string + dsPtr->length, end = string+length;
1519                string < end; string++, dst++) {
1520            *dst = *string;
1521        }
1522        *dst = '\0';
1523        dsPtr->length += length;
1524        return dsPtr->string;
1525    }
1526    
1527    /*
1528     *----------------------------------------------------------------------
1529     *
1530     * Tcl_DStringAppendElement --
1531     *
1532     *      Append a list element to the current value of a dynamic string.
1533     *
1534     * Results:
1535     *      The return value is a pointer to the dynamic string's new value.
1536     *
1537     * Side effects:
1538     *      String is reformatted as a list element and added to the current
1539     *      value of the string.  Memory gets reallocated if needed to
1540     *      accomodate the string's new size.
1541     *
1542     *----------------------------------------------------------------------
1543     */
1544    
1545    char *
1546    Tcl_DStringAppendElement(dsPtr, string)
1547        Tcl_DString *dsPtr;         /* Structure describing dynamic string. */
1548        CONST char *string;         /* String to append.  Must be
1549                                     * null-terminated. */
1550    {
1551        int newSize, flags;
1552        char *dst;
1553    
1554        newSize = Tcl_ScanElement(string, &flags) + dsPtr->length + 1;
1555    
1556        /*
1557         * Allocate a larger buffer for the string if the current one isn't
1558         * large enough.  Allocate extra space in the new buffer so that there
1559         * will be room to grow before we have to allocate again.
1560         * SPECIAL NOTE: must use memcpy, not strcpy, to copy the string
1561         * to a larger buffer, since there may be embedded NULLs in the
1562         * string in some cases.
1563         */
1564    
1565        if (newSize >= dsPtr->spaceAvl) {
1566            dsPtr->spaceAvl = newSize * 2;
1567            if (dsPtr->string == dsPtr->staticSpace) {
1568                char *newString;
1569    
1570                newString = (char *) ckalloc((unsigned) dsPtr->spaceAvl);
1571                memcpy((VOID *) newString, (VOID *) dsPtr->string,
1572                        (size_t) dsPtr->length);
1573                dsPtr->string = newString;
1574            } else {
1575                dsPtr->string = (char *) ckrealloc((VOID *) dsPtr->string,
1576                        (size_t) dsPtr->spaceAvl);
1577            }
1578        }
1579    
1580        /*
1581         * Convert the new string to a list element and copy it into the
1582         * buffer at the end, with a space, if needed.
1583         */
1584    
1585        dst = dsPtr->string + dsPtr->length;
1586        if (TclNeedSpace(dsPtr->string, dst)) {
1587            *dst = ' ';
1588            dst++;
1589            dsPtr->length++;
1590        }
1591        dsPtr->length += Tcl_ConvertElement(string, dst, flags);
1592        return dsPtr->string;
1593    }
1594    
1595    /*
1596     *----------------------------------------------------------------------
1597     *
1598     * Tcl_DStringSetLength --
1599     *
1600     *      Change the length of a dynamic string.  This can cause the
1601     *      string to either grow or shrink, depending on the value of
1602     *      length.
1603     *
1604     * Results:
1605     *      None.
1606     *
1607     * Side effects:
1608     *      The length of dsPtr is changed to length and a null byte is
1609     *      stored at that position in the string.  If length is larger
1610     *      than the space allocated for dsPtr, then a panic occurs.
1611     *
1612     *----------------------------------------------------------------------
1613     */
1614    
1615    void
1616    Tcl_DStringSetLength(dsPtr, length)
1617        Tcl_DString *dsPtr;         /* Structure describing dynamic string. */
1618        int length;                 /* New length for dynamic string. */
1619    {
1620        int newsize;
1621    
1622        if (length < 0) {
1623            length = 0;
1624        }
1625        if (length >= dsPtr->spaceAvl) {
1626            /*
1627             * There are two interesting cases here.  In the first case, the user
1628             * may be trying to allocate a large buffer of a specific size.  It
1629             * would be wasteful to overallocate that buffer, so we just allocate
1630             * enough for the requested size plus the trailing null byte.  In the
1631             * second case, we are growing the buffer incrementally, so we need
1632             * behavior similar to Tcl_DStringAppend.  The requested length will
1633             * usually be a small delta above the current spaceAvl, so we'll end up
1634             * doubling the old size.  This won't grow the buffer quite as quickly,
1635             * but it should be close enough.
1636             */
1637    
1638            newsize = dsPtr->spaceAvl * 2;
1639            if (length < newsize) {
1640                dsPtr->spaceAvl = newsize;
1641            } else {
1642                dsPtr->spaceAvl = length + 1;
1643            }
1644            if (dsPtr->string == dsPtr->staticSpace) {
1645                char *newString;
1646    
1647                newString = (char *) ckalloc((unsigned) dsPtr->spaceAvl);
1648                memcpy((VOID *) newString, (VOID *) dsPtr->string,
1649                        (size_t) dsPtr->length);
1650                dsPtr->string = newString;
1651            } else {
1652                dsPtr->string = (char *) ckrealloc((VOID *) dsPtr->string,
1653                        (size_t) dsPtr->spaceAvl);
1654            }
1655        }
1656        dsPtr->length = length;
1657        dsPtr->string[length] = 0;
1658    }
1659    
1660    /*
1661     *----------------------------------------------------------------------
1662     *
1663     * Tcl_DStringFree --
1664     *
1665     *      Frees up any memory allocated for the dynamic string and
1666     *      reinitializes the string to an empty state.
1667     *
1668     * Results:
1669     *      None.
1670     *
1671     * Side effects:
1672     *      The previous contents of the dynamic string are lost, and
1673     *      the new value is an empty string.
1674     *
1675     *---------------------------------------------------------------------- */
1676    
1677    void
1678    Tcl_DStringFree(dsPtr)
1679        Tcl_DString *dsPtr;         /* Structure describing dynamic string. */
1680    {
1681        if (dsPtr->string != dsPtr->staticSpace) {
1682            ckfree(dsPtr->string);
1683        }
1684        dsPtr->string = dsPtr->staticSpace;
1685        dsPtr->length = 0;
1686        dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;
1687        dsPtr->staticSpace[0] = '\0';
1688    }
1689    
1690    /*
1691     *----------------------------------------------------------------------
1692     *
1693     * Tcl_DStringResult --
1694     *
1695     *      This procedure moves the value of a dynamic string into an
1696     *      interpreter as its string result. Afterwards, the dynamic string
1697     *      is reset to an empty string.
1698     *
1699     * Results:
1700     *      None.
1701     *
1702     * Side effects:
1703     *      The string is "moved" to interp's result, and any existing
1704     *      string result for interp is freed. dsPtr is reinitialized to
1705     *      an empty string.
1706     *
1707     *----------------------------------------------------------------------
1708     */
1709    
1710    void
1711    Tcl_DStringResult(interp, dsPtr)
1712        Tcl_Interp *interp;         /* Interpreter whose result is to be reset. */
1713        Tcl_DString *dsPtr;         /* Dynamic string that is to become the
1714                                     * result of interp. */
1715    {
1716        Tcl_ResetResult(interp);
1717        
1718        if (dsPtr->string != dsPtr->staticSpace) {
1719            interp->result = dsPtr->string;
1720            interp->freeProc = TCL_DYNAMIC;
1721        } else if (dsPtr->length < TCL_RESULT_SIZE) {
1722            interp->result = ((Interp *) interp)->resultSpace;
1723            strcpy(interp->result, dsPtr->string);
1724        } else {
1725            Tcl_SetResult(interp, dsPtr->string, TCL_VOLATILE);
1726        }
1727        
1728        dsPtr->string = dsPtr->staticSpace;
1729        dsPtr->length = 0;
1730        dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;
1731        dsPtr->staticSpace[0] = '\0';
1732    }
1733    
1734    /*
1735     *----------------------------------------------------------------------
1736     *
1737     * Tcl_DStringGetResult --
1738     *
1739     *      This procedure moves an interpreter's result into a dynamic string.
1740     *
1741     * Results:
1742     *      None.
1743     *
1744     * Side effects:
1745     *      The interpreter's string result is cleared, and the previous
1746     *      contents of dsPtr are freed.
1747     *
1748     *      If the string result is empty, the object result is moved to the
1749     *      string result, then the object result is reset.
1750     *
1751     *----------------------------------------------------------------------
1752     */
1753    
1754    void
1755    Tcl_DStringGetResult(interp, dsPtr)
1756        Tcl_Interp *interp;         /* Interpreter whose result is to be reset. */
1757        Tcl_DString *dsPtr;         /* Dynamic string that is to become the
1758                                     * result of interp. */
1759    {
1760        Interp *iPtr = (Interp *) interp;
1761        
1762        if (dsPtr->string != dsPtr->staticSpace) {
1763            ckfree(dsPtr->string);
1764        }
1765    
1766        /*
1767         * If the string result is empty, move the object result to the
1768         * string result, then reset the object result.
1769         */
1770    
1771        if (*(iPtr->result) == 0) {
1772            Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)),
1773                    TCL_VOLATILE);
1774        }
1775    
1776        dsPtr->length = strlen(iPtr->result);
1777        if (iPtr->freeProc != NULL) {
1778            if ((iPtr->freeProc == TCL_DYNAMIC)
1779                    || (iPtr->freeProc == (Tcl_FreeProc *) free)) {
1780                dsPtr->string = iPtr->result;
1781                dsPtr->spaceAvl = dsPtr->length+1;
1782            } else {
1783                dsPtr->string = (char *) ckalloc((unsigned) (dsPtr->length+1));
1784                strcpy(dsPtr->string, iPtr->result);
1785                (*iPtr->freeProc)(iPtr->result);
1786            }
1787            dsPtr->spaceAvl = dsPtr->length+1;
1788            iPtr->freeProc = NULL;
1789        } else {
1790            if (dsPtr->length < TCL_DSTRING_STATIC_SIZE) {
1791                dsPtr->string = dsPtr->staticSpace;
1792                dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;
1793            } else {
1794                dsPtr->string = (char *) ckalloc((unsigned) (dsPtr->length + 1));
1795                dsPtr->spaceAvl = dsPtr->length + 1;
1796            }
1797            strcpy(dsPtr->string, iPtr->result);
1798        }
1799        
1800        iPtr->result = iPtr->resultSpace;
1801        iPtr->resultSpace[0] = 0;
1802    }
1803    
1804    /*
1805     *----------------------------------------------------------------------
1806     *
1807     * Tcl_DStringStartSublist --
1808     *
1809     *      This procedure adds the necessary information to a dynamic
1810     *      string (e.g. " {" to start a sublist.  Future element
1811     *      appends will be in the sublist rather than the main list.
1812     *
1813     * Results:
1814     *      None.
1815     *
1816     * Side effects:
1817     *      Characters get added to the dynamic string.
1818     *
1819     *----------------------------------------------------------------------
1820     */
1821    
1822    void
1823    Tcl_DStringStartSublist(dsPtr)
1824        Tcl_DString *dsPtr;                 /* Dynamic string. */
1825    {
1826        if (TclNeedSpace(dsPtr->string, dsPtr->string + dsPtr->length)) {
1827            Tcl_DStringAppend(dsPtr, " {", -1);
1828        } else {
1829            Tcl_DStringAppend(dsPtr, "{", -1);
1830        }
1831    }
1832    
1833    /*
1834     *----------------------------------------------------------------------
1835     *
1836     * Tcl_DStringEndSublist --
1837     *
1838     *      This procedure adds the necessary characters to a dynamic
1839     *      string to end a sublist (e.g. "}").  Future element appends
1840     *      will be in the enclosing (sub)list rather than the current
1841     *      sublist.
1842     *
1843     * Results:
1844     *      None.
1845     *
1846     * Side effects:
1847     *      None.
1848     *
1849     *----------------------------------------------------------------------
1850     */
1851    
1852    void
1853    Tcl_DStringEndSublist(dsPtr)
1854        Tcl_DString *dsPtr;                 /* Dynamic string. */
1855    {
1856        Tcl_DStringAppend(dsPtr, "}", -1);
1857    }
1858    
1859    /*
1860     *----------------------------------------------------------------------
1861     *
1862     * Tcl_PrintDouble --
1863     *
1864     *      Given a floating-point value, this procedure converts it to
1865     *      an ASCII string using.
1866     *
1867     * Results:
1868     *      The ASCII equivalent of "value" is written at "dst".  It is
1869     *      written using the current precision, and it is guaranteed to
1870     *      contain a decimal point or exponent, so that it looks like
1871     *      a floating-point value and not an integer.
1872     *
1873     * Side effects:
1874     *      None.
1875     *
1876     *----------------------------------------------------------------------
1877     */
1878    
1879    void
1880    Tcl_PrintDouble(interp, value, dst)
1881        Tcl_Interp *interp;                 /* Interpreter whose tcl_precision
1882                                             * variable used to be used to control
1883                                             * printing.  It's ignored now. */
1884        double value;                       /* Value to print as string. */
1885        char *dst;                          /* Where to store converted value;
1886                                             * must have at least TCL_DOUBLE_SPACE
1887                                             * characters. */
1888    {
1889        char *p, c;
1890        Tcl_UniChar ch;
1891    
1892        Tcl_MutexLock(&precisionMutex);
1893        sprintf(dst, precisionFormat, value);
1894        Tcl_MutexUnlock(&precisionMutex);
1895    
1896        /*
1897         * If the ASCII result looks like an integer, add ".0" so that it
1898         * doesn't look like an integer anymore.  This prevents floating-point
1899         * values from being converted to integers unintentionally.
1900         */
1901    
1902        for (p = dst; *p != 0; ) {
1903            p += Tcl_UtfToUniChar(p, &ch);
1904            c = UCHAR(ch);
1905            if ((c == '.') || isalpha(UCHAR(c))) {  /* INTL: ISO only. */
1906                return;
1907            }
1908        }
1909        p[0] = '.';
1910        p[1] = '0';
1911        p[2] = 0;
1912    }
1913    
1914    /*
1915     *----------------------------------------------------------------------
1916     *
1917     * TclPrecTraceProc --
1918     *
1919     *      This procedure is invoked whenever the variable "tcl_precision"
1920     *      is written.
1921     *
1922     * Results:
1923     *      Returns NULL if all went well, or an error message if the
1924     *      new value for the variable doesn't make sense.
1925     *
1926     * Side effects:
1927     *      If the new value doesn't make sense then this procedure
1928     *      undoes the effect of the variable modification.  Otherwise
1929     *      it modifies the format string that's used by Tcl_PrintDouble.
1930     *
1931     *----------------------------------------------------------------------
1932     */
1933    
1934            /* ARGSUSED */
1935    char *
1936    TclPrecTraceProc(clientData, interp, name1, name2, flags)
1937        ClientData clientData;      /* Not used. */
1938        Tcl_Interp *interp;         /* Interpreter containing variable. */
1939        char *name1;                /* Name of variable. */
1940        char *name2;                /* Second part of variable name. */
1941        int flags;                  /* Information about what happened. */
1942    {
1943        char *value, *end;
1944        int prec;
1945    
1946        /*
1947         * If the variable is unset, then recreate the trace.
1948         */
1949    
1950        if (flags & TCL_TRACE_UNSETS) {
1951            if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1952                Tcl_TraceVar2(interp, name1, name2,
1953                        TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES
1954                        |TCL_TRACE_UNSETS, TclPrecTraceProc, clientData);
1955            }
1956            return (char *) NULL;
1957        }
1958    
1959        /*
1960         * When the variable is read, reset its value from our shared
1961         * value.  This is needed in case the variable was modified in
1962         * some other interpreter so that this interpreter's value is
1963         * out of date.
1964         */
1965    
1966        Tcl_MutexLock(&precisionMutex);
1967