/*1* tclUtil.c --2*3* This file contains utility procedures that are used by many Tcl4* commands.5*6* Copyright (c) 1987-1993 The Regents of the University of California.7* Copyright (c) 1994-1995 Sun Microsystems, Inc.8*9* See the file "license.terms" for information on usage and redistribution10* of this file, and for a DISCLAIMER OF ALL WARRANTIES.11*12* SCCS: @(#) tclUtil.c 1.114 96/06/06 13:48:5813*/1415#include "tclInt.h"16#include "tclPort.h"1718/*19* The following values are used in the flags returned by Tcl_ScanElement20* and used by Tcl_ConvertElement. The value TCL_DONT_USE_BRACES is also21* defined in tcl.h; make sure its value doesn't overlap with any of the22* values below.23*24* TCL_DONT_USE_BRACES - 1 means the string mustn't be enclosed in25* braces (e.g. it contains unmatched braces,26* or ends in a backslash character, or user27* just doesn't want braces); handle all28* special characters by adding backslashes.29* USE_BRACES - 1 means the string contains a special30* character that can be handled simply by31* enclosing the entire argument in braces.32* BRACES_UNMATCHED - 1 means that braces aren't properly matched33* in the argument.34*/3536#define USE_BRACES 237#define BRACES_UNMATCHED 43839/*40* Function prototypes for local procedures in this file:41*/4243static void SetupAppendBuffer _ANSI_ARGS_((Interp *iPtr,44int newSpace));4546/*47*----------------------------------------------------------------------48*49* TclFindElement --50*51* Given a pointer into a Tcl list, locate the first (or next)52* element in the list.53*54* Results:55* The return value is normally TCL_OK, which means that the56* element was successfully located. If TCL_ERROR is returned57* it means that list didn't have proper list structure;58* interp->result contains a more detailed error message.59*60* If TCL_OK is returned, then *elementPtr will be set to point61* to the first element of list, and *nextPtr will be set to point62* to the character just after any white space following the last63* character that's part of the element. If this is the last argument64* in the list, then *nextPtr will point to the NULL character at the65* end of list. If sizePtr is non-NULL, *sizePtr is filled in with66* the number of characters in the element. If the element is in67* braces, then *elementPtr will point to the character after the68* opening brace and *sizePtr will not include either of the braces.69* If there isn't an element in the list, *sizePtr will be zero, and70* both *elementPtr and *termPtr will refer to the null character at71* the end of list. Note: this procedure does NOT collapse backslash72* sequences.73*74* Side effects:75* None.76*77*----------------------------------------------------------------------78*/7980int81TclFindElement(interp, list, elementPtr, nextPtr, sizePtr, bracePtr)82Tcl_Interp *interp; /* Interpreter to use for error reporting.83* If NULL, then no error message is left84* after errors. */85register char *list; /* String containing Tcl list with zero86* or more elements (possibly in braces). */87char **elementPtr; /* Fill in with location of first significant88* character in first element of list. */89char **nextPtr; /* Fill in with location of character just90* after all white space following end of91* argument (i.e. next argument or end of92* list). */93int *sizePtr; /* If non-zero, fill in with size of94* element. */95int *bracePtr; /* If non-zero fill in with non-zero/zero96* to indicate that arg was/wasn't97* in braces. */98{99register char *p;100int openBraces = 0;101int inQuotes = 0;102int size;103104/*105* Skim off leading white space and check for an opening brace or106* quote. Note: use of "isascii" below and elsewhere in this107* procedure is a temporary hack (7/27/90) because Mx uses characters108* with the high-order bit set for some things. This should probably109* be changed back eventually, or all of Tcl should call isascii.110*/111112while (isspace(UCHAR(*list))) {113list++;114}115if (*list == '{') {116openBraces = 1;117list++;118} else if (*list == '"') {119inQuotes = 1;120list++;121}122if (bracePtr != 0) {123*bracePtr = openBraces;124}125p = list;126127/*128* Find the end of the element (either a space or a close brace or129* the end of the string).130*/131132while (1) {133switch (*p) {134135/*136* Open brace: don't treat specially unless the element is137* in braces. In this case, keep a nesting count.138*/139140case '{':141if (openBraces != 0) {142openBraces++;143}144break;145146/*147* Close brace: if element is in braces, keep nesting148* count and quit when the last close brace is seen.149*/150151case '}':152if (openBraces == 1) {153char *p2;154155size = p - list;156p++;157if (isspace(UCHAR(*p)) || (*p == 0)) {158goto done;159}160for (p2 = p; (*p2 != 0) && (!isspace(UCHAR(*p2)))161&& (p2 < p+20); p2++) {162/* null body */163}164if (interp != NULL) {165Tcl_ResetResult(interp);166sprintf(interp->result,167"list element in braces followed by \"%.*s\" instead of space",168(int) (p2-p), p);169}170return TCL_ERROR;171} else if (openBraces != 0) {172openBraces--;173}174break;175176/*177* Backslash: skip over everything up to the end of the178* backslash sequence.179*/180181case '\\': {182int size;183184(void) Tcl_Backslash(p, &size);185p += size - 1;186break;187}188189/*190* Space: ignore if element is in braces or quotes; otherwise191* terminate element.192*/193194case ' ':195case '\f':196case '\n':197case '\r':198case '\t':199case '\v':200if ((openBraces == 0) && !inQuotes) {201size = p - list;202goto done;203}204break;205206/*207* Double-quote: if element is in quotes then terminate it.208*/209210case '"':211if (inQuotes) {212char *p2;213214size = p-list;215p++;216if (isspace(UCHAR(*p)) || (*p == 0)) {217goto done;218}219for (p2 = p; (*p2 != 0) && (!isspace(UCHAR(*p2)))220&& (p2 < p+20); p2++) {221/* null body */222}223if (interp != NULL) {224Tcl_ResetResult(interp);225sprintf(interp->result,226"list element in quotes followed by \"%.*s\" %s", (int) (p2-p), p,227"instead of space");228}229return TCL_ERROR;230}231break;232233/*234* End of list: terminate element.235*/236237case 0:238if (openBraces != 0) {239if (interp != NULL) {240Tcl_SetResult(interp, "unmatched open brace in list",241TCL_STATIC);242}243return TCL_ERROR;244} else if (inQuotes) {245if (interp != NULL) {246Tcl_SetResult(interp, "unmatched open quote in list",247TCL_STATIC);248}249return TCL_ERROR;250}251size = p - list;252goto done;253254}255p++;256}257258done:259while (isspace(UCHAR(*p))) {260p++;261}262*elementPtr = list;263*nextPtr = p;264if (sizePtr != 0) {265*sizePtr = size;266}267return TCL_OK;268}269270/*271*----------------------------------------------------------------------272*273* TclCopyAndCollapse --274*275* Copy a string and eliminate any backslashes that aren't in braces.276*277* Results:278* There is no return value. Count chars. get copied from src279* to dst. Along the way, if backslash sequences are found outside280* braces, the backslashes are eliminated in the copy.281* After scanning count chars. from source, a null character is282* placed at the end of dst.283*284* Side effects:285* None.286*287*----------------------------------------------------------------------288*/289290void291TclCopyAndCollapse(count, src, dst)292int count; /* Total number of characters to copy293* from src. */294register char *src; /* Copy from here... */295register char *dst; /* ... to here. */296{297register char c;298int numRead;299300for (c = *src; count > 0; src++, c = *src, count--) {301if (c == '\\') {302*dst = Tcl_Backslash(src, &numRead);303dst++;304src += numRead-1;305count -= numRead-1;306} else {307*dst = c;308dst++;309}310}311*dst = 0;312}313314/*315*----------------------------------------------------------------------316*317* Tcl_SplitList --318*319* Splits a list up into its constituent fields.320*321* Results322* The return value is normally TCL_OK, which means that323* the list was successfully split up. If TCL_ERROR is324* returned, it means that "list" didn't have proper list325* structure; interp->result will contain a more detailed326* error message.327*328* *argvPtr will be filled in with the address of an array329* whose elements point to the elements of list, in order.330* *argcPtr will get filled in with the number of valid elements331* in the array. A single block of memory is dynamically allocated332* to hold both the argv array and a copy of the list (with333* backslashes and braces removed in the standard way).334* The caller must eventually free this memory by calling free()335* on *argvPtr. Note: *argvPtr and *argcPtr are only modified336* if the procedure returns normally.337*338* Side effects:339* Memory is allocated.340*341*----------------------------------------------------------------------342*/343344int345Tcl_TclSplitList(interp, list, argcPtr, argvPtr)346Tcl_Interp *interp; /* Interpreter to use for error reporting.347* If NULL, then no error message is left. */348char *list; /* Pointer to string with list structure. */349int *argcPtr; /* Pointer to location to fill in with350* the number of elements in the list. */351char ***argvPtr; /* Pointer to place to store pointer to array352* of pointers to list elements. */353{354char **argv;355register char *p;356int size, i, result, elSize, brace;357char *element;358359/*360* Figure out how much space to allocate. There must be enough361* space for both the array of pointers and also for a copy of362* the list. To estimate the number of pointers needed, count363* the number of space characters in the list.364*/365366for (size = 1, p = list; *p != 0; p++) {367if (isspace(UCHAR(*p))) {368size++;369}370}371size++; /* Leave space for final NULL pointer. */372argv = (char **) ckalloc((unsigned)373((size * sizeof(char *)) + (p - list) + 1));374for (i = 0, p = ((char *) argv) + size*sizeof(char *);375*list != 0; i++) {376result = TclFindElement(interp, list, &element, &list, &elSize, &brace);377if (result != TCL_OK) {378ckfree((char *) argv);379return result;380}381if (*element == 0) {382break;383}384if (i >= size) {385ckfree((char *) argv);386if (interp != NULL) {387Tcl_SetResult(interp, "internal error in Tcl_SplitList",388TCL_STATIC);389}390return TCL_ERROR;391}392argv[i] = p;393if (brace) {394strncpy(p, element, (size_t) elSize);395p += elSize;396*p = 0;397p++;398} else {399TclCopyAndCollapse(elSize, element, p);400p += elSize+1;401}402}403404argv[i] = NULL;405*argvPtr = argv;406*argcPtr = i;407return TCL_OK;408}409410/*411*----------------------------------------------------------------------412*413* Tcl_ScanElement --414*415* This procedure is a companion procedure to Tcl_ConvertElement.416* It scans a string to see what needs to be done to it (e.g.417* add backslashes or enclosing braces) to make the string into418* a valid Tcl list element.419*420* Results:421* The return value is an overestimate of the number of characters422* that will be needed by Tcl_ConvertElement to produce a valid423* list element from string. The word at *flagPtr is filled in424* with a value needed by Tcl_ConvertElement when doing the actual425* conversion.426*427* Side effects:428* None.429*430*----------------------------------------------------------------------431*/432433int434Tcl_TclScanElement(string, flagPtr)435char *string; /* String to convert to Tcl list element. */436int *flagPtr; /* Where to store information to guide437* Tcl_ConvertElement. */438{439int flags, nestingLevel;440register char *p;441442/*443* This procedure and Tcl_ConvertElement together do two things:444*445* 1. They produce a proper list, one that will yield back the446* argument strings when evaluated or when disassembled with447* Tcl_SplitList. This is the most important thing.448*449* 2. They try to produce legible output, which means minimizing the450* use of backslashes (using braces instead). However, there are451* some situations where backslashes must be used (e.g. an element452* like "{abc": the leading brace will have to be backslashed. For453* each element, one of three things must be done:454*455* (a) Use the element as-is (it doesn't contain anything special456* characters). This is the most desirable option.457*458* (b) Enclose the element in braces, but leave the contents alone.459* This happens if the element contains embedded space, or if it460* contains characters with special interpretation ($, [, ;, or \),461* or if it starts with a brace or double-quote, or if there are462* no characters in the element.463*464* (c) Don't enclose the element in braces, but add backslashes to465* prevent special interpretation of special characters. This is a466* last resort used when the argument would normally fall under case467* (b) but contains unmatched braces. It also occurs if the last468* character of the argument is a backslash or if the element contains469* a backslash followed by newline.470*471* The procedure figures out how many bytes will be needed to store472* the result (actually, it overestimates). It also collects information473* about the element in the form of a flags word.474*/475476nestingLevel = 0;477flags = 0;478if (string == NULL) {479string = "";480}481p = string;482if ((*p == '{') || (*p == '"') || (*p == 0)) {483flags |= USE_BRACES;484}485for ( ; *p != 0; p++) {486switch (*p) {487case '{':488nestingLevel++;489break;490case '}':491nestingLevel--;492if (nestingLevel < 0) {493flags |= TCL_DONT_USE_BRACES|BRACES_UNMATCHED;494}495break;496case '[':497case '$':498case ';':499case ' ':500case '\f':501case '\n':502case '\r':503case '\t':504case '\v':505flags |= USE_BRACES;506break;507case '\\':508if ((p[1] == 0) || (p[1] == '\n')) {509flags = TCL_DONT_USE_BRACES;510} else {511int size;512513(void) Tcl_Backslash(p, &size);514p += size-1;515flags |= USE_BRACES;516}517break;518}519}520if (nestingLevel != 0) {521flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED;522}523*flagPtr = flags;524525/*526* Allow enough space to backslash every character plus leave527* two spaces for braces.528*/529530return 2*(p-string) + 2;531}532533/*534*----------------------------------------------------------------------535*536* Tcl_ConvertElement --537*538* This is a companion procedure to Tcl_ScanElement. Given the539* information produced by Tcl_ScanElement, this procedure converts540* a string to a list element equal to that string.541*542* Results:543* Information is copied to *dst in the form of a list element544* identical to src (i.e. if Tcl_SplitList is applied to dst it545* will produce a string identical to src). The return value is546* a count of the number of characters copied (not including the547* terminating NULL character).548*549* Side effects:550* None.551*552*----------------------------------------------------------------------553*/554555int556Tcl_TclConvertElement(src, dst, flags)557register char *src; /* Source information for list element. */558char *dst; /* Place to put list-ified element. */559int flags; /* Flags produced by Tcl_ScanElement. */560{561register char *p = dst;562563/*564* See the comment block at the beginning of the Tcl_ScanElement565* code for details of how this works.566*/567568if ((src == NULL) || (*src == 0)) {569p[0] = '{';570p[1] = '}';571p[2] = 0;572return 2;573}574if ((flags & USE_BRACES) && !(flags & TCL_DONT_USE_BRACES)) {575*p = '{';576p++;577for ( ; *src != 0; src++, p++) {578*p = *src;579}580*p = '}';581p++;582} else {583if (*src == '{') {584/*585* Can't have a leading brace unless the whole element is586* enclosed in braces. Add a backslash before the brace.587* Furthermore, this may destroy the balance between open588* and close braces, so set BRACES_UNMATCHED.589*/590591p[0] = '\\';592p[1] = '{';593p += 2;594src++;595flags |= BRACES_UNMATCHED;596}597for (; *src != 0 ; src++) {598switch (*src) {599case ']':600case '[':601case '$':602case ';':603case ' ':604case '\\':605case '"':606*p = '\\';607p++;608break;609case '{':610case '}':611/*612* It may not seem necessary to backslash braces, but613* it is. The reason for this is that the resulting614* list element may actually be an element of a sub-list615* enclosed in braces (e.g. if Tcl_DStringStartSublist616* has been invoked), so there may be a brace mismatch617* if the braces aren't backslashed.618*/619620if (flags & BRACES_UNMATCHED) {621*p = '\\';622p++;623}624break;625case '\f':626*p = '\\';627p++;628*p = 'f';629p++;630continue;631case '\n':632*p = '\\';633p++;634*p = 'n';635p++;636continue;637case '\r':638*p = '\\';639p++;640*p = 'r';641p++;642continue;643case '\t':644*p = '\\';645p++;646*p = 't';647p++;648continue;649case '\v':650*p = '\\';651p++;652*p = 'v';653p++;654continue;655}656*p = *src;657p++;658}659}660*p = '\0';661return p-dst;662}663664/*665*----------------------------------------------------------------------666*667* Tcl_Merge --668*669* Given a collection of strings, merge them together into a670* single string that has proper Tcl list structured (i.e.671* Tcl_SplitList may be used to retrieve strings equal to the672* original elements, and Tcl_Eval will parse the string back673* into its original elements).674*675* Results:676* The return value is the address of a dynamically-allocated677* string containing the merged list.678*679* Side effects:680* None.681*682*----------------------------------------------------------------------683*/684685char *686Tcl_TclMerge(argc, argv)687int argc; /* How many strings to merge. */688char **argv; /* Array of string values. */689{690# define LOCAL_SIZE 20691int localFlags[LOCAL_SIZE], *flagPtr;692int numChars;693char *result;694register char *dst;695int i;696697/*698* Pass 1: estimate space, gather flags.699*/700701if (argc <= LOCAL_SIZE) {702flagPtr = localFlags;703} else {704flagPtr = (int *) ckalloc((unsigned) argc*sizeof(int));705}706numChars = 1;707for (i = 0; i < argc; i++) {708numChars += Tcl_ScanElement(argv[i], &flagPtr[i]) + 1;709}710711/*712* Pass two: copy into the result area.713*/714715result = (char *) ckalloc((unsigned) numChars);716dst = result;717for (i = 0; i < argc; i++) {718numChars = Tcl_ConvertElement(argv[i], dst, flagPtr[i]);719dst += numChars;720*dst = ' ';721dst++;722}723if (dst == result) {724*dst = 0;725} else {726dst[-1] = 0;727}728729if (flagPtr != localFlags) {730ckfree((char *) flagPtr);731}732return result;733}734735/*736*----------------------------------------------------------------------737*738* Tcl_Concat --739*740* Concatenate a set of strings into a single large string.741*742* Results:743* The return value is dynamically-allocated string containing744* a concatenation of all the strings in argv, with spaces between745* the original argv elements.746*747* Side effects:748* Memory is allocated for the result; the caller is responsible749* for freeing the memory.750*751*----------------------------------------------------------------------752*/753754char *755Tcl_Concat(argc, argv)756int argc; /* Number of strings to concatenate. */757char **argv; /* Array of strings to concatenate. */758{759int totalSize, i;760register char *p;761char *result;762763for (totalSize = 1, i = 0; i < argc; i++) {764totalSize += strlen(argv[i]) + 1;765}766result = (char *) ckalloc((unsigned) totalSize);767if (argc == 0) {768*result = '\0';769return result;770}771for (p = result, i = 0; i < argc; i++) {772char *element;773int length;774775/*776* Clip white space off the front and back of the string777* to generate a neater result, and ignore any empty778* elements.779*/780781element = argv[i];782while (isspace(UCHAR(*element))) {783element++;784}785for (length = strlen(element);786(length > 0) && (isspace(UCHAR(element[length-1])));787length--) {788/* Null loop body. */789}790if (length == 0) {791continue;792}793(void) strncpy(p, element, (size_t) length);794p += length;795*p = ' ';796p++;797}798if (p != result) {799p[-1] = 0;800} else {801*p = 0;802}803return result;804}805806/*807*----------------------------------------------------------------------808*809* Tcl_StringMatch --810*811* See if a particular string matches a particular pattern.812*813* Results:814* The return value is 1 if string matches pattern, and815* 0 otherwise. The matching operation permits the following816* special characters in the pattern: *?\[] (see the manual817* entry for details on what these mean).818*819* Side effects:820* None.821*822*----------------------------------------------------------------------823*/824825int826Tcl_StringMatch(string, pattern)827register char *string; /* String. */828register char *pattern; /* Pattern, which may contain829* special characters. */830{831char c2;832833while (1) {834/* See if we're at the end of both the pattern and the string.835* If so, we succeeded. If we're at the end of the pattern836* but not at the end of the string, we failed.837*/838839if (*pattern == 0) {840if (*string == 0) {841return 1;842} else {843return 0;844}845}846if ((*string == 0) && (*pattern != '*')) {847return 0;848}849850/* Check for a "*" as the next pattern character. It matches851* any substring. We handle this by calling ourselves852* recursively for each postfix of string, until either we853* match or we reach the end of the string.854*/855856if (*pattern == '*') {857pattern += 1;858if (*pattern == 0) {859return 1;860}861while (1) {862if (Tcl_StringMatch(string, pattern)) {863return 1;864}865if (*string == 0) {866return 0;867}868string += 1;869}870}871872/* Check for a "?" as the next pattern character. It matches873* any single character.874*/875876if (*pattern == '?') {877goto thisCharOK;878}879880/* Check for a "[" as the next pattern character. It is followed881* by a list of characters that are acceptable, or by a range882* (two characters separated by "-").883*/884885if (*pattern == '[') {886pattern += 1;887while (1) {888if ((*pattern == ']') || (*pattern == 0)) {889return 0;890}891if (*pattern == *string) {892break;893}894if (pattern[1] == '-') {895c2 = pattern[2];896if (c2 == 0) {897return 0;898}899if ((*pattern <= *string) && (c2 >= *string)) {900break;901}902if ((*pattern >= *string) && (c2 <= *string)) {903break;904}905pattern += 2;906}907pattern += 1;908}909while (*pattern != ']') {910if (*pattern == 0) {911pattern--;912break;913}914pattern += 1;915}916goto thisCharOK;917}918919/* If the next pattern character is '/', just strip off the '/'920* so we do exact matching on the character that follows.921*/922923if (*pattern == '\\') {924pattern += 1;925if (*pattern == 0) {926return 0;927}928}929930/* There's no special character. Just make sure that the next931* characters of each string match.932*/933934if (*pattern != *string) {935return 0;936}937938thisCharOK: pattern += 1;939string += 1;940}941}942943/*944*----------------------------------------------------------------------945*946* Tcl_SetResult --947*948* Arrange for "string" to be the Tcl return value.949*950* Results:951* None.952*953* Side effects:954* interp->result is left pointing either to "string" (if "copy" is 0)955* or to a copy of string.956*957*----------------------------------------------------------------------958*/959960void961Tcl_SetResult(interp, string, freeProc)962Tcl_Interp *interp; /* Interpreter with which to associate the963* return value. */964char *string; /* Value to be returned. If NULL,965* the result is set to an empty string. */966Tcl_FreeProc *freeProc; /* Gives information about the string:967* TCL_STATIC, TCL_VOLATILE, or the address968* of a Tcl_FreeProc such as free. */969{970register Interp *iPtr = (Interp *) interp;971int length;972Tcl_FreeProc *oldFreeProc = iPtr->freeProc;973char *oldResult = iPtr->result;974975if (string == NULL) {976iPtr->resultSpace[0] = 0;977iPtr->result = iPtr->resultSpace;978iPtr->freeProc = 0;979} else if (freeProc == TCL_VOLATILE) {980length = strlen(string);981if (length > TCL_RESULT_SIZE) {982iPtr->result = (char *) ckalloc((unsigned) length+1);983iPtr->freeProc = TCL_DYNAMIC;984} else {985iPtr->result = iPtr->resultSpace;986iPtr->freeProc = 0;987}988strcpy(iPtr->result, string);989} else {990iPtr->result = string;991iPtr->freeProc = freeProc;992}993994/*995* If the old result was dynamically-allocated, free it up. Do it996* here, rather than at the beginning, in case the new result value997* was part of the old result value.998*/9991000if (oldFreeProc != 0) {1001if ((oldFreeProc == TCL_DYNAMIC)1002|| (oldFreeProc == (Tcl_FreeProc *) free)) {1003ckfree(oldResult);1004} else {1005(*oldFreeProc)(oldResult);1006}1007}1008}10091010/*1011*----------------------------------------------------------------------1012*1013* Tcl_AppendResult --1014*1015* Append a variable number of strings onto the result already1016* present for an interpreter.1017*1018* Results:1019* None.1020*1021* Side effects:1022* The result in the interpreter given by the first argument1023* is extended by the strings given by the second and following1024* arguments (up to a terminating NULL argument).1025*1026*----------------------------------------------------------------------1027*/10281029/* VARARGS2 */1030void1031Tcl_AppendResult TCL_VARARGS_DEF(Tcl_Interp *,arg1)1032{1033va_list argList;1034register Interp *iPtr;1035char *string;1036int newSpace;10371038/*1039* First, scan through all the arguments to see how much space is1040* needed.1041*/10421043iPtr = (Interp *) TCL_VARARGS_START(Tcl_Interp *,arg1,argList);1044newSpace = 0;1045while (1) {1046string = va_arg(argList, char *);1047if (string == NULL) {1048break;1049}1050newSpace += strlen(string);1051}1052va_end(argList);10531054/*1055* If the append buffer isn't already setup and large enough1056* to hold the new data, set it up.1057*/10581059if ((iPtr->result != iPtr->appendResult)1060|| (iPtr->appendResult[iPtr->appendUsed] != 0)1061|| ((newSpace + iPtr->appendUsed) >= iPtr->appendAvl)) {1062SetupAppendBuffer(iPtr, newSpace);1063}10641065/*1066* Final step: go through all the argument strings again, copying1067* them into the buffer.1068*/10691070TCL_VARARGS_START(Tcl_Interp *,arg1,argList);1071while (1) {1072string = va_arg(argList, char *);1073if (string == NULL) {1074break;1075}1076strcpy(iPtr->appendResult + iPtr->appendUsed, string);1077iPtr->appendUsed += strlen(string);1078}1079va_end(argList);1080}10811082/*1083*----------------------------------------------------------------------1084*1085* Tcl_AppendElement --1086*1087* Convert a string to a valid Tcl list element and append it1088* to the current result (which is ostensibly a list).1089*1090* Results:1091* None.1092*1093* Side effects:1094* The result in the interpreter given by the first argument1095* is extended with a list element converted from string. A1096* separator space is added before the converted list element1097* unless the current result is empty, contains the single1098* character "{", or ends in " {".1099*1100*----------------------------------------------------------------------1101*/11021103void1104Tcl_AppendElement(interp, string)1105Tcl_Interp *interp; /* Interpreter whose result is to be1106* extended. */1107char *string; /* String to convert to list element and1108* add to result. */1109{1110register Interp *iPtr = (Interp *) interp;1111int size, flags;1112char *dst;11131114/*1115* See how much space is needed, and grow the append buffer if1116* needed to accommodate the list element.1117*/11181119size = Tcl_ScanElement(string, &flags) + 1;1120if ((iPtr->result != iPtr->appendResult)1121|| (iPtr->appendResult[iPtr->appendUsed] != 0)1122|| ((size + iPtr->appendUsed) >= iPtr->appendAvl)) {1123SetupAppendBuffer(iPtr, size+iPtr->appendUsed);1124}11251126/*1127* Convert the string into a list element and copy it to the1128* buffer that's forming, with a space separator if needed.1129*/11301131dst = iPtr->appendResult + iPtr->appendUsed;1132if (TclNeedSpace(iPtr->appendResult, dst)) {1133iPtr->appendUsed++;1134*dst = ' ';1135dst++;1136}1137iPtr->appendUsed += Tcl_ConvertElement(string, dst, flags);1138}11391140/*1141*----------------------------------------------------------------------1142*1143* SetupAppendBuffer --1144*1145* This procedure makes sure that there is an append buffer1146* properly initialized for interp, and that it has at least1147* enough room to accommodate newSpace new bytes of information.1148*1149* Results:1150* None.1151*1152* Side effects:1153* None.1154*1155*----------------------------------------------------------------------1156*/11571158static void1159SetupAppendBuffer(iPtr, newSpace)1160register Interp *iPtr; /* Interpreter whose result is being set up. */1161int newSpace; /* Make sure that at least this many bytes1162* of new information may be added. */1163{1164int totalSpace;11651166/*1167* Make the append buffer larger, if that's necessary, then1168* copy the current result into the append buffer and make the1169* append buffer the official Tcl result.1170*/11711172if (iPtr->result != iPtr->appendResult) {1173/*1174* If an oversized buffer was used recently, then free it up1175* so we go back to a smaller buffer. This avoids tying up1176* memory forever after a large operation.1177*/11781179if (iPtr->appendAvl > 500) {1180ckfree(iPtr->appendResult);1181iPtr->appendResult = NULL;1182iPtr->appendAvl = 0;1183}1184iPtr->appendUsed = strlen(iPtr->result);1185} else if (iPtr->result[iPtr->appendUsed] != 0) {1186/*1187* Most likely someone has modified a result created by1188* Tcl_AppendResult et al. so that it has a different size.1189* Just recompute the size.1190*/11911192iPtr->appendUsed = strlen(iPtr->result);1193}1194totalSpace = newSpace + iPtr->appendUsed;1195if (totalSpace >= iPtr->appendAvl) {1196char *new;11971198if (totalSpace < 100) {1199totalSpace = 200;1200} else {1201totalSpace *= 2;1202}1203new = (char *) ckalloc((unsigned) totalSpace);1204strcpy(new, iPtr->result);1205if (iPtr->appendResult != NULL) {1206ckfree(iPtr->appendResult);1207}1208iPtr->appendResult = new;1209iPtr->appendAvl = totalSpace;1210} else if (iPtr->result != iPtr->appendResult) {1211strcpy(iPtr->appendResult, iPtr->result);1212}1213Tcl_FreeResult(iPtr);1214iPtr->result = iPtr->appendResult;1215}12161217/*1218*----------------------------------------------------------------------1219*1220* Tcl_ResetResult --1221*1222* This procedure restores the result area for an interpreter1223* to its default initialized state, freeing up any memory that1224* may have been allocated for the result and clearing any1225* error information for the interpreter.1226*1227* Results:1228* None.1229*1230* Side effects:1231* None.1232*1233*----------------------------------------------------------------------1234*/12351236void1237Tcl_ResetResult(interp)1238Tcl_Interp *interp; /* Interpreter for which to clear result. */1239{1240register Interp *iPtr = (Interp *) interp;12411242Tcl_FreeResult(iPtr);1243iPtr->result = iPtr->resultSpace;1244iPtr->resultSpace[0] = 0;1245iPtr->flags &=1246~(ERR_ALREADY_LOGGED | ERR_IN_PROGRESS | ERROR_CODE_SET);1247}12481249/*1250*----------------------------------------------------------------------1251*1252* Tcl_SetErrorCode --1253*1254* This procedure is called to record machine-readable information1255* about an error that is about to be returned.1256*1257* Results:1258* None.1259*1260* Side effects:1261* The errorCode global variable is modified to hold all of the1262* arguments to this procedure, in a list form with each argument1263* becoming one element of the list. A flag is set internally1264* to remember that errorCode has been set, so the variable doesn't1265* get set automatically when the error is returned.1266*1267*----------------------------------------------------------------------1268*/1269/* VARARGS2 */1270void1271Tcl_SetErrorCode TCL_VARARGS_DEF(Tcl_Interp *,arg1)1272{1273va_list argList;1274char *string;1275int flags;1276Interp *iPtr;12771278/*1279* Scan through the arguments one at a time, appending them to1280* $errorCode as list elements.1281*/12821283iPtr = (Interp *) TCL_VARARGS_START(Tcl_Interp *,arg1,argList);1284flags = TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT;1285while (1) {1286string = va_arg(argList, char *);1287if (string == NULL) {1288break;1289}1290(void) Tcl_SetVar2((Tcl_Interp *) iPtr, "errorCode",1291(char *) NULL, string, flags);1292flags |= TCL_APPEND_VALUE;1293}1294va_end(argList);1295iPtr->flags |= ERROR_CODE_SET;1296}12971298/*1299*----------------------------------------------------------------------1300*1301* TclGetListIndex --1302*1303* Parse a list index, which may be either an integer or the1304* value "end".1305*1306* Results:1307* The return value is either TCL_OK or TCL_ERROR. If it is1308* TCL_OK, then the index corresponding to string is left in1309* *indexPtr. If the return value is TCL_ERROR, then string1310* was bogus; an error message is returned in interp->result.1311* If a negative index is specified, it is rounded up to 0.1312* The index value may be larger than the size of the list1313* (this happens when "end" is specified).1314*1315* Side effects:1316* None.1317*1318*----------------------------------------------------------------------1319*/13201321int1322TclGetListIndex(interp, string, indexPtr)1323Tcl_Interp *interp; /* Interpreter for error reporting. */1324char *string; /* String containing list index. */1325int *indexPtr; /* Where to store index. */1326{1327if (isdigit(UCHAR(*string)) || (*string == '-')) {1328if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {1329return TCL_ERROR;1330}1331if (*indexPtr < 0) {1332*indexPtr = 0;1333}1334} else if (strncmp(string, "end", strlen(string)) == 0) {1335*indexPtr = INT_MAX;1336} else {1337Tcl_AppendResult(interp, "bad index \"", string,1338"\": must be integer or \"end\"", (char *) NULL);1339return TCL_ERROR;1340}1341return TCL_OK;1342}13431344/*1345*----------------------------------------------------------------------1346*1347* Tcl_RegExpCompile --1348*1349* Compile a regular expression into a form suitable for fast1350* matching. This procedure retains a small cache of pre-compiled1351* regular expressions in the interpreter, in order to avoid1352* compilation costs as much as possible.1353*1354* Results:1355* The return value is a pointer to the compiled form of string,1356* suitable for passing to Tcl_RegExpExec. This compiled form1357* is only valid up until the next call to this procedure, so1358* don't keep these around for a long time! If an error occurred1359* while compiling the pattern, then NULL is returned and an error1360* message is left in interp->result.1361*1362* Side effects:1363* The cache of compiled regexp's in interp will be modified to1364* hold information for string, if such information isn't already1365* present in the cache.1366*1367*----------------------------------------------------------------------1368*/13691370Tcl_RegExp1371Tcl_RegExpCompile(interp, string)1372Tcl_Interp *interp; /* For use in error reporting. */1373char *string; /* String for which to produce1374* compiled regular expression. */1375{1376register Interp *iPtr = (Interp *) interp;1377int i, length;1378regexp *result;13791380length = strlen(string);1381for (i = 0; i < NUM_REGEXPS; i++) {1382if ((length == iPtr->patLengths[i])1383&& (strcmp(string, iPtr->patterns[i]) == 0)) {1384/*1385* Move the matched pattern to the first slot in the1386* cache and shift the other patterns down one position.1387*/13881389if (i != 0) {1390int j;1391char *cachedString;13921393cachedString = iPtr->patterns[i];1394result = iPtr->regexps[i];1395for (j = i-1; j >= 0; j--) {1396iPtr->patterns[j+1] = iPtr->patterns[j];1397iPtr->patLengths[j+1] = iPtr->patLengths[j];1398iPtr->regexps[j+1] = iPtr->regexps[j];1399}1400iPtr->patterns[0] = cachedString;1401iPtr->patLengths[0] = length;1402iPtr->regexps[0] = result;1403}1404return (Tcl_RegExp) iPtr->regexps[0];1405}1406}14071408/*1409* No match in the cache. Compile the string and add it to the1410* cache.1411*/14121413TclRegError((char *) NULL);1414result = TclRegComp(string);1415if (TclGetRegError() != NULL) {1416Tcl_AppendResult(interp,1417"couldn't compile regular expression pattern: ",1418TclGetRegError(), (char *) NULL);1419return NULL;1420}1421if (iPtr->patterns[NUM_REGEXPS-1] != NULL) {1422ckfree(iPtr->patterns[NUM_REGEXPS-1]);1423ckfree((char *) iPtr->regexps[NUM_REGEXPS-1]);1424}1425for (i = NUM_REGEXPS - 2; i >= 0; i--) {1426iPtr->patterns[i+1] = iPtr->patterns[i];1427iPtr->patLengths[i+1] = iPtr->patLengths[i];1428iPtr->regexps[i+1] = iPtr->regexps[i];1429}1430iPtr->patterns[0] = (char *) ckalloc((unsigned) (length+1));1431strcpy(iPtr->patterns[0], string);1432iPtr->patLengths[0] = length;1433iPtr->regexps[0] = result;1434return (Tcl_RegExp) result;1435}14361437/*1438*----------------------------------------------------------------------1439*1440* Tcl_RegExpExec --1441*1442* Execute the regular expression matcher using a compiled form1443* of a regular expression and save information about any match1444* that is found.1445*1446* Results:1447* If an error occurs during the matching operation then -11448* is returned and interp->result contains an error message.1449* Otherwise the return value is 1 if a matching range is1450* found and 0 if there is no matching range.1451*1452* Side effects:1453* None.1454*1455*----------------------------------------------------------------------1456*/14571458int1459Tcl_RegExpExec(interp, re, string, start)1460Tcl_Interp *interp; /* Interpreter to use for error reporting. */1461Tcl_RegExp re; /* Compiled regular expression; must have1462* been returned by previous call to1463* Tcl_RegExpCompile. */1464char *string; /* String against which to match re. */1465char *start; /* If string is part of a larger string,1466* this identifies beginning of larger1467* string, so that "^" won't match. */1468{1469int match;14701471regexp *regexpPtr = (regexp *) re;1472TclRegError((char *) NULL);1473match = TclRegExec(regexpPtr, string, start);1474if (TclGetRegError() != NULL) {1475Tcl_ResetResult(interp);1476Tcl_AppendResult(interp, "error while matching regular expression: ",1477TclGetRegError(), (char *) NULL);1478return -1;1479}1480return match;1481}14821483/*1484*----------------------------------------------------------------------1485*1486* Tcl_RegExpRange --1487*1488* Returns pointers describing the range of a regular expression match,1489* or one of the subranges within the match.1490*1491* Results:1492* The variables at *startPtr and *endPtr are modified to hold the1493* addresses of the endpoints of the range given by index. If the1494* specified range doesn't exist then NULLs are returned.1495*1496* Side effects:1497* None.1498*1499*----------------------------------------------------------------------1500*/15011502void1503Tcl_RegExpRange(re, index, startPtr, endPtr)1504Tcl_RegExp re; /* Compiled regular expression that has1505* been passed to Tcl_RegExpExec. */1506int index; /* 0 means give the range of the entire1507* match, > 0 means give the range of1508* a matching subrange. Must be no greater1509* than NSUBEXP. */1510char **startPtr; /* Store address of first character in1511* (sub-) range here. */1512char **endPtr; /* Store address of character just after last1513* in (sub-) range here. */1514{1515regexp *regexpPtr = (regexp *) re;15161517if (index >= NSUBEXP) {1518*startPtr = *endPtr = NULL;1519} else {1520*startPtr = regexpPtr->startp[index];1521*endPtr = regexpPtr->endp[index];1522}1523}15241525/*1526*----------------------------------------------------------------------1527*1528* Tcl_RegExpMatch --1529*1530* See if a string matches a regular expression.1531*1532* Results:1533* If an error occurs during the matching operation then -11534* is returned and interp->result contains an error message.1535* Otherwise the return value is 1 if "string" matches "pattern"1536* and 0 otherwise.1537*1538* Side effects:1539* None.1540*1541*----------------------------------------------------------------------1542*/15431544int1545Tcl_RegExpMatch(interp, string, pattern)1546Tcl_Interp *interp; /* Used for error reporting. */1547char *string; /* String. */1548char *pattern; /* Regular expression to match against1549* string. */1550{1551Tcl_RegExp re;15521553re = Tcl_RegExpCompile(interp, pattern);1554if (re == NULL) {1555return -1;1556}1557return Tcl_RegExpExec(interp, re, string, string);1558}15591560/*1561*----------------------------------------------------------------------1562*1563* Tcl_DStringInit --1564*1565* Initializes a dynamic string, discarding any previous contents1566* of the string (Tcl_DStringFree should have been called already1567* if the dynamic string was previously in use).1568*1569* Results:1570* None.1571*1572* Side effects:1573* The dynamic string is initialized to be empty.1574*1575*----------------------------------------------------------------------1576*/15771578void1579Tcl_DStringInit(dsPtr)1580register Tcl_DString *dsPtr; /* Pointer to structure for1581* dynamic string. */1582{1583dsPtr->string = dsPtr->staticSpace;1584dsPtr->length = 0;1585dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;1586dsPtr->staticSpace[0] = 0;1587}15881589/*1590*----------------------------------------------------------------------1591*1592* Tcl_DStringAppend --1593*1594* Append more characters to the current value of a dynamic string.1595*1596* Results:1597* The return value is a pointer to the dynamic string's new value.1598*1599* Side effects:1600* Length bytes from string (or all of string if length is less1601* than zero) are added to the current value of the string. Memory1602* gets reallocated if needed to accomodate the string's new size.1603*1604*----------------------------------------------------------------------1605*/16061607char *1608Tcl_DStringAppend(dsPtr, string, length)1609register Tcl_DString *dsPtr; /* Structure describing dynamic1610* string. */1611char *string; /* String to append. If length is1612* -1 then this must be1613* null-terminated. */1614int length; /* Number of characters from string1615* to append. If < 0, then append all1616* of string, up to null at end. */1617{1618int newSize;1619char *newString, *dst, *end;16201621if (length < 0) {1622length = strlen(string);1623}1624newSize = length + dsPtr->length;16251626/*1627* Allocate a larger buffer for the string if the current one isn't1628* large enough. Allocate extra space in the new buffer so that there1629* will be room to grow before we have to allocate again.1630*/16311632if (newSize >= dsPtr->spaceAvl) {1633dsPtr->spaceAvl = newSize*2;1634newString = (char *) ckalloc((unsigned) dsPtr->spaceAvl);1635memcpy((VOID *)newString, (VOID *) dsPtr->string,1636(size_t) dsPtr->length);1637if (dsPtr->string != dsPtr->staticSpace) {1638ckfree(dsPtr->string);1639}1640dsPtr->string = newString;1641}16421643/*1644* Copy the new string into the buffer at the end of the old1645* one.1646*/16471648for (dst = dsPtr->string + dsPtr->length, end = string+length;1649string < end; string++, dst++) {1650*dst = *string;1651}1652*dst = 0;1653dsPtr->length += length;1654return dsPtr->string;1655}16561657/*1658*----------------------------------------------------------------------1659*1660* Tcl_DStringAppendElement --1661*1662* Append a list element to the current value of a dynamic string.1663*1664* Results:1665* The return value is a pointer to the dynamic string's new value.1666*1667* Side effects:1668* String is reformatted as a list element and added to the current1669* value of the string. Memory gets reallocated if needed to1670* accomodate the string's new size.1671*1672*----------------------------------------------------------------------1673*/16741675char *1676Tcl_DStringAppendElement(dsPtr, string)1677register Tcl_DString *dsPtr; /* Structure describing dynamic1678* string. */1679char *string; /* String to append. Must be1680* null-terminated. */1681{1682int newSize, flags;1683char *dst, *newString;16841685newSize = Tcl_ScanElement(string, &flags) + dsPtr->length + 1;16861687/*1688* Allocate a larger buffer for the string if the current one isn't1689* large enough. Allocate extra space in the new buffer so that there1690* will be room to grow before we have to allocate again.1691* SPECIAL NOTE: must use memcpy, not strcpy, to copy the string1692* to a larger buffer, since there may be embedded NULLs in the1693* string in some cases.1694*/16951696if (newSize >= dsPtr->spaceAvl) {1697dsPtr->spaceAvl = newSize*2;1698newString = (char *) ckalloc((unsigned) dsPtr->spaceAvl);1699memcpy((VOID *) newString, (VOID *) dsPtr->string,1700(size_t) dsPtr->length);1701if (dsPtr->string != dsPtr->staticSpace) {1702ckfree(dsPtr->string);1703}1704dsPtr->string = newString;1705}17061707/*1708* Convert the new string to a list element and copy it into the1709* buffer at the end, with a space, if needed.1710*/17111712dst = dsPtr->string + dsPtr->length;1713if (TclNeedSpace(dsPtr->string, dst)) {1714*dst = ' ';1715dst++;1716dsPtr->length++;1717}1718dsPtr->length += Tcl_ConvertElement(string, dst, flags);1719return dsPtr->string;1720}17211722/*1723*----------------------------------------------------------------------1724*1725* Tcl_DStringSetLength --1726*1727* Change the length of a dynamic string. This can cause the1728* string to either grow or shrink, depending on the value of1729* length.1730*1731* Results:1732* None.1733*1734* Side effects:1735* The length of dsPtr is changed to length and a null byte is1736* stored at that position in the string. If length is larger1737* than the space allocated for dsPtr, then a panic occurs.1738*1739*----------------------------------------------------------------------1740*/17411742void1743Tcl_DStringSetLength(dsPtr, length)1744register Tcl_DString *dsPtr; /* Structure describing dynamic1745* string. */1746int length; /* New length for dynamic string. */1747{1748if (length < 0) {1749length = 0;1750}1751if (length >= dsPtr->spaceAvl) {1752char *newString;17531754dsPtr->spaceAvl = length+1;1755newString = (char *) ckalloc((unsigned) dsPtr->spaceAvl);17561757/*1758* SPECIAL NOTE: must use memcpy, not strcpy, to copy the string1759* to a larger buffer, since there may be embedded NULLs in the1760* string in some cases.1761*/17621763memcpy((VOID *) newString, (VOID *) dsPtr->string,1764(size_t) dsPtr->length);1765if (dsPtr->string != dsPtr->staticSpace) {1766ckfree(dsPtr->string);1767}1768dsPtr->string = newString;1769}1770dsPtr->length = length;1771dsPtr->string[length] = 0;1772}17731774/*1775*----------------------------------------------------------------------1776*1777* Tcl_DStringFree --1778*1779* Frees up any memory allocated for the dynamic string and1780* reinitializes the string to an empty state.1781*1782* Results:1783* None.1784*1785* Side effects:1786* The previous contents of the dynamic string are lost, and1787* the new value is an empty string.1788*1789*----------------------------------------------------------------------1790*/17911792void1793Tcl_DStringFree(dsPtr)1794register Tcl_DString *dsPtr; /* Structure describing dynamic1795* string. */1796{1797if (dsPtr->string != dsPtr->staticSpace) {1798ckfree(dsPtr->string);1799}1800dsPtr->string = dsPtr->staticSpace;1801dsPtr->length = 0;1802dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;1803dsPtr->staticSpace[0] = 0;1804}18051806/*1807*----------------------------------------------------------------------1808*1809* Tcl_DStringResult --1810*1811* This procedure moves the value of a dynamic string into an1812* interpreter as its result. The string itself is reinitialized1813* to an empty string.1814*1815* Results:1816* None.1817*1818* Side effects:1819* The string is "moved" to interp's result, and any existing1820* result for interp is freed up. DsPtr is reinitialized to1821* an empty string.1822*1823*----------------------------------------------------------------------1824*/18251826void1827Tcl_DStringResult(interp, dsPtr)1828Tcl_Interp *interp; /* Interpreter whose result is to be1829* reset. */1830Tcl_DString *dsPtr; /* Dynamic string that is to become1831* the result of interp. */1832{1833Tcl_ResetResult(interp);1834if (dsPtr->string != dsPtr->staticSpace) {1835interp->result = dsPtr->string;1836interp->freeProc = TCL_DYNAMIC;1837} else if (dsPtr->length < TCL_RESULT_SIZE) {1838interp->result = ((Interp *) interp)->resultSpace;1839strcpy(interp->result, dsPtr->string);1840} else {1841Tcl_SetResult(interp, dsPtr->string, TCL_VOLATILE);1842}1843dsPtr->string = dsPtr->staticSpace;1844dsPtr->length = 0;1845dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;1846dsPtr->staticSpace[0] = 0;1847}18481849/*1850*----------------------------------------------------------------------1851*1852* Tcl_DStringGetResult --1853*1854* This procedure moves the result of an interpreter into a1855* dynamic string.1856*1857* Results:1858* None.1859*1860* Side effects:1861* The interpreter's result is cleared, and the previous contents1862* of dsPtr are freed.1863*1864*----------------------------------------------------------------------1865*/18661867void1868Tcl_DStringGetResult(interp, dsPtr)1869Tcl_Interp *interp; /* Interpreter whose result is to be1870* reset. */1871Tcl_DString *dsPtr; /* Dynamic string that is to become1872* the result of interp. */1873{1874Interp *iPtr = (Interp *) interp;1875if (dsPtr->string != dsPtr->staticSpace) {1876ckfree(dsPtr->string);1877}1878dsPtr->length = strlen(iPtr->result);1879if (iPtr->freeProc != NULL) {1880if ((iPtr->freeProc == TCL_DYNAMIC)1881|| (iPtr->freeProc == (Tcl_FreeProc *) free)) {1882dsPtr->string = iPtr->result;1883dsPtr->spaceAvl = dsPtr->length+1;1884} else {1885dsPtr->string = (char *) ckalloc((unsigned) (dsPtr->length+1));1886strcpy(dsPtr->string, iPtr->result);1887(*iPtr->freeProc)(iPtr->result);1888}1889dsPtr->spaceAvl = dsPtr->length+1;1890iPtr->freeProc = NULL;1891} else {1892if (dsPtr->length < TCL_DSTRING_STATIC_SIZE) {1893dsPtr->string = dsPtr->staticSpace;1894dsPtr->spaceAvl = TCL_DSTRING_STATIC_SIZE;1895} else {1896dsPtr->string = (char *) ckalloc((unsigned) (dsPtr->length + 1));1897dsPtr->spaceAvl = dsPtr->length + 1;1898}1899strcpy(dsPtr->string, iPtr->result);1900}1901iPtr->result = iPtr->resultSpace;1902iPtr->resultSpace[0] = 0;1903}19041905/*1906*----------------------------------------------------------------------1907*1908* Tcl_DStringStartSublist --1909*1910* This procedure adds the necessary information to a dynamic1911* string (e.g. " {" to start a sublist. Future element1912* appends will be in the sublist rather than the main list.1913*1914* Results:1915* None.1916*1917* Side effects:1918* Characters get added to the dynamic string.1919*1920*----------------------------------------------------------------------1921*/19221923void1924Tcl_DStringStartSublist(dsPtr)1925Tcl_DString *dsPtr; /* Dynamic string. */1926{1927if (TclNeedSpace(dsPtr->string, dsPtr->string + dsPtr->length)) {1928Tcl_DStringAppend(dsPtr, " {", -1);1929} else {1930Tcl_DStringAppend(dsPtr, "{", -1);1931}1932}19331934/*1935*----------------------------------------------------------------------1936*1937* Tcl_DStringEndSublist --1938*1939* This procedure adds the necessary characters to a dynamic1940* string to end a sublist (e.g. "}"). Future element appends1941* will be in the enclosing (sub)list rather than the current1942* sublist.1943*1944* Results:1945* None.1946*1947* Side effects:1948* None.1949*1950*----------------------------------------------------------------------1951*/19521953void1954Tcl_DStringEndSublist(dsPtr)1955Tcl_DString *dsPtr; /* Dynamic string. */1956{1957Tcl_DStringAppend(dsPtr, "}", -1);1958}19591960/*1961*----------------------------------------------------------------------1962*1963* Tcl_PrintDouble --1964*1965* Given a floating-point value, this procedure converts it to1966* an ASCII string using.1967*1968* Results:1969* The ASCII equivalent of "value" is written at "dst". It is1970* written using the current precision, and it is guaranteed to1971* contain a decimal point or exponent, so that it looks like1972* a floating-point value and not an integer.1973*1974* Side effects:1975* None.1976*1977*----------------------------------------------------------------------1978*/19791980void1981Tcl_PrintDouble(interp, value, dst)1982Tcl_Interp *interp; /* Interpreter whose tcl_precision1983* variable controls printing. */1984double value; /* Value to print as string. */1985char *dst; /* Where to store converted value;1986* must have at least TCL_DOUBLE_SPACE1987* characters. */1988{1989register char *p;1990sprintf(dst, ((Interp *) interp)->pdFormat, value);19911992/*1993* If the ASCII result looks like an integer, add ".0" so that it1994* doesn't look like an integer anymore. This prevents floating-point1995* values from being converted to integers unintentionally.1996*/19971998for (p = dst; *p != 0; p++) {1999if ((*p == '.') || (isalpha(UCHAR(*p)))) {2000return;2001}2002}2003p[0] = '.';2004p[1] = '0';2005p[2] = 0;2006}20072008/*2009*----------------------------------------------------------------------2010*2011* TclPrecTraceProc --2012*2013* This procedure is invoked whenever the variable "tcl_precision"2014* is written.2015*2016* Results:2017* Returns NULL if all went well, or an error message if the2018* new value for the variable doesn't make sense.2019*2020* Side effects:2021* If the new value doesn't make sense then this procedure2022* undoes the effect of the variable modification. Otherwise2023* it modifies the format string that's used by Tcl_PrintDouble.2024*2025*----------------------------------------------------------------------2026*/20272028/* ARGSUSED */2029char *2030TclPrecTraceProc(clientData, interp, name1, name2, flags)2031ClientData clientData; /* Not used. */2032Tcl_Interp *interp; /* Interpreter containing variable. */2033char *name1; /* Name of variable. */2034char *name2; /* Second part of variable name. */2035int flags; /* Information about what happened. */2036{2037register Interp *iPtr = (Interp *) interp;2038char *value, *end;2039int prec;20402041/*2042* If the variable is unset, then recreate the trace and restore2043* the default value of the format string.2044*/20452046if (flags & TCL_TRACE_UNSETS) {2047if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {2048Tcl_TraceVar2(interp, name1, name2,2049TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,2050TclPrecTraceProc, clientData);2051}2052strcpy(iPtr->pdFormat, DEFAULT_PD_FORMAT);2053iPtr->pdPrec = DEFAULT_PD_PREC;2054return (char *) NULL;2055}20562057value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);2058if (value == NULL) {2059value = "";2060}2061prec = strtoul(value, &end, 10);2062if ((prec <= 0) || (prec > TCL_MAX_PREC) || (prec > 100) ||2063(end == value) || (*end != 0)) {2064char oldValue[10];20652066sprintf(oldValue, "%d", iPtr->pdPrec);2067Tcl_SetVar2(interp, name1, name2, oldValue, flags & TCL_GLOBAL_ONLY);2068return "improper value for precision";2069}2070sprintf(iPtr->pdFormat, "%%.%dg", prec);2071iPtr->pdPrec = prec;2072return (char *) NULL;2073}20742075/*2076*----------------------------------------------------------------------2077*2078* TclNeedSpace --2079*2080* This procedure checks to see whether it is appropriate to2081* add a space before appending a new list element to an2082* existing string.2083*2084* Results:2085* The return value is 1 if a space is appropriate, 0 otherwise.2086*2087* Side effects:2088* None.2089*2090*----------------------------------------------------------------------2091*/20922093int2094TclNeedSpace(start, end)2095char *start; /* First character in string. */2096char *end; /* End of string (place where space will2097* be added, if appropriate). */2098{2099/*2100* A space is needed unless either2101* (a) we're at the start of the string, or2102* (b) the trailing characters of the string consist of one or more2103* open curly braces preceded by a space or extending back to2104* the beginning of the string.2105* (c) the trailing characters of the string consist of a space2106* preceded by a character other than backslash.2107*/21082109if (end == start) {2110return 0;2111}2112end--;2113if (*end != '{') {2114if (isspace(UCHAR(*end)) && ((end == start) || (end[-1] != '\\'))) {2115return 0;2116}2117return 1;2118}2119do {2120if (end == start) {2121return 0;2122}2123end--;2124} while (*end == '{');2125if (isspace(UCHAR(*end))) {2126return 0;2127}2128return 1;2129}213021312132