📄 tclutil.c
字号:
/* * 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-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tclUtil.c 1.2 98/06/12 16:49:08 */#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;/* * 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. * * NOTE: these variables are not thread-safe. */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. *//* * Function prototypes for local procedures in this file: */static void SetupAppendBuffer _ANSI_ARGS_((Interp *iPtr, int newSpace));/* *---------------------------------------------------------------------- * * 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; * interp->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. * *---------------------------------------------------------------------- */intTclFindElement(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. */ 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. */ char **elementPtr; /* Where to put address of first significant * character in first element of list. */ 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. */{ char *p = list; char *elemStart; /* Points to first byte of first element. */ char *limit; /* Points just after list's last byte. */ int openBraces = 0; /* Brace nesting level during parse. */ int inQuotes = 0; int size = 0; /* Init. avoids compiler warning. */ int numChars; 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. Note: use of "isascii" below and elsewhere in this * procedure is a temporary hack (7/27/90) because Mx uses characters * with the high-order bit set for some things. This should probably * be changed back eventually, or all of Tcl should call isascii. */ limit = (list + listLength); while ((p < limit) && (isspace(UCHAR(*p)))) { 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))) { goto done; } /* * Garbage after the closing brace; return an error. */ if (interp != NULL) { char buf[100]; p2 = p; while ((p2 < limit) && (!isspace(UCHAR(*p2))) && (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 '\\': { (void) Tcl_Backslash(p, &numChars); 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))) { goto done; } /* * Garbage after the closing quote; return an error. */ if (interp != NULL) { char buf[100]; p2 = p; while ((p2 < limit) && (!isspace(UCHAR(*p2))) && (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)))) { 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. * *---------------------------------------------------------------------- */intTclCopyAndCollapse(count, src, dst) int count; /* Number of characters to copy from src. */ char *src; /* Copy from here... */ char *dst; /* ... to here. */{ char c; int numRead; int newCount = 0; for (c = *src; count > 0; src++, c = *src, count--) { if (c == '\\') { *dst = Tcl_Backslash(src, &numRead); dst++; src += numRead-1; count -= numRead-1; newCount++; } 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; interp->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. * *---------------------------------------------------------------------- */intTcl_SplitList(interp, list, argcPtr, argvPtr) Tcl_Interp *interp; /* Interpreter to use for error reporting. * If NULL, no error message is left. */ 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; char *p; int length, size, i, result, elSize, brace; 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, p = list; *p != 0; p++) { if (isspace(UCHAR(*p))) { size++; } } size++; /* Leave space for final NULL pointer. */ argv = (char **) ckalloc((unsigned) ((size * sizeof(char *)) + (p - list) + 1)); length = strlen(list); for (i = 0, p = ((char *) argv) + size*sizeof(char *); *list != 0; i++) { 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. * *---------------------------------------------------------------------- */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -