summaryrefslogtreecommitdiff
path: root/tcl/generic
diff options
context:
space:
mode:
authorKeith Seitz <keiths@redhat.com>2002-09-24 19:55:43 +0000
committerKeith Seitz <keiths@redhat.com>2002-09-24 19:55:43 +0000
commit0e8f9dd357b81ada6f8f4a215b928d63ca983f97 (patch)
tree7474a17bfcb82d128f44269ac686c462e2fc191e /tcl/generic
parente18731d328254b7e926369741b282fbffc840ea5 (diff)
downloadgdb-0e8f9dd357b81ada6f8f4a215b928d63ca983f97.tar.gz
import tcl 8.4.0
Diffstat (limited to 'tcl/generic')
-rw-r--r--tcl/generic/regc_cvec.c186
-rw-r--r--tcl/generic/regc_locale.c1119
-rw-r--r--tcl/generic/tcl.decls886
-rw-r--r--tcl/generic/tcl.h1065
-rw-r--r--tcl/generic/tclAlloc.c17
-rw-r--r--tcl/generic/tclAsync.c155
-rw-r--r--tcl/generic/tclBasic.c2206
-rw-r--r--tcl/generic/tclBinary.c229
-rw-r--r--tcl/generic/tclCkalloc.c316
-rw-r--r--tcl/generic/tclClock.c25
-rw-r--r--tcl/generic/tclCmdAH.c808
-rw-r--r--tcl/generic/tclCmdIL.c1108
-rw-r--r--tcl/generic/tclCmdMZ.c3051
-rw-r--r--tcl/generic/tclCompCmds.c2544
-rw-r--r--tcl/generic/tclCompExpr.c185
-rw-r--r--tcl/generic/tclCompile.c529
-rw-r--r--tcl/generic/tclCompile.h220
-rw-r--r--tcl/generic/tclDate.c20
-rw-r--r--tcl/generic/tclDecls.h1384
-rw-r--r--tcl/generic/tclEncoding.c164
-rw-r--r--tcl/generic/tclEnv.c163
-rw-r--r--tcl/generic/tclEvent.c83
-rw-r--r--tcl/generic/tclExecute.c6540
-rw-r--r--tcl/generic/tclFCmd.c613
-rw-r--r--tcl/generic/tclFileName.c1666
-rw-r--r--tcl/generic/tclGet.c15
-rw-r--r--tcl/generic/tclGetDate.y20
-rw-r--r--tcl/generic/tclHash.c993
-rw-r--r--tcl/generic/tclHistory.c2
-rw-r--r--tcl/generic/tclIO.c1806
-rw-r--r--tcl/generic/tclIO.h19
-rw-r--r--tcl/generic/tclIOCmd.c125
-rw-r--r--tcl/generic/tclIOGT.c162
-rw-r--r--tcl/generic/tclIOSock.c8
-rw-r--r--tcl/generic/tclIOUtil.c4650
-rw-r--r--tcl/generic/tclIndexObj.c245
-rw-r--r--tcl/generic/tclInitScript.h7
-rw-r--r--tcl/generic/tclInt.decls502
-rw-r--r--tcl/generic/tclInt.h740
-rw-r--r--tcl/generic/tclIntDecls.h537
-rw-r--r--tcl/generic/tclIntPlatDecls.h127
-rw-r--r--tcl/generic/tclInterp.c312
-rw-r--r--tcl/generic/tclLink.c252
-rw-r--r--tcl/generic/tclListObj.c639
-rw-r--r--tcl/generic/tclLiteral.c54
-rw-r--r--tcl/generic/tclLoad.c61
-rw-r--r--tcl/generic/tclLoadNone.c57
-rw-r--r--tcl/generic/tclMain.c548
-rw-r--r--tcl/generic/tclNamesp.c186
-rw-r--r--tcl/generic/tclNotify.c24
-rw-r--r--tcl/generic/tclObj.c1418
-rw-r--r--tcl/generic/tclPanic.c31
-rw-r--r--tcl/generic/tclParse.c1844
-rw-r--r--tcl/generic/tclParseExpr.c649
-rw-r--r--tcl/generic/tclPipe.c31
-rw-r--r--tcl/generic/tclPkg.c139
-rw-r--r--tcl/generic/tclPlatDecls.h44
-rw-r--r--tcl/generic/tclPort.h20
-rw-r--r--tcl/generic/tclPosixStr.c15
-rw-r--r--tcl/generic/tclProc.c234
-rw-r--r--tcl/generic/tclRegexp.c23
-rw-r--r--tcl/generic/tclResolve.c12
-rw-r--r--tcl/generic/tclResult.c2
-rw-r--r--tcl/generic/tclScan.c144
-rw-r--r--tcl/generic/tclStringObj.c343
-rw-r--r--tcl/generic/tclStubInit.c185
-rw-r--r--tcl/generic/tclStubLib.c6
-rw-r--r--tcl/generic/tclTest.c1655
-rw-r--r--tcl/generic/tclTestObj.c48
-rw-r--r--tcl/generic/tclThread.c1
-rw-r--r--tcl/generic/tclThreadAlloc.c955
-rw-r--r--tcl/generic/tclThreadJoin.c311
-rw-r--r--tcl/generic/tclThreadTest.c114
-rw-r--r--tcl/generic/tclTimer.c26
-rw-r--r--tcl/generic/tclUniData.c1070
-rw-r--r--tcl/generic/tclUtf.c444
-rw-r--r--tcl/generic/tclUtil.c644
-rw-r--r--tcl/generic/tclVar.c3482
78 files changed, 36072 insertions, 15191 deletions
diff --git a/tcl/generic/regc_cvec.c b/tcl/generic/regc_cvec.c
index 86765ea1f73..d2d56fc70a2 100644
--- a/tcl/generic/regc_cvec.c
+++ b/tcl/generic/regc_cvec.c
@@ -36,26 +36,27 @@
*/
static struct cvec *
newcvec(nchrs, nranges, nmcces)
-int nchrs; /* to hold this many chrs... */
-int nranges; /* ... and this many ranges... */
-int nmcces; /* ... and this many MCCEs */
+ int nchrs; /* to hold this many chrs... */
+ int nranges; /* ... and this many ranges... */
+ int nmcces; /* ... and this many MCCEs */
{
- size_t n;
- size_t nc;
- struct cvec *cv;
+ size_t n;
+ size_t nc;
+ struct cvec *cv;
- nc = (size_t)nchrs + (size_t)nmcces*(MAXMCCE+1) + (size_t)nranges*2;
- n = sizeof(struct cvec) + (size_t)(nmcces-1)*sizeof(chr *) +
- nc*sizeof(chr);
- cv = (struct cvec *)MALLOC(n);
- if (cv == NULL)
- return NULL;
- cv->chrspace = nc;
- cv->chrs = (chr *)&cv->mcces[nmcces]; /* chrs just after MCCE ptrs */
- cv->mccespace = nmcces;
- cv->ranges = cv->chrs + nchrs + nmcces*(MAXMCCE+1);
- cv->rangespace = nranges;
- return clearcvec(cv);
+ nc = (size_t)nchrs + (size_t)nmcces*(MAXMCCE+1) + (size_t)nranges*2;
+ n = sizeof(struct cvec) + (size_t)(nmcces-1)*sizeof(chr *)
+ + nc*sizeof(chr);
+ cv = (struct cvec *)MALLOC(n);
+ if (cv == NULL) {
+ return NULL;
+ }
+ cv->chrspace = nchrs;
+ cv->chrs = (chr *)&cv->mcces[nmcces]; /* chrs just after MCCE ptrs */
+ cv->mccespace = nmcces;
+ cv->ranges = cv->chrs + nchrs + nmcces*(MAXMCCE+1);
+ cv->rangespace = nranges;
+ return clearcvec(cv);
}
/*
@@ -65,20 +66,21 @@ int nmcces; /* ... and this many MCCEs */
*/
static struct cvec *
clearcvec(cv)
-struct cvec *cv;
+ struct cvec *cv; /* character vector */
{
- int i;
+ int i;
- assert(cv != NULL);
- cv->nchrs = 0;
- assert(cv->chrs == (chr *)&cv->mcces[cv->mccespace]);
- cv->nmcces = 0;
- cv->nmccechrs = 0;
- cv->nranges = 0;
- for (i = 0; i < cv->mccespace; i++)
- cv->mcces[i] = NULL;
+ assert(cv != NULL);
+ cv->nchrs = 0;
+ assert(cv->chrs == (chr *)&cv->mcces[cv->mccespace]);
+ cv->nmcces = 0;
+ cv->nmccechrs = 0;
+ cv->nranges = 0;
+ for (i = 0; i < cv->mccespace; i++) {
+ cv->mcces[i] = NULL;
+ }
- return cv;
+ return cv;
}
/*
@@ -87,11 +89,11 @@ struct cvec *cv;
*/
static VOID
addchr(cv, c)
-struct cvec *cv;
-pchr c;
+ struct cvec *cv; /* character vector */
+ pchr c; /* character to add */
{
- assert(cv->nchrs < cv->chrspace - cv->nmccechrs);
- cv->chrs[cv->nchrs++] = (chr)c;
+ assert(cv->nchrs < cv->chrspace - cv->nmccechrs);
+ cv->chrs[cv->nchrs++] = (chr)c;
}
/*
@@ -100,14 +102,14 @@ pchr c;
*/
static VOID
addrange(cv, from, to)
-struct cvec *cv;
-pchr from;
-pchr to;
+ struct cvec *cv; /* character vector */
+ pchr from; /* first character of range */
+ pchr to; /* last character of range */
{
- assert(cv->nranges < cv->rangespace);
- cv->ranges[cv->nranges*2] = (chr)from;
- cv->ranges[cv->nranges*2 + 1] = (chr)to;
- cv->nranges++;
+ assert(cv->nranges < cv->rangespace);
+ cv->ranges[cv->nranges*2] = (chr)from;
+ cv->ranges[cv->nranges*2 + 1] = (chr)to;
+ cv->nranges++;
}
/*
@@ -116,49 +118,55 @@ pchr to;
*/
static VOID
addmcce(cv, startp, endp)
-struct cvec *cv;
-chr *startp; /* beginning of text */
-chr *endp; /* just past end of text */
+ struct cvec *cv; /* character vector */
+ chr *startp; /* beginning of text */
+ chr *endp; /* just past end of text */
{
- int len;
- int i;
- chr *s;
- chr *d;
+ int len;
+ int i;
+ chr *s;
+ chr *d;
- if (startp == NULL && endp == NULL)
- return;
- len = endp - startp;
- assert(len > 0);
- assert(cv->nchrs + len < cv->chrspace - cv->nmccechrs);
- assert(cv->nmcces < cv->mccespace);
- d = &cv->chrs[cv->chrspace - cv->nmccechrs - len - 1];
- cv->mcces[cv->nmcces++] = d;
- for (s = startp, i = len; i > 0; s++, i--)
- *d++ = *s;
- *d++ = 0; /* endmarker */
- assert(d == &cv->chrs[cv->chrspace - cv->nmccechrs]);
- cv->nmccechrs += len + 1;
+ if (startp == NULL && endp == NULL) {
+ return;
+ }
+ len = endp - startp;
+ assert(len > 0);
+ assert(cv->nchrs + len < cv->chrspace - cv->nmccechrs);
+ assert(cv->nmcces < cv->mccespace);
+ d = &cv->chrs[cv->chrspace - cv->nmccechrs - len - 1];
+ cv->mcces[cv->nmcces++] = d;
+ for (s = startp, i = len; i > 0; s++, i--) {
+ *d++ = *s;
+ }
+ *d++ = 0; /* endmarker */
+ assert(d == &cv->chrs[cv->chrspace - cv->nmccechrs]);
+ cv->nmccechrs += len + 1;
}
/*
- haschr - does a cvec contain this chr?
^ static int haschr(struct cvec *, pchr);
*/
-static int /* predicate */
+static int /* predicate */
haschr(cv, c)
-struct cvec *cv;
-pchr c;
+ struct cvec *cv; /* character vector */
+ pchr c; /* character to test for */
{
- int i;
- chr *p;
+ int i;
+ chr *p;
- for (p = cv->chrs, i = cv->nchrs; i > 0; p++, i--)
- if (*p == c)
- return 1;
- for (p = cv->ranges, i = cv->nranges; i > 0; p += 2, i--)
- if (*p <= c && c <= *(p+1))
- return 1;
- return 0;
+ for (p = cv->chrs, i = cv->nchrs; i > 0; p++, i--) {
+ if (*p == c) {
+ return 1;
+ }
+ }
+ for (p = cv->ranges, i = cv->nranges; i > 0; p += 2, i--) {
+ if ((*p <= c) && (c <= *(p+1))) {
+ return 1;
+ }
+ }
+ return 0;
}
/*
@@ -167,23 +175,25 @@ pchr c;
*/
static struct cvec *
getcvec(v, nchrs, nranges, nmcces)
-struct vars *v;
-int nchrs; /* to hold this many chrs... */
-int nranges; /* ... and this many ranges... */
-int nmcces; /* ... and this many MCCEs */
+ struct vars *v; /* context */
+ int nchrs; /* to hold this many chrs... */
+ int nranges; /* ... and this many ranges... */
+ int nmcces; /* ... and this many MCCEs */
{
- if (v->cv != NULL && nchrs <= v->cv->chrspace &&
- nranges <= v->cv->rangespace &&
- nmcces <= v->cv->mccespace)
- return clearcvec(v->cv);
+ if (v->cv != NULL && nchrs <= v->cv->chrspace &&
+ nranges <= v->cv->rangespace && nmcces <= v->cv->mccespace) {
+ return clearcvec(v->cv);
+ }
- if (v->cv != NULL)
- freecvec(v->cv);
- v->cv = newcvec(nchrs, nranges, nmcces);
- if (v->cv == NULL)
- ERR(REG_ESPACE);
+ if (v->cv != NULL) {
+ freecvec(v->cv);
+ }
+ v->cv = newcvec(nchrs, nranges, nmcces);
+ if (v->cv == NULL) {
+ ERR(REG_ESPACE);
+ }
- return v->cv;
+ return v->cv;
}
/*
@@ -192,7 +202,7 @@ int nmcces; /* ... and this many MCCEs */
*/
static VOID
freecvec(cv)
-struct cvec *cv;
+ struct cvec *cv; /* character vector */
{
- FREE(cv);
+ FREE(cv);
}
diff --git a/tcl/generic/regc_locale.c b/tcl/generic/regc_locale.c
index 100ba0a9415..695b665b1f2 100644
--- a/tcl/generic/regc_locale.c
+++ b/tcl/generic/regc_locale.c
@@ -15,105 +15,105 @@
/* ASCII character-name table */
static struct cname {
- char *name;
- char code;
+ char *name;
+ char code;
} cnames[] = {
- {"NUL", '\0'},
- {"SOH", '\001'},
- {"STX", '\002'},
- {"ETX", '\003'},
- {"EOT", '\004'},
- {"ENQ", '\005'},
- {"ACK", '\006'},
- {"BEL", '\007'},
- {"alert", '\007'},
- {"BS", '\010'},
- {"backspace", '\b'},
- {"HT", '\011'},
- {"tab", '\t'},
- {"LF", '\012'},
- {"newline", '\n'},
- {"VT", '\013'},
- {"vertical-tab", '\v'},
- {"FF", '\014'},
- {"form-feed", '\f'},
- {"CR", '\015'},
- {"carriage-return", '\r'},
- {"SO", '\016'},
- {"SI", '\017'},
- {"DLE", '\020'},
- {"DC1", '\021'},
- {"DC2", '\022'},
- {"DC3", '\023'},
- {"DC4", '\024'},
- {"NAK", '\025'},
- {"SYN", '\026'},
- {"ETB", '\027'},
- {"CAN", '\030'},
- {"EM", '\031'},
- {"SUB", '\032'},
- {"ESC", '\033'},
- {"IS4", '\034'},
- {"FS", '\034'},
- {"IS3", '\035'},
- {"GS", '\035'},
- {"IS2", '\036'},
- {"RS", '\036'},
- {"IS1", '\037'},
- {"US", '\037'},
- {"space", ' '},
- {"exclamation-mark", '!'},
- {"quotation-mark", '"'},
- {"number-sign", '#'},
- {"dollar-sign", '$'},
- {"percent-sign", '%'},
- {"ampersand", '&'},
- {"apostrophe", '\''},
- {"left-parenthesis", '('},
- {"right-parenthesis", ')'},
- {"asterisk", '*'},
- {"plus-sign", '+'},
- {"comma", ','},
- {"hyphen", '-'},
- {"hyphen-minus", '-'},
- {"period", '.'},
- {"full-stop", '.'},
- {"slash", '/'},
- {"solidus", '/'},
- {"zero", '0'},
- {"one", '1'},
- {"two", '2'},
- {"three", '3'},
- {"four", '4'},
- {"five", '5'},
- {"six", '6'},
- {"seven", '7'},
- {"eight", '8'},
- {"nine", '9'},
- {"colon", ':'},
- {"semicolon", ';'},
- {"less-than-sign", '<'},
- {"equals-sign", '='},
- {"greater-than-sign", '>'},
- {"question-mark", '?'},
- {"commercial-at", '@'},
- {"left-square-bracket", '['},
- {"backslash", '\\'},
- {"reverse-solidus", '\\'},
- {"right-square-bracket", ']'},
- {"circumflex", '^'},
- {"circumflex-accent", '^'},
- {"underscore", '_'},
- {"low-line", '_'},
- {"grave-accent", '`'},
- {"left-brace", '{'},
- {"left-curly-bracket", '{'},
- {"vertical-line", '|'},
- {"right-brace", '}'},
- {"right-curly-bracket", '}'},
- {"tilde", '~'},
- {"DEL", '\177'},
- {NULL, 0}
+ {"NUL", '\0'},
+ {"SOH", '\001'},
+ {"STX", '\002'},
+ {"ETX", '\003'},
+ {"EOT", '\004'},
+ {"ENQ", '\005'},
+ {"ACK", '\006'},
+ {"BEL", '\007'},
+ {"alert", '\007'},
+ {"BS", '\010'},
+ {"backspace", '\b'},
+ {"HT", '\011'},
+ {"tab", '\t'},
+ {"LF", '\012'},
+ {"newline", '\n'},
+ {"VT", '\013'},
+ {"vertical-tab", '\v'},
+ {"FF", '\014'},
+ {"form-feed", '\f'},
+ {"CR", '\015'},
+ {"carriage-return", '\r'},
+ {"SO", '\016'},
+ {"SI", '\017'},
+ {"DLE", '\020'},
+ {"DC1", '\021'},
+ {"DC2", '\022'},
+ {"DC3", '\023'},
+ {"DC4", '\024'},
+ {"NAK", '\025'},
+ {"SYN", '\026'},
+ {"ETB", '\027'},
+ {"CAN", '\030'},
+ {"EM", '\031'},
+ {"SUB", '\032'},
+ {"ESC", '\033'},
+ {"IS4", '\034'},
+ {"FS", '\034'},
+ {"IS3", '\035'},
+ {"GS", '\035'},
+ {"IS2", '\036'},
+ {"RS", '\036'},
+ {"IS1", '\037'},
+ {"US", '\037'},
+ {"space", ' '},
+ {"exclamation-mark",'!'},
+ {"quotation-mark", '"'},
+ {"number-sign", '#'},
+ {"dollar-sign", '$'},
+ {"percent-sign", '%'},
+ {"ampersand", '&'},
+ {"apostrophe", '\''},
+ {"left-parenthesis",'('},
+ {"right-parenthesis", ')'},
+ {"asterisk", '*'},
+ {"plus-sign", '+'},
+ {"comma", ','},
+ {"hyphen", '-'},
+ {"hyphen-minus", '-'},
+ {"period", '.'},
+ {"full-stop", '.'},
+ {"slash", '/'},
+ {"solidus", '/'},
+ {"zero", '0'},
+ {"one", '1'},
+ {"two", '2'},
+ {"three", '3'},
+ {"four", '4'},
+ {"five", '5'},
+ {"six", '6'},
+ {"seven", '7'},
+ {"eight", '8'},
+ {"nine", '9'},
+ {"colon", ':'},
+ {"semicolon", ';'},
+ {"less-than-sign", '<'},
+ {"equals-sign", '='},
+ {"greater-than-sign", '>'},
+ {"question-mark", '?'},
+ {"commercial-at", '@'},
+ {"left-square-bracket", '['},
+ {"backslash", '\\'},
+ {"reverse-solidus", '\\'},
+ {"right-square-bracket", ']'},
+ {"circumflex", '^'},
+ {"circumflex-accent", '^'},
+ {"underscore", '_'},
+ {"low-line", '_'},
+ {"grave-accent", '`'},
+ {"left-brace", '{'},
+ {"left-curly-bracket", '{'},
+ {"vertical-line", '|'},
+ {"right-brace", '}'},
+ {"right-curly-bracket", '}'},
+ {"tilde", '~'},
+ {"DEL", '\177'},
+ {NULL, 0}
};
/* Unicode character-class tables */
@@ -123,17 +123,22 @@ typedef struct crange {
chr end;
} crange;
-/* Unicode: (Alphabetic) */
+/*
+ * Declarations of Unicode character ranges. This code
+ * is automatically generated by the tools/uniClass.tcl script
+ * and used in generic/regc_locale.c. Do not modify by hand.
+ */
+
+/* Unicode: alphabetic characters */
static crange alphaRangeTable[] = {
{0x0041, 0x005a}, {0x0061, 0x007a}, {0x00c0, 0x00d6}, {0x00d8, 0x00f6},
- {0x00f8, 0x01f5}, {0x01fa, 0x0217}, {0x0250, 0x02a8}, {0x02b0, 0x02b8},
+ {0x00f8, 0x021f}, {0x0222, 0x0233}, {0x0250, 0x02ad}, {0x02b0, 0x02b8},
{0x02bb, 0x02c1}, {0x02e0, 0x02e4}, {0x0388, 0x038a}, {0x038e, 0x03a1},
- {0x03a3, 0x03ce}, {0x03d0, 0x03d6}, {0x03e2, 0x03f3}, {0x0401, 0x040c},
- {0x040e, 0x044f}, {0x0451, 0x045c}, {0x045e, 0x0481}, {0x0490, 0x04c4},
- {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x0531, 0x0556}, {0x0561, 0x0587},
+ {0x03a3, 0x03ce}, {0x03d0, 0x03d7}, {0x03da, 0x03f5}, {0x0400, 0x0481},
+ {0x048c, 0x04c4}, {0x04d0, 0x04f5}, {0x0531, 0x0556}, {0x0561, 0x0587},
{0x05d0, 0x05ea}, {0x05f0, 0x05f2}, {0x0621, 0x063a}, {0x0640, 0x064a},
- {0x0671, 0x06b7}, {0x06ba, 0x06be}, {0x06c0, 0x06ce}, {0x06d0, 0x06d3},
+ {0x0671, 0x06d3}, {0x06fa, 0x06fc}, {0x0712, 0x072c}, {0x0780, 0x07a5},
{0x0905, 0x0939}, {0x0958, 0x0961}, {0x0985, 0x098c}, {0x0993, 0x09a8},
{0x09aa, 0x09b0}, {0x09b6, 0x09b9}, {0x09df, 0x09e1}, {0x0a05, 0x0a0a},
{0x0a13, 0x0a28}, {0x0a2a, 0x0a30}, {0x0a59, 0x0a5c}, {0x0a72, 0x0a74},
@@ -144,90 +149,104 @@ static crange alphaRangeTable[] = {
{0x0c05, 0x0c0c}, {0x0c0e, 0x0c10}, {0x0c12, 0x0c28}, {0x0c2a, 0x0c33},
{0x0c35, 0x0c39}, {0x0c85, 0x0c8c}, {0x0c8e, 0x0c90}, {0x0c92, 0x0ca8},
{0x0caa, 0x0cb3}, {0x0cb5, 0x0cb9}, {0x0d05, 0x0d0c}, {0x0d0e, 0x0d10},
- {0x0d12, 0x0d28}, {0x0d2a, 0x0d39}, {0x0e01, 0x0e30}, {0x0e40, 0x0e46},
+ {0x0d12, 0x0d28}, {0x0d2a, 0x0d39}, {0x0d85, 0x0d96}, {0x0d9a, 0x0db1},
+ {0x0db3, 0x0dbb}, {0x0dc0, 0x0dc6}, {0x0e01, 0x0e30}, {0x0e40, 0x0e46},
{0x0e94, 0x0e97}, {0x0e99, 0x0e9f}, {0x0ea1, 0x0ea3}, {0x0ead, 0x0eb0},
- {0x0ec0, 0x0ec4}, {0x0f40, 0x0f47}, {0x0f49, 0x0f69}, {0x0f88, 0x0f8b},
- {0x10a0, 0x10c5}, {0x10d0, 0x10f6}, {0x1100, 0x1159}, {0x115f, 0x11a2},
- {0x11a8, 0x11f9}, {0x1e00, 0x1e9b}, {0x1ea0, 0x1ef9}, {0x1f00, 0x1f15},
- {0x1f18, 0x1f1d}, {0x1f20, 0x1f45}, {0x1f48, 0x1f4d}, {0x1f50, 0x1f57},
- {0x1f5f, 0x1f7d}, {0x1f80, 0x1fb4}, {0x1fb6, 0x1fbc}, {0x1fc2, 0x1fc4},
- {0x1fc6, 0x1fcc}, {0x1fd0, 0x1fd3}, {0x1fd6, 0x1fdb}, {0x1fe0, 0x1fec},
- {0x1ff2, 0x1ff4}, {0x1ff6, 0x1ffc}, {0x210a, 0x2113}, {0x2118, 0x211d},
- {0x212a, 0x2131}, {0x2133, 0x2138}, {0x3031, 0x3035}, {0x3041, 0x3094},
- {0x30a1, 0x30fa}, {0x30fc, 0x30fe}, {0x3105, 0x312c}, {0x3131, 0x318e},
- {0x4e00, 0x9fa5}, {0xac00, 0xd7a3}, {0xf900, 0xfa2d}, {0xfb00, 0xfb06},
- {0xfb13, 0xfb17}, {0xfb1f, 0xfb28}, {0xfb2a, 0xfb36}, {0xfb38, 0xfb3c},
- {0xfb46, 0xfbb1}, {0xfbd3, 0xfd3d}, {0xfd50, 0xfd8f}, {0xfd92, 0xfdc7},
- {0xfdf0, 0xfdfb}, {0xfe70, 0xfe72}, {0xfe76, 0xfefc}, {0xff21, 0xff3a},
- {0xff41, 0xff5a}, {0xff66, 0xffbe}, {0xffc2, 0xffc7}, {0xffca, 0xffcf},
- {0xffd2, 0xffd7}, {0xffda, 0xffdc}
+ {0x0ec0, 0x0ec4}, {0x0f40, 0x0f47}, {0x0f49, 0x0f6a}, {0x0f88, 0x0f8b},
+ {0x1000, 0x1021}, {0x1023, 0x1027}, {0x1050, 0x1055}, {0x10a0, 0x10c5},
+ {0x10d0, 0x10f6}, {0x1100, 0x1159}, {0x115f, 0x11a2}, {0x11a8, 0x11f9},
+ {0x1200, 0x1206}, {0x1208, 0x1246}, {0x124a, 0x124d}, {0x1250, 0x1256},
+ {0x125a, 0x125d}, {0x1260, 0x1286}, {0x128a, 0x128d}, {0x1290, 0x12ae},
+ {0x12b2, 0x12b5}, {0x12b8, 0x12be}, {0x12c2, 0x12c5}, {0x12c8, 0x12ce},
+ {0x12d0, 0x12d6}, {0x12d8, 0x12ee}, {0x12f0, 0x130e}, {0x1312, 0x1315},
+ {0x1318, 0x131e}, {0x1320, 0x1346}, {0x1348, 0x135a}, {0x13a0, 0x13f4},
+ {0x1401, 0x166c}, {0x166f, 0x1676}, {0x1681, 0x169a}, {0x16a0, 0x16ea},
+ {0x1780, 0x17b3}, {0x1820, 0x1877}, {0x1880, 0x18a8}, {0x1e00, 0x1e9b},
+ {0x1ea0, 0x1ef9}, {0x1f00, 0x1f15}, {0x1f18, 0x1f1d}, {0x1f20, 0x1f45},
+ {0x1f48, 0x1f4d}, {0x1f50, 0x1f57}, {0x1f5f, 0x1f7d}, {0x1f80, 0x1fb4},
+ {0x1fb6, 0x1fbc}, {0x1fc2, 0x1fc4}, {0x1fc6, 0x1fcc}, {0x1fd0, 0x1fd3},
+ {0x1fd6, 0x1fdb}, {0x1fe0, 0x1fec}, {0x1ff2, 0x1ff4}, {0x1ff6, 0x1ffc},
+ {0x210a, 0x2113}, {0x2119, 0x211d}, {0x212a, 0x212d}, {0x212f, 0x2131},
+ {0x2133, 0x2139}, {0x3031, 0x3035}, {0x3041, 0x3094}, {0x30a1, 0x30fa},
+ {0x30fc, 0x30fe}, {0x3105, 0x312c}, {0x3131, 0x318e}, {0x31a0, 0x31b7},
+ {0x3400, 0x4db5}, {0x4e00, 0x9fa5}, {0xa000, 0xa48c}, {0xac00, 0xd7a3},
+ {0xf900, 0xfa2d}, {0xfb00, 0xfb06}, {0xfb13, 0xfb17}, {0xfb1f, 0xfb28},
+ {0xfb2a, 0xfb36}, {0xfb38, 0xfb3c}, {0xfb46, 0xfbb1}, {0xfbd3, 0xfd3d},
+ {0xfd50, 0xfd8f}, {0xfd92, 0xfdc7}, {0xfdf0, 0xfdfb}, {0xfe70, 0xfe72},
+ {0xfe76, 0xfefc}, {0xff21, 0xff3a}, {0xff41, 0xff5a}, {0xff66, 0xffbe},
+ {0xffc2, 0xffc7}, {0xffca, 0xffcf}, {0xffd2, 0xffd7}, {0xffda, 0xffdc}
};
#define NUM_ALPHA_RANGE (sizeof(alphaRangeTable)/sizeof(crange))
static chr alphaCharTable[] = {
- 0x00aa, 0x00b5, 0x00ba, 0x02d0, 0x02d1, 0x037a, 0x0386, 0x038c, 0x03da,
- 0x03dc, 0x03de, 0x03e0, 0x04c7, 0x04c8, 0x04cb, 0x04cc, 0x04f8, 0x04f9,
- 0x0559, 0x06d5, 0x06e5, 0x06e6, 0x093d, 0x0950, 0x098f, 0x0990, 0x09b2,
- 0x09dc, 0x09dd, 0x09f0, 0x09f1, 0x0a0f, 0x0a10, 0x0a32, 0x0a33, 0x0a35,
- 0x0a36, 0x0a38, 0x0a39, 0x0a5e, 0x0a8d, 0x0ab2, 0x0ab3, 0x0abd, 0x0ad0,
- 0x0ae0, 0x0b0f, 0x0b10, 0x0b32, 0x0b33, 0x0b3d, 0x0b5c, 0x0b5d, 0x0b99,
- 0x0b9a, 0x0b9c, 0x0b9e, 0x0b9f, 0x0ba3, 0x0ba4, 0x0c60, 0x0c61, 0x0cde,
- 0x0ce0, 0x0ce1, 0x0d60, 0x0d61, 0x0e32, 0x0e33, 0x0e81, 0x0e82, 0x0e84,
- 0x0e87, 0x0e88, 0x0e8a, 0x0e8d, 0x0ea5, 0x0ea7, 0x0eaa, 0x0eab, 0x0eb2,
- 0x0eb3, 0x0ebd, 0x0ec6, 0x0edc, 0x0edd, 0x0f00, 0x1f59, 0x1f5b, 0x1f5d,
- 0x1fbe, 0x207f, 0x2102, 0x2107, 0x2115, 0x2124, 0x2126, 0x2128, 0x3005,
- 0x3006, 0x309d, 0x309e, 0xfb3e, 0xfb40, 0xfb41, 0xfb43, 0xfb44, 0xfe74
+ 0x00aa, 0x00b5, 0x00ba, 0x02d0, 0x02d1, 0x02ee, 0x037a, 0x0386, 0x038c,
+ 0x04c7, 0x04c8, 0x04cb, 0x04cc, 0x04f8, 0x04f9, 0x0559, 0x06d5, 0x06e5,
+ 0x06e6, 0x0710, 0x093d, 0x0950, 0x098f, 0x0990, 0x09b2, 0x09dc, 0x09dd,
+ 0x09f0, 0x09f1, 0x0a0f, 0x0a10, 0x0a32, 0x0a33, 0x0a35, 0x0a36, 0x0a38,
+ 0x0a39, 0x0a5e, 0x0a8d, 0x0ab2, 0x0ab3, 0x0abd, 0x0ad0, 0x0ae0, 0x0b0f,
+ 0x0b10, 0x0b32, 0x0b33, 0x0b3d, 0x0b5c, 0x0b5d, 0x0b99, 0x0b9a, 0x0b9c,
+ 0x0b9e, 0x0b9f, 0x0ba3, 0x0ba4, 0x0c60, 0x0c61, 0x0cde, 0x0ce0, 0x0ce1,
+ 0x0d60, 0x0d61, 0x0dbd, 0x0e32, 0x0e33, 0x0e81, 0x0e82, 0x0e84, 0x0e87,
+ 0x0e88, 0x0e8a, 0x0e8d, 0x0ea5, 0x0ea7, 0x0eaa, 0x0eab, 0x0eb2, 0x0eb3,
+ 0x0ebd, 0x0ec6, 0x0edc, 0x0edd, 0x0f00, 0x1029, 0x102a, 0x1248, 0x1258,
+ 0x1288, 0x12b0, 0x12c0, 0x1310, 0x1f59, 0x1f5b, 0x1f5d, 0x1fbe, 0x207f,
+ 0x2102, 0x2107, 0x2115, 0x2124, 0x2126, 0x2128, 0x3005, 0x3006, 0x309d,
+ 0x309e, 0xfb1d, 0xfb3e, 0xfb40, 0xfb41, 0xfb43, 0xfb44, 0xfe74, 0xfffe
};
#define NUM_ALPHA_CHAR (sizeof(alphaCharTable)/sizeof(chr))
-/* Unicode: (Decimal digit) */
+/* Unicode: decimal digit characters */
static crange digitRangeTable[] = {
{0x0030, 0x0039}, {0x0660, 0x0669}, {0x06f0, 0x06f9}, {0x0966, 0x096f},
{0x09e6, 0x09ef}, {0x0a66, 0x0a6f}, {0x0ae6, 0x0aef}, {0x0b66, 0x0b6f},
{0x0be7, 0x0bef}, {0x0c66, 0x0c6f}, {0x0ce6, 0x0cef}, {0x0d66, 0x0d6f},
- {0x0e50, 0x0e59}, {0x0ed0, 0x0ed9}, {0x0f20, 0x0f29}, {0xff10, 0xff19}
+ {0x0e50, 0x0e59}, {0x0ed0, 0x0ed9}, {0x0f20, 0x0f29}, {0x1040, 0x1049},
+ {0x1369, 0x1371}, {0x17e0, 0x17e9}, {0x1810, 0x1819}, {0xff10, 0xff19}
};
#define NUM_DIGIT_RANGE (sizeof(digitRangeTable)/sizeof(crange))
-/* Unicode: (Punctuation) */
+/* no singletons of digit characters */
+
+/* Unicode: punctuation characters */
static crange punctRangeTable[] = {
{0x0021, 0x0023}, {0x0025, 0x002a}, {0x002c, 0x002f}, {0x005b, 0x005d},
- {0x055a, 0x055f}, {0x066a, 0x066d}, {0x0f04, 0x0f12}, {0x0f3a, 0x0f3d},
- {0x2010, 0x2027}, {0x2030, 0x2043}, {0x3001, 0x3003}, {0x3008, 0x3011},
- {0x3014, 0x301f}, {0xfe30, 0xfe44}, {0xfe49, 0xfe52}, {0xfe54, 0xfe61},
- {0xff01, 0xff03}, {0xff05, 0xff0a}, {0xff0c, 0xff0f}, {0xff3b, 0xff3d},
- {0xff61, 0xff65}
+ {0x055a, 0x055f}, {0x066a, 0x066d}, {0x0700, 0x070d}, {0x0f04, 0x0f12},
+ {0x0f3a, 0x0f3d}, {0x104a, 0x104f}, {0x1361, 0x1368}, {0x16eb, 0x16ed},
+ {0x17d4, 0x17da}, {0x1800, 0x180a}, {0x2010, 0x2027}, {0x2030, 0x2043},
+ {0x2048, 0x204d}, {0x3001, 0x3003}, {0x3008, 0x3011}, {0x3014, 0x301f},
+ {0xfe30, 0xfe44}, {0xfe49, 0xfe52}, {0xfe54, 0xfe61}, {0xff01, 0xff03},
+ {0xff05, 0xff0a}, {0xff0c, 0xff0f}, {0xff3b, 0xff3d}, {0xff61, 0xff65}
};
#define NUM_PUNCT_RANGE (sizeof(punctRangeTable)/sizeof(crange))
static chr punctCharTable[] = {
0x003a, 0x003b, 0x003f, 0x0040, 0x005f, 0x007b, 0x007d, 0x00a1, 0x00ab,
- 0x00ad, 0x00b7, 0x00bb, 0x00bf, 0x037e, 0x0387, 0x0589, 0x05be, 0x05c0,
- 0x05c3, 0x05f3, 0x05f4, 0x060c, 0x061b, 0x061f, 0x06d4, 0x0964, 0x0965,
- 0x0970, 0x0e5a, 0x0e5b, 0x0f85, 0x10fb, 0x2045, 0x2046, 0x207d, 0x207e,
- 0x208d, 0x208e, 0x2329, 0x232a, 0x3030, 0x30fb, 0xfd3e, 0xfd3f, 0xfe63,
- 0xfe68, 0xfe6a, 0xfe6b, 0xff1a, 0xff1b, 0xff1f, 0xff20, 0xff3f, 0xff5b,
- 0xff5d
+ 0x00ad, 0x00b7, 0x00bb, 0x00bf, 0x037e, 0x0387, 0x0589, 0x058a, 0x05be,
+ 0x05c0, 0x05c3, 0x05f3, 0x05f4, 0x060c, 0x061b, 0x061f, 0x06d4, 0x0964,
+ 0x0965, 0x0970, 0x0df4, 0x0e4f, 0x0e5a, 0x0e5b, 0x0f85, 0x10fb, 0x166d,
+ 0x166e, 0x169b, 0x169c, 0x17dc, 0x2045, 0x2046, 0x207d, 0x207e, 0x208d,
+ 0x208e, 0x2329, 0x232a, 0x3030, 0x30fb, 0xfd3e, 0xfd3f, 0xfe63, 0xfe68,
+ 0xfe6a, 0xfe6b, 0xff1a, 0xff1b, 0xff1f, 0xff20, 0xff3f, 0xff5b, 0xff5d
};
#define NUM_PUNCT_CHAR (sizeof(punctCharTable)/sizeof(chr))
-/* Unicode: (White space) */
+/* Unicode: white space characters */
static crange spaceRangeTable[] = {
- {0x0009, 0x000d}, {0x2000, 0x200b},
+ {0x0009, 0x000d}, {0x2000, 0x200b}
};
#define NUM_SPACE_RANGE (sizeof(spaceRangeTable)/sizeof(crange))
static chr spaceCharTable[] = {
- 0x0020, 0x00a0, 0x2028, 0x2029, 0x3000
+ 0x0020, 0x00a0, 0x1680, 0x2028, 0x2029, 0x202f, 0x3000
};
#define NUM_SPACE_CHAR (sizeof(spaceCharTable)/sizeof(chr))
@@ -236,8 +255,8 @@ static chr spaceCharTable[] = {
static crange lowerRangeTable[] = {
{0x0061, 0x007a}, {0x00df, 0x00f6}, {0x00f8, 0x00ff}, {0x017e, 0x0180},
- {0x0199, 0x019b}, {0x0250, 0x02a8}, {0x03ac, 0x03ce}, {0x03ef, 0x03f2},
- {0x0430, 0x044f}, {0x0451, 0x045c}, {0x0561, 0x0587}, {0x10d0, 0x10f6},
+ {0x0199, 0x019b}, {0x01bd, 0x01bf}, {0x0250, 0x02ad}, {0x03ac, 0x03ce},
+ {0x03d5, 0x03d7}, {0x03ef, 0x03f3}, {0x0430, 0x045f}, {0x0561, 0x0587},
{0x1e95, 0x1e9b}, {0x1f00, 0x1f07}, {0x1f10, 0x1f15}, {0x1f20, 0x1f27},
{0x1f30, 0x1f37}, {0x1f40, 0x1f45}, {0x1f50, 0x1f57}, {0x1f60, 0x1f67},
{0x1f70, 0x1f7d}, {0x1f80, 0x1f87}, {0x1f90, 0x1f97}, {0x1fa0, 0x1fa7},
@@ -256,20 +275,22 @@ static chr lowerCharTable[] = {
0x0153, 0x0155, 0x0157, 0x0159, 0x015b, 0x015d, 0x015f, 0x0161, 0x0163,
0x0165, 0x0167, 0x0169, 0x016b, 0x016d, 0x016f, 0x0171, 0x0173, 0x0175,
0x0177, 0x017a, 0x017c, 0x0183, 0x0185, 0x0188, 0x018c, 0x018d, 0x0192,
- 0x0195, 0x019e, 0x01a1, 0x01a3, 0x01a5, 0x01a8, 0x01ab, 0x01ad, 0x01b0,
- 0x01b4, 0x01b6, 0x01b9, 0x01ba, 0x01bd, 0x01c6, 0x01c9, 0x01cc, 0x01ce,
+ 0x0195, 0x019e, 0x01a1, 0x01a3, 0x01a5, 0x01a8, 0x01aa, 0x01ab, 0x01ad,
+ 0x01b0, 0x01b4, 0x01b6, 0x01b9, 0x01ba, 0x01c6, 0x01c9, 0x01cc, 0x01ce,
0x01d0, 0x01d2, 0x01d4, 0x01d6, 0x01d8, 0x01da, 0x01dc, 0x01dd, 0x01df,
0x01e1, 0x01e3, 0x01e5, 0x01e7, 0x01e9, 0x01eb, 0x01ed, 0x01ef, 0x01f0,
- 0x01f3, 0x01f5, 0x01fb, 0x01fd, 0x01ff, 0x0201, 0x0203, 0x0205, 0x0207,
- 0x0209, 0x020b, 0x020d, 0x020f, 0x0211, 0x0213, 0x0215, 0x0217, 0x0390,
- 0x03d0, 0x03d1, 0x03d5, 0x03d6, 0x03e3, 0x03e5, 0x03e7, 0x03e9, 0x03eb,
- 0x03ed, 0x045e, 0x045f, 0x0461, 0x0463, 0x0465, 0x0467, 0x0469, 0x046b,
- 0x046d, 0x046f, 0x0471, 0x0473, 0x0475, 0x0477, 0x0479, 0x047b, 0x047d,
- 0x047f, 0x0481, 0x0491, 0x0493, 0x0495, 0x0497, 0x0499, 0x049b, 0x049d,
- 0x049f, 0x04a1, 0x04a3, 0x04a5, 0x04a7, 0x04a9, 0x04ab, 0x04ad, 0x04af,
- 0x04b1, 0x04b3, 0x04b5, 0x04b7, 0x04b9, 0x04bb, 0x04bd, 0x04bf, 0x04c2,
- 0x04c4, 0x04c8, 0x04cc, 0x04d1, 0x04d3, 0x04d5, 0x04d7, 0x04d9, 0x04db,
- 0x04dd, 0x04df, 0x04e1, 0x04e3, 0x04e5, 0x04e7, 0x04e9, 0x04eb, 0x04ef,
+ 0x01f3, 0x01f5, 0x01f9, 0x01fb, 0x01fd, 0x01ff, 0x0201, 0x0203, 0x0205,
+ 0x0207, 0x0209, 0x020b, 0x020d, 0x020f, 0x0211, 0x0213, 0x0215, 0x0217,
+ 0x0219, 0x021b, 0x021d, 0x021f, 0x0223, 0x0225, 0x0227, 0x0229, 0x022b,
+ 0x022d, 0x022f, 0x0231, 0x0233, 0x0390, 0x03d0, 0x03d1, 0x03db, 0x03dd,
+ 0x03df, 0x03e1, 0x03e3, 0x03e5, 0x03e7, 0x03e9, 0x03eb, 0x03ed, 0x03f5,
+ 0x0461, 0x0463, 0x0465, 0x0467, 0x0469, 0x046b, 0x046d, 0x046f, 0x0471,
+ 0x0473, 0x0475, 0x0477, 0x0479, 0x047b, 0x047d, 0x047f, 0x0481, 0x048d,
+ 0x048f, 0x0491, 0x0493, 0x0495, 0x0497, 0x0499, 0x049b, 0x049d, 0x049f,
+ 0x04a1, 0x04a3, 0x04a5, 0x04a7, 0x04a9, 0x04ab, 0x04ad, 0x04af, 0x04b1,
+ 0x04b3, 0x04b5, 0x04b7, 0x04b9, 0x04bb, 0x04bd, 0x04bf, 0x04c2, 0x04c4,
+ 0x04c8, 0x04cc, 0x04d1, 0x04d3, 0x04d5, 0x04d7, 0x04d9, 0x04db, 0x04dd,
+ 0x04df, 0x04e1, 0x04e3, 0x04e5, 0x04e7, 0x04e9, 0x04eb, 0x04ed, 0x04ef,
0x04f1, 0x04f3, 0x04f5, 0x04f9, 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09,
0x1e0b, 0x1e0d, 0x1e0f, 0x1e11, 0x1e13, 0x1e15, 0x1e17, 0x1e19, 0x1e1b,
0x1e1d, 0x1e1f, 0x1e21, 0x1e23, 0x1e25, 0x1e27, 0x1e29, 0x1e2b, 0x1e2d,
@@ -285,7 +306,7 @@ static chr lowerCharTable[] = {
0x1edd, 0x1edf, 0x1ee1, 0x1ee3, 0x1ee5, 0x1ee7, 0x1ee9, 0x1eeb, 0x1eed,
0x1eef, 0x1ef1, 0x1ef3, 0x1ef5, 0x1ef7, 0x1ef9, 0x1fb6, 0x1fb7, 0x1fbe,
0x1fc6, 0x1fc7, 0x1fd6, 0x1fd7, 0x1ff6, 0x1ff7, 0x207f, 0x210a, 0x210e,
- 0x210f, 0x2113, 0x2118, 0x212e, 0x212f, 0x2134
+ 0x210f, 0x2113, 0x212f, 0x2134, 0x2139
};
#define NUM_LOWER_CHAR (sizeof(lowerCharTable)/sizeof(chr))
@@ -294,14 +315,13 @@ static chr lowerCharTable[] = {
static crange upperRangeTable[] = {
{0x0041, 0x005a}, {0x00c0, 0x00d6}, {0x00d8, 0x00de}, {0x0189, 0x018b},
- {0x018e, 0x0191}, {0x0196, 0x0198}, {0x01b1, 0x01b3}, {0x0388, 0x038a},
- {0x0391, 0x03a1}, {0x03a3, 0x03ab}, {0x03d2, 0x03d4}, {0x0401, 0x040c},
- {0x040e, 0x042f}, {0x0531, 0x0556}, {0x10a0, 0x10c5}, {0x1f08, 0x1f0f},
+ {0x018e, 0x0191}, {0x0196, 0x0198}, {0x01b1, 0x01b3}, {0x01f6, 0x01f8},
+ {0x0388, 0x038a}, {0x0391, 0x03a1}, {0x03a3, 0x03ab}, {0x03d2, 0x03d4},
+ {0x0400, 0x042f}, {0x0531, 0x0556}, {0x10a0, 0x10c5}, {0x1f08, 0x1f0f},
{0x1f18, 0x1f1d}, {0x1f28, 0x1f2f}, {0x1f38, 0x1f3f}, {0x1f48, 0x1f4d},
- {0x1f68, 0x1f6f}, {0x1f88, 0x1f8f}, {0x1f98, 0x1f9f}, {0x1fa8, 0x1faf},
- {0x1fb8, 0x1fbc}, {0x1fc8, 0x1fcc}, {0x1fd8, 0x1fdb}, {0x1fe8, 0x1fec},
- {0x1ff8, 0x1ffc}, {0x210b, 0x210d}, {0x2110, 0x2112}, {0x2119, 0x211d},
- {0x212a, 0x212d}, {0xff21, 0xff3a}
+ {0x1f68, 0x1f6f}, {0x1fb8, 0x1fbb}, {0x1fc8, 0x1fcb}, {0x1fd8, 0x1fdb},
+ {0x1fe8, 0x1fec}, {0x1ff8, 0x1ffb}, {0x210b, 0x210d}, {0x2110, 0x2112},
+ {0x2119, 0x211d}, {0x212a, 0x212d}, {0xff21, 0xff3a}
};
#define NUM_UPPER_RANGE (sizeof(upperRangeTable)/sizeof(crange))
@@ -320,15 +340,17 @@ static chr upperCharTable[] = {
0x01cf, 0x01d1, 0x01d3, 0x01d5, 0x01d7, 0x01d9, 0x01db, 0x01de, 0x01e0,
0x01e2, 0x01e4, 0x01e6, 0x01e8, 0x01ea, 0x01ec, 0x01ee, 0x01f1, 0x01f4,
0x01fa, 0x01fc, 0x01fe, 0x0200, 0x0202, 0x0204, 0x0206, 0x0208, 0x020a,
- 0x020c, 0x020e, 0x0210, 0x0212, 0x0214, 0x0216, 0x0386, 0x038c, 0x038e,
- 0x038f, 0x03da, 0x03dc, 0x03de, 0x03e0, 0x03e2, 0x03e4, 0x03e6, 0x03e8,
- 0x03ea, 0x03ec, 0x03ee, 0x0460, 0x0462, 0x0464, 0x0466, 0x0468, 0x046a,
- 0x046c, 0x046e, 0x0470, 0x0472, 0x0474, 0x0476, 0x0478, 0x047a, 0x047c,
- 0x047e, 0x0480, 0x0490, 0x0492, 0x0494, 0x0496, 0x0498, 0x049a, 0x049c,
- 0x049e, 0x04a0, 0x04a2, 0x04a4, 0x04a6, 0x04a8, 0x04aa, 0x04ac, 0x04ae,
- 0x04b0, 0x04b2, 0x04b4, 0x04b6, 0x04b8, 0x04ba, 0x04bc, 0x04be, 0x04c1,
- 0x04c3, 0x04c7, 0x04cb, 0x04d0, 0x04d2, 0x04d4, 0x04d6, 0x04d8, 0x04da,
- 0x04dc, 0x04de, 0x04e0, 0x04e2, 0x04e4, 0x04e6, 0x04e8, 0x04ea, 0x04ee,
+ 0x020c, 0x020e, 0x0210, 0x0212, 0x0214, 0x0216, 0x0218, 0x021a, 0x021c,
+ 0x021e, 0x0222, 0x0224, 0x0226, 0x0228, 0x022a, 0x022c, 0x022e, 0x0230,
+ 0x0232, 0x0386, 0x038c, 0x038e, 0x038f, 0x03da, 0x03dc, 0x03de, 0x03e0,
+ 0x03e2, 0x03e4, 0x03e6, 0x03e8, 0x03ea, 0x03ec, 0x03ee, 0x03f4, 0x0460,
+ 0x0462, 0x0464, 0x0466, 0x0468, 0x046a, 0x046c, 0x046e, 0x0470, 0x0472,
+ 0x0474, 0x0476, 0x0478, 0x047a, 0x047c, 0x047e, 0x0480, 0x048c, 0x048e,
+ 0x0490, 0x0492, 0x0494, 0x0496, 0x0498, 0x049a, 0x049c, 0x049e, 0x04a0,
+ 0x04a2, 0x04a4, 0x04a6, 0x04a8, 0x04aa, 0x04ac, 0x04ae, 0x04b0, 0x04b2,
+ 0x04b4, 0x04b6, 0x04b8, 0x04ba, 0x04bc, 0x04be, 0x04c0, 0x04c1, 0x04c3,
+ 0x04c7, 0x04cb, 0x04d0, 0x04d2, 0x04d4, 0x04d6, 0x04d8, 0x04da, 0x04dc,
+ 0x04de, 0x04e0, 0x04e2, 0x04e4, 0x04e6, 0x04e8, 0x04ea, 0x04ec, 0x04ee,
0x04f0, 0x04f2, 0x04f4, 0x04f8, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08,
0x1e0a, 0x1e0c, 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a,
0x1e1c, 0x1e1e, 0x1e20, 0x1e22, 0x1e24, 0x1e26, 0x1e28, 0x1e2a, 0x1e2c,
@@ -349,66 +371,83 @@ static chr upperCharTable[] = {
#define NUM_UPPER_CHAR (sizeof(upperCharTable)/sizeof(chr))
-/*
- * The graph table includes the set of characters that are Unicode
- * print characters excluding space.
- */
+/* Unicode: unicode print characters excluding space */
static crange graphRangeTable[] = {
- {0x0021, 0x007e}, {0x00a0, 0x011f}, {0x0121, 0x01f5}, {0x01fa, 0x0217},
- {0x0250, 0x02a8}, {0x02b0, 0x02de}, {0x02e0, 0x02e9}, {0x0300, 0x031f},
- {0x0321, 0x0345}, {0x0384, 0x038a}, {0x038e, 0x03a1}, {0x03a3, 0x03ce},
- {0x03d0, 0x03d6}, {0x03e2, 0x03f3}, {0x0401, 0x040c}, {0x040e, 0x041f},
- {0x0421, 0x044f}, {0x0451, 0x045c}, {0x045e, 0x0486}, {0x0490, 0x04c4},
- {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x0531, 0x0556}, {0x0559, 0x055f},
+ {0x0021, 0x007e}, {0x00a0, 0x011f}, {0x0121, 0x021f}, {0x0222, 0x0233},
+ {0x0250, 0x02ad}, {0x02b0, 0x02ee}, {0x0300, 0x031f}, {0x0321, 0x034e},
+ {0x0360, 0x0362}, {0x0384, 0x038a}, {0x038e, 0x03a1}, {0x03a3, 0x03ce},
+ {0x03d0, 0x03d7}, {0x03da, 0x03f5}, {0x0400, 0x041f}, {0x0421, 0x0486},
+ {0x048c, 0x04c4}, {0x04d0, 0x04f5}, {0x0531, 0x0556}, {0x0559, 0x055f},
{0x0561, 0x0587}, {0x0591, 0x05a1}, {0x05a3, 0x05b9}, {0x05bb, 0x05c4},
- {0x05d0, 0x05ea}, {0x05f0, 0x05f4}, {0x0621, 0x063a}, {0x0640, 0x0652},
- {0x0660, 0x066d}, {0x0670, 0x06b7}, {0x06ba, 0x06be}, {0x06c0, 0x06ce},
- {0x06d0, 0x06ed}, {0x06f0, 0x06f9}, {0x0901, 0x0903}, {0x0905, 0x091f},
- {0x0921, 0x0939}, {0x093c, 0x094d}, {0x0950, 0x0954}, {0x0958, 0x0970},
- {0x0981, 0x0983}, {0x0985, 0x098c}, {0x0993, 0x09a8}, {0x09aa, 0x09b0},
- {0x09b6, 0x09b9}, {0x09be, 0x09c4}, {0x09cb, 0x09cd}, {0x09df, 0x09e3},
- {0x09e6, 0x09fa}, {0x0a05, 0x0a0a}, {0x0a13, 0x0a1f}, {0x0a21, 0x0a28},
- {0x0a2a, 0x0a30}, {0x0a3e, 0x0a42}, {0x0a4b, 0x0a4d}, {0x0a59, 0x0a5c},
- {0x0a66, 0x0a74}, {0x0a81, 0x0a83}, {0x0a85, 0x0a8b}, {0x0a8f, 0x0a91},
- {0x0a93, 0x0aa8}, {0x0aaa, 0x0ab0}, {0x0ab5, 0x0ab9}, {0x0abc, 0x0ac5},
- {0x0ac7, 0x0ac9}, {0x0acb, 0x0acd}, {0x0ae6, 0x0aef}, {0x0b01, 0x0b03},
- {0x0b05, 0x0b0c}, {0x0b13, 0x0b1f}, {0x0b21, 0x0b28}, {0x0b2a, 0x0b30},
- {0x0b36, 0x0b39}, {0x0b3c, 0x0b43}, {0x0b4b, 0x0b4d}, {0x0b5f, 0x0b61},
- {0x0b66, 0x0b70}, {0x0b85, 0x0b8a}, {0x0b8e, 0x0b90}, {0x0b92, 0x0b95},
- {0x0ba8, 0x0baa}, {0x0bae, 0x0bb5}, {0x0bb7, 0x0bb9}, {0x0bbe, 0x0bc2},
- {0x0bc6, 0x0bc8}, {0x0bca, 0x0bcd}, {0x0be7, 0x0bf2}, {0x0c01, 0x0c03},
- {0x0c05, 0x0c0c}, {0x0c0e, 0x0c10}, {0x0c12, 0x0c1f}, {0x0c21, 0x0c28},
- {0x0c2a, 0x0c33}, {0x0c35, 0x0c39}, {0x0c3e, 0x0c44}, {0x0c46, 0x0c48},
- {0x0c4a, 0x0c4d}, {0x0c66, 0x0c6f}, {0x0c85, 0x0c8c}, {0x0c8e, 0x0c90},
- {0x0c92, 0x0ca8}, {0x0caa, 0x0cb3}, {0x0cb5, 0x0cb9}, {0x0cbe, 0x0cc4},
- {0x0cc6, 0x0cc8}, {0x0cca, 0x0ccd}, {0x0ce6, 0x0cef}, {0x0d05, 0x0d0c},
- {0x0d0e, 0x0d10}, {0x0d12, 0x0d1f}, {0x0d21, 0x0d28}, {0x0d2a, 0x0d39},
- {0x0d3e, 0x0d43}, {0x0d46, 0x0d48}, {0x0d4a, 0x0d4d}, {0x0d66, 0x0d6f},
- {0x0e01, 0x0e1f}, {0x0e21, 0x0e3a}, {0x0e3f, 0x0e5b}, {0x0e94, 0x0e97},
- {0x0e99, 0x0e9f}, {0x0ea1, 0x0ea3}, {0x0ead, 0x0eb9}, {0x0ebb, 0x0ebd},
- {0x0ec0, 0x0ec4}, {0x0ec8, 0x0ecd}, {0x0ed0, 0x0ed9}, {0x0f00, 0x0f1f},
- {0x0f21, 0x0f47}, {0x0f49, 0x0f69}, {0x0f71, 0x0f8b}, {0x0f90, 0x0f95},
- {0x0f99, 0x0fad}, {0x0fb1, 0x0fb7}, {0x10a0, 0x10c5}, {0x10d0, 0x10f6},
- {0x1100, 0x111f}, {0x1121, 0x1159}, {0x115f, 0x11a2}, {0x11a8, 0x11f9},
+ {0x05d0, 0x05ea}, {0x05f0, 0x05f4}, {0x0621, 0x063a}, {0x0640, 0x0655},
+ {0x0660, 0x066d}, {0x0670, 0x06ed}, {0x06f0, 0x06fe}, {0x0700, 0x070d},
+ {0x0710, 0x071f}, {0x0721, 0x072c}, {0x0730, 0x074a}, {0x0780, 0x07b0},
+ {0x0901, 0x0903}, {0x0905, 0x091f}, {0x0921, 0x0939}, {0x093c, 0x094d},
+ {0x0950, 0x0954}, {0x0958, 0x0970}, {0x0981, 0x0983}, {0x0985, 0x098c},
+ {0x0993, 0x09a8}, {0x09aa, 0x09b0}, {0x09b6, 0x09b9}, {0x09be, 0x09c4},
+ {0x09cb, 0x09cd}, {0x09df, 0x09e3}, {0x09e6, 0x09fa}, {0x0a05, 0x0a0a},
+ {0x0a13, 0x0a1f}, {0x0a21, 0x0a28}, {0x0a2a, 0x0a30}, {0x0a3e, 0x0a42},
+ {0x0a4b, 0x0a4d}, {0x0a59, 0x0a5c}, {0x0a66, 0x0a74}, {0x0a81, 0x0a83},
+ {0x0a85, 0x0a8b}, {0x0a8f, 0x0a91}, {0x0a93, 0x0aa8}, {0x0aaa, 0x0ab0},
+ {0x0ab5, 0x0ab9}, {0x0abc, 0x0ac5}, {0x0ac7, 0x0ac9}, {0x0acb, 0x0acd},
+ {0x0ae6, 0x0aef}, {0x0b01, 0x0b03}, {0x0b05, 0x0b0c}, {0x0b13, 0x0b1f},
+ {0x0b21, 0x0b28}, {0x0b2a, 0x0b30}, {0x0b36, 0x0b39}, {0x0b3c, 0x0b43},
+ {0x0b4b, 0x0b4d}, {0x0b5f, 0x0b61}, {0x0b66, 0x0b70}, {0x0b85, 0x0b8a},
+ {0x0b8e, 0x0b90}, {0x0b92, 0x0b95}, {0x0ba8, 0x0baa}, {0x0bae, 0x0bb5},
+ {0x0bb7, 0x0bb9}, {0x0bbe, 0x0bc2}, {0x0bc6, 0x0bc8}, {0x0bca, 0x0bcd},
+ {0x0be7, 0x0bf2}, {0x0c01, 0x0c03}, {0x0c05, 0x0c0c}, {0x0c0e, 0x0c10},
+ {0x0c12, 0x0c1f}, {0x0c21, 0x0c28}, {0x0c2a, 0x0c33}, {0x0c35, 0x0c39},
+ {0x0c3e, 0x0c44}, {0x0c46, 0x0c48}, {0x0c4a, 0x0c4d}, {0x0c66, 0x0c6f},
+ {0x0c85, 0x0c8c}, {0x0c8e, 0x0c90}, {0x0c92, 0x0ca8}, {0x0caa, 0x0cb3},
+ {0x0cb5, 0x0cb9}, {0x0cbe, 0x0cc4}, {0x0cc6, 0x0cc8}, {0x0cca, 0x0ccd},
+ {0x0ce6, 0x0cef}, {0x0d05, 0x0d0c}, {0x0d0e, 0x0d10}, {0x0d12, 0x0d1f},
+ {0x0d21, 0x0d28}, {0x0d2a, 0x0d39}, {0x0d3e, 0x0d43}, {0x0d46, 0x0d48},
+ {0x0d4a, 0x0d4d}, {0x0d66, 0x0d6f}, {0x0d85, 0x0d96}, {0x0d9a, 0x0db1},
+ {0x0db3, 0x0dbb}, {0x0dc0, 0x0dc6}, {0x0dcf, 0x0dd4}, {0x0dd8, 0x0ddf},
+ {0x0df2, 0x0df4}, {0x0e01, 0x0e1f}, {0x0e21, 0x0e3a}, {0x0e3f, 0x0e5b},
+ {0x0e94, 0x0e97}, {0x0e99, 0x0e9f}, {0x0ea1, 0x0ea3}, {0x0ead, 0x0eb9},
+ {0x0ebb, 0x0ebd}, {0x0ec0, 0x0ec4}, {0x0ec8, 0x0ecd}, {0x0ed0, 0x0ed9},
+ {0x0f00, 0x0f1f}, {0x0f21, 0x0f47}, {0x0f49, 0x0f6a}, {0x0f71, 0x0f8b},
+ {0x0f90, 0x0f97}, {0x0f99, 0x0fbc}, {0x0fbe, 0x0fcc}, {0x1000, 0x101f},
+ {0x1023, 0x1027}, {0x102c, 0x1032}, {0x1036, 0x1039}, {0x1040, 0x1059},
+ {0x10a0, 0x10c5}, {0x10d0, 0x10f6}, {0x1100, 0x111f}, {0x1121, 0x1159},
+ {0x115f, 0x11a2}, {0x11a8, 0x11f9}, {0x1200, 0x1206}, {0x1208, 0x121f},
+ {0x1221, 0x1246}, {0x124a, 0x124d}, {0x1250, 0x1256}, {0x125a, 0x125d},
+ {0x1260, 0x1286}, {0x128a, 0x128d}, {0x1290, 0x12ae}, {0x12b2, 0x12b5},
+ {0x12b8, 0x12be}, {0x12c2, 0x12c5}, {0x12c8, 0x12ce}, {0x12d0, 0x12d6},
+ {0x12d8, 0x12ee}, {0x12f0, 0x130e}, {0x1312, 0x1315}, {0x1318, 0x131e},
+ {0x1321, 0x1346}, {0x1348, 0x135a}, {0x1361, 0x137c}, {0x13a0, 0x13f4},
+ {0x1401, 0x141f}, {0x1421, 0x151f}, {0x1521, 0x161f}, {0x1621, 0x1676},
+ {0x1680, 0x169c}, {0x16a0, 0x16f0}, {0x1780, 0x17dc}, {0x17e0, 0x17e9},
+ {0x1800, 0x180a}, {0x1810, 0x1819}, {0x1821, 0x1877}, {0x1880, 0x18a9},
{0x1e00, 0x1e1f}, {0x1e21, 0x1e9b}, {0x1ea0, 0x1ef9}, {0x1f00, 0x1f15},
{0x1f18, 0x1f1d}, {0x1f21, 0x1f45}, {0x1f48, 0x1f4d}, {0x1f50, 0x1f57},
{0x1f5f, 0x1f7d}, {0x1f80, 0x1fb4}, {0x1fb6, 0x1fc4}, {0x1fc6, 0x1fd3},
{0x1fd6, 0x1fdb}, {0x1fdd, 0x1fef}, {0x1ff2, 0x1ff4}, {0x1ff6, 0x1ffe},
- {0x2000, 0x200b}, {0x2010, 0x201f}, {0x2021, 0x2029}, {0x2030, 0x2046},
- {0x2074, 0x208e}, {0x20a0, 0x20ac}, {0x20d0, 0x20e1}, {0x2100, 0x211f},
- {0x2121, 0x2138}, {0x2153, 0x2182}, {0x2190, 0x21ea}, {0x2200, 0x221f},
- {0x2221, 0x22f1}, {0x2302, 0x231f}, {0x2321, 0x237a}, {0x2400, 0x241f},
- {0x2421, 0x2424}, {0x2440, 0x244a}, {0x2460, 0x24ea}, {0x2500, 0x251f},
- {0x2521, 0x2595}, {0x25a0, 0x25ef}, {0x2600, 0x2613}, {0x261a, 0x261f},
- {0x2621, 0x266f}, {0x2701, 0x2704}, {0x2706, 0x2709}, {0x270c, 0x271f},
- {0x2721, 0x2727}, {0x2729, 0x274b}, {0x274f, 0x2752}, {0x2758, 0x275e},
- {0x2761, 0x2767}, {0x2776, 0x2794}, {0x2798, 0x27af}, {0x27b1, 0x27be},
- {0x3000, 0x301f}, {0x3021, 0x3037}, {0x3041, 0x3094}, {0x3099, 0x309e},
- {0x30a1, 0x30fe}, {0x3105, 0x311f}, {0x3121, 0x312c}, {0x3131, 0x318e},
- {0x3190, 0x319f}, {0x3200, 0x321c}, {0x3221, 0x3243}, {0x3260, 0x327b},
- {0x327f, 0x32b0}, {0x32c0, 0x32cb}, {0x32d0, 0x32fe}, {0x3300, 0x331f},
- {0x3321, 0x3376}, {0x337b, 0x33dd}, {0x33e0, 0x33fe}, {0x4e00, 0x4e1f},
+ {0x2000, 0x200b}, {0x2010, 0x201f}, {0x2021, 0x2029}, {0x202f, 0x2046},
+ {0x2048, 0x204d}, {0x2074, 0x208e}, {0x20a0, 0x20af}, {0x20d0, 0x20e3},
+ {0x2100, 0x211f}, {0x2121, 0x213a}, {0x2153, 0x2183}, {0x2190, 0x21f3},
+ {0x2200, 0x221f}, {0x2221, 0x22f1}, {0x2300, 0x231f}, {0x2321, 0x237b},
+ {0x237d, 0x239a}, {0x2400, 0x241f}, {0x2421, 0x2426}, {0x2440, 0x244a},
+ {0x2460, 0x24ea}, {0x2500, 0x251f}, {0x2521, 0x2595}, {0x25a0, 0x25f7},
+ {0x2600, 0x2613}, {0x2619, 0x261f}, {0x2621, 0x2671}, {0x2701, 0x2704},
+ {0x2706, 0x2709}, {0x270c, 0x271f}, {0x2721, 0x2727}, {0x2729, 0x274b},
+ {0x274f, 0x2752}, {0x2758, 0x275e}, {0x2761, 0x2767}, {0x2776, 0x2794},
+ {0x2798, 0x27af}, {0x27b1, 0x27be}, {0x2800, 0x281f}, {0x2821, 0x28ff},
+ {0x2e80, 0x2e99}, {0x2e9b, 0x2ef3}, {0x2f00, 0x2f1f}, {0x2f21, 0x2fd5},
+ {0x2ff0, 0x2ffb}, {0x3000, 0x301f}, {0x3021, 0x303a}, {0x3041, 0x3094},
+ {0x3099, 0x309e}, {0x30a1, 0x30fe}, {0x3105, 0x311f}, {0x3121, 0x312c},
+ {0x3131, 0x318e}, {0x3190, 0x31b7}, {0x3200, 0x321c}, {0x3221, 0x3243},
+ {0x3260, 0x327b}, {0x327f, 0x32b0}, {0x32c0, 0x32cb}, {0x32d0, 0x32fe},
+ {0x3300, 0x331f}, {0x3321, 0x3376}, {0x337b, 0x33dd}, {0x33e0, 0x33fe},
+ {0x3400, 0x341f}, {0x3421, 0x351f}, {0x3521, 0x361f}, {0x3621, 0x371f},
+ {0x3721, 0x381f}, {0x3821, 0x391f}, {0x3921, 0x3a1f}, {0x3a21, 0x3b1f},
+ {0x3b21, 0x3c1f}, {0x3c21, 0x3d1f}, {0x3d21, 0x3e1f}, {0x3e21, 0x3f1f},
+ {0x3f21, 0x401f}, {0x4021, 0x411f}, {0x4121, 0x421f}, {0x4221, 0x431f},
+ {0x4321, 0x441f}, {0x4421, 0x451f}, {0x4521, 0x461f}, {0x4621, 0x471f},
+ {0x4721, 0x481f}, {0x4821, 0x491f}, {0x4921, 0x4a1f}, {0x4a21, 0x4b1f},
+ {0x4b21, 0x4c1f}, {0x4c21, 0x4d1f}, {0x4d21, 0x4db5}, {0x4e00, 0x4e1f},
{0x4e21, 0x4f1f}, {0x4f21, 0x501f}, {0x5021, 0x511f}, {0x5121, 0x521f},
{0x5221, 0x531f}, {0x5321, 0x541f}, {0x5421, 0x551f}, {0x5521, 0x561f},
{0x5621, 0x571f}, {0x5721, 0x581f}, {0x5821, 0x591f}, {0x5921, 0x5a1f},
@@ -429,49 +468,55 @@ static crange graphRangeTable[] = {
{0x9221, 0x931f}, {0x9321, 0x941f}, {0x9421, 0x951f}, {0x9521, 0x961f},
{0x9621, 0x971f}, {0x9721, 0x981f}, {0x9821, 0x991f}, {0x9921, 0x9a1f},
{0x9a21, 0x9b1f}, {0x9b21, 0x9c1f}, {0x9c21, 0x9d1f}, {0x9d21, 0x9e1f},
- {0x9e21, 0x9f1f}, {0x9f21, 0x9fa5}, {0xac00, 0xac1f}, {0xac21, 0xad1f},
- {0xad21, 0xae1f}, {0xae21, 0xaf1f}, {0xaf21, 0xb01f}, {0xb021, 0xb11f},
- {0xb121, 0xb21f}, {0xb221, 0xb31f}, {0xb321, 0xb41f}, {0xb421, 0xb51f},
- {0xb521, 0xb61f}, {0xb621, 0xb71f}, {0xb721, 0xb81f}, {0xb821, 0xb91f},
- {0xb921, 0xba1f}, {0xba21, 0xbb1f}, {0xbb21, 0xbc1f}, {0xbc21, 0xbd1f},
- {0xbd21, 0xbe1f}, {0xbe21, 0xbf1f}, {0xbf21, 0xc01f}, {0xc021, 0xc11f},
- {0xc121, 0xc21f}, {0xc221, 0xc31f}, {0xc321, 0xc41f}, {0xc421, 0xc51f},
- {0xc521, 0xc61f}, {0xc621, 0xc71f}, {0xc721, 0xc81f}, {0xc821, 0xc91f},
- {0xc921, 0xca1f}, {0xca21, 0xcb1f}, {0xcb21, 0xcc1f}, {0xcc21, 0xcd1f},
- {0xcd21, 0xce1f}, {0xce21, 0xcf1f}, {0xcf21, 0xd01f}, {0xd021, 0xd11f},
- {0xd121, 0xd21f}, {0xd221, 0xd31f}, {0xd321, 0xd41f}, {0xd421, 0xd51f},
- {0xd521, 0xd61f}, {0xd621, 0xd71f}, {0xd721, 0xd7a3}, {0xf900, 0xf91f},
- {0xf921, 0xfa1f}, {0xfa21, 0xfa2d}, {0xfb00, 0xfb06}, {0xfb13, 0xfb17},
- {0xfb21, 0xfb36}, {0xfb38, 0xfb3c}, {0xfb46, 0xfbb1}, {0xfbd3, 0xfc1f},
- {0xfc21, 0xfd1f}, {0xfd21, 0xfd3f}, {0xfd50, 0xfd8f}, {0xfd92, 0xfdc7},
- {0xfdf0, 0xfdfb}, {0xfe21, 0xfe23}, {0xfe30, 0xfe44}, {0xfe49, 0xfe52},
- {0xfe54, 0xfe66}, {0xfe68, 0xfe6b}, {0xfe70, 0xfe72}, {0xfe76, 0xfefc},
- {0xff01, 0xff1f}, {0xff21, 0xff5e}, {0xff61, 0xffbe}, {0xffc2, 0xffc7},
- {0xffca, 0xffcf}, {0xffd2, 0xffd7}, {0xffda, 0xffdc}, {0xffe0, 0xffe6},
- {0xffe8, 0xffee}
+ {0x9e21, 0x9f1f}, {0x9f21, 0x9fa5}, {0xa000, 0xa01f}, {0xa021, 0xa11f},
+ {0xa121, 0xa21f}, {0xa221, 0xa31f}, {0xa321, 0xa41f}, {0xa421, 0xa48c},
+ {0xa490, 0xa4a1}, {0xa4a4, 0xa4b3}, {0xa4b5, 0xa4c0}, {0xa4c2, 0xa4c4},
+ {0xac00, 0xac1f}, {0xac21, 0xad1f}, {0xad21, 0xae1f}, {0xae21, 0xaf1f},
+ {0xaf21, 0xb01f}, {0xb021, 0xb11f}, {0xb121, 0xb21f}, {0xb221, 0xb31f},
+ {0xb321, 0xb41f}, {0xb421, 0xb51f}, {0xb521, 0xb61f}, {0xb621, 0xb71f},
+ {0xb721, 0xb81f}, {0xb821, 0xb91f}, {0xb921, 0xba1f}, {0xba21, 0xbb1f},
+ {0xbb21, 0xbc1f}, {0xbc21, 0xbd1f}, {0xbd21, 0xbe1f}, {0xbe21, 0xbf1f},
+ {0xbf21, 0xc01f}, {0xc021, 0xc11f}, {0xc121, 0xc21f}, {0xc221, 0xc31f},
+ {0xc321, 0xc41f}, {0xc421, 0xc51f}, {0xc521, 0xc61f}, {0xc621, 0xc71f},
+ {0xc721, 0xc81f}, {0xc821, 0xc91f}, {0xc921, 0xca1f}, {0xca21, 0xcb1f},
+ {0xcb21, 0xcc1f}, {0xcc21, 0xcd1f}, {0xcd21, 0xce1f}, {0xce21, 0xcf1f},
+ {0xcf21, 0xd01f}, {0xd021, 0xd11f}, {0xd121, 0xd21f}, {0xd221, 0xd31f},
+ {0xd321, 0xd41f}, {0xd421, 0xd51f}, {0xd521, 0xd61f}, {0xd621, 0xd71f},
+ {0xd721, 0xd7a3}, {0xf900, 0xf91f}, {0xf921, 0xfa1f}, {0xfa21, 0xfa2d},
+ {0xfb00, 0xfb06}, {0xfb13, 0xfb17}, {0xfb1d, 0xfb1f}, {0xfb21, 0xfb36},
+ {0xfb38, 0xfb3c}, {0xfb46, 0xfbb1}, {0xfbd3, 0xfc1f}, {0xfc21, 0xfd1f},
+ {0xfd21, 0xfd3f}, {0xfd50, 0xfd8f}, {0xfd92, 0xfdc7}, {0xfdf0, 0xfdfb},
+ {0xfe21, 0xfe23}, {0xfe30, 0xfe44}, {0xfe49, 0xfe52}, {0xfe54, 0xfe66},
+ {0xfe68, 0xfe6b}, {0xfe70, 0xfe72}, {0xfe76, 0xfefc}, {0xff01, 0xff1f},
+ {0xff21, 0xff5e}, {0xff61, 0xffbe}, {0xffc2, 0xffc7}, {0xffca, 0xffcf},
+ {0xffd2, 0xffd7}, {0xffda, 0xffdc}, {0xffe0, 0xffe6}, {0xffe8, 0xffee},
+ {0xfffc, 0xffff}
};
#define NUM_GRAPH_RANGE (sizeof(graphRangeTable)/sizeof(crange))
static chr graphCharTable[] = {
- 0x0360, 0x0361, 0x0374, 0x0375, 0x037a, 0x037e, 0x038c, 0x03da, 0x03dc,
- 0x03de, 0x03e0, 0x04c7, 0x04c8, 0x04cb, 0x04cc, 0x04f8, 0x04f9, 0x0589,
- 0x060c, 0x061b, 0x061f, 0x098f, 0x0990, 0x09b2, 0x09bc, 0x09c7, 0x09c8,
- 0x09d7, 0x09dc, 0x09dd, 0x0a02, 0x0a0f, 0x0a10, 0x0a32, 0x0a33, 0x0a35,
- 0x0a36, 0x0a38, 0x0a39, 0x0a3c, 0x0a47, 0x0a48, 0x0a5e, 0x0a8d, 0x0ab2,
- 0x0ab3, 0x0ad0, 0x0ae0, 0x0b0f, 0x0b10, 0x0b32, 0x0b33, 0x0b47, 0x0b48,
- 0x0b56, 0x0b57, 0x0b5c, 0x0b5d, 0x0b82, 0x0b83, 0x0b99, 0x0b9a, 0x0b9c,
- 0x0b9e, 0x0b9f, 0x0ba3, 0x0ba4, 0x0bd7, 0x0c55, 0x0c56, 0x0c60, 0x0c61,
- 0x0c82, 0x0c83, 0x0cd5, 0x0cd6, 0x0cde, 0x0ce0, 0x0ce1, 0x0d02, 0x0d03,
- 0x0d57, 0x0d60, 0x0d61, 0x0e81, 0x0e82, 0x0e84, 0x0e87, 0x0e88, 0x0e8a,
- 0x0e8d, 0x0ea5, 0x0ea7, 0x0eaa, 0x0eab, 0x0ec6, 0x0edc, 0x0edd, 0x0f97,
- 0x0fb9, 0x10fb, 0x1f59, 0x1f5b, 0x1f5d, 0x2070, 0x2300, 0x274d, 0x2756,
- 0x303f, 0xfb1e, 0xfb1f, 0xfb3e, 0xfb40, 0xfb41, 0xfb43, 0xfb44, 0xfe74,
- 0xfffc, 0xfffd
+ 0x0374, 0x0375, 0x037a, 0x037e, 0x038c, 0x0488, 0x0489, 0x04c7, 0x04c8,
+ 0x04cb, 0x04cc, 0x04f8, 0x04f9, 0x0589, 0x058a, 0x060c, 0x061b, 0x061f,
+ 0x098f, 0x0990, 0x09b2, 0x09bc, 0x09c7, 0x09c8, 0x09d7, 0x09dc, 0x09dd,
+ 0x0a02, 0x0a0f, 0x0a10, 0x0a32, 0x0a33, 0x0a35, 0x0a36, 0x0a38, 0x0a39,
+ 0x0a3c, 0x0a47, 0x0a48, 0x0a5e, 0x0a8d, 0x0ab2, 0x0ab3, 0x0ad0, 0x0ae0,
+ 0x0b0f, 0x0b10, 0x0b32, 0x0b33, 0x0b47, 0x0b48, 0x0b56, 0x0b57, 0x0b5c,
+ 0x0b5d, 0x0b82, 0x0b83, 0x0b99, 0x0b9a, 0x0b9c, 0x0b9e, 0x0b9f, 0x0ba3,
+ 0x0ba4, 0x0bd7, 0x0c55, 0x0c56, 0x0c60, 0x0c61, 0x0c82, 0x0c83, 0x0cd5,
+ 0x0cd6, 0x0cde, 0x0ce0, 0x0ce1, 0x0d02, 0x0d03, 0x0d57, 0x0d60, 0x0d61,
+ 0x0d82, 0x0d83, 0x0dbd, 0x0dca, 0x0dd6, 0x0e81, 0x0e82, 0x0e84, 0x0e87,
+ 0x0e88, 0x0e8a, 0x0e8d, 0x0ea5, 0x0ea7, 0x0eaa, 0x0eab, 0x0ec6, 0x0edc,
+ 0x0edd, 0x0fcf, 0x1021, 0x1029, 0x102a, 0x10fb, 0x1248, 0x1258, 0x1288,
+ 0x12b0, 0x12c0, 0x1310, 0x1f59, 0x1f5b, 0x1f5d, 0x2070, 0x274d, 0x2756,
+ 0x303e, 0x303f, 0xa4c6, 0xfb3e, 0xfb40, 0xfb41, 0xfb43, 0xfb44, 0xfe74
};
#define NUM_GRAPH_CHAR (sizeof(graphCharTable)/sizeof(chr))
+/*
+ * End of auto-generated Unicode character ranges declarations.
+ */
#define CH NOCELT
@@ -481,9 +526,12 @@ static chr graphCharTable[] = {
*/
static int
nmcces(v)
-struct vars *v;
+ struct vars *v; /* context */
{
- return 0;
+ /*
+ * No multi-character collating elements defined at the moment.
+ */
+ return 0;
}
/*
@@ -492,9 +540,9 @@ struct vars *v;
*/
static int
nleaders(v)
-struct vars *v;
+ struct vars *v; /* context */
{
- return 0;
+ return 0;
}
/*
@@ -503,10 +551,10 @@ struct vars *v;
*/
static struct cvec *
allmcces(v, cv)
-struct vars *v;
-struct cvec *cv; /* this is supposed to have enough room */
+ struct vars *v; /* context */
+ struct cvec *cv; /* this is supposed to have enough room */
{
- return clearcvec(cv);
+ return clearcvec(cv);
}
/*
@@ -515,36 +563,40 @@ struct cvec *cv; /* this is supposed to have enough room */
*/
static celt
element(v, startp, endp)
-struct vars *v;
-chr *startp; /* points to start of name */
-chr *endp; /* points just past end of name */
+ struct vars *v; /* context */
+ chr *startp; /* points to start of name */
+ chr *endp; /* points just past end of name */
{
- struct cname *cn;
- size_t len;
- Tcl_DString ds;
- char *np;
-
- /* generic: one-chr names stand for themselves */
- assert(startp < endp);
- len = endp - startp;
- if (len == 1)
- return *startp;
-
- NOTE(REG_ULOCALE);
-
- /* search table */
- Tcl_DStringInit(&ds);
- np = Tcl_UniCharToUtfDString(startp, (int)len, &ds);
- for (cn = cnames; cn->name != NULL; cn++)
- if (strlen(cn->name) == len && strncmp(cn->name, np, len) == 0)
- break; /* NOTE BREAK OUT */
- Tcl_DStringFree(&ds);
- if (cn->name != NULL)
- return CHR(cn->code);
-
- /* couldn't find it */
- ERR(REG_ECOLLATE);
- return 0;
+ struct cname *cn;
+ size_t len;
+ Tcl_DString ds;
+ CONST char *np;
+
+ /* generic: one-chr names stand for themselves */
+ assert(startp < endp);
+ len = endp - startp;
+ if (len == 1) {
+ return *startp;
+ }
+
+ NOTE(REG_ULOCALE);
+
+ /* search table */
+ Tcl_DStringInit(&ds);
+ np = Tcl_UniCharToUtfDString(startp, (int)len, &ds);
+ for (cn=cnames; cn->name!=NULL; cn++) {
+ if (strlen(cn->name)==len && strncmp(cn->name, np, len)==0) {
+ break; /* NOTE BREAK OUT */
+ }
+ }
+ Tcl_DStringFree(&ds);
+ if (cn->name != NULL) {
+ return CHR(cn->code);
+ }
+
+ /* couldn't find it */
+ ERR(REG_ECOLLATE);
+ return 0;
}
/*
@@ -553,71 +605,71 @@ chr *endp; /* points just past end of name */
*/
static struct cvec *
range(v, a, b, cases)
-struct vars *v;
-celt a;
-celt b; /* might equal a */
-int cases; /* case-independent? */
+ struct vars *v; /* context */
+ celt a; /* range start */
+ celt b; /* range end, might equal a */
+ int cases; /* case-independent? */
{
- int nchrs;
- struct cvec *cv;
- celt c, lc, uc, tc;
+ int nchrs;
+ struct cvec *cv;
+ celt c, lc, uc, tc;
- if (a != b && !before(a, b)) {
- ERR(REG_ERANGE);
- return NULL;
- }
+ if (a != b && !before(a, b)) {
+ ERR(REG_ERANGE);
+ return NULL;
+ }
- if (!cases) { /* easy version */
- cv = getcvec(v, 0, 1, 0);
- NOERRN();
- addrange(cv, a, b);
- return cv;
- }
+ if (!cases) { /* easy version */
+ cv = getcvec(v, 0, 1, 0);
+ NOERRN();
+ addrange(cv, a, b);
+ return cv;
+ }
- /*
- * When case-independent, it's hard to decide when cvec ranges are
- * usable, so for now at least, we won't try. We allocate enough
- * space for two case variants plus a little extra for the two
- * title case variants.
- */
+ /*
+ * When case-independent, it's hard to decide when cvec ranges are
+ * usable, so for now at least, we won't try. We allocate enough
+ * space for two case variants plus a little extra for the two
+ * title case variants.
+ */
- nchrs = (b - a + 1)*2 + 4;
+ nchrs = (b - a + 1)*2 + 4;
- cv = getcvec(v, nchrs, 0, 0);
- NOERRN();
+ cv = getcvec(v, nchrs, 0, 0);
+ NOERRN();
- for (c = a; c <= b; c++) {
- addchr(cv, c);
- lc = Tcl_UniCharToLower((chr)c);
- uc = Tcl_UniCharToUpper((chr)c);
- tc = Tcl_UniCharToTitle((chr)c);
- if (c != lc) {
- addchr(cv, lc);
- }
- if (c != uc) {
- addchr(cv, uc);
- }
- if (c != tc && tc != uc) {
- addchr(cv, tc);
- }
+ for (c=a; c<=b; c++) {
+ addchr(cv, c);
+ lc = Tcl_UniCharToLower((chr)c);
+ uc = Tcl_UniCharToUpper((chr)c);
+ tc = Tcl_UniCharToTitle((chr)c);
+ if (c != lc) {
+ addchr(cv, lc);
+ }
+ if (c != uc) {
+ addchr(cv, uc);
+ }
+ if (c != tc && tc != uc) {
+ addchr(cv, tc);
}
+ }
- return cv;
+ return cv;
}
/*
- before - is celt x before celt y, for purposes of range legality?
^ static int before(celt, celt);
*/
-static int /* predicate */
+static int /* predicate */
before(x, y)
-celt x;
-celt y;
+ celt x, y; /* collating elements */
{
- /* trivial because no MCCEs */
- if (x < y)
- return 1;
- return 0;
+ /* trivial because no MCCEs */
+ if (x < y) {
+ return 1;
+ }
+ return 0;
}
/*
@@ -627,31 +679,33 @@ celt y;
*/
static struct cvec *
eclass(v, c, cases)
-struct vars *v;
-celt c;
-int cases; /* all cases? */
+ struct vars *v; /* context */
+ celt c; /* Collating element representing
+ * the equivalence class. */
+ int cases; /* all cases? */
{
- struct cvec *cv;
-
- /* crude fake equivalence class for testing */
- if ((v->cflags&REG_FAKE) && c == 'x') {
- cv = getcvec(v, 4, 0, 0);
- addchr(cv, (chr)'x');
- addchr(cv, (chr)'y');
- if (cases) {
- addchr(cv, (chr)'X');
- addchr(cv, (chr)'Y');
- }
- return cv;
+ struct cvec *cv;
+
+ /* crude fake equivalence class for testing */
+ if ((v->cflags&REG_FAKE) && c == 'x') {
+ cv = getcvec(v, 4, 0, 0);
+ addchr(cv, (chr)'x');
+ addchr(cv, (chr)'y');
+ if (cases) {
+ addchr(cv, (chr)'X');
+ addchr(cv, (chr)'Y');
}
-
- /* otherwise, none */
- if (cases)
- return allcases(v, c);
- cv = getcvec(v, 1, 0, 0);
- assert(cv != NULL);
- addchr(cv, (chr)c);
return cv;
+ }
+
+ /* otherwise, none */
+ if (cases) {
+ return allcases(v, c);
+ }
+ cv = getcvec(v, 1, 0, 0);
+ assert(cv != NULL);
+ addchr(cv, (chr)c);
+ return cv;
}
/*
@@ -661,15 +715,16 @@ int cases; /* all cases? */
*/
static struct cvec *
cclass(v, startp, endp, cases)
-struct vars *v;
-chr *startp; /* where the name starts */
-chr *endp; /* just past the end of the name */
-int cases; /* case-independent? */
+ struct vars *v; /* context */
+ chr *startp; /* where the name starts */
+ chr *endp; /* just past the end of the name */
+ int cases; /* case-independent? */
{
size_t len;
struct cvec *cv = NULL;
Tcl_DString ds;
- char *np, **namePtr;
+ CONST char *np;
+ char **namePtr;
int i, index;
/*
@@ -709,7 +764,7 @@ int cases; /* case-independent? */
*/
index = -1;
- for (namePtr = classNames, i = 0; *namePtr != NULL; namePtr++, i++) {
+ for (namePtr=classNames,i=0 ; *namePtr!=NULL ; namePtr++,i++) {
if ((strlen(*namePtr) == len) && (strncmp(*namePtr, np, len) == 0)) {
index = i;
break;
@@ -726,129 +781,137 @@ int cases; /* case-independent? */
*/
switch((enum classes) index) {
- case CC_PRINT:
- case CC_ALNUM:
- cv = getcvec(v, NUM_ALPHA_CHAR,
- NUM_DIGIT_RANGE + NUM_ALPHA_RANGE, 0);
- if (cv) {
- for (i = 0; i < NUM_ALPHA_CHAR; i++) {
- addchr(cv, alphaCharTable[i]);
- }
- for (i = 0; i < NUM_ALPHA_RANGE; i++) {
- addrange(cv, alphaRangeTable[i].start,
- alphaRangeTable[i].end);
- }
- for (i = 0; i < NUM_DIGIT_RANGE; i++) {
- addrange(cv, digitRangeTable[i].start,
- digitRangeTable[i].end);
- }
+ case CC_PRINT:
+ case CC_ALNUM:
+ cv = getcvec(v, NUM_ALPHA_CHAR, NUM_DIGIT_RANGE + NUM_ALPHA_RANGE, 0);
+ if (cv) {
+ for (i=0 ; i<NUM_ALPHA_CHAR ; i++) {
+ addchr(cv, alphaCharTable[i]);
}
- break;
- case CC_ALPHA:
- cv = getcvec(v, NUM_ALPHA_CHAR, NUM_ALPHA_RANGE, 0);
- if (cv) {
- for (i = 0; i < NUM_ALPHA_RANGE; i++) {
- addrange(cv, alphaRangeTable[i].start,
- alphaRangeTable[i].end);
- }
- for (i = 0; i < NUM_ALPHA_CHAR; i++) {
- addchr(cv, alphaCharTable[i]);
- }
+ for (i=0 ; i<NUM_ALPHA_RANGE ; i++) {
+ addrange(cv, alphaRangeTable[i].start,
+ alphaRangeTable[i].end);
}
- break;
- case CC_ASCII:
- cv = getcvec(v, 0, 1, 0);
- if (cv) {
- addrange(cv, 0, 0x7f);
+ for (i=0 ; i<NUM_DIGIT_RANGE ; i++) {
+ addrange(cv, digitRangeTable[i].start,
+ digitRangeTable[i].end);
}
- break;
- case CC_BLANK:
- cv = getcvec(v, 2, 0, 0);
- addchr(cv, '\t');
- addchr(cv, ' ');
- break;
- case CC_CNTRL:
- cv = getcvec(v, 0, 2, 0);
- addrange(cv, 0x0, 0x1f);
- addrange(cv, 0x7f, 0x9f);
- break;
- case CC_DIGIT:
- cv = getcvec(v, 0, NUM_DIGIT_RANGE, 0);
- if (cv) {
- for (i = 0; i < NUM_DIGIT_RANGE; i++) {
- addrange(cv, digitRangeTable[i].start,
- digitRangeTable[i].end);
- }
+ }
+ break;
+ case CC_ALPHA:
+ cv = getcvec(v, NUM_ALPHA_CHAR, NUM_ALPHA_RANGE, 0);
+ if (cv) {
+ for (i=0 ; i<NUM_ALPHA_RANGE ; i++) {
+ addrange(cv, alphaRangeTable[i].start,
+ alphaRangeTable[i].end);
}
- break;
- case CC_PUNCT:
- cv = getcvec(v, NUM_PUNCT_CHAR, NUM_PUNCT_RANGE, 0);
- if (cv) {
- for (i = 0; i < NUM_PUNCT_RANGE; i++) {
- addrange(cv, punctRangeTable[i].start,
- punctRangeTable[i].end);
- }
- for (i = 0; i < NUM_PUNCT_CHAR; i++) {
- addchr(cv, punctCharTable[i]);
- }
+ for (i=0 ; i<NUM_ALPHA_CHAR ; i++) {
+ addchr(cv, alphaCharTable[i]);
}
- break;
- case CC_XDIGIT:
- cv = getcvec(v, 0, NUM_DIGIT_RANGE+2, 0);
- if (cv) {
- addrange(cv, '0', '9');
- addrange(cv, 'a', 'f');
- addrange(cv, 'A', 'F');
+ }
+ break;
+ case CC_ASCII:
+ cv = getcvec(v, 0, 1, 0);
+ if (cv) {
+ addrange(cv, 0, 0x7f);
+ }
+ break;
+ case CC_BLANK:
+ cv = getcvec(v, 2, 0, 0);
+ addchr(cv, '\t');
+ addchr(cv, ' ');
+ break;
+ case CC_CNTRL:
+ cv = getcvec(v, 0, 2, 0);
+ addrange(cv, 0x0, 0x1f);
+ addrange(cv, 0x7f, 0x9f);
+ break;
+ case CC_DIGIT:
+ cv = getcvec(v, 0, NUM_DIGIT_RANGE, 0);
+ if (cv) {
+ for (i=0 ; i<NUM_DIGIT_RANGE ; i++) {
+ addrange(cv, digitRangeTable[i].start,
+ digitRangeTable[i].end);
}
- break;
- case CC_SPACE:
- cv = getcvec(v, NUM_SPACE_CHAR, NUM_SPACE_RANGE, 0);
- if (cv) {
- for (i = 0; i < NUM_SPACE_RANGE; i++) {
- addrange(cv, spaceRangeTable[i].start,
- spaceRangeTable[i].end);
- }
- for (i = 0; i < NUM_SPACE_CHAR; i++) {
- addchr(cv, spaceCharTable[i]);
- }
+ }
+ break;
+ case CC_PUNCT:
+ cv = getcvec(v, NUM_PUNCT_CHAR, NUM_PUNCT_RANGE, 0);
+ if (cv) {
+ for (i=0 ; i<NUM_PUNCT_RANGE ; i++) {
+ addrange(cv, punctRangeTable[i].start,
+ punctRangeTable[i].end);
}
- break;
- case CC_LOWER:
- cv = getcvec(v, NUM_LOWER_CHAR, NUM_LOWER_RANGE, 0);
- if (cv) {
- for (i = 0; i < NUM_LOWER_RANGE; i++) {
- addrange(cv, lowerRangeTable[i].start,
- lowerRangeTable[i].end);
- }
- for (i = 0; i < NUM_LOWER_CHAR; i++) {
- addchr(cv, lowerCharTable[i]);
- }
+ for (i=0 ; i<NUM_PUNCT_CHAR ; i++) {
+ addchr(cv, punctCharTable[i]);
}
- break;
- case CC_UPPER:
- cv = getcvec(v, NUM_UPPER_CHAR, NUM_UPPER_RANGE, 0);
- if (cv) {
- for (i = 0; i < NUM_UPPER_RANGE; i++) {
- addrange(cv, upperRangeTable[i].start,
- upperRangeTable[i].end);
- }
- for (i = 0; i < NUM_UPPER_CHAR; i++) {
- addchr(cv, upperCharTable[i]);
- }
+ }
+ break;
+ case CC_XDIGIT:
+ /*
+ * This is a 3 instead of (NUM_DIGIT_RANGE+2) because I've no
+ * idea how to define the digits 'a' through 'f' in
+ * non-western locales. The concept is quite possibly non
+ * portable, or only used in contextx where the characters
+ * used would be the western ones anyway! Whatever is
+ * actually the case, the number of ranges is fixed (until
+ * someone comes up with a better arrangement!)
+ */
+ cv = getcvec(v, 0, 3, 0);
+ if (cv) {
+ addrange(cv, '0', '9');
+ addrange(cv, 'a', 'f');
+ addrange(cv, 'A', 'F');
+ }
+ break;
+ case CC_SPACE:
+ cv = getcvec(v, NUM_SPACE_CHAR, NUM_SPACE_RANGE, 0);
+ if (cv) {
+ for (i=0 ; i<NUM_SPACE_RANGE ; i++) {
+ addrange(cv, spaceRangeTable[i].start,
+ spaceRangeTable[i].end);
}
- break;
- case CC_GRAPH:
- cv = getcvec(v, NUM_GRAPH_CHAR, NUM_GRAPH_RANGE, 0);
- if (cv) {
- for (i = 0; i < NUM_GRAPH_RANGE; i++) {
- addrange(cv, graphRangeTable[i].start,
- graphRangeTable[i].end);
- }
- for (i = 0; i < NUM_GRAPH_CHAR; i++) {
- addchr(cv, graphCharTable[i]);
- }
+ for (i=0 ; i<NUM_SPACE_CHAR ; i++) {
+ addchr(cv, spaceCharTable[i]);
}
- break;
+ }
+ break;
+ case CC_LOWER:
+ cv = getcvec(v, NUM_LOWER_CHAR, NUM_LOWER_RANGE, 0);
+ if (cv) {
+ for (i=0 ; i<NUM_LOWER_RANGE ; i++) {
+ addrange(cv, lowerRangeTable[i].start,
+ lowerRangeTable[i].end);
+ }
+ for (i=0 ; i<NUM_LOWER_CHAR ; i++) {
+ addchr(cv, lowerCharTable[i]);
+ }
+ }
+ break;
+ case CC_UPPER:
+ cv = getcvec(v, NUM_UPPER_CHAR, NUM_UPPER_RANGE, 0);
+ if (cv) {
+ for (i=0 ; i<NUM_UPPER_RANGE ; i++) {
+ addrange(cv, upperRangeTable[i].start,
+ upperRangeTable[i].end);
+ }
+ for (i=0 ; i<NUM_UPPER_CHAR ; i++) {
+ addchr(cv, upperCharTable[i]);
+ }
+ }
+ break;
+ case CC_GRAPH:
+ cv = getcvec(v, NUM_GRAPH_CHAR, NUM_GRAPH_RANGE, 0);
+ if (cv) {
+ for (i=0 ; i<NUM_GRAPH_RANGE ; i++) {
+ addrange(cv, graphRangeTable[i].start,
+ graphRangeTable[i].end);
+ }
+ for (i=0 ; i<NUM_GRAPH_CHAR ; i++) {
+ addchr(cv, graphCharTable[i]);
+ }
+ }
+ break;
}
if (cv == NULL) {
ERR(REG_ESPACE);
@@ -864,28 +927,28 @@ int cases; /* case-independent? */
*/
static struct cvec *
allcases(v, pc)
-struct vars *v;
-pchr pc;
+ struct vars *v; /* context */
+ pchr pc; /* character to get case equivs of */
{
- struct cvec *cv;
- chr c = (chr)pc;
- chr lc, uc, tc;
-
- lc = Tcl_UniCharToLower((chr)c);
- uc = Tcl_UniCharToUpper((chr)c);
- tc = Tcl_UniCharToTitle((chr)c);
-
- if (tc != uc) {
- cv = getcvec(v, 3, 0, 0);
- addchr(cv, tc);
- } else {
- cv = getcvec(v, 2, 0, 0);
- }
- addchr(cv, lc);
- if (lc != uc) {
- addchr(cv, uc);
- }
- return cv;
+ struct cvec *cv;
+ chr c = (chr)pc;
+ chr lc, uc, tc;
+
+ lc = Tcl_UniCharToLower((chr)c);
+ uc = Tcl_UniCharToUpper((chr)c);
+ tc = Tcl_UniCharToTitle((chr)c);
+
+ if (tc != uc) {
+ cv = getcvec(v, 3, 0, 0);
+ addchr(cv, tc);
+ } else {
+ cv = getcvec(v, 2, 0, 0);
+ }
+ addchr(cv, lc);
+ if (lc != uc) {
+ addchr(cv, uc);
+ }
+ return cv;
}
/*
@@ -896,13 +959,12 @@ pchr pc;
* stop at embedded NULs!
^ static int cmp(CONST chr *, CONST chr *, size_t);
*/
-static int /* 0 for equal, nonzero for unequal */
+static int /* 0 for equal, nonzero for unequal */
cmp(x, y, len)
-CONST chr *x;
-CONST chr *y;
-size_t len; /* exact length of comparison */
+ CONST chr *x, *y; /* strings to compare */
+ size_t len; /* exact length of comparison */
{
- return memcmp(VS(x), VS(y), len*sizeof(chr));
+ return memcmp(VS(x), VS(y), len*sizeof(chr));
}
/*
@@ -913,18 +975,15 @@ size_t len; /* exact length of comparison */
* stop at embedded NULs!
^ static int casecmp(CONST chr *, CONST chr *, size_t);
*/
-static int /* 0 for equal, nonzero for unequal */
+static int /* 0 for equal, nonzero for unequal */
casecmp(x, y, len)
-CONST chr *x;
-CONST chr *y;
-size_t len; /* exact length of comparison */
+ CONST chr *x, *y; /* strings to compare */
+ size_t len; /* exact length of comparison */
{
- size_t i;
- CONST chr *xp;
- CONST chr *yp;
-
- for (xp = x, yp = y, i = len; i > 0; i--)
- if (Tcl_UniCharToLower(*xp++) != Tcl_UniCharToLower(*yp++))
- return 1;
- return 0;
+ for (; len > 0; len--, x++, y++) {
+ if ((*x!=*y) && (Tcl_UniCharToLower(*x) != Tcl_UniCharToLower(*y))) {
+ return 1;
+ }
+ }
+ return 0;
}
diff --git a/tcl/generic/tcl.decls b/tcl/generic/tcl.decls
index 7b9a74bdf9b..676b2b58da3 100644
--- a/tcl/generic/tcl.decls
+++ b/tcl/generic/tcl.decls
@@ -7,6 +7,7 @@
#
#
# Copyright (c) 1998-1999 by Scriptics Corporation.
+# Copyright (c) 2001, 2002 by Kevin B. Kenny. All rights reserved.
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
@@ -27,15 +28,15 @@ hooks {tclPlat tclInt tclIntPlat}
# to preserve backwards compatibility.
declare 0 generic {
- int Tcl_PkgProvideEx(Tcl_Interp *interp, char *name, char *version, \
- ClientData clientData)
+ int Tcl_PkgProvideEx(Tcl_Interp* interp, CONST char* name,
+ CONST char* version, ClientData clientData)
}
declare 1 generic {
- char * Tcl_PkgRequireEx(Tcl_Interp *interp, char *name, char *version, \
- int exact, ClientData *clientDataPtr)
+ CONST84_RETURN char * Tcl_PkgRequireEx(Tcl_Interp *interp, CONST char *name,
+ CONST char *version, int exact, ClientData *clientDataPtr)
}
declare 2 generic {
- void Tcl_Panic(char *format, ...)
+ void Tcl_Panic(CONST char *format, ...)
}
declare 3 generic {
char * Tcl_Alloc(unsigned int size)
@@ -47,13 +48,14 @@ declare 5 generic {
char * Tcl_Realloc(char *ptr, unsigned int size)
}
declare 6 generic {
- char * Tcl_DbCkalloc(unsigned int size, char *file, int line)
+ char * Tcl_DbCkalloc(unsigned int size, CONST char *file, int line)
}
declare 7 generic {
- int Tcl_DbCkfree(char *ptr, char *file, int line)
+ int Tcl_DbCkfree(char *ptr, CONST char *file, int line)
}
declare 8 generic {
- char * Tcl_DbCkrealloc(char *ptr, unsigned int size, char *file, int line)
+ char * Tcl_DbCkrealloc(char *ptr, unsigned int size,
+ CONST char *file, int line)
}
# Tcl_CreateFileHandler and Tcl_DeleteFileHandler are only available on unix,
@@ -61,7 +63,7 @@ declare 8 generic {
# compatibility reasons.
declare 9 unix {
- void Tcl_CreateFileHandler(int fd, int mask, Tcl_FileProc *proc, \
+ void Tcl_CreateFileHandler(int fd, int mask, Tcl_FileProc *proc,
ClientData clientData)
}
declare 10 unix {
@@ -84,47 +86,48 @@ declare 15 generic {
void Tcl_AppendStringsToObj(Tcl_Obj *objPtr, ...)
}
declare 16 generic {
- void Tcl_AppendToObj(Tcl_Obj *objPtr, char *bytes, int length)
+ void Tcl_AppendToObj(Tcl_Obj* objPtr, CONST char* bytes, int length)
}
declare 17 generic {
Tcl_Obj * Tcl_ConcatObj(int objc, Tcl_Obj *CONST objv[])
}
declare 18 generic {
- int Tcl_ConvertToType(Tcl_Interp *interp, Tcl_Obj *objPtr, \
+ int Tcl_ConvertToType(Tcl_Interp *interp, Tcl_Obj *objPtr,
Tcl_ObjType *typePtr)
}
declare 19 generic {
- void Tcl_DbDecrRefCount(Tcl_Obj *objPtr, char *file, int line)
+ void Tcl_DbDecrRefCount(Tcl_Obj *objPtr, CONST char *file, int line)
}
declare 20 generic {
- void Tcl_DbIncrRefCount(Tcl_Obj *objPtr, char *file, int line)
+ void Tcl_DbIncrRefCount(Tcl_Obj *objPtr, CONST char *file, int line)
}
declare 21 generic {
- int Tcl_DbIsShared(Tcl_Obj *objPtr, char *file, int line)
+ int Tcl_DbIsShared(Tcl_Obj *objPtr, CONST char *file, int line)
}
declare 22 generic {
- Tcl_Obj * Tcl_DbNewBooleanObj(int boolValue, char *file, int line)
+ Tcl_Obj * Tcl_DbNewBooleanObj(int boolValue, CONST char *file, int line)
}
declare 23 generic {
- Tcl_Obj * Tcl_DbNewByteArrayObj(unsigned char *bytes, int length, \
- char *file, int line)
+ Tcl_Obj * Tcl_DbNewByteArrayObj(CONST unsigned char *bytes, int length,
+ CONST char *file, int line)
}
declare 24 generic {
- Tcl_Obj * Tcl_DbNewDoubleObj(double doubleValue, char *file, int line)
+ Tcl_Obj * Tcl_DbNewDoubleObj(double doubleValue,
+ CONST char *file, int line)
}
declare 25 generic {
- Tcl_Obj * Tcl_DbNewListObj(int objc, Tcl_Obj *CONST objv[], char *file, \
- int line)
+ Tcl_Obj * Tcl_DbNewListObj(int objc, Tcl_Obj *CONST *objv,
+ CONST char *file, int line)
}
declare 26 generic {
- Tcl_Obj * Tcl_DbNewLongObj(long longValue, char *file, int line)
+ Tcl_Obj * Tcl_DbNewLongObj(long longValue, CONST char *file, int line)
}
declare 27 generic {
- Tcl_Obj * Tcl_DbNewObj(char *file, int line)
+ Tcl_Obj * Tcl_DbNewObj(CONST char *file, int line)
}
declare 28 generic {
- Tcl_Obj * Tcl_DbNewStringObj(CONST char *bytes, int length, \
- char *file, int line)
+ Tcl_Obj * Tcl_DbNewStringObj(CONST char *bytes, int length,
+ CONST char *file, int line)
}
declare 29 generic {
Tcl_Obj * Tcl_DuplicateObj(Tcl_Obj *objPtr)
@@ -133,28 +136,28 @@ declare 30 generic {
void TclFreeObj(Tcl_Obj *objPtr)
}
declare 31 generic {
- int Tcl_GetBoolean(Tcl_Interp *interp, char *str, int *boolPtr)
+ int Tcl_GetBoolean(Tcl_Interp *interp, CONST char *str, int *boolPtr)
}
declare 32 generic {
- int Tcl_GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, \
+ int Tcl_GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
int *boolPtr)
}
declare 33 generic {
unsigned char * Tcl_GetByteArrayFromObj(Tcl_Obj *objPtr, int *lengthPtr)
}
declare 34 generic {
- int Tcl_GetDouble(Tcl_Interp *interp, char *str, double *doublePtr)
+ int Tcl_GetDouble(Tcl_Interp *interp, CONST char *str, double *doublePtr)
}
declare 35 generic {
- int Tcl_GetDoubleFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, \
+ int Tcl_GetDoubleFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
double *doublePtr)
}
declare 36 generic {
- int Tcl_GetIndexFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, \
- char **tablePtr, char *msg, int flags, int *indexPtr)
+ int Tcl_GetIndexFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ CONST84 char **tablePtr, CONST char *msg, int flags, int *indexPtr)
}
declare 37 generic {
- int Tcl_GetInt(Tcl_Interp *interp, char *str, int *intPtr)
+ int Tcl_GetInt(Tcl_Interp *interp, CONST char *str, int *intPtr)
}
declare 38 generic {
int Tcl_GetIntFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *intPtr)
@@ -163,7 +166,7 @@ declare 39 generic {
int Tcl_GetLongFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, long *longPtr)
}
declare 40 generic {
- Tcl_ObjType * Tcl_GetObjType(char *typeName)
+ Tcl_ObjType * Tcl_GetObjType(CONST char *typeName)
}
declare 41 generic {
char * Tcl_GetStringFromObj(Tcl_Obj *objPtr, int *lengthPtr)
@@ -172,33 +175,34 @@ declare 42 generic {
void Tcl_InvalidateStringRep(Tcl_Obj *objPtr)
}
declare 43 generic {
- int Tcl_ListObjAppendList(Tcl_Interp *interp, Tcl_Obj *listPtr, \
+ int Tcl_ListObjAppendList(Tcl_Interp *interp, Tcl_Obj *listPtr,
Tcl_Obj *elemListPtr)
}
declare 44 generic {
- int Tcl_ListObjAppendElement(Tcl_Interp *interp, Tcl_Obj *listPtr, \
+ int Tcl_ListObjAppendElement(Tcl_Interp *interp, Tcl_Obj *listPtr,
Tcl_Obj *objPtr)
}
declare 45 generic {
- int Tcl_ListObjGetElements(Tcl_Interp *interp, Tcl_Obj *listPtr, \
+ int Tcl_ListObjGetElements(Tcl_Interp *interp, Tcl_Obj *listPtr,
int *objcPtr, Tcl_Obj ***objvPtr)
}
declare 46 generic {
- int Tcl_ListObjIndex(Tcl_Interp *interp, Tcl_Obj *listPtr, int index, \
+ int Tcl_ListObjIndex(Tcl_Interp *interp, Tcl_Obj *listPtr, int index,
Tcl_Obj **objPtrPtr)
}
declare 47 generic {
- int Tcl_ListObjLength(Tcl_Interp *interp, Tcl_Obj *listPtr, int *intPtr)
+ int Tcl_ListObjLength(Tcl_Interp *interp, Tcl_Obj *listPtr,
+ int *lengthPtr)
}
declare 48 generic {
- int Tcl_ListObjReplace(Tcl_Interp *interp, Tcl_Obj *listPtr, int first, \
+ int Tcl_ListObjReplace(Tcl_Interp *interp, Tcl_Obj *listPtr, int first,
int count, int objc, Tcl_Obj *CONST objv[])
}
declare 49 generic {
- Tcl_Obj * Tcl_NewBooleanObj(int boolValue)
+ Tcl_Obj *Tcl_NewBooleanObj(int boolValue)
}
declare 50 generic {
- Tcl_Obj * Tcl_NewByteArrayObj(unsigned char *bytes, int length)
+ Tcl_Obj *Tcl_NewByteArrayObj(CONST unsigned char* bytes, int length)
}
declare 51 generic {
Tcl_Obj * Tcl_NewDoubleObj(double doubleValue)
@@ -225,7 +229,8 @@ declare 58 generic {
unsigned char * Tcl_SetByteArrayLength(Tcl_Obj *objPtr, int length)
}
declare 59 generic {
- void Tcl_SetByteArrayObj(Tcl_Obj *objPtr, unsigned char *bytes, int length)
+ void Tcl_SetByteArrayObj(Tcl_Obj *objPtr, CONST unsigned char *bytes,
+ int length)
}
declare 60 generic {
void Tcl_SetDoubleObj(Tcl_Obj *objPtr, double doubleValue)
@@ -243,13 +248,13 @@ declare 64 generic {
void Tcl_SetObjLength(Tcl_Obj *objPtr, int length)
}
declare 65 generic {
- void Tcl_SetStringObj(Tcl_Obj *objPtr, char *bytes, int length)
+ void Tcl_SetStringObj(Tcl_Obj* objPtr, CONST char* bytes, int length)
}
declare 66 generic {
void Tcl_AddErrorInfo(Tcl_Interp *interp, CONST char *message)
}
declare 67 generic {
- void Tcl_AddObjErrorInfo(Tcl_Interp *interp, CONST char *message, \
+ void Tcl_AddObjErrorInfo(Tcl_Interp *interp, CONST char *message,
int length)
}
declare 68 generic {
@@ -262,7 +267,7 @@ declare 70 generic {
void Tcl_AppendResult(Tcl_Interp *interp, ...)
}
declare 71 generic {
- Tcl_AsyncHandler Tcl_AsyncCreate(Tcl_AsyncProc *proc, \
+ Tcl_AsyncHandler Tcl_AsyncCreate(Tcl_AsyncProc *proc,
ClientData clientData)
}
declare 72 generic {
@@ -284,11 +289,11 @@ declare 77 generic {
char Tcl_Backslash(CONST char *src, int *readPtr)
}
declare 78 generic {
- int Tcl_BadChannelOption(Tcl_Interp *interp, char *optionName, \
- char *optionList)
+ int Tcl_BadChannelOption(Tcl_Interp *interp, CONST char *optionName,
+ CONST char *optionList)
}
declare 79 generic {
- void Tcl_CallWhenDeleted(Tcl_Interp *interp, Tcl_InterpDeleteProc *proc, \
+ void Tcl_CallWhenDeleted(Tcl_Interp *interp, Tcl_InterpDeleteProc *proc,
ClientData clientData)
}
declare 80 generic {
@@ -298,46 +303,47 @@ declare 81 generic {
int Tcl_Close(Tcl_Interp *interp, Tcl_Channel chan)
}
declare 82 generic {
- int Tcl_CommandComplete(char *cmd)
+ int Tcl_CommandComplete(CONST char *cmd)
}
declare 83 generic {
- char * Tcl_Concat(int argc, char **argv)
+ char * Tcl_Concat(int argc, CONST84 char * CONST *argv)
}
declare 84 generic {
int Tcl_ConvertElement(CONST char *src, char *dst, int flags)
}
declare 85 generic {
- int Tcl_ConvertCountedElement(CONST char *src, int length, char *dst, \
+ int Tcl_ConvertCountedElement(CONST char *src, int length, char *dst,
int flags)
}
declare 86 generic {
- int Tcl_CreateAlias(Tcl_Interp *slave, char *slaveCmd, \
- Tcl_Interp *target, char *targetCmd, int argc, char **argv)
+ int Tcl_CreateAlias(Tcl_Interp *slave, CONST char *slaveCmd,
+ Tcl_Interp *target, CONST char *targetCmd, int argc,
+ CONST84 char * CONST *argv)
}
declare 87 generic {
- int Tcl_CreateAliasObj(Tcl_Interp *slave, char *slaveCmd, \
- Tcl_Interp *target, char *targetCmd, int objc, \
+ int Tcl_CreateAliasObj(Tcl_Interp *slave, CONST char *slaveCmd,
+ Tcl_Interp *target, CONST char *targetCmd, int objc,
Tcl_Obj *CONST objv[])
}
declare 88 generic {
- Tcl_Channel Tcl_CreateChannel(Tcl_ChannelType *typePtr, char *chanName, \
- ClientData instanceData, int mask)
+ Tcl_Channel Tcl_CreateChannel(Tcl_ChannelType *typePtr,
+ CONST char *chanName, ClientData instanceData, int mask)
}
declare 89 generic {
- void Tcl_CreateChannelHandler(Tcl_Channel chan, int mask, \
+ void Tcl_CreateChannelHandler(Tcl_Channel chan, int mask,
Tcl_ChannelProc *proc, ClientData clientData)
}
declare 90 generic {
- void Tcl_CreateCloseHandler(Tcl_Channel chan, Tcl_CloseProc *proc, \
+ void Tcl_CreateCloseHandler(Tcl_Channel chan, Tcl_CloseProc *proc,
ClientData clientData)
}
declare 91 generic {
- Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, char *cmdName, \
- Tcl_CmdProc *proc, ClientData clientData, \
+ Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, CONST char *cmdName,
+ Tcl_CmdProc *proc, ClientData clientData,
Tcl_CmdDeleteProc *deleteProc)
}
declare 92 generic {
- void Tcl_CreateEventSource(Tcl_EventSetupProc *setupProc, \
+ void Tcl_CreateEventSource(Tcl_EventSetupProc *setupProc,
Tcl_EventCheckProc *checkProc, ClientData clientData)
}
declare 93 generic {
@@ -347,39 +353,41 @@ declare 94 generic {
Tcl_Interp * Tcl_CreateInterp(void)
}
declare 95 generic {
- void Tcl_CreateMathFunc(Tcl_Interp *interp, char *name, int numArgs, \
- Tcl_ValueType *argTypes, Tcl_MathProc *proc, ClientData clientData)
+ void Tcl_CreateMathFunc(Tcl_Interp *interp, CONST char *name,
+ int numArgs, Tcl_ValueType *argTypes,
+ Tcl_MathProc *proc, ClientData clientData)
}
declare 96 generic {
- Tcl_Command Tcl_CreateObjCommand(Tcl_Interp *interp, char *cmdName, \
- Tcl_ObjCmdProc *proc, ClientData clientData, \
+ Tcl_Command Tcl_CreateObjCommand(Tcl_Interp *interp,
+ CONST char *cmdName,
+ Tcl_ObjCmdProc *proc, ClientData clientData,
Tcl_CmdDeleteProc *deleteProc)
}
declare 97 generic {
- Tcl_Interp * Tcl_CreateSlave(Tcl_Interp *interp, char *slaveName, \
+ Tcl_Interp * Tcl_CreateSlave(Tcl_Interp *interp, CONST char *slaveName,
int isSafe)
}
declare 98 generic {
- Tcl_TimerToken Tcl_CreateTimerHandler(int milliseconds, \
+ Tcl_TimerToken Tcl_CreateTimerHandler(int milliseconds,
Tcl_TimerProc *proc, ClientData clientData)
}
declare 99 generic {
- Tcl_Trace Tcl_CreateTrace(Tcl_Interp *interp, int level, \
+ Tcl_Trace Tcl_CreateTrace(Tcl_Interp *interp, int level,
Tcl_CmdTraceProc *proc, ClientData clientData)
}
declare 100 generic {
- void Tcl_DeleteAssocData(Tcl_Interp *interp, char *name)
+ void Tcl_DeleteAssocData(Tcl_Interp *interp, CONST char *name)
}
declare 101 generic {
- void Tcl_DeleteChannelHandler(Tcl_Channel chan, Tcl_ChannelProc *proc, \
+ void Tcl_DeleteChannelHandler(Tcl_Channel chan, Tcl_ChannelProc *proc,
ClientData clientData)
}
declare 102 generic {
- void Tcl_DeleteCloseHandler(Tcl_Channel chan, Tcl_CloseProc *proc, \
+ void Tcl_DeleteCloseHandler(Tcl_Channel chan, Tcl_CloseProc *proc,
ClientData clientData)
}
declare 103 generic {
- int Tcl_DeleteCommand(Tcl_Interp *interp, char *cmdName)
+ int Tcl_DeleteCommand(Tcl_Interp *interp, CONST char *cmdName)
}
declare 104 generic {
int Tcl_DeleteCommandFromToken(Tcl_Interp *interp, Tcl_Command command)
@@ -388,7 +396,7 @@ declare 105 generic {
void Tcl_DeleteEvents(Tcl_EventDeleteProc *proc, ClientData clientData)
}
declare 106 generic {
- void Tcl_DeleteEventSource(Tcl_EventSetupProc *setupProc, \
+ void Tcl_DeleteEventSource(Tcl_EventSetupProc *setupProc,
Tcl_EventCheckProc *checkProc, ClientData clientData)
}
declare 107 generic {
@@ -413,7 +421,7 @@ declare 113 generic {
void Tcl_DeleteTrace(Tcl_Interp *interp, Tcl_Trace trace)
}
declare 114 generic {
- void Tcl_DontCallWhenDeleted(Tcl_Interp *interp, \
+ void Tcl_DontCallWhenDeleted(Tcl_Interp *interp,
Tcl_InterpDeleteProc *proc, ClientData clientData)
}
declare 115 generic {
@@ -453,16 +461,17 @@ declare 126 generic {
int Tcl_Eof(Tcl_Channel chan)
}
declare 127 generic {
- char * Tcl_ErrnoId(void)
+ CONST84_RETURN char * Tcl_ErrnoId(void)
}
declare 128 generic {
- char * Tcl_ErrnoMsg(int err)
+ CONST84_RETURN char * Tcl_ErrnoMsg(int err)
}
declare 129 generic {
- int Tcl_Eval(Tcl_Interp *interp, char *string)
+ int Tcl_Eval(Tcl_Interp *interp, CONST char *string)
}
+# This is obsolete, use Tcl_FSEvalFile
declare 130 generic {
- int Tcl_EvalFile(Tcl_Interp *interp, char *fileName)
+ int Tcl_EvalFile(Tcl_Interp *interp, CONST char *fileName)
}
declare 131 generic {
int Tcl_EvalObj(Tcl_Interp *interp, Tcl_Obj *objPtr)
@@ -474,33 +483,33 @@ declare 133 generic {
void Tcl_Exit(int status)
}
declare 134 generic {
- int Tcl_ExposeCommand(Tcl_Interp *interp, char *hiddenCmdToken, \
- char *cmdName)
+ int Tcl_ExposeCommand(Tcl_Interp *interp, CONST char *hiddenCmdToken,
+ CONST char *cmdName)
}
declare 135 generic {
- int Tcl_ExprBoolean(Tcl_Interp *interp, char *str, int *ptr)
+ int Tcl_ExprBoolean(Tcl_Interp *interp, CONST char *str, int *ptr)
}
declare 136 generic {
int Tcl_ExprBooleanObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *ptr)
}
declare 137 generic {
- int Tcl_ExprDouble(Tcl_Interp *interp, char *str, double *ptr)
+ int Tcl_ExprDouble(Tcl_Interp *interp, CONST char *str, double *ptr)
}
declare 138 generic {
int Tcl_ExprDoubleObj(Tcl_Interp *interp, Tcl_Obj *objPtr, double *ptr)
}
declare 139 generic {
- int Tcl_ExprLong(Tcl_Interp *interp, char *str, long *ptr)
+ int Tcl_ExprLong(Tcl_Interp *interp, CONST char *str, long *ptr)
}
declare 140 generic {
int Tcl_ExprLongObj(Tcl_Interp *interp, Tcl_Obj *objPtr, long *ptr)
}
declare 141 generic {
- int Tcl_ExprObj(Tcl_Interp *interp, Tcl_Obj *objPtr, \
+ int Tcl_ExprObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
Tcl_Obj **resultPtrPtr)
}
declare 142 generic {
- int Tcl_ExprString(Tcl_Interp *interp, char *string)
+ int Tcl_ExprString(Tcl_Interp *interp, CONST char *string)
}
declare 143 generic {
void Tcl_Finalize(void)
@@ -509,7 +518,7 @@ declare 144 generic {
void Tcl_FindExecutable(CONST char *argv0)
}
declare 145 generic {
- Tcl_HashEntry * Tcl_FirstHashEntry(Tcl_HashTable *tablePtr, \
+ Tcl_HashEntry * Tcl_FirstHashEntry(Tcl_HashTable *tablePtr,
Tcl_HashSearch *searchPtr)
}
declare 146 generic {
@@ -519,28 +528,28 @@ declare 147 generic {
void Tcl_FreeResult(Tcl_Interp *interp)
}
declare 148 generic {
- int Tcl_GetAlias(Tcl_Interp *interp, char *slaveCmd, \
- Tcl_Interp **targetInterpPtr, char **targetCmdPtr, int *argcPtr, \
- char ***argvPtr)
+ int Tcl_GetAlias(Tcl_Interp *interp, CONST char *slaveCmd,
+ Tcl_Interp **targetInterpPtr, CONST84 char **targetCmdPtr,
+ int *argcPtr, CONST84 char ***argvPtr)
}
declare 149 generic {
- int Tcl_GetAliasObj(Tcl_Interp *interp, char *slaveCmd, \
- Tcl_Interp **targetInterpPtr, char **targetCmdPtr, int *objcPtr, \
- Tcl_Obj ***objv)
+ int Tcl_GetAliasObj(Tcl_Interp *interp, CONST char *slaveCmd,
+ Tcl_Interp **targetInterpPtr, CONST84 char **targetCmdPtr,
+ int *objcPtr, Tcl_Obj ***objv)
}
declare 150 generic {
- ClientData Tcl_GetAssocData(Tcl_Interp *interp, char *name, \
+ ClientData Tcl_GetAssocData(Tcl_Interp *interp, CONST char *name,
Tcl_InterpDeleteProc **procPtr)
}
declare 151 generic {
- Tcl_Channel Tcl_GetChannel(Tcl_Interp *interp, char *chanName, \
+ Tcl_Channel Tcl_GetChannel(Tcl_Interp *interp, CONST char *chanName,
int *modePtr)
}
declare 152 generic {
int Tcl_GetChannelBufferSize(Tcl_Channel chan)
}
declare 153 generic {
- int Tcl_GetChannelHandle(Tcl_Channel chan, int direction, \
+ int Tcl_GetChannelHandle(Tcl_Channel chan, int direction,
ClientData *handlePtr)
}
declare 154 generic {
@@ -550,27 +559,28 @@ declare 155 generic {
int Tcl_GetChannelMode(Tcl_Channel chan)
}
declare 156 generic {
- char * Tcl_GetChannelName(Tcl_Channel chan)
+ CONST84_RETURN char * Tcl_GetChannelName(Tcl_Channel chan)
}
declare 157 generic {
- int Tcl_GetChannelOption(Tcl_Interp *interp, Tcl_Channel chan, \
- char *optionName, Tcl_DString *dsPtr)
+ int Tcl_GetChannelOption(Tcl_Interp *interp, Tcl_Channel chan,
+ CONST char *optionName, Tcl_DString *dsPtr)
}
declare 158 generic {
Tcl_ChannelType * Tcl_GetChannelType(Tcl_Channel chan)
}
declare 159 generic {
- int Tcl_GetCommandInfo(Tcl_Interp *interp, char *cmdName, \
+ int Tcl_GetCommandInfo(Tcl_Interp *interp, CONST char *cmdName,
Tcl_CmdInfo *infoPtr)
}
declare 160 generic {
- char * Tcl_GetCommandName(Tcl_Interp *interp, Tcl_Command command)
+ CONST84_RETURN char * Tcl_GetCommandName(Tcl_Interp *interp,
+ Tcl_Command command)
}
declare 161 generic {
int Tcl_GetErrno(void)
}
declare 162 generic {
- char * Tcl_GetHostName(void)
+ CONST84_RETURN char * Tcl_GetHostName(void)
}
declare 163 generic {
int Tcl_GetInterpPath(Tcl_Interp *askInterp, Tcl_Interp *slaveInterp)
@@ -589,12 +599,13 @@ declare 166 generic {
# generic interface, so we inlcude it here for compatibility reasons.
declare 167 unix {
- int Tcl_GetOpenFile(Tcl_Interp *interp, char *str, int forWriting, \
+ int Tcl_GetOpenFile(Tcl_Interp *interp, CONST char *str, int forWriting,
int checkUsage, ClientData *filePtr)
}
-
+# Obsolete. Should now use Tcl_FSGetPathType which is objectified
+# and therefore usually faster.
declare 168 generic {
- Tcl_PathType Tcl_GetPathType(char *path)
+ Tcl_PathType Tcl_GetPathType(CONST char *path)
}
declare 169 generic {
int Tcl_Gets(Tcl_Channel chan, Tcl_DString *dsPtr)
@@ -606,29 +617,31 @@ declare 171 generic {
int Tcl_GetServiceMode(void)
}
declare 172 generic {
- Tcl_Interp * Tcl_GetSlave(Tcl_Interp *interp, char *slaveName)
+ Tcl_Interp * Tcl_GetSlave(Tcl_Interp *interp, CONST char *slaveName)
}
declare 173 generic {
Tcl_Channel Tcl_GetStdChannel(int type)
}
declare 174 generic {
- char * Tcl_GetStringResult(Tcl_Interp *interp)
+ CONST84_RETURN char * Tcl_GetStringResult(Tcl_Interp *interp)
}
declare 175 generic {
- char * Tcl_GetVar(Tcl_Interp *interp, char *varName, int flags)
+ CONST84_RETURN char * Tcl_GetVar(Tcl_Interp *interp, CONST char *varName,
+ int flags)
}
declare 176 generic {
- char * Tcl_GetVar2(Tcl_Interp *interp, char *part1, char *part2, int flags)
+ CONST84_RETURN char * Tcl_GetVar2(Tcl_Interp *interp, CONST char *part1,
+ CONST char *part2, int flags)
}
declare 177 generic {
- int Tcl_GlobalEval(Tcl_Interp *interp, char *command)
+ int Tcl_GlobalEval(Tcl_Interp *interp, CONST char *command)
}
declare 178 generic {
int Tcl_GlobalEvalObj(Tcl_Interp *interp, Tcl_Obj *objPtr)
}
declare 179 generic {
- int Tcl_HideCommand(Tcl_Interp *interp, char *cmdName, \
- char *hiddenCmdToken)
+ int Tcl_HideCommand(Tcl_Interp *interp, CONST char *cmdName,
+ CONST char *hiddenCmdToken)
}
declare 180 generic {
int Tcl_Init(Tcl_Interp *interp)
@@ -648,11 +661,14 @@ declare 184 generic {
declare 185 generic {
int Tcl_IsSafe(Tcl_Interp *interp)
}
+# Obsolete, use Tcl_FSJoinPath
declare 186 generic {
- char * Tcl_JoinPath(int argc, char **argv, Tcl_DString *resultPtr)
+ char * Tcl_JoinPath(int argc, CONST84 char * CONST *argv,
+ Tcl_DString *resultPtr)
}
declare 187 generic {
- int Tcl_LinkVar(Tcl_Interp *interp, char *varName, char *addr, int type)
+ int Tcl_LinkVar(Tcl_Interp *interp, CONST char *varName, char *addr,
+ int type)
}
# This slot is reserved for use by the plus patch:
@@ -670,7 +686,7 @@ declare 191 generic {
Tcl_Channel Tcl_MakeTcpClientChannel(ClientData tcpSocket)
}
declare 192 generic {
- char * Tcl_Merge(int argc, char **argv)
+ char * Tcl_Merge(int argc, CONST84 char * CONST *argv)
}
declare 193 generic {
Tcl_HashEntry * Tcl_NextHashEntry(Tcl_HashSearch *searchPtr)
@@ -679,28 +695,30 @@ declare 194 generic {
void Tcl_NotifyChannel(Tcl_Channel channel, int mask)
}
declare 195 generic {
- Tcl_Obj * Tcl_ObjGetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, \
+ Tcl_Obj * Tcl_ObjGetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr,
Tcl_Obj *part2Ptr, int flags)
}
declare 196 generic {
- Tcl_Obj * Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, \
+ Tcl_Obj * Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr,
Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags)
}
declare 197 {unix win} {
- Tcl_Channel Tcl_OpenCommandChannel(Tcl_Interp *interp, int argc, \
- char **argv, int flags)
+ Tcl_Channel Tcl_OpenCommandChannel(Tcl_Interp *interp, int argc,
+ CONST84 char **argv, int flags)
}
+# This is obsolete, use Tcl_FSOpenFileChannel
declare 198 generic {
- Tcl_Channel Tcl_OpenFileChannel(Tcl_Interp *interp, char *fileName, \
- char *modeString, int permissions)
+ Tcl_Channel Tcl_OpenFileChannel(Tcl_Interp *interp, CONST char *fileName,
+ CONST char *modeString, int permissions)
}
declare 199 generic {
- Tcl_Channel Tcl_OpenTcpClient(Tcl_Interp *interp, int port, \
- char *address, char *myaddr, int myport, int async)
+ Tcl_Channel Tcl_OpenTcpClient(Tcl_Interp *interp, int port,
+ CONST char *address, CONST char *myaddr, int myport, int async)
}
declare 200 generic {
- Tcl_Channel Tcl_OpenTcpServer(Tcl_Interp *interp, int port, char *host, \
- Tcl_TcpAcceptProc *acceptProc, ClientData callbackData)
+ Tcl_Channel Tcl_OpenTcpServer(Tcl_Interp *interp, int port,
+ CONST char *host, Tcl_TcpAcceptProc *acceptProc,
+ ClientData callbackData)
}
declare 201 generic {
void Tcl_Preserve(ClientData data)
@@ -712,7 +730,7 @@ declare 203 generic {
int Tcl_PutEnv(CONST char *string)
}
declare 204 generic {
- char * Tcl_PosixError(Tcl_Interp *interp)
+ CONST84_RETURN char * Tcl_PosixError(Tcl_Interp *interp)
}
declare 205 generic {
void Tcl_QueueEvent(Tcl_Event *evPtr, Tcl_QueuePosition position)
@@ -724,7 +742,7 @@ declare 207 {unix win} {
void Tcl_ReapDetachedProcs(void)
}
declare 208 generic {
- int Tcl_RecordAndEval(Tcl_Interp *interp, char *cmd, int flags)
+ int Tcl_RecordAndEval(Tcl_Interp *interp, CONST char *cmd, int flags)
}
declare 209 generic {
int Tcl_RecordAndEvalObj(Tcl_Interp *interp, Tcl_Obj *cmdPtr, int flags)
@@ -736,18 +754,19 @@ declare 211 generic {
void Tcl_RegisterObjType(Tcl_ObjType *typePtr)
}
declare 212 generic {
- Tcl_RegExp Tcl_RegExpCompile(Tcl_Interp *interp, char *string)
+ Tcl_RegExp Tcl_RegExpCompile(Tcl_Interp *interp, CONST char *string)
}
declare 213 generic {
- int Tcl_RegExpExec(Tcl_Interp *interp, Tcl_RegExp regexp, \
+ int Tcl_RegExpExec(Tcl_Interp *interp, Tcl_RegExp regexp,
CONST char *str, CONST char *start)
}
declare 214 generic {
- int Tcl_RegExpMatch(Tcl_Interp *interp, char *str, char *pattern)
+ int Tcl_RegExpMatch(Tcl_Interp *interp, CONST char *str,
+ CONST char *pattern)
}
declare 215 generic {
- void Tcl_RegExpRange(Tcl_RegExp regexp, int index, char **startPtr, \
- char **endPtr)
+ void Tcl_RegExpRange(Tcl_RegExp regexp, int index,
+ CONST84 char **startPtr, CONST84 char **endPtr)
}
declare 216 generic {
void Tcl_Release(ClientData clientData)
@@ -761,8 +780,9 @@ declare 218 generic {
declare 219 generic {
int Tcl_ScanCountedElement(CONST char *str, int length, int *flagPtr)
}
+# Obsolete
declare 220 generic {
- int Tcl_Seek(Tcl_Channel chan, int offset, int mode)
+ int Tcl_SeekOld(Tcl_Channel chan, int offset, int mode)
}
declare 221 generic {
int Tcl_ServiceAll(void)
@@ -771,19 +791,19 @@ declare 222 generic {
int Tcl_ServiceEvent(int flags)
}
declare 223 generic {
- void Tcl_SetAssocData(Tcl_Interp *interp, char *name, \
+ void Tcl_SetAssocData(Tcl_Interp *interp, CONST char *name,
Tcl_InterpDeleteProc *proc, ClientData clientData)
}
declare 224 generic {
void Tcl_SetChannelBufferSize(Tcl_Channel chan, int sz)
}
declare 225 generic {
- int Tcl_SetChannelOption(Tcl_Interp *interp, Tcl_Channel chan, \
- char *optionName, char *newValue)
+ int Tcl_SetChannelOption(Tcl_Interp *interp, Tcl_Channel chan,
+ CONST char *optionName, CONST char *newValue)
}
declare 226 generic {
- int Tcl_SetCommandInfo(Tcl_Interp *interp, char *cmdName, \
- Tcl_CmdInfo *infoPtr)
+ int Tcl_SetCommandInfo(Tcl_Interp *interp, CONST char *cmdName,
+ CONST Tcl_CmdInfo *infoPtr)
}
declare 227 generic {
void Tcl_SetErrno(int err)
@@ -801,7 +821,7 @@ declare 231 generic {
int Tcl_SetRecursionLimit(Tcl_Interp *interp, int depth)
}
declare 232 generic {
- void Tcl_SetResult(Tcl_Interp *interp, char *str, \
+ void Tcl_SetResult(Tcl_Interp *interp, char *str,
Tcl_FreeProc *freeProc)
}
declare 233 generic {
@@ -817,110 +837,115 @@ declare 236 generic {
void Tcl_SetStdChannel(Tcl_Channel channel, int type)
}
declare 237 generic {
- char * Tcl_SetVar(Tcl_Interp *interp, char *varName, char *newValue, \
- int flags)
+ CONST84_RETURN char * Tcl_SetVar(Tcl_Interp *interp, CONST char *varName,
+ CONST char *newValue, int flags)
}
declare 238 generic {
- char * Tcl_SetVar2(Tcl_Interp *interp, char *part1, char *part2, \
- char *newValue, int flags)
+ CONST84_RETURN char * Tcl_SetVar2(Tcl_Interp *interp, CONST char *part1,
+ CONST char *part2, CONST char *newValue, int flags)
}
declare 239 generic {
- char * Tcl_SignalId(int sig)
+ CONST84_RETURN char * Tcl_SignalId(int sig)
}
declare 240 generic {
- char * Tcl_SignalMsg(int sig)
+ CONST84_RETURN char * Tcl_SignalMsg(int sig)
}
declare 241 generic {
void Tcl_SourceRCFile(Tcl_Interp *interp)
}
declare 242 generic {
- int Tcl_SplitList(Tcl_Interp *interp, CONST char *listStr, int *argcPtr, \
- char ***argvPtr)
+ int Tcl_SplitList(Tcl_Interp *interp, CONST char *listStr, int *argcPtr,
+ CONST84 char ***argvPtr)
}
+# Obsolete, use Tcl_FSSplitPath
declare 243 generic {
- void Tcl_SplitPath(CONST char *path, int *argcPtr, char ***argvPtr)
+ void Tcl_SplitPath(CONST char *path, int *argcPtr, CONST84 char ***argvPtr)
}
declare 244 generic {
- void Tcl_StaticPackage(Tcl_Interp *interp, char *pkgName, \
+ void Tcl_StaticPackage(Tcl_Interp *interp, CONST char *pkgName,
Tcl_PackageInitProc *initProc, Tcl_PackageInitProc *safeInitProc)
}
declare 245 generic {
int Tcl_StringMatch(CONST char *str, CONST char *pattern)
}
+# Obsolete
declare 246 generic {
- int Tcl_Tell(Tcl_Channel chan)
+ int Tcl_TellOld(Tcl_Channel chan)
}
declare 247 generic {
- int Tcl_TraceVar(Tcl_Interp *interp, char *varName, int flags, \
+ int Tcl_TraceVar(Tcl_Interp *interp, CONST char *varName, int flags,
Tcl_VarTraceProc *proc, ClientData clientData)
}
declare 248 generic {
- int Tcl_TraceVar2(Tcl_Interp *interp, char *part1, char *part2, \
+ int Tcl_TraceVar2(Tcl_Interp *interp, CONST char *part1, CONST char *part2,
int flags, Tcl_VarTraceProc *proc, ClientData clientData)
}
declare 249 generic {
- char * Tcl_TranslateFileName(Tcl_Interp *interp, char *name, \
+ char * Tcl_TranslateFileName(Tcl_Interp *interp, CONST char *name,
Tcl_DString *bufferPtr)
}
declare 250 generic {
- int Tcl_Ungets(Tcl_Channel chan, char *str, int len, int atHead)
+ int Tcl_Ungets(Tcl_Channel chan, CONST char *str, int len, int atHead)
}
declare 251 generic {
- void Tcl_UnlinkVar(Tcl_Interp *interp, char *varName)
+ void Tcl_UnlinkVar(Tcl_Interp *interp, CONST char *varName)
}
declare 252 generic {
int Tcl_UnregisterChannel(Tcl_Interp *interp, Tcl_Channel chan)
}
declare 253 generic {
- int Tcl_UnsetVar(Tcl_Interp *interp, char *varName, int flags)
+ int Tcl_UnsetVar(Tcl_Interp *interp, CONST char *varName, int flags)
}
declare 254 generic {
- int Tcl_UnsetVar2(Tcl_Interp *interp, char *part1, char *part2, int flags)
+ int Tcl_UnsetVar2(Tcl_Interp *interp, CONST char *part1, CONST char *part2,
+ int flags)
}
declare 255 generic {
- void Tcl_UntraceVar(Tcl_Interp *interp, char *varName, int flags, \
+ void Tcl_UntraceVar(Tcl_Interp *interp, CONST char *varName, int flags,
Tcl_VarTraceProc *proc, ClientData clientData)
}
declare 256 generic {
- void Tcl_UntraceVar2(Tcl_Interp *interp, char *part1, char *part2, \
- int flags, Tcl_VarTraceProc *proc, ClientData clientData)
+ void Tcl_UntraceVar2(Tcl_Interp *interp, CONST char *part1,
+ CONST char *part2, int flags, Tcl_VarTraceProc *proc,
+ ClientData clientData)
}
declare 257 generic {
- void Tcl_UpdateLinkedVar(Tcl_Interp *interp, char *varName)
+ void Tcl_UpdateLinkedVar(Tcl_Interp *interp, CONST char *varName)
}
declare 258 generic {
- int Tcl_UpVar(Tcl_Interp *interp, char *frameName, char *varName, \
- char *localName, int flags)
+ int Tcl_UpVar(Tcl_Interp *interp, CONST char *frameName,
+ CONST char *varName, CONST char *localName, int flags)
}
declare 259 generic {
- int Tcl_UpVar2(Tcl_Interp *interp, char *frameName, char *part1, \
- char *part2, char *localName, int flags)
+ int Tcl_UpVar2(Tcl_Interp *interp, CONST char *frameName, CONST char *part1,
+ CONST char *part2, CONST char *localName, int flags)
}
declare 260 generic {
int Tcl_VarEval(Tcl_Interp *interp, ...)
}
declare 261 generic {
- ClientData Tcl_VarTraceInfo(Tcl_Interp *interp, char *varName, \
+ ClientData Tcl_VarTraceInfo(Tcl_Interp *interp, CONST char *varName,
int flags, Tcl_VarTraceProc *procPtr, ClientData prevClientData)
}
declare 262 generic {
- ClientData Tcl_VarTraceInfo2(Tcl_Interp *interp, char *part1, \
- char *part2, int flags, Tcl_VarTraceProc *procPtr, \
+ ClientData Tcl_VarTraceInfo2(Tcl_Interp *interp, CONST char *part1,
+ CONST char *part2, int flags, Tcl_VarTraceProc *procPtr,
ClientData prevClientData)
}
declare 263 generic {
- int Tcl_Write(Tcl_Channel chan, char *s, int slen)
+ int Tcl_Write(Tcl_Channel chan, CONST char *s, int slen)
}
declare 264 generic {
- void Tcl_WrongNumArgs(Tcl_Interp *interp, int objc, \
- Tcl_Obj *CONST objv[], char *message)
+ void Tcl_WrongNumArgs(Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[], CONST char *message)
}
declare 265 generic {
- int Tcl_DumpActiveMemory(char *fileName)
+ int Tcl_DumpActiveMemory(CONST char *fileName)
}
declare 266 generic {
- void Tcl_ValidateAllMemory(char *file, int line)
+ void Tcl_ValidateAllMemory(CONST char *file, int line)
}
+
declare 267 generic {
void Tcl_AppendResultVA(Tcl_Interp *interp, va_list argList)
}
@@ -928,25 +953,27 @@ declare 268 generic {
void Tcl_AppendStringsToObjVA(Tcl_Obj *objPtr, va_list argList)
}
declare 269 generic {
- char * Tcl_HashStats(Tcl_HashTable *tablePtr)
+ CONST84_RETURN char * Tcl_HashStats(Tcl_HashTable *tablePtr)
}
declare 270 generic {
- char * Tcl_ParseVar(Tcl_Interp *interp, char *str, char **termPtr)
+ CONST84_RETURN char * Tcl_ParseVar(Tcl_Interp *interp, CONST char *str,
+ CONST84 char **termPtr)
}
declare 271 generic {
- char * Tcl_PkgPresent(Tcl_Interp *interp, char *name, char *version, \
- int exact)
+ CONST84_RETURN char * Tcl_PkgPresent(Tcl_Interp *interp, CONST char *name,
+ CONST char *version, int exact)
}
declare 272 generic {
- char * Tcl_PkgPresentEx(Tcl_Interp *interp, char *name, char *version, \
- int exact, ClientData *clientDataPtr)
+ CONST84_RETURN char * Tcl_PkgPresentEx(Tcl_Interp *interp, CONST char *name,
+ CONST char *version, int exact, ClientData *clientDataPtr)
}
declare 273 generic {
- int Tcl_PkgProvide(Tcl_Interp *interp, char *name, char *version)
+ int Tcl_PkgProvide(Tcl_Interp *interp, CONST char *name,
+ CONST char *version)
}
declare 274 generic {
- char * Tcl_PkgRequire(Tcl_Interp *interp, char *name, char *version, \
- int exact)
+ CONST84_RETURN char * Tcl_PkgRequire(Tcl_Interp *interp, CONST char *name,
+ CONST char *version, int exact)
}
declare 275 generic {
void Tcl_SetErrorCodeVA(Tcl_Interp *interp, va_list argList)
@@ -957,8 +984,8 @@ declare 276 generic {
declare 277 generic {
Tcl_Pid Tcl_WaitPid(Tcl_Pid pid, int *statPtr, int options)
}
-declare 278 {unix win} {
- void Tcl_PanicVA(char *format, va_list argList)
+declare 278 generic {
+ void Tcl_PanicVA(CONST char *format, va_list argList)
}
declare 279 generic {
void Tcl_GetVersion(int *major, int *minor, int *patchLevel, int *type)
@@ -982,9 +1009,8 @@ declare 280 generic {
# version into the new one).
declare 281 generic {
- Tcl_Channel Tcl_StackChannel(Tcl_Interp *interp, \
- Tcl_ChannelType *typePtr, ClientData instanceData, \
- int mask, Tcl_Channel prevChan)
+ Tcl_Channel Tcl_StackChannel(Tcl_Interp *interp, Tcl_ChannelType *typePtr,
+ ClientData instanceData, int mask, Tcl_Channel prevChan)
}
declare 282 generic {
int Tcl_UnstackChannel(Tcl_Interp *interp, Tcl_Channel chan)
@@ -992,9 +1018,13 @@ declare 282 generic {
declare 283 generic {
Tcl_Channel Tcl_GetStackedChannel(Tcl_Channel chan)
}
+
+# 284 was reserved, but added in 8.4a2
+declare 284 generic {
+ void Tcl_SetMainLoop(Tcl_MainLoopProc *proc)
+}
+
# Reserved for future use (8.0.x vs. 8.1)
-# declare 284 generic {
-# }
# declare 285 generic {
# }
@@ -1017,10 +1047,11 @@ declare 290 generic {
void Tcl_DiscardResult(Tcl_SavedResult *statePtr)
}
declare 291 generic {
- int Tcl_EvalEx(Tcl_Interp *interp, char *script, int numBytes, int flags)
+ int Tcl_EvalEx(Tcl_Interp *interp, CONST char *script, int numBytes,
+ int flags)
}
declare 292 generic {
- int Tcl_EvalObjv(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], \
+ int Tcl_EvalObjv(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[],
int flags)
}
declare 293 generic {
@@ -1030,14 +1061,14 @@ declare 294 generic {
void Tcl_ExitThread(int status)
}
declare 295 generic {
- int Tcl_ExternalToUtf(Tcl_Interp *interp, Tcl_Encoding encoding, \
- CONST char *src, int srcLen, int flags, \
- Tcl_EncodingState *statePtr, char *dst, int dstLen, \
+ int Tcl_ExternalToUtf(Tcl_Interp *interp, Tcl_Encoding encoding,
+ CONST char *src, int srcLen, int flags,
+ Tcl_EncodingState *statePtr, char *dst, int dstLen,
int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr)
}
declare 296 generic {
- char * Tcl_ExternalToUtfDString(Tcl_Encoding encoding, CONST char *src, \
- int srcLen, Tcl_DString *dsPtr)
+ char * Tcl_ExternalToUtfDString(Tcl_Encoding encoding,
+ CONST char *src, int srcLen, Tcl_DString *dsPtr)
}
declare 297 generic {
void Tcl_FinalizeThread(void)
@@ -1055,21 +1086,22 @@ declare 301 generic {
Tcl_Encoding Tcl_GetEncoding(Tcl_Interp *interp, CONST char *name)
}
declare 302 generic {
- char * Tcl_GetEncodingName(Tcl_Encoding encoding)
+ CONST84_RETURN char * Tcl_GetEncodingName(Tcl_Encoding encoding)
}
declare 303 generic {
void Tcl_GetEncodingNames(Tcl_Interp *interp)
}
declare 304 generic {
- int Tcl_GetIndexFromObjStruct(Tcl_Interp *interp, Tcl_Obj *objPtr, \
- char **tablePtr, int offset, char *msg, int flags, int *indexPtr)
+ int Tcl_GetIndexFromObjStruct(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ CONST VOID *tablePtr, int offset, CONST char *msg, int flags,
+ int *indexPtr)
}
declare 305 generic {
VOID * Tcl_GetThreadData(Tcl_ThreadDataKey *keyPtr, int size)
}
declare 306 generic {
- Tcl_Obj * Tcl_GetVar2Ex(Tcl_Interp *interp, char *part1, char *part2, \
- int flags)
+ Tcl_Obj * Tcl_GetVar2Ex(Tcl_Interp *interp, CONST char *part1,
+ CONST char *part2, int flags)
}
declare 307 generic {
ClientData Tcl_InitNotifier(void)
@@ -1084,14 +1116,14 @@ declare 310 generic {
void Tcl_ConditionNotify(Tcl_Condition *condPtr)
}
declare 311 generic {
- void Tcl_ConditionWait(Tcl_Condition *condPtr, Tcl_Mutex *mutexPtr, \
+ void Tcl_ConditionWait(Tcl_Condition *condPtr, Tcl_Mutex *mutexPtr,
Tcl_Time *timePtr)
}
declare 312 generic {
int Tcl_NumUtfChars(CONST char *src, int len)
}
declare 313 generic {
- int Tcl_ReadChars(Tcl_Channel channel, Tcl_Obj *objPtr, int charsToRead, \
+ int Tcl_ReadChars(Tcl_Channel channel, Tcl_Obj *objPtr, int charsToRead,
int appendFlag)
}
declare 314 generic {
@@ -1104,14 +1136,14 @@ declare 316 generic {
int Tcl_SetSystemEncoding(Tcl_Interp *interp, CONST char *name)
}
declare 317 generic {
- Tcl_Obj * Tcl_SetVar2Ex(Tcl_Interp *interp, char *part1, char *part2, \
- Tcl_Obj *newValuePtr, int flags)
+ Tcl_Obj * Tcl_SetVar2Ex(Tcl_Interp *interp, CONST char *part1,
+ CONST char *part2, Tcl_Obj *newValuePtr, int flags)
}
declare 318 generic {
void Tcl_ThreadAlert(Tcl_ThreadId threadId)
}
declare 319 generic {
- void Tcl_ThreadQueueEvent(Tcl_ThreadId threadId, Tcl_Event* evPtr, \
+ void Tcl_ThreadQueueEvent(Tcl_ThreadId threadId, Tcl_Event* evPtr,
Tcl_QueuePosition position)
}
declare 320 generic {
@@ -1130,7 +1162,7 @@ declare 324 generic {
int Tcl_UniCharToUtf(int ch, char *buf)
}
declare 325 generic {
- char * Tcl_UtfAtIndex(CONST char *src, int index)
+ CONST84_RETURN char * Tcl_UtfAtIndex(CONST char *src, int index)
}
declare 326 generic {
int Tcl_UtfCharComplete(CONST char *src, int len)
@@ -1139,26 +1171,26 @@ declare 327 generic {
int Tcl_UtfBackslash(CONST char *src, int *readPtr, char *dst)
}
declare 328 generic {
- char * Tcl_UtfFindFirst(CONST char *src, int ch)
+ CONST84_RETURN char * Tcl_UtfFindFirst(CONST char *src, int ch)
}
declare 329 generic {
- char * Tcl_UtfFindLast(CONST char *src, int ch)
+ CONST84_RETURN char * Tcl_UtfFindLast(CONST char *src, int ch)
}
declare 330 generic {
- char * Tcl_UtfNext(CONST char *src)
+ CONST84_RETURN char * Tcl_UtfNext(CONST char *src)
}
declare 331 generic {
- char * Tcl_UtfPrev(CONST char *src, CONST char *start)
+ CONST84_RETURN char * Tcl_UtfPrev(CONST char *src, CONST char *start)
}
declare 332 generic {
- int Tcl_UtfToExternal(Tcl_Interp *interp, Tcl_Encoding encoding, \
- CONST char *src, int srcLen, int flags, \
- Tcl_EncodingState *statePtr, char *dst, int dstLen, \
+ int Tcl_UtfToExternal(Tcl_Interp *interp, Tcl_Encoding encoding,
+ CONST char *src, int srcLen, int flags,
+ Tcl_EncodingState *statePtr, char *dst, int dstLen,
int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr)
}
declare 333 generic {
- char * Tcl_UtfToExternalDString(Tcl_Encoding encoding, CONST char *src, \
- int srcLen, Tcl_DString *dsPtr)
+ char * Tcl_UtfToExternalDString(Tcl_Encoding encoding,
+ CONST char *src, int srcLen, Tcl_DString *dsPtr)
}
declare 334 generic {
int Tcl_UtfToLower(char *src)
@@ -1182,10 +1214,10 @@ declare 340 generic {
char * Tcl_GetString(Tcl_Obj *objPtr)
}
declare 341 generic {
- char * Tcl_GetDefaultEncodingDir(void)
+ CONST84_RETURN char * Tcl_GetDefaultEncodingDir(void)
}
declare 342 generic {
- void Tcl_SetDefaultEncodingDir(char *path)
+ void Tcl_SetDefaultEncodingDir(CONST char *path)
}
declare 343 generic {
void Tcl_AlertNotifier(ClientData clientData)
@@ -1215,55 +1247,59 @@ declare 351 generic {
int Tcl_UniCharIsWordChar(int ch)
}
declare 352 generic {
- int Tcl_UniCharLen(Tcl_UniChar *str)
+ int Tcl_UniCharLen(CONST Tcl_UniChar *str)
}
declare 353 generic {
- int Tcl_UniCharNcmp(CONST Tcl_UniChar *cs, CONST Tcl_UniChar *ct,\
- unsigned long n)
+ int Tcl_UniCharNcmp(CONST Tcl_UniChar *cs, CONST Tcl_UniChar *ct,
+ unsigned long n)
}
declare 354 generic {
- char * Tcl_UniCharToUtfDString(CONST Tcl_UniChar *string, int numChars, \
- Tcl_DString *dsPtr)
+ char * Tcl_UniCharToUtfDString(CONST Tcl_UniChar *string,
+ int numChars, Tcl_DString *dsPtr)
}
declare 355 generic {
- Tcl_UniChar * Tcl_UtfToUniCharDString(CONST char *string, int length, \
- Tcl_DString *dsPtr)
+ Tcl_UniChar * Tcl_UtfToUniCharDString(CONST char *string,
+ int length, Tcl_DString *dsPtr)
}
declare 356 generic {
- Tcl_RegExp Tcl_GetRegExpFromObj(Tcl_Interp *interp, Tcl_Obj *patObj, int flags)
+ Tcl_RegExp Tcl_GetRegExpFromObj(Tcl_Interp *interp, Tcl_Obj *patObj,
+ int flags)
}
declare 357 generic {
- Tcl_Obj *Tcl_EvalTokens (Tcl_Interp *interp, Tcl_Token *tokenPtr, \
+ Tcl_Obj *Tcl_EvalTokens(Tcl_Interp *interp, Tcl_Token *tokenPtr,
int count)
}
declare 358 generic {
- void Tcl_FreeParse (Tcl_Parse *parsePtr)
+ void Tcl_FreeParse(Tcl_Parse *parsePtr)
}
declare 359 generic {
- void Tcl_LogCommandInfo (Tcl_Interp *interp, char *script, \
- char *command, int length)
+ void Tcl_LogCommandInfo(Tcl_Interp *interp, CONST char *script,
+ CONST char *command, int length)
}
declare 360 generic {
- int Tcl_ParseBraces (Tcl_Interp *interp, char *string, \
- int numBytes, Tcl_Parse *parsePtr,int append, char **termPtr)
+ int Tcl_ParseBraces(Tcl_Interp *interp, CONST char *string, int numBytes,
+ Tcl_Parse *parsePtr, int append, CONST84 char **termPtr)
}
declare 361 generic {
- int Tcl_ParseCommand (Tcl_Interp *interp, char *string, int numBytes, \
+ int Tcl_ParseCommand(Tcl_Interp *interp, CONST char *string, int numBytes,
int nested, Tcl_Parse *parsePtr)
}
declare 362 generic {
- int Tcl_ParseExpr(Tcl_Interp *interp, char *string, int numBytes, \
+ int Tcl_ParseExpr(Tcl_Interp *interp, CONST char *string, int numBytes,
Tcl_Parse *parsePtr)
}
declare 363 generic {
- int Tcl_ParseQuotedString(Tcl_Interp *interp, char *string, int numBytes, \
- Tcl_Parse *parsePtr, int append, char **termPtr)
+ int Tcl_ParseQuotedString(Tcl_Interp *interp, CONST char *string,
+ int numBytes, Tcl_Parse *parsePtr, int append,
+ CONST84 char **termPtr)
}
declare 364 generic {
- int Tcl_ParseVarName (Tcl_Interp *interp, char *string, \
- int numBytes, Tcl_Parse *parsePtr, int append)
+ int Tcl_ParseVarName(Tcl_Interp *interp, CONST char *string, int numBytes,
+ Tcl_Parse *parsePtr, int append)
}
+# These 4 functions are obsolete, use Tcl_FSGetCwd, Tcl_FSChdir,
+# Tcl_FSAccess and Tcl_FSStat
declare 365 generic {
char *Tcl_GetCwd(Tcl_Interp *interp, Tcl_DString *cwdPtr)
}
@@ -1298,37 +1334,37 @@ declare 375 generic {
int Tcl_UniCharIsPunct(int ch)
}
declare 376 generic {
- int Tcl_RegExpExecObj(Tcl_Interp *interp, Tcl_RegExp regexp, \
+ int Tcl_RegExpExecObj(Tcl_Interp *interp, Tcl_RegExp regexp,
Tcl_Obj *objPtr, int offset, int nmatches, int flags)
}
declare 377 generic {
void Tcl_RegExpGetInfo(Tcl_RegExp regexp, Tcl_RegExpInfo *infoPtr)
}
declare 378 generic {
- Tcl_Obj * Tcl_NewUnicodeObj(Tcl_UniChar *unicode, int numChars)
+ Tcl_Obj * Tcl_NewUnicodeObj(CONST Tcl_UniChar *unicode, int numChars)
}
declare 379 generic {
- void Tcl_SetUnicodeObj(Tcl_Obj *objPtr, Tcl_UniChar *unicode, \
+ void Tcl_SetUnicodeObj(Tcl_Obj *objPtr, CONST Tcl_UniChar *unicode,
int numChars)
}
declare 380 generic {
- int Tcl_GetCharLength (Tcl_Obj *objPtr)
+ int Tcl_GetCharLength(Tcl_Obj *objPtr)
}
declare 381 generic {
- Tcl_UniChar Tcl_GetUniChar (Tcl_Obj *objPtr, int index)
+ Tcl_UniChar Tcl_GetUniChar(Tcl_Obj *objPtr, int index)
}
declare 382 generic {
- Tcl_UniChar * Tcl_GetUnicode (Tcl_Obj *objPtr)
+ Tcl_UniChar * Tcl_GetUnicode(Tcl_Obj *objPtr)
}
declare 383 generic {
- Tcl_Obj * Tcl_GetRange (Tcl_Obj *objPtr, int first, int last)
+ Tcl_Obj * Tcl_GetRange(Tcl_Obj *objPtr, int first, int last)
}
declare 384 generic {
- void Tcl_AppendUnicodeToObj (Tcl_Obj *objPtr, \
- Tcl_UniChar *unicode, int length)
+ void Tcl_AppendUnicodeToObj(Tcl_Obj *objPtr, CONST Tcl_UniChar *unicode,
+ int length)
}
declare 385 generic {
- int Tcl_RegExpMatchObj(Tcl_Interp *interp, Tcl_Obj *stringObj, \
+ int Tcl_RegExpMatchObj(Tcl_Interp *interp, Tcl_Obj *stringObj,
Tcl_Obj *patternObj)
}
declare 386 generic {
@@ -1341,43 +1377,44 @@ declare 388 generic {
int Tcl_GetChannelNames(Tcl_Interp *interp)
}
declare 389 generic {
- int Tcl_GetChannelNamesEx(Tcl_Interp *interp, char *pattern)
+ int Tcl_GetChannelNamesEx(Tcl_Interp *interp, CONST char *pattern)
}
declare 390 generic {
- int Tcl_ProcObjCmd(ClientData clientData, Tcl_Interp *interp, \
+ int Tcl_ProcObjCmd(ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
}
declare 391 generic {
- void Tcl_ConditionFinalize (Tcl_Condition *condPtr)
+ void Tcl_ConditionFinalize(Tcl_Condition *condPtr)
}
declare 392 generic {
- void Tcl_MutexFinalize (Tcl_Mutex *mutex)
+ void Tcl_MutexFinalize(Tcl_Mutex *mutex)
}
declare 393 generic {
- int Tcl_CreateThread (Tcl_ThreadId *idPtr, Tcl_ThreadCreateProc proc, \
+ int Tcl_CreateThread(Tcl_ThreadId *idPtr, Tcl_ThreadCreateProc proc,
ClientData clientData, int stackSize, int flags)
}
+# Introduced in 8.3.2
declare 394 generic {
- int Tcl_ReadRaw (Tcl_Channel chan, char *dst, int bytesToRead)
+ int Tcl_ReadRaw(Tcl_Channel chan, char *dst, int bytesToRead)
}
declare 395 generic {
- int Tcl_WriteRaw (Tcl_Channel chan, char *src, int srcLen)
+ int Tcl_WriteRaw(Tcl_Channel chan, CONST char *src, int srcLen)
}
declare 396 generic {
- Tcl_Channel Tcl_GetTopChannel (Tcl_Channel chan)
+ Tcl_Channel Tcl_GetTopChannel(Tcl_Channel chan)
}
declare 397 generic {
- int Tcl_ChannelBuffered (Tcl_Channel chan)
+ int Tcl_ChannelBuffered(Tcl_Channel chan)
}
declare 398 generic {
- char * Tcl_ChannelName(Tcl_ChannelType *chanTypePtr)
+ CONST84_RETURN char * Tcl_ChannelName(Tcl_ChannelType *chanTypePtr)
}
declare 399 generic {
Tcl_ChannelTypeVersion Tcl_ChannelVersion(Tcl_ChannelType *chanTypePtr)
}
declare 400 generic {
- Tcl_DriverBlockModeProc * Tcl_ChannelBlockModeProc(Tcl_ChannelType \
+ Tcl_DriverBlockModeProc * Tcl_ChannelBlockModeProc(Tcl_ChannelType
*chanTypePtr)
}
declare 401 generic {
@@ -1396,28 +1433,327 @@ declare 405 generic {
Tcl_DriverSeekProc * Tcl_ChannelSeekProc(Tcl_ChannelType *chanTypePtr)
}
declare 406 generic {
- Tcl_DriverSetOptionProc * Tcl_ChannelSetOptionProc(Tcl_ChannelType \
+ Tcl_DriverSetOptionProc * Tcl_ChannelSetOptionProc(Tcl_ChannelType
*chanTypePtr)
}
declare 407 generic {
- Tcl_DriverGetOptionProc * Tcl_ChannelGetOptionProc(Tcl_ChannelType \
+ Tcl_DriverGetOptionProc * Tcl_ChannelGetOptionProc(Tcl_ChannelType
*chanTypePtr)
}
declare 408 generic {
Tcl_DriverWatchProc * Tcl_ChannelWatchProc(Tcl_ChannelType *chanTypePtr)
}
declare 409 generic {
- Tcl_DriverGetHandleProc * Tcl_ChannelGetHandleProc(Tcl_ChannelType \
+ Tcl_DriverGetHandleProc * Tcl_ChannelGetHandleProc(Tcl_ChannelType
*chanTypePtr)
}
declare 410 generic {
Tcl_DriverFlushProc * Tcl_ChannelFlushProc(Tcl_ChannelType *chanTypePtr)
}
declare 411 generic {
- Tcl_DriverHandlerProc * Tcl_ChannelHandlerProc(Tcl_ChannelType \
+ Tcl_DriverHandlerProc * Tcl_ChannelHandlerProc(Tcl_ChannelType
*chanTypePtr)
}
+# Introduced in 8.4a2
+declare 412 generic {
+ int Tcl_JoinThread(Tcl_ThreadId id, int* result)
+}
+declare 413 generic {
+ int Tcl_IsChannelShared(Tcl_Channel channel)
+}
+declare 414 generic {
+ int Tcl_IsChannelRegistered(Tcl_Interp* interp, Tcl_Channel channel)
+}
+declare 415 generic {
+ void Tcl_CutChannel(Tcl_Channel channel)
+}
+declare 416 generic {
+ void Tcl_SpliceChannel(Tcl_Channel channel)
+}
+declare 417 generic {
+ void Tcl_ClearChannelHandlers(Tcl_Channel channel)
+}
+declare 418 generic {
+ int Tcl_IsChannelExisting(CONST char* channelName)
+}
+
+declare 419 generic {
+ int Tcl_UniCharNcasecmp(CONST Tcl_UniChar *cs, CONST Tcl_UniChar *ct,
+ unsigned long n)
+}
+declare 420 generic {
+ int Tcl_UniCharCaseMatch(CONST Tcl_UniChar *ustr,
+ CONST Tcl_UniChar *pattern, int nocase)
+}
+
+declare 421 generic {
+ Tcl_HashEntry *Tcl_FindHashEntry(Tcl_HashTable *tablePtr, CONST char *key)
+}
+
+declare 422 generic {
+ Tcl_HashEntry *Tcl_CreateHashEntry(Tcl_HashTable *tablePtr,
+ CONST char *key, int *newPtr)
+}
+
+declare 423 generic {
+ void Tcl_InitCustomHashTable(Tcl_HashTable *tablePtr, int keyType,
+ Tcl_HashKeyType *typePtr)
+}
+
+declare 424 generic {
+ void Tcl_InitObjHashTable(Tcl_HashTable *tablePtr)
+}
+declare 425 generic {
+ ClientData Tcl_CommandTraceInfo(Tcl_Interp *interp, CONST char *varName,
+ int flags, Tcl_CommandTraceProc *procPtr,
+ ClientData prevClientData)
+}
+declare 426 generic {
+ int Tcl_TraceCommand(Tcl_Interp *interp, CONST char *varName, int flags,
+ Tcl_CommandTraceProc *proc, ClientData clientData)
+}
+declare 427 generic {
+ void Tcl_UntraceCommand(Tcl_Interp *interp, CONST char *varName,
+ int flags, Tcl_CommandTraceProc *proc, ClientData clientData)
+}
+declare 428 generic {
+ char * Tcl_AttemptAlloc(unsigned int size)
+}
+declare 429 generic {
+ char * Tcl_AttemptDbCkalloc(unsigned int size, CONST char *file, int line)
+}
+declare 430 generic {
+ char * Tcl_AttemptRealloc(char *ptr, unsigned int size)
+}
+declare 431 generic {
+ char * Tcl_AttemptDbCkrealloc(char *ptr, unsigned int size,
+ CONST char *file, int line)
+}
+declare 432 generic {
+ int Tcl_AttemptSetObjLength(Tcl_Obj *objPtr, int length)
+}
+declare 433 generic {
+ Tcl_ThreadId Tcl_GetChannelThread(Tcl_Channel channel)
+}
+# introduced in 8.4a3
+declare 434 generic {
+ Tcl_UniChar * Tcl_GetUnicodeFromObj(Tcl_Obj *objPtr, int *lengthPtr)
+}
+declare 435 generic {
+ int Tcl_GetMathFuncInfo(Tcl_Interp *interp, CONST char *name,
+ int *numArgsPtr, Tcl_ValueType **argTypesPtr,
+ Tcl_MathProc **procPtr, ClientData *clientDataPtr)
+}
+declare 436 generic {
+ Tcl_Obj * Tcl_ListMathFuncs(Tcl_Interp *interp, CONST char *pattern)
+}
+declare 437 generic {
+ Tcl_Obj * Tcl_SubstObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int flags)
+}
+declare 438 generic {
+ int Tcl_DetachChannel(Tcl_Interp* interp, Tcl_Channel channel)
+}
+declare 439 generic {
+ int Tcl_IsStandardChannel(Tcl_Channel channel)
+}
+# New functions due to TIP#17
+declare 440 generic {
+ int Tcl_FSCopyFile(Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr)
+}
+declare 441 generic {
+ int Tcl_FSCopyDirectory(Tcl_Obj *srcPathPtr,
+ Tcl_Obj *destPathPtr, Tcl_Obj **errorPtr)
+}
+declare 442 generic {
+ int Tcl_FSCreateDirectory(Tcl_Obj *pathPtr)
+}
+declare 443 generic {
+ int Tcl_FSDeleteFile(Tcl_Obj *pathPtr)
+}
+declare 444 generic {
+ int Tcl_FSLoadFile(Tcl_Interp * interp,
+ Tcl_Obj *pathPtr, CONST char * sym1, CONST char * sym2,
+ Tcl_PackageInitProc ** proc1Ptr,
+ Tcl_PackageInitProc ** proc2Ptr,
+ Tcl_LoadHandle * handlePtr,
+ Tcl_FSUnloadFileProc **unloadProcPtr)
+}
+declare 445 generic {
+ int Tcl_FSMatchInDirectory(Tcl_Interp *interp, Tcl_Obj *result,
+ Tcl_Obj *pathPtr, CONST char *pattern, Tcl_GlobTypeData *types)
+}
+declare 446 generic {
+ Tcl_Obj * Tcl_FSLink(Tcl_Obj *pathPtr, Tcl_Obj *toPtr, int linkAction)
+}
+declare 447 generic {
+ int Tcl_FSRemoveDirectory(Tcl_Obj *pathPtr,
+ int recursive, Tcl_Obj **errorPtr)
+}
+declare 448 generic {
+ int Tcl_FSRenameFile(Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr)
+}
+declare 449 generic {
+ int Tcl_FSLstat(Tcl_Obj *pathPtr, Tcl_StatBuf *buf)
+}
+declare 450 generic {
+ int Tcl_FSUtime(Tcl_Obj *pathPtr, struct utimbuf *tval)
+}
+declare 451 generic {
+ int Tcl_FSFileAttrsGet(Tcl_Interp *interp,
+ int index, Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef)
+}
+declare 452 generic {
+ int Tcl_FSFileAttrsSet(Tcl_Interp *interp,
+ int index, Tcl_Obj *pathPtr, Tcl_Obj *objPtr)
+}
+declare 453 generic {
+ CONST char ** Tcl_FSFileAttrStrings(Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef)
+}
+declare 454 generic {
+ int Tcl_FSStat(Tcl_Obj *pathPtr, Tcl_StatBuf *buf)
+}
+declare 455 generic {
+ int Tcl_FSAccess(Tcl_Obj *pathPtr, int mode)
+}
+declare 456 generic {
+ Tcl_Channel Tcl_FSOpenFileChannel(Tcl_Interp *interp, Tcl_Obj *pathPtr,
+ CONST char *modeString, int permissions)
+}
+declare 457 generic {
+ Tcl_Obj* Tcl_FSGetCwd(Tcl_Interp *interp)
+}
+declare 458 generic {
+ int Tcl_FSChdir(Tcl_Obj *pathPtr)
+}
+declare 459 generic {
+ int Tcl_FSConvertToPathType(Tcl_Interp *interp, Tcl_Obj *pathPtr)
+}
+declare 460 generic {
+ Tcl_Obj* Tcl_FSJoinPath(Tcl_Obj *listObj, int elements)
+}
+declare 461 generic {
+ Tcl_Obj* Tcl_FSSplitPath(Tcl_Obj* pathPtr, int *lenPtr)
+}
+declare 462 generic {
+ int Tcl_FSEqualPaths(Tcl_Obj* firstPtr, Tcl_Obj* secondPtr)
+}
+declare 463 generic {
+ Tcl_Obj* Tcl_FSGetNormalizedPath(Tcl_Interp *interp, Tcl_Obj* pathObjPtr)
+}
+declare 464 generic {
+ Tcl_Obj* Tcl_FSJoinToPath(Tcl_Obj *basePtr, int objc,
+ Tcl_Obj *CONST objv[])
+}
+declare 465 generic {
+ ClientData Tcl_FSGetInternalRep(Tcl_Obj* pathObjPtr,
+ Tcl_Filesystem *fsPtr)
+}
+declare 466 generic {
+ Tcl_Obj* Tcl_FSGetTranslatedPath(Tcl_Interp *interp, Tcl_Obj* pathPtr)
+}
+declare 467 generic {
+ int Tcl_FSEvalFile(Tcl_Interp *interp, Tcl_Obj *fileName)
+}
+declare 468 generic {
+ Tcl_Obj* Tcl_FSNewNativePath(Tcl_Filesystem* fromFilesystem,
+ ClientData clientData)
+}
+declare 469 generic {
+ CONST char* Tcl_FSGetNativePath(Tcl_Obj* pathObjPtr)
+}
+declare 470 generic {
+ Tcl_Obj* Tcl_FSFileSystemInfo(Tcl_Obj* pathObjPtr)
+}
+declare 471 generic {
+ Tcl_Obj* Tcl_FSPathSeparator(Tcl_Obj* pathObjPtr)
+}
+declare 472 generic {
+ Tcl_Obj* Tcl_FSListVolumes(void)
+}
+declare 473 generic {
+ int Tcl_FSRegister(ClientData clientData, Tcl_Filesystem *fsPtr)
+}
+declare 474 generic {
+ int Tcl_FSUnregister(Tcl_Filesystem *fsPtr)
+}
+declare 475 generic {
+ ClientData Tcl_FSData(Tcl_Filesystem *fsPtr)
+}
+declare 476 generic {
+ CONST char* Tcl_FSGetTranslatedStringPath(Tcl_Interp *interp,
+ Tcl_Obj* pathPtr)
+}
+declare 477 generic {
+ Tcl_Filesystem* Tcl_FSGetFileSystemForPath(Tcl_Obj* pathObjPtr)
+}
+declare 478 generic {
+ Tcl_PathType Tcl_FSGetPathType(Tcl_Obj *pathObjPtr)
+}
+# New function due to TIP#49
+declare 479 generic {
+ int Tcl_OutputBuffered(Tcl_Channel chan)
+}
+declare 480 generic {
+ void Tcl_FSMountsChanged(Tcl_Filesystem *fsPtr)
+}
+# New function due to TIP#56
+declare 481 generic {
+ int Tcl_EvalTokensStandard(Tcl_Interp *interp, Tcl_Token *tokenPtr,
+ int count)
+}
+
+# New export due to TIP#73
+declare 482 generic {
+ void Tcl_GetTime(Tcl_Time* timeBuf)
+}
+
+# New exports due to TIP#32
+
+declare 483 generic {
+ Tcl_Trace Tcl_CreateObjTrace(Tcl_Interp* interp, int level, int flags,
+ Tcl_CmdObjTraceProc* objProc, ClientData clientData,
+ Tcl_CmdObjTraceDeleteProc* delProc)
+}
+declare 484 generic {
+ int Tcl_GetCommandInfoFromToken(Tcl_Command token, Tcl_CmdInfo* infoPtr)
+}
+declare 485 generic {
+ int Tcl_SetCommandInfoFromToken(Tcl_Command token,
+ CONST Tcl_CmdInfo* infoPtr)
+}
+
+### New functions on 64-bit dev branch ###
+declare 486 generic {
+ Tcl_Obj * Tcl_DbNewWideIntObj(Tcl_WideInt wideValue,
+ CONST char *file, int line)
+}
+declare 487 generic {
+ int Tcl_GetWideIntFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ Tcl_WideInt *widePtr)
+}
+declare 488 generic {
+ Tcl_Obj * Tcl_NewWideIntObj(Tcl_WideInt wideValue)
+}
+declare 489 generic {
+ void Tcl_SetWideIntObj(Tcl_Obj *objPtr, Tcl_WideInt wideValue)
+}
+declare 490 generic {
+ Tcl_StatBuf * Tcl_AllocStatBuf(void)
+}
+declare 491 generic {
+ Tcl_WideInt Tcl_Seek(Tcl_Channel chan, Tcl_WideInt offset, int mode)
+}
+declare 492 generic {
+ Tcl_WideInt Tcl_Tell(Tcl_Channel chan)
+}
+
+# New export due to TIP#91
+declare 493 generic {
+ Tcl_DriverWideSeekProc * Tcl_ChannelWideSeekProc(
+ Tcl_ChannelType *chanTypePtr)
+}
+
##############################################################################
# Define the platform specific public Tcl interface. These functions are
@@ -1453,20 +1789,20 @@ declare 1 mac {
char * Tcl_MacConvertTextResource(Handle resource)
}
declare 2 mac {
- int Tcl_MacEvalResource(Tcl_Interp *interp, char *resourceName, \
- int resourceNumber, char *fileName)
+ int Tcl_MacEvalResource(Tcl_Interp *interp, CONST char *resourceName,
+ int resourceNumber, CONST char *fileName)
}
declare 3 mac {
- Handle Tcl_MacFindResource(Tcl_Interp *interp, long resourceType, \
- char *resourceName, int resourceNumber, char *resFileRef, \
- int * releaseIt)
+ Handle Tcl_MacFindResource(Tcl_Interp *interp, long resourceType,
+ CONST char *resourceName, int resourceNumber,
+ CONST char *resFileRef, int * releaseIt)
}
# These routines support the new OSType object type (i.e. the packed 4
# character type and creator codes).
declare 4 mac {
- int Tcl_GetOSTypeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, \
+ int Tcl_GetOSTypeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
OSType *osTypePtr)
}
declare 5 mac {
@@ -1477,8 +1813,7 @@ declare 6 mac {
}
# These are not in MSL 2.1.2, so we need to export them from the
-# Tcl shared library. They are found in the compat directory
-# except the panic routine which is found in tclMacPanic.h.
+# Tcl shared library. They are found in the compat directory.
declare 7 mac {
int strncasecmp(CONST char *s1, CONST char *s2, size_t n)
@@ -1487,3 +1822,14 @@ declare 8 mac {
int strcasecmp(CONST char *s1, CONST char *s2)
}
+##################
+# Mac OS X declarations
+#
+
+declare 0 macosx {
+ int Tcl_MacOSXOpenBundleResources(Tcl_Interp *interp,
+ CONST char *bundleName,
+ int hasResourceFile,
+ int maxPathLen,
+ char *libraryPath)
+}
diff --git a/tcl/generic/tcl.h b/tcl/generic/tcl.h
index f4574f30d95..c3e4cd6bf9d 100644
--- a/tcl/generic/tcl.h
+++ b/tcl/generic/tcl.h
@@ -8,6 +8,7 @@
* Copyright (c) 1993-1996 Lucent Technologies.
* Copyright (c) 1994-1998 Sun Microsystems, Inc.
* Copyright (c) 1998-2000 by Scriptics Corporation.
+ * Copyright (c) 2002 by Kevin B. Kenny. All rights reserved.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -43,26 +44,23 @@ extern "C" {
* win/configure.in (as above)
* win/tcl.m4 (not patchlevel)
* win/makefile.vc (not patchlevel) 2 LOC
- * win/pkgIndex.tcl (not patchlevel, for tclregNN.dll)
* README (sections 0 and 2)
* mac/README (2 LOC, not patchlevel)
* win/README.binary (sections 0-4)
* win/README (not patchlevel) (sections 0 and 2)
- * unix/README (not patchlevel) (part (h))
* unix/tcl.spec (2 LOC Major/Minor, 1 LOC patch)
- * tests/basic.test (not patchlevel) (version checks)
+ * tests/basic.test (1 LOC M/M, not patchlevel)
* tools/tcl.hpj.in (not patchlevel, for windows installer)
* tools/tcl.wse.in (for windows installer)
* tools/tclSplash.bmp (not patchlevel)
*/
-
#define TCL_MAJOR_VERSION 8
-#define TCL_MINOR_VERSION 3
+#define TCL_MINOR_VERSION 4
#define TCL_RELEASE_LEVEL TCL_FINAL_RELEASE
-#define TCL_RELEASE_SERIAL 2
+#define TCL_RELEASE_SERIAL 0
-#define TCL_VERSION "8.3"
-#define TCL_PATCH_LEVEL "8.3.2"
+#define TCL_VERSION "8.4"
+#define TCL_PATCH_LEVEL "8.4.0"
/*
* The following definitions set up the proper options for Windows
@@ -70,34 +68,21 @@ extern "C" {
*/
#ifndef __WIN32__
-# if defined(_WIN32) || defined(WIN32) || \
- defined(__CYGWIN__) || defined(__MINGW32__)
+# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
# define __WIN32__
+# ifndef WIN32
+# define WIN32
+# endif
# endif
#endif
+/*
+ * STRICT: See MSDN Article Q83456
+ */
#ifdef __WIN32__
# ifndef STRICT
# define STRICT
# endif
-# ifndef USE_PROTOTYPE
-# define USE_PROTOTYPE 1
-# endif
-# ifndef HAS_STDARG
-# define HAS_STDARG 1
-# endif
-# ifndef USE_PROTOTYPE
-# define USE_PROTOTYPE 1
-# endif
-
-/*
- * Under Windows we need to call Tcl_Alloc in all cases to avoid competing
- * C run-time library issues.
- */
-
-# ifndef USE_TCLALLOC
-# define USE_TCLALLOC 1
-# endif
#endif /* __WIN32__ */
/*
@@ -106,9 +91,7 @@ extern "C" {
*/
#ifdef MAC_TCL
-# ifndef HAS_STDARG
-# define HAS_STDARG 1
-# endif
+#include <ConditionalMacros.h>
# ifndef USE_TCLALLOC
# define USE_TCLALLOC 1
# endif
@@ -118,34 +101,40 @@ extern "C" {
# define INLINE
#endif
+
/*
* Utility macros: STRINGIFY takes an argument and wraps it in "" (double
* quotation marks), JOIN joins two arguments.
*/
-
-#define VERBATIM(x) x
-#ifdef _MSC_VER
-# define STRINGIFY(x) STRINGIFY1(x)
-# define STRINGIFY1(x) #x
-# define JOIN(a,b) JOIN1(a,b)
-# define JOIN1(a,b) a##b
-#else
-# ifdef RESOURCE_INCLUDED
+#ifndef STRINGIFY
# define STRINGIFY(x) STRINGIFY1(x)
# define STRINGIFY1(x) #x
+#endif
+#ifndef JOIN
# define JOIN(a,b) JOIN1(a,b)
# define JOIN1(a,b) a##b
-# else
-# ifdef __STDC__
-# define STRINGIFY(x) #x
-# define JOIN(a,b) a##b
-# else
-# define STRINGIFY(x) "x"
-# define JOIN(a,b) VERBATIM(a)VERBATIM(b)
-# endif
-# endif
#endif
+/*
+ * A special definition used to allow this header file to be included
+ * from windows resource files so that they can obtain version
+ * information. RC_INVOKED is defined by default by the RC tool.
+ * Resource compilers don't like all the C stuff, like typedefs and
+ * procedure declarations, that occur below, so block them out.
+ */
+
+#ifndef RC_INVOKED
+
+/*
+ * A special definition for Macintosh used to allow this header file
+ * to be included in resource files so that they can get obtain
+ * version information from this file. Resource compilers don't like
+ * all the C stuff, like typedefs and procedure declarations, that
+ * occur below.
+*/
+
+#ifndef RESOURCE_INCLUDED
+
/*
* Special macro to define mutexes, that doesn't do anything
* if we are not using threads.
@@ -171,19 +160,12 @@ extern "C" {
#define Tcl_ConditionFinalize(condPtr)
#endif /* TCL_THREADS */
-/*
- * A special definition used to allow this header file to be included
- * in resource files so that they can get obtain version information from
- * this file. Resource compilers don't like all the C stuff, like typedefs
- * and procedure declarations, that occur below.
- */
-
-#ifndef RESOURCE_INCLUDED
#ifndef BUFSIZ
-#include <stdio.h>
+# include <stdio.h>
#endif
+
/*
* Definitions that allow Tcl functions with variable numbers of
* arguments to be used with either varargs.h or stdarg.h. TCL_VARARGS
@@ -193,23 +175,15 @@ extern "C" {
* string for use in the function definition. TCL_VARARGS_START
* initializes the va_list data structure and returns the first argument.
*/
-
-#if defined(__STDC__) || defined(HAS_STDARG)
+#if !defined(NO_STDARG)
# include <stdarg.h>
-
# define TCL_VARARGS(type, name) (type name, ...)
# define TCL_VARARGS_DEF(type, name) (type name, ...)
# define TCL_VARARGS_START(type, name, list) (va_start(list, name), name)
#else
# include <varargs.h>
-
-# ifdef __cplusplus
-# define TCL_VARARGS(type, name) (type name, ...)
-# define TCL_VARARGS_DEF(type, name) (type va_alist, ...)
-# else
-# define TCL_VARARGS(type, name) ()
-# define TCL_VARARGS_DEF(type, name) (va_alist)
-# endif
+# define TCL_VARARGS(type, name) ()
+# define TCL_VARARGS_DEF(type, name) (va_alist)
# define TCL_VARARGS_START(type, name, list) \
(va_start(list), va_arg(list, type))
#endif
@@ -223,16 +197,16 @@ extern "C" {
*/
#ifdef STATIC_BUILD
-# define DLLIMPORT
-# define DLLEXPORT
+# define DLLIMPORT
+# define DLLEXPORT
#else
-# if defined(__WIN32__) && (defined(_MSC_VER) || (defined(__GNUC__) && defined(__declspec)))
-# define DLLIMPORT __declspec(dllimport)
-# define DLLEXPORT __declspec(dllexport)
-# else
-# define DLLIMPORT
-# define DLLEXPORT
-# endif
+# if (defined(__WIN32__) && (defined(_MSC_VER) || (__BORLANDC__ >= 0x0550) || (defined(__GNUC__) && defined(__declspec)))) || (defined(MAC_TCL) && FUNCTION_DECLSPEC)
+# define DLLIMPORT __declspec(dllimport)
+# define DLLEXPORT __declspec(dllexport)
+# else
+# define DLLIMPORT
+# define DLLEXPORT
+# endif
#endif
/*
@@ -248,45 +222,64 @@ extern "C" {
* name of a library we are building, is set on the compile line for sources
* that are to be placed in the library. When this macro is set, the
* storage class will be set to DLLEXPORT. At the end of the header file, the
- * storage class will be reset to DLLIMPORt.
+ * storage class will be reset to DLLIMPORT.
*/
-
#undef TCL_STORAGE_CLASS
#ifdef BUILD_tcl
-# define TCL_STORAGE_CLASS DLLEXPORT
+# define TCL_STORAGE_CLASS DLLEXPORT
#else
-# ifdef USE_TCL_STUBS
-# define TCL_STORAGE_CLASS
-# else
-# define TCL_STORAGE_CLASS DLLIMPORT
-# endif
+# ifdef USE_TCL_STUBS
+# define TCL_STORAGE_CLASS
+# else
+# define TCL_STORAGE_CLASS DLLIMPORT
+# endif
#endif
+
/*
* Definitions that allow this header file to be used either with or
* without ANSI C features like function prototypes.
*/
-
#undef _ANSI_ARGS_
#undef CONST
#ifndef INLINE
# define INLINE
#endif
-#if ((defined(__STDC__) || defined(SABER)) && !defined(NO_PROTOTYPE)) || defined(__cplusplus) || defined(USE_PROTOTYPE)
-# define _USING_PROTOTYPES_ 1
-# define _ANSI_ARGS_(x) x
+#ifndef NO_CONST
# define CONST const
#else
-# define _ANSI_ARGS_(x) ()
# define CONST
#endif
+#ifndef NO_PROTOTYPES
+# define _ANSI_ARGS_(x) x
+#else
+# define _ANSI_ARGS_(x) ()
+#endif
+
+#ifdef USE_NON_CONST
+# ifdef USE_COMPAT_CONST
+# error define at most one of USE_NON_CONST and USE_COMPAT_CONST
+# endif
+# define CONST84
+# define CONST84_RETURN
+#else
+# ifdef USE_COMPAT_CONST
+# define CONST84
+# define CONST84_RETURN CONST
+# else
+# define CONST84 CONST
+# define CONST84_RETURN CONST
+# endif
+#endif
+
+
/*
* Make sure EXTERN isn't defined elsewhere
*/
#ifdef EXTERN
-#undef EXTERN
+# undef EXTERN
#endif /* EXTERN */
#ifdef __cplusplus
@@ -295,23 +288,13 @@ extern "C" {
# define EXTERN extern TCL_STORAGE_CLASS
#endif
+
/*
- * Macro to use instead of "void" for arguments that must have
- * type "void *" in ANSI C; maps them to type "char *" in
- * non-ANSI systems.
- */
-#ifndef __WIN32__
-#ifndef VOID
-# ifdef __STDC__
-# define VOID void
-# else
-# define VOID char
-# endif
-#endif
-#else /* __WIN32__ */
-/*
- * The following code is copied from winnt.h
+ * The following code is copied from winnt.h.
+ * If we don't replicate it here, then <windows.h> can't be included
+ * after tcl.h, since tcl.h also defines VOID.
*/
+#ifdef __WIN32__
#ifndef VOID
#define VOID void
typedef char CHAR;
@@ -321,22 +304,130 @@ typedef long LONG;
#endif /* __WIN32__ */
/*
- * Miscellaneous declarations.
+ * Macro to use instead of "void" for arguments that must have
+ * type "void *" in ANSI C; maps them to type "char *" in
+ * non-ANSI systems.
*/
+#ifndef NO_VOID
+# define VOID void
+#else
+# define VOID char
+#endif
+
+/*
+ * Miscellaneous declarations.
+ */
#ifndef NULL
-#define NULL 0
+# define NULL 0
#endif
#ifndef _CLIENTDATA
-# if defined(__STDC__) || defined(__cplusplus)
- typedef void *ClientData;
+# ifndef NO_VOID
+ typedef void *ClientData;
# else
- typedef int *ClientData;
-# endif /* __STDC__ */
-#define _CLIENTDATA
+ typedef int *ClientData;
+# endif
+# define _CLIENTDATA
+#endif
+
+/*
+ * Define Tcl_WideInt to be a type that is (at least) 64-bits wide,
+ * and define Tcl_WideUInt to be the unsigned variant of that type
+ * (assuming that where we have one, we can have the other.)
+ *
+ * At the moment, this only works on Unix systems anyway...
+ *
+ * Also defines the following macros:
+ * TCL_WIDE_INT_IS_LONG - if wide ints are really longs (i.e. we're on
+ * a real 64-bit system.)
+ * Tcl_WideAsLong - forgetful converter from wideInt to long.
+ * Tcl_LongAsWide - sign-extending converter from long to wideInt.
+ * Tcl_WideAsDouble - converter from wideInt to double.
+ * Tcl_DoubleAsWide - converter from double to wideInt.
+ *
+ * The following invariant should hold for any long value 'longVal':
+ * longVal == Tcl_WideAsLong(Tcl_LongAsWide(longVal))
+ *
+ * Note on converting between Tcl_WideInt and strings. This
+ * implementation (in tclObj.c) depends on the functions strtoull()
+ * and, where sprintf(...,"%lld",...) does not work, lltostr().
+ * Although strtoull() is fairly straight-forward, lltostr() is a most
+ * unusual function on Solaris8 (taking its operating buffer
+ * backwards) so any changes you make will need to be done
+ * cautiously...
+ */
+
+#if !defined(TCL_WIDE_INT_TYPE)&&!defined(TCL_WIDE_INT_IS_LONG)
+# ifdef __WIN32__
+# define TCL_WIDE_INT_TYPE __int64
+# ifdef __BORLANDC__
+typedef struct stati64 Tcl_StatBuf;
+# define TCL_LL_MODIFIER "L"
+# define TCL_LL_MODIFIER_SIZE 1
+# else /* __BORLANDC__ */
+typedef struct _stati64 Tcl_StatBuf;
+# define TCL_LL_MODIFIER "I64"
+# define TCL_LL_MODIFIER_SIZE 3
+# endif /* __BORLANDC__ */
+# else /* __WIN32__ */
+/*
+ * Don't know what platform it is and configure hasn't discovered what
+ * is going on for us. Try to guess...
+ */
+# ifdef NO_LIMITS_H
+# error please define either TCL_WIDE_INT_TYPE or TCL_WIDE_INT_IS_LONG
+# else /* !NO_LIMITS_H */
+# include <limits.h>
+# if (INT_MAX < LONG_MAX)
+# define TCL_WIDE_INT_IS_LONG 1
+# else
+# define TCL_WIDE_INT_TYPE long long
+# endif
+# endif /* NO_LIMITS_H */
+# endif /* __WIN32__ */
+#endif /* !TCL_WIDE_INT_TYPE & !TCL_WIDE_INT_IS_LONG */
+#ifdef TCL_WIDE_INT_IS_LONG
+# undef TCL_WIDE_INT_TYPE
+# define TCL_WIDE_INT_TYPE long
+#endif /* TCL_WIDE_INT_IS_LONG */
+
+typedef TCL_WIDE_INT_TYPE Tcl_WideInt;
+typedef unsigned TCL_WIDE_INT_TYPE Tcl_WideUInt;
+
+#ifdef TCL_WIDE_INT_IS_LONG
+typedef struct stat Tcl_StatBuf;
+# define Tcl_WideAsLong(val) ((long)(val))
+# define Tcl_LongAsWide(val) ((long)(val))
+# define Tcl_WideAsDouble(val) ((double)((long)(val)))
+# define Tcl_DoubleAsWide(val) ((long)((double)(val)))
+#else /* TCL_WIDE_INT_IS_LONG */
+# ifndef __WIN32__
+# ifdef HAVE_STRUCT_STAT64
+typedef struct stat64 Tcl_StatBuf;
+# else
+typedef struct stat Tcl_StatBuf;
+# endif /* HAVE_STRUCT_STAT64 */
+# define TCL_LL_MODIFIER "ll"
+# define TCL_LL_MODIFIER_SIZE 2
+# endif /* !__WIN32__ */
+# define Tcl_WideAsLong(val) ((long)((Tcl_WideInt)(val)))
+# define Tcl_LongAsWide(val) ((Tcl_WideInt)((long)(val)))
+# define Tcl_WideAsDouble(val) ((double)((Tcl_WideInt)(val)))
+# define Tcl_DoubleAsWide(val) ((Tcl_WideInt)((double)(val)))
+#endif /* TCL_WIDE_INT_IS_LONG */
+
+
+/*
+ * This flag controls whether binary compatability is maintained with
+ * extensions built against a previous version of Tcl. This is true
+ * by default.
+ */
+#ifndef TCL_PRESERVE_BINARY_COMPATABILITY
+# define TCL_PRESERVE_BINARY_COMPATABILITY 1
#endif
+
/*
* Data structures defined opaquely in this module. The definitions below
* just provide dummy types. A few fields are made visible in Tcl_Interp
@@ -388,6 +479,7 @@ typedef struct Tcl_TimerToken_ *Tcl_TimerToken;
typedef struct Tcl_Trace_ *Tcl_Trace;
typedef struct Tcl_Var_ *Tcl_Var;
typedef struct Tcl_ChannelTypeVersion_ *Tcl_ChannelTypeVersion;
+typedef struct Tcl_LoadHandle_ *Tcl_LoadHandle;
/*
* Definition of the interface to procedures implementing threads.
@@ -395,7 +487,6 @@ typedef struct Tcl_ChannelTypeVersion_ *Tcl_ChannelTypeVersion;
* 'Tcl_CreateThread' and will be called as the main fuction of
* the new thread created by that call.
*/
-
#ifdef MAC_TCL
typedef pascal void *(Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData));
#elif defined __WIN32__
@@ -422,12 +513,10 @@ typedef void (Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData));
#endif
-
/*
* Definition of values for default stacksize and the possible flags to be
* given to Tcl_CreateThread.
*/
-
#define TCL_THREAD_STACK_DEFAULT (0) /* Use default size for stack */
#define TCL_THREAD_NOFLAGS (0000) /* Standard flags, default behaviour */
#define TCL_THREAD_JOINABLE (0001) /* Mark the thread as joinable */
@@ -435,7 +524,6 @@ typedef void (Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData));
/*
* Flag values passed to Tcl_GetRegExpFromObj.
*/
-
#define TCL_REG_BASIC 000000 /* BREs (convenience) */
#define TCL_REG_EXTENDED 000001 /* EREs */
#define TCL_REG_ADVF 000002 /* advanced features in EREs */
@@ -455,7 +543,6 @@ typedef void (Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData));
* The following flag is experimental and only intended for use by Expect. It
* will probably go away in a later release.
*/
-
#define TCL_REG_BOSONLY 002000 /* prepend \A to pattern so it only
* matches at the beginning of the
* string. */
@@ -463,7 +550,6 @@ typedef void (Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData));
/*
* Flags values passed to Tcl_RegExpExecObj.
*/
-
#define TCL_REG_NOTBOL 0001 /* Beginning of string does not match ^. */
#define TCL_REG_NOTEOL 0002 /* End of string does not match $. */
@@ -472,7 +558,6 @@ typedef void (Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData));
* relative to the start of the match string, not the beginning of the
* entire string.
*/
-
typedef struct Tcl_RegExpIndices {
long start; /* character offset of first character in match */
long end; /* character offset of first character after the
@@ -493,8 +578,8 @@ typedef struct Tcl_RegExpInfo {
* Picky compilers complain if this typdef doesn't appear before the
* struct's reference in tclDecls.h.
*/
-
-typedef struct stat *Tcl_Stat_;
+typedef Tcl_StatBuf *Tcl_Stat_;
+typedef struct stat *Tcl_OldStat_;
/*
* When a TCL command returns, the interpreter contains a result from the
@@ -516,7 +601,6 @@ typedef struct stat *Tcl_Stat_;
* TCL_CONTINUE Go on to the next iteration of the current loop;
* the interpreter's result is meaningless.
*/
-
#define TCL_OK 0
#define TCL_ERROR 1
#define TCL_RETURN 2
@@ -526,15 +610,31 @@ typedef struct stat *Tcl_Stat_;
#define TCL_RESULT_SIZE 200
/*
- * Argument descriptors for math function callbacks in expressions:
+ * Flags to control what substitutions are performed by Tcl_SubstObj():
*/
+#define TCL_SUBST_COMMANDS 001
+#define TCL_SUBST_VARIABLES 002
+#define TCL_SUBST_BACKSLASHES 004
+#define TCL_SUBST_ALL 007
+
-typedef enum {TCL_INT, TCL_DOUBLE, TCL_EITHER} Tcl_ValueType;
+/*
+ * Argument descriptors for math function callbacks in expressions:
+ */
+typedef enum {
+ TCL_INT, TCL_DOUBLE, TCL_EITHER, TCL_WIDE_INT
+#ifdef TCL_WIDE_INT_IS_LONG
+ = TCL_INT
+#endif
+} Tcl_ValueType;
typedef struct Tcl_Value {
Tcl_ValueType type; /* Indicates intValue or doubleValue is
* valid, or both. */
long intValue; /* Integer value. */
double doubleValue; /* Double-precision floating value. */
+#ifndef TCL_WIDE_INT_IS_LONG
+ Tcl_WideInt wideValue; /* Wide (min. 64-bit) integer value. */
+#endif
} Tcl_Value;
/*
@@ -542,9 +642,9 @@ typedef struct Tcl_Value {
* reference to Tcl_Obj is encountered in the procedure types declared
* below.
*/
-
struct Tcl_Obj;
+
/*
* Procedure types defined by Tcl:
*/
@@ -556,10 +656,14 @@ typedef void (Tcl_ChannelProc) _ANSI_ARGS_((ClientData clientData, int mask));
typedef void (Tcl_CloseProc) _ANSI_ARGS_((ClientData data));
typedef void (Tcl_CmdDeleteProc) _ANSI_ARGS_((ClientData clientData));
typedef int (Tcl_CmdProc) _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char *argv[]));
+ Tcl_Interp *interp, int argc, CONST84 char *argv[]));
typedef void (Tcl_CmdTraceProc) _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int level, char *command, Tcl_CmdProc *proc,
- ClientData cmdClientData, int argc, char *argv[]));
+ ClientData cmdClientData, int argc, CONST84 char *argv[]));
+typedef int (Tcl_CmdObjTraceProc) _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int level, CONST char *command,
+ Tcl_Command commandInfo, int objc, struct Tcl_Obj * CONST * objv));
+typedef void (Tcl_CmdObjTraceDeleteProc) _ANSI_ARGS_((ClientData clientData));
typedef void (Tcl_DupInternalRepProc) _ANSI_ARGS_((struct Tcl_Obj *srcPtr,
struct Tcl_Obj *dupPtr));
typedef int (Tcl_EncodingConvertProc)_ANSI_ARGS_((ClientData clientData,
@@ -586,9 +690,9 @@ typedef int (Tcl_MathProc) _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, Tcl_Value *args, Tcl_Value *resultPtr));
typedef void (Tcl_NamespaceDeleteProc) _ANSI_ARGS_((ClientData clientData));
typedef int (Tcl_ObjCmdProc) _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int objc, struct Tcl_Obj *CONST objv[]));
+ Tcl_Interp *interp, int objc, struct Tcl_Obj * CONST * objv));
typedef int (Tcl_PackageInitProc) _ANSI_ARGS_((Tcl_Interp *interp));
-typedef void (Tcl_PanicProc) _ANSI_ARGS_(TCL_VARARGS(char *, format));
+typedef void (Tcl_PanicProc) _ANSI_ARGS_(TCL_VARARGS(CONST char *, format));
typedef void (Tcl_TcpAcceptProc) _ANSI_ARGS_((ClientData callbackData,
Tcl_Channel chan, char *address, int port));
typedef void (Tcl_TimerProc) _ANSI_ARGS_((ClientData clientData));
@@ -596,10 +700,19 @@ typedef int (Tcl_SetFromAnyProc) _ANSI_ARGS_((Tcl_Interp *interp,
struct Tcl_Obj *objPtr));
typedef void (Tcl_UpdateStringProc) _ANSI_ARGS_((struct Tcl_Obj *objPtr));
typedef char *(Tcl_VarTraceProc) _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, char *part1, char *part2, int flags));
+ Tcl_Interp *interp, CONST84 char *part1, CONST84 char *part2, int flags));
+typedef void (Tcl_CommandTraceProc) _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, CONST char *oldName, CONST char *newName,
+ int flags));
typedef void (Tcl_CreateFileHandlerProc) _ANSI_ARGS_((int fd, int mask,
Tcl_FileProc *proc, ClientData clientData));
typedef void (Tcl_DeleteFileHandlerProc) _ANSI_ARGS_((int fd));
+typedef void (Tcl_AlertNotifierProc) _ANSI_ARGS_((ClientData clientData));
+typedef void (Tcl_ServiceModeHookProc) _ANSI_ARGS_((int mode));
+typedef ClientData (Tcl_InitNotifierProc) _ANSI_ARGS_((VOID));
+typedef void (Tcl_FinalizeNotifierProc) _ANSI_ARGS_((ClientData clientData));
+typedef void (Tcl_MainLoopProc) _ANSI_ARGS_((void));
+
/*
* The following structure represents a type of object, which is a
@@ -626,6 +739,7 @@ typedef struct Tcl_ObjType {
* failure. */
} Tcl_ObjType;
+
/*
* One of the following structures exists for each object in the Tcl
* system. An object stores a value as either a string, some internal
@@ -655,6 +769,7 @@ typedef struct Tcl_Obj {
long longValue; /* - an long integer value */
double doubleValue; /* - a double-precision floating value */
VOID *otherValuePtr; /* - another, type-specific value */
+ Tcl_WideInt wideValue; /* - a long long value */
struct { /* - internal rep as two pointers */
VOID *ptr1;
VOID *ptr2;
@@ -662,6 +777,7 @@ typedef struct Tcl_Obj {
} internalRep;
} Tcl_Obj;
+
/*
* Macros to increment and decrement a Tcl_Obj's reference count, and to
* test whether an object is shared (i.e. has reference count > 1).
@@ -672,7 +788,6 @@ typedef struct Tcl_Obj {
* "obj" twice. This means that you should avoid calling it with an
* expression that is expensive to compute or has side effects.
*/
-
void Tcl_IncrRefCount _ANSI_ARGS_((Tcl_Obj *objPtr));
void Tcl_DecrRefCount _ANSI_ARGS_((Tcl_Obj *objPtr));
int Tcl_IsShared _ANSI_ARGS_((Tcl_Obj *objPtr));
@@ -716,14 +831,16 @@ int Tcl_IsShared _ANSI_ARGS_((Tcl_Obj *objPtr));
Tcl_DbNewObj(__FILE__, __LINE__)
# define Tcl_NewStringObj(bytes, len) \
Tcl_DbNewStringObj(bytes, len, __FILE__, __LINE__)
+# define Tcl_NewWideIntObj(val) \
+ Tcl_DbNewWideIntObj(val, __FILE__, __LINE__)
#endif /* TCL_MEM_DEBUG */
+
/*
* The following structure contains the state needed by
* Tcl_SaveResult. No-one outside of Tcl should access any of these
* fields. This structure is typically allocated on the stack.
*/
-
typedef struct Tcl_SavedResult {
char *result;
Tcl_FreeProc *freeProc;
@@ -759,6 +876,7 @@ typedef struct Tcl_Namespace {
* namespace. */
} Tcl_Namespace;
+
/*
* The following structure represents a call frame, or activation record.
* A call frame defines a naming context for a procedure call: its local
@@ -795,6 +913,7 @@ typedef struct Tcl_CallFrame {
char* dummy10;
} Tcl_CallFrame;
+
/*
* Information about commands that is returned by Tcl_GetCommandInfo and
* passed to Tcl_SetCommandInfo. objProc is an objc/objv object-based
@@ -809,7 +928,7 @@ typedef struct Tcl_CallFrame {
* does string-to-object or object-to-string argument conversions then
* calls the other procedure.
*/
-
+
typedef struct Tcl_CmdInfo {
int isNativeObjectProc; /* 1 if objProc was registered by a call to
* Tcl_CreateObjCommand; 0 otherwise.
@@ -833,10 +952,9 @@ typedef struct Tcl_CmdInfo {
/*
* The structure defined below is used to hold dynamic strings. The only
- * field that clients should use is the string field, and they should
- * never modify it.
+ * field that clients should use is the string field, accessible via the
+ * macro Tcl_DStringValue.
*/
-
#define TCL_DSTRING_STATIC_SIZE 200
typedef struct Tcl_DString {
char *string; /* Points to beginning of string: either
@@ -859,7 +977,6 @@ typedef struct Tcl_DString {
* be specified in the "tcl_precision" variable, and the number of
* bytes of buffer space required by Tcl_PrintDouble.
*/
-
#define TCL_MAX_PREC 17
#define TCL_DOUBLE_SPACE (TCL_MAX_PREC+10)
@@ -868,7 +985,6 @@ typedef struct Tcl_DString {
* string representation of an integer in base 10 (assuming the existence
* of 64-bit integers).
*/
-
#define TCL_INTEGER_SPACE 24
/*
@@ -876,14 +992,12 @@ typedef struct Tcl_DString {
* output braces (careful! if you change this flag be sure to change
* the definitions at the front of tclUtil.c).
*/
-
#define TCL_DONT_USE_BRACES 1
/*
* Flag that may be passed to Tcl_GetIndexFromObj to force it to disallow
* abbreviated strings.
*/
-
#define TCL_EXACT 1
/*
@@ -891,16 +1005,15 @@ typedef struct Tcl_DString {
* WARNING: these bit choices must not conflict with the bit choices
* for evalFlag bits in tclInt.h!!
*/
-
#define TCL_NO_EVAL 0x10000
#define TCL_EVAL_GLOBAL 0x20000
#define TCL_EVAL_DIRECT 0x40000
+#define TCL_EVAL_INVOKE 0x80000
/*
* Special freeProc values that may be passed to Tcl_SetResult (see
* the man page for details):
*/
-
#define TCL_VOLATILE ((Tcl_FreeProc *) 1)
#define TCL_STATIC ((Tcl_FreeProc *) 0)
#define TCL_DYNAMIC ((Tcl_FreeProc *) 3)
@@ -908,7 +1021,6 @@ typedef struct Tcl_DString {
/*
* Flag values passed to variable-related procedures.
*/
-
#define TCL_GLOBAL_ONLY 1
#define TCL_NAMESPACE_ONLY 2
#define TCL_APPEND_VALUE 4
@@ -920,6 +1032,30 @@ typedef struct Tcl_DString {
#define TCL_INTERP_DESTROYED 0x100
#define TCL_LEAVE_ERR_MSG 0x200
#define TCL_TRACE_ARRAY 0x800
+#ifndef TCL_REMOVE_OBSOLETE_TRACES
+/* Required to support old variable/vdelete/vinfo traces */
+#define TCL_TRACE_OLD_STYLE 0x1000
+#endif
+/* Indicate the semantics of the result of a trace */
+#define TCL_TRACE_RESULT_DYNAMIC 0x8000
+#define TCL_TRACE_RESULT_OBJECT 0x10000
+
+/*
+ * Flag values passed to command-related procedures.
+ */
+
+#define TCL_TRACE_RENAME 0x2000
+#define TCL_TRACE_DELETE 0x4000
+
+#define TCL_ALLOW_INLINE_COMPILATION 0x20000
+
+/*
+ * Flag values passed to Tcl_CreateObjTrace, and used internally
+ * by command execution traces. Slots 4,8,16 and 32 are
+ * used internally by execution traces (see tclCmdMZ.c)
+ */
+#define TCL_TRACE_ENTER_EXEC 1
+#define TCL_TRACE_LEAVE_EXEC 2
/*
* The TCL_PARSE_PART1 flag is deprecated and has no effect.
@@ -929,28 +1065,45 @@ typedef struct Tcl_DString {
* flag)
*/
#ifndef TCL_NO_DEPRECATED
-#define TCL_PARSE_PART1 0x400
+# define TCL_PARSE_PART1 0x400
#endif
/*
* Types for linked variables:
*/
-
#define TCL_LINK_INT 1
#define TCL_LINK_DOUBLE 2
#define TCL_LINK_BOOLEAN 3
#define TCL_LINK_STRING 4
+#define TCL_LINK_WIDE_INT 5
#define TCL_LINK_READ_ONLY 0x80
+
/*
- * Forward declaration of Tcl_HashTable. Needed by some C++ compilers
- * to prevent errors when the forward reference to Tcl_HashTable is
- * encountered in the Tcl_HashEntry structure.
+ * Forward declarations of Tcl_HashTable and related types.
*/
+typedef struct Tcl_HashKeyType Tcl_HashKeyType;
+typedef struct Tcl_HashTable Tcl_HashTable;
+typedef struct Tcl_HashEntry Tcl_HashEntry;
-#ifdef __cplusplus
-struct Tcl_HashTable;
+typedef unsigned int (Tcl_HashKeyProc) _ANSI_ARGS_((Tcl_HashTable *tablePtr,
+ VOID *keyPtr));
+typedef int (Tcl_CompareHashKeysProc) _ANSI_ARGS_((VOID *keyPtr,
+ Tcl_HashEntry *hPtr));
+typedef Tcl_HashEntry *(Tcl_AllocHashEntryProc) _ANSI_ARGS_((
+ Tcl_HashTable *tablePtr, VOID *keyPtr));
+typedef void (Tcl_FreeHashEntryProc) _ANSI_ARGS_((Tcl_HashEntry *hPtr));
+
+/*
+ * This flag controls whether the hash table stores the hash of a key, or
+ * recalculates it. There should be no reason for turning this flag off
+ * as it is completely binary and source compatible unless you directly
+ * access the bucketPtr member of the Tcl_HashTableEntry structure. This
+ * member has been removed and the space used to store the hash value.
+ */
+#ifndef TCL_HASH_KEY_STORE_HASH
+# define TCL_HASH_KEY_STORE_HASH 1
#endif
/*
@@ -959,18 +1112,30 @@ struct Tcl_HashTable;
* defined below.
*/
-typedef struct Tcl_HashEntry {
- struct Tcl_HashEntry *nextPtr; /* Pointer to next entry in this
+struct Tcl_HashEntry {
+ Tcl_HashEntry *nextPtr; /* Pointer to next entry in this
* hash bucket, or NULL for end of
* chain. */
- struct Tcl_HashTable *tablePtr; /* Pointer to table containing entry. */
- struct Tcl_HashEntry **bucketPtr; /* Pointer to bucket that points to
+ Tcl_HashTable *tablePtr; /* Pointer to table containing entry. */
+#if TCL_HASH_KEY_STORE_HASH
+# if TCL_PRESERVE_BINARY_COMPATABILITY
+ VOID *hash; /* Hash value, stored as pointer to
+ * ensure that the offsets of the
+ * fields in this structure are not
+ * changed. */
+# else
+ unsigned int hash; /* Hash value. */
+# endif
+#else
+ Tcl_HashEntry **bucketPtr; /* Pointer to bucket that points to
* first entry in this entry's chain:
* used for deleting the entry. */
+#endif
ClientData clientData; /* Application stores something here
* with Tcl_SetHashValue. */
union { /* Key has one of these forms: */
char *oneWordValue; /* One-word value for key. */
+ Tcl_Obj *objPtr; /* Tcl_Obj * key value. */
int words[1]; /* Multiple integer words for key.
* The actual size will be as large
* as necessary for this table's
@@ -979,7 +1144,63 @@ typedef struct Tcl_HashEntry {
* will be as large as needed to hold
* the key. */
} key; /* MUST BE LAST FIELD IN RECORD!! */
-} Tcl_HashEntry;
+};
+
+/*
+ * Flags used in Tcl_HashKeyType.
+ *
+ * TCL_HASH_KEY_RANDOMIZE_HASH:
+ * There are some things, pointers for example
+ * which don't hash well because they do not use
+ * the lower bits. If this flag is set then the
+ * hash table will attempt to rectify this by
+ * randomising the bits and then using the upper
+ * N bits as the index into the table.
+ */
+#define TCL_HASH_KEY_RANDOMIZE_HASH 0x1
+
+/*
+ * Structure definition for the methods associated with a hash table
+ * key type.
+ */
+#define TCL_HASH_KEY_TYPE_VERSION 1
+struct Tcl_HashKeyType {
+ int version; /* Version of the table. If this structure is
+ * extended in future then the version can be
+ * used to distinguish between different
+ * structures.
+ */
+
+ int flags; /* Flags, see above for details. */
+
+ /* Calculates a hash value for the key. If this is NULL then the pointer
+ * itself is used as a hash value.
+ */
+ Tcl_HashKeyProc *hashKeyProc;
+
+ /* Compares two keys and returns zero if they do not match, and non-zero
+ * if they do. If this is NULL then the pointers are compared.
+ */
+ Tcl_CompareHashKeysProc *compareKeysProc;
+
+ /* Called to allocate memory for a new entry, i.e. if the key is a
+ * string then this could allocate a single block which contains enough
+ * space for both the entry and the string. Only the key field of the
+ * allocated Tcl_HashEntry structure needs to be filled in. If something
+ * else needs to be done to the key, i.e. incrementing a reference count
+ * then that should be done by this function. If this is NULL then Tcl_Alloc
+ * is used to allocate enough space for a Tcl_HashEntry and the key pointer
+ * is assigned to key.oneWordValue.
+ */
+ Tcl_AllocHashEntryProc *allocEntryProc;
+
+ /* Called to free memory associated with an entry. If something else needs
+ * to be done to the key, i.e. decrementing a reference count then that
+ * should be done by this function. If this is NULL then Tcl_Free is used
+ * to free the Tcl_HashEntry.
+ */
+ Tcl_FreeHashEntryProc *freeEntryProc;
+};
/*
* Structure definition for a hash table. Must be in tcl.h so clients
@@ -988,7 +1209,7 @@ typedef struct Tcl_HashEntry {
*/
#define TCL_SMALL_HASH_TABLE 4
-typedef struct Tcl_HashTable {
+struct Tcl_HashTable {
Tcl_HashEntry **buckets; /* Pointer to bucket array. Each
* element points to first entry in
* bucket's hash chain, or NULL. */
@@ -1007,16 +1228,20 @@ typedef struct Tcl_HashTable {
int mask; /* Mask value used in hashing
* function. */
int keyType; /* Type of keys used in this table.
- * It's either TCL_STRING_KEYS,
- * TCL_ONE_WORD_KEYS, or an integer
- * giving the number of ints that
- * is the size of the key.
+ * It's either TCL_CUSTOM_KEYS,
+ * TCL_STRING_KEYS, TCL_ONE_WORD_KEYS,
+ * or an integer giving the number of
+ * ints that is the size of the key.
*/
- Tcl_HashEntry *(*findProc) _ANSI_ARGS_((struct Tcl_HashTable *tablePtr,
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+ Tcl_HashEntry *(*findProc) _ANSI_ARGS_((Tcl_HashTable *tablePtr,
CONST char *key));
- Tcl_HashEntry *(*createProc) _ANSI_ARGS_((struct Tcl_HashTable *tablePtr,
+ Tcl_HashEntry *(*createProc) _ANSI_ARGS_((Tcl_HashTable *tablePtr,
CONST char *key, int *newPtr));
-} Tcl_HashTable;
+#endif
+ Tcl_HashKeyType *typePtr; /* Type of the keys used in the
+ * Tcl_HashTable. */
+};
/*
* Structure definition for information used to keep track of searches
@@ -1033,36 +1258,79 @@ typedef struct Tcl_HashSearch {
/*
* Acceptable key types for hash tables:
+ *
+ * TCL_STRING_KEYS: The keys are strings, they are copied into
+ * the entry.
+ * TCL_ONE_WORD_KEYS: The keys are pointers, the pointer is stored
+ * in the entry.
+ * TCL_CUSTOM_TYPE_KEYS: The keys are arbitrary types which are copied
+ * into the entry.
+ * TCL_CUSTOM_PTR_KEYS: The keys are pointers to arbitrary types, the
+ * pointer is stored in the entry.
+ *
+ * While maintaining binary compatability the above have to be distinct
+ * values as they are used to differentiate between old versions of the
+ * hash table which don't have a typePtr and new ones which do. Once binary
+ * compatability is discarded in favour of making more wide spread changes
+ * TCL_STRING_KEYS can be the same as TCL_CUSTOM_TYPE_KEYS, and
+ * TCL_ONE_WORD_KEYS can be the same as TCL_CUSTOM_PTR_KEYS because they
+ * simply determine how the key is accessed from the entry and not the
+ * behaviour.
*/
#define TCL_STRING_KEYS 0
#define TCL_ONE_WORD_KEYS 1
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+# define TCL_CUSTOM_TYPE_KEYS -2
+# define TCL_CUSTOM_PTR_KEYS -1
+#else
+# define TCL_CUSTOM_TYPE_KEYS TCL_STRING_KEYS
+# define TCL_CUSTOM_PTR_KEYS TCL_ONE_WORD_KEYS
+#endif
+
/*
* Macros for clients to use to access fields of hash entries:
*/
#define Tcl_GetHashValue(h) ((h)->clientData)
#define Tcl_SetHashValue(h, value) ((h)->clientData = (ClientData) (value))
-#define Tcl_GetHashKey(tablePtr, h) \
- ((char *) (((tablePtr)->keyType == TCL_ONE_WORD_KEYS) ? (h)->key.oneWordValue \
- : (h)->key.string))
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+# define Tcl_GetHashKey(tablePtr, h) \
+ ((char *) (((tablePtr)->keyType == TCL_ONE_WORD_KEYS || \
+ (tablePtr)->keyType == TCL_CUSTOM_PTR_KEYS) \
+ ? (h)->key.oneWordValue \
+ : (h)->key.string))
+#else
+# define Tcl_GetHashKey(tablePtr, h) \
+ ((char *) (((tablePtr)->keyType == TCL_ONE_WORD_KEYS) \
+ ? (h)->key.oneWordValue \
+ : (h)->key.string))
+#endif
/*
* Macros to use for clients to use to invoke find and create procedures
* for hash tables:
*/
-#define Tcl_FindHashEntry(tablePtr, key) \
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+# define Tcl_FindHashEntry(tablePtr, key) \
(*((tablePtr)->findProc))(tablePtr, key)
-#define Tcl_CreateHashEntry(tablePtr, key, newPtr) \
+# define Tcl_CreateHashEntry(tablePtr, key, newPtr) \
(*((tablePtr)->createProc))(tablePtr, key, newPtr)
+#else /* !TCL_PRESERVE_BINARY_COMPATABILITY */
+/*
+ * Macro to use new extended version of Tcl_InitHashTable.
+ */
+# define Tcl_InitHashTable(tablePtr, keyType) \
+ Tcl_InitHashTableEx(tablePtr, keyType, NULL)
+#endif /* TCL_PRESERVE_BINARY_COMPATABILITY */
+
/*
* Flag values to pass to Tcl_DoOneEvent to disable searches
* for some kinds of events:
*/
-
#define TCL_DONT_WAIT (1<<1)
#define TCL_WINDOW_EVENTS (1<<2)
#define TCL_FILE_EVENTS (1<<3)
@@ -1079,7 +1347,6 @@ typedef struct Tcl_HashSearch {
* a Tcl_Event header followed by additional information specific to that
* event.
*/
-
struct Tcl_Event {
Tcl_EventProc *proc; /* Procedure to call to service this event. */
struct Tcl_Event *nextPtr; /* Next in list of pending events, or NULL. */
@@ -1088,7 +1355,6 @@ struct Tcl_Event {
/*
* Positions to pass to Tcl_QueueEvent:
*/
-
typedef enum {
TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, TCL_QUEUE_MARK
} Tcl_QueuePosition;
@@ -1097,17 +1363,16 @@ typedef enum {
* Values to pass to Tcl_SetServiceMode to specify the behavior of notifier
* event routines.
*/
-
#define TCL_SERVICE_NONE 0
#define TCL_SERVICE_ALL 1
+
/*
* The following structure keeps is used to hold a time value, either as
* an absolute time (the number of seconds from the epoch) or as an
* elapsed time. On Unix systems the epoch is Midnight Jan 1, 1970 GMT.
* On Macintosh systems the epoch is Midnight Jan 1, 1904 GMT.
*/
-
typedef struct Tcl_Time {
long sec; /* Seconds. */
long usec; /* Microseconds. */
@@ -1116,11 +1381,11 @@ typedef struct Tcl_Time {
typedef void (Tcl_SetTimerProc) _ANSI_ARGS_((Tcl_Time *timePtr));
typedef int (Tcl_WaitForEventProc) _ANSI_ARGS_((Tcl_Time *timePtr));
+
/*
* Bits to pass to Tcl_CreateFileHandler and Tcl_CreateChannelHandler
* to indicate what sorts of events are of interest:
*/
-
#define TCL_READABLE (1<<1)
#define TCL_WRITABLE (1<<2)
#define TCL_EXCEPTION (1<<3)
@@ -1130,7 +1395,6 @@ typedef int (Tcl_WaitForEventProc) _ANSI_ARGS_((Tcl_Time *timePtr));
* disposition of the stdio handles. TCL_STDIN, TCL_STDOUT, TCL_STDERR,
* are also used in Tcl_GetStdChannel.
*/
-
#define TCL_STDIN (1<<1)
#define TCL_STDOUT (1<<2)
#define TCL_STDERR (1<<3)
@@ -1140,28 +1404,25 @@ typedef int (Tcl_WaitForEventProc) _ANSI_ARGS_((Tcl_Time *timePtr));
* Bits passed to Tcl_DriverClose2Proc to indicate which side of a channel
* should be closed.
*/
-
#define TCL_CLOSE_READ (1<<1)
-#define TCL_CLOSE_WRITE (1<<2)
+#define TCL_CLOSE_WRITE (1<<2)
/*
* Value to use as the closeProc for a channel that supports the
* close2Proc interface.
*/
-
#define TCL_CLOSE2PROC ((Tcl_DriverCloseProc *)1)
/*
* Channel version tag. This was introduced in 8.3.2/8.4.
*/
-
#define TCL_CHANNEL_VERSION_1 ((Tcl_ChannelTypeVersion) 0x1)
#define TCL_CHANNEL_VERSION_2 ((Tcl_ChannelTypeVersion) 0x2)
+#define TCL_CHANNEL_VERSION_3 ((Tcl_ChannelTypeVersion) 0x3)
/*
* Typedefs for the various operations in a channel type:
*/
-
typedef int (Tcl_DriverBlockModeProc) _ANSI_ARGS_((
ClientData instanceData, int mode));
typedef int (Tcl_DriverCloseProc) _ANSI_ARGS_((ClientData instanceData,
@@ -1171,15 +1432,15 @@ typedef int (Tcl_DriverClose2Proc) _ANSI_ARGS_((ClientData instanceData,
typedef int (Tcl_DriverInputProc) _ANSI_ARGS_((ClientData instanceData,
char *buf, int toRead, int *errorCodePtr));
typedef int (Tcl_DriverOutputProc) _ANSI_ARGS_((ClientData instanceData,
- char *buf, int toWrite, int *errorCodePtr));
+ CONST84 char *buf, int toWrite, int *errorCodePtr));
typedef int (Tcl_DriverSeekProc) _ANSI_ARGS_((ClientData instanceData,
long offset, int mode, int *errorCodePtr));
typedef int (Tcl_DriverSetOptionProc) _ANSI_ARGS_((
ClientData instanceData, Tcl_Interp *interp,
- char *optionName, char *value));
+ CONST char *optionName, CONST char *value));
typedef int (Tcl_DriverGetOptionProc) _ANSI_ARGS_((
ClientData instanceData, Tcl_Interp *interp,
- char *optionName, Tcl_DString *dsPtr));
+ CONST84 char *optionName, Tcl_DString *dsPtr));
typedef void (Tcl_DriverWatchProc) _ANSI_ARGS_((
ClientData instanceData, int mask));
typedef int (Tcl_DriverGetHandleProc) _ANSI_ARGS_((
@@ -1189,19 +1450,23 @@ typedef int (Tcl_DriverFlushProc) _ANSI_ARGS_((
ClientData instanceData));
typedef int (Tcl_DriverHandlerProc) _ANSI_ARGS_((
ClientData instanceData, int interestMask));
+typedef Tcl_WideInt (Tcl_DriverWideSeekProc) _ANSI_ARGS_((
+ ClientData instanceData, Tcl_WideInt offset,
+ int mode, int *errorCodePtr));
+
/*
* The following declarations either map ckalloc and ckfree to
* malloc and free, or they map them to procedures with all sorts
* of debugging hooks defined in tclCkalloc.c.
*/
-
#ifdef TCL_MEM_DEBUG
# define ckalloc(x) Tcl_DbCkalloc(x, __FILE__, __LINE__)
# define ckfree(x) Tcl_DbCkfree(x, __FILE__, __LINE__)
# define ckrealloc(x,y) Tcl_DbCkrealloc((x), (y),__FILE__, __LINE__)
-
+# define attemptckalloc(x) Tcl_AttemptDbCkalloc(x, __FILE__, __LINE__)
+# define attemptckrealloc(x,y) Tcl_AttemptDbCkrealloc((x), (y), __FILE__, __LINE__)
#else /* !TCL_MEM_DEBUG */
/*
@@ -1210,10 +1475,11 @@ typedef int (Tcl_DriverHandlerProc) _ANSI_ARGS_((
* is using the same memory allocator both inside and outside of the
* Tcl library.
*/
-
# define ckalloc(x) Tcl_Alloc(x)
# define ckfree(x) Tcl_Free(x)
# define ckrealloc(x,y) Tcl_Realloc(x,y)
+# define attemptckalloc(x) Tcl_AttemptAlloc(x)
+# define attemptckrealloc(x,y) Tcl_AttemptRealloc(x,y)
# define Tcl_InitMemory(x)
# define Tcl_DumpActiveMemory(x)
# define Tcl_ValidateAllMemory(x,y)
@@ -1221,17 +1487,6 @@ typedef int (Tcl_DriverHandlerProc) _ANSI_ARGS_((
#endif /* !TCL_MEM_DEBUG */
/*
- * Enum for different end of line translation and recognition modes.
- */
-
-typedef enum Tcl_EolTranslation {
- TCL_TRANSLATE_AUTO, /* Eol == \r, \n and \r\n. */
- TCL_TRANSLATE_CR, /* Eol == \r. */
- TCL_TRANSLATE_LF, /* Eol == \n. */
- TCL_TRANSLATE_CRLF /* Eol == \r\n. */
-} Tcl_EolTranslation;
-
-/*
* struct Tcl_ChannelType:
*
* One such structure exists for each type (kind) of channel.
@@ -1241,11 +1496,10 @@ typedef enum Tcl_EolTranslation {
* It is recommend that the Tcl_Channel* functions are used to access
* elements of this structure, instead of direct accessing.
*/
-
typedef struct Tcl_ChannelType {
char *typeName; /* The name of the channel type in Tcl
- * commands. This storage is owned by
- * channel type. */
+ * commands. This storage is owned by
+ * channel type. */
Tcl_ChannelTypeVersion version; /* Version of the channel type. */
Tcl_DriverCloseProc *closeProc; /* Procedure to call to close the
* channel, or TCL_CLOSE2PROC if the
@@ -1274,13 +1528,22 @@ typedef struct Tcl_ChannelType {
/* Set blocking mode for the
* raw channel. May be NULL. */
/*
- * Only valid in TCL_CHANNEL_VERSION_2 channels
+ * Only valid in TCL_CHANNEL_VERSION_2 channels or later
*/
Tcl_DriverFlushProc *flushProc; /* Procedure to call to flush a
* channel. May be NULL. */
Tcl_DriverHandlerProc *handlerProc; /* Procedure to call to handle a
* channel event. This will be passed
* up the stacked channel chain. */
+ /*
+ * Only valid in TCL_CHANNEL_VERSION_3 channels or later
+ */
+ Tcl_DriverWideSeekProc *wideSeekProc;
+ /* Procedure to call to seek
+ * on the channel which can
+ * handle 64-bit offsets. May be
+ * NULL, and must be NULL if
+ * seekProc is NULL. */
} Tcl_ChannelType;
/*
@@ -1288,38 +1551,346 @@ typedef struct Tcl_ChannelType {
* set the channel into blocking or nonblocking mode. They are passed
* as arguments to the blockModeProc procedure in the above structure.
*/
-
-#define TCL_MODE_BLOCKING 0 /* Put channel into blocking mode. */
-#define TCL_MODE_NONBLOCKING 1 /* Put channel into nonblocking
+#define TCL_MODE_BLOCKING 0 /* Put channel into blocking mode. */
+#define TCL_MODE_NONBLOCKING 1 /* Put channel into nonblocking
* mode. */
/*
* Enum for different types of file paths.
*/
-
typedef enum Tcl_PathType {
TCL_PATH_ABSOLUTE,
TCL_PATH_RELATIVE,
TCL_PATH_VOLUME_RELATIVE
} Tcl_PathType;
+
+/*
+ * The following structure is used to pass glob type data amongst
+ * the various glob routines and Tcl_FSMatchInDirectory.
+ */
+typedef struct Tcl_GlobTypeData {
+ /* Corresponds to bcdpfls as in 'find -t' */
+ int type;
+ /* Corresponds to file permissions */
+ int perm;
+ /* Acceptable mac type */
+ Tcl_Obj* macType;
+ /* Acceptable mac creator */
+ Tcl_Obj* macCreator;
+} Tcl_GlobTypeData;
+
+/*
+ * type and permission definitions for glob command
+ */
+#define TCL_GLOB_TYPE_BLOCK (1<<0)
+#define TCL_GLOB_TYPE_CHAR (1<<1)
+#define TCL_GLOB_TYPE_DIR (1<<2)
+#define TCL_GLOB_TYPE_PIPE (1<<3)
+#define TCL_GLOB_TYPE_FILE (1<<4)
+#define TCL_GLOB_TYPE_LINK (1<<5)
+#define TCL_GLOB_TYPE_SOCK (1<<6)
+
+#define TCL_GLOB_PERM_RONLY (1<<0)
+#define TCL_GLOB_PERM_HIDDEN (1<<1)
+#define TCL_GLOB_PERM_R (1<<2)
+#define TCL_GLOB_PERM_W (1<<3)
+#define TCL_GLOB_PERM_X (1<<4)
+
+
+/*
+ * Typedefs for the various filesystem operations:
+ */
+typedef int (Tcl_FSStatProc) _ANSI_ARGS_((Tcl_Obj *pathPtr, Tcl_StatBuf *buf));
+typedef int (Tcl_FSAccessProc) _ANSI_ARGS_((Tcl_Obj *pathPtr, int mode));
+typedef Tcl_Channel (Tcl_FSOpenFileChannelProc)
+ _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *pathPtr,
+ int mode, int permissions));
+typedef int (Tcl_FSMatchInDirectoryProc) _ANSI_ARGS_((Tcl_Interp* interp,
+ Tcl_Obj *result, Tcl_Obj *pathPtr, CONST char *pattern,
+ Tcl_GlobTypeData * types));
+typedef Tcl_Obj* (Tcl_FSGetCwdProc) _ANSI_ARGS_((Tcl_Interp *interp));
+typedef int (Tcl_FSChdirProc) _ANSI_ARGS_((Tcl_Obj *pathPtr));
+typedef int (Tcl_FSLstatProc) _ANSI_ARGS_((Tcl_Obj *pathPtr,
+ Tcl_StatBuf *buf));
+typedef int (Tcl_FSCreateDirectoryProc) _ANSI_ARGS_((Tcl_Obj *pathPtr));
+typedef int (Tcl_FSDeleteFileProc) _ANSI_ARGS_((Tcl_Obj *pathPtr));
+typedef int (Tcl_FSCopyDirectoryProc) _ANSI_ARGS_((Tcl_Obj *srcPathPtr,
+ Tcl_Obj *destPathPtr, Tcl_Obj **errorPtr));
+typedef int (Tcl_FSCopyFileProc) _ANSI_ARGS_((Tcl_Obj *srcPathPtr,
+ Tcl_Obj *destPathPtr));
+typedef int (Tcl_FSRemoveDirectoryProc) _ANSI_ARGS_((Tcl_Obj *pathPtr,
+ int recursive, Tcl_Obj **errorPtr));
+typedef int (Tcl_FSRenameFileProc) _ANSI_ARGS_((Tcl_Obj *srcPathPtr,
+ Tcl_Obj *destPathPtr));
+typedef void (Tcl_FSUnloadFileProc) _ANSI_ARGS_((Tcl_LoadHandle loadHandle));
+typedef Tcl_Obj* (Tcl_FSListVolumesProc) _ANSI_ARGS_((void));
+/* We have to declare the utime structure here. */
+struct utimbuf;
+typedef int (Tcl_FSUtimeProc) _ANSI_ARGS_((Tcl_Obj *pathPtr,
+ struct utimbuf *tval));
+typedef int (Tcl_FSNormalizePathProc) _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *pathPtr, int nextCheckpoint));
+typedef int (Tcl_FSFileAttrsGetProc) _ANSI_ARGS_((Tcl_Interp *interp,
+ int index, Tcl_Obj *pathPtr,
+ Tcl_Obj **objPtrRef));
+typedef CONST char** (Tcl_FSFileAttrStringsProc) _ANSI_ARGS_((Tcl_Obj *pathPtr,
+ Tcl_Obj** objPtrRef));
+typedef int (Tcl_FSFileAttrsSetProc) _ANSI_ARGS_((Tcl_Interp *interp,
+ int index, Tcl_Obj *pathPtr,
+ Tcl_Obj *objPtr));
+typedef Tcl_Obj* (Tcl_FSLinkProc) _ANSI_ARGS_((Tcl_Obj *pathPtr,
+ Tcl_Obj *toPtr, int linkType));
+typedef int (Tcl_FSLoadFileProc) _ANSI_ARGS_((Tcl_Interp * interp,
+ Tcl_Obj *pathPtr,
+ Tcl_LoadHandle *handlePtr,
+ Tcl_FSUnloadFileProc **unloadProcPtr));
+typedef int (Tcl_FSPathInFilesystemProc) _ANSI_ARGS_((Tcl_Obj *pathPtr,
+ ClientData *clientDataPtr));
+typedef Tcl_Obj* (Tcl_FSFilesystemPathTypeProc)
+ _ANSI_ARGS_((Tcl_Obj *pathPtr));
+typedef Tcl_Obj* (Tcl_FSFilesystemSeparatorProc)
+ _ANSI_ARGS_((Tcl_Obj *pathPtr));
+typedef void (Tcl_FSFreeInternalRepProc) _ANSI_ARGS_((ClientData clientData));
+typedef ClientData (Tcl_FSDupInternalRepProc)
+ _ANSI_ARGS_((ClientData clientData));
+typedef Tcl_Obj* (Tcl_FSInternalToNormalizedProc)
+ _ANSI_ARGS_((ClientData clientData));
+typedef ClientData (Tcl_FSCreateInternalRepProc) _ANSI_ARGS_((Tcl_Obj *pathPtr));
+
+typedef struct Tcl_FSVersion_ *Tcl_FSVersion;
+
+/*
+ *----------------------------------------------------------------
+ * Data structures related to hooking into the filesystem
+ *----------------------------------------------------------------
+ */
+
+/*
+ * Filesystem version tag. This was introduced in 8.4.
+ */
+#define TCL_FILESYSTEM_VERSION_1 ((Tcl_FSVersion) 0x1)
+
+/*
+ * struct Tcl_Filesystem:
+ *
+ * One such structure exists for each type (kind) of filesystem.
+ * It collects together in one place all the functions that are
+ * part of the specific filesystem. Tcl always accesses the
+ * filesystem through one of these structures.
+ *
+ * Not all entries need be non-NULL; any which are NULL are simply
+ * ignored. However, a complete filesystem should provide all of
+ * these functions. The explanations in the structure show
+ * the importance of each function.
+ */
+
+typedef struct Tcl_Filesystem {
+ CONST char *typeName; /* The name of the filesystem. */
+ int structureLength; /* Length of this structure, so future
+ * binary compatibility can be assured. */
+ Tcl_FSVersion version;
+ /* Version of the filesystem type. */
+ Tcl_FSPathInFilesystemProc *pathInFilesystemProc;
+ /* Function to check whether a path is in
+ * this filesystem. This is the most
+ * important filesystem procedure. */
+ Tcl_FSDupInternalRepProc *dupInternalRepProc;
+ /* Function to duplicate internal fs rep. May
+ * be NULL (but then fs is less efficient). */
+ Tcl_FSFreeInternalRepProc *freeInternalRepProc;
+ /* Function to free internal fs rep. Must
+ * be implemented, if internal representations
+ * need freeing, otherwise it can be NULL. */
+ Tcl_FSInternalToNormalizedProc *internalToNormalizedProc;
+ /* Function to convert internal representation
+ * to a normalized path. Only required if
+ * the fs creates pure path objects with no
+ * string/path representation. */
+ Tcl_FSCreateInternalRepProc *createInternalRepProc;
+ /* Function to create a filesystem-specific
+ * internal representation. May be NULL
+ * if paths have no internal representation,
+ * or if the Tcl_FSPathInFilesystemProc
+ * for this filesystem always immediately
+ * creates an internal representation for
+ * paths it accepts. */
+ Tcl_FSNormalizePathProc *normalizePathProc;
+ /* Function to normalize a path. Should
+ * be implemented for all filesystems
+ * which can have multiple string
+ * representations for the same path
+ * object. */
+ Tcl_FSFilesystemPathTypeProc *filesystemPathTypeProc;
+ /* Function to determine the type of a
+ * path in this filesystem. May be NULL. */
+ Tcl_FSFilesystemSeparatorProc *filesystemSeparatorProc;
+ /* Function to return the separator
+ * character(s) for this filesystem. Must
+ * be implemented. */
+ Tcl_FSStatProc *statProc;
+ /*
+ * Function to process a 'Tcl_FSStat()'
+ * call. Must be implemented for any
+ * reasonable filesystem.
+ */
+ Tcl_FSAccessProc *accessProc;
+ /*
+ * Function to process a 'Tcl_FSAccess()'
+ * call. Must be implemented for any
+ * reasonable filesystem.
+ */
+ Tcl_FSOpenFileChannelProc *openFileChannelProc;
+ /*
+ * Function to process a
+ * 'Tcl_FSOpenFileChannel()' call. Must be
+ * implemented for any reasonable
+ * filesystem.
+ */
+ Tcl_FSMatchInDirectoryProc *matchInDirectoryProc;
+ /* Function to process a
+ * 'Tcl_FSMatchInDirectory()'. If not
+ * implemented, then glob and recursive
+ * copy functionality will be lacking in
+ * the filesystem. */
+ Tcl_FSUtimeProc *utimeProc;
+ /* Function to process a
+ * 'Tcl_FSUtime()' call. Required to
+ * allow setting (not reading) of times
+ * with 'file mtime', 'file atime' and
+ * the open-r/open-w/fcopy implementation
+ * of 'file copy'. */
+ Tcl_FSLinkProc *linkProc;
+ /* Function to process a
+ * 'Tcl_FSLink()' call. Should be
+ * implemented only if the filesystem supports
+ * links (reading or creating). */
+ Tcl_FSListVolumesProc *listVolumesProc;
+ /* Function to list any filesystem volumes
+ * added by this filesystem. Should be
+ * implemented only if the filesystem adds
+ * volumes at the head of the filesystem. */
+ Tcl_FSFileAttrStringsProc *fileAttrStringsProc;
+ /* Function to list all attributes strings
+ * which are valid for this filesystem.
+ * If not implemented the filesystem will
+ * not support the 'file attributes' command.
+ * This allows arbitrary additional information
+ * to be attached to files in the filesystem. */
+ Tcl_FSFileAttrsGetProc *fileAttrsGetProc;
+ /* Function to process a
+ * 'Tcl_FSFileAttrsGet()' call, used by
+ * 'file attributes'. */
+ Tcl_FSFileAttrsSetProc *fileAttrsSetProc;
+ /* Function to process a
+ * 'Tcl_FSFileAttrsSet()' call, used by
+ * 'file attributes'. */
+ Tcl_FSCreateDirectoryProc *createDirectoryProc;
+ /* Function to process a
+ * 'Tcl_FSCreateDirectory()' call. Should
+ * be implemented unless the FS is
+ * read-only. */
+ Tcl_FSRemoveDirectoryProc *removeDirectoryProc;
+ /* Function to process a
+ * 'Tcl_FSRemoveDirectory()' call. Should
+ * be implemented unless the FS is
+ * read-only. */
+ Tcl_FSDeleteFileProc *deleteFileProc;
+ /* Function to process a
+ * 'Tcl_FSDeleteFile()' call. Should
+ * be implemented unless the FS is
+ * read-only. */
+ Tcl_FSCopyFileProc *copyFileProc;
+ /* Function to process a
+ * 'Tcl_FSCopyFile()' call. If not
+ * implemented Tcl will fall back
+ * on open-r, open-w and fcopy as
+ * a copying mechanism, for copying
+ * actions initiated in Tcl (not C). */
+ Tcl_FSRenameFileProc *renameFileProc;
+ /* Function to process a
+ * 'Tcl_FSRenameFile()' call. If not
+ * implemented, Tcl will fall back on
+ * a copy and delete mechanism, for
+ * rename actions initiated in Tcl (not C). */
+ Tcl_FSCopyDirectoryProc *copyDirectoryProc;
+ /* Function to process a
+ * 'Tcl_FSCopyDirectory()' call. If
+ * not implemented, Tcl will fall back
+ * on a recursive create-dir, file copy
+ * mechanism, for copying actions
+ * initiated in Tcl (not C). */
+ Tcl_FSLstatProc *lstatProc;
+ /* Function to process a
+ * 'Tcl_FSLstat()' call. If not implemented,
+ * Tcl will attempt to use the 'statProc'
+ * defined above instead. */
+ Tcl_FSLoadFileProc *loadFileProc;
+ /* Function to process a
+ * 'Tcl_FSLoadFile()' call. If not
+ * implemented, Tcl will fall back on
+ * a copy to native-temp followed by a
+ * Tcl_FSLoadFile on that temporary copy. */
+ Tcl_FSGetCwdProc *getCwdProc;
+ /*
+ * Function to process a 'Tcl_FSGetCwd()'
+ * call. Most filesystems need not
+ * implement this. It will usually only be
+ * called once, if 'getcwd' is called
+ * before 'chdir'. May be NULL.
+ */
+ Tcl_FSChdirProc *chdirProc;
+ /*
+ * Function to process a 'Tcl_FSChdir()'
+ * call. If filesystems do not implement
+ * this, it will be emulated by a series of
+ * directory access checks. Otherwise,
+ * virtual filesystems which do implement
+ * it need only respond with a positive
+ * return result if the dirName is a valid
+ * directory in their filesystem. They
+ * need not remember the result, since that
+ * will be automatically remembered for use
+ * by GetCwd. Real filesystems should
+ * carry out the correct action (i.e. call
+ * the correct system 'chdir' api). If not
+ * implemented, then 'cd' and 'pwd' will
+ * fail inside the filesystem.
+ */
+} Tcl_Filesystem;
+
+/*
+ * The following definitions are used as values for the 'linkAction' flag
+ * to Tcl_FSLink, or the linkProc of any filesystem. Any combination
+ * of flags can be given. For link creation, the linkProc should create
+ * a link which matches any of the types given.
+ *
+ * TCL_CREATE_SYMBOLIC_LINK: Create a symbolic or soft link.
+ * TCL_CREATE_HARD_LINK: Create a hard link.
+ */
+#define TCL_CREATE_SYMBOLIC_LINK 0x01
+#define TCL_CREATE_HARD_LINK 0x02
+
/*
* The following structure represents the Notifier functions that
* you can override with the Tcl_SetNotifier call.
*/
-
typedef struct Tcl_NotifierProcs {
Tcl_SetTimerProc *setTimerProc;
Tcl_WaitForEventProc *waitForEventProc;
Tcl_CreateFileHandlerProc *createFileHandlerProc;
Tcl_DeleteFileHandlerProc *deleteFileHandlerProc;
+ Tcl_InitNotifierProc *initNotifierProc;
+ Tcl_FinalizeNotifierProc *finalizeNotifierProc;
+ Tcl_AlertNotifierProc *alertNotifierProc;
+ Tcl_ServiceModeHookProc *serviceModeHookProc;
} Tcl_NotifierProcs;
+
/*
* The following structure represents a user-defined encoding. It collects
* together all the functions that are used by the specific encoding.
*/
-
typedef struct Tcl_EncodingType {
CONST char *encodingName; /* The name of the encoding, e.g. "euc-jp".
* This name is the unique key for this
@@ -1373,16 +1944,14 @@ typedef struct Tcl_EncodingType {
* in the destination buffer and then continue
* to sonvert the source.
*/
-
#define TCL_ENCODING_START 0x01
#define TCL_ENCODING_END 0x02
#define TCL_ENCODING_STOPONERROR 0x04
+
/*
- *----------------------------------------------------------------
- * The following data structures and declarations are for the new
- * Tcl parser. This stuff should all move to tcl.h eventually.
- *----------------------------------------------------------------
+ * The following data structures and declarations are for the new Tcl
+ * parser.
*/
/*
@@ -1390,11 +1959,10 @@ typedef struct Tcl_EncodingType {
* variable reference, one of the following structures is created to
* describe the token.
*/
-
typedef struct Tcl_Token {
int type; /* Type of token, such as TCL_TOKEN_WORD;
* see below for valid types. */
- char *start; /* First character in token. */
+ CONST char *start; /* First character in token. */
int size; /* Number of bytes in token. */
int numComponents; /* If this token is composed of other
* tokens, this field tells how many of
@@ -1476,7 +2044,6 @@ typedef struct Tcl_Token {
* operator's operands. NumComponents is
* always 0.
*/
-
#define TCL_TOKEN_WORD 1
#define TCL_TOKEN_SIMPLE_WORD 2
#define TCL_TOKEN_TEXT 4
@@ -1491,7 +2058,6 @@ typedef struct Tcl_Token {
* will be stored in the error field of the Tcl_Parse structure
* defined below.
*/
-
#define TCL_PARSE_SUCCESS 0
#define TCL_PARSE_QUOTE_EXTRA 1
#define TCL_PARSE_BRACE_EXTRA 2
@@ -1507,18 +2073,17 @@ typedef struct Tcl_Token {
* A structure of the following type is filled in by Tcl_ParseCommand.
* It describes a single command parsed from an input string.
*/
-
#define NUM_STATIC_TOKENS 20
typedef struct Tcl_Parse {
- char *commentStart; /* Pointer to # that begins the first of
+ CONST char *commentStart; /* Pointer to # that begins the first of
* one or more comments preceding the
* command. */
int commentSize; /* Number of bytes in comments (up through
* newline character that terminates the
* last comment). If there were no
* comments, this field is 0. */
- char *commandStart; /* First character in first word of command. */
+ CONST char *commandStart; /* First character in first word of command. */
int commandSize; /* Number of bytes in command, including
* first character of first word, up
* through the terminating newline,
@@ -1542,13 +2107,13 @@ typedef struct Tcl_Parse {
* Tcl_ParseCommand.
*/
- char *string; /* The original command string passed to
+ CONST char *string; /* The original command string passed to
* Tcl_ParseCommand. */
- char *end; /* Points to the character just after the
+ CONST char *end; /* Points to the character just after the
* last one in the command string. */
Tcl_Interp *interp; /* Interpreter to use for error reporting,
* or NULL. */
- char *term; /* Points to character in string that
+ CONST char *term; /* Points to character in string that
* terminated most recent token. Filled in
* by ParseTokens. If an error occurs,
* points to beginning of region where the
@@ -1597,40 +2162,40 @@ typedef struct Tcl_Parse {
* encoding. This error is reported only if
* TCL_ENCODING_STOPONERROR was specified.
*/
-
#define TCL_CONVERT_MULTIBYTE -1
#define TCL_CONVERT_SYNTAX -2
#define TCL_CONVERT_UNKNOWN -3
#define TCL_CONVERT_NOSPACE -4
+
/*
* The maximum number of bytes that are necessary to represent a single
* Unicode character in UTF-8.
*/
-
#define TCL_UTF_MAX 3
/*
- * This represents a Unicode character.
+ * This represents a Unicode character. Any changes to this should
+ * also be reflected in regcustom.h.
*/
-
typedef unsigned short Tcl_UniChar;
+
/*
* Deprecated Tcl procedures:
*/
-
#ifndef TCL_NO_DEPRECATED
-#define Tcl_EvalObj(interp,objPtr) Tcl_EvalObjEx((interp),(objPtr),0)
-#define Tcl_GlobalEvalObj(interp,objPtr) \
+# define Tcl_EvalObj(interp,objPtr) \
+ Tcl_EvalObjEx((interp),(objPtr),0)
+# define Tcl_GlobalEvalObj(interp,objPtr) \
Tcl_EvalObjEx((interp),(objPtr),TCL_EVAL_GLOBAL)
#endif
+
/*
* These function have been renamed. The old names are deprecated, but we
* define these macros for backwards compatibilty.
*/
-
#define Tcl_Ckalloc Tcl_Alloc
#define Tcl_Ckfree Tcl_Free
#define Tcl_Ckrealloc Tcl_Realloc
@@ -1639,6 +2204,7 @@ typedef unsigned short Tcl_UniChar;
#define panic Tcl_Panic
#define panicVA Tcl_PanicVA
+
/*
* The following constant is used to test for older versions of Tcl
* in the stubs tables.
@@ -1647,7 +2213,7 @@ typedef unsigned short Tcl_UniChar;
* value since the stubs tables don't match.
*/
-#define TCL_STUB_MAGIC 0xFCA3BACF
+#define TCL_STUB_MAGIC ((int)0xFCA3BACF)
/*
* The following function is required to be defined in all stubs aware
@@ -1657,8 +2223,8 @@ typedef unsigned short Tcl_UniChar;
* linked into an application.
*/
-EXTERN char * Tcl_InitStubs _ANSI_ARGS_((Tcl_Interp *interp,
- char *version, int exact));
+EXTERN CONST char * Tcl_InitStubs _ANSI_ARGS_((Tcl_Interp *interp,
+ CONST char *version, int exact));
#ifndef USE_TCL_STUBS
@@ -1680,6 +2246,26 @@ EXTERN char * Tcl_InitStubs _ANSI_ARGS_((Tcl_Interp *interp,
#include "tclDecls.h"
/*
+ * Include platform specific public function declarations that are
+ * accessible via the stubs table.
+ */
+
+/*
+ * tclPlatDecls.h can't be included here on the Mac, as we need
+ * Mac specific headers to define the Mac types used in this file,
+ * but these Mac haders conflict with a number of tk types
+ * and thus can't be included in the globally read tcl.h
+ * This header was originally added here as a fix for bug 5241
+ * (stub link error for symbols in TclPlatStubs table), as a work-
+ * around for the bug on the mac, tclMac.h is included immediately
+ * after tcl.h in the tcl precompiled header (with DLLEXPORT set).
+ */
+
+#if !defined(MAC_TCL)
+#include "tclPlatDecls.h"
+#endif
+
+/*
* Public functions that are not accessible via the stubs table.
*/
@@ -1691,24 +2277,23 @@ EXTERN void Tcl_Main _ANSI_ARGS_((int argc, char **argv,
* This function is not *implemented* by the tcl library, so the storage
* class is neither DLLEXPORT nor DLLIMPORT
*/
-
#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS
EXTERN int Tcl_AppInit _ANSI_ARGS_((Tcl_Interp *interp));
-#endif /* RESOURCE_INCLUDED */
+#endif /* RC_INVOKED */
#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLIMPORT
+#endif /* RESOURCE_INCLUDED */
+
/*
* end block for C++
*/
-
#ifdef __cplusplus
}
#endif
-
-#endif /* _TCL */
+#endif /* _TCL */
diff --git a/tcl/generic/tclAlloc.c b/tcl/generic/tclAlloc.c
index 44c4e94b2c2..b510fb95bca 100644
--- a/tcl/generic/tclAlloc.c
+++ b/tcl/generic/tclAlloc.c
@@ -18,6 +18,13 @@
* RCS: @(#) $Id$
*/
+/*
+ * Windows and Unix use an alternative allocator when building with threads
+ * that has significantly reduced lock contention.
+ */
+
+#if !defined(TCL_THREADS) || !defined(USE_THREAD_ALLOC)
+
#include "tclInt.h"
#include "tclPort.h"
@@ -30,12 +37,10 @@
#endif
/*
- * With gcc this will already be defined. This should really
- * make use of AC_CHECK_TYPE(caddr_t) but that can wait
- * until we use config.h properly.
+ * We should really make use of AC_CHECK_TYPE(caddr_t)
+ * here, but it can wait until Tcl uses config.h properly.
*/
-
-#if defined(MAC_TCL) || defined(_MSC_VER) || defined(__MINGW32__)
+#if defined(MAC_TCL) || defined(_MSC_VER) || defined(__MINGW32__) || defined(__BORLANDC__)
typedef unsigned long caddr_t;
#endif
@@ -723,4 +728,4 @@ TclpRealloc(cp, nbytes)
}
#endif /* !USE_TCLALLOC */
-
+#endif /* !TCL_THREADS */
diff --git a/tcl/generic/tclAsync.c b/tcl/generic/tclAsync.c
index 6ec8ca9934b..cf689dee2cc 100644
--- a/tcl/generic/tclAsync.c
+++ b/tcl/generic/tclAsync.c
@@ -18,6 +18,9 @@
#include "tclInt.h"
#include "tclPort.h"
+/* Forward declaration */
+struct ThreadSpecificData;
+
/*
* One of the following structures exists for each asynchronous
* handler:
@@ -33,34 +36,74 @@ typedef struct AsyncHandler {
* is invoked. */
ClientData clientData; /* Value to pass to handler when it
* is invoked. */
+ struct ThreadSpecificData *originTsd;
+ /* Used in Tcl_AsyncMark to modify thread-
+ * specific data from outside the thread
+ * it is associated to. */
+ Tcl_ThreadId originThrdId; /* Origin thread where this token was
+ * created and where it will be
+ * yielded. */
} AsyncHandler;
-/*
- * The variables below maintain a list of all existing handlers.
- */
-static AsyncHandler *firstHandler; /* First handler defined for process,
- * or NULL if none. */
-static AsyncHandler *lastHandler; /* Last handler or NULL. */
+typedef struct ThreadSpecificData {
+ /*
+ * The variables below maintain a list of all existing handlers
+ * specific to the calling thread.
+ */
+ AsyncHandler *firstHandler; /* First handler defined for process,
+ * or NULL if none. */
+ AsyncHandler *lastHandler; /* Last handler or NULL. */
-TCL_DECLARE_MUTEX(asyncMutex) /* Process-wide async handler lock */
+ /*
+ * The variable below is set to 1 whenever a handler becomes ready and
+ * it is cleared to zero whenever Tcl_AsyncInvoke is called. It can be
+ * checked elsewhere in the application by calling Tcl_AsyncReady to see
+ * if Tcl_AsyncInvoke should be invoked.
+ */
-/*
- * The variable below is set to 1 whenever a handler becomes ready and
- * it is cleared to zero whenever Tcl_AsyncInvoke is called. It can be
- * checked elsewhere in the application by calling Tcl_AsyncReady to see
- * if Tcl_AsyncInvoke should be invoked.
- */
+ int asyncReady;
+
+ /*
+ * The variable below indicates whether Tcl_AsyncInvoke is currently
+ * working. If so then we won't set asyncReady again until
+ * Tcl_AsyncInvoke returns.
+ */
-static int asyncReady = 0;
+ int asyncActive;
+ Tcl_Mutex asyncMutex; /* Thread-specific AsyncHandler linked-list lock */
+
+} ThreadSpecificData;
+static Tcl_ThreadDataKey dataKey;
+
+
/*
- * The variable below indicates whether Tcl_AsyncInvoke is currently
- * working. If so then we won't set asyncReady again until
- * Tcl_AsyncInvoke returns.
+ *----------------------------------------------------------------------
+ *
+ * TclFinalizeAsync --
+ *
+ * Finalizes the mutex in the thread local data structure for the
+ * async subsystem.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Forgets knowledge of the mutex should it have been created.
+ *
+ *----------------------------------------------------------------------
*/
-static int asyncActive = 0;
+void
+TclFinalizeAsync()
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ if (tsdPtr->asyncMutex != NULL) {
+ Tcl_MutexFinalize(&tsdPtr->asyncMutex);
+ }
+}
/*
*----------------------------------------------------------------------
@@ -88,20 +131,24 @@ Tcl_AsyncCreate(proc, clientData)
ClientData clientData; /* Argument to pass to handler. */
{
AsyncHandler *asyncPtr;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler));
asyncPtr->ready = 0;
asyncPtr->nextPtr = NULL;
asyncPtr->proc = proc;
asyncPtr->clientData = clientData;
- Tcl_MutexLock(&asyncMutex);
- if (firstHandler == NULL) {
- firstHandler = asyncPtr;
+ asyncPtr->originTsd = tsdPtr;
+ asyncPtr->originThrdId = Tcl_GetCurrentThread();
+
+ Tcl_MutexLock(&tsdPtr->asyncMutex);
+ if (tsdPtr->firstHandler == NULL) {
+ tsdPtr->firstHandler = asyncPtr;
} else {
- lastHandler->nextPtr = asyncPtr;
+ tsdPtr->lastHandler->nextPtr = asyncPtr;
}
- lastHandler = asyncPtr;
- Tcl_MutexUnlock(&asyncMutex);
+ tsdPtr->lastHandler = asyncPtr;
+ Tcl_MutexUnlock(&tsdPtr->asyncMutex);
return (Tcl_AsyncHandler) asyncPtr;
}
@@ -128,13 +175,15 @@ void
Tcl_AsyncMark(async)
Tcl_AsyncHandler async; /* Token for handler. */
{
- Tcl_MutexLock(&asyncMutex);
- ((AsyncHandler *) async)->ready = 1;
- if (!asyncActive) {
- asyncReady = 1;
- TclpAsyncMark(async);
+ AsyncHandler *token = (AsyncHandler *) async;
+
+ Tcl_MutexLock(&token->originTsd->asyncMutex);
+ token->ready = 1;
+ if (!token->originTsd->asyncActive) {
+ token->originTsd->asyncReady = 1;
+ Tcl_ThreadAlert(token->originThrdId);
}
- Tcl_MutexUnlock(&asyncMutex);
+ Tcl_MutexUnlock(&token->originTsd->asyncMutex);
}
/*
@@ -167,14 +216,16 @@ Tcl_AsyncInvoke(interp, code)
* just completed. */
{
AsyncHandler *asyncPtr;
- Tcl_MutexLock(&asyncMutex);
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ Tcl_MutexLock(&tsdPtr->asyncMutex);
- if (asyncReady == 0) {
- Tcl_MutexUnlock(&asyncMutex);
+ if (tsdPtr->asyncReady == 0) {
+ Tcl_MutexUnlock(&tsdPtr->asyncMutex);
return code;
}
- asyncReady = 0;
- asyncActive = 1;
+ tsdPtr->asyncReady = 0;
+ tsdPtr->asyncActive = 1;
if (interp == NULL) {
code = 0;
}
@@ -191,7 +242,7 @@ Tcl_AsyncInvoke(interp, code)
*/
while (1) {
- for (asyncPtr = firstHandler; asyncPtr != NULL;
+ for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL;
asyncPtr = asyncPtr->nextPtr) {
if (asyncPtr->ready) {
break;
@@ -201,12 +252,12 @@ Tcl_AsyncInvoke(interp, code)
break;
}
asyncPtr->ready = 0;
- Tcl_MutexUnlock(&asyncMutex);
+ Tcl_MutexUnlock(&tsdPtr->asyncMutex);
code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code);
- Tcl_MutexLock(&asyncMutex);
+ Tcl_MutexLock(&tsdPtr->asyncMutex);
}
- asyncActive = 0;
- Tcl_MutexUnlock(&asyncMutex);
+ tsdPtr->asyncActive = 0;
+ Tcl_MutexUnlock(&tsdPtr->asyncMutex);
return code;
}
@@ -231,26 +282,27 @@ void
Tcl_AsyncDelete(async)
Tcl_AsyncHandler async; /* Token for handler to delete. */
{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
AsyncHandler *asyncPtr = (AsyncHandler *) async;
AsyncHandler *prevPtr;
- Tcl_MutexLock(&asyncMutex);
- if (firstHandler == asyncPtr) {
- firstHandler = asyncPtr->nextPtr;
- if (firstHandler == NULL) {
- lastHandler = NULL;
+ Tcl_MutexLock(&tsdPtr->asyncMutex);
+ if (tsdPtr->firstHandler == asyncPtr) {
+ tsdPtr->firstHandler = asyncPtr->nextPtr;
+ if (tsdPtr->firstHandler == NULL) {
+ tsdPtr->lastHandler = NULL;
}
} else {
- prevPtr = firstHandler;
+ prevPtr = tsdPtr->firstHandler;
while (prevPtr->nextPtr != asyncPtr) {
prevPtr = prevPtr->nextPtr;
}
prevPtr->nextPtr = asyncPtr->nextPtr;
- if (lastHandler == asyncPtr) {
- lastHandler = prevPtr;
+ if (tsdPtr->lastHandler == asyncPtr) {
+ tsdPtr->lastHandler = prevPtr;
}
}
- Tcl_MutexUnlock(&asyncMutex);
+ Tcl_MutexUnlock(&tsdPtr->asyncMutex);
ckfree((char *) asyncPtr);
}
@@ -261,7 +313,7 @@ Tcl_AsyncDelete(async)
*
* This procedure can be used to tell whether Tcl_AsyncInvoke
* needs to be called. This procedure is the external interface
- * for checking the internal asyncReady variable.
+ * for checking the thread-specific asyncReady variable.
*
* Results:
* The return value is 1 whenever a handler is ready and is 0
@@ -276,5 +328,6 @@ Tcl_AsyncDelete(async)
int
Tcl_AsyncReady()
{
- return asyncReady;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ return tsdPtr->asyncReady;
}
diff --git a/tcl/generic/tclBasic.c b/tcl/generic/tclBasic.c
index 8c6a19de4ef..1fe5f109c56 100644
--- a/tcl/generic/tclBasic.c
+++ b/tcl/generic/tclBasic.c
@@ -3,11 +3,12 @@
*
* Contains the basic facilities for TCL command interpretation,
* including interpreter creation and deletion, command creation
- * and deletion, and command parsing and execution.
+ * and deletion, and command/script execution.
*
* Copyright (c) 1987-1994 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
* Copyright (c) 1998-1999 by Scriptics Corporation.
+ * Copyright (c) 2001, 2002 by Kevin B. Kenny. All rights reserved.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -25,12 +26,20 @@
* Static procedures in this file:
*/
+static char * CallCommandTraces _ANSI_ARGS_((Interp *iPtr,
+ Command *cmdPtr, CONST char *oldName,
+ CONST char* newName, int flags));
static void DeleteInterpProc _ANSI_ARGS_((Tcl_Interp *interp));
static void ProcessUnexpectedResult _ANSI_ARGS_((
Tcl_Interp *interp, int returnCode));
-static void RecordTracebackInfo _ANSI_ARGS_((
- Tcl_Interp *interp, Tcl_Obj *objPtr,
- int numSrcBytes));
+static int StringTraceProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp* interp,
+ int level,
+ CONST char* command,
+ Tcl_Command commandInfo,
+ int objc,
+ Tcl_Obj *CONST objv[]));
+static void StringTraceDeleteProc _ANSI_ARGS_((ClientData clientData));
extern TclStubs tclStubs;
@@ -62,7 +71,7 @@ static CmdInfo builtInCmds[] = {
*/
{"append", (Tcl_CmdProc *) NULL, Tcl_AppendObjCmd,
- (CompileProc *) NULL, 1},
+ TclCompileAppendCmd, 1},
{"array", (Tcl_CmdProc *) NULL, Tcl_ArrayObjCmd,
(CompileProc *) NULL, 1},
{"binary", (Tcl_CmdProc *) NULL, Tcl_BinaryObjCmd,
@@ -110,15 +119,15 @@ static CmdInfo builtInCmds[] = {
{"join", (Tcl_CmdProc *) NULL, Tcl_JoinObjCmd,
(CompileProc *) NULL, 1},
{"lappend", (Tcl_CmdProc *) NULL, Tcl_LappendObjCmd,
- (CompileProc *) NULL, 1},
+ TclCompileLappendCmd, 1},
{"lindex", (Tcl_CmdProc *) NULL, Tcl_LindexObjCmd,
- (CompileProc *) NULL, 1},
+ TclCompileLindexCmd, 1},
{"linsert", (Tcl_CmdProc *) NULL, Tcl_LinsertObjCmd,
(CompileProc *) NULL, 1},
{"list", (Tcl_CmdProc *) NULL, Tcl_ListObjCmd,
- (CompileProc *) NULL, 1},
+ TclCompileListCmd, 1},
{"llength", (Tcl_CmdProc *) NULL, Tcl_LlengthObjCmd,
- (CompileProc *) NULL, 1},
+ TclCompileLlengthCmd, 1},
{"load", (Tcl_CmdProc *) NULL, Tcl_LoadObjCmd,
(CompileProc *) NULL, 0},
{"lrange", (Tcl_CmdProc *) NULL, Tcl_LrangeObjCmd,
@@ -127,6 +136,8 @@ static CmdInfo builtInCmds[] = {
(CompileProc *) NULL, 1},
{"lsearch", (Tcl_CmdProc *) NULL, Tcl_LsearchObjCmd,
(CompileProc *) NULL, 1},
+ {"lset", (Tcl_CmdProc *) NULL, Tcl_LsetObjCmd,
+ TclCompileLsetCmd, 1},
{"lsort", (Tcl_CmdProc *) NULL, Tcl_LsortObjCmd,
(CompileProc *) NULL, 1},
{"namespace", (Tcl_CmdProc *) NULL, Tcl_NamespaceObjCmd,
@@ -136,13 +147,13 @@ static CmdInfo builtInCmds[] = {
{"proc", (Tcl_CmdProc *) NULL, Tcl_ProcObjCmd,
(CompileProc *) NULL, 1},
{"regexp", (Tcl_CmdProc *) NULL, Tcl_RegexpObjCmd,
- (CompileProc *) NULL, 1},
+ TclCompileRegexpCmd, 1},
{"regsub", (Tcl_CmdProc *) NULL, Tcl_RegsubObjCmd,
(CompileProc *) NULL, 1},
{"rename", (Tcl_CmdProc *) NULL, Tcl_RenameObjCmd,
(CompileProc *) NULL, 1},
{"return", (Tcl_CmdProc *) NULL, Tcl_ReturnObjCmd,
- (CompileProc *) NULL, 1},
+ TclCompileReturnCmd, 1},
{"scan", (Tcl_CmdProc *) NULL, Tcl_ScanObjCmd,
(CompileProc *) NULL, 1},
{"set", (Tcl_CmdProc *) NULL, Tcl_SetObjCmd,
@@ -150,7 +161,7 @@ static CmdInfo builtInCmds[] = {
{"split", (Tcl_CmdProc *) NULL, Tcl_SplitObjCmd,
(CompileProc *) NULL, 1},
{"string", (Tcl_CmdProc *) NULL, Tcl_StringObjCmd,
- (CompileProc *) NULL, 1},
+ TclCompileStringCmd, 1},
{"subst", (Tcl_CmdProc *) NULL, Tcl_SubstObjCmd,
(CompileProc *) NULL, 1},
{"switch", (Tcl_CmdProc *) NULL, Tcl_SwitchObjCmd,
@@ -239,6 +250,15 @@ static CmdInfo builtInCmds[] = {
(CompileProc *) NULL, 0}
};
+/*
+ * The following structure holds the client data for string-based
+ * trace procs
+ */
+
+typedef struct StringTraceData {
+ ClientData clientData; /* Client data from Tcl_CreateTrace */
+ Tcl_CmdTraceProc* proc; /* Trace procedure from Tcl_CreateTrace */
+} StringTraceData;
/*
*----------------------------------------------------------------------
@@ -253,8 +273,8 @@ static CmdInfo builtInCmds[] = {
* Tcl_DeleteInterp.
*
* Side effects:
- * The command interpreter is initialized with an empty variable
- * table and the built-in commands.
+ * The command interpreter is initialized with the built-in commands
+ * and with the variables documented in tclvars(n).
*
*----------------------------------------------------------------------
*/
@@ -311,10 +331,10 @@ Tcl_CreateInterp()
Tcl_InitHashTable(&iPtr->mathFuncTable, TCL_STRING_KEYS);
iPtr->numLevels = 0;
- iPtr->maxNestingDepth = 1000;
+ iPtr->maxNestingDepth = MAX_NESTING_DEPTH;
iPtr->framePtr = NULL;
iPtr->varFramePtr = NULL;
- iPtr->activeTracePtr = NULL;
+ iPtr->activeVarTracePtr = NULL;
iPtr->returnCode = TCL_OK;
iPtr->errorInfo = NULL;
iPtr->errorCode = NULL;
@@ -335,6 +355,9 @@ Tcl_CreateInterp()
iPtr->scriptFile = NULL;
iPtr->flags = 0;
iPtr->tracePtr = NULL;
+ iPtr->tracesForbiddingInline = 0;
+ iPtr->activeCmdTracePtr = NULL;
+ iPtr->activeInterpTracePtr = NULL;
iPtr->assocData = (Tcl_HashTable *) NULL;
iPtr->execEnvPtr = NULL; /* set after namespaces initialized */
iPtr->emptyObjPtr = Tcl_NewObj(); /* another empty object */
@@ -447,8 +470,9 @@ Tcl_CreateInterp()
}
cmdPtr->deleteProc = NULL;
cmdPtr->deleteData = (ClientData) NULL;
- cmdPtr->deleted = 0;
+ cmdPtr->flags = 0;
cmdPtr->importRefPtr = NULL;
+ cmdPtr->tracePtr = NULL;
Tcl_SetHashValue(hPtr, cmdPtr);
}
}
@@ -458,7 +482,7 @@ Tcl_CreateInterp()
*/
i = 0;
- for (builtinFuncPtr = builtinFuncTable; builtinFuncPtr->name != NULL;
+ for (builtinFuncPtr = tclBuiltinFuncTable; builtinFuncPtr->name != NULL;
builtinFuncPtr++) {
Tcl_CreateMathFunc((Tcl_Interp *) iPtr, builtinFuncPtr->name,
builtinFuncPtr->numArgs, builtinFuncPtr->argTypes,
@@ -518,6 +542,9 @@ Tcl_CreateInterp()
((order.c[0] == 1) ? "littleEndian" : "bigEndian"),
TCL_GLOBAL_ONLY);
+ Tcl_SetVar2Ex(interp, "tcl_platform", "wordSize",
+ Tcl_NewLongObj((long) sizeof(long)), TCL_GLOBAL_ONLY);
+
/*
* Set up other variables such as tcl_version and tcl_library
*/
@@ -715,7 +742,7 @@ Tcl_DontCallWhenDeleted(interp, proc, clientData)
void
Tcl_SetAssocData(interp, name, proc, clientData)
Tcl_Interp *interp; /* Interpreter to associate with. */
- char *name; /* Name for association. */
+ CONST char *name; /* Name for association. */
Tcl_InterpDeleteProc *proc; /* Proc to call when interpreter is
* about to be deleted. */
ClientData clientData; /* One-word value to pass to proc. */
@@ -761,7 +788,7 @@ Tcl_SetAssocData(interp, name, proc, clientData)
void
Tcl_DeleteAssocData(interp, name)
Tcl_Interp *interp; /* Interpreter to associate with. */
- char *name; /* Name of association. */
+ CONST char *name; /* Name of association. */
{
Interp *iPtr = (Interp *) interp;
AssocData *dPtr;
@@ -803,7 +830,7 @@ Tcl_DeleteAssocData(interp, name)
ClientData
Tcl_GetAssocData(interp, name, procPtr)
Tcl_Interp *interp; /* Interpreter associated with. */
- char *name; /* Name of association. */
+ CONST char *name; /* Name of association. */
Tcl_InterpDeleteProc **procPtr; /* Pointer to place to store address
* of current deletion callback. */
{
@@ -1048,10 +1075,7 @@ DeleteInterpProc(interp)
}
TclFreePackageInfo(iPtr);
while (iPtr->tracePtr != NULL) {
- Trace *nextPtr = iPtr->tracePtr->nextPtr;
-
- ckfree((char *) iPtr->tracePtr);
- iPtr->tracePtr = nextPtr;
+ Tcl_DeleteTrace( (Tcl_Interp*) iPtr, (Tcl_Trace) iPtr->tracePtr );
}
if (iPtr->execEnvPtr != NULL) {
TclDeleteExecEnv(iPtr->execEnvPtr);
@@ -1098,8 +1122,8 @@ DeleteInterpProc(interp)
int
Tcl_HideCommand(interp, cmdName, hiddenCmdToken)
Tcl_Interp *interp; /* Interpreter in which to hide command. */
- char *cmdName; /* Name of command to hide. */
- char *hiddenCmdToken; /* Token name of the to-be-hidden command. */
+ CONST char *cmdName; /* Name of command to hide. */
+ CONST char *hiddenCmdToken; /* Token name of the to-be-hidden command. */
{
Interp *iPtr = (Interp *) interp;
Tcl_Command cmd;
@@ -1261,8 +1285,8 @@ int
Tcl_ExposeCommand(interp, hiddenCmdToken, cmdName)
Tcl_Interp *interp; /* Interpreter in which to make command
* callable. */
- char *hiddenCmdToken; /* Name of hidden command. */
- char *cmdName; /* Name of to-be-exposed command. */
+ CONST char *hiddenCmdToken; /* Name of hidden command. */
+ CONST char *cmdName; /* Name of to-be-exposed command. */
{
Interp *iPtr = (Interp *) interp;
Command *cmdPtr;
@@ -1415,7 +1439,7 @@ Tcl_Command
Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc)
Tcl_Interp *interp; /* Token for command interpreter returned by
* a previous call to Tcl_CreateInterp. */
- char *cmdName; /* Name of command. If it contains namespace
+ CONST char *cmdName; /* Name of command. If it contains namespace
* qualifiers, the new command is put in the
* specified namespace; otherwise it is put
* in the global namespace. */
@@ -1430,7 +1454,7 @@ Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc)
Namespace *nsPtr, *dummy1, *dummy2;
Command *cmdPtr, *refCmdPtr;
Tcl_HashEntry *hPtr;
- char *tail;
+ CONST char *tail;
int new;
ImportedCmdData *dataPtr;
@@ -1498,8 +1522,9 @@ Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc)
cmdPtr->clientData = clientData;
cmdPtr->deleteProc = deleteProc;
cmdPtr->deleteData = clientData;
- cmdPtr->deleted = 0;
+ cmdPtr->flags = 0;
cmdPtr->importRefPtr = NULL;
+ cmdPtr->tracePtr = NULL;
/*
* Plug in any existing import references found above. Be sure
@@ -1559,7 +1584,7 @@ Tcl_Command
Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc)
Tcl_Interp *interp; /* Token for command interpreter (returned
* by previous call to Tcl_CreateInterp). */
- char *cmdName; /* Name of command. If it contains namespace
+ CONST char *cmdName; /* Name of command. If it contains namespace
* qualifiers, the new command is put in the
* specified namespace; otherwise it is put
* in the global namespace. */
@@ -1576,7 +1601,7 @@ Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc)
Namespace *nsPtr, *dummy1, *dummy2;
Command *cmdPtr, *refCmdPtr;
Tcl_HashEntry *hPtr;
- char *tail;
+ CONST char *tail;
int new;
ImportedCmdData *dataPtr;
@@ -1659,8 +1684,9 @@ Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc)
cmdPtr->clientData = (ClientData) cmdPtr;
cmdPtr->deleteProc = deleteProc;
cmdPtr->deleteData = clientData;
- cmdPtr->deleted = 0;
+ cmdPtr->flags = 0;
cmdPtr->importRefPtr = NULL;
+ cmdPtr->tracePtr = NULL;
/*
* Plug in any existing import references found above. Be sure
@@ -1727,8 +1753,8 @@ TclInvokeStringCommand(clientData, interp, objc, objv)
*/
#define NUM_ARGS 20
- char *(argStorage[NUM_ARGS]);
- char **argv = argStorage;
+ CONST char *(argStorage[NUM_ARGS]);
+ CONST char **argv = argStorage;
/*
* Create the string argument array "argv". Make sure argv is large
@@ -1737,7 +1763,7 @@ TclInvokeStringCommand(clientData, interp, objc, objv)
*/
if ((objc + 1) > NUM_ARGS) {
- argv = (char **) ckalloc((unsigned)(objc + 1) * sizeof(char *));
+ argv = (CONST char **) ckalloc((unsigned)(objc + 1) * sizeof(char *));
}
for (i = 0; i < objc; i++) {
@@ -1788,7 +1814,7 @@ TclInvokeObjectCommand(clientData, interp, argc, argv)
ClientData clientData; /* Points to command's Command structure. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- register char **argv; /* Argument strings. */
+ register CONST char **argv; /* Argument strings. */
{
Command *cmdPtr = (Command *) clientData;
register Tcl_Obj *objPtr;
@@ -1886,7 +1912,7 @@ TclRenameCommand(interp, oldName, newName)
char *newName; /* New command name. */
{
Interp *iPtr = (Interp *) interp;
- char *newTail;
+ CONST char *newTail;
Namespace *cmdNsPtr, *newNsPtr, *dummy1, *dummy2;
Tcl_Command cmd;
Command *cmdPtr;
@@ -1976,6 +2002,15 @@ TclRenameCommand(interp, oldName, newName)
}
/*
+ * Script for rename traces can delete the command "oldName".
+ * Therefore increment the reference count for cmdPtr so that
+ * it's Command structure is freed only towards the end of this
+ * function by calling TclCleanupCommand.
+ */
+ cmdPtr->refCount++;
+ CallCommandTraces(iPtr,cmdPtr,oldName,newName,TCL_TRACE_RENAME);
+
+ /*
* The new command name is okay, so remove the command from its
* current namespace. This is like deleting the command, so bump
* the cmdEpoch to invalidate any cached references to the command.
@@ -1995,6 +2030,12 @@ TclRenameCommand(interp, oldName, newName)
iPtr->compileEpoch++;
}
+ /*
+ * Now free the Command structure, if the "oldName" command has
+ * been deleted by invocation of rename traces.
+ */
+ TclCleanupCommand(cmdPtr);
+
return TCL_OK;
}
@@ -2024,15 +2065,48 @@ int
Tcl_SetCommandInfo(interp, cmdName, infoPtr)
Tcl_Interp *interp; /* Interpreter in which to look
* for command. */
- char *cmdName; /* Name of desired command. */
- Tcl_CmdInfo *infoPtr; /* Where to find information
+ CONST char *cmdName; /* Name of desired command. */
+ CONST Tcl_CmdInfo *infoPtr; /* Where to find information
* to store in the command. */
{
Tcl_Command cmd;
- Command *cmdPtr;
cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL,
/*flags*/ 0);
+
+ return Tcl_SetCommandInfoFromToken( cmd, infoPtr );
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_SetCommandInfoFromToken --
+ *
+ * Modifies various information about a Tcl command. Note that
+ * this procedure will not change a command's namespace; use
+ * Tcl_RenameCommand to do that. Also, the isNativeObjectProc
+ * member of *infoPtr is ignored.
+ *
+ * Results:
+ * If cmdName exists in interp, then the information at *infoPtr
+ * is stored with the command in place of the current information
+ * and 1 is returned. If the command doesn't exist then 0 is
+ * returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_SetCommandInfoFromToken( cmd, infoPtr )
+ Tcl_Command cmd;
+ CONST Tcl_CmdInfo* infoPtr;
+{
+ Command* cmdPtr; /* Internal representation of the command */
+
if (cmd == (Tcl_Command) NULL) {
return 0;
}
@@ -2079,16 +2153,46 @@ int
Tcl_GetCommandInfo(interp, cmdName, infoPtr)
Tcl_Interp *interp; /* Interpreter in which to look
* for command. */
- char *cmdName; /* Name of desired command. */
+ CONST char *cmdName; /* Name of desired command. */
Tcl_CmdInfo *infoPtr; /* Where to store information about
* command. */
{
Tcl_Command cmd;
- Command *cmdPtr;
cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL,
/*flags*/ 0);
- if (cmd == (Tcl_Command) NULL) {
+
+ return Tcl_GetCommandInfoFromToken( cmd, infoPtr );
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_GetCommandInfoFromToken --
+ *
+ * Returns various information about a Tcl command.
+ *
+ * Results:
+ * Copies information from the command identified by 'cmd' into
+ * a caller-supplied structure and returns 1. If the 'cmd' is
+ * NULL, leaves the structure untouched and returns 0.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_GetCommandInfoFromToken( cmd, infoPtr )
+ Tcl_Command cmd;
+ Tcl_CmdInfo* infoPtr;
+{
+
+ Command* cmdPtr; /* Internal representation of the command */
+
+ if ( cmd == (Tcl_Command) NULL ) {
return 0;
}
@@ -2107,7 +2211,9 @@ Tcl_GetCommandInfo(interp, cmdName, infoPtr)
infoPtr->deleteProc = cmdPtr->deleteProc;
infoPtr->deleteData = cmdPtr->deleteData;
infoPtr->namespacePtr = (Tcl_Namespace *) cmdPtr->nsPtr;
+
return 1;
+
}
/*
@@ -2128,7 +2234,7 @@ Tcl_GetCommandInfo(interp, cmdName, infoPtr)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_GetCommandName(interp, command)
Tcl_Interp *interp; /* Interpreter containing the command. */
Tcl_Command command; /* Token for command returned by a previous
@@ -2225,7 +2331,7 @@ int
Tcl_DeleteCommand(interp, cmdName)
Tcl_Interp *interp; /* Token for command interpreter (returned
* by a previous Tcl_CreateInterp call). */
- char *cmdName; /* Name of command to remove. */
+ CONST char *cmdName; /* Name of command to remove. */
{
Tcl_Command cmd;
@@ -2281,7 +2387,7 @@ Tcl_DeleteCommandFromToken(interp, cmd)
* flag allows us to detect these cases and skip nested deletes.
*/
- if (cmdPtr->deleted) {
+ if (cmdPtr->flags & CMD_IS_DELETED) {
/*
* Another deletion is already in progress. Remove the hash
* table entry now, but don't invoke a callback or free the
@@ -2293,6 +2399,33 @@ Tcl_DeleteCommandFromToken(interp, cmd)
return 0;
}
+ /*
+ * We must delete this command, even though both traces and
+ * delete procs may try to avoid this (renaming the command etc).
+ * Also traces and delete procs may try to delete the command
+ * themsevles. This flag declares that a delete is in progress
+ * and that recursive deletes should be ignored.
+ */
+ cmdPtr->flags |= CMD_IS_DELETED;
+
+ /*
+ * Call trace procedures for the command being deleted. Then delete
+ * its traces.
+ */
+
+ if (cmdPtr->tracePtr != NULL) {
+ CommandTrace *tracePtr;
+ CallCommandTraces(iPtr,cmdPtr,NULL,NULL,TCL_TRACE_DELETE);
+ /* Now delete these traces */
+ tracePtr = cmdPtr->tracePtr;
+ while (tracePtr != NULL) {
+ CommandTrace *nextPtr = tracePtr->nextPtr;
+ Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
+ tracePtr = nextPtr;
+ }
+ cmdPtr->tracePtr = NULL;
+ }
+
/*
* If the command being deleted has a compile procedure, increment the
* interpreter's compileEpoch to invalidate its compiled code. This
@@ -2306,7 +2439,6 @@ Tcl_DeleteCommandFromToken(interp, cmd)
iPtr->compileEpoch++;
}
- cmdPtr->deleted = 1;
if (cmdPtr->deleteProc != NULL) {
/*
* Delete the command's client data. If this was an imported command
@@ -2381,6 +2513,98 @@ Tcl_DeleteCommandFromToken(interp, cmd)
TclCleanupCommand(cmdPtr);
return 0;
}
+static char *
+CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags)
+ Interp *iPtr; /* Interpreter containing command. */
+ Command *cmdPtr; /* Command whose traces are to be
+ * invoked. */
+ CONST char *oldName; /* Command's old name, or NULL if we
+ * must get the name from cmdPtr */
+ CONST char *newName; /* Command's new name, or NULL if
+ * the command is not being renamed */
+ int flags; /* Flags passed to trace procedures:
+ * indicates what's happening to command,
+ * plus other stuff like TCL_GLOBAL_ONLY,
+ * TCL_NAMESPACE_ONLY, and
+ * TCL_INTERP_DESTROYED. */
+{
+ register CommandTrace *tracePtr;
+ ActiveCommandTrace active;
+ char *result;
+ Tcl_Obj *oldNamePtr = NULL;
+
+ if (cmdPtr->flags & CMD_TRACE_ACTIVE) {
+ /*
+ * While a rename trace is active, we will not process any more
+ * rename traces; while a delete trace is active we will never
+ * reach here -- because Tcl_DeleteCommandFromToken checks for the
+ * condition (cmdPtr->flags & CMD_IS_DELETED) and returns immediately
+ * when a command deletion is in progress. For all other traces,
+ * delete traces will not be invoked but a call to TraceCommandProc
+ * will ensure that tracePtr->clientData is freed whenever the
+ * command "oldName" is deleted.
+ */
+ if (cmdPtr->flags & TCL_TRACE_RENAME) {
+ flags &= ~TCL_TRACE_RENAME;
+ }
+ if (flags == 0) {
+ return NULL;
+ }
+ }
+ cmdPtr->flags |= CMD_TRACE_ACTIVE;
+ cmdPtr->refCount++;
+
+ result = NULL;
+ active.nextPtr = iPtr->activeCmdTracePtr;
+ iPtr->activeCmdTracePtr = &active;
+
+ if (flags & TCL_TRACE_DELETE) {
+ flags |= TCL_TRACE_DESTROYED;
+ }
+ active.cmdPtr = cmdPtr;
+ Tcl_Preserve((ClientData) iPtr);
+ for (tracePtr = cmdPtr->tracePtr; tracePtr != NULL;
+ tracePtr = active.nextTracePtr) {
+ active.nextTracePtr = tracePtr->nextPtr;
+ if (!(tracePtr->flags & flags)) {
+ continue;
+ }
+ cmdPtr->flags |= tracePtr->flags;
+ if (oldName == NULL) {
+ TclNewObj(oldNamePtr);
+ Tcl_IncrRefCount(oldNamePtr);
+ Tcl_GetCommandFullName((Tcl_Interp *) iPtr,
+ (Tcl_Command) cmdPtr, oldNamePtr);
+ oldName = TclGetString(oldNamePtr);
+ }
+ Tcl_Preserve((ClientData) tracePtr);
+ (*tracePtr->traceProc)(tracePtr->clientData,
+ (Tcl_Interp *) iPtr, oldName, newName, flags);
+ cmdPtr->flags &= ~tracePtr->flags;
+ Tcl_Release((ClientData) tracePtr);
+ }
+
+ /*
+ * If a new object was created to hold the full oldName,
+ * free it now.
+ */
+
+ if (oldNamePtr != NULL) {
+ TclDecrRefCount(oldNamePtr);
+ }
+
+ /*
+ * Restore the variable's flags, remove the record of our active
+ * traces, and then return.
+ */
+
+ cmdPtr->flags &= ~CMD_TRACE_ACTIVE;
+ cmdPtr->refCount--;
+ iPtr->activeCmdTracePtr = active.nextPtr;
+ Tcl_Release((ClientData) iPtr);
+ return result;
+}
+
/*
*----------------------------------------------------------------------
@@ -2441,7 +2665,7 @@ void
Tcl_CreateMathFunc(interp, name, numArgs, argTypes, proc, clientData)
Tcl_Interp *interp; /* Interpreter in which function is
* to be available. */
- char *name; /* Name of function (e.g. "sin"). */
+ CONST char *name; /* Name of function (e.g. "sin"). */
int numArgs; /* Nnumber of arguments required by
* function. */
Tcl_ValueType *argTypes; /* Array of types acceptable for
@@ -2502,294 +2726,1254 @@ Tcl_CreateMathFunc(interp, name, numArgs, argTypes, proc, clientData)
/*
*----------------------------------------------------------------------
*
- * Tcl_EvalObjEx --
+ * Tcl_GetMathFuncInfo --
*
- * Execute Tcl commands stored in a Tcl object. These commands are
- * compiled into bytecodes if necessary, unless TCL_EVAL_DIRECT
- * is specified.
+ * Discovers how a particular math function was created in a given
+ * interpreter.
*
* Results:
- * The return value is one of the return codes defined in tcl.h
- * (such as TCL_OK), and the interpreter's result contains a value
- * to supplement the return code.
+ * TCL_OK if it succeeds, TCL_ERROR else (leaving an error message
+ * in the interpreter result if that happens.)
*
* Side effects:
- * The object is converted, if necessary, to a ByteCode object that
- * holds the bytecode instructions for the commands. Executing the
- * commands will almost certainly have side effects that depend
- * on those commands.
- *
- * Just as in Tcl_Eval, interp->termOffset is set to the offset of the
- * last character executed in the objPtr's string.
+ * If this function succeeds, the variables pointed to by the
+ * numArgsPtr and argTypePtr arguments will be updated to detail the
+ * arguments allowed by the function. The variable pointed to by the
+ * procPtr argument will be set to NULL if the function is a builtin
+ * function, and will be set to the address of the C function used to
+ * implement the math function otherwise (in which case the variable
+ * pointed to by the clientDataPtr argument will also be updated.)
*
*----------------------------------------------------------------------
*/
int
-Tcl_EvalObjEx(interp, objPtr, flags)
- Tcl_Interp *interp; /* Token for command interpreter
- * (returned by a previous call to
- * Tcl_CreateInterp). */
- register Tcl_Obj *objPtr; /* Pointer to object containing
- * commands to execute. */
- int flags; /* Collection of OR-ed bits that
- * control the evaluation of the
- * script. Supported values are
- * TCL_EVAL_GLOBAL and
- * TCL_EVAL_DIRECT. */
+Tcl_GetMathFuncInfo(interp, name, numArgsPtr, argTypesPtr, procPtr,
+ clientDataPtr)
+ Tcl_Interp *interp;
+ CONST char *name;
+ int *numArgsPtr;
+ Tcl_ValueType **argTypesPtr;
+ Tcl_MathProc **procPtr;
+ ClientData *clientDataPtr;
{
- register Interp *iPtr = (Interp *) interp;
- int evalFlags; /* Interp->evalFlags value when the
- * procedure was called. */
- register ByteCode* codePtr; /* Tcl Internal type of bytecode. */
- int oldCount = iPtr->cmdCount; /* Used to tell whether any commands
- * at all were executed. */
- int numSrcBytes;
- int result;
- CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr
- * in case TCL_EVAL_GLOBAL was set. */
- Namespace *namespacePtr;
+ Interp *iPtr = (Interp *) interp;
+ Tcl_HashEntry *hPtr;
+ MathFunc *mathFuncPtr;
+ Tcl_ValueType *argTypes;
+ int i,numArgs;
- Tcl_IncrRefCount(objPtr);
+ hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, name);
+ if (hPtr == NULL) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "math function \"", name, "\" not known in this interpreter",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr);
- if ((iPtr->flags & USE_EVAL_DIRECT) || (flags & TCL_EVAL_DIRECT)) {
- /*
- * We're not supposed to use the compiler or byte-code interpreter.
- * Let Tcl_EvalEx evaluate the command directly (and probably
- * more slowly).
- *
- * Pure List Optimization (no string representation). In this
- * case, we can safely use Tcl_EvalObjv instead and get an
- * appreciable improvement in execution speed. This is because it
- * allows us to avoid a setFromAny step that would just pack
- * everything into a string and back out again.
- *
- * USE_EVAL_DIRECT is a special flag used for testing purpose only
- * (ensure we go into the TCL_EVAL_DIRECT path, avoiding opt)
- */
- if (!(iPtr->flags & USE_EVAL_DIRECT) &&
- (objPtr->typePtr == &tclListType) && /* is a list... */
- (objPtr->bytes == NULL) /* ...without a string rep */) {
- register List *listRepPtr =
- (List *) objPtr->internalRep.otherValuePtr;
- result = Tcl_EvalObjv(interp, listRepPtr->elemCount,
- listRepPtr->elements, flags);
- } else {
- register char *p;
- p = Tcl_GetStringFromObj(objPtr, &numSrcBytes);
- result = Tcl_EvalEx(interp, p, numSrcBytes, flags);
- }
- Tcl_DecrRefCount(objPtr);
- return result;
+ *numArgsPtr = numArgs = mathFuncPtr->numArgs;
+ if (numArgs == 0) {
+ /* Avoid doing zero-sized allocs... */
+ numArgs = 1;
+ }
+ *argTypesPtr = argTypes =
+ (Tcl_ValueType *)ckalloc(numArgs * sizeof(Tcl_ValueType));
+ for (i = 0; i < mathFuncPtr->numArgs; i++) {
+ argTypes[i] = mathFuncPtr->argTypes[i];
}
- /*
- * Prevent the object from being deleted as a side effect of evaling it.
- */
+ if (mathFuncPtr->builtinFuncIndex == -1) {
+ *procPtr = (Tcl_MathProc *) NULL;
+ } else {
+ *procPtr = mathFuncPtr->proc;
+ *clientDataPtr = mathFuncPtr->clientData;
+ }
- savedVarFramePtr = iPtr->varFramePtr;
- if (flags & TCL_EVAL_GLOBAL) {
- iPtr->varFramePtr = NULL;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_ListMathFuncs --
+ *
+ * Produces a list of all the math functions defined in a given
+ * interpreter.
+ *
+ * Results:
+ * A pointer to a Tcl_Obj structure with a reference count of zero,
+ * or NULL in the case of an error (in which case a suitable error
+ * message will be left in the interpreter result.)
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+Tcl_ListMathFuncs(interp, pattern)
+ Tcl_Interp *interp;
+ CONST char *pattern;
+{
+ Interp *iPtr = (Interp *) interp;
+ Tcl_Obj *resultList = Tcl_NewObj();
+ register Tcl_HashEntry *hPtr;
+ Tcl_HashSearch hSearch;
+ CONST char *name;
+
+ for (hPtr = Tcl_FirstHashEntry(&iPtr->mathFuncTable, &hSearch);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) {
+ name = Tcl_GetHashKey(&iPtr->mathFuncTable, hPtr);
+ if ((pattern == NULL || Tcl_StringMatch(name, pattern)) &&
+ /* I don't expect this to fail, but... */
+ Tcl_ListObjAppendElement(interp, resultList,
+ Tcl_NewStringObj(name,-1)) != TCL_OK) {
+ Tcl_DecrRefCount(resultList);
+ return NULL;
+ }
}
+ return resultList;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclInterpReady --
+ *
+ * Check if an interpreter is ready to eval commands or scripts,
+ * i.e., if it was not deleted and if the nesting level is not
+ * too high.
+ *
+ * Results:
+ * The return value is TCL_OK if it the interpreter is ready,
+ * TCL_ERROR otherwise.
+ *
+ * Side effects:
+ * The interpreters object and string results are cleared.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclInterpReady(interp)
+ Tcl_Interp *interp;
+{
+ register Interp *iPtr = (Interp *) interp;
/*
- * Reset both the interpreter's string and object results and clear out
- * any error information. This makes sure that we return an empty
- * result if there are no commands in the command string.
+ * Reset both the interpreter's string and object results and clear
+ * out any previous error information.
*/
Tcl_ResetResult(interp);
/*
+ * If the interpreter has been deleted, return an error.
+ */
+
+ if (iPtr->flags & DELETED) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendToObj(Tcl_GetObjResult(interp),
+ "attempt to call eval in deleted interpreter", -1);
+ Tcl_SetErrorCode(interp, "CORE", "IDELETE",
+ "attempt to call eval in deleted interpreter",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
* Check depth of nested calls to Tcl_Eval: if this gets too large,
* it's probably because of an infinite loop somewhere.
*/
- iPtr->numLevels++;
- if (iPtr->numLevels > iPtr->maxNestingDepth) {
+ if (((iPtr->numLevels) >= iPtr->maxNestingDepth)
+ || (TclpCheckStackSpace() == 0)) {
Tcl_AppendToObj(Tcl_GetObjResult(interp),
- "too many nested calls to Tcl_EvalObj (infinite loop?)", -1);
- result = TCL_ERROR;
- goto done;
+ "too many nested evaluations (infinite loop?)", -1);
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclEvalObjvInternal --
+ *
+ * This procedure evaluates a Tcl command that has already been
+ * parsed into words, with one Tcl_Obj holding each word. The caller
+ * is responsible for checking that the interpreter is ready to
+ * evaluate (by calling TclInterpReady), and also to manage the
+ * iPtr->numLevels.
+ *
+ * Results:
+ * The return value is a standard Tcl completion code such as
+ * TCL_OK or TCL_ERROR. A result or error message is left in
+ * interp's result. If an error occurs, this procedure does
+ * NOT add any information to the errorInfo variable.
+ *
+ * Side effects:
+ * Depends on the command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclEvalObjvInternal(interp, objc, objv, command, length, flags)
+ Tcl_Interp *interp; /* Interpreter in which to evaluate the
+ * command. Also used for error
+ * reporting. */
+ int objc; /* Number of words in command. */
+ Tcl_Obj *CONST objv[]; /* An array of pointers to objects that are
+ * the words that make up the command. */
+ CONST char *command; /* Points to the beginning of the string
+ * representation of the command; this
+ * is used for traces. If the string
+ * representation of the command is
+ * unknown, an empty string should be
+ * supplied. If it is NULL, no traces will
+ * be called. */
+ int length; /* Number of bytes in command; if -1, all
+ * characters up to the first null byte are
+ * used. */
+ int flags; /* Collection of OR-ed bits that control
+ * the evaluation of the script. Only
+ * TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE are
+ * currently supported. */
+
+{
+ Command *cmdPtr;
+ Interp *iPtr = (Interp *) interp;
+ Tcl_Obj **newObjv;
+ int i;
+ CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr
+ * in case TCL_EVAL_GLOBAL was set. */
+ int code = TCL_OK;
+ int traceCode = TCL_OK;
+ int checkTraces = 1;
+
+ if (objc == 0) {
+ return TCL_OK;
}
/*
- * On the Mac, we will never reach the default recursion limit before
- * blowing the stack. So we need to do a check here.
+ * If any execution traces rename or delete the current command,
+ * we may need (at most) two passes here.
*/
+ while (1) {
- if (TclpCheckStackSpace() == 0) {
- /*NOTREACHED*/
- Tcl_AppendToObj(Tcl_GetObjResult(interp),
- "too many nested calls to Tcl_EvalObj (infinite loop?)", -1);
- result = TCL_ERROR;
- goto done;
+ /*
+ * Find the procedure to execute this command. If there isn't one,
+ * then see if there is a command "unknown". If so, create a new
+ * word array with "unknown" as the first word and the original
+ * command words as arguments. Then call ourselves recursively
+ * to execute it.
+ */
+
+ savedVarFramePtr = iPtr->varFramePtr;
+ if (flags & TCL_EVAL_INVOKE) {
+ iPtr->varFramePtr = NULL;
+ }
+ cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]);
+ iPtr->varFramePtr = savedVarFramePtr;
+
+ if (cmdPtr == NULL) {
+ newObjv = (Tcl_Obj **) ckalloc((unsigned)
+ ((objc + 1) * sizeof (Tcl_Obj *)));
+ for (i = objc-1; i >= 0; i--) {
+ newObjv[i+1] = objv[i];
+ }
+ newObjv[0] = Tcl_NewStringObj("::unknown", -1);
+ Tcl_IncrRefCount(newObjv[0]);
+ cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, newObjv[0]);
+ if (cmdPtr == NULL) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "invalid command name \"", Tcl_GetString(objv[0]), "\"",
+ (char *) NULL);
+ code = TCL_ERROR;
+ } else if (TclInterpReady(interp) == TCL_ERROR) {
+ code = TCL_ERROR;
+ } else {
+ iPtr->numLevels++;
+ code = TclEvalObjvInternal(interp, objc+1, newObjv, command, length, 0);
+ iPtr->numLevels--;
+ }
+ Tcl_DecrRefCount(newObjv[0]);
+ ckfree((char *) newObjv);
+ goto done;
+ }
+
+ /*
+ * Call trace procedures if needed.
+ */
+ if ((checkTraces) && (command != NULL)) {
+ int cmdEpoch = cmdPtr->cmdEpoch;
+ cmdPtr->refCount++;
+ /* If the first set of traces modifies/deletes the command or
+ * any existing traces, then the set checkTraces to 0 and
+ * go through this while loop one more time.
+ */
+ if (iPtr->tracePtr != NULL && traceCode == TCL_OK) {
+ traceCode = TclCheckInterpTraces(interp, command, length,
+ cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv);
+ }
+ if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES)
+ && (traceCode == TCL_OK)) {
+ traceCode = TclCheckExecutionTraces(interp, command, length,
+ cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv);
+ }
+ cmdPtr->refCount--;
+ if (cmdEpoch != cmdPtr->cmdEpoch) {
+ /* The command has been modified in some way */
+ checkTraces = 0;
+ continue;
+ }
+ }
+ break;
}
/*
- * If the interpreter has been deleted, return an error.
+ * Finally, invoke the command's Tcl_ObjCmdProc.
*/
-
- if (iPtr->flags & DELETED) {
- Tcl_ResetResult(interp);
- Tcl_AppendToObj(Tcl_GetObjResult(interp),
- "attempt to call eval in deleted interpreter", -1);
- Tcl_SetErrorCode(interp, "CORE", "IDELETE",
- "attempt to call eval in deleted interpreter",
- (char *) NULL);
- result = TCL_ERROR;
- goto done;
+ cmdPtr->refCount++;
+ iPtr->cmdCount++;
+ if ( code == TCL_OK && traceCode == TCL_OK) {
+ savedVarFramePtr = iPtr->varFramePtr;
+ if (flags & TCL_EVAL_GLOBAL) {
+ iPtr->varFramePtr = NULL;
+ }
+ code = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, objc, objv);
+ iPtr->varFramePtr = savedVarFramePtr;
+ }
+ if (Tcl_AsyncReady()) {
+ code = Tcl_AsyncInvoke(interp, code);
}
/*
- * Get the ByteCode from the object. If it exists, make sure it hasn't
- * been invalidated by, e.g., someone redefining a command with a
- * compile procedure (this might make the compiled code wrong). If
- * necessary, convert the object to be a ByteCode object and compile it.
- * Also, if the code was compiled in/for a different interpreter,
- * or for a different namespace, or for the same namespace but
- * with different name resolution rules, we recompile it.
- *
- * Precompiled objects, however, are immutable and therefore
- * they are not recompiled, even if the epoch has changed.
- *
- * To be pedantically correct, we should also check that the
- * originating procPtr is the same as the current context procPtr
- * (assuming one exists at all - none for global level). This
- * code is #def'ed out because [info body] was changed to never
- * return a bytecode type object, which should obviate us from
- * the extra checks here.
+ * Call 'leave' command traces
*/
+ if (!(cmdPtr->flags & CMD_IS_DELETED)) {
+ if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES) && (traceCode == TCL_OK)) {
+ traceCode = TclCheckExecutionTraces (interp, command, length,
+ cmdPtr, code, TCL_TRACE_LEAVE_EXEC, objc, objv);
+ }
+ if (iPtr->tracePtr != NULL && traceCode == TCL_OK) {
+ traceCode = TclCheckInterpTraces(interp, command, length,
+ cmdPtr, code, TCL_TRACE_LEAVE_EXEC, objc, objv);
+ }
+ }
+ TclCleanupCommand(cmdPtr);
- if (iPtr->varFramePtr != NULL) {
- namespacePtr = iPtr->varFramePtr->nsPtr;
- } else {
- namespacePtr = iPtr->globalNsPtr;
+ /*
+ * If one of the trace invocation resulted in error, then
+ * change the result code accordingly. Note, that the
+ * interp->result should already be set correctly by the
+ * call to TraceExecutionProc.
+ */
+
+ if (traceCode != TCL_OK) {
+ code = traceCode;
+ }
+
+ /*
+ * If the interpreter has a non-empty string result, the result
+ * object is either empty or stale because some procedure set
+ * interp->result directly. If so, move the string result to the
+ * result object, then reset the string result.
+ */
+
+ if (*(iPtr->result) != 0) {
+ (void) Tcl_GetObjResult(interp);
}
- if (objPtr->typePtr == &tclByteCodeType) {
- codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
-
- if (((Interp *) *codePtr->interpHandle != iPtr)
- || (codePtr->compileEpoch != iPtr->compileEpoch)
-#ifdef CHECK_PROC_ORIGINATION /* [Bug: 3412 Pedantic] */
- || (codePtr->procPtr != NULL && !(iPtr->varFramePtr &&
- iPtr->varFramePtr->procPtr == codePtr->procPtr))
-#endif
- || (codePtr->nsPtr != namespacePtr)
- || (codePtr->nsEpoch != namespacePtr->resolverEpoch)) {
- if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) {
- if ((Interp *) *codePtr->interpHandle != iPtr) {
- panic("Tcl_EvalObj: compiled script jumped interps");
- }
- codePtr->compileEpoch = iPtr->compileEpoch;
- } else {
- tclByteCodeType.freeIntRepProc(objPtr);
- }
+ done:
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_EvalObjv --
+ *
+ * This procedure evaluates a Tcl command that has already been
+ * parsed into words, with one Tcl_Obj holding each word.
+ *
+ * Results:
+ * The return value is a standard Tcl completion code such as
+ * TCL_OK or TCL_ERROR. A result or error message is left in
+ * interp's result.
+ *
+ * Side effects:
+ * Depends on the command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_EvalObjv(interp, objc, objv, flags)
+ Tcl_Interp *interp; /* Interpreter in which to evaluate the
+ * command. Also used for error
+ * reporting. */
+ int objc; /* Number of words in command. */
+ Tcl_Obj *CONST objv[]; /* An array of pointers to objects that are
+ * the words that make up the command. */
+ int flags; /* Collection of OR-ed bits that control
+ * the evaluation of the script. Only
+ * TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE
+ * are currently supported. */
+{
+ Interp *iPtr = (Interp *)interp;
+ Trace *tracePtr;
+ Tcl_DString cmdBuf;
+ char *cmdString = ""; /* A command string is only necessary for
+ * command traces or error logs; it will be
+ * generated to replace this default value if
+ * necessary. */
+ int cmdLen = 0; /* a non-zero value indicates that a command
+ * string was generated. */
+ int code = TCL_OK;
+ int i;
+ int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS);
+
+ for (tracePtr = iPtr->tracePtr; tracePtr; tracePtr = tracePtr->nextPtr) {
+ if ((tracePtr->level == 0) || (iPtr->numLevels <= tracePtr->level)) {
+ /*
+ * The command may be needed for an execution trace. Generate a
+ * command string.
+ */
+
+ Tcl_DStringInit(&cmdBuf);
+ for (i = 0; i < objc; i++) {
+ Tcl_DStringAppendElement(&cmdBuf, Tcl_GetString(objv[i]));
+ }
+ cmdString = Tcl_DStringValue(&cmdBuf);
+ cmdLen = Tcl_DStringLength(&cmdBuf);
+ break;
}
}
- if (objPtr->typePtr != &tclByteCodeType) {
- iPtr->errorLine = 1;
- result = tclByteCodeType.setFromAnyProc(interp, objPtr);
- if (result != TCL_OK) {
- goto done;
+
+ code = TclInterpReady(interp);
+ if (code == TCL_OK) {
+ iPtr->numLevels++;
+ code = TclEvalObjvInternal(interp, objc, objv, cmdString, cmdLen,
+ flags);
+ iPtr->numLevels--;
+ }
+
+ /*
+ * If we are again at the top level, process any unusual
+ * return code returned by the evaluated code.
+ */
+
+ if (iPtr->numLevels == 0) {
+ if (code == TCL_RETURN) {
+ code = TclUpdateReturnInfo(iPtr);
}
- } else {
- codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
- if (((Interp *) *codePtr->interpHandle != iPtr)
- || (codePtr->compileEpoch != iPtr->compileEpoch)) {
- (*tclByteCodeType.freeIntRepProc)(objPtr);
- iPtr->errorLine = 1;
- result = (*tclByteCodeType.setFromAnyProc)(interp, objPtr);
- if (result != TCL_OK) {
- iPtr->numLevels--;
- return result;
+ if ((code != TCL_OK) && (code != TCL_ERROR)
+ && !allowExceptions) {
+ ProcessUnexpectedResult(interp, code);
+ code = TCL_ERROR;
+ }
+ }
+
+ if ((code == TCL_ERROR) && !(flags & TCL_EVAL_INVOKE)) {
+
+ /*
+ * If there was an error, a command string will be needed for the
+ * error log: generate it now if it was not done previously.
+ */
+
+ if (cmdLen == 0) {
+ Tcl_DStringInit(&cmdBuf);
+ for (i = 0; i < objc; i++) {
+ Tcl_DStringAppendElement(&cmdBuf, Tcl_GetString(objv[i]));
}
+ cmdString = Tcl_DStringValue(&cmdBuf);
+ cmdLen = Tcl_DStringLength(&cmdBuf);
}
+ Tcl_LogCommandInfo(interp, cmdString, cmdString, cmdLen);
+ }
+
+ if (cmdLen != 0) {
+ Tcl_DStringFree(&cmdBuf);
+ }
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_LogCommandInfo --
+ *
+ * This procedure is invoked after an error occurs in an interpreter.
+ * It adds information to the "errorInfo" variable to describe the
+ * command that was being executed when the error occurred.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information about the command is added to errorInfo and the
+ * line number stored internally in the interpreter is set. If this
+ * is the first call to this procedure or Tcl_AddObjErrorInfo since
+ * an error occurred, then old information in errorInfo is
+ * deleted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_LogCommandInfo(interp, script, command, length)
+ Tcl_Interp *interp; /* Interpreter in which to log information. */
+ CONST char *script; /* First character in script containing
+ * command (must be <= command). */
+ CONST char *command; /* First character in command that
+ * generated the error. */
+ int length; /* Number of bytes in command (-1 means
+ * use all bytes up to first null byte). */
+{
+ char buffer[200];
+ register CONST char *p;
+ char *ellipsis = "";
+ Interp *iPtr = (Interp *) interp;
+
+ if (iPtr->flags & ERR_ALREADY_LOGGED) {
+ /*
+ * Someone else has already logged error information for this
+ * command; we shouldn't add anything more.
+ */
+
+ return;
}
- codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
/*
- * Extract then reset the compilation flags in the interpreter.
- * Resetting the flags must be done after any compilation.
+ * Compute the line number where the error occurred.
*/
- evalFlags = iPtr->evalFlags;
- iPtr->evalFlags = 0;
+ iPtr->errorLine = 1;
+ for (p = script; p != command; p++) {
+ if (*p == '\n') {
+ iPtr->errorLine++;
+ }
+ }
+
+ /*
+ * Create an error message to add to errorInfo, including up to a
+ * maximum number of characters of the command.
+ */
+
+ if (length < 0) {
+ length = strlen(command);
+ }
+ if (length > 150) {
+ length = 150;
+ ellipsis = "...";
+ }
+ if (!(iPtr->flags & ERR_IN_PROGRESS)) {
+ sprintf(buffer, "\n while executing\n\"%.*s%s\"",
+ length, command, ellipsis);
+ } else {
+ sprintf(buffer, "\n invoked from within\n\"%.*s%s\"",
+ length, command, ellipsis);
+ }
+ Tcl_AddObjErrorInfo(interp, buffer, -1);
+ iPtr->flags &= ~ERR_ALREADY_LOGGED;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_EvalTokensStandard --
+ *
+ * Given an array of tokens parsed from a Tcl command (e.g., the
+ * tokens that make up a word or the index for an array variable)
+ * this procedure evaluates the tokens and concatenates their
+ * values to form a single result value.
+ *
+ * Results:
+ * The return value is a standard Tcl completion code such as
+ * TCL_OK or TCL_ERROR. A result or error message is left in
+ * interp's result.
+ *
+ * Side effects:
+ * Depends on the array of tokens being evaled.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_EvalTokensStandard(interp, tokenPtr, count)
+ Tcl_Interp *interp; /* Interpreter in which to lookup
+ * variables, execute nested commands,
+ * and report errors. */
+ Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens
+ * to evaluate and concatenate. */
+ int count; /* Number of tokens to consider at tokenPtr.
+ * Must be at least 1. */
+{
+ Tcl_Obj *resultPtr, *indexPtr, *valuePtr;
+ char buffer[TCL_UTF_MAX];
+#ifdef TCL_MEM_DEBUG
+# define MAX_VAR_CHARS 5
+#else
+# define MAX_VAR_CHARS 30
+#endif
+ char nameBuffer[MAX_VAR_CHARS+1];
+ char *varName, *index;
+ CONST char *p = NULL; /* Initialized to avoid compiler warning. */
+ int length, code;
/*
- * Execute the commands. If the code was compiled from an empty string,
- * don't bother executing the code.
+ * The only tricky thing about this procedure is that it attempts to
+ * avoid object creation and string copying whenever possible. For
+ * example, if the value is just a nested command, then use the
+ * command's result object directly.
*/
- numSrcBytes = codePtr->numSrcBytes;
- if ((numSrcBytes > 0) || (codePtr->flags & TCL_BYTECODE_PRECOMPILED)) {
+ code = TCL_OK;
+ resultPtr = NULL;
+ Tcl_ResetResult(interp);
+ for ( ; count > 0; count--, tokenPtr++) {
+ valuePtr = NULL;
+
/*
- * Increment the code's ref count while it is being executed. If
- * afterwards no references to it remain, free the code.
+ * The switch statement below computes the next value to be
+ * concat to the result, as either a range of text or an
+ * object.
*/
-
- codePtr->refCount++;
- result = TclExecuteByteCode(interp, codePtr);
- codePtr->refCount--;
- if (codePtr->refCount <= 0) {
- TclCleanupByteCode(codePtr);
+
+ switch (tokenPtr->type) {
+ case TCL_TOKEN_TEXT:
+ p = tokenPtr->start;
+ length = tokenPtr->size;
+ break;
+
+ case TCL_TOKEN_BS:
+ length = Tcl_UtfBackslash(tokenPtr->start, (int *) NULL,
+ buffer);
+ p = buffer;
+ break;
+
+ case TCL_TOKEN_COMMAND:
+ code = Tcl_EvalEx(interp, tokenPtr->start+1, tokenPtr->size-2,
+ 0);
+ if (code != TCL_OK) {
+ goto done;
+ }
+ valuePtr = Tcl_GetObjResult(interp);
+ break;
+
+ case TCL_TOKEN_VARIABLE:
+ if (tokenPtr->numComponents == 1) {
+ indexPtr = NULL;
+ index = NULL;
+ } else {
+ code = Tcl_EvalTokensStandard(interp, tokenPtr+2,
+ tokenPtr->numComponents - 1);
+ if (code != TCL_OK) {
+ goto done;
+ }
+ indexPtr = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(indexPtr);
+ index = Tcl_GetString(indexPtr);
+ }
+
+ /*
+ * We have to make a copy of the variable name in order
+ * to have a null-terminated string. We can't make a
+ * temporary modification to the script to null-terminate
+ * the name, because a trace callback might potentially
+ * reuse the script and be affected by the null character.
+ */
+
+ if (tokenPtr[1].size <= MAX_VAR_CHARS) {
+ varName = nameBuffer;
+ } else {
+ varName = ckalloc((unsigned) (tokenPtr[1].size + 1));
+ }
+ strncpy(varName, tokenPtr[1].start, (size_t) tokenPtr[1].size);
+ varName[tokenPtr[1].size] = 0;
+ valuePtr = Tcl_GetVar2Ex(interp, varName, index,
+ TCL_LEAVE_ERR_MSG);
+ if (varName != nameBuffer) {
+ ckfree(varName);
+ }
+ if (indexPtr != NULL) {
+ Tcl_DecrRefCount(indexPtr);
+ }
+ if (valuePtr == NULL) {
+ code = TCL_ERROR;
+ goto done;
+ }
+ count -= tokenPtr->numComponents;
+ tokenPtr += tokenPtr->numComponents;
+ break;
+
+ default:
+ panic("unexpected token type in Tcl_EvalTokensStandard");
}
+
+ /*
+ * If valuePtr isn't NULL, the next piece of text comes from that
+ * object; otherwise, take length bytes starting at p.
+ */
+
+ if (resultPtr == NULL) {
+ if (valuePtr != NULL) {
+ resultPtr = valuePtr;
+ } else {
+ resultPtr = Tcl_NewStringObj(p, length);
+ }
+ Tcl_IncrRefCount(resultPtr);
+ } else {
+ if (Tcl_IsShared(resultPtr)) {
+ Tcl_DecrRefCount(resultPtr);
+ resultPtr = Tcl_DuplicateObj(resultPtr);
+ Tcl_IncrRefCount(resultPtr);
+ }
+ if (valuePtr != NULL) {
+ p = Tcl_GetStringFromObj(valuePtr, &length);
+ }
+ Tcl_AppendToObj(resultPtr, p, length);
+ }
+ }
+ if (resultPtr != NULL) {
+ Tcl_SetObjResult(interp, resultPtr);
} else {
- result = TCL_OK;
+ code = TCL_ERROR;
+ }
+
+ done:
+ if (resultPtr != NULL) {
+ Tcl_DecrRefCount(resultPtr);
}
+ return code;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_EvalTokens --
+ *
+ * Given an array of tokens parsed from a Tcl command (e.g., the
+ * tokens that make up a word or the index for an array variable)
+ * this procedure evaluates the tokens and concatenates their
+ * values to form a single result value.
+ *
+ * Results:
+ * The return value is a pointer to a newly allocated Tcl_Obj
+ * containing the value of the array of tokens. The reference
+ * count of the returned object has been incremented. If an error
+ * occurs in evaluating the tokens then a NULL value is returned
+ * and an error message is left in interp's result.
+ *
+ * Side effects:
+ * A new object is allocated to hold the result.
+ *
+ *----------------------------------------------------------------------
+ *
+ * This uses a non-standard return convention; its use is now deprecated.
+ * It is a wrapper for the new function Tcl_EvalTokensStandard, and is not
+ * used in the core any longer. It is only kept for backward compatibility.
+ */
+
+Tcl_Obj *
+Tcl_EvalTokens(interp, tokenPtr, count)
+ Tcl_Interp *interp; /* Interpreter in which to lookup
+ * variables, execute nested commands,
+ * and report errors. */
+ Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens
+ * to evaluate and concatenate. */
+ int count; /* Number of tokens to consider at tokenPtr.
+ * Must be at least 1. */
+{
+ int code;
+ Tcl_Obj *resPtr;
+
+ code = Tcl_EvalTokensStandard(interp, tokenPtr, count);
+ if (code == TCL_OK) {
+ resPtr = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(resPtr);
+ Tcl_ResetResult(interp);
+ return resPtr;
+ } else {
+ return NULL;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_EvalEx --
+ *
+ * This procedure evaluates a Tcl script without using the compiler
+ * or byte-code interpreter. It just parses the script, creates
+ * values for each word of each command, then calls EvalObjv
+ * to execute each command.
+ *
+ * Results:
+ * The return value is a standard Tcl completion code such as
+ * TCL_OK or TCL_ERROR. A result or error message is left in
+ * interp's result.
+ *
+ * Side effects:
+ * Depends on the script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_EvalEx(interp, script, numBytes, flags)
+ Tcl_Interp *interp; /* Interpreter in which to evaluate the
+ * script. Also used for error reporting. */
+ CONST char *script; /* First character of script to evaluate. */
+ int numBytes; /* Number of bytes in script. If < 0, the
+ * script consists of all bytes up to the
+ * first null character. */
+ int flags; /* Collection of OR-ed bits that control
+ * the evaluation of the script. Only
+ * TCL_EVAL_GLOBAL is currently
+ * supported. */
+{
+ Interp *iPtr = (Interp *) interp;
+ CONST char *p, *next;
+ Tcl_Parse parse;
+#define NUM_STATIC_OBJS 20
+ Tcl_Obj *staticObjArray[NUM_STATIC_OBJS], **objv;
+ Tcl_Token *tokenPtr;
+ int i, code, commandLength, bytesLeft, nested;
+ CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr
+ * in case TCL_EVAL_GLOBAL was set. */
+ int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS);
+
+ /* For nested scripts, this variable will be set to point to the first
+ * char after the end of the script - needed only to compare pointers,
+ * nothing will be read nor written there.
+ */
+
+ CONST char *onePast = NULL;
/*
- * If no commands at all were executed, check for asynchronous
- * handlers so that they at least get one change to execute.
- * This is needed to handle event loops written in Tcl with
- * empty bodies.
+ * The variables below keep track of how much state has been
+ * allocated while evaluating the script, so that it can be freed
+ * properly if an error occurs.
*/
- if ((oldCount == iPtr->cmdCount) && Tcl_AsyncReady()) {
- result = Tcl_AsyncInvoke(interp, result);
+ int gotParse = 0, objectsUsed = 0;
+
+ if (numBytes < 0) {
+ numBytes = strlen(script);
+ }
+ Tcl_ResetResult(interp);
+
+ savedVarFramePtr = iPtr->varFramePtr;
+ if (flags & TCL_EVAL_GLOBAL) {
+ iPtr->varFramePtr = NULL;
}
/*
- * Update the interpreter's evaluation level count. If we are again at
- * the top level, process any unusual return code returned by the
- * evaluated code.
+ * Each iteration through the following loop parses the next
+ * command from the script and then executes it.
*/
- if (iPtr->numLevels == 1) {
- if (result == TCL_RETURN) {
- result = TclUpdateReturnInfo(iPtr);
+ objv = staticObjArray;
+ p = script;
+ bytesLeft = numBytes;
+ if (iPtr->evalFlags & TCL_BRACKET_TERM) {
+ nested = 1;
+ onePast = script + numBytes;
+ } else {
+ nested = 0;
+ }
+ iPtr->evalFlags = 0;
+ do {
+ if (Tcl_ParseCommand(interp, p, bytesLeft, nested, &parse)
+ != TCL_OK) {
+ code = TCL_ERROR;
+ goto error;
}
- if ((result != TCL_OK) && (result != TCL_ERROR)
- && ((evalFlags & TCL_ALLOW_EXCEPTIONS) == 0)) {
- ProcessUnexpectedResult(interp, result);
- result = TCL_ERROR;
+ gotParse = 1;
+
+ /*
+ * A nested script can only terminate in ']'. If the script is not
+ * nested, onePast is NULL and the second test is not performed.
+ */
+
+ next = parse.commandStart + parse.commandSize;
+ if ((next == onePast) && (onePast[-1] != ']')) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("missing close-bracket", -1));
+ code = TCL_ERROR;
+ goto error;
}
- }
+ if (parse.numWords > 0) {
+ /*
+ * Generate an array of objects for the words of the command.
+ */
+
+ if (parse.numWords <= NUM_STATIC_OBJS) {
+ objv = staticObjArray;
+ } else {
+ objv = (Tcl_Obj **) ckalloc((unsigned)
+ (parse.numWords * sizeof (Tcl_Obj *)));
+ }
+ for (objectsUsed = 0, tokenPtr = parse.tokenPtr;
+ objectsUsed < parse.numWords;
+ objectsUsed++, tokenPtr += (tokenPtr->numComponents + 1)) {
+ code = Tcl_EvalTokensStandard(interp, tokenPtr+1,
+ tokenPtr->numComponents);
+ if (code == TCL_OK) {
+ objv[objectsUsed] = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(objv[objectsUsed]);
+ } else {
+ goto error;
+ }
+ }
+
+ /*
+ * Execute the command and free the objects for its words.
+ */
+
+ if (TclInterpReady(interp) == TCL_ERROR) {
+ code = TCL_ERROR;
+ } else {
+ iPtr->numLevels++;
+ code = TclEvalObjvInternal(interp, objectsUsed, objv, p,
+ parse.commandStart + parse.commandSize - p, 0);
+ iPtr->numLevels--;
+ }
+ if (code != TCL_OK) {
+ if (iPtr->numLevels == 0) {
+ if (code == TCL_RETURN) {
+ code = TclUpdateReturnInfo(iPtr);
+ }
+ if ((code != TCL_OK) && (code != TCL_ERROR)
+ && !allowExceptions) {
+ ProcessUnexpectedResult(interp, code);
+ code = TCL_ERROR;
+ }
+ }
+ goto error;
+ }
+ for (i = 0; i < objectsUsed; i++) {
+ Tcl_DecrRefCount(objv[i]);
+ }
+ objectsUsed = 0;
+ if (objv != staticObjArray) {
+ ckfree((char *) objv);
+ objv = staticObjArray;
+ }
+ }
+
+ /*
+ * Advance to the next command in the script.
+ */
+
+ bytesLeft -= next - p;
+ p = next;
+ Tcl_FreeParse(&parse);
+ gotParse = 0;
+ if ((nested != 0) && (p > script) && (p[-1] == ']')) {
+ /*
+ * We get here in the special case where the TCL_BRACKET_TERM
+ * flag was set in the interpreter and we reached a close
+ * bracket in the script. Return immediately.
+ */
+
+ iPtr->termOffset = (p - 1) - script;
+ iPtr->varFramePtr = savedVarFramePtr;
+ return TCL_OK;
+ }
+ } while (bytesLeft > 0);
+ iPtr->termOffset = p - script;
+ iPtr->varFramePtr = savedVarFramePtr;
+ return TCL_OK;
+
+ error:
/*
- * If an error occurred, record information about what was being
- * executed when the error occurred.
+ * Generate various pieces of error information, such as the line
+ * number where the error occurred and information to add to the
+ * errorInfo variable. Then free resources that had been allocated
+ * to the command.
*/
- if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
- RecordTracebackInfo(interp, objPtr, numSrcBytes);
+ if ((code == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
+ commandLength = parse.commandSize;
+ if ((parse.commandStart + commandLength) != (script + numBytes)) {
+ /*
+ * The command where the error occurred didn't end at the end
+ * of the script (i.e. it ended at a terminator character such
+ * as ";". Reduce the length by one so that the error message
+ * doesn't include the terminator character.
+ */
+
+ commandLength -= 1;
+ }
+ Tcl_LogCommandInfo(interp, script, parse.commandStart, commandLength);
+ }
+
+ for (i = 0; i < objectsUsed; i++) {
+ Tcl_DecrRefCount(objv[i]);
}
+ if (gotParse) {
+ next = parse.commandStart + parse.commandSize;
+ bytesLeft -= next - p;
+ p = next;
+ Tcl_FreeParse(&parse);
+
+ if ((nested != 0) && (p > script)) {
+ CONST char *nextCmd = NULL; /* pointer to start of next command */
+
+ /*
+ * We get here in the special case where the TCL_BRACKET_TERM
+ * flag was set in the interpreter.
+ *
+ * At this point, we want to find the end of the script
+ * (either end of script or the closing ']').
+ */
+
+ while ((p[-1] != ']') && bytesLeft) {
+ if (Tcl_ParseCommand(NULL, p, bytesLeft, nested, &parse)
+ != TCL_OK) {
+ /*
+ * We were looking for the ']' to close the script.
+ * But if we find a syntax error, it is ok to quit
+ * early since in that case we no longer need to know
+ * where the ']' is (if there was one). We reset the
+ * pointer to the start of the command that after the
+ * one causing the return. -- hobbs
+ */
+
+ p = (nextCmd == NULL) ? parse.commandStart : nextCmd;
+ break;
+ }
+
+ if (nextCmd == NULL) {
+ nextCmd = parse.commandStart;
+ }
+
+ /*
+ * Advance to the next command in the script.
+ */
+
+ next = parse.commandStart + parse.commandSize;
+ bytesLeft -= next - p;
+ p = next;
+ Tcl_FreeParse(&parse);
+ }
+ iPtr->termOffset = (p - 1) - script;
+ } else {
+ iPtr->termOffset = p - script;
+ }
+ }
+ if (objv != staticObjArray) {
+ ckfree((char *) objv);
+ }
+ iPtr->varFramePtr = savedVarFramePtr;
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_Eval --
+ *
+ * Execute a Tcl command in a string. This procedure executes the
+ * script directly, rather than compiling it to bytecodes. Before
+ * the arrival of the bytecode compiler in Tcl 8.0 Tcl_Eval was
+ * the main procedure used for executing Tcl commands, but nowadays
+ * it isn't used much.
+ *
+ * Results:
+ * The return value is one of the return codes defined in tcl.h
+ * (such as TCL_OK), and interp's result contains a value
+ * to supplement the return code. The value of the result
+ * will persist only until the next call to Tcl_Eval or Tcl_EvalObj:
+ * you must copy it or lose it!
+ *
+ * Side effects:
+ * Can be almost arbitrary, depending on the commands in the script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_Eval(interp, string)
+ Tcl_Interp *interp; /* Token for command interpreter (returned
+ * by previous call to Tcl_CreateInterp). */
+ CONST char *string; /* Pointer to TCL command to execute. */
+{
+ int code = Tcl_EvalEx(interp, string, -1, 0);
/*
- * Set the interpreter's termOffset member to the offset of the
- * character just after the last one executed. We approximate the offset
- * of the last character executed by using the number of characters
- * compiled.
+ * For backwards compatibility with old C code that predates the
+ * object system in Tcl 8.0, we have to mirror the object result
+ * back into the string result (some callers may expect it there).
*/
- iPtr->termOffset = numSrcBytes;
- iPtr->flags &= ~ERR_ALREADY_LOGGED;
+ Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)),
+ TCL_VOLATILE);
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_EvalObj, Tcl_GlobalEvalObj --
+ *
+ * These functions are deprecated but we keep them around for backwards
+ * compatibility reasons.
+ *
+ * Results:
+ * See the functions they call.
+ *
+ * Side effects:
+ * See the functions they call.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#undef Tcl_EvalObj
+int
+Tcl_EvalObj(interp, objPtr)
+ Tcl_Interp * interp;
+ Tcl_Obj * objPtr;
+{
+ return Tcl_EvalObjEx(interp, objPtr, 0);
+}
+
+#undef Tcl_GlobalEvalObj
+int
+Tcl_GlobalEvalObj(interp, objPtr)
+ Tcl_Interp * interp;
+ Tcl_Obj * objPtr;
+{
+ return Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_GLOBAL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_EvalObjEx --
+ *
+ * Execute Tcl commands stored in a Tcl object. These commands are
+ * compiled into bytecodes if necessary, unless TCL_EVAL_DIRECT
+ * is specified.
+ *
+ * Results:
+ * The return value is one of the return codes defined in tcl.h
+ * (such as TCL_OK), and the interpreter's result contains a value
+ * to supplement the return code.
+ *
+ * Side effects:
+ * The object is converted, if necessary, to a ByteCode object that
+ * holds the bytecode instructions for the commands. Executing the
+ * commands will almost certainly have side effects that depend
+ * on those commands.
+ *
+ * Just as in Tcl_Eval, interp->termOffset is set to the offset of the
+ * last character executed in the objPtr's string.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_EvalObjEx(interp, objPtr, flags)
+ Tcl_Interp *interp; /* Token for command interpreter
+ * (returned by a previous call to
+ * Tcl_CreateInterp). */
+ register Tcl_Obj *objPtr; /* Pointer to object containing
+ * commands to execute. */
+ int flags; /* Collection of OR-ed bits that
+ * control the evaluation of the
+ * script. Supported values are
+ * TCL_EVAL_GLOBAL and
+ * TCL_EVAL_DIRECT. */
+{
+ register Interp *iPtr = (Interp *) interp;
+ char *script;
+ int numSrcBytes;
+ int result;
+ CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr
+ * in case TCL_EVAL_GLOBAL was set. */
+ int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS);
+
+ Tcl_IncrRefCount(objPtr);
+
+ if ((iPtr->flags & USE_EVAL_DIRECT) || (flags & TCL_EVAL_DIRECT)) {
+ /*
+ * We're not supposed to use the compiler or byte-code interpreter.
+ * Let Tcl_EvalEx evaluate the command directly (and probably
+ * more slowly).
+ *
+ * Pure List Optimization (no string representation). In this
+ * case, we can safely use Tcl_EvalObjv instead and get an
+ * appreciable improvement in execution speed. This is because it
+ * allows us to avoid a setFromAny step that would just pack
+ * everything into a string and back out again.
+ *
+ * USE_EVAL_DIRECT is a special flag used for testing purpose only
+ * (ensure we go into the TCL_EVAL_DIRECT path, avoiding opt)
+ */
+ if (!(iPtr->flags & USE_EVAL_DIRECT) &&
+ (objPtr->typePtr == &tclListType) && /* is a list... */
+ (objPtr->bytes == NULL) /* ...without a string rep */) {
+ register List *listRepPtr =
+ (List *) objPtr->internalRep.twoPtrValue.ptr1;
+ result = Tcl_EvalObjv(interp, listRepPtr->elemCount,
+ listRepPtr->elements, flags);
+ } else {
+ script = Tcl_GetStringFromObj(objPtr, &numSrcBytes);
+ result = Tcl_EvalEx(interp, script, numSrcBytes, flags);
+ }
+ } else {
+ /*
+ * Let the compiler/engine subsystem do the evaluation.
+ */
+
+ savedVarFramePtr = iPtr->varFramePtr;
+ if (flags & TCL_EVAL_GLOBAL) {
+ iPtr->varFramePtr = NULL;
+ }
+
+ result = TclCompEvalObj(interp, objPtr);
+
+ /*
+ * If we are again at the top level, process any unusual
+ * return code returned by the evaluated code.
+ */
+
+ if (iPtr->numLevels == 0) {
+ if (result == TCL_RETURN) {
+ result = TclUpdateReturnInfo(iPtr);
+ }
+ if ((result != TCL_OK) && (result != TCL_ERROR)
+ && !allowExceptions) {
+ ProcessUnexpectedResult(interp, result);
+ result = TCL_ERROR;
+
+ /*
+ * If an error was created here, record information about
+ * what was being executed when the error occurred.
+ */
+
+ if (!(iPtr->flags & ERR_ALREADY_LOGGED)) {
+ script = Tcl_GetStringFromObj(objPtr, &numSrcBytes);
+ Tcl_LogCommandInfo(interp, script, script, numSrcBytes);
+ iPtr->flags &= ~ERR_ALREADY_LOGGED;
+ }
+ }
+ }
+ iPtr->evalFlags = 0;
+ iPtr->varFramePtr = savedVarFramePtr;
+ }
- done:
TclDecrRefCount(objPtr);
- iPtr->varFramePtr = savedVarFramePtr;
- iPtr->numLevels--;
return result;
}
@@ -2835,61 +4019,6 @@ ProcessUnexpectedResult(interp, returnCode)
}
/*
- *----------------------------------------------------------------------
- *
- * RecordTracebackInfo --
- *
- * Procedure called by Tcl_EvalObj to record information about what was
- * being executed when the error occurred.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Appends information about the script being evaluated to the
- * interpreter's "errorInfo" variable.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-RecordTracebackInfo(interp, objPtr, numSrcBytes)
- Tcl_Interp *interp; /* The interpreter in which the error
- * occurred. */
- Tcl_Obj *objPtr; /* Points to object containing script whose
- * evaluation resulted in an error. */
- int numSrcBytes; /* Number of bytes compiled in script. */
-{
- Interp *iPtr = (Interp *) interp;
- char buf[200];
- char *ellipsis, *bytes;
- int length;
-
- /*
- * Decide how much of the command to print in the error message
- * (up to a certain number of bytes).
- */
-
- bytes = Tcl_GetStringFromObj(objPtr, &length);
- length = TclMin(numSrcBytes, length);
-
- ellipsis = "";
- if (length > 150) {
- length = 150;
- ellipsis = " ...";
- }
-
- if (!(iPtr->flags & ERR_IN_PROGRESS)) {
- sprintf(buf, "\n while executing\n\"%.*s%s\"",
- length, bytes, ellipsis);
- } else {
- sprintf(buf, "\n invoked from within\n\"%.*s%s\"",
- length, bytes, ellipsis);
- }
- Tcl_AddObjErrorInfo(interp, buf, -1);
-}
-
-/*
*---------------------------------------------------------------------------
*
* Tcl_ExprLong, Tcl_ExprDouble, Tcl_ExprBoolean --
@@ -2914,7 +4043,7 @@ int
Tcl_ExprLong(interp, string, ptr)
Tcl_Interp *interp; /* Context in which to evaluate the
* expression. */
- char *string; /* Expression to evaluate. */
+ CONST char *string; /* Expression to evaluate. */
long *ptr; /* Where to store result. */
{
register Tcl_Obj *exprPtr;
@@ -2965,7 +4094,7 @@ int
Tcl_ExprDouble(interp, string, ptr)
Tcl_Interp *interp; /* Context in which to evaluate the
* expression. */
- char *string; /* Expression to evaluate. */
+ CONST char *string; /* Expression to evaluate. */
double *ptr; /* Where to store result. */
{
register Tcl_Obj *exprPtr;
@@ -3016,7 +4145,7 @@ int
Tcl_ExprBoolean(interp, string, ptr)
Tcl_Interp *interp; /* Context in which to evaluate the
* expression. */
- char *string; /* Expression to evaluate. */
+ CONST char *string; /* Expression to evaluate. */
int *ptr; /* Where to store 0/1 result. */
{
register Tcl_Obj *exprPtr;
@@ -3185,7 +4314,7 @@ int
TclInvoke(interp, argc, argv, flags)
Tcl_Interp *interp; /* Where to invoke the command. */
int argc; /* Count of args. */
- register char **argv; /* The arg strings; argv[0] is the name of
+ register CONST char **argv; /* The arg strings; argv[0] is the name of
* the command to invoke. */
int flags; /* Combination of flags controlling the
* call: TCL_INVOKE_HIDDEN and
@@ -3282,7 +4411,7 @@ int
TclGlobalInvoke(interp, argc, argv, flags)
Tcl_Interp *interp; /* Where to invoke the command. */
int argc; /* Count of args. */
- register char **argv; /* The arg strings; argv[0] is the name of
+ register CONST char **argv; /* The arg strings; argv[0] is the name of
* the command to invoke. */
int flags; /* Combination of flags controlling the
* call: TCL_INVOKE_HIDDEN and
@@ -3537,7 +4666,7 @@ int
Tcl_ExprString(interp, string)
Tcl_Interp *interp; /* Context in which to evaluate the
* expression. */
- char *string; /* Expression to evaluate. */
+ CONST char *string; /* Expression to evaluate. */
{
register Tcl_Obj *exprPtr;
Tcl_Obj *resultPtr;
@@ -3593,214 +4722,112 @@ Tcl_ExprString(interp, string)
}
/*
- *--------------------------------------------------------------
+ *----------------------------------------------------------------------
*
- * Tcl_ExprObj --
+ * Tcl_CreateObjTrace --
*
- * Evaluate an expression in a Tcl_Obj.
+ * Arrange for a procedure to be called to trace command execution.
*
* Results:
- * A standard Tcl object result. If the result is other than TCL_OK,
- * then the interpreter's result contains an error message. If the
- * result is TCL_OK, then a pointer to the expression's result value
- * object is stored in resultPtrPtr. In that case, the object's ref
- * count is incremented to reflect the reference returned to the
- * caller; the caller is then responsible for the resulting object
- * and must, for example, decrement the ref count when it is finished
- * with the object.
+ * The return value is a token for the trace, which may be passed
+ * to Tcl_DeleteTrace to eliminate the trace.
*
* Side effects:
- * Any side effects caused by subcommands in the expression, if any.
- * The interpreter result is not modified unless there is an error.
+ * From now on, proc will be called just before a command procedure
+ * is called to execute a Tcl command. Calls to proc will have the
+ * following form:
*
- *--------------------------------------------------------------
+ * void proc( ClientData clientData,
+ * Tcl_Interp* interp,
+ * int level,
+ * CONST char* command,
+ * Tcl_Command commandInfo,
+ * int objc,
+ * Tcl_Obj *CONST objv[] );
+ *
+ * The 'clientData' and 'interp' arguments to 'proc' will be the
+ * same as the arguments to Tcl_CreateObjTrace. The 'level'
+ * argument gives the nesting depth of command interpretation within
+ * the interpreter. The 'command' argument is the ASCII text of
+ * the command being evaluated -- before any substitutions are
+ * performed. The 'commandInfo' argument gives a handle to the
+ * command procedure that will be evaluated. The 'objc' and 'objv'
+ * parameters give the parameter vector that will be passed to the
+ * command procedure. proc does not return a value.
+ *
+ * It is permissible for 'proc' to call Tcl_SetCommandTokenInfo
+ * to change the command procedure or client data for the command
+ * being evaluated, and these changes will take effect with the
+ * current evaluation.
+ *
+ * The 'level' argument specifies the maximum nesting level of calls
+ * to be traced. If the execution depth of the interpreter exceeds
+ * 'level', the trace callback is not executed.
+ *
+ * The 'flags' argument is either zero or the value,
+ * TCL_ALLOW_INLINE_COMPILATION. If the TCL_ALLOW_INLINE_COMPILATION
+ * flag is not present, the bytecode compiler will not generate inline
+ * code for Tcl's built-in commands. This behavior will have a significant
+ * impact on performance, but will ensure that all command evaluations are
+ * traced. If the TCL_ALLOW_INLINE_COMPILATION flag is present, the
+ * bytecode compiler will have its normal behavior of compiling in-line
+ * code for some of Tcl's built-in commands. In this case, the tracing
+ * will be imprecise -- in-line code will not be traced -- but run-time
+ * performance will be improved. The latter behavior is desired for
+ * many applications such as profiling of run time.
+ *
+ * When the trace is deleted, the 'delProc' procedure will be invoked,
+ * passing it the original client data.
+ *
+ *----------------------------------------------------------------------
*/
-int
-Tcl_ExprObj(interp, objPtr, resultPtrPtr)
- Tcl_Interp *interp; /* Context in which to evaluate the
- * expression. */
- register Tcl_Obj *objPtr; /* Points to Tcl object containing
- * expression to evaluate. */
- Tcl_Obj **resultPtrPtr; /* Where the Tcl_Obj* that is the expression
- * result is stored if no errors occur. */
+Tcl_Trace
+Tcl_CreateObjTrace( interp, level, flags, proc, clientData, delProc )
+ Tcl_Interp* interp; /* Tcl interpreter */
+ int level; /* Maximum nesting level */
+ int flags; /* Flags, see above */
+ Tcl_CmdObjTraceProc* proc; /* Trace callback */
+ ClientData clientData; /* Client data for the callback */
+ Tcl_CmdObjTraceDeleteProc* delProc;
+ /* Procedure to call when trace is deleted */
{
- Interp *iPtr = (Interp *) interp;
- CompileEnv compEnv; /* Compilation environment structure
- * allocated in frame. */
- LiteralTable *localTablePtr = &(compEnv.localLitTable);
- register ByteCode *codePtr = NULL;
- /* Tcl Internal type of bytecode.
- * Initialized to avoid compiler warning. */
- AuxData *auxDataPtr;
- LiteralEntry *entryPtr;
- Tcl_Obj *saveObjPtr;
- char *string;
- int length, i, result;
-
- /*
- * First handle some common expressions specially.
- */
-
- string = Tcl_GetStringFromObj(objPtr, &length);
- if (length == 1) {
- if (*string == '0') {
- *resultPtrPtr = Tcl_NewLongObj(0);
- Tcl_IncrRefCount(*resultPtrPtr);
- return TCL_OK;
- } else if (*string == '1') {
- *resultPtrPtr = Tcl_NewLongObj(1);
- Tcl_IncrRefCount(*resultPtrPtr);
- return TCL_OK;
- }
- } else if ((length == 2) && (*string == '!')) {
- if (*(string+1) == '0') {
- *resultPtrPtr = Tcl_NewLongObj(1);
- Tcl_IncrRefCount(*resultPtrPtr);
- return TCL_OK;
- } else if (*(string+1) == '1') {
- *resultPtrPtr = Tcl_NewLongObj(0);
- Tcl_IncrRefCount(*resultPtrPtr);
- return TCL_OK;
- }
- }
+ register Trace *tracePtr;
+ register Interp *iPtr = (Interp *) interp;
- /*
- * Get the ByteCode from the object. If it exists, make sure it hasn't
- * been invalidated by, e.g., someone redefining a command with a
- * compile procedure (this might make the compiled code wrong). If
- * necessary, convert the object to be a ByteCode object and compile it.
- * Also, if the code was compiled in/for a different interpreter, we
- * recompile it.
- *
- * Precompiled expressions, however, are immutable and therefore
- * they are not recompiled, even if the epoch has changed.
- *
- */
+ /* Test if this trace allows inline compilation of commands */
- if (objPtr->typePtr == &tclByteCodeType) {
- codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
- if (((Interp *) *codePtr->interpHandle != iPtr)
- || (codePtr->compileEpoch != iPtr->compileEpoch)) {
- if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) {
- if ((Interp *) *codePtr->interpHandle != iPtr) {
- panic("Tcl_ExprObj: compiled expression jumped interps");
- }
- codePtr->compileEpoch = iPtr->compileEpoch;
- } else {
- (*tclByteCodeType.freeIntRepProc)(objPtr);
- objPtr->typePtr = (Tcl_ObjType *) NULL;
- }
- }
- }
- if (objPtr->typePtr != &tclByteCodeType) {
- TclInitCompileEnv(interp, &compEnv, string, length);
- result = TclCompileExpr(interp, string, length, &compEnv);
+ if ( ! ( flags & TCL_ALLOW_INLINE_COMPILATION ) ) {
- /*
- * Free the compilation environment's literal table bucket array if
- * it was dynamically allocated.
- */
+ if ( iPtr->tracesForbiddingInline == 0 ) {
- if (localTablePtr->buckets != localTablePtr->staticBuckets) {
- ckfree((char *) localTablePtr->buckets);
- }
-
- if (result != TCL_OK) {
/*
- * Compilation errors. Free storage allocated for compilation.
+ * When the first trace forbidding inline compilation is
+ * created, invalidate existing compiled code for this
+ * interpreter and arrange (by setting the
+ * DONT_COMPILE_CMDS_INLINE flag) that when compiling new
+ * code, no commands will be compiled inline (i.e., into
+ * an inline sequence of instructions). We do this because
+ * commands that were compiled inline will never result in
+ * a command trace being called.
*/
-#ifdef TCL_COMPILE_DEBUG
- TclVerifyLocalLiteralTable(&compEnv);
-#endif /*TCL_COMPILE_DEBUG*/
- entryPtr = compEnv.literalArrayPtr;
- for (i = 0; i < compEnv.literalArrayNext; i++) {
- TclReleaseLiteral(interp, entryPtr->objPtr);
- entryPtr++;
- }
-#ifdef TCL_COMPILE_DEBUG
- TclVerifyGlobalLiteralTable(iPtr);
-#endif /*TCL_COMPILE_DEBUG*/
-
- auxDataPtr = compEnv.auxDataArrayPtr;
- for (i = 0; i < compEnv.auxDataArrayNext; i++) {
- if (auxDataPtr->type->freeProc != NULL) {
- auxDataPtr->type->freeProc(auxDataPtr->clientData);
- }
- auxDataPtr++;
- }
- TclFreeCompileEnv(&compEnv);
- return result;
- }
-
- /*
- * Successful compilation. If the expression yielded no
- * instructions, push an zero object as the expression's result.
- */
-
- if (compEnv.codeNext == compEnv.codeStart) {
- TclEmitPush(TclRegisterLiteral(&compEnv, "0", 1, /*onHeap*/ 0),
- &compEnv);
- }
-
- /*
- * Add a "done" instruction as the last instruction and change the
- * object into a ByteCode object. Ownership of the literal objects
- * and aux data items is given to the ByteCode object.
- */
-
- compEnv.numSrcBytes = iPtr->termOffset;
- TclEmitOpcode(INST_DONE, &compEnv);
- TclInitByteCodeObj(objPtr, &compEnv);
- TclFreeCompileEnv(&compEnv);
- codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
-#ifdef TCL_COMPILE_DEBUG
- if (tclTraceCompile == 2) {
- TclPrintByteCodeObj(interp, objPtr);
+ iPtr->compileEpoch++;
+ iPtr->flags |= DONT_COMPILE_CMDS_INLINE;
}
-#endif /* TCL_COMPILE_DEBUG */
+ ++ iPtr->tracesForbiddingInline;
}
-
- /*
- * Execute the expression after first saving the interpreter's result.
- */
- saveObjPtr = Tcl_GetObjResult(interp);
- Tcl_IncrRefCount(saveObjPtr);
- Tcl_ResetResult(interp);
+ tracePtr = (Trace *) ckalloc(sizeof(Trace));
+ tracePtr->level = level;
+ tracePtr->proc = proc;
+ tracePtr->clientData = clientData;
+ tracePtr->delProc = delProc;
+ tracePtr->nextPtr = iPtr->tracePtr;
+ tracePtr->flags = flags;
+ iPtr->tracePtr = tracePtr;
- /*
- * Increment the code's ref count while it is being executed. If
- * afterwards no references to it remain, free the code.
- */
-
- codePtr->refCount++;
- result = TclExecuteByteCode(interp, codePtr);
- codePtr->refCount--;
- if (codePtr->refCount <= 0) {
- TclCleanupByteCode(codePtr);
- objPtr->typePtr = NULL;
- objPtr->internalRep.otherValuePtr = NULL;
- }
-
- /*
- * If the expression evaluated successfully, store a pointer to its
- * value object in resultPtrPtr then restore the old interpreter result.
- * We increment the object's ref count to reflect the reference that we
- * are returning to the caller. We also decrement the ref count of the
- * interpreter's result object after calling Tcl_SetResult since we
- * next store into that field directly.
- */
-
- if (result == TCL_OK) {
- *resultPtrPtr = iPtr->objResultPtr;
- Tcl_IncrRefCount(iPtr->objResultPtr);
-
- Tcl_SetObjResult(interp, saveObjPtr);
- }
- Tcl_DecrRefCount(saveObjPtr);
- return result;
+ return (Tcl_Trace) tracePtr;
}
/*
@@ -3855,28 +4882,95 @@ Tcl_CreateTrace(interp, level, proc, clientData)
* command. */
ClientData clientData; /* Arbitrary value word to pass to proc. */
{
- register Trace *tracePtr;
- register Interp *iPtr = (Interp *) interp;
+ StringTraceData* data;
+ data = (StringTraceData*) ckalloc( sizeof( *data ));
+ data->clientData = clientData;
+ data->proc = proc;
+ return Tcl_CreateObjTrace( interp, level, 0, StringTraceProc,
+ (ClientData) data, StringTraceDeleteProc );
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringTraceProc --
+ *
+ * Invoke a string-based trace procedure from an object-based
+ * callback.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Whatever the string-based trace procedure does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+StringTraceProc( clientData, interp, level, command, commandInfo, objc, objv )
+ ClientData clientData;
+ Tcl_Interp* interp;
+ int level;
+ CONST char* command;
+ Tcl_Command commandInfo;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ StringTraceData* data = (StringTraceData*) clientData;
+ Command* cmdPtr = (Command*) commandInfo;
+
+ CONST char** argv; /* Args to pass to string trace proc */
+
+ int i;
/*
- * Invalidate existing compiled code for this interpreter and arrange
- * (by setting the DONT_COMPILE_CMDS_INLINE flag) that when compiling
- * new code, no commands will be compiled inline (i.e., into an inline
- * sequence of instructions). We do this because commands that were
- * compiled inline will never result in a command trace being called.
+ * This is a bit messy because we have to emulate the old trace
+ * interface, which uses strings for everything.
*/
+
+ argv = (CONST char **) ckalloc((unsigned) ( (objc + 1)
+ * sizeof(CONST char *) ));
+ for (i = 0; i < objc; i++) {
+ argv[i] = Tcl_GetString(objv[i]);
+ }
+ argv[objc] = 0;
- iPtr->compileEpoch++;
- iPtr->flags |= DONT_COMPILE_CMDS_INLINE;
+ /*
+ * Invoke the command procedure. Note that we cast away const-ness
+ * on two parameters for compatibility with legacy code; the code
+ * MUST NOT modify either command or argv.
+ */
+
+ ( data->proc )( data->clientData, interp, level,
+ (char*) command, cmdPtr->proc, cmdPtr->clientData,
+ objc, argv );
+ ckfree( (char*) argv );
- tracePtr = (Trace *) ckalloc(sizeof(Trace));
- tracePtr->level = level;
- tracePtr->proc = proc;
- tracePtr->clientData = clientData;
- tracePtr->nextPtr = iPtr->tracePtr;
- iPtr->tracePtr = tracePtr;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringTraceDeleteProc --
+ *
+ * Clean up memory when a string-based trace is deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Allocated memory is returned to the system.
+ *
+ *----------------------------------------------------------------------
+ */
- return (Tcl_Trace) tracePtr;
+static void
+StringTraceDeleteProc( clientData )
+ ClientData clientData;
+{
+ ckfree( (char*) clientData );
}
/*
@@ -3902,31 +4996,49 @@ Tcl_DeleteTrace(interp, trace)
Tcl_Trace trace; /* Token for trace (returned previously by
* Tcl_CreateTrace). */
{
- register Interp *iPtr = (Interp *) interp;
- register Trace *tracePtr = (Trace *) trace;
- register Trace *tracePtr2;
+ Interp *iPtr = (Interp *) interp;
+ Trace *tracePtr = (Trace *) trace;
+ register Trace **tracePtr2 = &( iPtr->tracePtr );
- if (iPtr->tracePtr == tracePtr) {
- iPtr->tracePtr = tracePtr->nextPtr;
- ckfree((char *) tracePtr);
- } else {
- for (tracePtr2 = iPtr->tracePtr; tracePtr2 != NULL;
- tracePtr2 = tracePtr2->nextPtr) {
- if (tracePtr2->nextPtr == tracePtr) {
- tracePtr2->nextPtr = tracePtr->nextPtr;
- ckfree((char *) tracePtr);
- break;
- }
+ /*
+ * Locate the trace entry in the interpreter's trace list,
+ * and remove it from the list.
+ */
+
+ while ( (*tracePtr2) != NULL && (*tracePtr2) != tracePtr ) {
+ tracePtr2 = &((*tracePtr2)->nextPtr);
+ }
+ if ( *tracePtr2 == NULL ) {
+ return;
+ }
+ (*tracePtr2) = (*tracePtr2)->nextPtr;
+
+ /*
+ * If the trace forbids bytecode compilation, change the interpreter's
+ * state. If bytecode compilation is now permitted, flag the fact and
+ * advance the compilation epoch so that procs will be recompiled to
+ * take advantage of it.
+ */
+
+ if ( ! (tracePtr->flags & TCL_ALLOW_INLINE_COMPILATION ) ) {
+ -- iPtr->tracesForbiddingInline;
+ if ( iPtr->tracesForbiddingInline == 0 ) {
+ iPtr->flags &= ~DONT_COMPILE_CMDS_INLINE;
+ ++ iPtr->compileEpoch;
}
}
- if (iPtr->tracePtr == NULL) {
- /*
- * When compiling new code, allow commands to be compiled inline.
- */
+ /*
+ * Execute any delete callback.
+ */
- iPtr->flags &= ~DONT_COMPILE_CMDS_INLINE;
+ if ( tracePtr->delProc != NULL ) {
+ ( tracePtr->delProc )( tracePtr->clientData );
}
+
+ /* Delete the trace object */
+
+ Tcl_EventuallyFree( (char*) tracePtr, TCL_DYNAMIC);
}
/*
@@ -4004,11 +5116,11 @@ Tcl_AddObjErrorInfo(interp, message, length)
iPtr->flags |= ERR_IN_PROGRESS;
if (iPtr->result[0] == 0) {
- (void) Tcl_SetVar2Ex(interp, "errorInfo", NULL, iPtr->objResultPtr,
- TCL_GLOBAL_ONLY);
+ Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorInfo, NULL,
+ iPtr->objResultPtr, TCL_GLOBAL_ONLY);
} else { /* use the string result */
- Tcl_SetVar2(interp, "errorInfo", (char *) NULL, interp->result,
- TCL_GLOBAL_ONLY);
+ Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorInfo, NULL,
+ Tcl_NewStringObj(interp->result, -1), TCL_GLOBAL_ONLY);
}
/*
@@ -4017,8 +5129,8 @@ Tcl_AddObjErrorInfo(interp, message, length)
*/
if (!(iPtr->flags & ERROR_CODE_SET)) {
- (void) Tcl_SetVar2(interp, "errorCode", (char *) NULL, "NONE",
- TCL_GLOBAL_ONLY);
+ Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorCode, NULL,
+ Tcl_NewStringObj("NONE", -1), TCL_GLOBAL_ONLY);
}
}
@@ -4029,8 +5141,8 @@ Tcl_AddObjErrorInfo(interp, message, length)
if (length != 0) {
messagePtr = Tcl_NewStringObj(message, length);
Tcl_IncrRefCount(messagePtr);
- Tcl_SetVar2Ex(interp, "errorInfo", NULL, messagePtr,
- (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE));
+ Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorInfo, NULL,
+ messagePtr, (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE));
Tcl_DecrRefCount(messagePtr); /* free msg object appended above */
}
}
@@ -4138,7 +5250,7 @@ Tcl_VarEval TCL_VARARGS_DEF(Tcl_Interp *,arg1)
int
Tcl_GlobalEval(interp, command)
Tcl_Interp *interp; /* Interpreter in which to evaluate command. */
- char *command; /* Command to evaluate. */
+ CONST char *command; /* Command to evaluate. */
{
register Interp *iPtr = (Interp *) interp;
int result;
@@ -4232,7 +5344,8 @@ Tcl_AllowExceptions(interp)
*----------------------------------------------------------------------
*/
-void Tcl_GetVersion(majorV, minorV, patchLevelV, type)
+void
+Tcl_GetVersion(majorV, minorV, patchLevelV, type)
int *majorV;
int *minorV;
int *patchLevelV;
@@ -4252,4 +5365,3 @@ void Tcl_GetVersion(majorV, minorV, patchLevelV, type)
}
}
-
diff --git a/tcl/generic/tclBinary.c b/tcl/generic/tclBinary.c
index 199109637a5..6065d018d5a 100644
--- a/tcl/generic/tclBinary.c
+++ b/tcl/generic/tclBinary.c
@@ -13,9 +13,9 @@
* RCS: @(#) $Id$
*/
-#include <math.h>
#include "tclInt.h"
#include "tclPort.h"
+#include <math.h>
/*
* The following constants are used by GetFormatSpec to indicate various
@@ -26,6 +26,26 @@
#define BINARY_NOCOUNT -2 /* No count was specified in format. */
/*
+ * The following defines the maximum number of different (integer)
+ * numbers placed in the object cache by 'binary scan' before it bails
+ * out and switches back to Plan A (creating a new object for each
+ * value.) Theoretically, it would be possible to keep the cache
+ * about for the values that are already in it, but that makes the
+ * code slower in practise when overflow happens, and makes little
+ * odds the rest of the time (as measured on my machine.) It is also
+ * slower (on the sample I tried at least) to grow the cache to hold
+ * all items we might want to put in it; presumably the extra cost of
+ * managing the memory for the enlarged table outweighs the benefit
+ * from allocating fewer objects. This is probably because as the
+ * number of objects increases, the likelihood of reuse of any
+ * particular one drops, and there is very little gain from larger
+ * maximum cache sizes (the value below is chosen to allow caching to
+ * work in full with conversion of bytes.) - DKF
+ */
+
+#define BINARY_SCAN_MAX_CACHE 260
+
+/*
* Prototypes for local procedures defined in this file:
*/
@@ -36,7 +56,8 @@ static int FormatNumber _ANSI_ARGS_((Tcl_Interp *interp, int type,
static void FreeByteArrayInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr));
static int GetFormatSpec _ANSI_ARGS_((char **formatPtr,
char *cmdPtr, int *countPtr));
-static Tcl_Obj * ScanNumber _ANSI_ARGS_((unsigned char *buffer, int type));
+static Tcl_Obj * ScanNumber _ANSI_ARGS_((unsigned char *buffer,
+ int type, Tcl_HashTable **numberCachePtr));
static int SetByteArrayFromAny _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Obj *objPtr));
static void UpdateStringOfByteArray _ANSI_ARGS_((Tcl_Obj *listPtr));
@@ -125,7 +146,7 @@ typedef struct ByteArray {
Tcl_Obj *
Tcl_NewByteArrayObj(bytes, length)
- unsigned char *bytes; /* The array of bytes used to initialize
+ CONST unsigned char *bytes; /* The array of bytes used to initialize
* the new object. */
int length; /* Length of the array of bytes, which must
* be >= 0. */
@@ -137,7 +158,7 @@ Tcl_NewByteArrayObj(bytes, length)
Tcl_Obj *
Tcl_NewByteArrayObj(bytes, length)
- unsigned char *bytes; /* The array of bytes used to initialize
+ CONST unsigned char *bytes; /* The array of bytes used to initialize
* the new object. */
int length; /* Length of the array of bytes, which must
* be >= 0. */
@@ -159,8 +180,8 @@ Tcl_NewByteArrayObj(bytes, length)
* TCL_MEM_DEBUG is defined. It is the same as the Tcl_NewByteArrayObj
* above except that it calls Tcl_DbCkalloc directly with the file name
* and line number from its caller. This simplifies debugging since then
- * the checkmem command will report the correct file name and line number
- * when reporting objects that haven't been freed.
+ * the [memory active] command will report the correct file name and line
+ * number when reporting objects that haven't been freed.
*
* When TCL_MEM_DEBUG is not defined, this procedure just returns the
* result of calling Tcl_NewByteArrayObj.
@@ -180,11 +201,11 @@ Tcl_NewByteArrayObj(bytes, length)
Tcl_Obj *
Tcl_DbNewByteArrayObj(bytes, length, file, line)
- unsigned char *bytes; /* The array of bytes used to initialize
+ CONST unsigned char *bytes; /* The array of bytes used to initialize
* the new object. */
int length; /* Length of the array of bytes, which must
* be >= 0. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -200,11 +221,11 @@ Tcl_DbNewByteArrayObj(bytes, length, file, line)
Tcl_Obj *
Tcl_DbNewByteArrayObj(bytes, length, file, line)
- unsigned char *bytes; /* The array of bytes used to initialize
+ CONST unsigned char *bytes; /* The array of bytes used to initialize
* the new object. */
int length; /* Length of the array of bytes, which must
* be >= 0. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -234,7 +255,7 @@ Tcl_DbNewByteArrayObj(bytes, length, file, line)
void
Tcl_SetByteArrayObj(objPtr, bytes, length)
Tcl_Obj *objPtr; /* Object to initialize as a ByteArray. */
- unsigned char *bytes; /* The array of bytes to use as the new
+ CONST unsigned char *bytes; /* The array of bytes to use as the new
* value. */
int length; /* Length of the array of bytes, which must
* be >= 0. */
@@ -561,7 +582,7 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
* cursor has visited.*/
char *errorString, *errorValue, *str;
int offset, size, length, index;
- static char *options[] = {
+ static CONST char *options[] = {
"format", "scan", NULL
};
enum options {
@@ -644,6 +665,11 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
size = 4;
goto doNumbers;
}
+ case 'w':
+ case 'W': {
+ size = 8;
+ goto doNumbers;
+ }
case 'f': {
size = sizeof(float);
goto doNumbers;
@@ -924,6 +950,8 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
case 'S':
case 'i':
case 'I':
+ case 'w':
+ case 'W':
case 'd':
case 'f': {
int listc, i;
@@ -996,12 +1024,16 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
case BINARY_SCAN: {
int i;
Tcl_Obj *valuePtr, *elementPtr;
+ Tcl_HashTable numberCacheHash;
+ Tcl_HashTable *numberCachePtr;
if (objc < 4) {
Tcl_WrongNumArgs(interp, 2, objv,
"value formatString ?varName varName ...?");
return TCL_ERROR;
}
+ numberCachePtr = &numberCacheHash;
+ Tcl_InitHashTable(numberCachePtr, TCL_ONE_WORD_KEYS);
buffer = Tcl_GetByteArrayFromObj(objv[2], &length);
format = Tcl_GetString(objv[3]);
cursor = buffer;
@@ -1018,6 +1050,9 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
unsigned char *src;
if (arg >= objc) {
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
goto badIndex;
}
if (count == BINARY_ALL) {
@@ -1051,6 +1086,9 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
NULL, valuePtr, TCL_LEAVE_ERR_MSG);
arg++;
if (resultPtr == NULL) {
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
Tcl_DecrRefCount(valuePtr); /* unneeded */
return TCL_ERROR;
}
@@ -1063,6 +1101,9 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
char *dest;
if (arg >= objc) {
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
goto badIndex;
}
if (count == BINARY_ALL) {
@@ -1104,6 +1145,9 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
NULL, valuePtr, TCL_LEAVE_ERR_MSG);
arg++;
if (resultPtr == NULL) {
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
Tcl_DecrRefCount(valuePtr); /* unneeded */
return TCL_ERROR;
}
@@ -1118,6 +1162,9 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
static char hexdigit[] = "0123456789abcdef";
if (arg >= objc) {
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
goto badIndex;
}
if (count == BINARY_ALL) {
@@ -1159,6 +1206,9 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
NULL, valuePtr, TCL_LEAVE_ERR_MSG);
arg++;
if (resultPtr == NULL) {
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
Tcl_DecrRefCount(valuePtr); /* unneeded */
return TCL_ERROR;
}
@@ -1179,6 +1229,11 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
size = 4;
goto scanNumber;
}
+ case 'w':
+ case 'W': {
+ size = 8;
+ goto scanNumber;
+ }
case 'f': {
size = sizeof(float);
goto scanNumber;
@@ -1191,13 +1246,17 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
scanNumber:
if (arg >= objc) {
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
goto badIndex;
}
if (count == BINARY_NOCOUNT) {
if ((length - offset) < size) {
goto done;
}
- valuePtr = ScanNumber(buffer+offset, cmd);
+ valuePtr = ScanNumber(buffer+offset, cmd,
+ &numberCachePtr);
offset += size;
} else {
if (count == BINARY_ALL) {
@@ -1209,7 +1268,8 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
valuePtr = Tcl_NewObj();
src = buffer+offset;
for (i = 0; i < count; i++) {
- elementPtr = ScanNumber(src, cmd);
+ elementPtr = ScanNumber(src, cmd,
+ &numberCachePtr);
src += size;
Tcl_ListObjAppendElement(NULL, valuePtr,
elementPtr);
@@ -1221,6 +1281,9 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
NULL, valuePtr, TCL_LEAVE_ERR_MSG);
arg++;
if (resultPtr == NULL) {
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
Tcl_DecrRefCount(valuePtr); /* unneeded */
return TCL_ERROR;
}
@@ -1251,6 +1314,9 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
}
case '@': {
if (count == BINARY_NOCOUNT) {
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
goto badCount;
}
if ((count == BINARY_ALL) || (count > length)) {
@@ -1261,6 +1327,9 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
break;
}
default: {
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
errorString = str;
goto badfield;
}
@@ -1274,6 +1343,9 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)
done:
Tcl_ResetResult(interp);
Tcl_SetLongObj(Tcl_GetObjResult(interp), arg - 4);
+ if (numberCachePtr != NULL) {
+ Tcl_DeleteHashTable(numberCachePtr);
+ }
break;
}
}
@@ -1393,10 +1465,13 @@ FormatNumber(interp, type, src, cursorPtr)
Tcl_Obj *src; /* Number to format. */
unsigned char **cursorPtr; /* Pointer to index into destination buffer. */
{
- int value;
+ long value;
double dvalue;
+ Tcl_WideInt wvalue;
- if ((type == 'd') || (type == 'f')) {
+ switch (type) {
+ case 'd':
+ case 'f':
/*
* For floating point types, we need to copy the data using
* memcpy to avoid alignment issues.
@@ -1425,8 +1500,39 @@ FormatNumber(interp, type, src, cursorPtr)
memcpy((VOID *) *cursorPtr, (VOID *) &fvalue, sizeof(float));
*cursorPtr += sizeof(float);
}
- } else {
- if (Tcl_GetIntFromObj(interp, src, &value) != TCL_OK) {
+ return TCL_OK;
+
+ /*
+ * Next cases separate from other integer cases because we
+ * need a different API to get a wide.
+ */
+ case 'w':
+ case 'W':
+ if (Tcl_GetWideIntFromObj(interp, src, &wvalue) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (type == 'w') {
+ *(*cursorPtr)++ = (unsigned char) wvalue;
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 8);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 16);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 24);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 32);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 40);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 48);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 56);
+ } else {
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 56);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 48);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 40);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 32);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 24);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 16);
+ *(*cursorPtr)++ = (unsigned char) (wvalue >> 8);
+ *(*cursorPtr)++ = (unsigned char) wvalue;
+ }
+ return TCL_OK;
+ default:
+ if (Tcl_GetLongFromObj(interp, src, &value) != TCL_OK) {
return TCL_ERROR;
}
if (type == 'c') {
@@ -1448,8 +1554,8 @@ FormatNumber(interp, type, src, cursorPtr)
*(*cursorPtr)++ = (unsigned char) (value >> 8);
*(*cursorPtr)++ = (unsigned char) value;
}
+ return TCL_OK;
}
- return TCL_OK;
}
/*
@@ -1465,17 +1571,24 @@ FormatNumber(interp, type, src, cursorPtr)
* This object has a ref count of zero.
*
* Side effects:
- * None.
+ * Might reuse an object in the number cache, place a new object
+ * in the cache, or delete the cache and set the reference to
+ * it (itself passed in by reference) to NULL.
*
*----------------------------------------------------------------------
*/
static Tcl_Obj *
-ScanNumber(buffer, type)
+ScanNumber(buffer, type, numberCachePtrPtr)
unsigned char *buffer; /* Buffer to scan number from. */
int type; /* Format character from "binary scan" */
+ Tcl_HashTable **numberCachePtrPtr;
+ /* Place to look for cache of scanned
+ * value objects, or NULL if too many
+ * different numbers have been scanned. */
{
long value;
+ Tcl_WideInt wvalue;
/*
* We cannot rely on the compiler to properly sign extend integer values
@@ -1486,7 +1599,7 @@ ScanNumber(buffer, type)
*/
switch (type) {
- case 'c': {
+ case 'c':
/*
* Characters need special handling. We want to produce a
* signed result, but on some platforms (such as AIX) chars
@@ -1498,28 +1611,26 @@ ScanNumber(buffer, type)
if (value & 0x80) {
value |= -0x100;
}
- return Tcl_NewLongObj((long)value);
- }
- case 's': {
+ goto returnNumericObject;
+
+ case 's':
value = (long) (buffer[0] + (buffer[1] << 8));
goto shortValue;
- }
- case 'S': {
+ case 'S':
value = (long) (buffer[1] + (buffer[0] << 8));
shortValue:
if (value & 0x8000) {
value |= -0x10000;
}
- return Tcl_NewLongObj(value);
- }
- case 'i': {
+ goto returnNumericObject;
+
+ case 'i':
value = (long) (buffer[0]
+ (buffer[1] << 8)
+ (buffer[2] << 16)
+ (buffer[3] << 24));
goto intValue;
- }
- case 'I': {
+ case 'I':
value = (long) (buffer[3]
+ (buffer[2] << 8)
+ (buffer[1] << 16)
@@ -1534,8 +1645,58 @@ ScanNumber(buffer, type)
value -= (((unsigned int)1)<<31);
value -= (((unsigned int)1)<<31);
}
- return Tcl_NewLongObj(value);
- }
+ returnNumericObject:
+ if (*numberCachePtrPtr == NULL) {
+ return Tcl_NewLongObj(value);
+ } else {
+ register Tcl_HashTable *tablePtr = *numberCachePtrPtr;
+ register Tcl_HashEntry *hPtr;
+ int isNew;
+
+ hPtr = Tcl_CreateHashEntry(tablePtr, (char *)value, &isNew);
+ if (!isNew) {
+ return (Tcl_Obj *) Tcl_GetHashValue(hPtr);
+ }
+ if (tablePtr->numEntries > BINARY_SCAN_MAX_CACHE) {
+ /*
+ * We've overflowed the cache! Someone's parsing
+ * a LOT of varied binary data in a single call!
+ * Bail out by switching back to the old behaviour
+ * for the rest of the scan.
+ *
+ * Note that anyone just using the 'c' conversion
+ * (for bytes) cannot trigger this.
+ */
+ Tcl_DeleteHashTable(tablePtr);
+ *numberCachePtrPtr = NULL;
+ return Tcl_NewLongObj(value);
+ } else {
+ register Tcl_Obj *objPtr = Tcl_NewLongObj(value);
+ /* Don't need to fiddle with refcount... */
+ Tcl_SetHashValue(hPtr, (ClientData) objPtr);
+ return objPtr;
+ }
+ }
+ case 'w':
+ value = (long) (buffer[4]
+ | (buffer[5] << 8)
+ | (buffer[6] << 16)
+ | (buffer[7] << 24));
+ wvalue = ((Tcl_WideInt) value) << 32 | (buffer[0]
+ | (buffer[1] << 8)
+ | (buffer[2] << 16)
+ | (buffer[3] << 24));
+ return Tcl_NewWideIntObj(wvalue);
+ case 'W':
+ value = (long) (buffer[3]
+ | (buffer[2] << 8)
+ | (buffer[1] << 16)
+ | (buffer[0] << 24));
+ wvalue = ((Tcl_WideInt) value) << 32 | (buffer[7]
+ | (buffer[6] << 8)
+ | (buffer[5] << 16)
+ | (buffer[4] << 24));
+ return Tcl_NewWideIntObj(wvalue);
case 'f': {
float fvalue;
memcpy((VOID *) &fvalue, (VOID *) buffer, sizeof(float));
diff --git a/tcl/generic/tclCkalloc.c b/tcl/generic/tclCkalloc.c
index 1eb906d2af8..ff0917972ca 100644
--- a/tcl/generic/tclCkalloc.c
+++ b/tcl/generic/tclCkalloc.c
@@ -54,7 +54,7 @@ struct mem_header {
struct mem_header *blink;
MemTag *tagPtr; /* Tag from "memory tag" command; may be
* NULL. */
- char *file;
+ CONST char *file;
long length;
int line;
unsigned char low_guard[LOW_GUARD_SIZE];
@@ -111,6 +111,7 @@ static int init_malloced_bodies = TRUE;
char *tclMemDumpFileName = NULL;
+static char *onExitMemDumpFileName = NULL;
static char dumpFile[100]; /* Records where to dump memory allocation
* information. */
@@ -127,11 +128,11 @@ static int ckallocInit = 0;
*/
static int CheckmemCmd _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char *argv[]));
+ Tcl_Interp *interp, int argc, CONST char *argv[]));
static int MemoryCmd _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static void ValidateMemory _ANSI_ARGS_((
- struct mem_header *memHeaderP, char *file,
+ struct mem_header *memHeaderP, CONST char *file,
int line, int nukeGuards));
/*
@@ -200,7 +201,7 @@ TclDumpMemoryInfo(outFile)
static void
ValidateMemory(memHeaderP, file, line, nukeGuards)
struct mem_header *memHeaderP; /* Memory chunk to validate */
- char *file; /* File containing the call to
+ CONST char *file; /* File containing the call to
* Tcl_ValidateAllMemory */
int line; /* Line number of call to
* Tcl_ValidateAllMemory */
@@ -280,8 +281,8 @@ ValidateMemory(memHeaderP, file, line, nukeGuards)
*/
void
Tcl_ValidateAllMemory (file, line)
- char *file; /* File from which Tcl_ValidateAllMemory was called */
- int line; /* Line number of call to Tcl_ValidateAllMemory */
+ CONST char *file; /* File from which Tcl_ValidateAllMemory was called */
+ int line; /* Line number of call to Tcl_ValidateAllMemory */
{
struct mem_header *memScanP;
@@ -304,13 +305,13 @@ Tcl_ValidateAllMemory (file, line)
* information will be written to stderr.
*
* Results:
- * Return TCL_ERROR if an error accessing the file occures, `errno'
+ * Return TCL_ERROR if an error accessing the file occurs, `errno'
* will have the file error number left in it.
*----------------------------------------------------------------------
*/
int
Tcl_DumpActiveMemory (fileName)
- char *fileName; /* Name of the file to write info to */
+ CONST char *fileName; /* Name of the file to write info to */
{
FILE *fileP;
struct mem_header *memScanP;
@@ -364,7 +365,7 @@ Tcl_DumpActiveMemory (fileName)
char *
Tcl_DbCkalloc(size, file, line)
unsigned int size;
- char *file;
+ CONST char *file;
int line;
{
struct mem_header *result;
@@ -377,7 +378,7 @@ Tcl_DbCkalloc(size, file, line)
if (result == NULL) {
fflush(stdout);
TclDumpMemoryInfo(stderr);
- panic("unable to alloc %d bytes, %s line %d", size, file, line);
+ panic("unable to alloc %ud bytes, %s line %d", size, file, line);
}
/*
@@ -421,7 +422,7 @@ Tcl_DbCkalloc(size, file, line)
}
if (alloc_tracing)
- fprintf(stderr,"ckalloc %lx %d %s %d\n",
+ fprintf(stderr,"ckalloc %lx %ud %s %d\n",
(long unsigned int) result->body, size, file, line);
if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
@@ -445,6 +446,92 @@ Tcl_DbCkalloc(size, file, line)
return result->body;
}
+
+char *
+Tcl_AttemptDbCkalloc(size, file, line)
+ unsigned int size;
+ CONST char *file;
+ int line;
+{
+ struct mem_header *result;
+
+ if (validate_memory)
+ Tcl_ValidateAllMemory (file, line);
+
+ result = (struct mem_header *) TclpAlloc((unsigned)size +
+ sizeof(struct mem_header) + HIGH_GUARD_SIZE);
+ if (result == NULL) {
+ fflush(stdout);
+ TclDumpMemoryInfo(stderr);
+ return NULL;
+ }
+
+ /*
+ * Fill in guard zones and size. Also initialize the contents of
+ * the block with bogus bytes to detect uses of initialized data.
+ * Link into allocated list.
+ */
+ if (init_malloced_bodies) {
+ memset ((VOID *) result, GUARD_VALUE,
+ size + sizeof(struct mem_header) + HIGH_GUARD_SIZE);
+ } else {
+ memset ((char *) result->low_guard, GUARD_VALUE, LOW_GUARD_SIZE);
+ memset (result->body + size, GUARD_VALUE, HIGH_GUARD_SIZE);
+ }
+ if (!ckallocInit) {
+ TclInitDbCkalloc();
+ }
+ Tcl_MutexLock(ckallocMutexPtr);
+ result->length = size;
+ result->tagPtr = curTagPtr;
+ if (curTagPtr != NULL) {
+ curTagPtr->refCount++;
+ }
+ result->file = file;
+ result->line = line;
+ result->flink = allocHead;
+ result->blink = NULL;
+
+ if (allocHead != NULL)
+ allocHead->blink = result;
+ allocHead = result;
+
+ total_mallocs++;
+ if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
+ (void) fflush(stdout);
+ fprintf(stderr, "reached malloc trace enable point (%d)\n",
+ total_mallocs);
+ fflush(stderr);
+ alloc_tracing = TRUE;
+ trace_on_at_malloc = 0;
+ }
+
+ if (alloc_tracing)
+ fprintf(stderr,"ckalloc %lx %ud %s %d\n",
+ (long unsigned int) result->body, size, file, line);
+
+ if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
+ break_on_malloc = 0;
+ (void) fflush(stdout);
+ fprintf(stderr,"reached malloc break limit (%d)\n",
+ total_mallocs);
+ fprintf(stderr, "program will now enter C debugger\n");
+ (void) fflush(stderr);
+ abort();
+ }
+
+ current_malloc_packets++;
+ if (current_malloc_packets > maximum_malloc_packets)
+ maximum_malloc_packets = current_malloc_packets;
+ current_bytes_malloced += size;
+ if (current_bytes_malloced > maximum_bytes_malloced)
+ maximum_bytes_malloced = current_bytes_malloced;
+
+ Tcl_MutexUnlock(ckallocMutexPtr);
+
+ return result->body;
+}
+
/*
*----------------------------------------------------------------------
@@ -467,9 +554,9 @@ Tcl_DbCkalloc(size, file, line)
int
Tcl_DbCkfree(ptr, file, line)
- char *ptr;
- char *file;
- int line;
+ char *ptr;
+ CONST char *file;
+ int line;
{
struct mem_header *memp;
@@ -542,10 +629,10 @@ Tcl_DbCkfree(ptr, file, line)
*/
char *
Tcl_DbCkrealloc(ptr, size, file, line)
- char *ptr;
+ char *ptr;
unsigned int size;
- char *file;
- int line;
+ CONST char *file;
+ int line;
{
char *new;
unsigned int copySize;
@@ -572,6 +659,41 @@ Tcl_DbCkrealloc(ptr, size, file, line)
return new;
}
+char *
+Tcl_AttemptDbCkrealloc(ptr, size, file, line)
+ char *ptr;
+ unsigned int size;
+ CONST char *file;
+ int line;
+{
+ char *new;
+ unsigned int copySize;
+ struct mem_header *memp;
+
+ if (ptr == NULL) {
+ return Tcl_AttemptDbCkalloc(size, file, line);
+ }
+
+ /*
+ * See comment from Tcl_DbCkfree before you change the following
+ * line.
+ */
+
+ memp = (struct mem_header *) (((unsigned long) ptr) - BODY_OFFSET);
+
+ copySize = size;
+ if (copySize > (unsigned int) memp->length) {
+ copySize = memp->length;
+ }
+ new = Tcl_AttemptDbCkalloc(size, file, line);
+ if (new == NULL) {
+ return NULL;
+ }
+ memcpy((VOID *) new, (VOID *) ptr, (size_t) copySize);
+ Tcl_DbCkfree(ptr, file, line);
+ return new;
+}
+
/*
*----------------------------------------------------------------------
@@ -593,6 +715,8 @@ Tcl_DbCkrealloc(ptr, size, file, line)
#undef Tcl_Alloc
#undef Tcl_Free
#undef Tcl_Realloc
+#undef Tcl_AttemptAlloc
+#undef Tcl_AttemptRealloc
char *
Tcl_Alloc(size)
@@ -601,6 +725,13 @@ Tcl_Alloc(size)
return Tcl_DbCkalloc(size, "unknown", 0);
}
+char *
+Tcl_AttemptAlloc(size)
+ unsigned int size;
+{
+ return Tcl_AttemptDbCkalloc(size, "unknown", 0);
+}
+
void
Tcl_Free(ptr)
char *ptr;
@@ -615,6 +746,13 @@ Tcl_Realloc(ptr, size)
{
return Tcl_DbCkrealloc(ptr, size, "unknown", 0);
}
+char *
+Tcl_AttemptRealloc(ptr, size)
+ char *ptr;
+ unsigned int size;
+{
+ return Tcl_AttemptDbCkrealloc(ptr, size, "unknown", 0);
+}
/*
*----------------------------------------------------------------------
@@ -622,11 +760,14 @@ Tcl_Realloc(ptr, size)
* MemoryCmd --
* Implements the Tcl "memory" command, which provides Tcl-level
* control of Tcl memory debugging information.
+ * memory active $file
+ * memory break_on_malloc $count
* memory info
- * memory display
- * memory break_on_malloc count
- * memory trace_on_at_malloc count
+ * memory init on|off
+ * memory onexit $file
+ * memory tag $string
* memory trace on|off
+ * memory trace_on_at_malloc $count
* memory validate on|off
*
* Results:
@@ -640,9 +781,9 @@ MemoryCmd (clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
- char **argv;
+ CONST char **argv;
{
- char *fileName;
+ CONST char *fileName;
Tcl_DString buffer;
int result;
@@ -652,10 +793,10 @@ MemoryCmd (clientData, interp, argc, argv)
return TCL_ERROR;
}
- if (strcmp(argv[1],"active") == 0) {
+ if ((strcmp(argv[1],"active") == 0) || (strcmp(argv[1],"display") == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " active file\"", (char *) NULL);
+ argv[0], " ", argv[1], " file\"", (char *) NULL);
return TCL_ERROR;
}
fileName = Tcl_TranslateFileName(interp, argv[2], &buffer);
@@ -681,14 +822,14 @@ MemoryCmd (clientData, interp, argc, argv)
return TCL_OK;
}
if (strcmp(argv[1],"info") == 0) {
- char buffer[400];
- sprintf(buffer, "%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n",
+ char buf[400];
+ sprintf(buf, "%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n%-25s %10d\n",
"total mallocs", total_mallocs, "total frees", total_frees,
"current packets allocated", current_malloc_packets,
"current bytes allocated", current_bytes_malloced,
"maximum packets allocated", maximum_malloc_packets,
"maximum bytes allocated", maximum_bytes_malloced);
- Tcl_SetResult(interp, buffer, TCL_VOLATILE);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
}
if (strcmp(argv[1],"init") == 0) {
@@ -698,6 +839,21 @@ MemoryCmd (clientData, interp, argc, argv)
init_malloced_bodies = (strcmp(argv[2],"on") == 0);
return TCL_OK;
}
+ if (strcmp(argv[1],"onexit") == 0) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " onexit file\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ fileName = Tcl_TranslateFileName(interp, argv[2], &buffer);
+ if (fileName == NULL) {
+ return TCL_ERROR;
+ }
+ onExitMemDumpFileName = dumpFile;
+ strcpy(onExitMemDumpFileName,fileName);
+ Tcl_DStringFree(&buffer);
+ return TCL_OK;
+ }
if (strcmp(argv[1],"tag") == 0) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
@@ -738,7 +894,7 @@ MemoryCmd (clientData, interp, argc, argv)
}
Tcl_AppendResult(interp, "bad option \"", argv[1],
- "\": should be active, break_on_malloc, info, init, ",
+ "\": should be active, break_on_malloc, info, init, onexit, ",
"tag, trace, trace_on_at_malloc, or validate", (char *) NULL);
return TCL_ERROR;
@@ -777,7 +933,7 @@ CheckmemCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Interpreter for evaluation. */
int argc; /* Number of arguments. */
- char *argv[]; /* String values of arguments. */
+ CONST char *argv[]; /* String values of arguments. */
{
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
@@ -854,7 +1010,7 @@ Tcl_Alloc (size)
* a special pointer on failure, but we only check for NULL
*/
if ((result == NULL) && size) {
- panic("unable to alloc %d bytes", size);
+ panic("unable to alloc %ud bytes", size);
}
return result;
}
@@ -862,7 +1018,7 @@ Tcl_Alloc (size)
char *
Tcl_DbCkalloc(size, file, line)
unsigned int size;
- char *file;
+ CONST char *file;
int line;
{
char *result;
@@ -871,10 +1027,42 @@ Tcl_DbCkalloc(size, file, line)
if ((result == NULL) && size) {
fflush(stdout);
- panic("unable to alloc %d bytes, %s line %d", size, file, line);
+ panic("unable to alloc %ud bytes, %s line %d", size, file, line);
}
return result;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AttemptAlloc --
+ * Interface to TclpAlloc when TCL_MEM_DEBUG is disabled. It does not
+ * check that memory was actually allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+Tcl_AttemptAlloc (size)
+ unsigned int size;
+{
+ char *result;
+
+ result = TclpAlloc(size);
+ return result;
+}
+
+char *
+Tcl_AttemptDbCkalloc(size, file, line)
+ unsigned int size;
+ CONST char *file;
+ int line;
+{
+ char *result;
+
+ result = (char *) TclpAlloc(size);
+ return result;
+}
/*
@@ -897,17 +1085,17 @@ Tcl_Realloc(ptr, size)
result = TclpRealloc(ptr, size);
if ((result == NULL) && size) {
- panic("unable to realloc %d bytes", size);
+ panic("unable to realloc %ud bytes", size);
}
return result;
}
char *
Tcl_DbCkrealloc(ptr, size, file, line)
- char *ptr;
+ char *ptr;
unsigned int size;
- char *file;
- int line;
+ CONST char *file;
+ int line;
{
char *result;
@@ -915,7 +1103,7 @@ Tcl_DbCkrealloc(ptr, size, file, line)
if ((result == NULL) && size) {
fflush(stdout);
- panic("unable to realloc %d bytes, %s line %d", size, file, line);
+ panic("unable to realloc %ud bytes, %s line %d", size, file, line);
}
return result;
}
@@ -923,6 +1111,40 @@ Tcl_DbCkrealloc(ptr, size, file, line)
/*
*----------------------------------------------------------------------
*
+ * Tcl_AttemptRealloc --
+ * Interface to TclpRealloc when TCL_MEM_DEBUG is disabled. It does
+ * not check that memory was actually allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+Tcl_AttemptRealloc(ptr, size)
+ char *ptr;
+ unsigned int size;
+{
+ char *result;
+
+ result = TclpRealloc(ptr, size);
+ return result;
+}
+
+char *
+Tcl_AttemptDbCkrealloc(ptr, size, file, line)
+ char *ptr;
+ unsigned int size;
+ CONST char *file;
+ int line;
+{
+ char *result;
+
+ result = (char *) TclpRealloc(ptr, size);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_Free --
* Interface to TclpFree when TCL_MEM_DEBUG is disabled. Done here
* rather in the macro to keep some modules from being compiled with
@@ -940,9 +1162,9 @@ Tcl_Free (ptr)
int
Tcl_DbCkfree(ptr, file, line)
- char *ptr;
- char *file;
- int line;
+ char *ptr;
+ CONST char *file;
+ int line;
{
TclpFree(ptr);
return 0;
@@ -966,15 +1188,15 @@ Tcl_InitMemory(interp)
int
Tcl_DumpActiveMemory(fileName)
- char *fileName;
+ CONST char *fileName;
{
return TCL_OK;
}
void
Tcl_ValidateAllMemory(file, line)
- char *file;
- int line;
+ CONST char *file;
+ int line;
{
}
@@ -1010,12 +1232,15 @@ void
TclFinalizeMemorySubsystem()
{
#ifdef TCL_MEM_DEBUG
- Tcl_MutexLock(ckallocMutexPtr);
if (tclMemDumpFileName != NULL) {
Tcl_DumpActiveMemory(tclMemDumpFileName);
+ } else if (onExitMemDumpFileName != NULL) {
+ Tcl_DumpActiveMemory(onExitMemDumpFileName);
}
+ Tcl_MutexLock(ckallocMutexPtr);
if (curTagPtr != NULL) {
TclpFree((char *) curTagPtr);
+ curTagPtr = NULL;
}
allocHead = NULL;
Tcl_MutexUnlock(ckallocMutexPtr);
@@ -1025,4 +1250,3 @@ TclFinalizeMemorySubsystem()
TclFinalizeAllocSubsystem();
#endif
}
-
diff --git a/tcl/generic/tclClock.c b/tcl/generic/tclClock.c
index ed79949feaa..f68ca6a26a5 100644
--- a/tcl/generic/tclClock.c
+++ b/tcl/generic/tclClock.c
@@ -67,13 +67,13 @@ Tcl_ClockObjCmd (client, interp, objc, objv)
Tcl_Obj *baseObjPtr = NULL;
char *scanStr;
- static char *switches[] =
+ static CONST char *switches[] =
{"clicks", "format", "scan", "seconds", (char *) NULL};
enum command { COMMAND_CLICKS, COMMAND_FORMAT, COMMAND_SCAN,
COMMAND_SECONDS
};
- static char *formatSwitches[] = {"-format", "-gmt", (char *) NULL};
- static char *scanSwitches[] = {"-base", "-gmt", (char *) NULL};
+ static CONST char *formatSwitches[] = {"-format", "-gmt", (char *) NULL};
+ static CONST char *scanSwitches[] = {"-base", "-gmt", (char *) NULL};
resultPtr = Tcl_GetObjResult(interp);
if (objc < 2) {
@@ -109,7 +109,7 @@ Tcl_ClockObjCmd (client, interp, objc, objv)
* We can enforce at least millisecond granularity
*/
Tcl_Time time;
- TclpGetTime(&time);
+ Tcl_GetTime(&time);
Tcl_SetLongObj(resultPtr,
(long) (time.sec*1000 + time.usec/1000));
} else {
@@ -289,7 +289,7 @@ FormatClock(interp, clockVal, useGMT, format)
return TCL_OK;
}
-#ifndef HAVE_TM_ZONE
+#if !defined(HAVE_TM_ZONE) && !defined(WIN32)
/*
* This is a kludge for systems not having the timezone string in
* struct tm. No matter what was specified, they use the local
@@ -297,7 +297,7 @@ FormatClock(interp, clockVal, useGMT, format)
*/
if (useGMT) {
- char *varValue;
+ CONST char *varValue;
varValue = Tcl_GetVar2(interp, "env", "TZ", TCL_GLOBAL_ONLY);
if (varValue != NULL) {
@@ -327,15 +327,18 @@ FormatClock(interp, clockVal, useGMT, format)
bufSize++;
}
}
+ Tcl_DStringInit(&uniBuffer);
+ Tcl_UtfToExternalDString(NULL, format, -1, &uniBuffer);
Tcl_DStringInit(&buffer);
Tcl_DStringSetLength(&buffer, bufSize);
Tcl_MutexLock(&clockMutex);
- result = TclpStrftime(buffer.string, (unsigned int) bufSize, format,
- timeDataPtr);
+ result = TclpStrftime(buffer.string, (unsigned int) bufSize,
+ Tcl_DStringValue(&uniBuffer), timeDataPtr, useGMT);
Tcl_MutexUnlock(&clockMutex);
+ Tcl_DStringFree(&uniBuffer);
-#ifndef HAVE_TM_ZONE
+#if !defined(HAVE_TM_ZONE) && !defined(WIN32)
if (useGMT) {
if (savedTZEnv != NULL) {
Tcl_SetVar2(interp, "env", "TZ", savedTZEnv, TCL_GLOBAL_ONLY);
@@ -361,8 +364,7 @@ FormatClock(interp, clockVal, useGMT, format)
}
/*
- * Convert the time to external encoding, in case we asked for
- * a localized return value. [Bug: 3345]
+ * Convert the time to UTF from external encoding [Bug: 3345]
*/
Tcl_DStringInit(&uniBuffer);
Tcl_ExternalToUtfDString(NULL, buffer.string, -1, &uniBuffer);
@@ -374,4 +376,3 @@ FormatClock(interp, clockVal, useGMT, format)
return TCL_OK;
}
-
diff --git a/tcl/generic/tclCmdAH.c b/tcl/generic/tclCmdAH.c
index 7788917ef99..e82dee2b03c 100644
--- a/tcl/generic/tclCmdAH.c
+++ b/tcl/generic/tclCmdAH.c
@@ -18,8 +18,6 @@
#include "tclPort.h"
#include <locale.h>
-typedef int (StatProc)_ANSI_ARGS_((CONST char *path, struct stat *buf));
-
/*
* Prototypes for local procedures defined in this file:
*/
@@ -27,15 +25,11 @@ typedef int (StatProc)_ANSI_ARGS_((CONST char *path, struct stat *buf));
static int CheckAccess _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Obj *objPtr, int mode));
static int GetStatBuf _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Obj *objPtr, StatProc *statProc,
- struct stat *statPtr));
+ Tcl_Obj *objPtr, Tcl_FSStatProc *statProc,
+ Tcl_StatBuf *statPtr));
static char * GetTypeFromMode _ANSI_ARGS_((int mode));
-static int SplitPath _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Obj *objPtr, int *argcPtr, char ***argvPtr));
static int StoreStatData _ANSI_ARGS_((Tcl_Interp *interp,
- char *varName, struct stat *statPtr));
-static char ** StringifyObjects _ANSI_ARGS_((int objc,
- Tcl_Obj *CONST objv[]));
+ char *varName, Tcl_StatBuf *statPtr));
/*
*----------------------------------------------------------------------
@@ -99,9 +93,8 @@ Tcl_CaseObjCmd(dummy, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
register int i;
- int body, result;
+ int body, result, caseObjc;
char *string, *arg;
- int caseObjc;
Tcl_Obj *CONST *caseObjv;
Tcl_Obj *armPtr;
@@ -137,7 +130,7 @@ Tcl_CaseObjCmd(dummy, interp, objc, objv)
for (i = 0; i < caseObjc; i += 2) {
int patObjc, j;
- char **patObjv;
+ CONST char **patObjv;
char *pat;
unsigned char *p;
@@ -307,8 +300,7 @@ Tcl_CdObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *dirName;
- Tcl_DString ds;
+ Tcl_Obj *dir;
int result;
if (objc > 2) {
@@ -317,23 +309,25 @@ Tcl_CdObjCmd(dummy, interp, objc, objv)
}
if (objc == 2) {
- dirName = Tcl_GetString(objv[1]);
+ dir = objv[1];
} else {
- dirName = "~";
+ dir = Tcl_NewStringObj("~",1);
+ Tcl_IncrRefCount(dir);
}
- if (Tcl_TranslateFileName(interp, dirName, &ds) == NULL) {
- return TCL_ERROR;
+ if (Tcl_FSConvertToPathType(interp, dir) != TCL_OK) {
+ result = TCL_ERROR;
+ } else {
+ result = Tcl_FSChdir(dir);
+ if (result != TCL_OK) {
+ Tcl_AppendResult(interp, "couldn't change working directory to \"",
+ Tcl_GetString(dir), "\": ", Tcl_PosixError(interp), (char *) NULL);
+ result = TCL_ERROR;
+ }
}
-
- result = Tcl_Chdir(Tcl_DStringValue(&ds));
- Tcl_DStringFree(&ds);
-
- if (result != 0) {
- Tcl_AppendResult(interp, "couldn't change working directory to \"",
- dirName, "\": ", Tcl_PosixError(interp), (char *) NULL);
- return TCL_ERROR;
+ if (objc != 2) {
+ Tcl_DecrRefCount(dir);
}
- return TCL_OK;
+ return result;
}
/*
@@ -432,7 +426,7 @@ Tcl_EncodingObjCmd(dummy, interp, objc, objv)
Tcl_DString ds;
Tcl_Obj *resultPtr;
- static char *optionStrings[] = {
+ static CONST char *optionStrings[] = {
"convertfrom", "convertto", "names", "system",
NULL
};
@@ -517,7 +511,8 @@ Tcl_EncodingObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
if (objc == 2) {
- Tcl_SetResult(interp, Tcl_GetEncodingName(NULL), TCL_STATIC);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ Tcl_GetEncodingName(NULL), -1);
} else {
return Tcl_SetSystemEncoding(interp,
Tcl_GetStringFromObj(objv[2], NULL));
@@ -729,6 +724,7 @@ Tcl_ExprObjCmd(dummy, interp, objc, objv)
* Create a new object holding the concatenated argument strings.
*/
+ /*** QUESTION: Do we need to copy the slow way? ***/
bytes = Tcl_GetStringFromObj(objv[1], &length);
objPtr = Tcl_NewStringObj(bytes, length);
Tcl_IncrRefCount(objPtr);
@@ -765,7 +761,9 @@ Tcl_ExprObjCmd(dummy, interp, objc, objv)
* See the user documentation for details on what it does.
* PLEASE NOTE THAT THIS FAILS WITH FILENAMES AND PATHS WITH
* EMBEDDED NULLS, WHICH COULD THEORETICALLY HAPPEN ON A MAC.
- *
+ * With the object-based Tcl_FS APIs, the above NOTE may no
+ * longer be true. In any case this assertion should be tested.
+ *
* Results:
* A standard Tcl result.
*
@@ -783,21 +781,22 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- Tcl_Obj *resultPtr;
int index;
/*
* This list of constants should match the fileOption string array below.
*/
- static char *fileOptions[] = {
+ static CONST char *fileOptions[] = {
"atime", "attributes", "channels", "copy",
"delete",
"dirname", "executable", "exists", "extension",
- "isdirectory", "isfile", "join", "lstat",
- "mtime", "mkdir", "nativename", "owned",
+ "isdirectory", "isfile", "join", "link",
+ "lstat", "mtime", "mkdir", "nativename",
+ "normalize", "owned",
"pathtype", "readable", "readlink", "rename",
- "rootname", "size", "split", "stat",
+ "rootname", "separator", "size", "split",
+ "stat", "system",
"tail", "type", "volumes", "writable",
(char *) NULL
};
@@ -805,10 +804,12 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
FILE_ATIME, FILE_ATTRIBUTES, FILE_CHANNELS, FILE_COPY,
FILE_DELETE,
FILE_DIRNAME, FILE_EXECUTABLE, FILE_EXISTS, FILE_EXTENSION,
- FILE_ISDIRECTORY, FILE_ISFILE, FILE_JOIN, FILE_LSTAT,
- FILE_MTIME, FILE_MKDIR, FILE_NATIVENAME, FILE_OWNED,
+ FILE_ISDIRECTORY, FILE_ISFILE, FILE_JOIN, FILE_LINK,
+ FILE_LSTAT, FILE_MTIME, FILE_MKDIR, FILE_NATIVENAME,
+ FILE_NORMALIZE, FILE_OWNED,
FILE_PATHTYPE, FILE_READABLE, FILE_READLINK, FILE_RENAME,
- FILE_ROOTNAME, FILE_SIZE, FILE_SPLIT, FILE_STAT,
+ FILE_ROOTNAME, FILE_SEPARATOR, FILE_SIZE, FILE_SPLIT,
+ FILE_STAT, FILE_SYSTEM,
FILE_TAIL, FILE_TYPE, FILE_VOLUMES, FILE_WRITABLE
};
@@ -821,18 +822,16 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
- resultPtr = Tcl_GetObjResult(interp);
switch ((enum options) index) {
case FILE_ATIME: {
- struct stat buf;
- char *fileName;
+ Tcl_StatBuf buf;
struct utimbuf tval;
if ((objc < 3) || (objc > 4)) {
Tcl_WrongNumArgs(interp, 2, objv, "name ?time?");
return TCL_ERROR;
}
- if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
+ if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
return TCL_ERROR;
}
if (objc == 4) {
@@ -842,11 +841,10 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
}
tval.actime = buf.st_atime;
tval.modtime = buf.st_mtime;
- fileName = Tcl_GetString(objv[2]);
- if (utime(fileName, &tval) != 0) {
- Tcl_AppendStringsToObj(resultPtr,
+ if (Tcl_FSUtime(objv[2], &tval) != 0) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"could not set access time for file \"",
- fileName, "\": ",
+ Tcl_GetString(objv[2]), "\": ",
Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
}
@@ -856,11 +854,11 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
* one we sent in. However, fs's like FAT don't
* even know what atime is.
*/
- if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
+ if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
return TCL_ERROR;
}
}
- Tcl_SetLongObj(resultPtr, (long) buf.st_atime);
+ Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) buf.st_atime);
return TCL_OK;
}
case FILE_ATTRIBUTES: {
@@ -875,57 +873,24 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
((objc == 2) ? NULL : Tcl_GetString(objv[2])));
}
case FILE_COPY: {
- int result;
- char **argv;
-
- argv = StringifyObjects(objc, objv);
- result = TclFileCopyCmd(interp, objc, argv);
- ckfree((char *) argv);
- return result;
+ return TclFileCopyCmd(interp, objc, objv);
}
case FILE_DELETE: {
- int result;
- char **argv;
-
- argv = StringifyObjects(objc, objv);
- result = TclFileDeleteCmd(interp, objc, argv);
- ckfree((char *) argv);
- return result;
+ return TclFileDeleteCmd(interp, objc, objv);
}
case FILE_DIRNAME: {
- int argc;
- char **argv;
-
+ Tcl_Obj *dirPtr;
if (objc != 3) {
goto only3Args;
}
- if (SplitPath(interp, objv[2], &argc, &argv) != TCL_OK) {
- return TCL_ERROR;
- }
-
- /*
- * Return all but the last component. If there is only one
- * component, return it if the path was non-relative, otherwise
- * return the current directory.
- */
-
- if (argc > 1) {
- Tcl_DString ds;
-
- Tcl_DStringInit(&ds);
- Tcl_JoinPath(argc - 1, argv, &ds);
- Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&ds),
- Tcl_DStringLength(&ds));
- Tcl_DStringFree(&ds);
- } else if ((argc == 0)
- || (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) {
- Tcl_SetStringObj(resultPtr,
- ((tclPlatform == TCL_PLATFORM_MAC) ? ":" : "."), 1);
+ dirPtr = TclFileDirname(interp, objv[2]);
+ if (dirPtr == NULL) {
+ return TCL_ERROR;
} else {
- Tcl_SetStringObj(resultPtr, argv[0], -1);
+ Tcl_SetObjResult(interp, dirPtr);
+ Tcl_DecrRefCount(dirPtr);
+ return TCL_OK;
}
- ckfree((char *) argv);
- return TCL_OK;
}
case FILE_EXECUTABLE: {
if (objc != 3) {
@@ -947,79 +912,162 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
fileName = Tcl_GetString(objv[2]);
extension = TclGetExtension(fileName);
if (extension != NULL) {
- Tcl_SetStringObj(resultPtr, extension, -1);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), extension, -1);
}
return TCL_OK;
}
case FILE_ISDIRECTORY: {
int value;
- struct stat buf;
+ Tcl_StatBuf buf;
if (objc != 3) {
goto only3Args;
}
value = 0;
- if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) {
+ if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
value = S_ISDIR(buf.st_mode);
}
- Tcl_SetBooleanObj(resultPtr, value);
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
return TCL_OK;
}
case FILE_ISFILE: {
int value;
- struct stat buf;
+ Tcl_StatBuf buf;
if (objc != 3) {
goto only3Args;
}
value = 0;
- if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) {
+ if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
value = S_ISREG(buf.st_mode);
}
- Tcl_SetBooleanObj(resultPtr, value);
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
return TCL_OK;
}
case FILE_JOIN: {
- char **argv;
- Tcl_DString ds;
+ Tcl_Obj *resObj;
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?");
return TCL_ERROR;
}
- argv = StringifyObjects(objc - 2, objv + 2);
- Tcl_DStringInit(&ds);
- Tcl_JoinPath(objc - 2, argv, &ds);
- Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&ds),
- Tcl_DStringLength(&ds));
- Tcl_DStringFree(&ds);
- ckfree((char *) argv);
+ resObj = Tcl_FSJoinToPath(NULL, objc - 2, objv + 2);
+ Tcl_SetObjResult(interp, resObj);
+ return TCL_OK;
+ }
+ case FILE_LINK: {
+ Tcl_Obj *contents;
+ int index;
+
+ if (objc < 3 || objc > 5) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-linktype? linkname ?target?");
+ return TCL_ERROR;
+ }
+
+ /* Index of the 'source' argument */
+ if (objc == 5) {
+ index = 3;
+ } else {
+ index = 2;
+ }
+
+ if (objc > 3) {
+ int linkAction;
+ if (objc == 5) {
+ /* We have a '-linktype' argument */
+ static CONST char *linkTypes[] = {
+ "-symbolic", "-hard", NULL
+ };
+ if (Tcl_GetIndexFromObj(interp, objv[2], linkTypes,
+ "switch", 0, &linkAction) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (linkAction == 0) {
+ linkAction = TCL_CREATE_SYMBOLIC_LINK;
+ } else {
+ linkAction = TCL_CREATE_HARD_LINK;
+ }
+ } else {
+ linkAction = TCL_CREATE_SYMBOLIC_LINK|TCL_CREATE_HARD_LINK;
+ }
+ if (Tcl_FSConvertToPathType(interp, objv[index]) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Create link from source to target */
+ contents = Tcl_FSLink(objv[index], objv[index+1], linkAction);
+ if (contents == NULL) {
+ /*
+ * We handle two common error cases specially, and
+ * for all other errors, we use the standard posix
+ * error message.
+ */
+ if (errno == EEXIST) {
+ Tcl_AppendResult(interp, "could not create new link \"",
+ Tcl_GetString(objv[index]),
+ "\": that path already exists", (char *) NULL);
+ } else if (errno == ENOENT) {
+ Tcl_AppendResult(interp, "could not create new link \"",
+ Tcl_GetString(objv[index]),
+ "\" since target \"",
+ Tcl_GetString(objv[index+1]),
+ "\" doesn't exist",
+ (char *) NULL);
+ } else {
+ Tcl_AppendResult(interp, "could not create new link \"",
+ Tcl_GetString(objv[index]), "\" pointing to \"",
+ Tcl_GetString(objv[index+1]), "\": ",
+ Tcl_PosixError(interp), (char *) NULL);
+ }
+ return TCL_ERROR;
+ }
+ } else {
+ if (Tcl_FSConvertToPathType(interp, objv[index]) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Read link */
+ contents = Tcl_FSLink(objv[index], NULL, 0);
+ if (contents == NULL) {
+ Tcl_AppendResult(interp, "could not read link \"",
+ Tcl_GetString(objv[index]), "\": ",
+ Tcl_PosixError(interp), (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ Tcl_SetObjResult(interp, contents);
+ if (objc == 3) {
+ /*
+ * If we are reading a link, we need to free this
+ * result refCount. If we are creating a link, this
+ * will just be objv[index+1], and so we don't own it.
+ */
+ Tcl_DecrRefCount(contents);
+ }
return TCL_OK;
}
case FILE_LSTAT: {
char *varName;
- struct stat buf;
+ Tcl_StatBuf buf;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "name varName");
return TCL_ERROR;
}
- if (GetStatBuf(interp, objv[2], TclpLstat, &buf) != TCL_OK) {
+ if (GetStatBuf(interp, objv[2], Tcl_FSLstat, &buf) != TCL_OK) {
return TCL_ERROR;
}
varName = Tcl_GetString(objv[3]);
return StoreStatData(interp, varName, &buf);
}
case FILE_MTIME: {
- struct stat buf;
- char *fileName;
+ Tcl_StatBuf buf;
struct utimbuf tval;
if ((objc < 3) || (objc > 4)) {
Tcl_WrongNumArgs(interp, 2, objv, "name ?time?");
return TCL_ERROR;
}
- if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
+ if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
return TCL_ERROR;
}
if (objc == 4) {
@@ -1029,11 +1077,10 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
}
tval.actime = buf.st_atime;
tval.modtime = buf.st_mtime;
- fileName = Tcl_GetString(objv[2]);
- if (utime(fileName, &tval) != 0) {
- Tcl_AppendStringsToObj(resultPtr,
+ if (Tcl_FSUtime(objv[2], &tval) != 0) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"could not set modification time for file \"",
- fileName, "\": ",
+ Tcl_GetString(objv[2]), "\": ",
Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
}
@@ -1043,28 +1090,22 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
* one we sent in. However, fs's like FAT don't
* even know what atime is.
*/
- if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
+ if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
return TCL_ERROR;
}
}
- Tcl_SetLongObj(resultPtr, (long) buf.st_mtime);
+ Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) buf.st_mtime);
return TCL_OK;
}
case FILE_MKDIR: {
- char **argv;
- int result;
-
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?");
return TCL_ERROR;
}
- argv = StringifyObjects(objc, objv);
- result = TclFileMakeDirsCmd(interp, objc, argv);
- ckfree((char *) argv);
- return result;
+ return TclFileMakeDirsCmd(interp, objc, objv);
}
case FILE_NATIVENAME: {
- char *fileName;
+ CONST char *fileName;
Tcl_DString ds;
if (objc != 3) {
@@ -1075,19 +1116,32 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
if (fileName == NULL) {
return TCL_ERROR;
}
- Tcl_SetStringObj(resultPtr, fileName, Tcl_DStringLength(&ds));
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), fileName,
+ Tcl_DStringLength(&ds));
Tcl_DStringFree(&ds);
return TCL_OK;
}
+ case FILE_NORMALIZE: {
+ Tcl_Obj *fileName;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "filename");
+ return TCL_ERROR;
+ }
+
+ fileName = Tcl_FSGetNormalizedPath(interp, objv[2]);
+ Tcl_SetObjResult(interp, fileName);
+ return TCL_OK;
+ }
case FILE_OWNED: {
int value;
- struct stat buf;
+ Tcl_StatBuf buf;
if (objc != 3) {
goto only3Args;
}
value = 0;
- if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) {
+ if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
/*
* For Windows and Macintosh, there are no user ids
* associated with a file, so we always return 1.
@@ -1099,25 +1153,23 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
value = (geteuid() == buf.st_uid);
#endif
}
- Tcl_SetBooleanObj(resultPtr, value);
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
return TCL_OK;
}
case FILE_PATHTYPE: {
- char *fileName;
-
if (objc != 3) {
goto only3Args;
}
- fileName = Tcl_GetString(objv[2]);
- switch (Tcl_GetPathType(fileName)) {
+ switch (Tcl_FSGetPathType(objv[2])) {
case TCL_PATH_ABSOLUTE:
- Tcl_SetStringObj(resultPtr, "absolute", -1);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), "absolute", -1);
break;
case TCL_PATH_RELATIVE:
- Tcl_SetStringObj(resultPtr, "relative", -1);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), "relative", -1);
break;
case TCL_PATH_VOLUME_RELATIVE:
- Tcl_SetStringObj(resultPtr, "volumerelative", -1);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ "volumerelative", -1);
break;
}
return TCL_OK;
@@ -1129,52 +1181,30 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
return CheckAccess(interp, objv[2], R_OK);
}
case FILE_READLINK: {
- char *fileName, *contents;
- Tcl_DString name, link;
+ Tcl_Obj *contents;
if (objc != 3) {
goto only3Args;
}
- fileName = Tcl_GetString(objv[2]);
- fileName = Tcl_TranslateFileName(interp, fileName, &name);
- if (fileName == NULL) {
+ if (Tcl_FSConvertToPathType(interp, objv[2]) != TCL_OK) {
return TCL_ERROR;
}
- /*
- * If S_IFLNK isn't defined it means that the machine doesn't
- * support symbolic links, so the file can't possibly be a
- * symbolic link. Generate an EINVAL error, which is what
- * happens on machines that do support symbolic links when
- * you invoke readlink on a file that isn't a symbolic link.
- */
-
-#ifndef S_IFLNK
- contents = NULL;
- errno = EINVAL;
-#else
- contents = TclpReadlink(fileName, &link);
-#endif /* S_IFLNK */
+ contents = Tcl_FSLink(objv[2], NULL, 0);
- Tcl_DStringFree(&name);
if (contents == NULL) {
Tcl_AppendResult(interp, "could not readlink \"",
Tcl_GetString(objv[2]), "\": ",
Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
}
- Tcl_DStringResult(interp, &link);
+ Tcl_SetObjResult(interp, contents);
+ Tcl_DecrRefCount(contents);
return TCL_OK;
}
case FILE_RENAME: {
- int result;
- char **argv;
-
- argv = StringifyObjects(objc, objv);
- result = TclFileRenameCmd(interp, objc, argv);
- ckfree((char *) argv);
- return result;
+ return TclFileRenameCmd(interp, objc, objv);
}
case FILE_ROOTNAME: {
int length;
@@ -1188,64 +1218,113 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
if (extension == NULL) {
Tcl_SetObjResult(interp, objv[2]);
} else {
- Tcl_SetStringObj(resultPtr, fileName,
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), fileName,
(int) (length - strlen(extension)));
}
return TCL_OK;
}
+ case FILE_SEPARATOR: {
+ if ((objc < 2) || (objc > 3)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?name?");
+ return TCL_ERROR;
+ }
+ if (objc == 2) {
+ char *separator = NULL; /* lint */
+ switch (tclPlatform) {
+ case TCL_PLATFORM_UNIX:
+ separator = "/";
+ break;
+ case TCL_PLATFORM_WINDOWS:
+ separator = "\\";
+ break;
+ case TCL_PLATFORM_MAC:
+ separator = ":";
+ break;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(separator,1));
+ } else {
+ Tcl_Obj *separatorObj = Tcl_FSPathSeparator(objv[2]);
+ if (separatorObj != NULL) {
+ Tcl_SetObjResult(interp, separatorObj);
+ } else {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("Unrecognised path",-1));
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+ }
case FILE_SIZE: {
- struct stat buf;
+ Tcl_StatBuf buf;
if (objc != 3) {
goto only3Args;
}
- if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
+ if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
return TCL_ERROR;
}
- Tcl_SetLongObj(resultPtr, (long) buf.st_size);
+ Tcl_SetWideIntObj(Tcl_GetObjResult(interp),
+ (Tcl_WideInt) buf.st_size);
return TCL_OK;
}
case FILE_SPLIT: {
- int i, argc;
- char **argv;
- char *fileName;
- Tcl_Obj *objPtr;
-
if (objc != 3) {
goto only3Args;
}
- fileName = Tcl_GetString(objv[2]);
- Tcl_SplitPath(fileName, &argc, &argv);
- for (i = 0; i < argc; i++) {
- objPtr = Tcl_NewStringObj(argv[i], -1);
- Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
- }
- ckfree((char *) argv);
+ Tcl_SetObjResult(interp, Tcl_FSSplitPath(objv[2], NULL));
return TCL_OK;
}
case FILE_STAT: {
char *varName;
- struct stat buf;
+ Tcl_StatBuf buf;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 1, objv, "stat name varName");
return TCL_ERROR;
}
- if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
+ if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
return TCL_ERROR;
}
varName = Tcl_GetString(objv[3]);
return StoreStatData(interp, varName, &buf);
}
+ case FILE_SYSTEM: {
+ Tcl_Obj* fsInfo;
+ if (objc != 3) {
+ goto only3Args;
+ }
+ fsInfo = Tcl_FSFileSystemInfo(objv[2]);
+ if (fsInfo != NULL) {
+ Tcl_SetObjResult(interp, fsInfo);
+ return TCL_OK;
+ } else {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("Unrecognised path",-1));
+ return TCL_ERROR;
+ }
+ }
case FILE_TAIL: {
- int argc;
- char **argv;
+ int splitElements;
+ Tcl_Obj *splitPtr;
if (objc != 3) {
goto only3Args;
}
- if (SplitPath(interp, objv[2], &argc, &argv) != TCL_OK) {
- return TCL_ERROR;
+ /*
+ * The behaviour we want here is slightly different to
+ * the standard Tcl_FSSplitPath in the handling of home
+ * directories; Tcl_FSSplitPath preserves the "~" while
+ * this code computes the actual full path name, if we
+ * had just a single component.
+ */
+ splitPtr = Tcl_FSSplitPath(objv[2], &splitElements);
+ if ((splitElements == 1) && (Tcl_GetString(objv[2])[0] == '~')) {
+ Tcl_DecrRefCount(splitPtr);
+ splitPtr = Tcl_FSGetNormalizedPath(interp, objv[2]);
+ if (splitPtr == NULL) {
+ return TCL_ERROR;
+ }
+ splitPtr = Tcl_FSSplitPath(splitPtr, &splitElements);
}
/*
@@ -1253,25 +1332,28 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
* and it is the root of an absolute path.
*/
- if (argc > 0) {
- if ((argc > 1)
- || (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) {
- Tcl_SetStringObj(resultPtr, argv[argc - 1], -1);
+ if (splitElements > 0) {
+ if ((splitElements > 1)
+ || (Tcl_FSGetPathType(objv[2]) == TCL_PATH_RELATIVE)) {
+
+ Tcl_Obj *tail = NULL;
+ Tcl_ListObjIndex(NULL, splitPtr, splitElements-1, &tail);
+ Tcl_SetObjResult(interp, tail);
}
}
- ckfree((char *) argv);
+ Tcl_DecrRefCount(splitPtr);
return TCL_OK;
}
case FILE_TYPE: {
- struct stat buf;
+ Tcl_StatBuf buf;
if (objc != 3) {
goto only3Args;
}
- if (GetStatBuf(interp, objv[2], TclpLstat, &buf) != TCL_OK) {
+ if (GetStatBuf(interp, objv[2], Tcl_FSLstat, &buf) != TCL_OK) {
return TCL_ERROR;
}
- Tcl_SetStringObj(resultPtr,
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
GetTypeFromMode((unsigned short) buf.st_mode), -1);
return TCL_OK;
}
@@ -1280,7 +1362,8 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
Tcl_WrongNumArgs(interp, 2, objv, NULL);
return TCL_ERROR;
}
- return TclpListVolumes(interp);
+ Tcl_SetObjResult(interp, Tcl_FSListVolumes());
+ return TCL_OK;
}
case FILE_WRITABLE: {
if (objc != 3) {
@@ -1298,63 +1381,6 @@ Tcl_FileObjCmd(dummy, interp, objc, objv)
/*
*---------------------------------------------------------------------------
*
- * SplitPath --
- *
- * Utility procedure used by Tcl_FileObjCmd() to split a path.
- * Differs from standard Tcl_SplitPath in its handling of home
- * directories; Tcl_SplitPath preserves the "~" while this
- * procedure computes the actual full path name.
- *
- * Results:
- * The return value is TCL_OK if the path could be split, TCL_ERROR
- * otherwise. If TCL_ERROR was returned, an error message is left
- * in interp. If TCL_OK was returned, *argvPtr is set to a newly
- * allocated array of strings that represent the individual
- * directories in the specified path, and *argcPtr is filled with
- * the length of that array.
- *
- * Side effects:
- * Memory allocated. The caller must eventually free this memory
- * by calling ckfree() on *argvPtr.
- *
- *---------------------------------------------------------------------------
- */
-
-static int
-SplitPath(interp, objPtr, argcPtr, argvPtr)
- Tcl_Interp *interp; /* Interp for error return. May be NULL. */
- Tcl_Obj *objPtr; /* Path to be split. */
- int *argcPtr; /* Filled with length of following array. */
- char ***argvPtr; /* Filled with array of strings representing
- * the elements of the specified path. */
-{
- char *fileName;
-
- fileName = Tcl_GetString(objPtr);
-
- /*
- * If there is only one element, and it starts with a tilde,
- * perform tilde substitution and resplit the path.
- */
-
- Tcl_SplitPath(fileName, argcPtr, argvPtr);
- if ((*argcPtr == 1) && (fileName[0] == '~')) {
- Tcl_DString ds;
-
- ckfree((char *) *argvPtr);
- fileName = Tcl_TranslateFileName(interp, fileName, &ds);
- if (fileName == NULL) {
- return TCL_ERROR;
- }
- Tcl_SplitPath(fileName, argcPtr, argvPtr);
- Tcl_DStringFree(&ds);
- }
- return TCL_OK;
-}
-
-/*
- *---------------------------------------------------------------------------
- *
* CheckAccess --
*
* Utility procedure used by Tcl_FileObjCmd() to query file
@@ -1379,16 +1405,11 @@ CheckAccess(interp, objPtr, mode)
* access(). */
{
int value;
- char *fileName;
- Tcl_DString ds;
- fileName = Tcl_GetString(objPtr);
- fileName = Tcl_TranslateFileName(interp, fileName, &ds);
- if (fileName == NULL) {
+ if (Tcl_FSConvertToPathType(interp, objPtr) != TCL_OK) {
value = 0;
} else {
- value = (TclAccess(fileName, mode) == 0);
- Tcl_DStringFree(&ds);
+ value = (Tcl_FSAccess(objPtr, mode) == 0);
}
Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
@@ -1419,23 +1440,18 @@ static int
GetStatBuf(interp, objPtr, statProc, statPtr)
Tcl_Interp *interp; /* Interp for error return. May be NULL. */
Tcl_Obj *objPtr; /* Path name to examine. */
- StatProc *statProc; /* Either stat() or lstat() depending on
+ Tcl_FSStatProc *statProc; /* Either stat() or lstat() depending on
* desired behavior. */
- struct stat *statPtr; /* Filled with info about file obtained by
+ Tcl_StatBuf *statPtr; /* Filled with info about file obtained by
* calling (*statProc)(). */
{
- char *fileName;
- Tcl_DString ds;
int status;
- fileName = Tcl_GetString(objPtr);
- fileName = Tcl_TranslateFileName(interp, fileName, &ds);
- if (fileName == NULL) {
+ if (Tcl_FSConvertToPathType(interp, objPtr) != TCL_OK) {
return TCL_ERROR;
}
- status = (*statProc)(Tcl_DStringValue(&ds), statPtr);
- Tcl_DStringFree(&ds);
+ status = (*statProc)(objPtr, statPtr);
if (status < 0) {
if (interp != NULL) {
@@ -1472,66 +1488,52 @@ StoreStatData(interp, varName, statPtr)
Tcl_Interp *interp; /* Interpreter for error reports. */
char *varName; /* Name of associative array variable
* in which to store stat results. */
- struct stat *statPtr; /* Pointer to buffer containing
+ Tcl_StatBuf *statPtr; /* Pointer to buffer containing
* stat data to store in varName. */
{
- char string[TCL_INTEGER_SPACE];
+ Tcl_Obj *var = Tcl_NewStringObj(varName, -1);
+ Tcl_Obj *field = Tcl_NewObj();
+ Tcl_Obj *value;
+ register unsigned short mode;
- TclFormatInt(string, (long) statPtr->st_dev);
- if (Tcl_SetVar2(interp, varName, "dev", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- TclFormatInt(string, (long) statPtr->st_ino);
- if (Tcl_SetVar2(interp, varName, "ino", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- TclFormatInt(string, (unsigned short) statPtr->st_mode);
- if (Tcl_SetVar2(interp, varName, "mode", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- TclFormatInt(string, (long) statPtr->st_nlink);
- if (Tcl_SetVar2(interp, varName, "nlink", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- TclFormatInt(string, (long) statPtr->st_uid);
- if (Tcl_SetVar2(interp, varName, "uid", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- TclFormatInt(string, (long) statPtr->st_gid);
- if (Tcl_SetVar2(interp, varName, "gid", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- sprintf(string, "%lu", (unsigned long) statPtr->st_size);
- if (Tcl_SetVar2(interp, varName, "size", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- TclFormatInt(string, (long) statPtr->st_atime);
- if (Tcl_SetVar2(interp, varName, "atime", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- TclFormatInt(string, (long) statPtr->st_mtime);
- if (Tcl_SetVar2(interp, varName, "mtime", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- TclFormatInt(string, (long) statPtr->st_ctime);
- if (Tcl_SetVar2(interp, varName, "ctime", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- if (Tcl_SetVar2(interp, varName, "type",
- GetTypeFromMode((unsigned short) statPtr->st_mode),
- TCL_LEAVE_ERR_MSG) == NULL) {
- return TCL_ERROR;
- }
+ /*
+ * Assume Tcl_ObjSetVar2() does not keep a copy of the field name!
+ */
+#define STORE_ARY(fieldName, object) \
+ Tcl_SetStringObj(field, (fieldName), -1); \
+ value = (object); \
+ if (Tcl_ObjSetVar2(interp,var,field,value,TCL_LEAVE_ERR_MSG) == NULL) { \
+ Tcl_DecrRefCount(var); \
+ Tcl_DecrRefCount(field); \
+ Tcl_DecrRefCount(value); \
+ return TCL_ERROR; \
+ }
+
+ Tcl_IncrRefCount(var);
+ Tcl_IncrRefCount(field);
+ STORE_ARY("dev", Tcl_NewLongObj((long)statPtr->st_dev));
+ /*
+ * Watch out porters; the inode is meant to be an *unsigned* value,
+ * so the cast might fail when there isn't a real arithmentic 'long
+ * long' type...
+ */
+ STORE_ARY("ino", Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_ino));
+ STORE_ARY("nlink", Tcl_NewLongObj((long)statPtr->st_nlink));
+ STORE_ARY("uid", Tcl_NewLongObj((long)statPtr->st_uid));
+ STORE_ARY("gid", Tcl_NewLongObj((long)statPtr->st_gid));
+ STORE_ARY("size", Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_size));
+#ifdef HAVE_ST_BLOCKS
+ STORE_ARY("blocks",Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_blocks));
+#endif
+ STORE_ARY("atime", Tcl_NewLongObj((long)statPtr->st_atime));
+ STORE_ARY("mtime", Tcl_NewLongObj((long)statPtr->st_mtime));
+ STORE_ARY("ctime", Tcl_NewLongObj((long)statPtr->st_ctime));
+ mode = (unsigned short) statPtr->st_mode;
+ STORE_ARY("mode", Tcl_NewIntObj(mode));
+ STORE_ARY("type", Tcl_NewStringObj(GetTypeFromMode(mode), -1));
+#undef STORE_ARY
+ Tcl_DecrRefCount(var);
+ Tcl_DecrRefCount(field);
return TCL_OK;
}
@@ -1710,17 +1712,17 @@ Tcl_ForeachObjCmd(dummy, interp, objc, objv)
Tcl_Obj **argObjv = argObjStorage;
#define STATIC_LIST_SIZE 4
- int indexArray[STATIC_LIST_SIZE]; /* Array of value list indices */
- int varcListArray[STATIC_LIST_SIZE]; /* # loop variables per list */
- Tcl_Obj **varvListArray[STATIC_LIST_SIZE]; /* Array of var name lists */
- int argcListArray[STATIC_LIST_SIZE]; /* Array of value list sizes */
- Tcl_Obj **argvListArray[STATIC_LIST_SIZE]; /* Array of value lists */
-
- int *index = indexArray;
- int *varcList = varcListArray;
- Tcl_Obj ***varvList = varvListArray;
- int *argcList = argcListArray;
- Tcl_Obj ***argvList = argvListArray;
+ int indexArray[STATIC_LIST_SIZE];
+ int varcListArray[STATIC_LIST_SIZE];
+ Tcl_Obj **varvListArray[STATIC_LIST_SIZE];
+ int argcListArray[STATIC_LIST_SIZE];
+ Tcl_Obj **argvListArray[STATIC_LIST_SIZE];
+
+ int *index = indexArray; /* Array of value list indices */
+ int *varcList = varcListArray; /* # loop variables per list */
+ Tcl_Obj ***varvList = varvListArray; /* Array of var name lists */
+ int *argcList = argcListArray; /* Array of value list sizes */
+ Tcl_Obj ***argvList = argvListArray; /* Array of value lists */
if (objc < 4 || (objc%2 != 0)) {
Tcl_WrongNumArgs(interp, 1, objv,
@@ -1806,24 +1808,23 @@ Tcl_ForeachObjCmd(dummy, interp, objc, objv)
for (j = 0; j < maxj; j++) {
for (i = 0; i < numLists; i++) {
/*
- * If a variable or value list object has been converted to
- * another kind of Tcl object, convert it back to a list object
- * and refetch the pointer to its element array.
+ * Refetch the list members; we assume that the sizes are
+ * the same, but the array of elements might be different
+ * if the internal rep of the objects has been lost and
+ * recreated (it is too difficult to accurately tell when
+ * this happens, which can lead to some wierd crashes,
+ * like Bug #494348...)
*/
- if (argObjv[1+i*2]->typePtr != &tclListType) {
- result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
- &varcList[i], &varvList[i]);
- if (result != TCL_OK) {
- panic("Tcl_ForeachObjCmd: could not reconvert variable list %d to a list object\n", i);
- }
+ result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
+ &varcList[i], &varvList[i]);
+ if (result != TCL_OK) {
+ panic("Tcl_ForeachObjCmd: could not reconvert variable list %d to a list object\n", i);
}
- if (argObjv[2+i*2]->typePtr != &tclListType) {
- result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
- &argcList[i], &argvList[i]);
- if (result != TCL_OK) {
- panic("Tcl_ForeachObjCmd: could not reconvert value list %d to a list object\n", i);
- }
+ result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
+ &argcList[i], &argvList[i]);
+ if (result != TCL_OK) {
+ panic("Tcl_ForeachObjCmd: could not reconvert value list %d to a list object\n", i);
}
for (v = 0; v < varcList[i]; v++) {
@@ -1920,9 +1921,9 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
{
char *format; /* Used to read characters from the format
* string. */
- int formatLen; /* The length of the format string */
+ int formatLen; /* The length of the format string */
char *endPtr; /* Points to the last char in format array */
- char newFormat[40]; /* A new format specifier is generated here. */
+ char newFormat[43]; /* A new format specifier is generated here. */
int width; /* Field width from field specifier, or 0 if
* no width given. */
int precision; /* Field precision from field specifier, or 0
@@ -1930,12 +1931,16 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
int size; /* Number of bytes needed for result of
* conversion, based on type of conversion
* ("e", "s", etc.), width, and precision. */
- int intValue; /* Used to hold value to pass to sprintf, if
+ long intValue; /* Used to hold value to pass to sprintf, if
* it's a one-word integer or char value */
char *ptrValue = NULL; /* Used to hold value to pass to sprintf, if
* it's a one-word value. */
double doubleValue; /* Used to hold value to pass to sprintf if
* it's a double value. */
+#ifndef TCL_WIDE_INT_IS_LONG
+ Tcl_WideInt wideValue; /* Used to hold value to pass to sprintf if
+ * it's a 'long long' value. */
+#endif /* TCL_WIDE_INT_IS_LONG */
int whichValue; /* Indicates which of intValue, ptrValue,
* or doubleValue has the value to pass to
* sprintf, according to the following
@@ -1945,8 +1950,9 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
# define PTR_VALUE 2
# define DOUBLE_VALUE 3
# define STRING_VALUE 4
+# define WIDE_VALUE 5
# define MAX_FLOAT_SIZE 320
-
+
Tcl_Obj *resultPtr; /* Where result is stored finally. */
char staticBuf[MAX_FLOAT_SIZE + 1];
/* A static buffer to copy the format results
@@ -1973,6 +1979,9 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
* been set for the current field. */
int gotZero; /* Non-zero indicates that a zero flag has
* been seen in the current field. */
+#ifndef TCL_WIDE_INT_IS_LONG
+ int useWide; /* Value to be printed is Tcl_WideInt. */
+#endif /* TCL_WIDE_INT_IS_LONG */
/*
* This procedure is a bit nasty. The goal is to use sprintf to
@@ -1982,7 +1991,8 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
* whatever's generated. This is hard to estimate.
* 3. there's no way to move the arguments from objv to the call
* to sprintf in a reasonable way. This is particularly nasty
- * because some of the arguments may be two-word values (doubles).
+ * because some of the arguments may be two-word values (doubles
+ * and wide-ints).
* So, what happens here is to scan the format string one % group
* at a time, making many individual calls to sprintf.
*/
@@ -1992,7 +2002,7 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
- format = (char *) Tcl_GetStringFromObj(objv[1], &formatLen);
+ format = Tcl_GetStringFromObj(objv[1], &formatLen);
endPtr = format + formatLen;
resultPtr = Tcl_NewObj();
objIndex = 2;
@@ -2002,6 +2012,9 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
width = precision = noPercent = useShort = 0;
gotZero = gotMinus = gotPrecision = 0;
+#ifndef TCL_WIDE_INT_IS_LONG
+ useWide = 0;
+#endif /* TCL_WIDE_INT_IS_LONG */
whichValue = PTR_VALUE;
/*
@@ -2081,7 +2094,7 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
newPtr++;
format++;
}
- if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
+ if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
width = strtoul(format, &end, 10); /* INTL: Tcl source. */
format = end;
} else if (*format == '*') {
@@ -2124,7 +2137,7 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
format++;
gotPrecision = 1;
}
- if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
+ if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
precision = strtoul(format, &end, 10); /* INTL: "C" locale. */
format = end;
} else if (*format == '*') {
@@ -2145,6 +2158,11 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
}
}
if (*format == 'l') {
+#ifndef TCL_WIDE_INT_IS_LONG
+ useWide = 1;
+ strcpy(newPtr, TCL_LL_MODIFIER);
+ newPtr += TCL_LL_MODIFIER_SIZE;
+#endif /* TCL_WIDE_INT_IS_LONG */
format++;
} else if (*format == 'h') {
useShort = 1;
@@ -2166,10 +2184,32 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
case 'u':
case 'x':
case 'X':
- if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */
+#ifndef TCL_WIDE_INT_IS_LONG
+ if (useWide) {
+ if (Tcl_GetWideIntFromObj(interp, /* INTL: Tcl source. */
+ objv[objIndex], &wideValue) != TCL_OK) {
+ goto fmtError;
+ }
+ whichValue = WIDE_VALUE;
+ size = 40 + precision;
+ break;
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ if (Tcl_GetLongFromObj(interp, /* INTL: Tcl source. */
objv[objIndex], &intValue) != TCL_OK) {
goto fmtError;
}
+#if (LONG_MAX > INT_MAX)
+ /*
+ * Add the 'l' for long format type because we are on
+ * an LP64 archtecture and we are really going to pass
+ * a long argument to sprintf.
+ */
+ newPtr++;
+ *newPtr = 0;
+ newPtr[-1] = newPtr[-2];
+ newPtr[-2] = 'l';
+#endif /* LONG_MAX > INT_MAX */
whichValue = INT_VALUE;
size = 40 + precision;
break;
@@ -2193,7 +2233,7 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
}
break;
case 'c':
- if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */
+ if (Tcl_GetLongFromObj(interp, /* INTL: Tcl source. */
objv[objIndex], &intValue) != TCL_OK) {
goto fmtError;
}
@@ -2254,6 +2294,12 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
sprintf(dst, newFormat, doubleValue); /* INTL: user locale. */
break;
}
+#ifndef TCL_WIDE_INT_IS_LONG
+ case WIDE_VALUE: {
+ sprintf(dst, newFormat, wideValue);
+ break;
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
case INT_VALUE: {
if (useShort) {
sprintf(dst, newFormat, (short) intValue);
@@ -2345,43 +2391,3 @@ Tcl_FormatObjCmd(dummy, interp, objc, objv)
Tcl_DecrRefCount(resultPtr);
return TCL_ERROR;
}
-
-/*
- *---------------------------------------------------------------------------
- *
- * StringifyObjects --
- *
- * Helper function to bridge the gap between an object-based procedure
- * and an older string-based procedure.
- *
- * Given an array of objects, allocate an array that consists of the
- * string representations of those objects.
- *
- * Results:
- * The return value is a pointer to the newly allocated array of
- * strings. Elements 0 to (objc-1) of the string array point to the
- * string representation of the corresponding element in the source
- * object array; element objc of the string array is NULL.
- *
- * Side effects:
- * Memory allocated. The caller must eventually free this memory
- * by calling ckfree() on the return value.
- *
- *---------------------------------------------------------------------------
- */
-
-static char **
-StringifyObjects(objc, objv)
- int objc; /* Number of arguments. */
- Tcl_Obj *CONST objv[]; /* Argument objects. */
-{
- int i;
- char **argv;
-
- argv = (char **) ckalloc((objc + 1) * sizeof(char *));
- for (i = 0; i < objc; i++) {
- argv[i] = Tcl_GetString(objv[i]);
- }
- argv[i] = NULL;
- return argv;
-}
diff --git a/tcl/generic/tclCmdIL.c b/tcl/generic/tclCmdIL.c
index 54ed56fa0e8..dae26d8f10a 100644
--- a/tcl/generic/tclCmdIL.c
+++ b/tcl/generic/tclCmdIL.c
@@ -10,6 +10,7 @@
* Copyright (c) 1993-1997 Lucent Technologies.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
* Copyright (c) 1998-1999 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -19,7 +20,6 @@
#include "tclInt.h"
#include "tclPort.h"
-#include "tclCompile.h"
#include "tclRegexp.h"
/*
@@ -73,11 +73,18 @@ typedef struct SortInfo {
#define SORTMODE_DICTIONARY 4
/*
+ * Magic values for the index field of the SortInfo structure.
+ * Note that the index "end-1" will be translated to SORTIDX_END-1, etc.
+ */
+#define SORTIDX_NONE -1 /* Not indexed; use whole value. */
+#define SORTIDX_END -2 /* Indexed from end. */
+
+/*
* Forward declarations for procedures defined in this file:
*/
static void AppendLocals _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Obj *listPtr, char *pattern,
+ Tcl_Obj *listPtr, CONST char *pattern,
int includeLinks));
static int DictionaryCompare _ANSI_ARGS_((char *left,
char *right));
@@ -102,6 +109,9 @@ static int InfoDefaultCmd _ANSI_ARGS_((ClientData dummy,
static int InfoExistsCmd _ANSI_ARGS_((ClientData dummy,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
+static int InfoFunctionsCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
static int InfoGlobalsCmd _ANSI_ARGS_((ClientData dummy,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
@@ -313,10 +323,36 @@ Tcl_IncrObjCmd(dummy, interp, objc, objv)
if (objc == 2) {
incrAmount = 1;
} else {
+#ifdef TCL_WIDE_INT_IS_LONG
if (Tcl_GetLongFromObj(interp, objv[2], &incrAmount) != TCL_OK) {
Tcl_AddErrorInfo(interp, "\n (reading increment)");
return TCL_ERROR;
}
+#else
+ /*
+ * Need to be a bit cautious to ensure that [expr]-like rules
+ * are enforced for interpretation of wide integers, despite
+ * the fact that the underlying API itself is a 'long' only one.
+ */
+ if (objv[2]->typePtr == &tclIntType) {
+ incrAmount = objv[2]->internalRep.longValue;
+ } else if (objv[2]->typePtr == &tclWideIntType) {
+ incrAmount = Tcl_WideAsLong(objv[2]->internalRep.wideValue);
+ } else {
+ Tcl_WideInt wide;
+
+ if (Tcl_GetWideIntFromObj(interp, objv[2], &wide) != TCL_OK) {
+ Tcl_AddErrorInfo(interp, "\n (reading increment)");
+ return TCL_ERROR;
+ }
+ incrAmount = Tcl_WideAsLong(wide);
+ if ((wide <= Tcl_LongAsWide(LONG_MAX))
+ && (wide >= Tcl_LongAsWide(LONG_MIN))) {
+ objv[2]->typePtr = &tclIntType;
+ objv[2]->internalRep.longValue = incrAmount;
+ }
+ }
+#endif
}
/*
@@ -363,16 +399,16 @@ Tcl_InfoObjCmd(clientData, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- static char *subCmds[] = {
+ static CONST char *subCmds[] = {
"args", "body", "cmdcount", "commands",
- "complete", "default", "exists", "globals",
+ "complete", "default", "exists", "functions", "globals",
"hostname", "level", "library", "loaded",
"locals", "nameofexecutable", "patchlevel", "procs",
"script", "sharedlibextension", "tclversion", "vars",
(char *) NULL};
enum ISubCmdIdx {
IArgsIdx, IBodyIdx, ICmdCountIdx, ICommandsIdx,
- ICompleteIdx, IDefaultIdx, IExistsIdx, IGlobalsIdx,
+ ICompleteIdx, IDefaultIdx, IExistsIdx, IFunctionsIdx, IGlobalsIdx,
IHostnameIdx, ILevelIdx, ILibraryIdx, ILoadedIdx,
ILocalsIdx, INameOfExecutableIdx, IPatchLevelIdx, IProcsIdx,
IScriptIdx, ISharedLibExtensionIdx, ITclVersionIdx, IVarsIdx
@@ -412,6 +448,9 @@ Tcl_InfoObjCmd(clientData, interp, objc, objv)
case IExistsIdx:
result = InfoExistsCmd(clientData, interp, objc, objv);
break;
+ case IFunctionsIdx:
+ result = InfoFunctionsCmd(clientData, interp, objc, objv);
+ break;
case IGlobalsIdx:
result = InfoGlobalsCmd(clientData, interp, objc, objv);
break;
@@ -562,23 +601,24 @@ InfoBodyCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
- /*
- * We should not return a bytecompiled body. If it is precompiled,
- * then the bodyPtr's string representation is bogus, since sources
- * are not available. If it was just a bytecompiled body, then it
- * is likely to not be of any use to the caller, as it was compiled
- * for a separate procedure context [Bug: 3412], and noone else can
- * reasonably use it.
- * In order to make sure that later manipulations of the object do not
- * invalidate the internal representation, we make a copy of the string
- * representation and return that one, instead.
+ /*
+ * Here we used to return procPtr->bodyPtr, except when the body was
+ * bytecompiled - in that case, the return was a copy of the body's
+ * string rep. In order to better isolate the implementation details
+ * of the compiler/engine subsystem, we now always return a copy of
+ * the string rep. It is important to return a copy so that later
+ * manipulations of the object do not invalidate the internal rep.
*/
bodyPtr = procPtr->bodyPtr;
- resultPtr = bodyPtr;
- if (bodyPtr->typePtr == &tclByteCodeType) {
- resultPtr = Tcl_NewStringObj(bodyPtr->bytes, bodyPtr->length);
+ if (bodyPtr->bytes == NULL) {
+ /*
+ * The string rep might not be valid if the procedure has
+ * never been run before. [Bug #545644]
+ */
+ (void) Tcl_GetString(bodyPtr);
}
+ resultPtr = Tcl_NewStringObj(bodyPtr->bytes, bodyPtr->length);
Tcl_SetObjResult(interp, resultPtr);
return TCL_OK;
@@ -654,7 +694,8 @@ InfoCommandsCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *cmdName, *pattern, *simplePattern;
+ char *cmdName, *pattern;
+ CONST char *simplePattern;
register Tcl_HashEntry *entryPtr;
Tcl_HashSearch search;
Namespace *nsPtr;
@@ -927,6 +968,54 @@ InfoExistsCmd(dummy, interp, objc, objv)
/*
*----------------------------------------------------------------------
*
+ * InfoFunctionsCmd --
+ *
+ * Called to implement the "info functions" command that returns the
+ * list of math functions matching an optional pattern. Handles the
+ * following syntax:
+ *
+ * info functions ?pattern?
+ *
+ * Results:
+ * Returns TCL_OK if successful and TCL_ERROR if there is an error.
+ *
+ * Side effects:
+ * Returns a result in the interpreter's result object. If there is
+ * an error, the result is an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoFunctionsCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *pattern;
+ Tcl_Obj *listPtr;
+
+ if (objc == 2) {
+ pattern = NULL;
+ } else if (objc == 3) {
+ pattern = Tcl_GetString(objv[2]);
+ } else {
+ Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
+ return TCL_ERROR;
+ }
+
+ listPtr = Tcl_ListMathFuncs(interp, pattern);
+ if (listPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, listPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* InfoGlobalsCmd --
*
* Called to implement the "info globals" command that returns the list
@@ -1018,7 +1107,7 @@ InfoHostnameCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *name;
+ CONST char *name;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 2, objv, NULL);
return TCL_ERROR;
@@ -1136,7 +1225,7 @@ InfoLibraryCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *libDirName;
+ CONST char *libDirName;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 2, objv, NULL);
@@ -1276,7 +1365,7 @@ static void
AppendLocals(interp, listPtr, pattern, includeLinks)
Tcl_Interp *interp; /* Current interpreter. */
Tcl_Obj *listPtr; /* List object to append names to. */
- char *pattern; /* Pattern to match against. */
+ CONST char *pattern; /* Pattern to match against. */
int includeLinks; /* 1 if upvars should be included, else 0. */
{
Interp *iPtr = (Interp *) interp;
@@ -1298,7 +1387,8 @@ AppendLocals(interp, listPtr, pattern, includeLinks)
* Skip nameless (temporary) variables and undefined variables
*/
- if (!TclIsVarTemporary(localPtr) && !TclIsVarUndefined(varPtr)) {
+ if (!TclIsVarTemporary(localPtr) && !TclIsVarUndefined(varPtr)
+ && (includeLinks || !TclIsVarLink(varPtr))) {
varName = varPtr->name;
if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) {
Tcl_ListObjAppendElement(interp, listPtr,
@@ -1365,7 +1455,7 @@ InfoNameOfExecutableCmd(dummy, interp, objc, objv)
nameOfExecutable = Tcl_GetNameOfExecutable();
if (nameOfExecutable != NULL) {
- Tcl_SetStringObj(Tcl_GetObjResult(interp), (char *)nameOfExecutable, -1);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), nameOfExecutable, -1);
}
return TCL_OK;
}
@@ -1398,7 +1488,7 @@ InfoPatchLevelCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *patchlevel;
+ CONST char *patchlevel;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 2, objv, NULL);
@@ -1445,7 +1535,8 @@ InfoProcsCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *cmdName, *pattern, *simplePattern;
+ char *cmdName, *pattern;
+ CONST char *simplePattern;
Namespace *nsPtr;
#ifdef INFO_PROCS_SEARCH_GLOBAL_NS
Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
@@ -1506,19 +1597,19 @@ InfoProcsCmd(dummy, interp, objc, objv)
|| Tcl_StringMatch(cmdName, simplePattern)) {
cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
- if (specificNsInPattern) {
- elemObjPtr = Tcl_NewObj();
- Tcl_GetCommandFullName(interp, (Tcl_Command) cmdPtr,
- elemObjPtr);
- } else {
- elemObjPtr = Tcl_NewStringObj(cmdName, -1);
- }
-
realCmdPtr = (Command *)
TclGetOriginalCommand((Tcl_Command) cmdPtr);
if (TclIsProc(cmdPtr)
|| ((realCmdPtr != NULL) && TclIsProc(realCmdPtr))) {
+ if (specificNsInPattern) {
+ elemObjPtr = Tcl_NewObj();
+ Tcl_GetCommandFullName(interp, (Tcl_Command) cmdPtr,
+ elemObjPtr);
+ } else {
+ elemObjPtr = Tcl_NewStringObj(cmdName, -1);
+ }
+
Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
}
}
@@ -1578,14 +1669,17 @@ InfoProcsCmd(dummy, interp, objc, objv)
* script file that is currently being evaluated. Handles the
* following syntax:
*
- * info script
+ * info script ?newName?
+ *
+ * If newName is specified, it will set that as the internal name.
*
* Results:
* Returns TCL_OK if successful and TCL_ERROR if there is an error.
*
* Side effects:
* Returns a result in the interpreter's result object. If there is
- * an error, the result is an error message.
+ * an error, the result is an error message. It may change the
+ * internal script filename.
*
*----------------------------------------------------------------------
*/
@@ -1598,13 +1692,20 @@ InfoScriptCmd(dummy, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
Interp *iPtr = (Interp *) interp;
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ if ((objc != 2) && (objc != 3)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?filename?");
return TCL_ERROR;
}
+ if (objc == 3) {
+ if (iPtr->scriptFile != NULL) {
+ Tcl_DecrRefCount(iPtr->scriptFile);
+ }
+ iPtr->scriptFile = objv[2];
+ Tcl_IncrRefCount(iPtr->scriptFile);
+ }
if (iPtr->scriptFile != NULL) {
- Tcl_SetStringObj(Tcl_GetObjResult(interp), iPtr->scriptFile, -1);
+ Tcl_SetObjResult(interp, iPtr->scriptFile);
}
return TCL_OK;
}
@@ -1675,7 +1776,7 @@ InfoTclVersionCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *version;
+ CONST char *version;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 2, objv, NULL);
@@ -1723,7 +1824,8 @@ InfoVarsCmd(dummy, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
Interp *iPtr = (Interp *) interp;
- char *varName, *pattern, *simplePattern;
+ char *varName, *pattern;
+ CONST char *simplePattern;
register Tcl_HashEntry *entryPtr;
Tcl_HashSearch search;
Var *varPtr;
@@ -1936,61 +2038,334 @@ Tcl_LindexObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- Tcl_Obj *listPtr;
- Tcl_Obj **elemPtrs;
- int listLen, index, result;
- if (objc != 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "list index");
+ Tcl_Obj *elemPtr; /* Pointer to the element being extracted */
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "list ?index...?");
return TCL_ERROR;
}
/*
- * Convert the first argument to a list if necessary.
+ * If objc == 3, then objv[ 2 ] may be either a single index or
+ * a list of indices: go to TclLindexList to determine which.
+ * If objc >= 4, or objc == 2, then objv[ 2 .. objc-2 ] are all
+ * single indices and processed as such in TclLindexFlat.
*/
- listPtr = objv[1];
- result = Tcl_ListObjGetElements(interp, listPtr, &listLen, &elemPtrs);
- if (result != TCL_OK) {
- return result;
- }
+ if ( objc == 3 ) {
+
+ elemPtr = TclLindexList( interp, objv[ 1 ], objv[ 2 ] );
+
+ } else {
+ elemPtr = TclLindexFlat( interp, objv[ 1 ], objc-2, objv+2 );
+
+ }
+
/*
- * Get the index from objv[2].
+ * Set the interpreter's object result to the last element extracted
*/
- result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
- &index);
- if (result != TCL_OK) {
- return result;
+ if ( elemPtr == NULL ) {
+ return TCL_ERROR;
+ } else {
+ Tcl_SetObjResult(interp, elemPtr);
+ Tcl_DecrRefCount( elemPtr );
+ return TCL_OK;
}
- if ((index < 0) || (index >= listLen)) {
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclLindexList --
+ *
+ * This procedure handles the 'lindex' command when objc==3.
+ *
+ * Results:
+ * Returns a pointer to the object extracted, or NULL if an
+ * error occurred.
+ *
+ * Side effects:
+ * None.
+ *
+ * If objv[1] can be parsed as a list, TclLindexList handles extraction
+ * of the desired element locally. Otherwise, it invokes
+ * TclLindexFlat to treat objv[1] as a scalar.
+ *
+ * The reference count of the returned object includes one reference
+ * corresponding to the pointer returned. Thus, the calling code will
+ * usually do something like:
+ * Tcl_SetObjResult( interp, result );
+ * Tcl_DecrRefCount( result );
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TclLindexList( interp, listPtr, argPtr )
+ Tcl_Interp* interp; /* Tcl interpreter */
+ Tcl_Obj* listPtr; /* List being unpacked */
+ Tcl_Obj* argPtr; /* Index or index list */
+{
+
+ Tcl_Obj **elemPtrs; /* Elements of the list being manipulated. */
+ int listLen; /* Length of the list being manipulated. */
+ int index; /* Index into the list */
+ int result; /* Result returned from a Tcl library call */
+ int i; /* Current index number */
+ Tcl_Obj** indices; /* Array of list indices */
+ int indexCount; /* Size of the array of list indices */
+ Tcl_Obj* oldListPtr; /* Temp location to preserve the list
+ * pointer when replacing it with a sublist */
+
+ /*
+ * Determine whether argPtr designates a list or a single index.
+ * We have to be careful about the order of the checks to avoid
+ * repeated shimmering; see TIP#22 and TIP#33 for the details.
+ */
+
+ if ( argPtr->typePtr != &tclListType
+ && TclGetIntForIndex( NULL , argPtr, 0, &index ) == TCL_OK ) {
+
+ /*
+ * argPtr designates a single index.
+ */
+
+ return TclLindexFlat( interp, listPtr, 1, &argPtr );
+
+ } else if ( Tcl_ListObjGetElements( NULL, argPtr, &indexCount, &indices )
+ != TCL_OK ) {
+
/*
- * The index is out of range: the result is an empty string object.
+ * argPtr designates something that is neither an index nor a
+ * well-formed list. Report the error via TclLindexFlat.
*/
- return TCL_OK;
+ return TclLindexFlat( interp, listPtr, 1, &argPtr );
}
/*
- * Make sure listPtr still refers to a list object. It might have been
- * converted to an int above if the argument objects were shared.
+ * Record the reference to the list that we are maintaining in
+ * the activation record.
*/
- if (listPtr->typePtr != &tclListType) {
- result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
- &elemPtrs);
+ Tcl_IncrRefCount( listPtr );
+
+ /*
+ * argPtr designates a list, and the 'else if' above has parsed it
+ * into indexCount and indices.
+ */
+
+ for ( i = 0; i < indexCount; ++i ) {
+
+ /*
+ * Convert the current listPtr to a list if necessary.
+ */
+
+ result = Tcl_ListObjGetElements( interp, listPtr,
+ &listLen, &elemPtrs);
if (result != TCL_OK) {
- return result;
+ Tcl_DecrRefCount( listPtr );
+ return NULL;
}
- }
+
+ /*
+ * Get the index from indices[ i ]
+ */
+
+ result = TclGetIntForIndex( interp, indices[ i ],
+ /*endValue*/ (listLen - 1),
+ &index );
+ if ( result != TCL_OK ) {
+ /*
+ * Index could not be parsed
+ */
+
+ Tcl_DecrRefCount( listPtr );
+ return NULL;
+
+ } else if ( index < 0
+ || index >= listLen ) {
+ /*
+ * Index is out of range
+ */
+ Tcl_DecrRefCount( listPtr );
+ listPtr = Tcl_NewObj();
+ Tcl_IncrRefCount( listPtr );
+ return listPtr;
+ }
+
+ /*
+ * Make sure listPtr still refers to a list object.
+ * If it shared a Tcl_Obj structure with the arguments, then
+ * it might have just been converted to something else.
+ */
+
+ if (listPtr->typePtr != &tclListType) {
+ result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
+ &elemPtrs);
+ if (result != TCL_OK) {
+ Tcl_DecrRefCount( listPtr );
+ return NULL;
+ }
+ }
+
+ /*
+ * Extract the pointer to the appropriate element
+ */
+
+ oldListPtr = listPtr;
+ listPtr = elemPtrs[ index ];
+ Tcl_IncrRefCount( listPtr );
+ Tcl_DecrRefCount( oldListPtr );
+
+ /*
+ * The work we did above may have caused the internal rep
+ * of *argPtr to change to something else. Get it back.
+ */
+
+ result = Tcl_ListObjGetElements( interp, argPtr,
+ &indexCount, &indices );
+ if ( result != TCL_OK ) {
+ /*
+ * This can't happen unless some extension corrupted a Tcl_Obj.
+ */
+ Tcl_DecrRefCount( listPtr );
+ return NULL;
+ }
+
+ } /* end for */
/*
- * Set the interpreter's object result to the index-th list element.
+ * Return the last object extracted. Its reference count will include
+ * the reference being returned.
*/
- Tcl_SetObjResult(interp, elemPtrs[index]);
- return TCL_OK;
+ return listPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclLindexFlat --
+ *
+ * This procedure handles the 'lindex' command, given that the
+ * arguments to the command are known to be a flat list.
+ *
+ * Results:
+ * Returns a standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ * This procedure is called from either tclExecute.c or
+ * Tcl_LindexObjCmd whenever either is presented with
+ * objc == 2 or objc >= 4. It is also called from TclLindexList
+ * for the objc==3 case once it is determined that objv[2] cannot
+ * be parsed as a list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TclLindexFlat( interp, listPtr, indexCount, indexArray )
+ Tcl_Interp* interp; /* Tcl interpreter */
+ Tcl_Obj* listPtr; /* Tcl object representing the list */
+ int indexCount; /* Count of indices */
+ Tcl_Obj* CONST indexArray[];
+ /* Array of pointers to Tcl objects
+ * representing the indices in the
+ * list */
+{
+
+ int i; /* Current list index */
+ int result; /* Result of Tcl library calls */
+ int listLen; /* Length of the current list being
+ * processed */
+ Tcl_Obj** elemPtrs; /* Array of pointers to the elements
+ * of the current list */
+ int index; /* Parsed version of the current element
+ * of indexArray */
+ Tcl_Obj* oldListPtr; /* Temporary to hold listPtr so that
+ * its ref count can be decremented. */
+
+ /*
+ * Record the reference to the 'listPtr' object that we are
+ * maintaining in the C activation record.
+ */
+
+ Tcl_IncrRefCount( listPtr );
+
+ for ( i = 0; i < indexCount; ++i ) {
+
+ /*
+ * Convert the current listPtr to a list if necessary.
+ */
+
+ result = Tcl_ListObjGetElements(interp, listPtr,
+ &listLen, &elemPtrs);
+ if (result != TCL_OK) {
+ Tcl_DecrRefCount( listPtr );
+ return NULL;
+ }
+
+ /*
+ * Get the index from objv[i]
+ */
+
+ result = TclGetIntForIndex( interp, indexArray[ i ],
+ /*endValue*/ (listLen - 1),
+ &index );
+ if ( result != TCL_OK ) {
+
+ /* Index could not be parsed */
+
+ Tcl_DecrRefCount( listPtr );
+ return NULL;
+
+ } else if ( index < 0
+ || index >= listLen ) {
+
+ /*
+ * Index is out of range
+ */
+
+ Tcl_DecrRefCount( listPtr );
+ listPtr = Tcl_NewObj();
+ Tcl_IncrRefCount( listPtr );
+ return listPtr;
+ }
+
+ /*
+ * Make sure listPtr still refers to a list object.
+ * It might have been converted to something else above
+ * if objv[1] overlaps with one of the other parameters.
+ */
+
+ if (listPtr->typePtr != &tclListType) {
+ result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
+ &elemPtrs);
+ if (result != TCL_OK) {
+ Tcl_DecrRefCount( listPtr );
+ return NULL;
+ }
+ }
+
+ /*
+ * Extract the pointer to the appropriate element
+ */
+
+ oldListPtr = listPtr;
+ listPtr = elemPtrs[ index ];
+ Tcl_IncrRefCount( listPtr );
+ Tcl_DecrRefCount( oldListPtr );
+
+ }
+
+ return listPtr;
+
}
/*
@@ -2019,77 +2394,58 @@ Tcl_LinsertObjCmd(dummy, interp, objc, objv)
register int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- Tcl_Obj *listPtr, *resultPtr;
- Tcl_ObjType *typePtr;
+ Tcl_Obj *listPtr;
int index, isDuplicate, len, result;
-
+
if (objc < 4) {
Tcl_WrongNumArgs(interp, 1, objv, "list index element ?element ...?");
return TCL_ERROR;
}
- /*
- * Get the index first since, if a conversion to int is needed, it
- * will invalidate the list's internal representation.
- */
-
result = Tcl_ListObjLength(interp, objv[1], &len);
if (result != TCL_OK) {
return result;
}
- result = TclGetIntForIndex(interp, objv[2], /*endValue*/ len, &index);
+ /*
+ * Get the index. "end" is interpreted to be the index after the last
+ * element, such that using it will cause any inserted elements to be
+ * appended to the list.
+ */
+
+ result = TclGetIntForIndex(interp, objv[2], /*end*/ len, &index);
if (result != TCL_OK) {
return result;
}
+ if (index > len) {
+ index = len;
+ }
/*
* If the list object is unshared we can modify it directly. Otherwise
- * we create a copy to modify: this is "copy on write". We create the
- * duplicate directly in the interpreter's object result.
+ * we create a copy to modify: this is "copy on write".
*/
-
+
listPtr = objv[1];
isDuplicate = 0;
if (Tcl_IsShared(listPtr)) {
- /*
- * The following code must reflect the logic in Tcl_DuplicateObj()
- * except that it must duplicate the list object directly into the
- * interpreter's result.
- */
-
- Tcl_ResetResult(interp);
- resultPtr = Tcl_GetObjResult(interp);
- typePtr = listPtr->typePtr;
- if (listPtr->bytes == NULL) {
- resultPtr->bytes = NULL;
- } else if (listPtr->bytes != tclEmptyStringRep) {
- len = listPtr->length;
- TclInitStringRep(resultPtr, listPtr->bytes, len);
- }
- if (typePtr != NULL) {
- if (typePtr->dupIntRepProc == NULL) {
- resultPtr->internalRep = listPtr->internalRep;
- resultPtr->typePtr = typePtr;
- } else {
- (*typePtr->dupIntRepProc)(listPtr, resultPtr);
- }
- }
- listPtr = resultPtr;
+ listPtr = Tcl_DuplicateObj(listPtr);
isDuplicate = 1;
}
-
- if ((objc == 4) && (index == INT_MAX)) {
+
+ if ((objc == 4) && (index == len)) {
/*
* Special case: insert one element at the end of the list.
*/
-
result = Tcl_ListObjAppendElement(interp, listPtr, objv[3]);
} else if (objc > 3) {
result = Tcl_ListObjReplace(interp, listPtr, index, 0,
(objc-3), &(objv[3]));
}
if (result != TCL_OK) {
+ if (isDuplicate) {
+ Tcl_DecrRefCount(listPtr); /* free unneeded obj */
+ }
return result;
}
@@ -2097,9 +2453,7 @@ Tcl_LinsertObjCmd(dummy, interp, objc, objv)
* Set the interpreter's object result.
*/
- if (!isDuplicate) {
- Tcl_SetObjResult(interp, listPtr);
- }
+ Tcl_SetObjResult(interp, listPtr);
return TCL_OK;
}
@@ -2306,9 +2660,7 @@ Tcl_LreplaceObjCmd(dummy, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
register Tcl_Obj *listPtr;
- int createdNewObj, first, last, listLen, numToDelete;
- int firstArgLen, result;
- char *firstArg;
+ int isDuplicate, first, last, listLen, numToDelete, result;
if (objc < 4) {
Tcl_WrongNumArgs(interp, 1, objv,
@@ -2316,53 +2668,43 @@ Tcl_LreplaceObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
- /*
- * If the list object is unshared we can modify it directly, otherwise
- * we create a copy to modify: this is "copy on write".
- */
-
- listPtr = objv[1];
- createdNewObj = 0;
- if (Tcl_IsShared(listPtr)) {
- listPtr = Tcl_DuplicateObj(listPtr);
- createdNewObj = 1;
- }
- result = Tcl_ListObjLength(interp, listPtr, &listLen);
+ result = Tcl_ListObjLength(interp, objv[1], &listLen);
if (result != TCL_OK) {
- errorReturn:
- if (createdNewObj) {
- Tcl_DecrRefCount(listPtr); /* free unneeded obj */
- }
return result;
}
/*
- * Get the first and last indexes.
+ * Get the first and last indexes. "end" is interpreted to be the index
+ * for the last element, such that using it will cause that element to
+ * be included for deletion.
*/
- result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
- &first);
+ result = TclGetIntForIndex(interp, objv[2], /*end*/ (listLen - 1), &first);
if (result != TCL_OK) {
- goto errorReturn;
+ return result;
}
- firstArg = Tcl_GetStringFromObj(objv[2], &firstArgLen);
- result = TclGetIntForIndex(interp, objv[3], /*endValue*/ (listLen - 1),
- &last);
+ result = TclGetIntForIndex(interp, objv[3], /*end*/ (listLen - 1), &last);
if (result != TCL_OK) {
- goto errorReturn;
+ return result;
}
if (first < 0) {
first = 0;
}
- if ((first >= listLen) && (listLen > 0)
- && (strncmp(firstArg, "end", (unsigned) firstArgLen) != 0)) {
+
+ /*
+ * Complain if the user asked for a start element that is greater than the
+ * list length. This won't ever trigger for the "end*" case as that will
+ * be properly constrained by TclGetIntForIndex because we use listLen-1
+ * (to allow for replacing the last elem).
+ */
+
+ if ((first >= listLen) && (listLen > 0)) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"list doesn't contain element ",
Tcl_GetString(objv[2]), (int *) NULL);
- result = TCL_ERROR;
- goto errorReturn;
+ return TCL_ERROR;
}
if (last >= listLen) {
last = (listLen - 1);
@@ -2373,6 +2715,17 @@ Tcl_LreplaceObjCmd(dummy, interp, objc, objv)
numToDelete = 0;
}
+ /*
+ * If the list object is unshared we can modify it directly, otherwise
+ * we create a copy to modify: this is "copy on write".
+ */
+
+ listPtr = objv[1];
+ isDuplicate = 0;
+ if (Tcl_IsShared(listPtr)) {
+ listPtr = Tcl_DuplicateObj(listPtr);
+ isDuplicate = 1;
+ }
if (objc > 4) {
result = Tcl_ListObjReplace(interp, listPtr, first, numToDelete,
(objc-4), &(objv[4]));
@@ -2381,7 +2734,10 @@ Tcl_LreplaceObjCmd(dummy, interp, objc, objv)
0, NULL);
}
if (result != TCL_OK) {
- goto errorReturn;
+ if (isDuplicate) {
+ Tcl_DecrRefCount(listPtr); /* free unneeded obj */
+ }
+ return result;
}
/*
@@ -2418,23 +2774,120 @@ Tcl_LsearchObjCmd(clientData, interp, objc, objv)
{
char *bytes, *patternBytes;
int i, match, mode, index, result, listc, length, elemLen;
- Tcl_Obj *patObj, **listv;
- static char *options[] = {
- "-exact", "-glob", "-regexp", NULL
+ int dataType, isIncreasing, lower, upper, patInt, objInt;
+ int offset, allMatches, inlineReturn, negatedMatch;
+ double patDouble, objDouble;
+ Tcl_Obj *patObj, **listv, *listPtr, *startPtr;
+ static CONST char *options[] = {
+ "-all", "-ascii", "-decreasing", "-dictionary",
+ "-exact", "-glob", "-increasing", "-inline",
+ "-integer", "-not", "-real", "-regexp",
+ "-sorted", "-start", NULL
};
enum options {
- LSEARCH_EXACT, LSEARCH_GLOB, LSEARCH_REGEXP
+ LSEARCH_ALL, LSEARCH_ASCII, LSEARCH_DECREASING, LSEARCH_DICTIONARY,
+ LSEARCH_EXACT, LSEARCH_GLOB, LSEARCH_INCREASING, LSEARCH_INLINE,
+ LSEARCH_INTEGER, LSEARCH_NOT, LSEARCH_REAL, LSEARCH_REGEXP,
+ LSEARCH_SORTED, LSEARCH_START
};
+ enum datatypes {
+ ASCII, DICTIONARY, INTEGER, REAL
+ };
+ enum modes {
+ EXACT, GLOB, REGEXP, SORTED
+ };
+
+ mode = GLOB;
+ dataType = ASCII;
+ isIncreasing = 1;
+ allMatches = 0;
+ inlineReturn = 0;
+ negatedMatch = 0;
+ listPtr = NULL;
+ startPtr = NULL;
+ offset = 0;
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "?options? list pattern");
+ return TCL_ERROR;
+ }
- mode = LSEARCH_GLOB;
- if (objc == 4) {
- if (Tcl_GetIndexFromObj(interp, objv[1], options, "search mode", 0,
- &mode) != TCL_OK) {
+ for (i = 1; i < objc-2; i++) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, &index)
+ != TCL_OK) {
+ if (startPtr) {
+ Tcl_DecrRefCount(startPtr);
+ }
return TCL_ERROR;
}
- } else if (objc != 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "?mode? list pattern");
- return TCL_ERROR;
+ switch ((enum options) index) {
+ case LSEARCH_ALL: /* -all */
+ allMatches = 1;
+ break;
+ case LSEARCH_ASCII: /* -ascii */
+ dataType = ASCII;
+ break;
+ case LSEARCH_DECREASING: /* -decreasing */
+ isIncreasing = 0;
+ break;
+ case LSEARCH_DICTIONARY: /* -dictionary */
+ dataType = DICTIONARY;
+ break;
+ case LSEARCH_EXACT: /* -increasing */
+ mode = EXACT;
+ break;
+ case LSEARCH_GLOB: /* -glob */
+ mode = GLOB;
+ break;
+ case LSEARCH_INCREASING: /* -increasing */
+ isIncreasing = 1;
+ break;
+ case LSEARCH_INLINE: /* -inline */
+ inlineReturn = 1;
+ break;
+ case LSEARCH_INTEGER: /* -integer */
+ dataType = INTEGER;
+ break;
+ case LSEARCH_NOT: /* -not */
+ negatedMatch = 1;
+ break;
+ case LSEARCH_REAL: /* -real */
+ dataType = REAL;
+ break;
+ case LSEARCH_REGEXP: /* -regexp */
+ mode = REGEXP;
+ break;
+ case LSEARCH_SORTED: /* -sorted */
+ mode = SORTED;
+ break;
+ case LSEARCH_START: /* -start */
+ /*
+ * If there was a previous -start option, release its saved
+ * index because it will either be replaced or there will be
+ * an error.
+ */
+ if (startPtr) {
+ Tcl_DecrRefCount(startPtr);
+ }
+ if (i > objc-4) {
+ Tcl_AppendResult(interp, "missing starting index", NULL);
+ return TCL_ERROR;
+ }
+ i++;
+ if (objv[i] == objv[objc - 2]) {
+ /*
+ * Take copy to prevent shimmering problems. Note
+ * that it does not matter if the index obj is also a
+ * component of the list being searched. We only need
+ * to copy where the list and the index are
+ * one-and-the-same.
+ */
+ startPtr = Tcl_DuplicateObj(objv[i]);
+ } else {
+ startPtr = objv[i];
+ Tcl_IncrRefCount(startPtr);
+ }
+ }
}
/*
@@ -2444,48 +2897,328 @@ Tcl_LsearchObjCmd(clientData, interp, objc, objv)
result = Tcl_ListObjGetElements(interp, objv[objc - 2], &listc, &listv);
if (result != TCL_OK) {
+ if (startPtr) {
+ Tcl_DecrRefCount(startPtr);
+ }
return result;
}
+ /*
+ * Get the user-specified start offset.
+ */
+ if (startPtr) {
+ result = TclGetIntForIndex(interp, startPtr, listc-1, &offset);
+ Tcl_DecrRefCount(startPtr);
+ if (result != TCL_OK) {
+ return result;
+ }
+ if (offset < 0) {
+ offset = 0;
+ } else if (offset > listc-1) {
+ offset = listc-1;
+ }
+ }
+
patObj = objv[objc - 1];
- patternBytes = Tcl_GetStringFromObj(patObj, &length);
+ patternBytes = NULL;
+ if ((enum modes) mode == EXACT || (enum modes) mode == SORTED) {
+ switch ((enum datatypes) dataType) {
+ case ASCII:
+ case DICTIONARY:
+ patternBytes = Tcl_GetStringFromObj(patObj, &length);
+ break;
+ case INTEGER:
+ result = Tcl_GetIntFromObj(interp, patObj, &patInt);
+ if (result != TCL_OK) {
+ return result;
+ }
+ break;
+ case REAL:
+ result = Tcl_GetDoubleFromObj(interp, patObj, &patDouble);
+ if (result != TCL_OK) {
+ return result;
+ }
+ break;
+ }
+ } else {
+ patternBytes = Tcl_GetStringFromObj(patObj, &length);
+ }
+ /*
+ * Set default index value to -1, indicating failure; if we find the
+ * item in the course of our search, index will be set to the correct
+ * value.
+ */
index = -1;
- for (i = 0; i < listc; i++) {
- match = 0;
- switch ((enum options) mode) {
- case LSEARCH_EXACT: {
- bytes = Tcl_GetStringFromObj(listv[i], &elemLen);
- if (length == elemLen) {
- match = (memcmp(bytes, patternBytes,
- (size_t) length) == 0);
+ match = 0;
+
+ if ((enum modes) mode == SORTED && !allMatches && !negatedMatch) {
+ /*
+ * If the data is sorted, we can do a more intelligent search.
+ * Note that there is no point in being smart when -all was
+ * specified; in that case, we have to look at all items anyway,
+ * and there is no sense in doing this when the match sense is
+ * inverted.
+ */
+ lower = offset - 1;
+ upper = listc;
+ while (lower + 1 != upper) {
+ i = (lower + upper)/2;
+ switch ((enum datatypes) dataType) {
+ case ASCII:
+ bytes = Tcl_GetString(listv[i]);
+ match = strcmp(patternBytes, bytes);
+ break;
+ case DICTIONARY:
+ bytes = Tcl_GetString(listv[i]);
+ match = DictionaryCompare(patternBytes, bytes);
+ break;
+ case INTEGER:
+ result = Tcl_GetIntFromObj(interp, listv[i], &objInt);
+ if (result != TCL_OK) {
+ return result;
+ }
+ if (patInt == objInt) {
+ match = 0;
+ } else if (patInt < objInt) {
+ match = -1;
+ } else {
+ match = 1;
}
break;
- }
- case LSEARCH_GLOB: {
- match = Tcl_StringMatch(Tcl_GetString(listv[i]), patternBytes);
+ case REAL:
+ result = Tcl_GetDoubleFromObj(interp, listv[i], &objDouble);
+ if (result != TCL_OK) {
+ return result;
+ }
+ if (patDouble == objDouble) {
+ match = 0;
+ } else if (patDouble < objDouble) {
+ match = -1;
+ } else {
+ match = 1;
+ }
break;
}
- case LSEARCH_REGEXP: {
+ if (match == 0) {
+ /*
+ * Normally, binary search is written to stop when it
+ * finds a match. If there are duplicates of an element in
+ * the list, our first match might not be the first occurance.
+ * Consider: 0 0 0 1 1 1 2 2 2
+ * To maintain consistancy with standard lsearch semantics,
+ * we must find the leftmost occurance of the pattern in the
+ * list. Thus we don't just stop searching here. This
+ * variation means that a search always makes log n
+ * comparisons (normal binary search might "get lucky" with
+ * an early comparison).
+ */
+ index = i;
+ upper = i;
+ } else if (match > 0) {
+ if (isIncreasing) {
+ lower = i;
+ } else {
+ upper = i;
+ }
+ } else {
+ if (isIncreasing) {
+ upper = i;
+ } else {
+ lower = i;
+ }
+ }
+ }
+
+ } else {
+ /*
+ * We need to do a linear search, because (at least one) of:
+ * - our matcher can only tell equal vs. not equal
+ * - our matching sense is negated
+ * - we're building a list of all matched items
+ */
+ if (allMatches) {
+ listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ }
+ for (i = offset; i < listc; i++) {
+ match = 0;
+ switch ((enum modes) mode) {
+ case SORTED:
+ case EXACT:
+ switch ((enum datatypes) dataType) {
+ case ASCII:
+ bytes = Tcl_GetStringFromObj(listv[i], &elemLen);
+ if (length == elemLen) {
+ match = (memcmp(bytes, patternBytes,
+ (size_t) length) == 0);
+ }
+ break;
+ case DICTIONARY:
+ bytes = Tcl_GetString(listv[i]);
+ match = (DictionaryCompare(bytes, patternBytes) == 0);
+ break;
+ case INTEGER:
+ result = Tcl_GetIntFromObj(interp, listv[i], &objInt);
+ if (result != TCL_OK) {
+ if (listPtr) {
+ Tcl_DecrRefCount(listPtr);
+ }
+ return result;
+ }
+ match = (objInt == patInt);
+ break;
+ case REAL:
+ result = Tcl_GetDoubleFromObj(interp, listv[i],
+ &objDouble);
+ if (result != TCL_OK) {
+ if (listPtr) {
+ Tcl_DecrRefCount(listPtr);
+ }
+ return result;
+ }
+ match = (objDouble == patDouble);
+ break;
+ }
+ break;
+ case GLOB:
+ match = Tcl_StringMatch(Tcl_GetString(listv[i]),
+ patternBytes);
+ break;
+ case REGEXP:
match = Tcl_RegExpMatchObj(interp, listv[i], patObj);
if (match < 0) {
+ if (listPtr) {
+ Tcl_DecrRefCount(listPtr);
+ }
return TCL_ERROR;
}
break;
}
+ /*
+ * Invert match condition for -not
+ */
+ if (negatedMatch) {
+ match = !match;
+ }
+ if (match != 0) {
+ if (!allMatches) {
+ index = i;
+ break;
+ } else if (inlineReturn) {
+ /*
+ * Note that these appends are not expected to fail.
+ */
+ Tcl_ListObjAppendElement(interp, listPtr, listv[i]);
+ } else {
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewIntObj(i));
+ }
+ }
}
- if (match != 0) {
- index = i;
- break;
- }
}
- Tcl_SetIntObj(Tcl_GetObjResult(interp), index);
+
+ /*
+ * Return everything or a single value.
+ */
+ if (allMatches) {
+ Tcl_SetObjResult(interp, listPtr);
+ } else if (!inlineReturn) {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), index);
+ } else if (index < 0) {
+ /*
+ * Is this superfluous? The result should be a blank object
+ * by default...
+ */
+ Tcl_SetObjResult(interp, Tcl_NewObj());
+ } else {
+ Tcl_SetObjResult(interp, listv[index]);
+ }
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
+ * Tcl_LsetObjCmd --
+ *
+ * This procedure is invoked to process the "lset" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_LsetObjCmd( clientData, interp, objc, objv )
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument values. */
+{
+
+ Tcl_Obj* listPtr; /* Pointer to the list being altered. */
+ Tcl_Obj* finalValuePtr; /* Value finally assigned to the variable */
+
+ /* Check parameter count */
+
+ if ( objc < 3 ) {
+ Tcl_WrongNumArgs( interp, 1, objv, "listVar index ?index...? value" );
+ return TCL_ERROR;
+ }
+
+ /* Look up the list variable's value */
+
+ listPtr = Tcl_ObjGetVar2( interp, objv[ 1 ], (Tcl_Obj*) NULL,
+ TCL_LEAVE_ERR_MSG );
+ if ( listPtr == NULL ) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Substitute the value in the value. Return either the value or
+ * else an unshared copy of it.
+ */
+
+ if ( objc == 4 ) {
+ finalValuePtr = TclLsetList( interp, listPtr,
+ objv[ 2 ], objv[ 3 ] );
+ } else {
+ finalValuePtr = TclLsetFlat( interp, listPtr,
+ objc-3, objv+2, objv[ objc-1 ] );
+ }
+
+ /*
+ * If substitution has failed, bail out.
+ */
+
+ if ( finalValuePtr == NULL ) {
+ return TCL_ERROR;
+ }
+
+ /* Finally, update the variable so that traces fire. */
+
+ listPtr = Tcl_ObjSetVar2( interp, objv[1], NULL, finalValuePtr,
+ TCL_LEAVE_ERR_MSG );
+ Tcl_DecrRefCount( finalValuePtr );
+ if ( listPtr == NULL ) {
+ return TCL_ERROR;
+ }
+
+ /* Return the new value of the variable as the interpreter result. */
+
+ Tcl_SetObjResult( interp, listPtr );
+ return TCL_OK;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_LsortObjCmd --
*
* This procedure is invoked to process the "lsort" Tcl command.
@@ -2516,7 +3249,7 @@ Tcl_LsortObjCmd(clientData, interp, objc, objv)
SortInfo sortInfo; /* Information about this sort that
* needs to be passed to the
* comparison function */
- static char *switches[] = {
+ static CONST char *switches[] = {
"-ascii", "-command", "-decreasing", "-dictionary", "-increasing",
"-index", "-integer", "-real", "-unique", (char *) NULL
};
@@ -2533,7 +3266,7 @@ Tcl_LsortObjCmd(clientData, interp, objc, objv)
sortInfo.isIncreasing = 1;
sortInfo.sortMode = SORTMODE_ASCII;
- sortInfo.index = -1;
+ sortInfo.index = SORTIDX_NONE;
sortInfo.interp = interp;
sortInfo.resultCode = TCL_OK;
cmdPtr = NULL;
@@ -2574,11 +3307,10 @@ Tcl_LsortObjCmd(clientData, interp, objc, objv)
-1);
return TCL_ERROR;
}
- if (TclGetIntForIndex(interp, objv[i+1], -2, &sortInfo.index)
- != TCL_OK) {
+ if (TclGetIntForIndex(interp, objv[i+1], SORTIDX_END,
+ &sortInfo.index) != TCL_OK) {
return TCL_ERROR;
}
- cmdPtr = objv[i+1];
i++;
break;
case 6: /* -integer */
@@ -2616,12 +3348,9 @@ Tcl_LsortObjCmd(clientData, interp, objc, objv)
sortInfo.resultCode = Tcl_ListObjGetElements(interp, objv[objc-1],
&length, &listObjPtrs);
- if (sortInfo.resultCode != TCL_OK) {
+ if (sortInfo.resultCode != TCL_OK || length <= 0) {
goto done;
}
- if (length <= 0) {
- return TCL_OK;
- }
elementArray = (SortElement *) ckalloc(length * sizeof(SortElement));
for (i=0; i < length; i++){
elementArray[i].objPtr = listObjPtrs[i];
@@ -2832,20 +3561,20 @@ SortCompare(objPtr1, objPtr2, infoPtr)
return order;
}
- if (infoPtr->index != -1) {
+ if (infoPtr->index != SORTIDX_NONE) {
/*
* The "-index" option was specified. Treat each object as a
* list, extract the requested element from each list, and
- * compare the elements, not the lists. The special index "end"
- * is signaled here with a large negative index.
+ * compare the elements, not the lists. "end"-relative indices
+ * are signaled here with large negative values.
*/
if (Tcl_ListObjLength(infoPtr->interp, objPtr1, &listLen) != TCL_OK) {
infoPtr->resultCode = TCL_ERROR;
return order;
}
- if (infoPtr->index < -1) {
- index = listLen - 1;
+ if (infoPtr->index < SORTIDX_NONE) {
+ index = listLen + infoPtr->index + 1;
} else {
index = infoPtr->index;
}
@@ -2871,8 +3600,8 @@ SortCompare(objPtr1, objPtr2, infoPtr)
infoPtr->resultCode = TCL_ERROR;
return order;
}
- if (infoPtr->index < -1) {
- index = listLen - 1;
+ if (infoPtr->index < SORTIDX_NONE) {
+ index = listLen + infoPtr->index + 1;
} else {
index = infoPtr->index;
}
@@ -3097,4 +3826,3 @@ DictionaryCompare(left, right)
}
return diff;
}
-
diff --git a/tcl/generic/tclCmdMZ.c b/tcl/generic/tclCmdMZ.c
index abc7a30d822..d13bc8e5914 100644
--- a/tcl/generic/tclCmdMZ.c
+++ b/tcl/generic/tclCmdMZ.c
@@ -8,7 +8,8 @@
*
* Copyright (c) 1987-1993 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
- * Copyright (c) 1998-1999 by Scriptics Corporation.
+ * Copyright (c) 1998-2000 Scriptics Corporation.
+ * Copyright (c) 2002 ActiveState Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -18,50 +19,110 @@
#include "tclInt.h"
#include "tclPort.h"
-#include "tclCompile.h"
#include "tclRegexp.h"
/*
- * Flag values used by Tcl_ScanObjCmd.
+ * Structure used to hold information about variable traces:
*/
-#define SCAN_NOSKIP 0x1 /* Don't skip blanks. */
-#define SCAN_SUPPRESS 0x2 /* Suppress assignment. */
-#define SCAN_UNSIGNED 0x4 /* Read an unsigned value. */
-#define SCAN_WIDTH 0x8 /* A width value was supplied. */
-
-#define SCAN_SIGNOK 0x10 /* A +/- character is allowed. */
-#define SCAN_NODIGITS 0x20 /* No digits have been scanned. */
-#define SCAN_NOZERO 0x40 /* No zero digits have been scanned. */
-#define SCAN_XOK 0x80 /* An 'x' is allowed. */
-#define SCAN_PTOK 0x100 /* Decimal point is allowed. */
-#define SCAN_EXPOK 0x200 /* An exponent is allowed. */
+typedef struct {
+ int flags; /* Operations for which Tcl command is
+ * to be invoked. */
+ size_t length; /* Number of non-NULL chars. in command. */
+ char command[4]; /* Space for Tcl command to invoke. Actual
+ * size will be as large as necessary to
+ * hold command. This field must be the
+ * last in the structure, so that it can
+ * be larger than 4 bytes. */
+} TraceVarInfo;
/*
- * Structure used to hold information about variable traces:
+ * Structure used to hold information about command traces:
*/
typedef struct {
int flags; /* Operations for which Tcl command is
* to be invoked. */
- char *errMsg; /* Error message returned from Tcl command,
- * or NULL. Malloc'ed. */
size_t length; /* Number of non-NULL chars. in command. */
+ Tcl_Trace stepTrace; /* Used for execution traces, when tracing
+ * inside the given command */
+ int startLevel; /* Used for bookkeeping with execution traces */
+ int curFlags; /* Trace flags for the current command */
+ int curCode; /* Return code for the current command */
char command[4]; /* Space for Tcl command to invoke. Actual
* size will be as large as necessary to
* hold command. This field must be the
* last in the structure, so that it can
* be larger than 4 bytes. */
-} TraceVarInfo;
+} TraceCommandInfo;
+
+/*
+ * Used by command execution traces. Note that we assume in the code
+ * that the first two defines are exactly 4 times the
+ * 'TCL_TRACE_ENTER_EXEC' and 'TCL_TRACE_LEAVE_EXEC' constants.
+ *
+ * TCL_TRACE_ENTER_DURING_EXEC - Trace each command inside the command
+ * currently being traced, before execution.
+ * TCL_TRACE_LEAVE_DURING_EXEC - Trace each command inside the command
+ * currently being traced, after execution.
+ * TCL_TRACE_ANY_EXEC - OR'd combination of all EXEC flags.
+ * TCL_TRACE_EXEC_IN_PROGRESS - The callback procedure on this trace
+ * is currently executing. Therefore we
+ * don't let further traces execute.
+ * TCL_TRACE_EXEC_DIRECT - This execution trace is triggered directly
+ * by the command being traced, not because
+ * of an internal trace.
+ * The flags 'TCL_TRACE_DESTROYED' and 'TCL_INTERP_DESTROYED' may also
+ * be used in command execution traces.
+ */
+#define TCL_TRACE_ENTER_DURING_EXEC 4
+#define TCL_TRACE_LEAVE_DURING_EXEC 8
+#define TCL_TRACE_ANY_EXEC 15
+#define TCL_TRACE_EXEC_IN_PROGRESS 0x10
+#define TCL_TRACE_EXEC_DIRECT 0x20
/*
* Forward declarations for procedures defined in this file:
*/
+typedef int (Tcl_TraceTypeObjCmd) _ANSI_ARGS_((Tcl_Interp *interp,
+ int optionIndex, int objc, Tcl_Obj *CONST objv[]));
+
+Tcl_TraceTypeObjCmd TclTraceVariableObjCmd;
+Tcl_TraceTypeObjCmd TclTraceCommandObjCmd;
+Tcl_TraceTypeObjCmd TclTraceExecutionObjCmd;
+
+/*
+ * Each subcommand has a number of 'types' to which it can apply.
+ * Currently 'execution', 'command' and 'variable' are the only
+ * types supported. These three arrays MUST be kept in sync!
+ * In the future we may provide an API to add to the list of
+ * supported trace types.
+ */
+static CONST char *traceTypeOptions[] = {
+ "execution", "command", "variable", (char*) NULL
+};
+static Tcl_TraceTypeObjCmd* traceSubCmds[] = {
+ TclTraceExecutionObjCmd,
+ TclTraceCommandObjCmd,
+ TclTraceVariableObjCmd,
+};
+
+/*
+ * Declarations for local procedures to this file:
+ */
+static int CallTraceProcedure _ANSI_ARGS_((Tcl_Interp *interp,
+ Trace *tracePtr, Command *cmdPtr,
+ CONST char *command, int numChars,
+ int objc, Tcl_Obj *CONST objv[]));
static char * TraceVarProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, char *name1, char *name2,
- int flags));
-
+ Tcl_Interp *interp, CONST char *name1,
+ CONST char *name2, int flags));
+static void TraceCommandProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, CONST char *oldName,
+ CONST char *newName, int flags));
+static Tcl_CmdObjTraceProc TraceExecutionProc;
+
/*
*----------------------------------------------------------------------
*
@@ -87,17 +148,19 @@ Tcl_PwdObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- Tcl_DString ds;
+ Tcl_Obj *retVal;
if (objc != 1) {
Tcl_WrongNumArgs(interp, 1, objv, NULL);
return TCL_ERROR;
}
- if (Tcl_GetCwd(interp, &ds) == NULL) {
+ retVal = Tcl_FSGetCwd(interp);
+ if (retVal == NULL) {
return TCL_ERROR;
}
- Tcl_DStringResult(interp, &ds);
+ Tcl_SetObjResult(interp, retVal);
+ Tcl_DecrRefCount(retVal);
return TCL_OK;
}
@@ -131,7 +194,7 @@ Tcl_RegexpObjCmd(dummy, interp, objc, objv)
Tcl_RegExp regExpr;
Tcl_Obj *objPtr, *resultPtr;
Tcl_RegExpInfo info;
- static char *options[] = {
+ static CONST char *options[] = {
"-all", "-about", "-indices", "-inline",
"-expanded", "-line", "-linestop", "-lineanchor",
"-nocase", "-start", "--", (char *) NULL
@@ -235,19 +298,30 @@ Tcl_RegexpObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
- regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
- if (regExpr == NULL) {
- return TCL_ERROR;
- }
- objPtr = objv[1];
-
+ /*
+ * Handle the odd about case separately.
+ */
if (about) {
- if (TclRegAbout(interp, regExpr) < 0) {
+ regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
+ if ((regExpr == NULL) || (TclRegAbout(interp, regExpr) < 0)) {
return TCL_ERROR;
}
return TCL_OK;
}
+ /*
+ * Get the length of the string that we are matching against so
+ * we can do the termination test for -all matches. Do this before
+ * getting the regexp to avoid shimmering problems.
+ */
+ objPtr = objv[1];
+ stringLength = Tcl_GetCharLength(objPtr);
+
+ regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
+ if (regExpr == NULL) {
+ return TCL_ERROR;
+ }
+
if (offset > 0) {
/*
* Add flag if using offset (string is part of a larger string),
@@ -275,12 +349,6 @@ Tcl_RegexpObjCmd(dummy, interp, objc, objv)
}
/*
- * Get the length of the string that we are matching against so
- * we can do the termination test for -all matches.
- */
- stringLength = Tcl_GetCharLength(objPtr);
-
- /*
* The following loop is to handle multiple matches within the
* same source string; each iteration handles one match. If "-all"
* hasn't been specified then the loop body only gets executed once.
@@ -337,7 +405,11 @@ Tcl_RegexpObjCmd(dummy, interp, objc, objv)
int start, end;
Tcl_Obj *objs[2];
- if (i <= info.nsubs) {
+ /*
+ * Only adjust the match area if there was a match for
+ * that area. (Scriptics Bug 4391/SF Bug #219232)
+ */
+ if (i <= info.nsubs && info.matches[i].start >= 0) {
start = offset + info.matches[i].start;
end = offset + info.matches[i].end;
@@ -402,6 +474,7 @@ Tcl_RegexpObjCmd(dummy, interp, objc, objv)
}
offset += info.matches[0].end;
all++;
+ eflags |= TCL_REG_NOTBOL;
if (offset >= stringLength) {
break;
}
@@ -411,9 +484,12 @@ Tcl_RegexpObjCmd(dummy, interp, objc, objv)
* Set the interpreter's object result to an integer object
* with value 1 if -all wasn't specified, otherwise it's all-1
* (the number of times through the while - 1).
+ * Get the resultPtr again as the Tcl_ObjSetVar2 above may have
+ * cause the result to change. [Patch #558324] (watson).
*/
if (!doinline) {
+ resultPtr = Tcl_GetObjResult(interp);
Tcl_SetIntObj(resultPtr, (all ? all-1 : 1));
}
return TCL_OK;
@@ -444,13 +520,14 @@ Tcl_RegsubObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- int i, result, cflags, all, wlen, numMatches, offset;
+ int idx, result, cflags, all, wlen, wsublen, numMatches, offset;
+ int start, end, subStart, subEnd, match;
Tcl_RegExp regExpr;
- Tcl_Obj *resultPtr, *varPtr, *objPtr;
- Tcl_UniChar *wstring;
- char *subspec;
+ Tcl_RegExpInfo info;
+ Tcl_Obj *resultPtr, *subPtr, *objPtr;
+ Tcl_UniChar ch, *wsrc, *wfirstChar, *wstring, *wsubspec, *wend;
- static char *options[] = {
+ static CONST char *options[] = {
"-all", "-nocase", "-expanded",
"-line", "-linestop", "-lineanchor", "-start",
"--", NULL
@@ -464,17 +541,18 @@ Tcl_RegsubObjCmd(dummy, interp, objc, objv)
cflags = TCL_REG_ADVANCED;
all = 0;
offset = 0;
+ resultPtr = NULL;
- for (i = 1; i < objc; i++) {
+ for (idx = 1; idx < objc; idx++) {
char *name;
int index;
- name = Tcl_GetString(objv[i]);
+ name = Tcl_GetString(objv[idx]);
if (name[0] != '-') {
break;
}
- if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,
- &index) != TCL_OK) {
+ if (Tcl_GetIndexFromObj(interp, objv[idx], options, "switch",
+ TCL_EXACT, &index) != TCL_OK) {
return TCL_ERROR;
}
switch ((enum options) index) {
@@ -503,10 +581,10 @@ Tcl_RegsubObjCmd(dummy, interp, objc, objv)
break;
}
case REGSUB_START: {
- if (++i >= objc) {
+ if (++idx >= objc) {
goto endOfForLoop;
}
- if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {
+ if (Tcl_GetIntFromObj(interp, objv[idx], &offset) != TCL_OK) {
return TCL_ERROR;
}
if (offset < 0) {
@@ -515,34 +593,117 @@ Tcl_RegsubObjCmd(dummy, interp, objc, objv)
break;
}
case REGSUB_LAST: {
- i++;
+ idx++;
goto endOfForLoop;
}
}
}
endOfForLoop:
- if (objc - i != 4) {
+ if (objc-idx < 3 || objc-idx > 4) {
Tcl_WrongNumArgs(interp, 1, objv,
- "?switches? exp string subSpec varName");
+ "?switches? exp string subSpec ?varName?");
return TCL_ERROR;
}
- objv += i;
+ objc -= idx;
+ objv += idx;
+
+ if (all && (offset == 0)
+ && (strpbrk(Tcl_GetString(objv[2]), "&\\") == NULL)
+ && (strpbrk(Tcl_GetString(objv[0]), "*+?{}()[].\\|^$") == NULL)) {
+ /*
+ * This is a simple one pair string map situation. We make use of
+ * a slightly modified version of the one pair STR_MAP code.
+ */
+ int slen, nocase;
+ int (*strCmpFn)_ANSI_ARGS_((CONST Tcl_UniChar *, CONST Tcl_UniChar *,
+ unsigned long));
+ Tcl_UniChar *p, wsrclc;
+
+ numMatches = 0;
+ nocase = (cflags & TCL_REG_NOCASE);
+ strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
+
+ wsrc = Tcl_GetUnicodeFromObj(objv[0], &slen);
+ wstring = Tcl_GetUnicodeFromObj(objv[1], &wlen);
+ wsubspec = Tcl_GetUnicodeFromObj(objv[2], &wsublen);
+ wend = wstring + wlen - (slen ? slen - 1 : 0);
+ result = TCL_OK;
+
+ if (slen == 0) {
+ /*
+ * regsub behavior for "" matches between each character.
+ * 'string map' skips the "" case.
+ */
+ if (wstring < wend) {
+ resultPtr = Tcl_NewUnicodeObj(wstring, 0);
+ Tcl_IncrRefCount(resultPtr);
+ for (; wstring < wend; wstring++) {
+ Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen);
+ Tcl_AppendUnicodeToObj(resultPtr, wstring, 1);
+ numMatches++;
+ }
+ wlen = 0;
+ }
+ } else {
+ wsrclc = Tcl_UniCharToLower(*wsrc);
+ for (p = wfirstChar = wstring; wstring < wend; wstring++) {
+ if (((*wstring == *wsrc) ||
+ (nocase && (Tcl_UniCharToLower(*wstring) ==
+ wsrclc))) &&
+ ((slen == 1) || (strCmpFn(wstring, wsrc,
+ (unsigned long) slen) == 0))) {
+ if (numMatches == 0) {
+ resultPtr = Tcl_NewUnicodeObj(wstring, 0);
+ Tcl_IncrRefCount(resultPtr);
+ }
+ if (p != wstring) {
+ Tcl_AppendUnicodeToObj(resultPtr, p, wstring - p);
+ p = wstring + slen;
+ } else {
+ p += slen;
+ }
+ wstring = p - 1;
+
+ Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen);
+ numMatches++;
+ }
+ }
+ if (numMatches) {
+ wlen = wfirstChar + wlen - p;
+ wstring = p;
+ }
+ }
+ objPtr = NULL;
+ subPtr = NULL;
+ goto regsubDone;
+ }
regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
if (regExpr == NULL) {
return TCL_ERROR;
}
- result = TCL_OK;
- resultPtr = Tcl_NewObj();
- Tcl_IncrRefCount(resultPtr);
+ /*
+ * Make sure to avoid problems where the objects are shared. This
+ * can cause RegExpObj <> UnicodeObj shimmering that causes data
+ * corruption. [Bug #461322]
+ */
- objPtr = objv[1];
- wlen = Tcl_GetCharLength(objPtr);
- wstring = Tcl_GetUnicode(objPtr);
- subspec = Tcl_GetString(objv[2]);
- varPtr = objv[3];
+ if (objv[1] == objv[0]) {
+ objPtr = Tcl_DuplicateObj(objv[1]);
+ } else {
+ objPtr = objv[1];
+ }
+ wstring = Tcl_GetUnicodeFromObj(objPtr, &wlen);
+ if (objv[2] == objv[0]) {
+ subPtr = Tcl_DuplicateObj(objv[2]);
+ } else {
+ subPtr = objv[2];
+ }
+ wsubspec = Tcl_GetUnicodeFromObj(subPtr, &wsublen);
+
+ result = TCL_OK;
/*
* The following loop is to handle multiple matches within the
@@ -553,10 +714,6 @@ Tcl_RegsubObjCmd(dummy, interp, objc, objv)
numMatches = 0;
for ( ; offset < wlen; ) {
- int start, end, subStart, subEnd, match;
- char *src, *firstChar;
- char c;
- Tcl_RegExpInfo info;
/*
* The flags argument is set if string is part of a larger string,
@@ -573,11 +730,16 @@ Tcl_RegsubObjCmd(dummy, interp, objc, objv)
if (match == 0) {
break;
}
- if ((numMatches == 0) && (offset > 0)) {
- /* Copy the initial portion of the string in if an offset
- * was specified.
- */
- Tcl_AppendUnicodeToObj(resultPtr, wstring, offset);
+ if (numMatches == 0) {
+ resultPtr = Tcl_NewUnicodeObj(wstring, 0);
+ Tcl_IncrRefCount(resultPtr);
+ if (offset > 0) {
+ /*
+ * Copy the initial portion of the string in if an offset
+ * was specified.
+ */
+ Tcl_AppendUnicodeToObj(resultPtr, wstring, offset);
+ }
}
numMatches++;
@@ -598,22 +760,22 @@ Tcl_RegsubObjCmd(dummy, interp, objc, objv)
* subSpec to reduce the number of calls to Tcl_SetVar.
*/
- src = subspec;
- firstChar = subspec;
- for (c = *src; c != '\0'; src++, c = *src) {
- int index;
-
- if (c == '&') {
- index = 0;
- } else if (c == '\\') {
- c = src[1];
- if ((c >= '0') && (c <= '9')) {
- index = c - '0';
- } else if ((c == '\\') || (c == '&')) {
- Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);
- Tcl_AppendToObj(resultPtr, &c, 1);
- firstChar = src + 2;
- src++;
+ wsrc = wfirstChar = wsubspec;
+ wend = wsubspec + wsublen;
+ for (ch = *wsrc; wsrc != wend; wsrc++, ch = *wsrc) {
+ if (ch == '&') {
+ idx = 0;
+ } else if (ch == '\\') {
+ ch = wsrc[1];
+ if ((ch >= '0') && (ch <= '9')) {
+ idx = ch - '0';
+ } else if ((ch == '\\') || (ch == '&')) {
+ *wsrc = ch;
+ Tcl_AppendUnicodeToObj(resultPtr, wfirstChar,
+ wsrc - wfirstChar + 1);
+ *wsrc = '\\';
+ wfirstChar = wsrc + 2;
+ wsrc++;
continue;
} else {
continue;
@@ -621,24 +783,25 @@ Tcl_RegsubObjCmd(dummy, interp, objc, objv)
} else {
continue;
}
- if (firstChar != src) {
- Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);
+ if (wfirstChar != wsrc) {
+ Tcl_AppendUnicodeToObj(resultPtr, wfirstChar,
+ wsrc - wfirstChar);
}
- if (index <= info.nsubs) {
- subStart = info.matches[index].start;
- subEnd = info.matches[index].end;
+ if (idx <= info.nsubs) {
+ subStart = info.matches[idx].start;
+ subEnd = info.matches[idx].end;
if ((subStart >= 0) && (subEnd >= 0)) {
Tcl_AppendUnicodeToObj(resultPtr,
wstring + offset + subStart, subEnd - subStart);
}
}
- if (*src == '\\') {
- src++;
+ if (*wsrc == '\\') {
+ wsrc++;
}
- firstChar = src + 1;
+ wfirstChar = wsrc + 1;
}
- if (firstChar != src) {
- Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);
+ if (wfirstChar != wsrc) {
+ Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, wsrc - wfirstChar);
}
if (end == 0) {
/*
@@ -648,8 +811,9 @@ Tcl_RegsubObjCmd(dummy, interp, objc, objv)
Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
offset++;
+ } else {
+ offset += end;
}
- offset += end;
if (!all) {
break;
}
@@ -659,31 +823,41 @@ Tcl_RegsubObjCmd(dummy, interp, objc, objv)
* Copy the portion of the source string after the last match to the
* result variable.
*/
-
+ regsubDone:
if (numMatches == 0) {
/*
* On zero matches, just ignore the offset, since it shouldn't
* matter to us in this case, and the user may have skewed it.
*/
- Tcl_AppendUnicodeToObj(resultPtr, wstring, wlen);
+ resultPtr = objv[1];
+ Tcl_IncrRefCount(resultPtr);
} else if (offset < wlen) {
Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset);
}
- if (Tcl_ObjSetVar2(interp, varPtr, NULL, resultPtr, 0) == NULL) {
- Tcl_AppendResult(interp, "couldn't set variable \"",
- Tcl_GetString(varPtr), "\"", (char *) NULL);
- result = TCL_ERROR;
+ if (objc == 4) {
+ if (Tcl_ObjSetVar2(interp, objv[3], NULL, resultPtr, 0) == NULL) {
+ Tcl_AppendResult(interp, "couldn't set variable \"",
+ Tcl_GetString(objv[3]), "\"", (char *) NULL);
+ result = TCL_ERROR;
+ } else {
+ /*
+ * Set the interpreter's object result to an integer object
+ * holding the number of matches.
+ */
+
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), numMatches);
+ }
} else {
/*
- * Set the interpreter's object result to an integer object holding the
- * number of matches.
+ * No varname supplied, so just return the modified string.
*/
-
- Tcl_SetIntObj(Tcl_GetObjResult(interp), numMatches);
+ Tcl_SetObjResult(interp, resultPtr);
}
done:
- Tcl_DecrRefCount(resultPtr);
+ if (objPtr && (objv[1] == objv[0])) { Tcl_DecrRefCount(objPtr); }
+ if (subPtr && (objv[2] == objv[0])) { Tcl_DecrRefCount(subPtr); }
+ if (resultPtr) { Tcl_DecrRefCount(resultPtr); }
return result;
}
@@ -845,17 +1019,12 @@ Tcl_SourceObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *bytes;
- int result;
-
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "fileName");
return TCL_ERROR;
}
- bytes = Tcl_GetString(objv[1]);
- result = Tcl_EvalFile(interp, bytes);
- return result;
+ return Tcl_FSEvalFile(interp, objv[1]);
}
/*
@@ -908,15 +1077,34 @@ Tcl_SplitObjCmd(dummy, interp, objc, objv)
* Do nothing.
*/
} else if (splitCharLen == 0) {
+ Tcl_HashTable charReuseTable;
+ Tcl_HashEntry *hPtr;
+ int isNew;
+
/*
* Handle the special case of splitting on every character.
+ *
+ * Uses a hash table to ensure that each kind of character has
+ * only one Tcl_Obj instance (multiply-referenced) in the
+ * final list. This is a *major* win when splitting on a long
+ * string (especially in the megabyte range!) - DKF
*/
+ Tcl_InitHashTable(&charReuseTable, TCL_ONE_WORD_KEYS);
for ( ; string < end; string += len) {
len = Tcl_UtfToUniChar(string, &ch);
- objPtr = Tcl_NewStringObj(string, len);
+ /* Assume Tcl_UniChar is an integral type... */
+ hPtr = Tcl_CreateHashEntry(&charReuseTable, (char*)0 + ch, &isNew);
+ if (isNew) {
+ objPtr = Tcl_NewStringObj(string, len);
+ /* Don't need to fiddle with refcount... */
+ Tcl_SetHashValue(hPtr, (ClientData) objPtr);
+ } else {
+ objPtr = (Tcl_Obj*) Tcl_GetHashValue(hPtr);
+ }
Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
}
+ Tcl_DeleteHashTable(&charReuseTable);
} else {
char *element, *p, *splitEnd;
int splitLen;
@@ -957,6 +1145,11 @@ Tcl_SplitObjCmd(dummy, interp, objc, objv)
* that this command only functions correctly on properly formed
* Tcl UTF strings.
*
+ * Note that the primary methods here (equal, compare, match, ...)
+ * have bytecode equivalents. You will find the code for those in
+ * tclExecute.c. The code here will only be used in the non-bc
+ * case (like in an 'eval').
+ *
* Results:
* A standard Tcl result.
*
@@ -978,7 +1171,7 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
Tcl_Obj *resultPtr;
char *string1, *string2;
int length1, length2;
- static char *options[] = {
+ static CONST char *options[] = {
"bytelength", "compare", "equal", "first",
"index", "is", "last", "length",
"map", "match", "range", "repeat",
@@ -1009,7 +1202,14 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
switch ((enum options) index) {
case STR_EQUAL:
case STR_COMPARE: {
+ /*
+ * Remember to keep code here in some sync with the
+ * byte-compiled versions in tclExecute.c (INST_STR_EQ,
+ * INST_STR_NEQ and INST_STR_CMP as well as the expr string
+ * comparison in INST_EQ/INST_NEQ/INST_LT/...).
+ */
int i, match, length, nocase = 0, reqlength = -1;
+ int (*strCmpFn)();
if (objc < 4 || objc > 7) {
str_cmp_args:
@@ -1021,10 +1221,10 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
for (i = 2; i < objc-2; i++) {
string2 = Tcl_GetStringFromObj(objv[i], &length2);
if ((length2 > 1)
- && strncmp(string2, "-nocase", (size_t) length2) == 0) {
+ && strncmp(string2, "-nocase", (size_t)length2) == 0) {
nocase = 1;
} else if ((length2 > 1)
- && strncmp(string2, "-length", (size_t) length2) == 0) {
+ && strncmp(string2, "-length", (size_t)length2) == 0) {
if (i+1 >= objc-2) {
goto str_cmp_args;
}
@@ -1040,58 +1240,80 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
}
}
- string1 = Tcl_GetStringFromObj(objv[objc-2], &length1);
- string2 = Tcl_GetStringFromObj(objv[objc-1], &length2);
/*
- * This is the min length IN BYTES of the two strings
+ * From now on, we only access the two objects at the end
+ * of the argument array.
*/
- length = (length1 < length2) ? length1 : length2;
+ objv += objc-2;
- if (reqlength == 0) {
+ if ((reqlength == 0) || (objv[0] == objv[1])) {
/*
- * Anything matches at 0 chars, right?
+ * Alway match at 0 chars of if it is the same obj.
*/
- match = 0;
- } else if (nocase || ((reqlength > 0) && (reqlength <= length))) {
+ Tcl_SetBooleanObj(resultPtr,
+ ((enum options) index == STR_EQUAL));
+ break;
+ } else if (!nocase && objv[0]->typePtr == &tclByteArrayType &&
+ objv[1]->typePtr == &tclByteArrayType) {
/*
- * with -nocase or -length we have to check true char length
- * as it could be smaller than expected
+ * Use binary versions of comparisons since that won't
+ * cause undue type conversions and it is much faster.
+ * Only do this if we're case-sensitive (which is all
+ * that really makes sense with byte arrays anyway, and
+ * we have no memcasecmp() for some reason... :^)
*/
-
- length1 = Tcl_NumUtfChars(string1, length1);
- length2 = Tcl_NumUtfChars(string2, length2);
- length = (length1 < length2) ? length1 : length2;
-
+ string1 = (char*) Tcl_GetByteArrayFromObj(objv[0], &length1);
+ string2 = (char*) Tcl_GetByteArrayFromObj(objv[1], &length2);
+ strCmpFn = memcmp;
+ } else if ((objv[0]->typePtr == &tclStringType)
+ && (objv[1]->typePtr == &tclStringType)) {
/*
- * Do the reqlength check again, against 0 as well for
- * the benfit of nocase
+ * Do a unicode-specific comparison if both of the args
+ * are of String type. In benchmark testing this proved
+ * the most efficient check between the unicode and
+ * string comparison operations.
*/
+ string1 = (char*) Tcl_GetUnicodeFromObj(objv[0], &length1);
+ string2 = (char*) Tcl_GetUnicodeFromObj(objv[1], &length2);
+ strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
+ } else {
+ /*
+ * As a catch-all we will work with UTF-8. We cannot use
+ * memcmp() as that is unsafe with any string containing
+ * NULL (\xC0\x80 in Tcl's utf rep). We can use the more
+ * efficient TclpUtfNcmp2 if we are case-sensitive and no
+ * specific length was requested.
+ */
+ string1 = (char*) Tcl_GetStringFromObj(objv[0], &length1);
+ string2 = (char*) Tcl_GetStringFromObj(objv[1], &length2);
+ if ((reqlength < 0) && !nocase) {
+ strCmpFn = TclpUtfNcmp2;
+ } else {
+ length1 = Tcl_NumUtfChars(string1, length1);
+ length2 = Tcl_NumUtfChars(string2, length2);
+ strCmpFn = nocase ? Tcl_UtfNcasecmp : Tcl_UtfNcmp;
+ }
+ }
- if ((reqlength > 0) && (reqlength < length)) {
+ if (((enum options) index == STR_EQUAL)
+ && (reqlength < 0) && (length1 != length2)) {
+ match = 1; /* this will be reversed below */
+ } else {
+ length = (length1 < length2) ? length1 : length2;
+ if (reqlength > 0 && reqlength < length) {
length = reqlength;
} else if (reqlength < 0) {
/*
* The requested length is negative, so we ignore it by
- * setting it to the longer of the two lengths.
+ * setting it to length + 1 so we correct the match var.
*/
-
- reqlength = (length1 > length2) ? length1 : length2;
- }
- if (nocase) {
- match = Tcl_UtfNcasecmp(string1, string2,
- (unsigned) length);
- } else {
- match = Tcl_UtfNcmp(string1, string2, (unsigned) length);
+ reqlength = length + 1;
}
+ match = strCmpFn(string1, string2, (unsigned) length);
if ((match == 0) && (reqlength > length)) {
match = length1 - length2;
}
- } else {
- match = memcmp(string1, string2, (unsigned) length);
- if (match == 0) {
- match = length1 - length2;
- }
}
if ((enum options) index == STR_EQUAL) {
@@ -1103,91 +1325,79 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
break;
}
case STR_FIRST: {
- register char *p, *end;
- int match, utflen, start;
+ Tcl_UniChar *ustring1, *ustring2;
+ int match, start;
if (objc < 4 || objc > 5) {
Tcl_WrongNumArgs(interp, 2, objv,
- "string1 string2 ?startIndex?");
+ "subString string ?startIndex?");
return TCL_ERROR;
}
/*
- * This algorithm fails on improperly formed UTF strings.
* We are searching string2 for the sequence string1.
*/
match = -1;
start = 0;
- utflen = -1;
- string1 = Tcl_GetStringFromObj(objv[2], &length1);
- string2 = Tcl_GetStringFromObj(objv[3], &length2);
+ length2 = -1;
+
+ ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
+ ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);
if (objc == 5) {
/*
- * If a startIndex is specified, we will need to fast forward
- * to that point in the string before we think about a match
+ * If a startIndex is specified, we will need to fast
+ * forward to that point in the string before we think
+ * about a match
*/
- utflen = Tcl_NumUtfChars(string2, length2);
- if (TclGetIntForIndex(interp, objv[4], utflen-1,
- &start) != TCL_OK) {
+ if (TclGetIntForIndex(interp, objv[4], length2 - 1,
+ &start) != TCL_OK) {
return TCL_ERROR;
}
- if (start >= utflen) {
+ if (start >= length2) {
goto str_first_done;
} else if (start > 0) {
- if (length2 == utflen) {
- /* no unicode chars */
- string2 += start;
- length2 -= start;
- } else {
- char *s = Tcl_UtfAtIndex(string2, start);
- length2 -= s - string2;
- string2 = s;
- }
+ ustring2 += start;
+ length2 -= start;
+ } else if (start < 0) {
+ /*
+ * Invalid start index mapped to string start;
+ * Bug #423581
+ */
+ start = 0;
}
}
if (length1 > 0) {
- end = string2 + length2 - length1 + 1;
- for (p = string2; p < end; p++) {
+ register Tcl_UniChar *p, *end;
+
+ end = ustring2 + length2 - length1 + 1;
+ for (p = ustring2; p < end; p++) {
/*
* Scan forward to find the first character.
*/
-
- p = memchr(p, *string1, (unsigned) (end - p));
- if (p == NULL) {
- break;
- }
- if (memcmp(string1, p, (unsigned) length1) == 0) {
- match = p - string2;
+ if ((*p == *ustring1) &&
+ (TclUniCharNcmp(ustring1, p,
+ (unsigned long) length1) == 0)) {
+ match = p - ustring2;
break;
}
}
}
-
/*
* Compute the character index of the matching string by
* counting the number of characters before the match.
*/
- str_first_done:
- if (match != -1) {
- if (objc == 4) {
- match = Tcl_NumUtfChars(string2, match);
- } else if (length2 == utflen) {
- /* no unicode chars */
- match += start;
- } else {
- match = start + Tcl_NumUtfChars(string2, match);
- }
+ if ((match != -1) && (objc == 5)) {
+ match += start;
}
+
+ str_first_done:
Tcl_SetIntObj(resultPtr, match);
break;
}
case STR_INDEX: {
- char buf[TCL_UTF_MAX];
- Tcl_UniChar unichar;
-
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");
return TCL_ERROR;
@@ -1201,33 +1411,33 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
*/
if (objv[2]->typePtr == &tclByteArrayType) {
-
- string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);
+ string1 = (char *) Tcl_GetByteArrayFromObj(objv[2], &length1);
if (TclGetIntForIndex(interp, objv[3], length1 - 1,
&index) != TCL_OK) {
return TCL_ERROR;
}
- Tcl_SetByteArrayObj(resultPtr,
- (unsigned char *)(&string1[index]), 1);
+ if ((index >= 0) && (index < length1)) {
+ Tcl_SetByteArrayObj(resultPtr,
+ (unsigned char *)(&string1[index]), 1);
+ }
} else {
- string1 = Tcl_GetStringFromObj(objv[2], &length1);
-
/*
- * convert to Unicode internal rep to calulate what
- * 'end' really means.
+ * Get Unicode char length to calulate what 'end' means.
*/
+ length1 = Tcl_GetCharLength(objv[2]);
- length2 = Tcl_GetCharLength(objv[2]);
-
- if (TclGetIntForIndex(interp, objv[3], length2 - 1,
+ if (TclGetIntForIndex(interp, objv[3], length1 - 1,
&index) != TCL_OK) {
return TCL_ERROR;
}
- if ((index >= 0) && (index < length2)) {
- unichar = Tcl_GetUniChar(objv[2], index);
- length2 = Tcl_UniCharToUtf((int)unichar, buf);
- Tcl_SetStringObj(resultPtr, buf, length2);
+ if ((index >= 0) && (index < length1)) {
+ char buf[TCL_UTF_MAX];
+ Tcl_UniChar ch;
+
+ ch = Tcl_GetUniChar(objv[2], index);
+ length1 = Tcl_UniCharToUtf(ch, buf);
+ Tcl_SetStringObj(resultPtr, buf, length1);
}
}
break;
@@ -1244,7 +1454,7 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
int i, failat = 0, result = 1, strict = 0;
Tcl_Obj *objPtr, *failVarObj = NULL;
- static char *isOptions[] = {
+ static CONST char *isOptions[] = {
"alnum", "alpha", "ascii", "control",
"boolean", "digit", "double", "false",
"graph", "integer", "lower", "print",
@@ -1275,7 +1485,8 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
strncmp(string2, "-strict", (size_t) length2) == 0) {
strict = 1;
} else if ((length2 > 1) &&
- strncmp(string2, "-failindex", (size_t) length2) == 0) {
+ strncmp(string2, "-failindex",
+ (size_t) length2) == 0) {
if (i+1 >= objc-1) {
Tcl_WrongNumArgs(interp, 3, objv,
"?-strict? ?-failindex var? str");
@@ -1375,7 +1586,11 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
*/
if (TclLooksLikeInt(string1, length1)) {
errno = 0;
- strtoul(string1, &stop, 0);
+#ifdef TCL_WIDE_INT_IS_LONG
+ strtoul(string1, &stop, 0); /* INTL: Tcl source. */
+#else
+ strtoull(string1, &stop, 0); /* INTL: Tcl source. */
+#endif
if (stop == end) {
if (errno == ERANGE) {
result = 0;
@@ -1429,7 +1644,11 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
*/
result = 0;
errno = 0;
+#ifdef TCL_WIDE_INT_IS_LONG
strtoul(string1, &stop, 0); /* INTL: Tcl source. */
+#else
+ strtoull(string1, &stop, 0); /* INTL: Tcl source. */
+#endif
if (errno == ERANGE) {
/*
* if (errno == ERANGE), then it was an over/underflow
@@ -1508,78 +1727,61 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
break;
}
case STR_LAST: {
- register char *p;
- int match, utflen, start;
+ Tcl_UniChar *ustring1, *ustring2, *p;
+ int match, start;
if (objc < 4 || objc > 5) {
Tcl_WrongNumArgs(interp, 2, objv,
- "string1 string2 ?startIndex?");
+ "subString string ?startIndex?");
return TCL_ERROR;
}
/*
- * This algorithm fails on improperly formed UTF strings.
+ * We are searching string2 for the sequence string1.
*/
match = -1;
start = 0;
- utflen = -1;
- string1 = Tcl_GetStringFromObj(objv[2], &length1);
- string2 = Tcl_GetStringFromObj(objv[3], &length2);
+ length2 = -1;
+
+ ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
+ ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);
if (objc == 5) {
/*
* If a startIndex is specified, we will need to restrict
* the string range to that char index in the string
*/
- utflen = Tcl_NumUtfChars(string2, length2);
- if (TclGetIntForIndex(interp, objv[4], utflen-1,
- &start) != TCL_OK) {
+ if (TclGetIntForIndex(interp, objv[4], length2 - 1,
+ &start) != TCL_OK) {
return TCL_ERROR;
}
if (start < 0) {
goto str_last_done;
- } else if (start < utflen) {
- if (length2 == utflen) {
- /* no unicode chars */
- p = string2 + start + 1 - length1;
- } else {
- p = Tcl_UtfAtIndex(string2, start+1) - length1;
- }
+ } else if (start < length2) {
+ p = ustring2 + start + 1 - length1;
} else {
- p = string2 + length2 - length1;
+ p = ustring2 + length2 - length1;
}
} else {
- p = string2 + length2 - length1;
+ p = ustring2 + length2 - length1;
}
if (length1 > 0) {
- for (; p >= string2; p--) {
+ for (; p >= ustring2; p--) {
/*
* Scan backwards to find the first character.
*/
-
- while ((p != string2) && (*p != *string1)) {
- p--;
- }
- if (memcmp(string1, p, (unsigned) length1) == 0) {
- match = p - string2;
+ if ((*p == *ustring1) &&
+ (memcmp((char *) ustring1, (char *) p, (size_t)
+ (length1 * sizeof(Tcl_UniChar))) == 0)) {
+ match = p - ustring2;
break;
}
}
}
- /*
- * Compute the character index of the matching string by counting
- * the number of characters before the match.
- */
- str_last_done:
- if (match != -1) {
- if ((objc == 4) || (length2 != utflen)) {
- /* only check when we've got unicode chars */
- match = Tcl_NumUtfChars(string2, match);
- }
- }
+ str_last_done:
Tcl_SetIntObj(resultPtr, match);
break;
}
@@ -1592,7 +1794,6 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
if ((enum options) index == STR_BYTELENGTH) {
(void) Tcl_GetStringFromObj(objv[2], &length1);
- Tcl_SetIntObj(resultPtr, length1);
} else {
/*
* If we have a ByteArray object, avoid recomputing the
@@ -1603,20 +1804,19 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
if (objv[2]->typePtr == &tclByteArrayType) {
(void) Tcl_GetByteArrayFromObj(objv[2], &length1);
- Tcl_SetIntObj(resultPtr, length1);
} else {
- Tcl_SetIntObj(resultPtr,
- Tcl_GetCharLength(objv[2]));
+ length1 = Tcl_GetCharLength(objv[2]);
}
}
+ Tcl_SetIntObj(resultPtr, length1);
break;
}
case STR_MAP: {
- int uselen, mapElemc, len, nocase = 0;
+ int mapElemc, nocase = 0;
Tcl_Obj **mapElemv;
- char *end;
- Tcl_UniChar ch;
- int (*str_comp_fn)();
+ Tcl_UniChar *ustring1, *ustring2, *p, *end;
+ int (*strCmpFn)_ANSI_ARGS_((CONST Tcl_UniChar*,
+ CONST Tcl_UniChar*, unsigned long));
if (objc < 4 || objc > 5) {
Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? charMap string");
@@ -1645,6 +1845,7 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
* empty charMap, just return whatever string was given
*/
Tcl_SetObjResult(interp, objv[objc-1]);
+ return TCL_OK;
} else if (mapElemc & 1) {
/*
* The charMap must be an even number of key/value items
@@ -1652,63 +1853,131 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
Tcl_SetStringObj(resultPtr, "char map list unbalanced", -1);
return TCL_ERROR;
}
- string1 = Tcl_GetStringFromObj(objv[objc-1], &length1);
+ objc--;
+
+ ustring1 = Tcl_GetUnicodeFromObj(objv[objc], &length1);
if (length1 == 0) {
+ /*
+ * Empty input string, just stop now
+ */
break;
}
- end = string1 + length1;
+ end = ustring1 + length1;
- if (nocase) {
- length1 = Tcl_NumUtfChars(string1, length1);
- str_comp_fn = Tcl_UtfNcasecmp;
- } else {
- str_comp_fn = memcmp;
- }
+ strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
- for ( ; string1 < end; string1 += len) {
- len = Tcl_UtfToUniChar(string1, &ch);
- for (index = 0; index < mapElemc; index +=2) {
- /*
- * Get the key string to match on
- */
- string2 = Tcl_GetStringFromObj(mapElemv[index],
- &length2);
- if (nocase) {
- uselen = Tcl_NumUtfChars(string2, length2);
- } else {
- uselen = length2;
+ /*
+ * Force result to be Unicode
+ */
+ Tcl_SetUnicodeObj(resultPtr, ustring1, 0);
+
+ if (mapElemc == 2) {
+ /*
+ * Special case for one map pair which avoids the extra
+ * for loop and extra calls to get Unicode data. The
+ * algorithm is otherwise identical to the multi-pair case.
+ * This will be >30% faster on larger strings.
+ */
+ int mapLen;
+ Tcl_UniChar *mapString, u2lc;
+
+ ustring2 = Tcl_GetUnicodeFromObj(mapElemv[0], &length2);
+ p = ustring1;
+ if (length2 == 0) {
+ ustring1 = end;
+ } else {
+ mapString = Tcl_GetUnicodeFromObj(mapElemv[1], &mapLen);
+ u2lc = (nocase ? Tcl_UniCharToLower(*ustring2) : 0);
+ for (; ustring1 < end; ustring1++) {
+ if (((*ustring1 == *ustring2) ||
+ (nocase && (Tcl_UniCharToLower(*ustring1) ==
+ u2lc))) &&
+ ((length2 == 1) || strCmpFn(ustring1, ustring2,
+ (unsigned long) length2) == 0)) {
+ if (p != ustring1) {
+ Tcl_AppendUnicodeToObj(resultPtr, p,
+ ustring1 - p);
+ p = ustring1 + length2;
+ } else {
+ p += length2;
+ }
+ ustring1 = p - 1;
+
+ Tcl_AppendUnicodeToObj(resultPtr, mapString,
+ mapLen);
+ }
}
- if ((uselen > 0) && (uselen <= length1) &&
- (str_comp_fn(string2, string1, uselen) == 0)) {
- /*
- * Adjust len to be full length of matched string
- * it has to be the BYTE length
- */
- len = length2;
+ }
+ } else {
+ Tcl_UniChar **mapStrings, *u2lc = NULL;
+ int *mapLens;
+ /*
+ * Precompute pointers to the unicode string and length.
+ * This saves us repeated function calls later,
+ * significantly speeding up the algorithm. We only need
+ * the lowercase first char in the nocase case.
+ */
+ mapStrings = (Tcl_UniChar **) ckalloc((mapElemc * 2)
+ * sizeof(Tcl_UniChar *));
+ mapLens = (int *) ckalloc((mapElemc * 2) * sizeof(int));
+ if (nocase) {
+ u2lc = (Tcl_UniChar *)
+ ckalloc((mapElemc) * sizeof(Tcl_UniChar));
+ }
+ for (index = 0; index < mapElemc; index++) {
+ mapStrings[index] = Tcl_GetUnicodeFromObj(mapElemv[index],
+ &(mapLens[index]));
+ if (nocase && ((index % 2) == 0)) {
+ u2lc[index/2] = Tcl_UniCharToLower(*mapStrings[index]);
+ }
+ }
+ for (p = ustring1; ustring1 < end; ustring1++) {
+ for (index = 0; index < mapElemc; index += 2) {
/*
- * Change string2 and length2 to the map value
+ * Get the key string to match on.
*/
- string2 = Tcl_GetStringFromObj(mapElemv[index+1],
- &length2);
- Tcl_AppendToObj(resultPtr, string2, length2);
- break;
+ ustring2 = mapStrings[index];
+ length2 = mapLens[index];
+ if ((length2 > 0) && ((*ustring1 == *ustring2) ||
+ (nocase && (Tcl_UniCharToLower(*ustring1) ==
+ u2lc[index/2]))) &&
+ ((length2 == 1) || strCmpFn(ustring2, ustring1,
+ (unsigned long) length2) == 0)) {
+ if (p != ustring1) {
+ /*
+ * Put the skipped chars onto the result first
+ */
+ Tcl_AppendUnicodeToObj(resultPtr, p,
+ ustring1 - p);
+ p = ustring1 + length2;
+ } else {
+ p += length2;
+ }
+ /*
+ * Adjust len to be full length of matched string
+ */
+ ustring1 = p - 1;
+
+ /*
+ * Append the map value to the unicode string
+ */
+ Tcl_AppendUnicodeToObj(resultPtr,
+ mapStrings[index+1], mapLens[index+1]);
+ break;
+ }
}
}
- if (index == mapElemc) {
- /*
- * No match was found, put the char onto result
- */
- Tcl_AppendToObj(resultPtr, string1, len);
+ ckfree((char *) mapStrings);
+ ckfree((char *) mapLens);
+ if (nocase) {
+ ckfree((char *) u2lc);
}
+ }
+ if (p != ustring1) {
/*
- * in nocase, length1 is in chars
- * otherwise it is in bytes
+ * Put the rest of the unmapped chars onto result
*/
- if (nocase) {
- length1--;
- } else {
- length1 -= len;
- }
+ Tcl_AppendUnicodeToObj(resultPtr, p, ustring1 - p);
}
break;
}
@@ -1734,9 +2003,8 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
}
Tcl_SetBooleanObj(resultPtr,
- Tcl_StringCaseMatch(Tcl_GetString(objv[objc-1]),
- Tcl_GetString(objv[objc-2]),
- nocase));
+ Tcl_UniCharCaseMatch(Tcl_GetUnicode(objv[objc-1]),
+ Tcl_GetUnicode(objv[objc-2]), nocase));
break;
}
case STR_RANGE: {
@@ -1748,64 +2016,24 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
}
/*
- * If we have a ByteArray object, avoid indexing in the
- * Utf string since the byte array contains one byte per
- * character. Otherwise, use the Unicode string rep to
- * get the range.
+ * Get the length in actual characters.
*/
+ length1 = Tcl_GetCharLength(objv[2]) - 1;
- if (objv[2]->typePtr == &tclByteArrayType) {
-
- string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);
-
- if (TclGetIntForIndex(interp, objv[3], length1 - 1,
- &first) != TCL_OK) {
- return TCL_ERROR;
- }
- if (TclGetIntForIndex(interp, objv[4], length1 - 1,
- &last) != TCL_OK) {
- return TCL_ERROR;
- }
- if (first < 0) {
- first = 0;
- }
- if (last >= length1 - 1) {
- last = length1 - 1;
- }
- if (last >= first) {
- int numBytes = last - first + 1;
- resultPtr = Tcl_NewByteArrayObj(
- (unsigned char *) &string1[first], numBytes);
- Tcl_SetObjResult(interp, resultPtr);
- }
- } else {
- string1 = Tcl_GetStringFromObj(objv[2], &length1);
-
- /*
- * Convert to Unicode internal rep to calulate length and
- * create a result object.
- */
+ if ((TclGetIntForIndex(interp, objv[3], length1, &first) != TCL_OK)
+ || (TclGetIntForIndex(interp, objv[4], length1,
+ &last) != TCL_OK)) {
+ return TCL_ERROR;
+ }
- length2 = Tcl_GetCharLength(objv[2]) - 1;
-
- if (TclGetIntForIndex(interp, objv[3], length2,
- &first) != TCL_OK) {
- return TCL_ERROR;
- }
- if (TclGetIntForIndex(interp, objv[4], length2,
- &last) != TCL_OK) {
- return TCL_ERROR;
- }
- if (first < 0) {
- first = 0;
- }
- if (last >= length2) {
- last = length2;
- }
- if (last >= first) {
- resultPtr = Tcl_GetRange(objv[2], first, last);
- Tcl_SetObjResult(interp, resultPtr);
- }
+ if (first < 0) {
+ first = 0;
+ }
+ if (last >= length1) {
+ last = length1;
+ }
+ if (last >= first) {
+ Tcl_SetObjResult(interp, Tcl_GetRange(objv[2], first, last));
}
break;
}
@@ -1821,15 +2049,41 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
- string1 = Tcl_GetStringFromObj(objv[2], &length1);
- if (length1 > 0) {
- for (index = 0; index < count; index++) {
- Tcl_AppendToObj(resultPtr, string1, length1);
+ if (count == 1) {
+ Tcl_SetObjResult(interp, objv[2]);
+ } else if (count > 1) {
+ string1 = Tcl_GetStringFromObj(objv[2], &length1);
+ if (length1 > 0) {
+ /*
+ * Only build up a string that has data. Instead of
+ * building it up with repeated appends, we just allocate
+ * the necessary space once and copy the string value in.
+ */
+ length2 = length1 * count;
+ /*
+ * Include space for the NULL
+ */
+ string2 = (char *) ckalloc((size_t) length2+1);
+ for (index = 0; index < count; index++) {
+ memcpy(string2 + (length1 * index), string1,
+ (size_t) length1);
+ }
+ string2[length2] = '\0';
+ /*
+ * We have to directly assign this instead of using
+ * Tcl_SetStringObj (and indirectly TclInitStringRep)
+ * because that makes another copy of the data.
+ */
+ resultPtr = Tcl_NewObj();
+ resultPtr->bytes = string2;
+ resultPtr->length = length2;
+ Tcl_SetObjResult(interp, resultPtr);
}
}
break;
}
case STR_REPLACE: {
+ Tcl_UniChar *ustring1;
int first, last;
if (objc < 5 || objc > 6) {
@@ -1838,33 +2092,29 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
- string1 = Tcl_GetStringFromObj(objv[2], &length1);
- length1 = Tcl_NumUtfChars(string1, length1) - 1;
- if (TclGetIntForIndex(interp, objv[3], length1,
- &first) != TCL_OK) {
- return TCL_ERROR;
- }
- if (TclGetIntForIndex(interp, objv[4], length1,
- &last) != TCL_OK) {
+ ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
+ length1--;
+
+ if ((TclGetIntForIndex(interp, objv[3], length1, &first) != TCL_OK)
+ || (TclGetIntForIndex(interp, objv[4], length1,
+ &last) != TCL_OK)) {
return TCL_ERROR;
}
- if ((last < first) || (first > length1) || (last < 0)) {
+
+ if ((last < first) || (last < 0) || (first > length1)) {
Tcl_SetObjResult(interp, objv[2]);
} else {
- char *start, *end;
-
if (first < 0) {
first = 0;
}
- start = Tcl_UtfAtIndex(string1, first);
- end = Tcl_UtfAtIndex(start, ((last > length1) ? length1 : last)
- - first + 1);
- Tcl_SetStringObj(resultPtr, string1, start - string1);
+
+ Tcl_SetUnicodeObj(resultPtr, ustring1, first);
if (objc == 6) {
Tcl_AppendObjToObj(resultPtr, objv[5]);
}
if (last < length1) {
- Tcl_AppendToObj(resultPtr, end, -1);
+ Tcl_AppendUnicodeToObj(resultPtr, ustring1 + last + 1,
+ length1 - last);
}
}
break;
@@ -1898,7 +2148,7 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
Tcl_SetObjLength(resultPtr, length1);
} else {
int first, last;
- char *start, *end;
+ CONST char *start, *end;
length1 = Tcl_NumUtfChars(string1, length1) - 1;
if (TclGetIntForIndex(interp, objv[3], length1,
@@ -1942,7 +2192,7 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
case STR_TRIM: {
Tcl_UniChar ch, trim;
- register char *p, *end;
+ register CONST char *p, *end;
char *check, *checkEnd;
int offset;
@@ -2031,7 +2281,7 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
case STR_WORDEND: {
int cur;
Tcl_UniChar ch;
- char *p, *end;
+ CONST char *p, *end;
int numChars;
if (objc != 4) {
@@ -2069,7 +2319,7 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
case STR_WORDSTART: {
int cur;
Tcl_UniChar ch;
- char *p;
+ CONST char *p;
int numChars;
if (objc != 4) {
@@ -2114,8 +2364,7 @@ Tcl_StringObjCmd(dummy, interp, objc, objv)
*
* This procedure is invoked to process the "subst" Tcl command.
* See the user documentation for details on what it does. This
- * command is an almost direct copy of an implementation by
- * Andrew Payne.
+ * command relies on Tcl_SubstObj() for its implementation.
*
* Results:
* A standard Tcl result.
@@ -2134,27 +2383,21 @@ Tcl_SubstObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- static char *substOptions[] = {
+ static CONST char *substOptions[] = {
"-nobackslashes", "-nocommands", "-novariables", (char *) NULL
};
enum substOptions {
SUBST_NOBACKSLASHES, SUBST_NOCOMMANDS, SUBST_NOVARS
};
- Interp *iPtr = (Interp *) interp;
- Tcl_DString result;
- char *p, *old, *value;
- int optionIndex, code, count, doVars, doCmds, doBackslashes, i;
+ Tcl_Obj *resultPtr;
+ int optionIndex, flags, i;
/*
* Parse command-line options.
*/
- doVars = doCmds = doBackslashes = 1;
+ flags = TCL_SUBST_ALL;
for (i = 1; i < (objc-1); i++) {
- p = Tcl_GetString(objv[i]);
- if (*p != '-') {
- break;
- }
if (Tcl_GetIndexFromObj(interp, objv[i], substOptions,
"switch", 0, &optionIndex) != TCL_OK) {
@@ -2162,15 +2405,15 @@ Tcl_SubstObjCmd(dummy, interp, objc, objv)
}
switch (optionIndex) {
case SUBST_NOBACKSLASHES: {
- doBackslashes = 0;
+ flags &= ~TCL_SUBST_BACKSLASHES;
break;
}
case SUBST_NOCOMMANDS: {
- doCmds = 0;
+ flags &= ~TCL_SUBST_COMMANDS;
break;
}
case SUBST_NOVARS: {
- doVars = 0;
+ flags &= ~TCL_SUBST_VARIABLES;
break;
}
default: {
@@ -2185,76 +2428,168 @@ Tcl_SubstObjCmd(dummy, interp, objc, objv)
}
/*
- * Scan through the string one character at a time, performing
- * command, variable, and backslash substitutions.
+ * Perform the substitution.
*/
+ resultPtr = Tcl_SubstObj(interp, objv[i], flags);
- Tcl_DStringInit(&result);
- old = p = Tcl_GetString(objv[i]);
- while (*p != 0) {
+ if (resultPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, resultPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_SubstObj --
+ *
+ * This function performs the substitutions specified on the
+ * given string as described in the user documentation for the
+ * "subst" Tcl command. This code is heavily based on an
+ * implementation by Andrew Payne. Note that if a command
+ * substitution returns TCL_CONTINUE or TCL_RETURN from its
+ * evaluation and is not completely well-formed, the results are
+ * not defined (or at least hard to characterise.) This fault
+ * will be fixed at some point, but the cost of the only sane
+ * fix (well-formedness check first) is such that you need to
+ * "precompile and cache" to stop everyone from being hit with
+ * the consequences every time through. Note that the current
+ * behaviour is not a security hole; it just restarts parsing
+ * the string following the substitution in a mildly surprising
+ * place, and it is a very bad idea to count on this remaining
+ * the same in future...
+ *
+ * Results:
+ * A Tcl_Obj* containing the substituted string, or NULL to
+ * indicate that an error occurred.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+Tcl_SubstObj(interp, objPtr, flags)
+ Tcl_Interp *interp;
+ Tcl_Obj *objPtr;
+ int flags;
+{
+ Tcl_Obj *resultObj;
+ char *p, *old;
+
+ old = p = Tcl_GetString(objPtr);
+ resultObj = Tcl_NewStringObj("", 0);
+ while (1) {
switch (*p) {
- case '\\':
- if (doBackslashes) {
- char buf[TCL_UTF_MAX];
+ case 0:
+ if (p != old) {
+ Tcl_AppendToObj(resultObj, old, p-old);
+ }
+ return resultObj;
- if (p != old) {
- Tcl_DStringAppend(&result, old, p-old);
- }
- Tcl_DStringAppend(&result, buf,
- Tcl_UtfBackslash(p, &count, buf));
- p += count;
- old = p;
- } else {
- p++;
+ case '\\':
+ if (flags & TCL_SUBST_BACKSLASHES) {
+ char buf[TCL_UTF_MAX];
+ int count;
+
+ if (p != old) {
+ Tcl_AppendToObj(resultObj, old, p-old);
}
- break;
+ Tcl_AppendToObj(resultObj, buf,
+ Tcl_UtfBackslash(p, &count, buf));
+ p += count;
+ old = p;
+ } else {
+ p++;
+ }
+ break;
- case '$':
- if (doVars) {
- if (p != old) {
- Tcl_DStringAppend(&result, old, p-old);
- }
- value = Tcl_ParseVar(interp, p, &p);
- if (value == NULL) {
- Tcl_DStringFree(&result);
- return TCL_ERROR;
- }
- Tcl_DStringAppend(&result, value, -1);
- old = p;
- } else {
+ case '$':
+ if (flags & TCL_SUBST_VARIABLES) {
+ Tcl_Parse parse;
+ int code;
+
+ /*
+ * Code is simpler overall if we (effectively) inline
+ * Tcl_ParseVar, particularly as that allows us to use
+ * a non-string interface when we come to appending
+ * the variable contents to the result object. There
+ * are a few other optimisations that doing this
+ * enables (like being able to continue the run of
+ * unsubstituted characters straight through if a '$'
+ * does not precede a variable name.)
+ */
+ if (Tcl_ParseVarName(interp, p, -1, &parse, 0) != TCL_OK) {
+ goto errorResult;
+ }
+ if (parse.numTokens == 1) {
+ /*
+ * There isn't a variable name after all: the $ is
+ * just a $.
+ */
p++;
+ break;
}
- break;
-
- case '[':
- if (doCmds) {
- if (p != old) {
- Tcl_DStringAppend(&result, old, p-old);
- }
- iPtr->evalFlags = TCL_BRACKET_TERM;
- code = Tcl_Eval(interp, p+1);
- if (code == TCL_ERROR) {
- Tcl_DStringFree(&result);
- return code;
- }
- old = p = (p+1 + iPtr->termOffset+1);
- Tcl_DStringAppend(&result, iPtr->result, -1);
+ if (p != old) {
+ Tcl_AppendToObj(resultObj, old, p-old);
+ }
+ p += parse.tokenPtr->size;
+ code = Tcl_EvalTokensStandard(interp, parse.tokenPtr,
+ parse.numTokens);
+ if (code == TCL_ERROR) {
+ goto errorResult;
+ }
+ if (code == TCL_BREAK) {
Tcl_ResetResult(interp);
- } else {
- p++;
+ return resultObj;
}
- break;
+ if (code != TCL_CONTINUE) {
+ Tcl_AppendObjToObj(resultObj, Tcl_GetObjResult(interp));
+ }
+ Tcl_ResetResult(interp);
+ old = p;
+ } else {
+ p++;
+ }
+ break;
+
+ case '[':
+ if (flags & TCL_SUBST_COMMANDS) {
+ Interp *iPtr = (Interp *) interp;
+ int code;
- default:
+ if (p != old) {
+ Tcl_AppendToObj(resultObj, old, p-old);
+ }
+ iPtr->evalFlags = TCL_BRACKET_TERM;
+ code = Tcl_EvalEx(interp, p+1, -1, 0);
+ switch (code) {
+ case TCL_ERROR:
+ goto errorResult;
+ case TCL_BREAK:
+ Tcl_ResetResult(interp);
+ return resultObj;
+ default:
+ Tcl_AppendObjToObj(resultObj, Tcl_GetObjResult(interp));
+ case TCL_CONTINUE:
+ Tcl_ResetResult(interp);
+ old = p = (p+1 + iPtr->termOffset + 1);
+ }
+ } else {
p++;
- break;
+ }
+ break;
+ default:
+ p++;
+ break;
}
}
- if (p != old) {
- Tcl_DStringAppend(&result, old, p-old);
- }
- Tcl_DStringResult(interp, &result);
- return TCL_OK;
+
+ errorResult:
+ Tcl_DecrRefCount(resultObj);
+ return NULL;
}
/*
@@ -2282,10 +2617,11 @@ Tcl_SwitchObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- int i, j, index, mode, matched, result, splitObjs, seenComment;
+ int i, j, index, mode, matched, result, splitObjs;
char *string, *pattern;
Tcl_Obj *stringObj;
- static char *options[] = {
+ Tcl_Obj *CONST *savedObjv = objv;
+ static CONST char *options[] = {
"-exact", "-glob", "-regexp", "--",
NULL
};
@@ -2332,46 +2668,72 @@ Tcl_SwitchObjCmd(dummy, interp, objc, objv)
if (Tcl_ListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK) {
return TCL_ERROR;
}
- objv = listv;
- splitObjs = 1;
- }
- seenComment = 0;
- for (i = 0; i < objc; i += 2) {
- if (i == objc - 1) {
- Tcl_ResetResult(interp);
- Tcl_AppendToObj(Tcl_GetObjResult(interp),
- "extra switch pattern with no body", -1);
-
- /*
- * Check if this can be due to a badly placed comment
- * in the switch block
- */
-
- if (splitObjs && seenComment) {
- Tcl_AppendToObj(Tcl_GetObjResult(interp),
- ", this may be due to a comment incorrectly placed outside of a switch body - see the \"switch\" documentation", -1);
- }
+ /*
+ * Ensure that the list is non-empty.
+ */
+ if (objc < 1) {
+ Tcl_WrongNumArgs(interp, 1, savedObjv,
+ "?switches? string {pattern body ... ?default body?}");
return TCL_ERROR;
}
+ objv = listv;
+ splitObjs = 1;
+ }
- /*
- * See if the pattern matches the string.
- */
+ /*
+ * Complain if there is an odd number of words in the list of
+ * patterns and bodies.
+ */
- pattern = Tcl_GetString(objv[i]);
+ if (objc % 2) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "extra switch pattern with no body", NULL);
/*
+ * Check if this can be due to a badly placed comment
+ * in the switch block.
+ *
* The following is an heuristic to detect the infamous
* "comment in switch" error: just check if a pattern
* begins with '#'.
*/
- if (splitObjs && *pattern == '#') {
- seenComment = 1;
+ if (splitObjs) {
+ for (i=0 ; i<objc ; i+=2) {
+ if (Tcl_GetString(objv[i])[0] == '#') {
+ Tcl_AppendResult(interp, ", this may be due to a ",
+ "comment incorrectly placed outside of a ",
+ "switch body - see the \"switch\" ",
+ "documentation", NULL);
+ break;
+ }
+ }
}
+ return TCL_ERROR;
+ }
+
+ /*
+ * Complain if the last body is a continuation. Note that this
+ * check assumes that the list is non-empty!
+ */
+
+ if (strcmp(Tcl_GetString(objv[objc-1]), "-") == 0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "no body specified for pattern \"",
+ Tcl_GetString(objv[objc-2]), "\"", NULL);
+ return TCL_ERROR;
+ }
+
+ for (i = 0; i < objc; i += 2) {
+ /*
+ * See if the pattern matches the string.
+ */
+
+ pattern = Tcl_GetString(objv[i]);
+
matched = 0;
if ((i == objc - 2)
&& (*pattern == 'd')
@@ -2405,10 +2767,11 @@ Tcl_SwitchObjCmd(dummy, interp, objc, objv)
for (j = i + 1; ; j += 2) {
if (j >= objc) {
- Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "no body specified for pattern \"", pattern,
- "\"", (char *) NULL);
- return TCL_ERROR;
+ /*
+ * This shouldn't happen since we've checked that the
+ * last body is not a continuation...
+ */
+ panic("fall-out when searching for body to match pattern");
}
if (strcmp(Tcl_GetString(objv[j]), "-") != 0) {
break;
@@ -2473,17 +2836,17 @@ Tcl_TimeObjCmd(dummy, interp, objc, objv)
objPtr = objv[1];
i = count;
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
while (i-- > 0) {
result = Tcl_EvalObjEx(interp, objPtr, 0);
if (result != TCL_OK) {
return result;
}
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
- totalMicroSec =
- (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
+ totalMicroSec = ( ( (double) ( stop.sec - start.sec ) ) * 1.0e6
+ + ( stop.usec - start.usec ) );
sprintf(buf, "%.0f microseconds per iteration",
((count <= 0) ? 0 : totalMicroSec/count));
Tcl_ResetResult(interp);
@@ -2498,13 +2861,17 @@ Tcl_TimeObjCmd(dummy, interp, objc, objv)
*
* This procedure is invoked to process the "trace" Tcl command.
* See the user documentation for details on what it does.
+ *
+ * Standard syntax as of Tcl 8.4 is
+ *
+ * trace {add|info|remove} {command|variable} name ops cmd
+ *
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
- *
*----------------------------------------------------------------------
*/
@@ -2517,17 +2884,26 @@ Tcl_TraceObjCmd(dummy, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
int optionIndex, commandLength;
- char *name, *rwuOps, *command, *p;
+ char *name, *flagOps, *command, *p;
size_t length;
- static char *traceOptions[] = {
- "variable", "vdelete", "vinfo", (char *) NULL
+ /* Main sub commands to 'trace' */
+ static CONST char *traceOptions[] = {
+ "add", "info", "remove",
+#ifndef TCL_REMOVE_OBSOLETE_TRACES
+ "variable", "vdelete", "vinfo",
+#endif
+ (char *) NULL
};
+ /* 'OLD' options are pre-Tcl-8.4 style */
enum traceOptions {
- TRACE_VARIABLE, TRACE_VDELETE, TRACE_VINFO
+ TRACE_ADD, TRACE_INFO, TRACE_REMOVE,
+#ifndef TCL_REMOVE_OBSOLETE_TRACES
+ TRACE_OLD_VARIABLE, TRACE_OLD_VDELETE, TRACE_OLD_VINFO
+#endif
};
if (objc < 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "option [arg arg ...]");
+ Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
return TCL_ERROR;
}
@@ -2536,162 +2912,1497 @@ Tcl_TraceObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
switch ((enum traceOptions) optionIndex) {
- case TRACE_VARIABLE: {
- int flags;
- TraceVarInfo *tvarPtr;
- if (objc != 5) {
- Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
+ case TRACE_ADD:
+ case TRACE_REMOVE:
+ case TRACE_INFO: {
+ /*
+ * All sub commands of trace add/remove must take at least
+ * one more argument. Beyond that we let the subcommand itself
+ * control the argument structure.
+ */
+ int typeIndex;
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "type ?arg arg ...?");
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIndexFromObj(interp, objv[2], traceTypeOptions,
+ "option", 0, &typeIndex) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return (traceSubCmds[typeIndex])(interp, optionIndex, objc, objv);
+ break;
+ }
+#ifndef TCL_REMOVE_OBSOLETE_TRACES
+ case TRACE_OLD_VARIABLE: {
+ int flags;
+ TraceVarInfo *tvarPtr;
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
+ return TCL_ERROR;
+ }
+
+ flags = 0;
+ flagOps = Tcl_GetString(objv[3]);
+ for (p = flagOps; *p != 0; p++) {
+ if (*p == 'r') {
+ flags |= TCL_TRACE_READS;
+ } else if (*p == 'w') {
+ flags |= TCL_TRACE_WRITES;
+ } else if (*p == 'u') {
+ flags |= TCL_TRACE_UNSETS;
+ } else if (*p == 'a') {
+ flags |= TCL_TRACE_ARRAY;
+ } else {
+ goto badVarOps;
+ }
+ }
+ if (flags == 0) {
+ goto badVarOps;
+ }
+ flags |= TCL_TRACE_OLD_STYLE;
+
+ command = Tcl_GetStringFromObj(objv[4], &commandLength);
+ length = (size_t) commandLength;
+ tvarPtr = (TraceVarInfo *) ckalloc((unsigned)
+ (sizeof(TraceVarInfo) - sizeof(tvarPtr->command)
+ + length + 1));
+ tvarPtr->flags = flags;
+ tvarPtr->length = length;
+ flags |= TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT;
+ strcpy(tvarPtr->command, command);
+ name = Tcl_GetString(objv[2]);
+ if (Tcl_TraceVar(interp, name, flags, TraceVarProc,
+ (ClientData) tvarPtr) != TCL_OK) {
+ ckfree((char *) tvarPtr);
+ return TCL_ERROR;
+ }
+ break;
+ }
+ case TRACE_OLD_VDELETE: {
+ int flags;
+ TraceVarInfo *tvarPtr;
+ ClientData clientData;
+
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
+ return TCL_ERROR;
+ }
+
+ flags = 0;
+ flagOps = Tcl_GetString(objv[3]);
+ for (p = flagOps; *p != 0; p++) {
+ if (*p == 'r') {
+ flags |= TCL_TRACE_READS;
+ } else if (*p == 'w') {
+ flags |= TCL_TRACE_WRITES;
+ } else if (*p == 'u') {
+ flags |= TCL_TRACE_UNSETS;
+ } else if (*p == 'a') {
+ flags |= TCL_TRACE_ARRAY;
+ } else {
+ goto badVarOps;
+ }
+ }
+ if (flags == 0) {
+ goto badVarOps;
+ }
+ flags |= TCL_TRACE_OLD_STYLE;
+
+ /*
+ * Search through all of our traces on this variable to
+ * see if there's one with the given command. If so, then
+ * delete the first one that matches.
+ */
+
+ command = Tcl_GetStringFromObj(objv[4], &commandLength);
+ length = (size_t) commandLength;
+ clientData = 0;
+ name = Tcl_GetString(objv[2]);
+ while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
+ TraceVarProc, clientData)) != 0) {
+ tvarPtr = (TraceVarInfo *) clientData;
+ if ((tvarPtr->length == length) && (tvarPtr->flags == flags)
+ && (strncmp(command, tvarPtr->command,
+ (size_t) length) == 0)) {
+ Tcl_UntraceVar2(interp, name, NULL,
+ flags | TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT,
+ TraceVarProc, clientData);
+ Tcl_EventuallyFree((ClientData) tvarPtr, TCL_DYNAMIC);
+ break;
+ }
+ }
+ break;
+ }
+ case TRACE_OLD_VINFO: {
+ ClientData clientData;
+ char ops[5];
+ Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "name");
+ return TCL_ERROR;
+ }
+ resultListPtr = Tcl_GetObjResult(interp);
+ clientData = 0;
+ name = Tcl_GetString(objv[2]);
+ while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
+ TraceVarProc, clientData)) != 0) {
+
+ TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
+
+ pairObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ p = ops;
+ if (tvarPtr->flags & TCL_TRACE_READS) {
+ *p = 'r';
+ p++;
+ }
+ if (tvarPtr->flags & TCL_TRACE_WRITES) {
+ *p = 'w';
+ p++;
+ }
+ if (tvarPtr->flags & TCL_TRACE_UNSETS) {
+ *p = 'u';
+ p++;
+ }
+ if (tvarPtr->flags & TCL_TRACE_ARRAY) {
+ *p = 'a';
+ p++;
+ }
+ *p = '\0';
+
+ /*
+ * Build a pair (2-item list) with the ops string as
+ * the first obj element and the tvarPtr->command string
+ * as the second obj element. Append the pair (as an
+ * element) to the end of the result object list.
+ */
+
+ elemObjPtr = Tcl_NewStringObj(ops, -1);
+ Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
+ elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
+ Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
+ Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
+ }
+ Tcl_SetObjResult(interp, resultListPtr);
+ break;
+ }
+#endif /* TCL_REMOVE_OBSOLETE_TRACES */
+ }
+ return TCL_OK;
+
+ badVarOps:
+ Tcl_AppendResult(interp, "bad operations \"", flagOps,
+ "\": should be one or more of rwua", (char *) NULL);
+ return TCL_ERROR;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclTraceExecutionObjCmd --
+ *
+ * Helper function for Tcl_TraceObjCmd; implements the
+ * [trace {add|remove|info} execution ...] subcommands.
+ * See the user documentation for details on what these do.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Depends on the operation (add, remove, or info) being performed;
+ * may add or remove command traces on a command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
+ Tcl_Interp *interp; /* Current interpreter. */
+ int optionIndex; /* Add, info or remove */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ int commandLength, index;
+ char *name, *command;
+ size_t length;
+ enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
+ static CONST char *opStrings[] = { "enter", "leave",
+ "enterstep", "leavestep", (char *) NULL };
+ enum operations { TRACE_EXEC_ENTER, TRACE_EXEC_LEAVE,
+ TRACE_EXEC_ENTER_STEP, TRACE_EXEC_LEAVE_STEP };
+
+ switch ((enum traceOptions) optionIndex) {
+ case TRACE_ADD:
+ case TRACE_REMOVE: {
+ int flags = 0;
+ int i, listLen, result;
+ Tcl_Obj **elemPtrs;
+ if (objc != 6) {
+ Tcl_WrongNumArgs(interp, 3, objv, "name opList execution");
+ return TCL_ERROR;
+ }
+ /*
+ * Make sure the ops argument is a list object; get its length and
+ * a pointer to its array of element pointers.
+ */
+
+ result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
+ &elemPtrs);
+ if (result != TCL_OK) {
+ return result;
+ }
+ if (listLen == 0) {
+ Tcl_SetResult(interp, "bad operation list \"\": must be "
+ "one or more of enter, leave, enterstep, or leavestep", TCL_STATIC);
+ return TCL_ERROR;
+ }
+ for (i = 0; i < listLen; i++) {
+ if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
+ "operation", TCL_EXACT, &index) != TCL_OK) {
return TCL_ERROR;
}
+ switch ((enum operations) index) {
+ case TRACE_EXEC_ENTER:
+ flags |= TCL_TRACE_ENTER_EXEC;
+ break;
+ case TRACE_EXEC_LEAVE:
+ flags |= TCL_TRACE_LEAVE_EXEC;
+ break;
+ case TRACE_EXEC_ENTER_STEP:
+ flags |= TCL_TRACE_ENTER_DURING_EXEC;
+ break;
+ case TRACE_EXEC_LEAVE_STEP:
+ flags |= TCL_TRACE_LEAVE_DURING_EXEC;
+ break;
+ }
+ }
+ command = Tcl_GetStringFromObj(objv[5], &commandLength);
+ length = (size_t) commandLength;
+ if ((enum traceOptions) optionIndex == TRACE_ADD) {
+ TraceCommandInfo *tcmdPtr;
+ tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
+ (sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
+ + length + 1));
+ tcmdPtr->flags = flags;
+ tcmdPtr->stepTrace = NULL;
+ tcmdPtr->startLevel = 0;
+ tcmdPtr->length = length;
+ flags |= TCL_TRACE_DELETE;
+ if (flags & (TRACE_EXEC_ENTER_STEP | TRACE_EXEC_LEAVE_STEP)) {
+ flags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
+ }
+ strcpy(tcmdPtr->command, command);
+ name = Tcl_GetString(objv[3]);
+ if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
+ (ClientData) tcmdPtr) != TCL_OK) {
+ ckfree((char *) tcmdPtr);
+ return TCL_ERROR;
+ }
+ } else {
+ /*
+ * Search through all of our traces on this command to
+ * see if there's one with the given command. If so, then
+ * delete the first one that matches.
+ */
+
+ TraceCommandInfo *tcmdPtr;
+ ClientData clientData;
+ clientData = 0;
+ name = Tcl_GetString(objv[3]);
+ while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
+ TraceCommandProc, clientData)) != 0) {
+ tcmdPtr = (TraceCommandInfo *) clientData;
+ /*
+ * In checking the 'flags' field we must remove any extraneous
+ * flags which may have been temporarily added by various pieces
+ * of the trace mechanism.
+ */
+ if ((tcmdPtr->length == length)
+ && ((tcmdPtr->flags & (TCL_TRACE_ANY_EXEC | TCL_TRACE_RENAME |
+ TCL_TRACE_DELETE)) == flags)
+ && (strncmp(command, tcmdPtr->command,
+ (size_t) length) == 0)) {
+ flags |= TCL_TRACE_DELETE;
+ if (flags & (TRACE_EXEC_ENTER_STEP | TRACE_EXEC_LEAVE_STEP)) {
+ flags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
+ }
+ Tcl_UntraceCommand(interp, name,
+ flags, TraceCommandProc, clientData);
+ if (tcmdPtr->stepTrace != NULL) {
+ /*
+ * We need to remove the interpreter-wide trace
+ * which we created to allow 'step' traces.
+ */
+ Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
+ tcmdPtr->stepTrace = NULL;
+ }
+ /* Postpone deletion */
+ if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
+ tcmdPtr->flags = 0;
+ } else {
+ Tcl_EventuallyFree((ClientData) tcmdPtr, TCL_DYNAMIC);
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ case TRACE_INFO: {
+ ClientData clientData;
+ Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "name");
+ return TCL_ERROR;
+ }
- flags = 0;
- rwuOps = Tcl_GetString(objv[3]);
- for (p = rwuOps; *p != 0; p++) {
- if (*p == 'r') {
- flags |= TCL_TRACE_READS;
- } else if (*p == 'w') {
- flags |= TCL_TRACE_WRITES;
- } else if (*p == 'u') {
- flags |= TCL_TRACE_UNSETS;
- } else {
- goto badOps;
+ resultListPtr = Tcl_GetObjResult(interp);
+ clientData = 0;
+ name = Tcl_GetString(objv[3]);
+ while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
+ TraceCommandProc, clientData)) != 0) {
+
+ TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
+
+ eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+
+ /*
+ * Build a list with the ops list as the first obj
+ * element and the tcmdPtr->command string as the
+ * second obj element. Append this list (as an
+ * element) to the end of the result object list.
+ */
+
+ elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ if (tcmdPtr->flags & TCL_TRACE_ENTER_EXEC) {
+ Tcl_ListObjAppendElement(NULL, elemObjPtr,
+ Tcl_NewStringObj("enter",6));
+ }
+ if (tcmdPtr->flags & TCL_TRACE_LEAVE_EXEC) {
+ Tcl_ListObjAppendElement(NULL, elemObjPtr,
+ Tcl_NewStringObj("leave",5));
+ }
+ if (tcmdPtr->flags & TCL_TRACE_ENTER_DURING_EXEC) {
+ Tcl_ListObjAppendElement(NULL, elemObjPtr,
+ Tcl_NewStringObj("enterstep",9));
+ }
+ if (tcmdPtr->flags & TCL_TRACE_LEAVE_DURING_EXEC) {
+ Tcl_ListObjAppendElement(NULL, elemObjPtr,
+ Tcl_NewStringObj("leavestep",10));
+ }
+ Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
+
+ elemObjPtr = Tcl_NewStringObj(tcmdPtr->command, -1);
+ Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
+ Tcl_ListObjAppendElement(interp, resultListPtr,
+ eachTraceObjPtr);
+ }
+ Tcl_SetObjResult(interp, resultListPtr);
+ break;
+ }
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclTraceCommandObjCmd --
+ *
+ * Helper function for Tcl_TraceObjCmd; implements the
+ * [trace {add|info|remove} command ...] subcommands.
+ * See the user documentation for details on what these do.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Depends on the operation (add, remove, or info) being performed;
+ * may add or remove command traces on a command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclTraceCommandObjCmd(interp, optionIndex, objc, objv)
+ Tcl_Interp *interp; /* Current interpreter. */
+ int optionIndex; /* Add, info or remove */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ int commandLength, index;
+ char *name, *command;
+ size_t length;
+ enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
+ static CONST char *opStrings[] = { "delete", "rename", (char *) NULL };
+ enum operations { TRACE_CMD_DELETE, TRACE_CMD_RENAME };
+
+ switch ((enum traceOptions) optionIndex) {
+ case TRACE_ADD:
+ case TRACE_REMOVE: {
+ int flags = 0;
+ int i, listLen, result;
+ Tcl_Obj **elemPtrs;
+ if (objc != 6) {
+ Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
+ return TCL_ERROR;
+ }
+ /*
+ * Make sure the ops argument is a list object; get its length and
+ * a pointer to its array of element pointers.
+ */
+
+ result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
+ &elemPtrs);
+ if (result != TCL_OK) {
+ return result;
+ }
+ if (listLen == 0) {
+ Tcl_SetResult(interp, "bad operation list \"\": must be "
+ "one or more of delete or rename", TCL_STATIC);
+ return TCL_ERROR;
+ }
+ for (i = 0; i < listLen; i++) {
+ if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
+ "operation", TCL_EXACT, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch ((enum operations) index) {
+ case TRACE_CMD_RENAME:
+ flags |= TCL_TRACE_RENAME;
+ break;
+ case TRACE_CMD_DELETE:
+ flags |= TCL_TRACE_DELETE;
+ break;
+ }
+ }
+ command = Tcl_GetStringFromObj(objv[5], &commandLength);
+ length = (size_t) commandLength;
+ if ((enum traceOptions) optionIndex == TRACE_ADD) {
+ TraceCommandInfo *tcmdPtr;
+ tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
+ (sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
+ + length + 1));
+ tcmdPtr->flags = flags;
+ tcmdPtr->stepTrace = NULL;
+ tcmdPtr->startLevel = 0;
+ tcmdPtr->length = length;
+ flags |= TCL_TRACE_DELETE;
+ strcpy(tcmdPtr->command, command);
+ name = Tcl_GetString(objv[3]);
+ if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
+ (ClientData) tcmdPtr) != TCL_OK) {
+ ckfree((char *) tcmdPtr);
+ return TCL_ERROR;
+ }
+ } else {
+ /*
+ * Search through all of our traces on this command to
+ * see if there's one with the given command. If so, then
+ * delete the first one that matches.
+ */
+
+ TraceCommandInfo *tcmdPtr;
+ ClientData clientData;
+ clientData = 0;
+ name = Tcl_GetString(objv[3]);
+ while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
+ TraceCommandProc, clientData)) != 0) {
+ tcmdPtr = (TraceCommandInfo *) clientData;
+ if ((tcmdPtr->length == length)
+ && (tcmdPtr->flags == flags)
+ && (strncmp(command, tcmdPtr->command,
+ (size_t) length) == 0)) {
+ Tcl_UntraceCommand(interp, name,
+ flags | TCL_TRACE_DELETE,
+ TraceCommandProc, clientData);
+ ckfree((char *) tcmdPtr);
+ break;
}
}
- if (flags == 0) {
- goto badOps;
+ }
+ break;
+ }
+ case TRACE_INFO: {
+ ClientData clientData;
+ Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "name");
+ return TCL_ERROR;
+ }
+
+ resultListPtr = Tcl_GetObjResult(interp);
+ clientData = 0;
+ name = Tcl_GetString(objv[3]);
+ while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
+ TraceCommandProc, clientData)) != 0) {
+
+ TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
+
+ eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+
+ /*
+ * Build a list with the ops list as
+ * the first obj element and the tcmdPtr->command string
+ * as the second obj element. Append this list (as an
+ * element) to the end of the result object list.
+ */
+
+ elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ if (tcmdPtr->flags & TCL_TRACE_RENAME) {
+ Tcl_ListObjAppendElement(NULL, elemObjPtr,
+ Tcl_NewStringObj("rename",6));
+ }
+ if (tcmdPtr->flags & TCL_TRACE_DELETE) {
+ Tcl_ListObjAppendElement(NULL, elemObjPtr,
+ Tcl_NewStringObj("delete",6));
}
+ Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
+
+ elemObjPtr = Tcl_NewStringObj(tcmdPtr->command, -1);
+ Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
+ Tcl_ListObjAppendElement(interp, resultListPtr,
+ eachTraceObjPtr);
+ }
+ Tcl_SetObjResult(interp, resultListPtr);
+ break;
+ }
+ }
+ return TCL_OK;
+}
- command = Tcl_GetStringFromObj(objv[4], &commandLength);
- length = (size_t) commandLength;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclTraceVariableObjCmd --
+ *
+ * Helper function for Tcl_TraceObjCmd; implements the
+ * [trace {add|info|remove} variable ...] subcommands.
+ * See the user documentation for details on what these do.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Depends on the operation (add, remove, or info) being performed;
+ * may add or remove variable traces on a variable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclTraceVariableObjCmd(interp, optionIndex, objc, objv)
+ Tcl_Interp *interp; /* Current interpreter. */
+ int optionIndex; /* Add, info or remove */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ int commandLength, index;
+ char *name, *command;
+ size_t length;
+ enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
+ static CONST char *opStrings[] = { "array", "read", "unset", "write",
+ (char *) NULL };
+ enum operations { TRACE_VAR_ARRAY, TRACE_VAR_READ, TRACE_VAR_UNSET,
+ TRACE_VAR_WRITE };
+
+ switch ((enum traceOptions) optionIndex) {
+ case TRACE_ADD:
+ case TRACE_REMOVE: {
+ int flags = 0;
+ int i, listLen, result;
+ Tcl_Obj **elemPtrs;
+ if (objc != 6) {
+ Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
+ return TCL_ERROR;
+ }
+ /*
+ * Make sure the ops argument is a list object; get its length and
+ * a pointer to its array of element pointers.
+ */
+
+ result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
+ &elemPtrs);
+ if (result != TCL_OK) {
+ return result;
+ }
+ if (listLen == 0) {
+ Tcl_SetResult(interp, "bad operation list \"\": must be "
+ "one or more of array, read, unset, or write",
+ TCL_STATIC);
+ return TCL_ERROR;
+ }
+ for (i = 0; i < listLen ; i++) {
+ if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
+ "operation", TCL_EXACT, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch ((enum operations) index) {
+ case TRACE_VAR_ARRAY:
+ flags |= TCL_TRACE_ARRAY;
+ break;
+ case TRACE_VAR_READ:
+ flags |= TCL_TRACE_READS;
+ break;
+ case TRACE_VAR_UNSET:
+ flags |= TCL_TRACE_UNSETS;
+ break;
+ case TRACE_VAR_WRITE:
+ flags |= TCL_TRACE_WRITES;
+ break;
+ }
+ }
+ command = Tcl_GetStringFromObj(objv[5], &commandLength);
+ length = (size_t) commandLength;
+ if ((enum traceOptions) optionIndex == TRACE_ADD) {
+ TraceVarInfo *tvarPtr;
tvarPtr = (TraceVarInfo *) ckalloc((unsigned)
(sizeof(TraceVarInfo) - sizeof(tvarPtr->command)
+ length + 1));
tvarPtr->flags = flags;
- tvarPtr->errMsg = NULL;
tvarPtr->length = length;
- flags |= TCL_TRACE_UNSETS;
+ flags |= TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT;
strcpy(tvarPtr->command, command);
- name = Tcl_GetString(objv[2]);
+ name = Tcl_GetString(objv[3]);
if (Tcl_TraceVar(interp, name, flags, TraceVarProc,
(ClientData) tvarPtr) != TCL_OK) {
ckfree((char *) tvarPtr);
return TCL_ERROR;
}
- break;
- }
- case TRACE_VDELETE: {
- int flags;
- TraceVarInfo *tvarPtr;
- ClientData clientData;
-
- if (objc != 5) {
- Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
- return TCL_ERROR;
- }
-
- flags = 0;
- rwuOps = Tcl_GetString(objv[3]);
- for (p = rwuOps; *p != 0; p++) {
- if (*p == 'r') {
- flags |= TCL_TRACE_READS;
- } else if (*p == 'w') {
- flags |= TCL_TRACE_WRITES;
- } else if (*p == 'u') {
- flags |= TCL_TRACE_UNSETS;
- } else {
- goto badOps;
- }
- }
- if (flags == 0) {
- goto badOps;
- }
-
+ } else {
/*
* Search through all of our traces on this variable to
* see if there's one with the given command. If so, then
* delete the first one that matches.
*/
- command = Tcl_GetStringFromObj(objv[4], &commandLength);
- length = (size_t) commandLength;
- clientData = 0;
- name = Tcl_GetString(objv[2]);
+ TraceVarInfo *tvarPtr;
+ ClientData clientData = 0;
+ name = Tcl_GetString(objv[3]);
while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
TraceVarProc, clientData)) != 0) {
tvarPtr = (TraceVarInfo *) clientData;
- if ((tvarPtr->length == length) && (tvarPtr->flags == flags)
+ if ((tvarPtr->length == length)
+ && (tvarPtr->flags == flags)
&& (strncmp(command, tvarPtr->command,
(size_t) length) == 0)) {
- Tcl_UntraceVar(interp, name, flags | TCL_TRACE_UNSETS,
+ Tcl_UntraceVar2(interp, name, NULL,
+ flags | TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT,
TraceVarProc, clientData);
- if (tvarPtr->errMsg != NULL) {
- ckfree(tvarPtr->errMsg);
- }
- ckfree((char *) tvarPtr);
+ Tcl_EventuallyFree((ClientData) tvarPtr, TCL_DYNAMIC);
break;
}
}
- break;
}
- case TRACE_VINFO: {
- ClientData clientData;
- char ops[4];
- Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
+ break;
+ }
+ case TRACE_INFO: {
+ ClientData clientData;
+ Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "name");
+ return TCL_ERROR;
+ }
- if (objc != 3) {
- Tcl_WrongNumArgs(interp, 2, objv, "name");
- return TCL_ERROR;
+ resultListPtr = Tcl_GetObjResult(interp);
+ clientData = 0;
+ name = Tcl_GetString(objv[3]);
+ while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
+ TraceVarProc, clientData)) != 0) {
+
+ TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
+
+ eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ /*
+ * Build a list with the ops list as
+ * the first obj element and the tcmdPtr->command string
+ * as the second obj element. Append this list (as an
+ * element) to the end of the result object list.
+ */
+
+ elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ if (tvarPtr->flags & TCL_TRACE_ARRAY) {
+ Tcl_ListObjAppendElement(NULL, elemObjPtr,
+ Tcl_NewStringObj("array", 5));
}
- resultListPtr = Tcl_GetObjResult(interp);
- clientData = 0;
- name = Tcl_GetString(objv[2]);
- while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
- TraceVarProc, clientData)) != 0) {
+ if (tvarPtr->flags & TCL_TRACE_READS) {
+ Tcl_ListObjAppendElement(NULL, elemObjPtr,
+ Tcl_NewStringObj("read", 4));
+ }
+ if (tvarPtr->flags & TCL_TRACE_WRITES) {
+ Tcl_ListObjAppendElement(NULL, elemObjPtr,
+ Tcl_NewStringObj("write", 5));
+ }
+ if (tvarPtr->flags & TCL_TRACE_UNSETS) {
+ Tcl_ListObjAppendElement(NULL, elemObjPtr,
+ Tcl_NewStringObj("unset", 5));
+ }
+ Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
+
+ elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
+ Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
+ Tcl_ListObjAppendElement(interp, resultListPtr,
+ eachTraceObjPtr);
+ }
+ Tcl_SetObjResult(interp, resultListPtr);
+ break;
+ }
+ }
+ return TCL_OK;
+}
- TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_CommandTraceInfo --
+ *
+ * Return the clientData value associated with a trace on a
+ * command. This procedure can also be used to step through
+ * all of the traces on a particular command that have the
+ * same trace procedure.
+ *
+ * Results:
+ * The return value is the clientData value associated with
+ * a trace on the given command. Information will only be
+ * returned for a trace with proc as trace procedure. If
+ * the clientData argument is NULL then the first such trace is
+ * returned; otherwise, the next relevant one after the one
+ * given by clientData will be returned. If the command
+ * doesn't exist, or if there are no (more) traces for it,
+ * then NULL is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
- pairObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
- p = ops;
- if (tvarPtr->flags & TCL_TRACE_READS) {
- *p = 'r';
- p++;
- }
- if (tvarPtr->flags & TCL_TRACE_WRITES) {
- *p = 'w';
- p++;
- }
- if (tvarPtr->flags & TCL_TRACE_UNSETS) {
- *p = 'u';
- p++;
- }
- *p = '\0';
+ClientData
+Tcl_CommandTraceInfo(interp, cmdName, flags, proc, prevClientData)
+ Tcl_Interp *interp; /* Interpreter containing command. */
+ CONST char *cmdName; /* Name of command. */
+ int flags; /* OR-ed combo or TCL_GLOBAL_ONLY,
+ * TCL_NAMESPACE_ONLY (can be 0). */
+ Tcl_CommandTraceProc *proc; /* Procedure assocated with trace. */
+ ClientData prevClientData; /* If non-NULL, gives last value returned
+ * by this procedure, so this call will
+ * return the next trace after that one.
+ * If NULL, this call will return the
+ * first trace. */
+{
+ Command *cmdPtr;
+ register CommandTrace *tracePtr;
- /*
- * Build a pair (2-item list) with the ops string as
- * the first obj element and the tvarPtr->command string
- * as the second obj element. Append the pair (as an
- * element) to the end of the result object list.
- */
+ cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName,
+ NULL, TCL_LEAVE_ERR_MSG);
+ if (cmdPtr == NULL) {
+ return NULL;
+ }
- elemObjPtr = Tcl_NewStringObj(ops, -1);
- Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
- elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
- Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
- Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
- }
- Tcl_SetObjResult(interp, resultListPtr);
+ /*
+ * Find the relevant trace, if any, and return its clientData.
+ */
+
+ tracePtr = cmdPtr->tracePtr;
+ if (prevClientData != NULL) {
+ for ( ; tracePtr != NULL; tracePtr = tracePtr->nextPtr) {
+ if ((tracePtr->clientData == prevClientData)
+ && (tracePtr->traceProc == proc)) {
+ tracePtr = tracePtr->nextPtr;
break;
}
- default: {
- panic("Tcl_TraceObjCmd: bad option index to TraceOptions");
- }
+ }
+ }
+ for ( ; tracePtr != NULL; tracePtr = tracePtr->nextPtr) {
+ if (tracePtr->traceProc == proc) {
+ return tracePtr->clientData;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_TraceCommand --
+ *
+ * Arrange for rename/deletes to a command to cause a
+ * procedure to be invoked, which can monitor the operations.
+ *
+ * Also optionally arrange for execution of that command
+ * to cause a procedure to be invoked.
+ *
+ * Results:
+ * A standard Tcl return value.
+ *
+ * Side effects:
+ * A trace is set up on the command given by cmdName, such that
+ * future changes to the command will be intermediated by
+ * proc. See the manual entry for complete details on the calling
+ * sequence for proc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_TraceCommand(interp, cmdName, flags, proc, clientData)
+ Tcl_Interp *interp; /* Interpreter in which command is
+ * to be traced. */
+ CONST char *cmdName; /* Name of command. */
+ int flags; /* OR-ed collection of bits, including any
+ * of TCL_TRACE_RENAME, TCL_TRACE_DELETE,
+ * and any of the TRACE_*_EXEC flags */
+ Tcl_CommandTraceProc *proc; /* Procedure to call when specified ops are
+ * invoked upon varName. */
+ ClientData clientData; /* Arbitrary argument to pass to proc. */
+{
+ Command *cmdPtr;
+ register CommandTrace *tracePtr;
+
+ cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName,
+ NULL, TCL_LEAVE_ERR_MSG);
+ if (cmdPtr == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Set up trace information.
+ */
+
+ tracePtr = (CommandTrace *) ckalloc(sizeof(CommandTrace));
+ tracePtr->traceProc = proc;
+ tracePtr->clientData = clientData;
+ tracePtr->flags = flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE
+ | TCL_TRACE_ANY_EXEC);
+ tracePtr->nextPtr = cmdPtr->tracePtr;
+ cmdPtr->tracePtr = tracePtr;
+ if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
+ cmdPtr->flags |= CMD_HAS_EXEC_TRACES;
}
return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_UntraceCommand --
+ *
+ * Remove a previously-created trace for a command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If there exists a trace for the command given by cmdName
+ * with the given flags, proc, and clientData, then that trace
+ * is removed.
+ *
+ *----------------------------------------------------------------------
+ */
- badOps:
- Tcl_AppendResult(interp, "bad operations \"", rwuOps,
- "\": should be one or more of rwu", (char *) NULL);
- return TCL_ERROR;
+void
+Tcl_UntraceCommand(interp, cmdName, flags, proc, clientData)
+ Tcl_Interp *interp; /* Interpreter containing command. */
+ CONST char *cmdName; /* Name of command. */
+ int flags; /* OR-ed collection of bits, including any
+ * of TCL_TRACE_RENAME, TCL_TRACE_DELETE,
+ * and any of the TRACE_*_EXEC flags */
+ Tcl_CommandTraceProc *proc; /* Procedure assocated with trace. */
+ ClientData clientData; /* Arbitrary argument to pass to proc. */
+{
+ register CommandTrace *tracePtr;
+ CommandTrace *prevPtr;
+ Command *cmdPtr;
+ Interp *iPtr = (Interp *) interp;
+ ActiveCommandTrace *activePtr;
+ int hasExecTraces = 0;
+
+ cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName,
+ NULL, TCL_LEAVE_ERR_MSG);
+ if (cmdPtr == NULL) {
+ return;
+ }
+
+ flags &= (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC);
+
+ for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; ;
+ prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
+ if (tracePtr == NULL) {
+ return;
+ }
+ if ((tracePtr->traceProc == proc) && ((tracePtr->flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC)) == flags)
+ && (tracePtr->clientData == clientData)) {
+ if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
+ hasExecTraces = 1;
+ }
+ break;
+ }
+ }
+
+ /*
+ * The code below makes it possible to delete traces while traces
+ * are active: it makes sure that the deleted trace won't be
+ * processed by CallCommandTraces.
+ */
+
+ for (activePtr = iPtr->activeCmdTracePtr; activePtr != NULL;
+ activePtr = activePtr->nextPtr) {
+ if (activePtr->nextTracePtr == tracePtr) {
+ activePtr->nextTracePtr = tracePtr->nextPtr;
+ }
+ }
+ if (prevPtr == NULL) {
+ cmdPtr->tracePtr = tracePtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = tracePtr->nextPtr;
+ }
+ tracePtr->flags = 0;
+ Tcl_EventuallyFree((int*)tracePtr, TCL_DYNAMIC);
+
+ if (hasExecTraces) {
+ for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; tracePtr != NULL ;
+ prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
+ if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
+ return;
+ }
+ }
+ /*
+ * None of the remaining traces on this command are execution
+ * traces. We therefore remove this flag:
+ */
+ cmdPtr->flags &= ~CMD_HAS_EXEC_TRACES;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceCommandProc --
+ *
+ * This procedure is called to handle command changes that have
+ * been traced using the "trace" command, when using the
+ * 'rename' or 'delete' options.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the command associated with the trace.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+TraceCommandProc(clientData, interp, oldName, newName, flags)
+ ClientData clientData; /* Information about the command trace. */
+ Tcl_Interp *interp; /* Interpreter containing command. */
+ CONST char *oldName; /* Name of command being changed. */
+ CONST char *newName; /* New name of command. Empty string
+ * or NULL means command is being deleted
+ * (renamed to ""). */
+ int flags; /* OR-ed bits giving operation and other
+ * information. */
+{
+ Tcl_SavedResult state;
+ TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
+ int code;
+ Tcl_DString cmd;
+
+ Tcl_Preserve((ClientData) tcmdPtr);
+
+ if ((tcmdPtr->flags & flags) && !(flags & TCL_INTERP_DESTROYED)) {
+ /*
+ * Generate a command to execute by appending list elements
+ * for the old and new command name and the operation.
+ */
+
+ Tcl_DStringInit(&cmd);
+ Tcl_DStringAppend(&cmd, tcmdPtr->command, (int) tcmdPtr->length);
+ Tcl_DStringAppendElement(&cmd, oldName);
+ Tcl_DStringAppendElement(&cmd, (newName ? newName : ""));
+ if (flags & TCL_TRACE_RENAME) {
+ Tcl_DStringAppend(&cmd, " rename", 7);
+ } else if (flags & TCL_TRACE_DELETE) {
+ Tcl_DStringAppend(&cmd, " delete", 7);
+ }
+
+ /*
+ * Execute the command. Save the interp's result used for
+ * the command. We discard any object result the command returns.
+ *
+ * Add the TCL_TRACE_DESTROYED flag to tcmdPtr to indicate to
+ * other areas that this will be destroyed by us, otherwise a
+ * double-free might occur depending on what the eval does.
+ */
+
+ Tcl_SaveResult(interp, &state);
+ if (flags & TCL_TRACE_DESTROYED) {
+ tcmdPtr->flags |= TCL_TRACE_DESTROYED;
+ }
+
+ code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
+ Tcl_DStringLength(&cmd), 0);
+ if (code != TCL_OK) {
+ /* We ignore errors in these traced commands */
+ }
+
+ Tcl_RestoreResult(interp, &state);
+
+ Tcl_DStringFree(&cmd);
+ }
+ /*
+ * We delete when the trace was destroyed or if this is a delete trace,
+ * because command deletes are unconditional, so the trace must go away.
+ */
+ if (flags & (TCL_TRACE_DESTROYED | TCL_TRACE_DELETE)) {
+ if (tcmdPtr->stepTrace != NULL) {
+ Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
+ tcmdPtr->stepTrace = NULL;
+ }
+ /* Postpone deletion, until exec trace returns */
+ if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
+ tcmdPtr->flags = 0;
+ } else {
+ Tcl_EventuallyFree((ClientData) tcmdPtr, TCL_DYNAMIC);
+ }
+ }
+ Tcl_Release((ClientData) tcmdPtr);
+ return;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCheckExecutionTraces --
+ *
+ * Checks on all current command execution traces, and invokes
+ * procedures which have been registered. This procedure can be
+ * used by other code which performs execution to unify the
+ * tracing system, so that execution traces will function for that
+ * other code.
+ *
+ * For instance extensions like [incr Tcl] which use their
+ * own execution technique can make use of Tcl's tracing.
+ *
+ * This procedure is called by 'TclEvalObjvInternal'
+ *
+ * Results:
+ * The return value is a standard Tcl completion code such as
+ * TCL_OK or TCL_ERROR, etc.
+ *
+ * Side effects:
+ * Those side effects made by any trace procedures called.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code, traceFlags, objc, objv)
+ Tcl_Interp *interp; /* The current interpreter. */
+ CONST char *command; /* Pointer to beginning of the current
+ * command string. */
+ int numChars; /* The number of characters in 'command'
+ * which are part of the command string. */
+ Command *cmdPtr; /* Points to command's Command struct. */
+ int code; /* The current result code. */
+ int traceFlags; /* Current tracing situation. */
+ int objc; /* Number of arguments for the command. */
+ Tcl_Obj *CONST objv[]; /* Pointers to Tcl_Obj of each argument. */
+{
+ Interp *iPtr = (Interp *) interp;
+ CommandTrace *tracePtr, *lastTracePtr;
+ ActiveCommandTrace active;
+ int curLevel;
+ int traceCode = TCL_OK;
+ TraceCommandInfo* tcmdPtr;
+
+ if (command == NULL || cmdPtr->tracePtr == NULL) {
+ return(traceCode);
+ }
+
+ curLevel = ((iPtr->varFramePtr == NULL) ? 0 : iPtr->varFramePtr->level);
+
+ active.nextPtr = iPtr->activeCmdTracePtr;
+ iPtr->activeCmdTracePtr = &active;
+
+ active.cmdPtr = cmdPtr;
+ lastTracePtr = NULL;
+ for ( tracePtr = cmdPtr->tracePtr;
+ (traceCode == TCL_OK) && (tracePtr != NULL);
+ tracePtr = active.nextTracePtr) {
+ if (traceFlags & TCL_TRACE_LEAVE_EXEC) {
+ /* execute the trace command in order of creation for "leave" */
+ active.nextTracePtr = NULL;
+ tracePtr = cmdPtr->tracePtr;
+ while (tracePtr->nextPtr != lastTracePtr) {
+ active.nextTracePtr = tracePtr;
+ tracePtr = tracePtr->nextPtr;
+ }
+ } else {
+ active.nextTracePtr = tracePtr->nextPtr;
+ }
+ tcmdPtr = (TraceCommandInfo*)tracePtr->clientData;
+ if (tcmdPtr->flags != 0) {
+ tcmdPtr->curFlags = traceFlags | TCL_TRACE_EXEC_DIRECT;
+ tcmdPtr->curCode = code;
+ traceCode = TraceExecutionProc((ClientData)tcmdPtr, interp,
+ curLevel, command, (Tcl_Command)cmdPtr, objc, objv);
+ }
+ lastTracePtr = tracePtr;
+ }
+ iPtr->activeCmdTracePtr = active.nextPtr;
+ return(traceCode);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCheckInterpTraces --
+ *
+ * Checks on all current traces, and invokes procedures which
+ * have been registered. This procedure can be used by other
+ * code which performs execution to unify the tracing system.
+ * For instance extensions like [incr Tcl] which use their
+ * own execution technique can make use of Tcl's tracing.
+ *
+ * This procedure is called by 'TclEvalObjvInternal'
+ *
+ * Results:
+ * The return value is a standard Tcl completion code such as
+ * TCL_OK or TCL_ERROR, etc.
+ *
+ * Side effects:
+ * Those side effects made by any trace procedures called.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclCheckInterpTraces(interp, command, numChars, cmdPtr, code, traceFlags, objc, objv)
+ Tcl_Interp *interp; /* The current interpreter. */
+ CONST char *command; /* Pointer to beginning of the current
+ * command string. */
+ int numChars; /* The number of characters in 'command'
+ * which are part of the command string. */
+ Command *cmdPtr; /* Points to command's Command struct. */
+ int code; /* The current result code. */
+ int traceFlags; /* Current tracing situation. */
+ int objc; /* Number of arguments for the command. */
+ Tcl_Obj *CONST objv[]; /* Pointers to Tcl_Obj of each argument. */
+{
+ Interp *iPtr = (Interp *) interp;
+ Trace *tracePtr, *lastTracePtr;
+ ActiveInterpTrace active;
+ int curLevel;
+ int traceCode = TCL_OK;
+ TraceCommandInfo* tcmdPtr;
+
+ if (command == NULL || iPtr->tracePtr == NULL ||
+ (iPtr->flags & INTERP_TRACE_IN_PROGRESS)) {
+ return(traceCode);
+ }
+
+ curLevel = ((iPtr->varFramePtr == NULL) ? 0 : iPtr->varFramePtr->level);
+
+ active.nextPtr = iPtr->activeInterpTracePtr;
+ iPtr->activeInterpTracePtr = &active;
+
+ lastTracePtr = NULL;
+ for ( tracePtr = iPtr->tracePtr;
+ (traceCode == TCL_OK) && (tracePtr != NULL);
+ tracePtr = active.nextTracePtr) {
+ if (traceFlags & TCL_TRACE_ENTER_EXEC) {
+ /* execute the trace command in reverse order of creation
+ * for "enterstep" operation. The order is changed for
+ * ""enterstep" instead of for "leavestep as was done in
+ * TclCheckExecutionTraces because for step traces,
+ * Tcl_CreateObjTrace creates one more linked list of traces
+ * which results in one more reversal of trace invocation.
+ */
+ active.nextTracePtr = NULL;
+ tracePtr = iPtr->tracePtr;
+ while (tracePtr->nextPtr != lastTracePtr) {
+ active.nextTracePtr = tracePtr;
+ tracePtr = tracePtr->nextPtr;
+ }
+ } else {
+ active.nextTracePtr = tracePtr->nextPtr;
+ }
+ if (tracePtr->level > 0 && curLevel > tracePtr->level) {
+ continue;
+ }
+ if (!(tracePtr->flags & TCL_TRACE_EXEC_IN_PROGRESS)) {
+ /*
+ * The proc invoked might delete the traced command which
+ * which might try to free tracePtr. We want to use tracePtr
+ * until the end of this if section, so we use
+ * Tcl_Preserve() and Tcl_Release() to be sureit is not
+ * freed while we still need it.
+ */
+ Tcl_Preserve((ClientData) tracePtr);
+ tracePtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
+ if ((tracePtr->flags != TCL_TRACE_EXEC_IN_PROGRESS) &&
+ ((tracePtr->flags & traceFlags) != 0)) {
+ tcmdPtr = (TraceCommandInfo*)tracePtr->clientData;
+ tcmdPtr->curFlags = traceFlags;
+ tcmdPtr->curCode = code;
+ traceCode = (tracePtr->proc)((ClientData)tcmdPtr,
+ (Tcl_Interp*)interp,
+ curLevel, command,
+ (Tcl_Command)cmdPtr,
+ objc, objv);
+ } else {
+ if (traceFlags & TCL_TRACE_ENTER_EXEC) {
+ /*
+ * Old-style interpreter-wide traces only trigger
+ * before the command is executed.
+ */
+ traceCode = CallTraceProcedure(interp, tracePtr, cmdPtr,
+ command, numChars, objc, objv);
+ }
+ }
+ tracePtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
+ Tcl_Release((ClientData) tracePtr);
+ }
+ lastTracePtr = tracePtr;
+ }
+ iPtr->activeInterpTracePtr = active.nextPtr;
+ return(traceCode);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CallTraceProcedure --
+ *
+ * Invokes a trace procedure registered with an interpreter. These
+ * procedures trace command execution. Currently this trace procedure
+ * is called with the address of the string-based Tcl_CmdProc for the
+ * command, not the Tcl_ObjCmdProc.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Those side effects made by the trace procedure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CallTraceProcedure(interp, tracePtr, cmdPtr, command, numChars, objc, objv)
+ Tcl_Interp *interp; /* The current interpreter. */
+ register Trace *tracePtr; /* Describes the trace procedure to call. */
+ Command *cmdPtr; /* Points to command's Command struct. */
+ CONST char *command; /* Points to the first character of the
+ * command's source before substitutions. */
+ int numChars; /* The number of characters in the
+ * command's source. */
+ register int objc; /* Number of arguments for the command. */
+ Tcl_Obj *CONST objv[]; /* Pointers to Tcl_Obj of each argument. */
+{
+ Interp *iPtr = (Interp *) interp;
+ char *commandCopy;
+ int traceCode;
+
+ /*
+ * Copy the command characters into a new string.
+ */
+
+ commandCopy = (char *) ckalloc((unsigned) (numChars + 1));
+ memcpy((VOID *) commandCopy, (VOID *) command, (size_t) numChars);
+ commandCopy[numChars] = '\0';
+
+ /*
+ * Call the trace procedure then free allocated storage.
+ */
+
+ traceCode = (tracePtr->proc)( tracePtr->clientData, (Tcl_Interp*) iPtr,
+ iPtr->numLevels, commandCopy,
+ (Tcl_Command) cmdPtr, objc, objv );
+
+ ckfree((char *) commandCopy);
+ return(traceCode);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceExecutionProc --
+ *
+ * This procedure is invoked whenever code relevant to a
+ * 'trace execution' command is executed. It is called in one
+ * of two ways in Tcl's core:
+ *
+ * (i) by the TclCheckExecutionTraces, when an execution trace has been
+ * triggered.
+ * (ii) by TclCheckInterpTraces, when a prior execution trace has
+ * created a trace of the internals of a procedure, passing in
+ * this procedure as the one to be called.
+ *
+ * Results:
+ * The return value is a standard Tcl completion code such as
+ * TCL_OK or TCL_ERROR, etc.
+ *
+ * Side effects:
+ * May invoke an arbitrary Tcl procedure, and may create or
+ * delete an interpreter-wide trace.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TraceExecutionProc(ClientData clientData, Tcl_Interp *interp,
+ int level, CONST char* command, Tcl_Command cmdInfo,
+ int objc, struct Tcl_Obj *CONST objv[]) {
+ int call = 0;
+ Interp *iPtr = (Interp *) interp;
+ TraceCommandInfo* tcmdPtr = (TraceCommandInfo*)clientData;
+ int flags = tcmdPtr->curFlags;
+ int code = tcmdPtr->curCode;
+ int traceCode = TCL_OK;
+
+ if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
+ /*
+ * Inside any kind of execution trace callback, we do
+ * not allow any further execution trace callbacks to
+ * be called for the same trace.
+ */
+ return(traceCode);
+ }
+
+ if (!(flags & TCL_INTERP_DESTROYED)) {
+ /*
+ * Check whether the current call is going to eval arbitrary
+ * Tcl code with a generated trace, or whether we are only
+ * going to setup interpreter-wide traces to implement the
+ * 'step' traces. This latter situation can happen if
+ * we create a command trace without either before or after
+ * operations, but with either of the step operations.
+ */
+ if (flags & TCL_TRACE_EXEC_DIRECT) {
+ call = flags & tcmdPtr->flags & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
+ } else {
+ call = 1;
+ }
+ /*
+ * First, if we have returned back to the level at which we
+ * created an interpreter trace, we remove it
+ */
+ if (flags & TCL_TRACE_LEAVE_EXEC) {
+ if ((tcmdPtr->stepTrace != NULL) && (level == tcmdPtr->startLevel)) {
+ Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
+ tcmdPtr->stepTrace = NULL;
+ }
+
+ }
+
+ /*
+ * Second, create the tcl callback, if required.
+ */
+ if (call) {
+ Tcl_SavedResult state;
+ Tcl_DString cmd;
+ Tcl_DString sub;
+ int i;
+
+ Tcl_DStringInit(&cmd);
+ Tcl_DStringAppend(&cmd, tcmdPtr->command, (int)tcmdPtr->length);
+ /* Append command with arguments */
+ Tcl_DStringInit(&sub);
+ for (i = 0; i < objc; i++) {
+ char* str;
+ int len;
+ str = Tcl_GetStringFromObj(objv[i],&len);
+ Tcl_DStringAppendElement(&sub, str);
+ }
+ Tcl_DStringAppendElement(&cmd, Tcl_DStringValue(&sub));
+ Tcl_DStringFree(&sub);
+
+ if (flags & TCL_TRACE_ENTER_EXEC) {
+ /* Append trace operation */
+ if (flags & TCL_TRACE_EXEC_DIRECT) {
+ Tcl_DStringAppendElement(&cmd, "enter");
+ } else {
+ Tcl_DStringAppendElement(&cmd, "enterstep");
+ }
+ } else if (flags & TCL_TRACE_LEAVE_EXEC) {
+ Tcl_Obj* resultCode;
+ char* resultCodeStr;
+
+ /* Append result code */
+ resultCode = Tcl_NewIntObj(code);
+ resultCodeStr = Tcl_GetString(resultCode);
+ Tcl_DStringAppendElement(&cmd, resultCodeStr);
+ Tcl_DecrRefCount(resultCode);
+
+ /* Append result string */
+ Tcl_DStringAppendElement(&cmd, Tcl_GetStringResult(interp));
+ /* Append trace operation */
+ if (flags & TCL_TRACE_EXEC_DIRECT) {
+ Tcl_DStringAppendElement(&cmd, "leave");
+ } else {
+ Tcl_DStringAppendElement(&cmd, "leavestep");
+ }
+ } else {
+ panic("TraceExecutionProc: bad flag combination");
+ }
+
+ /*
+ * Execute the command. Save the interp's result used for
+ * the command. We discard any object result the command returns.
+ */
+
+ Tcl_SaveResult(interp, &state);
+
+ tcmdPtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
+ iPtr->flags |= INTERP_TRACE_IN_PROGRESS;
+ Tcl_Preserve((ClientData)tcmdPtr);
+ /*
+ * This line can have quite arbitrary side-effects,
+ * including deleting the trace, the command being
+ * traced, or even the interpreter.
+ */
+ traceCode = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
+ tcmdPtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
+ iPtr->flags &= ~INTERP_TRACE_IN_PROGRESS;
+ if (tcmdPtr->flags == 0) {
+ flags |= TCL_TRACE_DESTROYED;
+ }
+
+ if (traceCode == TCL_OK) {
+ /* Restore result if trace execution was successful */
+ Tcl_RestoreResult(interp, &state);
+ }
+
+ Tcl_DStringFree(&cmd);
+ }
+
+ /*
+ * Third, create an interpreter trace, if we need one for
+ * subsequent internal execution traces.
+ */
+ if ((flags & TCL_TRACE_ENTER_EXEC) && (tcmdPtr->stepTrace == NULL)
+ && (tcmdPtr->flags & (TCL_TRACE_ENTER_DURING_EXEC | TCL_TRACE_LEAVE_DURING_EXEC))) {
+ tcmdPtr->startLevel = level;
+ tcmdPtr->stepTrace = Tcl_CreateObjTrace(interp, 0,
+ (tcmdPtr->flags & TCL_TRACE_ANY_EXEC) >> 2,
+ TraceExecutionProc, (ClientData)tcmdPtr, NULL);
+ }
+ }
+ if (flags & TCL_TRACE_DESTROYED) {
+ if (tcmdPtr->stepTrace != NULL) {
+ Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
+ tcmdPtr->stepTrace = NULL;
+ }
+ Tcl_EventuallyFree((ClientData)tcmdPtr, TCL_DYNAMIC);
+ }
+ if (call) {
+ Tcl_Release((ClientData)tcmdPtr);
+ }
+ return(traceCode);
}
/*
@@ -2717,8 +4428,8 @@ static char *
TraceVarProc(clientData, interp, name1, name2, flags)
ClientData clientData; /* Information about the variable trace. */
Tcl_Interp *interp; /* Interpreter containing variable. */
- char *name1; /* Name of variable or array. */
- char *name2; /* Name of element within array; NULL means
+ CONST char *name1; /* Name of variable or array. */
+ CONST char *name2; /* Name of element within array; NULL means
* scalar variable is being referenced. */
int flags; /* OR-ed bits giving operation and other
* information. */
@@ -2729,64 +4440,91 @@ TraceVarProc(clientData, interp, name1, name2, flags)
int code;
Tcl_DString cmd;
+ /*
+ * We might call Tcl_Eval() below, and that might evaluate
+ * [trace vdelete] which might try to free tvarPtr. We want
+ * to use tvarPtr until the end of this function, so we use
+ * Tcl_Preserve() and Tcl_Release() to be sure it is not
+ * freed while we still need it.
+ */
+
+ Tcl_Preserve((ClientData) tvarPtr);
+
result = NULL;
- if (tvarPtr->errMsg != NULL) {
- ckfree(tvarPtr->errMsg);
- tvarPtr->errMsg = NULL;
- }
if ((tvarPtr->flags & flags) && !(flags & TCL_INTERP_DESTROYED)) {
+ if (tvarPtr->length != (size_t) 0) {
+ /*
+ * Generate a command to execute by appending list elements
+ * for the two variable names and the operation.
+ */
- /*
- * Generate a command to execute by appending list elements
- * for the two variable names and the operation. The five
- * extra characters are for three space, the opcode character,
- * and the terminating null.
- */
+ Tcl_DStringInit(&cmd);
+ Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
+ Tcl_DStringAppendElement(&cmd, name1);
+ Tcl_DStringAppendElement(&cmd, (name2 ? name2 : ""));
+#ifndef TCL_REMOVE_OBSOLETE_TRACES
+ if (tvarPtr->flags & TCL_TRACE_OLD_STYLE) {
+ if (flags & TCL_TRACE_ARRAY) {
+ Tcl_DStringAppend(&cmd, " a", 2);
+ } else if (flags & TCL_TRACE_READS) {
+ Tcl_DStringAppend(&cmd, " r", 2);
+ } else if (flags & TCL_TRACE_WRITES) {
+ Tcl_DStringAppend(&cmd, " w", 2);
+ } else if (flags & TCL_TRACE_UNSETS) {
+ Tcl_DStringAppend(&cmd, " u", 2);
+ }
+ } else {
+#endif
+ if (flags & TCL_TRACE_ARRAY) {
+ Tcl_DStringAppend(&cmd, " array", 6);
+ } else if (flags & TCL_TRACE_READS) {
+ Tcl_DStringAppend(&cmd, " read", 5);
+ } else if (flags & TCL_TRACE_WRITES) {
+ Tcl_DStringAppend(&cmd, " write", 6);
+ } else if (flags & TCL_TRACE_UNSETS) {
+ Tcl_DStringAppend(&cmd, " unset", 6);
+ }
+#ifndef TCL_REMOVE_OBSOLETE_TRACES
+ }
+#endif
+
+ /*
+ * Execute the command. Save the interp's result used for
+ * the command. We discard any object result the command returns.
+ *
+ * Add the TCL_TRACE_DESTROYED flag to tvarPtr to indicate to
+ * other areas that this will be destroyed by us, otherwise a
+ * double-free might occur depending on what the eval does.
+ */
- if (name2 == NULL) {
- name2 = "";
- }
- Tcl_DStringInit(&cmd);
- Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
- Tcl_DStringAppendElement(&cmd, name1);
- Tcl_DStringAppendElement(&cmd, name2);
- if (flags & TCL_TRACE_READS) {
- Tcl_DStringAppend(&cmd, " r", 2);
- } else if (flags & TCL_TRACE_WRITES) {
- Tcl_DStringAppend(&cmd, " w", 2);
- } else if (flags & TCL_TRACE_UNSETS) {
- Tcl_DStringAppend(&cmd, " u", 2);
- }
+ Tcl_SaveResult(interp, &state);
+ if (flags & TCL_TRACE_DESTROYED) {
+ tvarPtr->flags |= TCL_TRACE_DESTROYED;
+ }
- /*
- * Execute the command. Save the interp's result used for
- * the command. We discard any object result the command returns.
- */
+ code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
+ Tcl_DStringLength(&cmd), 0);
+ if (code != TCL_OK) { /* copy error msg to result */
+ register Tcl_Obj *errMsgObj = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(errMsgObj);
+ result = (char *) errMsgObj;
+ }
- Tcl_SaveResult(interp, &state);
+ Tcl_RestoreResult(interp, &state);
- code = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
- if (code != TCL_OK) { /* copy error msg to result */
- char *string;
- int length;
-
- string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length);
- tvarPtr->errMsg = (char *) ckalloc((unsigned) (length + 1));
- memcpy(tvarPtr->errMsg, string, (size_t) (length + 1));
- result = tvarPtr->errMsg;
+ Tcl_DStringFree(&cmd);
}
-
- Tcl_RestoreResult(interp, &state);
-
- Tcl_DStringFree(&cmd);
}
if (flags & TCL_TRACE_DESTROYED) {
- result = NULL;
- if (tvarPtr->errMsg != NULL) {
- ckfree(tvarPtr->errMsg);
+ if (result != NULL) {
+ register Tcl_Obj *errMsgObj = (Tcl_Obj *) result;
+
+ Tcl_DecrRefCount(errMsgObj);
+ result = NULL;
}
- ckfree((char *) tvarPtr);
+ Tcl_EventuallyFree((ClientData) tvarPtr, TCL_DYNAMIC);
}
+ Tcl_Release((ClientData) tvarPtr);
return result;
}
@@ -2855,4 +4593,3 @@ Tcl_WhileObjCmd(dummy, interp, objc, objv)
return result;
}
-
diff --git a/tcl/generic/tclCompCmds.c b/tcl/generic/tclCompCmds.c
index f15b5aa5378..43a24e08557 100644
--- a/tcl/generic/tclCompCmds.c
+++ b/tcl/generic/tclCompCmds.c
@@ -5,6 +5,8 @@
* Tcl commands into a sequence of instructions ("bytecodes").
*
* Copyright (c) 1997-1998 Sun Microsystems, Inc.
+ * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
+ * Copyright (c) 2002 ActiveState Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -20,8 +22,17 @@
*/
static ClientData DupForeachInfo _ANSI_ARGS_((ClientData clientData));
-static void FreeForeachInfo _ANSI_ARGS_((
- ClientData clientData));
+static void FreeForeachInfo _ANSI_ARGS_((ClientData clientData));
+static int TclPushVarName _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Token *varTokenPtr, CompileEnv *envPtr, int flags,
+ int *localIndexPtr, int *simpleVarNamePtr, int *isScalarPtr));
+
+/*
+ * Flags bits used by TclPushVarName.
+ */
+
+#define TCL_CREATE_VAR 1 /* Create a compiled local if none is found */
+#define TCL_NO_LARGE_INDEX 2 /* Do not return localIndex value > 255 */
/*
* The structures below define the AuxData types defined in this file.
@@ -36,6 +47,130 @@ AuxDataType tclForeachInfoType = {
/*
*----------------------------------------------------------------------
*
+ * TclCompileAppendCmd --
+ *
+ * Procedure called to compile the "append" command.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is normally TCL_OK
+ * unless there was an error while parsing string. If an error occurs
+ * then the interpreter's result contains a standard error message. If
+ * complation fails because the command requires a second level of
+ * substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the
+ * command should be compiled "out of line" by emitting code to
+ * invoke its command procedure (Tcl_AppendObjCmd) at runtime.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute the "append" command
+ * at runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileAppendCmd(interp, parsePtr, envPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Parse *parsePtr; /* Points to a parse structure for the
+ * command created by Tcl_ParseCommand. */
+ CompileEnv *envPtr; /* Holds resulting instructions. */
+{
+ Tcl_Token *varTokenPtr, *valueTokenPtr;
+ int simpleVarName, isScalar, localIndex, numWords;
+ int code = TCL_OK;
+
+ numWords = parsePtr->numWords;
+ if (numWords == 1) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendToObj(Tcl_GetObjResult(interp),
+ "wrong # args: should be \"append varName ?value value ...?\"",
+ -1);
+ return TCL_ERROR;
+ } else if (numWords == 2) {
+ /*
+ * append varName === set varName
+ */
+ return TclCompileSetCmd(interp, parsePtr, envPtr);
+ } else if (numWords > 3) {
+ /*
+ * APPEND instructions currently only handle one value
+ */
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ /*
+ * Decide if we can use a frame slot for the var/array name or if we
+ * need to emit code to compute and push the name at runtime. We use a
+ * frame slot (entry in the array of local vars) if we are compiling a
+ * procedure body and if the name is simple text that does not include
+ * namespace qualifiers.
+ */
+
+ varTokenPtr = parsePtr->tokenPtr
+ + (parsePtr->tokenPtr->numComponents + 1);
+
+ code = TclPushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR,
+ &localIndex, &simpleVarName, &isScalar);
+ if (code != TCL_OK) {
+ goto done;
+ }
+
+ /*
+ * We are doing an assignment, otherwise TclCompileSetCmd was called,
+ * so push the new value. This will need to be extended to push a
+ * value for each argument.
+ */
+
+ if (numWords > 2) {
+ valueTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ TclEmitPush(TclRegisterNewLiteral(envPtr,
+ valueTokenPtr[1].start, valueTokenPtr[1].size), envPtr);
+ } else {
+ code = TclCompileTokens(interp, valueTokenPtr+1,
+ valueTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ goto done;
+ }
+ }
+ }
+
+ /*
+ * Emit instructions to set/get the variable.
+ */
+
+ if (simpleVarName) {
+ if (isScalar) {
+ if (localIndex >= 0) {
+ if (localIndex <= 255) {
+ TclEmitInstInt1(INST_APPEND_SCALAR1, localIndex, envPtr);
+ } else {
+ TclEmitInstInt4(INST_APPEND_SCALAR4, localIndex, envPtr);
+ }
+ } else {
+ TclEmitOpcode(INST_APPEND_STK, envPtr);
+ }
+ } else {
+ if (localIndex >= 0) {
+ if (localIndex <= 255) {
+ TclEmitInstInt1(INST_APPEND_ARRAY1, localIndex, envPtr);
+ } else {
+ TclEmitInstInt4(INST_APPEND_ARRAY4, localIndex, envPtr);
+ }
+ } else {
+ TclEmitOpcode(INST_APPEND_ARRAY_STK, envPtr);
+ }
+ }
+ } else {
+ TclEmitOpcode(INST_APPEND_STK, envPtr);
+ }
+
+ done:
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclCompileBreakCmd --
*
* Procedure called to compile the "break" command.
@@ -45,9 +180,6 @@ AuxDataType tclForeachInfoType = {
* there was an error during compilation. If an error occurs then
* the interpreter's result contains a standard error message.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the command.
- *
* Side effects:
* Instructions are added to envPtr to execute the "break" command
* at runtime.
@@ -66,7 +198,6 @@ TclCompileBreakCmd(interp, parsePtr, envPtr)
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp),
"wrong # args: should be \"break\"", -1);
- envPtr->maxStackDepth = 0;
return TCL_ERROR;
}
@@ -75,7 +206,6 @@ TclCompileBreakCmd(interp, parsePtr, envPtr)
*/
TclEmitOpcode(INST_BREAK, envPtr);
- envPtr->maxStackDepth = 0;
return TCL_OK;
}
@@ -95,9 +225,6 @@ TclCompileBreakCmd(interp, parsePtr, envPtr)
* should be compiled "out of line" by emitting code to invoke its
* command procedure at runtime.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the command.
- *
* Side effects:
* Instructions are added to envPtr to execute the "catch" command
* at runtime.
@@ -114,12 +241,11 @@ TclCompileCatchCmd(interp, parsePtr, envPtr)
{
JumpFixup jumpFixup;
Tcl_Token *cmdTokenPtr, *nameTokenPtr;
- char *name;
- int localIndex, nameChars, range, maxDepth, startOffset, jumpDist;
+ CONST char *name;
+ int localIndex, nameChars, range, startOffset, jumpDist;
int code;
- char buffer[32 + TCL_INTEGER_SPACE];
+ int savedStackDepth = envPtr->currStackDepth;
- envPtr->maxStackDepth = 0;
if ((parsePtr->numWords != 2) && (parsePtr->numWords != 3)) {
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp),
@@ -165,8 +291,6 @@ TclCompileCatchCmd(interp, parsePtr, envPtr)
* We will compile the catch command. Emit a beginCatch instruction at
* the start of the catch body: the subcommand it controls.
*/
-
- maxDepth = 0;
envPtr->exceptDepth++;
envPtr->maxExceptDepth =
@@ -174,19 +298,31 @@ TclCompileCatchCmd(interp, parsePtr, envPtr)
range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
TclEmitInstInt4(INST_BEGIN_CATCH4, range, envPtr);
- startOffset = (envPtr->codeNext - envPtr->codeStart);
+ /*
+ * If the body is a simple word, compile the instructions to
+ * eval it. Otherwise, compile instructions to substitute its
+ * text without catching, a catch instruction that resets the
+ * stack to what it was before substituting the body, and then
+ * an instruction to eval the body. Care has to be taken to
+ * register the correct startOffset for the catch range so that
+ * errors in the substitution are not catched [Bug 219184]
+ */
+
+ if (cmdTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ startOffset = (envPtr->codeNext - envPtr->codeStart);
+ code = TclCompileCmdWord(interp, cmdTokenPtr+1, 1, envPtr);
+ } else {
+ code = TclCompileTokens(interp, cmdTokenPtr+1,
+ cmdTokenPtr->numComponents, envPtr);
+ startOffset = (envPtr->codeNext - envPtr->codeStart);
+ TclEmitOpcode(INST_EVAL_STK, envPtr);
+ }
envPtr->exceptArrayPtr[range].codeOffset = startOffset;
- code = TclCompileCmdWord(interp, cmdTokenPtr+1,
- cmdTokenPtr->numComponents, envPtr);
+
if (code != TCL_OK) {
- if (code == TCL_ERROR) {
- sprintf(buffer, "\n (\"catch\" body line %d)",
- interp->errorLine);
- Tcl_AddObjErrorInfo(interp, buffer, -1);
- }
+ code = TCL_OUT_LINE_COMPILE;
goto done;
}
- maxDepth = envPtr->maxStackDepth;
envPtr->exceptArrayPtr[range].numCodeBytes =
(envPtr->codeNext - envPtr->codeStart) - startOffset;
@@ -204,11 +340,7 @@ TclCompileCatchCmd(interp, parsePtr, envPtr)
}
}
TclEmitOpcode(INST_POP, envPtr);
- TclEmitPush(TclRegisterLiteral(envPtr, "0", 1, /*onHeap*/ 0),
- envPtr);
- if (maxDepth == 0) {
- maxDepth = 1;
- }
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "0", 1), envPtr);
TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup);
/*
@@ -217,6 +349,7 @@ TclCompileCatchCmd(interp, parsePtr, envPtr)
* the catch's error target.
*/
+ envPtr->currStackDepth = savedStackDepth;
envPtr->exceptArrayPtr[range].catchOffset =
(envPtr->codeNext - envPtr->codeStart);
if (localIndex != -1) {
@@ -230,6 +363,7 @@ TclCompileCatchCmd(interp, parsePtr, envPtr)
}
TclEmitOpcode(INST_PUSH_RETURN_CODE, envPtr);
+
/*
* Update the target of the jump after the "no errors" code, then emit
* an endCatch instruction at the end of the catch command.
@@ -243,8 +377,8 @@ TclCompileCatchCmd(interp, parsePtr, envPtr)
TclEmitOpcode(INST_END_CATCH, envPtr);
done:
+ envPtr->currStackDepth = savedStackDepth + 1;
envPtr->exceptDepth--;
- envPtr->maxStackDepth = maxDepth;
return code;
}
@@ -260,9 +394,6 @@ TclCompileCatchCmd(interp, parsePtr, envPtr)
* there was an error while parsing string. If an error occurs then
* the interpreter's result contains a standard error message.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the command.
- *
* Side effects:
* Instructions are added to envPtr to execute the "continue" command
* at runtime.
@@ -285,7 +416,6 @@ TclCompileContinueCmd(interp, parsePtr, envPtr)
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp),
"wrong # args: should be \"continue\"", -1);
- envPtr->maxStackDepth = 0;
return TCL_ERROR;
}
@@ -294,7 +424,6 @@ TclCompileContinueCmd(interp, parsePtr, envPtr)
*/
TclEmitOpcode(INST_CONTINUE, envPtr);
- envPtr->maxStackDepth = 0;
return TCL_OK;
}
@@ -310,9 +439,6 @@ TclCompileContinueCmd(interp, parsePtr, envPtr)
* unless there was an error while parsing string. If an error occurs
* then the interpreter's result contains a standard error message.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the "expr" command.
- *
* Side effects:
* Instructions are added to envPtr to execute the "expr" command
* at runtime.
@@ -329,7 +455,6 @@ TclCompileExprCmd(interp, parsePtr, envPtr)
{
Tcl_Token *firstWordPtr;
- envPtr->maxStackDepth = 0;
if (parsePtr->numWords == 1) {
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp),
@@ -355,16 +480,12 @@ TclCompileExprCmd(interp, parsePtr, envPtr)
* there was an error while parsing string. If an error occurs then
* the interpreter's result contains a standard error message.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the command.
- *
* Side effects:
* Instructions are added to envPtr to execute the "for" command
* at runtime.
*
*----------------------------------------------------------------------
*/
-
int
TclCompileForCmd(interp, parsePtr, envPtr)
Tcl_Interp *interp; /* Used for error reporting. */
@@ -373,13 +494,12 @@ TclCompileForCmd(interp, parsePtr, envPtr)
CompileEnv *envPtr; /* Holds resulting instructions. */
{
Tcl_Token *startTokenPtr, *testTokenPtr, *nextTokenPtr, *bodyTokenPtr;
- JumpFixup jumpFalseFixup;
- int maxDepth, jumpBackDist, jumpBackOffset, testCodeOffset, jumpDist;
+ JumpFixup jumpEvalCondFixup;
+ int testCodeOffset, bodyCodeOffset, nextCodeOffset, jumpDist;
int bodyRange, nextRange, code;
- unsigned char *jumpPc;
char buffer[32 + TCL_INTEGER_SPACE];
+ int savedStackDepth = envPtr->currStackDepth;
- envPtr->maxStackDepth = 0;
if (parsePtr->numWords != 5) {
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp),
@@ -401,6 +521,18 @@ TclCompileForCmd(interp, parsePtr, envPtr)
}
/*
+ * Bail out also if the body or the next expression require substitutions
+ * in order to insure correct behaviour [Bug 219166]
+ */
+
+ nextTokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1);
+ bodyTokenPtr = nextTokenPtr + (nextTokenPtr->numComponents + 1);
+ if ((nextTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)
+ || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ /*
* Create ExceptionRange records for the body and the "next" command.
* The "next" command's ExceptionRange supports break but not continue
* (and has a -1 continueOffset).
@@ -416,7 +548,6 @@ TclCompileForCmd(interp, parsePtr, envPtr)
* Inline compile the initial command.
*/
- maxDepth = 0;
code = TclCompileCmdWord(interp, startTokenPtr+1,
startTokenPtr->numComponents, envPtr);
if (code != TCL_OK) {
@@ -426,35 +557,31 @@ TclCompileForCmd(interp, parsePtr, envPtr)
}
goto done;
}
- maxDepth = envPtr->maxStackDepth;
TclEmitOpcode(INST_POP, envPtr);
-
+
/*
- * Compile the test then emit the conditional jump that exits the for.
+ * Jump to the evaluation of the condition. This code uses the "loop
+ * rotation" optimisation (which eliminates one branch from the loop).
+ * "for start cond next body" produces then:
+ * start
+ * goto A
+ * B: body : bodyCodeOffset
+ * next : nextCodeOffset, continueOffset
+ * A: cond -> result : testCodeOffset
+ * if (result) goto B
*/
- testCodeOffset = (envPtr->codeNext - envPtr->codeStart);
- code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
- if (code != TCL_OK) {
- if (code == TCL_ERROR) {
- Tcl_AddObjErrorInfo(interp,
- "\n (\"for\" test expression)", -1);
- }
- goto done;
- }
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
- TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpFalseFixup);
+ TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpEvalCondFixup);
/*
* Compile the loop body.
*/
- nextTokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1);
- bodyTokenPtr = nextTokenPtr + (nextTokenPtr->numComponents + 1);
- envPtr->exceptArrayPtr[bodyRange].codeOffset =
- (envPtr->codeNext - envPtr->codeStart);
+ bodyCodeOffset = (envPtr->codeNext - envPtr->codeStart);
+
code = TclCompileCmdWord(interp, bodyTokenPtr+1,
bodyTokenPtr->numComponents, envPtr);
+ envPtr->currStackDepth = savedStackDepth + 1;
if (code != TCL_OK) {
if (code == TCL_ERROR) {
sprintf(buffer, "\n (\"for\" body line %d)",
@@ -463,22 +590,21 @@ TclCompileForCmd(interp, parsePtr, envPtr)
}
goto done;
}
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
envPtr->exceptArrayPtr[bodyRange].numCodeBytes =
- (envPtr->codeNext - envPtr->codeStart)
- - envPtr->exceptArrayPtr[bodyRange].codeOffset;
+ (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset;
TclEmitOpcode(INST_POP, envPtr);
+
/*
* Compile the "next" subcommand.
*/
- envPtr->exceptArrayPtr[bodyRange].continueOffset =
- (envPtr->codeNext - envPtr->codeStart);
- envPtr->exceptArrayPtr[nextRange].codeOffset =
- (envPtr->codeNext - envPtr->codeStart);
+ nextCodeOffset = (envPtr->codeNext - envPtr->codeStart);
+
+ envPtr->currStackDepth = savedStackDepth;
code = TclCompileCmdWord(interp, nextTokenPtr+1,
nextTokenPtr->numComponents, envPtr);
+ envPtr->currStackDepth = savedStackDepth + 1;
if (code != TCL_OK) {
if (code == TCL_ERROR) {
Tcl_AddObjErrorInfo(interp,
@@ -486,62 +612,53 @@ TclCompileForCmd(interp, parsePtr, envPtr)
}
goto done;
}
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
envPtr->exceptArrayPtr[nextRange].numCodeBytes =
(envPtr->codeNext - envPtr->codeStart)
- - envPtr->exceptArrayPtr[nextRange].codeOffset;
+ - nextCodeOffset;
TclEmitOpcode(INST_POP, envPtr);
-
- /*
- * Jump back to the test at the top of the loop. Generate a 4 byte jump
- * if the distance to the test is > 120 bytes. This is conservative and
- * ensures that we won't have to replace this jump if we later need to
- * replace the ifFalse jump with a 4 byte jump.
- */
-
- jumpBackOffset = (envPtr->codeNext - envPtr->codeStart);
- jumpBackDist = (jumpBackOffset - testCodeOffset);
- if (jumpBackDist > 120) {
- TclEmitInstInt4(INST_JUMP4, -jumpBackDist, envPtr);
- } else {
- TclEmitInstInt1(INST_JUMP1, -jumpBackDist, envPtr);
- }
+ envPtr->currStackDepth = savedStackDepth;
/*
- * Fix the target of the jumpFalse after the test.
+ * Compile the test expression then emit the conditional jump that
+ * terminates the for.
*/
- jumpDist = (envPtr->codeNext - envPtr->codeStart)
- - jumpFalseFixup.codeOffset;
- if (TclFixupForwardJump(envPtr, &jumpFalseFixup, jumpDist, 127)) {
- /*
- * Update the loop body and "next" command ExceptionRanges since
- * they moved down.
- */
-
- envPtr->exceptArrayPtr[bodyRange].codeOffset += 3;
- envPtr->exceptArrayPtr[bodyRange].continueOffset += 3;
- envPtr->exceptArrayPtr[nextRange].codeOffset += 3;
-
- /*
- * Update the jump back to the test at the top of the loop since it
- * also moved down 3 bytes.
- */
+ testCodeOffset = (envPtr->codeNext - envPtr->codeStart);
- jumpBackOffset += 3;
- jumpPc = (envPtr->codeStart + jumpBackOffset);
- jumpBackDist += 3;
- if (jumpBackDist > 120) {
- TclUpdateInstInt4AtPc(INST_JUMP4, -jumpBackDist, jumpPc);
- } else {
- TclUpdateInstInt1AtPc(INST_JUMP1, -jumpBackDist, jumpPc);
+ jumpDist = testCodeOffset - jumpEvalCondFixup.codeOffset;
+ if (TclFixupForwardJump(envPtr, &jumpEvalCondFixup, jumpDist, 127)) {
+ bodyCodeOffset += 3;
+ nextCodeOffset += 3;
+ testCodeOffset += 3;
+ }
+
+ envPtr->currStackDepth = savedStackDepth;
+ code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
+ if (code != TCL_OK) {
+ if (code == TCL_ERROR) {
+ Tcl_AddObjErrorInfo(interp,
+ "\n (\"for\" test expression)", -1);
}
+ goto done;
+ }
+ envPtr->currStackDepth = savedStackDepth + 1;
+
+ jumpDist = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset;
+ if (jumpDist > 127) {
+ TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr);
+ } else {
+ TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr);
}
/*
- * Set the loop's break target.
+ * Set the loop's offsets and break target.
*/
+ envPtr->exceptArrayPtr[bodyRange].codeOffset = bodyCodeOffset;
+ envPtr->exceptArrayPtr[bodyRange].continueOffset = nextCodeOffset;
+
+ envPtr->exceptArrayPtr[nextRange].codeOffset = nextCodeOffset;
+
envPtr->exceptArrayPtr[bodyRange].breakOffset =
envPtr->exceptArrayPtr[nextRange].breakOffset =
(envPtr->codeNext - envPtr->codeStart);
@@ -550,14 +667,11 @@ TclCompileForCmd(interp, parsePtr, envPtr)
* The for command's result is an empty string.
*/
- TclEmitPush(TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0), envPtr);
- if (maxDepth == 0) {
- maxDepth = 1;
- }
+ envPtr->currStackDepth = savedStackDepth;
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
code = TCL_OK;
done:
- envPtr->maxStackDepth = maxDepth;
envPtr->exceptDepth--;
return code;
}
@@ -578,14 +692,11 @@ TclCompileForCmd(interp, parsePtr, envPtr)
* should be compiled "out of line" by emitting code to invoke its
* command procedure at runtime.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the "while" command.
- *
* Side effects:
* Instructions are added to envPtr to execute the "foreach" command
* at runtime.
*
- *----------------------------------------------------------------------
+n*----------------------------------------------------------------------
*/
int
@@ -604,13 +715,12 @@ TclCompileForeachCmd(interp, parsePtr, envPtr)
int loopCtTemp; /* Index of temp var holding the loop's
* iteration count. */
Tcl_Token *tokenPtr, *bodyTokenPtr;
- char *varList;
unsigned char *jumpPc;
JumpFixup jumpFalseFixup;
- int jumpDist, jumpBackDist, jumpBackOffset, maxDepth, infoIndex, range;
+ int jumpDist, jumpBackDist, jumpBackOffset, infoIndex, range;
int numWords, numLists, numVars, loopIndex, tempVar, i, j, code;
- char savedChar;
char buffer[32 + TCL_INTEGER_SPACE];
+ int savedStackDepth = envPtr->currStackDepth;
/*
* We parse the variable list argument words and create two arrays:
@@ -620,22 +730,19 @@ TclCompileForeachCmd(interp, parsePtr, envPtr)
#define STATIC_VAR_LIST_SIZE 5
int varcListStaticSpace[STATIC_VAR_LIST_SIZE];
- char **varvListStaticSpace[STATIC_VAR_LIST_SIZE];
+ CONST char **varvListStaticSpace[STATIC_VAR_LIST_SIZE];
int *varcList = varcListStaticSpace;
- char ***varvList = varvListStaticSpace;
+ CONST char ***varvList = varvListStaticSpace;
/*
* If the foreach command isn't in a procedure, don't compile it inline:
* the payoff is too small.
*/
- envPtr->maxStackDepth = 0;
if (procPtr == NULL) {
return TCL_OUT_LINE_COMPILE;
}
- maxDepth = 0;
-
numWords = parsePtr->numWords;
if ((numWords < 4) || (numWords%2 != 0)) {
Tcl_ResetResult(interp);
@@ -645,17 +752,30 @@ TclCompileForeachCmd(interp, parsePtr, envPtr)
}
/*
+ * Bail out if the body requires substitutions
+ * in order to insure correct behaviour [Bug 219166]
+ */
+ for (i = 0, tokenPtr = parsePtr->tokenPtr;
+ i < numWords-1;
+ i++, tokenPtr += (tokenPtr->numComponents + 1)) {
+ }
+ bodyTokenPtr = tokenPtr;
+ if (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ /*
* Allocate storage for the varcList and varvList arrays if necessary.
*/
numLists = (numWords - 2)/2;
if (numLists > STATIC_VAR_LIST_SIZE) {
varcList = (int *) ckalloc(numLists * sizeof(int));
- varvList = (char ***) ckalloc(numLists * sizeof(char **));
+ varvList = (CONST char ***) ckalloc(numLists * sizeof(CONST char **));
}
for (loopIndex = 0; loopIndex < numLists; loopIndex++) {
varcList[loopIndex] = 0;
- varvList[loopIndex] = (char **) NULL;
+ varvList[loopIndex] = NULL;
}
/*
@@ -680,32 +800,29 @@ TclCompileForeachCmd(interp, parsePtr, envPtr)
if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
code = TCL_OUT_LINE_COMPILE;
goto done;
- }
- varList = tokenPtr[1].start;
- savedChar = varList[tokenPtr[1].size];
-
- /*
- * Note there is a danger that modifying the string could have
- * undesirable side effects. In this case, Tcl_SplitList does
- * not have any dependencies on shared strings so we should be
- * safe.
- */
+ } else {
+ /* Lots of copying going on here. Need a ListObj wizard
+ * to show a better way. */
- varList[tokenPtr[1].size] = '\0';
- code = Tcl_SplitList(interp, varList,
- &varcList[loopIndex], &varvList[loopIndex]);
- varList[tokenPtr[1].size] = savedChar;
- if (code != TCL_OK) {
- goto done;
- }
+ Tcl_DString varList;
- numVars = varcList[loopIndex];
- for (j = 0; j < numVars; j++) {
- char *varName = varvList[loopIndex][j];
- if (!TclIsLocalScalar(varName, (int) strlen(varName))) {
- code = TCL_OUT_LINE_COMPILE;
+ Tcl_DStringInit(&varList);
+ Tcl_DStringAppend(&varList, tokenPtr[1].start,
+ tokenPtr[1].size);
+ code = Tcl_SplitList(interp, Tcl_DStringValue(&varList),
+ &varcList[loopIndex], &varvList[loopIndex]);
+ Tcl_DStringFree(&varList);
+ if (code != TCL_OK) {
goto done;
}
+ numVars = varcList[loopIndex];
+ for (j = 0; j < numVars; j++) {
+ CONST char *varName = varvList[loopIndex][j];
+ if (!TclIsLocalScalar(varName, (int) strlen(varName))) {
+ code = TCL_OUT_LINE_COMPILE;
+ goto done;
+ }
+ }
}
loopIndex++;
}
@@ -749,7 +866,7 @@ TclCompileForeachCmd(interp, parsePtr, envPtr)
sizeof(ForeachVarList) + (numVars * sizeof(int)));
varListPtr->numVars = numVars;
for (j = 0; j < numVars; j++) {
- char *varName = varvList[loopIndex][j];
+ CONST char *varName = varvList[loopIndex][j];
int nameChars = strlen(varName);
varListPtr->varIndexes[j] = TclFindCompiledLocal(varName,
nameChars, /*create*/ 1, /*flags*/ VAR_SCALAR, procPtr);
@@ -774,7 +891,6 @@ TclCompileForeachCmd(interp, parsePtr, envPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
tempVar = (firstValueTemp + loopIndex);
if (tempVar <= 255) {
@@ -786,7 +902,6 @@ TclCompileForeachCmd(interp, parsePtr, envPtr)
loopIndex++;
}
}
- bodyTokenPtr = tokenPtr;
/*
* Initialize the temporary var that holds the count of loop iterations.
@@ -812,6 +927,7 @@ TclCompileForeachCmd(interp, parsePtr, envPtr)
(envPtr->codeNext - envPtr->codeStart);
code = TclCompileCmdWord(interp, bodyTokenPtr+1,
bodyTokenPtr->numComponents, envPtr);
+ envPtr->currStackDepth = savedStackDepth + 1;
if (code != TCL_OK) {
if (code == TCL_ERROR) {
sprintf(buffer, "\n (\"foreach\" body line %d)",
@@ -820,7 +936,6 @@ TclCompileForeachCmd(interp, parsePtr, envPtr)
}
goto done;
}
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
envPtr->exceptArrayPtr[range].numCodeBytes =
(envPtr->codeNext - envPtr->codeStart)
- envPtr->exceptArrayPtr[range].codeOffset;
@@ -881,22 +996,20 @@ TclCompileForeachCmd(interp, parsePtr, envPtr)
* The foreach command's result is an empty string.
*/
- TclEmitPush(TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0), envPtr);
- if (maxDepth == 0) {
- maxDepth = 1;
- }
+ envPtr->currStackDepth = savedStackDepth;
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
+ envPtr->currStackDepth = savedStackDepth + 1;
done:
for (loopIndex = 0; loopIndex < numLists; loopIndex++) {
- if (varvList[loopIndex] != (char **) NULL) {
- ckfree((char *) varvList[loopIndex]);
- }
+ if (varvList[loopIndex] != (CONST char **) NULL) {
+ ckfree((char *) varvList[loopIndex]);
+ }
}
if (varcList != varcListStaticSpace) {
ckfree((char *) varcList);
ckfree((char *) varvList);
}
- envPtr->maxStackDepth = maxDepth;
envPtr->exceptDepth--;
return code;
}
@@ -1005,16 +1118,12 @@ FreeForeachInfo(clientData)
* should be compiled "out of line" by emitting code to invoke its
* command procedure at runtime.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the command.
- *
* Side effects:
* Instructions are added to envPtr to execute the "if" command
* at runtime.
*
*----------------------------------------------------------------------
*/
-
int
TclCompileIfCmd(interp, parsePtr, envPtr)
Tcl_Interp *interp; /* Used for error reporting. */
@@ -1030,14 +1139,38 @@ TclCompileIfCmd(interp, parsePtr, envPtr)
* body to the end of the "if" when that PC
* is determined. */
Tcl_Token *tokenPtr, *testTokenPtr;
- int jumpDist, jumpFalseDist, jumpIndex;
- int numWords, wordIdx, numBytes, maxDepth, j, code;
- char *word;
+ int jumpDist, jumpFalseDist;
+ int jumpIndex = 0; /* avoid compiler warning. */
+ int numWords, wordIdx, numBytes, j, code;
+ CONST char *word;
char buffer[100];
+ int savedStackDepth = envPtr->currStackDepth;
+ /* Saved stack depth at the start of the first
+ * test; the envPtr current depth is restored
+ * to this value at the start of each test. */
+ int realCond = 1; /* set to 0 for static conditions: "if 0 {..}" */
+ int boolVal; /* value of static condition */
+ int compileScripts = 1;
+
+ /*
+ * Only compile the "if" command if all arguments are simple
+ * words, in order to insure correct substitution [Bug 219166]
+ */
+
+ tokenPtr = parsePtr->tokenPtr;
+ wordIdx = 0;
+ numWords = parsePtr->numWords;
+
+ for (wordIdx = 0; wordIdx < numWords; wordIdx++) {
+ if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+ tokenPtr += 2;
+ }
+
TclInitJumpFixupArray(&jumpFalseFixupArray);
TclInitJumpFixupArray(&jumpEndFixupArray);
- maxDepth = 0;
code = TCL_OK;
/*
@@ -1047,15 +1180,11 @@ TclCompileIfCmd(interp, parsePtr, envPtr)
tokenPtr = parsePtr->tokenPtr;
wordIdx = 0;
- numWords = parsePtr->numWords;
while (wordIdx < numWords) {
/*
* Stop looping if the token isn't "if" or "elseif".
*/
- if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
- break;
- }
word = tokenPtr[1].start;
numBytes = tokenPtr[1].size;
if ((tokenPtr == parsePtr->tokenPtr)
@@ -1077,28 +1206,52 @@ TclCompileIfCmd(interp, parsePtr, envPtr)
/*
* Compile the test expression then emit the conditional jump
- * around the "then" part. If the expression word isn't simple,
- * we back off and compile the if command out-of-line.
+ * around the "then" part.
*/
+ envPtr->currStackDepth = savedStackDepth;
testTokenPtr = tokenPtr;
- code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
- if (code != TCL_OK) {
- if (code == TCL_ERROR) {
- Tcl_AddObjErrorInfo(interp,
- "\n (\"if\" test expression)", -1);
+
+
+ if (realCond) {
+ /*
+ * Find out if the condition is a constant.
+ */
+
+ Tcl_Obj *boolObj = Tcl_NewStringObj(testTokenPtr[1].start,
+ testTokenPtr[1].size);
+ Tcl_IncrRefCount(boolObj);
+ code = Tcl_GetBooleanFromObj(NULL, boolObj, &boolVal);
+ Tcl_DecrRefCount(boolObj);
+ if (code == TCL_OK) {
+ /*
+ * A static condition
+ */
+ realCond = 0;
+ if (!boolVal) {
+ compileScripts = 0;
+ }
+ } else {
+ Tcl_ResetResult(interp);
+ code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
+ if (code != TCL_OK) {
+ if (code == TCL_ERROR) {
+ Tcl_AddObjErrorInfo(interp,
+ "\n (\"if\" test expression)", -1);
+ }
+ goto done;
+ }
+ if (jumpFalseFixupArray.next >= jumpFalseFixupArray.end) {
+ TclExpandJumpFixupArray(&jumpFalseFixupArray);
+ }
+ jumpIndex = jumpFalseFixupArray.next;
+ jumpFalseFixupArray.next++;
+ TclEmitForwardJump(envPtr, TCL_FALSE_JUMP,
+ &(jumpFalseFixupArray.fixup[jumpIndex]));
}
- goto done;
- }
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
- if (jumpFalseFixupArray.next >= jumpFalseFixupArray.end) {
- TclExpandJumpFixupArray(&jumpFalseFixupArray);
}
- jumpIndex = jumpFalseFixupArray.next;
- jumpFalseFixupArray.next++;
- TclEmitForwardJump(envPtr, TCL_FALSE_JUMP,
- &(jumpFalseFixupArray.fixup[jumpIndex]));
-
+
+
/*
* Skip over the optional "then" before the then clause.
*/
@@ -1132,56 +1285,83 @@ TclCompileIfCmd(interp, parsePtr, envPtr)
* Compile the "then" command body.
*/
- code = TclCompileCmdWord(interp, tokenPtr+1,
- tokenPtr->numComponents, envPtr);
- if (code != TCL_OK) {
- if (code == TCL_ERROR) {
- sprintf(buffer, "\n (\"if\" then script line %d)",
- interp->errorLine);
- Tcl_AddObjErrorInfo(interp, buffer, -1);
- }
- goto done;
+ if (compileScripts) {
+ envPtr->currStackDepth = savedStackDepth;
+ code = TclCompileCmdWord(interp, tokenPtr+1,
+ tokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ if (code == TCL_ERROR) {
+ sprintf(buffer, "\n (\"if\" then script line %d)",
+ interp->errorLine);
+ Tcl_AddObjErrorInfo(interp, buffer, -1);
+ }
+ goto done;
+ }
}
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
- /*
- * Jump to the end of the "if" command. Both jumpFalseFixupArray and
- * jumpEndFixupArray are indexed by "jumpIndex".
- */
-
- if (jumpEndFixupArray.next >= jumpEndFixupArray.end) {
- TclExpandJumpFixupArray(&jumpEndFixupArray);
- }
- jumpEndFixupArray.next++;
- TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,
- &(jumpEndFixupArray.fixup[jumpIndex]));
+ if (realCond) {
+ /*
+ * Jump to the end of the "if" command. Both jumpFalseFixupArray and
+ * jumpEndFixupArray are indexed by "jumpIndex".
+ */
+
+ if (jumpEndFixupArray.next >= jumpEndFixupArray.end) {
+ TclExpandJumpFixupArray(&jumpEndFixupArray);
+ }
+ jumpEndFixupArray.next++;
+ TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,
+ &(jumpEndFixupArray.fixup[jumpIndex]));
+
+ /*
+ * Fix the target of the jumpFalse after the test. Generate a 4 byte
+ * jump if the distance is > 120 bytes. This is conservative, and
+ * ensures that we won't have to replace this jump if we later also
+ * need to replace the proceeding jump to the end of the "if" with a
+ * 4 byte jump.
+ */
- /*
- * Fix the target of the jumpFalse after the test. Generate a 4 byte
- * jump if the distance is > 120 bytes. This is conservative, and
- * ensures that we won't have to replace this jump if we later also
- * need to replace the proceeding jump to the end of the "if" with a
- * 4 byte jump.
- */
+ jumpDist = (envPtr->codeNext - envPtr->codeStart)
+ - jumpFalseFixupArray.fixup[jumpIndex].codeOffset;
+ if (TclFixupForwardJump(envPtr,
+ &(jumpFalseFixupArray.fixup[jumpIndex]), jumpDist, 120)) {
+ /*
+ * Adjust the code offset for the proceeding jump to the end
+ * of the "if" command.
+ */
+
+ jumpEndFixupArray.fixup[jumpIndex].codeOffset += 3;
+ }
+ } else if (boolVal) {
+ /*
+ *We were processing an "if 1 {...}"; stop compiling
+ * scripts
+ */
- jumpDist = (envPtr->codeNext - envPtr->codeStart)
- - jumpFalseFixupArray.fixup[jumpIndex].codeOffset;
- if (TclFixupForwardJump(envPtr,
- &(jumpFalseFixupArray.fixup[jumpIndex]), jumpDist, 120)) {
- /*
- * Adjust the code offset for the proceeding jump to the end
- * of the "if" command.
+ compileScripts = 0;
+ } else {
+ /*
+ *We were processing an "if 0 {...}"; reset so that
+ * the rest (elseif, else) is compiled correctly
*/
- jumpEndFixupArray.fixup[jumpIndex].codeOffset += 3;
- }
+ realCond = 1;
+ compileScripts = 1;
+ }
tokenPtr += (tokenPtr->numComponents + 1);
wordIdx++;
}
/*
- * Check for the optional else clause.
+ * Restore the current stack depth in the environment; the
+ * "else" clause (or its default) will add 1 to this.
+ */
+
+ envPtr->currStackDepth = savedStackDepth;
+
+ /*
+ * Check for the optional else clause. Do not compile
+ * anything if this was an "if 1 {...}" case.
*/
if ((wordIdx < numWords)
@@ -1189,7 +1369,7 @@ TclCompileIfCmd(interp, parsePtr, envPtr)
/*
* There is an else clause. Skip over the optional "else" word.
*/
-
+
word = tokenPtr[1].start;
numBytes = tokenPtr[1].size;
if ((numBytes == 4) && (strncmp(word, "else", 4) == 0)) {
@@ -1204,21 +1384,22 @@ TclCompileIfCmd(interp, parsePtr, envPtr)
}
}
- /*
- * Compile the else command body.
- */
-
- code = TclCompileCmdWord(interp, tokenPtr+1,
- tokenPtr->numComponents, envPtr);
- if (code != TCL_OK) {
- if (code == TCL_ERROR) {
- sprintf(buffer, "\n (\"if\" else script line %d)",
- interp->errorLine);
- Tcl_AddObjErrorInfo(interp, buffer, -1);
+ if (compileScripts) {
+ /*
+ * Compile the else command body.
+ */
+
+ code = TclCompileCmdWord(interp, tokenPtr+1,
+ tokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ if (code == TCL_ERROR) {
+ sprintf(buffer, "\n (\"if\" else script line %d)",
+ interp->errorLine);
+ Tcl_AddObjErrorInfo(interp, buffer, -1);
+ }
+ goto done;
}
- goto done;
}
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
/*
* Make sure there are no words after the else clause.
@@ -1237,8 +1418,9 @@ TclCompileIfCmd(interp, parsePtr, envPtr)
* No else clause: the "if" command's result is an empty string.
*/
- TclEmitPush(TclRegisterLiteral(envPtr, "", 0,/*onHeap*/ 0), envPtr);
- maxDepth = TclMax(1, maxDepth);
+ if (compileScripts) {
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
+ }
}
/*
@@ -1272,15 +1454,15 @@ TclCompileIfCmd(interp, parsePtr, envPtr)
}
}
}
-
+
/*
* Free the jumpFixupArray array if malloc'ed storage was used.
*/
done:
+ envPtr->currStackDepth = savedStackDepth + 1;
TclFreeJumpFixupArray(&jumpFalseFixupArray);
TclFreeJumpFixupArray(&jumpEndFixupArray);
- envPtr->maxStackDepth = maxDepth;
return code;
}
@@ -1300,9 +1482,6 @@ TclCompileIfCmd(interp, parsePtr, envPtr)
* should be compiled "out of line" by emitting code to invoke its
* command procedure at runtime.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the "incr" command.
- *
* Side effects:
* Instructions are added to envPtr to execute the "incr" command
* at runtime.
@@ -1318,119 +1497,26 @@ TclCompileIncrCmd(interp, parsePtr, envPtr)
CompileEnv *envPtr; /* Holds resulting instructions. */
{
Tcl_Token *varTokenPtr, *incrTokenPtr;
- Tcl_Parse elemParse;
- int gotElemParse = 0;
- char *name, *elName, *p;
- int nameChars, elNameChars, haveImmValue, immValue, localIndex, i, code;
- int maxDepth = 0;
- char buffer[160];
-
- envPtr->maxStackDepth = 0;
+ int simpleVarName, isScalar, localIndex, haveImmValue, immValue;
+ int code = TCL_OK;
+
if ((parsePtr->numWords != 2) && (parsePtr->numWords != 3)) {
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp),
"wrong # args: should be \"incr varName ?increment?\"", -1);
return TCL_ERROR;
}
-
- name = NULL;
- elName = NULL;
- elNameChars = 0;
- localIndex = -1;
- code = TCL_OK;
varTokenPtr = parsePtr->tokenPtr
+ (parsePtr->tokenPtr->numComponents + 1);
- /*
- * Check not only that the type is TCL_TOKEN_SIMPLE_WORD, but whether
- * curly braces surround the variable name.
- * This really matters for array elements to handle things like
- * set {x($foo)} 5
- * which raises an undefined var error if we are not careful here.
- * This goes with the hack in TclCompileSetCmd.
- */
- if ((varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) &&
- (varTokenPtr->start[0] != '{')) {
- /*
- * A simple variable name. Divide it up into "name" and "elName"
- * strings. If it is not a local variable, look it up at runtime.
- */
-
- name = varTokenPtr[1].start;
- nameChars = varTokenPtr[1].size;
- for (i = 0, p = name; i < nameChars; i++, p++) {
- if (*p == '(') {
- char *openParen = p;
- p = (name + nameChars-1);
- if (*p == ')') { /* last char is ')' => array reference */
- nameChars = (openParen - name);
- elName = openParen+1;
- elNameChars = (p - elName);
- }
- break;
- }
- }
- if (envPtr->procPtr != NULL) {
- localIndex = TclFindCompiledLocal(name, nameChars,
- /*create*/ 0, /*flags*/ 0, envPtr->procPtr);
- if (localIndex > 255) { /* we'll push the name */
- localIndex = -1;
- }
- }
- if (localIndex < 0) {
- TclEmitPush(TclRegisterLiteral(envPtr, name, nameChars,
- /*onHeap*/ 0), envPtr);
- maxDepth = 1;
- }
- /*
- * Compile the element script, if any.
- */
-
- if (elName != NULL) {
- /*
- * Temporarily replace the '(' and ')' by '"'s.
- */
-
- *(elName-1) = '"';
- *(elName+elNameChars) = '"';
- code = Tcl_ParseCommand(interp, elName-1, elNameChars+2,
- /*nested*/ 0, &elemParse);
- *(elName-1) = '(';
- *(elName+elNameChars) = ')';
- gotElemParse = 1;
- if ((code != TCL_OK) || (elemParse.numWords > 1)) {
- sprintf(buffer, "\n (parsing index for array \"%.*s\")",
- TclMin(nameChars, 100), name);
- Tcl_AddObjErrorInfo(interp, buffer, -1);
- code = TCL_ERROR;
- goto done;
- } else if (elemParse.numWords == 1) {
- code = TclCompileTokens(interp, elemParse.tokenPtr+1,
- elemParse.tokenPtr->numComponents, envPtr);
- if (code != TCL_OK) {
- goto done;
- }
- maxDepth += envPtr->maxStackDepth;
- } else {
- TclEmitPush(TclRegisterLiteral(envPtr, "", 0,
- /*alreadyAlloced*/ 0), envPtr);
- maxDepth += 1;
- }
- }
- } else {
- /*
- * Not a simple variable name. Look it up at runtime.
- */
-
- code = TclCompileTokens(interp, varTokenPtr+1,
- varTokenPtr->numComponents, envPtr);
- if (code != TCL_OK) {
- goto done;
- }
- maxDepth = envPtr->maxStackDepth;
+ code = TclPushVarName(interp, varTokenPtr, envPtr,
+ (TCL_NO_LARGE_INDEX | TCL_CREATE_VAR),
+ &localIndex, &simpleVarName, &isScalar);
+ if (code != TCL_OK) {
+ goto done;
}
-
+
/*
* If an increment is given, push it, but see first if it's a small
* integer.
@@ -1441,11 +1527,11 @@ TclCompileIncrCmd(interp, parsePtr, envPtr)
if (parsePtr->numWords == 3) {
incrTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
if (incrTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
- char *word = incrTokenPtr[1].start;
+ CONST char *word = incrTokenPtr[1].start;
int numBytes = incrTokenPtr[1].size;
- char savedChar = word[numBytes];
+ int validLength = TclParseInteger(word, numBytes);
long n;
-
+
/*
* Note there is a danger that modifying the string could have
* undesirable side effects. In this case, TclLooksLikeInt and
@@ -1453,19 +1539,20 @@ TclCompileIncrCmd(interp, parsePtr, envPtr)
* should be safe.
*/
- word[numBytes] = '\0';
- if (TclLooksLikeInt(word, numBytes)
- && (TclGetLong((Tcl_Interp *) NULL, word, &n) == TCL_OK)) {
- if ((-127 <= n) && (n <= 127)) {
+ if (validLength == numBytes) {
+ int code;
+ Tcl_Obj *longObj = Tcl_NewStringObj(word, numBytes);
+ Tcl_IncrRefCount(longObj);
+ code = Tcl_GetLongFromObj(NULL, longObj, &n);
+ Tcl_DecrRefCount(longObj);
+ if ((code == TCL_OK) && (-127 <= n) && (n <= 127)) {
haveImmValue = 1;
immValue = n;
}
}
- word[numBytes] = savedChar;
if (!haveImmValue) {
- TclEmitPush(TclRegisterLiteral(envPtr, word, numBytes,
- /*onHeap*/ 0), envPtr);
- maxDepth += 1;
+ TclEmitPush(
+ TclRegisterNewLiteral(envPtr, word, numBytes), envPtr);
}
} else {
code = TclCompileTokens(interp, incrTokenPtr+1,
@@ -1477,7 +1564,6 @@ TclCompileIncrCmd(interp, parsePtr, envPtr)
}
goto done;
}
- maxDepth += envPtr->maxStackDepth;
}
} else { /* no incr amount given so use 1 */
haveImmValue = 1;
@@ -1488,20 +1574,18 @@ TclCompileIncrCmd(interp, parsePtr, envPtr)
* Emit the instruction to increment the variable.
*/
- if (name != NULL) {
- if (elName == NULL) {
+ if (simpleVarName) {
+ if (isScalar) {
if (localIndex >= 0) {
if (haveImmValue) {
- TclEmitInstInt1(INST_INCR_SCALAR1_IMM, localIndex,
- envPtr);
+ TclEmitInstInt1(INST_INCR_SCALAR1_IMM, localIndex, envPtr);
TclEmitInt1(immValue, envPtr);
} else {
TclEmitInstInt1(INST_INCR_SCALAR1, localIndex, envPtr);
}
} else {
if (haveImmValue) {
- TclEmitInstInt1(INST_INCR_SCALAR_STK_IMM, immValue,
- envPtr);
+ TclEmitInstInt1(INST_INCR_SCALAR_STK_IMM, immValue, envPtr);
} else {
TclEmitOpcode(INST_INCR_SCALAR_STK, envPtr);
}
@@ -1509,16 +1593,14 @@ TclCompileIncrCmd(interp, parsePtr, envPtr)
} else {
if (localIndex >= 0) {
if (haveImmValue) {
- TclEmitInstInt1(INST_INCR_ARRAY1_IMM, localIndex,
- envPtr);
+ TclEmitInstInt1(INST_INCR_ARRAY1_IMM, localIndex, envPtr);
TclEmitInt1(immValue, envPtr);
} else {
TclEmitInstInt1(INST_INCR_ARRAY1, localIndex, envPtr);
}
} else {
if (haveImmValue) {
- TclEmitInstInt1(INST_INCR_ARRAY_STK_IMM, immValue,
- envPtr);
+ TclEmitInstInt1(INST_INCR_ARRAY_STK_IMM, immValue, envPtr);
} else {
TclEmitOpcode(INST_INCR_ARRAY_STK, envPtr);
}
@@ -1533,66 +1615,64 @@ TclCompileIncrCmd(interp, parsePtr, envPtr)
}
done:
- if (gotElemParse) {
- Tcl_FreeParse(&elemParse);
- }
- envPtr->maxStackDepth = maxDepth;
return code;
}
/*
*----------------------------------------------------------------------
*
- * TclCompileSetCmd --
+ * TclCompileLappendCmd --
*
- * Procedure called to compile the "set" command.
+ * Procedure called to compile the "lappend" command.
*
* Results:
* The return value is a standard Tcl result, which is normally TCL_OK
* unless there was an error while parsing string. If an error occurs
* then the interpreter's result contains a standard error message. If
- * complation fails because the set command requires a second level of
+ * complation fails because the command requires a second level of
* substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the
- * set command should be compiled "out of line" by emitting code to
- * invoke its command procedure (Tcl_SetCmd) at runtime.
- *
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the incr command.
+ * command should be compiled "out of line" by emitting code to
+ * invoke its command procedure (Tcl_LappendObjCmd) at runtime.
*
* Side effects:
- * Instructions are added to envPtr to execute the "set" command
+ * Instructions are added to envPtr to execute the "lappend" command
* at runtime.
*
*----------------------------------------------------------------------
*/
int
-TclCompileSetCmd(interp, parsePtr, envPtr)
+TclCompileLappendCmd(interp, parsePtr, envPtr)
Tcl_Interp *interp; /* Used for error reporting. */
Tcl_Parse *parsePtr; /* Points to a parse structure for the
* command created by Tcl_ParseCommand. */
CompileEnv *envPtr; /* Holds resulting instructions. */
{
Tcl_Token *varTokenPtr, *valueTokenPtr;
- Tcl_Parse elemParse;
- int gotElemParse = 0;
- register char *p;
- char *name, *elName;
- int nameChars, elNameChars;
- register int i, n;
- int isAssignment, simpleVarName, localIndex, numWords;
- int maxDepth = 0;
+ int numValues, simpleVarName, isScalar, localIndex, numWords;
int code = TCL_OK;
- envPtr->maxStackDepth = 0;
+ /*
+ * If we're not in a procedure, don't compile.
+ */
+ if (envPtr->procPtr == NULL) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+
numWords = parsePtr->numWords;
- if ((numWords != 2) && (numWords != 3)) {
+ if (numWords == 1) {
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp),
- "wrong # args: should be \"set varName ?newValue?\"", -1);
- return TCL_ERROR;
+ "wrong # args: should be \"lappend varName ?value value ...?\"", -1);
+ return TCL_ERROR;
}
- isAssignment = (numWords == 3);
+ if (numWords != 3) {
+ /*
+ * LAPPEND instructions currently only handle one value appends
+ */
+ return TCL_OUT_LINE_COMPILE;
+ }
+ numValues = (numWords - 2);
/*
* Decide if we can use a frame slot for the var/array name or if we
@@ -1602,196 +1682,852 @@ TclCompileSetCmd(interp, parsePtr, envPtr)
* namespace qualifiers.
*/
- simpleVarName = 0;
- name = elName = NULL;
- nameChars = elNameChars = 0;
- localIndex = -1;
-
varTokenPtr = parsePtr->tokenPtr
+ (parsePtr->tokenPtr->numComponents + 1);
+
+ code = TclPushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR,
+ &localIndex, &simpleVarName, &isScalar);
+ if (code != TCL_OK) {
+ goto done;
+ }
+
/*
- * Check not only that the type is TCL_TOKEN_SIMPLE_WORD, but whether
- * curly braces surround the variable name.
- * This really matters for array elements to handle things like
- * set {x($foo)} 5
- * which raises an undefined var error if we are not careful here.
- * This goes with the hack in TclCompileIncrCmd.
+ * If we are doing an assignment, push the new value.
+ * In the no values case, create an empty object.
*/
- if ((varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) &&
- (varTokenPtr->start[0] != '{')) {
- simpleVarName = 1;
- name = varTokenPtr[1].start;
- nameChars = varTokenPtr[1].size;
- /* last char is ')' => potential array reference */
- if ( *(name + nameChars - 1) == ')') {
- for (i = 0, p = name; i < nameChars; i++, p++) {
- if (*p == '(') {
- elName = p + 1;
- elNameChars = nameChars - i - 2;
- nameChars = i ;
- break;
- }
+ if (numWords > 2) {
+ valueTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ TclEmitPush(TclRegisterNewLiteral(envPtr,
+ valueTokenPtr[1].start, valueTokenPtr[1].size), envPtr);
+ } else {
+ code = TclCompileTokens(interp, valueTokenPtr+1,
+ valueTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ goto done;
}
}
-
+#if 0
+ } else {
/*
- * If elName contains any double quotes ("), we can't inline
- * compile the element script using the replace '()' by '"'
- * technique below.
+ * We need to carefully handle the two arg case, as lappend
+ * always creates the variable.
*/
- for (i = 0, p = elName; i < elNameChars; i++, p++) {
- if (*p == '"') {
- simpleVarName = 0;
- break;
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
+ numValues = 1;
+#endif
+ }
+
+ /*
+ * Emit instructions to set/get the variable.
+ */
+
+ /*
+ * The *_STK opcodes should be refactored to make better use of existing
+ * LOAD/STORE instructions.
+ */
+ if (simpleVarName) {
+ if (isScalar) {
+ if (localIndex >= 0) {
+ if (localIndex <= 255) {
+ TclEmitInstInt1(INST_LAPPEND_SCALAR1, localIndex, envPtr);
+ } else {
+ TclEmitInstInt4(INST_LAPPEND_SCALAR4, localIndex, envPtr);
+ }
+ } else {
+ TclEmitOpcode(INST_LAPPEND_STK, envPtr);
+ }
+ } else {
+ if (localIndex >= 0) {
+ if (localIndex <= 255) {
+ TclEmitInstInt1(INST_LAPPEND_ARRAY1, localIndex, envPtr);
+ } else {
+ TclEmitInstInt4(INST_LAPPEND_ARRAY4, localIndex, envPtr);
+ }
+ } else {
+ TclEmitOpcode(INST_LAPPEND_ARRAY_STK, envPtr);
}
}
- } else if (((n = varTokenPtr->numComponents) > 1)
- && (varTokenPtr[1].type == TCL_TOKEN_TEXT)
- && (varTokenPtr[n].type == TCL_TOKEN_TEXT)
- && (varTokenPtr[n].start[varTokenPtr[n].size - 1] == ')')) {
- simpleVarName = 0;
+ } else {
+ TclEmitOpcode(INST_LAPPEND_STK, envPtr);
+ }
- /*
- * Check for parentheses inside first token
- */
- for (i = 0, p = varTokenPtr[1].start;
- i < varTokenPtr[1].size; i++, p++) {
- if (*p == '(') {
- simpleVarName = 1;
- break;
- }
- }
- if (simpleVarName) {
- name = varTokenPtr[1].start;
- nameChars = p - varTokenPtr[1].start;
- elName = p + 1;
- elNameChars = (varTokenPtr[n].start - p) + varTokenPtr[n].size - 2;
+ done:
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileLindexCmd --
+ *
+ * Procedure called to compile the "lindex" command.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is TCL_OK if the
+ * compilation was successful. If the command cannot be byte-compiled,
+ * TCL_OUT_LINE_COMPILE is returned. If an error occurs then the
+ * interpreter's result contains an error message, and TCL_ERROR is
+ * returned.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute the "lindex" command
+ * at runtime.
+ *
+ *----------------------------------------------------------------------
+ */
- /*
- * If elName contains any double quotes ("), we can't inline
- * compile the element script using the replace '()' by '"'
- * technique below.
- */
-
- for (i = 0, p = elName; i < elNameChars; i++, p++) {
- if (*p == '"') {
- simpleVarName = 0;
- break;
- }
- }
- }
+int
+TclCompileLindexCmd(interp, parsePtr, envPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Parse *parsePtr; /* Points to a parse structure for the
+ * command created by Tcl_ParseCommand. */
+ CompileEnv *envPtr; /* Holds resulting instructions. */
+{
+ Tcl_Token *varTokenPtr;
+ int code, i;
+
+ int numWords;
+ numWords = parsePtr->numWords;
+
+ /*
+ * Quit if too few args
+ */
+
+ if ( numWords <= 1 ) {
+ return TCL_OUT_LINE_COMPILE;
}
- if (simpleVarName) {
+ varTokenPtr = parsePtr->tokenPtr
+ + (parsePtr->tokenPtr->numComponents + 1);
+
+ /*
+ * Push the operands onto the stack.
+ */
+
+ for ( i = 1 ; i < numWords ; i++ ) {
+ if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ TclEmitPush(
+ TclRegisterNewLiteral( envPtr, varTokenPtr[1].start,
+ varTokenPtr[1].size), envPtr);
+ } else {
+ code = TclCompileTokens(interp, varTokenPtr+1,
+ varTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ return code;
+ }
+ }
+ varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ }
+
+ /*
+ * Emit INST_LIST_INDEX if objc==3, or INST_LIST_INDEX_MULTI
+ * if there are multiple index args.
+ */
+
+ if ( numWords == 3 ) {
+ TclEmitOpcode( INST_LIST_INDEX, envPtr );
+ } else {
+ TclEmitInstInt4( INST_LIST_INDEX_MULTI, numWords-1, envPtr );
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileListCmd --
+ *
+ * Procedure called to compile the "list" command.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is normally TCL_OK
+ * unless there was an error while parsing string. If an error occurs
+ * then the interpreter's result contains a standard error message. If
+ * complation fails because the command requires a second level of
+ * substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the
+ * command should be compiled "out of line" by emitting code to
+ * invoke its command procedure (Tcl_ListObjCmd) at runtime.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute the "list" command
+ * at runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileListCmd(interp, parsePtr, envPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Parse *parsePtr; /* Points to a parse structure for the
+ * command created by Tcl_ParseCommand. */
+ CompileEnv *envPtr; /* Holds resulting instructions. */
+{
+ /*
+ * If we're not in a procedure, don't compile.
+ */
+ if (envPtr->procPtr == NULL) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ if (parsePtr->numWords == 1) {
/*
- * See whether name has any namespace separators (::'s).
+ * Empty args case
*/
- int hasNsQualifiers = 0;
- for (i = 0, p = name; i < nameChars; i++, p++) {
- if ((*p == ':') && ((i+1) < nameChars) && (*(p+1) == ':')) {
- hasNsQualifiers = 1;
- break;
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
+ } else {
+ /*
+ * Push the all values onto the stack.
+ */
+ Tcl_Token *valueTokenPtr;
+ int i, code, numWords;
+
+ numWords = parsePtr->numWords;
+
+ valueTokenPtr = parsePtr->tokenPtr
+ + (parsePtr->tokenPtr->numComponents + 1);
+ for (i = 1; i < numWords; i++) {
+ if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ TclEmitPush(TclRegisterNewLiteral(envPtr,
+ valueTokenPtr[1].start, valueTokenPtr[1].size), envPtr);
+ } else {
+ code = TclCompileTokens(interp, valueTokenPtr+1,
+ valueTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ return code;
+ }
}
+ valueTokenPtr = valueTokenPtr + (valueTokenPtr->numComponents + 1);
}
-
+ TclEmitInstInt4(INST_LIST, numWords - 1, envPtr);
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileLlengthCmd --
+ *
+ * Procedure called to compile the "llength" command.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is TCL_OK if the
+ * compilation was successful. If the command cannot be byte-compiled,
+ * TCL_OUT_LINE_COMPILE is returned. If an error occurs then the
+ * interpreter's result contains an error message, and TCL_ERROR is
+ * returned.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute the "llength" command
+ * at runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileLlengthCmd(interp, parsePtr, envPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Parse *parsePtr; /* Points to a parse structure for the
+ * command created by Tcl_ParseCommand. */
+ CompileEnv *envPtr; /* Holds resulting instructions. */
+{
+ Tcl_Token *varTokenPtr;
+ int code;
+
+ if (parsePtr->numWords != 2) {
+ Tcl_SetResult(interp, "wrong # args: should be \"llength list\"",
+ TCL_STATIC);
+ return TCL_ERROR;
+ }
+ varTokenPtr = parsePtr->tokenPtr
+ + (parsePtr->tokenPtr->numComponents + 1);
+
+ if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
/*
- * Look up the var name's index in the array of local vars in the
- * proc frame. If retrieving the var's value and it doesn't already
- * exist, push its name and look it up at runtime.
+ * We could simply count the number of elements here and push
+ * that value, but that is too rare a case to waste the code space.
*/
+ TclEmitPush(TclRegisterNewLiteral(envPtr, varTokenPtr[1].start,
+ varTokenPtr[1].size), envPtr);
+ } else {
+ code = TclCompileTokens(interp, varTokenPtr+1,
+ varTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ return code;
+ }
+ }
+ TclEmitOpcode(INST_LIST_LENGTH, envPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileLsetCmd --
+ *
+ * Procedure called to compile the "lset" command.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is TCL_OK if
+ * the compilation was successful. If the "lset" command is too
+ * complex for this function, then TCL_OUT_LINE_COMPILE is returned,
+ * indicating that the command should be compiled "out of line"
+ * (that is, not byte-compiled). If an error occurs, TCL_ERROR is
+ * returned, and the interpreter result contains an error message.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute the "lset" command
+ * at runtime.
+ *
+ * The general template for execution of the "lset" command is:
+ * (1) Instructions to push the variable name, unless the
+ * variable is local to the stack frame.
+ * (2) If the variable is an array element, instructions
+ * to push the array element name.
+ * (3) Instructions to push each of zero or more "index" arguments
+ * to the stack, followed with the "newValue" element.
+ * (4) Instructions to duplicate the variable name and/or array
+ * element name onto the top of the stack, if either was
+ * pushed at steps (1) and (2).
+ * (5) The appropriate INST_LOAD_* instruction to place the
+ * original value of the list variable at top of stack.
+ * (6) At this point, the stack contains:
+ * varName? arrayElementName? index1 index2 ... newValue oldList
+ * The compiler emits one of INST_LSET_FLAT or INST_LSET_LIST
+ * according as whether there is exactly one index element (LIST)
+ * or either zero or else two or more (FLAT). This instruction
+ * removes everything from the stack except for the two names
+ * and pushes the new value of the variable.
+ * (7) Finally, INST_STORE_* stores the new value in the variable
+ * and cleans up the stack.
+ *
+ *----------------------------------------------------------------------
+ */
- if ((envPtr->procPtr != NULL) && !hasNsQualifiers) {
- localIndex = TclFindCompiledLocal(name, nameChars,
- /*create*/ isAssignment,
- /*flags*/ ((elName==NULL)? VAR_SCALAR : VAR_ARRAY),
- envPtr->procPtr);
+int
+TclCompileLsetCmd( interp, parsePtr, envPtr )
+ Tcl_Interp* interp; /* Tcl interpreter for error reporting */
+ Tcl_Parse* parsePtr; /* Points to a parse structure for
+ * the command */
+ CompileEnv* envPtr; /* Holds the resulting instructions */
+{
+
+ int tempDepth; /* Depth used for emitting one part
+ * of the code burst. */
+ Tcl_Token* varTokenPtr; /* Pointer to the Tcl_Token representing
+ * the parse of the variable name */
+
+ int result; /* Status return from library calls */
+
+ int localIndex; /* Index of var in local var table */
+ int simpleVarName; /* Flag == 1 if var name is simple */
+ int isScalar; /* Flag == 1 if scalar, 0 if array */
+
+ int i;
+
+ /* Check argument count */
+
+ if ( parsePtr->numWords < 3 ) {
+ /* Fail at run time, not in compilation */
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ /*
+ * Decide if we can use a frame slot for the var/array name or if we
+ * need to emit code to compute and push the name at runtime. We use a
+ * frame slot (entry in the array of local vars) if we are compiling a
+ * procedure body and if the name is simple text that does not include
+ * namespace qualifiers.
+ */
+
+ varTokenPtr = parsePtr->tokenPtr
+ + (parsePtr->tokenPtr->numComponents + 1);
+ result = TclPushVarName( interp, varTokenPtr, envPtr,
+ TCL_CREATE_VAR, &localIndex, &simpleVarName, &isScalar );
+ if (result != TCL_OK) {
+ return result;
+ }
+
+ /* Push the "index" args and the new element value. */
+
+ for ( i = 2; i < parsePtr->numWords; ++i ) {
+
+ /* Advance to next arg */
+
+ varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+
+ /* Push an arg */
+
+ if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ TclEmitPush(TclRegisterNewLiteral( envPtr, varTokenPtr[1].start,
+ varTokenPtr[1].size), envPtr);
+ } else {
+ result = TclCompileTokens(interp, varTokenPtr+1,
+ varTokenPtr->numComponents, envPtr);
+ if ( result != TCL_OK ) {
+ return result;
+ }
+ }
+ }
+
+ /*
+ * Duplicate the variable name if it's been pushed.
+ */
+
+ if ( !simpleVarName || localIndex < 0 ) {
+ if ( !simpleVarName || isScalar ) {
+ tempDepth = parsePtr->numWords - 2;
+ } else {
+ tempDepth = parsePtr->numWords - 1;
+ }
+ TclEmitInstInt4( INST_OVER, tempDepth, envPtr );
+ }
+
+ /*
+ * Duplicate an array index if one's been pushed
+ */
+
+ if ( simpleVarName && !isScalar ) {
+ if ( localIndex < 0 ) {
+ tempDepth = parsePtr->numWords - 1;
+ } else {
+ tempDepth = parsePtr->numWords - 2;
+ }
+ TclEmitInstInt4( INST_OVER, tempDepth, envPtr );
+ }
+
+ /*
+ * Emit code to load the variable's value.
+ */
+
+ if ( !simpleVarName ) {
+ TclEmitOpcode( INST_LOAD_STK, envPtr );
+ } else if ( isScalar ) {
+ if ( localIndex < 0 ) {
+ TclEmitOpcode( INST_LOAD_SCALAR_STK, envPtr );
+ } else if ( localIndex < 0x100 ) {
+ TclEmitInstInt1( INST_LOAD_SCALAR1, localIndex, envPtr );
+ } else {
+ TclEmitInstInt4( INST_LOAD_SCALAR4, localIndex, envPtr );
+ }
+ } else {
+ if ( localIndex < 0 ) {
+ TclEmitOpcode( INST_LOAD_ARRAY_STK, envPtr );
+ } else if ( localIndex < 0x100 ) {
+ TclEmitInstInt1( INST_LOAD_ARRAY1, localIndex, envPtr );
+ } else {
+ TclEmitInstInt4( INST_LOAD_ARRAY4, localIndex, envPtr );
+ }
+ }
+
+ /*
+ * Emit the correct variety of 'lset' instruction
+ */
+
+ if ( parsePtr->numWords == 4 ) {
+ TclEmitOpcode( INST_LSET_LIST, envPtr );
+ } else {
+ TclEmitInstInt4( INST_LSET_FLAT, (parsePtr->numWords - 1), envPtr );
+ }
+
+ /*
+ * Emit code to put the value back in the variable
+ */
+
+ if ( !simpleVarName ) {
+ TclEmitOpcode( INST_STORE_STK, envPtr );
+ } else if ( isScalar ) {
+ if ( localIndex < 0 ) {
+ TclEmitOpcode( INST_STORE_SCALAR_STK, envPtr );
+ } else if ( localIndex < 0x100 ) {
+ TclEmitInstInt1( INST_STORE_SCALAR1, localIndex, envPtr );
+ } else {
+ TclEmitInstInt4( INST_STORE_SCALAR4, localIndex, envPtr );
+ }
+ } else {
+ if ( localIndex < 0 ) {
+ TclEmitOpcode( INST_STORE_ARRAY_STK, envPtr );
+ } else if ( localIndex < 0x100 ) {
+ TclEmitInstInt1( INST_STORE_ARRAY1, localIndex, envPtr );
+ } else {
+ TclEmitInstInt4( INST_STORE_ARRAY4, localIndex, envPtr );
+ }
+ }
+
+ return TCL_OK;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileRegexpCmd --
+ *
+ * Procedure called to compile the "regexp" command.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is TCL_OK if
+ * the compilation was successful. If the "regexp" command is too
+ * complex for this function, then TCL_OUT_LINE_COMPILE is returned,
+ * indicating that the command should be compiled "out of line"
+ * (that is, not byte-compiled). If an error occurs, TCL_ERROR is
+ * returned, and the interpreter result contains an error message.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute the "regexp" command
+ * at runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileRegexpCmd(interp, parsePtr, envPtr)
+ Tcl_Interp* interp; /* Tcl interpreter for error reporting */
+ Tcl_Parse* parsePtr; /* Points to a parse structure for
+ * the command */
+ CompileEnv* envPtr; /* Holds the resulting instructions */
+{
+ Tcl_Token *varTokenPtr; /* Pointer to the Tcl_Token representing
+ * the parse of the RE or string */
+ int i, len, code, exactMatch, nocase;
+ Tcl_Obj *patternObj;
+ CONST char *str;
+
+ /*
+ * We are only interested in compiling simple regexp cases.
+ * Currently supported compile cases are:
+ * regexp ?-nocase? ?--? staticString $var
+ * regexp ?-nocase? ?--? {^staticString$} $var
+ */
+ if (parsePtr->numWords < 3) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ nocase = 0;
+ varTokenPtr = parsePtr->tokenPtr;
+
+ /*
+ * We only look for -nocase and -- as options. Everything else
+ * gets pushed to runtime execution. This is different than regexp's
+ * runtime option handling, but satisfies our stricter needs.
+ */
+ for (i = 1; i < parsePtr->numWords - 2; i++) {
+ varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+ /* Not a simple string - punt to runtime. */
+ return TCL_OUT_LINE_COMPILE;
}
- if (localIndex >= 0) {
- maxDepth = 0;
+ str = varTokenPtr[1].start;
+ len = varTokenPtr[1].size;
+ if ((len == 2) && (str[0] == '-') && (str[1] == '-')) {
+ i++;
+ break;
+ } else if ((len > 1)
+ && (strncmp(str, "-nocase", (unsigned) len) == 0)) {
+ nocase = 1;
} else {
- TclEmitPush(TclRegisterLiteral(envPtr, name, nameChars,
- /*onHeap*/ 0), envPtr);
- maxDepth = 1;
+ /* Not an option we recognize. */
+ return TCL_OUT_LINE_COMPILE;
}
+ }
+
+ if ((parsePtr->numWords - i) != 2) {
+ /* We don't support capturing to variables */
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ /*
+ * Get the regexp string. If it is not a simple string, punt to runtime.
+ * If it has a '-', it could be an incorrectly formed regexp command.
+ */
+ varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ str = varTokenPtr[1].start;
+ len = varTokenPtr[1].size;
+ if ((varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) || (*str == '-')) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+ if (len == 0) {
/*
- * Compile the element script, if any.
+ * The semantics of regexp are always match on re == "".
*/
-
- if (elName != NULL) {
- /*
- * Temporarily replace the '(' and ')' by '"'s.
- */
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "1", 1), envPtr);
+ return TCL_OK;
+ }
- *(elName-1) = '"';
- *(elName+elNameChars) = '"';
- code = Tcl_ParseCommand(interp, elName-1, elNameChars+2,
- /*nested*/ 0, &elemParse);
- *(elName-1) = '(';
- *(elName+elNameChars) = ')';
- gotElemParse = 1;
- if ((code != TCL_OK) || (elemParse.numWords > 1)) {
- char buffer[160];
- sprintf(buffer, "\n (parsing index for array \"%.*s\")",
- TclMin(nameChars, 100), name);
- Tcl_AddObjErrorInfo(interp, buffer, -1);
- code = TCL_ERROR;
- goto done;
- } else if (elemParse.numWords == 1) {
- code = TclCompileTokens(interp, elemParse.tokenPtr+1,
- elemParse.tokenPtr->numComponents, envPtr);
- if (code != TCL_OK) {
- goto done;
- }
- maxDepth += envPtr->maxStackDepth;
- } else {
- TclEmitPush(TclRegisterLiteral(envPtr, "", 0,
- /*alreadyAlloced*/ 0), envPtr);
- maxDepth += 1;
- }
- }
+ /*
+ * On the first (pattern) arg, check to see if any RE special characters
+ * are in the word. If not, this is the same as 'string equal'.
+ * We can use strchr here because the glob chars are all in the ascii-7
+ * range. If -nocase was specified, we can't do this because INST_STR_EQ
+ * has no support for nocase.
+ */
+
+ if (Tcl_RegExpCompile(NULL, str) == NULL) {
+ /*
+ * This is a bad RE. Let it complain at runtime.
+ */
+ return TCL_OUT_LINE_COMPILE;
+ }
+#if 0
+ if ((len > 2) && (*str == '.') && (str[1] == '*')) {
+ str += 2; len -= 2;
+ }
+ if ((len > 2) && (str[len-3] != '\\')
+ && (str[len-2] == '.') && (str[len-1] == '*')) {
+ len -= 2;
+ }
+#endif
+ if ((len > 1) && (str[0] == '^') && (str[len-1] == '$')
+ && (str[len-2] != '\\')) {
+ /*
+ * It appears and exact search was requested (ie ^foo$), so strip
+ * off the special chars and signal exactMatch.
+ */
+ str++; len -= 2;
+ exactMatch = 1;
+ } else {
+ exactMatch = 0;
+ }
+
+ patternObj = Tcl_NewStringObj(str, len);
+ Tcl_IncrRefCount(patternObj);
+ code = (strpbrk(Tcl_GetString(patternObj), "*+?{}()[].\\|^$") != NULL);
+ Tcl_DecrRefCount(patternObj);
+ if (code) {
+ /* We don't do anything with REs with special chars yet. */
+ return TCL_OUT_LINE_COMPILE;
+ }
+ if (exactMatch) {
+ TclEmitPush(TclRegisterNewLiteral(envPtr, str, len), envPtr);
} else {
/*
- * The var name isn't simple: compile and push it.
+ * This needs to find the substring anywhere in the string, so
+ * use string match and *foo*.
*/
+ char *newStr = ckalloc((unsigned) len + 3);
+ newStr[0] = '*';
+ strncpy(newStr + 1, str, (size_t) len);
+ newStr[len+1] = '*';
+ newStr[len+2] = '\0';
+ TclEmitPush(TclRegisterNewLiteral(envPtr, newStr, len+2), envPtr);
+ ckfree((char *) newStr);
+ }
+ /*
+ * Push the string arg
+ */
+ varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ TclEmitPush(TclRegisterNewLiteral(envPtr,
+ varTokenPtr[1].start, varTokenPtr[1].size), envPtr);
+ } else {
code = TclCompileTokens(interp, varTokenPtr+1,
varTokenPtr->numComponents, envPtr);
if (code != TCL_OK) {
- goto done;
+ return code;
}
- maxDepth += envPtr->maxStackDepth;
}
-
+
+ if (exactMatch && !nocase) {
+ TclEmitOpcode(INST_STR_EQ, envPtr);
+ } else {
+ TclEmitInstInt1(INST_STR_MATCH, nocase, envPtr);
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileReturnCmd --
+ *
+ * Procedure called to compile the "return" command.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is TCL_OK if the
+ * compilation was successful. If the particular return command is
+ * too complex for this function (ie, return with any flags like "-code"
+ * or "-errorinfo"), TCL_OUT_LINE_COMPILE is returned, indicating that
+ * the command should be compiled "out of line" (eg, not byte compiled).
+ * If an error occurs then the interpreter's result contains a standard
+ * error message.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute the "return" command
+ * at runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileReturnCmd(interp, parsePtr, envPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Parse *parsePtr; /* Points to a parse structure for the
+ * command created by Tcl_ParseCommand. */
+ CompileEnv *envPtr; /* Holds resulting instructions. */
+{
+ Tcl_Token *varTokenPtr;
+ int code;
+
+ /*
+ * If we're not in a procedure, don't compile.
+ */
+
+ if (envPtr->procPtr == NULL) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ switch (parsePtr->numWords) {
+ case 1: {
+ /*
+ * Simple case: [return]
+ * Just push the literal string "".
+ */
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
+ break;
+ }
+ case 2: {
+ /*
+ * More complex cases:
+ * [return "foo"]
+ * [return $value]
+ * [return [otherCmd]]
+ */
+ varTokenPtr = parsePtr->tokenPtr
+ + (parsePtr->tokenPtr->numComponents + 1);
+ if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ /*
+ * [return "foo"] case: the parse token is a simple word,
+ * so just push it.
+ */
+ TclEmitPush(TclRegisterNewLiteral(envPtr, varTokenPtr[1].start,
+ varTokenPtr[1].size), envPtr);
+ } else {
+ /*
+ * Parse token is more complex, so compile it; this handles the
+ * variable reference and nested command cases. If the
+ * parse token can be byte-compiled, then this instance of
+ * "return" will be byte-compiled; otherwise it will be
+ * out line compiled.
+ */
+ code = TclCompileTokens(interp, varTokenPtr+1,
+ varTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ return code;
+ }
+ }
+ break;
+ }
+ default: {
+ /*
+ * Most complex return cases: everything else, including
+ * [return -code error], etc.
+ */
+ return TCL_OUT_LINE_COMPILE;
+ }
+ }
+
+ /*
+ * The INST_DONE opcode actually causes the branching out of the
+ * subroutine, and takes the top stack item as the return result
+ * (which is why we pushed the value above).
+ */
+ TclEmitOpcode(INST_DONE, envPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileSetCmd --
+ *
+ * Procedure called to compile the "set" command.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is normally TCL_OK
+ * unless there was an error while parsing string. If an error occurs
+ * then the interpreter's result contains a standard error message. If
+ * complation fails because the set command requires a second level of
+ * substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the
+ * set command should be compiled "out of line" by emitting code to
+ * invoke its command procedure (Tcl_SetCmd) at runtime.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute the "set" command
+ * at runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileSetCmd(interp, parsePtr, envPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Parse *parsePtr; /* Points to a parse structure for the
+ * command created by Tcl_ParseCommand. */
+ CompileEnv *envPtr; /* Holds resulting instructions. */
+{
+ Tcl_Token *varTokenPtr, *valueTokenPtr;
+ int isAssignment, isScalar, simpleVarName, localIndex, numWords;
+ int code = TCL_OK;
+
+ numWords = parsePtr->numWords;
+ if ((numWords != 2) && (numWords != 3)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendToObj(Tcl_GetObjResult(interp),
+ "wrong # args: should be \"set varName ?newValue?\"", -1);
+ return TCL_ERROR;
+ }
+ isAssignment = (numWords == 3);
+
+ /*
+ * Decide if we can use a frame slot for the var/array name or if we
+ * need to emit code to compute and push the name at runtime. We use a
+ * frame slot (entry in the array of local vars) if we are compiling a
+ * procedure body and if the name is simple text that does not include
+ * namespace qualifiers.
+ */
+
+ varTokenPtr = parsePtr->tokenPtr
+ + (parsePtr->tokenPtr->numComponents + 1);
+
+ code = TclPushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR,
+ &localIndex, &simpleVarName, &isScalar);
+ if (code != TCL_OK) {
+ goto done;
+ }
+
/*
* If we are doing an assignment, push the new value.
*/
-
+
if (isAssignment) {
valueTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
- TclEmitPush(TclRegisterLiteral(envPtr, valueTokenPtr[1].start,
- valueTokenPtr[1].size, /*onHeap*/ 0), envPtr);
- maxDepth += 1;
+ TclEmitPush(TclRegisterNewLiteral(envPtr, valueTokenPtr[1].start,
+ valueTokenPtr[1].size), envPtr);
} else {
code = TclCompileTokens(interp, valueTokenPtr+1,
valueTokenPtr->numComponents, envPtr);
if (code != TCL_OK) {
goto done;
}
- maxDepth += envPtr->maxStackDepth;
}
}
-
+
/*
* Emit instructions to set/get the variable.
*/
if (simpleVarName) {
- if (elName == NULL) {
+ if (isScalar) {
if (localIndex >= 0) {
if (localIndex <= 255) {
TclEmitInstInt1((isAssignment?
@@ -1804,8 +2540,7 @@ TclCompileSetCmd(interp, parsePtr, envPtr)
}
} else {
TclEmitOpcode((isAssignment?
- INST_STORE_SCALAR_STK : INST_LOAD_SCALAR_STK),
- envPtr);
+ INST_STORE_SCALAR_STK : INST_LOAD_SCALAR_STK), envPtr);
}
} else {
if (localIndex >= 0) {
@@ -1820,26 +2555,323 @@ TclCompileSetCmd(interp, parsePtr, envPtr)
}
} else {
TclEmitOpcode((isAssignment?
- INST_STORE_ARRAY_STK : INST_LOAD_ARRAY_STK),
- envPtr);
+ INST_STORE_ARRAY_STK : INST_LOAD_ARRAY_STK), envPtr);
}
}
} else {
- TclEmitOpcode((isAssignment? INST_STORE_STK : INST_LOAD_STK),
- envPtr);
+ TclEmitOpcode((isAssignment? INST_STORE_STK : INST_LOAD_STK), envPtr);
}
done:
- if (gotElemParse) {
- Tcl_FreeParse(&elemParse);
- }
- envPtr->maxStackDepth = maxDepth;
return code;
}
/*
*----------------------------------------------------------------------
*
+ * TclCompileStringCmd --
+ *
+ * Procedure called to compile the "string" command.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is TCL_OK if the
+ * compilation was successful. If the command cannot be byte-compiled,
+ * TCL_OUT_LINE_COMPILE is returned. If an error occurs then the
+ * interpreter's result contains an error message, and TCL_ERROR is
+ * returned.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute the "string" command
+ * at runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileStringCmd(interp, parsePtr, envPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Parse *parsePtr; /* Points to a parse structure for the
+ * command created by Tcl_ParseCommand. */
+ CompileEnv *envPtr; /* Holds resulting instructions. */
+{
+ Tcl_Token *opTokenPtr, *varTokenPtr;
+ Tcl_Obj *opObj;
+ int index;
+ int code;
+
+ static CONST char *options[] = {
+ "bytelength", "compare", "equal", "first",
+ "index", "is", "last", "length",
+ "map", "match", "range", "repeat",
+ "replace", "tolower", "toupper", "totitle",
+ "trim", "trimleft", "trimright",
+ "wordend", "wordstart", (char *) NULL
+ };
+ enum options {
+ STR_BYTELENGTH, STR_COMPARE, STR_EQUAL, STR_FIRST,
+ STR_INDEX, STR_IS, STR_LAST, STR_LENGTH,
+ STR_MAP, STR_MATCH, STR_RANGE, STR_REPEAT,
+ STR_REPLACE, STR_TOLOWER, STR_TOUPPER, STR_TOTITLE,
+ STR_TRIM, STR_TRIMLEFT, STR_TRIMRIGHT,
+ STR_WORDEND, STR_WORDSTART
+ };
+
+ if (parsePtr->numWords < 2) {
+ /* Fail at run time, not in compilation */
+ return TCL_OUT_LINE_COMPILE;
+ }
+ opTokenPtr = parsePtr->tokenPtr
+ + (parsePtr->tokenPtr->numComponents + 1);
+
+ opObj = Tcl_NewStringObj(opTokenPtr->start, opTokenPtr->size);
+ if (Tcl_GetIndexFromObj(interp, opObj, options, "option", 0,
+ &index) != TCL_OK) {
+ Tcl_DecrRefCount(opObj);
+ Tcl_ResetResult(interp);
+ return TCL_OUT_LINE_COMPILE;
+ }
+ Tcl_DecrRefCount(opObj);
+
+ varTokenPtr = opTokenPtr + (opTokenPtr->numComponents + 1);
+
+ switch ((enum options) index) {
+ case STR_BYTELENGTH:
+ case STR_FIRST:
+ case STR_IS:
+ case STR_LAST:
+ case STR_MAP:
+ case STR_RANGE:
+ case STR_REPEAT:
+ case STR_REPLACE:
+ case STR_TOLOWER:
+ case STR_TOUPPER:
+ case STR_TOTITLE:
+ case STR_TRIM:
+ case STR_TRIMLEFT:
+ case STR_TRIMRIGHT:
+ case STR_WORDEND:
+ case STR_WORDSTART:
+ /*
+ * All other cases: compile out of line.
+ */
+ return TCL_OUT_LINE_COMPILE;
+
+ case STR_COMPARE:
+ case STR_EQUAL: {
+ int i;
+ /*
+ * If there are any flags to the command, we can't byte compile it
+ * because the INST_STR_EQ bytecode doesn't support flags.
+ */
+
+ if (parsePtr->numWords != 4) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ /*
+ * Push the two operands onto the stack.
+ */
+
+ for (i = 0; i < 2; i++) {
+ if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ TclEmitPush(TclRegisterNewLiteral(envPtr,
+ varTokenPtr[1].start, varTokenPtr[1].size), envPtr);
+ } else {
+ code = TclCompileTokens(interp, varTokenPtr+1,
+ varTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ return code;
+ }
+ }
+ varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ }
+
+ TclEmitOpcode(((((enum options) index) == STR_COMPARE) ?
+ INST_STR_CMP : INST_STR_EQ), envPtr);
+ return TCL_OK;
+ }
+ case STR_INDEX: {
+ int i;
+
+ if (parsePtr->numWords != 4) {
+ /* Fail at run time, not in compilation */
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ /*
+ * Push the two operands onto the stack.
+ */
+
+ for (i = 0; i < 2; i++) {
+ if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ TclEmitPush(TclRegisterNewLiteral(envPtr,
+ varTokenPtr[1].start, varTokenPtr[1].size), envPtr);
+ } else {
+ code = TclCompileTokens(interp, varTokenPtr+1,
+ varTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ return code;
+ }
+ }
+ varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ }
+
+ TclEmitOpcode(INST_STR_INDEX, envPtr);
+ return TCL_OK;
+ }
+ case STR_LENGTH: {
+ if (parsePtr->numWords != 3) {
+ /* Fail at run time, not in compilation */
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ /*
+ * Here someone is asking for the length of a static string.
+ * Just push the actual character (not byte) length.
+ */
+ char buf[TCL_INTEGER_SPACE];
+ int len = Tcl_NumUtfChars(varTokenPtr[1].start,
+ varTokenPtr[1].size);
+ len = sprintf(buf, "%d", len);
+ TclEmitPush(TclRegisterNewLiteral(envPtr, buf, len), envPtr);
+ return TCL_OK;
+ } else {
+ code = TclCompileTokens(interp, varTokenPtr+1,
+ varTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ return code;
+ }
+ }
+ TclEmitOpcode(INST_STR_LEN, envPtr);
+ return TCL_OK;
+ }
+ case STR_MATCH: {
+ int i, length, exactMatch = 0, nocase = 0;
+ CONST char *str;
+
+ if (parsePtr->numWords < 4 || parsePtr->numWords > 5) {
+ /* Fail at run time, not in compilation */
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ if (parsePtr->numWords == 5) {
+ if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+ str = varTokenPtr[1].start;
+ length = varTokenPtr[1].size;
+ if ((length > 1) &&
+ strncmp(str, "-nocase", (size_t) length) == 0) {
+ nocase = 1;
+ } else {
+ /* Fail at run time, not in compilation */
+ return TCL_OUT_LINE_COMPILE;
+ }
+ varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ str = varTokenPtr[1].start;
+ length = varTokenPtr[1].size;
+ if (!nocase && (i == 0)) {
+ /*
+ * On the first (pattern) arg, check to see if any
+ * glob special characters are in the word '*[]?\\'.
+ * If not, this is the same as 'string equal'. We
+ * can use strpbrk here because the glob chars are all
+ * in the ascii-7 range. If -nocase was specified,
+ * we can't do this because INST_STR_EQ has no support
+ * for nocase.
+ */
+ Tcl_Obj *copy = Tcl_NewStringObj(str, length);
+ Tcl_IncrRefCount(copy);
+ exactMatch = (strpbrk(Tcl_GetString(copy),
+ "*[]?\\") == NULL);
+ Tcl_DecrRefCount(copy);
+ }
+ TclEmitPush(
+ TclRegisterNewLiteral(envPtr, str, length), envPtr);
+ } else {
+ code = TclCompileTokens(interp, varTokenPtr+1,
+ varTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ return code;
+ }
+ }
+ varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ }
+
+ if (exactMatch) {
+ TclEmitOpcode(INST_STR_EQ, envPtr);
+ } else {
+ TclEmitInstInt1(INST_STR_MATCH, nocase, envPtr);
+ }
+ return TCL_OK;
+ }
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileVariableCmd --
+ *
+ * Procedure called to reserve the local variables for the
+ * "variable" command. The command itself is *not* compiled.
+ *
+ * Results:
+ * Always returns TCL_OUT_LINE_COMPILE.
+ *
+ * Side effects:
+ * Indexed local variables are added to the environment.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclCompileVariableCmd(interp, parsePtr, envPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Parse *parsePtr; /* Points to a parse structure for the
+ * command created by Tcl_ParseCommand. */
+ CompileEnv *envPtr; /* Holds resulting instructions. */
+{
+ Tcl_Token *varTokenPtr;
+ int i, numWords;
+ CONST char *varName, *tail;
+
+ if (envPtr->procPtr == NULL) {
+ return TCL_OUT_LINE_COMPILE;
+ }
+
+ numWords = parsePtr->numWords;
+
+ varTokenPtr = parsePtr->tokenPtr
+ + (parsePtr->tokenPtr->numComponents + 1);
+ for (i = 1; i < numWords; i += 2) {
+ if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+ varName = varTokenPtr[1].start;
+ tail = varName + varTokenPtr[1].size - 1;
+ if ((*tail == ')') || (tail < varName)) continue;
+ while ((tail > varName) && ((*tail != ':') || (*(tail-1) != ':'))) {
+ tail--;
+ }
+ if ((*tail == ':') && (tail > varName)) {
+ tail++;
+ }
+ (void) TclFindCompiledLocal(tail, (tail-varName+1),
+ /*create*/ 1, /*flags*/ 0, envPtr->procPtr);
+ varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+ }
+ }
+ return TCL_OUT_LINE_COMPILE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclCompileWhileCmd --
*
* Procedure called to compile the "while" command.
@@ -1853,9 +2885,6 @@ TclCompileSetCmd(interp, parsePtr, envPtr)
* indicating that the while command should be compiled "out of line"
* by emitting code to invoke its command procedure at runtime.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the "while" command.
- *
* Side effects:
* Instructions are added to envPtr to execute the "while" command
* at runtime.
@@ -1871,14 +2900,16 @@ TclCompileWhileCmd(interp, parsePtr, envPtr)
CompileEnv *envPtr; /* Holds resulting instructions. */
{
Tcl_Token *testTokenPtr, *bodyTokenPtr;
- JumpFixup jumpFalseFixup;
- unsigned char *jumpPc;
- int testCodeOffset, jumpDist, jumpBackDist, jumpBackOffset;
- int range, maxDepth, code;
+ JumpFixup jumpEvalCondFixup;
+ int testCodeOffset, bodyCodeOffset, jumpDist;
+ int range, code;
char buffer[32 + TCL_INTEGER_SPACE];
+ int savedStackDepth = envPtr->currStackDepth;
+ int loopMayEnd = 1; /* This is set to 0 if it is recognized as
+ * an infinite loop. */
+ Tcl_Obj *boolObj;
+ int boolVal;
- envPtr->maxStackDepth = 0;
- maxDepth = 0;
if (parsePtr->numWords != 3) {
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp),
@@ -1890,15 +2921,45 @@ TclCompileWhileCmd(interp, parsePtr, envPtr)
* If the test expression requires substitutions, don't compile the
* while command inline. E.g., the expression might cause the loop to
* never execute or execute forever, as in "while "$x < 5" {}".
+ *
+ * Bail out also if the body expression requires substitutions
+ * in order to insure correct behaviour [Bug 219166]
*/
testTokenPtr = parsePtr->tokenPtr
+ (parsePtr->tokenPtr->numComponents + 1);
- if (testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+ bodyTokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1);
+ if ((testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)
+ || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) {
return TCL_OUT_LINE_COMPILE;
}
/*
+ * Find out if the condition is a constant.
+ */
+
+ boolObj = Tcl_NewStringObj(testTokenPtr[1].start, testTokenPtr[1].size);
+ Tcl_IncrRefCount(boolObj);
+ code = Tcl_GetBooleanFromObj(NULL, boolObj, &boolVal);
+ Tcl_DecrRefCount(boolObj);
+ if (code == TCL_OK) {
+ if (boolVal) {
+ /*
+ * it is an infinite loop
+ */
+
+ loopMayEnd = 0;
+ } else {
+ /*
+ * This is an empty loop: "while 0 {...}" or such.
+ * Compile no bytecodes.
+ */
+
+ goto pushResult;
+ }
+ }
+
+ /*
* Create a ExceptionRange record for the loop body. This is used to
* implement break and continue.
*/
@@ -1907,36 +2968,37 @@ TclCompileWhileCmd(interp, parsePtr, envPtr)
envPtr->maxExceptDepth =
TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth);
range = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
- envPtr->exceptArrayPtr[range].continueOffset =
- (envPtr->codeNext - envPtr->codeStart);
/*
- * Compile the test expression then emit the conditional jump that
- * terminates the while. We already know it's a simple word.
+ * Jump to the evaluation of the condition. This code uses the "loop
+ * rotation" optimisation (which eliminates one branch from the loop).
+ * "while cond body" produces then:
+ * goto A
+ * B: body : bodyCodeOffset
+ * A: cond -> result : testCodeOffset, continueOffset
+ * if (result) goto B
+ *
+ * The infinite loop "while 1 body" produces:
+ * B: body : all three offsets here
+ * goto B
*/
- testCodeOffset = (envPtr->codeNext - envPtr->codeStart);
- envPtr->exceptArrayPtr[range].continueOffset = testCodeOffset;
- code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
- if (code != TCL_OK) {
- if (code == TCL_ERROR) {
- Tcl_AddObjErrorInfo(interp,
- "\n (\"while\" test expression)", -1);
- }
- goto error;
+ if (loopMayEnd) {
+ TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpEvalCondFixup);
+ testCodeOffset = 0; /* avoid compiler warning */
+ } else {
+ testCodeOffset = (envPtr->codeNext - envPtr->codeStart);
}
- maxDepth = envPtr->maxStackDepth;
- TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpFalseFixup);
+
/*
* Compile the loop body.
*/
- bodyTokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1);
- envPtr->exceptArrayPtr[range].codeOffset =
- (envPtr->codeNext - envPtr->codeStart);
+ bodyCodeOffset = (envPtr->codeNext - envPtr->codeStart);
code = TclCompileCmdWord(interp, bodyTokenPtr+1,
bodyTokenPtr->numComponents, envPtr);
+ envPtr->currStackDepth = savedStackDepth + 1;
if (code != TCL_OK) {
if (code == TCL_ERROR) {
sprintf(buffer, "\n (\"while\" body line %d)",
@@ -1945,59 +3007,55 @@ TclCompileWhileCmd(interp, parsePtr, envPtr)
}
goto error;
}
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
envPtr->exceptArrayPtr[range].numCodeBytes =
- (envPtr->codeNext - envPtr->codeStart)
- - envPtr->exceptArrayPtr[range].codeOffset;
+ (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset;
TclEmitOpcode(INST_POP, envPtr);
-
- /*
- * Jump back to the test at the top of the loop. Generate a 4 byte jump
- * if the distance to the test is > 120 bytes. This is conservative and
- * ensures that we won't have to replace this jump if we later need to
- * replace the ifFalse jump with a 4 byte jump.
- */
-
- jumpBackOffset = (envPtr->codeNext - envPtr->codeStart);
- jumpBackDist = (jumpBackOffset - testCodeOffset);
- if (jumpBackDist > 120) {
- TclEmitInstInt4(INST_JUMP4, -jumpBackDist, envPtr);
- } else {
- TclEmitInstInt1(INST_JUMP1, -jumpBackDist, envPtr);
- }
/*
- * Fix the target of the jumpFalse after the test.
+ * Compile the test expression then emit the conditional jump that
+ * terminates the while. We already know it's a simple word.
*/
- jumpDist = (envPtr->codeNext - envPtr->codeStart)
- - jumpFalseFixup.codeOffset;
- if (TclFixupForwardJump(envPtr, &jumpFalseFixup, jumpDist, 127)) {
- /*
- * Update the loop body's starting PC offset since it moved down.
- */
-
- envPtr->exceptArrayPtr[range].codeOffset += 3;
-
- /*
- * Update the jump back to the test at the top of the loop since it
- * also moved down 3 bytes.
- */
-
- jumpBackOffset += 3;
- jumpPc = (envPtr->codeStart + jumpBackOffset);
- jumpBackDist += 3;
- if (jumpBackDist > 120) {
- TclUpdateInstInt4AtPc(INST_JUMP4, -jumpBackDist, jumpPc);
+ if (loopMayEnd) {
+ testCodeOffset = (envPtr->codeNext - envPtr->codeStart);
+ jumpDist = testCodeOffset - jumpEvalCondFixup.codeOffset;
+ if (TclFixupForwardJump(envPtr, &jumpEvalCondFixup, jumpDist, 127)) {
+ bodyCodeOffset += 3;
+ testCodeOffset += 3;
+ }
+ envPtr->currStackDepth = savedStackDepth;
+ code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
+ if (code != TCL_OK) {
+ if (code == TCL_ERROR) {
+ Tcl_AddObjErrorInfo(interp,
+ "\n (\"while\" test expression)", -1);
+ }
+ goto error;
+ }
+ envPtr->currStackDepth = savedStackDepth + 1;
+
+ jumpDist = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset;
+ if (jumpDist > 127) {
+ TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr);
} else {
- TclUpdateInstInt1AtPc(INST_JUMP1, -jumpBackDist, jumpPc);
+ TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr);
}
+ } else {
+ jumpDist = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset;
+ if (jumpDist > 127) {
+ TclEmitInstInt4(INST_JUMP4, -jumpDist, envPtr);
+ } else {
+ TclEmitInstInt1(INST_JUMP1, -jumpDist, envPtr);
+ }
}
+
/*
- * Set the loop's break target.
+ * Set the loop's body, continue and break offsets.
*/
+ envPtr->exceptArrayPtr[range].continueOffset = testCodeOffset;
+ envPtr->exceptArrayPtr[range].codeOffset = bodyCodeOffset;
envPtr->exceptArrayPtr[range].breakOffset =
(envPtr->codeNext - envPtr->codeStart);
@@ -2005,19 +3063,259 @@ TclCompileWhileCmd(interp, parsePtr, envPtr)
* The while command's result is an empty string.
*/
- TclEmitPush(TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0), envPtr);
- if (maxDepth == 0) {
- maxDepth = 1;
- }
- envPtr->maxStackDepth = maxDepth;
+ pushResult:
+ envPtr->currStackDepth = savedStackDepth;
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
envPtr->exceptDepth--;
return TCL_OK;
error:
- envPtr->maxStackDepth = maxDepth;
envPtr->exceptDepth--;
return code;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclPushVarName --
+ *
+ * Procedure used in the compiling where pushing a variable name
+ * is necessary (append, lappend, set).
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is normally TCL_OK
+ * unless there was an error while parsing string. If an error occurs
+ * then the interpreter's result contains a standard error message.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute the "set" command
+ * at runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TclPushVarName(interp, varTokenPtr, envPtr, flags, localIndexPtr,
+ simpleVarNamePtr, isScalarPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Token *varTokenPtr; /* Points to a variable token. */
+ CompileEnv *envPtr; /* Holds resulting instructions. */
+ int flags; /* takes TCL_CREATE_VAR or
+ * TCL_NO_LARGE_INDEX */
+ int *localIndexPtr; /* must not be NULL */
+ int *simpleVarNamePtr; /* must not be NULL */
+ int *isScalarPtr; /* must not be NULL */
+{
+ register CONST char *p;
+ CONST char *name, *elName;
+ register int i, n;
+ int nameChars, elNameChars, simpleVarName, localIndex;
+ int code = TCL_OK;
+
+ Tcl_Token *elemTokenPtr = NULL;
+ int elemTokenCount = 0;
+ int allocedTokens = 0;
+ int removedParen = 0;
+ /*
+ * Decide if we can use a frame slot for the var/array name or if we
+ * need to emit code to compute and push the name at runtime. We use a
+ * frame slot (entry in the array of local vars) if we are compiling a
+ * procedure body and if the name is simple text that does not include
+ * namespace qualifiers.
+ */
+ simpleVarName = 0;
+ name = elName = NULL;
+ nameChars = elNameChars = 0;
+ localIndex = -1;
+ /*
+ * Check not only that the type is TCL_TOKEN_SIMPLE_WORD, but whether
+ * curly braces surround the variable name.
+ * This really matters for array elements to handle things like
+ * set {x($foo)} 5
+ * which raises an undefined var error if we are not careful here.
+ */
+
+ if ((varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) &&
+ (varTokenPtr->start[0] != '{')) {
+ /*
+ * A simple variable name. Divide it up into "name" and "elName"
+ * strings. If it is not a local variable, look it up at runtime.
+ */
+ simpleVarName = 1;
+
+ name = varTokenPtr[1].start;
+ nameChars = varTokenPtr[1].size;
+ if ( *(name + nameChars - 1) == ')') {
+ /*
+ * last char is ')' => potential array reference.
+ */
+
+ for (i = 0, p = name; i < nameChars; i++, p++) {
+ if (*p == '(') {
+ elName = p + 1;
+ elNameChars = nameChars - i - 2;
+ nameChars = i ;
+ break;
+ }
+ }
+
+ if ((elName != NULL) && elNameChars) {
+ /*
+ * An array element, the element name is a simple
+ * string: assemble the corresponding token.
+ */
+
+ elemTokenPtr = (Tcl_Token *) ckalloc(sizeof(Tcl_Token));
+ allocedTokens = 1;
+ elemTokenPtr->type = TCL_TOKEN_TEXT;
+ elemTokenPtr->start = elName;
+ elemTokenPtr->size = elNameChars;
+ elemTokenPtr->numComponents = 0;
+ elemTokenCount = 1;
+ }
+ }
+ } else if (((n = varTokenPtr->numComponents) > 1)
+ && (varTokenPtr[1].type == TCL_TOKEN_TEXT)
+ && (varTokenPtr[n].type == TCL_TOKEN_TEXT)
+ && (varTokenPtr[n].start[varTokenPtr[n].size - 1] == ')')) {
+
+ /*
+ * Check for parentheses inside first token
+ */
+
+ simpleVarName = 0;
+ for (i = 0, p = varTokenPtr[1].start;
+ i < varTokenPtr[1].size; i++, p++) {
+ if (*p == '(') {
+ simpleVarName = 1;
+ break;
+ }
+ }
+ if (simpleVarName) {
+ int remainingChars;
+
+ /*
+ * Check the last token: if it is just ')', do not count
+ * it. Otherwise, remove the ')' and flag so that it is
+ * restored at the end.
+ */
+
+ if (varTokenPtr[n].size == 1) {
+ --n;
+ } else {
+ --varTokenPtr[n].size;
+ removedParen = n;
+ }
+
+ name = varTokenPtr[1].start;
+ nameChars = p - varTokenPtr[1].start;
+ elName = p + 1;
+ remainingChars = (varTokenPtr[2].start - p) - 1;
+ elNameChars = (varTokenPtr[n].start - p) + varTokenPtr[n].size - 2;
+
+ if (remainingChars) {
+ /*
+ * Make a first token with the extra characters in the first
+ * token.
+ */
+
+ elemTokenPtr = (Tcl_Token *) ckalloc(n * sizeof(Tcl_Token));
+ allocedTokens = 1;
+ elemTokenPtr->type = TCL_TOKEN_TEXT;
+ elemTokenPtr->start = elName;
+ elemTokenPtr->size = remainingChars;
+ elemTokenPtr->numComponents = 0;
+ elemTokenCount = n;
+
+ /*
+ * Copy the remaining tokens.
+ */
+
+ memcpy((void *) (elemTokenPtr+1), (void *) (&varTokenPtr[2]),
+ ((n-1) * sizeof(Tcl_Token)));
+ } else {
+ /*
+ * Use the already available tokens.
+ */
+
+ elemTokenPtr = &varTokenPtr[2];
+ elemTokenCount = n - 1;
+ }
+ }
+ }
+
+ if (simpleVarName) {
+ /*
+ * See whether name has any namespace separators (::'s).
+ */
+
+ int hasNsQualifiers = 0;
+ for (i = 0, p = name; i < nameChars; i++, p++) {
+ if ((*p == ':') && ((i+1) < nameChars) && (*(p+1) == ':')) {
+ hasNsQualifiers = 1;
+ break;
+ }
+ }
+
+ /*
+ * Look up the var name's index in the array of local vars in the
+ * proc frame. If retrieving the var's value and it doesn't already
+ * exist, push its name and look it up at runtime.
+ */
+
+ if ((envPtr->procPtr != NULL) && !hasNsQualifiers) {
+ localIndex = TclFindCompiledLocal(name, nameChars,
+ /*create*/ (flags & TCL_CREATE_VAR),
+ /*flags*/ ((elName==NULL)? VAR_SCALAR : VAR_ARRAY),
+ envPtr->procPtr);
+ if ((flags & TCL_NO_LARGE_INDEX) && (localIndex > 255)) {
+ /* we'll push the name */
+ localIndex = -1;
+ }
+ }
+ if (localIndex < 0) {
+ TclEmitPush(TclRegisterNewLiteral(envPtr, name, nameChars), envPtr);
+ }
+
+ /*
+ * Compile the element script, if any.
+ */
+
+ if (elName != NULL) {
+ if (elNameChars) {
+ code = TclCompileTokens(interp, elemTokenPtr,
+ elemTokenCount, envPtr);
+ if (code != TCL_OK) {
+ goto done;
+ }
+ } else {
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
+ }
+ }
+ } else {
+ /*
+ * The var name isn't simple: compile and push it.
+ */
+
+ code = TclCompileTokens(interp, varTokenPtr+1,
+ varTokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ goto done;
+ }
+ }
+
+ done:
+ if (removedParen) {
+ ++varTokenPtr[removedParen].size;
+ }
+ if (allocedTokens) {
+ ckfree((char *) elemTokenPtr);
+ }
+ *localIndexPtr = localIndex;
+ *simpleVarNamePtr = simpleVarName;
+ *isScalarPtr = (elName == NULL);
+ return code;
+}
diff --git a/tcl/generic/tclCompExpr.c b/tcl/generic/tclCompExpr.c
index ff368e20004..d1f25b5e157 100644
--- a/tcl/generic/tclCompExpr.c
+++ b/tcl/generic/tclCompExpr.c
@@ -4,6 +4,7 @@
* This file contains the code to compile Tcl expressions.
*
* Copyright (c) 1997 Sun Microsystems, Inc.
+ * Copyright (c) 1998-2000 by Scriptics Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -50,26 +51,14 @@ typedef struct ExprInfo {
Tcl_Interp *interp; /* Used for error reporting. */
Tcl_Parse *parsePtr; /* Structure filled with information about
* the parsed expression. */
- char *expr; /* The expression that was originally passed
+ CONST char *expr; /* The expression that was originally passed
* to TclCompileExpr. */
- char *lastChar; /* Points just after last byte of expr. */
+ CONST char *lastChar; /* Points just after last byte of expr. */
int hasOperators; /* Set 1 if the expr has operators; 0 if
* expr is only a primary. If 1 after
* compiling an expr, a tryCvtToNumeric
* instruction is emitted to convert the
* primary to a number if possible. */
- int exprIsJustVarRef; /* Set 1 if the expr consists of just a
- * variable reference as in the expression
- * of "if $b then...". Otherwise 0. If 1 the
- * expr is compiled out-of-line in order to
- * implement expr's 2 level substitution
- * semantics properly. */
- int exprIsComparison; /* Set 1 if the top-level operator in the
- * expr is a comparison. Otherwise 0. If 1,
- * because the operands might be strings,
- * the expr is compiled out-of-line in order
- * to implement expr's 2 level substitution
- * semantics properly. */
} ExprInfo;
/*
@@ -101,6 +90,8 @@ typedef struct ExprInfo {
#define OP_QUESTY 18
#define OP_LNOT 19
#define OP_BITNOT 20
+#define OP_STREQ 21
+#define OP_STRNEQ 22
/*
* Table describing the expression operators. Entries in this table must
@@ -119,7 +110,7 @@ typedef struct OperatorDesc {
* Ignored if numOperands is 0. */
} OperatorDesc;
-OperatorDesc operatorTable[] = {
+static OperatorDesc operatorTable[] = {
{"*", 2, INST_MULT},
{"/", 2, INST_DIV},
{"%", 2, INST_MOD},
@@ -141,6 +132,8 @@ OperatorDesc operatorTable[] = {
{"?", 0},
{"!", 1, INST_LNOT},
{"~", 1, INST_BITNOT},
+ {"eq", 2, INST_STR_EQ},
+ {"ne", 2, INST_STR_NEQ},
{NULL}
};
@@ -163,7 +156,7 @@ static int CompileLandOrLorExpr _ANSI_ARGS_((
ExprInfo *infoPtr, CompileEnv *envPtr,
Tcl_Token **endPtrPtr));
static int CompileMathFuncCall _ANSI_ARGS_((
- Tcl_Token *exprTokenPtr, char *funcName,
+ Tcl_Token *exprTokenPtr, CONST char *funcName,
ExprInfo *infoPtr, CompileEnv *envPtr,
Tcl_Token **endPtrPtr));
static int CompileSubExpr _ANSI_ARGS_((
@@ -201,19 +194,6 @@ static void LogSyntaxError _ANSI_ARGS_((ExprInfo *infoPtr));
* on failure. If TCL_ERROR is returned, then the interpreter's result
* contains an error message.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the expression.
- *
- * envPtr->exprIsJustVarRef is set 1 if the expression consisted of
- * a single variable reference as in the expression of "if $b then...".
- * Otherwise it is set 0. This is used to implement Tcl's two level
- * expression substitution semantics properly.
- *
- * envPtr->exprIsComparison is set 1 if the top-level operator in the
- * expr is a comparison. Otherwise it is set 0. If 1, because the
- * operands might be strings, the expr is compiled out-of-line in order
- * to implement expr's 2 level substitution semantics properly.
- *
* Side effects:
* Adds instructions to envPtr to evaluate the expression at runtime.
*
@@ -223,7 +203,7 @@ static void LogSyntaxError _ANSI_ARGS_((ExprInfo *infoPtr));
int
TclCompileExpr(interp, script, numBytes, envPtr)
Tcl_Interp *interp; /* Used for error reporting. */
- char *script; /* The source script to compile. */
+ CONST char *script; /* The source script to compile. */
int numBytes; /* Number of bytes in script. If < 0, the
* string consists of all bytes up to the
* first null character. */
@@ -232,7 +212,7 @@ TclCompileExpr(interp, script, numBytes, envPtr)
ExprInfo info;
Tcl_Parse parse;
Tcl_HashEntry *hPtr;
- int maxDepth, new, i, code;
+ int new, i, code;
/*
* If this is the first time we've been called, initialize the table
@@ -268,14 +248,11 @@ TclCompileExpr(interp, script, numBytes, envPtr)
info.expr = script;
info.lastChar = (script + numBytes);
info.hasOperators = 0;
- info.exprIsJustVarRef = 1; /* will be set 0 if anything else is seen */
- info.exprIsComparison = 0;
/*
* Parse the expression then compile it.
*/
- maxDepth = 0;
code = Tcl_ParseExpr(interp, script, numBytes, &parse);
if (code != TCL_OK) {
goto done;
@@ -286,7 +263,6 @@ TclCompileExpr(interp, script, numBytes, envPtr)
Tcl_FreeParse(&parse);
goto done;
}
- maxDepth = envPtr->maxStackDepth;
if (!info.hasOperators) {
/*
@@ -301,9 +277,6 @@ TclCompileExpr(interp, script, numBytes, envPtr)
Tcl_FreeParse(&parse);
done:
- envPtr->maxStackDepth = maxDepth;
- envPtr->exprIsJustVarRef = info.exprIsJustVarRef;
- envPtr->exprIsComparison = info.exprIsComparison;
return code;
}
@@ -352,19 +325,6 @@ TclFinalizeCompilation()
* on failure. If TCL_ERROR is returned, then the interpreter's result
* contains an error message.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the subexpression.
- *
- * envPtr->exprIsJustVarRef is set 1 if the subexpression consisted of
- * a single variable reference as in the expression of "if $b then...".
- * Otherwise it is set 0. This is used to implement Tcl's two level
- * expression substitution semantics properly.
- *
- * envPtr->exprIsComparison is set 1 if the top-level operator in the
- * subexpression is a comparison. Otherwise it is set 0. If 1, because
- * the operands might be strings, the expr is compiled out-of-line in
- * order to implement expr's 2 level substitution semantics properly.
- *
* Side effects:
* Adds instructions to envPtr to evaluate the subexpression.
*
@@ -383,15 +343,15 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
Tcl_Token *tokenPtr, *endPtr, *afterSubexprPtr;
OperatorDesc *opDescPtr;
Tcl_HashEntry *hPtr;
- char *operator;
- int maxDepth, objIndex, opIndex, length, code;
+ CONST char *operator;
+ Tcl_DString opBuf;
+ int objIndex, opIndex, length, code;
char buffer[TCL_UTF_MAX];
if (exprTokenPtr->type != TCL_TOKEN_SUB_EXPR) {
panic("CompileSubExpr: token type %d not TCL_TOKEN_SUB_EXPR\n",
exprTokenPtr->type);
}
- maxDepth = 0;
code = TCL_OK;
/*
@@ -410,37 +370,30 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr += (tokenPtr->numComponents + 1);
- infoPtr->exprIsJustVarRef = 0;
break;
case TCL_TOKEN_TEXT:
if (tokenPtr->size > 0) {
- objIndex = TclRegisterLiteral(envPtr, tokenPtr->start,
- tokenPtr->size, /*onHeap*/ 0);
+ objIndex = TclRegisterNewLiteral(envPtr, tokenPtr->start,
+ tokenPtr->size);
} else {
- objIndex = TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0);
+ objIndex = TclRegisterNewLiteral(envPtr, "", 0);
}
TclEmitPush(objIndex, envPtr);
- maxDepth = 1;
tokenPtr += 1;
- infoPtr->exprIsJustVarRef = 0;
break;
case TCL_TOKEN_BS:
length = Tcl_UtfBackslash(tokenPtr->start, (int *) NULL,
buffer);
if (length > 0) {
- objIndex = TclRegisterLiteral(envPtr, buffer, length,
- /*onHeap*/ 0);
+ objIndex = TclRegisterNewLiteral(envPtr, buffer, length);
} else {
- objIndex = TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0);
+ objIndex = TclRegisterNewLiteral(envPtr, "", 0);
}
TclEmitPush(objIndex, envPtr);
- maxDepth = 1;
tokenPtr += 1;
- infoPtr->exprIsJustVarRef = 0;
break;
case TCL_TOKEN_COMMAND:
@@ -449,9 +402,7 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr += 1;
- infoPtr->exprIsJustVarRef = 0;
break;
case TCL_TOKEN_VARIABLE:
@@ -459,42 +410,37 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr += (tokenPtr->numComponents + 1);
break;
case TCL_TOKEN_SUB_EXPR:
- infoPtr->exprIsComparison = 0;
code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr += (tokenPtr->numComponents + 1);
break;
- case TCL_TOKEN_OPERATOR: {
- Tcl_DString operatorDString;
-
- Tcl_DStringInit(&operatorDString);
- Tcl_DStringAppend(&operatorDString, tokenPtr->start,
- tokenPtr->size);
- operator = Tcl_DStringValue(&operatorDString);
+ case TCL_TOKEN_OPERATOR:
+ /*
+ * Look up the operator. If the operator isn't found, treat it
+ * as a math function.
+ */
+ Tcl_DStringInit(&opBuf);
+ operator = Tcl_DStringAppend(&opBuf,
+ tokenPtr->start, tokenPtr->size);
hPtr = Tcl_FindHashEntry(&opHashTable, operator);
if (hPtr == NULL) {
code = CompileMathFuncCall(exprTokenPtr, operator, infoPtr,
envPtr, &endPtr);
- Tcl_DStringFree(&operatorDString);
+ Tcl_DStringFree(&opBuf);
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr = endPtr;
- infoPtr->exprIsJustVarRef = 0;
- infoPtr->exprIsComparison = 0;
break;
}
- Tcl_DStringFree(&operatorDString);
+ Tcl_DStringFree(&opBuf);
opIndex = (int) Tcl_GetHashValue(hPtr);
opDescPtr = &(operatorTable[opIndex]);
@@ -509,7 +455,6 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr += (tokenPtr->numComponents + 1);
if (opDescPtr->numOperands == 2) {
@@ -517,15 +462,10 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = TclMax((envPtr->maxStackDepth + 1),
- maxDepth);
tokenPtr += (tokenPtr->numComponents + 1);
}
TclEmitOpcode(opDescPtr->instruction, envPtr);
infoPtr->hasOperators = 1;
- infoPtr->exprIsJustVarRef = 0;
- infoPtr->exprIsComparison =
- ((opIndex >= OP_LESS) && (opIndex <= OP_NEQ));
break;
}
@@ -542,7 +482,6 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr += (tokenPtr->numComponents + 1);
/*
@@ -566,8 +505,6 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = TclMax((envPtr->maxStackDepth + 1),
- maxDepth);
tokenPtr += (tokenPtr->numComponents + 1);
TclEmitOpcode(((opIndex==OP_PLUS)? INST_ADD : INST_SUB),
envPtr);
@@ -580,7 +517,6 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr = endPtr;
break;
@@ -590,7 +526,6 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr = endPtr;
break;
@@ -599,10 +534,7 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
opIndex);
} /* end switch on operator requiring special treatment */
infoPtr->hasOperators = 1;
- infoPtr->exprIsJustVarRef = 0;
- infoPtr->exprIsComparison = 0;
break;
- }
default:
panic("CompileSubExpr: unexpected token type %d\n",
@@ -622,7 +554,6 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
}
done:
- envPtr->maxStackDepth = maxDepth;
return code;
}
@@ -641,9 +572,6 @@ CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
* endPtrPtr. If TCL_ERROR is returned, then the interpreter's result
* contains an error message.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the expression.
- *
* Side effects:
* Adds instructions to envPtr to evaluate the expression at runtime.
*
@@ -669,19 +597,18 @@ CompileLandOrLorExpr(exprTokenPtr, opIndex, infoPtr, envPtr, endPtrPtr)
/* Used to fix up jumps used to convert the
* first operand to 0 or 1. */
Tcl_Token *tokenPtr;
- int dist, maxDepth, code;
+ int dist, code;
+ int savedStackDepth = envPtr->currStackDepth;
/*
* Emit code for the first operand.
*/
- maxDepth = 0;
tokenPtr = exprTokenPtr+2;
code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr += (tokenPtr->numComponents + 1);
/*
@@ -690,14 +617,15 @@ CompileLandOrLorExpr(exprTokenPtr, opIndex, infoPtr, envPtr, endPtrPtr)
*/
TclEmitForwardJump(envPtr, TCL_TRUE_JUMP, &lhsTrueFixup);
- TclEmitPush(TclRegisterLiteral(envPtr, "0", 1, /*onHeap*/ 0), envPtr);
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "0", 1), envPtr);
TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &lhsEndFixup);
dist = (envPtr->codeNext - envPtr->codeStart) - lhsTrueFixup.codeOffset;
if (TclFixupForwardJump(envPtr, &lhsTrueFixup, dist, 127)) {
badDist:
panic("CompileLandOrLorExpr: bad jump distance %d\n", dist);
}
- TclEmitPush(TclRegisterLiteral(envPtr, "1", 1, /*onHeap*/ 0), envPtr);
+ envPtr->currStackDepth = savedStackDepth;
+ TclEmitPush(TclRegisterNewLiteral(envPtr, "1", 1), envPtr);
dist = (envPtr->codeNext - envPtr->codeStart) - lhsEndFixup.codeOffset;
if (TclFixupForwardJump(envPtr, &lhsEndFixup, dist, 127)) {
goto badDist;
@@ -722,7 +650,6 @@ CompileLandOrLorExpr(exprTokenPtr, opIndex, infoPtr, envPtr, endPtrPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);
tokenPtr += (tokenPtr->numComponents + 1);
/*
@@ -744,7 +671,7 @@ CompileLandOrLorExpr(exprTokenPtr, opIndex, infoPtr, envPtr, endPtrPtr)
*endPtrPtr = tokenPtr;
done:
- envPtr->maxStackDepth = maxDepth;
+ envPtr->currStackDepth = savedStackDepth + 1;
return code;
}
@@ -763,9 +690,6 @@ CompileLandOrLorExpr(exprTokenPtr, opIndex, infoPtr, envPtr, endPtrPtr)
* endPtrPtr. If TCL_ERROR is returned, then the interpreter's result
* contains an error message.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the expression.
- *
* Side effects:
* Adds instructions to envPtr to evaluate the expression at runtime.
*
@@ -788,19 +712,18 @@ CompileCondExpr(exprTokenPtr, infoPtr, envPtr, endPtrPtr)
* around the then and else expressions when
* their target PCs are determined. */
Tcl_Token *tokenPtr;
- int elseCodeOffset, dist, maxDepth, code;
+ int elseCodeOffset, dist, code;
+ int savedStackDepth = envPtr->currStackDepth;
/*
* Emit code for the test.
*/
- maxDepth = 0;
tokenPtr = exprTokenPtr+2;
code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
if (code != TCL_OK) {
goto done;
}
- maxDepth = envPtr->maxStackDepth;
tokenPtr += (tokenPtr->numComponents + 1);
/*
@@ -821,7 +744,6 @@ CompileCondExpr(exprTokenPtr, infoPtr, envPtr, endPtrPtr)
if (code != TCL_OK) {
goto done;
}
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
tokenPtr += (tokenPtr->numComponents + 1);
if (!infoPtr->hasOperators) {
TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);
@@ -838,13 +760,13 @@ CompileCondExpr(exprTokenPtr, infoPtr, envPtr, endPtrPtr)
* Compile the "else" expression.
*/
+ envPtr->currStackDepth = savedStackDepth;
elseCodeOffset = (envPtr->codeNext - envPtr->codeStart);
infoPtr->hasOperators = 0;
code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
if (code != TCL_OK) {
goto done;
}
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
tokenPtr += (tokenPtr->numComponents + 1);
if (!infoPtr->hasOperators) {
TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);
@@ -874,7 +796,7 @@ CompileCondExpr(exprTokenPtr, infoPtr, envPtr, endPtrPtr)
*endPtrPtr = tokenPtr;
done:
- envPtr->maxStackDepth = maxDepth;
+ envPtr->currStackDepth = savedStackDepth + 1;
return code;
}
@@ -893,9 +815,6 @@ CompileCondExpr(exprTokenPtr, infoPtr, envPtr, endPtrPtr)
* endPtrPtr. If TCL_ERROR is returned, then the interpreter's result
* contains an error message.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the function.
- *
* Side effects:
* Adds instructions to envPtr to evaluate the math function at
* runtime.
@@ -907,7 +826,7 @@ static int
CompileMathFuncCall(exprTokenPtr, funcName, infoPtr, envPtr, endPtrPtr)
Tcl_Token *exprTokenPtr; /* Points to TCL_TOKEN_SUB_EXPR token
* containing the math function call. */
- char *funcName; /* Name of the math function. */
+ CONST char *funcName; /* Name of the math function. */
ExprInfo *infoPtr; /* Describes the compilation state for the
* expression being compiled. */
CompileEnv *envPtr; /* Holds resulting instructions. */
@@ -920,14 +839,13 @@ CompileMathFuncCall(exprTokenPtr, funcName, infoPtr, envPtr, endPtrPtr)
MathFunc *mathFuncPtr;
Tcl_HashEntry *hPtr;
Tcl_Token *tokenPtr, *afterSubexprPtr;
- int maxDepth, code, i;
+ int code, i;
/*
* Look up the MathFunc record for the function.
*/
code = TCL_OK;
- maxDepth = 0;
hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, funcName);
if (hPtr == NULL) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
@@ -942,9 +860,7 @@ CompileMathFuncCall(exprTokenPtr, funcName, infoPtr, envPtr, endPtrPtr)
*/
if (mathFuncPtr->builtinFuncIndex < 0) {
- TclEmitPush(TclRegisterLiteral(envPtr, funcName, -1, /*onHeap*/ 0),
- envPtr);
- maxDepth = 1;
+ TclEmitPush(TclRegisterNewLiteral(envPtr, funcName, -1), envPtr);
}
/*
@@ -962,13 +878,11 @@ CompileMathFuncCall(exprTokenPtr, funcName, infoPtr, envPtr, endPtrPtr)
code = TCL_ERROR;
goto done;
}
- infoPtr->exprIsComparison = 0;
code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
if (code != TCL_OK) {
goto done;
}
tokenPtr += (tokenPtr->numComponents + 1);
- maxDepth++;
}
if (tokenPtr != afterSubexprPtr) {
Tcl_ResetResult(interp);
@@ -992,15 +906,25 @@ CompileMathFuncCall(exprTokenPtr, funcName, infoPtr, envPtr, endPtrPtr)
*/
if (mathFuncPtr->builtinFuncIndex >= 0) { /* a builtin function */
- TclEmitInstInt1(INST_CALL_BUILTIN_FUNC1,
+ /*
+ * Adjust the current stack depth by the number of arguments
+ * of the builtin function. This cannot be handled by the
+ * TclEmitInstInt1 macro as the number of arguments is not
+ * passed as an operand.
+ */
+
+ if (envPtr->maxStackDepth < envPtr->currStackDepth) {
+ envPtr->maxStackDepth = envPtr->currStackDepth;
+ }
+ TclEmitInstInt1(INST_CALL_BUILTIN_FUNC1,
mathFuncPtr->builtinFuncIndex, envPtr);
+ envPtr->currStackDepth -= mathFuncPtr->numArgs;
} else {
TclEmitInstInt1(INST_CALL_FUNC1, (mathFuncPtr->numArgs+1), envPtr);
}
*endPtrPtr = afterSubexprPtr;
done:
- envPtr->maxStackDepth = maxDepth;
return code;
}
@@ -1033,6 +957,7 @@ LogSyntaxError(infoPtr)
sprintf(buffer, "syntax error in expression \"%.*s\"",
((numBytes > 60)? 60 : numBytes), infoPtr->expr);
+ Tcl_ResetResult(infoPtr->interp);
Tcl_AppendStringsToObj(Tcl_GetObjResult(infoPtr->interp),
buffer, (char *) NULL);
}
diff --git a/tcl/generic/tclCompile.c b/tcl/generic/tclCompile.c
index 4df50f28378..68be045c1b8 100644
--- a/tcl/generic/tclCompile.c
+++ b/tcl/generic/tclCompile.c
@@ -6,6 +6,7 @@
* sequence of instructions ("bytecodes").
*
* Copyright (c) 1996-1998 Sun Microsystems, Inc.
+ * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -34,8 +35,10 @@ TCL_DECLARE_MUTEX(tableMutex)
* This variable is linked to the Tcl variable "tcl_traceCompile".
*/
+#ifdef TCL_COMPILE_DEBUG
int tclTraceCompile = 0;
static int traceInitialized = 0;
+#endif
/*
* A table describing the Tcl bytecode instructions. Entries in this table
@@ -49,167 +52,223 @@ static int traceInitialized = 0;
* existence of a procedure call frame to distinguish these.
*/
-InstructionDesc instructionTable[] = {
- /* Name Bytes #Opnds Operand types Stack top, next */
- {"done", 1, 0, {OPERAND_NONE}},
- /* Finish ByteCode execution and return stktop (top stack item) */
- {"push1", 2, 1, {OPERAND_UINT1}},
- /* Push object at ByteCode objArray[op1] */
- {"push4", 5, 1, {OPERAND_UINT4}},
- /* Push object at ByteCode objArray[op4] */
- {"pop", 1, 0, {OPERAND_NONE}},
- /* Pop the topmost stack object */
- {"dup", 1, 0, {OPERAND_NONE}},
- /* Duplicate the topmost stack object and push the result */
- {"concat1", 2, 1, {OPERAND_UINT1}},
- /* Concatenate the top op1 items and push result */
- {"invokeStk1", 2, 1, {OPERAND_UINT1}},
- /* Invoke command named objv[0]; <objc,objv> = <op1,top op1> */
- {"invokeStk4", 5, 1, {OPERAND_UINT4}},
- /* Invoke command named objv[0]; <objc,objv> = <op4,top op4> */
- {"evalStk", 1, 0, {OPERAND_NONE}},
- /* Evaluate command in stktop using Tcl_EvalObj. */
- {"exprStk", 1, 0, {OPERAND_NONE}},
- /* Execute expression in stktop using Tcl_ExprStringObj. */
+InstructionDesc tclInstructionTable[] = {
+ /* Name Bytes stackEffect #Opnds Operand types Stack top, next */
+ {"done", 1, -1, 0, {OPERAND_NONE}},
+ /* Finish ByteCode execution and return stktop (top stack item) */
+ {"push1", 2, +1, 1, {OPERAND_UINT1}},
+ /* Push object at ByteCode objArray[op1] */
+ {"push4", 5, +1, 1, {OPERAND_UINT4}},
+ /* Push object at ByteCode objArray[op4] */
+ {"pop", 1, -1, 0, {OPERAND_NONE}},
+ /* Pop the topmost stack object */
+ {"dup", 1, +1, 0, {OPERAND_NONE}},
+ /* Duplicate the topmost stack object and push the result */
+ {"concat1", 2, INT_MIN, 1, {OPERAND_UINT1}},
+ /* Concatenate the top op1 items and push result */
+ {"invokeStk1", 2, INT_MIN, 1, {OPERAND_UINT1}},
+ /* Invoke command named objv[0]; <objc,objv> = <op1,top op1> */
+ {"invokeStk4", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* Invoke command named objv[0]; <objc,objv> = <op4,top op4> */
+ {"evalStk", 1, 0, 0, {OPERAND_NONE}},
+ /* Evaluate command in stktop using Tcl_EvalObj. */
+ {"exprStk", 1, 0, 0, {OPERAND_NONE}},
+ /* Execute expression in stktop using Tcl_ExprStringObj. */
- {"loadScalar1", 2, 1, {OPERAND_UINT1}},
- /* Load scalar variable at index op1 <= 255 in call frame */
- {"loadScalar4", 5, 1, {OPERAND_UINT4}},
- /* Load scalar variable at index op1 >= 256 in call frame */
- {"loadScalarStk", 1, 0, {OPERAND_NONE}},
- /* Load scalar variable; scalar's name is stktop */
- {"loadArray1", 2, 1, {OPERAND_UINT1}},
- /* Load array element; array at slot op1<=255, element is stktop */
- {"loadArray4", 5, 1, {OPERAND_UINT4}},
- /* Load array element; array at slot op1 > 255, element is stktop */
- {"loadArrayStk", 1, 0, {OPERAND_NONE}},
- /* Load array element; element is stktop, array name is stknext */
- {"loadStk", 1, 0, {OPERAND_NONE}},
- /* Load general variable; unparsed variable name is stktop */
- {"storeScalar1", 2, 1, {OPERAND_UINT1}},
- /* Store scalar variable at op1<=255 in frame; value is stktop */
- {"storeScalar4", 5, 1, {OPERAND_UINT4}},
- /* Store scalar variable at op1 > 255 in frame; value is stktop */
- {"storeScalarStk", 1, 0, {OPERAND_NONE}},
- /* Store scalar; value is stktop, scalar name is stknext */
- {"storeArray1", 2, 1, {OPERAND_UINT1}},
- /* Store array element; array at op1<=255, value is top then elem */
- {"storeArray4", 5, 1, {OPERAND_UINT4}},
- /* Store array element; array at op1>=256, value is top then elem */
- {"storeArrayStk", 1, 0, {OPERAND_NONE}},
- /* Store array element; value is stktop, then elem, array names */
- {"storeStk", 1, 0, {OPERAND_NONE}},
- /* Store general variable; value is stktop, then unparsed name */
+ {"loadScalar1", 2, 1, 1, {OPERAND_UINT1}},
+ /* Load scalar variable at index op1 <= 255 in call frame */
+ {"loadScalar4", 5, 1, 1, {OPERAND_UINT4}},
+ /* Load scalar variable at index op1 >= 256 in call frame */
+ {"loadScalarStk", 1, 0, 0, {OPERAND_NONE}},
+ /* Load scalar variable; scalar's name is stktop */
+ {"loadArray1", 2, 0, 1, {OPERAND_UINT1}},
+ /* Load array element; array at slot op1<=255, element is stktop */
+ {"loadArray4", 5, 0, 1, {OPERAND_UINT4}},
+ /* Load array element; array at slot op1 > 255, element is stktop */
+ {"loadArrayStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Load array element; element is stktop, array name is stknext */
+ {"loadStk", 1, 0, 0, {OPERAND_NONE}},
+ /* Load general variable; unparsed variable name is stktop */
+ {"storeScalar1", 2, 0, 1, {OPERAND_UINT1}},
+ /* Store scalar variable at op1<=255 in frame; value is stktop */
+ {"storeScalar4", 5, 0, 1, {OPERAND_UINT4}},
+ /* Store scalar variable at op1 > 255 in frame; value is stktop */
+ {"storeScalarStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Store scalar; value is stktop, scalar name is stknext */
+ {"storeArray1", 2, -1, 1, {OPERAND_UINT1}},
+ /* Store array element; array at op1<=255, value is top then elem */
+ {"storeArray4", 5, -1, 1, {OPERAND_UINT4}},
+ /* Store array element; array at op1>=256, value is top then elem */
+ {"storeArrayStk", 1, -2, 0, {OPERAND_NONE}},
+ /* Store array element; value is stktop, then elem, array names */
+ {"storeStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Store general variable; value is stktop, then unparsed name */
- {"incrScalar1", 2, 1, {OPERAND_UINT1}},
- /* Incr scalar at index op1<=255 in frame; incr amount is stktop */
- {"incrScalarStk", 1, 0, {OPERAND_NONE}},
- /* Incr scalar; incr amount is stktop, scalar's name is stknext */
- {"incrArray1", 2, 1, {OPERAND_UINT1}},
- /* Incr array elem; arr at slot op1<=255, amount is top then elem */
- {"incrArrayStk", 1, 0, {OPERAND_NONE}},
- /* Incr array element; amount is top then elem then array names */
- {"incrStk", 1, 0, {OPERAND_NONE}},
- /* Incr general variable; amount is stktop then unparsed var name */
- {"incrScalar1Imm", 3, 2, {OPERAND_UINT1, OPERAND_INT1}},
- /* Incr scalar at slot op1 <= 255; amount is 2nd operand byte */
- {"incrScalarStkImm", 2, 1, {OPERAND_INT1}},
- /* Incr scalar; scalar name is stktop; incr amount is op1 */
- {"incrArray1Imm", 3, 2, {OPERAND_UINT1, OPERAND_INT1}},
- /* Incr array elem; array at slot op1 <= 255, elem is stktop,
+ {"incrScalar1", 2, 0, 1, {OPERAND_UINT1}},
+ /* Incr scalar at index op1<=255 in frame; incr amount is stktop */
+ {"incrScalarStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Incr scalar; incr amount is stktop, scalar's name is stknext */
+ {"incrArray1", 2, -1, 1, {OPERAND_UINT1}},
+ /* Incr array elem; arr at slot op1<=255, amount is top then elem */
+ {"incrArrayStk", 1, -2, 0, {OPERAND_NONE}},
+ /* Incr array element; amount is top then elem then array names */
+ {"incrStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Incr general variable; amount is stktop then unparsed var name */
+ {"incrScalar1Imm", 3, +1, 2, {OPERAND_UINT1, OPERAND_INT1}},
+ /* Incr scalar at slot op1 <= 255; amount is 2nd operand byte */
+ {"incrScalarStkImm", 2, 0, 1, {OPERAND_INT1}},
+ /* Incr scalar; scalar name is stktop; incr amount is op1 */
+ {"incrArray1Imm", 3, 0, 2, {OPERAND_UINT1, OPERAND_INT1}},
+ /* Incr array elem; array at slot op1 <= 255, elem is stktop,
* amount is 2nd operand byte */
- {"incrArrayStkImm", 2, 1, {OPERAND_INT1}},
- /* Incr array element; elem is top then array name, amount is op1 */
- {"incrStkImm", 2, 1, {OPERAND_INT1}},
- /* Incr general variable; unparsed name is top, amount is op1 */
+ {"incrArrayStkImm", 2, -1, 1, {OPERAND_INT1}},
+ /* Incr array element; elem is top then array name, amount is op1 */
+ {"incrStkImm", 2, 0, 1, {OPERAND_INT1}},
+ /* Incr general variable; unparsed name is top, amount is op1 */
- {"jump1", 2, 1, {OPERAND_INT1}},
- /* Jump relative to (pc + op1) */
- {"jump4", 5, 1, {OPERAND_INT4}},
- /* Jump relative to (pc + op4) */
- {"jumpTrue1", 2, 1, {OPERAND_INT1}},
- /* Jump relative to (pc + op1) if stktop expr object is true */
- {"jumpTrue4", 5, 1, {OPERAND_INT4}},
- /* Jump relative to (pc + op4) if stktop expr object is true */
- {"jumpFalse1", 2, 1, {OPERAND_INT1}},
- /* Jump relative to (pc + op1) if stktop expr object is false */
- {"jumpFalse4", 5, 1, {OPERAND_INT4}},
- /* Jump relative to (pc + op4) if stktop expr object is false */
-
- {"lor", 1, 0, {OPERAND_NONE}},
- /* Logical or: push (stknext || stktop) */
- {"land", 1, 0, {OPERAND_NONE}},
- /* Logical and: push (stknext && stktop) */
- {"bitor", 1, 0, {OPERAND_NONE}},
- /* Bitwise or: push (stknext | stktop) */
- {"bitxor", 1, 0, {OPERAND_NONE}},
- /* Bitwise xor push (stknext ^ stktop) */
- {"bitand", 1, 0, {OPERAND_NONE}},
- /* Bitwise and: push (stknext & stktop) */
- {"eq", 1, 0, {OPERAND_NONE}},
- /* Equal: push (stknext == stktop) */
- {"neq", 1, 0, {OPERAND_NONE}},
- /* Not equal: push (stknext != stktop) */
- {"lt", 1, 0, {OPERAND_NONE}},
- /* Less: push (stknext < stktop) */
- {"gt", 1, 0, {OPERAND_NONE}},
- /* Greater: push (stknext || stktop) */
- {"le", 1, 0, {OPERAND_NONE}},
- /* Logical or: push (stknext || stktop) */
- {"ge", 1, 0, {OPERAND_NONE}},
- /* Logical or: push (stknext || stktop) */
- {"lshift", 1, 0, {OPERAND_NONE}},
- /* Left shift: push (stknext << stktop) */
- {"rshift", 1, 0, {OPERAND_NONE}},
- /* Right shift: push (stknext >> stktop) */
- {"add", 1, 0, {OPERAND_NONE}},
- /* Add: push (stknext + stktop) */
- {"sub", 1, 0, {OPERAND_NONE}},
- /* Sub: push (stkext - stktop) */
- {"mult", 1, 0, {OPERAND_NONE}},
- /* Multiply: push (stknext * stktop) */
- {"div", 1, 0, {OPERAND_NONE}},
- /* Divide: push (stknext / stktop) */
- {"mod", 1, 0, {OPERAND_NONE}},
- /* Mod: push (stknext % stktop) */
- {"uplus", 1, 0, {OPERAND_NONE}},
- /* Unary plus: push +stktop */
- {"uminus", 1, 0, {OPERAND_NONE}},
- /* Unary minus: push -stktop */
- {"bitnot", 1, 0, {OPERAND_NONE}},
- /* Bitwise not: push ~stktop */
- {"not", 1, 0, {OPERAND_NONE}},
- /* Logical not: push !stktop */
- {"callBuiltinFunc1", 2, 1, {OPERAND_UINT1}},
- /* Call builtin math function with index op1; any args are on stk */
- {"callFunc1", 2, 1, {OPERAND_UINT1}},
- /* Call non-builtin func objv[0]; <objc,objv>=<op1,top op1> */
- {"tryCvtToNumeric", 1, 0, {OPERAND_NONE}},
- /* Try converting stktop to first int then double if possible. */
-
- {"break", 1, 0, {OPERAND_NONE}},
- /* Abort closest enclosing loop; if none, return TCL_BREAK code. */
- {"continue", 1, 0, {OPERAND_NONE}},
- /* Skip to next iteration of closest enclosing loop; if none,
+ {"jump1", 2, 0, 1, {OPERAND_INT1}},
+ /* Jump relative to (pc + op1) */
+ {"jump4", 5, 0, 1, {OPERAND_INT4}},
+ /* Jump relative to (pc + op4) */
+ {"jumpTrue1", 2, -1, 1, {OPERAND_INT1}},
+ /* Jump relative to (pc + op1) if stktop expr object is true */
+ {"jumpTrue4", 5, -1, 1, {OPERAND_INT4}},
+ /* Jump relative to (pc + op4) if stktop expr object is true */
+ {"jumpFalse1", 2, -1, 1, {OPERAND_INT1}},
+ /* Jump relative to (pc + op1) if stktop expr object is false */
+ {"jumpFalse4", 5, -1, 1, {OPERAND_INT4}},
+ /* Jump relative to (pc + op4) if stktop expr object is false */
+
+ {"lor", 1, -1, 0, {OPERAND_NONE}},
+ /* Logical or: push (stknext || stktop) */
+ {"land", 1, -1, 0, {OPERAND_NONE}},
+ /* Logical and: push (stknext && stktop) */
+ {"bitor", 1, -1, 0, {OPERAND_NONE}},
+ /* Bitwise or: push (stknext | stktop) */
+ {"bitxor", 1, -1, 0, {OPERAND_NONE}},
+ /* Bitwise xor push (stknext ^ stktop) */
+ {"bitand", 1, -1, 0, {OPERAND_NONE}},
+ /* Bitwise and: push (stknext & stktop) */
+ {"eq", 1, -1, 0, {OPERAND_NONE}},
+ /* Equal: push (stknext == stktop) */
+ {"neq", 1, -1, 0, {OPERAND_NONE}},
+ /* Not equal: push (stknext != stktop) */
+ {"lt", 1, -1, 0, {OPERAND_NONE}},
+ /* Less: push (stknext < stktop) */
+ {"gt", 1, -1, 0, {OPERAND_NONE}},
+ /* Greater: push (stknext || stktop) */
+ {"le", 1, -1, 0, {OPERAND_NONE}},
+ /* Logical or: push (stknext || stktop) */
+ {"ge", 1, -1, 0, {OPERAND_NONE}},
+ /* Logical or: push (stknext || stktop) */
+ {"lshift", 1, -1, 0, {OPERAND_NONE}},
+ /* Left shift: push (stknext << stktop) */
+ {"rshift", 1, -1, 0, {OPERAND_NONE}},
+ /* Right shift: push (stknext >> stktop) */
+ {"add", 1, -1, 0, {OPERAND_NONE}},
+ /* Add: push (stknext + stktop) */
+ {"sub", 1, -1, 0, {OPERAND_NONE}},
+ /* Sub: push (stkext - stktop) */
+ {"mult", 1, -1, 0, {OPERAND_NONE}},
+ /* Multiply: push (stknext * stktop) */
+ {"div", 1, -1, 0, {OPERAND_NONE}},
+ /* Divide: push (stknext / stktop) */
+ {"mod", 1, -1, 0, {OPERAND_NONE}},
+ /* Mod: push (stknext % stktop) */
+ {"uplus", 1, 0, 0, {OPERAND_NONE}},
+ /* Unary plus: push +stktop */
+ {"uminus", 1, 0, 0, {OPERAND_NONE}},
+ /* Unary minus: push -stktop */
+ {"bitnot", 1, 0, 0, {OPERAND_NONE}},
+ /* Bitwise not: push ~stktop */
+ {"not", 1, 0, 0, {OPERAND_NONE}},
+ /* Logical not: push !stktop */
+ {"callBuiltinFunc1", 2, 1, 1, {OPERAND_UINT1}},
+ /* Call builtin math function with index op1; any args are on stk */
+ {"callFunc1", 2, INT_MIN, 1, {OPERAND_UINT1}},
+ /* Call non-builtin func objv[0]; <objc,objv>=<op1,top op1> */
+ {"tryCvtToNumeric", 1, 0, 0, {OPERAND_NONE}},
+ /* Try converting stktop to first int then double if possible. */
+
+ {"break", 1, 0, 0, {OPERAND_NONE}},
+ /* Abort closest enclosing loop; if none, return TCL_BREAK code. */
+ {"continue", 1, 0, 0, {OPERAND_NONE}},
+ /* Skip to next iteration of closest enclosing loop; if none,
* return TCL_CONTINUE code. */
- {"foreach_start4", 5, 1, {OPERAND_UINT4}},
- /* Initialize execution of a foreach loop. Operand is aux data index
+ {"foreach_start4", 5, 0, 1, {OPERAND_UINT4}},
+ /* Initialize execution of a foreach loop. Operand is aux data index
* of the ForeachInfo structure for the foreach command. */
- {"foreach_step4", 5, 1, {OPERAND_UINT4}},
- /* "Step" or begin next iteration of foreach loop. Push 0 if to
+ {"foreach_step4", 5, +1, 1, {OPERAND_UINT4}},
+ /* "Step" or begin next iteration of foreach loop. Push 0 if to
* terminate loop, else push 1. */
- {"beginCatch4", 5, 1, {OPERAND_UINT4}},
- /* Record start of catch with the operand's exception index.
+ {"beginCatch4", 5, 0, 1, {OPERAND_UINT4}},
+ /* Record start of catch with the operand's exception index.
* Push the current stack depth onto a special catch stack. */
- {"endCatch", 1, 0, {OPERAND_NONE}},
- /* End of last catch. Pop the bytecode interpreter's catch stack. */
- {"pushResult", 1, 0, {OPERAND_NONE}},
- /* Push the interpreter's object result onto the stack. */
- {"pushReturnCode", 1, 0, {OPERAND_NONE}},
- /* Push interpreter's return code (e.g. TCL_OK or TCL_ERROR) as
+ {"endCatch", 1, 0, 0, {OPERAND_NONE}},
+ /* End of last catch. Pop the bytecode interpreter's catch stack. */
+ {"pushResult", 1, +1, 0, {OPERAND_NONE}},
+ /* Push the interpreter's object result onto the stack. */
+ {"pushReturnCode", 1, +1, 0, {OPERAND_NONE}},
+ /* Push interpreter's return code (e.g. TCL_OK or TCL_ERROR) as
* a new object onto the stack. */
+ {"streq", 1, -1, 0, {OPERAND_NONE}},
+ /* Str Equal: push (stknext eq stktop) */
+ {"strneq", 1, -1, 0, {OPERAND_NONE}},
+ /* Str !Equal: push (stknext neq stktop) */
+ {"strcmp", 1, -1, 0, {OPERAND_NONE}},
+ /* Str Compare: push (stknext cmp stktop) */
+ {"strlen", 1, 0, 0, {OPERAND_NONE}},
+ /* Str Length: push (strlen stktop) */
+ {"strindex", 1, -1, 0, {OPERAND_NONE}},
+ /* Str Index: push (strindex stknext stktop) */
+ {"strmatch", 2, -1, 1, {OPERAND_INT1}},
+ /* Str Match: push (strmatch stknext stktop) opnd == nocase */
+ {"list", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* List: push (stk1 stk2 ... stktop) */
+ {"listindex", 1, -1, 0, {OPERAND_NONE}},
+ /* List Index: push (listindex stknext stktop) */
+ {"listlength", 1, 0, 0, {OPERAND_NONE}},
+ /* List Len: push (listlength stktop) */
+ {"appendScalar1", 2, 0, 1, {OPERAND_UINT1}},
+ /* Append scalar variable at op1<=255 in frame; value is stktop */
+ {"appendScalar4", 5, 0, 1, {OPERAND_UINT4}},
+ /* Append scalar variable at op1 > 255 in frame; value is stktop */
+ {"appendArray1", 2, -1, 1, {OPERAND_UINT1}},
+ /* Append array element; array at op1<=255, value is top then elem */
+ {"appendArray4", 5, -1, 1, {OPERAND_UINT4}},
+ /* Append array element; array at op1>=256, value is top then elem */
+ {"appendArrayStk", 1, -2, 0, {OPERAND_NONE}},
+ /* Append array element; value is stktop, then elem, array names */
+ {"appendStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Append general variable; value is stktop, then unparsed name */
+ {"lappendScalar1", 2, 0, 1, {OPERAND_UINT1}},
+ /* Lappend scalar variable at op1<=255 in frame; value is stktop */
+ {"lappendScalar4", 5, 0, 1, {OPERAND_UINT4}},
+ /* Lappend scalar variable at op1 > 255 in frame; value is stktop */
+ {"lappendArray1", 2, -1, 1, {OPERAND_UINT1}},
+ /* Lappend array element; array at op1<=255, value is top then elem */
+ {"lappendArray4", 5, -1, 1, {OPERAND_UINT4}},
+ /* Lappend array element; array at op1>=256, value is top then elem */
+ {"lappendArrayStk", 1, -2, 0, {OPERAND_NONE}},
+ /* Lappend array element; value is stktop, then elem, array names */
+ {"lappendStk", 1, -1, 0, {OPERAND_NONE}},
+ /* Lappend general variable; value is stktop, then unparsed name */
+ {"lindexMulti", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* Lindex with generalized args, operand is number of stacked objs
+ * used: (operand-1) entries from stktop are the indices; then list
+ * to process. */
+ {"over", 5, +1, 1, {OPERAND_UINT4}},
+ /* Duplicate the arg-th element from top of stack (TOS=0) */
+ {"lsetList", 1, -2, 0, {OPERAND_NONE}},
+ /* Four-arg version of 'lset'. stktop is old value; next is
+ * new element value, next is the index list; pushes new value */
+ {"lsetFlat", 5, INT_MIN, 1, {OPERAND_UINT4}},
+ /* Three- or >=5-arg version of 'lset', operand is number of
+ * stacked objs: stktop is old value, next is new element value, next
+ * come (operand-2) indices; pushes the new value.
+ */
{0}
};
@@ -233,7 +292,8 @@ static void FreeByteCodeInternalRep _ANSI_ARGS_((
static int GetCmdLocEncodingSize _ANSI_ARGS_((
CompileEnv *envPtr));
static void LogCompilationInfo _ANSI_ARGS_((Tcl_Interp *interp,
- char *script, char *command, int length));
+ CONST char *script, CONST char *command,
+ int length));
#ifdef TCL_COMPILE_STATS
static void RecordByteCodeStats _ANSI_ARGS_((
ByteCode *codePtr));
@@ -298,6 +358,7 @@ TclSetByteCodeFromAny(interp, objPtr, hookProc, clientData)
int length, nested, result;
char *string;
+#ifdef TCL_COMPILE_DEBUG
if (!traceInitialized) {
if (Tcl_LinkVar(interp, "tcl_traceCompile",
(char *) &tclTraceCompile, TCL_LINK_INT) != TCL_OK) {
@@ -305,6 +366,7 @@ TclSetByteCodeFromAny(interp, objPtr, hookProc, clientData)
}
traceInitialized = 1;
}
+#endif
if (iPtr->evalFlags & TCL_BRACKET_TERM) {
nested = 1;
@@ -342,7 +404,7 @@ TclSetByteCodeFromAny(interp, objPtr, hookProc, clientData)
TclInitByteCodeObj(objPtr, &compEnv);
#ifdef TCL_COMPILE_DEBUG
- if (tclTraceCompile == 2) {
+ if (tclTraceCompile >= 2) {
TclPrintByteCodeObj(interp, objPtr);
}
#endif /* TCL_COMPILE_DEBUG */
@@ -531,7 +593,7 @@ TclCleanupByteCode(codePtr)
(double) (codePtr->numAuxDataItems * sizeof(AuxData));
statsPtr->currentCmdMapBytes -= (double) codePtr->numCmdLocBytes;
- TclpGetTime(&destroyTime);
+ Tcl_GetTime(&destroyTime);
lifetimeSec = destroyTime.sec - codePtr->createTime.sec;
if (lifetimeSec > 2000) { /* avoid overflow */
lifetimeSec = 2000;
@@ -641,9 +703,8 @@ TclInitCompileEnv(interp, envPtr, string, numBytes)
envPtr->exceptDepth = 0;
envPtr->maxExceptDepth = 0;
envPtr->maxStackDepth = 0;
+ envPtr->currStackDepth = 0;
TclInitLiteralTable(&(envPtr->localLitTable));
- envPtr->exprIsJustVarRef = 0;
- envPtr->exprIsComparison = 0;
envPtr->codeStart = envPtr->staticCodeSpace;
envPtr->codeNext = envPtr->codeStart;
@@ -728,8 +789,6 @@ TclFreeCompileEnv(envPtr)
* interp->termOffset is set to the offset of the character in the
* script just after the last one successfully processed; this will be
* the offset of the ']' if (flags & TCL_BRACKET_TERM).
- * envPtr->maxStackDepth is set to the maximum number of stack elements
- * needed to execute the script's commands.
*
* Side effects:
* Adds instructions to envPtr to evaluate the script at runtime.
@@ -740,7 +799,7 @@ TclFreeCompileEnv(envPtr)
int
TclCompileScript(interp, script, numBytes, nested, envPtr)
Tcl_Interp *interp; /* Used for error and status reporting. */
- char *script; /* The source script to compile. */
+ CONST char *script; /* The source script to compile. */
int numBytes; /* Number of bytes in script. If < 0, the
* script consists of all bytes up to the
* first null character. */
@@ -752,8 +811,6 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
{
Interp *iPtr = (Interp *) interp;
Tcl_Parse parse;
- int maxDepth = 0; /* Maximum number of stack elements needed
- * to execute all cmds. */
int lastTopLevelCmdIndex = -1;
/* Index of most recent toplevel command in
* the command location table. Initialized
@@ -761,7 +818,7 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
int startCodeOffset = -1; /* Offset of first byte of current command's
* code. Init. to avoid compiler warning. */
unsigned char *entryCodeNext = envPtr->codeNext;
- char *p, *next;
+ CONST char *p, *next;
Namespace *cmdNsPtr;
Command *cmdPtr;
Tcl_Token *tokenPtr;
@@ -829,6 +886,7 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
commandLength -= 1;
}
+#ifdef TCL_COMPILE_DEBUG
/*
* If tracing, print a line for each top level command compiled.
*/
@@ -840,7 +898,7 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
TclMin(commandLength, 55));
fprintf(stdout, "\n");
}
-
+#endif
/*
* Each iteration of the following loop compiles one word
* from the command.
@@ -889,12 +947,11 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
if ((cmdPtr != NULL)
&& (cmdPtr->compileProc != NULL)
+ && !(cmdPtr->flags & CMD_HAS_EXEC_TRACES)
&& !(iPtr->flags & DONT_COMPILE_CMDS_INLINE)) {
code = (*(cmdPtr->compileProc))(interp, &parse,
envPtr);
if (code == TCL_OK) {
- maxDepth = TclMax(envPtr->maxStackDepth,
- maxDepth);
goto finishCommand;
} else if (code == TCL_OUT_LINE_COMPILE) {
/* do nothing */
@@ -916,21 +973,18 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
* reduce runtime lookups.
*/
- objIndex = TclRegisterLiteral(envPtr,
- tokenPtr[1].start, tokenPtr[1].size,
- /*onHeap*/ 0);
+ objIndex = TclRegisterNewLiteral(envPtr,
+ tokenPtr[1].start, tokenPtr[1].size);
if (cmdPtr != NULL) {
TclSetCmdNameObj(interp,
envPtr->literalArrayPtr[objIndex].objPtr,
cmdPtr);
}
} else {
- objIndex = TclRegisterLiteral(envPtr,
- tokenPtr[1].start, tokenPtr[1].size,
- /*onHeap*/ 0);
+ objIndex = TclRegisterNewLiteral(envPtr,
+ tokenPtr[1].start, tokenPtr[1].size);
}
TclEmitPush(objIndex, envPtr);
- maxDepth = TclMax((wordIdx + 1), maxDepth);
} else {
/*
* The word is not a simple string of characters.
@@ -941,8 +995,6 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
if (code != TCL_OK) {
goto error;
}
- maxDepth = TclMax((wordIdx + envPtr->maxStackDepth),
- maxDepth);
}
}
@@ -998,7 +1050,6 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
if (envPtr->codeNext == entryCodeNext) {
TclEmitPush(TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0),
envPtr);
- maxDepth = 1;
}
if ((nested != 0) && (p > script) && (p[-1] == ']')) {
@@ -1006,7 +1057,6 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
} else {
iPtr->termOffset = (p - script);
}
- envPtr->maxStackDepth = maxDepth;
Tcl_DStringFree(&ds);
return TCL_OK;
@@ -1039,7 +1089,6 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
Tcl_FreeParse(&parse);
}
iPtr->termOffset = (p - script);
- envPtr->maxStackDepth = maxDepth;
Tcl_DStringFree(&ds);
return code;
}
@@ -1058,9 +1107,6 @@ TclCompileScript(interp, script, numBytes, nested, envPtr)
* The return value is a standard Tcl result. If an error occurs, an
* error message is left in the interpreter's result.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to evaluate the tokens.
- *
* Side effects:
* Instructions are added to envPtr to push and evaluate the tokens
* at runtime.
@@ -1080,13 +1126,12 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
Tcl_DString textBuffer; /* Holds concatenated chars from adjacent
* TCL_TOKEN_TEXT, TCL_TOKEN_BS tokens. */
char buffer[TCL_UTF_MAX];
- char *name, *p;
- int numObjsToConcat, nameBytes, hasNsQualifiers, localVar;
- int length, maxDepth, depthForVar, i, code;
+ CONST char *name, *p;
+ int numObjsToConcat, nameBytes, localVarName, localVar;
+ int length, i, code;
unsigned char *entryCodeNext = envPtr->codeNext;
Tcl_DStringInit(&textBuffer);
- maxDepth = 0;
numObjsToConcat = 0;
for ( ; count > 0; count--, tokenPtr++) {
switch (tokenPtr->type) {
@@ -1114,7 +1159,6 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
Tcl_DStringLength(&textBuffer), /*onHeap*/ 0);
TclEmitPush(literal, envPtr);
numObjsToConcat++;
- maxDepth = TclMax(numObjsToConcat, maxDepth);
Tcl_DStringFree(&textBuffer);
}
@@ -1123,8 +1167,6 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
if (code != TCL_OK) {
goto error;
}
- maxDepth = TclMax((numObjsToConcat + envPtr->maxStackDepth),
- maxDepth);
numObjsToConcat++;
break;
@@ -1141,44 +1183,49 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
Tcl_DStringLength(&textBuffer), /*onHeap*/ 0);
TclEmitPush(literal, envPtr);
numObjsToConcat++;
- maxDepth = TclMax(numObjsToConcat, maxDepth);
Tcl_DStringFree(&textBuffer);
}
/*
- * Check if the name contains any namespace qualifiers.
+ * Determine how the variable name should be handled: if it contains
+ * any namespace qualifiers it is not a local variable (localVarName=-1);
+ * if it looks like an array element and the token has a single component,
+ * it should not be created here [Bug 569438] (localVarName=0); otherwise,
+ * the local variable can safely be created (localVarName=1).
*/
name = tokenPtr[1].start;
nameBytes = tokenPtr[1].size;
- hasNsQualifiers = 0;
- for (i = 0, p = name; i < nameBytes; i++, p++) {
- if ((*p == ':') && (i < (nameBytes-1))
- && (*(p+1) == ':')) {
- hasNsQualifiers = 1;
- break;
+ localVarName = -1;
+ if (envPtr->procPtr != NULL) {
+ localVarName = 1;
+ for (i = 0, p = name; i < nameBytes; i++, p++) {
+ if ((*p == ':') && (i < (nameBytes-1))
+ && (*(p+1) == ':')) {
+ localVarName = -1;
+ break;
+ } else if ((*p == '(')
+ && (tokenPtr->numComponents == 1)
+ && (*(name + nameBytes - 1) == ')')) {
+ localVarName = 0;
+ break;
+ }
}
}
/*
* Either push the variable's name, or find its index in
- * the array of local variables in a procedure frame.
+ * the array of local variables in a procedure frame.
*/
- depthForVar = 0;
- if ((envPtr->procPtr == NULL) || hasNsQualifiers) {
- localVar = -1;
- TclEmitPush(TclRegisterLiteral(envPtr, name, nameBytes,
- /*onHeap*/ 0), envPtr);
- depthForVar = 1;
- } else {
+ localVar = -1;
+ if (localVarName != -1) {
localVar = TclFindCompiledLocal(name, nameBytes,
- /*create*/ 0, /*flags*/ 0, envPtr->procPtr);
- if (localVar < 0) {
- TclEmitPush(TclRegisterLiteral(envPtr, name,
- nameBytes, /*onHeap*/ 0), envPtr);
- depthForVar = 1;
- }
+ localVarName, /*flags*/ 0, envPtr->procPtr);
+ }
+ if (localVar < 0) {
+ TclEmitPush(TclRegisterNewLiteral(envPtr, name, nameBytes),
+ envPtr);
}
/*
@@ -1199,13 +1246,13 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
code = TclCompileTokens(interp, tokenPtr+2,
tokenPtr->numComponents-1, envPtr);
if (code != TCL_OK) {
- sprintf(buffer,
+ char errorBuffer[150];
+ sprintf(errorBuffer,
"\n (parsing index for array \"%.*s\")",
((nameBytes > 100)? 100 : nameBytes), name);
- Tcl_AddObjErrorInfo(interp, buffer, -1);
+ Tcl_AddObjErrorInfo(interp, errorBuffer, -1);
goto error;
}
- depthForVar += envPtr->maxStackDepth;
if (localVar < 0) {
TclEmitOpcode(INST_LOAD_ARRAY_STK, envPtr);
} else if (localVar <= 255) {
@@ -1216,7 +1263,6 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
envPtr);
}
}
- maxDepth = TclMax(numObjsToConcat + depthForVar, maxDepth);
numObjsToConcat++;
count -= tokenPtr->numComponents;
tokenPtr += tokenPtr->numComponents;
@@ -1238,7 +1284,6 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
Tcl_DStringLength(&textBuffer), /*onHeap*/ 0);
TclEmitPush(literal, envPtr);
numObjsToConcat++;
- maxDepth = TclMax(numObjsToConcat, maxDepth);
}
/*
@@ -1260,15 +1305,12 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
if (envPtr->codeNext == entryCodeNext) {
TclEmitPush(TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0),
envPtr);
- maxDepth = 1;
}
Tcl_DStringFree(&textBuffer);
- envPtr->maxStackDepth = maxDepth;
return TCL_OK;
error:
Tcl_DStringFree(&textBuffer);
- envPtr->maxStackDepth = maxDepth;
return code;
}
@@ -1287,9 +1329,6 @@ TclCompileTokens(interp, tokenPtr, count, envPtr)
* The return value is a standard Tcl result. If an error occurs, an
* error message is left in the interpreter's result.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the tokens.
- *
* Side effects:
* Instructions are added to envPtr to execute the tokens at runtime.
*
@@ -1312,7 +1351,6 @@ TclCompileCmdWord(interp, tokenPtr, count, envPtr)
* into an inline sequence of instructions.
*/
- envPtr->maxStackDepth = 0;
if ((count == 1) && (tokenPtr->type == TCL_TOKEN_TEXT)) {
code = TclCompileScript(interp, tokenPtr->start, tokenPtr->size,
/*nested*/ 0, envPtr);
@@ -1348,9 +1386,6 @@ TclCompileCmdWord(interp, tokenPtr, count, envPtr)
* The return value is a standard Tcl result. If an error occurs, an
* error message is left in the interpreter's result.
*
- * envPtr->maxStackDepth is updated with the maximum number of stack
- * elements needed to execute the expression.
- *
* Side effects:
* Instructions are added to envPtr to execute the expression.
*
@@ -1369,13 +1404,9 @@ TclCompileExprWords(interp, tokenPtr, numWords, envPtr)
CompileEnv *envPtr; /* Holds the resulting instructions. */
{
Tcl_Token *wordPtr;
- int maxDepth, range, numBytes, i, code;
- char *script;
- int saveExprIsJustVarRef = envPtr->exprIsJustVarRef;
- int saveExprIsComparison = envPtr->exprIsComparison;
+ int range, numBytes, i, code;
+ CONST char *script;
- envPtr->maxStackDepth = 0;
- maxDepth = 0;
range = -1;
code = TCL_OK;
@@ -1411,9 +1442,6 @@ TclCompileExprWords(interp, tokenPtr, numWords, envPtr)
if (i < (numWords - 1)) {
TclEmitPush(TclRegisterLiteral(envPtr, " ", 1, /*onHeap*/ 0),
envPtr);
- maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);
- } else {
- maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
}
wordPtr += (wordPtr->numComponents + 1);
}
@@ -1429,9 +1457,6 @@ TclCompileExprWords(interp, tokenPtr, numWords, envPtr)
TclEmitOpcode(INST_EXPR_STK, envPtr);
}
- envPtr->exprIsJustVarRef = saveExprIsJustVarRef;
- envPtr->exprIsComparison = saveExprIsComparison;
- envPtr->maxStackDepth = maxDepth;
return code;
}
@@ -1523,7 +1548,7 @@ TclInitByteCodeObj(objPtr, envPtr)
codePtr->numCmdLocBytes = cmdLocBytes;
codePtr->maxExceptDepth = envPtr->maxExceptDepth;
codePtr->maxStackDepth = envPtr->maxStackDepth;
-
+
p += sizeof(ByteCode);
codePtr->codeStart = p;
memcpy((VOID *) p, (VOID *) envPtr->codeStart, (size_t) codeBytes);
@@ -1568,7 +1593,7 @@ TclInitByteCodeObj(objPtr, envPtr)
#ifdef TCL_COMPILE_STATS
codePtr->structureSize = structureSize
- (sizeof(size_t) + sizeof(Tcl_Time));
- TclpGetTime(&(codePtr->createTime));
+ Tcl_GetTime(&(codePtr->createTime));
RecordByteCodeStats(codePtr);
#endif /* TCL_COMPILE_STATS */
@@ -1613,15 +1638,15 @@ static void
LogCompilationInfo(interp, script, command, length)
Tcl_Interp *interp; /* Interpreter in which to log the
* information. */
- char *script; /* First character in script containing
+ CONST char *script; /* First character in script containing
* command (must be <= command). */
- char *command; /* First character in command that
+ CONST char *command; /* First character in command that
* generated the error. */
int length; /* Number of bytes in command (-1 means
* use all bytes up to first null byte). */
{
char buffer[200];
- register char *p;
+ register CONST char *p;
char *ellipsis = "";
Interp *iPtr = (Interp *) interp;
@@ -1690,7 +1715,7 @@ LogCompilationInfo(interp, script, command, length)
int
TclFindCompiledLocal(name, nameBytes, create, flags, procPtr)
- register char *name; /* Points to first character of the name of
+ register CONST char *name; /* Points to first character of the name of
* a scalar or array variable. If NULL, a
* temporary var should be created. */
int nameBytes; /* Number of bytes in the name. */
@@ -1744,7 +1769,7 @@ TclFindCompiledLocal(name, nameBytes, create, flags, procPtr)
localPtr->nextPtr = NULL;
localPtr->nameLength = nameBytes;
localPtr->frameIndex = localVar;
- localPtr->flags = flags;
+ localPtr->flags = flags | VAR_UNDEFINED;
if (name == NULL) {
localPtr->flags |= VAR_TEMPORARY;
}
@@ -1868,7 +1893,7 @@ TclInitCompiledLocals(interp, framePtr, nsPtr)
varPtr->refCount = 0;
varPtr->tracePtr = NULL;
varPtr->searchPtr = NULL;
- varPtr->flags = (localPtr->flags | VAR_UNDEFINED);
+ varPtr->flags = localPtr->flags;
}
varPtr++;
}
@@ -1895,10 +1920,13 @@ TclInitCompiledLocals(interp, framePtr, nsPtr)
*/
void
-TclExpandCodeArray(envPtr)
- CompileEnv *envPtr; /* Points to the CompileEnv whose code array
+TclExpandCodeArray(envArgPtr)
+ void *envArgPtr; /* Points to the CompileEnv whose code array
* must be enlarged. */
{
+ CompileEnv *envPtr = (CompileEnv*) envArgPtr; /* Points to the CompileEnv whose code array
+ * must be enlarged. */
+
/*
* envPtr->codeNext is equal to envPtr->codeEnd. The currently defined
* code bytes are stored between envPtr->codeStart and
@@ -2489,7 +2517,7 @@ TclFixupForwardJump(envPtr, jumpFixupPtr, jumpDist, distThreshold)
*
* Results:
* Returns a pointer to the global instruction table, same as the
- * expression (&instructionTable[0]).
+ * expression (&tclInstructionTable[0]).
*
* Side effects:
* None.
@@ -2497,10 +2525,10 @@ TclFixupForwardJump(envPtr, jumpFixupPtr, jumpDist, distThreshold)
*----------------------------------------------------------------------
*/
-InstructionDesc *
+void * /* == InstructionDesc* == */
TclGetInstructionTable()
{
- return &instructionTable[0];
+ return &tclInstructionTable[0];
}
/*
@@ -3157,7 +3185,7 @@ TclPrintInstruction(codePtr, pc)
{
Proc *procPtr = codePtr->procPtr;
unsigned char opCode = *pc;
- register InstructionDesc *instDesc = &instructionTable[opCode];
+ register InstructionDesc *instDesc = &tclInstructionTable[opCode];
unsigned char *codeStart = codePtr->codeStart;
unsigned int pcOffset = (pc - codeStart);
int opnd, i, j;
@@ -3383,7 +3411,7 @@ RecordByteCodeStats(codePtr)
statsPtr->currentByteCodeBytes += (double) codePtr->structureSize;
statsPtr->srcCount[TclLog2(codePtr->numSrcBytes)]++;
- statsPtr->byteCodeCount[TclLog2(codePtr->structureSize)]++;
+ statsPtr->byteCodeCount[TclLog2((int)(codePtr->structureSize))]++;
statsPtr->currentInstBytes += (double) codePtr->numCodeBytes;
statsPtr->currentLitBytes +=
@@ -3395,4 +3423,3 @@ RecordByteCodeStats(codePtr)
statsPtr->currentCmdMapBytes += (double) codePtr->numCmdLocBytes;
}
#endif /* TCL_COMPILE_STATS */
-
diff --git a/tcl/generic/tclCompile.h b/tcl/generic/tclCompile.h
index cd513510f38..92c8aae5587 100644
--- a/tcl/generic/tclCompile.h
+++ b/tcl/generic/tclCompile.h
@@ -2,6 +2,8 @@
* tclCompile.h --
*
* Copyright (c) 1996-1998 Sun Microsystems, Inc.
+ * Copyright (c) 1998-2000 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -28,14 +30,7 @@
*------------------------------------------------------------------------
*/
-/*
- * Variable that denotes the command name Tcl object type. Objects of this
- * type cache the Command pointer that results from looking up command names
- * in the command hashtable.
- */
-
-extern Tcl_ObjType tclCmdNameType;
-
+#ifdef TCL_COMPILE_DEBUG
/*
* Variable that controls whether compilation tracing is enabled and, if so,
* what level of tracing is desired:
@@ -46,7 +41,9 @@ extern Tcl_ObjType tclCmdNameType;
*/
extern int tclTraceCompile;
+#endif
+#ifdef TCL_COMPILE_DEBUG
/*
* Variable that controls whether execution tracing is enabled and, if so,
* what level of tracing is desired:
@@ -58,6 +55,7 @@ extern int tclTraceCompile;
*/
extern int tclTraceExec;
+#endif
/*
*------------------------------------------------------------------------
@@ -211,23 +209,12 @@ typedef struct CompileEnv {
int maxStackDepth; /* Maximum number of stack elements needed
* to execute the code. Set by compilation
* procedures before returning. */
+ int currStackDepth; /* Current stack depth. */
LiteralTable localLitTable; /* Contains LiteralEntry's describing
* all Tcl objects referenced by this
* compiled code. Indexed by the string
* representations of the literals. Used to
* avoid creating duplicate objects. */
- int exprIsJustVarRef; /* Set 1 if the expression last compiled by
- * TclCompileExpr consisted of just a
- * variable reference as in the expression
- * of "if $b then...". Otherwise 0. Used
- * to implement expr's 2 level substitution
- * semantics properly. */
- int exprIsComparison; /* Set 1 if the top-level operator in the
- * expression last compiled is a comparison.
- * Otherwise 0. If 1, since the operands
- * might be strings, the expr is compiled
- * out-of-line to implement expr's 2 level
- * substitution semantics properly. */
unsigned char *codeStart; /* Points to the first byte of the code. */
unsigned char *codeNext; /* Points to next code array byte to use. */
unsigned char *codeEnd; /* Points just after the last allocated
@@ -397,11 +384,11 @@ typedef struct ByteCode {
} ByteCode;
/*
- * Opcodes for the Tcl bytecode instructions. These must correspond to the
- * entries in the table of instruction descriptions, instructionTable, in
- * tclCompile.c. Also, the order and number of the expression opcodes
- * (e.g., INST_LOR) must match the entries in the array operatorStrings in
- * tclExecute.c.
+ * Opcodes for the Tcl bytecode instructions. These must correspond to
+ * the entries in the table of instruction descriptions,
+ * tclInstructionTable, in tclCompile.c. Also, the order and number of
+ * the expression opcodes (e.g., INST_LOR) must match the entries in
+ * the array operatorStrings in tclExecute.c.
*/
/* Opcodes 0 to 9 */
@@ -493,8 +480,50 @@ typedef struct ByteCode {
#define INST_PUSH_RESULT 71
#define INST_PUSH_RETURN_CODE 72
+/* Opcodes 73 to 78 */
+#define INST_STR_EQ 73
+#define INST_STR_NEQ 74
+#define INST_STR_CMP 75
+#define INST_STR_LEN 76
+#define INST_STR_INDEX 77
+#define INST_STR_MATCH 78
+
+/* Opcodes 78 to 81 */
+#define INST_LIST 79
+#define INST_LIST_INDEX 80
+#define INST_LIST_LENGTH 81
+
+/* Opcodes 82 to 87 */
+#define INST_APPEND_SCALAR1 82
+#define INST_APPEND_SCALAR4 83
+#define INST_APPEND_ARRAY1 84
+#define INST_APPEND_ARRAY4 85
+#define INST_APPEND_ARRAY_STK 86
+#define INST_APPEND_STK 87
+
+/* Opcodes 88 to 93 */
+#define INST_LAPPEND_SCALAR1 88
+#define INST_LAPPEND_SCALAR4 89
+#define INST_LAPPEND_ARRAY1 90
+#define INST_LAPPEND_ARRAY4 91
+#define INST_LAPPEND_ARRAY_STK 92
+#define INST_LAPPEND_STK 93
+
+/* TIP #22 - LINDEX operator with flat arg list */
+
+#define INST_LIST_INDEX_MULTI 94
+
+/*
+ * TIP #33 - 'lset' command. Code gen also required a Forth-like
+ * OVER operation.
+ */
+
+#define INST_OVER 95
+#define INST_LSET_LIST 96
+#define INST_LSET_FLAT 97
+
/* The last opcode */
-#define LAST_INST_OPCODE 72
+#define LAST_INST_OPCODE 97
/*
* Table describing the Tcl bytecode instructions: their name (for
@@ -518,17 +547,23 @@ typedef enum InstOperandType {
typedef struct InstructionDesc {
char *name; /* Name of instruction. */
int numBytes; /* Total number of bytes for instruction. */
+ int stackEffect; /* The worst-case balance stack effect of the
+ * instruction, used for stack requirements
+ * computations. The value INT_MIN signals
+ * that the instruction's worst case effect
+ * is (1-opnd1).
+ */
int numOperands; /* Number of operands. */
InstOperandType opTypes[MAX_INSTRUCTION_OPERANDS];
/* The type of each operand. */
} InstructionDesc;
-extern InstructionDesc instructionTable[];
+extern InstructionDesc tclInstructionTable[];
/*
* Definitions of the values of the INST_CALL_BUILTIN_FUNC instruction's
* operand byte. Each value denotes a builtin Tcl math function. These
- * values must correspond to the entries in the builtinFuncTable array
+ * values must correspond to the entries in the tclBuiltinFuncTable array
* below and to the values stored in the tclInt.h MathFunc structure's
* builtinFuncIndex field.
*/
@@ -558,8 +593,9 @@ extern InstructionDesc instructionTable[];
#define BUILTIN_FUNC_RAND 22
#define BUILTIN_FUNC_ROUND 23
#define BUILTIN_FUNC_SRAND 24
+#define BUILTIN_FUNC_WIDE 25
-#define LAST_BUILTIN_FUNC 24
+#define LAST_BUILTIN_FUNC 25
/*
* Table describing the built-in math functions. Entries in this table are
@@ -580,7 +616,7 @@ typedef struct {
* function when invoking it. */
} BuiltinFunc;
-extern BuiltinFunc builtinFuncTable[];
+extern BuiltinFunc tclBuiltinFuncTable[];
/*
* Compilation of some Tcl constructs such as if commands and the logical or
@@ -672,40 +708,27 @@ typedef struct ForeachInfo {
extern AuxDataType tclForeachInfoType;
+
/*
- * Structure containing a cached pointer to a command that is the result
- * of resolving the command's name in some namespace. It is the internal
- * representation for a cmdName object. It contains the pointer along
- * with some information that is used to check the pointer's validity.
- */
-
-typedef struct ResolvedCmdName {
- Command *cmdPtr; /* A cached Command pointer. */
- Namespace *refNsPtr; /* Points to the namespace containing the
- * reference (not the namespace that
- * contains the referenced command). */
- long refNsId; /* refNsPtr's unique namespace id. Used to
- * verify that refNsPtr is still valid
- * (e.g., it's possible that the cmd's
- * containing namespace was deleted and a
- * new one created at the same address). */
- int refNsCmdEpoch; /* Value of the referencing namespace's
- * cmdRefEpoch when the pointer was cached.
- * Before using the cached pointer, we check
- * if the namespace's epoch was incremented;
- * if so, this cached pointer is invalid. */
- int cmdEpoch; /* Value of the command's cmdEpoch when this
- * pointer was cached. Before using the
- * cached pointer, we check if the cmd's
- * epoch was incremented; if so, the cmd was
- * renamed, deleted, hidden, or exposed, and
- * so the pointer is invalid. */
- int refCount; /* Reference count: 1 for each cmdName
- * object that has a pointer to this
- * ResolvedCmdName structure as its internal
- * rep. This structure can be freed when
- * refCount becomes zero. */
-} ResolvedCmdName;
+ *----------------------------------------------------------------
+ * Procedures exported by tclBasic.c to be used within the engine.
+ *----------------------------------------------------------------
+ */
+
+EXTERN int TclEvalObjvInternal _ANSI_ARGS_((Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[], CONST char *command, int length,
+ int flags));
+EXTERN int TclInterpReady _ANSI_ARGS_((Tcl_Interp *interp));
+
+
+/*
+ *----------------------------------------------------------------
+ * Procedures exported by the engine to be used by tclBasic.c
+ *----------------------------------------------------------------
+ */
+
+EXTERN int TclCompEvalObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr));
/*
*----------------------------------------------------------------
@@ -719,13 +742,13 @@ EXTERN int TclCompileCmdWord _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Token *tokenPtr, int count,
CompileEnv *envPtr));
EXTERN int TclCompileExpr _ANSI_ARGS_((Tcl_Interp *interp,
- char *script, int numBytes,
+ CONST char *script, int numBytes,
CompileEnv *envPtr));
EXTERN int TclCompileExprWords _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Token *tokenPtr, int numWords,
CompileEnv *envPtr));
EXTERN int TclCompileScript _ANSI_ARGS_((Tcl_Interp *interp,
- char *script, int numBytes, int nested,
+ CONST char *script, int numBytes, int nested,
CompileEnv *envPtr));
EXTERN int TclCompileTokens _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Token *tokenPtr, int count,
@@ -743,15 +766,10 @@ EXTERN void TclEmitForwardJump _ANSI_ARGS_((CompileEnv *envPtr,
EXTERN ExceptionRange * TclGetExceptionRangeForPc _ANSI_ARGS_((
unsigned char *pc, int catchOnly,
ByteCode* codePtr));
-EXTERN InstructionDesc * TclGetInstructionTable _ANSI_ARGS_(());
-EXTERN int TclExecuteByteCode _ANSI_ARGS_((Tcl_Interp *interp,
- ByteCode *codePtr));
-EXTERN void TclExpandCodeArray _ANSI_ARGS_((
- CompileEnv *envPtr));
EXTERN void TclExpandJumpFixupArray _ANSI_ARGS_((
JumpFixupArray *fixupArrayPtr));
EXTERN void TclFinalizeAuxDataTypeTable _ANSI_ARGS_((void));
-EXTERN int TclFindCompiledLocal _ANSI_ARGS_((char *name,
+EXTERN int TclFindCompiledLocal _ANSI_ARGS_((CONST char *name,
int nameChars, int create, int flags,
Proc *procPtr));
EXTERN LiteralEntry * TclLookupLiteralEntry _ANSI_ARGS_((
@@ -810,6 +828,40 @@ EXTERN void TclVerifyLocalLiteralTable _ANSI_ARGS_((
*/
/*
+ * Form of TclRegisterLiteral with onHeap == 0.
+ * In that case, it is safe to cast away CONSTness, and it
+ * is cleanest to do that here, all in one place.
+ */
+
+#define TclRegisterNewLiteral(envPtr, bytes, length) \
+ TclRegisterLiteral(envPtr, (char *)(bytes), length, /*onHeap*/ 0)
+
+/*
+ * Macro used to update the stack requirements.
+ * It is called by the macros TclEmitOpCode, TclEmitInst1 and
+ * TclEmitInst4.
+ * Remark that the very last instruction of a bytecode always
+ * reduces the stack level: INST_DONE or INST_POP, so that the
+ * maxStackdepth is always updated.
+ */
+
+#define TclUpdateStackReqs(op, i, envPtr) \
+ {\
+ int delta = tclInstructionTable[(op)].stackEffect;\
+ if (delta) {\
+ if (delta < 0) {\
+ if((envPtr)->maxStackDepth < (envPtr)->currStackDepth) {\
+ (envPtr)->maxStackDepth = (envPtr)->currStackDepth;\
+ }\
+ if (delta == INT_MIN) {\
+ delta = 1 - (i);\
+ }\
+ }\
+ (envPtr)->currStackDepth += delta;\
+ }\
+ }
+
+/*
* Macro to emit an opcode byte into a CompileEnv's code array.
* The ANSI C "prototype" for this macro is:
*
@@ -820,7 +872,8 @@ EXTERN void TclVerifyLocalLiteralTable _ANSI_ARGS_((
#define TclEmitOpcode(op, envPtr) \
if ((envPtr)->codeNext == (envPtr)->codeEnd) \
TclExpandCodeArray(envPtr); \
- *(envPtr)->codeNext++ = (unsigned char) (op)
+ *(envPtr)->codeNext++ = (unsigned char) (op);\
+ TclUpdateStackReqs(op, 0, envPtr)
/*
* Macro to emit an integer operand.
@@ -846,12 +899,14 @@ EXTERN void TclVerifyLocalLiteralTable _ANSI_ARGS_((
* CompileEnv *envPtr));
*/
+
#define TclEmitInstInt1(op, i, envPtr) \
if (((envPtr)->codeNext + 2) > (envPtr)->codeEnd) { \
TclExpandCodeArray(envPtr); \
} \
*(envPtr)->codeNext++ = (unsigned char) (op); \
- *(envPtr)->codeNext++ = (unsigned char) ((unsigned int) (i))
+ *(envPtr)->codeNext++ = (unsigned char) ((unsigned int) (i));\
+ TclUpdateStackReqs(op, i, envPtr)
#define TclEmitInstInt4(op, i, envPtr) \
if (((envPtr)->codeNext + 5) > (envPtr)->codeEnd) { \
@@ -865,7 +920,8 @@ EXTERN void TclVerifyLocalLiteralTable _ANSI_ARGS_((
*(envPtr)->codeNext++ = \
(unsigned char) ((unsigned int) (i) >> 8); \
*(envPtr)->codeNext++ = \
- (unsigned char) ((unsigned int) (i) )
+ (unsigned char) ((unsigned int) (i) );\
+ TclUpdateStackReqs(op, i, envPtr)
/*
* Macro to push a Tcl object onto the Tcl evaluation stack. It emits the
@@ -877,10 +933,13 @@ EXTERN void TclVerifyLocalLiteralTable _ANSI_ARGS_((
*/
#define TclEmitPush(objIndex, envPtr) \
- if ((objIndex) <= 255) { \
- TclEmitInstInt1(INST_PUSH1, (objIndex), (envPtr)); \
- } else { \
- TclEmitInstInt4(INST_PUSH4, (objIndex), (envPtr)); \
+ {\
+ register int objIndexCopy = (objIndex);\
+ if (objIndexCopy <= 255) { \
+ TclEmitInstInt1(INST_PUSH1, objIndexCopy, (envPtr)); \
+ } else { \
+ TclEmitInstInt4(INST_PUSH4, objIndexCopy, (envPtr)); \
+ }\
}
/*
@@ -978,3 +1037,8 @@ EXTERN void TclVerifyLocalLiteralTable _ANSI_ARGS_((
# define TCL_STORAGE_CLASS DLLIMPORT
#endif /* _TCLCOMPILATION */
+
+
+
+
+
diff --git a/tcl/generic/tclDate.c b/tcl/generic/tclDate.c
index c7d01419618..9b87542c25c 100644
--- a/tcl/generic/tclDate.c
+++ b/tcl/generic/tclDate.c
@@ -16,7 +16,7 @@
#include "tclInt.h"
#include "tclPort.h"
-#ifdef MAC_TCL
+#if defined(MAC_TCL) && !defined(TCL_MAC_USE_MSL_EPOCH)
# define EPOCH 1904
# define START_OF_TIME 1904
# define END_OF_TIME 2039
@@ -579,6 +579,23 @@ RelativeMonth(Start, RelMonth, TimePtr)
result = Convert(Month, (time_t) tm->tm_mday, Year,
(time_t) tm->tm_hour, (time_t) tm->tm_min, (time_t) tm->tm_sec,
MER24, DSTmaybe, &Julian);
+
+ /*
+ * The Julian time returned above is behind by one day, if "month"
+ * or "year" is used to specify relative time and the GMT flag is true.
+ * This problem occurs only when the current time is closer to
+ * midnight, the difference being not more than its time difference
+ * with GMT. For example, in US/Pacific time zone, the problem occurs
+ * whenever the current time is between midnight to 8:00am or 7:00amDST.
+ * See Bug# 413397 for more details and sample script.
+ * To resolve this bug, we simply add the number of seconds corresponding
+ * to timezone difference with GMT to Julian time, if GMT flag is true.
+ */
+
+ if (TclDateTimezone == 0) {
+ Julian += TclpGetTimeZone((unsigned long) Start) * 60L;
+ }
+
/*
* The following iteration takes into account the case were we jump
* into a "short month". Far example, "one month from Jan 31" will
@@ -1853,4 +1870,3 @@ case 55:{
goto TclDatestack; /* reset registers in driver code */
}
-
diff --git a/tcl/generic/tclDecls.h b/tcl/generic/tclDecls.h
index b231e49d59c..7af2597c099 100644
--- a/tcl/generic/tclDecls.h
+++ b/tcl/generic/tclDecls.h
@@ -27,15 +27,16 @@
*/
/* 0 */
-EXTERN int Tcl_PkgProvideEx _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, char * version,
+EXTERN int Tcl_PkgProvideEx _ANSI_ARGS_((Tcl_Interp* interp,
+ CONST char* name, CONST char* version,
ClientData clientData));
/* 1 */
-EXTERN char * Tcl_PkgRequireEx _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, char * version, int exact,
+EXTERN CONST84_RETURN char * Tcl_PkgRequireEx _ANSI_ARGS_((
+ Tcl_Interp * interp, CONST char * name,
+ CONST char * version, int exact,
ClientData * clientDataPtr));
/* 2 */
-EXTERN void Tcl_Panic _ANSI_ARGS_(TCL_VARARGS(char *,format));
+EXTERN void Tcl_Panic _ANSI_ARGS_(TCL_VARARGS(CONST char *,format));
/* 3 */
EXTERN char * Tcl_Alloc _ANSI_ARGS_((unsigned int size));
/* 4 */
@@ -45,13 +46,14 @@ EXTERN char * Tcl_Realloc _ANSI_ARGS_((char * ptr,
unsigned int size));
/* 6 */
EXTERN char * Tcl_DbCkalloc _ANSI_ARGS_((unsigned int size,
- char * file, int line));
+ CONST char * file, int line));
/* 7 */
-EXTERN int Tcl_DbCkfree _ANSI_ARGS_((char * ptr, char * file,
- int line));
+EXTERN int Tcl_DbCkfree _ANSI_ARGS_((char * ptr,
+ CONST char * file, int line));
/* 8 */
EXTERN char * Tcl_DbCkrealloc _ANSI_ARGS_((char * ptr,
- unsigned int size, char * file, int line));
+ unsigned int size, CONST char * file,
+ int line));
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
/* 9 */
EXTERN void Tcl_CreateFileHandler _ANSI_ARGS_((int fd, int mask,
@@ -73,8 +75,8 @@ EXTERN int Tcl_AppendAllObjTypes _ANSI_ARGS_((
/* 15 */
EXTERN void Tcl_AppendStringsToObj _ANSI_ARGS_(TCL_VARARGS(Tcl_Obj *,objPtr));
/* 16 */
-EXTERN void Tcl_AppendToObj _ANSI_ARGS_((Tcl_Obj * objPtr,
- char * bytes, int length));
+EXTERN void Tcl_AppendToObj _ANSI_ARGS_((Tcl_Obj* objPtr,
+ CONST char* bytes, int length));
/* 17 */
EXTERN Tcl_Obj * Tcl_ConcatObj _ANSI_ARGS_((int objc,
Tcl_Obj *CONST objv[]));
@@ -83,41 +85,43 @@ EXTERN int Tcl_ConvertToType _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * objPtr, Tcl_ObjType * typePtr));
/* 19 */
EXTERN void Tcl_DbDecrRefCount _ANSI_ARGS_((Tcl_Obj * objPtr,
- char * file, int line));
+ CONST char * file, int line));
/* 20 */
EXTERN void Tcl_DbIncrRefCount _ANSI_ARGS_((Tcl_Obj * objPtr,
- char * file, int line));
+ CONST char * file, int line));
/* 21 */
EXTERN int Tcl_DbIsShared _ANSI_ARGS_((Tcl_Obj * objPtr,
- char * file, int line));
+ CONST char * file, int line));
/* 22 */
EXTERN Tcl_Obj * Tcl_DbNewBooleanObj _ANSI_ARGS_((int boolValue,
- char * file, int line));
+ CONST char * file, int line));
/* 23 */
EXTERN Tcl_Obj * Tcl_DbNewByteArrayObj _ANSI_ARGS_((
- unsigned char * bytes, int length,
- char * file, int line));
+ CONST unsigned char * bytes, int length,
+ CONST char * file, int line));
/* 24 */
EXTERN Tcl_Obj * Tcl_DbNewDoubleObj _ANSI_ARGS_((double doubleValue,
- char * file, int line));
+ CONST char * file, int line));
/* 25 */
EXTERN Tcl_Obj * Tcl_DbNewListObj _ANSI_ARGS_((int objc,
- Tcl_Obj *CONST objv[], char * file, int line));
+ Tcl_Obj *CONST * objv, CONST char * file,
+ int line));
/* 26 */
EXTERN Tcl_Obj * Tcl_DbNewLongObj _ANSI_ARGS_((long longValue,
- char * file, int line));
+ CONST char * file, int line));
/* 27 */
-EXTERN Tcl_Obj * Tcl_DbNewObj _ANSI_ARGS_((char * file, int line));
+EXTERN Tcl_Obj * Tcl_DbNewObj _ANSI_ARGS_((CONST char * file,
+ int line));
/* 28 */
EXTERN Tcl_Obj * Tcl_DbNewStringObj _ANSI_ARGS_((CONST char * bytes,
- int length, char * file, int line));
+ int length, CONST char * file, int line));
/* 29 */
EXTERN Tcl_Obj * Tcl_DuplicateObj _ANSI_ARGS_((Tcl_Obj * objPtr));
/* 30 */
EXTERN void TclFreeObj _ANSI_ARGS_((Tcl_Obj * objPtr));
/* 31 */
EXTERN int Tcl_GetBoolean _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, int * boolPtr));
+ CONST char * str, int * boolPtr));
/* 32 */
EXTERN int Tcl_GetBooleanFromObj _ANSI_ARGS_((
Tcl_Interp * interp, Tcl_Obj * objPtr,
@@ -127,18 +131,18 @@ EXTERN unsigned char * Tcl_GetByteArrayFromObj _ANSI_ARGS_((
Tcl_Obj * objPtr, int * lengthPtr));
/* 34 */
EXTERN int Tcl_GetDouble _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, double * doublePtr));
+ CONST char * str, double * doublePtr));
/* 35 */
EXTERN int Tcl_GetDoubleFromObj _ANSI_ARGS_((
Tcl_Interp * interp, Tcl_Obj * objPtr,
double * doublePtr));
/* 36 */
EXTERN int Tcl_GetIndexFromObj _ANSI_ARGS_((Tcl_Interp * interp,
- Tcl_Obj * objPtr, char ** tablePtr,
- char * msg, int flags, int * indexPtr));
+ Tcl_Obj * objPtr, CONST84 char ** tablePtr,
+ CONST char * msg, int flags, int * indexPtr));
/* 37 */
EXTERN int Tcl_GetInt _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, int * intPtr));
+ CONST char * str, int * intPtr));
/* 38 */
EXTERN int Tcl_GetIntFromObj _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * objPtr, int * intPtr));
@@ -146,7 +150,7 @@ EXTERN int Tcl_GetIntFromObj _ANSI_ARGS_((Tcl_Interp * interp,
EXTERN int Tcl_GetLongFromObj _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * objPtr, long * longPtr));
/* 40 */
-EXTERN Tcl_ObjType * Tcl_GetObjType _ANSI_ARGS_((char * typeName));
+EXTERN Tcl_ObjType * Tcl_GetObjType _ANSI_ARGS_((CONST char * typeName));
/* 41 */
EXTERN char * Tcl_GetStringFromObj _ANSI_ARGS_((Tcl_Obj * objPtr,
int * lengthPtr));
@@ -171,7 +175,7 @@ EXTERN int Tcl_ListObjIndex _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj ** objPtrPtr));
/* 47 */
EXTERN int Tcl_ListObjLength _ANSI_ARGS_((Tcl_Interp * interp,
- Tcl_Obj * listPtr, int * intPtr));
+ Tcl_Obj * listPtr, int * lengthPtr));
/* 48 */
EXTERN int Tcl_ListObjReplace _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * listPtr, int first, int count,
@@ -180,7 +184,7 @@ EXTERN int Tcl_ListObjReplace _ANSI_ARGS_((Tcl_Interp * interp,
EXTERN Tcl_Obj * Tcl_NewBooleanObj _ANSI_ARGS_((int boolValue));
/* 50 */
EXTERN Tcl_Obj * Tcl_NewByteArrayObj _ANSI_ARGS_((
- unsigned char * bytes, int length));
+ CONST unsigned char* bytes, int length));
/* 51 */
EXTERN Tcl_Obj * Tcl_NewDoubleObj _ANSI_ARGS_((double doubleValue));
/* 52 */
@@ -203,7 +207,7 @@ EXTERN unsigned char * Tcl_SetByteArrayLength _ANSI_ARGS_((Tcl_Obj * objPtr,
int length));
/* 59 */
EXTERN void Tcl_SetByteArrayObj _ANSI_ARGS_((Tcl_Obj * objPtr,
- unsigned char * bytes, int length));
+ CONST unsigned char * bytes, int length));
/* 60 */
EXTERN void Tcl_SetDoubleObj _ANSI_ARGS_((Tcl_Obj * objPtr,
double doubleValue));
@@ -220,8 +224,8 @@ EXTERN void Tcl_SetLongObj _ANSI_ARGS_((Tcl_Obj * objPtr,
EXTERN void Tcl_SetObjLength _ANSI_ARGS_((Tcl_Obj * objPtr,
int length));
/* 65 */
-EXTERN void Tcl_SetStringObj _ANSI_ARGS_((Tcl_Obj * objPtr,
- char * bytes, int length));
+EXTERN void Tcl_SetStringObj _ANSI_ARGS_((Tcl_Obj* objPtr,
+ CONST char* bytes, int length));
/* 66 */
EXTERN void Tcl_AddErrorInfo _ANSI_ARGS_((Tcl_Interp * interp,
CONST char * message));
@@ -254,8 +258,8 @@ EXTERN char Tcl_Backslash _ANSI_ARGS_((CONST char * src,
int * readPtr));
/* 78 */
EXTERN int Tcl_BadChannelOption _ANSI_ARGS_((
- Tcl_Interp * interp, char * optionName,
- char * optionList));
+ Tcl_Interp * interp, CONST char * optionName,
+ CONST char * optionList));
/* 79 */
EXTERN void Tcl_CallWhenDeleted _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_InterpDeleteProc * proc,
@@ -268,9 +272,10 @@ EXTERN void Tcl_CancelIdleCall _ANSI_ARGS_((
EXTERN int Tcl_Close _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Channel chan));
/* 82 */
-EXTERN int Tcl_CommandComplete _ANSI_ARGS_((char * cmd));
+EXTERN int Tcl_CommandComplete _ANSI_ARGS_((CONST char * cmd));
/* 83 */
-EXTERN char * Tcl_Concat _ANSI_ARGS_((int argc, char ** argv));
+EXTERN char * Tcl_Concat _ANSI_ARGS_((int argc,
+ CONST84 char * CONST * argv));
/* 84 */
EXTERN int Tcl_ConvertElement _ANSI_ARGS_((CONST char * src,
char * dst, int flags));
@@ -280,16 +285,18 @@ EXTERN int Tcl_ConvertCountedElement _ANSI_ARGS_((
int flags));
/* 86 */
EXTERN int Tcl_CreateAlias _ANSI_ARGS_((Tcl_Interp * slave,
- char * slaveCmd, Tcl_Interp * target,
- char * targetCmd, int argc, char ** argv));
+ CONST char * slaveCmd, Tcl_Interp * target,
+ CONST char * targetCmd, int argc,
+ CONST84 char * CONST * argv));
/* 87 */
EXTERN int Tcl_CreateAliasObj _ANSI_ARGS_((Tcl_Interp * slave,
- char * slaveCmd, Tcl_Interp * target,
- char * targetCmd, int objc,
+ CONST char * slaveCmd, Tcl_Interp * target,
+ CONST char * targetCmd, int objc,
Tcl_Obj *CONST objv[]));
/* 88 */
EXTERN Tcl_Channel Tcl_CreateChannel _ANSI_ARGS_((
- Tcl_ChannelType * typePtr, char * chanName,
+ Tcl_ChannelType * typePtr,
+ CONST char * chanName,
ClientData instanceData, int mask));
/* 89 */
EXTERN void Tcl_CreateChannelHandler _ANSI_ARGS_((
@@ -301,7 +308,7 @@ EXTERN void Tcl_CreateCloseHandler _ANSI_ARGS_((Tcl_Channel chan,
Tcl_CloseProc * proc, ClientData clientData));
/* 91 */
EXTERN Tcl_Command Tcl_CreateCommand _ANSI_ARGS_((Tcl_Interp * interp,
- char * cmdName, Tcl_CmdProc * proc,
+ CONST char * cmdName, Tcl_CmdProc * proc,
ClientData clientData,
Tcl_CmdDeleteProc * deleteProc));
/* 92 */
@@ -316,17 +323,17 @@ EXTERN void Tcl_CreateExitHandler _ANSI_ARGS_((
EXTERN Tcl_Interp * Tcl_CreateInterp _ANSI_ARGS_((void));
/* 95 */
EXTERN void Tcl_CreateMathFunc _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, int numArgs,
+ CONST char * name, int numArgs,
Tcl_ValueType * argTypes,
Tcl_MathProc * proc, ClientData clientData));
/* 96 */
EXTERN Tcl_Command Tcl_CreateObjCommand _ANSI_ARGS_((
- Tcl_Interp * interp, char * cmdName,
+ Tcl_Interp * interp, CONST char * cmdName,
Tcl_ObjCmdProc * proc, ClientData clientData,
Tcl_CmdDeleteProc * deleteProc));
/* 97 */
EXTERN Tcl_Interp * Tcl_CreateSlave _ANSI_ARGS_((Tcl_Interp * interp,
- char * slaveName, int isSafe));
+ CONST char * slaveName, int isSafe));
/* 98 */
EXTERN Tcl_TimerToken Tcl_CreateTimerHandler _ANSI_ARGS_((int milliseconds,
Tcl_TimerProc * proc, ClientData clientData));
@@ -336,7 +343,7 @@ EXTERN Tcl_Trace Tcl_CreateTrace _ANSI_ARGS_((Tcl_Interp * interp,
ClientData clientData));
/* 100 */
EXTERN void Tcl_DeleteAssocData _ANSI_ARGS_((Tcl_Interp * interp,
- char * name));
+ CONST char * name));
/* 101 */
EXTERN void Tcl_DeleteChannelHandler _ANSI_ARGS_((
Tcl_Channel chan, Tcl_ChannelProc * proc,
@@ -346,7 +353,7 @@ EXTERN void Tcl_DeleteCloseHandler _ANSI_ARGS_((Tcl_Channel chan,
Tcl_CloseProc * proc, ClientData clientData));
/* 103 */
EXTERN int Tcl_DeleteCommand _ANSI_ARGS_((Tcl_Interp * interp,
- char * cmdName));
+ CONST char * cmdName));
/* 104 */
EXTERN int Tcl_DeleteCommandFromToken _ANSI_ARGS_((
Tcl_Interp * interp, Tcl_Command command));
@@ -424,15 +431,15 @@ EXTERN void Tcl_DStringStartSublist _ANSI_ARGS_((
/* 126 */
EXTERN int Tcl_Eof _ANSI_ARGS_((Tcl_Channel chan));
/* 127 */
-EXTERN char * Tcl_ErrnoId _ANSI_ARGS_((void));
+EXTERN CONST84_RETURN char * Tcl_ErrnoId _ANSI_ARGS_((void));
/* 128 */
-EXTERN char * Tcl_ErrnoMsg _ANSI_ARGS_((int err));
+EXTERN CONST84_RETURN char * Tcl_ErrnoMsg _ANSI_ARGS_((int err));
/* 129 */
EXTERN int Tcl_Eval _ANSI_ARGS_((Tcl_Interp * interp,
- char * string));
+ CONST char * string));
/* 130 */
EXTERN int Tcl_EvalFile _ANSI_ARGS_((Tcl_Interp * interp,
- char * fileName));
+ CONST char * fileName));
/* 131 */
EXTERN int Tcl_EvalObj _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * objPtr));
@@ -444,22 +451,23 @@ EXTERN void Tcl_EventuallyFree _ANSI_ARGS_((
EXTERN void Tcl_Exit _ANSI_ARGS_((int status));
/* 134 */
EXTERN int Tcl_ExposeCommand _ANSI_ARGS_((Tcl_Interp * interp,
- char * hiddenCmdToken, char * cmdName));
+ CONST char * hiddenCmdToken,
+ CONST char * cmdName));
/* 135 */
EXTERN int Tcl_ExprBoolean _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, int * ptr));
+ CONST char * str, int * ptr));
/* 136 */
EXTERN int Tcl_ExprBooleanObj _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * objPtr, int * ptr));
/* 137 */
EXTERN int Tcl_ExprDouble _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, double * ptr));
+ CONST char * str, double * ptr));
/* 138 */
EXTERN int Tcl_ExprDoubleObj _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * objPtr, double * ptr));
/* 139 */
EXTERN int Tcl_ExprLong _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, long * ptr));
+ CONST char * str, long * ptr));
/* 140 */
EXTERN int Tcl_ExprLongObj _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * objPtr, long * ptr));
@@ -468,7 +476,7 @@ EXTERN int Tcl_ExprObj _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * objPtr, Tcl_Obj ** resultPtrPtr));
/* 142 */
EXTERN int Tcl_ExprString _ANSI_ARGS_((Tcl_Interp * interp,
- char * string));
+ CONST char * string));
/* 143 */
EXTERN void Tcl_Finalize _ANSI_ARGS_((void));
/* 144 */
@@ -483,22 +491,23 @@ EXTERN int Tcl_Flush _ANSI_ARGS_((Tcl_Channel chan));
EXTERN void Tcl_FreeResult _ANSI_ARGS_((Tcl_Interp * interp));
/* 148 */
EXTERN int Tcl_GetAlias _ANSI_ARGS_((Tcl_Interp * interp,
- char * slaveCmd,
+ CONST char * slaveCmd,
Tcl_Interp ** targetInterpPtr,
- char ** targetCmdPtr, int * argcPtr,
- char *** argvPtr));
+ CONST84 char ** targetCmdPtr, int * argcPtr,
+ CONST84 char *** argvPtr));
/* 149 */
EXTERN int Tcl_GetAliasObj _ANSI_ARGS_((Tcl_Interp * interp,
- char * slaveCmd,
+ CONST char * slaveCmd,
Tcl_Interp ** targetInterpPtr,
- char ** targetCmdPtr, int * objcPtr,
+ CONST84 char ** targetCmdPtr, int * objcPtr,
Tcl_Obj *** objv));
/* 150 */
EXTERN ClientData Tcl_GetAssocData _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, Tcl_InterpDeleteProc ** procPtr));
+ CONST char * name,
+ Tcl_InterpDeleteProc ** procPtr));
/* 151 */
EXTERN Tcl_Channel Tcl_GetChannel _ANSI_ARGS_((Tcl_Interp * interp,
- char * chanName, int * modePtr));
+ CONST char * chanName, int * modePtr));
/* 152 */
EXTERN int Tcl_GetChannelBufferSize _ANSI_ARGS_((
Tcl_Channel chan));
@@ -511,23 +520,24 @@ EXTERN ClientData Tcl_GetChannelInstanceData _ANSI_ARGS_((
/* 155 */
EXTERN int Tcl_GetChannelMode _ANSI_ARGS_((Tcl_Channel chan));
/* 156 */
-EXTERN char * Tcl_GetChannelName _ANSI_ARGS_((Tcl_Channel chan));
+EXTERN CONST84_RETURN char * Tcl_GetChannelName _ANSI_ARGS_((
+ Tcl_Channel chan));
/* 157 */
EXTERN int Tcl_GetChannelOption _ANSI_ARGS_((
Tcl_Interp * interp, Tcl_Channel chan,
- char * optionName, Tcl_DString * dsPtr));
+ CONST char * optionName, Tcl_DString * dsPtr));
/* 158 */
EXTERN Tcl_ChannelType * Tcl_GetChannelType _ANSI_ARGS_((Tcl_Channel chan));
/* 159 */
EXTERN int Tcl_GetCommandInfo _ANSI_ARGS_((Tcl_Interp * interp,
- char * cmdName, Tcl_CmdInfo * infoPtr));
+ CONST char * cmdName, Tcl_CmdInfo * infoPtr));
/* 160 */
-EXTERN char * Tcl_GetCommandName _ANSI_ARGS_((Tcl_Interp * interp,
- Tcl_Command command));
+EXTERN CONST84_RETURN char * Tcl_GetCommandName _ANSI_ARGS_((
+ Tcl_Interp * interp, Tcl_Command command));
/* 161 */
EXTERN int Tcl_GetErrno _ANSI_ARGS_((void));
/* 162 */
-EXTERN char * Tcl_GetHostName _ANSI_ARGS_((void));
+EXTERN CONST84_RETURN char * Tcl_GetHostName _ANSI_ARGS_((void));
/* 163 */
EXTERN int Tcl_GetInterpPath _ANSI_ARGS_((
Tcl_Interp * askInterp,
@@ -541,11 +551,11 @@ EXTERN Tcl_Obj * Tcl_GetObjResult _ANSI_ARGS_((Tcl_Interp * interp));
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
/* 167 */
EXTERN int Tcl_GetOpenFile _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, int forWriting, int checkUsage,
- ClientData * filePtr));
+ CONST char * str, int forWriting,
+ int checkUsage, ClientData * filePtr));
#endif /* UNIX */
/* 168 */
-EXTERN Tcl_PathType Tcl_GetPathType _ANSI_ARGS_((char * path));
+EXTERN Tcl_PathType Tcl_GetPathType _ANSI_ARGS_((CONST char * path));
/* 169 */
EXTERN int Tcl_Gets _ANSI_ARGS_((Tcl_Channel chan,
Tcl_DString * dsPtr));
@@ -556,26 +566,29 @@ EXTERN int Tcl_GetsObj _ANSI_ARGS_((Tcl_Channel chan,
EXTERN int Tcl_GetServiceMode _ANSI_ARGS_((void));
/* 172 */
EXTERN Tcl_Interp * Tcl_GetSlave _ANSI_ARGS_((Tcl_Interp * interp,
- char * slaveName));
+ CONST char * slaveName));
/* 173 */
EXTERN Tcl_Channel Tcl_GetStdChannel _ANSI_ARGS_((int type));
/* 174 */
-EXTERN char * Tcl_GetStringResult _ANSI_ARGS_((Tcl_Interp * interp));
+EXTERN CONST84_RETURN char * Tcl_GetStringResult _ANSI_ARGS_((
+ Tcl_Interp * interp));
/* 175 */
-EXTERN char * Tcl_GetVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * varName, int flags));
+EXTERN CONST84_RETURN char * Tcl_GetVar _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * varName, int flags));
/* 176 */
-EXTERN char * Tcl_GetVar2 _ANSI_ARGS_((Tcl_Interp * interp,
- char * part1, char * part2, int flags));
+EXTERN CONST84_RETURN char * Tcl_GetVar2 _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * part1, CONST char * part2,
+ int flags));
/* 177 */
EXTERN int Tcl_GlobalEval _ANSI_ARGS_((Tcl_Interp * interp,
- char * command));
+ CONST char * command));
/* 178 */
EXTERN int Tcl_GlobalEvalObj _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * objPtr));
/* 179 */
EXTERN int Tcl_HideCommand _ANSI_ARGS_((Tcl_Interp * interp,
- char * cmdName, char * hiddenCmdToken));
+ CONST char * cmdName,
+ CONST char * hiddenCmdToken));
/* 180 */
EXTERN int Tcl_Init _ANSI_ARGS_((Tcl_Interp * interp));
/* 181 */
@@ -590,11 +603,12 @@ EXTERN int Tcl_InterpDeleted _ANSI_ARGS_((Tcl_Interp * interp));
/* 185 */
EXTERN int Tcl_IsSafe _ANSI_ARGS_((Tcl_Interp * interp));
/* 186 */
-EXTERN char * Tcl_JoinPath _ANSI_ARGS_((int argc, char ** argv,
+EXTERN char * Tcl_JoinPath _ANSI_ARGS_((int argc,
+ CONST84 char * CONST * argv,
Tcl_DString * resultPtr));
/* 187 */
EXTERN int Tcl_LinkVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * varName, char * addr, int type));
+ CONST char * varName, char * addr, int type));
/* Slot 188 is reserved */
/* 189 */
EXTERN Tcl_Channel Tcl_MakeFileChannel _ANSI_ARGS_((ClientData handle,
@@ -605,7 +619,8 @@ EXTERN int Tcl_MakeSafe _ANSI_ARGS_((Tcl_Interp * interp));
EXTERN Tcl_Channel Tcl_MakeTcpClientChannel _ANSI_ARGS_((
ClientData tcpSocket));
/* 192 */
-EXTERN char * Tcl_Merge _ANSI_ARGS_((int argc, char ** argv));
+EXTERN char * Tcl_Merge _ANSI_ARGS_((int argc,
+ CONST84 char * CONST * argv));
/* 193 */
EXTERN Tcl_HashEntry * Tcl_NextHashEntry _ANSI_ARGS_((
Tcl_HashSearch * searchPtr));
@@ -623,26 +638,26 @@ EXTERN Tcl_Obj * Tcl_ObjSetVar2 _ANSI_ARGS_((Tcl_Interp * interp,
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
/* 197 */
EXTERN Tcl_Channel Tcl_OpenCommandChannel _ANSI_ARGS_((
- Tcl_Interp * interp, int argc, char ** argv,
- int flags));
+ Tcl_Interp * interp, int argc,
+ CONST84 char ** argv, int flags));
#endif /* UNIX */
#ifdef __WIN32__
/* 197 */
EXTERN Tcl_Channel Tcl_OpenCommandChannel _ANSI_ARGS_((
- Tcl_Interp * interp, int argc, char ** argv,
- int flags));
+ Tcl_Interp * interp, int argc,
+ CONST84 char ** argv, int flags));
#endif /* __WIN32__ */
/* 198 */
EXTERN Tcl_Channel Tcl_OpenFileChannel _ANSI_ARGS_((Tcl_Interp * interp,
- char * fileName, char * modeString,
- int permissions));
+ CONST char * fileName,
+ CONST char * modeString, int permissions));
/* 199 */
EXTERN Tcl_Channel Tcl_OpenTcpClient _ANSI_ARGS_((Tcl_Interp * interp,
- int port, char * address, char * myaddr,
- int myport, int async));
+ int port, CONST char * address,
+ CONST char * myaddr, int myport, int async));
/* 200 */
EXTERN Tcl_Channel Tcl_OpenTcpServer _ANSI_ARGS_((Tcl_Interp * interp,
- int port, char * host,
+ int port, CONST char * host,
Tcl_TcpAcceptProc * acceptProc,
ClientData callbackData));
/* 201 */
@@ -653,7 +668,7 @@ EXTERN void Tcl_PrintDouble _ANSI_ARGS_((Tcl_Interp * interp,
/* 203 */
EXTERN int Tcl_PutEnv _ANSI_ARGS_((CONST char * string));
/* 204 */
-EXTERN char * Tcl_PosixError _ANSI_ARGS_((Tcl_Interp * interp));
+EXTERN CONST84_RETURN char * Tcl_PosixError _ANSI_ARGS_((Tcl_Interp * interp));
/* 205 */
EXTERN void Tcl_QueueEvent _ANSI_ARGS_((Tcl_Event * evPtr,
Tcl_QueuePosition position));
@@ -670,7 +685,7 @@ EXTERN void Tcl_ReapDetachedProcs _ANSI_ARGS_((void));
#endif /* __WIN32__ */
/* 208 */
EXTERN int Tcl_RecordAndEval _ANSI_ARGS_((Tcl_Interp * interp,
- char * cmd, int flags));
+ CONST char * cmd, int flags));
/* 209 */
EXTERN int Tcl_RecordAndEvalObj _ANSI_ARGS_((
Tcl_Interp * interp, Tcl_Obj * cmdPtr,
@@ -683,17 +698,18 @@ EXTERN void Tcl_RegisterObjType _ANSI_ARGS_((
Tcl_ObjType * typePtr));
/* 212 */
EXTERN Tcl_RegExp Tcl_RegExpCompile _ANSI_ARGS_((Tcl_Interp * interp,
- char * string));
+ CONST char * string));
/* 213 */
EXTERN int Tcl_RegExpExec _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_RegExp regexp, CONST char * str,
CONST char * start));
/* 214 */
EXTERN int Tcl_RegExpMatch _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, char * pattern));
+ CONST char * str, CONST char * pattern));
/* 215 */
EXTERN void Tcl_RegExpRange _ANSI_ARGS_((Tcl_RegExp regexp,
- int index, char ** startPtr, char ** endPtr));
+ int index, CONST84 char ** startPtr,
+ CONST84 char ** endPtr));
/* 216 */
EXTERN void Tcl_Release _ANSI_ARGS_((ClientData clientData));
/* 217 */
@@ -705,15 +721,16 @@ EXTERN int Tcl_ScanElement _ANSI_ARGS_((CONST char * str,
EXTERN int Tcl_ScanCountedElement _ANSI_ARGS_((CONST char * str,
int length, int * flagPtr));
/* 220 */
-EXTERN int Tcl_Seek _ANSI_ARGS_((Tcl_Channel chan, int offset,
- int mode));
+EXTERN int Tcl_SeekOld _ANSI_ARGS_((Tcl_Channel chan,
+ int offset, int mode));
/* 221 */
EXTERN int Tcl_ServiceAll _ANSI_ARGS_((void));
/* 222 */
EXTERN int Tcl_ServiceEvent _ANSI_ARGS_((int flags));
/* 223 */
EXTERN void Tcl_SetAssocData _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, Tcl_InterpDeleteProc * proc,
+ CONST char * name,
+ Tcl_InterpDeleteProc * proc,
ClientData clientData));
/* 224 */
EXTERN void Tcl_SetChannelBufferSize _ANSI_ARGS_((
@@ -721,10 +738,12 @@ EXTERN void Tcl_SetChannelBufferSize _ANSI_ARGS_((
/* 225 */
EXTERN int Tcl_SetChannelOption _ANSI_ARGS_((
Tcl_Interp * interp, Tcl_Channel chan,
- char * optionName, char * newValue));
+ CONST char * optionName,
+ CONST char * newValue));
/* 226 */
EXTERN int Tcl_SetCommandInfo _ANSI_ARGS_((Tcl_Interp * interp,
- char * cmdName, Tcl_CmdInfo * infoPtr));
+ CONST char * cmdName,
+ CONST Tcl_CmdInfo * infoPtr));
/* 227 */
EXTERN void Tcl_SetErrno _ANSI_ARGS_((int err));
/* 228 */
@@ -752,108 +771,112 @@ EXTERN void Tcl_SetObjResult _ANSI_ARGS_((Tcl_Interp * interp,
EXTERN void Tcl_SetStdChannel _ANSI_ARGS_((Tcl_Channel channel,
int type));
/* 237 */
-EXTERN char * Tcl_SetVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * varName, char * newValue, int flags));
-/* 238 */
-EXTERN char * Tcl_SetVar2 _ANSI_ARGS_((Tcl_Interp * interp,
- char * part1, char * part2, char * newValue,
+EXTERN CONST84_RETURN char * Tcl_SetVar _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * varName, CONST char * newValue,
int flags));
+/* 238 */
+EXTERN CONST84_RETURN char * Tcl_SetVar2 _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * part1, CONST char * part2,
+ CONST char * newValue, int flags));
/* 239 */
-EXTERN char * Tcl_SignalId _ANSI_ARGS_((int sig));
+EXTERN CONST84_RETURN char * Tcl_SignalId _ANSI_ARGS_((int sig));
/* 240 */
-EXTERN char * Tcl_SignalMsg _ANSI_ARGS_((int sig));
+EXTERN CONST84_RETURN char * Tcl_SignalMsg _ANSI_ARGS_((int sig));
/* 241 */
EXTERN void Tcl_SourceRCFile _ANSI_ARGS_((Tcl_Interp * interp));
/* 242 */
EXTERN int Tcl_SplitList _ANSI_ARGS_((Tcl_Interp * interp,
CONST char * listStr, int * argcPtr,
- char *** argvPtr));
+ CONST84 char *** argvPtr));
/* 243 */
EXTERN void Tcl_SplitPath _ANSI_ARGS_((CONST char * path,
- int * argcPtr, char *** argvPtr));
+ int * argcPtr, CONST84 char *** argvPtr));
/* 244 */
EXTERN void Tcl_StaticPackage _ANSI_ARGS_((Tcl_Interp * interp,
- char * pkgName,
+ CONST char * pkgName,
Tcl_PackageInitProc * initProc,
Tcl_PackageInitProc * safeInitProc));
/* 245 */
EXTERN int Tcl_StringMatch _ANSI_ARGS_((CONST char * str,
CONST char * pattern));
/* 246 */
-EXTERN int Tcl_Tell _ANSI_ARGS_((Tcl_Channel chan));
+EXTERN int Tcl_TellOld _ANSI_ARGS_((Tcl_Channel chan));
/* 247 */
EXTERN int Tcl_TraceVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * varName, int flags,
+ CONST char * varName, int flags,
Tcl_VarTraceProc * proc,
ClientData clientData));
/* 248 */
EXTERN int Tcl_TraceVar2 _ANSI_ARGS_((Tcl_Interp * interp,
- char * part1, char * part2, int flags,
- Tcl_VarTraceProc * proc,
+ CONST char * part1, CONST char * part2,
+ int flags, Tcl_VarTraceProc * proc,
ClientData clientData));
/* 249 */
EXTERN char * Tcl_TranslateFileName _ANSI_ARGS_((
- Tcl_Interp * interp, char * name,
+ Tcl_Interp * interp, CONST char * name,
Tcl_DString * bufferPtr));
/* 250 */
-EXTERN int Tcl_Ungets _ANSI_ARGS_((Tcl_Channel chan, char * str,
- int len, int atHead));
+EXTERN int Tcl_Ungets _ANSI_ARGS_((Tcl_Channel chan,
+ CONST char * str, int len, int atHead));
/* 251 */
EXTERN void Tcl_UnlinkVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * varName));
+ CONST char * varName));
/* 252 */
EXTERN int Tcl_UnregisterChannel _ANSI_ARGS_((
Tcl_Interp * interp, Tcl_Channel chan));
/* 253 */
EXTERN int Tcl_UnsetVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * varName, int flags));
+ CONST char * varName, int flags));
/* 254 */
EXTERN int Tcl_UnsetVar2 _ANSI_ARGS_((Tcl_Interp * interp,
- char * part1, char * part2, int flags));
+ CONST char * part1, CONST char * part2,
+ int flags));
/* 255 */
EXTERN void Tcl_UntraceVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * varName, int flags,
+ CONST char * varName, int flags,
Tcl_VarTraceProc * proc,
ClientData clientData));
/* 256 */
EXTERN void Tcl_UntraceVar2 _ANSI_ARGS_((Tcl_Interp * interp,
- char * part1, char * part2, int flags,
- Tcl_VarTraceProc * proc,
+ CONST char * part1, CONST char * part2,
+ int flags, Tcl_VarTraceProc * proc,
ClientData clientData));
/* 257 */
EXTERN void Tcl_UpdateLinkedVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * varName));
+ CONST char * varName));
/* 258 */
EXTERN int Tcl_UpVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * frameName, char * varName,
- char * localName, int flags));
+ CONST char * frameName, CONST char * varName,
+ CONST char * localName, int flags));
/* 259 */
EXTERN int Tcl_UpVar2 _ANSI_ARGS_((Tcl_Interp * interp,
- char * frameName, char * part1, char * part2,
- char * localName, int flags));
+ CONST char * frameName, CONST char * part1,
+ CONST char * part2, CONST char * localName,
+ int flags));
/* 260 */
EXTERN int Tcl_VarEval _ANSI_ARGS_(TCL_VARARGS(Tcl_Interp *,interp));
/* 261 */
EXTERN ClientData Tcl_VarTraceInfo _ANSI_ARGS_((Tcl_Interp * interp,
- char * varName, int flags,
+ CONST char * varName, int flags,
Tcl_VarTraceProc * procPtr,
ClientData prevClientData));
/* 262 */
EXTERN ClientData Tcl_VarTraceInfo2 _ANSI_ARGS_((Tcl_Interp * interp,
- char * part1, char * part2, int flags,
- Tcl_VarTraceProc * procPtr,
+ CONST char * part1, CONST char * part2,
+ int flags, Tcl_VarTraceProc * procPtr,
ClientData prevClientData));
/* 263 */
-EXTERN int Tcl_Write _ANSI_ARGS_((Tcl_Channel chan, char * s,
- int slen));
+EXTERN int Tcl_Write _ANSI_ARGS_((Tcl_Channel chan,
+ CONST char * s, int slen));
/* 264 */
EXTERN void Tcl_WrongNumArgs _ANSI_ARGS_((Tcl_Interp * interp,
int objc, Tcl_Obj *CONST objv[],
- char * message));
+ CONST char * message));
/* 265 */
-EXTERN int Tcl_DumpActiveMemory _ANSI_ARGS_((char * fileName));
+EXTERN int Tcl_DumpActiveMemory _ANSI_ARGS_((
+ CONST char * fileName));
/* 266 */
-EXTERN void Tcl_ValidateAllMemory _ANSI_ARGS_((char * file,
+EXTERN void Tcl_ValidateAllMemory _ANSI_ARGS_((CONST char * file,
int line));
/* 267 */
EXTERN void Tcl_AppendResultVA _ANSI_ARGS_((Tcl_Interp * interp,
@@ -862,23 +885,27 @@ EXTERN void Tcl_AppendResultVA _ANSI_ARGS_((Tcl_Interp * interp,
EXTERN void Tcl_AppendStringsToObjVA _ANSI_ARGS_((
Tcl_Obj * objPtr, va_list argList));
/* 269 */
-EXTERN char * Tcl_HashStats _ANSI_ARGS_((Tcl_HashTable * tablePtr));
+EXTERN CONST84_RETURN char * Tcl_HashStats _ANSI_ARGS_((
+ Tcl_HashTable * tablePtr));
/* 270 */
-EXTERN char * Tcl_ParseVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, char ** termPtr));
+EXTERN CONST84_RETURN char * Tcl_ParseVar _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * str, CONST84 char ** termPtr));
/* 271 */
-EXTERN char * Tcl_PkgPresent _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, char * version, int exact));
+EXTERN CONST84_RETURN char * Tcl_PkgPresent _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * name, CONST char * version,
+ int exact));
/* 272 */
-EXTERN char * Tcl_PkgPresentEx _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, char * version, int exact,
+EXTERN CONST84_RETURN char * Tcl_PkgPresentEx _ANSI_ARGS_((
+ Tcl_Interp * interp, CONST char * name,
+ CONST char * version, int exact,
ClientData * clientDataPtr));
/* 273 */
EXTERN int Tcl_PkgProvide _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, char * version));
+ CONST char * name, CONST char * version));
/* 274 */
-EXTERN char * Tcl_PkgRequire _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, char * version, int exact));
+EXTERN CONST84_RETURN char * Tcl_PkgRequire _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * name, CONST char * version,
+ int exact));
/* 275 */
EXTERN void Tcl_SetErrorCodeVA _ANSI_ARGS_((Tcl_Interp * interp,
va_list argList));
@@ -888,16 +915,9 @@ EXTERN int Tcl_VarEvalVA _ANSI_ARGS_((Tcl_Interp * interp,
/* 277 */
EXTERN Tcl_Pid Tcl_WaitPid _ANSI_ARGS_((Tcl_Pid pid, int * statPtr,
int options));
-#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
-/* 278 */
-EXTERN void Tcl_PanicVA _ANSI_ARGS_((char * format,
- va_list argList));
-#endif /* UNIX */
-#ifdef __WIN32__
/* 278 */
-EXTERN void Tcl_PanicVA _ANSI_ARGS_((char * format,
+EXTERN void Tcl_PanicVA _ANSI_ARGS_((CONST char * format,
va_list argList));
-#endif /* __WIN32__ */
/* 279 */
EXTERN void Tcl_GetVersion _ANSI_ARGS_((int * major, int * minor,
int * patchLevel, int * type));
@@ -913,7 +933,8 @@ EXTERN int Tcl_UnstackChannel _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Channel chan));
/* 283 */
EXTERN Tcl_Channel Tcl_GetStackedChannel _ANSI_ARGS_((Tcl_Channel chan));
-/* Slot 284 is reserved */
+/* 284 */
+EXTERN void Tcl_SetMainLoop _ANSI_ARGS_((Tcl_MainLoopProc * proc));
/* Slot 285 is reserved */
/* 286 */
EXTERN void Tcl_AppendObjToObj _ANSI_ARGS_((Tcl_Obj * objPtr,
@@ -932,7 +953,7 @@ EXTERN void Tcl_DiscardResult _ANSI_ARGS_((
Tcl_SavedResult * statePtr));
/* 291 */
EXTERN int Tcl_EvalEx _ANSI_ARGS_((Tcl_Interp * interp,
- char * script, int numBytes, int flags));
+ CONST char * script, int numBytes, int flags));
/* 292 */
EXTERN int Tcl_EvalObjv _ANSI_ARGS_((Tcl_Interp * interp,
int objc, Tcl_Obj *CONST objv[], int flags));
@@ -965,7 +986,7 @@ EXTERN Tcl_ThreadId Tcl_GetCurrentThread _ANSI_ARGS_((void));
EXTERN Tcl_Encoding Tcl_GetEncoding _ANSI_ARGS_((Tcl_Interp * interp,
CONST char * name));
/* 302 */
-EXTERN char * Tcl_GetEncodingName _ANSI_ARGS_((
+EXTERN CONST84_RETURN char * Tcl_GetEncodingName _ANSI_ARGS_((
Tcl_Encoding encoding));
/* 303 */
EXTERN void Tcl_GetEncodingNames _ANSI_ARGS_((
@@ -973,14 +994,15 @@ EXTERN void Tcl_GetEncodingNames _ANSI_ARGS_((
/* 304 */
EXTERN int Tcl_GetIndexFromObjStruct _ANSI_ARGS_((
Tcl_Interp * interp, Tcl_Obj * objPtr,
- char ** tablePtr, int offset, char * msg,
- int flags, int * indexPtr));
+ CONST VOID * tablePtr, int offset,
+ CONST char * msg, int flags, int * indexPtr));
/* 305 */
EXTERN VOID * Tcl_GetThreadData _ANSI_ARGS_((
Tcl_ThreadDataKey * keyPtr, int size));
/* 306 */
EXTERN Tcl_Obj * Tcl_GetVar2Ex _ANSI_ARGS_((Tcl_Interp * interp,
- char * part1, char * part2, int flags));
+ CONST char * part1, CONST char * part2,
+ int flags));
/* 307 */
EXTERN ClientData Tcl_InitNotifier _ANSI_ARGS_((void));
/* 308 */
@@ -1012,7 +1034,7 @@ EXTERN int Tcl_SetSystemEncoding _ANSI_ARGS_((
Tcl_Interp * interp, CONST char * name));
/* 317 */
EXTERN Tcl_Obj * Tcl_SetVar2Ex _ANSI_ARGS_((Tcl_Interp * interp,
- char * part1, char * part2,
+ CONST char * part1, CONST char * part2,
Tcl_Obj * newValuePtr, int flags));
/* 318 */
EXTERN void Tcl_ThreadAlert _ANSI_ARGS_((Tcl_ThreadId threadId));
@@ -1032,7 +1054,7 @@ EXTERN Tcl_UniChar Tcl_UniCharToUpper _ANSI_ARGS_((int ch));
/* 324 */
EXTERN int Tcl_UniCharToUtf _ANSI_ARGS_((int ch, char * buf));
/* 325 */
-EXTERN char * Tcl_UtfAtIndex _ANSI_ARGS_((CONST char * src,
+EXTERN CONST84_RETURN char * Tcl_UtfAtIndex _ANSI_ARGS_((CONST char * src,
int index));
/* 326 */
EXTERN int Tcl_UtfCharComplete _ANSI_ARGS_((CONST char * src,
@@ -1041,15 +1063,15 @@ EXTERN int Tcl_UtfCharComplete _ANSI_ARGS_((CONST char * src,
EXTERN int Tcl_UtfBackslash _ANSI_ARGS_((CONST char * src,
int * readPtr, char * dst));
/* 328 */
-EXTERN char * Tcl_UtfFindFirst _ANSI_ARGS_((CONST char * src,
+EXTERN CONST84_RETURN char * Tcl_UtfFindFirst _ANSI_ARGS_((CONST char * src,
int ch));
/* 329 */
-EXTERN char * Tcl_UtfFindLast _ANSI_ARGS_((CONST char * src,
+EXTERN CONST84_RETURN char * Tcl_UtfFindLast _ANSI_ARGS_((CONST char * src,
int ch));
/* 330 */
-EXTERN char * Tcl_UtfNext _ANSI_ARGS_((CONST char * src));
+EXTERN CONST84_RETURN char * Tcl_UtfNext _ANSI_ARGS_((CONST char * src));
/* 331 */
-EXTERN char * Tcl_UtfPrev _ANSI_ARGS_((CONST char * src,
+EXTERN CONST84_RETURN char * Tcl_UtfPrev _ANSI_ARGS_((CONST char * src,
CONST char * start));
/* 332 */
EXTERN int Tcl_UtfToExternal _ANSI_ARGS_((Tcl_Interp * interp,
@@ -1080,9 +1102,10 @@ EXTERN int Tcl_WriteObj _ANSI_ARGS_((Tcl_Channel chan,
/* 340 */
EXTERN char * Tcl_GetString _ANSI_ARGS_((Tcl_Obj * objPtr));
/* 341 */
-EXTERN char * Tcl_GetDefaultEncodingDir _ANSI_ARGS_((void));
+EXTERN CONST84_RETURN char * Tcl_GetDefaultEncodingDir _ANSI_ARGS_((void));
/* 342 */
-EXTERN void Tcl_SetDefaultEncodingDir _ANSI_ARGS_((char * path));
+EXTERN void Tcl_SetDefaultEncodingDir _ANSI_ARGS_((
+ CONST char * path));
/* 343 */
EXTERN void Tcl_AlertNotifier _ANSI_ARGS_((ClientData clientData));
/* 344 */
@@ -1102,7 +1125,7 @@ EXTERN int Tcl_UniCharIsUpper _ANSI_ARGS_((int ch));
/* 351 */
EXTERN int Tcl_UniCharIsWordChar _ANSI_ARGS_((int ch));
/* 352 */
-EXTERN int Tcl_UniCharLen _ANSI_ARGS_((Tcl_UniChar * str));
+EXTERN int Tcl_UniCharLen _ANSI_ARGS_((CONST Tcl_UniChar * str));
/* 353 */
EXTERN int Tcl_UniCharNcmp _ANSI_ARGS_((CONST Tcl_UniChar * cs,
CONST Tcl_UniChar * ct, unsigned long n));
@@ -1125,28 +1148,29 @@ EXTERN Tcl_Obj * Tcl_EvalTokens _ANSI_ARGS_((Tcl_Interp * interp,
EXTERN void Tcl_FreeParse _ANSI_ARGS_((Tcl_Parse * parsePtr));
/* 359 */
EXTERN void Tcl_LogCommandInfo _ANSI_ARGS_((Tcl_Interp * interp,
- char * script, char * command, int length));
+ CONST char * script, CONST char * command,
+ int length));
/* 360 */
EXTERN int Tcl_ParseBraces _ANSI_ARGS_((Tcl_Interp * interp,
- char * string, int numBytes,
+ CONST char * string, int numBytes,
Tcl_Parse * parsePtr, int append,
- char ** termPtr));
+ CONST84 char ** termPtr));
/* 361 */
EXTERN int Tcl_ParseCommand _ANSI_ARGS_((Tcl_Interp * interp,
- char * string, int numBytes, int nested,
- Tcl_Parse * parsePtr));
+ CONST char * string, int numBytes,
+ int nested, Tcl_Parse * parsePtr));
/* 362 */
EXTERN int Tcl_ParseExpr _ANSI_ARGS_((Tcl_Interp * interp,
- char * string, int numBytes,
+ CONST char * string, int numBytes,
Tcl_Parse * parsePtr));
/* 363 */
EXTERN int Tcl_ParseQuotedString _ANSI_ARGS_((
- Tcl_Interp * interp, char * string,
+ Tcl_Interp * interp, CONST char * string,
int numBytes, Tcl_Parse * parsePtr,
- int append, char ** termPtr));
+ int append, CONST84 char ** termPtr));
/* 364 */
EXTERN int Tcl_ParseVarName _ANSI_ARGS_((Tcl_Interp * interp,
- char * string, int numBytes,
+ CONST char * string, int numBytes,
Tcl_Parse * parsePtr, int append));
/* 365 */
EXTERN char * Tcl_GetCwd _ANSI_ARGS_((Tcl_Interp * interp,
@@ -1183,11 +1207,11 @@ EXTERN int Tcl_RegExpExecObj _ANSI_ARGS_((Tcl_Interp * interp,
EXTERN void Tcl_RegExpGetInfo _ANSI_ARGS_((Tcl_RegExp regexp,
Tcl_RegExpInfo * infoPtr));
/* 378 */
-EXTERN Tcl_Obj * Tcl_NewUnicodeObj _ANSI_ARGS_((Tcl_UniChar * unicode,
- int numChars));
+EXTERN Tcl_Obj * Tcl_NewUnicodeObj _ANSI_ARGS_((
+ CONST Tcl_UniChar * unicode, int numChars));
/* 379 */
EXTERN void Tcl_SetUnicodeObj _ANSI_ARGS_((Tcl_Obj * objPtr,
- Tcl_UniChar * unicode, int numChars));
+ CONST Tcl_UniChar * unicode, int numChars));
/* 380 */
EXTERN int Tcl_GetCharLength _ANSI_ARGS_((Tcl_Obj * objPtr));
/* 381 */
@@ -1200,7 +1224,7 @@ EXTERN Tcl_Obj * Tcl_GetRange _ANSI_ARGS_((Tcl_Obj * objPtr,
int first, int last));
/* 384 */
EXTERN void Tcl_AppendUnicodeToObj _ANSI_ARGS_((Tcl_Obj * objPtr,
- Tcl_UniChar * unicode, int length));
+ CONST Tcl_UniChar * unicode, int length));
/* 385 */
EXTERN int Tcl_RegExpMatchObj _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * stringObj, Tcl_Obj * patternObj));
@@ -1213,7 +1237,7 @@ EXTERN Tcl_Mutex * Tcl_GetAllocMutex _ANSI_ARGS_((void));
EXTERN int Tcl_GetChannelNames _ANSI_ARGS_((Tcl_Interp * interp));
/* 389 */
EXTERN int Tcl_GetChannelNamesEx _ANSI_ARGS_((
- Tcl_Interp * interp, char * pattern));
+ Tcl_Interp * interp, CONST char * pattern));
/* 390 */
EXTERN int Tcl_ProcObjCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp * interp, int objc,
@@ -1233,13 +1257,13 @@ EXTERN int Tcl_ReadRaw _ANSI_ARGS_((Tcl_Channel chan,
char * dst, int bytesToRead));
/* 395 */
EXTERN int Tcl_WriteRaw _ANSI_ARGS_((Tcl_Channel chan,
- char * src, int srcLen));
+ CONST char * src, int srcLen));
/* 396 */
EXTERN Tcl_Channel Tcl_GetTopChannel _ANSI_ARGS_((Tcl_Channel chan));
/* 397 */
EXTERN int Tcl_ChannelBuffered _ANSI_ARGS_((Tcl_Channel chan));
/* 398 */
-EXTERN char * Tcl_ChannelName _ANSI_ARGS_((
+EXTERN CONST84_RETURN char * Tcl_ChannelName _ANSI_ARGS_((
Tcl_ChannelType * chanTypePtr));
/* 399 */
EXTERN Tcl_ChannelTypeVersion Tcl_ChannelVersion _ANSI_ARGS_((
@@ -1280,6 +1304,266 @@ EXTERN Tcl_DriverFlushProc * Tcl_ChannelFlushProc _ANSI_ARGS_((
/* 411 */
EXTERN Tcl_DriverHandlerProc * Tcl_ChannelHandlerProc _ANSI_ARGS_((
Tcl_ChannelType * chanTypePtr));
+/* 412 */
+EXTERN int Tcl_JoinThread _ANSI_ARGS_((Tcl_ThreadId id,
+ int* result));
+/* 413 */
+EXTERN int Tcl_IsChannelShared _ANSI_ARGS_((Tcl_Channel channel));
+/* 414 */
+EXTERN int Tcl_IsChannelRegistered _ANSI_ARGS_((
+ Tcl_Interp* interp, Tcl_Channel channel));
+/* 415 */
+EXTERN void Tcl_CutChannel _ANSI_ARGS_((Tcl_Channel channel));
+/* 416 */
+EXTERN void Tcl_SpliceChannel _ANSI_ARGS_((Tcl_Channel channel));
+/* 417 */
+EXTERN void Tcl_ClearChannelHandlers _ANSI_ARGS_((
+ Tcl_Channel channel));
+/* 418 */
+EXTERN int Tcl_IsChannelExisting _ANSI_ARGS_((
+ CONST char* channelName));
+/* 419 */
+EXTERN int Tcl_UniCharNcasecmp _ANSI_ARGS_((
+ CONST Tcl_UniChar * cs,
+ CONST Tcl_UniChar * ct, unsigned long n));
+/* 420 */
+EXTERN int Tcl_UniCharCaseMatch _ANSI_ARGS_((
+ CONST Tcl_UniChar * ustr,
+ CONST Tcl_UniChar * pattern, int nocase));
+/* 421 */
+EXTERN Tcl_HashEntry * Tcl_FindHashEntry _ANSI_ARGS_((
+ Tcl_HashTable * tablePtr, CONST char * key));
+/* 422 */
+EXTERN Tcl_HashEntry * Tcl_CreateHashEntry _ANSI_ARGS_((
+ Tcl_HashTable * tablePtr, CONST char * key,
+ int * newPtr));
+/* 423 */
+EXTERN void Tcl_InitCustomHashTable _ANSI_ARGS_((
+ Tcl_HashTable * tablePtr, int keyType,
+ Tcl_HashKeyType * typePtr));
+/* 424 */
+EXTERN void Tcl_InitObjHashTable _ANSI_ARGS_((
+ Tcl_HashTable * tablePtr));
+/* 425 */
+EXTERN ClientData Tcl_CommandTraceInfo _ANSI_ARGS_((
+ Tcl_Interp * interp, CONST char * varName,
+ int flags, Tcl_CommandTraceProc * procPtr,
+ ClientData prevClientData));
+/* 426 */
+EXTERN int Tcl_TraceCommand _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * varName, int flags,
+ Tcl_CommandTraceProc * proc,
+ ClientData clientData));
+/* 427 */
+EXTERN void Tcl_UntraceCommand _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * varName, int flags,
+ Tcl_CommandTraceProc * proc,
+ ClientData clientData));
+/* 428 */
+EXTERN char * Tcl_AttemptAlloc _ANSI_ARGS_((unsigned int size));
+/* 429 */
+EXTERN char * Tcl_AttemptDbCkalloc _ANSI_ARGS_((unsigned int size,
+ CONST char * file, int line));
+/* 430 */
+EXTERN char * Tcl_AttemptRealloc _ANSI_ARGS_((char * ptr,
+ unsigned int size));
+/* 431 */
+EXTERN char * Tcl_AttemptDbCkrealloc _ANSI_ARGS_((char * ptr,
+ unsigned int size, CONST char * file,
+ int line));
+/* 432 */
+EXTERN int Tcl_AttemptSetObjLength _ANSI_ARGS_((
+ Tcl_Obj * objPtr, int length));
+/* 433 */
+EXTERN Tcl_ThreadId Tcl_GetChannelThread _ANSI_ARGS_((
+ Tcl_Channel channel));
+/* 434 */
+EXTERN Tcl_UniChar * Tcl_GetUnicodeFromObj _ANSI_ARGS_((Tcl_Obj * objPtr,
+ int * lengthPtr));
+/* 435 */
+EXTERN int Tcl_GetMathFuncInfo _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * name, int * numArgsPtr,
+ Tcl_ValueType ** argTypesPtr,
+ Tcl_MathProc ** procPtr,
+ ClientData * clientDataPtr));
+/* 436 */
+EXTERN Tcl_Obj * Tcl_ListMathFuncs _ANSI_ARGS_((Tcl_Interp * interp,
+ CONST char * pattern));
+/* 437 */
+EXTERN Tcl_Obj * Tcl_SubstObj _ANSI_ARGS_((Tcl_Interp * interp,
+ Tcl_Obj * objPtr, int flags));
+/* 438 */
+EXTERN int Tcl_DetachChannel _ANSI_ARGS_((Tcl_Interp* interp,
+ Tcl_Channel channel));
+/* 439 */
+EXTERN int Tcl_IsStandardChannel _ANSI_ARGS_((
+ Tcl_Channel channel));
+/* 440 */
+EXTERN int Tcl_FSCopyFile _ANSI_ARGS_((Tcl_Obj * srcPathPtr,
+ Tcl_Obj * destPathPtr));
+/* 441 */
+EXTERN int Tcl_FSCopyDirectory _ANSI_ARGS_((
+ Tcl_Obj * srcPathPtr, Tcl_Obj * destPathPtr,
+ Tcl_Obj ** errorPtr));
+/* 442 */
+EXTERN int Tcl_FSCreateDirectory _ANSI_ARGS_((Tcl_Obj * pathPtr));
+/* 443 */
+EXTERN int Tcl_FSDeleteFile _ANSI_ARGS_((Tcl_Obj * pathPtr));
+/* 444 */
+EXTERN int Tcl_FSLoadFile _ANSI_ARGS_((Tcl_Interp * interp,
+ Tcl_Obj * pathPtr, CONST char * sym1,
+ CONST char * sym2,
+ Tcl_PackageInitProc ** proc1Ptr,
+ Tcl_PackageInitProc ** proc2Ptr,
+ Tcl_LoadHandle * handlePtr,
+ Tcl_FSUnloadFileProc ** unloadProcPtr));
+/* 445 */
+EXTERN int Tcl_FSMatchInDirectory _ANSI_ARGS_((
+ Tcl_Interp * interp, Tcl_Obj * result,
+ Tcl_Obj * pathPtr, CONST char * pattern,
+ Tcl_GlobTypeData * types));
+/* 446 */
+EXTERN Tcl_Obj * Tcl_FSLink _ANSI_ARGS_((Tcl_Obj * pathPtr,
+ Tcl_Obj * toPtr, int linkAction));
+/* 447 */
+EXTERN int Tcl_FSRemoveDirectory _ANSI_ARGS_((Tcl_Obj * pathPtr,
+ int recursive, Tcl_Obj ** errorPtr));
+/* 448 */
+EXTERN int Tcl_FSRenameFile _ANSI_ARGS_((Tcl_Obj * srcPathPtr,
+ Tcl_Obj * destPathPtr));
+/* 449 */
+EXTERN int Tcl_FSLstat _ANSI_ARGS_((Tcl_Obj * pathPtr,
+ Tcl_StatBuf * buf));
+/* 450 */
+EXTERN int Tcl_FSUtime _ANSI_ARGS_((Tcl_Obj * pathPtr,
+ struct utimbuf * tval));
+/* 451 */
+EXTERN int Tcl_FSFileAttrsGet _ANSI_ARGS_((Tcl_Interp * interp,
+ int index, Tcl_Obj * pathPtr,
+ Tcl_Obj ** objPtrRef));
+/* 452 */
+EXTERN int Tcl_FSFileAttrsSet _ANSI_ARGS_((Tcl_Interp * interp,
+ int index, Tcl_Obj * pathPtr,
+ Tcl_Obj * objPtr));
+/* 453 */
+EXTERN CONST char ** Tcl_FSFileAttrStrings _ANSI_ARGS_((Tcl_Obj * pathPtr,
+ Tcl_Obj ** objPtrRef));
+/* 454 */
+EXTERN int Tcl_FSStat _ANSI_ARGS_((Tcl_Obj * pathPtr,
+ Tcl_StatBuf * buf));
+/* 455 */
+EXTERN int Tcl_FSAccess _ANSI_ARGS_((Tcl_Obj * pathPtr,
+ int mode));
+/* 456 */
+EXTERN Tcl_Channel Tcl_FSOpenFileChannel _ANSI_ARGS_((
+ Tcl_Interp * interp, Tcl_Obj * pathPtr,
+ CONST char * modeString, int permissions));
+/* 457 */
+EXTERN Tcl_Obj* Tcl_FSGetCwd _ANSI_ARGS_((Tcl_Interp * interp));
+/* 458 */
+EXTERN int Tcl_FSChdir _ANSI_ARGS_((Tcl_Obj * pathPtr));
+/* 459 */
+EXTERN int Tcl_FSConvertToPathType _ANSI_ARGS_((
+ Tcl_Interp * interp, Tcl_Obj * pathPtr));
+/* 460 */
+EXTERN Tcl_Obj* Tcl_FSJoinPath _ANSI_ARGS_((Tcl_Obj * listObj,
+ int elements));
+/* 461 */
+EXTERN Tcl_Obj* Tcl_FSSplitPath _ANSI_ARGS_((Tcl_Obj* pathPtr,
+ int * lenPtr));
+/* 462 */
+EXTERN int Tcl_FSEqualPaths _ANSI_ARGS_((Tcl_Obj* firstPtr,
+ Tcl_Obj* secondPtr));
+/* 463 */
+EXTERN Tcl_Obj* Tcl_FSGetNormalizedPath _ANSI_ARGS_((
+ Tcl_Interp * interp, Tcl_Obj* pathObjPtr));
+/* 464 */
+EXTERN Tcl_Obj* Tcl_FSJoinToPath _ANSI_ARGS_((Tcl_Obj * basePtr,
+ int objc, Tcl_Obj *CONST objv[]));
+/* 465 */
+EXTERN ClientData Tcl_FSGetInternalRep _ANSI_ARGS_((
+ Tcl_Obj* pathObjPtr, Tcl_Filesystem * fsPtr));
+/* 466 */
+EXTERN Tcl_Obj* Tcl_FSGetTranslatedPath _ANSI_ARGS_((
+ Tcl_Interp * interp, Tcl_Obj* pathPtr));
+/* 467 */
+EXTERN int Tcl_FSEvalFile _ANSI_ARGS_((Tcl_Interp * interp,
+ Tcl_Obj * fileName));
+/* 468 */
+EXTERN Tcl_Obj* Tcl_FSNewNativePath _ANSI_ARGS_((
+ Tcl_Filesystem* fromFilesystem,
+ ClientData clientData));
+/* 469 */
+EXTERN CONST char* Tcl_FSGetNativePath _ANSI_ARGS_((Tcl_Obj* pathObjPtr));
+/* 470 */
+EXTERN Tcl_Obj* Tcl_FSFileSystemInfo _ANSI_ARGS_((
+ Tcl_Obj* pathObjPtr));
+/* 471 */
+EXTERN Tcl_Obj* Tcl_FSPathSeparator _ANSI_ARGS_((Tcl_Obj* pathObjPtr));
+/* 472 */
+EXTERN Tcl_Obj* Tcl_FSListVolumes _ANSI_ARGS_((void));
+/* 473 */
+EXTERN int Tcl_FSRegister _ANSI_ARGS_((ClientData clientData,
+ Tcl_Filesystem * fsPtr));
+/* 474 */
+EXTERN int Tcl_FSUnregister _ANSI_ARGS_((Tcl_Filesystem * fsPtr));
+/* 475 */
+EXTERN ClientData Tcl_FSData _ANSI_ARGS_((Tcl_Filesystem * fsPtr));
+/* 476 */
+EXTERN CONST char* Tcl_FSGetTranslatedStringPath _ANSI_ARGS_((
+ Tcl_Interp * interp, Tcl_Obj* pathPtr));
+/* 477 */
+EXTERN Tcl_Filesystem* Tcl_FSGetFileSystemForPath _ANSI_ARGS_((
+ Tcl_Obj* pathObjPtr));
+/* 478 */
+EXTERN Tcl_PathType Tcl_FSGetPathType _ANSI_ARGS_((Tcl_Obj * pathObjPtr));
+/* 479 */
+EXTERN int Tcl_OutputBuffered _ANSI_ARGS_((Tcl_Channel chan));
+/* 480 */
+EXTERN void Tcl_FSMountsChanged _ANSI_ARGS_((
+ Tcl_Filesystem * fsPtr));
+/* 481 */
+EXTERN int Tcl_EvalTokensStandard _ANSI_ARGS_((
+ Tcl_Interp * interp, Tcl_Token * tokenPtr,
+ int count));
+/* 482 */
+EXTERN void Tcl_GetTime _ANSI_ARGS_((Tcl_Time* timeBuf));
+/* 483 */
+EXTERN Tcl_Trace Tcl_CreateObjTrace _ANSI_ARGS_((Tcl_Interp* interp,
+ int level, int flags,
+ Tcl_CmdObjTraceProc* objProc,
+ ClientData clientData,
+ Tcl_CmdObjTraceDeleteProc* delProc));
+/* 484 */
+EXTERN int Tcl_GetCommandInfoFromToken _ANSI_ARGS_((
+ Tcl_Command token, Tcl_CmdInfo* infoPtr));
+/* 485 */
+EXTERN int Tcl_SetCommandInfoFromToken _ANSI_ARGS_((
+ Tcl_Command token,
+ CONST Tcl_CmdInfo* infoPtr));
+/* 486 */
+EXTERN Tcl_Obj * Tcl_DbNewWideIntObj _ANSI_ARGS_((
+ Tcl_WideInt wideValue, CONST char * file,
+ int line));
+/* 487 */
+EXTERN int Tcl_GetWideIntFromObj _ANSI_ARGS_((
+ Tcl_Interp * interp, Tcl_Obj * objPtr,
+ Tcl_WideInt * widePtr));
+/* 488 */
+EXTERN Tcl_Obj * Tcl_NewWideIntObj _ANSI_ARGS_((Tcl_WideInt wideValue));
+/* 489 */
+EXTERN void Tcl_SetWideIntObj _ANSI_ARGS_((Tcl_Obj * objPtr,
+ Tcl_WideInt wideValue));
+/* 490 */
+EXTERN Tcl_StatBuf * Tcl_AllocStatBuf _ANSI_ARGS_((void));
+/* 491 */
+EXTERN Tcl_WideInt Tcl_Seek _ANSI_ARGS_((Tcl_Channel chan,
+ Tcl_WideInt offset, int mode));
+/* 492 */
+EXTERN Tcl_WideInt Tcl_Tell _ANSI_ARGS_((Tcl_Channel chan));
+/* 493 */
+EXTERN Tcl_DriverWideSeekProc * Tcl_ChannelWideSeekProc _ANSI_ARGS_((
+ Tcl_ChannelType * chanTypePtr));
typedef struct TclStubHooks {
struct TclPlatStubs *tclPlatStubs;
@@ -1291,15 +1575,15 @@ typedef struct TclStubs {
int magic;
struct TclStubHooks *hooks;
- int (*tcl_PkgProvideEx) _ANSI_ARGS_((Tcl_Interp * interp, char * name, char * version, ClientData clientData)); /* 0 */
- char * (*tcl_PkgRequireEx) _ANSI_ARGS_((Tcl_Interp * interp, char * name, char * version, int exact, ClientData * clientDataPtr)); /* 1 */
- void (*tcl_Panic) _ANSI_ARGS_(TCL_VARARGS(char *,format)); /* 2 */
+ int (*tcl_PkgProvideEx) _ANSI_ARGS_((Tcl_Interp* interp, CONST char* name, CONST char* version, ClientData clientData)); /* 0 */
+ CONST84_RETURN char * (*tcl_PkgRequireEx) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, CONST char * version, int exact, ClientData * clientDataPtr)); /* 1 */
+ void (*tcl_Panic) _ANSI_ARGS_(TCL_VARARGS(CONST char *,format)); /* 2 */
char * (*tcl_Alloc) _ANSI_ARGS_((unsigned int size)); /* 3 */
void (*tcl_Free) _ANSI_ARGS_((char * ptr)); /* 4 */
char * (*tcl_Realloc) _ANSI_ARGS_((char * ptr, unsigned int size)); /* 5 */
- char * (*tcl_DbCkalloc) _ANSI_ARGS_((unsigned int size, char * file, int line)); /* 6 */
- int (*tcl_DbCkfree) _ANSI_ARGS_((char * ptr, char * file, int line)); /* 7 */
- char * (*tcl_DbCkrealloc) _ANSI_ARGS_((char * ptr, unsigned int size, char * file, int line)); /* 8 */
+ char * (*tcl_DbCkalloc) _ANSI_ARGS_((unsigned int size, CONST char * file, int line)); /* 6 */
+ int (*tcl_DbCkfree) _ANSI_ARGS_((char * ptr, CONST char * file, int line)); /* 7 */
+ char * (*tcl_DbCkrealloc) _ANSI_ARGS_((char * ptr, unsigned int size, CONST char * file, int line)); /* 8 */
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
void (*tcl_CreateFileHandler) _ANSI_ARGS_((int fd, int mask, Tcl_FileProc * proc, ClientData clientData)); /* 9 */
#endif /* UNIX */
@@ -1323,41 +1607,41 @@ typedef struct TclStubs {
int (*tcl_WaitForEvent) _ANSI_ARGS_((Tcl_Time * timePtr)); /* 13 */
int (*tcl_AppendAllObjTypes) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr)); /* 14 */
void (*tcl_AppendStringsToObj) _ANSI_ARGS_(TCL_VARARGS(Tcl_Obj *,objPtr)); /* 15 */
- void (*tcl_AppendToObj) _ANSI_ARGS_((Tcl_Obj * objPtr, char * bytes, int length)); /* 16 */
+ void (*tcl_AppendToObj) _ANSI_ARGS_((Tcl_Obj* objPtr, CONST char* bytes, int length)); /* 16 */
Tcl_Obj * (*tcl_ConcatObj) _ANSI_ARGS_((int objc, Tcl_Obj *CONST objv[])); /* 17 */
int (*tcl_ConvertToType) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, Tcl_ObjType * typePtr)); /* 18 */
- void (*tcl_DbDecrRefCount) _ANSI_ARGS_((Tcl_Obj * objPtr, char * file, int line)); /* 19 */
- void (*tcl_DbIncrRefCount) _ANSI_ARGS_((Tcl_Obj * objPtr, char * file, int line)); /* 20 */
- int (*tcl_DbIsShared) _ANSI_ARGS_((Tcl_Obj * objPtr, char * file, int line)); /* 21 */
- Tcl_Obj * (*tcl_DbNewBooleanObj) _ANSI_ARGS_((int boolValue, char * file, int line)); /* 22 */
- Tcl_Obj * (*tcl_DbNewByteArrayObj) _ANSI_ARGS_((unsigned char * bytes, int length, char * file, int line)); /* 23 */
- Tcl_Obj * (*tcl_DbNewDoubleObj) _ANSI_ARGS_((double doubleValue, char * file, int line)); /* 24 */
- Tcl_Obj * (*tcl_DbNewListObj) _ANSI_ARGS_((int objc, Tcl_Obj *CONST objv[], char * file, int line)); /* 25 */
- Tcl_Obj * (*tcl_DbNewLongObj) _ANSI_ARGS_((long longValue, char * file, int line)); /* 26 */
- Tcl_Obj * (*tcl_DbNewObj) _ANSI_ARGS_((char * file, int line)); /* 27 */
- Tcl_Obj * (*tcl_DbNewStringObj) _ANSI_ARGS_((CONST char * bytes, int length, char * file, int line)); /* 28 */
+ void (*tcl_DbDecrRefCount) _ANSI_ARGS_((Tcl_Obj * objPtr, CONST char * file, int line)); /* 19 */
+ void (*tcl_DbIncrRefCount) _ANSI_ARGS_((Tcl_Obj * objPtr, CONST char * file, int line)); /* 20 */
+ int (*tcl_DbIsShared) _ANSI_ARGS_((Tcl_Obj * objPtr, CONST char * file, int line)); /* 21 */
+ Tcl_Obj * (*tcl_DbNewBooleanObj) _ANSI_ARGS_((int boolValue, CONST char * file, int line)); /* 22 */
+ Tcl_Obj * (*tcl_DbNewByteArrayObj) _ANSI_ARGS_((CONST unsigned char * bytes, int length, CONST char * file, int line)); /* 23 */
+ Tcl_Obj * (*tcl_DbNewDoubleObj) _ANSI_ARGS_((double doubleValue, CONST char * file, int line)); /* 24 */
+ Tcl_Obj * (*tcl_DbNewListObj) _ANSI_ARGS_((int objc, Tcl_Obj *CONST * objv, CONST char * file, int line)); /* 25 */
+ Tcl_Obj * (*tcl_DbNewLongObj) _ANSI_ARGS_((long longValue, CONST char * file, int line)); /* 26 */
+ Tcl_Obj * (*tcl_DbNewObj) _ANSI_ARGS_((CONST char * file, int line)); /* 27 */
+ Tcl_Obj * (*tcl_DbNewStringObj) _ANSI_ARGS_((CONST char * bytes, int length, CONST char * file, int line)); /* 28 */
Tcl_Obj * (*tcl_DuplicateObj) _ANSI_ARGS_((Tcl_Obj * objPtr)); /* 29 */
void (*tclFreeObj) _ANSI_ARGS_((Tcl_Obj * objPtr)); /* 30 */
- int (*tcl_GetBoolean) _ANSI_ARGS_((Tcl_Interp * interp, char * str, int * boolPtr)); /* 31 */
+ int (*tcl_GetBoolean) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, int * boolPtr)); /* 31 */
int (*tcl_GetBooleanFromObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, int * boolPtr)); /* 32 */
unsigned char * (*tcl_GetByteArrayFromObj) _ANSI_ARGS_((Tcl_Obj * objPtr, int * lengthPtr)); /* 33 */
- int (*tcl_GetDouble) _ANSI_ARGS_((Tcl_Interp * interp, char * str, double * doublePtr)); /* 34 */
+ int (*tcl_GetDouble) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, double * doublePtr)); /* 34 */
int (*tcl_GetDoubleFromObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, double * doublePtr)); /* 35 */
- int (*tcl_GetIndexFromObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, char ** tablePtr, char * msg, int flags, int * indexPtr)); /* 36 */
- int (*tcl_GetInt) _ANSI_ARGS_((Tcl_Interp * interp, char * str, int * intPtr)); /* 37 */
+ int (*tcl_GetIndexFromObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, CONST84 char ** tablePtr, CONST char * msg, int flags, int * indexPtr)); /* 36 */
+ int (*tcl_GetInt) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, int * intPtr)); /* 37 */
int (*tcl_GetIntFromObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, int * intPtr)); /* 38 */
int (*tcl_GetLongFromObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, long * longPtr)); /* 39 */
- Tcl_ObjType * (*tcl_GetObjType) _ANSI_ARGS_((char * typeName)); /* 40 */
+ Tcl_ObjType * (*tcl_GetObjType) _ANSI_ARGS_((CONST char * typeName)); /* 40 */
char * (*tcl_GetStringFromObj) _ANSI_ARGS_((Tcl_Obj * objPtr, int * lengthPtr)); /* 41 */
void (*tcl_InvalidateStringRep) _ANSI_ARGS_((Tcl_Obj * objPtr)); /* 42 */
int (*tcl_ListObjAppendList) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * listPtr, Tcl_Obj * elemListPtr)); /* 43 */
int (*tcl_ListObjAppendElement) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * listPtr, Tcl_Obj * objPtr)); /* 44 */
int (*tcl_ListObjGetElements) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * listPtr, int * objcPtr, Tcl_Obj *** objvPtr)); /* 45 */
int (*tcl_ListObjIndex) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * listPtr, int index, Tcl_Obj ** objPtrPtr)); /* 46 */
- int (*tcl_ListObjLength) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * listPtr, int * intPtr)); /* 47 */
+ int (*tcl_ListObjLength) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * listPtr, int * lengthPtr)); /* 47 */
int (*tcl_ListObjReplace) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * listPtr, int first, int count, int objc, Tcl_Obj *CONST objv[])); /* 48 */
Tcl_Obj * (*tcl_NewBooleanObj) _ANSI_ARGS_((int boolValue)); /* 49 */
- Tcl_Obj * (*tcl_NewByteArrayObj) _ANSI_ARGS_((unsigned char * bytes, int length)); /* 50 */
+ Tcl_Obj * (*tcl_NewByteArrayObj) _ANSI_ARGS_((CONST unsigned char* bytes, int length)); /* 50 */
Tcl_Obj * (*tcl_NewDoubleObj) _ANSI_ARGS_((double doubleValue)); /* 51 */
Tcl_Obj * (*tcl_NewIntObj) _ANSI_ARGS_((int intValue)); /* 52 */
Tcl_Obj * (*tcl_NewListObj) _ANSI_ARGS_((int objc, Tcl_Obj *CONST objv[])); /* 53 */
@@ -1366,13 +1650,13 @@ typedef struct TclStubs {
Tcl_Obj * (*tcl_NewStringObj) _ANSI_ARGS_((CONST char * bytes, int length)); /* 56 */
void (*tcl_SetBooleanObj) _ANSI_ARGS_((Tcl_Obj * objPtr, int boolValue)); /* 57 */
unsigned char * (*tcl_SetByteArrayLength) _ANSI_ARGS_((Tcl_Obj * objPtr, int length)); /* 58 */
- void (*tcl_SetByteArrayObj) _ANSI_ARGS_((Tcl_Obj * objPtr, unsigned char * bytes, int length)); /* 59 */
+ void (*tcl_SetByteArrayObj) _ANSI_ARGS_((Tcl_Obj * objPtr, CONST unsigned char * bytes, int length)); /* 59 */
void (*tcl_SetDoubleObj) _ANSI_ARGS_((Tcl_Obj * objPtr, double doubleValue)); /* 60 */
void (*tcl_SetIntObj) _ANSI_ARGS_((Tcl_Obj * objPtr, int intValue)); /* 61 */
void (*tcl_SetListObj) _ANSI_ARGS_((Tcl_Obj * objPtr, int objc, Tcl_Obj *CONST objv[])); /* 62 */
void (*tcl_SetLongObj) _ANSI_ARGS_((Tcl_Obj * objPtr, long longValue)); /* 63 */
void (*tcl_SetObjLength) _ANSI_ARGS_((Tcl_Obj * objPtr, int length)); /* 64 */
- void (*tcl_SetStringObj) _ANSI_ARGS_((Tcl_Obj * objPtr, char * bytes, int length)); /* 65 */
+ void (*tcl_SetStringObj) _ANSI_ARGS_((Tcl_Obj* objPtr, CONST char* bytes, int length)); /* 65 */
void (*tcl_AddErrorInfo) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * message)); /* 66 */
void (*tcl_AddObjErrorInfo) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * message, int length)); /* 67 */
void (*tcl_AllowExceptions) _ANSI_ARGS_((Tcl_Interp * interp)); /* 68 */
@@ -1385,32 +1669,32 @@ typedef struct TclStubs {
int (*tcl_AsyncReady) _ANSI_ARGS_((void)); /* 75 */
void (*tcl_BackgroundError) _ANSI_ARGS_((Tcl_Interp * interp)); /* 76 */
char (*tcl_Backslash) _ANSI_ARGS_((CONST char * src, int * readPtr)); /* 77 */
- int (*tcl_BadChannelOption) _ANSI_ARGS_((Tcl_Interp * interp, char * optionName, char * optionList)); /* 78 */
+ int (*tcl_BadChannelOption) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * optionName, CONST char * optionList)); /* 78 */
void (*tcl_CallWhenDeleted) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_InterpDeleteProc * proc, ClientData clientData)); /* 79 */
void (*tcl_CancelIdleCall) _ANSI_ARGS_((Tcl_IdleProc * idleProc, ClientData clientData)); /* 80 */
int (*tcl_Close) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel chan)); /* 81 */
- int (*tcl_CommandComplete) _ANSI_ARGS_((char * cmd)); /* 82 */
- char * (*tcl_Concat) _ANSI_ARGS_((int argc, char ** argv)); /* 83 */
+ int (*tcl_CommandComplete) _ANSI_ARGS_((CONST char * cmd)); /* 82 */
+ char * (*tcl_Concat) _ANSI_ARGS_((int argc, CONST84 char * CONST * argv)); /* 83 */
int (*tcl_ConvertElement) _ANSI_ARGS_((CONST char * src, char * dst, int flags)); /* 84 */
int (*tcl_ConvertCountedElement) _ANSI_ARGS_((CONST char * src, int length, char * dst, int flags)); /* 85 */
- int (*tcl_CreateAlias) _ANSI_ARGS_((Tcl_Interp * slave, char * slaveCmd, Tcl_Interp * target, char * targetCmd, int argc, char ** argv)); /* 86 */
- int (*tcl_CreateAliasObj) _ANSI_ARGS_((Tcl_Interp * slave, char * slaveCmd, Tcl_Interp * target, char * targetCmd, int objc, Tcl_Obj *CONST objv[])); /* 87 */
- Tcl_Channel (*tcl_CreateChannel) _ANSI_ARGS_((Tcl_ChannelType * typePtr, char * chanName, ClientData instanceData, int mask)); /* 88 */
+ int (*tcl_CreateAlias) _ANSI_ARGS_((Tcl_Interp * slave, CONST char * slaveCmd, Tcl_Interp * target, CONST char * targetCmd, int argc, CONST84 char * CONST * argv)); /* 86 */
+ int (*tcl_CreateAliasObj) _ANSI_ARGS_((Tcl_Interp * slave, CONST char * slaveCmd, Tcl_Interp * target, CONST char * targetCmd, int objc, Tcl_Obj *CONST objv[])); /* 87 */
+ Tcl_Channel (*tcl_CreateChannel) _ANSI_ARGS_((Tcl_ChannelType * typePtr, CONST char * chanName, ClientData instanceData, int mask)); /* 88 */
void (*tcl_CreateChannelHandler) _ANSI_ARGS_((Tcl_Channel chan, int mask, Tcl_ChannelProc * proc, ClientData clientData)); /* 89 */
void (*tcl_CreateCloseHandler) _ANSI_ARGS_((Tcl_Channel chan, Tcl_CloseProc * proc, ClientData clientData)); /* 90 */
- Tcl_Command (*tcl_CreateCommand) _ANSI_ARGS_((Tcl_Interp * interp, char * cmdName, Tcl_CmdProc * proc, ClientData clientData, Tcl_CmdDeleteProc * deleteProc)); /* 91 */
+ Tcl_Command (*tcl_CreateCommand) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * cmdName, Tcl_CmdProc * proc, ClientData clientData, Tcl_CmdDeleteProc * deleteProc)); /* 91 */
void (*tcl_CreateEventSource) _ANSI_ARGS_((Tcl_EventSetupProc * setupProc, Tcl_EventCheckProc * checkProc, ClientData clientData)); /* 92 */
void (*tcl_CreateExitHandler) _ANSI_ARGS_((Tcl_ExitProc * proc, ClientData clientData)); /* 93 */
Tcl_Interp * (*tcl_CreateInterp) _ANSI_ARGS_((void)); /* 94 */
- void (*tcl_CreateMathFunc) _ANSI_ARGS_((Tcl_Interp * interp, char * name, int numArgs, Tcl_ValueType * argTypes, Tcl_MathProc * proc, ClientData clientData)); /* 95 */
- Tcl_Command (*tcl_CreateObjCommand) _ANSI_ARGS_((Tcl_Interp * interp, char * cmdName, Tcl_ObjCmdProc * proc, ClientData clientData, Tcl_CmdDeleteProc * deleteProc)); /* 96 */
- Tcl_Interp * (*tcl_CreateSlave) _ANSI_ARGS_((Tcl_Interp * interp, char * slaveName, int isSafe)); /* 97 */
+ void (*tcl_CreateMathFunc) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, int numArgs, Tcl_ValueType * argTypes, Tcl_MathProc * proc, ClientData clientData)); /* 95 */
+ Tcl_Command (*tcl_CreateObjCommand) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * cmdName, Tcl_ObjCmdProc * proc, ClientData clientData, Tcl_CmdDeleteProc * deleteProc)); /* 96 */
+ Tcl_Interp * (*tcl_CreateSlave) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * slaveName, int isSafe)); /* 97 */
Tcl_TimerToken (*tcl_CreateTimerHandler) _ANSI_ARGS_((int milliseconds, Tcl_TimerProc * proc, ClientData clientData)); /* 98 */
Tcl_Trace (*tcl_CreateTrace) _ANSI_ARGS_((Tcl_Interp * interp, int level, Tcl_CmdTraceProc * proc, ClientData clientData)); /* 99 */
- void (*tcl_DeleteAssocData) _ANSI_ARGS_((Tcl_Interp * interp, char * name)); /* 100 */
+ void (*tcl_DeleteAssocData) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name)); /* 100 */
void (*tcl_DeleteChannelHandler) _ANSI_ARGS_((Tcl_Channel chan, Tcl_ChannelProc * proc, ClientData clientData)); /* 101 */
void (*tcl_DeleteCloseHandler) _ANSI_ARGS_((Tcl_Channel chan, Tcl_CloseProc * proc, ClientData clientData)); /* 102 */
- int (*tcl_DeleteCommand) _ANSI_ARGS_((Tcl_Interp * interp, char * cmdName)); /* 103 */
+ int (*tcl_DeleteCommand) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * cmdName)); /* 103 */
int (*tcl_DeleteCommandFromToken) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Command command)); /* 104 */
void (*tcl_DeleteEvents) _ANSI_ARGS_((Tcl_EventDeleteProc * proc, ClientData clientData)); /* 105 */
void (*tcl_DeleteEventSource) _ANSI_ARGS_((Tcl_EventSetupProc * setupProc, Tcl_EventCheckProc * checkProc, ClientData clientData)); /* 106 */
@@ -1442,48 +1726,48 @@ typedef struct TclStubs {
void (*tcl_DStringSetLength) _ANSI_ARGS_((Tcl_DString * dsPtr, int length)); /* 124 */
void (*tcl_DStringStartSublist) _ANSI_ARGS_((Tcl_DString * dsPtr)); /* 125 */
int (*tcl_Eof) _ANSI_ARGS_((Tcl_Channel chan)); /* 126 */
- char * (*tcl_ErrnoId) _ANSI_ARGS_((void)); /* 127 */
- char * (*tcl_ErrnoMsg) _ANSI_ARGS_((int err)); /* 128 */
- int (*tcl_Eval) _ANSI_ARGS_((Tcl_Interp * interp, char * string)); /* 129 */
- int (*tcl_EvalFile) _ANSI_ARGS_((Tcl_Interp * interp, char * fileName)); /* 130 */
+ CONST84_RETURN char * (*tcl_ErrnoId) _ANSI_ARGS_((void)); /* 127 */
+ CONST84_RETURN char * (*tcl_ErrnoMsg) _ANSI_ARGS_((int err)); /* 128 */
+ int (*tcl_Eval) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * string)); /* 129 */
+ int (*tcl_EvalFile) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * fileName)); /* 130 */
int (*tcl_EvalObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr)); /* 131 */
void (*tcl_EventuallyFree) _ANSI_ARGS_((ClientData clientData, Tcl_FreeProc * freeProc)); /* 132 */
void (*tcl_Exit) _ANSI_ARGS_((int status)); /* 133 */
- int (*tcl_ExposeCommand) _ANSI_ARGS_((Tcl_Interp * interp, char * hiddenCmdToken, char * cmdName)); /* 134 */
- int (*tcl_ExprBoolean) _ANSI_ARGS_((Tcl_Interp * interp, char * str, int * ptr)); /* 135 */
+ int (*tcl_ExposeCommand) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * hiddenCmdToken, CONST char * cmdName)); /* 134 */
+ int (*tcl_ExprBoolean) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, int * ptr)); /* 135 */
int (*tcl_ExprBooleanObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, int * ptr)); /* 136 */
- int (*tcl_ExprDouble) _ANSI_ARGS_((Tcl_Interp * interp, char * str, double * ptr)); /* 137 */
+ int (*tcl_ExprDouble) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, double * ptr)); /* 137 */
int (*tcl_ExprDoubleObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, double * ptr)); /* 138 */
- int (*tcl_ExprLong) _ANSI_ARGS_((Tcl_Interp * interp, char * str, long * ptr)); /* 139 */
+ int (*tcl_ExprLong) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, long * ptr)); /* 139 */
int (*tcl_ExprLongObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, long * ptr)); /* 140 */
int (*tcl_ExprObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, Tcl_Obj ** resultPtrPtr)); /* 141 */
- int (*tcl_ExprString) _ANSI_ARGS_((Tcl_Interp * interp, char * string)); /* 142 */
+ int (*tcl_ExprString) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * string)); /* 142 */
void (*tcl_Finalize) _ANSI_ARGS_((void)); /* 143 */
void (*tcl_FindExecutable) _ANSI_ARGS_((CONST char * argv0)); /* 144 */
Tcl_HashEntry * (*tcl_FirstHashEntry) _ANSI_ARGS_((Tcl_HashTable * tablePtr, Tcl_HashSearch * searchPtr)); /* 145 */
int (*tcl_Flush) _ANSI_ARGS_((Tcl_Channel chan)); /* 146 */
void (*tcl_FreeResult) _ANSI_ARGS_((Tcl_Interp * interp)); /* 147 */
- int (*tcl_GetAlias) _ANSI_ARGS_((Tcl_Interp * interp, char * slaveCmd, Tcl_Interp ** targetInterpPtr, char ** targetCmdPtr, int * argcPtr, char *** argvPtr)); /* 148 */
- int (*tcl_GetAliasObj) _ANSI_ARGS_((Tcl_Interp * interp, char * slaveCmd, Tcl_Interp ** targetInterpPtr, char ** targetCmdPtr, int * objcPtr, Tcl_Obj *** objv)); /* 149 */
- ClientData (*tcl_GetAssocData) _ANSI_ARGS_((Tcl_Interp * interp, char * name, Tcl_InterpDeleteProc ** procPtr)); /* 150 */
- Tcl_Channel (*tcl_GetChannel) _ANSI_ARGS_((Tcl_Interp * interp, char * chanName, int * modePtr)); /* 151 */
+ int (*tcl_GetAlias) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * slaveCmd, Tcl_Interp ** targetInterpPtr, CONST84 char ** targetCmdPtr, int * argcPtr, CONST84 char *** argvPtr)); /* 148 */
+ int (*tcl_GetAliasObj) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * slaveCmd, Tcl_Interp ** targetInterpPtr, CONST84 char ** targetCmdPtr, int * objcPtr, Tcl_Obj *** objv)); /* 149 */
+ ClientData (*tcl_GetAssocData) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, Tcl_InterpDeleteProc ** procPtr)); /* 150 */
+ Tcl_Channel (*tcl_GetChannel) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * chanName, int * modePtr)); /* 151 */
int (*tcl_GetChannelBufferSize) _ANSI_ARGS_((Tcl_Channel chan)); /* 152 */
int (*tcl_GetChannelHandle) _ANSI_ARGS_((Tcl_Channel chan, int direction, ClientData * handlePtr)); /* 153 */
ClientData (*tcl_GetChannelInstanceData) _ANSI_ARGS_((Tcl_Channel chan)); /* 154 */
int (*tcl_GetChannelMode) _ANSI_ARGS_((Tcl_Channel chan)); /* 155 */
- char * (*tcl_GetChannelName) _ANSI_ARGS_((Tcl_Channel chan)); /* 156 */
- int (*tcl_GetChannelOption) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel chan, char * optionName, Tcl_DString * dsPtr)); /* 157 */
+ CONST84_RETURN char * (*tcl_GetChannelName) _ANSI_ARGS_((Tcl_Channel chan)); /* 156 */
+ int (*tcl_GetChannelOption) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel chan, CONST char * optionName, Tcl_DString * dsPtr)); /* 157 */
Tcl_ChannelType * (*tcl_GetChannelType) _ANSI_ARGS_((Tcl_Channel chan)); /* 158 */
- int (*tcl_GetCommandInfo) _ANSI_ARGS_((Tcl_Interp * interp, char * cmdName, Tcl_CmdInfo * infoPtr)); /* 159 */
- char * (*tcl_GetCommandName) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Command command)); /* 160 */
+ int (*tcl_GetCommandInfo) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * cmdName, Tcl_CmdInfo * infoPtr)); /* 159 */
+ CONST84_RETURN char * (*tcl_GetCommandName) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Command command)); /* 160 */
int (*tcl_GetErrno) _ANSI_ARGS_((void)); /* 161 */
- char * (*tcl_GetHostName) _ANSI_ARGS_((void)); /* 162 */
+ CONST84_RETURN char * (*tcl_GetHostName) _ANSI_ARGS_((void)); /* 162 */
int (*tcl_GetInterpPath) _ANSI_ARGS_((Tcl_Interp * askInterp, Tcl_Interp * slaveInterp)); /* 163 */
Tcl_Interp * (*tcl_GetMaster) _ANSI_ARGS_((Tcl_Interp * interp)); /* 164 */
CONST char * (*tcl_GetNameOfExecutable) _ANSI_ARGS_((void)); /* 165 */
Tcl_Obj * (*tcl_GetObjResult) _ANSI_ARGS_((Tcl_Interp * interp)); /* 166 */
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
- int (*tcl_GetOpenFile) _ANSI_ARGS_((Tcl_Interp * interp, char * str, int forWriting, int checkUsage, ClientData * filePtr)); /* 167 */
+ int (*tcl_GetOpenFile) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, int forWriting, int checkUsage, ClientData * filePtr)); /* 167 */
#endif /* UNIX */
#ifdef __WIN32__
void *reserved167;
@@ -1491,51 +1775,51 @@ typedef struct TclStubs {
#ifdef MAC_TCL
void *reserved167;
#endif /* MAC_TCL */
- Tcl_PathType (*tcl_GetPathType) _ANSI_ARGS_((char * path)); /* 168 */
+ Tcl_PathType (*tcl_GetPathType) _ANSI_ARGS_((CONST char * path)); /* 168 */
int (*tcl_Gets) _ANSI_ARGS_((Tcl_Channel chan, Tcl_DString * dsPtr)); /* 169 */
int (*tcl_GetsObj) _ANSI_ARGS_((Tcl_Channel chan, Tcl_Obj * objPtr)); /* 170 */
int (*tcl_GetServiceMode) _ANSI_ARGS_((void)); /* 171 */
- Tcl_Interp * (*tcl_GetSlave) _ANSI_ARGS_((Tcl_Interp * interp, char * slaveName)); /* 172 */
+ Tcl_Interp * (*tcl_GetSlave) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * slaveName)); /* 172 */
Tcl_Channel (*tcl_GetStdChannel) _ANSI_ARGS_((int type)); /* 173 */
- char * (*tcl_GetStringResult) _ANSI_ARGS_((Tcl_Interp * interp)); /* 174 */
- char * (*tcl_GetVar) _ANSI_ARGS_((Tcl_Interp * interp, char * varName, int flags)); /* 175 */
- char * (*tcl_GetVar2) _ANSI_ARGS_((Tcl_Interp * interp, char * part1, char * part2, int flags)); /* 176 */
- int (*tcl_GlobalEval) _ANSI_ARGS_((Tcl_Interp * interp, char * command)); /* 177 */
+ CONST84_RETURN char * (*tcl_GetStringResult) _ANSI_ARGS_((Tcl_Interp * interp)); /* 174 */
+ CONST84_RETURN char * (*tcl_GetVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName, int flags)); /* 175 */
+ CONST84_RETURN char * (*tcl_GetVar2) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * part1, CONST char * part2, int flags)); /* 176 */
+ int (*tcl_GlobalEval) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * command)); /* 177 */
int (*tcl_GlobalEvalObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr)); /* 178 */
- int (*tcl_HideCommand) _ANSI_ARGS_((Tcl_Interp * interp, char * cmdName, char * hiddenCmdToken)); /* 179 */
+ int (*tcl_HideCommand) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * cmdName, CONST char * hiddenCmdToken)); /* 179 */
int (*tcl_Init) _ANSI_ARGS_((Tcl_Interp * interp)); /* 180 */
void (*tcl_InitHashTable) _ANSI_ARGS_((Tcl_HashTable * tablePtr, int keyType)); /* 181 */
int (*tcl_InputBlocked) _ANSI_ARGS_((Tcl_Channel chan)); /* 182 */
int (*tcl_InputBuffered) _ANSI_ARGS_((Tcl_Channel chan)); /* 183 */
int (*tcl_InterpDeleted) _ANSI_ARGS_((Tcl_Interp * interp)); /* 184 */
int (*tcl_IsSafe) _ANSI_ARGS_((Tcl_Interp * interp)); /* 185 */
- char * (*tcl_JoinPath) _ANSI_ARGS_((int argc, char ** argv, Tcl_DString * resultPtr)); /* 186 */
- int (*tcl_LinkVar) _ANSI_ARGS_((Tcl_Interp * interp, char * varName, char * addr, int type)); /* 187 */
+ char * (*tcl_JoinPath) _ANSI_ARGS_((int argc, CONST84 char * CONST * argv, Tcl_DString * resultPtr)); /* 186 */
+ int (*tcl_LinkVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName, char * addr, int type)); /* 187 */
void *reserved188;
Tcl_Channel (*tcl_MakeFileChannel) _ANSI_ARGS_((ClientData handle, int mode)); /* 189 */
int (*tcl_MakeSafe) _ANSI_ARGS_((Tcl_Interp * interp)); /* 190 */
Tcl_Channel (*tcl_MakeTcpClientChannel) _ANSI_ARGS_((ClientData tcpSocket)); /* 191 */
- char * (*tcl_Merge) _ANSI_ARGS_((int argc, char ** argv)); /* 192 */
+ char * (*tcl_Merge) _ANSI_ARGS_((int argc, CONST84 char * CONST * argv)); /* 192 */
Tcl_HashEntry * (*tcl_NextHashEntry) _ANSI_ARGS_((Tcl_HashSearch * searchPtr)); /* 193 */
void (*tcl_NotifyChannel) _ANSI_ARGS_((Tcl_Channel channel, int mask)); /* 194 */
Tcl_Obj * (*tcl_ObjGetVar2) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * part1Ptr, Tcl_Obj * part2Ptr, int flags)); /* 195 */
Tcl_Obj * (*tcl_ObjSetVar2) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * part1Ptr, Tcl_Obj * part2Ptr, Tcl_Obj * newValuePtr, int flags)); /* 196 */
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
- Tcl_Channel (*tcl_OpenCommandChannel) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv, int flags)); /* 197 */
+ Tcl_Channel (*tcl_OpenCommandChannel) _ANSI_ARGS_((Tcl_Interp * interp, int argc, CONST84 char ** argv, int flags)); /* 197 */
#endif /* UNIX */
#ifdef __WIN32__
- Tcl_Channel (*tcl_OpenCommandChannel) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv, int flags)); /* 197 */
+ Tcl_Channel (*tcl_OpenCommandChannel) _ANSI_ARGS_((Tcl_Interp * interp, int argc, CONST84 char ** argv, int flags)); /* 197 */
#endif /* __WIN32__ */
#ifdef MAC_TCL
void *reserved197;
#endif /* MAC_TCL */
- Tcl_Channel (*tcl_OpenFileChannel) _ANSI_ARGS_((Tcl_Interp * interp, char * fileName, char * modeString, int permissions)); /* 198 */
- Tcl_Channel (*tcl_OpenTcpClient) _ANSI_ARGS_((Tcl_Interp * interp, int port, char * address, char * myaddr, int myport, int async)); /* 199 */
- Tcl_Channel (*tcl_OpenTcpServer) _ANSI_ARGS_((Tcl_Interp * interp, int port, char * host, Tcl_TcpAcceptProc * acceptProc, ClientData callbackData)); /* 200 */
+ Tcl_Channel (*tcl_OpenFileChannel) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * fileName, CONST char * modeString, int permissions)); /* 198 */
+ Tcl_Channel (*tcl_OpenTcpClient) _ANSI_ARGS_((Tcl_Interp * interp, int port, CONST char * address, CONST char * myaddr, int myport, int async)); /* 199 */
+ Tcl_Channel (*tcl_OpenTcpServer) _ANSI_ARGS_((Tcl_Interp * interp, int port, CONST char * host, Tcl_TcpAcceptProc * acceptProc, ClientData callbackData)); /* 200 */
void (*tcl_Preserve) _ANSI_ARGS_((ClientData data)); /* 201 */
void (*tcl_PrintDouble) _ANSI_ARGS_((Tcl_Interp * interp, double value, char * dst)); /* 202 */
int (*tcl_PutEnv) _ANSI_ARGS_((CONST char * string)); /* 203 */
- char * (*tcl_PosixError) _ANSI_ARGS_((Tcl_Interp * interp)); /* 204 */
+ CONST84_RETURN char * (*tcl_PosixError) _ANSI_ARGS_((Tcl_Interp * interp)); /* 204 */
void (*tcl_QueueEvent) _ANSI_ARGS_((Tcl_Event * evPtr, Tcl_QueuePosition position)); /* 205 */
int (*tcl_Read) _ANSI_ARGS_((Tcl_Channel chan, char * bufPtr, int toRead)); /* 206 */
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
@@ -1547,25 +1831,25 @@ typedef struct TclStubs {
#ifdef MAC_TCL
void *reserved207;
#endif /* MAC_TCL */
- int (*tcl_RecordAndEval) _ANSI_ARGS_((Tcl_Interp * interp, char * cmd, int flags)); /* 208 */
+ int (*tcl_RecordAndEval) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * cmd, int flags)); /* 208 */
int (*tcl_RecordAndEvalObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * cmdPtr, int flags)); /* 209 */
void (*tcl_RegisterChannel) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel chan)); /* 210 */
void (*tcl_RegisterObjType) _ANSI_ARGS_((Tcl_ObjType * typePtr)); /* 211 */
- Tcl_RegExp (*tcl_RegExpCompile) _ANSI_ARGS_((Tcl_Interp * interp, char * string)); /* 212 */
+ Tcl_RegExp (*tcl_RegExpCompile) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * string)); /* 212 */
int (*tcl_RegExpExec) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_RegExp regexp, CONST char * str, CONST char * start)); /* 213 */
- int (*tcl_RegExpMatch) _ANSI_ARGS_((Tcl_Interp * interp, char * str, char * pattern)); /* 214 */
- void (*tcl_RegExpRange) _ANSI_ARGS_((Tcl_RegExp regexp, int index, char ** startPtr, char ** endPtr)); /* 215 */
+ int (*tcl_RegExpMatch) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, CONST char * pattern)); /* 214 */
+ void (*tcl_RegExpRange) _ANSI_ARGS_((Tcl_RegExp regexp, int index, CONST84 char ** startPtr, CONST84 char ** endPtr)); /* 215 */
void (*tcl_Release) _ANSI_ARGS_((ClientData clientData)); /* 216 */
void (*tcl_ResetResult) _ANSI_ARGS_((Tcl_Interp * interp)); /* 217 */
int (*tcl_ScanElement) _ANSI_ARGS_((CONST char * str, int * flagPtr)); /* 218 */
int (*tcl_ScanCountedElement) _ANSI_ARGS_((CONST char * str, int length, int * flagPtr)); /* 219 */
- int (*tcl_Seek) _ANSI_ARGS_((Tcl_Channel chan, int offset, int mode)); /* 220 */
+ int (*tcl_SeekOld) _ANSI_ARGS_((Tcl_Channel chan, int offset, int mode)); /* 220 */
int (*tcl_ServiceAll) _ANSI_ARGS_((void)); /* 221 */
int (*tcl_ServiceEvent) _ANSI_ARGS_((int flags)); /* 222 */
- void (*tcl_SetAssocData) _ANSI_ARGS_((Tcl_Interp * interp, char * name, Tcl_InterpDeleteProc * proc, ClientData clientData)); /* 223 */
+ void (*tcl_SetAssocData) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, Tcl_InterpDeleteProc * proc, ClientData clientData)); /* 223 */
void (*tcl_SetChannelBufferSize) _ANSI_ARGS_((Tcl_Channel chan, int sz)); /* 224 */
- int (*tcl_SetChannelOption) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel chan, char * optionName, char * newValue)); /* 225 */
- int (*tcl_SetCommandInfo) _ANSI_ARGS_((Tcl_Interp * interp, char * cmdName, Tcl_CmdInfo * infoPtr)); /* 226 */
+ int (*tcl_SetChannelOption) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel chan, CONST char * optionName, CONST char * newValue)); /* 225 */
+ int (*tcl_SetCommandInfo) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * cmdName, CONST Tcl_CmdInfo * infoPtr)); /* 226 */
void (*tcl_SetErrno) _ANSI_ARGS_((int err)); /* 227 */
void (*tcl_SetErrorCode) _ANSI_ARGS_(TCL_VARARGS(Tcl_Interp *,interp)); /* 228 */
void (*tcl_SetMaxBlockTime) _ANSI_ARGS_((Tcl_Time * timePtr)); /* 229 */
@@ -1576,69 +1860,61 @@ typedef struct TclStubs {
void (*tcl_SetObjErrorCode) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * errorObjPtr)); /* 234 */
void (*tcl_SetObjResult) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * resultObjPtr)); /* 235 */
void (*tcl_SetStdChannel) _ANSI_ARGS_((Tcl_Channel channel, int type)); /* 236 */
- char * (*tcl_SetVar) _ANSI_ARGS_((Tcl_Interp * interp, char * varName, char * newValue, int flags)); /* 237 */
- char * (*tcl_SetVar2) _ANSI_ARGS_((Tcl_Interp * interp, char * part1, char * part2, char * newValue, int flags)); /* 238 */
- char * (*tcl_SignalId) _ANSI_ARGS_((int sig)); /* 239 */
- char * (*tcl_SignalMsg) _ANSI_ARGS_((int sig)); /* 240 */
+ CONST84_RETURN char * (*tcl_SetVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName, CONST char * newValue, int flags)); /* 237 */
+ CONST84_RETURN char * (*tcl_SetVar2) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * part1, CONST char * part2, CONST char * newValue, int flags)); /* 238 */
+ CONST84_RETURN char * (*tcl_SignalId) _ANSI_ARGS_((int sig)); /* 239 */
+ CONST84_RETURN char * (*tcl_SignalMsg) _ANSI_ARGS_((int sig)); /* 240 */
void (*tcl_SourceRCFile) _ANSI_ARGS_((Tcl_Interp * interp)); /* 241 */
- int (*tcl_SplitList) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * listStr, int * argcPtr, char *** argvPtr)); /* 242 */
- void (*tcl_SplitPath) _ANSI_ARGS_((CONST char * path, int * argcPtr, char *** argvPtr)); /* 243 */
- void (*tcl_StaticPackage) _ANSI_ARGS_((Tcl_Interp * interp, char * pkgName, Tcl_PackageInitProc * initProc, Tcl_PackageInitProc * safeInitProc)); /* 244 */
+ int (*tcl_SplitList) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * listStr, int * argcPtr, CONST84 char *** argvPtr)); /* 242 */
+ void (*tcl_SplitPath) _ANSI_ARGS_((CONST char * path, int * argcPtr, CONST84 char *** argvPtr)); /* 243 */
+ void (*tcl_StaticPackage) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * pkgName, Tcl_PackageInitProc * initProc, Tcl_PackageInitProc * safeInitProc)); /* 244 */
int (*tcl_StringMatch) _ANSI_ARGS_((CONST char * str, CONST char * pattern)); /* 245 */
- int (*tcl_Tell) _ANSI_ARGS_((Tcl_Channel chan)); /* 246 */
- int (*tcl_TraceVar) _ANSI_ARGS_((Tcl_Interp * interp, char * varName, int flags, Tcl_VarTraceProc * proc, ClientData clientData)); /* 247 */
- int (*tcl_TraceVar2) _ANSI_ARGS_((Tcl_Interp * interp, char * part1, char * part2, int flags, Tcl_VarTraceProc * proc, ClientData clientData)); /* 248 */
- char * (*tcl_TranslateFileName) _ANSI_ARGS_((Tcl_Interp * interp, char * name, Tcl_DString * bufferPtr)); /* 249 */
- int (*tcl_Ungets) _ANSI_ARGS_((Tcl_Channel chan, char * str, int len, int atHead)); /* 250 */
- void (*tcl_UnlinkVar) _ANSI_ARGS_((Tcl_Interp * interp, char * varName)); /* 251 */
+ int (*tcl_TellOld) _ANSI_ARGS_((Tcl_Channel chan)); /* 246 */
+ int (*tcl_TraceVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName, int flags, Tcl_VarTraceProc * proc, ClientData clientData)); /* 247 */
+ int (*tcl_TraceVar2) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * part1, CONST char * part2, int flags, Tcl_VarTraceProc * proc, ClientData clientData)); /* 248 */
+ char * (*tcl_TranslateFileName) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, Tcl_DString * bufferPtr)); /* 249 */
+ int (*tcl_Ungets) _ANSI_ARGS_((Tcl_Channel chan, CONST char * str, int len, int atHead)); /* 250 */
+ void (*tcl_UnlinkVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName)); /* 251 */
int (*tcl_UnregisterChannel) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel chan)); /* 252 */
- int (*tcl_UnsetVar) _ANSI_ARGS_((Tcl_Interp * interp, char * varName, int flags)); /* 253 */
- int (*tcl_UnsetVar2) _ANSI_ARGS_((Tcl_Interp * interp, char * part1, char * part2, int flags)); /* 254 */
- void (*tcl_UntraceVar) _ANSI_ARGS_((Tcl_Interp * interp, char * varName, int flags, Tcl_VarTraceProc * proc, ClientData clientData)); /* 255 */
- void (*tcl_UntraceVar2) _ANSI_ARGS_((Tcl_Interp * interp, char * part1, char * part2, int flags, Tcl_VarTraceProc * proc, ClientData clientData)); /* 256 */
- void (*tcl_UpdateLinkedVar) _ANSI_ARGS_((Tcl_Interp * interp, char * varName)); /* 257 */
- int (*tcl_UpVar) _ANSI_ARGS_((Tcl_Interp * interp, char * frameName, char * varName, char * localName, int flags)); /* 258 */
- int (*tcl_UpVar2) _ANSI_ARGS_((Tcl_Interp * interp, char * frameName, char * part1, char * part2, char * localName, int flags)); /* 259 */
+ int (*tcl_UnsetVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName, int flags)); /* 253 */
+ int (*tcl_UnsetVar2) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * part1, CONST char * part2, int flags)); /* 254 */
+ void (*tcl_UntraceVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName, int flags, Tcl_VarTraceProc * proc, ClientData clientData)); /* 255 */
+ void (*tcl_UntraceVar2) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * part1, CONST char * part2, int flags, Tcl_VarTraceProc * proc, ClientData clientData)); /* 256 */
+ void (*tcl_UpdateLinkedVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName)); /* 257 */
+ int (*tcl_UpVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * frameName, CONST char * varName, CONST char * localName, int flags)); /* 258 */
+ int (*tcl_UpVar2) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * frameName, CONST char * part1, CONST char * part2, CONST char * localName, int flags)); /* 259 */
int (*tcl_VarEval) _ANSI_ARGS_(TCL_VARARGS(Tcl_Interp *,interp)); /* 260 */
- ClientData (*tcl_VarTraceInfo) _ANSI_ARGS_((Tcl_Interp * interp, char * varName, int flags, Tcl_VarTraceProc * procPtr, ClientData prevClientData)); /* 261 */
- ClientData (*tcl_VarTraceInfo2) _ANSI_ARGS_((Tcl_Interp * interp, char * part1, char * part2, int flags, Tcl_VarTraceProc * procPtr, ClientData prevClientData)); /* 262 */
- int (*tcl_Write) _ANSI_ARGS_((Tcl_Channel chan, char * s, int slen)); /* 263 */
- void (*tcl_WrongNumArgs) _ANSI_ARGS_((Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[], char * message)); /* 264 */
- int (*tcl_DumpActiveMemory) _ANSI_ARGS_((char * fileName)); /* 265 */
- void (*tcl_ValidateAllMemory) _ANSI_ARGS_((char * file, int line)); /* 266 */
+ ClientData (*tcl_VarTraceInfo) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName, int flags, Tcl_VarTraceProc * procPtr, ClientData prevClientData)); /* 261 */
+ ClientData (*tcl_VarTraceInfo2) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * part1, CONST char * part2, int flags, Tcl_VarTraceProc * procPtr, ClientData prevClientData)); /* 262 */
+ int (*tcl_Write) _ANSI_ARGS_((Tcl_Channel chan, CONST char * s, int slen)); /* 263 */
+ void (*tcl_WrongNumArgs) _ANSI_ARGS_((Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[], CONST char * message)); /* 264 */
+ int (*tcl_DumpActiveMemory) _ANSI_ARGS_((CONST char * fileName)); /* 265 */
+ void (*tcl_ValidateAllMemory) _ANSI_ARGS_((CONST char * file, int line)); /* 266 */
void (*tcl_AppendResultVA) _ANSI_ARGS_((Tcl_Interp * interp, va_list argList)); /* 267 */
void (*tcl_AppendStringsToObjVA) _ANSI_ARGS_((Tcl_Obj * objPtr, va_list argList)); /* 268 */
- char * (*tcl_HashStats) _ANSI_ARGS_((Tcl_HashTable * tablePtr)); /* 269 */
- char * (*tcl_ParseVar) _ANSI_ARGS_((Tcl_Interp * interp, char * str, char ** termPtr)); /* 270 */
- char * (*tcl_PkgPresent) _ANSI_ARGS_((Tcl_Interp * interp, char * name, char * version, int exact)); /* 271 */
- char * (*tcl_PkgPresentEx) _ANSI_ARGS_((Tcl_Interp * interp, char * name, char * version, int exact, ClientData * clientDataPtr)); /* 272 */
- int (*tcl_PkgProvide) _ANSI_ARGS_((Tcl_Interp * interp, char * name, char * version)); /* 273 */
- char * (*tcl_PkgRequire) _ANSI_ARGS_((Tcl_Interp * interp, char * name, char * version, int exact)); /* 274 */
+ CONST84_RETURN char * (*tcl_HashStats) _ANSI_ARGS_((Tcl_HashTable * tablePtr)); /* 269 */
+ CONST84_RETURN char * (*tcl_ParseVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, CONST84 char ** termPtr)); /* 270 */
+ CONST84_RETURN char * (*tcl_PkgPresent) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, CONST char * version, int exact)); /* 271 */
+ CONST84_RETURN char * (*tcl_PkgPresentEx) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, CONST char * version, int exact, ClientData * clientDataPtr)); /* 272 */
+ int (*tcl_PkgProvide) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, CONST char * version)); /* 273 */
+ CONST84_RETURN char * (*tcl_PkgRequire) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, CONST char * version, int exact)); /* 274 */
void (*tcl_SetErrorCodeVA) _ANSI_ARGS_((Tcl_Interp * interp, va_list argList)); /* 275 */
int (*tcl_VarEvalVA) _ANSI_ARGS_((Tcl_Interp * interp, va_list argList)); /* 276 */
Tcl_Pid (*tcl_WaitPid) _ANSI_ARGS_((Tcl_Pid pid, int * statPtr, int options)); /* 277 */
-#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
- void (*tcl_PanicVA) _ANSI_ARGS_((char * format, va_list argList)); /* 278 */
-#endif /* UNIX */
-#ifdef __WIN32__
- void (*tcl_PanicVA) _ANSI_ARGS_((char * format, va_list argList)); /* 278 */
-#endif /* __WIN32__ */
-#ifdef MAC_TCL
- void *reserved278;
-#endif /* MAC_TCL */
+ void (*tcl_PanicVA) _ANSI_ARGS_((CONST char * format, va_list argList)); /* 278 */
void (*tcl_GetVersion) _ANSI_ARGS_((int * major, int * minor, int * patchLevel, int * type)); /* 279 */
void (*tcl_InitMemory) _ANSI_ARGS_((Tcl_Interp * interp)); /* 280 */
Tcl_Channel (*tcl_StackChannel) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_ChannelType * typePtr, ClientData instanceData, int mask, Tcl_Channel prevChan)); /* 281 */
int (*tcl_UnstackChannel) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel chan)); /* 282 */
Tcl_Channel (*tcl_GetStackedChannel) _ANSI_ARGS_((Tcl_Channel chan)); /* 283 */
- void *reserved284;
+ void (*tcl_SetMainLoop) _ANSI_ARGS_((Tcl_MainLoopProc * proc)); /* 284 */
void *reserved285;
void (*tcl_AppendObjToObj) _ANSI_ARGS_((Tcl_Obj * objPtr, Tcl_Obj * appendObjPtr)); /* 286 */
Tcl_Encoding (*tcl_CreateEncoding) _ANSI_ARGS_((Tcl_EncodingType * typePtr)); /* 287 */
void (*tcl_CreateThreadExitHandler) _ANSI_ARGS_((Tcl_ExitProc * proc, ClientData clientData)); /* 288 */
void (*tcl_DeleteThreadExitHandler) _ANSI_ARGS_((Tcl_ExitProc * proc, ClientData clientData)); /* 289 */
void (*tcl_DiscardResult) _ANSI_ARGS_((Tcl_SavedResult * statePtr)); /* 290 */
- int (*tcl_EvalEx) _ANSI_ARGS_((Tcl_Interp * interp, char * script, int numBytes, int flags)); /* 291 */
+ int (*tcl_EvalEx) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * script, int numBytes, int flags)); /* 291 */
int (*tcl_EvalObjv) _ANSI_ARGS_((Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[], int flags)); /* 292 */
int (*tcl_EvalObjEx) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, int flags)); /* 293 */
void (*tcl_ExitThread) _ANSI_ARGS_((int status)); /* 294 */
@@ -1649,11 +1925,11 @@ typedef struct TclStubs {
void (*tcl_FreeEncoding) _ANSI_ARGS_((Tcl_Encoding encoding)); /* 299 */
Tcl_ThreadId (*tcl_GetCurrentThread) _ANSI_ARGS_((void)); /* 300 */
Tcl_Encoding (*tcl_GetEncoding) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name)); /* 301 */
- char * (*tcl_GetEncodingName) _ANSI_ARGS_((Tcl_Encoding encoding)); /* 302 */
+ CONST84_RETURN char * (*tcl_GetEncodingName) _ANSI_ARGS_((Tcl_Encoding encoding)); /* 302 */
void (*tcl_GetEncodingNames) _ANSI_ARGS_((Tcl_Interp * interp)); /* 303 */
- int (*tcl_GetIndexFromObjStruct) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, char ** tablePtr, int offset, char * msg, int flags, int * indexPtr)); /* 304 */
+ int (*tcl_GetIndexFromObjStruct) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, CONST VOID * tablePtr, int offset, CONST char * msg, int flags, int * indexPtr)); /* 304 */
VOID * (*tcl_GetThreadData) _ANSI_ARGS_((Tcl_ThreadDataKey * keyPtr, int size)); /* 305 */
- Tcl_Obj * (*tcl_GetVar2Ex) _ANSI_ARGS_((Tcl_Interp * interp, char * part1, char * part2, int flags)); /* 306 */
+ Tcl_Obj * (*tcl_GetVar2Ex) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * part1, CONST char * part2, int flags)); /* 306 */
ClientData (*tcl_InitNotifier) _ANSI_ARGS_((void)); /* 307 */
void (*tcl_MutexLock) _ANSI_ARGS_((Tcl_Mutex * mutexPtr)); /* 308 */
void (*tcl_MutexUnlock) _ANSI_ARGS_((Tcl_Mutex * mutexPtr)); /* 309 */
@@ -1664,7 +1940,7 @@ typedef struct TclStubs {
void (*tcl_RestoreResult) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_SavedResult * statePtr)); /* 314 */
void (*tcl_SaveResult) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_SavedResult * statePtr)); /* 315 */
int (*tcl_SetSystemEncoding) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name)); /* 316 */
- Tcl_Obj * (*tcl_SetVar2Ex) _ANSI_ARGS_((Tcl_Interp * interp, char * part1, char * part2, Tcl_Obj * newValuePtr, int flags)); /* 317 */
+ Tcl_Obj * (*tcl_SetVar2Ex) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * part1, CONST char * part2, Tcl_Obj * newValuePtr, int flags)); /* 317 */
void (*tcl_ThreadAlert) _ANSI_ARGS_((Tcl_ThreadId threadId)); /* 318 */
void (*tcl_ThreadQueueEvent) _ANSI_ARGS_((Tcl_ThreadId threadId, Tcl_Event* evPtr, Tcl_QueuePosition position)); /* 319 */
Tcl_UniChar (*tcl_UniCharAtIndex) _ANSI_ARGS_((CONST char * src, int index)); /* 320 */
@@ -1672,13 +1948,13 @@ typedef struct TclStubs {
Tcl_UniChar (*tcl_UniCharToTitle) _ANSI_ARGS_((int ch)); /* 322 */
Tcl_UniChar (*tcl_UniCharToUpper) _ANSI_ARGS_((int ch)); /* 323 */
int (*tcl_UniCharToUtf) _ANSI_ARGS_((int ch, char * buf)); /* 324 */
- char * (*tcl_UtfAtIndex) _ANSI_ARGS_((CONST char * src, int index)); /* 325 */
+ CONST84_RETURN char * (*tcl_UtfAtIndex) _ANSI_ARGS_((CONST char * src, int index)); /* 325 */
int (*tcl_UtfCharComplete) _ANSI_ARGS_((CONST char * src, int len)); /* 326 */
int (*tcl_UtfBackslash) _ANSI_ARGS_((CONST char * src, int * readPtr, char * dst)); /* 327 */
- char * (*tcl_UtfFindFirst) _ANSI_ARGS_((CONST char * src, int ch)); /* 328 */
- char * (*tcl_UtfFindLast) _ANSI_ARGS_((CONST char * src, int ch)); /* 329 */
- char * (*tcl_UtfNext) _ANSI_ARGS_((CONST char * src)); /* 330 */
- char * (*tcl_UtfPrev) _ANSI_ARGS_((CONST char * src, CONST char * start)); /* 331 */
+ CONST84_RETURN char * (*tcl_UtfFindFirst) _ANSI_ARGS_((CONST char * src, int ch)); /* 328 */
+ CONST84_RETURN char * (*tcl_UtfFindLast) _ANSI_ARGS_((CONST char * src, int ch)); /* 329 */
+ CONST84_RETURN char * (*tcl_UtfNext) _ANSI_ARGS_((CONST char * src)); /* 330 */
+ CONST84_RETURN char * (*tcl_UtfPrev) _ANSI_ARGS_((CONST char * src, CONST char * start)); /* 331 */
int (*tcl_UtfToExternal) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Encoding encoding, CONST char * src, int srcLen, int flags, Tcl_EncodingState * statePtr, char * dst, int dstLen, int * srcReadPtr, int * dstWrotePtr, int * dstCharsPtr)); /* 332 */
char * (*tcl_UtfToExternalDString) _ANSI_ARGS_((Tcl_Encoding encoding, CONST char * src, int srcLen, Tcl_DString * dsPtr)); /* 333 */
int (*tcl_UtfToLower) _ANSI_ARGS_((char * src)); /* 334 */
@@ -1688,8 +1964,8 @@ typedef struct TclStubs {
int (*tcl_WriteChars) _ANSI_ARGS_((Tcl_Channel chan, CONST char * src, int srcLen)); /* 338 */
int (*tcl_WriteObj) _ANSI_ARGS_((Tcl_Channel chan, Tcl_Obj * objPtr)); /* 339 */
char * (*tcl_GetString) _ANSI_ARGS_((Tcl_Obj * objPtr)); /* 340 */
- char * (*tcl_GetDefaultEncodingDir) _ANSI_ARGS_((void)); /* 341 */
- void (*tcl_SetDefaultEncodingDir) _ANSI_ARGS_((char * path)); /* 342 */
+ CONST84_RETURN char * (*tcl_GetDefaultEncodingDir) _ANSI_ARGS_((void)); /* 341 */
+ void (*tcl_SetDefaultEncodingDir) _ANSI_ARGS_((CONST char * path)); /* 342 */
void (*tcl_AlertNotifier) _ANSI_ARGS_((ClientData clientData)); /* 343 */
void (*tcl_ServiceModeHook) _ANSI_ARGS_((int mode)); /* 344 */
int (*tcl_UniCharIsAlnum) _ANSI_ARGS_((int ch)); /* 345 */
@@ -1699,19 +1975,19 @@ typedef struct TclStubs {
int (*tcl_UniCharIsSpace) _ANSI_ARGS_((int ch)); /* 349 */
int (*tcl_UniCharIsUpper) _ANSI_ARGS_((int ch)); /* 350 */
int (*tcl_UniCharIsWordChar) _ANSI_ARGS_((int ch)); /* 351 */
- int (*tcl_UniCharLen) _ANSI_ARGS_((Tcl_UniChar * str)); /* 352 */
+ int (*tcl_UniCharLen) _ANSI_ARGS_((CONST Tcl_UniChar * str)); /* 352 */
int (*tcl_UniCharNcmp) _ANSI_ARGS_((CONST Tcl_UniChar * cs, CONST Tcl_UniChar * ct, unsigned long n)); /* 353 */
char * (*tcl_UniCharToUtfDString) _ANSI_ARGS_((CONST Tcl_UniChar * string, int numChars, Tcl_DString * dsPtr)); /* 354 */
Tcl_UniChar * (*tcl_UtfToUniCharDString) _ANSI_ARGS_((CONST char * string, int length, Tcl_DString * dsPtr)); /* 355 */
Tcl_RegExp (*tcl_GetRegExpFromObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * patObj, int flags)); /* 356 */
Tcl_Obj * (*tcl_EvalTokens) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Token * tokenPtr, int count)); /* 357 */
void (*tcl_FreeParse) _ANSI_ARGS_((Tcl_Parse * parsePtr)); /* 358 */
- void (*tcl_LogCommandInfo) _ANSI_ARGS_((Tcl_Interp * interp, char * script, char * command, int length)); /* 359 */
- int (*tcl_ParseBraces) _ANSI_ARGS_((Tcl_Interp * interp, char * string, int numBytes, Tcl_Parse * parsePtr, int append, char ** termPtr)); /* 360 */
- int (*tcl_ParseCommand) _ANSI_ARGS_((Tcl_Interp * interp, char * string, int numBytes, int nested, Tcl_Parse * parsePtr)); /* 361 */
- int (*tcl_ParseExpr) _ANSI_ARGS_((Tcl_Interp * interp, char * string, int numBytes, Tcl_Parse * parsePtr)); /* 362 */
- int (*tcl_ParseQuotedString) _ANSI_ARGS_((Tcl_Interp * interp, char * string, int numBytes, Tcl_Parse * parsePtr, int append, char ** termPtr)); /* 363 */
- int (*tcl_ParseVarName) _ANSI_ARGS_((Tcl_Interp * interp, char * string, int numBytes, Tcl_Parse * parsePtr, int append)); /* 364 */
+ void (*tcl_LogCommandInfo) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * script, CONST char * command, int length)); /* 359 */
+ int (*tcl_ParseBraces) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * string, int numBytes, Tcl_Parse * parsePtr, int append, CONST84 char ** termPtr)); /* 360 */
+ int (*tcl_ParseCommand) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * string, int numBytes, int nested, Tcl_Parse * parsePtr)); /* 361 */
+ int (*tcl_ParseExpr) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * string, int numBytes, Tcl_Parse * parsePtr)); /* 362 */
+ int (*tcl_ParseQuotedString) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * string, int numBytes, Tcl_Parse * parsePtr, int append, CONST84 char ** termPtr)); /* 363 */
+ int (*tcl_ParseVarName) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * string, int numBytes, Tcl_Parse * parsePtr, int append)); /* 364 */
char * (*tcl_GetCwd) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_DString * cwdPtr)); /* 365 */
int (*tcl_Chdir) _ANSI_ARGS_((CONST char * dirName)); /* 366 */
int (*tcl_Access) _ANSI_ARGS_((CONST char * path, int mode)); /* 367 */
@@ -1725,27 +2001,27 @@ typedef struct TclStubs {
int (*tcl_UniCharIsPunct) _ANSI_ARGS_((int ch)); /* 375 */
int (*tcl_RegExpExecObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_RegExp regexp, Tcl_Obj * objPtr, int offset, int nmatches, int flags)); /* 376 */
void (*tcl_RegExpGetInfo) _ANSI_ARGS_((Tcl_RegExp regexp, Tcl_RegExpInfo * infoPtr)); /* 377 */
- Tcl_Obj * (*tcl_NewUnicodeObj) _ANSI_ARGS_((Tcl_UniChar * unicode, int numChars)); /* 378 */
- void (*tcl_SetUnicodeObj) _ANSI_ARGS_((Tcl_Obj * objPtr, Tcl_UniChar * unicode, int numChars)); /* 379 */
+ Tcl_Obj * (*tcl_NewUnicodeObj) _ANSI_ARGS_((CONST Tcl_UniChar * unicode, int numChars)); /* 378 */
+ void (*tcl_SetUnicodeObj) _ANSI_ARGS_((Tcl_Obj * objPtr, CONST Tcl_UniChar * unicode, int numChars)); /* 379 */
int (*tcl_GetCharLength) _ANSI_ARGS_((Tcl_Obj * objPtr)); /* 380 */
Tcl_UniChar (*tcl_GetUniChar) _ANSI_ARGS_((Tcl_Obj * objPtr, int index)); /* 381 */
Tcl_UniChar * (*tcl_GetUnicode) _ANSI_ARGS_((Tcl_Obj * objPtr)); /* 382 */
Tcl_Obj * (*tcl_GetRange) _ANSI_ARGS_((Tcl_Obj * objPtr, int first, int last)); /* 383 */
- void (*tcl_AppendUnicodeToObj) _ANSI_ARGS_((Tcl_Obj * objPtr, Tcl_UniChar * unicode, int length)); /* 384 */
+ void (*tcl_AppendUnicodeToObj) _ANSI_ARGS_((Tcl_Obj * objPtr, CONST Tcl_UniChar * unicode, int length)); /* 384 */
int (*tcl_RegExpMatchObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * stringObj, Tcl_Obj * patternObj)); /* 385 */
void (*tcl_SetNotifier) _ANSI_ARGS_((Tcl_NotifierProcs * notifierProcPtr)); /* 386 */
Tcl_Mutex * (*tcl_GetAllocMutex) _ANSI_ARGS_((void)); /* 387 */
int (*tcl_GetChannelNames) _ANSI_ARGS_((Tcl_Interp * interp)); /* 388 */
- int (*tcl_GetChannelNamesEx) _ANSI_ARGS_((Tcl_Interp * interp, char * pattern)); /* 389 */
+ int (*tcl_GetChannelNamesEx) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * pattern)); /* 389 */
int (*tcl_ProcObjCmd) _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[])); /* 390 */
void (*tcl_ConditionFinalize) _ANSI_ARGS_((Tcl_Condition * condPtr)); /* 391 */
void (*tcl_MutexFinalize) _ANSI_ARGS_((Tcl_Mutex * mutex)); /* 392 */
int (*tcl_CreateThread) _ANSI_ARGS_((Tcl_ThreadId * idPtr, Tcl_ThreadCreateProc proc, ClientData clientData, int stackSize, int flags)); /* 393 */
int (*tcl_ReadRaw) _ANSI_ARGS_((Tcl_Channel chan, char * dst, int bytesToRead)); /* 394 */
- int (*tcl_WriteRaw) _ANSI_ARGS_((Tcl_Channel chan, char * src, int srcLen)); /* 395 */
+ int (*tcl_WriteRaw) _ANSI_ARGS_((Tcl_Channel chan, CONST char * src, int srcLen)); /* 395 */
Tcl_Channel (*tcl_GetTopChannel) _ANSI_ARGS_((Tcl_Channel chan)); /* 396 */
int (*tcl_ChannelBuffered) _ANSI_ARGS_((Tcl_Channel chan)); /* 397 */
- char * (*tcl_ChannelName) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 398 */
+ CONST84_RETURN char * (*tcl_ChannelName) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 398 */
Tcl_ChannelTypeVersion (*tcl_ChannelVersion) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 399 */
Tcl_DriverBlockModeProc * (*tcl_ChannelBlockModeProc) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 400 */
Tcl_DriverCloseProc * (*tcl_ChannelCloseProc) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 401 */
@@ -1759,6 +2035,88 @@ typedef struct TclStubs {
Tcl_DriverGetHandleProc * (*tcl_ChannelGetHandleProc) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 409 */
Tcl_DriverFlushProc * (*tcl_ChannelFlushProc) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 410 */
Tcl_DriverHandlerProc * (*tcl_ChannelHandlerProc) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 411 */
+ int (*tcl_JoinThread) _ANSI_ARGS_((Tcl_ThreadId id, int* result)); /* 412 */
+ int (*tcl_IsChannelShared) _ANSI_ARGS_((Tcl_Channel channel)); /* 413 */
+ int (*tcl_IsChannelRegistered) _ANSI_ARGS_((Tcl_Interp* interp, Tcl_Channel channel)); /* 414 */
+ void (*tcl_CutChannel) _ANSI_ARGS_((Tcl_Channel channel)); /* 415 */
+ void (*tcl_SpliceChannel) _ANSI_ARGS_((Tcl_Channel channel)); /* 416 */
+ void (*tcl_ClearChannelHandlers) _ANSI_ARGS_((Tcl_Channel channel)); /* 417 */
+ int (*tcl_IsChannelExisting) _ANSI_ARGS_((CONST char* channelName)); /* 418 */
+ int (*tcl_UniCharNcasecmp) _ANSI_ARGS_((CONST Tcl_UniChar * cs, CONST Tcl_UniChar * ct, unsigned long n)); /* 419 */
+ int (*tcl_UniCharCaseMatch) _ANSI_ARGS_((CONST Tcl_UniChar * ustr, CONST Tcl_UniChar * pattern, int nocase)); /* 420 */
+ Tcl_HashEntry * (*tcl_FindHashEntry) _ANSI_ARGS_((Tcl_HashTable * tablePtr, CONST char * key)); /* 421 */
+ Tcl_HashEntry * (*tcl_CreateHashEntry) _ANSI_ARGS_((Tcl_HashTable * tablePtr, CONST char * key, int * newPtr)); /* 422 */
+ void (*tcl_InitCustomHashTable) _ANSI_ARGS_((Tcl_HashTable * tablePtr, int keyType, Tcl_HashKeyType * typePtr)); /* 423 */
+ void (*tcl_InitObjHashTable) _ANSI_ARGS_((Tcl_HashTable * tablePtr)); /* 424 */
+ ClientData (*tcl_CommandTraceInfo) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName, int flags, Tcl_CommandTraceProc * procPtr, ClientData prevClientData)); /* 425 */
+ int (*tcl_TraceCommand) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName, int flags, Tcl_CommandTraceProc * proc, ClientData clientData)); /* 426 */
+ void (*tcl_UntraceCommand) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName, int flags, Tcl_CommandTraceProc * proc, ClientData clientData)); /* 427 */
+ char * (*tcl_AttemptAlloc) _ANSI_ARGS_((unsigned int size)); /* 428 */
+ char * (*tcl_AttemptDbCkalloc) _ANSI_ARGS_((unsigned int size, CONST char * file, int line)); /* 429 */
+ char * (*tcl_AttemptRealloc) _ANSI_ARGS_((char * ptr, unsigned int size)); /* 430 */
+ char * (*tcl_AttemptDbCkrealloc) _ANSI_ARGS_((char * ptr, unsigned int size, CONST char * file, int line)); /* 431 */
+ int (*tcl_AttemptSetObjLength) _ANSI_ARGS_((Tcl_Obj * objPtr, int length)); /* 432 */
+ Tcl_ThreadId (*tcl_GetChannelThread) _ANSI_ARGS_((Tcl_Channel channel)); /* 433 */
+ Tcl_UniChar * (*tcl_GetUnicodeFromObj) _ANSI_ARGS_((Tcl_Obj * objPtr, int * lengthPtr)); /* 434 */
+ int (*tcl_GetMathFuncInfo) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, int * numArgsPtr, Tcl_ValueType ** argTypesPtr, Tcl_MathProc ** procPtr, ClientData * clientDataPtr)); /* 435 */
+ Tcl_Obj * (*tcl_ListMathFuncs) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * pattern)); /* 436 */
+ Tcl_Obj * (*tcl_SubstObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, int flags)); /* 437 */
+ int (*tcl_DetachChannel) _ANSI_ARGS_((Tcl_Interp* interp, Tcl_Channel channel)); /* 438 */
+ int (*tcl_IsStandardChannel) _ANSI_ARGS_((Tcl_Channel channel)); /* 439 */
+ int (*tcl_FSCopyFile) _ANSI_ARGS_((Tcl_Obj * srcPathPtr, Tcl_Obj * destPathPtr)); /* 440 */
+ int (*tcl_FSCopyDirectory) _ANSI_ARGS_((Tcl_Obj * srcPathPtr, Tcl_Obj * destPathPtr, Tcl_Obj ** errorPtr)); /* 441 */
+ int (*tcl_FSCreateDirectory) _ANSI_ARGS_((Tcl_Obj * pathPtr)); /* 442 */
+ int (*tcl_FSDeleteFile) _ANSI_ARGS_((Tcl_Obj * pathPtr)); /* 443 */
+ int (*tcl_FSLoadFile) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * pathPtr, CONST char * sym1, CONST char * sym2, Tcl_PackageInitProc ** proc1Ptr, Tcl_PackageInitProc ** proc2Ptr, Tcl_LoadHandle * handlePtr, Tcl_FSUnloadFileProc ** unloadProcPtr)); /* 444 */
+ int (*tcl_FSMatchInDirectory) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * result, Tcl_Obj * pathPtr, CONST char * pattern, Tcl_GlobTypeData * types)); /* 445 */
+ Tcl_Obj * (*tcl_FSLink) _ANSI_ARGS_((Tcl_Obj * pathPtr, Tcl_Obj * toPtr, int linkAction)); /* 446 */
+ int (*tcl_FSRemoveDirectory) _ANSI_ARGS_((Tcl_Obj * pathPtr, int recursive, Tcl_Obj ** errorPtr)); /* 447 */
+ int (*tcl_FSRenameFile) _ANSI_ARGS_((Tcl_Obj * srcPathPtr, Tcl_Obj * destPathPtr)); /* 448 */
+ int (*tcl_FSLstat) _ANSI_ARGS_((Tcl_Obj * pathPtr, Tcl_StatBuf * buf)); /* 449 */
+ int (*tcl_FSUtime) _ANSI_ARGS_((Tcl_Obj * pathPtr, struct utimbuf * tval)); /* 450 */
+ int (*tcl_FSFileAttrsGet) _ANSI_ARGS_((Tcl_Interp * interp, int index, Tcl_Obj * pathPtr, Tcl_Obj ** objPtrRef)); /* 451 */
+ int (*tcl_FSFileAttrsSet) _ANSI_ARGS_((Tcl_Interp * interp, int index, Tcl_Obj * pathPtr, Tcl_Obj * objPtr)); /* 452 */
+ CONST char ** (*tcl_FSFileAttrStrings) _ANSI_ARGS_((Tcl_Obj * pathPtr, Tcl_Obj ** objPtrRef)); /* 453 */
+ int (*tcl_FSStat) _ANSI_ARGS_((Tcl_Obj * pathPtr, Tcl_StatBuf * buf)); /* 454 */
+ int (*tcl_FSAccess) _ANSI_ARGS_((Tcl_Obj * pathPtr, int mode)); /* 455 */
+ Tcl_Channel (*tcl_FSOpenFileChannel) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * pathPtr, CONST char * modeString, int permissions)); /* 456 */
+ Tcl_Obj* (*tcl_FSGetCwd) _ANSI_ARGS_((Tcl_Interp * interp)); /* 457 */
+ int (*tcl_FSChdir) _ANSI_ARGS_((Tcl_Obj * pathPtr)); /* 458 */
+ int (*tcl_FSConvertToPathType) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * pathPtr)); /* 459 */
+ Tcl_Obj* (*tcl_FSJoinPath) _ANSI_ARGS_((Tcl_Obj * listObj, int elements)); /* 460 */
+ Tcl_Obj* (*tcl_FSSplitPath) _ANSI_ARGS_((Tcl_Obj* pathPtr, int * lenPtr)); /* 461 */
+ int (*tcl_FSEqualPaths) _ANSI_ARGS_((Tcl_Obj* firstPtr, Tcl_Obj* secondPtr)); /* 462 */
+ Tcl_Obj* (*tcl_FSGetNormalizedPath) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj* pathObjPtr)); /* 463 */
+ Tcl_Obj* (*tcl_FSJoinToPath) _ANSI_ARGS_((Tcl_Obj * basePtr, int objc, Tcl_Obj *CONST objv[])); /* 464 */
+ ClientData (*tcl_FSGetInternalRep) _ANSI_ARGS_((Tcl_Obj* pathObjPtr, Tcl_Filesystem * fsPtr)); /* 465 */
+ Tcl_Obj* (*tcl_FSGetTranslatedPath) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj* pathPtr)); /* 466 */
+ int (*tcl_FSEvalFile) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * fileName)); /* 467 */
+ Tcl_Obj* (*tcl_FSNewNativePath) _ANSI_ARGS_((Tcl_Filesystem* fromFilesystem, ClientData clientData)); /* 468 */
+ CONST char* (*tcl_FSGetNativePath) _ANSI_ARGS_((Tcl_Obj* pathObjPtr)); /* 469 */
+ Tcl_Obj* (*tcl_FSFileSystemInfo) _ANSI_ARGS_((Tcl_Obj* pathObjPtr)); /* 470 */
+ Tcl_Obj* (*tcl_FSPathSeparator) _ANSI_ARGS_((Tcl_Obj* pathObjPtr)); /* 471 */
+ Tcl_Obj* (*tcl_FSListVolumes) _ANSI_ARGS_((void)); /* 472 */
+ int (*tcl_FSRegister) _ANSI_ARGS_((ClientData clientData, Tcl_Filesystem * fsPtr)); /* 473 */
+ int (*tcl_FSUnregister) _ANSI_ARGS_((Tcl_Filesystem * fsPtr)); /* 474 */
+ ClientData (*tcl_FSData) _ANSI_ARGS_((Tcl_Filesystem * fsPtr)); /* 475 */
+ CONST char* (*tcl_FSGetTranslatedStringPath) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj* pathPtr)); /* 476 */
+ Tcl_Filesystem* (*tcl_FSGetFileSystemForPath) _ANSI_ARGS_((Tcl_Obj* pathObjPtr)); /* 477 */
+ Tcl_PathType (*tcl_FSGetPathType) _ANSI_ARGS_((Tcl_Obj * pathObjPtr)); /* 478 */
+ int (*tcl_OutputBuffered) _ANSI_ARGS_((Tcl_Channel chan)); /* 479 */
+ void (*tcl_FSMountsChanged) _ANSI_ARGS_((Tcl_Filesystem * fsPtr)); /* 480 */
+ int (*tcl_EvalTokensStandard) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Token * tokenPtr, int count)); /* 481 */
+ void (*tcl_GetTime) _ANSI_ARGS_((Tcl_Time* timeBuf)); /* 482 */
+ Tcl_Trace (*tcl_CreateObjTrace) _ANSI_ARGS_((Tcl_Interp* interp, int level, int flags, Tcl_CmdObjTraceProc* objProc, ClientData clientData, Tcl_CmdObjTraceDeleteProc* delProc)); /* 483 */
+ int (*tcl_GetCommandInfoFromToken) _ANSI_ARGS_((Tcl_Command token, Tcl_CmdInfo* infoPtr)); /* 484 */
+ int (*tcl_SetCommandInfoFromToken) _ANSI_ARGS_((Tcl_Command token, CONST Tcl_CmdInfo* infoPtr)); /* 485 */
+ Tcl_Obj * (*tcl_DbNewWideIntObj) _ANSI_ARGS_((Tcl_WideInt wideValue, CONST char * file, int line)); /* 486 */
+ int (*tcl_GetWideIntFromObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, Tcl_WideInt * widePtr)); /* 487 */
+ Tcl_Obj * (*tcl_NewWideIntObj) _ANSI_ARGS_((Tcl_WideInt wideValue)); /* 488 */
+ void (*tcl_SetWideIntObj) _ANSI_ARGS_((Tcl_Obj * objPtr, Tcl_WideInt wideValue)); /* 489 */
+ Tcl_StatBuf * (*tcl_AllocStatBuf) _ANSI_ARGS_((void)); /* 490 */
+ Tcl_WideInt (*tcl_Seek) _ANSI_ARGS_((Tcl_Channel chan, Tcl_WideInt offset, int mode)); /* 491 */
+ Tcl_WideInt (*tcl_Tell) _ANSI_ARGS_((Tcl_Channel chan)); /* 492 */
+ Tcl_DriverWideSeekProc * (*tcl_ChannelWideSeekProc) _ANSI_ARGS_((Tcl_ChannelType * chanTypePtr)); /* 493 */
} TclStubs;
#ifdef __cplusplus
@@ -2682,9 +3040,9 @@ extern TclStubs *tclStubsPtr;
#define Tcl_ScanCountedElement \
(tclStubsPtr->tcl_ScanCountedElement) /* 219 */
#endif
-#ifndef Tcl_Seek
-#define Tcl_Seek \
- (tclStubsPtr->tcl_Seek) /* 220 */
+#ifndef Tcl_SeekOld
+#define Tcl_SeekOld \
+ (tclStubsPtr->tcl_SeekOld) /* 220 */
#endif
#ifndef Tcl_ServiceAll
#define Tcl_ServiceAll \
@@ -2786,9 +3144,9 @@ extern TclStubs *tclStubsPtr;
#define Tcl_StringMatch \
(tclStubsPtr->tcl_StringMatch) /* 245 */
#endif
-#ifndef Tcl_Tell
-#define Tcl_Tell \
- (tclStubsPtr->tcl_Tell) /* 246 */
+#ifndef Tcl_TellOld
+#define Tcl_TellOld \
+ (tclStubsPtr->tcl_TellOld) /* 246 */
#endif
#ifndef Tcl_TraceVar
#define Tcl_TraceVar \
@@ -2914,18 +3272,10 @@ extern TclStubs *tclStubsPtr;
#define Tcl_WaitPid \
(tclStubsPtr->tcl_WaitPid) /* 277 */
#endif
-#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
-#ifndef Tcl_PanicVA
-#define Tcl_PanicVA \
- (tclStubsPtr->tcl_PanicVA) /* 278 */
-#endif
-#endif /* UNIX */
-#ifdef __WIN32__
#ifndef Tcl_PanicVA
#define Tcl_PanicVA \
(tclStubsPtr->tcl_PanicVA) /* 278 */
#endif
-#endif /* __WIN32__ */
#ifndef Tcl_GetVersion
#define Tcl_GetVersion \
(tclStubsPtr->tcl_GetVersion) /* 279 */
@@ -2946,7 +3296,10 @@ extern TclStubs *tclStubsPtr;
#define Tcl_GetStackedChannel \
(tclStubsPtr->tcl_GetStackedChannel) /* 283 */
#endif
-/* Slot 284 is reserved */
+#ifndef Tcl_SetMainLoop
+#define Tcl_SetMainLoop \
+ (tclStubsPtr->tcl_SetMainLoop) /* 284 */
+#endif
/* Slot 285 is reserved */
#ifndef Tcl_AppendObjToObj
#define Tcl_AppendObjToObj \
@@ -3452,6 +3805,334 @@ extern TclStubs *tclStubsPtr;
#define Tcl_ChannelHandlerProc \
(tclStubsPtr->tcl_ChannelHandlerProc) /* 411 */
#endif
+#ifndef Tcl_JoinThread
+#define Tcl_JoinThread \
+ (tclStubsPtr->tcl_JoinThread) /* 412 */
+#endif
+#ifndef Tcl_IsChannelShared
+#define Tcl_IsChannelShared \
+ (tclStubsPtr->tcl_IsChannelShared) /* 413 */
+#endif
+#ifndef Tcl_IsChannelRegistered
+#define Tcl_IsChannelRegistered \
+ (tclStubsPtr->tcl_IsChannelRegistered) /* 414 */
+#endif
+#ifndef Tcl_CutChannel
+#define Tcl_CutChannel \
+ (tclStubsPtr->tcl_CutChannel) /* 415 */
+#endif
+#ifndef Tcl_SpliceChannel
+#define Tcl_SpliceChannel \
+ (tclStubsPtr->tcl_SpliceChannel) /* 416 */
+#endif
+#ifndef Tcl_ClearChannelHandlers
+#define Tcl_ClearChannelHandlers \
+ (tclStubsPtr->tcl_ClearChannelHandlers) /* 417 */
+#endif
+#ifndef Tcl_IsChannelExisting
+#define Tcl_IsChannelExisting \
+ (tclStubsPtr->tcl_IsChannelExisting) /* 418 */
+#endif
+#ifndef Tcl_UniCharNcasecmp
+#define Tcl_UniCharNcasecmp \
+ (tclStubsPtr->tcl_UniCharNcasecmp) /* 419 */
+#endif
+#ifndef Tcl_UniCharCaseMatch
+#define Tcl_UniCharCaseMatch \
+ (tclStubsPtr->tcl_UniCharCaseMatch) /* 420 */
+#endif
+#ifndef Tcl_FindHashEntry
+#define Tcl_FindHashEntry \
+ (tclStubsPtr->tcl_FindHashEntry) /* 421 */
+#endif
+#ifndef Tcl_CreateHashEntry
+#define Tcl_CreateHashEntry \
+ (tclStubsPtr->tcl_CreateHashEntry) /* 422 */
+#endif
+#ifndef Tcl_InitCustomHashTable
+#define Tcl_InitCustomHashTable \
+ (tclStubsPtr->tcl_InitCustomHashTable) /* 423 */
+#endif
+#ifndef Tcl_InitObjHashTable
+#define Tcl_InitObjHashTable \
+ (tclStubsPtr->tcl_InitObjHashTable) /* 424 */
+#endif
+#ifndef Tcl_CommandTraceInfo
+#define Tcl_CommandTraceInfo \
+ (tclStubsPtr->tcl_CommandTraceInfo) /* 425 */
+#endif
+#ifndef Tcl_TraceCommand
+#define Tcl_TraceCommand \
+ (tclStubsPtr->tcl_TraceCommand) /* 426 */
+#endif
+#ifndef Tcl_UntraceCommand
+#define Tcl_UntraceCommand \
+ (tclStubsPtr->tcl_UntraceCommand) /* 427 */
+#endif
+#ifndef Tcl_AttemptAlloc
+#define Tcl_AttemptAlloc \
+ (tclStubsPtr->tcl_AttemptAlloc) /* 428 */
+#endif
+#ifndef Tcl_AttemptDbCkalloc
+#define Tcl_AttemptDbCkalloc \
+ (tclStubsPtr->tcl_AttemptDbCkalloc) /* 429 */
+#endif
+#ifndef Tcl_AttemptRealloc
+#define Tcl_AttemptRealloc \
+ (tclStubsPtr->tcl_AttemptRealloc) /* 430 */
+#endif
+#ifndef Tcl_AttemptDbCkrealloc
+#define Tcl_AttemptDbCkrealloc \
+ (tclStubsPtr->tcl_AttemptDbCkrealloc) /* 431 */
+#endif
+#ifndef Tcl_AttemptSetObjLength
+#define Tcl_AttemptSetObjLength \
+ (tclStubsPtr->tcl_AttemptSetObjLength) /* 432 */
+#endif
+#ifndef Tcl_GetChannelThread
+#define Tcl_GetChannelThread \
+ (tclStubsPtr->tcl_GetChannelThread) /* 433 */
+#endif
+#ifndef Tcl_GetUnicodeFromObj
+#define Tcl_GetUnicodeFromObj \
+ (tclStubsPtr->tcl_GetUnicodeFromObj) /* 434 */
+#endif
+#ifndef Tcl_GetMathFuncInfo
+#define Tcl_GetMathFuncInfo \
+ (tclStubsPtr->tcl_GetMathFuncInfo) /* 435 */
+#endif
+#ifndef Tcl_ListMathFuncs
+#define Tcl_ListMathFuncs \
+ (tclStubsPtr->tcl_ListMathFuncs) /* 436 */
+#endif
+#ifndef Tcl_SubstObj
+#define Tcl_SubstObj \
+ (tclStubsPtr->tcl_SubstObj) /* 437 */
+#endif
+#ifndef Tcl_DetachChannel
+#define Tcl_DetachChannel \
+ (tclStubsPtr->tcl_DetachChannel) /* 438 */
+#endif
+#ifndef Tcl_IsStandardChannel
+#define Tcl_IsStandardChannel \
+ (tclStubsPtr->tcl_IsStandardChannel) /* 439 */
+#endif
+#ifndef Tcl_FSCopyFile
+#define Tcl_FSCopyFile \
+ (tclStubsPtr->tcl_FSCopyFile) /* 440 */
+#endif
+#ifndef Tcl_FSCopyDirectory
+#define Tcl_FSCopyDirectory \
+ (tclStubsPtr->tcl_FSCopyDirectory) /* 441 */
+#endif
+#ifndef Tcl_FSCreateDirectory
+#define Tcl_FSCreateDirectory \
+ (tclStubsPtr->tcl_FSCreateDirectory) /* 442 */
+#endif
+#ifndef Tcl_FSDeleteFile
+#define Tcl_FSDeleteFile \
+ (tclStubsPtr->tcl_FSDeleteFile) /* 443 */
+#endif
+#ifndef Tcl_FSLoadFile
+#define Tcl_FSLoadFile \
+ (tclStubsPtr->tcl_FSLoadFile) /* 444 */
+#endif
+#ifndef Tcl_FSMatchInDirectory
+#define Tcl_FSMatchInDirectory \
+ (tclStubsPtr->tcl_FSMatchInDirectory) /* 445 */
+#endif
+#ifndef Tcl_FSLink
+#define Tcl_FSLink \
+ (tclStubsPtr->tcl_FSLink) /* 446 */
+#endif
+#ifndef Tcl_FSRemoveDirectory
+#define Tcl_FSRemoveDirectory \
+ (tclStubsPtr->tcl_FSRemoveDirectory) /* 447 */
+#endif
+#ifndef Tcl_FSRenameFile
+#define Tcl_FSRenameFile \
+ (tclStubsPtr->tcl_FSRenameFile) /* 448 */
+#endif
+#ifndef Tcl_FSLstat
+#define Tcl_FSLstat \
+ (tclStubsPtr->tcl_FSLstat) /* 449 */
+#endif
+#ifndef Tcl_FSUtime
+#define Tcl_FSUtime \
+ (tclStubsPtr->tcl_FSUtime) /* 450 */
+#endif
+#ifndef Tcl_FSFileAttrsGet
+#define Tcl_FSFileAttrsGet \
+ (tclStubsPtr->tcl_FSFileAttrsGet) /* 451 */
+#endif
+#ifndef Tcl_FSFileAttrsSet
+#define Tcl_FSFileAttrsSet \
+ (tclStubsPtr->tcl_FSFileAttrsSet) /* 452 */
+#endif
+#ifndef Tcl_FSFileAttrStrings
+#define Tcl_FSFileAttrStrings \
+ (tclStubsPtr->tcl_FSFileAttrStrings) /* 453 */
+#endif
+#ifndef Tcl_FSStat
+#define Tcl_FSStat \
+ (tclStubsPtr->tcl_FSStat) /* 454 */
+#endif
+#ifndef Tcl_FSAccess
+#define Tcl_FSAccess \
+ (tclStubsPtr->tcl_FSAccess) /* 455 */
+#endif
+#ifndef Tcl_FSOpenFileChannel
+#define Tcl_FSOpenFileChannel \
+ (tclStubsPtr->tcl_FSOpenFileChannel) /* 456 */
+#endif
+#ifndef Tcl_FSGetCwd
+#define Tcl_FSGetCwd \
+ (tclStubsPtr->tcl_FSGetCwd) /* 457 */
+#endif
+#ifndef Tcl_FSChdir
+#define Tcl_FSChdir \
+ (tclStubsPtr->tcl_FSChdir) /* 458 */
+#endif
+#ifndef Tcl_FSConvertToPathType
+#define Tcl_FSConvertToPathType \
+ (tclStubsPtr->tcl_FSConvertToPathType) /* 459 */
+#endif
+#ifndef Tcl_FSJoinPath
+#define Tcl_FSJoinPath \
+ (tclStubsPtr->tcl_FSJoinPath) /* 460 */
+#endif
+#ifndef Tcl_FSSplitPath
+#define Tcl_FSSplitPath \
+ (tclStubsPtr->tcl_FSSplitPath) /* 461 */
+#endif
+#ifndef Tcl_FSEqualPaths
+#define Tcl_FSEqualPaths \
+ (tclStubsPtr->tcl_FSEqualPaths) /* 462 */
+#endif
+#ifndef Tcl_FSGetNormalizedPath
+#define Tcl_FSGetNormalizedPath \
+ (tclStubsPtr->tcl_FSGetNormalizedPath) /* 463 */
+#endif
+#ifndef Tcl_FSJoinToPath
+#define Tcl_FSJoinToPath \
+ (tclStubsPtr->tcl_FSJoinToPath) /* 464 */
+#endif
+#ifndef Tcl_FSGetInternalRep
+#define Tcl_FSGetInternalRep \
+ (tclStubsPtr->tcl_FSGetInternalRep) /* 465 */
+#endif
+#ifndef Tcl_FSGetTranslatedPath
+#define Tcl_FSGetTranslatedPath \
+ (tclStubsPtr->tcl_FSGetTranslatedPath) /* 466 */
+#endif
+#ifndef Tcl_FSEvalFile
+#define Tcl_FSEvalFile \
+ (tclStubsPtr->tcl_FSEvalFile) /* 467 */
+#endif
+#ifndef Tcl_FSNewNativePath
+#define Tcl_FSNewNativePath \
+ (tclStubsPtr->tcl_FSNewNativePath) /* 468 */
+#endif
+#ifndef Tcl_FSGetNativePath
+#define Tcl_FSGetNativePath \
+ (tclStubsPtr->tcl_FSGetNativePath) /* 469 */
+#endif
+#ifndef Tcl_FSFileSystemInfo
+#define Tcl_FSFileSystemInfo \
+ (tclStubsPtr->tcl_FSFileSystemInfo) /* 470 */
+#endif
+#ifndef Tcl_FSPathSeparator
+#define Tcl_FSPathSeparator \
+ (tclStubsPtr->tcl_FSPathSeparator) /* 471 */
+#endif
+#ifndef Tcl_FSListVolumes
+#define Tcl_FSListVolumes \
+ (tclStubsPtr->tcl_FSListVolumes) /* 472 */
+#endif
+#ifndef Tcl_FSRegister
+#define Tcl_FSRegister \
+ (tclStubsPtr->tcl_FSRegister) /* 473 */
+#endif
+#ifndef Tcl_FSUnregister
+#define Tcl_FSUnregister \
+ (tclStubsPtr->tcl_FSUnregister) /* 474 */
+#endif
+#ifndef Tcl_FSData
+#define Tcl_FSData \
+ (tclStubsPtr->tcl_FSData) /* 475 */
+#endif
+#ifndef Tcl_FSGetTranslatedStringPath
+#define Tcl_FSGetTranslatedStringPath \
+ (tclStubsPtr->tcl_FSGetTranslatedStringPath) /* 476 */
+#endif
+#ifndef Tcl_FSGetFileSystemForPath
+#define Tcl_FSGetFileSystemForPath \
+ (tclStubsPtr->tcl_FSGetFileSystemForPath) /* 477 */
+#endif
+#ifndef Tcl_FSGetPathType
+#define Tcl_FSGetPathType \
+ (tclStubsPtr->tcl_FSGetPathType) /* 478 */
+#endif
+#ifndef Tcl_OutputBuffered
+#define Tcl_OutputBuffered \
+ (tclStubsPtr->tcl_OutputBuffered) /* 479 */
+#endif
+#ifndef Tcl_FSMountsChanged
+#define Tcl_FSMountsChanged \
+ (tclStubsPtr->tcl_FSMountsChanged) /* 480 */
+#endif
+#ifndef Tcl_EvalTokensStandard
+#define Tcl_EvalTokensStandard \
+ (tclStubsPtr->tcl_EvalTokensStandard) /* 481 */
+#endif
+#ifndef Tcl_GetTime
+#define Tcl_GetTime \
+ (tclStubsPtr->tcl_GetTime) /* 482 */
+#endif
+#ifndef Tcl_CreateObjTrace
+#define Tcl_CreateObjTrace \
+ (tclStubsPtr->tcl_CreateObjTrace) /* 483 */
+#endif
+#ifndef Tcl_GetCommandInfoFromToken
+#define Tcl_GetCommandInfoFromToken \
+ (tclStubsPtr->tcl_GetCommandInfoFromToken) /* 484 */
+#endif
+#ifndef Tcl_SetCommandInfoFromToken
+#define Tcl_SetCommandInfoFromToken \
+ (tclStubsPtr->tcl_SetCommandInfoFromToken) /* 485 */
+#endif
+#ifndef Tcl_DbNewWideIntObj
+#define Tcl_DbNewWideIntObj \
+ (tclStubsPtr->tcl_DbNewWideIntObj) /* 486 */
+#endif
+#ifndef Tcl_GetWideIntFromObj
+#define Tcl_GetWideIntFromObj \
+ (tclStubsPtr->tcl_GetWideIntFromObj) /* 487 */
+#endif
+#ifndef Tcl_NewWideIntObj
+#define Tcl_NewWideIntObj \
+ (tclStubsPtr->tcl_NewWideIntObj) /* 488 */
+#endif
+#ifndef Tcl_SetWideIntObj
+#define Tcl_SetWideIntObj \
+ (tclStubsPtr->tcl_SetWideIntObj) /* 489 */
+#endif
+#ifndef Tcl_AllocStatBuf
+#define Tcl_AllocStatBuf \
+ (tclStubsPtr->tcl_AllocStatBuf) /* 490 */
+#endif
+#ifndef Tcl_Seek
+#define Tcl_Seek \
+ (tclStubsPtr->tcl_Seek) /* 491 */
+#endif
+#ifndef Tcl_Tell
+#define Tcl_Tell \
+ (tclStubsPtr->tcl_Tell) /* 492 */
+#endif
+#ifndef Tcl_ChannelWideSeekProc
+#define Tcl_ChannelWideSeekProc \
+ (tclStubsPtr->tcl_ChannelWideSeekProc) /* 493 */
+#endif
#endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */
@@ -3459,4 +4140,3 @@ extern TclStubs *tclStubsPtr;
#endif /* _TCLDECLS */
-
diff --git a/tcl/generic/tclEncoding.c b/tcl/generic/tclEncoding.c
index 8a43126c075..e97062a7eb1 100644
--- a/tcl/generic/tclEncoding.c
+++ b/tcl/generic/tclEncoding.c
@@ -310,18 +310,16 @@ TclFinalizeEncodingSubsystem()
{
Tcl_HashSearch search;
Tcl_HashEntry *hPtr;
- Encoding *encodingPtr;
Tcl_MutexLock(&encodingMutex);
encodingsInitialized = 0;
hPtr = Tcl_FirstHashEntry(&encodingTable, &search);
while (hPtr != NULL) {
- encodingPtr = (Encoding *) Tcl_GetHashValue(hPtr);
- if (encodingPtr->freeProc != NULL) {
- (*encodingPtr->freeProc)(encodingPtr->clientData);
- }
- ckfree((char *) encodingPtr->name);
- ckfree((char *) encodingPtr);
+ /*
+ * Call FreeEncoding instead of doing it directly to handle refcounts
+ * like escape encodings use. [Bug #524674]
+ */
+ FreeEncoding((Tcl_Encoding) Tcl_GetHashValue(hPtr));
hPtr = Tcl_NextHashEntry(&search);
}
Tcl_DeleteHashTable(&encodingTable);
@@ -341,7 +339,7 @@ TclFinalizeEncodingSubsystem()
*-------------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_GetDefaultEncodingDir()
{
return tclDefaultEncodingDir;
@@ -362,7 +360,7 @@ Tcl_GetDefaultEncodingDir()
void
Tcl_SetDefaultEncodingDir(path)
- char *path;
+ CONST char *path;
{
tclDefaultEncodingDir = (char *)ckalloc((unsigned) strlen(path) + 1);
strcpy(tclDefaultEncodingDir, path);
@@ -505,7 +503,7 @@ FreeEncoding(encoding)
*---------------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_GetEncodingName(encoding)
Tcl_Encoding encoding; /* The encoding whose name to fetch. */
{
@@ -563,20 +561,22 @@ Tcl_GetEncodingNames(interp)
if (pathPtr != NULL) {
int i, objc;
Tcl_Obj **objv;
- Tcl_DString pwdString;
char globArgString[10];
-
+ Tcl_Obj* encodingObj = Tcl_NewStringObj("encoding",-1);
+ Tcl_IncrRefCount(encodingObj);
+
objc = 0;
Tcl_ListObjGetElements(NULL, pathPtr, &objc, &objv);
- Tcl_GetCwd(interp, &pwdString);
-
for (i = 0; i < objc; i++) {
- char *string;
- int j, objc2, length;
- Tcl_Obj **objv2;
-
- string = Tcl_GetStringFromObj(objv[i], NULL);
+ Tcl_Obj *searchIn;
+
+ /*
+ * Construct the path from the element of pathPtr,
+ * joined with 'encoding'.
+ */
+ searchIn = Tcl_FSJoinToPath(objv[i],1,&encodingObj);
+ Tcl_IncrRefCount(searchIn);
Tcl_ResetResult(interp);
/*
@@ -586,15 +586,22 @@ Tcl_GetEncodingNames(interp)
*/
strcpy(globArgString, "*.enc");
- if ((Tcl_Chdir(string) == 0)
- && (Tcl_Chdir("encoding") == 0)
- && (TclGlob(interp, globArgString, NULL, 0, NULL) == TCL_OK)) {
- objc2 = 0;
+ /*
+ * The GLOBMODE_TAILS flag returns just the tail of each file
+ * which is the encoding name with a .enc extension
+ */
+ if ((TclGlob(interp, globArgString, searchIn,
+ TCL_GLOBMODE_TAILS, NULL) == TCL_OK)) {
+ int objc2 = 0;
+ Tcl_Obj **objv2;
+ int j;
Tcl_ListObjGetElements(NULL, Tcl_GetObjResult(interp), &objc2,
&objv2);
for (j = 0; j < objc2; j++) {
+ int length;
+ char *string;
string = Tcl_GetStringFromObj(objv2[j], &length);
length -= 4;
if (length > 0) {
@@ -604,9 +611,9 @@ Tcl_GetEncodingNames(interp)
}
}
}
- Tcl_Chdir(Tcl_DStringValue(&pwdString));
+ Tcl_DecrRefCount(searchIn);
}
- Tcl_DStringFree(&pwdString);
+ Tcl_DecrRefCount(encodingObj);
}
/*
@@ -828,7 +835,7 @@ Tcl_ExternalToUtfDString(encoding, src, srcLen, dstPtr)
*
* Tcl_ExternalToUtf --
*
- * Convert a source buffer from the specified encoding into UTF-8,
+ * Convert a source buffer from the specified encoding into UTF-8.
*
* Results:
* The return value is one of TCL_OK, TCL_CONVERT_MULTIBYTE,
@@ -1271,19 +1278,25 @@ OpenEncodingFile(dir, name)
CONST char *name;
{
- char *argv[3];
+ CONST char *argv[3];
Tcl_DString pathString;
- char *path;
+ CONST char *path;
Tcl_Channel chan;
+ Tcl_Obj *pathPtr;
- argv[0] = (char *) dir;
+ argv[0] = dir;
argv[1] = "encoding";
- argv[2] = (char *) name;
+ argv[2] = name;
Tcl_DStringInit(&pathString);
Tcl_JoinPath(3, argv, &pathString);
path = Tcl_DStringAppend(&pathString, ".enc", -1);
- chan = Tcl_OpenFileChannel(NULL, path, "r", 0);
+ pathPtr = Tcl_NewStringObj(path,-1);
+
+ Tcl_IncrRefCount(pathPtr);
+ chan = Tcl_FSOpenFileChannel(NULL, pathPtr, "r", 0);
+ Tcl_DecrRefCount(pathPtr);
+
Tcl_DStringFree(&pathString);
return chan;
@@ -1328,14 +1341,31 @@ LoadTableEncoding(interp, name, type, chan)
TableEncodingData *dataPtr;
unsigned short *pageMemPtr;
Tcl_EncodingType encType;
- char *hex;
+
+ /*
+ * Speed over memory. Use a full 256 character table to decode hex
+ * sequences in the encoding files.
+ */
+
static char staticHex[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0,
- 10, 11, 12, 13, 14, 15
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 ... 15 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 ... 31 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 ... 47 */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 48 ... 63 */
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 ... 79 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 ... 95 */
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 ... 111 */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 112 ... 127 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128 ... 143 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144 ... 159 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160 ... 175 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 176 ... 191 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 ... 207 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 208 ... 223 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224 ... 239 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 240 ... 255 */
};
- hex = staticHex - '0';
-
Tcl_DStringInit(&lineString);
Tcl_Gets(chan, &lineString);
line = Tcl_DStringValue(&lineString);
@@ -1383,15 +1413,15 @@ LoadTableEncoding(interp, name, type, chan)
Tcl_ReadChars(chan, objPtr, 3 + 16 * (16 * 4 + 1), 0);
p = Tcl_GetString(objPtr);
- hi = (hex[(int)p[0]] << 4) + hex[(int)p[1]];
+ hi = (staticHex[(unsigned int)p[0]] << 4) + staticHex[(unsigned int)p[1]];
dataPtr->toUnicode[hi] = pageMemPtr;
p += 2;
for (lo = 0; lo < 256; lo++) {
if ((lo & 0x0f) == 0) {
p++;
}
- ch = (hex[(int)p[0]] << 12) + (hex[(int)p[1]] << 8)
- + (hex[(int)p[2]] << 4) + hex[(int)p[3]];
+ ch = (staticHex[(unsigned int)p[0]] << 12) + (staticHex[(unsigned int)p[1]] << 8)
+ + (staticHex[(unsigned int)p[2]] << 4) + staticHex[(unsigned int)p[3]];
if (ch != 0) {
used[ch >> 8] = 1;
}
@@ -1510,7 +1540,6 @@ LoadTableEncoding(interp, name, type, chan)
encType.nullSize = (type == ENCODING_DOUBLEBYTE) ? 2 : 1;
encType.clientData = (ClientData) dataPtr;
return Tcl_CreateEncoding(&encType);
-
}
/*
@@ -1553,7 +1582,7 @@ LoadEscapeEncoding(name, chan)
while (1) {
int argc;
- char **argv;
+ CONST char **argv;
char *line;
Tcl_DString lineString;
@@ -2175,6 +2204,10 @@ TableFreeProc(clientData)
{
TableEncodingData *dataPtr;
+ /*
+ * Make sure we aren't freeing twice on shutdown. [Bug #219314]
+ */
+
dataPtr = (TableEncodingData *) clientData;
ckfree((char *) dataPtr->toUnicode);
ckfree((char *) dataPtr->fromUnicode);
@@ -2460,12 +2493,14 @@ EscapeFromUtfProc(clientData, src, srcLen, flags, statePtr, dst, dstLen,
dstStart = dst;
dstEnd = dst + dstLen - 1;
+ /*
+ * RFC1468 states that the text starts in ASCII, and switches to Japanese
+ * characters, and that the text must end in ASCII. [Patch #474358]
+ */
+
if (flags & TCL_ENCODING_START) {
- unsigned int len;
-
state = 0;
- len = dataPtr->subTables[0].sequenceLen;
- if (dst + dataPtr->initLen + len > dstEnd) {
+ if (dst + dataPtr->initLen > dstEnd) {
*srcReadPtr = 0;
*dstWrotePtr = 0;
return TCL_CONVERT_NOSPACE;
@@ -2473,9 +2508,6 @@ EscapeFromUtfProc(clientData, src, srcLen, flags, statePtr, dst, dstLen,
memcpy((VOID *) dst, (VOID *) dataPtr->init,
(size_t) dataPtr->initLen);
dst += dataPtr->initLen;
- memcpy((VOID *) dst, (VOID *) dataPtr->subTables[0].sequence,
- (size_t) len);
- dst += len;
} else {
state = (int) *statePtr;
}
@@ -2530,14 +2562,28 @@ EscapeFromUtfProc(clientData, src, srcLen, flags, statePtr, dst, dstLen,
tablePrefixBytes = tableDataPtr->prefixBytes;
tableFromUnicode = tableDataPtr->fromUnicode;
- subTablePtr = &dataPtr->subTables[state];
- if (dst + subTablePtr->sequenceLen > dstEnd) {
- result = TCL_CONVERT_NOSPACE;
- break;
+ /*
+ * The state variable has the value of oldState when word is 0.
+ * In this case, the escape sequense should not be copied to dst
+ * because the current character set is not changed.
+ */
+ if (state != oldState) {
+ subTablePtr = &dataPtr->subTables[state];
+ if ((dst + subTablePtr->sequenceLen) > dstEnd) {
+ /*
+ * If there is no space to write the escape sequence, the
+ * state variable must be changed to the value of oldState
+ * variable because this escape sequence must be written
+ * in the next conversion.
+ */
+ state = oldState;
+ result = TCL_CONVERT_NOSPACE;
+ break;
+ }
+ memcpy((VOID *) dst, (VOID *) subTablePtr->sequence,
+ (size_t) subTablePtr->sequenceLen);
+ dst += subTablePtr->sequenceLen;
}
- memcpy((VOID *) dst, (VOID *) subTablePtr->sequence,
- (size_t) subTablePtr->sequenceLen);
- dst += subTablePtr->sequenceLen;
}
if (tablePrefixBytes[(word >> 8)] != 0) {
@@ -2560,9 +2606,15 @@ EscapeFromUtfProc(clientData, src, srcLen, flags, statePtr, dst, dstLen,
}
if ((result == TCL_OK) && (flags & TCL_ENCODING_END)) {
- if (dst + dataPtr->finalLen > dstEnd) {
+ unsigned int len = dataPtr->subTables[0].sequenceLen;
+ if (dst + dataPtr->finalLen + (state?len:0) > dstEnd) {
result = TCL_CONVERT_NOSPACE;
} else {
+ if (state) {
+ memcpy((VOID *) dst, (VOID *) dataPtr->subTables[0].sequence,
+ (size_t) len);
+ dst += len;
+ }
memcpy((VOID *) dst, (VOID *) dataPtr->final,
(size_t) dataPtr->finalLen);
dst += dataPtr->finalLen;
diff --git a/tcl/generic/tclEnv.c b/tcl/generic/tclEnv.c
index 994bc292dbd..cc609bda6fe 100644
--- a/tcl/generic/tclEnv.c
+++ b/tcl/generic/tclEnv.c
@@ -20,28 +20,6 @@
TCL_DECLARE_MUTEX(envMutex) /* To serialize access to environ */
-/* CYGNUS LOCAL */
-#ifdef __CYGWIN32__
-
-/* On cygwin32, the environment is imported from the cygwin32 DLL. */
-
-__declspec(dllimport) extern char **__cygwin_environ;
-
-#define environ (__cygwin_environ)
-
-/* We need to use a special putenv function to handle PATH. */
-#ifndef USE_PUTENV
-#define USE_PUTENV
-#endif
-#define putenv TclCygwin32Putenv
-#endif
-/* END CYGNUS LOCAL */
-
-#ifdef TCL_THREADS
-
-static Tcl_Mutex envMutex; /* To serialize access to environ */
-#endif
-
static int cacheSize = 0; /* Number of env strings in environCache. */
static char **environCache = NULL;
/* Array containing all of the environment
@@ -68,18 +46,14 @@ char **environ = NULL;
*/
static char * EnvTraceProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, char *name1, char *name2,
- int flags));
+ Tcl_Interp *interp, CONST char *name1,
+ CONST char *name2, int flags));
static void ReplaceString _ANSI_ARGS_((CONST char *oldStr,
char *newStr));
void TclSetEnv _ANSI_ARGS_((CONST char *name,
CONST char *value));
void TclUnsetEnv _ANSI_ARGS_((CONST char *name));
-/* CYGNUS LOCAL */
-#ifdef __CYGWIN32__
-static void TclCygwin32Putenv _ANSI_ARGS_((CONST char *string));
-#endif
/*
*----------------------------------------------------------------------
@@ -200,7 +174,8 @@ TclSetEnv(name, value)
{
Tcl_DString envString;
int index, length, nameLength;
- char *p, *p2, *oldValue;
+ char *p, *oldValue;
+ CONST char *p2;
/*
* Figure out where the entry is going to go. If the name doesn't
@@ -218,12 +193,6 @@ TclSetEnv(name, value)
newEnviron = (char **) ckalloc((unsigned)
((length + 5) * sizeof(char *)));
-
- /* CYGNUS LOCAL: Added to avoid an error from Purify,
- although I don't personally see where the error would
- occur--ian. */
- memset((VOID *) newEnviron, 0, (length+5) * sizeof(char *));
-
memcpy((VOID *) newEnviron, (VOID *) environ,
length*sizeof(char *));
if (environSize != 0) {
@@ -231,6 +200,12 @@ TclSetEnv(name, value)
}
environ = newEnviron;
environSize = length + 5;
+#if defined(__APPLE__) && defined(__DYNAMIC__)
+ {
+ char ***e = _NSGetEnviron();
+ *e = environ;
+ }
+#endif
}
index = length;
environ[index + 1] = NULL;
@@ -238,7 +213,7 @@ TclSetEnv(name, value)
oldValue = NULL;
nameLength = strlen(name);
} else {
- char *env;
+ CONST char *env;
/*
* Compare the new value to the existing value. If they're
@@ -300,9 +275,23 @@ TclSetEnv(name, value)
if ((index != -1) && (environ[index] == p)) {
ReplaceString(oldValue, p);
+#ifdef HAVE_PUTENV_THAT_COPIES
+ } else {
+ /* This putenv() copies instead of taking ownership */
+ ckfree(p);
+#endif
}
Tcl_MutexUnlock(&envMutex);
+
+ if (!strcmp(name, "HOME")) {
+ /*
+ * If the user's home directory has changed, we must invalidate
+ * the filesystem cache, because '~' expansions will now be
+ * incorrect.
+ */
+ Tcl_FSMountsChanged(NULL);
+ }
}
/*
@@ -335,7 +324,8 @@ Tcl_PutEnv(string)
{
Tcl_DString nameString;
int nameLength;
- char *name, *value;
+ CONST char *name;
+ char *value;
if (string == NULL) {
return 0;
@@ -388,7 +378,7 @@ TclUnsetEnv(name)
CONST char *name; /* Name of variable to remove (UTF-8). */
{
char *oldValue;
- unsigned int length;
+ int length;
int index;
#ifdef USE_PUTENV
Tcl_DString envString;
@@ -475,7 +465,7 @@ TclUnsetEnv(name)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
TclGetEnv(name, valuePtr)
CONST char *name; /* Name of environment variable to find
* (UTF-8). */
@@ -484,7 +474,7 @@ TclGetEnv(name, valuePtr)
* stored. */
{
int length, index;
- char *result;
+ CONST char *result;
Tcl_MutexLock(&envMutex);
index = TclpFindVariable(name, &length);
@@ -535,8 +525,8 @@ EnvTraceProc(clientData, interp, name1, name2, flags)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Interpreter whose "env" variable is
* being modified. */
- char *name1; /* Better be "env". */
- char *name2; /* Name of variable being modified, or NULL
+ CONST char *name1; /* Better be "env". */
+ CONST char *name2; /* Name of variable being modified, or NULL
* if whole array is being deleted (UTF-8). */
int flags; /* Indicates what's happening. */
{
@@ -562,7 +552,7 @@ EnvTraceProc(clientData, interp, name1, name2, flags)
*/
if (flags & TCL_TRACE_WRITES) {
- char *value;
+ CONST char *value;
value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);
TclSetEnv(name2, value);
@@ -574,7 +564,7 @@ EnvTraceProc(clientData, interp, name1, name2, flags)
if (flags & TCL_TRACE_READS) {
Tcl_DString valueString;
- char *value;
+ CONST char *value;
value = TclGetEnv(name2, &valueString);
if (value == NULL) {
@@ -665,7 +655,7 @@ ReplaceString(oldStr, newStr)
ckfree((char *) environCache);
}
environCache = newCache;
- environCache[cacheSize] = (char *) newStr;
+ environCache[cacheSize] = newStr;
environCache[cacheSize+1] = NULL;
cacheSize += 5;
}
@@ -709,86 +699,3 @@ TclFinalizeEnvironment()
#endif
}
}
-
-/* CYGNUS LOCAL */
-#ifdef __CYGWIN32__
-
-/* When using cygwin32, when an environment variable changes, we need
- to synch with both the cygwin32 environment (in case the
- application C code calls fork) and the Windows environment (in case
- the application TCL code calls exec, which calls the Windows
- CreateProcess function). */
-
-static void
-TclCygwin32Putenv(str)
- const char *str;
-{
- char *name, *value;
-
- /* Get the name and value, so that we can change the environment
- variable for Windows. */
- name = (char *) alloca (strlen (str) + 1);
- strcpy (name, str);
- for (value = name; *value != '=' && *value != '\0'; ++value)
- ;
- if (*value == '\0')
- {
- /* Can't happen. */
- return;
- }
- *value = '\0';
- ++value;
- if (*value == '\0')
- value = NULL;
-
- /* Set the cygwin32 environment variable. */
-#undef putenv
- if (value == NULL)
- unsetenv (name);
- else
- putenv(str);
-
- /* Before changing the environment variable in Windows, if this is
- PATH, we need to convert the value back to a Windows style path.
-
- FIXME: The calling program may now it is running under windows,
- and may have set the path to a Windows path, or, worse, appended
- or prepended a Windows path to PATH. */
- if (strcmp (name, "PATH") != 0)
- {
- /* If this is Path, eliminate any PATH variable, to prevent any
- confusion. */
- if (strcmp (name, "Path") == 0)
- {
- SetEnvironmentVariable ("PATH", (char *) NULL);
- unsetenv ("PATH");
- }
-
- SetEnvironmentVariable (name, value);
- }
- else
- {
- char *buf;
-
- /* Eliminate any Path variable, to prevent any confusion. */
- SetEnvironmentVariable ("Path", (char *) NULL);
- unsetenv ("Path");
-
- if (value == NULL)
- buf = NULL;
- else
- {
- int size;
-
- size = cygwin32_posix_to_win32_path_list_buf_size (value);
- buf = (char *) alloca (size + 1);
- cygwin32_posix_to_win32_path_list (value, buf);
- }
-
- SetEnvironmentVariable (name, buf);
- }
-}
-
-#endif /* __CYGWIN32__ */
-/* END CYGNUS LOCAL */
-
diff --git a/tcl/generic/tclEvent.c b/tcl/generic/tclEvent.c
index c4b16abdfce..d3351856489 100644
--- a/tcl/generic/tclEvent.c
+++ b/tcl/generic/tclEvent.c
@@ -99,6 +99,11 @@ typedef struct ThreadSpecificData {
static Tcl_ThreadDataKey dataKey;
/*
+ * Common string for the library path for sharing across threads.
+ */
+char *tclLibraryPathStr;
+
+/*
* Prototypes for procedures referenced only in this file:
*/
@@ -106,8 +111,8 @@ static void BgErrorDeleteProc _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp));
static void HandleBgErrors _ANSI_ARGS_((ClientData clientData));
static char * VwaitVarProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, char *name1, char *name2,
- int flags));
+ Tcl_Interp *interp, CONST char *name1,
+ CONST char *name2, int flags));
/*
*----------------------------------------------------------------------
@@ -135,7 +140,7 @@ Tcl_BackgroundError(interp)
* occurred. */
{
BgError *errPtr;
- char *errResult, *varValue;
+ CONST char *errResult, *varValue;
ErrAssocData *assocPtr;
int length;
@@ -217,7 +222,7 @@ HandleBgErrors(clientData)
ClientData clientData; /* Pointer to ErrAssocData structure. */
{
Tcl_Interp *interp;
- char *argv[2];
+ CONST char *argv[2];
int code;
BgError *errPtr;
ErrAssocData *assocPtr = (ErrAssocData *) clientData;
@@ -285,7 +290,7 @@ HandleBgErrors(clientData)
int len;
string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &len);
- if (strcmp(string, "\"bgerror\" is an invalid command name or ambiguous abbreviation") == 0) {
+ if (Tcl_FindCommand(interp, "bgerror", NULL, TCL_GLOBAL_ONLY) == NULL) {
Tcl_WriteChars(errChannel, assocPtr->firstBgPtr->errorInfo, -1);
Tcl_WriteChars(errChannel, "\n", -1);
} else {
@@ -596,6 +601,12 @@ TclSetLibraryPath(pathPtr)
Tcl_DecrRefCount(tsdPtr->tclLibraryPath);
}
tsdPtr->tclLibraryPath = pathPtr;
+
+ /*
+ * No mutex locking is needed here as up the stack we're within
+ * TclpInitLock().
+ */
+ tclLibraryPathStr = Tcl_GetStringFromObj(pathPtr, NULL);
}
/*
@@ -619,6 +630,17 @@ Tcl_Obj *
TclGetLibraryPath()
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ if (tsdPtr->tclLibraryPath == NULL) {
+ /*
+ * Grab the shared string and place it into a new thread specific
+ * Tcl_Obj.
+ */
+ tsdPtr->tclLibraryPath = Tcl_NewStringObj(tclLibraryPathStr, -1);
+
+ /* take ownership */
+ Tcl_IncrRefCount(tsdPtr->tclLibraryPath);
+ }
return tsdPtr->tclLibraryPath;
}
@@ -744,10 +766,11 @@ Tcl_Finalize()
ThreadSpecificData *tsdPtr;
TclpInitLock();
- tsdPtr = TCL_TSD_INIT(&dataKey);
if (subsystemsInitialized != 0) {
subsystemsInitialized = 0;
+ tsdPtr = TCL_TSD_INIT(&dataKey);
+
/*
* Invoke exit handlers first.
*/
@@ -772,15 +795,6 @@ Tcl_Finalize()
Tcl_MutexUnlock(&exitMutex);
/*
- * Clean up the library path now, before we invalidate thread-local
- * storage.
- */
- if (tsdPtr->tclLibraryPath != NULL) {
- Tcl_DecrRefCount(tsdPtr->tclLibraryPath);
- tsdPtr->tclLibraryPath = NULL;
- }
-
- /*
* Clean up after the current thread now, after exit handlers.
* In particular, the testexithandler command sets up something
* that writes to standard output, which gets closed.
@@ -822,13 +836,12 @@ Tcl_Finalize()
TclFinalizeSynchronization();
- /*
- * We defer unloading of packages until very late
- * to avoid memory access issues. Both exit callbacks and
- * synchronization variables may be stored in packages.
+ /**
+ * Finalizing the filesystem must come after anything which
+ * might conceivably interact with the 'Tcl_FS' API. This
+ * will also unload any extensions which have been loaded.
*/
-
- TclFinalizeLoad();
+ TclFinalizeFilesystem();
/*
* There shouldn't be any malloc'ed memory after this.
@@ -870,6 +883,17 @@ Tcl_FinalizeThread()
*/
tsdPtr->inExit = 1;
+
+ /*
+ * Clean up the library path now, before we invalidate thread-local
+ * storage or calling thread exit handlers.
+ */
+
+ if (tsdPtr->tclLibraryPath != NULL) {
+ Tcl_DecrRefCount(tsdPtr->tclLibraryPath);
+ tsdPtr->tclLibraryPath = NULL;
+ }
+
for (exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL;
exitPtr = tsdPtr->firstExitPtr) {
/*
@@ -884,6 +908,7 @@ Tcl_FinalizeThread()
}
TclFinalizeIOSubsystem();
TclFinalizeNotifier();
+ TclFinalizeAsync();
/*
* Blow away all thread local storage blocks.
@@ -912,8 +937,13 @@ Tcl_FinalizeThread()
int
TclInExit()
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- return tsdPtr->inExit;
+ ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
+ TclThreadDataKeyGet(&dataKey);
+ if (tsdPtr == NULL) {
+ return inFinalize;
+ } else {
+ return tsdPtr->inExit;
+ }
}
/*
@@ -982,8 +1012,8 @@ static char *
VwaitVarProc(clientData, interp, name1, name2, flags)
ClientData clientData; /* Pointer to integer to set to 1. */
Tcl_Interp *interp; /* Interpreter containing variable. */
- char *name1; /* Name of variable. */
- char *name2; /* Second part of variable name. */
+ CONST char *name1; /* Name of variable. */
+ CONST char *name2; /* Second part of variable name. */
int flags; /* Information about what happened. */
{
int *donePtr = (int *) clientData;
@@ -1019,7 +1049,7 @@ Tcl_UpdateObjCmd(clientData, interp, objc, objv)
{
int optionIndex;
int flags = 0; /* Initialized to avoid compiler warning. */
- static char *updateOptions[] = {"idletasks", (char *) NULL};
+ static CONST char *updateOptions[] = {"idletasks", (char *) NULL};
enum updateOptions {REGEXP_IDLETASKS};
if (objc == 1) {
@@ -1055,4 +1085,3 @@ Tcl_UpdateObjCmd(clientData, interp, objc, objv)
Tcl_ResetResult(interp);
return TCL_OK;
}
-
diff --git a/tcl/generic/tclExecute.c b/tcl/generic/tclExecute.c
index 95c0c9e04d5..d86a7c1e9c5 100644
--- a/tcl/generic/tclExecute.c
+++ b/tcl/generic/tclExecute.c
@@ -5,6 +5,8 @@
* commands.
*
* Copyright (c) 1996-1997 Sun Microsystems, Inc.
+ * Copyright (c) 1998-2000 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -15,13 +17,8 @@
#include "tclInt.h"
#include "tclCompile.h"
-#ifdef NO_FLOAT_H
-# include "../compat/float.h"
-#else
-# include <float.h>
-#endif
#ifndef TCL_NO_MATH
-#include "tclMath.h"
+# include "tclMath.h"
#endif
/*
@@ -31,18 +28,40 @@
*/
#ifndef TCL_GENERIC_ONLY
-#include "tclPort.h"
-#else
-#define NO_ERRNO_H
-#endif
+# include "tclPort.h"
+#else /* TCL_GENERIC_ONLY */
+# ifndef NO_FLOAT_H
+# include <float.h>
+# else /* NO_FLOAT_H */
+# ifndef NO_VALUES_H
+# include <values.h>
+# endif /* !NO_VALUES_H */
+# endif /* !NO_FLOAT_H */
+# define NO_ERRNO_H
+#endif /* !TCL_GENERIC_ONLY */
#ifdef NO_ERRNO_H
int errno;
-#define EDOM 33
-#define ERANGE 34
+# define EDOM 33
+# define ERANGE 34
#endif
/*
+ * Need DBL_MAX for IS_INF() macro...
+ */
+#ifndef DBL_MAX
+# ifdef MAXDOUBLE
+# define DBL_MAX MAXDOUBLE
+# else /* !MAXDOUBLE */
+/*
+ * This value is from the Solaris headers, but doubles seem to be the
+ * same size everywhere. Long doubles aren't, but we don't use those.
+ */
+# define DBL_MAX 1.79769313486231570e+308
+# endif /* MAXDOUBLE */
+#endif /* !DBL_MAX */
+
+/*
* Boolean flag indicating whether the Tcl bytecode interpreter has been
* initialized.
*/
@@ -50,6 +69,7 @@ int errno;
static int execInitialized = 0;
TCL_DECLARE_MUTEX(execMutex)
+#ifdef TCL_COMPILE_DEBUG
/*
* Variable that controls whether execution tracing is enabled and, if so,
* what level of tracing is desired:
@@ -61,32 +81,6 @@ TCL_DECLARE_MUTEX(execMutex)
*/
int tclTraceExec = 0;
-
-typedef struct ThreadSpecificData {
- /*
- * The following global variable is use to signal matherr that Tcl
- * is responsible for the arithmetic, so errors can be handled in a
- * fashion appropriate for Tcl. Zero means no Tcl math is in
- * progress; non-zero means Tcl is doing math.
- */
-
- int mathInProgress;
-
-} ThreadSpecificData;
-
-static Tcl_ThreadDataKey dataKey;
-
-/*
- * The variable below serves no useful purpose except to generate
- * a reference to matherr, so that the Tcl version of matherr is
- * linked in rather than the system version. Without this reference
- * the need for matherr won't be discovered during linking until after
- * libtcl.a has been processed, so Tcl's version won't be used.
- */
-
-#ifdef NEED_MATHERR
-extern int matherr();
-int (*tclMatherrPtr)() = matherr;
#endif
/*
@@ -98,9 +92,10 @@ int (*tclMatherrPtr)() = matherr;
static char *operatorStrings[] = {
"||", "&&", "|", "^", "&", "==", "!=", "<", ">", "<=", ">=", "<<", ">>",
"+", "-", "*", "/", "%", "+", "-", "~", "!",
- "BUILTIN FUNCTION", "FUNCTION"
+ "BUILTIN FUNCTION", "FUNCTION",
+ "", "", "", "", "", "", "", "", "eq", "ne",
};
-
+
/*
* Mapping from Tcl result codes to strings; used for error and debugging
* messages.
@@ -113,26 +108,82 @@ static char *resultStrings[] = {
#endif
/*
+ * These are used by evalstats to monitor object usage in Tcl.
+ */
+
+#ifdef TCL_COMPILE_STATS
+long tclObjsAlloced = 0;
+long tclObjsFreed = 0;
+#define TCL_MAX_SHARED_OBJ_STATS 5
+long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS] = { 0, 0, 0, 0, 0 };
+#endif /* TCL_COMPILE_STATS */
+
+/*
* Macros for testing floating-point values for certain special cases. Test
* for not-a-number by comparing a value against itself; test for infinity
* by comparing against the largest floating-point value.
*/
#define IS_NAN(v) ((v) != (v))
-#ifdef DBL_MAX
-# define IS_INF(v) (((v) > DBL_MAX) || ((v) < -DBL_MAX))
-#else
-# define IS_INF(v) 0
-#endif
+#define IS_INF(v) (((v) > DBL_MAX) || ((v) < -DBL_MAX))
/*
- * Macro to adjust the program counter and restart the instruction execution
- * loop after each instruction is executed.
+ * The new macro for ending an instruction; note that a
+ * reasonable C-optimiser will resolve all branches
+ * at compile time. (result) is always a constant; the macro
+ * NEXT_INST_F handles constant (nCleanup), NEXT_INST_V is
+ * resolved at runtime for variable (nCleanup).
+ *
+ * ARGUMENTS:
+ * pcAdjustment: how much to increment pc
+ * nCleanup: how many objects to remove from the stack
+ * result: 0 indicates no object should be pushed on the
+ * stack; otherwise, push objResultPtr. If (result < 0),
+ * objResultPtr already has the correct reference count.
*/
-#define ADJUST_PC(instBytes) \
- pc += (instBytes); \
- continue
+#define NEXT_INST_F(pcAdjustment, nCleanup, result) \
+ if (nCleanup == 0) {\
+ if (result != 0) {\
+ if ((result) > 0) {\
+ PUSH_OBJECT(objResultPtr);\
+ } else {\
+ stackPtr[++stackTop] = objResultPtr;\
+ }\
+ } \
+ pc += (pcAdjustment);\
+ goto cleanup0;\
+ } else if (result != 0) {\
+ if ((result) > 0) {\
+ Tcl_IncrRefCount(objResultPtr);\
+ }\
+ pc += (pcAdjustment);\
+ switch (nCleanup) {\
+ case 1: goto cleanup1_pushObjResultPtr;\
+ case 2: goto cleanup2_pushObjResultPtr;\
+ default: panic("ERROR: bad usage of macro NEXT_INST_F");\
+ }\
+ } else {\
+ pc += (pcAdjustment);\
+ switch (nCleanup) {\
+ case 1: goto cleanup1;\
+ case 2: goto cleanup2;\
+ default: panic("ERROR: bad usage of macro NEXT_INST_F");\
+ }\
+ }
+
+#define NEXT_INST_V(pcAdjustment, nCleanup, result) \
+ pc += (pcAdjustment);\
+ cleanup = (nCleanup);\
+ if (result) {\
+ if ((result) > 0) {\
+ Tcl_IncrRefCount(objResultPtr);\
+ }\
+ goto cleanupV_pushObjResultPtr;\
+ } else {\
+ goto cleanupV;\
+ }
+
/*
* Macros used to cache often-referenced Tcl evaluation stack information
@@ -149,6 +200,7 @@ static char *resultStrings[] = {
#define DECACHE_STACK_INFO() \
eePtr->stackTop = stackTop
+
/*
* Macros used to access items on the Tcl evaluation stack. PUSH_OBJECT
* increments the object's ref count since it makes the stack have another
@@ -177,40 +229,130 @@ static char *resultStrings[] = {
*/
#ifdef TCL_COMPILE_DEBUG
-#define TRACE(a) \
+# define TRACE(a) \
if (traceInstructions) { \
fprintf(stdout, "%2d: %2d (%u) %s ", iPtr->numLevels, stackTop, \
(unsigned int)(pc - codePtr->codeStart), \
GetOpcodeName(pc)); \
printf a; \
}
-#define TRACE_WITH_OBJ(a, objPtr) \
+# define TRACE_APPEND(a) \
+ if (traceInstructions) { \
+ printf a; \
+ }
+# define TRACE_WITH_OBJ(a, objPtr) \
if (traceInstructions) { \
fprintf(stdout, "%2d: %2d (%u) %s ", iPtr->numLevels, stackTop, \
(unsigned int)(pc - codePtr->codeStart), \
GetOpcodeName(pc)); \
printf a; \
- TclPrintObject(stdout, (objPtr), 30); \
+ TclPrintObject(stdout, objPtr, 30); \
fprintf(stdout, "\n"); \
}
-#define O2S(objPtr) \
- Tcl_GetString(objPtr)
-#else
-#define TRACE(a)
-#define TRACE_WITH_OBJ(a, objPtr)
-#define O2S(objPtr)
+# define O2S(objPtr) \
+ (objPtr ? TclGetString(objPtr) : "")
+#else /* !TCL_COMPILE_DEBUG */
+# define TRACE(a)
+# define TRACE_APPEND(a)
+# define TRACE_WITH_OBJ(a, objPtr)
+# define O2S(objPtr)
#endif /* TCL_COMPILE_DEBUG */
+
+/*
+ * Most of the code to support working with wide values is factored
+ * out here because it greatly reduces the number of conditionals
+ * through the rest of the file. Note that this needs to be
+ * conditional because we do not want to alter Tcl's behaviour on
+ * native-64bit platforms...
+ */
+
+#ifndef TCL_WIDE_INT_IS_LONG
+#define W0 Tcl_LongAsWide(0)
+
+/*
+ * Macro to read a string containing either a wide or an int and
+ * decide which it is while decoding it at the same time. This
+ * enforces the policy that integer constants between LONG_MIN and
+ * LONG_MAX (inclusive) are represented by normal longs, and integer
+ * constants outside that range are represented by wide ints.
+ *
+ * GET_WIDE_OR_INT is the same as REQUIRE_WIDE_OR_INT except it never
+ * generates an error message.
+ */
+#define REQUIRE_WIDE_OR_INT(resultVar, objPtr, longVar, wideVar) \
+ (resultVar) = Tcl_GetWideIntFromObj(interp, (objPtr), &(wideVar)); \
+ if ((resultVar) == TCL_OK && (wideVar) >= Tcl_LongAsWide(LONG_MIN) \
+ && (wideVar) <= Tcl_LongAsWide(LONG_MAX)) { \
+ (objPtr)->typePtr = &tclIntType; \
+ (objPtr)->internalRep.longValue = (longVar) \
+ = Tcl_WideAsLong(wideVar); \
+ }
+#define GET_WIDE_OR_INT(resultVar, objPtr, longVar, wideVar) \
+ (resultVar) = Tcl_GetWideIntFromObj((Tcl_Interp *) NULL, (objPtr), \
+ &(wideVar)); \
+ if ((resultVar) == TCL_OK && (wideVar) >= Tcl_LongAsWide(LONG_MIN) \
+ && (wideVar) <= Tcl_LongAsWide(LONG_MAX)) { \
+ (objPtr)->typePtr = &tclIntType; \
+ (objPtr)->internalRep.longValue = (longVar) \
+ = Tcl_WideAsLong(wideVar); \
+ }
+#define IS_INTEGER_TYPE(typePtr) \
+ ((typePtr) == &tclIntType || (typePtr) == &tclWideIntType)
+/*
+ * Extract a double value from a general numeric object.
+ */
+#define GET_DOUBLE_VALUE(doubleVar, objPtr, typePtr) \
+ if ((typePtr) == &tclIntType) { \
+ (doubleVar) = (double) (objPtr)->internalRep.longValue; \
+ } else if ((typePtr) == &tclWideIntType) { \
+ (doubleVar) = Tcl_WideAsDouble((objPtr)->internalRep.wideValue);\
+ } else { \
+ (doubleVar) = (objPtr)->internalRep.doubleValue; \
+ }
+/*
+ * Combined with REQUIRE_WIDE_OR_INT, this gets a long value from
+ * an obj.
+ */
+#define FORCE_LONG(objPtr, longVar, wideVar) \
+ if ((objPtr)->typePtr == &tclWideIntType) { \
+ (longVar) = Tcl_WideAsLong(wideVar); \
+ }
+/*
+ * For tracing that uses wide values.
+ */
+#define LLTRACE(a) TRACE(a)
+#define LLTRACE_WITH_OBJ(a,b) TRACE_WITH_OBJ(a,b)
+#define LLD "%" TCL_LL_MODIFIER "d"
+#else /* TCL_WIDE_INT_IS_LONG */
+/*
+ * Versions of the above that do not use wide values.
+ */
+#define REQUIRE_WIDE_OR_INT(resultVar, objPtr, longVar, wideVar) \
+ (resultVar) = Tcl_GetLongFromObj(interp, (objPtr), &(longVar));
+#define GET_WIDE_OR_INT(resultVar, objPtr, longVar, wideVar) \
+ (resultVar) = Tcl_GetLongFromObj((Tcl_Interp *) NULL, (objPtr), \
+ &(longVar));
+#define IS_INTEGER_TYPE(typePtr) ((typePtr) == &tclIntType)
+#define GET_DOUBLE_VALUE(doubleVar, objPtr, typePtr) \
+ if ((typePtr) == &tclIntType) { \
+ (doubleVar) = (double) (objPtr)->internalRep.longValue; \
+ } else { \
+ (doubleVar) = (objPtr)->internalRep.doubleValue; \
+ }
+#define FORCE_LONG(objPtr, longVar, wideVar)
+#define LLTRACE(a)
+#define LLTRACE_WITH_OBJ(a,b)
+#endif /* TCL_WIDE_INT_IS_LONG */
+#define IS_NUMERIC_TYPE(typePtr) \
+ (IS_INTEGER_TYPE(typePtr) || (typePtr) == &tclDoubleType)
+
/*
* Declarations for local procedures to this file:
*/
-static void CallTraceProcedure _ANSI_ARGS_((Tcl_Interp *interp,
- Trace *tracePtr, Command *cmdPtr,
- char *command, int numChars,
- int objc, Tcl_Obj *objv[]));
-static void DupCmdNameInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr,
- Tcl_Obj *copyPtr));
+static int TclExecuteByteCode _ANSI_ARGS_((Tcl_Interp *interp,
+ ByteCode *codePtr));
static int ExprAbsFunc _ANSI_ARGS_((Tcl_Interp *interp,
ExecEnv *eePtr, ClientData clientData));
static int ExprBinaryFunc _ANSI_ARGS_((Tcl_Interp *interp,
@@ -229,12 +371,14 @@ static int ExprSrandFunc _ANSI_ARGS_((Tcl_Interp *interp,
ExecEnv *eePtr, ClientData clientData));
static int ExprUnaryFunc _ANSI_ARGS_((Tcl_Interp *interp,
ExecEnv *eePtr, ClientData clientData));
+#ifndef TCL_WIDE_INT_IS_LONG
+static int ExprWideFunc _ANSI_ARGS_((Tcl_Interp *interp,
+ ExecEnv *eePtr, ClientData clientData));
+#endif
#ifdef TCL_COMPILE_STATS
static int EvalStatsCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
#endif
-static void FreeCmdNameInternalRep _ANSI_ARGS_((
- Tcl_Obj *objPtr));
#ifdef TCL_COMPILE_DEBUG
static char * GetOpcodeName _ANSI_ARGS_((unsigned char *pc));
#endif
@@ -250,15 +394,10 @@ static void InitByteCodeExecution _ANSI_ARGS_((
Tcl_Interp *interp));
#ifdef TCL_COMPILE_DEBUG
static void PrintByteCodeInfo _ANSI_ARGS_((ByteCode *codePtr));
-#endif
-static int SetCmdNameFromAny _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Obj *objPtr));
-#ifdef TCL_COMPILE_DEBUG
static char * StringForResultCode _ANSI_ARGS_((int result));
static void ValidatePcAndStackTop _ANSI_ARGS_((
ByteCode *codePtr, unsigned char *pc,
- int stackTop, int stackLowerBound,
- int stackUpperBound));
+ int stackTop, int stackLowerBound));
#endif
static int VerifyExprObjType _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Obj *objPtr));
@@ -269,7 +408,7 @@ static int VerifyExprObjType _ANSI_ARGS_((Tcl_Interp *interp,
* operand byte.
*/
-BuiltinFunc builtinFuncTable[] = {
+BuiltinFunc tclBuiltinFuncTable[] = {
#ifndef TCL_NO_MATH
{"acos", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) acos},
{"asin", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) asin},
@@ -297,24 +436,13 @@ BuiltinFunc builtinFuncTable[] = {
{"rand", 0, {TCL_EITHER}, ExprRandFunc, 0}, /* NOTE: rand takes no args. */
{"round", 1, {TCL_EITHER}, ExprRoundFunc, 0},
{"srand", 1, {TCL_INT}, ExprSrandFunc, 0},
+#ifdef TCL_WIDE_INT_IS_LONG
+ {"wide", 1, {TCL_EITHER}, ExprIntFunc, 0},
+#else
+ {"wide", 1, {TCL_EITHER}, ExprWideFunc, 0},
+#endif /* TCL_WIDE_INT_IS_LONG */
{0},
};
-
-/*
- * The structure below defines the command name Tcl object type by means of
- * procedures that can be invoked by generic object code. Objects of this
- * type cache the Command pointer that results from looking up command names
- * in the command hashtable. Such objects appear as the zeroth ("command
- * name") argument in a Tcl command.
- */
-
-Tcl_ObjType tclCmdNameType = {
- "cmdName", /* name */
- FreeCmdNameInternalRep, /* freeIntRepProc */
- DupCmdNameInternalRep, /* dupIntRepProc */
- (Tcl_UpdateStringProc *) NULL, /* updateStringProc */
- SetCmdNameFromAny /* setFromAnyProc */
-};
/*
*----------------------------------------------------------------------
@@ -331,9 +459,8 @@ Tcl_ObjType tclCmdNameType = {
* This procedure initializes the array of instruction names. If
* compiling with the TCL_COMPILE_STATS flag, it initializes the
* array that counts the executions of each instruction and it
- * creates the "evalstats" command. It also registers the command name
- * Tcl_ObjType. It also establishes the link between the Tcl
- * "tcl_traceExec" and C "tclTraceExec" variables.
+ * creates the "evalstats" command. It also establishes the link
+ * between the Tcl "tcl_traceExec" and C "tclTraceExec" variables.
*
*----------------------------------------------------------------------
*/
@@ -344,12 +471,12 @@ InitByteCodeExecution(interp)
* "tcl_traceExec" is linked to control
* instruction tracing. */
{
- Tcl_RegisterObjType(&tclCmdNameType);
+#ifdef TCL_COMPILE_DEBUG
if (Tcl_LinkVar(interp, "tcl_traceExec", (char *) &tclTraceExec,
TCL_LINK_INT) != TCL_OK) {
panic("InitByteCodeExecution: can't create link for tcl_traceExec variable");
}
-
+#endif
#ifdef TCL_COMPILE_STATS
Tcl_CreateCommand(interp, "evalstats", EvalStatsCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
@@ -386,11 +513,28 @@ TclCreateExecEnv(interp)
* environment is being created. */
{
ExecEnv *eePtr = (ExecEnv *) ckalloc(sizeof(ExecEnv));
+ Tcl_Obj **stackPtr;
+
+ stackPtr = (Tcl_Obj **)
+ ckalloc((size_t) (TCL_STACK_INITIAL_SIZE * sizeof(Tcl_Obj *)));
+
+ /*
+ * Use the bottom pointer to keep a reference count; the
+ * execution environment holds a reference.
+ */
+
+ stackPtr++;
+ eePtr->stackPtr = stackPtr;
+ stackPtr[-1] = (Tcl_Obj *) ((char *) 1);
- eePtr->stackPtr = (Tcl_Obj **)
- ckalloc((unsigned) (TCL_STACK_INITIAL_SIZE * sizeof(Tcl_Obj *)));
eePtr->stackTop = -1;
- eePtr->stackEnd = (TCL_STACK_INITIAL_SIZE - 1);
+ eePtr->stackEnd = (TCL_STACK_INITIAL_SIZE - 2);
+
+ eePtr->errorInfo = Tcl_NewStringObj("::errorInfo", -1);
+ Tcl_IncrRefCount(eePtr->errorInfo);
+
+ eePtr->errorCode = Tcl_NewStringObj("::errorCode", -1);
+ Tcl_IncrRefCount(eePtr->errorCode);
Tcl_MutexLock(&execMutex);
if (!execInitialized) {
@@ -425,7 +569,13 @@ void
TclDeleteExecEnv(eePtr)
ExecEnv *eePtr; /* Execution environment to free. */
{
- ckfree((char *) eePtr->stackPtr);
+ if (eePtr->stackPtr[-1] == (Tcl_Obj *) ((char *) 1)) {
+ ckfree((char *) (eePtr->stackPtr-1));
+ } else {
+ panic("ERROR: freeing an execEnv whose stack is still in use.\n");
+ }
+ TclDecrRefCount(eePtr->errorInfo);
+ TclDecrRefCount(eePtr->errorCode);
ckfree((char *) eePtr);
}
@@ -487,17 +637,416 @@ GrowEvaluationStack(eePtr)
int currBytes = currElems * sizeof(Tcl_Obj *);
int newBytes = 2*currBytes;
Tcl_Obj **newStackPtr = (Tcl_Obj **) ckalloc((unsigned) newBytes);
+ Tcl_Obj **oldStackPtr = eePtr->stackPtr;
+
+ /*
+ * We keep the stack reference count as a (char *), as that
+ * works nicely as a portable pointer-sized counter.
+ */
+
+ char *refCount = (char *) oldStackPtr[-1];
/*
* Copy the existing stack items to the new stack space, free the old
- * storage if appropriate, and mark new space as malloc'ed.
+ * storage if appropriate, and record the refCount of the new stack
+ * held by the environment.
*/
- memcpy((VOID *) newStackPtr, (VOID *) eePtr->stackPtr,
+ newStackPtr++;
+ memcpy((VOID *) newStackPtr, (VOID *) oldStackPtr,
(size_t) currBytes);
- ckfree((char *) eePtr->stackPtr);
+
+ if (refCount == (char *) 1) {
+ ckfree((VOID *) (oldStackPtr-1));
+ } else {
+ /*
+ * Remove the reference corresponding to the
+ * environment pointer.
+ */
+
+ oldStackPtr[-1] = (Tcl_Obj *) (refCount-1);
+ }
+
eePtr->stackPtr = newStackPtr;
- eePtr->stackEnd = (newElems - 1); /* i.e. index of last usable item */
+ eePtr->stackEnd = (newElems - 2); /* index of last usable item */
+ newStackPtr[-1] = (Tcl_Obj *) ((char *) 1);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Tcl_ExprObj --
+ *
+ * Evaluate an expression in a Tcl_Obj.
+ *
+ * Results:
+ * A standard Tcl object result. If the result is other than TCL_OK,
+ * then the interpreter's result contains an error message. If the
+ * result is TCL_OK, then a pointer to the expression's result value
+ * object is stored in resultPtrPtr. In that case, the object's ref
+ * count is incremented to reflect the reference returned to the
+ * caller; the caller is then responsible for the resulting object
+ * and must, for example, decrement the ref count when it is finished
+ * with the object.
+ *
+ * Side effects:
+ * Any side effects caused by subcommands in the expression, if any.
+ * The interpreter result is not modified unless there is an error.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Tcl_ExprObj(interp, objPtr, resultPtrPtr)
+ Tcl_Interp *interp; /* Context in which to evaluate the
+ * expression. */
+ register Tcl_Obj *objPtr; /* Points to Tcl object containing
+ * expression to evaluate. */
+ Tcl_Obj **resultPtrPtr; /* Where the Tcl_Obj* that is the expression
+ * result is stored if no errors occur. */
+{
+ Interp *iPtr = (Interp *) interp;
+ CompileEnv compEnv; /* Compilation environment structure
+ * allocated in frame. */
+ LiteralTable *localTablePtr = &(compEnv.localLitTable);
+ register ByteCode *codePtr = NULL;
+ /* Tcl Internal type of bytecode.
+ * Initialized to avoid compiler warning. */
+ AuxData *auxDataPtr;
+ LiteralEntry *entryPtr;
+ Tcl_Obj *saveObjPtr;
+ char *string;
+ int length, i, result;
+
+ /*
+ * First handle some common expressions specially.
+ */
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ if (length == 1) {
+ if (*string == '0') {
+ *resultPtrPtr = Tcl_NewLongObj(0);
+ Tcl_IncrRefCount(*resultPtrPtr);
+ return TCL_OK;
+ } else if (*string == '1') {
+ *resultPtrPtr = Tcl_NewLongObj(1);
+ Tcl_IncrRefCount(*resultPtrPtr);
+ return TCL_OK;
+ }
+ } else if ((length == 2) && (*string == '!')) {
+ if (*(string+1) == '0') {
+ *resultPtrPtr = Tcl_NewLongObj(1);
+ Tcl_IncrRefCount(*resultPtrPtr);
+ return TCL_OK;
+ } else if (*(string+1) == '1') {
+ *resultPtrPtr = Tcl_NewLongObj(0);
+ Tcl_IncrRefCount(*resultPtrPtr);
+ return TCL_OK;
+ }
+ }
+
+ /*
+ * Get the ByteCode from the object. If it exists, make sure it hasn't
+ * been invalidated by, e.g., someone redefining a command with a
+ * compile procedure (this might make the compiled code wrong). If
+ * necessary, convert the object to be a ByteCode object and compile it.
+ * Also, if the code was compiled in/for a different interpreter, we
+ * recompile it.
+ *
+ * Precompiled expressions, however, are immutable and therefore
+ * they are not recompiled, even if the epoch has changed.
+ *
+ */
+
+ if (objPtr->typePtr == &tclByteCodeType) {
+ codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
+ if (((Interp *) *codePtr->interpHandle != iPtr)
+ || (codePtr->compileEpoch != iPtr->compileEpoch)) {
+ if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) {
+ if ((Interp *) *codePtr->interpHandle != iPtr) {
+ panic("Tcl_ExprObj: compiled expression jumped interps");
+ }
+ codePtr->compileEpoch = iPtr->compileEpoch;
+ } else {
+ (*tclByteCodeType.freeIntRepProc)(objPtr);
+ objPtr->typePtr = (Tcl_ObjType *) NULL;
+ }
+ }
+ }
+ if (objPtr->typePtr != &tclByteCodeType) {
+ TclInitCompileEnv(interp, &compEnv, string, length);
+ result = TclCompileExpr(interp, string, length, &compEnv);
+
+ /*
+ * Free the compilation environment's literal table bucket array if
+ * it was dynamically allocated.
+ */
+
+ if (localTablePtr->buckets != localTablePtr->staticBuckets) {
+ ckfree((char *) localTablePtr->buckets);
+ }
+
+ if (result != TCL_OK) {
+ /*
+ * Compilation errors. Free storage allocated for compilation.
+ */
+
+#ifdef TCL_COMPILE_DEBUG
+ TclVerifyLocalLiteralTable(&compEnv);
+#endif /*TCL_COMPILE_DEBUG*/
+ entryPtr = compEnv.literalArrayPtr;
+ for (i = 0; i < compEnv.literalArrayNext; i++) {
+ TclReleaseLiteral(interp, entryPtr->objPtr);
+ entryPtr++;
+ }
+#ifdef TCL_COMPILE_DEBUG
+ TclVerifyGlobalLiteralTable(iPtr);
+#endif /*TCL_COMPILE_DEBUG*/
+
+ auxDataPtr = compEnv.auxDataArrayPtr;
+ for (i = 0; i < compEnv.auxDataArrayNext; i++) {
+ if (auxDataPtr->type->freeProc != NULL) {
+ auxDataPtr->type->freeProc(auxDataPtr->clientData);
+ }
+ auxDataPtr++;
+ }
+ TclFreeCompileEnv(&compEnv);
+ return result;
+ }
+
+ /*
+ * Successful compilation. If the expression yielded no
+ * instructions, push an zero object as the expression's result.
+ */
+
+ if (compEnv.codeNext == compEnv.codeStart) {
+ TclEmitPush(TclRegisterLiteral(&compEnv, "0", 1, /*onHeap*/ 0),
+ &compEnv);
+ }
+
+ /*
+ * Add a "done" instruction as the last instruction and change the
+ * object into a ByteCode object. Ownership of the literal objects
+ * and aux data items is given to the ByteCode object.
+ */
+
+ compEnv.numSrcBytes = iPtr->termOffset;
+ TclEmitOpcode(INST_DONE, &compEnv);
+ TclInitByteCodeObj(objPtr, &compEnv);
+ TclFreeCompileEnv(&compEnv);
+ codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
+#ifdef TCL_COMPILE_DEBUG
+ if (tclTraceCompile == 2) {
+ TclPrintByteCodeObj(interp, objPtr);
+ }
+#endif /* TCL_COMPILE_DEBUG */
+ }
+
+ /*
+ * Execute the expression after first saving the interpreter's result.
+ */
+
+ saveObjPtr = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(saveObjPtr);
+ Tcl_ResetResult(interp);
+
+ /*
+ * Increment the code's ref count while it is being executed. If
+ * afterwards no references to it remain, free the code.
+ */
+
+ codePtr->refCount++;
+ result = TclExecuteByteCode(interp, codePtr);
+ codePtr->refCount--;
+ if (codePtr->refCount <= 0) {
+ TclCleanupByteCode(codePtr);
+ objPtr->typePtr = NULL;
+ objPtr->internalRep.otherValuePtr = NULL;
+ }
+
+ /*
+ * If the expression evaluated successfully, store a pointer to its
+ * value object in resultPtrPtr then restore the old interpreter result.
+ * We increment the object's ref count to reflect the reference that we
+ * are returning to the caller. We also decrement the ref count of the
+ * interpreter's result object after calling Tcl_SetResult since we
+ * next store into that field directly.
+ */
+
+ if (result == TCL_OK) {
+ *resultPtrPtr = iPtr->objResultPtr;
+ Tcl_IncrRefCount(iPtr->objResultPtr);
+
+ Tcl_SetObjResult(interp, saveObjPtr);
+ }
+ TclDecrRefCount(saveObjPtr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompEvalObj --
+ *
+ * This procedure evaluates the script contained in a Tcl_Obj by
+ * first compiling it and then passing it to TclExecuteByteCode.
+ *
+ * Results:
+ * The return value is one of the return codes defined in tcl.h
+ * (such as TCL_OK), and interp->objResultPtr refers to a Tcl object
+ * that either contains the result of executing the code or an
+ * error message.
+ *
+ * Side effects:
+ * Almost certainly, depending on the ByteCode's instructions.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompEvalObj(interp, objPtr)
+ Tcl_Interp *interp;
+ Tcl_Obj *objPtr;
+{
+ register Interp *iPtr = (Interp *) interp;
+ register ByteCode* codePtr; /* Tcl Internal type of bytecode. */
+ int oldCount = iPtr->cmdCount; /* Used to tell whether any commands
+ * at all were executed. */
+ char *script;
+ int numSrcBytes;
+ int result;
+ Namespace *namespacePtr;
+
+
+ /*
+ * Check that the interpreter is ready to execute scripts
+ */
+
+ if (TclInterpReady(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+ if (iPtr->varFramePtr != NULL) {
+ namespacePtr = iPtr->varFramePtr->nsPtr;
+ } else {
+ namespacePtr = iPtr->globalNsPtr;
+ }
+
+ /*
+ * If the object is not already of tclByteCodeType, compile it (and
+ * reset the compilation flags in the interpreter; this should be
+ * done after any compilation).
+ * Otherwise, check that it is "fresh" enough.
+ */
+
+ if (objPtr->typePtr != &tclByteCodeType) {
+ recompileObj:
+ iPtr->errorLine = 1;
+ result = tclByteCodeType.setFromAnyProc(interp, objPtr);
+ if (result != TCL_OK) {
+ return result;
+ }
+ iPtr->evalFlags = 0;
+ codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
+ } else {
+ /*
+ * Make sure the Bytecode hasn't been invalidated by, e.g., someone
+ * redefining a command with a compile procedure (this might make the
+ * compiled code wrong).
+ * The object needs to be recompiled if it was compiled in/for a
+ * different interpreter, or for a different namespace, or for the
+ * same namespace but with different name resolution rules.
+ * Precompiled objects, however, are immutable and therefore
+ * they are not recompiled, even if the epoch has changed.
+ *
+ * To be pedantically correct, we should also check that the
+ * originating procPtr is the same as the current context procPtr
+ * (assuming one exists at all - none for global level). This
+ * code is #def'ed out because [info body] was changed to never
+ * return a bytecode type object, which should obviate us from
+ * the extra checks here.
+ */
+ codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
+ if (((Interp *) *codePtr->interpHandle != iPtr)
+ || (codePtr->compileEpoch != iPtr->compileEpoch)
+#ifdef CHECK_PROC_ORIGINATION /* [Bug: 3412 Pedantic] */
+ || (codePtr->procPtr != NULL && !(iPtr->varFramePtr &&
+ iPtr->varFramePtr->procPtr == codePtr->procPtr))
+#endif
+ || (codePtr->nsPtr != namespacePtr)
+ || (codePtr->nsEpoch != namespacePtr->resolverEpoch)) {
+ if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) {
+ if ((Interp *) *codePtr->interpHandle != iPtr) {
+ panic("Tcl_EvalObj: compiled script jumped interps");
+ }
+ codePtr->compileEpoch = iPtr->compileEpoch;
+ } else {
+ /*
+ * This byteCode is invalid: free it and recompile
+ */
+ tclByteCodeType.freeIntRepProc(objPtr);
+ goto recompileObj;
+ }
+ }
+ }
+
+ /*
+ * Execute the commands. If the code was compiled from an empty string,
+ * don't bother executing the code.
+ */
+
+ numSrcBytes = codePtr->numSrcBytes;
+ if ((numSrcBytes > 0) || (codePtr->flags & TCL_BYTECODE_PRECOMPILED)) {
+ /*
+ * Increment the code's ref count while it is being executed. If
+ * afterwards no references to it remain, free the code.
+ */
+
+ codePtr->refCount++;
+ iPtr->numLevels++;
+ result = TclExecuteByteCode(interp, codePtr);
+ iPtr->numLevels--;
+ codePtr->refCount--;
+ if (codePtr->refCount <= 0) {
+ TclCleanupByteCode(codePtr);
+ }
+ } else {
+ result = TCL_OK;
+ }
+
+ /*
+ * If no commands at all were executed, check for asynchronous
+ * handlers so that they at least get one change to execute.
+ * This is needed to handle event loops written in Tcl with
+ * empty bodies.
+ */
+
+ if ((oldCount == iPtr->cmdCount) && Tcl_AsyncReady()) {
+ result = Tcl_AsyncInvoke(interp, result);
+
+
+ /*
+ * If an error occurred, record information about what was being
+ * executed when the error occurred.
+ */
+
+ if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
+ script = Tcl_GetStringFromObj(objPtr, &numSrcBytes);
+ Tcl_LogCommandInfo(interp, script, script, numSrcBytes);
+ }
+ }
+
+ /*
+ * Set the interpreter's termOffset member to the offset of the
+ * character just after the last one executed. We approximate the offset
+ * of the last character executed by using the number of characters
+ * compiled.
+ */
+
+ iPtr->termOffset = numSrcBytes;
+ iPtr->flags &= ~ERR_ALREADY_LOGGED;
+
+ return result;
}
/*
@@ -519,8 +1068,8 @@ GrowEvaluationStack(eePtr)
*
*----------------------------------------------------------------------
*/
-
-int
+
+static int
TclExecuteByteCode(interp, codePtr)
Tcl_Interp *interp; /* Token for command interpreter. */
ByteCode *codePtr; /* The bytecode sequence to interpret. */
@@ -534,7 +1083,7 @@ TclExecuteByteCode(interp, codePtr)
/* Cached top index of evaluation stack. */
register unsigned char *pc = codePtr->codeStart;
/* The current program counter. */
- int opnd; /* Current instruction's operand byte. */
+ int opnd; /* Current instruction's operand byte(s). */
int pcAdjustment; /* Hold pc adjustment after instruction. */
int initStackTop = stackTop;/* Stack top at start of execution. */
ExceptionRange *rangePtr; /* Points to closest loop or catch exception
@@ -542,11 +1091,23 @@ TclExecuteByteCode(interp, codePtr)
* instructions and processCatch to
* process break, continue, and errors. */
int result = TCL_OK; /* Return code returned after execution. */
- int traceInstructions = (tclTraceExec == 3);
+ int storeFlags;
Tcl_Obj *valuePtr, *value2Ptr, *objPtr;
char *bytes;
int length;
- long i;
+ long i = 0; /* Init. avoids compiler warning. */
+#ifndef TCL_WIDE_INT_IS_LONG
+ Tcl_WideInt w;
+#endif
+ register int cleanup;
+ Tcl_Obj *objResultPtr;
+ char *part1, *part2;
+ Var *varPtr, *arrayPtr;
+ CallFrame *varFramePtr = iPtr->varFramePtr;
+#ifdef TCL_COMPILE_DEBUG
+ int traceInstructions = (tclTraceExec == 3);
+ char cmdNameBuf[21];
+#endif
/*
* This procedure uses a stack to hold information about catch commands.
@@ -566,6 +1127,7 @@ TclExecuteByteCode(interp, codePtr)
fprintf(stdout, " Starting stack top=%d\n", eePtr->stackTop);
fflush(stdout);
}
+ opnd = 0; /* Init. avoids compiler warning. */
#endif
#ifdef TCL_COMPILE_STATS
@@ -593,2324 +1155,3059 @@ TclExecuteByteCode(interp, codePtr)
}
/*
- * Loop executing instructions until a "done" instruction, a TCL_RETURN,
- * or some error.
+ * Loop executing instructions until a "done" instruction, a
+ * TCL_RETURN, or some error.
*/
- for (;;) {
+ goto cleanup0;
+
+
+ /*
+ * Targets for standard instruction endings; unrolled
+ * for speed in the most frequent cases (instructions that
+ * consume up to two stack elements).
+ *
+ * This used to be a "for(;;)" loop, with each instruction doing
+ * its own cleanup.
+ */
+
+ cleanupV_pushObjResultPtr:
+ switch (cleanup) {
+ case 0:
+ stackPtr[++stackTop] = (objResultPtr);
+ goto cleanup0;
+ default:
+ cleanup -= 2;
+ while (cleanup--) {
+ valuePtr = POP_OBJECT();
+ TclDecrRefCount(valuePtr);
+ }
+ case 2:
+ cleanup2_pushObjResultPtr:
+ valuePtr = POP_OBJECT();
+ TclDecrRefCount(valuePtr);
+ case 1:
+ cleanup1_pushObjResultPtr:
+ valuePtr = stackPtr[stackTop];
+ TclDecrRefCount(valuePtr);
+ }
+ stackPtr[stackTop] = objResultPtr;
+ goto cleanup0;
+
+ cleanupV:
+ switch (cleanup) {
+ default:
+ cleanup -= 2;
+ while (cleanup--) {
+ valuePtr = POP_OBJECT();
+ TclDecrRefCount(valuePtr);
+ }
+ case 2:
+ cleanup2:
+ valuePtr = POP_OBJECT();
+ TclDecrRefCount(valuePtr);
+ case 1:
+ cleanup1:
+ valuePtr = POP_OBJECT();
+ TclDecrRefCount(valuePtr);
+ case 0:
+ /*
+ * We really want to do nothing now, but this is needed
+ * for some compilers (SunPro CC)
+ */
+ break;
+ }
+
+ cleanup0:
+
#ifdef TCL_COMPILE_DEBUG
- ValidatePcAndStackTop(codePtr, pc, stackTop, initStackTop,
- eePtr->stackEnd);
-#else /* not TCL_COMPILE_DEBUG */
- if (traceInstructions) {
- fprintf(stdout, "%2d: %2d ", iPtr->numLevels, stackTop);
- TclPrintInstruction(codePtr, pc);
- fflush(stdout);
- }
+ ValidatePcAndStackTop(codePtr, pc, stackTop, initStackTop);
+ if (traceInstructions) {
+ fprintf(stdout, "%2d: %2d ", iPtr->numLevels, stackTop);
+ TclPrintInstruction(codePtr, pc);
+ fflush(stdout);
+ }
#endif /* TCL_COMPILE_DEBUG */
-
+
#ifdef TCL_COMPILE_STATS
- iPtr->stats.instructionCount[*pc]++;
+ iPtr->stats.instructionCount[*pc]++;
#endif
- switch (*pc) {
- case INST_DONE:
- /*
- * Pop the topmost object from the stack, set the interpreter's
- * object result to point to it, and return.
- */
- valuePtr = POP_OBJECT();
- Tcl_SetObjResult(interp, valuePtr);
- TclDecrRefCount(valuePtr);
- if (stackTop != initStackTop) {
- fprintf(stderr, "\nTclExecuteByteCode: done instruction at pc %u: stack top %d != entry stack top %d\n",
- (unsigned int)(pc - codePtr->codeStart),
- (unsigned int) stackTop,
- (unsigned int) initStackTop);
- panic("TclExecuteByteCode execution failure: end stack top != start stack top");
- }
- TRACE_WITH_OBJ(("=> return code=%d, result=", result),
- iPtr->objResultPtr);
+ switch (*pc) {
+ case INST_DONE:
+ if (stackTop <= initStackTop) {
+ stackTop--;
+ goto abnormalReturn;
+ }
+
+ /*
+ * Set the interpreter's object result to point to the
+ * topmost object from the stack, and check for a possible
+ * [catch]. The stackTop's level and refCount will be handled
+ * by "processCatch" or "abnormalReturn".
+ */
+
+ valuePtr = stackPtr[stackTop];
+ Tcl_SetObjResult(interp, valuePtr);
#ifdef TCL_COMPILE_DEBUG
- if (traceInstructions) {
- fprintf(stdout, "\n");
- }
+ TRACE_WITH_OBJ(("=> return code=%d, result=", result),
+ iPtr->objResultPtr);
+ if (traceInstructions) {
+ fprintf(stdout, "\n");
+ }
#endif
- goto done;
-
- case INST_PUSH1:
-#ifdef TCL_COMPILE_DEBUG
- valuePtr = codePtr->objArrayPtr[TclGetUInt1AtPtr(pc+1)];
- PUSH_OBJECT(valuePtr);
- TRACE_WITH_OBJ(("%u => ", TclGetInt1AtPtr(pc+1)), valuePtr);
-#else
- PUSH_OBJECT(codePtr->objArrayPtr[TclGetUInt1AtPtr(pc+1)]);
-#endif /* TCL_COMPILE_DEBUG */
- ADJUST_PC(2);
-
- case INST_PUSH4:
- valuePtr = codePtr->objArrayPtr[TclGetUInt4AtPtr(pc+1)];
- PUSH_OBJECT(valuePtr);
- TRACE_WITH_OBJ(("%u => ", TclGetUInt4AtPtr(pc+1)), valuePtr);
- ADJUST_PC(5);
+ goto checkForCatch;
+
+ case INST_PUSH1:
+ objResultPtr = codePtr->objArrayPtr[TclGetUInt1AtPtr(pc+1)];
+ TRACE_WITH_OBJ(("%u => ", TclGetInt1AtPtr(pc+1)), objResultPtr);
+ NEXT_INST_F(2, 0, 1);
+
+ case INST_PUSH4:
+ objResultPtr = codePtr->objArrayPtr[TclGetUInt4AtPtr(pc+1)];
+ TRACE_WITH_OBJ(("%u => ", TclGetUInt4AtPtr(pc+1)), objResultPtr);
+ NEXT_INST_F(5, 0, 1);
+
+ case INST_POP:
+ TRACE_WITH_OBJ(("=> discarding "), stackPtr[stackTop]);
+ valuePtr = POP_OBJECT();
+ TclDecrRefCount(valuePtr);
+ NEXT_INST_F(1, 0, 0);
+
+ case INST_DUP:
+ objResultPtr = stackPtr[stackTop];
+ TRACE_WITH_OBJ(("=> "), objResultPtr);
+ NEXT_INST_F(1, 0, 1);
+
+ case INST_OVER:
+ opnd = TclGetUInt4AtPtr( pc+1 );
+ objResultPtr = stackPtr[ stackTop - opnd ];
+ TRACE_WITH_OBJ(("=> "), objResultPtr);
+ NEXT_INST_F(5, 0, 1);
+
+ case INST_CONCAT1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ {
+ int totalLen = 0;
- case INST_POP:
- valuePtr = POP_OBJECT();
- TRACE_WITH_OBJ(("=> discarding "), valuePtr);
- TclDecrRefCount(valuePtr); /* finished with pop'ed object. */
- ADJUST_PC(1);
-
- case INST_DUP:
- valuePtr = stackPtr[stackTop];
- PUSH_OBJECT(Tcl_DuplicateObj(valuePtr));
- TRACE_WITH_OBJ(("=> "), valuePtr);
- ADJUST_PC(1);
+ /*
+ * Concatenate strings (with no separators) from the top
+ * opnd items on the stack starting with the deepest item.
+ * First, determine how many characters are needed.
+ */
- case INST_CONCAT1:
- opnd = TclGetUInt1AtPtr(pc+1);
- {
- Tcl_Obj *concatObjPtr;
- int totalLen = 0;
+ for (i = (stackTop - (opnd-1)); i <= stackTop; i++) {
+ bytes = Tcl_GetStringFromObj(stackPtr[i], &length);
+ if (bytes != NULL) {
+ totalLen += length;
+ }
+ }
- /*
- * Concatenate strings (with no separators) from the top
- * opnd items on the stack starting with the deepest item.
- * First, determine how many characters are needed.
- */
+ /*
+ * Initialize the new append string object by appending the
+ * strings of the opnd stack objects. Also pop the objects.
+ */
+ TclNewObj(objResultPtr);
+ if (totalLen > 0) {
+ char *p = (char *) ckalloc((unsigned) (totalLen + 1));
+ objResultPtr->bytes = p;
+ objResultPtr->length = totalLen;
for (i = (stackTop - (opnd-1)); i <= stackTop; i++) {
- bytes = Tcl_GetStringFromObj(stackPtr[i], &length);
+ valuePtr = stackPtr[i];
+ bytes = Tcl_GetStringFromObj(valuePtr, &length);
if (bytes != NULL) {
- totalLen += length;
- }
- }
-
- /*
- * Initialize the new append string object by appending the
- * strings of the opnd stack objects. Also pop the objects.
- */
-
- TclNewObj(concatObjPtr);
- if (totalLen > 0) {
- char *p = (char *) ckalloc((unsigned) (totalLen + 1));
- concatObjPtr->bytes = p;
- concatObjPtr->length = totalLen;
- for (i = (stackTop - (opnd-1)); i <= stackTop; i++) {
- valuePtr = stackPtr[i];
- bytes = Tcl_GetStringFromObj(valuePtr, &length);
- if (bytes != NULL) {
- memcpy((VOID *) p, (VOID *) bytes,
- (size_t) length);
- p += length;
- }
- TclDecrRefCount(valuePtr);
- }
- *p = '\0';
- } else {
- for (i = (stackTop - (opnd-1)); i <= stackTop; i++) {
- Tcl_DecrRefCount(stackPtr[i]);
+ memcpy((VOID *) p, (VOID *) bytes,
+ (size_t) length);
+ p += length;
}
}
- stackTop -= opnd;
+ *p = '\0';
+ }
- PUSH_OBJECT(concatObjPtr);
- TRACE_WITH_OBJ(("%u => ", opnd), concatObjPtr);
- ADJUST_PC(2);
- }
+ TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr);
+ NEXT_INST_V(2, opnd, 1);
+ }
- case INST_INVOKE_STK4:
- opnd = TclGetUInt4AtPtr(pc+1);
- pcAdjustment = 5;
- goto doInvocation;
-
- case INST_INVOKE_STK1:
- opnd = TclGetUInt1AtPtr(pc+1);
- pcAdjustment = 2;
+ case INST_INVOKE_STK4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ pcAdjustment = 5;
+ goto doInvocation;
+
+ case INST_INVOKE_STK1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ pcAdjustment = 2;
- doInvocation:
- {
- int objc = opnd; /* The number of arguments. */
- Tcl_Obj **objv; /* The array of argument objects. */
- Command *cmdPtr; /* Points to command's Command struct. */
- int newPcOffset; /* New inst offset for break, continue. */
-#ifdef TCL_COMPILE_DEBUG
- int isUnknownCmd = 0;
- char cmdNameBuf[21];
-#endif /* TCL_COMPILE_DEBUG */
-
- /*
- * If the interpreter was deleted, return an error.
- */
-
- if (iPtr->flags & DELETED) {
- Tcl_ResetResult(interp);
- Tcl_AppendToObj(Tcl_GetObjResult(interp),
- "attempt to call eval in deleted interpreter", -1);
- Tcl_SetErrorCode(interp, "CORE", "IDELETE",
- "attempt to call eval in deleted interpreter",
- (char *) NULL);
- result = TCL_ERROR;
- goto checkForCatch;
- }
-
- /*
- * Find the procedure to execute this command. If the
- * command is not found, handle it with the "unknown" proc.
- */
+ doInvocation:
+ {
+ int objc = opnd; /* The number of arguments. */
+ Tcl_Obj **objv; /* The array of argument objects. */
- objv = &(stackPtr[stackTop - (objc-1)]);
- cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]);
- if (cmdPtr == NULL) {
- cmdPtr = (Command *) Tcl_FindCommand(interp, "unknown",
- (Tcl_Namespace *) NULL, TCL_GLOBAL_ONLY);
- if (cmdPtr == NULL) {
- Tcl_ResetResult(interp);
- Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "invalid command name \"",
- Tcl_GetString(objv[0]), "\"",
- (char *) NULL);
- TRACE(("%u => unknown proc not found: ", objc));
- result = TCL_ERROR;
- goto checkForCatch;
- }
-#ifdef TCL_COMPILE_DEBUG
- isUnknownCmd = 1;
-#endif /*TCL_COMPILE_DEBUG*/
- stackTop++; /* need room for new inserted objv[0] */
- for (i = objc-1; i >= 0; i--) {
- objv[i+1] = objv[i];
- }
- objc++;
- objv[0] = Tcl_NewStringObj("unknown", -1);
- Tcl_IncrRefCount(objv[0]);
- }
-
- /*
- * Call any trace procedures.
- */
-
- if (iPtr->tracePtr != NULL) {
- Trace *tracePtr, *nextTracePtr;
-
- for (tracePtr = iPtr->tracePtr; tracePtr != NULL;
- tracePtr = nextTracePtr) {
- nextTracePtr = tracePtr->nextPtr;
- if (iPtr->numLevels <= tracePtr->level) {
- int numChars;
- char *cmd = GetSrcInfoForPc(pc, codePtr,
- &numChars);
- if (cmd != NULL) {
- DECACHE_STACK_INFO();
- CallTraceProcedure(interp, tracePtr, cmdPtr,
- cmd, numChars, objc, objv);
- CACHE_STACK_INFO();
- }
- }
- }
- }
-
- /*
- * Finally, invoke the command's Tcl_ObjCmdProc. First reset
- * the interpreter's string and object results to their
- * default empty values since they could have gotten changed
- * by earlier invocations.
- */
-
- Tcl_ResetResult(interp);
- if (tclTraceExec >= 2) {
-#ifdef TCL_COMPILE_DEBUG
- if (traceInstructions) {
- strncpy(cmdNameBuf, Tcl_GetString(objv[0]), 20);
- TRACE(("%u => call ", (isUnknownCmd? objc-1:objc)));
- } else {
- fprintf(stdout, "%d: (%u) invoking ",
- iPtr->numLevels,
- (unsigned int)(pc - codePtr->codeStart));
- }
- for (i = 0; i < objc; i++) {
- TclPrintObject(stdout, objv[i], 15);
- fprintf(stdout, " ");
- }
- fprintf(stdout, "\n");
- fflush(stdout);
-#else /* TCL_COMPILE_DEBUG */
- fprintf(stdout, "%d: (%u) invoking %s\n",
- iPtr->numLevels,
- (unsigned int)(pc - codePtr->codeStart),
- Tcl_GetString(objv[0]));
-#endif /*TCL_COMPILE_DEBUG*/
- }
+ /*
+ * We keep the stack reference count as a (char *), as that
+ * works nicely as a portable pointer-sized counter.
+ */
- iPtr->cmdCount++;
- DECACHE_STACK_INFO();
- result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp,
- objc, objv);
- if (Tcl_AsyncReady()) {
- result = Tcl_AsyncInvoke(interp, result);
- }
- CACHE_STACK_INFO();
+ char **preservedStackRefCountPtr;
+
+ /*
+ * Reference to memory block containing
+ * objv array (must be kept live throughout
+ * trace and command invokations.)
+ */
- /*
- * If the interpreter has a non-empty string result, the
- * result object is either empty or stale because some
- * procedure set interp->result directly. If so, move the
- * string result to the result object, then reset the
- * string result.
- */
+ objv = &(stackPtr[stackTop - (objc-1)]);
- if (*(iPtr->result) != 0) {
- (void) Tcl_GetObjResult(interp);
+#ifdef TCL_COMPILE_DEBUG
+ if (tclTraceExec >= 2) {
+ if (traceInstructions) {
+ strncpy(cmdNameBuf, TclGetString(objv[0]), 20);
+ TRACE(("%u => call ", objc));
+ } else {
+ fprintf(stdout, "%d: (%u) invoking ",
+ iPtr->numLevels,
+ (unsigned int)(pc - codePtr->codeStart));
}
-
- /*
- * Pop the objc top stack elements and decrement their ref
- * counts.
- */
-
for (i = 0; i < objc; i++) {
- valuePtr = stackPtr[stackTop];
- TclDecrRefCount(valuePtr);
- stackTop--;
+ TclPrintObject(stdout, objv[i], 15);
+ fprintf(stdout, " ");
}
+ fprintf(stdout, "\n");
+ fflush(stdout);
+ }
+#endif /*TCL_COMPILE_DEBUG*/
- /*
- * Process the result of the Tcl_ObjCmdProc call.
- */
-
- switch (result) {
- case TCL_OK:
- /*
- * Push the call's object result and continue execution
- * with the next instruction.
- */
- PUSH_OBJECT(Tcl_GetObjResult(interp));
- TRACE_WITH_OBJ(("%u => ...after \"%.20s\", result=",
- objc, cmdNameBuf), Tcl_GetObjResult(interp));
- ADJUST_PC(pcAdjustment);
+ /*
+ * If trace procedures will be called, we need a
+ * command string to pass to TclEvalObjvInternal; note
+ * that a copy of the string will be made there to
+ * include the ending \0.
+ */
+
+ bytes = NULL;
+ length = 0;
+ if (iPtr->tracePtr != NULL) {
+ Trace *tracePtr, *nextTracePtr;
- case TCL_BREAK:
- case TCL_CONTINUE:
- /*
- * The invoked command requested a break or continue.
- * Find the closest enclosing loop or catch exception
- * range, if any. If a loop is found, terminate its
- * execution or skip to its next iteration. If the
- * closest is a catch exception range, jump to its
- * catchOffset. If no enclosing range is found, stop
- * execution and return the TCL_BREAK or TCL_CONTINUE.
- */
- rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0,
- codePtr);
- if (rangePtr == NULL) {
- TRACE(("%u => ... after \"%.20s\", no encl. loop or catch, returning %s\n",
- objc, cmdNameBuf,
- StringForResultCode(result)));
- goto abnormalReturn; /* no catch exists to check */
- }
- newPcOffset = 0;
- switch (rangePtr->type) {
- case LOOP_EXCEPTION_RANGE:
- if (result == TCL_BREAK) {
- newPcOffset = rangePtr->breakOffset;
- } else if (rangePtr->continueOffset == -1) {
- TRACE(("%u => ... after \"%.20s\", %s, loop w/o continue, checking for catch\n",
- objc, cmdNameBuf,
- StringForResultCode(result)));
- goto checkForCatch;
- } else {
- newPcOffset = rangePtr->continueOffset;
- }
- TRACE(("%u => ... after \"%.20s\", %s, range at %d, new pc %d\n",
- objc, cmdNameBuf,
- StringForResultCode(result),
- rangePtr->codeOffset, newPcOffset));
+ for (tracePtr = iPtr->tracePtr; tracePtr != NULL;
+ tracePtr = nextTracePtr) {
+ nextTracePtr = tracePtr->nextPtr;
+ if (tracePtr->level == 0 ||
+ iPtr->numLevels <= tracePtr->level) {
+ /*
+ * Traces will be called: get command string
+ */
+
+ bytes = GetSrcInfoForPc(pc, codePtr, &length);
break;
- case CATCH_EXCEPTION_RANGE:
- TRACE(("%u => ... after \"%.20s\", %s...\n",
- objc, cmdNameBuf,
- StringForResultCode(result)));
- goto processCatch; /* it will use rangePtr */
- default:
- panic("TclExecuteByteCode: bad ExceptionRange type\n");
}
- result = TCL_OK;
- pc = (codePtr->codeStart + newPcOffset);
- continue; /* restart outer instruction loop at pc */
-
- case TCL_ERROR:
- /*
- * The invoked command returned an error. Look for an
- * enclosing catch exception range, if any.
- */
- TRACE_WITH_OBJ(("%u => ... after \"%.20s\", TCL_ERROR ",
- objc, cmdNameBuf), Tcl_GetObjResult(interp));
- goto checkForCatch;
+ }
+ } else {
+ Command *cmdPtr;
+ cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]);
+ if ((cmdPtr != NULL) && (cmdPtr->flags & CMD_HAS_EXEC_TRACES)) {
+ bytes = GetSrcInfoForPc(pc, codePtr, &length);
+ }
+ }
- case TCL_RETURN:
- /*
- * The invoked command requested that the current
- * procedure stop execution and return. First check
- * for an enclosing catch exception range, if any.
- */
- TRACE(("%u => ... after \"%.20s\", TCL_RETURN\n",
- objc, cmdNameBuf));
- goto checkForCatch;
+ /*
+ * A reference to part of the stack vector itself
+ * escapes our control: increase its refCount
+ * to stop it from being deallocated by a recursive
+ * call to ourselves. The extra variable is needed
+ * because all others are liable to change due to the
+ * trace procedures.
+ */
- default:
- TRACE_WITH_OBJ(("%u => ... after \"%.20s\", OTHER RETURN CODE %d ",
- objc, cmdNameBuf, result),
- Tcl_GetObjResult(interp));
- goto checkForCatch;
- }
- }
-
- case INST_EVAL_STK:
- objPtr = POP_OBJECT();
+ preservedStackRefCountPtr = (char **) (stackPtr-1);
+ ++*preservedStackRefCountPtr;
+
+ /*
+ * Finally, let TclEvalObjvInternal handle the command.
+ */
+
+ Tcl_ResetResult(interp);
DECACHE_STACK_INFO();
- result = Tcl_EvalObjEx(interp, objPtr, 0);
+ result = TclEvalObjvInternal(interp, objc, objv, bytes, length, 0);
CACHE_STACK_INFO();
+
+ /*
+ * If the old stack is going to be released, it is
+ * safe to do so now, since no references to objv are
+ * going to be used from now on.
+ */
+
+ --*preservedStackRefCountPtr;
+ if (*preservedStackRefCountPtr == (char *) 0) {
+ ckfree((VOID *) preservedStackRefCountPtr);
+ }
+
if (result == TCL_OK) {
/*
- * Normal return; push the eval's object result.
- */
- PUSH_OBJECT(Tcl_GetObjResult(interp));
- TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)),
- Tcl_GetObjResult(interp));
- TclDecrRefCount(objPtr);
- ADJUST_PC(1);
- } else if ((result == TCL_BREAK) || (result == TCL_CONTINUE)) {
- /*
- * Find the closest enclosing loop or catch exception range,
- * if any. If a loop is found, terminate its execution or
- * skip to its next iteration. If the closest is a catch
- * exception range, jump to its catchOffset. If no enclosing
- * range is found, stop execution and return that same
- * TCL_BREAK or TCL_CONTINUE.
+ * Push the call's object result and continue execution
+ * with the next instruction.
*/
- int newPcOffset = 0; /* Pc offset computed during break,
- * continue, error processing. Init.
- * to avoid compiler warning. */
-
- rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0,
- codePtr);
- if (rangePtr == NULL) {
- TRACE(("\"%.30s\" => no encl. loop or catch, returning %s\n",
- O2S(objPtr), StringForResultCode(result)));
- Tcl_DecrRefCount(objPtr);
- goto abnormalReturn; /* no catch exists to check */
- }
- switch (rangePtr->type) {
- case LOOP_EXCEPTION_RANGE:
- if (result == TCL_BREAK) {
- newPcOffset = rangePtr->breakOffset;
- } else if (rangePtr->continueOffset == -1) {
- TRACE(("\"%.30s\" => %s, loop w/o continue, checking for catch\n",
- O2S(objPtr), StringForResultCode(result)));
- Tcl_DecrRefCount(objPtr);
- goto checkForCatch;
- } else {
- newPcOffset = rangePtr->continueOffset;
- }
- result = TCL_OK;
- TRACE_WITH_OBJ(("\"%.30s\" => %s, range at %d, new pc %d ",
- O2S(objPtr), StringForResultCode(result),
- rangePtr->codeOffset, newPcOffset), valuePtr);
- break;
- case CATCH_EXCEPTION_RANGE:
- TRACE_WITH_OBJ(("\"%.30s\" => %s ",
- O2S(objPtr), StringForResultCode(result)),
- valuePtr);
- Tcl_DecrRefCount(objPtr);
- goto processCatch; /* it will use rangePtr */
- default:
- panic("TclExecuteByteCode: unrecognized ExceptionRange type %d\n", rangePtr->type);
- }
- Tcl_DecrRefCount(objPtr);
- pc = (codePtr->codeStart + newPcOffset);
- continue; /* restart outer instruction loop at pc */
- } else { /* eval returned TCL_ERROR, TCL_RETURN, unknown code */
- TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ", O2S(objPtr)),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- goto checkForCatch;
- }
+ TRACE_WITH_OBJ(("%u => ... after \"%.20s\": TCL_OK, result=",
+ objc, cmdNameBuf), Tcl_GetObjResult(interp));
- case INST_EXPR_STK:
- objPtr = POP_OBJECT();
- Tcl_ResetResult(interp);
- DECACHE_STACK_INFO();
- result = Tcl_ExprObj(interp, objPtr, &valuePtr);
- CACHE_STACK_INFO();
- if (result != TCL_OK) {
- TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ",
- O2S(objPtr)), Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- goto checkForCatch;
+ objResultPtr = Tcl_GetObjResult(interp);
+ NEXT_INST_V(pcAdjustment, opnd, 1);
+ } else {
+ cleanup = opnd;
+ goto processExceptionReturn;
}
- stackPtr[++stackTop] = valuePtr; /* already has right refct */
- TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), valuePtr);
- TclDecrRefCount(objPtr);
- ADJUST_PC(1);
+ }
- case INST_LOAD_SCALAR1:
-#ifdef TCL_COMPILE_DEBUG
- opnd = TclGetUInt1AtPtr(pc+1);
- DECACHE_STACK_INFO();
- valuePtr = TclGetIndexedScalar(interp, opnd,
- /*leaveErrorMsg*/ 1);
- CACHE_STACK_INFO();
- if (valuePtr == NULL) {
- TRACE_WITH_OBJ(("%u => ERROR: ", opnd),
- Tcl_GetObjResult(interp));
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(valuePtr);
- TRACE_WITH_OBJ(("%u => ", opnd), valuePtr);
-#else /* TCL_COMPILE_DEBUG */
- DECACHE_STACK_INFO();
- opnd = TclGetUInt1AtPtr(pc+1);
- valuePtr = TclGetIndexedScalar(interp, opnd, /*leaveErrorMsg*/ 1);
- CACHE_STACK_INFO();
- if (valuePtr == NULL) {
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(valuePtr);
-#endif /* TCL_COMPILE_DEBUG */
- ADJUST_PC(2);
+ case INST_EVAL_STK:
+ /*
+ * Note to maintainers: it is important that INST_EVAL_STK
+ * pop its argument from the stack before jumping to
+ * checkForCatch! DO NOT OPTIMISE!
+ */
- case INST_LOAD_SCALAR4:
- opnd = TclGetUInt4AtPtr(pc+1);
- DECACHE_STACK_INFO();
- valuePtr = TclGetIndexedScalar(interp, opnd,
- /*leaveErrorMsg*/ 1);
- CACHE_STACK_INFO();
- if (valuePtr == NULL) {
- TRACE_WITH_OBJ(("%u => ERROR: ", opnd),
- Tcl_GetObjResult(interp));
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(valuePtr);
- TRACE_WITH_OBJ(("%u => ", opnd), valuePtr);
- ADJUST_PC(5);
+ objPtr = stackPtr[stackTop];
+ DECACHE_STACK_INFO();
+ result = TclCompEvalObj(interp, objPtr);
+ CACHE_STACK_INFO();
+ if (result == TCL_OK) {
+ /*
+ * Normal return; push the eval's object result.
+ */
- case INST_LOAD_SCALAR_STK:
- objPtr = POP_OBJECT(); /* scalar name */
- DECACHE_STACK_INFO();
- valuePtr = Tcl_ObjGetVar2(interp, objPtr, NULL, TCL_LEAVE_ERR_MSG);
- CACHE_STACK_INFO();
- if (valuePtr == NULL) {
- TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ", O2S(objPtr)),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(valuePtr);
- TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), valuePtr);
- TclDecrRefCount(objPtr);
- ADJUST_PC(1);
-
- case INST_LOAD_ARRAY4:
- opnd = TclGetUInt4AtPtr(pc+1);
- pcAdjustment = 5;
- goto doLoadArray;
-
- case INST_LOAD_ARRAY1:
- opnd = TclGetUInt1AtPtr(pc+1);
- pcAdjustment = 2;
-
- doLoadArray:
- {
- Tcl_Obj *elemPtr = POP_OBJECT();
-
- DECACHE_STACK_INFO();
- valuePtr = TclGetElementOfIndexedArray(interp, opnd,
- elemPtr, /*leaveErrorMsg*/ 1);
- CACHE_STACK_INFO();
- if (valuePtr == NULL) {
- TRACE_WITH_OBJ(("%u \"%.30s\" => ERROR: ",
- opnd, O2S(elemPtr)), Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(elemPtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(valuePtr);
- TRACE_WITH_OBJ(("%u \"%.30s\" => ",
- opnd, O2S(elemPtr)),valuePtr);
- TclDecrRefCount(elemPtr);
- }
- ADJUST_PC(pcAdjustment);
+ objResultPtr = Tcl_GetObjResult(interp);
+ TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)),
+ Tcl_GetObjResult(interp));
+ NEXT_INST_F(1, 1, 1);
+ } else {
+ cleanup = 1;
+ goto processExceptionReturn;
+ }
- case INST_LOAD_ARRAY_STK:
- {
- Tcl_Obj *elemPtr = POP_OBJECT();
-
- objPtr = POP_OBJECT(); /* array name */
- DECACHE_STACK_INFO();
- valuePtr = Tcl_ObjGetVar2(interp, objPtr, elemPtr,
- TCL_LEAVE_ERR_MSG);
- CACHE_STACK_INFO();
- if (valuePtr == NULL) {
- TRACE_WITH_OBJ(("\"%.30s(%.30s)\" => ERROR: ",
- O2S(objPtr), O2S(elemPtr)),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(elemPtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(valuePtr);
- TRACE_WITH_OBJ(("\"%.30s(%.30s)\" => ",
- O2S(objPtr), O2S(elemPtr)), valuePtr);
- TclDecrRefCount(objPtr);
- TclDecrRefCount(elemPtr);
- }
- ADJUST_PC(1);
+ case INST_EXPR_STK:
+ objPtr = stackPtr[stackTop];
+ Tcl_ResetResult(interp);
+ DECACHE_STACK_INFO();
+ result = Tcl_ExprObj(interp, objPtr, &valuePtr);
+ CACHE_STACK_INFO();
+ if (result != TCL_OK) {
+ TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ",
+ O2S(objPtr)), Tcl_GetObjResult(interp));
+ goto checkForCatch;
+ }
+ objResultPtr = valuePtr;
+ TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), valuePtr);
+ NEXT_INST_F(1, 1, -1); /* already has right refct */
- case INST_LOAD_STK:
- objPtr = POP_OBJECT(); /* variable name */
- DECACHE_STACK_INFO();
- valuePtr = Tcl_ObjGetVar2(interp, objPtr, NULL, TCL_LEAVE_ERR_MSG);
- CACHE_STACK_INFO();
- if (valuePtr == NULL) {
- TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ",
- O2S(objPtr)), Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(valuePtr);
- TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), valuePtr);
- TclDecrRefCount(objPtr);
- ADJUST_PC(1);
-
- case INST_STORE_SCALAR4:
- opnd = TclGetUInt4AtPtr(pc+1);
- pcAdjustment = 5;
- goto doStoreScalar;
-
- case INST_STORE_SCALAR1:
- opnd = TclGetUInt1AtPtr(pc+1);
- pcAdjustment = 2;
-
- doStoreScalar:
- valuePtr = POP_OBJECT();
- DECACHE_STACK_INFO();
- value2Ptr = TclSetIndexedScalar(interp, opnd, valuePtr,
- /*leaveErrorMsg*/ 1);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("%u <- \"%.30s\" => ERROR: ",
- opnd, O2S(valuePtr)), Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(valuePtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("%u <- \"%.30s\" => ",
- opnd, O2S(valuePtr)), value2Ptr);
- TclDecrRefCount(valuePtr);
- ADJUST_PC(pcAdjustment);
+ /*
+ * ---------------------------------------------------------
+ * Start of INST_LOAD instructions.
+ *
+ * WARNING: more 'goto' here than your doctor recommended!
+ * The different instructions set the value of some variables
+ * and then jump to somme common execution code.
+ */
- case INST_STORE_SCALAR_STK:
- valuePtr = POP_OBJECT();
- objPtr = POP_OBJECT(); /* scalar name */
- DECACHE_STACK_INFO();
- value2Ptr = Tcl_ObjSetVar2(interp, objPtr, NULL, valuePtr,
- TCL_LEAVE_ERR_MSG);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("\"%.30s\" <- \"%.30s\" => ERROR: ",
- O2S(objPtr), O2S(valuePtr)),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(valuePtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("\"%.30s\" <- \"%.30s\" => ",
- O2S(objPtr), O2S(valuePtr)), value2Ptr);
- TclDecrRefCount(objPtr);
- TclDecrRefCount(valuePtr);
- ADJUST_PC(1);
+ case INST_LOAD_SCALAR1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ varPtr = &(varFramePtr->compiledLocals[opnd]);
+ part1 = varPtr->name;
+ while (TclIsVarLink(varPtr)) {
+ varPtr = varPtr->value.linkPtr;
+ }
+ TRACE(("%u => ", opnd));
+ if (TclIsVarScalar(varPtr) && !TclIsVarUndefined(varPtr)
+ && (varPtr->tracePtr == NULL)) {
+ /*
+ * No errors, no traces: just get the value.
+ */
+ objResultPtr = varPtr->value.objPtr;
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+ NEXT_INST_F(2, 0, 1);
+ }
+ pcAdjustment = 2;
+ cleanup = 0;
+ arrayPtr = NULL;
+ part2 = NULL;
+ goto doCallPtrGetVar;
+
+ case INST_LOAD_SCALAR4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ varPtr = &(varFramePtr->compiledLocals[opnd]);
+ part1 = varPtr->name;
+ while (TclIsVarLink(varPtr)) {
+ varPtr = varPtr->value.linkPtr;
+ }
+ TRACE(("%u => ", opnd));
+ if (TclIsVarScalar(varPtr) && !TclIsVarUndefined(varPtr)
+ && (varPtr->tracePtr == NULL)) {
+ /*
+ * No errors, no traces: just get the value.
+ */
+ objResultPtr = varPtr->value.objPtr;
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+ NEXT_INST_F(5, 0, 1);
+ }
+ pcAdjustment = 5;
+ cleanup = 0;
+ arrayPtr = NULL;
+ part2 = NULL;
+ goto doCallPtrGetVar;
+
+ case INST_LOAD_ARRAY_STK:
+ cleanup = 2;
+ part2 = Tcl_GetString(stackPtr[stackTop]); /* element name */
+ objPtr = stackPtr[stackTop-1]; /* array name */
+ TRACE(("\"%.30s(%.30s)\" => ", O2S(objPtr), part2));
+ goto doLoadStk;
+
+ case INST_LOAD_STK:
+ case INST_LOAD_SCALAR_STK:
+ cleanup = 1;
+ part2 = NULL;
+ objPtr = stackPtr[stackTop]; /* variable name */
+ TRACE(("\"%.30s\" => ", O2S(objPtr)));
+
+ doLoadStk:
+ part1 = TclGetString(objPtr);
+ varPtr = TclObjLookupVar(interp, objPtr, part2,
+ TCL_LEAVE_ERR_MSG, "read",
+ /*createPart1*/ 0,
+ /*createPart2*/ 1, &arrayPtr);
+ if (varPtr == NULL) {
+ TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+ if (TclIsVarScalar(varPtr) && !TclIsVarUndefined(varPtr)
+ && (varPtr->tracePtr == NULL)
+ && ((arrayPtr == NULL)
+ || (arrayPtr->tracePtr == NULL))) {
+ /*
+ * No errors, no traces: just get the value.
+ */
+ objResultPtr = varPtr->value.objPtr;
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+ NEXT_INST_V(1, cleanup, 1);
+ }
+ pcAdjustment = 1;
+ goto doCallPtrGetVar;
- case INST_STORE_ARRAY4:
- opnd = TclGetUInt4AtPtr(pc+1);
- pcAdjustment = 5;
- goto doStoreArray;
+ case INST_LOAD_ARRAY4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ pcAdjustment = 5;
+ goto doLoadArray;
- case INST_STORE_ARRAY1:
- opnd = TclGetUInt1AtPtr(pc+1);
- pcAdjustment = 2;
-
- doStoreArray:
- {
- Tcl_Obj *elemPtr;
+ case INST_LOAD_ARRAY1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ pcAdjustment = 2;
+
+ doLoadArray:
+ part2 = TclGetString(stackPtr[stackTop]);
+ arrayPtr = &(varFramePtr->compiledLocals[opnd]);
+ part1 = arrayPtr->name;
+ while (TclIsVarLink(arrayPtr)) {
+ arrayPtr = arrayPtr->value.linkPtr;
+ }
+ TRACE(("%u \"%.30s\" => ", opnd, part2));
+ varPtr = TclLookupArrayElement(interp, part1, part2,
+ TCL_LEAVE_ERR_MSG, "read", 0, 1, arrayPtr);
+ if (varPtr == NULL) {
+ TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+ if (TclIsVarScalar(varPtr) && !TclIsVarUndefined(varPtr)
+ && (varPtr->tracePtr == NULL)
+ && ((arrayPtr == NULL)
+ || (arrayPtr->tracePtr == NULL))) {
+ /*
+ * No errors, no traces: just get the value.
+ */
+ objResultPtr = varPtr->value.objPtr;
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+ NEXT_INST_F(pcAdjustment, 1, 1);
+ }
+ cleanup = 1;
+ goto doCallPtrGetVar;
- valuePtr = POP_OBJECT();
- elemPtr = POP_OBJECT();
- DECACHE_STACK_INFO();
- value2Ptr = TclSetElementOfIndexedArray(interp, opnd,
- elemPtr, valuePtr, TCL_LEAVE_ERR_MSG);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("%u \"%.30s\" <- \"%.30s\" => ERROR: ",
- opnd, O2S(elemPtr), O2S(valuePtr)),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(elemPtr);
- Tcl_DecrRefCount(valuePtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("%u \"%.30s\" <- \"%.30s\" => ",
- opnd, O2S(elemPtr), O2S(valuePtr)), value2Ptr);
- TclDecrRefCount(elemPtr);
- TclDecrRefCount(valuePtr);
- }
- ADJUST_PC(pcAdjustment);
+ doCallPtrGetVar:
+ /*
+ * There are either errors or the variable is traced:
+ * call TclPtrGetVar to process fully.
+ */
- case INST_STORE_ARRAY_STK:
- {
- Tcl_Obj *elemPtr;
+ DECACHE_STACK_INFO();
+ objResultPtr = TclPtrGetVar(interp, varPtr, arrayPtr, part1,
+ part2, TCL_LEAVE_ERR_MSG);
+ CACHE_STACK_INFO();
+ if (objResultPtr == NULL) {
+ TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+ NEXT_INST_V(pcAdjustment, cleanup, 1);
- valuePtr = POP_OBJECT();
- elemPtr = POP_OBJECT();
- objPtr = POP_OBJECT(); /* array name */
- DECACHE_STACK_INFO();
- value2Ptr = Tcl_ObjSetVar2(interp, objPtr, elemPtr, valuePtr,
- TCL_LEAVE_ERR_MSG);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <- \"%.30s\" => ERROR: ",
- O2S(objPtr), O2S(elemPtr), O2S(valuePtr)),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(elemPtr);
- Tcl_DecrRefCount(valuePtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <- \"%.30s\" => ",
- O2S(objPtr), O2S(elemPtr), O2S(valuePtr)),
- value2Ptr);
- TclDecrRefCount(objPtr);
- TclDecrRefCount(elemPtr);
- TclDecrRefCount(valuePtr);
- }
- ADJUST_PC(1);
+ /*
+ * End of INST_LOAD instructions.
+ * ---------------------------------------------------------
+ */
- case INST_STORE_STK:
- valuePtr = POP_OBJECT();
- objPtr = POP_OBJECT(); /* variable name */
- DECACHE_STACK_INFO();
- value2Ptr = Tcl_ObjSetVar2(interp, objPtr, NULL, valuePtr,
- TCL_LEAVE_ERR_MSG);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("\"%.30s\" <- \"%.30s\" => ERROR: ",
- O2S(objPtr), O2S(valuePtr)),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(valuePtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("\"%.30s\" <- \"%.30s\" => ",
- O2S(objPtr), O2S(valuePtr)), value2Ptr);
- TclDecrRefCount(objPtr);
- TclDecrRefCount(valuePtr);
- ADJUST_PC(1);
+ /*
+ * ---------------------------------------------------------
+ * Start of INST_STORE and related instructions.
+ *
+ * WARNING: more 'goto' here than your doctor recommended!
+ * The different instructions set the value of some variables
+ * and then jump to somme common execution code.
+ */
- case INST_INCR_SCALAR1:
- opnd = TclGetUInt1AtPtr(pc+1);
- valuePtr = POP_OBJECT();
- if (valuePtr->typePtr != &tclIntType) {
- result = tclIntType.setFromAnyProc(interp, valuePtr);
- if (result != TCL_OK) {
- TRACE_WITH_OBJ(("%u (by %s) => ERROR converting increment amount to int: ",
- opnd, O2S(valuePtr)), Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(valuePtr);
- goto checkForCatch;
+ case INST_LAPPEND_STK:
+ valuePtr = stackPtr[stackTop]; /* value to append */
+ part2 = NULL;
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE
+ | TCL_LIST_ELEMENT | TCL_TRACE_READS);
+ goto doStoreStk;
+
+ case INST_LAPPEND_ARRAY_STK:
+ valuePtr = stackPtr[stackTop]; /* value to append */
+ part2 = TclGetString(stackPtr[stackTop - 1]);
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE
+ | TCL_LIST_ELEMENT | TCL_TRACE_READS);
+ goto doStoreStk;
+
+ case INST_APPEND_STK:
+ valuePtr = stackPtr[stackTop]; /* value to append */
+ part2 = NULL;
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE);
+ goto doStoreStk;
+
+ case INST_APPEND_ARRAY_STK:
+ valuePtr = stackPtr[stackTop]; /* value to append */
+ part2 = TclGetString(stackPtr[stackTop - 1]);
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE);
+ goto doStoreStk;
+
+ case INST_STORE_ARRAY_STK:
+ valuePtr = stackPtr[stackTop];
+ part2 = TclGetString(stackPtr[stackTop - 1]);
+ storeFlags = TCL_LEAVE_ERR_MSG;
+ goto doStoreStk;
+
+ case INST_STORE_STK:
+ case INST_STORE_SCALAR_STK:
+ valuePtr = stackPtr[stackTop];
+ part2 = NULL;
+ storeFlags = TCL_LEAVE_ERR_MSG;
+
+ doStoreStk:
+ objPtr = stackPtr[stackTop - 1 - (part2 != NULL)]; /* variable name */
+ part1 = TclGetString(objPtr);
+#ifdef TCL_COMPILE_DEBUG
+ if (part2 == NULL) {
+ TRACE(("\"%.30s\" <- \"%.30s\" =>",
+ part1, O2S(valuePtr)));
+ } else {
+ TRACE(("\"%.30s(%.30s)\" <- \"%.30s\" => ",
+ part1, part2, O2S(valuePtr)));
+ }
+#endif
+ varPtr = TclObjLookupVar(interp, objPtr, part2,
+ TCL_LEAVE_ERR_MSG, "set",
+ /*createPart1*/ 1,
+ /*createPart2*/ 1, &arrayPtr);
+ if (varPtr == NULL) {
+ TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+ cleanup = ((part2 == NULL)? 2 : 3);
+ pcAdjustment = 1;
+ goto doCallPtrSetVar;
+
+ case INST_LAPPEND_ARRAY4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ pcAdjustment = 5;
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE
+ | TCL_LIST_ELEMENT | TCL_TRACE_READS);
+ goto doStoreArray;
+
+ case INST_LAPPEND_ARRAY1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ pcAdjustment = 2;
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE
+ | TCL_LIST_ELEMENT | TCL_TRACE_READS);
+ goto doStoreArray;
+
+ case INST_APPEND_ARRAY4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ pcAdjustment = 5;
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE);
+ goto doStoreArray;
+
+ case INST_APPEND_ARRAY1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ pcAdjustment = 2;
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE);
+ goto doStoreArray;
+
+ case INST_STORE_ARRAY4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ pcAdjustment = 5;
+ storeFlags = TCL_LEAVE_ERR_MSG;
+ goto doStoreArray;
+
+ case INST_STORE_ARRAY1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ pcAdjustment = 2;
+ storeFlags = TCL_LEAVE_ERR_MSG;
+
+ doStoreArray:
+ valuePtr = stackPtr[stackTop];
+ part2 = TclGetString(stackPtr[stackTop - 1]);
+ arrayPtr = &(varFramePtr->compiledLocals[opnd]);
+ part1 = arrayPtr->name;
+ TRACE(("%u \"%.30s\" <- \"%.30s\" => ",
+ opnd, part2, O2S(valuePtr)));
+ while (TclIsVarLink(arrayPtr)) {
+ arrayPtr = arrayPtr->value.linkPtr;
+ }
+ varPtr = TclLookupArrayElement(interp, part1, part2,
+ TCL_LEAVE_ERR_MSG, "set", 1, 1, arrayPtr);
+ if (varPtr == NULL) {
+ TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+ cleanup = 2;
+ goto doCallPtrSetVar;
+
+ case INST_LAPPEND_SCALAR4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ pcAdjustment = 5;
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE
+ | TCL_LIST_ELEMENT | TCL_TRACE_READS);
+ goto doStoreScalar;
+
+ case INST_LAPPEND_SCALAR1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ pcAdjustment = 2;
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE
+ | TCL_LIST_ELEMENT | TCL_TRACE_READS);
+ goto doStoreScalar;
+
+ case INST_APPEND_SCALAR4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ pcAdjustment = 5;
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE);
+ goto doStoreScalar;
+
+ case INST_APPEND_SCALAR1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ pcAdjustment = 2;
+ storeFlags = (TCL_LEAVE_ERR_MSG | TCL_APPEND_VALUE);
+ goto doStoreScalar;
+
+ case INST_STORE_SCALAR4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ pcAdjustment = 5;
+ storeFlags = TCL_LEAVE_ERR_MSG;
+ goto doStoreScalar;
+
+ case INST_STORE_SCALAR1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ pcAdjustment = 2;
+ storeFlags = TCL_LEAVE_ERR_MSG;
+
+ doStoreScalar:
+ valuePtr = stackPtr[stackTop];
+ varPtr = &(varFramePtr->compiledLocals[opnd]);
+ part1 = varPtr->name;
+ TRACE(("%u <- \"%.30s\" => ", opnd, O2S(valuePtr)));
+ while (TclIsVarLink(varPtr)) {
+ varPtr = varPtr->value.linkPtr;
+ }
+ cleanup = 1;
+ arrayPtr = NULL;
+ part2 = NULL;
+
+ doCallPtrSetVar:
+ if ((storeFlags == TCL_LEAVE_ERR_MSG)
+ && !((varPtr->flags & VAR_IN_HASHTABLE)
+ && (varPtr->hPtr == NULL))
+ && (varPtr->tracePtr == NULL)
+ && (TclIsVarScalar(varPtr)
+ || TclIsVarUndefined(varPtr))
+ && ((arrayPtr == NULL)
+ || (arrayPtr->tracePtr == NULL))) {
+ /*
+ * No traces, no errors, plain 'set': we can safely inline.
+ * The value *will* be set to what's requested, so that
+ * the stack top remains pointing to the same Tcl_Obj.
+ */
+ valuePtr = varPtr->value.objPtr;
+ objResultPtr = stackPtr[stackTop];
+ if (valuePtr != objResultPtr) {
+ if (valuePtr != NULL) {
+ TclDecrRefCount(valuePtr);
+ } else {
+ TclSetVarScalar(varPtr);
+ TclClearVarUndefined(varPtr);
}
+ varPtr->value.objPtr = objResultPtr;
+ Tcl_IncrRefCount(objResultPtr);
}
- i = valuePtr->internalRep.longValue;
+#ifndef TCL_COMPILE_DEBUG
+ if (*(pc+pcAdjustment) == INST_POP) {
+ NEXT_INST_V((pcAdjustment+1), cleanup, 0);
+ }
+#else
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+#endif
+ NEXT_INST_V(pcAdjustment, cleanup, 1);
+ } else {
DECACHE_STACK_INFO();
- value2Ptr = TclIncrIndexedScalar(interp, opnd, i);
+ objResultPtr = TclPtrSetVar(interp, varPtr, arrayPtr,
+ part1, part2, valuePtr, storeFlags);
CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("%u (by %ld) => ERROR: ", opnd, i),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(valuePtr);
+ if (objResultPtr == NULL) {
+ TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
result = TCL_ERROR;
goto checkForCatch;
}
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("%u (by %ld) => ", opnd, i), value2Ptr);
- TclDecrRefCount(valuePtr);
- ADJUST_PC(2);
+ }
+#ifndef TCL_COMPILE_DEBUG
+ if (*(pc+pcAdjustment) == INST_POP) {
+ NEXT_INST_V((pcAdjustment+1), cleanup, 0);
+ }
+#endif
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+ NEXT_INST_V(pcAdjustment, cleanup, 1);
- case INST_INCR_SCALAR_STK:
- case INST_INCR_STK:
- valuePtr = POP_OBJECT();
- objPtr = POP_OBJECT(); /* scalar name */
- if (valuePtr->typePtr != &tclIntType) {
- result = tclIntType.setFromAnyProc(interp, valuePtr);
- if (result != TCL_OK) {
- TRACE_WITH_OBJ(("\"%.30s\" (by %s) => ERROR converting increment amount to int: ",
- O2S(objPtr), O2S(valuePtr)),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(valuePtr);
- goto checkForCatch;
- }
- }
+
+ /*
+ * End of INST_STORE and related instructions.
+ * ---------------------------------------------------------
+ */
+
+ /*
+ * ---------------------------------------------------------
+ * Start of INST_INCR instructions.
+ *
+ * WARNING: more 'goto' here than your doctor recommended!
+ * The different instructions set the value of some variables
+ * and then jump to somme common execution code.
+ */
+
+ case INST_INCR_SCALAR1:
+ case INST_INCR_ARRAY1:
+ case INST_INCR_ARRAY_STK:
+ case INST_INCR_SCALAR_STK:
+ case INST_INCR_STK:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ valuePtr = stackPtr[stackTop];
+ if (valuePtr->typePtr == &tclIntType) {
i = valuePtr->internalRep.longValue;
- DECACHE_STACK_INFO();
- value2Ptr = TclIncrVar2(interp, objPtr, (Tcl_Obj *) NULL, i,
- TCL_LEAVE_ERR_MSG);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("\"%.30s\" (by %ld) => ERROR: ",
- O2S(objPtr), i), Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(valuePtr);
- result = TCL_ERROR;
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (valuePtr->typePtr == &tclWideIntType) {
+ i = Tcl_WideAsLong(valuePtr->internalRep.wideValue);
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ REQUIRE_WIDE_OR_INT(result, valuePtr, i, w);
+ if (result != TCL_OK) {
+ TRACE_WITH_OBJ(("%u (by %s) => ERROR converting increment amount to int: ",
+ opnd, O2S(valuePtr)), Tcl_GetObjResult(interp));
goto checkForCatch;
}
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("\"%.30s\" (by %ld) => ", O2S(objPtr), i),
- value2Ptr);
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(valuePtr);
- ADJUST_PC(1);
-
- case INST_INCR_ARRAY1:
- {
- Tcl_Obj *elemPtr;
-
- opnd = TclGetUInt1AtPtr(pc+1);
- valuePtr = POP_OBJECT();
- elemPtr = POP_OBJECT();
- if (valuePtr->typePtr != &tclIntType) {
- result = tclIntType.setFromAnyProc(interp, valuePtr);
- if (result != TCL_OK) {
- TRACE_WITH_OBJ(("%u \"%.30s\" (by %s) => ERROR converting increment amount to int: ",
- opnd, O2S(elemPtr), O2S(valuePtr)),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(elemPtr);
- Tcl_DecrRefCount(valuePtr);
- goto checkForCatch;
- }
- }
- i = valuePtr->internalRep.longValue;
- DECACHE_STACK_INFO();
- value2Ptr = TclIncrElementOfIndexedArray(interp, opnd,
- elemPtr, i);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ERROR: ",
- opnd, O2S(elemPtr), i),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(elemPtr);
- Tcl_DecrRefCount(valuePtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ",
- opnd, O2S(elemPtr), i), value2Ptr);
- Tcl_DecrRefCount(elemPtr);
- Tcl_DecrRefCount(valuePtr);
- }
- ADJUST_PC(2);
-
- case INST_INCR_ARRAY_STK:
- {
- Tcl_Obj *elemPtr;
+ FORCE_LONG(valuePtr, i, w);
+ }
+ stackTop--;
+ TclDecrRefCount(valuePtr);
+ switch (*pc) {
+ case INST_INCR_SCALAR1:
+ pcAdjustment = 2;
+ goto doIncrScalar;
+ case INST_INCR_ARRAY1:
+ pcAdjustment = 2;
+ goto doIncrArray;
+ default:
+ pcAdjustment = 1;
+ goto doIncrStk;
+ }
- valuePtr = POP_OBJECT();
- elemPtr = POP_OBJECT();
- objPtr = POP_OBJECT(); /* array name */
- if (valuePtr->typePtr != &tclIntType) {
- result = tclIntType.setFromAnyProc(interp, valuePtr);
- if (result != TCL_OK) {
- TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %s) => ERROR converting increment amount to int: ",
- O2S(objPtr), O2S(elemPtr), O2S(valuePtr)),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(elemPtr);
- Tcl_DecrRefCount(valuePtr);
- goto checkForCatch;
- }
- }
- i = valuePtr->internalRep.longValue;
- DECACHE_STACK_INFO();
- value2Ptr = TclIncrVar2(interp, objPtr, elemPtr, i,
- TCL_LEAVE_ERR_MSG);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ERROR: ",
- O2S(objPtr), O2S(elemPtr), i),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(elemPtr);
- Tcl_DecrRefCount(valuePtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ",
- O2S(objPtr), O2S(elemPtr), i), value2Ptr);
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(elemPtr);
- Tcl_DecrRefCount(valuePtr);
- }
- ADJUST_PC(1);
+ case INST_INCR_ARRAY_STK_IMM:
+ case INST_INCR_SCALAR_STK_IMM:
+ case INST_INCR_STK_IMM:
+ i = TclGetInt1AtPtr(pc+1);
+ pcAdjustment = 2;
- case INST_INCR_SCALAR1_IMM:
- opnd = TclGetUInt1AtPtr(pc+1);
- i = TclGetInt1AtPtr(pc+2);
- DECACHE_STACK_INFO();
- value2Ptr = TclIncrIndexedScalar(interp, opnd, i);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("%u %ld => ERROR: ", opnd, i),
- Tcl_GetObjResult(interp));
- result = TCL_ERROR;
- goto checkForCatch;
+ doIncrStk:
+ if ((*pc == INST_INCR_ARRAY_STK_IMM)
+ || (*pc == INST_INCR_ARRAY_STK)) {
+ part2 = TclGetString(stackPtr[stackTop]);
+ objPtr = stackPtr[stackTop - 1];
+ TRACE(("\"%.30s(%.30s)\" (by %ld) => ",
+ O2S(objPtr), part2, i));
+ } else {
+ part2 = NULL;
+ objPtr = stackPtr[stackTop];
+ TRACE(("\"%.30s\" (by %ld) => ", O2S(objPtr), i));
+ }
+ part1 = TclGetString(objPtr);
+
+ varPtr = TclObjLookupVar(interp, objPtr, part2,
+ TCL_LEAVE_ERR_MSG, "read", 0, 1, &arrayPtr);
+ if (varPtr == NULL) {
+ Tcl_AddObjErrorInfo(interp,
+ "\n (reading value of variable to increment)", -1);
+ TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+ cleanup = ((part2 == NULL)? 1 : 2);
+ goto doIncrVar;
+
+ case INST_INCR_ARRAY1_IMM:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ i = TclGetInt1AtPtr(pc+2);
+ pcAdjustment = 3;
+
+ doIncrArray:
+ part2 = TclGetString(stackPtr[stackTop]);
+ arrayPtr = &(varFramePtr->compiledLocals[opnd]);
+ part1 = arrayPtr->name;
+ while (TclIsVarLink(arrayPtr)) {
+ arrayPtr = arrayPtr->value.linkPtr;
+ }
+ TRACE(("%u \"%.30s\" (by %ld) => ",
+ opnd, part2, i));
+ varPtr = TclLookupArrayElement(interp, part1, part2,
+ TCL_LEAVE_ERR_MSG, "read", 0, 1, arrayPtr);
+ if (varPtr == NULL) {
+ TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+ cleanup = 1;
+ goto doIncrVar;
+
+ case INST_INCR_SCALAR1_IMM:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ i = TclGetInt1AtPtr(pc+2);
+ pcAdjustment = 3;
+
+ doIncrScalar:
+ varPtr = &(varFramePtr->compiledLocals[opnd]);
+ part1 = varPtr->name;
+ while (TclIsVarLink(varPtr)) {
+ varPtr = varPtr->value.linkPtr;
+ }
+ arrayPtr = NULL;
+ part2 = NULL;
+ cleanup = 0;
+ TRACE(("%u %ld => ", opnd, i));
+
+
+ doIncrVar:
+ objPtr = varPtr->value.objPtr;
+ if (TclIsVarScalar(varPtr)
+ && !TclIsVarUndefined(varPtr)
+ && (varPtr->tracePtr == NULL)
+ && ((arrayPtr == NULL)
+ || (arrayPtr->tracePtr == NULL))
+ && (objPtr->typePtr == &tclIntType)) {
+ /*
+ * No errors, no traces, the variable already has an
+ * integer value: inline processing.
+ */
+
+ i += objPtr->internalRep.longValue;
+ if (Tcl_IsShared(objPtr)) {
+ objResultPtr = Tcl_NewLongObj(i);
+ TclDecrRefCount(objPtr);
+ Tcl_IncrRefCount(objResultPtr);
+ varPtr->value.objPtr = objResultPtr;
+ } else {
+ Tcl_SetLongObj(objPtr, i);
+ objResultPtr = objPtr;
}
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("%u %ld => ", opnd, i), value2Ptr);
- ADJUST_PC(3);
-
- case INST_INCR_SCALAR_STK_IMM:
- case INST_INCR_STK_IMM:
- objPtr = POP_OBJECT(); /* variable name */
- i = TclGetInt1AtPtr(pc+1);
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+ } else {
DECACHE_STACK_INFO();
- value2Ptr = TclIncrVar2(interp, objPtr, (Tcl_Obj *) NULL, i,
- TCL_LEAVE_ERR_MSG);
+ objResultPtr = TclPtrIncrVar(interp, varPtr, arrayPtr, part1,
+ part2, i, TCL_LEAVE_ERR_MSG);
CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("\"%.30s\" %ld => ERROR: ",
- O2S(objPtr), i), Tcl_GetObjResult(interp));
+ if (objResultPtr == NULL) {
+ TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
result = TCL_ERROR;
- Tcl_DecrRefCount(objPtr);
goto checkForCatch;
}
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("\"%.30s\" %ld => ", O2S(objPtr), i),
- value2Ptr);
- TclDecrRefCount(objPtr);
- ADJUST_PC(2);
-
- case INST_INCR_ARRAY1_IMM:
- {
- Tcl_Obj *elemPtr;
-
- opnd = TclGetUInt1AtPtr(pc+1);
- i = TclGetInt1AtPtr(pc+2);
- elemPtr = POP_OBJECT();
- DECACHE_STACK_INFO();
- value2Ptr = TclIncrElementOfIndexedArray(interp, opnd,
- elemPtr, i);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ERROR: ",
- opnd, O2S(elemPtr), i),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(elemPtr);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ",
- opnd, O2S(elemPtr), i), value2Ptr);
- Tcl_DecrRefCount(elemPtr);
- }
- ADJUST_PC(3);
+ }
+ TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
+#ifndef TCL_COMPILE_DEBUG
+ if (*(pc+pcAdjustment) == INST_POP) {
+ NEXT_INST_V((pcAdjustment+1), cleanup, 0);
+ }
+#endif
+ NEXT_INST_V(pcAdjustment, cleanup, 1);
+
+ /*
+ * End of INST_INCR instructions.
+ * ---------------------------------------------------------
+ */
+
+
+ case INST_JUMP1:
+ opnd = TclGetInt1AtPtr(pc+1);
+ TRACE(("%d => new pc %u\n", opnd,
+ (unsigned int)(pc + opnd - codePtr->codeStart)));
+ NEXT_INST_F(opnd, 0, 0);
+
+ case INST_JUMP4:
+ opnd = TclGetInt4AtPtr(pc+1);
+ TRACE(("%d => new pc %u\n", opnd,
+ (unsigned int)(pc + opnd - codePtr->codeStart)));
+ NEXT_INST_F(opnd, 0, 0);
+
+ case INST_JUMP_FALSE4:
+ opnd = 5; /* TRUE */
+ pcAdjustment = TclGetInt4AtPtr(pc+1); /* FALSE */
+ goto doJumpTrue;
+
+ case INST_JUMP_TRUE4:
+ opnd = TclGetInt4AtPtr(pc+1); /* TRUE */
+ pcAdjustment = 5; /* FALSE */
+ goto doJumpTrue;
+
+ case INST_JUMP_FALSE1:
+ opnd = 2; /* TRUE */
+ pcAdjustment = TclGetInt1AtPtr(pc+1); /* FALSE */
+ goto doJumpTrue;
+
+ case INST_JUMP_TRUE1:
+ opnd = TclGetInt1AtPtr(pc+1); /* TRUE */
+ pcAdjustment = 2; /* FALSE */
- case INST_INCR_ARRAY_STK_IMM:
- {
- Tcl_Obj *elemPtr;
-
- i = TclGetInt1AtPtr(pc+1);
- elemPtr = POP_OBJECT();
- objPtr = POP_OBJECT(); /* array name */
- DECACHE_STACK_INFO();
- value2Ptr = TclIncrVar2(interp, objPtr, elemPtr, i,
- TCL_LEAVE_ERR_MSG);
- CACHE_STACK_INFO();
- if (value2Ptr == NULL) {
- TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ERROR: ",
- O2S(objPtr), O2S(elemPtr), i),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(elemPtr);
- result = TCL_ERROR;
+ doJumpTrue:
+ {
+ int b;
+
+ valuePtr = stackPtr[stackTop];
+ if (valuePtr->typePtr == &tclIntType) {
+ b = (valuePtr->internalRep.longValue != 0);
+ } else if (valuePtr->typePtr == &tclDoubleType) {
+ b = (valuePtr->internalRep.doubleValue != 0.0);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (valuePtr->typePtr == &tclWideIntType) {
+ b = (valuePtr->internalRep.wideValue != W0);
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ result = Tcl_GetBooleanFromObj(interp, valuePtr, &b);
+ if (result != TCL_OK) {
+ TRACE_WITH_OBJ(("%d => ERROR: ", opnd), Tcl_GetObjResult(interp));
goto checkForCatch;
}
- PUSH_OBJECT(value2Ptr);
- TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ",
- O2S(objPtr), O2S(elemPtr), i), value2Ptr);
- Tcl_DecrRefCount(objPtr);
- Tcl_DecrRefCount(elemPtr);
}
- ADJUST_PC(2);
-
- case INST_JUMP1:
-#ifdef TCL_COMPILE_DEBUG
- opnd = TclGetInt1AtPtr(pc+1);
- TRACE(("%d => new pc %u\n", opnd,
- (unsigned int)(pc + opnd - codePtr->codeStart)));
- pc += opnd;
+#ifndef TCL_COMPILE_DEBUG
+ NEXT_INST_F((b? opnd : pcAdjustment), 1, 0);
#else
- pc += TclGetInt1AtPtr(pc+1);
-#endif /* TCL_COMPILE_DEBUG */
- continue;
-
- case INST_JUMP4:
- opnd = TclGetInt4AtPtr(pc+1);
- TRACE(("%d => new pc %u\n", opnd,
- (unsigned int)(pc + opnd - codePtr->codeStart)));
- ADJUST_PC(opnd);
-
- case INST_JUMP_TRUE4:
- opnd = TclGetInt4AtPtr(pc+1);
- pcAdjustment = 5;
- goto doJumpTrue;
-
- case INST_JUMP_TRUE1:
- opnd = TclGetInt1AtPtr(pc+1);
- pcAdjustment = 2;
-
- doJumpTrue:
- {
- int b;
-
- valuePtr = POP_OBJECT();
- if (valuePtr->typePtr == &tclIntType) {
- b = (valuePtr->internalRep.longValue != 0);
- } else if (valuePtr->typePtr == &tclDoubleType) {
- b = (valuePtr->internalRep.doubleValue != 0.0);
- } else {
- result = Tcl_GetBooleanFromObj(interp, valuePtr, &b);
- if (result != TCL_OK) {
- TRACE_WITH_OBJ(("%d => ERROR: ", opnd),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(valuePtr);
- goto checkForCatch;
- }
- }
- if (b) {
- TRACE(("%d => %.20s true, new pc %u\n",
- opnd, O2S(valuePtr),
+ if (b) {
+ if ((*pc == INST_JUMP_TRUE1) || (*pc == INST_JUMP_TRUE1)) {
+ TRACE(("%d => %.20s true, new pc %u\n", opnd, O2S(valuePtr),
(unsigned int)(pc+opnd - codePtr->codeStart)));
- TclDecrRefCount(valuePtr);
- ADJUST_PC(opnd);
} else {
+ TRACE(("%d => %.20s true\n", pcAdjustment, O2S(valuePtr)));
+ }
+ NEXT_INST_F(opnd, 1, 0);
+ } else {
+ if ((*pc == INST_JUMP_TRUE1) || (*pc == INST_JUMP_TRUE1)) {
TRACE(("%d => %.20s false\n", opnd, O2S(valuePtr)));
- TclDecrRefCount(valuePtr);
- ADJUST_PC(pcAdjustment);
+ } else {
+ opnd = pcAdjustment;
+ TRACE(("%d => %.20s false, new pc %u\n", opnd, O2S(valuePtr),
+ (unsigned int)(pc + opnd - codePtr->codeStart)));
}
+ NEXT_INST_F(pcAdjustment, 1, 0);
}
-
- case INST_JUMP_FALSE4:
- opnd = TclGetInt4AtPtr(pc+1);
- pcAdjustment = 5;
- goto doJumpFalse;
-
- case INST_JUMP_FALSE1:
- opnd = TclGetInt1AtPtr(pc+1);
- pcAdjustment = 2;
-
- doJumpFalse:
- {
- int b;
+#endif
+ }
+
+ case INST_LOR:
+ case INST_LAND:
+ {
+ /*
+ * Operands must be boolean or numeric. No int->double
+ * conversions are performed.
+ */
- valuePtr = POP_OBJECT();
+ int i1, i2;
+ int iResult;
+ char *s;
+ Tcl_ObjType *t1Ptr, *t2Ptr;
+
+ value2Ptr = stackPtr[stackTop];
+ valuePtr = stackPtr[stackTop - 1];;
+ t1Ptr = valuePtr->typePtr;
+ t2Ptr = value2Ptr->typePtr;
+
+ if ((t1Ptr == &tclIntType) || (t1Ptr == &tclBooleanType)) {
+ i1 = (valuePtr->internalRep.longValue != 0);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (t1Ptr == &tclWideIntType) {
+ i1 = (valuePtr->internalRep.wideValue != W0);
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else if (t1Ptr == &tclDoubleType) {
+ i1 = (valuePtr->internalRep.doubleValue != 0.0);
+ } else {
+ s = Tcl_GetStringFromObj(valuePtr, &length);
+ if (TclLooksLikeInt(s, length)) {
+#ifdef TCL_WIDE_INT_IS_LONG
+ result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
+ valuePtr, &i);
+ i1 = (i != 0);
+#else /* !TCL_WIDE_INT_IS_LONG */
+ GET_WIDE_OR_INT(result, valuePtr, i, w);
if (valuePtr->typePtr == &tclIntType) {
- b = (valuePtr->internalRep.longValue != 0);
- } else if (valuePtr->typePtr == &tclDoubleType) {
- b = (valuePtr->internalRep.doubleValue != 0.0);
+ i1 = (i != 0);
} else {
- result = Tcl_GetBooleanFromObj(interp, valuePtr, &b);
- if (result != TCL_OK) {
- TRACE_WITH_OBJ(("%d => ERROR: ", opnd),
- Tcl_GetObjResult(interp));
- Tcl_DecrRefCount(valuePtr);
- goto checkForCatch;
- }
- }
- if (b) {
- TRACE(("%d => %.20s true\n", opnd, O2S(valuePtr)));
- TclDecrRefCount(valuePtr);
- ADJUST_PC(pcAdjustment);
- } else {
- TRACE(("%d => %.20s false, new pc %u\n",
- opnd, O2S(valuePtr),
- (unsigned int)(pc + opnd - codePtr->codeStart)));
- TclDecrRefCount(valuePtr);
- ADJUST_PC(opnd);
+ i1 = (w != W0);
}
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ result = Tcl_GetBooleanFromObj((Tcl_Interp *) NULL,
+ valuePtr, &i1);
+ i1 = (i1 != 0);
}
-
- case INST_LOR:
- case INST_LAND:
- {
- /*
- * Operands must be boolean or numeric. No int->double
- * conversions are performed.
- */
-
- int i1, i2;
- int iResult;
- char *s;
- Tcl_ObjType *t1Ptr, *t2Ptr;
-
- value2Ptr = POP_OBJECT();
- valuePtr = POP_OBJECT();
- t1Ptr = valuePtr->typePtr;
- t2Ptr = value2Ptr->typePtr;
-
- if ((t1Ptr == &tclIntType) || (t1Ptr == &tclBooleanType)) {
- i1 = (valuePtr->internalRep.longValue != 0);
- } else if (t1Ptr == &tclDoubleType) {
- i1 = (valuePtr->internalRep.doubleValue != 0.0);
- } else {
- s = Tcl_GetStringFromObj(valuePtr, &length);
- if (TclLooksLikeInt(s, length)) {
- result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- valuePtr, &i);
- i1 = (i != 0);
- } else {
- result = Tcl_GetBooleanFromObj((Tcl_Interp *) NULL,
- valuePtr, &i1);
- i1 = (i1 != 0);
- }
- if (result != TCL_OK) {
- TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n",
- O2S(valuePtr),
- (t1Ptr? t1Ptr->name : "null")));
- IllegalExprOperandType(interp, pc, valuePtr);
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
- goto checkForCatch;
- }
- }
+ if (result != TCL_OK) {
+ TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr),
+ (t1Ptr? t1Ptr->name : "null")));
+ IllegalExprOperandType(interp, pc, valuePtr);
+ goto checkForCatch;
+ }
+ }
- if ((t2Ptr == &tclIntType) || (t2Ptr == &tclBooleanType)) {
- i2 = (value2Ptr->internalRep.longValue != 0);
- } else if (t2Ptr == &tclDoubleType) {
- i2 = (value2Ptr->internalRep.doubleValue != 0.0);
+ if ((t2Ptr == &tclIntType) || (t2Ptr == &tclBooleanType)) {
+ i2 = (value2Ptr->internalRep.longValue != 0);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (t2Ptr == &tclWideIntType) {
+ i2 = (value2Ptr->internalRep.wideValue != W0);
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else if (t2Ptr == &tclDoubleType) {
+ i2 = (value2Ptr->internalRep.doubleValue != 0.0);
+ } else {
+ s = Tcl_GetStringFromObj(value2Ptr, &length);
+ if (TclLooksLikeInt(s, length)) {
+#ifdef TCL_WIDE_INT_IS_LONG
+ result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
+ value2Ptr, &i);
+ i2 = (i != 0);
+#else /* !TCL_WIDE_INT_IS_LONG */
+ GET_WIDE_OR_INT(result, value2Ptr, i, w);
+ if (value2Ptr->typePtr == &tclIntType) {
+ i2 = (i != 0);
} else {
- s = Tcl_GetStringFromObj(value2Ptr, &length);
- if (TclLooksLikeInt(s, length)) {
- result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- value2Ptr, &i);
- i2 = (i != 0);
- } else {
- result = Tcl_GetBooleanFromObj((Tcl_Interp *) NULL,
- value2Ptr, &i2);
- }
- if (result != TCL_OK) {
- TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n",
- O2S(value2Ptr),
- (t2Ptr? t2Ptr->name : "null")));
- IllegalExprOperandType(interp, pc, value2Ptr);
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
- goto checkForCatch;
- }
+ i2 = (w != W0);
}
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ result = Tcl_GetBooleanFromObj((Tcl_Interp *) NULL, value2Ptr, &i2);
+ }
+ if (result != TCL_OK) {
+ TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(value2Ptr),
+ (t2Ptr? t2Ptr->name : "null")));
+ IllegalExprOperandType(interp, pc, value2Ptr);
+ goto checkForCatch;
+ }
+ }
+
+ /*
+ * Reuse the valuePtr object already on stack if possible.
+ */
+
+ if (*pc == INST_LOR) {
+ iResult = (i1 || i2);
+ } else {
+ iResult = (i1 && i2);
+ }
+ if (Tcl_IsShared(valuePtr)) {
+ objResultPtr = Tcl_NewLongObj(iResult);
+ TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), iResult));
+ NEXT_INST_F(1, 2, 1);
+ } else { /* reuse the valuePtr object */
+ TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), iResult));
+ Tcl_SetLongObj(valuePtr, iResult);
+ NEXT_INST_F(1, 1, 0);
+ }
+ }
+
+ /*
+ * ---------------------------------------------------------
+ * Start of INST_LIST and related instructions.
+ */
+
+ case INST_LIST:
+ /*
+ * Pop the opnd (objc) top stack elements into a new list obj
+ * and then decrement their ref counts.
+ */
+
+ opnd = TclGetUInt4AtPtr(pc+1);
+ objResultPtr = Tcl_NewListObj(opnd, &(stackPtr[stackTop - (opnd-1)]));
+ TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr);
+ NEXT_INST_V(5, opnd, 1);
+
+ case INST_LIST_LENGTH:
+ valuePtr = stackPtr[stackTop];
+
+ result = Tcl_ListObjLength(interp, valuePtr, &length);
+ if (result != TCL_OK) {
+ TRACE_WITH_OBJ(("%.30s => ERROR: ", O2S(valuePtr)),
+ Tcl_GetObjResult(interp));
+ goto checkForCatch;
+ }
+ objResultPtr = Tcl_NewIntObj(length);
+ TRACE(("%.20s => %d\n", O2S(valuePtr), length));
+ NEXT_INST_F(1, 1, 1);
+
+ case INST_LIST_INDEX:
+ /*** lindex with objc == 3 ***/
+ /*
+ * Pop the two operands
+ */
+ value2Ptr = stackPtr[stackTop];
+ valuePtr = stackPtr[stackTop- 1];
+
+ /*
+ * Extract the desired list element
+ */
+ objResultPtr = TclLindexList(interp, valuePtr, value2Ptr);
+ if (objResultPtr == NULL) {
+ TRACE_WITH_OBJ(("%.30s %.30s => ERROR: ", O2S(valuePtr), O2S(value2Ptr)),
+ Tcl_GetObjResult(interp));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+
+ /*
+ * Stash the list element on the stack
+ */
+ TRACE(("%.20s %.20s => %s\n",
+ O2S(valuePtr), O2S(value2Ptr), O2S(objResultPtr)));
+ NEXT_INST_F(1, 2, -1); /* already has the correct refCount */
+
+ case INST_LIST_INDEX_MULTI:
+ {
+ /*
+ * 'lindex' with multiple index args:
+ *
+ * Determine the count of index args.
+ */
+
+ int numIdx;
+
+ opnd = TclGetUInt4AtPtr(pc+1);
+ numIdx = opnd-1;
+
+ /*
+ * Do the 'lindex' operation.
+ */
+ objResultPtr = TclLindexFlat(interp, stackPtr[stackTop - numIdx],
+ numIdx, stackPtr + stackTop - numIdx + 1);
+
+ /*
+ * Check for errors
+ */
+ if (objResultPtr == NULL) {
+ TRACE_WITH_OBJ(("%d => ERROR: ", opnd), Tcl_GetObjResult(interp));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+
+ /*
+ * Set result
+ */
+ TRACE(("%d => %s\n", opnd, O2S(objResultPtr)));
+ NEXT_INST_V(5, opnd, -1);
+ }
+
+ case INST_LSET_FLAT:
+ {
+ /*
+ * Lset with 3, 5, or more args. Get the number
+ * of index args.
+ */
+ int numIdx;
+
+ opnd = TclGetUInt4AtPtr( pc + 1 );
+ numIdx = opnd - 2;
+
+ /*
+ * Get the old value of variable, and remove the stack ref.
+ * This is safe because the variable still references the
+ * object; the ref count will never go zero here.
+ */
+ value2Ptr = POP_OBJECT();
+ TclDecrRefCount(value2Ptr); /* This one should be done here */
+
+ /*
+ * Get the new element value.
+ */
+ valuePtr = stackPtr[stackTop];
+
+ /*
+ * Compute the new variable value
+ */
+ objResultPtr = TclLsetFlat(interp, value2Ptr, numIdx,
+ stackPtr + stackTop - numIdx, valuePtr);
+
+
+ /*
+ * Check for errors
+ */
+ if (objResultPtr == NULL) {
+ TRACE_WITH_OBJ(("%d => ERROR: ", opnd), Tcl_GetObjResult(interp));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+
+ /*
+ * Set result
+ */
+ TRACE(("%d => %s\n", opnd, O2S(objResultPtr)));
+ NEXT_INST_V(5, (numIdx+1), -1);
+ }
+
+ case INST_LSET_LIST:
+ /*
+ * 'lset' with 4 args.
+ *
+ * Get the old value of variable, and remove the stack ref.
+ * This is safe because the variable still references the
+ * object; the ref count will never go zero here.
+ */
+ objPtr = POP_OBJECT();
+ TclDecrRefCount(objPtr); /* This one should be done here */
+
+ /*
+ * Get the new element value, and the index list
+ */
+ valuePtr = stackPtr[stackTop];
+ value2Ptr = stackPtr[stackTop - 1];
+
+ /*
+ * Compute the new variable value
+ */
+ objResultPtr = TclLsetList(interp, objPtr, value2Ptr, valuePtr);
+
+ /*
+ * Check for errors
+ */
+ if (objResultPtr == NULL) {
+ TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ", O2S(value2Ptr)),
+ Tcl_GetObjResult(interp));
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+
+ /*
+ * Set result
+ */
+ TRACE(("=> %s\n", O2S(objResultPtr)));
+ NEXT_INST_F(1, 2, -1);
+
+ /*
+ * End of INST_LIST and related instructions.
+ * ---------------------------------------------------------
+ */
+
+ case INST_STR_EQ:
+ case INST_STR_NEQ:
+ {
+ /*
+ * String (in)equality check
+ */
+ int iResult;
+
+ value2Ptr = stackPtr[stackTop];
+ valuePtr = stackPtr[stackTop - 1];
+
+ if (valuePtr == value2Ptr) {
+ /*
+ * On the off-chance that the objects are the same,
+ * we don't really have to think hard about equality.
+ */
+ iResult = (*pc == INST_STR_EQ);
+ } else {
+ char *s1, *s2;
+ int s1len, s2len;
+
+ s1 = Tcl_GetStringFromObj(valuePtr, &s1len);
+ s2 = Tcl_GetStringFromObj(value2Ptr, &s2len);
+ if (s1len == s2len) {
/*
- * Reuse the valuePtr object already on stack if possible.
+ * We only need to check (in)equality when
+ * we have equal length strings.
*/
-
- if (*pc == INST_LOR) {
- iResult = (i1 || i2);
+ if (*pc == INST_STR_NEQ) {
+ iResult = (strcmp(s1, s2) != 0);
} else {
- iResult = (i1 && i2);
+ /* INST_STR_EQ */
+ iResult = (strcmp(s1, s2) == 0);
}
- if (Tcl_IsShared(valuePtr)) {
- PUSH_OBJECT(Tcl_NewLongObj(iResult));
- TRACE(("%.20s %.20s => %d\n",
- O2S(valuePtr), O2S(value2Ptr), iResult));
- TclDecrRefCount(valuePtr);
- } else { /* reuse the valuePtr object */
- TRACE(("%.20s %.20s => %d\n",
- O2S(valuePtr), O2S(value2Ptr), iResult));
- Tcl_SetLongObj(valuePtr, iResult);
- ++stackTop; /* valuePtr now on stk top has right r.c. */
- }
- TclDecrRefCount(value2Ptr);
+ } else {
+ iResult = (*pc == INST_STR_NEQ);
}
- ADJUST_PC(1);
-
- case INST_EQ:
- case INST_NEQ:
- case INST_LT:
- case INST_GT:
- case INST_LE:
- case INST_GE:
- {
+ }
+
+ TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), iResult));
+
+ /*
+ * Peep-hole optimisation: if you're about to jump, do jump
+ * from here.
+ */
+
+ pc++;
+#ifndef TCL_COMPILE_DEBUG
+ switch (*pc) {
+ case INST_JUMP_FALSE1:
+ NEXT_INST_F((iResult? 2 : TclGetInt1AtPtr(pc+1)), 2, 0);
+ case INST_JUMP_TRUE1:
+ NEXT_INST_F((iResult? TclGetInt1AtPtr(pc+1) : 2), 2, 0);
+ case INST_JUMP_FALSE4:
+ NEXT_INST_F((iResult? 5 : TclGetInt4AtPtr(pc+1)), 2, 0);
+ case INST_JUMP_TRUE4:
+ NEXT_INST_F((iResult? TclGetInt4AtPtr(pc+1) : 5), 2, 0);
+ }
+#endif
+ objResultPtr = Tcl_NewIntObj(iResult);
+ NEXT_INST_F(0, 2, 1);
+ }
+
+ case INST_STR_CMP:
+ {
+ /*
+ * String compare
+ */
+ CONST char *s1, *s2;
+ int s1len, s2len, iResult;
+
+ value2Ptr = stackPtr[stackTop];
+ valuePtr = stackPtr[stackTop - 1];
+
+ /*
+ * The comparison function should compare up to the
+ * minimum byte length only.
+ */
+ if (valuePtr == value2Ptr) {
+ /*
+ * In the pure equality case, set lengths too for
+ * the checks below (or we could goto beyond it).
+ */
+ iResult = s1len = s2len = 0;
+ } else if ((valuePtr->typePtr == &tclByteArrayType)
+ && (value2Ptr->typePtr == &tclByteArrayType)) {
+ s1 = (char *) Tcl_GetByteArrayFromObj(valuePtr, &s1len);
+ s2 = (char *) Tcl_GetByteArrayFromObj(value2Ptr, &s2len);
+ iResult = memcmp(s1, s2,
+ (size_t) ((s1len < s2len) ? s1len : s2len));
+ } else if (((valuePtr->typePtr == &tclStringType)
+ && (value2Ptr->typePtr == &tclStringType))) {
+ /*
+ * Do a unicode-specific comparison if both of the args
+ * are of String type. In benchmark testing this proved
+ * the most efficient check between the unicode and
+ * string comparison operations.
+ */
+ Tcl_UniChar *uni1, *uni2;
+ uni1 = Tcl_GetUnicodeFromObj(valuePtr, &s1len);
+ uni2 = Tcl_GetUnicodeFromObj(value2Ptr, &s2len);
+ iResult = TclUniCharNcmp(uni1, uni2,
+ (unsigned) ((s1len < s2len) ? s1len : s2len));
+ } else {
+ /*
+ * We can't do a simple memcmp in order to handle the
+ * special Tcl \xC0\x80 null encoding for utf-8.
+ */
+ s1 = Tcl_GetStringFromObj(valuePtr, &s1len);
+ s2 = Tcl_GetStringFromObj(value2Ptr, &s2len);
+ iResult = TclpUtfNcmp2(s1, s2,
+ (size_t) ((s1len < s2len) ? s1len : s2len));
+ }
+
+ /*
+ * Make sure only -1,0,1 is returned
+ */
+ if (iResult == 0) {
+ iResult = s1len - s2len;
+ }
+ if (iResult < 0) {
+ iResult = -1;
+ } else if (iResult > 0) {
+ iResult = 1;
+ }
+
+ objResultPtr = Tcl_NewIntObj(iResult);
+ TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), iResult));
+ NEXT_INST_F(1, 2, 1);
+ }
+
+ case INST_STR_LEN:
+ {
+ int length1;
+
+ valuePtr = stackPtr[stackTop];
+
+ if (valuePtr->typePtr == &tclByteArrayType) {
+ (void) Tcl_GetByteArrayFromObj(valuePtr, &length1);
+ } else {
+ length1 = Tcl_GetCharLength(valuePtr);
+ }
+ objResultPtr = Tcl_NewIntObj(length1);
+ TRACE(("%.20s => %d\n", O2S(valuePtr), length1));
+ NEXT_INST_F(1, 1, 1);
+ }
+
+ case INST_STR_INDEX:
+ {
+ /*
+ * String compare
+ */
+ int index;
+ bytes = NULL; /* lint */
+
+ value2Ptr = stackPtr[stackTop];
+ valuePtr = stackPtr[stackTop - 1];
+
+ /*
+ * If we have a ByteArray object, avoid indexing in the
+ * Utf string since the byte array contains one byte per
+ * character. Otherwise, use the Unicode string rep to
+ * get the index'th char.
+ */
+
+ if (valuePtr->typePtr == &tclByteArrayType) {
+ bytes = (char *)Tcl_GetByteArrayFromObj(valuePtr, &length);
+ } else {
+ /*
+ * Get Unicode char length to calulate what 'end' means.
+ */
+ length = Tcl_GetCharLength(valuePtr);
+ }
+
+ result = TclGetIntForIndex(interp, value2Ptr, length - 1, &index);
+ if (result != TCL_OK) {
+ goto checkForCatch;
+ }
+
+ if ((index >= 0) && (index < length)) {
+ if (valuePtr->typePtr == &tclByteArrayType) {
+ objResultPtr = Tcl_NewByteArrayObj((unsigned char *)
+ (&bytes[index]), 1);
+ } else {
+ char buf[TCL_UTF_MAX];
+ Tcl_UniChar ch;
+
+ ch = Tcl_GetUniChar(valuePtr, index);
/*
- * Any type is allowed but the two operands must have the
- * same type. We will compute value op value2.
+ * This could be:
+ * Tcl_NewUnicodeObj((CONST Tcl_UniChar *)&ch, 1)
+ * but creating the object as a string seems to be
+ * faster in practical use.
*/
+ length = Tcl_UniCharToUtf(ch, buf);
+ objResultPtr = Tcl_NewStringObj(buf, length);
+ }
+ } else {
+ TclNewObj(objResultPtr);
+ }
+
+ TRACE(("%.20s %.20s => %s\n", O2S(valuePtr), O2S(value2Ptr),
+ O2S(objResultPtr)));
+ NEXT_INST_F(1, 2, 1);
+ }
+
+ case INST_STR_MATCH:
+ {
+ int nocase, match;
+
+ nocase = TclGetInt1AtPtr(pc+1);
+ valuePtr = stackPtr[stackTop]; /* String */
+ value2Ptr = stackPtr[stackTop - 1]; /* Pattern */
+
+ /*
+ * Check that at least one of the objects is Unicode before
+ * promoting both.
+ */
+ if ((valuePtr->typePtr == &tclStringType)
+ || (value2Ptr->typePtr == &tclStringType)) {
+ match = Tcl_UniCharCaseMatch(Tcl_GetUnicode(valuePtr),
+ Tcl_GetUnicode(value2Ptr), nocase);
+ } else {
+ match = Tcl_StringCaseMatch(TclGetString(valuePtr),
+ TclGetString(value2Ptr), nocase);
+ }
+
+ /*
+ * Reuse value2Ptr object already on stack if possible.
+ * Adjustment is 2 due to the nocase byte
+ */
+
+ TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), match));
+ if (Tcl_IsShared(value2Ptr)) {
+ objResultPtr = Tcl_NewIntObj(match);
+ NEXT_INST_F(2, 2, 1);
+ } else { /* reuse the valuePtr object */
+ Tcl_SetIntObj(value2Ptr, match);
+ NEXT_INST_F(2, 1, 0);
+ }
+ }
+
+ case INST_EQ:
+ case INST_NEQ:
+ case INST_LT:
+ case INST_GT:
+ case INST_LE:
+ case INST_GE:
+ {
+ /*
+ * Any type is allowed but the two operands must have the
+ * same type. We will compute value op value2.
+ */
- Tcl_ObjType *t1Ptr, *t2Ptr;
- char *s1 = NULL; /* Init. avoids compiler warning. */
- char *s2 = NULL; /* Init. avoids compiler warning. */
- long i2 = 0; /* Init. avoids compiler warning. */
- double d1 = 0.0; /* Init. avoids compiler warning. */
- double d2 = 0.0; /* Init. avoids compiler warning. */
- long iResult = 0; /* Init. avoids compiler warning. */
+ Tcl_ObjType *t1Ptr, *t2Ptr;
+ char *s1 = NULL; /* Init. avoids compiler warning. */
+ char *s2 = NULL; /* Init. avoids compiler warning. */
+ long i2 = 0; /* Init. avoids compiler warning. */
+ double d1 = 0.0; /* Init. avoids compiler warning. */
+ double d2 = 0.0; /* Init. avoids compiler warning. */
+ long iResult = 0; /* Init. avoids compiler warning. */
- value2Ptr = POP_OBJECT();
- valuePtr = POP_OBJECT();
+ value2Ptr = stackPtr[stackTop];
+ valuePtr = stackPtr[stackTop - 1];
+
+ if (valuePtr == value2Ptr) {
+ /*
+ * Optimize the equal object case.
+ */
+ switch (*pc) {
+ case INST_EQ:
+ case INST_LE:
+ case INST_GE:
+ iResult = 1;
+ break;
+ case INST_NEQ:
+ case INST_LT:
+ case INST_GT:
+ iResult = 0;
+ break;
+ }
+ goto foundResult;
+ }
+
+ t1Ptr = valuePtr->typePtr;
+ t2Ptr = value2Ptr->typePtr;
+
+ /*
+ * We only want to coerce numeric validation if neither type
+ * is NULL. A NULL type means the arg is essentially an empty
+ * object ("", {} or [list]).
+ */
+ if (!( (!t1Ptr && !valuePtr->bytes)
+ || (valuePtr->bytes && !valuePtr->length)
+ || (!t2Ptr && !value2Ptr->bytes)
+ || (value2Ptr->bytes && !value2Ptr->length))) {
+ if (!IS_NUMERIC_TYPE(t1Ptr)) {
+ s1 = Tcl_GetStringFromObj(valuePtr, &length);
+ if (TclLooksLikeInt(s1, length)) {
+ GET_WIDE_OR_INT(iResult, valuePtr, i, w);
+ } else {
+ (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
+ valuePtr, &d1);
+ }
t1Ptr = valuePtr->typePtr;
+ }
+ if (!IS_NUMERIC_TYPE(t2Ptr)) {
+ s2 = Tcl_GetStringFromObj(value2Ptr, &length);
+ if (TclLooksLikeInt(s2, length)) {
+ GET_WIDE_OR_INT(iResult, value2Ptr, i2, w);
+ } else {
+ (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
+ value2Ptr, &d2);
+ }
t2Ptr = value2Ptr->typePtr;
-
- /*
- * We only want to coerce numeric validation if
- * neither type is NULL. A NULL type means the arg is
- * essentially an empty object ("", {} or [list]).
- */
- if (!((((t1Ptr == NULL) && (valuePtr->bytes == NULL))
- || (valuePtr->bytes && (valuePtr->length == 0)))
- || (((t2Ptr == NULL) && (value2Ptr->bytes == NULL))
- || (value2Ptr->bytes && (value2Ptr->length == 0))))) {
- if ((t1Ptr != &tclIntType) && (t1Ptr != &tclDoubleType)) {
- s1 = Tcl_GetStringFromObj(valuePtr, &length);
- if (TclLooksLikeInt(s1, length)) {
- (void) Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- valuePtr, &i);
- } else {
- (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
- valuePtr, &d1);
- }
- t1Ptr = valuePtr->typePtr;
+ }
+ }
+ if (!IS_NUMERIC_TYPE(t1Ptr) || !IS_NUMERIC_TYPE(t2Ptr)) {
+ /*
+ * One operand is not numeric. Compare as strings. NOTE:
+ * strcmp is not correct for \x00 < \x01, but that is
+ * unlikely to occur here. We could use the TclUtfNCmp2
+ * to handle this.
+ */
+ int s1len, s2len;
+ s1 = Tcl_GetStringFromObj(valuePtr, &s1len);
+ s2 = Tcl_GetStringFromObj(value2Ptr, &s2len);
+ switch (*pc) {
+ case INST_EQ:
+ if (s1len == s2len) {
+ iResult = (strcmp(s1, s2) == 0);
+ } else {
+ iResult = 0;
}
- if ((t2Ptr != &tclIntType) && (t2Ptr != &tclDoubleType)) {
- s2 = Tcl_GetStringFromObj(value2Ptr, &length);
- if (TclLooksLikeInt(s2, length)) {
- (void) Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- value2Ptr, &i2);
- } else {
- (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
- value2Ptr, &d2);
- }
- t2Ptr = value2Ptr->typePtr;
+ break;
+ case INST_NEQ:
+ if (s1len == s2len) {
+ iResult = (strcmp(s1, s2) != 0);
+ } else {
+ iResult = 1;
}
+ break;
+ case INST_LT:
+ iResult = (strcmp(s1, s2) < 0);
+ break;
+ case INST_GT:
+ iResult = (strcmp(s1, s2) > 0);
+ break;
+ case INST_LE:
+ iResult = (strcmp(s1, s2) <= 0);
+ break;
+ case INST_GE:
+ iResult = (strcmp(s1, s2) >= 0);
+ break;
+ }
+ } else if ((t1Ptr == &tclDoubleType)
+ || (t2Ptr == &tclDoubleType)) {
+ /*
+ * Compare as doubles.
+ */
+ if (t1Ptr == &tclDoubleType) {
+ d1 = valuePtr->internalRep.doubleValue;
+ GET_DOUBLE_VALUE(d2, value2Ptr, t2Ptr);
+ } else { /* t1Ptr is integer, t2Ptr is double */
+ GET_DOUBLE_VALUE(d1, valuePtr, t1Ptr);
+ d2 = value2Ptr->internalRep.doubleValue;
+ }
+ switch (*pc) {
+ case INST_EQ:
+ iResult = d1 == d2;
+ break;
+ case INST_NEQ:
+ iResult = d1 != d2;
+ break;
+ case INST_LT:
+ iResult = d1 < d2;
+ break;
+ case INST_GT:
+ iResult = d1 > d2;
+ break;
+ case INST_LE:
+ iResult = d1 <= d2;
+ break;
+ case INST_GE:
+ iResult = d1 >= d2;
+ break;
+ }
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if ((t1Ptr == &tclWideIntType)
+ || (t2Ptr == &tclWideIntType)) {
+ Tcl_WideInt w2;
+ /*
+ * Compare as wide ints (neither are doubles)
+ */
+ if (t1Ptr == &tclIntType) {
+ w = Tcl_LongAsWide(valuePtr->internalRep.longValue);
+ w2 = value2Ptr->internalRep.wideValue;
+ } else if (t2Ptr == &tclIntType) {
+ w = valuePtr->internalRep.wideValue;
+ w2 = Tcl_LongAsWide(value2Ptr->internalRep.longValue);
+ } else {
+ w = valuePtr->internalRep.wideValue;
+ w2 = value2Ptr->internalRep.wideValue;
+ }
+ switch (*pc) {
+ case INST_EQ:
+ iResult = w == w2;
+ break;
+ case INST_NEQ:
+ iResult = w != w2;
+ break;
+ case INST_LT:
+ iResult = w < w2;
+ break;
+ case INST_GT:
+ iResult = w > w2;
+ break;
+ case INST_LE:
+ iResult = w <= w2;
+ break;
+ case INST_GE:
+ iResult = w >= w2;
+ break;
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ /*
+ * Compare as ints.
+ */
+ i = valuePtr->internalRep.longValue;
+ i2 = value2Ptr->internalRep.longValue;
+ switch (*pc) {
+ case INST_EQ:
+ iResult = i == i2;
+ break;
+ case INST_NEQ:
+ iResult = i != i2;
+ break;
+ case INST_LT:
+ iResult = i < i2;
+ break;
+ case INST_GT:
+ iResult = i > i2;
+ break;
+ case INST_LE:
+ iResult = i <= i2;
+ break;
+ case INST_GE:
+ iResult = i >= i2;
+ break;
+ }
+ }
+
+ foundResult:
+ TRACE(("%.20s %.20s => %ld\n", O2S(valuePtr), O2S(value2Ptr), iResult));
+
+ /*
+ * Peep-hole optimisation: if you're about to jump, do jump
+ * from here.
+ */
+
+ pc++;
+#ifndef TCL_COMPILE_DEBUG
+ switch (*pc) {
+ case INST_JUMP_FALSE1:
+ NEXT_INST_F((iResult? 2 : TclGetInt1AtPtr(pc+1)), 2, 0);
+ case INST_JUMP_TRUE1:
+ NEXT_INST_F((iResult? TclGetInt1AtPtr(pc+1) : 2), 2, 0);
+ case INST_JUMP_FALSE4:
+ NEXT_INST_F((iResult? 5 : TclGetInt4AtPtr(pc+1)), 2, 0);
+ case INST_JUMP_TRUE4:
+ NEXT_INST_F((iResult? TclGetInt4AtPtr(pc+1) : 5), 2, 0);
+ }
+#endif
+ objResultPtr = Tcl_NewIntObj(iResult);
+ NEXT_INST_F(0, 2, 1);
+ }
+
+ case INST_MOD:
+ case INST_LSHIFT:
+ case INST_RSHIFT:
+ case INST_BITOR:
+ case INST_BITXOR:
+ case INST_BITAND:
+ {
+ /*
+ * Only integers are allowed. We compute value op value2.
+ */
+
+ long i2 = 0, rem, negative;
+ long iResult = 0; /* Init. avoids compiler warning. */
+#ifndef TCL_WIDE_INT_IS_LONG
+ Tcl_WideInt w2, wResult = W0;
+ int doWide = 0;
+#endif /* TCL_WIDE_INT_IS_LONG */
+
+ value2Ptr = stackPtr[stackTop];
+ valuePtr = stackPtr[stackTop - 1];
+ if (valuePtr->typePtr == &tclIntType) {
+ i = valuePtr->internalRep.longValue;
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (valuePtr->typePtr == &tclWideIntType) {
+ w = valuePtr->internalRep.wideValue;
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else { /* try to convert to int */
+ REQUIRE_WIDE_OR_INT(result, valuePtr, i, w);
+ if (result != TCL_OK) {
+ TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n",
+ O2S(valuePtr), O2S(value2Ptr),
+ (valuePtr->typePtr?
+ valuePtr->typePtr->name : "null")));
+ IllegalExprOperandType(interp, pc, valuePtr);
+ goto checkForCatch;
+ }
+ }
+ if (value2Ptr->typePtr == &tclIntType) {
+ i2 = value2Ptr->internalRep.longValue;
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (value2Ptr->typePtr == &tclWideIntType) {
+ w2 = value2Ptr->internalRep.wideValue;
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ REQUIRE_WIDE_OR_INT(result, value2Ptr, i2, w2);
+ if (result != TCL_OK) {
+ TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n",
+ O2S(valuePtr), O2S(value2Ptr),
+ (value2Ptr->typePtr?
+ value2Ptr->typePtr->name : "null")));
+ IllegalExprOperandType(interp, pc, value2Ptr);
+ goto checkForCatch;
+ }
+ }
+
+ switch (*pc) {
+ case INST_MOD:
+ /*
+ * This code is tricky: C doesn't guarantee much about
+ * the quotient or remainder, but Tcl does. The
+ * remainder always has the same sign as the divisor and
+ * a smaller absolute value.
+ */
+#ifdef TCL_WIDE_INT_IS_LONG
+ if (i2 == 0) {
+ TRACE(("%ld %ld => DIVIDE BY ZERO\n", i, i2));
+ goto divideByZero;
+ }
+#else /* !TCL_WIDE_INT_IS_LONG */
+ if (value2Ptr->typePtr == &tclWideIntType && w2 == W0) {
+ if (valuePtr->typePtr == &tclIntType) {
+ LLTRACE(("%ld "LLD" => DIVIDE BY ZERO\n", i, w2));
+ } else {
+ LLTRACE((LLD" "LLD" => DIVIDE BY ZERO\n", w, w2));
}
- if (((t1Ptr != &tclIntType) && (t1Ptr != &tclDoubleType))
- || ((t2Ptr != &tclIntType) && (t2Ptr != &tclDoubleType))) {
- /*
- * One operand is not numeric. Compare as strings.
- */
- int cmpValue;
- s1 = Tcl_GetString(valuePtr);
- s2 = Tcl_GetString(value2Ptr);
- cmpValue = strcmp(s1, s2);
- switch (*pc) {
- case INST_EQ:
- iResult = (cmpValue == 0);
- break;
- case INST_NEQ:
- iResult = (cmpValue != 0);
- break;
- case INST_LT:
- iResult = (cmpValue < 0);
- break;
- case INST_GT:
- iResult = (cmpValue > 0);
- break;
- case INST_LE:
- iResult = (cmpValue <= 0);
- break;
- case INST_GE:
- iResult = (cmpValue >= 0);
- break;
- }
- } else if ((t1Ptr == &tclDoubleType)
- || (t2Ptr == &tclDoubleType)) {
- /*
- * Compare as doubles.
- */
- if (t1Ptr == &tclDoubleType) {
- d1 = valuePtr->internalRep.doubleValue;
- if (t2Ptr == &tclIntType) {
- d2 = value2Ptr->internalRep.longValue;
- } else {
- d2 = value2Ptr->internalRep.doubleValue;
- }
- } else { /* t1Ptr is int, t2Ptr is double */
- d1 = valuePtr->internalRep.longValue;
- d2 = value2Ptr->internalRep.doubleValue;
- }
- switch (*pc) {
- case INST_EQ:
- iResult = d1 == d2;
- break;
- case INST_NEQ:
- iResult = d1 != d2;
- break;
- case INST_LT:
- iResult = d1 < d2;
- break;
- case INST_GT:
- iResult = d1 > d2;
- break;
- case INST_LE:
- iResult = d1 <= d2;
- break;
- case INST_GE:
- iResult = d1 >= d2;
- break;
- }
+ goto divideByZero;
+ }
+ if (value2Ptr->typePtr == &tclIntType && i2 == 0) {
+ if (valuePtr->typePtr == &tclIntType) {
+ TRACE(("%ld %ld => DIVIDE BY ZERO\n", i, i2));
} else {
- /*
- * Compare as ints.
- */
- i = valuePtr->internalRep.longValue;
- i2 = value2Ptr->internalRep.longValue;
- switch (*pc) {
- case INST_EQ:
- iResult = i == i2;
- break;
- case INST_NEQ:
- iResult = i != i2;
- break;
- case INST_LT:
- iResult = i < i2;
- break;
- case INST_GT:
- iResult = i > i2;
- break;
- case INST_LE:
- iResult = i <= i2;
- break;
- case INST_GE:
- iResult = i >= i2;
- break;
- }
+ LLTRACE((LLD" %ld => DIVIDE BY ZERO\n", w, i2));
}
-
+ goto divideByZero;
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ negative = 0;
+#ifndef TCL_WIDE_INT_IS_LONG
+ if (valuePtr->typePtr == &tclWideIntType
+ || value2Ptr->typePtr == &tclWideIntType) {
+ Tcl_WideInt wRemainder;
/*
- * Reuse the valuePtr object already on stack if possible.
+ * Promote to wide
*/
-
- if (Tcl_IsShared(valuePtr)) {
- PUSH_OBJECT(Tcl_NewLongObj(iResult));
- TRACE(("%.20s %.20s => %ld\n",
- O2S(valuePtr), O2S(value2Ptr), iResult));
- TclDecrRefCount(valuePtr);
- } else { /* reuse the valuePtr object */
- TRACE(("%.20s %.20s => %ld\n",
- O2S(valuePtr), O2S(value2Ptr), iResult));
- Tcl_SetLongObj(valuePtr, iResult);
- ++stackTop; /* valuePtr now on stk top has right r.c. */
+ if (valuePtr->typePtr == &tclIntType) {
+ w = Tcl_LongAsWide(i);
+ } else if (value2Ptr->typePtr == &tclIntType) {
+ w2 = Tcl_LongAsWide(i2);
}
- TclDecrRefCount(value2Ptr);
+ if (w2 < 0) {
+ w2 = -w2;
+ w = -w;
+ negative = 1;
+ }
+ wRemainder = w % w2;
+ if (wRemainder < 0) {
+ wRemainder += w2;
+ }
+ if (negative) {
+ wRemainder = -wRemainder;
+ }
+ wResult = wRemainder;
+ doWide = 1;
+ break;
}
- ADJUST_PC(1);
-
- case INST_MOD:
+#endif /* TCL_WIDE_INT_IS_LONG */
+ if (i2 < 0) {
+ i2 = -i2;
+ i = -i;
+ negative = 1;
+ }
+ rem = i % i2;
+ if (rem < 0) {
+ rem += i2;
+ }
+ if (negative) {
+ rem = -rem;
+ }
+ iResult = rem;
+ break;
case INST_LSHIFT:
+#ifndef TCL_WIDE_INT_IS_LONG
+ /*
+ * Shifts are never usefully 64-bits wide!
+ */
+ FORCE_LONG(value2Ptr, i2, w2);
+ if (valuePtr->typePtr == &tclWideIntType) {
+#ifdef TCL_COMPILE_DEBUG
+ w2 = Tcl_LongAsWide(i2);
+#endif /* TCL_COMPILE_DEBUG */
+ wResult = w << i2;
+ doWide = 1;
+ break;
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ iResult = i << i2;
+ break;
case INST_RSHIFT:
+ /*
+ * The following code is a bit tricky: it ensures that
+ * right shifts propagate the sign bit even on machines
+ * where ">>" won't do it by default.
+ */
+#ifndef TCL_WIDE_INT_IS_LONG
+ /*
+ * Shifts are never usefully 64-bits wide!
+ */
+ FORCE_LONG(value2Ptr, i2, w2);
+ if (valuePtr->typePtr == &tclWideIntType) {
+#ifdef TCL_COMPILE_DEBUG
+ w2 = Tcl_LongAsWide(i2);
+#endif /* TCL_COMPILE_DEBUG */
+ if (w < 0) {
+ wResult = ~((~w) >> i2);
+ } else {
+ wResult = w >> i2;
+ }
+ doWide = 1;
+ break;
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ if (i < 0) {
+ iResult = ~((~i) >> i2);
+ } else {
+ iResult = i >> i2;
+ }
+ break;
case INST_BITOR:
+#ifndef TCL_WIDE_INT_IS_LONG
+ if (valuePtr->typePtr == &tclWideIntType
+ || value2Ptr->typePtr == &tclWideIntType) {
+ /*
+ * Promote to wide
+ */
+ if (valuePtr->typePtr == &tclIntType) {
+ w = Tcl_LongAsWide(i);
+ } else if (value2Ptr->typePtr == &tclIntType) {
+ w2 = Tcl_LongAsWide(i2);
+ }
+ wResult = w | w2;
+ doWide = 1;
+ break;
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ iResult = i | i2;
+ break;
case INST_BITXOR:
+#ifndef TCL_WIDE_INT_IS_LONG
+ if (valuePtr->typePtr == &tclWideIntType
+ || value2Ptr->typePtr == &tclWideIntType) {
+ /*
+ * Promote to wide
+ */
+ if (valuePtr->typePtr == &tclIntType) {
+ w = Tcl_LongAsWide(i);
+ } else if (value2Ptr->typePtr == &tclIntType) {
+ w2 = Tcl_LongAsWide(i2);
+ }
+ wResult = w ^ w2;
+ doWide = 1;
+ break;
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ iResult = i ^ i2;
+ break;
case INST_BITAND:
- {
+#ifndef TCL_WIDE_INT_IS_LONG
+ if (valuePtr->typePtr == &tclWideIntType
+ || value2Ptr->typePtr == &tclWideIntType) {
/*
- * Only integers are allowed. We compute value op value2.
+ * Promote to wide
*/
+ if (valuePtr->typePtr == &tclIntType) {
+ w = Tcl_LongAsWide(i);
+ } else if (value2Ptr->typePtr == &tclIntType) {
+ w2 = Tcl_LongAsWide(i2);
+ }
+ wResult = w & w2;
+ doWide = 1;
+ break;
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ iResult = i & i2;
+ break;
+ }
- long i2, rem, negative;
- long iResult = 0; /* Init. avoids compiler warning. */
+ /*
+ * Reuse the valuePtr object already on stack if possible.
+ */
- value2Ptr = POP_OBJECT();
- valuePtr = POP_OBJECT();
- if (valuePtr->typePtr == &tclIntType) {
- i = valuePtr->internalRep.longValue;
- } else { /* try to convert to int */
- result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- valuePtr, &i);
- if (result != TCL_OK) {
- TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n",
- O2S(valuePtr), O2S(value2Ptr),
- (valuePtr->typePtr?
- valuePtr->typePtr->name : "null")));
- IllegalExprOperandType(interp, pc, valuePtr);
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
- goto checkForCatch;
+ if (Tcl_IsShared(valuePtr)) {
+#ifndef TCL_WIDE_INT_IS_LONG
+ if (doWide) {
+ objResultPtr = Tcl_NewWideIntObj(wResult);
+ LLTRACE((LLD" "LLD" => "LLD"\n", w, w2, wResult));
+ } else {
+#endif /* TCL_WIDE_INT_IS_LONG */
+ objResultPtr = Tcl_NewLongObj(iResult);
+ TRACE(("%ld %ld => %ld\n", i, i2, iResult));
+#ifndef TCL_WIDE_INT_IS_LONG
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ NEXT_INST_F(1, 2, 1);
+ } else { /* reuse the valuePtr object */
+#ifndef TCL_WIDE_INT_IS_LONG
+ if (doWide) {
+ LLTRACE((LLD" "LLD" => "LLD"\n", w, w2, wResult));
+ Tcl_SetWideIntObj(valuePtr, wResult);
+ } else {
+#endif /* TCL_WIDE_INT_IS_LONG */
+ TRACE(("%ld %ld => %ld\n", i, i2, iResult));
+ Tcl_SetLongObj(valuePtr, iResult);
+#ifndef TCL_WIDE_INT_IS_LONG
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ NEXT_INST_F(1, 1, 0);
+ }
+ }
+
+ case INST_ADD:
+ case INST_SUB:
+ case INST_MULT:
+ case INST_DIV:
+ {
+ /*
+ * Operands must be numeric and ints get converted to floats
+ * if necessary. We compute value op value2.
+ */
+
+ Tcl_ObjType *t1Ptr, *t2Ptr;
+ long i2 = 0, quot, rem; /* Init. avoids compiler warning. */
+ double d1, d2;
+ long iResult = 0; /* Init. avoids compiler warning. */
+ double dResult = 0.0; /* Init. avoids compiler warning. */
+ int doDouble = 0; /* 1 if doing floating arithmetic */
+#ifndef TCL_WIDE_INT_IS_LONG
+ Tcl_WideInt w2, wquot, wrem;
+ Tcl_WideInt wResult = W0; /* Init. avoids compiler warning. */
+ int doWide = 0; /* 1 if doing wide arithmetic. */
+#endif /* TCL_WIDE_INT_IS_LONG */
+
+ value2Ptr = stackPtr[stackTop];
+ valuePtr = stackPtr[stackTop - 1];
+ t1Ptr = valuePtr->typePtr;
+ t2Ptr = value2Ptr->typePtr;
+
+ if (t1Ptr == &tclIntType) {
+ i = valuePtr->internalRep.longValue;
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (t1Ptr == &tclWideIntType) {
+ w = valuePtr->internalRep.wideValue;
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else if ((t1Ptr == &tclDoubleType)
+ && (valuePtr->bytes == NULL)) {
+ /*
+ * We can only use the internal rep directly if there is
+ * no string rep. Otherwise the string rep might actually
+ * look like an integer, which is preferred.
+ */
+
+ d1 = valuePtr->internalRep.doubleValue;
+ } else {
+ char *s = Tcl_GetStringFromObj(valuePtr, &length);
+ if (TclLooksLikeInt(s, length)) {
+ GET_WIDE_OR_INT(result, valuePtr, i, w);
+ } else {
+ result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
+ valuePtr, &d1);
+ }
+ if (result != TCL_OK) {
+ TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n",
+ s, O2S(valuePtr),
+ (valuePtr->typePtr?
+ valuePtr->typePtr->name : "null")));
+ IllegalExprOperandType(interp, pc, valuePtr);
+ goto checkForCatch;
+ }
+ t1Ptr = valuePtr->typePtr;
+ }
+
+ if (t2Ptr == &tclIntType) {
+ i2 = value2Ptr->internalRep.longValue;
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (t2Ptr == &tclWideIntType) {
+ w2 = value2Ptr->internalRep.wideValue;
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else if ((t2Ptr == &tclDoubleType)
+ && (value2Ptr->bytes == NULL)) {
+ /*
+ * We can only use the internal rep directly if there is
+ * no string rep. Otherwise the string rep might actually
+ * look like an integer, which is preferred.
+ */
+
+ d2 = value2Ptr->internalRep.doubleValue;
+ } else {
+ char *s = Tcl_GetStringFromObj(value2Ptr, &length);
+ if (TclLooksLikeInt(s, length)) {
+ GET_WIDE_OR_INT(result, value2Ptr, i2, w2);
+ } else {
+ result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
+ value2Ptr, &d2);
+ }
+ if (result != TCL_OK) {
+ TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n",
+ O2S(value2Ptr), s,
+ (value2Ptr->typePtr?
+ value2Ptr->typePtr->name : "null")));
+ IllegalExprOperandType(interp, pc, value2Ptr);
+ goto checkForCatch;
+ }
+ t2Ptr = value2Ptr->typePtr;
+ }
+
+ if ((t1Ptr == &tclDoubleType) || (t2Ptr == &tclDoubleType)) {
+ /*
+ * Do double arithmetic.
+ */
+ doDouble = 1;
+ if (t1Ptr == &tclIntType) {
+ d1 = i; /* promote value 1 to double */
+ } else if (t2Ptr == &tclIntType) {
+ d2 = i2; /* promote value 2 to double */
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (t1Ptr == &tclWideIntType) {
+ d1 = Tcl_WideAsDouble(w);
+ } else if (t2Ptr == &tclWideIntType) {
+ d2 = Tcl_WideAsDouble(w2);
+#endif /* TCL_WIDE_INT_IS_LONG */
+ }
+ switch (*pc) {
+ case INST_ADD:
+ dResult = d1 + d2;
+ break;
+ case INST_SUB:
+ dResult = d1 - d2;
+ break;
+ case INST_MULT:
+ dResult = d1 * d2;
+ break;
+ case INST_DIV:
+ if (d2 == 0.0) {
+ TRACE(("%.6g %.6g => DIVIDE BY ZERO\n", d1, d2));
+ goto divideByZero;
}
- }
- if (value2Ptr->typePtr == &tclIntType) {
- i2 = value2Ptr->internalRep.longValue;
- } else {
- result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- value2Ptr, &i2);
- if (result != TCL_OK) {
- TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n",
- O2S(valuePtr), O2S(value2Ptr),
- (value2Ptr->typePtr?
- value2Ptr->typePtr->name : "null")));
- IllegalExprOperandType(interp, pc, value2Ptr);
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
- goto checkForCatch;
+ dResult = d1 / d2;
+ break;
+ }
+
+ /*
+ * Check now for IEEE floating-point error.
+ */
+
+ if (IS_NAN(dResult) || IS_INF(dResult)) {
+ TRACE(("%.20s %.20s => IEEE FLOATING PT ERROR\n",
+ O2S(valuePtr), O2S(value2Ptr)));
+ TclExprFloatError(interp, dResult);
+ result = TCL_ERROR;
+ goto checkForCatch;
+ }
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if ((t1Ptr == &tclWideIntType)
+ || (t2Ptr == &tclWideIntType)) {
+ /*
+ * Do wide integer arithmetic.
+ */
+ doWide = 1;
+ if (t1Ptr == &tclIntType) {
+ w = Tcl_LongAsWide(i);
+ } else if (t2Ptr == &tclIntType) {
+ w2 = Tcl_LongAsWide(i2);
+ }
+ switch (*pc) {
+ case INST_ADD:
+ wResult = w + w2;
+ break;
+ case INST_SUB:
+ wResult = w - w2;
+ break;
+ case INST_MULT:
+ wResult = w * w2;
+ break;
+ case INST_DIV:
+ /*
+ * This code is tricky: C doesn't guarantee much
+ * about the quotient or remainder, but Tcl does.
+ * The remainder always has the same sign as the
+ * divisor and a smaller absolute value.
+ */
+ if (w2 == W0) {
+ LLTRACE((LLD" "LLD" => DIVIDE BY ZERO\n", w, w2));
+ goto divideByZero;
}
- }
-
- switch (*pc) {
- case INST_MOD:
+ if (w2 < 0) {
+ w2 = -w2;
+ w = -w;
+ }
+ wquot = w / w2;
+ wrem = w % w2;
+ if (wrem < W0) {
+ wquot -= 1;
+ }
+ wResult = wquot;
+ break;
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ /*
+ * Do integer arithmetic.
+ */
+ switch (*pc) {
+ case INST_ADD:
+ iResult = i + i2;
+ break;
+ case INST_SUB:
+ iResult = i - i2;
+ break;
+ case INST_MULT:
+ iResult = i * i2;
+ break;
+ case INST_DIV:
/*
- * This code is tricky: C doesn't guarantee much about
- * the quotient or remainder, but Tcl does. The
- * remainder always has the same sign as the divisor and
- * a smaller absolute value.
+ * This code is tricky: C doesn't guarantee much
+ * about the quotient or remainder, but Tcl does.
+ * The remainder always has the same sign as the
+ * divisor and a smaller absolute value.
*/
if (i2 == 0) {
TRACE(("%ld %ld => DIVIDE BY ZERO\n", i, i2));
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
goto divideByZero;
}
- negative = 0;
if (i2 < 0) {
i2 = -i2;
i = -i;
- negative = 1;
}
+ quot = i / i2;
rem = i % i2;
if (rem < 0) {
- rem += i2;
- }
- if (negative) {
- rem = -rem;
+ quot -= 1;
}
- iResult = rem;
+ iResult = quot;
break;
- case INST_LSHIFT:
- iResult = i << i2;
- break;
- case INST_RSHIFT:
- /*
- * The following code is a bit tricky: it ensures that
- * right shifts propagate the sign bit even on machines
- * where ">>" won't do it by default.
- */
- if (i < 0) {
- iResult = ~((~i) >> i2);
- } else {
- iResult = i >> i2;
- }
- break;
- case INST_BITOR:
- iResult = i | i2;
- break;
- case INST_BITXOR:
- iResult = i ^ i2;
- break;
- case INST_BITAND:
- iResult = i & i2;
- break;
- }
+ }
+ }
- /*
- * Reuse the valuePtr object already on stack if possible.
- */
+ /*
+ * Reuse the valuePtr object already on stack if possible.
+ */
- if (Tcl_IsShared(valuePtr)) {
- PUSH_OBJECT(Tcl_NewLongObj(iResult));
- TRACE(("%ld %ld => %ld\n", i, i2, iResult));
- TclDecrRefCount(valuePtr);
- } else { /* reuse the valuePtr object */
- TRACE(("%ld %ld => %ld\n", i, i2, iResult));
- Tcl_SetLongObj(valuePtr, iResult);
- ++stackTop; /* valuePtr now on stk top has right r.c. */
- }
- TclDecrRefCount(value2Ptr);
+ if (Tcl_IsShared(valuePtr)) {
+ if (doDouble) {
+ objResultPtr = Tcl_NewDoubleObj(dResult);
+ TRACE(("%.6g %.6g => %.6g\n", d1, d2, dResult));
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (doWide) {
+ objResultPtr = Tcl_NewWideIntObj(wResult);
+ LLTRACE((LLD" "LLD" => "LLD"\n", w, w2, wResult));
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ objResultPtr = Tcl_NewLongObj(iResult);
+ TRACE(("%ld %ld => %ld\n", i, i2, iResult));
+ }
+ NEXT_INST_F(1, 2, 1);
+ } else { /* reuse the valuePtr object */
+ if (doDouble) { /* NB: stack top is off by 1 */
+ TRACE(("%.6g %.6g => %.6g\n", d1, d2, dResult));
+ Tcl_SetDoubleObj(valuePtr, dResult);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (doWide) {
+ LLTRACE((LLD" "LLD" => "LLD"\n", w, w2, wResult));
+ Tcl_SetWideIntObj(valuePtr, wResult);
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ TRACE(("%ld %ld => %ld\n", i, i2, iResult));
+ Tcl_SetLongObj(valuePtr, iResult);
}
- ADJUST_PC(1);
-
- case INST_ADD:
- case INST_SUB:
- case INST_MULT:
- case INST_DIV:
- {
- /*
- * Operands must be numeric and ints get converted to floats
- * if necessary. We compute value op value2.
- */
+ NEXT_INST_F(1, 1, 0);
+ }
+ }
- Tcl_ObjType *t1Ptr, *t2Ptr;
- long i2, quot, rem;
- double d1, d2;
- long iResult = 0; /* Init. avoids compiler warning. */
- double dResult = 0.0; /* Init. avoids compiler warning. */
- int doDouble = 0; /* 1 if doing floating arithmetic */
-
- value2Ptr = POP_OBJECT();
- valuePtr = POP_OBJECT();
- t1Ptr = valuePtr->typePtr;
- t2Ptr = value2Ptr->typePtr;
+ case INST_UPLUS:
+ {
+ /*
+ * Operand must be numeric.
+ */
+
+ double d;
+ Tcl_ObjType *tPtr;
- if (t1Ptr == &tclIntType) {
- i = valuePtr->internalRep.longValue;
- } else if ((t1Ptr == &tclDoubleType)
- && (valuePtr->bytes == NULL)) {
- /*
- * We can only use the internal rep directly if there is
- * no string rep. Otherwise the string rep might actually
- * look like an integer, which is preferred.
- */
+ valuePtr = stackPtr[stackTop];
+ tPtr = valuePtr->typePtr;
+ if (!IS_INTEGER_TYPE(tPtr) && ((tPtr != &tclDoubleType)
+ || (valuePtr->bytes != NULL))) {
+ char *s = Tcl_GetStringFromObj(valuePtr, &length);
+ if (TclLooksLikeInt(s, length)) {
+ GET_WIDE_OR_INT(result, valuePtr, i, w);
+ } else {
+ result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, valuePtr, &d);
+ }
+ if (result != TCL_OK) {
+ TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n",
+ s, (tPtr? tPtr->name : "null")));
+ IllegalExprOperandType(interp, pc, valuePtr);
+ goto checkForCatch;
+ }
+ tPtr = valuePtr->typePtr;
+ }
- d1 = valuePtr->internalRep.doubleValue;
+ /*
+ * Ensure that the operand's string rep is the same as the
+ * formatted version of its internal rep. This makes sure
+ * that "expr +000123" yields "83", not "000123". We
+ * implement this by _discarding_ the string rep since we
+ * know it will be regenerated, if needed later, by
+ * formatting the internal rep's value.
+ */
+
+ if (Tcl_IsShared(valuePtr)) {
+ if (tPtr == &tclIntType) {
+ i = valuePtr->internalRep.longValue;
+ objResultPtr = Tcl_NewLongObj(i);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (tPtr == &tclWideIntType) {
+ w = valuePtr->internalRep.wideValue;
+ objResultPtr = Tcl_NewWideIntObj(w);
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ d = valuePtr->internalRep.doubleValue;
+ objResultPtr = Tcl_NewDoubleObj(d);
+ }
+ TRACE_WITH_OBJ(("%s => ", O2S(objResultPtr)), objResultPtr);
+ NEXT_INST_F(1, 1, 1);
+ } else {
+ Tcl_InvalidateStringRep(valuePtr);
+ TRACE_WITH_OBJ(("%s => ", O2S(valuePtr)), valuePtr);
+ NEXT_INST_F(1, 0, 0);
+ }
+ }
+
+ case INST_UMINUS:
+ case INST_LNOT:
+ {
+ /*
+ * The operand must be numeric or a boolean string as
+ * accepted by Tcl_GetBooleanFromObj(). If the operand
+ * object is unshared modify it directly, otherwise
+ * create a copy to modify: this is "copy on write".
+ * Free any old string representation since it is now
+ * invalid.
+ */
+
+ double d;
+ int boolvar;
+ Tcl_ObjType *tPtr;
+
+ valuePtr = stackPtr[stackTop];
+ tPtr = valuePtr->typePtr;
+ if (!IS_INTEGER_TYPE(tPtr) && ((tPtr != &tclDoubleType)
+ || (valuePtr->bytes != NULL))) {
+ if ((tPtr == &tclBooleanType) && (valuePtr->bytes == NULL)) {
+ valuePtr->typePtr = &tclIntType;
+ } else {
+ char *s = Tcl_GetStringFromObj(valuePtr, &length);
+ if (TclLooksLikeInt(s, length)) {
+ GET_WIDE_OR_INT(result, valuePtr, i, w);
} else {
- char *s = Tcl_GetStringFromObj(valuePtr, &length);
- if (TclLooksLikeInt(s, length)) {
- result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- valuePtr, &i);
- } else {
- result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
- valuePtr, &d1);
- }
- if (result != TCL_OK) {
- TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n",
- s, O2S(valuePtr),
- (valuePtr->typePtr?
- valuePtr->typePtr->name : "null")));
- IllegalExprOperandType(interp, pc, valuePtr);
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
- goto checkForCatch;
- }
- t1Ptr = valuePtr->typePtr;
+ result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
+ valuePtr, &d);
}
-
- if (t2Ptr == &tclIntType) {
- i2 = value2Ptr->internalRep.longValue;
- } else if ((t2Ptr == &tclDoubleType)
- && (value2Ptr->bytes == NULL)) {
- /*
- * We can only use the internal rep directly if there is
- * no string rep. Otherwise the string rep might actually
- * look like an integer, which is preferred.
- */
+ if (result == TCL_ERROR && *pc == INST_LNOT) {
+ result = Tcl_GetBooleanFromObj((Tcl_Interp *)NULL,
+ valuePtr, &boolvar);
+ i = (long)boolvar; /* i is long, not int! */
+ }
+ if (result != TCL_OK) {
+ TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n",
+ s, (tPtr? tPtr->name : "null")));
+ IllegalExprOperandType(interp, pc, valuePtr);
+ goto checkForCatch;
+ }
+ }
+ tPtr = valuePtr->typePtr;
+ }
- d2 = value2Ptr->internalRep.doubleValue;
+ if (Tcl_IsShared(valuePtr)) {
+ /*
+ * Create a new object.
+ */
+ if ((tPtr == &tclIntType) || (tPtr == &tclBooleanType)) {
+ i = valuePtr->internalRep.longValue;
+ objResultPtr = Tcl_NewLongObj(
+ (*pc == INST_UMINUS)? -i : !i);
+ TRACE_WITH_OBJ(("%ld => ", i), objResultPtr);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (tPtr == &tclWideIntType) {
+ w = valuePtr->internalRep.wideValue;
+ if (*pc == INST_UMINUS) {
+ objResultPtr = Tcl_NewWideIntObj(-w);
} else {
- char *s = Tcl_GetStringFromObj(value2Ptr, &length);
- if (TclLooksLikeInt(s, length)) {
- result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- value2Ptr, &i2);
- } else {
- result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
- value2Ptr, &d2);
- }
- if (result != TCL_OK) {
- TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n",
- O2S(value2Ptr), s,
- (value2Ptr->typePtr?
- value2Ptr->typePtr->name : "null")));
- IllegalExprOperandType(interp, pc, value2Ptr);
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
- goto checkForCatch;
- }
- t2Ptr = value2Ptr->typePtr;
+ objResultPtr = Tcl_NewLongObj(w == W0);
}
-
- if ((t1Ptr == &tclDoubleType) || (t2Ptr == &tclDoubleType)) {
- /*
- * Do double arithmetic.
- */
- doDouble = 1;
- if (t1Ptr == &tclIntType) {
- d1 = i; /* promote value 1 to double */
- } else if (t2Ptr == &tclIntType) {
- d2 = i2; /* promote value 2 to double */
- }
- switch (*pc) {
- case INST_ADD:
- dResult = d1 + d2;
- break;
- case INST_SUB:
- dResult = d1 - d2;
- break;
- case INST_MULT:
- dResult = d1 * d2;
- break;
- case INST_DIV:
- if (d2 == 0.0) {
- TRACE(("%.6g %.6g => DIVIDE BY ZERO\n", d1, d2));
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
- goto divideByZero;
- }
- dResult = d1 / d2;
- break;
- }
-
+ LLTRACE_WITH_OBJ((LLD" => ", w), objResultPtr);
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ d = valuePtr->internalRep.doubleValue;
+ if (*pc == INST_UMINUS) {
+ objResultPtr = Tcl_NewDoubleObj(-d);
+ } else {
/*
- * Check now for IEEE floating-point error.
+ * Should be able to use "!d", but apparently
+ * some compilers can't handle it.
*/
-
- if (IS_NAN(dResult) || IS_INF(dResult)) {
- TRACE(("%.20s %.20s => IEEE FLOATING PT ERROR\n",
- O2S(valuePtr), O2S(value2Ptr)));
- TclExprFloatError(interp, dResult);
- result = TCL_ERROR;
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
- goto checkForCatch;
- }
+ objResultPtr = Tcl_NewLongObj((d==0.0)? 1 : 0);
+ }
+ TRACE_WITH_OBJ(("%.6g => ", d), objResultPtr);
+ }
+ NEXT_INST_F(1, 1, 1);
+ } else {
+ /*
+ * valuePtr is unshared. Modify it directly.
+ */
+ if ((tPtr == &tclIntType) || (tPtr == &tclBooleanType)) {
+ i = valuePtr->internalRep.longValue;
+ Tcl_SetLongObj(valuePtr,
+ (*pc == INST_UMINUS)? -i : !i);
+ TRACE_WITH_OBJ(("%ld => ", i), valuePtr);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (tPtr == &tclWideIntType) {
+ w = valuePtr->internalRep.wideValue;
+ if (*pc == INST_UMINUS) {
+ Tcl_SetWideIntObj(valuePtr, -w);
+ } else {
+ Tcl_SetLongObj(valuePtr, w == W0);
+ }
+ LLTRACE_WITH_OBJ((LLD" => ", w), valuePtr);
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ d = valuePtr->internalRep.doubleValue;
+ if (*pc == INST_UMINUS) {
+ Tcl_SetDoubleObj(valuePtr, -d);
} else {
/*
- * Do integer arithmetic.
+ * Should be able to use "!d", but apparently
+ * some compilers can't handle it.
*/
- switch (*pc) {
- case INST_ADD:
- iResult = i + i2;
- break;
- case INST_SUB:
- iResult = i - i2;
- break;
- case INST_MULT:
- iResult = i * i2;
- break;
- case INST_DIV:
- /*
- * This code is tricky: C doesn't guarantee much
- * about the quotient or remainder, but Tcl does.
- * The remainder always has the same sign as the
- * divisor and a smaller absolute value.
- */
- if (i2 == 0) {
- TRACE(("%ld %ld => DIVIDE BY ZERO\n", i, i2));
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
- goto divideByZero;
- }
- if (i2 < 0) {
- i2 = -i2;
- i = -i;
- }
- quot = i / i2;
- rem = i % i2;
- if (rem < 0) {
- quot -= 1;
- }
- iResult = quot;
- break;
- }
+ Tcl_SetLongObj(valuePtr, (d==0.0)? 1 : 0);
}
+ TRACE_WITH_OBJ(("%.6g => ", d), valuePtr);
+ }
+ NEXT_INST_F(1, 0, 0);
+ }
+ }
- /*
- * Reuse the valuePtr object already on stack if possible.
- */
+ case INST_BITNOT:
+ {
+ /*
+ * The operand must be an integer. If the operand object is
+ * unshared modify it directly, otherwise modify a copy.
+ * Free any old string representation since it is now
+ * invalid.
+ */
- if (Tcl_IsShared(valuePtr)) {
- if (doDouble) {
- PUSH_OBJECT(Tcl_NewDoubleObj(dResult));
- TRACE(("%.6g %.6g => %.6g\n", d1, d2, dResult));
- } else {
- PUSH_OBJECT(Tcl_NewLongObj(iResult));
- TRACE(("%ld %ld => %ld\n", i, i2, iResult));
- }
- TclDecrRefCount(valuePtr);
- } else { /* reuse the valuePtr object */
- if (doDouble) { /* NB: stack top is off by 1 */
- TRACE(("%.6g %.6g => %.6g\n", d1, d2, dResult));
- Tcl_SetDoubleObj(valuePtr, dResult);
- } else {
- TRACE(("%ld %ld => %ld\n", i, i2, iResult));
- Tcl_SetLongObj(valuePtr, iResult);
- }
- ++stackTop; /* valuePtr now on stk top has right r.c. */
- }
- TclDecrRefCount(value2Ptr);
+ Tcl_ObjType *tPtr;
+
+ valuePtr = stackPtr[stackTop];
+ tPtr = valuePtr->typePtr;
+ if (!IS_INTEGER_TYPE(tPtr)) {
+ REQUIRE_WIDE_OR_INT(result, valuePtr, i, w);
+ if (result != TCL_OK) { /* try to convert to double */
+ TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n",
+ O2S(valuePtr), (tPtr? tPtr->name : "null")));
+ IllegalExprOperandType(interp, pc, valuePtr);
+ goto checkForCatch;
}
- ADJUST_PC(1);
-
- case INST_UPLUS:
- {
- /*
- * Operand must be numeric.
- */
-
- double d;
- Tcl_ObjType *tPtr;
+ }
- valuePtr = stackPtr[stackTop];
- tPtr = valuePtr->typePtr;
- if ((tPtr != &tclIntType) && ((tPtr != &tclDoubleType)
- || (valuePtr->bytes != NULL))) {
- char *s = Tcl_GetStringFromObj(valuePtr, &length);
- if (TclLooksLikeInt(s, length)) {
- result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- valuePtr, &i);
- } else {
- result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
- valuePtr, &d);
- }
- if (result != TCL_OK) {
- TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n",
- s, (tPtr? tPtr->name : "null")));
- IllegalExprOperandType(interp, pc, valuePtr);
- goto checkForCatch;
- }
- tPtr = valuePtr->typePtr;
- }
-
+#ifndef TCL_WIDE_INT_IS_LONG
+ if (valuePtr->typePtr == &tclWideIntType) {
+ w = valuePtr->internalRep.wideValue;
+ if (Tcl_IsShared(valuePtr)) {
+ objResultPtr = Tcl_NewWideIntObj(~w);
+ LLTRACE(("0x%llx => (%llu)\n", w, ~w));
+ NEXT_INST_F(1, 1, 1);
+ } else {
/*
- * Ensure that the operand's string rep is the same as the
- * formatted version of its internal rep. This makes sure
- * that "expr +000123" yields "83", not "000123". We
- * implement this by _discarding_ the string rep since we
- * know it will be regenerated, if needed later, by
- * formatting the internal rep's value.
+ * valuePtr is unshared. Modify it directly.
*/
-
- if (Tcl_IsShared(valuePtr)) {
- if (tPtr == &tclIntType) {
- i = valuePtr->internalRep.longValue;
- objPtr = Tcl_NewLongObj(i);
- } else {
- d = valuePtr->internalRep.doubleValue;
- objPtr = Tcl_NewDoubleObj(d);
- }
- Tcl_IncrRefCount(objPtr);
- Tcl_DecrRefCount(valuePtr);
- valuePtr = objPtr;
- stackPtr[stackTop] = valuePtr;
- } else {
- Tcl_InvalidateStringRep(valuePtr);
- }
- TRACE_WITH_OBJ(("%s => ", O2S(valuePtr)), valuePtr);
+ Tcl_SetWideIntObj(valuePtr, ~w);
+ LLTRACE(("0x%llx => (%llu)\n", w, ~w));
+ NEXT_INST_F(1, 0, 0);
}
- ADJUST_PC(1);
-
- case INST_UMINUS:
- case INST_LNOT:
- {
+ } else {
+#endif /* TCL_WIDE_INT_IS_LONG */
+ i = valuePtr->internalRep.longValue;
+ if (Tcl_IsShared(valuePtr)) {
+ objResultPtr = Tcl_NewLongObj(~i);
+ TRACE(("0x%lx => (%lu)\n", i, ~i));
+ NEXT_INST_F(1, 1, 1);
+ } else {
/*
- * The operand must be numeric. If the operand object is
- * unshared modify it directly, otherwise create a copy to
- * modify: this is "copy on write". free any old string
- * representation since it is now invalid.
+ * valuePtr is unshared. Modify it directly.
*/
+ Tcl_SetLongObj(valuePtr, ~i);
+ TRACE(("0x%lx => (%lu)\n", i, ~i));
+ NEXT_INST_F(1, 0, 0);
+ }
+#ifndef TCL_WIDE_INT_IS_LONG
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ }
+
+ case INST_CALL_BUILTIN_FUNC1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ {
+ /*
+ * Call one of the built-in Tcl math functions.
+ */
+
+ BuiltinFunc *mathFuncPtr;
+
+ if ((opnd < 0) || (opnd > LAST_BUILTIN_FUNC)) {
+ TRACE(("UNRECOGNIZED BUILTIN FUNC CODE %d\n", opnd));
+ panic("TclExecuteByteCode: unrecognized builtin function code %d", opnd);
+ }
+ mathFuncPtr = &(tclBuiltinFuncTable[opnd]);
+ DECACHE_STACK_INFO();
+ result = (*mathFuncPtr->proc)(interp, eePtr,
+ mathFuncPtr->clientData);
+ CACHE_STACK_INFO();
+ if (result != TCL_OK) {
+ goto checkForCatch;
+ }
+ TRACE_WITH_OBJ(("%d => ", opnd), stackPtr[stackTop]);
+ }
+ NEXT_INST_F(2, 0, 0);
+
+ case INST_CALL_FUNC1:
+ opnd = TclGetUInt1AtPtr(pc+1);
+ {
+ /*
+ * Call a non-builtin Tcl math function previously
+ * registered by a call to Tcl_CreateMathFunc.
+ */
- double d;
- Tcl_ObjType *tPtr;
-
- valuePtr = POP_OBJECT();
- tPtr = valuePtr->typePtr;
- if ((tPtr != &tclIntType) && ((tPtr != &tclDoubleType)
- || (valuePtr->bytes != NULL))) {
- if ((tPtr == &tclBooleanType)
- && (valuePtr->bytes == NULL)) {
- valuePtr->typePtr = &tclIntType;
- } else {
- char *s = Tcl_GetStringFromObj(valuePtr, &length);
- if (TclLooksLikeInt(s, length)) {
- result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- valuePtr, &i);
- } else {
- result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
- valuePtr, &d);
- }
- if (result != TCL_OK) {
- TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n",
- s, (tPtr? tPtr->name : "null")));
- IllegalExprOperandType(interp, pc, valuePtr);
- Tcl_DecrRefCount(valuePtr);
- goto checkForCatch;
- }
- }
- tPtr = valuePtr->typePtr;
- }
+ int objc = opnd; /* Number of arguments. The function name
+ * is the 0-th argument. */
+ Tcl_Obj **objv; /* The array of arguments. The function
+ * name is objv[0]. */
+
+ objv = &(stackPtr[stackTop - (objc-1)]); /* "objv[0]" */
+ DECACHE_STACK_INFO();
+ result = ExprCallMathFunc(interp, eePtr, objc, objv);
+ CACHE_STACK_INFO();
+ if (result != TCL_OK) {
+ goto checkForCatch;
+ }
+ TRACE_WITH_OBJ(("%d => ", objc), stackPtr[stackTop]);
+ }
+ NEXT_INST_F(2, 0, 0);
+
+ case INST_TRY_CVT_TO_NUMERIC:
+ {
+ /*
+ * Try to convert the topmost stack object to an int or
+ * double object. This is done in order to support Tcl's
+ * policy of interpreting operands if at all possible as
+ * first integers, else floating-point numbers.
+ */
- if (Tcl_IsShared(valuePtr)) {
- /*
- * Create a new object.
- */
- if (tPtr == &tclIntType) {
- i = valuePtr->internalRep.longValue;
- objPtr = Tcl_NewLongObj(
- (*pc == INST_UMINUS)? -i : !i);
- TRACE_WITH_OBJ(("%ld => ", i), objPtr);
- } else {
- d = valuePtr->internalRep.doubleValue;
- if (*pc == INST_UMINUS) {
- objPtr = Tcl_NewDoubleObj(-d);
- } else {
- /*
- * Should be able to use "!d", but apparently
- * some compilers can't handle it.
- */
- objPtr = Tcl_NewLongObj((d==0.0)? 1 : 0);
- }
- TRACE_WITH_OBJ(("%.6g => ", d), objPtr);
- }
- PUSH_OBJECT(objPtr);
- TclDecrRefCount(valuePtr);
+ double d;
+ char *s;
+ Tcl_ObjType *tPtr;
+ int converted, needNew;
+
+ valuePtr = stackPtr[stackTop];
+ tPtr = valuePtr->typePtr;
+ converted = 0;
+ if (!IS_INTEGER_TYPE(tPtr) && ((tPtr != &tclDoubleType)
+ || (valuePtr->bytes != NULL))) {
+ if ((tPtr == &tclBooleanType) && (valuePtr->bytes == NULL)) {
+ valuePtr->typePtr = &tclIntType;
+ converted = 1;
+ } else {
+ s = Tcl_GetStringFromObj(valuePtr, &length);
+ if (TclLooksLikeInt(s, length)) {
+ GET_WIDE_OR_INT(result, valuePtr, i, w);
} else {
+ result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
+ valuePtr, &d);
+ }
+ if (result == TCL_OK) {
+ converted = 1;
+ }
+ result = TCL_OK; /* reset the result variable */
+ }
+ tPtr = valuePtr->typePtr;
+ }
+
+ /*
+ * Ensure that the topmost stack object, if numeric, has a
+ * string rep the same as the formatted version of its
+ * internal rep. This is used, e.g., to make sure that "expr
+ * {0001}" yields "1", not "0001". We implement this by
+ * _discarding_ the string rep since we know it will be
+ * regenerated, if needed later, by formatting the internal
+ * rep's value. Also check if there has been an IEEE
+ * floating point error.
+ */
+
+ objResultPtr = valuePtr;
+ needNew = 0;
+ if (IS_NUMERIC_TYPE(tPtr)) {
+ if (Tcl_IsShared(valuePtr)) {
+ if (valuePtr->bytes != NULL) {
/*
- * valuePtr is unshared. Modify it directly.
+ * We only need to make a copy of the object
+ * when it already had a string rep
*/
+ needNew = 1;
if (tPtr == &tclIntType) {
i = valuePtr->internalRep.longValue;
- Tcl_SetLongObj(valuePtr,
- (*pc == INST_UMINUS)? -i : !i);
- TRACE_WITH_OBJ(("%ld => ", i), valuePtr);
+ objResultPtr = Tcl_NewLongObj(i);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (tPtr == &tclWideIntType) {
+ w = valuePtr->internalRep.wideValue;
+ objResultPtr = Tcl_NewWideIntObj(w);
+#endif /* TCL_WIDE_INT_IS_LONG */
} else {
d = valuePtr->internalRep.doubleValue;
- if (*pc == INST_UMINUS) {
- Tcl_SetDoubleObj(valuePtr, -d);
- } else {
- /*
- * Should be able to use "!d", but apparently
- * some compilers can't handle it.
- */
- Tcl_SetLongObj(valuePtr, (d==0.0)? 1 : 0);
- }
- TRACE_WITH_OBJ(("%.6g => ", d), valuePtr);
+ objResultPtr = Tcl_NewDoubleObj(d);
}
- ++stackTop; /* valuePtr now on stk top has right r.c. */
+ tPtr = objResultPtr->typePtr;
}
+ } else {
+ Tcl_InvalidateStringRep(valuePtr);
}
- ADJUST_PC(1);
-
- case INST_BITNOT:
- {
- /*
- * The operand must be an integer. If the operand object is
- * unshared modify it directly, otherwise modify a copy.
- * Free any old string representation since it is now
- * invalid.
- */
-
- Tcl_ObjType *tPtr;
-
- valuePtr = POP_OBJECT();
- tPtr = valuePtr->typePtr;
- if (tPtr != &tclIntType) {
- result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- valuePtr, &i);
- if (result != TCL_OK) { /* try to convert to double */
- TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n",
- O2S(valuePtr), (tPtr? tPtr->name : "null")));
- IllegalExprOperandType(interp, pc, valuePtr);
- Tcl_DecrRefCount(valuePtr);
- goto checkForCatch;
- }
- }
-
- i = valuePtr->internalRep.longValue;
- if (Tcl_IsShared(valuePtr)) {
- PUSH_OBJECT(Tcl_NewLongObj(~i));
- TRACE(("0x%lx => (%lu)\n", i, ~i));
- TclDecrRefCount(valuePtr);
- } else {
- /*
- * valuePtr is unshared. Modify it directly.
- */
- Tcl_SetLongObj(valuePtr, ~i);
- ++stackTop; /* valuePtr now on stk top has right r.c. */
- TRACE(("0x%lx => (%lu)\n", i, ~i));
- }
- }
- ADJUST_PC(1);
-
- case INST_CALL_BUILTIN_FUNC1:
- opnd = TclGetUInt1AtPtr(pc+1);
- {
- /*
- * Call one of the built-in Tcl math functions.
- */
-
- BuiltinFunc *mathFuncPtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if ((opnd < 0) || (opnd > LAST_BUILTIN_FUNC)) {
- TRACE(("UNRECOGNIZED BUILTIN FUNC CODE %d\n", opnd));
- panic("TclExecuteByteCode: unrecognized builtin function code %d", opnd);
- }
- mathFuncPtr = &(builtinFuncTable[opnd]);
- DECACHE_STACK_INFO();
- tsdPtr->mathInProgress++;
- result = (*mathFuncPtr->proc)(interp, eePtr,
- mathFuncPtr->clientData);
- tsdPtr->mathInProgress--;
- CACHE_STACK_INFO();
- if (result != TCL_OK) {
- goto checkForCatch;
- }
- TRACE_WITH_OBJ(("%d => ", opnd), stackPtr[stackTop]);
- }
- ADJUST_PC(2);
-
- case INST_CALL_FUNC1:
- opnd = TclGetUInt1AtPtr(pc+1);
- {
- /*
- * Call a non-builtin Tcl math function previously
- * registered by a call to Tcl_CreateMathFunc.
- */
- int objc = opnd; /* Number of arguments. The function name
- * is the 0-th argument. */
- Tcl_Obj **objv; /* The array of arguments. The function
- * name is objv[0]. */
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- objv = &(stackPtr[stackTop - (objc-1)]); /* "objv[0]" */
- DECACHE_STACK_INFO();
- tsdPtr->mathInProgress++;
- result = ExprCallMathFunc(interp, eePtr, objc, objv);
- tsdPtr->mathInProgress--;
- CACHE_STACK_INFO();
- if (result != TCL_OK) {
+ if (tPtr == &tclDoubleType) {
+ d = objResultPtr->internalRep.doubleValue;
+ if (IS_NAN(d) || IS_INF(d)) {
+ TRACE(("\"%.20s\" => IEEE FLOATING PT ERROR\n",
+ O2S(objResultPtr)));
+ TclExprFloatError(interp, d);
+ result = TCL_ERROR;
goto checkForCatch;
}
- TRACE_WITH_OBJ(("%d => ", objc), stackPtr[stackTop]);
- ADJUST_PC(2);
}
+ converted = converted; /* lint, converted not used. */
+ TRACE(("\"%.20s\" => numeric, %s, %s\n", O2S(valuePtr),
+ (converted? "converted" : "not converted"),
+ (needNew? "new Tcl_Obj" : "same Tcl_Obj")));
+ } else {
+ TRACE(("\"%.20s\" => not numeric\n", O2S(valuePtr)));
+ }
+ if (needNew) {
+ NEXT_INST_F(1, 1, 1);
+ } else {
+ NEXT_INST_F(1, 0, 0);
+ }
+ }
+
+ case INST_BREAK:
+ Tcl_ResetResult(interp);
+ result = TCL_BREAK;
+ cleanup = 0;
+ goto processExceptionReturn;
- case INST_TRY_CVT_TO_NUMERIC:
- {
- /*
- * Try to convert the topmost stack object to an int or
- * double object. This is done in order to support Tcl's
- * policy of interpreting operands if at all possible as
- * first integers, else floating-point numbers.
- */
-
- double d;
- char *s;
- Tcl_ObjType *tPtr;
- int converted, shared;
-
- valuePtr = stackPtr[stackTop];
- tPtr = valuePtr->typePtr;
- converted = 0;
- if ((tPtr != &tclIntType) && ((tPtr != &tclDoubleType)
- || (valuePtr->bytes != NULL))) {
- if ((tPtr == &tclBooleanType)
- && (valuePtr->bytes == NULL)) {
- valuePtr->typePtr = &tclIntType;
- converted = 1;
- } else {
- s = Tcl_GetStringFromObj(valuePtr, &length);
- if (TclLooksLikeInt(s, length)) {
- result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
- valuePtr, &i);
- } else {
- result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
- valuePtr, &d);
- }
- if (result == TCL_OK) {
- converted = 1;
- }
- result = TCL_OK; /* reset the result variable */
- }
- tPtr = valuePtr->typePtr;
- }
+ case INST_CONTINUE:
+ Tcl_ResetResult(interp);
+ result = TCL_CONTINUE;
+ cleanup = 0;
+ goto processExceptionReturn;
- /*
- * Ensure that the topmost stack object, if numeric, has a
- * string rep the same as the formatted version of its
- * internal rep. This is used, e.g., to make sure that "expr
- * {0001}" yields "1", not "0001". We implement this by
- * _discarding_ the string rep since we know it will be
- * regenerated, if needed later, by formatting the internal
- * rep's value. Also check if there has been an IEEE
- * floating point error.
- */
+ case INST_FOREACH_START4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ {
+ /*
+ * Initialize the temporary local var that holds the count
+ * of the number of iterations of the loop body to -1.
+ */
- if ((tPtr == &tclIntType) || (tPtr == &tclDoubleType)) {
- shared = 0;
- if (Tcl_IsShared(valuePtr)) {
- shared = 1;
- if (valuePtr->bytes != NULL) {
- /*
- * We only need to make a copy of the object
- * when it already had a string rep
- */
- if (tPtr == &tclIntType) {
- i = valuePtr->internalRep.longValue;
- objPtr = Tcl_NewLongObj(i);
- } else {
- d = valuePtr->internalRep.doubleValue;
- objPtr = Tcl_NewDoubleObj(d);
- }
- Tcl_IncrRefCount(objPtr);
- TclDecrRefCount(valuePtr);
- valuePtr = objPtr;
- stackPtr[stackTop] = valuePtr;
- tPtr = valuePtr->typePtr;
- }
- } else {
- Tcl_InvalidateStringRep(valuePtr);
- }
-
- if (tPtr == &tclDoubleType) {
- d = valuePtr->internalRep.doubleValue;
- if (IS_NAN(d) || IS_INF(d)) {
- TRACE(("\"%.20s\" => IEEE FLOATING PT ERROR\n",
- O2S(valuePtr)));
- TclExprFloatError(interp, d);
- result = TCL_ERROR;
- goto checkForCatch;
- }
- }
- shared = shared; /* lint, shared not used. */
- converted = converted; /* lint, converted not used. */
- TRACE(("\"%.20s\" => numeric, %s, %s\n", O2S(valuePtr),
- (converted? "converted" : "not converted"),
- (shared? "shared" : "not shared")));
- } else {
- TRACE(("\"%.20s\" => not numeric\n", O2S(valuePtr)));
- }
+ ForeachInfo *infoPtr = (ForeachInfo *)
+ codePtr->auxDataArrayPtr[opnd].clientData;
+ int iterTmpIndex = infoPtr->loopCtTemp;
+ Var *compiledLocals = iPtr->varFramePtr->compiledLocals;
+ Var *iterVarPtr = &(compiledLocals[iterTmpIndex]);
+ Tcl_Obj *oldValuePtr = iterVarPtr->value.objPtr;
+
+ if (oldValuePtr == NULL) {
+ iterVarPtr->value.objPtr = Tcl_NewLongObj(-1);
+ Tcl_IncrRefCount(iterVarPtr->value.objPtr);
+ } else {
+ Tcl_SetLongObj(oldValuePtr, -1);
}
- ADJUST_PC(1);
+ TclSetVarScalar(iterVarPtr);
+ TclClearVarUndefined(iterVarPtr);
+ TRACE(("%u => loop iter count temp %d\n",
+ opnd, iterTmpIndex));
+ }
+
+#ifndef TCL_COMPILE_DEBUG
+ /*
+ * Remark that the compiler ALWAYS sets INST_FOREACH_STEP4
+ * immediately after INST_FOREACH_START4 - let us just fall
+ * through instead of jumping back to the top.
+ */
- case INST_BREAK:
+ pc += 5;
+#else
+ NEXT_INST_F(5, 0, 0);
+#endif
+ case INST_FOREACH_STEP4:
+ opnd = TclGetUInt4AtPtr(pc+1);
+ {
/*
- * First reset the interpreter's result. Then find the closest
- * enclosing loop or catch exception range, if any. If a loop is
- * found, terminate its execution. If the closest is a catch
- * exception range, jump to its catchOffset. If no enclosing
- * range is found, stop execution and return TCL_BREAK.
+ * "Step" a foreach loop (i.e., begin its next iteration) by
+ * assigning the next value list element to each loop var.
*/
- Tcl_ResetResult(interp);
- rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, codePtr);
- if (rangePtr == NULL) {
- TRACE(("=> no encl. loop or catch, returning TCL_BREAK\n"));
- result = TCL_BREAK;
- goto abnormalReturn; /* no catch exists to check */
- }
- switch (rangePtr->type) {
- case LOOP_EXCEPTION_RANGE:
- result = TCL_OK;
- TRACE(("=> range at %d, new pc %d\n",
- rangePtr->codeOffset, rangePtr->breakOffset));
- break;
- case CATCH_EXCEPTION_RANGE:
- result = TCL_BREAK;
- TRACE(("=> ...\n"));
- goto processCatch; /* it will use rangePtr */
- default:
- panic("TclExecuteByteCode: unrecognized ExceptionRange type %d\n", rangePtr->type);
- }
- pc = (codePtr->codeStart + rangePtr->breakOffset);
- continue; /* restart outer instruction loop at pc */
-
- case INST_CONTINUE:
- /*
- * Find the closest enclosing loop or catch exception range,
- * if any. If a loop is found, skip to its next iteration.
- * If the closest is a catch exception range, jump to its
- * catchOffset. If no enclosing range is found, stop
- * execution and return TCL_CONTINUE.
+ ForeachInfo *infoPtr = (ForeachInfo *)
+ codePtr->auxDataArrayPtr[opnd].clientData;
+ ForeachVarList *varListPtr;
+ int numLists = infoPtr->numLists;
+ Var *compiledLocals = iPtr->varFramePtr->compiledLocals;
+ Tcl_Obj *listPtr;
+ List *listRepPtr;
+ Var *iterVarPtr, *listVarPtr;
+ int iterNum, listTmpIndex, listLen, numVars;
+ int varIndex, valIndex, continueLoop, j;
+
+ /*
+ * Increment the temp holding the loop iteration number.
*/
- Tcl_ResetResult(interp);
- rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, codePtr);
- if (rangePtr == NULL) {
- TRACE(("=> no encl. loop or catch, returning TCL_CONTINUE\n"));
- result = TCL_CONTINUE;
- goto abnormalReturn;
- }
- switch (rangePtr->type) {
- case LOOP_EXCEPTION_RANGE:
- if (rangePtr->continueOffset == -1) {
- TRACE(("=> loop w/o continue, checking for catch\n"));
+ iterVarPtr = &(compiledLocals[infoPtr->loopCtTemp]);
+ valuePtr = iterVarPtr->value.objPtr;
+ iterNum = (valuePtr->internalRep.longValue + 1);
+ Tcl_SetLongObj(valuePtr, iterNum);
+
+ /*
+ * Check whether all value lists are exhausted and we should
+ * stop the loop.
+ */
+
+ continueLoop = 0;
+ listTmpIndex = infoPtr->firstValueTemp;
+ for (i = 0; i < numLists; i++) {
+ varListPtr = infoPtr->varLists[i];
+ numVars = varListPtr->numVars;
+
+ listVarPtr = &(compiledLocals[listTmpIndex]);
+ listPtr = listVarPtr->value.objPtr;
+ result = Tcl_ListObjLength(interp, listPtr, &listLen);
+ if (result != TCL_OK) {
+ TRACE_WITH_OBJ(("%u => ERROR converting list %ld, \"%s\": ",
+ opnd, i, O2S(listPtr)), Tcl_GetObjResult(interp));
goto checkForCatch;
- } else {
- result = TCL_OK;
- TRACE(("=> range at %d, new pc %d\n",
- rangePtr->codeOffset, rangePtr->continueOffset));
}
- break;
- case CATCH_EXCEPTION_RANGE:
- result = TCL_CONTINUE;
- TRACE(("=> ...\n"));
- goto processCatch; /* it will use rangePtr */
- default:
- panic("TclExecuteByteCode: unrecognized ExceptionRange type %d\n", rangePtr->type);
- }
- pc = (codePtr->codeStart + rangePtr->continueOffset);
- continue; /* restart outer instruction loop at pc */
-
- case INST_FOREACH_START4:
- opnd = TclGetUInt4AtPtr(pc+1);
- {
- /*
- * Initialize the temporary local var that holds the count
- * of the number of iterations of the loop body to -1.
- */
-
- ForeachInfo *infoPtr = (ForeachInfo *)
- codePtr->auxDataArrayPtr[opnd].clientData;
- int iterTmpIndex = infoPtr->loopCtTemp;
- Var *compiledLocals = iPtr->varFramePtr->compiledLocals;
- Var *iterVarPtr = &(compiledLocals[iterTmpIndex]);
- Tcl_Obj *oldValuePtr = iterVarPtr->value.objPtr;
-
- if (oldValuePtr == NULL) {
- iterVarPtr->value.objPtr = Tcl_NewLongObj(-1);
- Tcl_IncrRefCount(iterVarPtr->value.objPtr);
- } else {
- Tcl_SetLongObj(oldValuePtr, -1);
+ if (listLen > (iterNum * numVars)) {
+ continueLoop = 1;
}
- TclSetVarScalar(iterVarPtr);
- TclClearVarUndefined(iterVarPtr);
- TRACE(("%u => loop iter count temp %d\n",
- opnd, iterTmpIndex));
+ listTmpIndex++;
}
- ADJUST_PC(5);
-
- case INST_FOREACH_STEP4:
- opnd = TclGetUInt4AtPtr(pc+1);
- {
- /*
- * "Step" a foreach loop (i.e., begin its next iteration) by
- * assigning the next value list element to each loop var.
- */
-
- ForeachInfo *infoPtr = (ForeachInfo *)
- codePtr->auxDataArrayPtr[opnd].clientData;
- ForeachVarList *varListPtr;
- int numLists = infoPtr->numLists;
- Var *compiledLocals = iPtr->varFramePtr->compiledLocals;
- Tcl_Obj *listPtr;
- List *listRepPtr;
- Var *iterVarPtr, *listVarPtr;
- int iterNum, listTmpIndex, listLen, numVars;
- int varIndex, valIndex, continueLoop, j;
-
- /*
- * Increment the temp holding the loop iteration number.
- */
- iterVarPtr = &(compiledLocals[infoPtr->loopCtTemp]);
- valuePtr = iterVarPtr->value.objPtr;
- iterNum = (valuePtr->internalRep.longValue + 1);
- Tcl_SetLongObj(valuePtr, iterNum);
+ /*
+ * If some var in some var list still has a remaining list
+ * element iterate one more time. Assign to var the next
+ * element from its value list. We already checked above
+ * that each list temp holds a valid list object.
+ */
- /*
- * Check whether all value lists are exhausted and we should
- * stop the loop.
- */
-
- continueLoop = 0;
+ if (continueLoop) {
listTmpIndex = infoPtr->firstValueTemp;
for (i = 0; i < numLists; i++) {
varListPtr = infoPtr->varLists[i];
numVars = varListPtr->numVars;
-
+
listVarPtr = &(compiledLocals[listTmpIndex]);
listPtr = listVarPtr->value.objPtr;
- result = Tcl_ListObjLength(interp, listPtr, &listLen);
- if (result != TCL_OK) {
- TRACE_WITH_OBJ(("%u => ERROR converting list %ld, \"%s\": ",
- opnd, i, O2S(listPtr)),
- Tcl_GetObjResult(interp));
- goto checkForCatch;
- }
- if (listLen > (iterNum * numVars)) {
- continueLoop = 1;
- }
- listTmpIndex++;
- }
-
- /*
- * If some var in some var list still has a remaining list
- * element iterate one more time. Assign to var the next
- * element from its value list. We already checked above
- * that each list temp holds a valid list object.
- */
-
- if (continueLoop) {
- listTmpIndex = infoPtr->firstValueTemp;
- for (i = 0; i < numLists; i++) {
- varListPtr = infoPtr->varLists[i];
- numVars = varListPtr->numVars;
-
- listVarPtr = &(compiledLocals[listTmpIndex]);
- listPtr = listVarPtr->value.objPtr;
- listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
- listLen = listRepPtr->elemCount;
+ listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
+ listLen = listRepPtr->elemCount;
- valIndex = (iterNum * numVars);
- for (j = 0; j < numVars; j++) {
- int setEmptyStr = 0;
- if (valIndex >= listLen) {
- setEmptyStr = 1;
- valuePtr = Tcl_NewObj();
- } else {
- valuePtr = listRepPtr->elements[valIndex];
- }
+ valIndex = (iterNum * numVars);
+ for (j = 0; j < numVars; j++) {
+ int setEmptyStr = 0;
+ if (valIndex >= listLen) {
+ setEmptyStr = 1;
+ TclNewObj(valuePtr);
+ } else {
+ valuePtr = listRepPtr->elements[valIndex];
+ }
- varIndex = varListPtr->varIndexes[j];
+ varIndex = varListPtr->varIndexes[j];
+ varPtr = &(varFramePtr->compiledLocals[varIndex]);
+ part1 = varPtr->name;
+ while (TclIsVarLink(varPtr)) {
+ varPtr = varPtr->value.linkPtr;
+ }
+ if (!((varPtr->flags & VAR_IN_HASHTABLE) && (varPtr->hPtr == NULL))
+ && (varPtr->tracePtr == NULL)
+ && (TclIsVarScalar(varPtr) || TclIsVarUndefined(varPtr))) {
+ value2Ptr = varPtr->value.objPtr;
+ if (valuePtr != value2Ptr) {
+ if (value2Ptr != NULL) {
+ TclDecrRefCount(value2Ptr);
+ } else {
+ TclSetVarScalar(varPtr);
+ TclClearVarUndefined(varPtr);
+ }
+ varPtr->value.objPtr = valuePtr;
+ Tcl_IncrRefCount(valuePtr);
+ }
+ } else {
DECACHE_STACK_INFO();
- value2Ptr = TclSetIndexedScalar(interp,
- varIndex, valuePtr, /*leaveErrorMsg*/ 1);
+ value2Ptr = TclPtrSetVar(interp, varPtr, NULL, part1,
+ NULL, valuePtr, TCL_LEAVE_ERR_MSG);
CACHE_STACK_INFO();
if (value2Ptr == NULL) {
TRACE_WITH_OBJ(("%u => ERROR init. index temp %d: ",
- opnd, varIndex),
- Tcl_GetObjResult(interp));
+ opnd, varIndex),
+ Tcl_GetObjResult(interp));
if (setEmptyStr) {
- Tcl_DecrRefCount(valuePtr);
+ TclDecrRefCount(valuePtr);
}
result = TCL_ERROR;
goto checkForCatch;
}
- valIndex++;
}
- listTmpIndex++;
+ valIndex++;
}
+ listTmpIndex++;
}
-
- /*
- * Push 1 if at least one value list had a remaining element
- * and the loop should continue. Otherwise push 0.
- */
-
- PUSH_OBJECT(Tcl_NewLongObj(continueLoop));
- TRACE(("%u => %d lists, iter %d, %s loop\n",
- opnd, numLists, iterNum,
- (continueLoop? "continue" : "exit")));
}
- ADJUST_PC(5);
+ TRACE(("%u => %d lists, iter %d, %s loop\n", opnd, numLists,
+ iterNum, (continueLoop? "continue" : "exit")));
- case INST_BEGIN_CATCH4:
- /*
- * Record start of the catch command with exception range index
- * equal to the operand. Push the current stack depth onto the
- * special catch stack.
+ /*
+ * Run-time peep-hole optimisation: the compiler ALWAYS follows
+ * INST_FOREACH_STEP4 with an INST_JUMP_FALSE. We just skip that
+ * instruction and jump direct from here.
*/
- catchStackPtr[++catchTop] = stackTop;
- TRACE(("%u => catchTop=%d, stackTop=%d\n",
- TclGetUInt4AtPtr(pc+1), catchTop, stackTop));
- ADJUST_PC(5);
- case INST_END_CATCH:
- catchTop--;
- result = TCL_OK;
- TRACE(("=> catchTop=%d\n", catchTop));
- ADJUST_PC(1);
+ pc += 5;
+ if (*pc == INST_JUMP_FALSE1) {
+ NEXT_INST_F((continueLoop? 2 : TclGetInt1AtPtr(pc+1)), 0, 0);
+ } else {
+ NEXT_INST_F((continueLoop? 5 : TclGetInt4AtPtr(pc+1)), 0, 0);
+ }
+ }
- case INST_PUSH_RESULT:
- PUSH_OBJECT(Tcl_GetObjResult(interp));
- TRACE_WITH_OBJ(("=> "), Tcl_GetObjResult(interp));
- ADJUST_PC(1);
+ case INST_BEGIN_CATCH4:
+ /*
+ * Record start of the catch command with exception range index
+ * equal to the operand. Push the current stack depth onto the
+ * special catch stack.
+ */
+ catchStackPtr[++catchTop] = stackTop;
+ TRACE(("%u => catchTop=%d, stackTop=%d\n",
+ TclGetUInt4AtPtr(pc+1), catchTop, stackTop));
+ NEXT_INST_F(5, 0, 0);
+
+ case INST_END_CATCH:
+ catchTop--;
+ result = TCL_OK;
+ TRACE(("=> catchTop=%d\n", catchTop));
+ NEXT_INST_F(1, 0, 0);
+
+ case INST_PUSH_RESULT:
+ objResultPtr = Tcl_GetObjResult(interp);
+ TRACE_WITH_OBJ(("=> "), Tcl_GetObjResult(interp));
+ NEXT_INST_F(1, 0, 1);
- case INST_PUSH_RETURN_CODE:
- PUSH_OBJECT(Tcl_NewLongObj(result));
- TRACE(("=> %u\n", result));
- ADJUST_PC(1);
+ case INST_PUSH_RETURN_CODE:
+ objResultPtr = Tcl_NewLongObj(result);
+ TRACE(("=> %u\n", result));
+ NEXT_INST_F(1, 0, 1);
- default:
- panic("TclExecuteByteCode: unrecognized opCode %u", *pc);
- } /* end of switch on opCode */
+ default:
+ panic("TclExecuteByteCode: unrecognized opCode %u", *pc);
+ } /* end of switch on opCode */
- /*
- * Division by zero in an expression. Control only reaches this
- * point by "goto divideByZero".
- */
-
- divideByZero:
- Tcl_ResetResult(interp);
- Tcl_AppendToObj(Tcl_GetObjResult(interp), "divide by zero", -1);
- Tcl_SetErrorCode(interp, "ARITH", "DIVZERO", "divide by zero",
- (char *) NULL);
- result = TCL_ERROR;
+ /*
+ * Division by zero in an expression. Control only reaches this
+ * point by "goto divideByZero".
+ */
- /*
- * Execution has generated an "exception" such as TCL_ERROR. If the
- * exception is an error, record information about what was being
- * executed when the error occurred. Find the closest enclosing
- * catch range, if any. If no enclosing catch range is found, stop
- * execution and return the "exception" code.
- */
+ divideByZero:
+ Tcl_ResetResult(interp);
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), "divide by zero", -1);
+ Tcl_SetErrorCode(interp, "ARITH", "DIVZERO", "divide by zero",
+ (char *) NULL);
+ result = TCL_ERROR;
+ goto checkForCatch;
- checkForCatch:
- if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
- bytes = GetSrcInfoForPc(pc, codePtr, &length);
- if (bytes != NULL) {
- Tcl_LogCommandInfo(interp, codePtr->source, bytes, length);
- iPtr->flags |= ERR_ALREADY_LOGGED;
- }
- }
- rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 1, codePtr);
+ /*
+ * An external evaluation (INST_INVOKE or INST_EVAL) returned
+ * something different from TCL_OK, or else INST_BREAK or
+ * INST_CONTINUE were called.
+ */
+
+ processExceptionReturn:
+#if TCL_COMPILE_DEBUG
+ switch (*pc) {
+ case INST_INVOKE_STK1:
+ case INST_INVOKE_STK4:
+ TRACE(("%u => ... after \"%.20s\": ", opnd, cmdNameBuf));
+ break;
+ case INST_EVAL_STK:
+ /*
+ * Note that the object at stacktop has to be used
+ * before doing the cleanup.
+ */
+
+ TRACE(("\"%.30s\" => ", O2S(stackPtr[stackTop])));
+ break;
+ default:
+ TRACE(("=> "));
+ }
+#endif
+ if ((result == TCL_CONTINUE) || (result == TCL_BREAK)) {
+ rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, codePtr);
if (rangePtr == NULL) {
-#ifdef TCL_COMPILE_DEBUG
- if (traceInstructions) {
- fprintf(stdout, " ... no enclosing catch, returning %s\n",
- StringForResultCode(result));
- }
-#endif
+ TRACE_APPEND(("no encl. loop or catch, returning %s\n",
+ StringForResultCode(result)));
goto abnormalReturn;
+ }
+ if (rangePtr->type == CATCH_EXCEPTION_RANGE) {
+ TRACE_APPEND(("%s ...\n", StringForResultCode(result)));
+ goto processCatch;
}
-
- /*
- * A catch exception range (rangePtr) was found to handle an
- * "exception". It was found either by checkForCatch just above or
- * by an instruction during break, continue, or error processing.
- * Jump to its catchOffset after unwinding the operand stack to
- * the depth it had when starting to execute the range's catch
- * command.
- */
-
- processCatch:
- while (stackTop > catchStackPtr[catchTop]) {
+ while (cleanup--) {
valuePtr = POP_OBJECT();
TclDecrRefCount(valuePtr);
}
+ if (result == TCL_BREAK) {
+ result = TCL_OK;
+ pc = (codePtr->codeStart + rangePtr->breakOffset);
+ TRACE_APPEND(("%s, range at %d, new pc %d\n",
+ StringForResultCode(result),
+ rangePtr->codeOffset, rangePtr->breakOffset));
+ NEXT_INST_F(0, 0, 0);
+ } else {
+ if (rangePtr->continueOffset == -1) {
+ TRACE_APPEND(("%s, loop w/o continue, checking for catch\n",
+ StringForResultCode(result)));
+ goto checkForCatch;
+ }
+ result = TCL_OK;
+ pc = (codePtr->codeStart + rangePtr->continueOffset);
+ TRACE_APPEND(("%s, range at %d, new pc %d\n",
+ StringForResultCode(result),
+ rangePtr->codeOffset, rangePtr->continueOffset));
+ NEXT_INST_F(0, 0, 0);
+ }
+#if TCL_COMPILE_DEBUG
+ } else if (traceInstructions) {
+ if ((result != TCL_ERROR) && (result != TCL_RETURN)) {
+ objPtr = Tcl_GetObjResult(interp);
+ TRACE_APPEND(("OTHER RETURN CODE %d, result= \"%s\"\n ",
+ result, O2S(objPtr)));
+ } else {
+ objPtr = Tcl_GetObjResult(interp);
+ TRACE_APPEND(("%s, result= \"%s\"\n",
+ StringForResultCode(result), O2S(objPtr)));
+ }
+#endif
+ }
+
+ /*
+ * Execution has generated an "exception" such as TCL_ERROR. If the
+ * exception is an error, record information about what was being
+ * executed when the error occurred. Find the closest enclosing
+ * catch range, if any. If no enclosing catch range is found, stop
+ * execution and return the "exception" code.
+ */
+
+ checkForCatch:
+ if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
+ bytes = GetSrcInfoForPc(pc, codePtr, &length);
+ if (bytes != NULL) {
+ Tcl_LogCommandInfo(interp, codePtr->source, bytes, length);
+ iPtr->flags |= ERR_ALREADY_LOGGED;
+ }
+ }
+ if (catchTop == -1) {
+#ifdef TCL_COMPILE_DEBUG
+ if (traceInstructions) {
+ fprintf(stdout, " ... no enclosing catch, returning %s\n",
+ StringForResultCode(result));
+ }
+#endif
+ goto abnormalReturn;
+ }
+ rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 1, codePtr);
+ if (rangePtr == NULL) {
+ /*
+ * This is only possible when compiling a [catch] that sends its
+ * script to INST_EVAL. Cannot correct the compiler without
+ * breakingcompat with previous .tbc compiled scripts.
+ */
#ifdef TCL_COMPILE_DEBUG
if (traceInstructions) {
- fprintf(stdout, " ... found catch at %d, catchTop=%d, unwound to %d, new pc %u\n",
+ fprintf(stdout, " ... no enclosing catch, returning %s\n",
+ StringForResultCode(result));
+ }
+#endif
+ goto abnormalReturn;
+ }
+
+ /*
+ * A catch exception range (rangePtr) was found to handle an
+ * "exception". It was found either by checkForCatch just above or
+ * by an instruction during break, continue, or error processing.
+ * Jump to its catchOffset after unwinding the operand stack to
+ * the depth it had when starting to execute the range's catch
+ * command.
+ */
+
+ processCatch:
+ while (stackTop > catchStackPtr[catchTop]) {
+ valuePtr = POP_OBJECT();
+ TclDecrRefCount(valuePtr);
+ }
+#ifdef TCL_COMPILE_DEBUG
+ if (traceInstructions) {
+ fprintf(stdout, " ... found catch at %d, catchTop=%d, unwound to %d, new pc %u\n",
rangePtr->codeOffset, catchTop, catchStackPtr[catchTop],
(unsigned int)(rangePtr->catchOffset));
- }
+ }
#endif
- pc = (codePtr->codeStart + rangePtr->catchOffset);
- continue; /* restart the execution loop at pc */
- } /* end of infinite loop dispatching on instructions */
+ pc = (codePtr->codeStart + rangePtr->catchOffset);
+ NEXT_INST_F(0, 0, 0); /* restart the execution loop at pc */
+
+ /*
+ * end of infinite loop dispatching on instructions.
+ */
/*
* Abnormal return code. Restore the stack to state it had when starting
- * to execute the ByteCode.
+ * to execute the ByteCode. Panic if the stack is below the initial level.
*/
- abnormalReturn:
+ abnormalReturn:
while (stackTop > initStackTop) {
valuePtr = POP_OBJECT();
- Tcl_DecrRefCount(valuePtr);
+ TclDecrRefCount(valuePtr);
}
-
+ if (stackTop < initStackTop) {
+ fprintf(stderr, "\nTclExecuteByteCode: abnormal return at pc %u: stack top %d < entry stack top %d\n",
+ (unsigned int)(pc - codePtr->codeStart),
+ (unsigned int) stackTop,
+ (unsigned int) initStackTop);
+ panic("TclExecuteByteCode execution failure: end stack top < start stack top");
+ }
+
/*
* Free the catch stack array if malloc'ed storage was used.
*/
- done:
if (catchStackPtr != catchStackStorage) {
ckfree((char *) catchStackPtr);
}
@@ -3004,8 +4301,7 @@ PrintByteCodeInfo(codePtr)
#ifdef TCL_COMPILE_DEBUG
static void
-ValidatePcAndStackTop(codePtr, pc, stackTop, stackLowerBound,
- stackUpperBound)
+ValidatePcAndStackTop(codePtr, pc, stackTop, stackLowerBound)
register ByteCode *codePtr; /* The bytecode whose summary is printed
* to stdout. */
unsigned char *pc; /* Points to first byte of a bytecode
@@ -3014,8 +4310,9 @@ ValidatePcAndStackTop(codePtr, pc, stackTop, stackLowerBound,
* stackLowerBound and stackUpperBound
* (inclusive). */
int stackLowerBound; /* Smallest legal value for stackTop. */
- int stackUpperBound; /* Greatest legal value for stackTop. */
{
+ int stackUpperBound = stackLowerBound + codePtr->maxStackDepth;
+ /* Greatest legal value for stackTop. */
unsigned int relativePc = (unsigned int) (pc - codePtr->codeStart);
unsigned int codeStart = (unsigned int) codePtr->codeStart;
unsigned int codeEnd = (unsigned int)
@@ -3030,15 +4327,15 @@ ValidatePcAndStackTop(codePtr, pc, stackTop, stackLowerBound,
if ((unsigned int) opCode > LAST_INST_OPCODE) {
fprintf(stderr, "\nBad opcode %d at pc %u in TclExecuteByteCode\n",
(unsigned int) opCode, relativePc);
- panic("TclExecuteByteCode execution failure: bad opcode");
+ panic("TclExecuteByteCode execution failure: bad opcode");
}
if ((stackTop < stackLowerBound) || (stackTop > stackUpperBound)) {
int numChars;
char *cmd = GetSrcInfoForPc(pc, codePtr, &numChars);
char *ellipsis = "";
- fprintf(stderr, "\nBad stack top %d at pc %u in TclExecuteByteCode",
- stackTop, relativePc);
+ fprintf(stderr, "\nBad stack top %d at pc %u in TclExecuteByteCode (min %i, max %i)",
+ stackTop, relativePc, stackLowerBound, stackUpperBound);
if (cmd != NULL) {
if (numChars > 100) {
numChars = 100;
@@ -3090,27 +4387,101 @@ IllegalExprOperandType(interp, pc, opndPtr)
operatorStrings[opCode - INST_LOR], "\"", (char *) NULL);
} else {
char *msg = "non-numeric string";
- if (opndPtr->typePtr != &tclDoubleType) {
+ char *s, *p;
+ int length;
+ int looksLikeInt = 0;
+
+ s = Tcl_GetStringFromObj(opndPtr, &length);
+ p = s;
+ /*
+ * strtod() isn't at all consistent about detecting Inf and
+ * NaN between platforms.
+ */
+ if (length == 3) {
+ if ((s[0]=='n' || s[0]=='N') && (s[1]=='a' || s[1]=='A') &&
+ (s[2]=='n' || s[2]=='N')) {
+ msg = "non-numeric floating-point value";
+ goto makeErrorMessage;
+ }
+ if ((s[0]=='i' || s[0]=='I') && (s[1]=='n' || s[1]=='N') &&
+ (s[2]=='f' || s[2]=='F')) {
+ msg = "infinite floating-point value";
+ goto makeErrorMessage;
+ }
+ }
+
+ /*
+ * We cannot use TclLooksLikeInt here because it passes strings
+ * like "10;" [Bug 587140]. We'll accept as "looking like ints"
+ * for the present purposes any string that looks formally like
+ * a (decimal|octal|hex) integer.
+ */
+
+ while (length && isspace(UCHAR(*p))) {
+ length--;
+ p++;
+ }
+ if (length && ((*p == '+') || (*p == '-'))) {
+ length--;
+ p++;
+ }
+ if (length) {
+ if ((*p == '0') && ((*(p+1) == 'x') || (*(p+1) == 'X'))) {
+ p += 2;
+ length -= 2;
+ looksLikeInt = ((length > 0) && isxdigit(UCHAR(*p)));
+ if (looksLikeInt) {
+ length--;
+ p++;
+ while (length && isxdigit(UCHAR(*p))) {
+ length--;
+ p++;
+ }
+ }
+ } else {
+ looksLikeInt = (length && isdigit(UCHAR(*p)));
+ if (looksLikeInt) {
+ length--;
+ p++;
+ while (length && isdigit(UCHAR(*p))) {
+ length--;
+ p++;
+ }
+ }
+ }
+ while (length && isspace(UCHAR(*p))) {
+ length--;
+ p++;
+ }
+ looksLikeInt = !length;
+ }
+ if (looksLikeInt) {
/*
- * See if the operand can be interpreted as a double in order to
- * improve the error message.
+ * If something that looks like an integer could not be
+ * converted, then it *must* be a bad octal or too large
+ * to represent [Bug 542588].
+ */
+
+ if (TclCheckBadOctal(NULL, s)) {
+ msg = "invalid octal number";
+ } else {
+ msg = "integer value too large to represent";
+ Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW",
+ "integer value too large to represent", (char *) NULL);
+ }
+ } else {
+ /*
+ * See if the operand can be interpreted as a double in
+ * order to improve the error message.
*/
- char *s = Tcl_GetString(opndPtr);
double d;
if (Tcl_GetDouble((Tcl_Interp *) NULL, s, &d) == TCL_OK) {
- /*
- * Make sure that what appears to be a double
- * (ie 08) isn't really a bad octal
- */
- if (TclCheckBadOctal(NULL, Tcl_GetString(opndPtr))) {
- msg = "invalid octal number";
- } else {
- msg = "floating-point value";
- }
+ msg = "floating-point value";
}
}
+ makeErrorMessage:
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "can't use ",
msg, " as operand of \"", operatorStrings[opCode - INST_LOR],
"\"", (char *) NULL);
@@ -3120,74 +4491,6 @@ IllegalExprOperandType(interp, pc, opndPtr)
/*
*----------------------------------------------------------------------
*
- * CallTraceProcedure --
- *
- * Invokes a trace procedure registered with an interpreter. These
- * procedures trace command execution. Currently this trace procedure
- * is called with the address of the string-based Tcl_CmdProc for the
- * command, not the Tcl_ObjCmdProc.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Those side effects made by the trace procedure.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-CallTraceProcedure(interp, tracePtr, cmdPtr, command, numChars, objc, objv)
- Tcl_Interp *interp; /* The current interpreter. */
- register Trace *tracePtr; /* Describes the trace procedure to call. */
- Command *cmdPtr; /* Points to command's Command struct. */
- char *command; /* Points to the first character of the
- * command's source before substitutions. */
- int numChars; /* The number of characters in the
- * command's source. */
- register int objc; /* Number of arguments for the command. */
- Tcl_Obj *objv[]; /* Pointers to Tcl_Obj of each argument. */
-{
- Interp *iPtr = (Interp *) interp;
- register char **argv;
- register int i;
- int length;
- char *p;
-
- /*
- * Get the string rep from the objv argument objects and place their
- * pointers in argv. First make sure argv is large enough to hold the
- * objc args plus 1 extra word for the zero end-of-argv word.
- */
-
- argv = (char **) ckalloc((unsigned)(objc + 1) * sizeof(char *));
- for (i = 0; i < objc; i++) {
- argv[i] = Tcl_GetStringFromObj(objv[i], &length);
- }
- argv[objc] = 0;
-
- /*
- * Copy the command characters into a new string.
- */
-
- p = (char *) ckalloc((unsigned) (numChars + 1));
- memcpy((VOID *) p, (VOID *) command, (size_t) numChars);
- p[numChars] = '\0';
-
- /*
- * Call the trace procedure then free allocated storage.
- */
-
- (*tracePtr->proc)(tracePtr->clientData, interp, iPtr->numLevels,
- p, cmdPtr->proc, cmdPtr->clientData, objc, argv);
-
- ckfree((char *) argv);
- ckfree((char *) p);
-}
-
-/*
- *----------------------------------------------------------------------
- *
* GetSrcInfoForPc --
*
* Given a program counter value, finds the closest command in the
@@ -3349,25 +4652,28 @@ GetExceptRangeForPc(pc, catchOnly, codePtr)
int numRanges = codePtr->numExceptRanges;
register ExceptionRange *rangePtr;
int pcOffset = (pc - codePtr->codeStart);
- register int i, level;
+ register int start;
if (numRanges == 0) {
return NULL;
}
- rangeArrayPtr = codePtr->exceptArrayPtr;
- for (level = codePtr->maxExceptDepth; level >= 0; level--) {
- for (i = 0; i < numRanges; i++) {
- rangePtr = &(rangeArrayPtr[i]);
- if (rangePtr->nestingLevel == level) {
- int start = rangePtr->codeOffset;
- int end = (start + rangePtr->numCodeBytes);
- if ((start <= pcOffset) && (pcOffset < end)) {
- if ((!catchOnly)
- || (rangePtr->type == CATCH_EXCEPTION_RANGE)) {
- return rangePtr;
- }
- }
+ /*
+ * This exploits peculiarities of our compiler: nested ranges
+ * are always *after* their containing ranges, so that by scanning
+ * backwards we are sure that the first matching range is indeed
+ * the deepest.
+ */
+
+ rangeArrayPtr = codePtr->exceptArrayPtr;
+ rangePtr = rangeArrayPtr + numRanges;
+ while (--rangePtr >= rangeArrayPtr) {
+ start = rangePtr->codeOffset;
+ if ((start <= pcOffset) &&
+ (pcOffset < (start + rangePtr->numCodeBytes))) {
+ if ((!catchOnly)
+ || (rangePtr->type == CATCH_EXCEPTION_RANGE)) {
+ return rangePtr;
}
}
}
@@ -3400,7 +4706,7 @@ GetOpcodeName(pc)
{
unsigned char opCode = *pc;
- return instructionTable[opCode].name;
+ return tclInstructionTable[opCode].name;
}
#endif /* TCL_COMPILE_DEBUG */
@@ -3418,7 +4724,8 @@ GetOpcodeName(pc)
* TCL_OK if it was int or double, TCL_ERROR otherwise
*
* Side effects:
- * objPtr is ensured to be either tclIntType of tclDoubleType.
+ * objPtr is ensured to be of tclIntType, tclWideIntType or
+ * tclDoubleType.
*
*----------------------------------------------------------------------
*/
@@ -3429,16 +4736,20 @@ VerifyExprObjType(interp, objPtr)
* function. */
Tcl_Obj *objPtr; /* Points to the object to type check. */
{
- if ((objPtr->typePtr == &tclIntType) ||
- (objPtr->typePtr == &tclDoubleType)) {
+ if (IS_NUMERIC_TYPE(objPtr->typePtr)) {
return TCL_OK;
} else {
int length, result = TCL_OK;
char *s = Tcl_GetStringFromObj(objPtr, &length);
if (TclLooksLikeInt(s, length)) {
+#ifdef TCL_WIDE_INT_IS_LONG
long i;
result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, objPtr, &i);
+#else /* !TCL_WIDE_INT_IS_LONG */
+ Tcl_WideInt w;
+ result = Tcl_GetWideIntFromObj((Tcl_Interp *) NULL, objPtr, &w);
+#endif /* TCL_WIDE_INT_IS_LONG */
} else {
double d;
result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, objPtr, &d);
@@ -3515,12 +4826,8 @@ ExprUnaryFunc(interp, eePtr, clientData)
result = TCL_ERROR;
goto done;
}
-
- if (valuePtr->typePtr == &tclIntType) {
- d = (double) valuePtr->internalRep.longValue;
- } else {
- d = valuePtr->internalRep.doubleValue;
- }
+
+ GET_DOUBLE_VALUE(d, valuePtr, valuePtr->typePtr);
errno = 0;
dResult = (*func)(d);
@@ -3541,7 +4848,7 @@ ExprUnaryFunc(interp, eePtr, clientData)
*/
done:
- Tcl_DecrRefCount(valuePtr);
+ TclDecrRefCount(valuePtr);
DECACHE_STACK_INFO();
return result;
}
@@ -3586,17 +4893,8 @@ ExprBinaryFunc(interp, eePtr, clientData)
goto done;
}
- if (valuePtr->typePtr == &tclIntType) {
- d1 = (double) valuePtr->internalRep.longValue;
- } else {
- d1 = valuePtr->internalRep.doubleValue;
- }
-
- if (value2Ptr->typePtr == &tclIntType) {
- d2 = (double) value2Ptr->internalRep.longValue;
- } else {
- d2 = value2Ptr->internalRep.doubleValue;
- }
+ GET_DOUBLE_VALUE(d1, valuePtr, valuePtr->typePtr);
+ GET_DOUBLE_VALUE(d2, value2Ptr, value2Ptr->typePtr);
errno = 0;
dResult = (*func)(d1, d2);
@@ -3617,8 +4915,8 @@ ExprBinaryFunc(interp, eePtr, clientData)
*/
done:
- Tcl_DecrRefCount(valuePtr);
- Tcl_DecrRefCount(value2Ptr);
+ TclDecrRefCount(valuePtr);
+ TclDecrRefCount(value2Ptr);
DECACHE_STACK_INFO();
return result;
}
@@ -3676,6 +4974,25 @@ ExprAbsFunc(interp, eePtr, clientData)
iResult = i;
}
PUSH_OBJECT(Tcl_NewLongObj(iResult));
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (valuePtr->typePtr == &tclWideIntType) {
+ Tcl_WideInt wResult, w = valuePtr->internalRep.wideValue;
+ if (w < W0) {
+ wResult = -w;
+ if (wResult < 0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendToObj(Tcl_GetObjResult(interp),
+ "integer value too large to represent", -1);
+ Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW",
+ "integer value too large to represent", (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ } else {
+ wResult = w;
+ }
+ PUSH_OBJECT(Tcl_NewWideIntObj(wResult));
+#endif /* TCL_WIDE_INT_IS_LONG */
} else {
d = valuePtr->internalRep.doubleValue;
if (d < 0.0) {
@@ -3696,7 +5013,7 @@ ExprAbsFunc(interp, eePtr, clientData)
*/
done:
- Tcl_DecrRefCount(valuePtr);
+ TclDecrRefCount(valuePtr);
DECACHE_STACK_INFO();
return result;
}
@@ -3733,11 +5050,7 @@ ExprDoubleFunc(interp, eePtr, clientData)
goto done;
}
- if (valuePtr->typePtr == &tclIntType) {
- dResult = (double) valuePtr->internalRep.longValue;
- } else {
- dResult = valuePtr->internalRep.doubleValue;
- }
+ GET_DOUBLE_VALUE(dResult, valuePtr, valuePtr->typePtr);
/*
* Push a Tcl object with the result.
@@ -3750,7 +5063,7 @@ ExprDoubleFunc(interp, eePtr, clientData)
*/
done:
- Tcl_DecrRefCount(valuePtr);
+ TclDecrRefCount(valuePtr);
DECACHE_STACK_INFO();
return result;
}
@@ -3790,6 +5103,10 @@ ExprIntFunc(interp, eePtr, clientData)
if (valuePtr->typePtr == &tclIntType) {
iResult = valuePtr->internalRep.longValue;
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (valuePtr->typePtr == &tclWideIntType) {
+ iResult = Tcl_WideAsLong(valuePtr->internalRep.wideValue);
+#endif /* TCL_WIDE_INT_IS_LONG */
} else {
d = valuePtr->internalRep.doubleValue;
if (d < 0.0) {
@@ -3827,10 +5144,91 @@ ExprIntFunc(interp, eePtr, clientData)
*/
done:
- Tcl_DecrRefCount(valuePtr);
+ TclDecrRefCount(valuePtr);
+ DECACHE_STACK_INFO();
+ return result;
+}
+
+#ifndef TCL_WIDE_INT_IS_LONG
+static int
+ExprWideFunc(interp, eePtr, clientData)
+ Tcl_Interp *interp; /* The interpreter in which to execute the
+ * function. */
+ ExecEnv *eePtr; /* Points to the environment for executing
+ * the function. */
+ ClientData clientData; /* Ignored. */
+{
+ Tcl_Obj **stackPtr; /* Cached evaluation stack base pointer. */
+ register int stackTop; /* Cached top index of evaluation stack. */
+ register Tcl_Obj *valuePtr;
+ Tcl_WideInt wResult;
+ double d;
+ int result;
+
+ /*
+ * Set stackPtr and stackTop from eePtr.
+ */
+
+ result = TCL_OK;
+ CACHE_STACK_INFO();
+
+ /*
+ * Pop the argument from the evaluation stack.
+ */
+
+ valuePtr = POP_OBJECT();
+
+ if (VerifyExprObjType(interp, valuePtr) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+
+ if (valuePtr->typePtr == &tclWideIntType) {
+ wResult = valuePtr->internalRep.wideValue;
+ } else if (valuePtr->typePtr == &tclIntType) {
+ wResult = Tcl_LongAsWide(valuePtr->internalRep.longValue);
+ } else {
+ d = valuePtr->internalRep.doubleValue;
+ if (d < 0.0) {
+ if (d < Tcl_WideAsDouble(LLONG_MIN)) {
+ tooLarge:
+ Tcl_ResetResult(interp);
+ Tcl_AppendToObj(Tcl_GetObjResult(interp),
+ "integer value too large to represent", -1);
+ Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW",
+ "integer value too large to represent", (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ } else {
+ if (d > Tcl_WideAsDouble(LLONG_MAX)) {
+ goto tooLarge;
+ }
+ }
+ if (IS_NAN(d) || IS_INF(d)) {
+ TclExprFloatError(interp, d);
+ result = TCL_ERROR;
+ goto done;
+ }
+ wResult = Tcl_DoubleAsWide(d);
+ }
+
+ /*
+ * Push a Tcl object with the result.
+ */
+
+ PUSH_OBJECT(Tcl_NewWideIntObj(wResult));
+
+ /*
+ * Reflect the change to stackTop back in eePtr.
+ */
+
+ done:
+ TclDecrRefCount(valuePtr);
DECACHE_STACK_INFO();
return result;
}
+#endif /* TCL_WIDE_INT_IS_LONG */
static int
ExprRandFunc(interp, eePtr, clientData)
@@ -3844,11 +5242,27 @@ ExprRandFunc(interp, eePtr, clientData)
register int stackTop; /* Cached top index of evaluation stack. */
Interp *iPtr = (Interp *) interp;
double dResult;
- int tmp;
+ long tmp; /* Algorithm assumes at least 32 bits.
+ * Only long guarantees that. See below. */
if (!(iPtr->flags & RAND_SEED_INITIALIZED)) {
iPtr->flags |= RAND_SEED_INITIALIZED;
- iPtr->randSeed = TclpGetClicks();
+
+ /*
+ * Take into consideration the thread this interp is running in order
+ * to insure different seeds in different threads (bug #416643)
+ */
+
+ iPtr->randSeed = TclpGetClicks() + ((long)Tcl_GetCurrentThread()<<12);
+
+ /*
+ * Make sure 1 <= randSeed <= (2^31) - 2. See below.
+ */
+
+ iPtr->randSeed &= (unsigned long) 0x7fffffff;
+ if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7fffffff)) {
+ iPtr->randSeed ^= 123459876;
+ }
}
/*
@@ -3861,11 +5275,20 @@ ExprRandFunc(interp, eePtr, clientData)
* Generate the random number using the linear congruential
* generator defined by the following recurrence:
* seed = ( IA * seed ) mod IM
- * where IA is 16807 and IM is (2^31) - 1. In order to avoid
- * potential problems with integer overflow, the code uses
- * additional constants IQ and IR such that
+ * where IA is 16807 and IM is (2^31) - 1. The recurrence maps
+ * a seed in the range [1, IM - 1] to a new seed in that same range.
+ * The recurrence maps IM to 0, and maps 0 back to 0, so those two
+ * values must not be allowed as initial values of seed.
+ *
+ * In order to avoid potential problems with integer overflow, the
+ * recurrence is implemented in terms of additional constants
+ * IQ and IR such that
* IM = IA*IQ + IR
- * For details on how this algorithm works, refer to the following
+ * None of the operations in the implementation overflows a 32-bit
+ * signed integer, and the C type long is guaranteed to be at least
+ * 32 bits wide.
+ *
+ * For more details on how this algorithm works, refer to the following
* papers:
*
* S.K. Park & K.W. Miller, "Random number generators: good ones
@@ -3881,14 +5304,6 @@ ExprRandFunc(interp, eePtr, clientData)
#define RAND_IR 2836
#define RAND_MASK 123459876
- if (iPtr->randSeed == 0) {
- /*
- * Don't allow a 0 seed, since it breaks the generator. Shift
- * it to some other value.
- */
-
- iPtr->randSeed = 123459876;
- }
tmp = iPtr->randSeed/RAND_IQ;
iPtr->randSeed = RAND_IA*(iPtr->randSeed - tmp*RAND_IQ) - RAND_IR*tmp;
if (iPtr->randSeed < 0) {
@@ -3896,14 +5311,10 @@ ExprRandFunc(interp, eePtr, clientData)
}
/*
- * On 64-bit architectures we need to mask off the upper bits to
- * ensure we only have a 32-bit range. The constant has the
- * bizarre form below in order to make sure that it doesn't
- * get sign-extended (the rules for sign extension are very
- * concat, particularly on 64-bit machines).
+ * Since the recurrence keeps seed values in the range [1, RAND_IM - 1],
+ * dividing by RAND_IM yields a double in the range (0, 1).
*/
- iPtr->randSeed &= ((((unsigned long) 0xfffffff) << 4) | 0xf);
dResult = iPtr->randSeed * (1.0/RAND_IM);
/*
@@ -3955,6 +5366,11 @@ ExprRoundFunc(interp, eePtr, clientData)
if (valuePtr->typePtr == &tclIntType) {
iResult = valuePtr->internalRep.longValue;
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (valuePtr->typePtr == &tclWideIntType) {
+ PUSH_OBJECT(Tcl_NewWideIntObj(valuePtr->internalRep.wideValue));
+ goto done;
+#endif /* TCL_WIDE_INT_IS_LONG */
} else {
d = valuePtr->internalRep.doubleValue;
if (d < 0.0) {
@@ -3995,7 +5411,7 @@ ExprRoundFunc(interp, eePtr, clientData)
*/
done:
- Tcl_DecrRefCount(valuePtr);
+ TclDecrRefCount(valuePtr);
DECACHE_STACK_INFO();
return result;
}
@@ -4035,6 +5451,10 @@ ExprSrandFunc(interp, eePtr, clientData)
if (valuePtr->typePtr == &tclIntType) {
i = valuePtr->internalRep.longValue;
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (valuePtr->typePtr == &tclWideIntType) {
+ i = Tcl_WideAsLong(valuePtr->internalRep.wideValue);
+#endif /* TCL_WIDE_INT_IS_LONG */
} else {
/*
* At this point, the only other possible type is double
@@ -4044,17 +5464,22 @@ ExprSrandFunc(interp, eePtr, clientData)
"can't use floating-point value as argument to srand",
(char *) NULL);
badValue:
- Tcl_DecrRefCount(valuePtr);
+ TclDecrRefCount(valuePtr);
DECACHE_STACK_INFO();
return TCL_ERROR;
}
/*
- * Reset the seed.
+ * Reset the seed. Make sure 1 <= randSeed <= 2^31 - 2.
+ * See comments in ExprRandFunc() for more details.
*/
iPtr->flags |= RAND_SEED_INITIALIZED;
iPtr->randSeed = i;
+ iPtr->randSeed &= (unsigned long) 0x7fffffff;
+ if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7fffffff)) {
+ iPtr->randSeed ^= 123459876;
+ }
/*
* To avoid duplicating the random number generation code we simply
@@ -4062,7 +5487,7 @@ ExprSrandFunc(interp, eePtr, clientData)
* function will always succeed.
*/
- Tcl_DecrRefCount(valuePtr);
+ TclDecrRefCount(valuePtr);
DECACHE_STACK_INFO();
ExprRandFunc(interp, eePtr, clientData);
@@ -4113,7 +5538,6 @@ ExprCallMathFunc(interp, eePtr, objc, objv)
long i;
double d;
int j, k, result;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
Tcl_ResetResult(interp);
@@ -4127,7 +5551,7 @@ ExprCallMathFunc(interp, eePtr, objc, objv)
* Look up the MathFunc record for the function.
*/
- funcName = Tcl_GetString(objv[0]);
+ funcName = TclGetString(objv[0]);
hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, funcName);
if (hPtr == NULL) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
@@ -4167,15 +5591,39 @@ ExprCallMathFunc(interp, eePtr, objc, objv)
if (mathFuncPtr->argTypes[k] == TCL_DOUBLE) {
args[k].type = TCL_DOUBLE;
args[k].doubleValue = i;
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (mathFuncPtr->argTypes[k] == TCL_WIDE_INT) {
+ args[k].type = TCL_WIDE_INT;
+ args[k].wideValue = Tcl_LongAsWide(i);
+#endif /* !TCL_WIDE_INT_IS_LONG */
} else {
args[k].type = TCL_INT;
args[k].intValue = i;
}
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (valuePtr->typePtr == &tclWideIntType) {
+ Tcl_WideInt w = valuePtr->internalRep.wideValue;
+ if (mathFuncPtr->argTypes[k] == TCL_DOUBLE) {
+ args[k].type = TCL_DOUBLE;
+ args[k].wideValue = (Tcl_WideInt) Tcl_WideAsDouble(w);
+ } else if (mathFuncPtr->argTypes[k] == TCL_INT) {
+ args[k].type = TCL_INT;
+ args[k].wideValue = Tcl_WideAsLong(w);
+ } else {
+ args[k].type = TCL_WIDE_INT;
+ args[k].wideValue = w;
+ }
+#endif /* !TCL_WIDE_INT_IS_LONG */
} else {
d = valuePtr->internalRep.doubleValue;
if (mathFuncPtr->argTypes[k] == TCL_INT) {
args[k].type = TCL_INT;
args[k].intValue = (long) d;
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (mathFuncPtr->argTypes[k] == TCL_WIDE_INT) {
+ args[k].type = TCL_WIDE_INT;
+ args[k].wideValue = Tcl_DoubleAsWide(d);
+#endif /* !TCL_WIDE_INT_IS_LONG */
} else {
args[k].type = TCL_DOUBLE;
args[k].doubleValue = d;
@@ -4187,10 +5635,8 @@ ExprCallMathFunc(interp, eePtr, objc, objv)
* Invoke the function and copy its result back into valuePtr.
*/
- tsdPtr->mathInProgress++;
result = (*mathFuncPtr->proc)(mathFuncPtr->clientData, interp, args,
&funcResult);
- tsdPtr->mathInProgress--;
if (result != TCL_OK) {
goto done;
}
@@ -4198,14 +5644,12 @@ ExprCallMathFunc(interp, eePtr, objc, objv)
/*
* Pop the objc top stack elements and decrement their ref counts.
*/
-
- i = (stackTop - (objc-1));
- while (i <= stackTop) {
- valuePtr = stackPtr[i];
- Tcl_DecrRefCount(valuePtr);
- i++;
+
+ k = (stackTop - (objc-1));
+ while (stackTop >= k) {
+ valuePtr = POP_OBJECT();
+ TclDecrRefCount(valuePtr);
}
- stackTop -= objc;
/*
* Push the call's object result.
@@ -4213,6 +5657,10 @@ ExprCallMathFunc(interp, eePtr, objc, objv)
if (funcResult.type == TCL_INT) {
PUSH_OBJECT(Tcl_NewLongObj(funcResult.intValue));
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (funcResult.type == TCL_WIDE_INT) {
+ PUSH_OBJECT(Tcl_NewWideIntObj(funcResult.wideValue));
+#endif /* !TCL_WIDE_INT_IS_LONG */
} else {
d = funcResult.doubleValue;
if (IS_NAN(d) || IS_INF(d)) {
@@ -4282,30 +5730,6 @@ TclExprFloatError(interp, value)
}
}
-/*
- *----------------------------------------------------------------------
- *
- * TclMathInProgress --
- *
- * This procedure is called to find out if Tcl is doing math
- * in this thread.
- *
- * Results:
- * 0 or 1.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclMathInProgress()
-{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- return tsdPtr->mathInProgress;
-}
-
#ifdef TCL_COMPILE_STATS
/*
*----------------------------------------------------------------------
@@ -4449,7 +5873,7 @@ EvalStatsCmd(unused, interp, argc, argv)
fprintf(stdout, " Mean code/source %.1f\n",
totalCodeBytes / statsPtr->totalSrcBytes);
- fprintf(stdout, "\nCurrent ByteCodes %ld\n",
+ fprintf(stdout, "\nCurrent (active) ByteCodes %ld\n",
numCurrentByteCodes);
fprintf(stdout, " Source bytes %.6g\n",
statsPtr->currentSrcBytes);
@@ -4472,6 +5896,29 @@ EvalStatsCmd(unused, interp, argc, argv)
(currentCodeBytes / statsPtr->currentSrcBytes) + 1.0);
/*
+ * Tcl_IsShared statistics check
+ *
+ * This gives the refcount of each obj as Tcl_IsShared was called
+ * for it. Shared objects must be duplicated before they can be
+ * modified.
+ */
+
+ numSharedMultX = 0;
+ fprintf(stdout, "\nTcl_IsShared object check (all objects):\n");
+ fprintf(stdout, " Object had refcount <=1 (not shared) %ld\n",
+ tclObjsShared[1]);
+ for (i = 2; i < TCL_MAX_SHARED_OBJ_STATS; i++) {
+ fprintf(stdout, " refcount ==%d %ld\n",
+ i, tclObjsShared[i]);
+ numSharedMultX += tclObjsShared[i];
+ }
+ fprintf(stdout, " refcount >=%d %ld\n",
+ i, tclObjsShared[0]);
+ numSharedMultX += tclObjsShared[0];
+ fprintf(stdout, " Total shared objects %d\n",
+ numSharedMultX);
+
+ /*
* Literal table statistics.
*/
@@ -4511,7 +5958,7 @@ EvalStatsCmd(unused, interp, argc, argv)
(tclObjsAlloced - tclObjsFreed));
fprintf(stdout, "Total literal objects %ld\n",
statsPtr->numLiteralsCreated);
-
+
fprintf(stdout, "\nCurrent literal objects %d (%0.1f%% of current objects)\n",
globalTablePtr->numEntries,
(globalTablePtr->numEntries * 100.0) / (tclObjsAlloced-tclObjsFreed));
@@ -4662,7 +6109,7 @@ EvalStatsCmd(unused, interp, argc, argv)
decadeHigh, (sum * 100.0) / statsPtr->numCompilations);
}
- fprintf(stdout, "\nByteCode longevity (excludes current ByteCodes):\n");
+ fprintf(stdout, "\nByteCode longevity (excludes Current ByteCodes):\n");
fprintf(stdout, " Up to ms Percentage\n");
minSizeDecade = maxSizeDecade = 0;
for (i = 0; i < 31; i++) {
@@ -4694,7 +6141,7 @@ EvalStatsCmd(unused, interp, argc, argv)
for (i = 0; i <= LAST_INST_OPCODE; i++) {
if (statsPtr->instructionCount[i]) {
fprintf(stdout, "%20s %8ld %6.1f%%\n",
- instructionTable[i].name,
+ tclInstructionTable[i].name,
statsPtr->instructionCount[i],
(statsPtr->instructionCount[i]*100.0) / numInstructions);
}
@@ -4703,8 +6150,7 @@ EvalStatsCmd(unused, interp, argc, argv)
fprintf(stdout, "\nInstructions NEVER executed:\n");
for (i = 0; i <= LAST_INST_OPCODE; i++) {
if (statsPtr->instructionCount[i] == 0) {
- fprintf(stdout, "%20s\n",
- instructionTable[i].name);
+ fprintf(stdout, "%20s\n", tclInstructionTable[i].name);
}
}
@@ -4717,345 +6163,6 @@ EvalStatsCmd(unused, interp, argc, argv)
}
#endif /* TCL_COMPILE_STATS */
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_GetCommandFromObj --
- *
- * Returns the command specified by the name in a Tcl_Obj.
- *
- * Results:
- * Returns a token for the command if it is found. Otherwise, if it
- * can't be found or there is an error, returns NULL.
- *
- * Side effects:
- * May update the internal representation for the object, caching
- * the command reference so that the next time this procedure is
- * called with the same object, the command can be found quickly.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Command
-Tcl_GetCommandFromObj(interp, objPtr)
- Tcl_Interp *interp; /* The interpreter in which to resolve the
- * command and to report errors. */
- register Tcl_Obj *objPtr; /* The object containing the command's
- * name. If the name starts with "::", will
- * be looked up in global namespace. Else,
- * looked up first in the current namespace
- * if contextNsPtr is NULL, then in global
- * namespace. */
-{
- Interp *iPtr = (Interp *) interp;
- register ResolvedCmdName *resPtr;
- register Command *cmdPtr;
- Namespace *currNsPtr;
- int result;
-
- /*
- * Get the internal representation, converting to a command type if
- * needed. The internal representation is a ResolvedCmdName that points
- * to the actual command.
- */
-
- if (objPtr->typePtr != &tclCmdNameType) {
- result = tclCmdNameType.setFromAnyProc(interp, objPtr);
- if (result != TCL_OK) {
- return (Tcl_Command) NULL;
- }
- }
- resPtr = (ResolvedCmdName *) objPtr->internalRep.otherValuePtr;
-
- /*
- * Get the current namespace.
- */
-
- if (iPtr->varFramePtr != NULL) {
- currNsPtr = iPtr->varFramePtr->nsPtr;
- } else {
- currNsPtr = iPtr->globalNsPtr;
- }
-
- /*
- * Check the context namespace and the namespace epoch of the resolved
- * symbol to make sure that it is fresh. If not, then force another
- * conversion to the command type, to discard the old rep and create a
- * new one. Note that we verify that the namespace id of the context
- * namespace is the same as the one we cached; this insures that the
- * namespace wasn't deleted and a new one created at the same address
- * with the same command epoch.
- */
-
- cmdPtr = NULL;
- if ((resPtr != NULL)
- && (resPtr->refNsPtr == currNsPtr)
- && (resPtr->refNsId == currNsPtr->nsId)
- && (resPtr->refNsCmdEpoch == currNsPtr->cmdRefEpoch)) {
- cmdPtr = resPtr->cmdPtr;
- if (cmdPtr->cmdEpoch != resPtr->cmdEpoch) {
- cmdPtr = NULL;
- }
- }
-
- if (cmdPtr == NULL) {
- result = tclCmdNameType.setFromAnyProc(interp, objPtr);
- if (result != TCL_OK) {
- return (Tcl_Command) NULL;
- }
- resPtr = (ResolvedCmdName *) objPtr->internalRep.otherValuePtr;
- if (resPtr != NULL) {
- cmdPtr = resPtr->cmdPtr;
- }
- }
- return (Tcl_Command) cmdPtr;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclSetCmdNameObj --
- *
- * Modify an object to be an CmdName object that refers to the argument
- * Command structure.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The object's old internal rep is freed. It's string rep is not
- * changed. The refcount in the Command structure is incremented to
- * keep it from being freed if the command is later deleted until
- * TclExecuteByteCode has a chance to recognize that it was deleted.
- *
- *----------------------------------------------------------------------
- */
-
-void
-TclSetCmdNameObj(interp, objPtr, cmdPtr)
- Tcl_Interp *interp; /* Points to interpreter containing command
- * that should be cached in objPtr. */
- register Tcl_Obj *objPtr; /* Points to Tcl object to be changed to
- * a CmdName object. */
- Command *cmdPtr; /* Points to Command structure that the
- * CmdName object should refer to. */
-{
- Interp *iPtr = (Interp *) interp;
- register ResolvedCmdName *resPtr;
- Tcl_ObjType *oldTypePtr = objPtr->typePtr;
- register Namespace *currNsPtr;
-
- if (oldTypePtr == &tclCmdNameType) {
- return;
- }
-
- /*
- * Get the current namespace.
- */
-
- if (iPtr->varFramePtr != NULL) {
- currNsPtr = iPtr->varFramePtr->nsPtr;
- } else {
- currNsPtr = iPtr->globalNsPtr;
- }
-
- cmdPtr->refCount++;
- resPtr = (ResolvedCmdName *) ckalloc(sizeof(ResolvedCmdName));
- resPtr->cmdPtr = cmdPtr;
- resPtr->refNsPtr = currNsPtr;
- resPtr->refNsId = currNsPtr->nsId;
- resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch;
- resPtr->cmdEpoch = cmdPtr->cmdEpoch;
- resPtr->refCount = 1;
-
- if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
- oldTypePtr->freeIntRepProc(objPtr);
- }
- objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr;
- objPtr->internalRep.twoPtrValue.ptr2 = NULL;
- objPtr->typePtr = &tclCmdNameType;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * FreeCmdNameInternalRep --
- *
- * Frees the resources associated with a cmdName object's internal
- * representation.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Decrements the ref count of any cached ResolvedCmdName structure
- * pointed to by the cmdName's internal representation. If this is
- * the last use of the ResolvedCmdName, it is freed. This in turn
- * decrements the ref count of the Command structure pointed to by
- * the ResolvedSymbol, which may free the Command structure.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-FreeCmdNameInternalRep(objPtr)
- register Tcl_Obj *objPtr; /* CmdName object with internal
- * representation to free. */
-{
- register ResolvedCmdName *resPtr =
- (ResolvedCmdName *) objPtr->internalRep.otherValuePtr;
-
- if (resPtr != NULL) {
- /*
- * Decrement the reference count of the ResolvedCmdName structure.
- * If there are no more uses, free the ResolvedCmdName structure.
- */
-
- resPtr->refCount--;
- if (resPtr->refCount == 0) {
- /*
- * Now free the cached command, unless it is still in its
- * hash table or if there are other references to it
- * from other cmdName objects.
- */
-
- Command *cmdPtr = resPtr->cmdPtr;
- TclCleanupCommand(cmdPtr);
- ckfree((char *) resPtr);
- }
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DupCmdNameInternalRep --
- *
- * Initialize the internal representation of an cmdName Tcl_Obj to a
- * copy of the internal representation of an existing cmdName object.
- *
- * Results:
- * None.
- *
- * Side effects:
- * "copyPtr"s internal rep is set to point to the ResolvedCmdName
- * structure corresponding to "srcPtr"s internal rep. Increments the
- * ref count of the ResolvedCmdName structure pointed to by the
- * cmdName's internal representation.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-DupCmdNameInternalRep(srcPtr, copyPtr)
- Tcl_Obj *srcPtr; /* Object with internal rep to copy. */
- register Tcl_Obj *copyPtr; /* Object with internal rep to set. */
-{
- register ResolvedCmdName *resPtr =
- (ResolvedCmdName *) srcPtr->internalRep.otherValuePtr;
-
- copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr;
- copyPtr->internalRep.twoPtrValue.ptr2 = NULL;
- if (resPtr != NULL) {
- resPtr->refCount++;
- }
- copyPtr->typePtr = &tclCmdNameType;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * SetCmdNameFromAny --
- *
- * Generate an cmdName internal form for the Tcl object "objPtr".
- *
- * Results:
- * The return value is a standard Tcl result. The conversion always
- * succeeds and TCL_OK is returned.
- *
- * Side effects:
- * A pointer to a ResolvedCmdName structure that holds a cached pointer
- * to the command with a name that matches objPtr's string rep is
- * stored as objPtr's internal representation. This ResolvedCmdName
- * pointer will be NULL if no matching command was found. The ref count
- * of the cached Command's structure (if any) is also incremented.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-SetCmdNameFromAny(interp, objPtr)
- Tcl_Interp *interp; /* Used for error reporting if not NULL. */
- register Tcl_Obj *objPtr; /* The object to convert. */
-{
- Interp *iPtr = (Interp *) interp;
- char *name;
- Tcl_Command cmd;
- register Command *cmdPtr;
- Namespace *currNsPtr;
- register ResolvedCmdName *resPtr;
-
- /*
- * Get "objPtr"s string representation. Make it up-to-date if necessary.
- */
-
- name = objPtr->bytes;
- if (name == NULL) {
- name = Tcl_GetString(objPtr);
- }
-
- /*
- * Find the Command structure, if any, that describes the command called
- * "name". Build a ResolvedCmdName that holds a cached pointer to this
- * Command, and bump the reference count in the referenced Command
- * structure. A Command structure will not be deleted as long as it is
- * referenced from a CmdName object.
- */
-
- cmd = Tcl_FindCommand(interp, name, (Tcl_Namespace *) NULL,
- /*flags*/ 0);
- cmdPtr = (Command *) cmd;
- if (cmdPtr != NULL) {
- /*
- * Get the current namespace.
- */
-
- if (iPtr->varFramePtr != NULL) {
- currNsPtr = iPtr->varFramePtr->nsPtr;
- } else {
- currNsPtr = iPtr->globalNsPtr;
- }
-
- cmdPtr->refCount++;
- resPtr = (ResolvedCmdName *) ckalloc(sizeof(ResolvedCmdName));
- resPtr->cmdPtr = cmdPtr;
- resPtr->refNsPtr = currNsPtr;
- resPtr->refNsId = currNsPtr->nsId;
- resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch;
- resPtr->cmdEpoch = cmdPtr->cmdEpoch;
- resPtr->refCount = 1;
- } else {
- resPtr = NULL; /* no command named "name" was found */
- }
-
- /*
- * Free the old internalRep before setting the new one. We do this as
- * late as possible to allow the conversion code, in particular
- * GetStringFromObj, to use that old internalRep. If no Command
- * structure was found, leave NULL as the cached value.
- */
-
- if ((objPtr->typePtr != NULL)
- && (objPtr->typePtr->freeIntRepProc != NULL)) {
- objPtr->typePtr->freeIntRepProc(objPtr);
- }
-
- objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr;
- objPtr->internalRep.twoPtrValue.ptr2 = NULL;
- objPtr->typePtr = &tclCmdNameType;
- return TCL_OK;
-}
-
#ifdef TCL_COMPILE_DEBUG
/*
*----------------------------------------------------------------------
@@ -5092,4 +6199,3 @@ StringForResultCode(result)
return buf;
}
#endif /* TCL_COMPILE_DEBUG */
-
diff --git a/tcl/generic/tclFCmd.c b/tcl/generic/tclFCmd.c
index 8e1d84a838a..f51b4d4a63a 100644
--- a/tcl/generic/tclFCmd.c
+++ b/tcl/generic/tclFCmd.c
@@ -20,14 +20,14 @@
*/
static int CopyRenameOneFile _ANSI_ARGS_((Tcl_Interp *interp,
- char *source, char *dest, int copyFlag,
- int force));
-static char * FileBasename _ANSI_ARGS_((Tcl_Interp *interp,
- char *path, Tcl_DString *bufferPtr));
+ Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr,
+ int copyFlag, int force));
+static Tcl_Obj * FileBasename _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *pathPtr));
static int FileCopyRename _ANSI_ARGS_((Tcl_Interp *interp,
- int argc, char **argv, int copyFlag));
+ int objc, Tcl_Obj *CONST objv[], int copyFlag));
static int FileForceOption _ANSI_ARGS_((Tcl_Interp *interp,
- int argc, char **argv, int *forcePtr));
+ int objc, Tcl_Obj *CONST objv[], int *forcePtr));
/*
*---------------------------------------------------------------------------
@@ -49,12 +49,12 @@ static int FileForceOption _ANSI_ARGS_((Tcl_Interp *interp,
*/
int
-TclFileRenameCmd(interp, argc, argv)
+TclFileRenameCmd(interp, objc, objv)
Tcl_Interp *interp; /* Interp for error reporting. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings passed to Tcl_FileCmd. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument strings passed to Tcl_FileCmd. */
{
- return FileCopyRename(interp, argc, argv, 0);
+ return FileCopyRename(interp, objc, objv, 0);
}
/*
@@ -77,12 +77,12 @@ TclFileRenameCmd(interp, argc, argv)
*/
int
-TclFileCopyCmd(interp, argc, argv)
+TclFileCopyCmd(interp, objc, objv)
Tcl_Interp *interp; /* Used for error reporting */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings passed to Tcl_FileCmd. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument strings passed to Tcl_FileCmd. */
{
- return FileCopyRename(interp, argc, argv, 1);
+ return FileCopyRename(interp, objc, objv, 1);
}
/*
@@ -103,26 +103,26 @@ TclFileCopyCmd(interp, argc, argv)
*/
static int
-FileCopyRename(interp, argc, argv, copyFlag)
+FileCopyRename(interp, objc, objv, copyFlag)
Tcl_Interp *interp; /* Used for error reporting. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings passed to Tcl_FileCmd. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument strings passed to Tcl_FileCmd. */
int copyFlag; /* If non-zero, copy source(s). Otherwise,
* rename them. */
{
int i, result, force;
- struct stat statBuf;
- Tcl_DString targetBuffer;
- char *target;
+ Tcl_StatBuf statBuf;
+ Tcl_Obj *target;
- i = FileForceOption(interp, argc - 2, argv + 2, &force);
+ i = FileForceOption(interp, objc - 2, objv + 2, &force);
if (i < 0) {
return TCL_ERROR;
}
i += 2;
- if ((argc - i) < 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ", argv[1], " ?options? source ?source ...? target\"",
+ if ((objc - i) < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]),
+ " ?options? source ?source ...? target\"",
(char *) NULL);
return TCL_ERROR;
}
@@ -133,38 +133,38 @@ FileCopyRename(interp, argc, argv, copyFlag)
* directory.
*/
- target = Tcl_TranslateFileName(interp, argv[argc - 1], &targetBuffer);
- if (target == NULL) {
+ target = objv[objc - 1];
+ if (Tcl_FSConvertToPathType(interp, target) != TCL_OK) {
return TCL_ERROR;
}
result = TCL_OK;
/*
- * Call TclStat() so that if target is a symlink that points to a
+ * Call Tcl_FSStat() so that if target is a symlink that points to a
* directory we will put the sources in that directory instead of
* overwriting the symlink.
*/
- if ((TclStat(target, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode)) {
- if ((argc - i) > 2) {
+ if ((Tcl_FSStat(target, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode)) {
+ if ((objc - i) > 2) {
errno = ENOTDIR;
Tcl_PosixError(interp);
Tcl_AppendResult(interp, "error ",
((copyFlag) ? "copying" : "renaming"), ": target \"",
- argv[argc - 1], "\" is not a directory", (char *) NULL);
+ Tcl_GetString(target), "\" is not a directory",
+ (char *) NULL);
result = TCL_ERROR;
} else {
/*
- * Even though already have target == translated(argv[i+1]),
+ * Even though already have target == translated(objv[i+1]),
* pass the original argument down, so if there's an error, the
* error message will reflect the original arguments.
*/
- result = CopyRenameOneFile(interp, argv[i], argv[i + 1], copyFlag,
+ result = CopyRenameOneFile(interp, objv[i], objv[i + 1], copyFlag,
force);
}
- Tcl_DStringFree(&targetBuffer);
return result;
}
@@ -173,30 +173,31 @@ FileCopyRename(interp, argc, argv, copyFlag)
* from each source, and append it to the end of the target path.
*/
- for ( ; i < argc - 1; i++) {
- char *jargv[2];
- char *source, *newFileName;
- Tcl_DString sourceBuffer, newFileNameBuffer;
-
- source = FileBasename(interp, argv[i], &sourceBuffer);
+ for ( ; i < objc - 1; i++) {
+ Tcl_Obj *jargv[2];
+ Tcl_Obj *source, *newFileName;
+ Tcl_Obj *temp;
+
+ source = FileBasename(interp, objv[i]);
if (source == NULL) {
result = TCL_ERROR;
break;
}
- jargv[0] = argv[argc - 1];
+ jargv[0] = objv[objc - 1];
jargv[1] = source;
- Tcl_DStringInit(&newFileNameBuffer);
- newFileName = Tcl_JoinPath(2, jargv, &newFileNameBuffer);
- result = CopyRenameOneFile(interp, argv[i], newFileName, copyFlag,
+ temp = Tcl_NewListObj(2, jargv);
+ newFileName = Tcl_FSJoinPath(temp, -1);
+ Tcl_IncrRefCount(newFileName);
+ result = CopyRenameOneFile(interp, objv[i], newFileName, copyFlag,
force);
- Tcl_DStringFree(&sourceBuffer);
- Tcl_DStringFree(&newFileNameBuffer);
+ Tcl_DecrRefCount(newFileName);
+ Tcl_DecrRefCount(temp);
+ Tcl_DecrRefCount(source);
if (result == TCL_ERROR) {
break;
}
}
- Tcl_DStringFree(&targetBuffer);
return result;
}
@@ -219,74 +220,72 @@ FileCopyRename(interp, argc, argv, copyFlag)
*----------------------------------------------------------------------
*/
int
-TclFileMakeDirsCmd(interp, argc, argv)
+TclFileMakeDirsCmd(interp, objc, objv)
Tcl_Interp *interp; /* Used for error reporting. */
- int argc; /* Number of arguments */
- char **argv; /* Argument strings passed to Tcl_FileCmd. */
+ int objc; /* Number of arguments */
+ Tcl_Obj *CONST objv[]; /* Argument strings passed to Tcl_FileCmd. */
{
- Tcl_DString nameBuffer, targetBuffer;
- char *errfile;
- int result, i, j, pargc;
- char **pargv;
- struct stat statBuf;
+ Tcl_Obj *errfile;
+ int result, i, j, pobjc;
+ Tcl_Obj *split = NULL;
+ Tcl_Obj *target = NULL;
+ Tcl_StatBuf statBuf;
- pargv = NULL;
errfile = NULL;
- Tcl_DStringInit(&nameBuffer);
- Tcl_DStringInit(&targetBuffer);
result = TCL_OK;
- for (i = 2; i < argc; i++) {
- char *name = Tcl_TranslateFileName(interp, argv[i], &nameBuffer);
- if (name == NULL) {
+ for (i = 2; i < objc; i++) {
+ if (Tcl_FSConvertToPathType(interp, objv[i]) != TCL_OK) {
result = TCL_ERROR;
break;
}
- Tcl_SplitPath(name, &pargc, &pargv);
- if (pargc == 0) {
+ split = Tcl_FSSplitPath(objv[i],&pobjc);
+ if (pobjc == 0) {
errno = ENOENT;
- errfile = argv[i];
+ errfile = objv[i];
break;
}
- for (j = 0; j < pargc; j++) {
- char *target = Tcl_JoinPath(j + 1, pargv, &targetBuffer);
-
+ for (j = 0; j < pobjc; j++) {
+ target = Tcl_FSJoinPath(split, j + 1);
+ Tcl_IncrRefCount(target);
/*
- * Call TclStat() so that if target is a symlink that points
- * to a directory we will create subdirectories in that
- * directory.
+ * Call Tcl_FSStat() so that if target is a symlink that
+ * points to a directory we will create subdirectories in
+ * that directory.
*/
- if (TclStat(target, &statBuf) == 0) {
+ if (Tcl_FSStat(target, &statBuf) == 0) {
if (!S_ISDIR(statBuf.st_mode)) {
errno = EEXIST;
errfile = target;
goto done;
}
} else if ((errno != ENOENT)
- || (TclpCreateDirectory(target) != TCL_OK)) {
+ || (Tcl_FSCreateDirectory(target) != TCL_OK)) {
errfile = target;
goto done;
}
- Tcl_DStringFree(&targetBuffer);
+ /* Forget about this sub-path */
+ Tcl_DecrRefCount(target);
+ target = NULL;
}
- ckfree((char *) pargv);
- pargv = NULL;
- Tcl_DStringFree(&nameBuffer);
+ Tcl_DecrRefCount(split);
+ split = NULL;
}
done:
if (errfile != NULL) {
Tcl_AppendResult(interp, "can't create directory \"",
- errfile, "\": ", Tcl_PosixError(interp), (char *) NULL);
+ Tcl_GetString(errfile), "\": ", Tcl_PosixError(interp),
+ (char *) NULL);
result = TCL_ERROR;
}
-
- Tcl_DStringFree(&nameBuffer);
- Tcl_DStringFree(&targetBuffer);
- if (pargv != NULL) {
- ckfree((char *) pargv);
+ if (split != NULL) {
+ Tcl_DecrRefCount(split);
+ }
+ if (target != NULL) {
+ Tcl_DecrRefCount(target);
}
return result;
}
@@ -309,39 +308,35 @@ TclFileMakeDirsCmd(interp, argc, argv)
*/
int
-TclFileDeleteCmd(interp, argc, argv)
+TclFileDeleteCmd(interp, objc, objv)
Tcl_Interp *interp; /* Used for error reporting */
- int argc; /* Number of arguments */
- char **argv; /* Argument strings passed to Tcl_FileCmd. */
+ int objc; /* Number of arguments */
+ Tcl_Obj *CONST objv[]; /* Argument strings passed to Tcl_FileCmd. */
{
- Tcl_DString nameBuffer, errorBuffer;
int i, force, result;
- char *errfile;
+ Tcl_Obj *errfile;
+ Tcl_Obj *errorBuffer = NULL;
- i = FileForceOption(interp, argc - 2, argv + 2, &force);
+ i = FileForceOption(interp, objc - 2, objv + 2, &force);
if (i < 0) {
return TCL_ERROR;
}
i += 2;
- if ((argc - i) < 1) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ", argv[1], " ?options? file ?file ...?\"", (char *) NULL);
+ if ((objc - i) < 1) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]),
+ " ?options? file ?file ...?\"", (char *) NULL);
return TCL_ERROR;
}
errfile = NULL;
result = TCL_OK;
- Tcl_DStringInit(&errorBuffer);
- Tcl_DStringInit(&nameBuffer);
- for ( ; i < argc; i++) {
- struct stat statBuf;
- char *name;
+ for ( ; i < objc; i++) {
+ Tcl_StatBuf statBuf;
- errfile = argv[i];
- Tcl_DStringSetLength(&nameBuffer, 0);
- name = Tcl_TranslateFileName(interp, argv[i], &nameBuffer);
- if (name == NULL) {
+ errfile = objv[i];
+ if (Tcl_FSConvertToPathType(interp, objv[i]) != TCL_OK) {
result = TCL_ERROR;
goto done;
}
@@ -350,7 +345,7 @@ TclFileDeleteCmd(interp, argc, argv)
* Call lstat() to get info so can delete symbolic link itself.
*/
- if (TclpLstat(name, &statBuf) != 0) {
+ if (Tcl_FSLstat(objv[i], &statBuf) != 0) {
/*
* Trying to delete a file that does not exist is not
* considered an error, just a no-op
@@ -360,10 +355,15 @@ TclFileDeleteCmd(interp, argc, argv)
result = TCL_ERROR;
}
} else if (S_ISDIR(statBuf.st_mode)) {
- result = TclpRemoveDirectory(name, force, &errorBuffer);
+ /*
+ * We own a reference count on errorBuffer, if it was set
+ * as a result of this call.
+ */
+ result = Tcl_FSRemoveDirectory(objv[i], force, &errorBuffer);
if (result != TCL_OK) {
if ((force == 0) && (errno == EEXIST)) {
- Tcl_AppendResult(interp, "error deleting \"", argv[i],
+ Tcl_AppendResult(interp, "error deleting \"",
+ Tcl_GetString(objv[i]),
"\": directory not empty", (char *) NULL);
Tcl_PosixError(interp);
goto done;
@@ -373,26 +373,44 @@ TclFileDeleteCmd(interp, argc, argv)
* If possible, use the untranslated name for the file.
*/
- errfile = Tcl_DStringValue(&errorBuffer);
- if (strcmp(name, errfile) == 0) {
- errfile = argv[i];
+ errfile = errorBuffer;
+ /* FS supposed to check between translated objv and errfile */
+ if (Tcl_FSEqualPaths(objv[i], errfile)) {
+ errfile = objv[i];
}
}
} else {
- result = TclpDeleteFile(name);
+ result = Tcl_FSDeleteFile(objv[i]);
}
- if (result == TCL_ERROR) {
+ if (result != TCL_OK) {
+ result = TCL_ERROR;
+ /*
+ * It is important that we break on error, otherwise we
+ * might end up owning reference counts on numerous
+ * errorBuffers.
+ */
break;
}
}
if (result != TCL_OK) {
- Tcl_AppendResult(interp, "error deleting \"", errfile,
- "\": ", Tcl_PosixError(interp), (char *) NULL);
+ if (errfile == NULL) {
+ /*
+ * We try to accomodate poor error results from our
+ * Tcl_FS calls
+ */
+ Tcl_AppendResult(interp, "error deleting unknown file: ",
+ Tcl_PosixError(interp), (char *) NULL);
+ } else {
+ Tcl_AppendResult(interp, "error deleting \"",
+ Tcl_GetString(errfile), "\": ",
+ Tcl_PosixError(interp), (char *) NULL);
+ }
}
done:
- Tcl_DStringFree(&errorBuffer);
- Tcl_DStringFree(&nameBuffer);
+ if (errorBuffer != NULL) {
+ Tcl_DecrRefCount(errorBuffer);
+ }
return result;
}
@@ -418,9 +436,9 @@ TclFileDeleteCmd(interp, argc, argv)
static int
CopyRenameOneFile(interp, source, target, copyFlag, force)
Tcl_Interp *interp; /* Used for error reporting. */
- char *source; /* Pathname of file to copy. May need to
+ Tcl_Obj *source; /* Pathname of file to copy. May need to
* be translated. */
- char *target; /* Pathname of file to create/overwrite.
+ Tcl_Obj *target; /* Pathname of file to create/overwrite.
* May need to be translated. */
int copyFlag; /* If non-zero, copy files. Otherwise,
* rename them. */
@@ -429,23 +447,21 @@ CopyRenameOneFile(interp, source, target, copyFlag, force)
* exists. */
{
int result;
- Tcl_DString sourcePath, targetPath, errorBuffer;
- char *targetName, *sourceName, *errfile;
- struct stat sourceStatBuf, targetStatBuf;
+ Tcl_Obj *errfile, *errorBuffer;
+ /* If source is a link, then this is the real file/directory */
+ Tcl_Obj *actualSource = NULL;
+ Tcl_StatBuf sourceStatBuf, targetStatBuf;
- sourceName = Tcl_TranslateFileName(interp, source, &sourcePath);
- if (sourceName == NULL) {
+ if (Tcl_FSConvertToPathType(interp, source) != TCL_OK) {
return TCL_ERROR;
}
- targetName = Tcl_TranslateFileName(interp, target, &targetPath);
- if (targetName == NULL) {
- Tcl_DStringFree(&sourcePath);
+ if (Tcl_FSConvertToPathType(interp, target) != TCL_OK) {
return TCL_ERROR;
}
errfile = NULL;
+ errorBuffer = NULL;
result = TCL_ERROR;
- Tcl_DStringInit(&errorBuffer);
/*
* We want to copy/rename links and not the files they point to, so we
@@ -454,11 +470,11 @@ CopyRenameOneFile(interp, source, target, copyFlag, force)
* target.
*/
- if (TclpLstat(sourceName, &sourceStatBuf) != 0) {
+ if (Tcl_FSLstat(source, &sourceStatBuf) != 0) {
errfile = source;
goto done;
}
- if (TclpLstat(targetName, &targetStatBuf) != 0) {
+ if (Tcl_FSLstat(target, &targetStatBuf) != 0) {
if (errno != ENOENT) {
errfile = target;
goto done;
@@ -495,28 +511,31 @@ CopyRenameOneFile(interp, source, target, copyFlag, force)
if (S_ISDIR(sourceStatBuf.st_mode)
&& !S_ISDIR(targetStatBuf.st_mode)) {
errno = EISDIR;
- Tcl_AppendResult(interp, "can't overwrite file \"", target,
- "\" with directory \"", source, "\"", (char *) NULL);
+ Tcl_AppendResult(interp, "can't overwrite file \"",
+ Tcl_GetString(target), "\" with directory \"",
+ Tcl_GetString(source), "\"", (char *) NULL);
goto done;
}
if (!S_ISDIR(sourceStatBuf.st_mode)
&& S_ISDIR(targetStatBuf.st_mode)) {
errno = EISDIR;
- Tcl_AppendResult(interp, "can't overwrite directory \"", target,
- "\" with file \"", source, "\"", (char *) NULL);
+ Tcl_AppendResult(interp, "can't overwrite directory \"",
+ Tcl_GetString(target), "\" with file \"",
+ Tcl_GetString(source), "\"", (char *) NULL);
goto done;
}
}
if (copyFlag == 0) {
- result = TclpRenameFile(sourceName, targetName);
+ result = Tcl_FSRenameFile(source, target);
if (result == TCL_OK) {
goto done;
}
if (errno == EINVAL) {
- Tcl_AppendResult(interp, "error renaming \"", source, "\" to \"",
- target, "\": trying to rename a volume or ",
+ Tcl_AppendResult(interp, "error renaming \"",
+ Tcl_GetString(source), "\" to \"",
+ Tcl_GetString(target), "\": trying to rename a volume or ",
"move a directory into itself", (char *) NULL);
goto done;
} else if (errno != EXDEV) {
@@ -527,50 +546,138 @@ CopyRenameOneFile(interp, source, target, copyFlag, force)
/*
* The rename failed because the move was across file systems.
* Fall through to copy file and then remove original. Note that
- * the low-level TclpRenameFile is allowed to implement
- * cross-filesystem moves itself.
+ * the low-level Tcl_FSRenameFileProc in the filesystem is allowed
+ * to implement cross-filesystem moves itself, if it desires.
+ */
+ }
+
+ actualSource = source;
+ Tcl_IncrRefCount(actualSource);
+#if 0
+#ifdef S_ISLNK
+ /*
+ * To add a flag to make 'copy' copy links instead of files, we could
+ * add a condition to ignore this 'if' here.
+ */
+ if (copyFlag && S_ISLNK(sourceStatBuf.st_mode)) {
+ /*
+ * We want to copy files not links. Therefore we must follow the
+ * link. There are two purposes to this 'stat' call here. First
+ * we want to know if the linked-file/dir actually exists, and
+ * second, in the block of code which follows, some 20 lines
+ * down, we want to check if the thing is a file or directory.
*/
+ if (Tcl_FSStat(source, &sourceStatBuf) != 0) {
+ /* Actual file doesn't exist */
+ Tcl_AppendResult(interp,
+ "error copying \"", Tcl_GetString(source),
+ "\": the target of this link doesn't exist",
+ (char *) NULL);
+ goto done;
+ } else {
+ int counter = 0;
+ while (1) {
+ Tcl_Obj *path = Tcl_FSLink(actualSource, NULL, 0);
+ if (path == NULL) {
+ break;
+ }
+ Tcl_DecrRefCount(actualSource);
+ actualSource = path;
+ counter++;
+ /* Arbitrary limit of 20 links to follow */
+ if (counter > 20) {
+ /* Too many links */
+ Tcl_SetErrno(EMLINK);
+ errfile = source;
+ goto done;
+ }
+ }
+ /* Now 'actualSource' is the correct file */
+ }
}
+#endif
+#endif
if (S_ISDIR(sourceStatBuf.st_mode)) {
- result = TclpCopyDirectory(sourceName, targetName, &errorBuffer);
+ result = Tcl_FSCopyDirectory(actualSource, target, &errorBuffer);
if (result != TCL_OK) {
- errfile = Tcl_DStringValue(&errorBuffer);
- if (strcmp(errfile, sourceName) == 0) {
- errfile = source;
- } else if (strcmp(errfile, targetName) == 0) {
- errfile = target;
+ if (errno == EXDEV) {
+ /*
+ * The copy failed because we're trying to do a
+ * cross-filesystem copy. We do this through our Tcl
+ * library.
+ */
+ Tcl_SavedResult savedResult;
+ Tcl_Obj *copyCommand = Tcl_NewListObj(0,NULL);
+ Tcl_IncrRefCount(copyCommand);
+ Tcl_ListObjAppendElement(interp, copyCommand,
+ Tcl_NewStringObj("::tcl::CopyDirectory",-1));
+ if (copyFlag) {
+ Tcl_ListObjAppendElement(interp, copyCommand,
+ Tcl_NewStringObj("copying",-1));
+ } else {
+ Tcl_ListObjAppendElement(interp, copyCommand,
+ Tcl_NewStringObj("renaming",-1));
+ }
+ Tcl_ListObjAppendElement(interp, copyCommand, source);
+ Tcl_ListObjAppendElement(interp, copyCommand, target);
+ Tcl_SaveResult(interp, &savedResult);
+ result = Tcl_EvalObjEx(interp, copyCommand,
+ TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT);
+ Tcl_DecrRefCount(copyCommand);
+ if (result != TCL_OK) {
+ /*
+ * There was an error in the Tcl-level copy.
+ * We will pass on the Tcl error message and
+ * can ensure this by setting errfile to NULL
+ */
+ Tcl_DiscardResult(&savedResult);
+ errfile = NULL;
+ } else {
+ /* The copy was successful */
+ Tcl_RestoreResult(interp, &savedResult);
+ }
+ } else {
+ errfile = errorBuffer;
+ if (Tcl_FSEqualPaths(errfile, source)) {
+ errfile = source;
+ } else if (Tcl_FSEqualPaths(errfile, target)) {
+ errfile = target;
+ }
}
}
} else {
- result = TclpCopyFile(sourceName, targetName);
+ result = Tcl_FSCopyFile(actualSource, target);
+ if ((result != TCL_OK) && (errno == EXDEV)) {
+ result = TclCrossFilesystemCopy(interp, source, target);
+ }
if (result != TCL_OK) {
- /*
- * Well, there really shouldn't be a problem with source,
- * because up there we checked to see if it was ok to copy it.
+ /*
+ * We could examine 'errno' to double-check if the problem
+ * was with the target, but we checked the source above,
+ * so it should be quite clear
*/
-
errfile = target;
}
}
if ((copyFlag == 0) && (result == TCL_OK)) {
if (S_ISDIR(sourceStatBuf.st_mode)) {
- result = TclpRemoveDirectory(sourceName, 1, &errorBuffer);
+ result = Tcl_FSRemoveDirectory(source, 1, &errorBuffer);
if (result != TCL_OK) {
- errfile = Tcl_DStringValue(&errorBuffer);
- if (strcmp(errfile, sourceName) == 0) {
+ if (Tcl_FSEqualPaths(errfile, source) == 0) {
errfile = source;
}
}
} else {
- result = TclpDeleteFile(sourceName);
+ result = Tcl_FSDeleteFile(source);
if (result != TCL_OK) {
errfile = source;
}
}
if (result != TCL_OK) {
- Tcl_AppendResult(interp, "can't unlink \"", errfile, "\": ",
- Tcl_PosixError(interp), (char *) NULL);
+ Tcl_AppendResult(interp, "can't unlink \"",
+ Tcl_GetString(errfile), "\": ",
+ Tcl_PosixError(interp), (char *) NULL);
errfile = NULL;
}
}
@@ -579,19 +686,24 @@ CopyRenameOneFile(interp, source, target, copyFlag, force)
if (errfile != NULL) {
Tcl_AppendResult(interp,
((copyFlag) ? "error copying \"" : "error renaming \""),
- source, (char *) NULL);
+ Tcl_GetString(source), (char *) NULL);
if (errfile != source) {
- Tcl_AppendResult(interp, "\" to \"", target, (char *) NULL);
+ Tcl_AppendResult(interp, "\" to \"", Tcl_GetString(target),
+ (char *) NULL);
if (errfile != target) {
- Tcl_AppendResult(interp, "\": \"", errfile, (char *) NULL);
+ Tcl_AppendResult(interp, "\": \"", Tcl_GetString(errfile),
+ (char *) NULL);
}
}
Tcl_AppendResult(interp, "\": ", Tcl_PosixError(interp),
(char *) NULL);
}
- Tcl_DStringFree(&errorBuffer);
- Tcl_DStringFree(&sourcePath);
- Tcl_DStringFree(&targetPath);
+ if (errorBuffer != NULL) {
+ Tcl_DecrRefCount(errorBuffer);
+ }
+ if (actualSource != NULL) {
+ Tcl_DecrRefCount(actualSource);
+ }
return result;
}
@@ -616,10 +728,10 @@ CopyRenameOneFile(interp, source, target, copyFlag, force)
*/
static int
-FileForceOption(interp, argc, argv, forcePtr)
+FileForceOption(interp, objc, objv, forcePtr)
Tcl_Interp *interp; /* Interp, for error return. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. First command line
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument strings. First command line
* option, if it exists, begins at 0. */
int *forcePtr; /* If the "-force" was specified, *forcePtr
* is filled with 1, otherwise with 0. */
@@ -627,17 +739,17 @@ FileForceOption(interp, argc, argv, forcePtr)
int force, i;
force = 0;
- for (i = 0; i < argc; i++) {
- if (argv[i][0] != '-') {
+ for (i = 0; i < objc; i++) {
+ if (Tcl_GetString(objv[i])[0] != '-') {
break;
}
- if (strcmp(argv[i], "-force") == 0) {
+ if (strcmp(Tcl_GetString(objv[i]), "-force") == 0) {
force = 1;
- } else if (strcmp(argv[i], "--") == 0) {
+ } else if (strcmp(Tcl_GetString(objv[i]), "--") == 0) {
i++;
break;
} else {
- Tcl_AppendResult(interp, "bad option \"", argv[i],
+ Tcl_AppendResult(interp, "bad option \"", Tcl_GetString(objv[i]),
"\": should be -force or --", (char *)NULL);
return -1;
}
@@ -656,10 +768,9 @@ FileForceOption(interp, argc, argv, forcePtr)
* if path is the root directory, returns no characters.
*
* Results:
- * Appends the string that represents the basename to the end of
- * the specified initialized DString, returning a pointer to the
- * resulting string. If there is an error, an error message is left
- * in interp, NULL is returned, and the Tcl_DString is unmodified.
+ * Returns the string object that represents the basename. If there
+ * is an error, an error message is left in interp, and NULL is
+ * returned.
*
* Side effects:
* None.
@@ -667,47 +778,45 @@ FileForceOption(interp, argc, argv, forcePtr)
*---------------------------------------------------------------------------
*/
-static char *
-FileBasename(interp, path, bufferPtr)
+static Tcl_Obj *
+FileBasename(interp, pathPtr)
Tcl_Interp *interp; /* Interp, for error return. */
- char *path; /* Path whose basename to extract. */
- Tcl_DString *bufferPtr; /* Initialized DString that receives
- * basename. */
+ Tcl_Obj *pathPtr; /* Path whose basename to extract. */
{
- int argc;
- char **argv;
+ int objc;
+ Tcl_Obj *splitPtr;
+ Tcl_Obj *resultPtr = NULL;
- Tcl_SplitPath(path, &argc, &argv);
- if (argc == 0) {
- Tcl_DStringInit(bufferPtr);
- } else {
- if ((argc == 1) && (*path == '~')) {
- Tcl_DString buffer;
-
- ckfree((char *) argv);
- path = Tcl_TranslateFileName(interp, path, &buffer);
- if (path == NULL) {
+ splitPtr = Tcl_FSSplitPath(pathPtr, &objc);
+
+ if (objc != 0) {
+ if ((objc == 1) && (*Tcl_GetString(pathPtr) == '~')) {
+ Tcl_DecrRefCount(splitPtr);
+ if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
return NULL;
}
- Tcl_SplitPath(path, &argc, &argv);
- Tcl_DStringFree(&buffer);
+ splitPtr = Tcl_FSSplitPath(pathPtr, &objc);
}
- Tcl_DStringInit(bufferPtr);
/*
* Return the last component, unless it is the only component, and it
* is the root of an absolute path.
*/
- if (argc > 0) {
- if ((argc > 1)
- || (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) {
- Tcl_DStringAppend(bufferPtr, argv[argc - 1], -1);
+ if (objc > 0) {
+ Tcl_ListObjIndex(NULL, splitPtr, objc-1, &resultPtr);
+ if ((objc == 1) &&
+ (Tcl_FSGetPathType(resultPtr) != TCL_PATH_RELATIVE)) {
+ resultPtr = NULL;
}
}
}
- ckfree((char *) argv);
- return Tcl_DStringValue(bufferPtr);
+ if (resultPtr == NULL) {
+ resultPtr = Tcl_NewObj();
+ }
+ Tcl_IncrRefCount(resultPtr);
+ Tcl_DecrRefCount(splitPtr);
+ return resultPtr;
}
/*
@@ -715,15 +824,15 @@ FileBasename(interp, path, bufferPtr)
*
* TclFileAttrsCmd --
*
- * Sets or gets the platform-specific attributes of a file. The objc-objv
- * points to the file name with the rest of the command line following.
- * This routine uses platform-specific tables of option strings
- * and callbacks. The callback to get the attributes take three
- * parameters:
+ * Sets or gets the platform-specific attributes of a file. The
+ * objc-objv points to the file name with the rest of the command
+ * line following. This routine uses platform-specific tables of
+ * option strings and callbacks. The callback to get the
+ * attributes take three parameters:
* Tcl_Interp *interp; The interp to report errors with.
* Since this is an object-based API,
- * the object form of the result should be
- * used.
+ * the object form of the result should
+ * be used.
* CONST char *fileName; This is extracted using
* Tcl_TranslateFileName.
* TclObj **attrObjPtrPtr; A new object to hold the attribute
@@ -751,46 +860,80 @@ TclFileAttrsCmd(interp, objc, objv)
int objc; /* Number of command line arguments. */
Tcl_Obj *CONST objv[]; /* The command line objects. */
{
- char *fileName;
int result;
- Tcl_DString buffer;
-
+ CONST char ** attributeStrings;
+ Tcl_Obj* objStrings = NULL;
+ int numObjStrings = -1;
+ Tcl_Obj *filePtr;
+
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv,
"name ?option? ?value? ?option value ...?");
return TCL_ERROR;
}
- fileName = Tcl_GetString(objv[2]);
- fileName = Tcl_TranslateFileName(interp, fileName, &buffer);
- if (fileName == NULL) {
+ filePtr = objv[2];
+ if (Tcl_FSConvertToPathType(interp, filePtr) != TCL_OK) {
return TCL_ERROR;
}
objc -= 3;
objv += 3;
result = TCL_ERROR;
-
+ Tcl_SetErrno(0);
+ attributeStrings = Tcl_FSFileAttrStrings(filePtr, &objStrings);
+ if (attributeStrings == NULL) {
+ int index;
+ Tcl_Obj *objPtr;
+ if (objStrings == NULL) {
+ if (Tcl_GetErrno() != 0) {
+ /*
+ * There was an error, probably that the filePtr is
+ * not accepted by any filesystem
+ */
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "could not read \"", Tcl_GetString(filePtr),
+ "\": ", Tcl_PosixError(interp),
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ goto end;
+ }
+ /* We own the object now */
+ Tcl_IncrRefCount(objStrings);
+ /* Use objStrings as a list object */
+ if (Tcl_ListObjLength(interp, objStrings, &numObjStrings) != TCL_OK) {
+ goto end;
+ }
+ attributeStrings = (CONST char **)
+ ckalloc ((1+numObjStrings) * sizeof(char*));
+ for (index = 0; index < numObjStrings; index++) {
+ Tcl_ListObjIndex(interp, objStrings, index, &objPtr);
+ attributeStrings[index] = Tcl_GetString(objPtr);
+ }
+ attributeStrings[index] = NULL;
+ }
if (objc == 0) {
/*
* Get all attributes.
*/
int index;
- Tcl_Obj *listPtr, *objPtr;
+ Tcl_Obj *listPtr;
listPtr = Tcl_NewListObj(0, NULL);
- for (index = 0; tclpFileAttrStrings[index] != NULL; index++) {
- objPtr = Tcl_NewStringObj(tclpFileAttrStrings[index], -1);
+ for (index = 0; attributeStrings[index] != NULL; index++) {
+ Tcl_Obj *objPtr = Tcl_NewStringObj(attributeStrings[index], -1);
Tcl_ListObjAppendElement(interp, listPtr, objPtr);
-
- if ((*tclpFileAttrProcs[index].getProc)(interp, index, fileName,
- &objPtr) != TCL_OK) {
+ /* We now forget about objPtr, it is in the list */
+ objPtr = NULL;
+ if (Tcl_FSFileAttrsGet(interp, index, filePtr,
+ &objPtr) != TCL_OK) {
Tcl_DecrRefCount(listPtr);
goto end;
}
Tcl_ListObjAppendElement(interp, listPtr, objPtr);
- }
+ }
Tcl_SetObjResult(interp, listPtr);
} else if (objc == 1) {
/*
@@ -798,13 +941,20 @@ TclFileAttrsCmd(interp, objc, objv)
*/
int index;
- Tcl_Obj *objPtr;
-
- if (Tcl_GetIndexFromObj(interp, objv[0], tclpFileAttrStrings,
+ Tcl_Obj *objPtr = NULL;
+
+ if (numObjStrings == 0) {
+ Tcl_AppendResult(interp, "bad option \"",
+ Tcl_GetString(objv[0]), "\", there are no file attributes"
+ " in this filesystem.", (char *) NULL);
+ goto end;
+ }
+
+ if (Tcl_GetIndexFromObj(interp, objv[0], attributeStrings,
"option", 0, &index) != TCL_OK) {
goto end;
- }
- if ((*tclpFileAttrProcs[index].getProc)(interp, index, fileName,
+ }
+ if (Tcl_FSFileAttrsGet(interp, index, filePtr,
&objPtr) != TCL_OK) {
goto end;
}
@@ -816,8 +966,15 @@ TclFileAttrsCmd(interp, objc, objv)
int i, index;
+ if (numObjStrings == 0) {
+ Tcl_AppendResult(interp, "bad option \"",
+ Tcl_GetString(objv[0]), "\", there are no file attributes"
+ " in this filesystem.", (char *) NULL);
+ goto end;
+ }
+
for (i = 0; i < objc ; i += 2) {
- if (Tcl_GetIndexFromObj(interp, objv[i], tclpFileAttrStrings,
+ if (Tcl_GetIndexFromObj(interp, objv[i], attributeStrings,
"option", 0, &index) != TCL_OK) {
goto end;
}
@@ -827,7 +984,7 @@ TclFileAttrsCmd(interp, objc, objv)
(char *) NULL);
goto end;
}
- if ((*tclpFileAttrProcs[index].setProc)(interp, index, fileName,
+ if (Tcl_FSFileAttrsSet(interp, index, filePtr,
objv[i + 1]) != TCL_OK) {
goto end;
}
@@ -836,6 +993,16 @@ TclFileAttrsCmd(interp, objc, objv)
result = TCL_OK;
end:
- Tcl_DStringFree(&buffer);
+ if (numObjStrings != -1) {
+ /* Free up the array we allocated */
+ ckfree((char*)attributeStrings);
+ /*
+ * We don't need this object that was passed to us
+ * any more.
+ */
+ if (objStrings != NULL) {
+ Tcl_DecrRefCount(objStrings);
+ }
+ }
return result;
}
diff --git a/tcl/generic/tclFileName.c b/tcl/generic/tclFileName.c
index 32e9495c6df..431dad49047 100644
--- a/tcl/generic/tclFileName.c
+++ b/tcl/generic/tclFileName.c
@@ -17,18 +17,27 @@
#include "tclPort.h"
#include "tclRegexp.h"
-/*
- * The following regular expression matches the root portion of a Windows
- * absolute or volume relative path. It will match both UNC and drive relative
- * paths.
+/*
+ * This define is used to activate Tcl's interpretation of Unix-style
+ * paths (containing forward slashes, '.' and '..') on MacOS. A
+ * side-effect of this is that some paths become ambiguous.
*/
+#define MAC_UNDERSTANDS_UNIX_PATHS
-#define WIN_ROOT_PATTERN "^(([a-zA-Z]:)|[/\\\\][/\\\\]+([^/\\\\]+)[/\\\\]+([^/\\\\]+)|([/\\\\]))([/\\\\])*"
-
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
/*
* The following regular expression matches the root portion of a Macintosh
* absolute path. It will match degenerate Unix-style paths, tilde paths,
- * Unix-style paths, and Mac paths.
+ * Unix-style paths, and Mac paths. The various subexpressions in this
+ * can be summarised as follows: ^(/..|~user/unix|~user:mac|/unix|mac:dir).
+ * The subexpression indices which match the root portions, are as follows:
+ *
+ * degenerate unix-style: 2
+ * unix-tilde: 5
+ * mac-tilde: 7
+ * unix-style: 9 (or 10 to cut off the irrelevant header).
+ * mac: 12
+ *
*/
#define MAC_ROOT_PATTERN "^((/+([.][.]?/+)*([.][.]?)?)|(~[^:/]*)(/[^:]*)?|(~[^:]*)(:.*)?|/+([.][.]?/+)*([^:/]+)(/[^:]*)?|([^:]+):.*)$"
@@ -45,6 +54,11 @@ typedef struct ThreadSpecificData {
static Tcl_ThreadDataKey dataKey;
+static void FileNameCleanup _ANSI_ARGS_((ClientData clientData));
+static void FileNameInit _ANSI_ARGS_((void));
+
+#endif
+
/*
* The following variable is set in the TclPlatformInit call to one
* of: TCL_PLATFORM_UNIX, TCL_PLATFORM_MAC, or TCL_PLATFORM_WINDOWS.
@@ -53,32 +67,20 @@ static Tcl_ThreadDataKey dataKey;
TclPlatformType tclPlatform = TCL_PLATFORM_UNIX;
/*
- * The "globParameters" argument of the globbing functions is an
- * or'ed combination of the following values:
- */
-
-#define GLOBMODE_NO_COMPLAIN 1
-#define GLOBMODE_JOIN 2
-#define GLOBMODE_DIR 4
-
-/*
* Prototypes for local procedures defined in this file:
*/
-static char * DoTildeSubst _ANSI_ARGS_((Tcl_Interp *interp,
+static CONST char * DoTildeSubst _ANSI_ARGS_((Tcl_Interp *interp,
CONST char *user, Tcl_DString *resultPtr));
static CONST char * ExtractWinRoot _ANSI_ARGS_((CONST char *path,
- Tcl_DString *resultPtr, int offset, Tcl_PathType *typePtr));
-static void FileNameCleanup _ANSI_ARGS_((ClientData clientData));
-static void FileNameInit _ANSI_ARGS_((void));
+ Tcl_DString *resultPtr, int offset,
+ Tcl_PathType *typePtr));
static int SkipToChar _ANSI_ARGS_((char **stringPtr,
char *match));
-static char * SplitMacPath _ANSI_ARGS_((CONST char *path,
- Tcl_DString *bufPtr));
-static char * SplitWinPath _ANSI_ARGS_((CONST char *path,
- Tcl_DString *bufPtr));
-static char * SplitUnixPath _ANSI_ARGS_((CONST char *path,
- Tcl_DString *bufPtr));
+static Tcl_Obj* SplitMacPath _ANSI_ARGS_((CONST char *path));
+static Tcl_Obj* SplitWinPath _ANSI_ARGS_((CONST char *path));
+static Tcl_Obj* SplitUnixPath _ANSI_ARGS_((CONST char *path));
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
/*
*----------------------------------------------------------------------
@@ -132,6 +134,7 @@ FileNameCleanup(clientData)
Tcl_DecrRefCount(tsdPtr->macRootPatternPtr);
tsdPtr->initialized = 0;
}
+#endif
/*
*----------------------------------------------------------------------
@@ -161,22 +164,19 @@ ExtractWinRoot(path, resultPtr, offset, typePtr)
* stored. */
Tcl_PathType *typePtr; /* Where to store pathType result */
{
- FileNameInit();
-
-
if (path[0] == '/' || path[0] == '\\') {
/* Might be a UNC or Vol-Relative path */
- char *host, *share, *tail;
+ CONST char *host, *share, *tail;
int hlen, slen;
if (path[1] != '/' && path[1] != '\\') {
Tcl_DStringSetLength(resultPtr, offset);
*typePtr = TCL_PATH_VOLUME_RELATIVE;
Tcl_DStringAppend(resultPtr, "/", 1);
return &path[1];
- }
- host = (char *)&path[2];
+ }
+ host = &path[2];
- /* Skip seperators */
+ /* Skip separators */
while (host[0] == '/' || host[0] == '\\') host++;
for (hlen = 0; host[hlen];hlen++) {
@@ -184,6 +184,18 @@ ExtractWinRoot(path, resultPtr, offset, typePtr)
break;
}
if (host[hlen] == 0 || host[hlen+1] == 0) {
+ /*
+ * The path given is simply of the form
+ * '/foo', '//foo', '/////foo' or the same
+ * with backslashes. If there is exactly
+ * one leading '/' the path is volume relative
+ * (see filename man page). If there are more
+ * than one, we are simply assuming they
+ * are superfluous and we trim them away.
+ * (An alternative interpretation would
+ * be that it is a host name, but we have
+ * been documented that that is not the case).
+ */
*typePtr = TCL_PATH_VOLUME_RELATIVE;
Tcl_DStringAppend(resultPtr, "/", 1);
return &path[2];
@@ -191,7 +203,7 @@ ExtractWinRoot(path, resultPtr, offset, typePtr)
Tcl_DStringSetLength(resultPtr, offset);
share = &host[hlen];
- /* Skip seperators */
+ /* Skip separators */
while (share[0] == '/' || share[0] == '\\') share++;
for (slen = 0; share[slen];slen++) {
@@ -205,12 +217,12 @@ ExtractWinRoot(path, resultPtr, offset, typePtr)
tail = &share[slen];
- /* Skip seperators */
+ /* Skip separators */
while (tail[0] == '/' || tail[0] == '\\') tail++;
*typePtr = TCL_PATH_ABSOLUTE;
return tail;
- } else if (path[1] == ':') {
+ } else if (*path && path[1] == ':') {
/* Might be a drive sep */
Tcl_DStringSetLength(resultPtr, offset);
@@ -218,17 +230,17 @@ ExtractWinRoot(path, resultPtr, offset, typePtr)
*typePtr = TCL_PATH_VOLUME_RELATIVE;
Tcl_DStringAppend(resultPtr, path, 2);
return &path[2];
- } else {
+ } else {
char *tail = (char*)&path[3];
- /* Skip seperators */
- while (tail[0] == '/' || tail[0] == '\\') tail++;
+ /* Skip separators */
+ while (*tail && (tail[0] == '/' || tail[0] == '\\')) tail++;
*typePtr = TCL_PATH_ABSOLUTE;
Tcl_DStringAppend(resultPtr, path, 2);
- Tcl_DStringAppend(resultPtr, "/", 1);
+ Tcl_DStringAppend(resultPtr, "/", 1);
- return tail;
+ return tail;
}
} else {
*typePtr = TCL_PATH_RELATIVE;
@@ -243,6 +255,10 @@ ExtractWinRoot(path, resultPtr, offset, typePtr)
*
* Determines whether a given path is relative to the current
* directory, relative to the current volume, or absolute.
+ *
+ * The objectified Tcl_FSGetPathType should be used in
+ * preference to this function (as you can see below, this
+ * is just a wrapper around that other function).
*
* Results:
* Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
@@ -256,65 +272,258 @@ ExtractWinRoot(path, resultPtr, offset, typePtr)
Tcl_PathType
Tcl_GetPathType(path)
- char *path;
+ CONST char *path;
{
- ThreadSpecificData *tsdPtr;
- Tcl_PathType type = TCL_PATH_ABSOLUTE;
- Tcl_RegExp re;
-
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- /*
- * Paths that begin with / or ~ are absolute.
- */
+ Tcl_PathType type;
+ Tcl_Obj *tempObj = Tcl_NewStringObj(path,-1);
+ Tcl_IncrRefCount(tempObj);
+ type = Tcl_FSGetPathType(tempObj);
+ Tcl_DecrRefCount(tempObj);
+ return type;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpGetNativePathType --
+ *
+ * Determines whether a given path is relative to the current
+ * directory, relative to the current volume, or absolute, but
+ * ONLY FOR THE NATIVE FILESYSTEM. This function is called from
+ * tclIOUtil.c (but needs to be here due to its dependence on
+ * static variables/functions in this file). The exported
+ * function Tcl_FSGetPathType should be used by extensions.
+ *
+ * Results:
+ * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
+ * TCL_PATH_VOLUME_RELATIVE.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
- if ((path[0] != '/') && (path[0] != '~')) {
- type = TCL_PATH_RELATIVE;
+Tcl_PathType
+TclpGetNativePathType(pathObjPtr, driveNameLengthPtr, driveNameRef)
+ Tcl_Obj *pathObjPtr;
+ int *driveNameLengthPtr;
+ Tcl_Obj **driveNameRef;
+{
+ Tcl_PathType type = TCL_PATH_ABSOLUTE;
+ int pathLen;
+ char *path = Tcl_GetStringFromObj(pathObjPtr, &pathLen);
+
+ if (path[0] == '~') {
+ /*
+ * This case is common to all platforms.
+ * Paths that begin with ~ are absolute.
+ */
+ if (driveNameLengthPtr != NULL) {
+ char *end = path + 1;
+ while ((*end != '\0') && (*end != '/')) {
+ end++;
}
- break;
-
- case TCL_PLATFORM_MAC:
- if (path[0] == ':') {
- type = TCL_PATH_RELATIVE;
- } else if (path[0] != '~') {
- tsdPtr = TCL_TSD_INIT(&dataKey);
-
+ *driveNameLengthPtr = end - path;
+ }
+ } else {
+ switch (tclPlatform) {
+ case TCL_PLATFORM_UNIX: {
+ char *origPath = path;
+
/*
- * Since we have eliminated the easy cases, use the
- * root pattern to look for the other types.
+ * Paths that begin with / are absolute.
*/
- FileNameInit();
- re = Tcl_GetRegExpFromObj(NULL, tsdPtr->macRootPatternPtr,
- REG_ADVANCED);
-
- if (!Tcl_RegExpExec(NULL, re, path, path)) {
+#ifdef __QNX__
+ /*
+ * Check for QNX //<node id> prefix
+ */
+ if (*path && (pathLen > 3) && (path[0] == '/')
+ && (path[1] == '/') && isdigit(UCHAR(path[2]))) {
+ path += 3;
+ while (isdigit(UCHAR(*path))) {
+ ++path;
+ }
+ }
+#endif
+ if (path[0] == '/') {
+ if (driveNameLengthPtr != NULL) {
+ /*
+ * We need this addition in case the QNX code
+ * was used
+ */
+ *driveNameLengthPtr = (1 + path - origPath);
+ }
+ } else {
+ type = TCL_PATH_RELATIVE;
+ }
+ break;
+ }
+ case TCL_PLATFORM_MAC:
+ if (path[0] == ':') {
type = TCL_PATH_RELATIVE;
} else {
- char *unixRoot, *dummy;
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
+ ThreadSpecificData *tsdPtr;
+ Tcl_RegExp re;
- Tcl_RegExpRange(re, 2, &unixRoot, &dummy);
- if (unixRoot) {
+ tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ /*
+ * Since we have eliminated the easy cases, use the
+ * root pattern to look for the other types.
+ */
+
+ FileNameInit();
+ re = Tcl_GetRegExpFromObj(NULL, tsdPtr->macRootPatternPtr,
+ REG_ADVANCED);
+
+ if (!Tcl_RegExpExec(NULL, re, path, path)) {
+ type = TCL_PATH_RELATIVE;
+ } else {
+ CONST char *root, *end;
+ Tcl_RegExpRange(re, 2, &root, &end);
+ if (root != NULL) {
+ type = TCL_PATH_RELATIVE;
+ } else {
+ if (driveNameLengthPtr != NULL) {
+ Tcl_RegExpRange(re, 0, &root, &end);
+ *driveNameLengthPtr = end - root;
+ }
+ if (driveNameRef != NULL) {
+ if (*root == '/') {
+ char *c;
+ int gotColon = 0;
+ *driveNameRef = Tcl_NewStringObj(root + 1,
+ end - root -1);
+ c = Tcl_GetString(*driveNameRef);
+ while (*c != '\0') {
+ if (*c == '/') {
+ gotColon++;
+ *c = ':';
+ }
+ c++;
+ }
+ /*
+ * If there is no colon, we have just a
+ * volume name so we must add a colon so
+ * it is an absolute path.
+ */
+ if (gotColon == 0) {
+ Tcl_AppendToObj(*driveNameRef, ":", 1);
+ } else if ((gotColon > 1) &&
+ (*(c-1) == ':')) {
+ /* We have an extra colon */
+ Tcl_SetObjLength(*driveNameRef,
+ c - Tcl_GetString(*driveNameRef) - 1);
+ }
+ }
+ }
+ }
+ }
+#else
+ if (path[0] == '~') {
+ } else if (path[0] == ':') {
type = TCL_PATH_RELATIVE;
+ } else {
+ char *colonPos = strchr(path,':');
+ if (colonPos == NULL) {
+ type = TCL_PATH_RELATIVE;
+ } else {
+ }
}
+ if (type == TCL_PATH_ABSOLUTE) {
+ if (driveNameLengthPtr != NULL) {
+ *driveNameLengthPtr = strlen(path);
+ }
+ }
+#endif
}
- }
- break;
-
- case TCL_PLATFORM_WINDOWS:
- if (path[0] != '~') {
+ break;
+
+ case TCL_PLATFORM_WINDOWS: {
Tcl_DString ds;
-
+ CONST char *rootEnd;
+
Tcl_DStringInit(&ds);
- (VOID)ExtractWinRoot(path, &ds, 0, &type);
+ rootEnd = ExtractWinRoot(path, &ds, 0, &type);
+ if ((rootEnd != path) && (driveNameLengthPtr != NULL)) {
+ *driveNameLengthPtr = rootEnd - path;
+ if (driveNameRef != NULL) {
+ *driveNameRef = Tcl_NewStringObj(Tcl_DStringValue(&ds),
+ Tcl_DStringLength(&ds));
+ Tcl_IncrRefCount(*driveNameRef);
+ }
+ }
Tcl_DStringFree(&ds);
+ break;
}
- break;
+ }
}
return type;
}
/*
+ *---------------------------------------------------------------------------
+ *
+ * TclpNativeSplitPath --
+ *
+ * This function takes the given Tcl_Obj, which should be a valid
+ * path, and returns a Tcl List object containing each segment
+ * of that path as an element.
+ *
+ * Note this function currently calls the older Split(Plat)Path
+ * functions, which require more memory allocation than is
+ * desirable.
+ *
+ * Results:
+ * Returns list object with refCount of zero. If the passed in
+ * lenPtr is non-NULL, we use it to return the number of elements
+ * in the returned list.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+TclpNativeSplitPath(pathPtr, lenPtr)
+ Tcl_Obj *pathPtr; /* Path to split. */
+ int *lenPtr; /* int to store number of path elements. */
+{
+ Tcl_Obj *resultPtr = NULL; /* Needed only to prevent gcc warnings. */
+
+ /*
+ * Perform platform specific splitting.
+ */
+
+ switch (tclPlatform) {
+ case TCL_PLATFORM_UNIX:
+ resultPtr = SplitUnixPath(Tcl_GetString(pathPtr));
+ break;
+
+ case TCL_PLATFORM_WINDOWS:
+ resultPtr = SplitWinPath(Tcl_GetString(pathPtr));
+ break;
+
+ case TCL_PLATFORM_MAC:
+ resultPtr = SplitMacPath(Tcl_GetString(pathPtr));
+ break;
+ }
+
+ /*
+ * Compute the number of elements in the result.
+ */
+
+ if (lenPtr != NULL) {
+ Tcl_ListObjLength(NULL, resultPtr, lenPtr);
+ }
+ return resultPtr;
+}
+
+/*
*----------------------------------------------------------------------
*
* Tcl_SplitPath --
@@ -345,75 +554,70 @@ Tcl_SplitPath(path, argcPtr, argvPtr)
CONST char *path; /* Pointer to string containing a path. */
int *argcPtr; /* Pointer to location to fill in with
* the number of elements in the path. */
- char ***argvPtr; /* Pointer to place to store pointer to array
+ CONST char ***argvPtr; /* Pointer to place to store pointer to array
* of pointers to path elements. */
{
- int i, size;
- char *p;
- Tcl_DString buffer;
-
- Tcl_DStringInit(&buffer);
+ Tcl_Obj *resultPtr = NULL; /* Needed only to prevent gcc warnings. */
+ Tcl_Obj *tmpPtr, *eltPtr;
+ int i, size, len;
+ char *p, *str;
/*
- * Perform platform specific splitting. These routines will leave the
- * result in the specified buffer. Individual elements are terminated
- * with a null character.
+ * Perform the splitting, using objectified, vfs-aware code.
*/
- p = NULL; /* Needed only to prevent gcc warnings. */
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- p = SplitUnixPath(path, &buffer);
- break;
-
- case TCL_PLATFORM_WINDOWS:
- p = SplitWinPath(path, &buffer);
- break;
-
- case TCL_PLATFORM_MAC:
- p = SplitMacPath(path, &buffer);
- break;
- }
-
- /*
- * Compute the number of elements in the result.
- */
+ tmpPtr = Tcl_NewStringObj(path, -1);
+ Tcl_IncrRefCount(tmpPtr);
+ resultPtr = Tcl_FSSplitPath(tmpPtr, argcPtr);
+ Tcl_DecrRefCount(tmpPtr);
- size = Tcl_DStringLength(&buffer);
- *argcPtr = 0;
- for (i = 0; i < size; i++) {
- if (p[i] == '\0') {
- (*argcPtr)++;
- }
+ /* Calculate space required for the result */
+
+ size = 1;
+ for (i = 0; i < *argcPtr; i++) {
+ Tcl_ListObjIndex(NULL, resultPtr, i, &eltPtr);
+ Tcl_GetStringFromObj(eltPtr, &len);
+ size += len + 1;
}
/*
- * Allocate a buffer large enough to hold the contents of the
- * DString plus the argv pointers and the terminating NULL pointer.
+ * Allocate a buffer large enough to hold the contents of all of
+ * the list plus the argv pointers and the terminating NULL pointer.
*/
- *argvPtr = (char **) ckalloc((unsigned)
+ *argvPtr = (CONST char **) ckalloc((unsigned)
((((*argcPtr) + 1) * sizeof(char *)) + size));
/*
* Position p after the last argv pointer and copy the contents of
- * the DString.
+ * the list in, piece by piece.
*/
p = (char *) &(*argvPtr)[(*argcPtr) + 1];
- memcpy((VOID *) p, (VOID *) Tcl_DStringValue(&buffer), (size_t) size);
-
+ for (i = 0; i < *argcPtr; i++) {
+ Tcl_ListObjIndex(NULL, resultPtr, i, &eltPtr);
+ str = Tcl_GetStringFromObj(eltPtr, &len);
+ memcpy((VOID *) p, (VOID *) str, (size_t) len+1);
+ p += len+1;
+ }
+
/*
* Now set up the argv pointers.
*/
+ p = (char *) &(*argvPtr)[(*argcPtr) + 1];
+
for (i = 0; i < *argcPtr; i++) {
(*argvPtr)[i] = p;
while ((*p++) != '\0') {}
}
(*argvPtr)[i] = NULL;
- Tcl_DStringFree(&buffer);
+ /*
+ * Free the result ptr given to us by Tcl_FSSplitPath
+ */
+
+ Tcl_DecrRefCount(resultPtr);
}
/*
@@ -421,12 +625,11 @@ Tcl_SplitPath(path, argcPtr, argvPtr)
*
* SplitUnixPath --
*
- * This routine is used by Tcl_SplitPath to handle splitting
+ * This routine is used by Tcl_(FS)SplitPath to handle splitting
* Unix paths.
*
* Results:
- * Stores a null separated array of strings in the specified
- * Tcl_DString.
+ * Returns a newly allocated Tcl list object.
*
* Side effects:
* None.
@@ -434,13 +637,13 @@ Tcl_SplitPath(path, argcPtr, argvPtr)
*----------------------------------------------------------------------
*/
-static char *
-SplitUnixPath(path, bufPtr)
+static Tcl_Obj*
+SplitUnixPath(path)
CONST char *path; /* Pointer to string containing a path. */
- Tcl_DString *bufPtr; /* Pointer to DString to use for the result. */
{
int length;
CONST char *p, *elementStart;
+ Tcl_Obj *result = Tcl_NewObj();
/*
* Deal with the root directory as a special case.
@@ -460,7 +663,7 @@ SplitUnixPath(path, bufPtr)
#endif
if (path[0] == '/') {
- Tcl_DStringAppend(bufPtr, "/", 2);
+ Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj("/",1));
p = path+1;
} else {
p = path;
@@ -478,30 +681,33 @@ SplitUnixPath(path, bufPtr)
}
length = p - elementStart;
if (length > 0) {
+ Tcl_Obj *nextElt;
if ((elementStart[0] == '~') && (elementStart != path)) {
- Tcl_DStringAppend(bufPtr, "./", 2);
+ nextElt = Tcl_NewStringObj("./",2);
+ Tcl_AppendToObj(nextElt, elementStart, length);
+ } else {
+ nextElt = Tcl_NewStringObj(elementStart, length);
}
- Tcl_DStringAppend(bufPtr, elementStart, length);
- Tcl_DStringAppend(bufPtr, "", 1);
+ Tcl_ListObjAppendElement(NULL, result, nextElt);
}
if (*p++ == '\0') {
break;
}
}
- return Tcl_DStringValue(bufPtr);
+ return result;
}
+
/*
*----------------------------------------------------------------------
*
* SplitWinPath --
*
- * This routine is used by Tcl_SplitPath to handle splitting
+ * This routine is used by Tcl_(FS)SplitPath to handle splitting
* Windows paths.
*
* Results:
- * Stores a null separated array of strings in the specified
- * Tcl_DString.
+ * Returns a newly allocated Tcl list object.
*
* Side effects:
* None.
@@ -509,25 +715,30 @@ SplitUnixPath(path, bufPtr)
*----------------------------------------------------------------------
*/
-static char *
-SplitWinPath(path, bufPtr)
+static Tcl_Obj*
+SplitWinPath(path)
CONST char *path; /* Pointer to string containing a path. */
- Tcl_DString *bufPtr; /* Pointer to DString to use for the result. */
{
int length;
CONST char *p, *elementStart;
Tcl_PathType type = TCL_PATH_ABSOLUTE;
-
- p = ExtractWinRoot(path, bufPtr, 0, &type);
+ Tcl_DString buf;
+ Tcl_Obj *result = Tcl_NewObj();
+ Tcl_DStringInit(&buf);
+
+ p = ExtractWinRoot(path, &buf, 0, &type);
/*
* Terminate the root portion, if we matched something.
*/
if (p != path) {
- Tcl_DStringAppend(bufPtr, "", 1);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(Tcl_DStringValue(&buf),
+ Tcl_DStringLength(&buf)));
}
-
+ Tcl_DStringFree(&buf);
+
/*
* Split on slashes. Embedded elements that start with tilde will be
* prefixed with "./" so they are not affected by tilde substitution.
@@ -540,15 +751,18 @@ SplitWinPath(path, bufPtr)
}
length = p - elementStart;
if (length > 0) {
+ Tcl_Obj *nextElt;
if ((elementStart[0] == '~') && (elementStart != path)) {
- Tcl_DStringAppend(bufPtr, "./", 2);
+ nextElt = Tcl_NewStringObj("./",2);
+ Tcl_AppendToObj(nextElt, elementStart, length);
+ } else {
+ nextElt = Tcl_NewStringObj(elementStart, length);
}
- Tcl_DStringAppend(bufPtr, elementStart, length);
- Tcl_DStringAppend(bufPtr, "", 1);
+ Tcl_ListObjAppendElement(NULL, result, nextElt);
}
} while (*p++ != '\0');
- return Tcl_DStringValue(bufPtr);
+ return result;
}
/*
@@ -556,11 +770,11 @@ SplitWinPath(path, bufPtr)
*
* SplitMacPath --
*
- * This routine is used by Tcl_SplitPath to handle splitting
+ * This routine is used by Tcl_(FS)SplitPath to handle splitting
* Macintosh paths.
*
* Results:
- * Returns a newly allocated argv array.
+ * Returns a newly allocated Tcl list object.
*
* Side effects:
* None.
@@ -568,17 +782,23 @@ SplitWinPath(path, bufPtr)
*----------------------------------------------------------------------
*/
-static char *
-SplitMacPath(path, bufPtr)
+static Tcl_Obj*
+SplitMacPath(path)
CONST char *path; /* Pointer to string containing a path. */
- Tcl_DString *bufPtr; /* Pointer to DString to use for the result. */
{
int isMac = 0; /* 1 if is Mac-style, 0 if Unix-style path. */
- int i, length;
+ int length;
CONST char *p, *elementStart;
+ Tcl_Obj *result;
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
Tcl_RegExp re;
+ int i;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
+#endif
+
+ result = Tcl_NewObj();
+
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
/*
* Initialize the path name parser for Macintosh path names.
*/
@@ -594,7 +814,8 @@ SplitMacPath(path, bufPtr)
re = Tcl_GetRegExpFromObj(NULL, tsdPtr->macRootPatternPtr, REG_ADVANCED);
if (Tcl_RegExpExec(NULL, re, path, path) == 1) {
- char *start, *end;
+ CONST char *start, *end;
+ Tcl_Obj *nextElt;
/*
* Treat degenerate absolute paths like / and /../.. as
@@ -603,10 +824,11 @@ SplitMacPath(path, bufPtr)
Tcl_RegExpRange(re, 2, &start, &end);
if (start) {
- Tcl_DStringAppend(bufPtr, ":", 1);
+ Tcl_Obj *elt = Tcl_NewStringObj(":", 1);
Tcl_RegExpRange(re, 0, &start, &end);
- Tcl_DStringAppend(bufPtr, path, end - start + 1);
- return Tcl_DStringValue(bufPtr);
+ Tcl_AppendToObj(elt, path, end - start);
+ Tcl_ListObjAppendElement(NULL, result, elt);
+ return result;
}
Tcl_RegExpRange(re, 5, &start, &end);
@@ -629,7 +851,6 @@ SplitMacPath(path, bufPtr)
} else {
Tcl_RegExpRange(re, 10, &start, &end);
if (start) {
-
/*
* Normal Unix style paths.
*/
@@ -639,7 +860,6 @@ SplitMacPath(path, bufPtr)
} else {
Tcl_RegExpRange(re, 12, &start, &end);
if (start) {
-
/*
* Normal Mac style paths.
*/
@@ -650,36 +870,70 @@ SplitMacPath(path, bufPtr)
}
}
}
-
Tcl_RegExpRange(re, i, &start, &end);
length = end - start;
/*
- * Append the element and terminate it with a : and a null. Note that
- * we are forcing the DString to contain an extra null at the end.
+ * Append the element and terminate it with a :
*/
- Tcl_DStringAppend(bufPtr, start, length);
- Tcl_DStringAppend(bufPtr, ":", 2);
+ nextElt = Tcl_NewStringObj(start, length);
+ Tcl_AppendToObj(nextElt, ":", 1);
+ Tcl_ListObjAppendElement(NULL, result, nextElt);
p = end;
} else {
isMac = (strchr(path, ':') != NULL);
p = path;
}
+#else
+ if ((path[0] != ':') && (path[0] == '~' || (strchr(path,':') != NULL))) {
+ CONST char *end;
+ Tcl_Obj *nextElt;
+
+ isMac = 1;
+
+ end = strchr(path,':');
+ if (end == NULL) {
+ length = strlen(path);
+ } else {
+ length = end - path;
+ }
+
+ /*
+ * Append the element and terminate it with a :
+ */
+
+ nextElt = Tcl_NewStringObj(path, length);
+ Tcl_AppendToObj(nextElt, ":", 1);
+ Tcl_ListObjAppendElement(NULL, result, nextElt);
+ p = path + length;
+ } else {
+ isMac = (strchr(path, ':') != NULL);
+ isMac = 1;
+ p = path;
+ }
+#endif
if (isMac) {
/*
* p is pointing at the first colon in the path. There
* will always be one, since this is a Mac-style path.
+ * (This is no longer true if MAC_UNDERSTANDS_UNIX_PATHS
+ * is false, so we must check whether 'p' points to the
+ * end of the string.)
*/
-
- elementStart = p++;
+ elementStart = p;
+ if (*p == ':') {
+ p++;
+ }
+
while ((p = strchr(p, ':')) != NULL) {
length = p - elementStart;
if (length == 1) {
while (*p == ':') {
- Tcl_DStringAppend(bufPtr, "::", 3);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj("::", 2));
elementStart = p++;
}
} else {
@@ -692,18 +946,25 @@ SplitMacPath(path, bufPtr)
elementStart++;
length--;
}
- Tcl_DStringAppend(bufPtr, elementStart, length);
- Tcl_DStringAppend(bufPtr, "", 1);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(elementStart, length));
elementStart = p++;
}
}
- if (elementStart[1] != '\0' || elementStart == path) {
- if ((elementStart[1] != '~') && (elementStart[1] != '\0')
+ if (elementStart[0] != ':') {
+ if (elementStart[0] != '\0') {
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(elementStart, -1));
+ }
+ } else {
+ if (elementStart[1] != '\0' || elementStart == path) {
+ if ((elementStart[1] != '~') && (elementStart[1] != '\0')
&& (strchr(elementStart+1, '/') == NULL)) {
elementStart++;
+ }
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(elementStart, -1));
}
- Tcl_DStringAppend(bufPtr, elementStart, -1);
- Tcl_DStringAppend(bufPtr, "", 1);
}
} else {
@@ -719,16 +980,21 @@ SplitMacPath(path, bufPtr)
length = p - elementStart;
if (length > 0) {
if ((length == 1) && (elementStart[0] == '.')) {
- Tcl_DStringAppend(bufPtr, ":", 2);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(":", 1));
} else if ((length == 2) && (elementStart[0] == '.')
&& (elementStart[1] == '.')) {
- Tcl_DStringAppend(bufPtr, "::", 3);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj("::", 2));
} else {
+ Tcl_Obj *nextElt;
if (*elementStart == '~') {
- Tcl_DStringAppend(bufPtr, ":", 1);
+ nextElt = Tcl_NewStringObj(":",1);
+ Tcl_AppendToObj(nextElt, elementStart, length);
+ } else {
+ nextElt = Tcl_NewStringObj(elementStart, length);
}
- Tcl_DStringAppend(bufPtr, elementStart, length);
- Tcl_DStringAppend(bufPtr, "", 1);
+ Tcl_ListObjAppendElement(NULL, result, nextElt);
}
}
if (*p++ == '\0') {
@@ -736,239 +1002,301 @@ SplitMacPath(path, bufPtr)
}
}
}
- return Tcl_DStringValue(bufPtr);
+ return result;
}
/*
- *----------------------------------------------------------------------
+ *---------------------------------------------------------------------------
*
- * Tcl_JoinPath --
+ * Tcl_FSJoinToPath --
*
- * Combine a list of paths in a platform specific manner.
+ * This function takes the given object, which should usually be a
+ * valid path or NULL, and joins onto it the array of paths
+ * segments given.
*
* Results:
- * Appends the joined path to the end of the specified
- * returning a pointer to the resulting string. Note that
- * the Tcl_DString must already be initialized.
+ * Returns object with refCount of zero
*
* Side effects:
- * Modifies the Tcl_DString.
+ * None.
*
- *----------------------------------------------------------------------
+ *---------------------------------------------------------------------------
*/
-char *
-Tcl_JoinPath(argc, argv, resultPtr)
- int argc;
- char **argv;
- Tcl_DString *resultPtr; /* Pointer to previously initialized DString. */
+Tcl_Obj*
+Tcl_FSJoinToPath(basePtr, objc, objv)
+ Tcl_Obj *basePtr;
+ int objc;
+ Tcl_Obj *CONST objv[];
{
- int oldLength, length, i, needsSep;
- Tcl_DString buffer;
- char c, *dest;
- CONST char *p;
- Tcl_PathType type = TCL_PATH_ABSOLUTE;
-
- Tcl_DStringInit(&buffer);
- oldLength = Tcl_DStringLength(resultPtr);
+ int i;
+ Tcl_Obj *lobj, *ret;
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- for (i = 0; i < argc; i++) {
- p = argv[i];
- /*
- * If the path is absolute, reset the result buffer.
- * Consume any duplicate leading slashes or a ./ in
- * front of a tilde prefixed path that isn't at the
- * beginning of the path.
- */
+ if (basePtr == NULL) {
+ lobj = Tcl_NewListObj(0, NULL);
+ } else {
+ lobj = Tcl_NewListObj(1, &basePtr);
+ }
+
+ for (i = 0; i<objc;i++) {
+ Tcl_ListObjAppendElement(NULL, lobj, objv[i]);
+ }
+ ret = Tcl_FSJoinPath(lobj, -1);
+ Tcl_DecrRefCount(lobj);
+ return ret;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclpNativeJoinPath --
+ *
+ * 'prefix' is absolute, 'joining' is relative to prefix.
+ *
+ * Results:
+ * modifies prefix
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
-#ifdef __QNX__
- /*
- * Check for QNX //<node id> prefix
- */
- if (*p && (strlen(p) > 3) && (p[0] == '/') && (p[1] == '/')
- && isdigit(UCHAR(p[2]))) { /* INTL: digit */
- p += 3;
- while (isdigit(UCHAR(*p))) { /* INTL: digit */
- ++p;
- }
- }
-#endif
- if (*p == '/') {
- Tcl_DStringSetLength(resultPtr, oldLength);
- Tcl_DStringAppend(resultPtr, "/", 1);
- while (*p == '/') {
- p++;
- }
- } else if (*p == '~') {
- Tcl_DStringSetLength(resultPtr, oldLength);
- } else if ((Tcl_DStringLength(resultPtr) != oldLength)
- && (p[0] == '.') && (p[1] == '/')
- && (p[2] == '~')) {
- p += 2;
- }
+void
+TclpNativeJoinPath(prefix, joining)
+ Tcl_Obj *prefix;
+ char* joining;
+{
+ int length, needsSep;
+ char *dest, *p, *start;
+
+ start = Tcl_GetStringFromObj(prefix, &length);
- if (*p == '\0') {
- continue;
- }
+ /*
+ * Remove the ./ from tilde prefixed elements unless
+ * it is the first component.
+ */
+
+ p = joining;
+
+ if (length != 0) {
+ if ((p[0] == '.') && (p[1] == '/') && (p[2] == '~')) {
+ p += 2;
+ }
+ }
+
+ if (*p == '\0') {
+ return;
+ }
- /*
- * Append a separator if needed.
- */
- length = Tcl_DStringLength(resultPtr);
- if ((length != oldLength)
- && (Tcl_DStringValue(resultPtr)[length-1] != '/')) {
- Tcl_DStringAppend(resultPtr, "/", 1);
- length++;
- }
+ switch (tclPlatform) {
+ case TCL_PLATFORM_UNIX:
+ /*
+ * Append a separator if needed.
+ */
- /*
- * Append the element, eliminating duplicate and trailing
- * slashes.
- */
+ if (length > 0 && (start[length-1] != '/')) {
+ Tcl_AppendToObj(prefix, "/", 1);
+ length++;
+ }
+ needsSep = 0;
+
+ /*
+ * Append the element, eliminating duplicate and trailing
+ * slashes.
+ */
- Tcl_DStringSetLength(resultPtr, (int) (length + strlen(p)));
- dest = Tcl_DStringValue(resultPtr) + length;
- for (; *p != '\0'; p++) {
- if (*p == '/') {
- while (p[1] == '/') {
- p++;
- }
- if (p[1] != '\0') {
+ Tcl_SetObjLength(prefix, length + (int) strlen(p));
+
+ dest = Tcl_GetString(prefix) + length;
+ for (; *p != '\0'; p++) {
+ if (*p == '/') {
+ while (p[1] == '/') {
+ p++;
+ }
+ if (p[1] != '\0') {
+ if (needsSep) {
*dest++ = '/';
}
- } else {
- *dest++ = *p;
}
+ } else {
+ *dest++ = *p;
+ needsSep = 1;
}
- length = dest - Tcl_DStringValue(resultPtr);
- Tcl_DStringSetLength(resultPtr, length);
}
+ length = dest - Tcl_GetString(prefix);
+ Tcl_SetObjLength(prefix, length);
break;
case TCL_PLATFORM_WINDOWS:
/*
- * Iterate over all of the components. If a component is
- * absolute, then reset the result and start building the
- * path from the current component on.
+ * Check to see if we need to append a separator.
*/
- for (i = 0; i < argc; i++) {
- p = ExtractWinRoot(argv[i], resultPtr, oldLength, &type);
- length = Tcl_DStringLength(resultPtr);
-
- /*
- * If the pointer didn't move, then this is a relative path
- * or a tilde prefixed path.
- */
-
- if (p == argv[i]) {
- /*
- * Remove the ./ from tilde prefixed elements unless
- * it is the first component.
- */
+ if ((length > 0) &&
+ (start[length-1] != '/') && (start[length-1] != ':')) {
+ Tcl_AppendToObj(prefix, "/", 1);
+ length++;
+ }
+ needsSep = 0;
+
+ /*
+ * Append the element, eliminating duplicate and
+ * trailing slashes.
+ */
- if ((length != oldLength)
- && (p[0] == '.')
- && ((p[1] == '/') || (p[1] == '\\'))
- && (p[2] == '~')) {
- p += 2;
- } else if (*p == '~') {
- Tcl_DStringSetLength(resultPtr, oldLength);
- length = oldLength;
+ Tcl_SetObjLength(prefix, length + (int) strlen(p));
+ dest = Tcl_GetString(prefix) + length;
+ for (; *p != '\0'; p++) {
+ if ((*p == '/') || (*p == '\\')) {
+ while ((p[1] == '/') || (p[1] == '\\')) {
+ p++;
}
+ if ((p[1] != '\0') && needsSep) {
+ *dest++ = '/';
+ }
+ } else {
+ *dest++ = *p;
+ needsSep = 1;
}
+ }
+ length = dest - Tcl_GetString(prefix);
+ Tcl_SetObjLength(prefix, length);
+ break;
- if (*p != '\0') {
- /*
- * Check to see if we need to append a separator.
- */
+ case TCL_PLATFORM_MAC: {
+ int newLength;
+
+ /*
+ * Sort out separators. We basically add the object we've
+ * been given, but we have to make sure that there is
+ * exactly one separator inbetween (unless the object we're
+ * adding contains multiple contiguous colons, all of which
+ * we must add). Also if an object is just ':' we don't
+ * bother to add it unless it's the very first element.
+ */
-
- if (length != oldLength) {
- c = Tcl_DStringValue(resultPtr)[length-1];
- if ((c != '/') && (c != ':')) {
- Tcl_DStringAppend(resultPtr, "/", 1);
- }
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
+ int adjustedPath = 0;
+ if ((strchr(p, ':') == NULL) && (strchr(p, '/') != NULL)) {
+ char *start = p;
+ adjustedPath = 1;
+ while (*start != '\0') {
+ if (*start == '/') {
+ *start = ':';
}
-
- /*
- * Append the element, eliminating duplicate and
- * trailing slashes.
- */
-
- length = Tcl_DStringLength(resultPtr);
- Tcl_DStringSetLength(resultPtr, (int) (length + strlen(p)));
- dest = Tcl_DStringValue(resultPtr) + length;
- for (; *p != '\0'; p++) {
- if ((*p == '/') || (*p == '\\')) {
- while ((p[1] == '/') || (p[1] == '\\')) {
- p++;
- }
- if (p[1] != '\0') {
- *dest++ = '/';
- }
- } else {
- *dest++ = *p;
- }
+ start++;
+ }
+ }
+#endif
+ if (length > 0) {
+ if ((p[0] == ':') && (p[1] == '\0')) {
+ return;
+ }
+ if (start[length-1] != ':') {
+ if (*p != '\0' && *p != ':') {
+ Tcl_AppendToObj(prefix, ":", 1);
+ length++;
}
- length = dest - Tcl_DStringValue(resultPtr);
- Tcl_DStringSetLength(resultPtr, length);
+ } else if (*p == ':') {
+ p++;
+ }
+ } else {
+ if (*p != '\0' && *p != ':') {
+ Tcl_AppendToObj(prefix, ":", 1);
+ length++;
}
}
- break;
+
+ /*
+ * Append the element
+ */
- case TCL_PLATFORM_MAC:
- needsSep = 1;
- for (i = 0; i < argc; i++) {
- Tcl_DStringSetLength(&buffer, 0);
- p = SplitMacPath(argv[i], &buffer);
- if ((*p != ':') && (*p != '\0')
- && (strchr(p, ':') != NULL)) {
- Tcl_DStringSetLength(resultPtr, oldLength);
- length = strlen(p);
- Tcl_DStringAppend(resultPtr, p, length);
- needsSep = 0;
- p += length+1;
+ newLength = strlen(p);
+ /*
+ * It may not be good to just do 'Tcl_AppendToObj(prefix,
+ * p, newLength)' because the object may contain duplicate
+ * colons which we want to get rid of.
+ */
+ Tcl_AppendToObj(prefix, p, newLength);
+
+ /* Remove spurious trailing single ':' */
+ dest = Tcl_GetString(prefix) + length + newLength;
+ if (*(dest-1) == ':') {
+ if (dest-1 > Tcl_GetString(prefix)) {
+ if (*(dest-2) != ':') {
+ Tcl_SetObjLength(prefix, length + newLength -1);
+ }
}
-
- /*
- * Now append the rest of the path elements, skipping
- * : unless it is the first element of the path, and
- * watching out for :: et al. so we don't end up with
- * too many colons in the result.
- */
-
- for (; *p != '\0'; p += length+1) {
- if (p[0] == ':' && p[1] == '\0') {
- if (Tcl_DStringLength(resultPtr) != oldLength) {
- p++;
- } else {
- needsSep = 0;
- }
- } else {
- c = p[1];
- if (*p == ':') {
- if (!needsSep) {
- p++;
- }
- } else {
- if (needsSep) {
- Tcl_DStringAppend(resultPtr, ":", 1);
- }
- }
- needsSep = (c == ':') ? 0 : 1;
+ }
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
+ /* Revert the path to what it was */
+ if (adjustedPath) {
+ char *start = joining;
+ while (*start != '\0') {
+ if (*start == ':') {
+ *start = '/';
}
- length = strlen(p);
- Tcl_DStringAppend(resultPtr, p, length);
+ start++;
}
}
+#endif
break;
-
+ }
}
- Tcl_DStringFree(&buffer);
+ return;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_JoinPath --
+ *
+ * Combine a list of paths in a platform specific manner. The
+ * function 'Tcl_FSJoinPath' should be used in preference where
+ * possible.
+ *
+ * Results:
+ * Appends the joined path to the end of the specified
+ * Tcl_DString returning a pointer to the resulting string. Note
+ * that the Tcl_DString must already be initialized.
+ *
+ * Side effects:
+ * Modifies the Tcl_DString.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+Tcl_JoinPath(argc, argv, resultPtr)
+ int argc;
+ CONST char * CONST *argv;
+ Tcl_DString *resultPtr; /* Pointer to previously initialized DString */
+{
+ int i, len;
+ Tcl_Obj *listObj = Tcl_NewObj();
+ Tcl_Obj *resultObj;
+ char *resultStr;
+
+ /* Build the list of paths */
+ for (i = 0; i < argc; i++) {
+ Tcl_ListObjAppendElement(NULL, listObj,
+ Tcl_NewStringObj(argv[i], -1));
+ }
+
+ /* Ask the objectified code to join the paths */
+ Tcl_IncrRefCount(listObj);
+ resultObj = Tcl_FSJoinPath(listObj, argc);
+ Tcl_IncrRefCount(resultObj);
+ Tcl_DecrRefCount(listObj);
+
+ /* Store the result */
+ resultStr = Tcl_GetStringFromObj(resultObj, &len);
+ Tcl_DStringAppend(resultPtr, resultStr, len);
+ Tcl_DecrRefCount(resultObj);
+
+ /* Return a pointer to the result */
return Tcl_DStringValue(resultPtr);
}
@@ -1002,51 +1330,25 @@ char *
Tcl_TranslateFileName(interp, name, bufferPtr)
Tcl_Interp *interp; /* Interpreter in which to store error
* message (if necessary). */
- char *name; /* File name, which may begin with "~" (to
+ CONST char *name; /* File name, which may begin with "~" (to
* indicate current user's home directory) or
* "~<user>" (to indicate any user's home
* directory). */
Tcl_DString *bufferPtr; /* Uninitialized or free DString filled
* with name after tilde substitution. */
{
- register char *p;
-
- /*
- * Handle tilde substitutions, if needed.
- */
-
- if (name[0] == '~') {
- int argc, length;
- char **argv;
- Tcl_DString temp;
-
- Tcl_SplitPath(name, &argc, (char ***) &argv);
-
- /*
- * Strip the trailing ':' off of a Mac path before passing the user
- * name to DoTildeSubst.
- */
-
- if (tclPlatform == TCL_PLATFORM_MAC) {
- length = strlen(argv[0]);
- argv[0][length-1] = '\0';
- }
-
- Tcl_DStringInit(&temp);
- argv[0] = DoTildeSubst(interp, argv[0]+1, &temp);
- if (argv[0] == NULL) {
- Tcl_DStringFree(&temp);
- ckfree((char *)argv);
- return NULL;
- }
- Tcl_DStringInit(bufferPtr);
- Tcl_JoinPath(argc, (char **) argv, bufferPtr);
- Tcl_DStringFree(&temp);
- ckfree((char*)argv);
- } else {
- Tcl_DStringInit(bufferPtr);
- Tcl_JoinPath(1, (char **) &name, bufferPtr);
+ Tcl_Obj *path = Tcl_NewStringObj(name, -1);
+ CONST char *result;
+
+ Tcl_IncrRefCount(path);
+ result = Tcl_FSGetTranslatedStringPath(interp, path);
+ if (result == NULL) {
+ Tcl_DecrRefCount(path);
+ return NULL;
}
+ Tcl_DStringInit(bufferPtr);
+ Tcl_DStringAppend(bufferPtr, result, -1);
+ Tcl_DecrRefCount(path);
/*
* Convert forward slashes to backslashes in Windows paths because
@@ -1054,6 +1356,7 @@ Tcl_TranslateFileName(interp, name, bufferPtr)
*/
if (tclPlatform == TCL_PLATFORM_WINDOWS) {
+ register char *p;
for (p = Tcl_DStringValue(bufferPtr); *p != '\0'; p++) {
if (*p == '/') {
*p = '\\';
@@ -1098,11 +1401,15 @@ TclGetExtension(name)
break;
case TCL_PLATFORM_MAC:
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
if (strchr(name, ':') == NULL) {
lastSep = strrchr(name, '/');
} else {
lastSep = strrchr(name, ':');
}
+#else
+ lastSep = strrchr(name, ':');
+#endif
break;
case TCL_PLATFORM_WINDOWS:
@@ -1115,8 +1422,7 @@ TclGetExtension(name)
break;
}
p = strrchr(name, '.');
- if ((p != NULL) && (lastSep != NULL)
- && (lastSep > p)) {
+ if ((p != NULL) && (lastSep != NULL) && (lastSep > p)) {
p = NULL;
}
@@ -1152,7 +1458,7 @@ TclGetExtension(name)
*----------------------------------------------------------------------
*/
-static char *
+static CONST char *
DoTildeSubst(interp, user, resultPtr)
Tcl_Interp *interp; /* Interpreter in which to store error
* message (if necessary). */
@@ -1161,7 +1467,7 @@ DoTildeSubst(interp, user, resultPtr)
Tcl_DString *resultPtr; /* Initialized DString filled with name
* after tilde substitution. */
{
- char *dir;
+ CONST char *dir;
if (*user == '\0') {
Tcl_DString dirString;
@@ -1187,7 +1493,7 @@ DoTildeSubst(interp, user, resultPtr)
return NULL;
}
}
- return resultPtr->string;
+ return Tcl_DStringValue(resultPtr);
}
/*
@@ -1215,23 +1521,25 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- int index, i, globFlags, pathlength, length, join, dir, result;
- char *string, *pathOrDir, *separators;
+ int index, i, globFlags, length, join, dir, result;
+ char *string, *separators;
Tcl_Obj *typePtr, *resultPtr, *look;
- Tcl_DString prefix, directory;
- static char *options[] = {
- "-directory", "-join", "-nocomplain", "-path", "-types", "--", NULL
+ Tcl_Obj *pathOrDir = NULL;
+ Tcl_DString prefix;
+ static CONST char *options[] = {
+ "-directory", "-join", "-nocomplain", "-path", "-tails",
+ "-types", "--", NULL
};
enum options {
- GLOB_DIR, GLOB_JOIN, GLOB_NOCOMPLAIN, GLOB_PATH, GLOB_TYPE, GLOB_LAST
+ GLOB_DIR, GLOB_JOIN, GLOB_NOCOMPLAIN, GLOB_PATH, GLOB_TAILS,
+ GLOB_TYPE, GLOB_LAST
};
enum pathDirOptions {PATH_NONE = -1 , PATH_GENERAL = 0, PATH_DIR = 1};
- GlobTypeData *globTypes = NULL;
+ Tcl_GlobTypeData *globTypes = NULL;
globFlags = 0;
join = 0;
dir = PATH_NONE;
- pathOrDir = NULL;
typePtr = NULL;
resultPtr = Tcl_GetObjResult(interp);
for (i = 1; i < objc; i++) {
@@ -1255,7 +1563,7 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
}
switch (index) {
case GLOB_NOCOMPLAIN: /* -nocomplain */
- globFlags |= GLOBMODE_NO_COMPLAIN;
+ globFlags |= TCL_GLOBMODE_NO_COMPLAIN;
break;
case GLOB_DIR: /* -dir */
if (i == (objc-1)) {
@@ -1263,34 +1571,37 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
"missing argument to \"-directory\"", -1);
return TCL_ERROR;
}
- if (dir != -1) {
+ if (dir != PATH_NONE) {
Tcl_AppendToObj(resultPtr,
"\"-directory\" cannot be used with \"-path\"",
-1);
return TCL_ERROR;
}
dir = PATH_DIR;
- globFlags |= GLOBMODE_DIR;
- pathOrDir = Tcl_GetStringFromObj(objv[i+1], &pathlength);
+ globFlags |= TCL_GLOBMODE_DIR;
+ pathOrDir = objv[i+1];
i++;
break;
case GLOB_JOIN: /* -join */
join = 1;
break;
+ case GLOB_TAILS: /* -tails */
+ globFlags |= TCL_GLOBMODE_TAILS;
+ break;
case GLOB_PATH: /* -path */
if (i == (objc-1)) {
Tcl_AppendToObj(resultPtr,
"missing argument to \"-path\"", -1);
return TCL_ERROR;
}
- if (dir != -1) {
+ if (dir != PATH_NONE) {
Tcl_AppendToObj(resultPtr,
"\"-path\" cannot be used with \"-directory\"",
-1);
return TCL_ERROR;
}
dir = PATH_GENERAL;
- pathOrDir = Tcl_GetStringFromObj(objv[i+1], &pathlength);
+ pathOrDir = objv[i+1];
i++;
break;
case GLOB_TYPE: /* -types */
@@ -1316,7 +1627,13 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
Tcl_WrongNumArgs(interp, 1, objv, "?switches? name ?name ...?");
return TCL_ERROR;
}
-
+ if ((globFlags & TCL_GLOBMODE_TAILS) && (pathOrDir == NULL)) {
+ Tcl_AppendToObj(resultPtr,
+ "\"-tails\" must be used with either \"-directory\" or \"-path\"",
+ -1);
+ return TCL_ERROR;
+ }
+
separators = NULL; /* lint. */
switch (tclPlatform) {
case TCL_PLATFORM_UNIX:
@@ -1330,34 +1647,34 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
break;
}
if (dir == PATH_GENERAL) {
+ int pathlength;
char *last;
+ char *first = Tcl_GetStringFromObj(pathOrDir,&pathlength);
/*
* Find the last path separator in the path
*/
- last = pathOrDir + pathlength;
- for (; last != pathOrDir; last--) {
+ last = first + pathlength;
+ for (; last != first; last--) {
if (strchr(separators, *(last-1)) != NULL) {
break;
}
}
- if (last == pathOrDir + pathlength) {
+ if (last == first + pathlength) {
/* It's really a directory */
- dir = 1;
+ dir = PATH_DIR;
} else {
Tcl_DString pref;
char *search, *find;
Tcl_DStringInit(&pref);
- Tcl_DStringInit(&directory);
- if (last == pathOrDir) {
+ if (last == first) {
/* The whole thing is a prefix */
- Tcl_DStringAppend(&pref, pathOrDir, -1);
+ Tcl_DStringAppend(&pref, first, -1);
pathOrDir = NULL;
} else {
/* Have to split off the end */
- Tcl_DStringAppend(&pref, last, pathOrDir+pathlength-last);
- Tcl_DStringAppend(&directory, pathOrDir, last-pathOrDir-1);
- pathOrDir = Tcl_DStringValue(&directory);
+ Tcl_DStringAppend(&pref, last, first+pathlength-last);
+ pathOrDir = Tcl_NewStringObj(first, last-first-1);
}
/* Need to quote 'prefix' */
Tcl_DStringInit(&prefix);
@@ -1377,7 +1694,11 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
Tcl_DStringFree(&pref);
}
}
-
+
+ if (pathOrDir != NULL) {
+ Tcl_IncrRefCount(pathOrDir);
+ }
+
if (typePtr != NULL) {
/*
* The rest of the possible type arguments (except 'd') are
@@ -1385,7 +1706,7 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
* on an incompatible platform.
*/
Tcl_ListObjLength(interp, typePtr, &length);
- globTypes = (GlobTypeData*) ckalloc(sizeof(GlobTypeData));
+ globTypes = (Tcl_GlobTypeData*) ckalloc(sizeof(Tcl_GlobTypeData));
globTypes->type = 0;
globTypes->perm = 0;
globTypes->macType = NULL;
@@ -1468,17 +1789,25 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
}
}
/*
- * Error cases
+ * Error cases. We re-get the interpreter's result,
+ * just to be sure it hasn't changed, and we reset
+ * the 'join' flag to zero, since we haven't yet
+ * made use of it.
*/
badTypesArg:
+ resultPtr = Tcl_GetObjResult(interp);
Tcl_AppendToObj(resultPtr, "bad argument to \"-types\": ", -1);
Tcl_AppendObjToObj(resultPtr, look);
result = TCL_ERROR;
+ join = 0;
goto endOfGlob;
badMacTypesArg:
+ resultPtr = Tcl_GetObjResult(interp);
Tcl_AppendToObj(resultPtr,
- "only one MacOS type or creator argument to \"-types\" allowed", -1);
+ "only one MacOS type or creator argument"
+ " to \"-types\" allowed", -1);
result = TCL_ERROR;
+ join = 0;
goto endOfGlob;
}
}
@@ -1544,7 +1873,7 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
}
}
}
- if ((globFlags & GLOBMODE_NO_COMPLAIN) == 0) {
+ if ((globFlags & TCL_GLOBMODE_NO_COMPLAIN) == 0) {
if (Tcl_ListObjLength(interp, Tcl_GetObjResult(interp),
&length) != TCL_OK) {
/* This should never happen. Maybe we should be more dramatic */
@@ -1572,9 +1901,9 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
endOfGlob:
if (join || (dir == PATH_GENERAL)) {
Tcl_DStringFree(&prefix);
- if (dir == PATH_GENERAL) {
- Tcl_DStringFree(&directory);
- }
+ }
+ if (pathOrDir != NULL) {
+ Tcl_DecrRefCount(pathOrDir);
}
if (globTypes != NULL) {
if (globTypes->macType != NULL) {
@@ -1596,16 +1925,24 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv)
* This procedure prepares arguments for the TclDoGlob call.
* It sets the separator string based on the platform, performs
* tilde substitution, and calls TclDoGlob.
+ *
+ * The interpreter's result, on entry to this function, must
+ * be a valid Tcl list (e.g. it could be empty), since we will
+ * lappend any new results to that list. If it is not a valid
+ * list, this function will fail to do anything very meaningful.
*
* Results:
* The return value is a standard Tcl result indicating whether
* an error occurred in globbing. After a normal return the
* result in interp (set by TclDoGlob) holds all of the file names
- * given by the dir and rem arguments. After an error the
- * result in interp will hold an error message.
+ * given by the pattern and unquotedPrefix arguments. After an
+ * error the result in interp will hold an error message, unless
+ * the 'TCL_GLOBMODE_NO_COMPLAIN' flag was given, in which case
+ * an error results in a TCL_OK return leaving the interpreter's
+ * result unmodified.
*
* Side effects:
- * The currentArgString is written to.
+ * The 'pattern' is written to.
*
*----------------------------------------------------------------------
*/
@@ -1617,17 +1954,19 @@ TclGlob(interp, pattern, unquotedPrefix, globFlags, types)
* or appending list of matching file names. */
char *pattern; /* Glob pattern to match. Must not refer
* to a static string. */
- char *unquotedPrefix; /* Prefix to glob pattern, if non-null, which
- * is considered literally. May be static. */
+ Tcl_Obj *unquotedPrefix; /* Prefix to glob pattern, if non-null, which
+ * is considered literally. */
int globFlags; /* Stores or'ed combination of flags */
- GlobTypeData *types; /* Struct containing acceptable types.
+ Tcl_GlobTypeData *types; /* Struct containing acceptable types.
* May be NULL. */
{
char *separators;
- char *head, *tail, *start;
+ CONST char *head;
+ char *tail, *start;
char c;
- int result;
+ int result, prefixLen;
Tcl_DString buffer;
+ Tcl_Obj *oldResult;
separators = NULL; /* lint. */
switch (tclPlatform) {
@@ -1638,17 +1977,21 @@ TclGlob(interp, pattern, unquotedPrefix, globFlags, types)
separators = "/\\:";
break;
case TCL_PLATFORM_MAC:
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
if (unquotedPrefix == NULL) {
separators = (strchr(pattern, ':') == NULL) ? "/" : ":";
} else {
separators = ":";
}
+#else
+ separators = ":";
+#endif
break;
}
Tcl_DStringInit(&buffer);
if (unquotedPrefix != NULL) {
- start = unquotedPrefix;
+ start = Tcl_GetString(unquotedPrefix);
} else {
start = pattern;
}
@@ -1673,44 +2016,23 @@ TclGlob(interp, pattern, unquotedPrefix, globFlags, types)
}
/*
- * Determine the home directory for the specified user. Note that
- * we don't allow special characters in the user name.
+ * Determine the home directory for the specified user.
*/
c = *tail;
*tail = '\0';
- /*
- * I don't think we need to worry about special characters in
- * the user name anymore (Vince Darley, June 1999), since the
- * new code is designed to handle special chars.
- */
-#ifndef NOT_NEEDED_ANYMORE
- head = DoTildeSubst(interp, start+1, &buffer);
-#else
-
- if (strpbrk(start+1, "\\[]*?{}") == NULL) {
- head = DoTildeSubst(interp, start+1, &buffer);
+ if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) {
+ /*
+ * We will ignore any error message here, and we
+ * don't want to mess up the interpreter's result.
+ */
+ head = DoTildeSubst(NULL, start+1, &buffer);
} else {
- if (!(globFlags & GLOBMODE_NO_COMPLAIN)) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "globbing characters not ",
- "supported in user names", (char *) NULL);
- }
- head = NULL;
+ head = DoTildeSubst(interp, start+1, &buffer);
}
-#endif
*tail = c;
if (head == NULL) {
- if (globFlags & GLOBMODE_NO_COMPLAIN) {
- /*
- * We should in fact pass down the nocomplain flag
- * or save the interp result or use another mechanism
- * so the interp result is not mangled on errors in that case.
- * but that would a bigger change than reasonable for a patch
- * release.
- * (see fileName.test 15.2-15.4 for expected behaviour)
- */
- Tcl_ResetResult(interp);
+ if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) {
return TCL_OK;
} else {
return TCL_ERROR;
@@ -1726,30 +2048,113 @@ TclGlob(interp, pattern, unquotedPrefix, globFlags, types)
} else {
tail = pattern;
if (unquotedPrefix != NULL) {
- Tcl_DStringAppend(&buffer,unquotedPrefix,-1);
+ Tcl_DStringAppend(&buffer,Tcl_GetString(unquotedPrefix),-1);
}
}
+
/*
- * If the prefix is a directory, make sure it ends in a directory
- * separator.
+ * We want to remember the length of the current prefix,
+ * in case we are using TCL_GLOBMODE_TAILS. Also if we
+ * are using TCL_GLOBMODE_DIR, we must make sure the
+ * prefix ends in a directory separator.
*/
- if (unquotedPrefix != NULL) {
- if (globFlags & GLOBMODE_DIR) {
- c = Tcl_DStringValue(&buffer)[Tcl_DStringLength(&buffer)-1];
- if (strchr(separators, c) == NULL) {
+ prefixLen = Tcl_DStringLength(&buffer);
+
+ if (prefixLen > 0) {
+ c = Tcl_DStringValue(&buffer)[prefixLen-1];
+ if (strchr(separators, c) == NULL) {
+ /*
+ * If the prefix is a directory, make sure it ends in a
+ * directory separator.
+ */
+ if (globFlags & TCL_GLOBMODE_DIR) {
Tcl_DStringAppend(&buffer,separators,1);
}
+ prefixLen++;
}
}
+ /*
+ * We need to get the old result, in case it is over-written
+ * below when we still need it.
+ */
+ oldResult = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(oldResult);
+ Tcl_ResetResult(interp);
+
result = TclDoGlob(interp, separators, &buffer, tail, types);
- Tcl_DStringFree(&buffer);
+
if (result != TCL_OK) {
- if (globFlags & GLOBMODE_NO_COMPLAIN) {
- Tcl_ResetResult(interp);
- return TCL_OK;
+ if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) {
+ /* Put back the old result and reset the return code */
+ Tcl_SetObjResult(interp, oldResult);
+ result = TCL_OK;
+ }
+ } else {
+ /*
+ * Now we must concatenate the 'oldResult' and the current
+ * result, and then place that into the interpreter.
+ *
+ * If we only want the tails, we must strip off the prefix now.
+ * It may seem more efficient to pass the tails flag down into
+ * TclDoGlob, Tcl_FSMatchInDirectory, but those functions are
+ * continually adjusting the prefix as the various pieces of
+ * the pattern are assimilated, so that would add a lot of
+ * complexity to the code. This way is a little slower (when
+ * the -tails flag is given), but much simpler to code.
+ */
+ int objc, i;
+ Tcl_Obj **objv;
+
+ /* Ensure sole ownership */
+ if (Tcl_IsShared(oldResult)) {
+ Tcl_DecrRefCount(oldResult);
+ oldResult = Tcl_DuplicateObj(oldResult);
+ Tcl_IncrRefCount(oldResult);
+ }
+
+ Tcl_ListObjGetElements(NULL, Tcl_GetObjResult(interp),
+ &objc, &objv);
+#ifdef MAC_TCL
+ /* adjust prefixLen if TclDoGlob prepended a ':' */
+ if ((prefixLen > 0) && (objc > 0)
+ && (Tcl_DStringValue(&buffer)[0] != ':')) {
+ char *str = Tcl_GetStringFromObj(objv[0],NULL);
+ if (str[0] == ':') {
+ prefixLen++;
+ }
+ }
+#endif
+ for (i = 0; i< objc; i++) {
+ Tcl_Obj* elt;
+ if (globFlags & TCL_GLOBMODE_TAILS) {
+ int len;
+ char *oldStr = Tcl_GetStringFromObj(objv[i],&len);
+ if (len == prefixLen) {
+ if ((pattern[0] == '\0')
+ || (strchr(separators, pattern[0]) == NULL)) {
+ elt = Tcl_NewStringObj(".",1);
+ } else {
+ elt = Tcl_NewStringObj("/",1);
+ }
+ } else {
+ elt = Tcl_NewStringObj(oldStr + prefixLen,
+ len - prefixLen);
+ }
+ } else {
+ elt = objv[i];
+ }
+ /* Assumption that 'oldResult' is a valid list */
+ Tcl_ListObjAppendElement(interp, oldResult, elt);
}
+ Tcl_SetObjResult(interp, oldResult);
}
+ /*
+ * Release our temporary copy. All code paths above must
+ * end here so we free our reference.
+ */
+ Tcl_DecrRefCount(oldResult);
+ Tcl_DStringFree(&buffer);
return result;
}
@@ -1842,8 +2247,8 @@ TclDoGlob(interp, separators, headPtr, tail, types)
Tcl_DString *headPtr; /* Completely expanded prefix. */
char *tail; /* The unexpanded remainder of the path.
* Must not be a pointer to a static string. */
- GlobTypeData *types; /* List object containing list of acceptable types.
- * May be NULL. */
+ Tcl_GlobTypeData *types; /* List object containing list of acceptable
+ * types. May be NULL. */
{
int baseLength, quoted, count;
int result = TCL_OK;
@@ -1880,12 +2285,14 @@ TclDoGlob(interp, separators, headPtr, tail, types)
switch (tclPlatform) {
case TCL_PLATFORM_MAC:
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
if (*separators == '/') {
if (((length == 0) && (count == 0))
|| ((length > 0) && (lastChar != ':'))) {
Tcl_DStringAppend(headPtr, ":", 1);
}
} else {
+#endif
if (count == 0) {
if ((length > 0) && (lastChar != ':')) {
Tcl_DStringAppend(headPtr, ":", 1);
@@ -1898,7 +2305,9 @@ TclDoGlob(interp, separators, headPtr, tail, types)
Tcl_DStringAppend(headPtr, ":", 1);
}
}
+#ifdef MAC_UNDERSTANDS_UNIX_PATHS
}
+#endif
break;
case TCL_PLATFORM_WINDOWS:
/*
@@ -2000,8 +2409,8 @@ TclDoGlob(interp, separators, headPtr, tail, types)
Tcl_DStringSetLength(&newName, baseLength);
Tcl_DStringAppend(&newName, element, p-element);
Tcl_DStringAppend(&newName, closeBrace+1, -1);
- result = TclDoGlob(interp, separators,
- headPtr, Tcl_DStringValue(&newName), types);
+ result = TclDoGlob(interp, separators, headPtr,
+ Tcl_DStringValue(&newName), types);
if (result != TCL_OK) {
break;
}
@@ -2026,107 +2435,230 @@ TclDoGlob(interp, separators, headPtr, tail, types)
* if the string is a static.
*/
- savedChar = *p;
- *p = '\0';
- firstSpecialChar = strpbrk(tail, "*[]?\\");
- *p = savedChar;
+ savedChar = *p;
+ *p = '\0';
+ firstSpecialChar = strpbrk(tail, "*[]?\\");
+ *p = savedChar;
} else {
firstSpecialChar = strpbrk(tail, "*[]?\\");
}
if (firstSpecialChar != NULL) {
+ int ret;
+ Tcl_Obj *head = Tcl_NewStringObj(Tcl_DStringValue(headPtr),-1);
+ Tcl_IncrRefCount(head);
/*
- * Look for matching files in the current directory. The
- * implementation of this function is platform specific, but may
- * recursively call TclDoGlob. For each file that matches, it will
- * add the match onto the interp's result, or call TclDoGlob if there
- * are more characters to be processed.
+ * Look for matching files in the given directory. The
+ * implementation of this function is platform specific. For
+ * each file that matches, it will add the match onto the
+ * resultPtr given.
*/
+ if (*p == '\0') {
+ ret = Tcl_FSMatchInDirectory(interp, Tcl_GetObjResult(interp),
+ head, tail, types);
+ } else {
+ Tcl_Obj* resultPtr;
- return TclpMatchFilesTypes(interp, separators, headPtr, tail, p, types);
+ /*
+ * We do the recursion ourselves. This makes implementing
+ * Tcl_FSMatchInDirectory for each filesystem much easier.
+ */
+ Tcl_GlobTypeData dirOnly = { TCL_GLOB_TYPE_DIR, 0, NULL, NULL };
+ char save = *p;
+
+ *p = '\0';
+ resultPtr = Tcl_NewListObj(0, NULL);
+ ret = Tcl_FSMatchInDirectory(interp, resultPtr,
+ head, tail, &dirOnly);
+ *p = save;
+ if (ret == TCL_OK) {
+ int resLength;
+ ret = Tcl_ListObjLength(interp, resultPtr, &resLength);
+ if (ret == TCL_OK) {
+ int i;
+ for (i =0; i< resLength; i++) {
+ Tcl_Obj *elt;
+ Tcl_DString ds;
+ Tcl_ListObjIndex(interp, resultPtr, i, &elt);
+ Tcl_DStringInit(&ds);
+ Tcl_DStringAppend(&ds, Tcl_GetString(elt), -1);
+ if(tclPlatform == TCL_PLATFORM_MAC) {
+ Tcl_DStringAppend(&ds, ":",1);
+ } else {
+ Tcl_DStringAppend(&ds, "/",1);
+ }
+ ret = TclDoGlob(interp, separators, &ds, p+1, types);
+ Tcl_DStringFree(&ds);
+ if (ret != TCL_OK) {
+ break;
+ }
+ }
+ }
+ }
+ Tcl_DecrRefCount(resultPtr);
+ }
+ Tcl_DecrRefCount(head);
+ return ret;
}
Tcl_DStringAppend(headPtr, tail, p-tail);
if (*p != '\0') {
return TclDoGlob(interp, separators, headPtr, p, types);
- }
+ } else {
+ /*
+ * This is the code path reached by a command like 'glob foo'.
+ *
+ * There are no more wildcards in the pattern and no more
+ * unprocessed characters in the tail, so now we can construct
+ * the path, and pass it to Tcl_FSMatchInDirectory with an
+ * empty pattern to verify the existence of the file and check
+ * it is of the correct type (if a 'types' flag it given -- if
+ * no such flag was given, we could just use 'Tcl_FSLStat', but
+ * for simplicity we keep to a common approach).
+ */
- /*
- * There are no more wildcards in the pattern and no more unprocessed
- * characters in the tail, so now we can construct the path and verify
- * the existence of the file.
- */
+ Tcl_Obj *nameObj;
+ /* Used to deal with one special case pertinent to MacOS */
+ int macSpecialCase = 0;
- switch (tclPlatform) {
- case TCL_PLATFORM_MAC: {
- if (strchr(Tcl_DStringValue(headPtr), ':') == NULL) {
- Tcl_DStringAppend(headPtr, ":", 1);
- }
- name = Tcl_DStringValue(headPtr);
- if (TclpAccess(name, F_OK) == 0) {
- if ((name[1] != '\0') && (strchr(name+1, ':') == NULL)) {
- Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
- Tcl_NewStringObj(name + 1,-1));
- } else {
- Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
- Tcl_NewStringObj(name,-1));
+ switch (tclPlatform) {
+ case TCL_PLATFORM_MAC: {
+ if (strchr(Tcl_DStringValue(headPtr), ':') == NULL) {
+ Tcl_DStringAppend(headPtr, ":", 1);
}
+ macSpecialCase = 1;
+ break;
}
- break;
- }
- case TCL_PLATFORM_WINDOWS: {
- int exists;
-
- /*
- * We need to convert slashes to backslashes before checking
- * for the existence of the file. Once we are done, we need
- * to convert the slashes back.
- */
-
- if (Tcl_DStringLength(headPtr) == 0) {
- if (((*name == '\\') && (name[1] == '/' || name[1] == '\\'))
- || (*name == '/')) {
- Tcl_DStringAppend(headPtr, "\\", 1);
- } else {
- Tcl_DStringAppend(headPtr, ".", 1);
+ case TCL_PLATFORM_WINDOWS: {
+ if (Tcl_DStringLength(headPtr) == 0) {
+ if (((*name == '\\') && (name[1] == '/' || name[1] == '\\'))
+ || (*name == '/')) {
+ Tcl_DStringAppend(headPtr, "\\", 1);
+ } else {
+ Tcl_DStringAppend(headPtr, ".", 1);
+ }
}
- } else {
+ /*
+ * Convert to forward slashes. This is required to pass
+ * some Tcl tests. We should probably remove the conversions
+ * here and in tclWinFile.c, since they aren't needed since
+ * the dropping of support for Win32s.
+ */
for (p = Tcl_DStringValue(headPtr); *p != '\0'; p++) {
- if (*p == '/') {
- *p = '\\';
+ if (*p == '\\') {
+ *p = '/';
}
}
+ break;
}
- name = Tcl_DStringValue(headPtr);
- exists = (TclpAccess(name, F_OK) == 0);
-
- for (p = name; *p != '\0'; p++) {
- if (*p == '\\') {
- *p = '/';
+ case TCL_PLATFORM_UNIX: {
+ if (Tcl_DStringLength(headPtr) == 0) {
+ if ((*name == '\\' && name[1] == '/') || (*name == '/')) {
+ Tcl_DStringAppend(headPtr, "/", 1);
+ } else {
+ Tcl_DStringAppend(headPtr, ".", 1);
+ }
}
+ break;
}
- if (exists) {
- Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
- Tcl_NewStringObj(name,-1));
- }
- break;
}
- case TCL_PLATFORM_UNIX: {
- if (Tcl_DStringLength(headPtr) == 0) {
- if ((*name == '\\' && name[1] == '/') || (*name == '/')) {
- Tcl_DStringAppend(headPtr, "/", 1);
- } else {
- Tcl_DStringAppend(headPtr, ".", 1);
- }
- }
- name = Tcl_DStringValue(headPtr);
- if (TclpAccess(name, F_OK) == 0) {
- Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
- Tcl_NewStringObj(name,-1));
- }
- break;
+ /* Common for all platforms */
+ name = Tcl_DStringValue(headPtr);
+ nameObj = Tcl_NewStringObj(name, Tcl_DStringLength(headPtr));
+
+ Tcl_IncrRefCount(nameObj);
+ Tcl_FSMatchInDirectory(interp, Tcl_GetObjResult(interp), nameObj,
+ NULL, types);
+ Tcl_DecrRefCount(nameObj);
+ return TCL_OK;
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclFileDirname
+ *
+ * This procedure calculates the directory above a given
+ * path: basically 'file dirname'. It is used both by
+ * the 'dirname' subcommand of file and by code in tclIOUtil.c.
+ *
+ * Results:
+ * NULL if an error occurred, otherwise a Tcl_Obj owned by
+ * the caller (i.e. most likely with refCount 1).
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+TclFileDirname(interp, pathPtr)
+ Tcl_Interp *interp; /* Used for error reporting */
+ Tcl_Obj *pathPtr; /* Path to take dirname of */
+{
+ int splitElements;
+ Tcl_Obj *splitPtr;
+ Tcl_Obj *splitResultPtr = NULL;
+
+ /*
+ * The behaviour we want here is slightly different to
+ * the standard Tcl_FSSplitPath in the handling of home
+ * directories; Tcl_FSSplitPath preserves the "~" while
+ * this code computes the actual full path name, if we
+ * had just a single component.
+ */
+ splitPtr = Tcl_FSSplitPath(pathPtr, &splitElements);
+ if ((splitElements == 1) && (Tcl_GetString(pathPtr)[0] == '~')) {
+ Tcl_DecrRefCount(splitPtr);
+ splitPtr = Tcl_FSGetNormalizedPath(interp, pathPtr);
+ if (splitPtr == NULL) {
+ return NULL;
}
+ splitPtr = Tcl_FSSplitPath(splitPtr, &splitElements);
}
- return TCL_OK;
+ /*
+ * Return all but the last component. If there is only one
+ * component, return it if the path was non-relative, otherwise
+ * return the current directory.
+ */
+
+ if (splitElements > 1) {
+ splitResultPtr = Tcl_FSJoinPath(splitPtr, splitElements - 1);
+ } else if (splitElements == 0 ||
+ (Tcl_FSGetPathType(pathPtr) == TCL_PATH_RELATIVE)) {
+ splitResultPtr = Tcl_NewStringObj(
+ ((tclPlatform == TCL_PLATFORM_MAC) ? ":" : "."), 1);
+ } else {
+ Tcl_ListObjIndex(NULL, splitPtr, 0, &splitResultPtr);
+ }
+ Tcl_IncrRefCount(splitResultPtr);
+ Tcl_DecrRefCount(splitPtr);
+ return splitResultPtr;
}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_AllocStatBuf
+ *
+ * This procedure allocates a Tcl_StatBuf on the heap. It exists
+ * so that extensions may be used unchanged on systems where
+ * largefile support is optional.
+ *
+ * Results:
+ * A pointer to a Tcl_StatBuf which may be deallocated by being
+ * passed to ckfree().
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tcl_StatBuf *
+Tcl_AllocStatBuf() {
+ return (Tcl_StatBuf *) ckalloc(sizeof(Tcl_StatBuf));
+}
diff --git a/tcl/generic/tclGet.c b/tcl/generic/tclGet.c
index 72edad8d981..98e7308c015 100644
--- a/tcl/generic/tclGet.c
+++ b/tcl/generic/tclGet.c
@@ -41,11 +41,12 @@
int
Tcl_GetInt(interp, string, intPtr)
Tcl_Interp *interp; /* Interpreter to use for error reporting. */
- char *string; /* String containing a (possibly signed)
+ CONST char *string; /* String containing a (possibly signed)
* integer in a form acceptable to strtol. */
int *intPtr; /* Place to store converted result. */
{
- char *end, *p;
+ char *end;
+ CONST char *p;
long i;
/*
@@ -128,12 +129,13 @@ int
TclGetLong(interp, string, longPtr)
Tcl_Interp *interp; /* Interpreter used for error reporting
* if not NULL. */
- char *string; /* String containing a (possibly signed)
+ CONST char *string; /* String containing a (possibly signed)
* long integer in a form acceptable to
* strtoul. */
long *longPtr; /* Place to store converted long result. */
{
- char *end, *p;
+ char *end;
+ CONST char *p;
long i;
/*
@@ -205,7 +207,7 @@ TclGetLong(interp, string, longPtr)
int
Tcl_GetDouble(interp, string, doublePtr)
Tcl_Interp *interp; /* Interpreter used for error reporting. */
- char *string; /* String containing a floating-point number
+ CONST char *string; /* String containing a floating-point number
* in a form acceptable to strtod. */
double *doublePtr; /* Place to store converted result. */
{
@@ -262,7 +264,7 @@ Tcl_GetDouble(interp, string, doublePtr)
int
Tcl_GetBoolean(interp, string, boolPtr)
Tcl_Interp *interp; /* Interpreter used for error reporting. */
- char *string; /* String containing a boolean number
+ CONST char *string; /* String containing a boolean number
* specified either as 1/0 or true/false or
* yes/no. */
int *boolPtr; /* Place to store converted result, which
@@ -321,4 +323,3 @@ Tcl_GetBoolean(interp, string, boolPtr)
}
return TCL_OK;
}
-
diff --git a/tcl/generic/tclGetDate.y b/tcl/generic/tclGetDate.y
index 33eff627aad..d7f30f17a08 100644
--- a/tcl/generic/tclGetDate.y
+++ b/tcl/generic/tclGetDate.y
@@ -33,7 +33,7 @@
#include "tclInt.h"
#include "tclPort.h"
-#ifdef MAC_TCL
+#if defined(MAC_TCL) && !defined(TCL_MAC_USE_MSL_EPOCH)
# define EPOCH 1904
# define START_OF_TIME 1904
# define END_OF_TIME 2039
@@ -798,6 +798,23 @@ RelativeMonth(Start, RelMonth, TimePtr)
result = Convert(Month, (time_t) tm->tm_mday, Year,
(time_t) tm->tm_hour, (time_t) tm->tm_min, (time_t) tm->tm_sec,
MER24, DSTmaybe, &Julian);
+
+ /*
+ * The Julian time returned above is behind by one day, if "month"
+ * or "year" is used to specify relative time and the GMT flag is true.
+ * This problem occurs only when the current time is closer to
+ * midnight, the difference being not more than its time difference
+ * with GMT. For example, in US/Pacific time zone, the problem occurs
+ * whenever the current time is between midnight to 8:00am or 7:00amDST.
+ * See Bug# 413397 for more details and sample script.
+ * To resolve this bug, we simply add the number of seconds corresponding
+ * to timezone difference with GMT to Julian time, if GMT flag is true.
+ */
+
+ if (TclDateTimezone == 0) {
+ Julian += TclpGetTimeZone((unsigned long) Start) * 60L;
+ }
+
/*
* The following iteration takes into account the case were we jump
* into a "short month". Far example, "one month from Jan 31" will
@@ -1137,4 +1154,3 @@ TclGetDate(p, now, zone, timePtr)
*timePtr = Start;
return 0;
}
-
diff --git a/tcl/generic/tclHash.c b/tcl/generic/tclHash.c
index cc1dcf2e627..277609c2d78 100644
--- a/tcl/generic/tclHash.c
+++ b/tcl/generic/tclHash.c
@@ -16,13 +16,21 @@
#include "tclInt.h"
/*
+ * Prevent macros from clashing with function definitions.
+ */
+
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+# undef Tcl_FindHashEntry
+# undef Tcl_CreateHashEntry
+#endif
+
+/*
* When there are this many entries per bucket, on average, rebuild
* the hash table to make it larger.
*/
#define REBUILD_MULTIPLIER 3
-
/*
* The following macro takes a preliminary integer hash value and
* produces an index into a hash tables bucket list. The idea is
@@ -35,27 +43,86 @@
(((((long) (i))*1103515245) >> (tablePtr)->downShift) & (tablePtr)->mask)
/*
+ * Prototypes for the array hash key methods.
+ */
+
+static Tcl_HashEntry * AllocArrayEntry _ANSI_ARGS_((
+ Tcl_HashTable *tablePtr,
+ VOID *keyPtr));
+static int CompareArrayKeys _ANSI_ARGS_((
+ VOID *keyPtr, Tcl_HashEntry *hPtr));
+static unsigned int HashArrayKey _ANSI_ARGS_((
+ Tcl_HashTable *tablePtr,
+ VOID *keyPtr));
+
+/*
+ * Prototypes for the one word hash key methods.
+ */
+
+#if 0
+static Tcl_HashEntry * AllocOneWordEntry _ANSI_ARGS_((
+ Tcl_HashTable *tablePtr,
+ VOID *keyPtr));
+static int CompareOneWordKeys _ANSI_ARGS_((
+ VOID *keyPtr, Tcl_HashEntry *hPtr));
+static unsigned int HashOneWordKey _ANSI_ARGS_((
+ Tcl_HashTable *tablePtr,
+ VOID *keyPtr));
+#endif
+
+/*
+ * Prototypes for the string hash key methods.
+ */
+
+static Tcl_HashEntry * AllocStringEntry _ANSI_ARGS_((
+ Tcl_HashTable *tablePtr,
+ VOID *keyPtr));
+static int CompareStringKeys _ANSI_ARGS_((
+ VOID *keyPtr, Tcl_HashEntry *hPtr));
+static unsigned int HashStringKey _ANSI_ARGS_((
+ Tcl_HashTable *tablePtr,
+ VOID *keyPtr));
+
+/*
* Procedure prototypes for static procedures in this file:
*/
-static Tcl_HashEntry * ArrayFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
- CONST char *key));
-static Tcl_HashEntry * ArrayCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
- CONST char *key, int *newPtr));
+#if TCL_PRESERVE_BINARY_COMPATABILITY
static Tcl_HashEntry * BogusFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
CONST char *key));
static Tcl_HashEntry * BogusCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
CONST char *key, int *newPtr));
-static unsigned int HashString _ANSI_ARGS_((CONST char *string));
+#endif
+
static void RebuildTable _ANSI_ARGS_((Tcl_HashTable *tablePtr));
-static Tcl_HashEntry * StringFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
- CONST char *key));
-static Tcl_HashEntry * StringCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
- CONST char *key, int *newPtr));
-static Tcl_HashEntry * OneWordFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
- CONST char *key));
-static Tcl_HashEntry * OneWordCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
- CONST char *key, int *newPtr));
+
+Tcl_HashKeyType tclArrayHashKeyType = {
+ TCL_HASH_KEY_TYPE_VERSION, /* version */
+ TCL_HASH_KEY_RANDOMIZE_HASH, /* flags */
+ HashArrayKey, /* hashKeyProc */
+ CompareArrayKeys, /* compareKeysProc */
+ AllocArrayEntry, /* allocEntryProc */
+ NULL /* freeEntryProc */
+};
+
+Tcl_HashKeyType tclOneWordHashKeyType = {
+ TCL_HASH_KEY_TYPE_VERSION, /* version */
+ 0, /* flags */
+ NULL, /* HashOneWordKey, */ /* hashProc */
+ NULL, /* CompareOneWordKey, */ /* compareProc */
+ NULL, /* AllocOneWordKey, */ /* allocEntryProc */
+ NULL /* FreeOneWordKey, */ /* freeEntryProc */
+};
+
+Tcl_HashKeyType tclStringHashKeyType = {
+ TCL_HASH_KEY_TYPE_VERSION, /* version */
+ 0, /* flags */
+ HashStringKey, /* hashKeyProc */
+ CompareStringKeys, /* compareKeysProc */
+ AllocStringEntry, /* allocEntryProc */
+ NULL /* freeEntryProc */
+};
+
/*
*----------------------------------------------------------------------
@@ -75,6 +142,7 @@ static Tcl_HashEntry * OneWordCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
*----------------------------------------------------------------------
*/
+#undef Tcl_InitHashTable
void
Tcl_InitHashTable(tablePtr, keyType)
register Tcl_HashTable *tablePtr; /* Pointer to table record, which
@@ -83,8 +151,48 @@ Tcl_InitHashTable(tablePtr, keyType)
* TCL_STRING_KEYS, TCL_ONE_WORD_KEYS,
* or an integer >= 2. */
{
+ /*
+ * Use a special value to inform the extended version that it must
+ * not access any of the new fields in the Tcl_HashTable. If an
+ * extension is rebuilt then any calls to this function will be
+ * redirected to the extended version by a macro.
+ */
+ Tcl_InitCustomHashTable(tablePtr, keyType, (Tcl_HashKeyType *) -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_InitCustomHashTable --
+ *
+ * Given storage for a hash table, set up the fields to prepare
+ * the hash table for use. This is an extended version of
+ * Tcl_InitHashTable which supports user defined keys.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * TablePtr is now ready to be passed to Tcl_FindHashEntry and
+ * Tcl_CreateHashEntry.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_InitCustomHashTable(tablePtr, keyType, typePtr)
+ register Tcl_HashTable *tablePtr; /* Pointer to table record, which
+ * is supplied by the caller. */
+ int keyType; /* Type of keys to use in table:
+ * TCL_STRING_KEYS, TCL_ONE_WORD_KEYS,
+ * TCL_CUSTOM_TYPE_KEYS,
+ * TCL_CUSTOM_PTR_KEYS, or an
+ * integer >= 2. */
+ Tcl_HashKeyType *typePtr; /* Pointer to structure which defines
+ * the behaviour of this table. */
+{
#if (TCL_SMALL_HASH_TABLE != 4)
- panic("Tcl_InitHashTable: TCL_SMALL_HASH_TABLE is %d, not 4\n",
+ panic("Tcl_InitCustomHashTable: TCL_SMALL_HASH_TABLE is %d, not 4\n",
TCL_SMALL_HASH_TABLE);
#endif
@@ -97,16 +205,280 @@ Tcl_InitHashTable(tablePtr, keyType)
tablePtr->downShift = 28;
tablePtr->mask = 3;
tablePtr->keyType = keyType;
- if (keyType == TCL_STRING_KEYS) {
- tablePtr->findProc = StringFind;
- tablePtr->createProc = StringCreate;
- } else if (keyType == TCL_ONE_WORD_KEYS) {
- tablePtr->findProc = OneWordFind;
- tablePtr->createProc = OneWordCreate;
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+ tablePtr->findProc = Tcl_FindHashEntry;
+ tablePtr->createProc = Tcl_CreateHashEntry;
+
+ if (typePtr == NULL) {
+ /*
+ * The caller has been rebuilt so the hash table is an extended
+ * version.
+ */
+ } else if (typePtr != (Tcl_HashKeyType *) -1) {
+ /*
+ * The caller is requesting a customized hash table so it must be
+ * an extended version.
+ */
+ tablePtr->typePtr = typePtr;
+ } else {
+ /*
+ * The caller has not been rebuilt so the hash table is not
+ * extended.
+ */
+ }
+#else
+ if (typePtr == NULL) {
+ /*
+ * Use the key type to decide which key type is needed.
+ */
+ if (keyType == TCL_STRING_KEYS) {
+ typePtr = &tclStringHashKeyType;
+ } else if (keyType == TCL_ONE_WORD_KEYS) {
+ typePtr = &tclOneWordHashKeyType;
+ } else if (keyType == TCL_CUSTOM_TYPE_KEYS) {
+ Tcl_Panic ("No type structure specified for TCL_CUSTOM_TYPE_KEYS");
+ } else if (keyType == TCL_CUSTOM_PTR_KEYS) {
+ Tcl_Panic ("No type structure specified for TCL_CUSTOM_PTR_KEYS");
+ } else {
+ typePtr = &tclArrayHashKeyType;
+ }
+ } else if (typePtr == (Tcl_HashKeyType *) -1) {
+ /*
+ * If the caller has not been rebuilt then we cannot continue as
+ * the hash table is not an extended version.
+ */
+ Tcl_Panic ("Hash table is not compatible");
+ }
+ tablePtr->typePtr = typePtr;
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FindHashEntry --
+ *
+ * Given a hash table find the entry with a matching key.
+ *
+ * Results:
+ * The return value is a token for the matching entry in the
+ * hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_HashEntry *
+Tcl_FindHashEntry(tablePtr, key)
+ Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */
+ CONST char *key; /* Key to use to find matching entry. */
+{
+ register Tcl_HashEntry *hPtr;
+ Tcl_HashKeyType *typePtr;
+ unsigned int hash;
+ int index;
+
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+ if (tablePtr->keyType == TCL_STRING_KEYS) {
+ typePtr = &tclStringHashKeyType;
+ } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) {
+ typePtr = &tclOneWordHashKeyType;
+ } else if (tablePtr->keyType == TCL_CUSTOM_TYPE_KEYS
+ || tablePtr->keyType == TCL_CUSTOM_PTR_KEYS) {
+ typePtr = tablePtr->typePtr;
+ } else {
+ typePtr = &tclArrayHashKeyType;
+ }
+#else
+ typePtr = tablePtr->typePtr;
+ if (typePtr == NULL) {
+ Tcl_Panic("called Tcl_FindHashEntry on deleted table");
+ return NULL;
+ }
+#endif
+
+ if (typePtr->hashKeyProc) {
+ hash = typePtr->hashKeyProc (tablePtr, (VOID *) key);
+ if (typePtr->flags & TCL_HASH_KEY_RANDOMIZE_HASH) {
+ index = RANDOM_INDEX (tablePtr, hash);
+ } else {
+ index = hash & tablePtr->mask;
+ }
+ } else {
+ hash = (unsigned int) key;
+ index = RANDOM_INDEX (tablePtr, hash);
+ }
+
+ /*
+ * Search all of the entries in the appropriate bucket.
+ */
+
+ if (typePtr->compareKeysProc) {
+ for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
+ hPtr = hPtr->nextPtr) {
+#if TCL_HASH_KEY_STORE_HASH
+ if (hash != (unsigned int) hPtr->hash) {
+ continue;
+ }
+#endif
+ if (typePtr->compareKeysProc ((VOID *) key, hPtr)) {
+ return hPtr;
+ }
+ }
+ } else {
+ for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
+ hPtr = hPtr->nextPtr) {
+#if TCL_HASH_KEY_STORE_HASH
+ if (hash != (unsigned int) hPtr->hash) {
+ continue;
+ }
+#endif
+ if (key == hPtr->key.oneWordValue) {
+ return hPtr;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_CreateHashEntry --
+ *
+ * Given a hash table with string keys, and a string key, find
+ * the entry with a matching key. If there is no matching entry,
+ * then create a new entry that does match.
+ *
+ * Results:
+ * The return value is a pointer to the matching entry. If this
+ * is a newly-created entry, then *newPtr will be set to a non-zero
+ * value; otherwise *newPtr will be set to 0. If this is a new
+ * entry the value stored in the entry will initially be 0.
+ *
+ * Side effects:
+ * A new entry may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_HashEntry *
+Tcl_CreateHashEntry(tablePtr, key, newPtr)
+ Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */
+ CONST char *key; /* Key to use to find or create matching
+ * entry. */
+ int *newPtr; /* Store info here telling whether a new
+ * entry was created. */
+{
+ register Tcl_HashEntry *hPtr;
+ Tcl_HashKeyType *typePtr;
+ unsigned int hash;
+ int index;
+
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+ if (tablePtr->keyType == TCL_STRING_KEYS) {
+ typePtr = &tclStringHashKeyType;
+ } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) {
+ typePtr = &tclOneWordHashKeyType;
+ } else if (tablePtr->keyType == TCL_CUSTOM_TYPE_KEYS
+ || tablePtr->keyType == TCL_CUSTOM_PTR_KEYS) {
+ typePtr = tablePtr->typePtr;
+ } else {
+ typePtr = &tclArrayHashKeyType;
+ }
+#else
+ typePtr = tablePtr->typePtr;
+ if (typePtr == NULL) {
+ Tcl_Panic("called Tcl_CreateHashEntry on deleted table");
+ return NULL;
+ }
+#endif
+
+ if (typePtr->hashKeyProc) {
+ hash = typePtr->hashKeyProc (tablePtr, (VOID *) key);
+ if (typePtr->flags & TCL_HASH_KEY_RANDOMIZE_HASH) {
+ index = RANDOM_INDEX (tablePtr, hash);
+ } else {
+ index = hash & tablePtr->mask;
+ }
+ } else {
+ hash = (unsigned int) key;
+ index = RANDOM_INDEX (tablePtr, hash);
+ }
+
+ /*
+ * Search all of the entries in the appropriate bucket.
+ */
+
+ if (typePtr->compareKeysProc) {
+ for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
+ hPtr = hPtr->nextPtr) {
+#if TCL_HASH_KEY_STORE_HASH
+ if (hash != (unsigned int) hPtr->hash) {
+ continue;
+ }
+#endif
+ if (typePtr->compareKeysProc ((VOID *) key, hPtr)) {
+ *newPtr = 0;
+ return hPtr;
+ }
+ }
+ } else {
+ for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
+ hPtr = hPtr->nextPtr) {
+#if TCL_HASH_KEY_STORE_HASH
+ if (hash != (unsigned int) hPtr->hash) {
+ continue;
+ }
+#endif
+ if (key == hPtr->key.oneWordValue) {
+ *newPtr = 0;
+ return hPtr;
+ }
+ }
+ }
+
+ /*
+ * Entry not found. Add a new one to the bucket.
+ */
+
+ *newPtr = 1;
+ if (typePtr->allocEntryProc) {
+ hPtr = typePtr->allocEntryProc (tablePtr, (VOID *) key);
} else {
- tablePtr->findProc = ArrayFind;
- tablePtr->createProc = ArrayCreate;
- };
+ hPtr = (Tcl_HashEntry *) ckalloc((unsigned) sizeof(Tcl_HashEntry));
+ hPtr->key.oneWordValue = (char *) key;
+ }
+
+ hPtr->tablePtr = tablePtr;
+#if TCL_HASH_KEY_STORE_HASH
+# if TCL_PRESERVE_BINARY_COMPATABILITY
+ hPtr->hash = (VOID *) hash;
+# else
+ hPtr->hash = hash;
+# endif
+ hPtr->nextPtr = tablePtr->buckets[index];
+ tablePtr->buckets[index] = hPtr;
+#else
+ hPtr->bucketPtr = &(tablePtr->buckets[index]);
+ hPtr->nextPtr = *hPtr->bucketPtr;
+ *hPtr->bucketPtr = hPtr;
+#endif
+ hPtr->clientData = 0;
+ tablePtr->numEntries++;
+
+ /*
+ * If the table has exceeded a decent size, rebuild it with many
+ * more buckets.
+ */
+
+ if (tablePtr->numEntries >= tablePtr->rebuildSize) {
+ RebuildTable(tablePtr);
+ }
+ return hPtr;
}
/*
@@ -133,11 +505,47 @@ Tcl_DeleteHashEntry(entryPtr)
Tcl_HashEntry *entryPtr;
{
register Tcl_HashEntry *prevPtr;
+ Tcl_HashKeyType *typePtr;
+ Tcl_HashTable *tablePtr;
+ Tcl_HashEntry **bucketPtr;
+#if TCL_HASH_KEY_STORE_HASH
+ int index;
+#endif
+
+ tablePtr = entryPtr->tablePtr;
- if (*entryPtr->bucketPtr == entryPtr) {
- *entryPtr->bucketPtr = entryPtr->nextPtr;
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+ if (tablePtr->keyType == TCL_STRING_KEYS) {
+ typePtr = &tclStringHashKeyType;
+ } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) {
+ typePtr = &tclOneWordHashKeyType;
+ } else if (tablePtr->keyType == TCL_CUSTOM_TYPE_KEYS
+ || tablePtr->keyType == TCL_CUSTOM_PTR_KEYS) {
+ typePtr = tablePtr->typePtr;
+ } else {
+ typePtr = &tclArrayHashKeyType;
+ }
+#else
+ typePtr = tablePtr->typePtr;
+#endif
+
+#if TCL_HASH_KEY_STORE_HASH
+ if (typePtr->hashKeyProc == NULL
+ || typePtr->flags & TCL_HASH_KEY_RANDOMIZE_HASH) {
+ index = RANDOM_INDEX (tablePtr, entryPtr->hash);
+ } else {
+ index = ((unsigned int) entryPtr->hash) & tablePtr->mask;
+ }
+
+ bucketPtr = &(tablePtr->buckets[index]);
+#else
+ bucketPtr = entryPtr->bucketPtr;
+#endif
+
+ if (*bucketPtr == entryPtr) {
+ *bucketPtr = entryPtr->nextPtr;
} else {
- for (prevPtr = *entryPtr->bucketPtr; ; prevPtr = prevPtr->nextPtr) {
+ for (prevPtr = *bucketPtr; ; prevPtr = prevPtr->nextPtr) {
if (prevPtr == NULL) {
panic("malformed bucket chain in Tcl_DeleteHashEntry");
}
@@ -147,8 +555,13 @@ Tcl_DeleteHashEntry(entryPtr)
}
}
}
- entryPtr->tablePtr->numEntries--;
- ckfree((char *) entryPtr);
+
+ tablePtr->numEntries--;
+ if (typePtr->freeEntryProc) {
+ typePtr->freeEntryProc (entryPtr);
+ } else {
+ ckfree((char *) entryPtr);
+ }
}
/*
@@ -173,8 +586,24 @@ Tcl_DeleteHashTable(tablePtr)
register Tcl_HashTable *tablePtr; /* Table to delete. */
{
register Tcl_HashEntry *hPtr, *nextPtr;
+ Tcl_HashKeyType *typePtr;
int i;
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+ if (tablePtr->keyType == TCL_STRING_KEYS) {
+ typePtr = &tclStringHashKeyType;
+ } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) {
+ typePtr = &tclOneWordHashKeyType;
+ } else if (tablePtr->keyType == TCL_CUSTOM_TYPE_KEYS
+ || tablePtr->keyType == TCL_CUSTOM_PTR_KEYS) {
+ typePtr = tablePtr->typePtr;
+ } else {
+ typePtr = &tclArrayHashKeyType;
+ }
+#else
+ typePtr = tablePtr->typePtr;
+#endif
+
/*
* Free up all the entries in the table.
*/
@@ -183,7 +612,11 @@ Tcl_DeleteHashTable(tablePtr)
hPtr = tablePtr->buckets[i];
while (hPtr != NULL) {
nextPtr = hPtr->nextPtr;
- ckfree((char *) hPtr);
+ if (typePtr->freeEntryProc) {
+ typePtr->freeEntryProc (hPtr);
+ } else {
+ ckfree((char *) hPtr);
+ }
hPtr = nextPtr;
}
}
@@ -201,8 +634,12 @@ Tcl_DeleteHashTable(tablePtr)
* re-initialization.
*/
+#if TCL_PRESERVE_BINARY_COMPATABILITY
tablePtr->findProc = BogusFind;
tablePtr->createProc = BogusCreate;
+#else
+ tablePtr->typePtr = NULL;
+#endif
}
/*
@@ -299,7 +736,7 @@ Tcl_NextHashEntry(searchPtr)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_HashStats(tablePtr)
Tcl_HashTable *tablePtr; /* Table for which to produce stats. */
{
@@ -355,14 +792,12 @@ Tcl_HashStats(tablePtr)
/*
*----------------------------------------------------------------------
*
- * HashString --
+ * AllocArrayEntry --
*
- * Compute a one-word summary of a text string, which can be
- * used to generate a hash index.
+ * Allocate space for a Tcl_HashEntry containing the array key.
*
* Results:
- * The return value is a one-word summary of the information in
- * string.
+ * The return value is a pointer to the created entry.
*
* Side effects:
* None.
@@ -370,52 +805,42 @@ Tcl_HashStats(tablePtr)
*----------------------------------------------------------------------
*/
-static unsigned int
-HashString(string)
- register CONST char *string;/* String from which to compute hash value. */
+static Tcl_HashEntry *
+AllocArrayEntry(tablePtr, keyPtr)
+ Tcl_HashTable *tablePtr; /* Hash table. */
+ VOID *keyPtr; /* Key to store in the hash table entry. */
{
- register unsigned int result;
- register int c;
-
- /*
- * I tried a zillion different hash functions and asked many other
- * people for advice. Many people had their own favorite functions,
- * all different, but no-one had much idea why they were good ones.
- * I chose the one below (multiply by 9 and add new character)
- * because of the following reasons:
- *
- * 1. Multiplying by 10 is perfect for keys that are decimal strings,
- * and multiplying by 9 is just about as good.
- * 2. Times-9 is (shift-left-3) plus (old). This means that each
- * character's bits hang around in the low-order bits of the
- * hash value for ever, plus they spread fairly rapidly up to
- * the high-order bits to fill out the hash value. This seems
- * works well both for decimal and non-decimal strings.
- */
+ int *array = (int *) keyPtr;
+ register int *iPtr1, *iPtr2;
+ Tcl_HashEntry *hPtr;
+ int count;
+ unsigned int size;
- result = 0;
- while (1) {
- c = *string;
- string++;
- if (c == 0) {
- break;
- }
- result += (result<<3) + c;
+ count = tablePtr->keyType;
+
+ size = sizeof(Tcl_HashEntry) + (count*sizeof(int)) - sizeof(hPtr->key);
+ if (size < sizeof(Tcl_HashEntry))
+ size = sizeof(Tcl_HashEntry);
+ hPtr = (Tcl_HashEntry *) ckalloc(size);
+
+ for (iPtr1 = array, iPtr2 = hPtr->key.words;
+ count > 0; count--, iPtr1++, iPtr2++) {
+ *iPtr2 = *iPtr1;
}
- return result;
+
+ return hPtr;
}
/*
*----------------------------------------------------------------------
*
- * StringFind --
+ * CompareArrayKeys --
*
- * Given a hash table with string keys, and a string key, find
- * the entry with a matching key.
+ * Compares two array keys.
*
* Results:
- * The return value is a token for the matching entry in the
- * hash table, or NULL if there was no matching entry.
+ * The return value is 0 if they are different and 1 if they are
+ * the same.
*
* Side effects:
* None.
@@ -423,124 +848,38 @@ HashString(string)
*----------------------------------------------------------------------
*/
-static Tcl_HashEntry *
-StringFind(tablePtr, key)
- Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */
- CONST char *key; /* Key to use to find matching entry. */
+static int
+CompareArrayKeys(keyPtr, hPtr)
+ VOID *keyPtr; /* New key to compare. */
+ Tcl_HashEntry *hPtr; /* Existing key to compare. */
{
- register Tcl_HashEntry *hPtr;
- register CONST char *p1, *p2;
- int index;
-
- index = HashString(key) & tablePtr->mask;
-
- /*
- * Search all of the entries in the appropriate bucket.
- */
-
- for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
- hPtr = hPtr->nextPtr) {
- for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
- if (*p1 != *p2) {
- break;
- }
- if (*p1 == '\0') {
- return hPtr;
- }
+ register CONST int *iPtr1 = (CONST int *) keyPtr;
+ register CONST int *iPtr2 = (CONST int *) hPtr->key.words;
+ Tcl_HashTable *tablePtr = hPtr->tablePtr;
+ int count;
+
+ for (count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
+ if (count == 0) {
+ return 1;
}
- }
- return NULL;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * StringCreate --
- *
- * Given a hash table with string keys, and a string key, find
- * the entry with a matching key. If there is no matching entry,
- * then create a new entry that does match.
- *
- * Results:
- * The return value is a pointer to the matching entry. If this
- * is a newly-created entry, then *newPtr will be set to a non-zero
- * value; otherwise *newPtr will be set to 0. If this is a new
- * entry the value stored in the entry will initially be 0.
- *
- * Side effects:
- * A new entry may be added to the hash table.
- *
- *----------------------------------------------------------------------
- */
-
-static Tcl_HashEntry *
-StringCreate(tablePtr, key, newPtr)
- Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */
- CONST char *key; /* Key to use to find or create matching
- * entry. */
- int *newPtr; /* Store info here telling whether a new
- * entry was created. */
-{
- register Tcl_HashEntry *hPtr;
- register CONST char *p1, *p2;
- int index;
-
- index = HashString(key) & tablePtr->mask;
-
- /*
- * Search all of the entries in this bucket.
- */
-
- for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
- hPtr = hPtr->nextPtr) {
- for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
- if (*p1 != *p2) {
- break;
- }
- if (*p1 == '\0') {
- *newPtr = 0;
- return hPtr;
- }
+ if (*iPtr1 != *iPtr2) {
+ break;
}
}
-
- /*
- * Entry not found. Add a new one to the bucket.
- */
-
- *newPtr = 1;
- hPtr = (Tcl_HashEntry *) ckalloc((unsigned)
- (sizeof(Tcl_HashEntry) + strlen(key) - (sizeof(hPtr->key) -1)));
- hPtr->tablePtr = tablePtr;
- hPtr->bucketPtr = &(tablePtr->buckets[index]);
- hPtr->nextPtr = *hPtr->bucketPtr;
- hPtr->clientData = 0;
- strcpy(hPtr->key.string, key);
- *hPtr->bucketPtr = hPtr;
- tablePtr->numEntries++;
-
- /*
- * If the table has exceeded a decent size, rebuild it with many
- * more buckets.
- */
-
- if (tablePtr->numEntries >= tablePtr->rebuildSize) {
- RebuildTable(tablePtr);
- }
- return hPtr;
+ return 0;
}
/*
*----------------------------------------------------------------------
*
- * OneWordFind --
+ * HashArrayKey --
*
- * Given a hash table with one-word keys, and a one-word key, find
- * the entry with a matching key.
+ * Compute a one-word summary of an array, which can be
+ * used to generate a hash index.
*
* Results:
- * The return value is a token for the matching entry in the
- * hash table, or NULL if there was no matching entry.
+ * The return value is a one-word summary of the information in
+ * string.
*
* Side effects:
* None.
@@ -548,111 +887,66 @@ StringCreate(tablePtr, key, newPtr)
*----------------------------------------------------------------------
*/
-static Tcl_HashEntry *
-OneWordFind(tablePtr, key)
- Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */
- register CONST char *key; /* Key to use to find matching entry. */
+static unsigned int
+HashArrayKey(tablePtr, keyPtr)
+ Tcl_HashTable *tablePtr; /* Hash table. */
+ VOID *keyPtr; /* Key from which to compute hash value. */
{
- register Tcl_HashEntry *hPtr;
- int index;
-
- index = RANDOM_INDEX(tablePtr, key);
-
- /*
- * Search all of the entries in the appropriate bucket.
- */
+ register CONST int *array = (CONST int *) keyPtr;
+ register unsigned int result;
+ int count;
- for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
- hPtr = hPtr->nextPtr) {
- if (hPtr->key.oneWordValue == key) {
- return hPtr;
- }
+ for (result = 0, count = tablePtr->keyType; count > 0;
+ count--, array++) {
+ result += *array;
}
- return NULL;
+ return result;
}
/*
*----------------------------------------------------------------------
*
- * OneWordCreate --
+ * AllocStringEntry --
*
- * Given a hash table with one-word keys, and a one-word key, find
- * the entry with a matching key. If there is no matching entry,
- * then create a new entry that does match.
+ * Allocate space for a Tcl_HashEntry containing the string key.
*
* Results:
- * The return value is a pointer to the matching entry. If this
- * is a newly-created entry, then *newPtr will be set to a non-zero
- * value; otherwise *newPtr will be set to 0. If this is a new
- * entry the value stored in the entry will initially be 0.
+ * The return value is a pointer to the created entry.
*
* Side effects:
- * A new entry may be added to the hash table.
+ * None.
*
*----------------------------------------------------------------------
*/
static Tcl_HashEntry *
-OneWordCreate(tablePtr, key, newPtr)
- Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */
- register CONST char *key; /* Key to use to find or create matching
- * entry. */
- int *newPtr; /* Store info here telling whether a new
- * entry was created. */
+AllocStringEntry(tablePtr, keyPtr)
+ Tcl_HashTable *tablePtr; /* Hash table. */
+ VOID *keyPtr; /* Key to store in the hash table entry. */
{
- register Tcl_HashEntry *hPtr;
- int index;
-
- index = RANDOM_INDEX(tablePtr, key);
-
- /*
- * Search all of the entries in this bucket.
- */
-
- for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
- hPtr = hPtr->nextPtr) {
- if (hPtr->key.oneWordValue == key) {
- *newPtr = 0;
- return hPtr;
- }
- }
-
- /*
- * Entry not found. Add a new one to the bucket.
- */
-
- *newPtr = 1;
- hPtr = (Tcl_HashEntry *) ckalloc(sizeof(Tcl_HashEntry));
- hPtr->tablePtr = tablePtr;
- hPtr->bucketPtr = &(tablePtr->buckets[index]);
- hPtr->nextPtr = *hPtr->bucketPtr;
- hPtr->clientData = 0;
- hPtr->key.oneWordValue = (char *) key; /* CONST XXXX */
- *hPtr->bucketPtr = hPtr;
- tablePtr->numEntries++;
+ CONST char *string = (CONST char *) keyPtr;
+ Tcl_HashEntry *hPtr;
+ unsigned int size;
- /*
- * If the table has exceeded a decent size, rebuild it with many
- * more buckets.
- */
+ size = sizeof(Tcl_HashEntry) + strlen(string) + 1 - sizeof(hPtr->key);
+ if (size < sizeof(Tcl_HashEntry))
+ size = sizeof(Tcl_HashEntry);
+ hPtr = (Tcl_HashEntry *) ckalloc(size);
+ strcpy(hPtr->key.string, string);
- if (tablePtr->numEntries >= tablePtr->rebuildSize) {
- RebuildTable(tablePtr);
- }
return hPtr;
}
/*
*----------------------------------------------------------------------
*
- * ArrayFind --
+ * CompareStringKeys --
*
- * Given a hash table with array-of-int keys, and a key, find
- * the entry with a matching key.
+ * Compares two string keys.
*
* Results:
- * The return value is a token for the matching entry in the
- * hash table, or NULL if there was no matching entry.
+ * The return value is 0 if they are different and 1 if they are
+ * the same.
*
* Side effects:
* None.
@@ -660,128 +954,81 @@ OneWordCreate(tablePtr, key, newPtr)
*----------------------------------------------------------------------
*/
-static Tcl_HashEntry *
-ArrayFind(tablePtr, key)
- Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */
- CONST char *key; /* Key to use to find matching entry. */
+static int
+CompareStringKeys(keyPtr, hPtr)
+ VOID *keyPtr; /* New key to compare. */
+ Tcl_HashEntry *hPtr; /* Existing key to compare. */
{
- register Tcl_HashEntry *hPtr;
- int *arrayPtr = (int *) key;
- register int *iPtr1, *iPtr2;
- int index, count;
-
- for (index = 0, count = tablePtr->keyType, iPtr1 = arrayPtr;
- count > 0; count--, iPtr1++) {
- index += *iPtr1;
- }
- index = RANDOM_INDEX(tablePtr, index);
-
- /*
- * Search all of the entries in the appropriate bucket.
- */
+ register CONST char *p1 = (CONST char *) keyPtr;
+ register CONST char *p2 = (CONST char *) hPtr->key.string;
- for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
- hPtr = hPtr->nextPtr) {
- for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words,
- count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
- if (count == 0) {
- return hPtr;
- }
- if (*iPtr1 != *iPtr2) {
- break;
- }
+ for (;; p1++, p2++) {
+ if (*p1 != *p2) {
+ break;
+ }
+ if (*p1 == '\0') {
+ return 1;
}
}
- return NULL;
+ return 0;
}
/*
*----------------------------------------------------------------------
*
- * ArrayCreate --
+ * HashStringKey --
*
- * Given a hash table with one-word keys, and a one-word key, find
- * the entry with a matching key. If there is no matching entry,
- * then create a new entry that does match.
+ * Compute a one-word summary of a text string, which can be
+ * used to generate a hash index.
*
* Results:
- * The return value is a pointer to the matching entry. If this
- * is a newly-created entry, then *newPtr will be set to a non-zero
- * value; otherwise *newPtr will be set to 0. If this is a new
- * entry the value stored in the entry will initially be 0.
+ * The return value is a one-word summary of the information in
+ * string.
*
* Side effects:
- * A new entry may be added to the hash table.
+ * None.
*
*----------------------------------------------------------------------
*/
-static Tcl_HashEntry *
-ArrayCreate(tablePtr, key, newPtr)
- Tcl_HashTable *tablePtr; /* Table in which to lookup entry. */
- register CONST char *key; /* Key to use to find or create matching
- * entry. */
- int *newPtr; /* Store info here telling whether a new
- * entry was created. */
+static unsigned int
+HashStringKey(tablePtr, keyPtr)
+ Tcl_HashTable *tablePtr; /* Hash table. */
+ VOID *keyPtr; /* Key from which to compute hash value. */
{
- register Tcl_HashEntry *hPtr;
- int *arrayPtr = (int *) key;
- register int *iPtr1, *iPtr2;
- int index, count;
-
- for (index = 0, count = tablePtr->keyType, iPtr1 = arrayPtr;
- count > 0; count--, iPtr1++) {
- index += *iPtr1;
- }
- index = RANDOM_INDEX(tablePtr, index);
+ register CONST char *string = (CONST char *) keyPtr;
+ register unsigned int result;
+ register int c;
/*
- * Search all of the entries in the appropriate bucket.
+ * I tried a zillion different hash functions and asked many other
+ * people for advice. Many people had their own favorite functions,
+ * all different, but no-one had much idea why they were good ones.
+ * I chose the one below (multiply by 9 and add new character)
+ * because of the following reasons:
+ *
+ * 1. Multiplying by 10 is perfect for keys that are decimal strings,
+ * and multiplying by 9 is just about as good.
+ * 2. Times-9 is (shift-left-3) plus (old). This means that each
+ * character's bits hang around in the low-order bits of the
+ * hash value for ever, plus they spread fairly rapidly up to
+ * the high-order bits to fill out the hash value. This seems
+ * works well both for decimal and non-decimal strings.
*/
- for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
- hPtr = hPtr->nextPtr) {
- for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words,
- count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
- if (count == 0) {
- *newPtr = 0;
- return hPtr;
- }
- if (*iPtr1 != *iPtr2) {
- break;
- }
+ result = 0;
+ while (1) {
+ c = *string;
+ string++;
+ if (c == 0) {
+ break;
}
+ result += (result<<3) + c;
}
-
- /*
- * Entry not found. Add a new one to the bucket.
- */
-
- *newPtr = 1;
- hPtr = (Tcl_HashEntry *) ckalloc((unsigned) (sizeof(Tcl_HashEntry)
- + (tablePtr->keyType*sizeof(int)) - 4));
- hPtr->tablePtr = tablePtr;
- hPtr->bucketPtr = &(tablePtr->buckets[index]);
- hPtr->nextPtr = *hPtr->bucketPtr;
- hPtr->clientData = 0;
- for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words, count = tablePtr->keyType;
- count > 0; count--, iPtr1++, iPtr2++) {
- *iPtr2 = *iPtr1;
- }
- *hPtr->bucketPtr = hPtr;
- tablePtr->numEntries++;
-
- /*
- * If the table has exceeded a decent size, rebuild it with many
- * more buckets.
- */
-
- if (tablePtr->numEntries >= tablePtr->rebuildSize) {
- RebuildTable(tablePtr);
- }
- return hPtr;
+ return result;
}
+#if TCL_PRESERVE_BINARY_COMPATABILITY
/*
*----------------------------------------------------------------------
*
@@ -840,6 +1087,7 @@ BogusCreate(tablePtr, key, newPtr)
panic("called Tcl_CreateHashEntry on deleted table");
return NULL;
}
+#endif
/*
*----------------------------------------------------------------------
@@ -869,6 +1117,8 @@ RebuildTable(tablePtr)
Tcl_HashEntry **oldBuckets;
register Tcl_HashEntry **oldChainPtr, **newChainPtr;
register Tcl_HashEntry *hPtr;
+ Tcl_HashKeyType *typePtr;
+ VOID *key;
oldSize = tablePtr->numBuckets;
oldBuckets = tablePtr->buckets;
@@ -889,6 +1139,21 @@ RebuildTable(tablePtr)
tablePtr->downShift -= 2;
tablePtr->mask = (tablePtr->mask << 2) + 3;
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+ if (tablePtr->keyType == TCL_STRING_KEYS) {
+ typePtr = &tclStringHashKeyType;
+ } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) {
+ typePtr = &tclOneWordHashKeyType;
+ } else if (tablePtr->keyType == TCL_CUSTOM_TYPE_KEYS
+ || tablePtr->keyType == TCL_CUSTOM_PTR_KEYS) {
+ typePtr = tablePtr->typePtr;
+ } else {
+ typePtr = &tclArrayHashKeyType;
+ }
+#else
+ typePtr = tablePtr->typePtr;
+#endif
+
/*
* Rehash all of the existing entries into the new bucket array.
*/
@@ -896,23 +1161,35 @@ RebuildTable(tablePtr)
for (oldChainPtr = oldBuckets; oldSize > 0; oldSize--, oldChainPtr++) {
for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = *oldChainPtr) {
*oldChainPtr = hPtr->nextPtr;
- if (tablePtr->keyType == TCL_STRING_KEYS) {
- index = HashString(hPtr->key.string) & tablePtr->mask;
- } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) {
- index = RANDOM_INDEX(tablePtr, hPtr->key.oneWordValue);
- } else {
- register int *iPtr;
- int count;
- for (index = 0, count = tablePtr->keyType,
- iPtr = hPtr->key.words; count > 0; count--, iPtr++) {
- index += *iPtr;
+ key = (VOID *) Tcl_GetHashKey (tablePtr, hPtr);
+
+#if TCL_HASH_KEY_STORE_HASH
+ if (typePtr->hashKeyProc == NULL
+ || typePtr->flags & TCL_HASH_KEY_RANDOMIZE_HASH) {
+ index = RANDOM_INDEX (tablePtr, hPtr->hash);
+ } else {
+ index = ((unsigned int) hPtr->hash) & tablePtr->mask;
+ }
+ hPtr->nextPtr = tablePtr->buckets[index];
+ tablePtr->buckets[index] = hPtr;
+#else
+ if (typePtr->hashKeyProc) {
+ unsigned int hash;
+ hash = typePtr->hashKeyProc (tablePtr, (VOID *) key);
+ if (typePtr->flags & TCL_HASH_KEY_RANDOMIZE_HASH) {
+ index = RANDOM_INDEX (tablePtr, hash);
+ } else {
+ index = hash & tablePtr->mask;
}
- index = RANDOM_INDEX(tablePtr, index);
+ } else {
+ index = RANDOM_INDEX (tablePtr, key);
}
+
hPtr->bucketPtr = &(tablePtr->buckets[index]);
hPtr->nextPtr = *hPtr->bucketPtr;
*hPtr->bucketPtr = hPtr;
+#endif
}
}
diff --git a/tcl/generic/tclHistory.c b/tcl/generic/tclHistory.c
index e69f8ca28e7..5ac7bc70748 100644
--- a/tcl/generic/tclHistory.c
+++ b/tcl/generic/tclHistory.c
@@ -42,7 +42,7 @@ int
Tcl_RecordAndEval(interp, cmd, flags)
Tcl_Interp *interp; /* Token for interpreter in which command
* will be executed. */
- char *cmd; /* Command to record. */
+ CONST char *cmd; /* Command to record. */
int flags; /* Additional flags. TCL_NO_EVAL means
* only record: don't execute command.
* TCL_EVAL_GLOBAL means use Tcl_GlobalEval
diff --git a/tcl/generic/tclIO.c b/tcl/generic/tclIO.c
index ab37a1b003d..997d21701c5 100644
--- a/tcl/generic/tclIO.c
+++ b/tcl/generic/tclIO.c
@@ -92,8 +92,7 @@ static int CopyAndTranslateBuffer _ANSI_ARGS_((
ChannelState *statePtr, char *result,
int space));
static int CopyBuffer _ANSI_ARGS_((
- Channel *chanPtr, char *result,
- int space));
+ Channel *chanPtr, char *result, int space));
static int CopyData _ANSI_ARGS_((CopyState *csPtr, int mask));
static void CopyEventProc _ANSI_ARGS_((ClientData clientData,
int mask));
@@ -104,28 +103,36 @@ static void DeleteChannelTable _ANSI_ARGS_((
ClientData clientData, Tcl_Interp *interp));
static void DeleteScriptRecord _ANSI_ARGS_((Tcl_Interp *interp,
Channel *chanPtr, int mask));
+static int DetachChannel _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Channel chan));
static void DiscardInputQueued _ANSI_ARGS_((ChannelState *statePtr,
int discardSavedBuffers));
static void DiscardOutputQueued _ANSI_ARGS_((
ChannelState *chanPtr));
static int DoRead _ANSI_ARGS_((Channel *chanPtr, char *srcPtr,
int slen));
-static int DoWrite _ANSI_ARGS_((Channel *chanPtr, char *src,
+static int DoWrite _ANSI_ARGS_((Channel *chanPtr, CONST char *src,
int srcLen));
+static int DoReadChars _ANSI_ARGS_ ((Channel* chan,
+ Tcl_Obj* objPtr, int toRead, int appendFlag));
+static int DoWriteChars _ANSI_ARGS_ ((Channel* chan,
+ CONST char* src, int len));
static int FilterInputBytes _ANSI_ARGS_((Channel *chanPtr,
GetsState *statePtr));
static int FlushChannel _ANSI_ARGS_((Tcl_Interp *interp,
Channel *chanPtr, int calledFromAsyncFlush));
static Tcl_HashTable * GetChannelTable _ANSI_ARGS_((Tcl_Interp *interp));
static int GetInput _ANSI_ARGS_((Channel *chanPtr));
+static int HaveVersion _ANSI_ARGS_((Tcl_ChannelType *typePtr,
+ Tcl_ChannelTypeVersion minimumVersion));
static void PeekAhead _ANSI_ARGS_((Channel *chanPtr,
char **dstEndPtr, GetsState *gsPtr));
static int ReadBytes _ANSI_ARGS_((ChannelState *statePtr,
Tcl_Obj *objPtr, int charsLeft,
int *offsetPtr));
static int ReadChars _ANSI_ARGS_((ChannelState *statePtr,
- Tcl_Obj *objPtr, int charsLeft, int *offsetPtr,
- int *factorPtr));
+ Tcl_Obj *objPtr, int charsLeft,
+ int *offsetPtr, int *factorPtr));
static void RecycleBuffer _ANSI_ARGS_((ChannelState *statePtr,
ChannelBuffer *bufPtr, int mustDiscard));
static int StackSetBlockMode _ANSI_ARGS_((Channel *chanPtr,
@@ -134,11 +141,11 @@ static int SetBlockMode _ANSI_ARGS_((Tcl_Interp *interp,
Channel *chanPtr, int mode));
static void StopCopy _ANSI_ARGS_((CopyState *csPtr));
static int TranslateInputEOL _ANSI_ARGS_((ChannelState *statePtr,
- char *dst, CONST char *src, int *dstLenPtr,
- int *srcLenPtr));
+ char *dst, CONST char *src,
+ int *dstLenPtr, int *srcLenPtr));
static int TranslateOutputEOL _ANSI_ARGS_((ChannelState *statePtr,
- char *dst, CONST char *src, int *dstLenPtr,
- int *srcLenPtr));
+ char *dst, CONST char *src,
+ int *dstLenPtr, int *srcLenPtr));
static void UpdateInterest _ANSI_ARGS_((Channel *chanPtr));
static int WriteBytes _ANSI_ARGS_((Channel *chanPtr,
CONST char *src, int srcLen));
@@ -683,6 +690,38 @@ CheckForStdChannelsBeingClosed(chan)
/*
*----------------------------------------------------------------------
*
+ * Tcl_IsStandardChannel --
+ *
+ * Test if the given channel is a standard channel. No attempt
+ * is made to check if the channel or the standard channels
+ * are initialized or otherwise valid.
+ *
+ * Results:
+ * Returns 1 if true, 0 if false.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tcl_IsStandardChannel(chan)
+ Tcl_Channel chan; /* Channel to check. */
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ if ((chan == tsdPtr->stdinChannel)
+ || (chan == tsdPtr->stdoutChannel)
+ || (chan == tsdPtr->stderrChannel)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_RegisterChannel --
*
* Adds an already-open channel to the channel table of an interpreter.
@@ -718,7 +757,7 @@ Tcl_RegisterChannel(interp, chan)
chanPtr = ((Channel *) chan)->state->bottomChanPtr;
statePtr = chanPtr->state;
- if (statePtr->channelName == (char *) NULL) {
+ if (statePtr->channelName == (CONST char *) NULL) {
panic("Tcl_RegisterChannel: channel without name");
}
if (interp != (Tcl_Interp *) NULL) {
@@ -743,13 +782,21 @@ Tcl_RegisterChannel(interp, chan)
*
* Deletes the hash entry for a channel associated with an interpreter.
* If the interpreter given as argument is NULL, it only decrements the
- * reference count.
+ * reference count. (This all happens in the Tcl_DetachChannel helper
+ * function).
+ *
+ * Finally, if the reference count of the channel drops to zero,
+ * it is deleted.
*
* Results:
* A standard Tcl result.
*
* Side effects:
- * Deletes the hash entry for a channel associated with an interpreter.
+ * Calls Tcl_DetachChannel which deletes the hash entry for a channel
+ * associated with an interpreter.
+ *
+ * May delete the channel, which can have a variety of consequences,
+ * especially if we are forced to close the channel.
*
*----------------------------------------------------------------------
*/
@@ -759,46 +806,14 @@ Tcl_UnregisterChannel(interp, chan)
Tcl_Interp *interp; /* Interpreter in which channel is defined. */
Tcl_Channel chan; /* Channel to delete. */
{
- Tcl_HashTable *hTblPtr; /* Hash table of channels. */
- Tcl_HashEntry *hPtr; /* Search variable. */
- Channel *chanPtr; /* The real IO channel. */
ChannelState *statePtr; /* State of the real channel. */
- /*
- * Always (un)register bottom-most channel in the stack. This makes
- * management of the channel list easier because no manipulation is
- * necessary during (un)stack operation.
- */
- chanPtr = ((Channel *) chan)->state->bottomChanPtr;
- statePtr = chanPtr->state;
-
- if (interp != (Tcl_Interp *) NULL) {
- hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
- if (hTblPtr == (Tcl_HashTable *) NULL) {
- return TCL_OK;
- }
- hPtr = Tcl_FindHashEntry(hTblPtr, statePtr->channelName);
- if (hPtr == (Tcl_HashEntry *) NULL) {
- return TCL_OK;
- }
- if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
- return TCL_OK;
- }
- Tcl_DeleteHashEntry(hPtr);
-
- /*
- * Remove channel handlers that refer to this interpreter, so that they
- * will not be present if the actual close is delayed and more events
- * happen on the channel. This may occur if the channel is shared
- * between several interpreters, or if the channel has async
- * flushing active.
- */
-
- CleanupChannelHandlers(interp, chanPtr);
+ if (DetachChannel(interp, chan) != TCL_OK) {
+ return TCL_OK;
}
-
- statePtr->refCount--;
+ statePtr = ((Channel *) chan)->state->bottomChanPtr->state;
+
/*
* Perform special handling for standard channels being closed. If the
* refCount is now 1 it means that the last reference to the standard
@@ -825,15 +840,145 @@ Tcl_UnregisterChannel(interp, chan)
statePtr->curOutPtr->nextRemoved)) {
statePtr->flags |= BUFFER_READY;
}
- statePtr->flags |= CHANNEL_CLOSED;
+ Tcl_Preserve((ClientData)statePtr);
if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) {
- if (Tcl_Close(interp, chan) != TCL_OK) {
- return TCL_ERROR;
- }
+ /* We don't want to re-enter Tcl_Close */
+ if (!(statePtr->flags & CHANNEL_CLOSED)) {
+ if (Tcl_Close(interp, chan) != TCL_OK) {
+ statePtr->flags |= CHANNEL_CLOSED;
+ Tcl_Release((ClientData)statePtr);
+ return TCL_ERROR;
+ }
+ }
}
+ statePtr->flags |= CHANNEL_CLOSED;
+ Tcl_Release((ClientData)statePtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_DetachChannel --
+ *
+ * Deletes the hash entry for a channel associated with an interpreter.
+ * If the interpreter given as argument is NULL, it only decrements the
+ * reference count. Even if the ref count drops to zero, the
+ * channel is NOT closed or cleaned up. This allows a channel to
+ * be detached from an interpreter and left in the same state it
+ * was in when it was originally returned by 'Tcl_OpenFileChannel',
+ * for example.
+ *
+ * This function cannot be used on the standard channels, and
+ * will return TCL_ERROR if that is attempted.
+ *
+ * This function should only be necessary for special purposes
+ * in which you need to generate a pristine channel from one
+ * that has already been used. All ordinary purposes will almost
+ * always want to use Tcl_UnregisterChannel instead.
+ *
+ * Provided the channel is not attached to any other interpreter,
+ * it can then be closed with Tcl_Close, rather than with
+ * Tcl_UnregisterChannel.
+ *
+ * Results:
+ * A standard Tcl result. If the channel is not currently registered
+ * with the given interpreter, TCL_ERROR is returned, otherwise
+ * TCL_OK. However no error messages are left in the interp's result.
+ *
+ * Side effects:
+ * Deletes the hash entry for a channel associated with an
+ * interpreter.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_DetachChannel(interp, chan)
+ Tcl_Interp *interp; /* Interpreter in which channel is defined. */
+ Tcl_Channel chan; /* Channel to delete. */
+{
+ if (Tcl_IsStandardChannel(chan)) {
+ return TCL_ERROR;
+ }
+
+ return DetachChannel(interp, chan);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DetachChannel --
+ *
+ * Deletes the hash entry for a channel associated with an interpreter.
+ * If the interpreter given as argument is NULL, it only decrements the
+ * reference count. Even if the ref count drops to zero, the
+ * channel is NOT closed or cleaned up. This allows a channel to
+ * be detached from an interpreter and left in the same state it
+ * was in when it was originally returned by 'Tcl_OpenFileChannel',
+ * for example.
+ *
+ * Results:
+ * A standard Tcl result. If the channel is not currently registered
+ * with the given interpreter, TCL_ERROR is returned, otherwise
+ * TCL_OK. However no error messages are left in the interp's result.
+ *
+ * Side effects:
+ * Deletes the hash entry for a channel associated with an
+ * interpreter.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DetachChannel(interp, chan)
+ Tcl_Interp *interp; /* Interpreter in which channel is defined. */
+ Tcl_Channel chan; /* Channel to delete. */
+{
+ Tcl_HashTable *hTblPtr; /* Hash table of channels. */
+ Tcl_HashEntry *hPtr; /* Search variable. */
+ Channel *chanPtr; /* The real IO channel. */
+ ChannelState *statePtr; /* State of the real channel. */
+
+ /*
+ * Always (un)register bottom-most channel in the stack. This makes
+ * management of the channel list easier because no manipulation is
+ * necessary during (un)stack operation.
+ */
+ chanPtr = ((Channel *) chan)->state->bottomChanPtr;
+ statePtr = chanPtr->state;
+
+ if (interp != (Tcl_Interp *) NULL) {
+ hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
+ if (hTblPtr == (Tcl_HashTable *) NULL) {
+ return TCL_ERROR;
+ }
+ hPtr = Tcl_FindHashEntry(hTblPtr, statePtr->channelName);
+ if (hPtr == (Tcl_HashEntry *) NULL) {
+ return TCL_ERROR;
+ }
+ if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
+ return TCL_ERROR;
+ }
+ Tcl_DeleteHashEntry(hPtr);
+
+ /*
+ * Remove channel handlers that refer to this interpreter, so that they
+ * will not be present if the actual close is delayed and more events
+ * happen on the channel. This may occur if the channel is shared
+ * between several interpreters, or if the channel has async
+ * flushing active.
+ */
+
+ CleanupChannelHandlers(interp, chanPtr);
}
+
+ statePtr->refCount--;
+
return TCL_OK;
}
+
/*
*---------------------------------------------------------------------------
@@ -859,7 +1004,7 @@ Tcl_Channel
Tcl_GetChannel(interp, chanName, modePtr)
Tcl_Interp *interp; /* Interpreter in which to find or create
* the channel. */
- char *chanName; /* The name of the channel. */
+ CONST char *chanName; /* The name of the channel. */
int *modePtr; /* Where to store the mode in which the
* channel was opened? Will contain an ORed
* combination of TCL_READABLE and
@@ -868,7 +1013,7 @@ Tcl_GetChannel(interp, chanName, modePtr)
Channel *chanPtr; /* The actual channel. */
Tcl_HashTable *hTblPtr; /* Hash table of channels. */
Tcl_HashEntry *hPtr; /* Search variable. */
- char *name; /* Translated name. */
+ CONST char *name; /* Translated name. */
/*
* Substitute "stdin", etc. Note that even though we immediately
@@ -937,7 +1082,7 @@ Tcl_GetChannel(interp, chanName, modePtr)
Tcl_Channel
Tcl_CreateChannel(typePtr, chanName, instanceData, mask)
Tcl_ChannelType *typePtr; /* The channel type record. */
- char *chanName; /* Name of channel to record. */
+ CONST char *chanName; /* Name of channel to record. */
ClientData instanceData; /* Instance specific data. */
int mask; /* TCL_READABLE & TCL_WRITABLE to indicate
* if the channel is readable, writable. */
@@ -960,6 +1105,10 @@ Tcl_CreateChannel(typePtr, chanName, instanceData, mask)
assert(sizeof(Tcl_ChannelTypeVersion) == sizeof(Tcl_DriverBlockModeProc*));
+ /*
+ * JH: We could subsequently memset these to 0 to avoid the
+ * numerous assignments to 0/NULL below.
+ */
chanPtr = (Channel *) ckalloc((unsigned) sizeof(Channel));
statePtr = (ChannelState *) ckalloc((unsigned) sizeof(ChannelState));
chanPtr->state = statePtr;
@@ -973,8 +1122,9 @@ Tcl_CreateChannel(typePtr, chanName, instanceData, mask)
*/
if (chanName != (char *) NULL) {
- statePtr->channelName = ckalloc((unsigned) (strlen(chanName) + 1));
- strcpy(statePtr->channelName, chanName);
+ char *tmp = ckalloc((unsigned) (strlen(chanName) + 1));
+ statePtr->channelName = tmp;
+ strcpy(tmp, chanName);
} else {
panic("Tcl_CreateChannel: NULL channel name");
}
@@ -1044,10 +1194,20 @@ Tcl_CreateChannel(typePtr, chanName, instanceData, mask)
* Link the channel into the list of all channels; create an on-exit
* handler if there is not one already, to close off all the channels
* in the list on exit.
+ *
+ * JH: Could call Tcl_SpliceChannel, but need to avoid NULL check.
+ */
+
+ statePtr->nextCSPtr = tsdPtr->firstCSPtr;
+ tsdPtr->firstCSPtr = statePtr;
+
+ /*
+ * TIP #10. Mark the current thread as the one managing the new
+ * channel. Note: 'Tcl_GetCurrentThread' returns sensible
+ * values even for a non-threaded core.
*/
- statePtr->nextCSPtr = tsdPtr->firstCSPtr;
- tsdPtr->firstCSPtr = statePtr;
+ statePtr->managingThread = Tcl_GetCurrentThread ();
/*
* Install this channel in the first empty standard channel slot, if
@@ -1465,6 +1625,32 @@ Tcl_GetChannelInstanceData(chan)
/*
*----------------------------------------------------------------------
*
+ * Tcl_GetChannelThread --
+ *
+ * Given a channel structure, returns the thread managing it.
+ * TIP #10
+ *
+ * Results:
+ * Returns the id of the thread managing the channel.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_ThreadId
+Tcl_GetChannelThread(chan)
+ Tcl_Channel chan; /* The channel to return managing thread for. */
+{
+ Channel *chanPtr = (Channel *) chan; /* The actual channel. */
+
+ return chanPtr->state->managingThread;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_GetChannelType --
*
* Given a channel structure, returns the channel type structure.
@@ -1533,7 +1719,7 @@ Tcl_GetChannelMode(chan)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_GetChannelName(chan)
Tcl_Channel chan; /* The channel for which to return the name. */
{
@@ -1657,6 +1843,17 @@ RecycleBuffer(statePtr, bufPtr, mustDiscard)
}
/*
+ * Only save buffers which are at least as big as the requested
+ * buffersize for the channel. This is to honor dynamic changes
+ * of the buffersize made by the user.
+ */
+
+ if ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize) {
+ ckfree((char *) bufPtr);
+ return;
+ }
+
+ /*
* Only save buffers for the input queue if the channel is readable.
*/
@@ -1865,7 +2062,7 @@ FlushChannel(interp, chanPtr, calledFromAsyncFlush)
toWrite = bufPtr->nextAdded - bufPtr->nextRemoved;
written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData,
- (char *) bufPtr->buf + bufPtr->nextRemoved, toWrite,
+ bufPtr->buf + bufPtr->nextRemoved, toWrite,
&errorCode);
/*
@@ -1916,8 +2113,15 @@ FlushChannel(interp, chanPtr, calledFromAsyncFlush)
} else {
Tcl_SetErrno(errorCode);
if (interp != NULL) {
+
+ /*
+ * Casting away CONST here is safe because the
+ * TCL_VOLATILE flag guarantees CONST treatment
+ * of the Posix error string.
+ */
+
Tcl_SetResult(interp,
- Tcl_PosixError(interp), TCL_VOLATILE);
+ (char *) Tcl_PosixError(interp), TCL_VOLATILE);
}
}
@@ -2012,9 +2216,6 @@ CloseChannel(interp, chanPtr, errorCode)
{
int result = 0; /* Of calling driver close
* operation. */
- ChannelState *prevCSPtr; /* Preceding channel state in list of
- * all states - used to splice a
- * channel out of the list on close. */
ChannelState *statePtr; /* state of the channel stack. */
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
@@ -2059,38 +2260,11 @@ CloseChannel(interp, chanPtr, errorCode)
c = (char) statePtr->outEofChar;
(chanPtr->typePtr->outputProc) (chanPtr->instanceData, &c, 1, &dummy);
}
-#if 0
- /*
- * Remove TCL_READABLE and TCL_WRITABLE from statePtr->flags, so
- * that close callbacks can not do input or output (assuming they
- * squirreled the channel away in their clientData). This also
- * prevents infinite loops if the callback calls any C API that
- * could call FlushChannel.
- */
/*
- * This prevents any data from being flushed from stacked channels.
+ * Remove this channel from of the list of all channels.
*/
- statePtr->flags &= (~(TCL_READABLE|TCL_WRITABLE));
-#endif
-
- /*
- * Splice this channel out of the list of all channels.
- */
-
- if (tsdPtr->firstCSPtr && (statePtr == tsdPtr->firstCSPtr)) {
- tsdPtr->firstCSPtr = statePtr->nextCSPtr;
- } else {
- for (prevCSPtr = tsdPtr->firstCSPtr;
- prevCSPtr && (prevCSPtr->nextCSPtr != statePtr);
- prevCSPtr = prevCSPtr->nextCSPtr) {
- /* Empty loop body. */
- }
- if (prevCSPtr == (ChannelState *) NULL) {
- panic("FlushChannel: damaged channel list");
- }
- prevCSPtr->nextCSPtr = statePtr->nextCSPtr;
- }
+ Tcl_CutChannel((Tcl_Channel) chanPtr);
/*
* Close and free the channel driver state.
@@ -2111,7 +2285,7 @@ CloseChannel(interp, chanPtr, errorCode)
if (chanPtr == statePtr->bottomChanPtr) {
if (statePtr->channelName != (char *) NULL) {
- ckfree(statePtr->channelName);
+ ckfree((char *) statePtr->channelName);
statePtr->channelName = NULL;
}
@@ -2148,23 +2322,6 @@ CloseChannel(interp, chanPtr, errorCode)
*/
if (chanPtr->downChanPtr != (Channel *) NULL) {
-#if 0
- int code = TCL_OK;
-
- while (chanPtr->downChanPtr != (Channel *) NULL) {
- /*
- * Unwind the state of the transformation, and then restore the
- * state of (unstack) the underlying channel into the TOP channel
- * structure.
- */
- code = Tcl_UnstackChannel(interp, (Tcl_Channel) chanPtr);
- if (code == TCL_ERROR) {
- errorCode = Tcl_GetErrno();
- break;
- }
- chanPtr = chanPtr->downChanPtr;
- }
-#else
Channel *downChanPtr = chanPtr->downChanPtr;
statePtr->nextCSPtr = tsdPtr->firstCSPtr;
@@ -2176,15 +2333,18 @@ CloseChannel(interp, chanPtr, errorCode)
Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC);
return Tcl_Close(interp, (Tcl_Channel) downChanPtr);
-#endif
}
/*
* There is only the TOP Channel, so we free the remaining
- * pointers we have and then ourselves.
+ * pointers we have and then ourselves. Since this is the
+ * last of the channels in the stack, make sure to free the
+ * ChannelState structure associated with it. We use
+ * Tcl_EventuallyFree to allow for any last
*/
chanPtr->typePtr = NULL;
+ Tcl_EventuallyFree((ClientData) statePtr, TCL_DYNAMIC);
Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC);
return errorCode;
@@ -2193,6 +2353,118 @@ CloseChannel(interp, chanPtr, errorCode)
/*
*----------------------------------------------------------------------
*
+ * Tcl_CutChannel --
+ *
+ * Removes a channel from the (thread-)global list of all channels
+ * (in that thread). This is actually the statePtr for the stack
+ * of channel.
+ *
+ * Results:
+ * Nothing.
+ *
+ * Side effects:
+ * Resets the field 'nextCSPtr' of the specified channel state to NULL.
+ *
+ * NOTE:
+ * The channel to splice out of the list must not be referenced
+ * in any interpreter. This is something this procedure cannot
+ * check (despite the refcount) because the caller usually wants
+ * fiddle with the channel (like transfering it to a different
+ * thread) and thus keeps the refcount artifically high to prevent
+ * its destruction.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_CutChannel(chan)
+ Tcl_Channel chan; /* The channel being removed. Must
+ * not be referenced in any
+ * interpreter. */
+{
+ ThreadSpecificData* tsdPtr = TCL_TSD_INIT(&dataKey);
+ ChannelState *prevCSPtr; /* Preceding channel state in list of
+ * all states - used to splice a
+ * channel out of the list on close. */
+ ChannelState *statePtr = ((Channel *) chan)->state;
+ /* state of the channel stack. */
+
+ /*
+ * Remove this channel from of the list of all channels
+ * (in the current thread).
+ */
+
+ if (tsdPtr->firstCSPtr && (statePtr == tsdPtr->firstCSPtr)) {
+ tsdPtr->firstCSPtr = statePtr->nextCSPtr;
+ } else {
+ for (prevCSPtr = tsdPtr->firstCSPtr;
+ prevCSPtr && (prevCSPtr->nextCSPtr != statePtr);
+ prevCSPtr = prevCSPtr->nextCSPtr) {
+ /* Empty loop body. */
+ }
+ if (prevCSPtr == (ChannelState *) NULL) {
+ panic("FlushChannel: damaged channel list");
+ }
+ prevCSPtr->nextCSPtr = statePtr->nextCSPtr;
+ }
+
+ statePtr->nextCSPtr = (ChannelState *) NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_SpliceChannel --
+ *
+ * Adds a channel to the (thread-)global list of all channels
+ * (in that thread). Expects that the field 'nextChanPtr' in
+ * the channel is set to NULL.
+ *
+ * Results:
+ * Nothing.
+ *
+ * Side effects:
+ * Nothing.
+ *
+ * NOTE:
+ * The channel to add to the list must not be referenced in any
+ * interpreter. This is something this procedure cannot check
+ * (despite the refcount) because the caller usually wants figgle
+ * with the channel (like transfering it to a different thread)
+ * and thus keeps the refcount artifically high to prevent its
+ * destruction.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_SpliceChannel(chan)
+ Tcl_Channel chan; /* The channel being added. Must
+ * not be referenced in any
+ * interpreter. */
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ ChannelState *statePtr = ((Channel *) chan)->state;
+
+ if (statePtr->nextCSPtr != (ChannelState *) NULL) {
+ panic("Tcl_SpliceChannel: trying to add channel used in different list");
+ }
+
+ statePtr->nextCSPtr = tsdPtr->firstCSPtr;
+ tsdPtr->firstCSPtr = statePtr;
+
+ /*
+ * TIP #10. Mark the current thread as the new one managing this
+ * channel. Note: 'Tcl_GetCurrentThread' returns sensible
+ * values even for a non-threaded core.
+ */
+
+ statePtr->managingThread = Tcl_GetCurrentThread ();
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_Close --
*
* Closes a channel.
@@ -2220,15 +2492,11 @@ Tcl_Close(interp, chan)
* not be referenced in any
* interpreter. */
{
- ChannelHandler *chPtr, *chNext; /* Iterate over channel handlers. */
CloseCallback *cbPtr; /* Iterate over close callbacks
* for this channel. */
- EventScriptRecord *ePtr, *eNextPtr; /* Iterate over eventscript records. */
Channel *chanPtr; /* The real IO channel. */
ChannelState *statePtr; /* State of real IO channel. */
int result; /* Of calling FlushChannel. */
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- NextChannelHandler *nhPtr;
if (chan == (Tcl_Channel) NULL) {
return TCL_OK;
@@ -2257,6 +2525,100 @@ Tcl_Close(interp, chan)
}
/*
+ * When the channel has an escape sequence driven encoding such as
+ * iso2022, the terminated escape sequence must write to the buffer.
+ */
+ if ((statePtr->encoding != NULL) && (statePtr->curOutPtr != NULL)
+ && (CheckChannelErrors(statePtr, TCL_WRITABLE) == 0)) {
+ statePtr->outputEncodingFlags |= TCL_ENCODING_END;
+ WriteChars(chanPtr, "", 0);
+ }
+
+ Tcl_ClearChannelHandlers(chan);
+
+ /*
+ * Invoke the registered close callbacks and delete their records.
+ */
+
+ while (statePtr->closeCbPtr != (CloseCallback *) NULL) {
+ cbPtr = statePtr->closeCbPtr;
+ statePtr->closeCbPtr = cbPtr->nextPtr;
+ (cbPtr->proc) (cbPtr->clientData);
+ ckfree((char *) cbPtr);
+ }
+
+ /*
+ * Ensure that the last output buffer will be flushed.
+ */
+
+ if ((statePtr->curOutPtr != (ChannelBuffer *) NULL) &&
+ (statePtr->curOutPtr->nextAdded > statePtr->curOutPtr->nextRemoved)) {
+ statePtr->flags |= BUFFER_READY;
+ }
+
+ /*
+ * If this channel supports it, close the read side, since we don't need it
+ * anymore and this will help avoid deadlocks on some channel types.
+ */
+
+ if (chanPtr->typePtr->closeProc == TCL_CLOSE2PROC) {
+ result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp,
+ TCL_CLOSE_READ);
+ } else {
+ result = 0;
+ }
+
+ /*
+ * The call to FlushChannel will flush any queued output and invoke
+ * the close function of the channel driver, or it will set up the
+ * channel to be flushed and closed asynchronously.
+ */
+
+ statePtr->flags |= CHANNEL_CLOSED;
+ if ((FlushChannel(interp, chanPtr, 0) != 0) || (result != 0)) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_ClearChannelHandlers --
+ *
+ * Removes all channel handlers and event scripts from the channel,
+ * cancels all background copies involving the channel and any interest
+ * in events.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * See above. Deallocates memory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_ClearChannelHandlers (channel)
+ Tcl_Channel channel;
+{
+ ChannelHandler *chPtr, *chNext; /* Iterate over channel handlers. */
+ EventScriptRecord *ePtr, *eNextPtr; /* Iterate over eventscript records. */
+ Channel *chanPtr; /* The real IO channel. */
+ ChannelState *statePtr; /* State of real IO channel. */
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ NextChannelHandler *nhPtr;
+
+ /*
+ * This operation should occur at the top of a channel stack.
+ */
+
+ chanPtr = (Channel *) channel;
+ statePtr = chanPtr->state;
+ chanPtr = statePtr->topChanPtr;
+
+ /*
* Remove any references to channel handlers for this channel that
* may be about to be invoked.
*/
@@ -2310,50 +2672,6 @@ Tcl_Close(interp, chan)
ckfree((char *) ePtr);
}
statePtr->scriptRecordPtr = (EventScriptRecord *) NULL;
-
- /*
- * Invoke the registered close callbacks and delete their records.
- */
-
- while (statePtr->closeCbPtr != (CloseCallback *) NULL) {
- cbPtr = statePtr->closeCbPtr;
- statePtr->closeCbPtr = cbPtr->nextPtr;
- (cbPtr->proc) (cbPtr->clientData);
- ckfree((char *) cbPtr);
- }
-
- /*
- * Ensure that the last output buffer will be flushed.
- */
-
- if ((statePtr->curOutPtr != (ChannelBuffer *) NULL) &&
- (statePtr->curOutPtr->nextAdded > statePtr->curOutPtr->nextRemoved)) {
- statePtr->flags |= BUFFER_READY;
- }
-
- /*
- * If this channel supports it, close the read side, since we don't need it
- * anymore and this will help avoid deadlocks on some channel types.
- */
-
- if (chanPtr->typePtr->closeProc == TCL_CLOSE2PROC) {
- result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp,
- TCL_CLOSE_READ);
- } else {
- result = 0;
- }
-
- /*
- * The call to FlushChannel will flush any queued output and invoke
- * the close function of the channel driver, or it will set up the
- * channel to be flushed and closed asynchronously.
- */
-
- statePtr->flags |= CHANNEL_CLOSED;
- if ((FlushChannel(interp, chanPtr, 0) != 0) || (result != 0)) {
- return TCL_ERROR;
- }
- return TCL_OK;
}
/*
@@ -2364,7 +2682,10 @@ Tcl_Close(interp, chan)
* Puts a sequence of bytes into an output buffer, may queue the
* buffer for output if it gets full, and also remembers whether the
* current buffer is ready e.g. if it contains a newline and we are in
- * line buffering mode.
+ * line buffering mode. Compensates stacking, i.e. will redirect the
+ * data from the specified channel to the topmost channel in a stack.
+ *
+ * No encoding conversions are applied to the bytes being read.
*
* Results:
* The number of bytes written or -1 in case of error. If -1,
@@ -2380,7 +2701,7 @@ Tcl_Close(interp, chan)
int
Tcl_Write(chan, src, srcLen)
Tcl_Channel chan; /* The channel to buffer output for. */
- char *src; /* Data to queue in output buffer. */
+ CONST char *src; /* Data to queue in output buffer. */
int srcLen; /* Length of data in bytes, or < 0 for
* strlen(). */
{
@@ -2411,7 +2732,10 @@ Tcl_Write(chan, src, srcLen)
* Puts a sequence of bytes into an output buffer, may queue the
* buffer for output if it gets full, and also remembers whether the
* current buffer is ready e.g. if it contains a newline and we are in
- * line buffering mode.
+ * line buffering mode. Writes directly to the driver of the channel,
+ * does not compensate for stacking.
+ *
+ * No encoding conversions are applied to the bytes being read.
*
* Results:
* The number of bytes written or -1 in case of error. If -1,
@@ -2427,7 +2751,7 @@ Tcl_Write(chan, src, srcLen)
int
Tcl_WriteRaw(chan, src, srcLen)
Tcl_Channel chan; /* The channel to buffer output for. */
- char *src; /* Data to queue in output buffer. */
+ CONST char *src; /* Data to queue in output buffer. */
int srcLen; /* Length of data in bytes, or < 0 for
* strlen(). */
{
@@ -2467,7 +2791,8 @@ Tcl_WriteRaw(chan, src, srcLen)
* using the channel's current encoding, may queue the buffer for
* output if it gets full, and also remembers whether the current
* buffer is ready e.g. if it contains a newline and we are in
- * line buffering mode.
+ * line buffering mode. Compensates stacking, i.e. will redirect the
+ * data from the specified channel to the topmost channel in a stack.
*
* Results:
* The number of bytes written or -1 in case of error. If -1,
@@ -2487,18 +2812,55 @@ Tcl_WriteChars(chan, src, len)
int len; /* Length of string in bytes, or < 0 for
* strlen(). */
{
- /*
- * Always use the topmost channel of the stack
- */
- Channel *chanPtr;
ChannelState *statePtr; /* state info for channel */
statePtr = ((Channel *) chan)->state;
- chanPtr = statePtr->topChanPtr;
if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
return -1;
}
+
+ return DoWriteChars ((Channel*) chan, src, len);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DoWriteChars --
+ *
+ * Takes a sequence of UTF-8 characters and converts them for output
+ * using the channel's current encoding, may queue the buffer for
+ * output if it gets full, and also remembers whether the current
+ * buffer is ready e.g. if it contains a newline and we are in
+ * line buffering mode. Compensates stacking, i.e. will redirect the
+ * data from the specified channel to the topmost channel in a stack.
+ *
+ * Results:
+ * The number of bytes written or -1 in case of error. If -1,
+ * Tcl_GetErrno will return the error code.
+ *
+ * Side effects:
+ * May buffer up output and may cause output to be produced on the
+ * channel.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DoWriteChars(chanPtr, src, len)
+ Channel* chanPtr; /* The channel to buffer output for. */
+ CONST char *src; /* UTF-8 characters to queue in output buffer. */
+ int len; /* Length of string in bytes, or < 0 for
+ * strlen(). */
+{
+ /*
+ * Always use the topmost channel of the stack
+ */
+ ChannelState *statePtr; /* state info for channel */
+
+ statePtr = chanPtr->state;
+ chanPtr = statePtr->topChanPtr;
+
if (len < 0) {
len = strlen(src);
}
@@ -2603,7 +2965,7 @@ WriteBytes(chanPtr, src, srcLen)
ChannelState *statePtr = chanPtr->state; /* state info for channel */
ChannelBuffer *bufPtr;
char *dst;
- int dstLen, dstMax, sawLF, savedLF, total, toWrite;
+ int dstMax, sawLF, savedLF, total, dstLen, toWrite;
total = 0;
sawLF = 0;
@@ -2691,8 +3053,9 @@ WriteChars(chanPtr, src, srcLen)
ChannelState *statePtr = chanPtr->state; /* state info for channel */
ChannelBuffer *bufPtr;
char *dst, *stage;
- int saved, savedLF, sawLF, total, toWrite, flags;
- int dstWrote, dstLen, stageLen, stageMax, stageRead;
+ int saved, savedLF, sawLF, total, dstLen, stageMax, dstWrote;
+ int stageLen, toWrite, stageRead, endEncoding, result;
+ int consumedSomething;
Tcl_Encoding encoding;
char safe[BUFFER_PADDING];
@@ -2703,11 +3066,19 @@ WriteChars(chanPtr, src, srcLen)
encoding = statePtr->encoding;
/*
+ * Write the terminated escape sequence even if srcLen is 0.
+ */
+
+ endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);
+
+ /*
* Loop over all UTF-8 characters in src, storing them in staging buffer
* with proper EOL translation.
*/
- while (srcLen + savedLF > 0) {
+ consumedSomething = 1;
+ while (consumedSomething && (srcLen + savedLF + endEncoding > 0)) {
+ consumedSomething = 0;
stage = statePtr->outputStage;
stageMax = statePtr->bufSize;
stageLen = stageMax;
@@ -2742,17 +3113,12 @@ WriteChars(chanPtr, src, srcLen)
src += toWrite;
srcLen -= toWrite;
- flags = statePtr->outputEncodingFlags;
- if (srcLen == 0) {
- flags |= TCL_ENCODING_END;
- }
-
/*
* Loop over all UTF-8 characters in staging buffer, converting them
* to external encoding, storing them in output buffer.
*/
- while (stageLen + saved > 0) {
+ while (stageLen + saved + endEncoding > 0) {
bufPtr = statePtr->curOutPtr;
if (bufPtr == NULL) {
bufPtr = AllocChannelBuffer(statePtr->bufSize);
@@ -2775,10 +3141,31 @@ WriteChars(chanPtr, src, srcLen)
saved = 0;
}
- Tcl_UtfToExternal(NULL, encoding, stage, stageLen, flags,
+ result = Tcl_UtfToExternal(NULL, encoding, stage, stageLen,
+ statePtr->outputEncodingFlags,
&statePtr->outputEncodingState, dst,
dstLen + BUFFER_PADDING, &stageRead, &dstWrote, NULL);
- if (stageRead + dstWrote == 0) {
+
+ /* Fix for SF #506297, reported by Martin Forssen
+ * <ruric@users.sourceforge.net>.
+ *
+ * The encoding chosen in the script exposing the bug writes out
+ * three intro characters when TCL_ENCODING_START is set, but does
+ * not consume any input as TCL_ENCODING_END is cleared. As some
+ * output was generated the enclosing loop calls UtfToExternal
+ * again, again with START set. Three more characters in the out
+ * and still no use of input ... To break this infinite loop we
+ * remove TCL_ENCODING_START from the set of flags after the first
+ * call (no condition is required, the later calls remove an unset
+ * flag, which is a no-op). This causes the subsequent calls to
+ * UtfToExternal to consume and convert the actual input.
+ */
+
+ statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
+ /*
+ * The following code must be executed only when result is not 0.
+ */
+ if (result && ((stageRead + dstWrote) == 0)) {
/*
* We have an incomplete UTF-8 character at the end of the
* staging buffer. It will get moved to the beginning of the
@@ -2814,8 +3201,29 @@ WriteChars(chanPtr, src, srcLen)
stage += stageRead;
stageLen -= stageRead;
sawLF = 0;
+
+ consumedSomething = 1;
+
+ /*
+ * If all translated characters are written to the buffer,
+ * endEncoding is set to 0 because the escape sequence may be
+ * output.
+ */
+
+ if ((stageLen + saved == 0) && (result == 0)) {
+ endEncoding = 0;
+ }
}
}
+
+ /* If nothing was written and it happened because there was no progress
+ * in the UTF conversion, we throw an error.
+ */
+
+ if (!consumedSomething && (total == 0)) {
+ Tcl_SetErrno (EINVAL);
+ return -1;
+ }
return total;
}
@@ -3075,11 +3483,10 @@ Tcl_GetsObj(chan, objPtr)
Channel *chanPtr = (Channel *) chan;
ChannelState *statePtr = chanPtr->state; /* state info for channel */
ChannelBuffer *bufPtr;
- int inEofChar, skip, copiedTotal;
+ int inEofChar, skip, copiedTotal, oldLength, oldFlags, oldRemoved;
Tcl_Encoding encoding;
char *dst, *dstEnd, *eol, *eof;
Tcl_EncodingState oldState;
- int oldLength, oldFlags, oldRemoved;
/*
* This operation should occur at the top of a channel stack.
@@ -3288,13 +3695,13 @@ Tcl_GetsObj(chan, objPtr)
if (statePtr->flags & CHANNEL_EOF) {
skip = 0;
eol = dstEnd;
- if (eol == objPtr->bytes) {
+ if (eol == objPtr->bytes + oldLength) {
/*
- * If we didn't produce any bytes before encountering EOF,
+ * If we didn't append any bytes before encountering EOF,
* caller needs to see -1.
*/
- Tcl_SetObjLength(objPtr, 0);
+ Tcl_SetObjLength(objPtr, oldLength);
CommonGetsCleanup(chanPtr, encoding);
copiedTotal = -1;
goto done;
@@ -3317,8 +3724,9 @@ Tcl_GetsObj(chan, objPtr)
statePtr->inputEncodingState = gs.state;
Tcl_ExternalToUtf(NULL, gs.encoding, bufPtr->buf + bufPtr->nextRemoved,
gs.rawRead, statePtr->inputEncodingFlags,
- &statePtr->inputEncodingState, dst, eol - dst + skip + TCL_UTF_MAX,
- &gs.rawRead, NULL, &gs.charsWrote);
+ &statePtr->inputEncodingState, dst,
+ eol - dst + skip + TCL_UTF_MAX, &gs.rawRead, NULL,
+ &gs.charsWrote);
bufPtr->nextRemoved += gs.rawRead;
/*
@@ -3409,7 +3817,7 @@ FilterInputBytes(chanPtr, gsPtr)
char *dst;
int offset, toRead, dstNeeded, spaceLeft, result, rawLen, length;
Tcl_Obj *objPtr;
-#define ENCODING_LINESIZE 30 /* Lower bound on how many bytes to convert
+#define ENCODING_LINESIZE 20 /* Lower bound on how many bytes to convert
* at a time. Since we don't know a priori
* how many bytes of storage this many source
* bytes will use, we actually need at least
@@ -3438,7 +3846,7 @@ FilterInputBytes(chanPtr, gsPtr)
* seen EOL. Need to read more bytes from the channel device.
* Side effect is to allocate another channel buffer.
*/
-
+
read:
if (statePtr->flags & CHANNEL_BLOCKED) {
if (statePtr->flags & CHANNEL_NONBLOCKING) {
@@ -3491,7 +3899,14 @@ FilterInputBytes(chanPtr, gsPtr)
result = Tcl_ExternalToUtf(NULL, gsPtr->encoding, raw, rawLen,
statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
dst, spaceLeft, &gsPtr->rawRead, &gsPtr->bytesWrote,
- &gsPtr->charsWrote);
+ &gsPtr->charsWrote);
+
+ /*
+ * Make sure that if we go through 'gets', that we reset the
+ * TCL_ENCODING_START flag still. [Bug #523988]
+ */
+ statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
+
if (result == TCL_CONVERT_MULTIBYTE) {
/*
* The last few bytes in this channel buffer were the start of a
@@ -3762,7 +4177,7 @@ Tcl_Read(chan, dst, bytesToRead)
int
Tcl_ReadRaw(chan, bufPtr, bytesToRead)
Tcl_Channel chan; /* The channel from which to read. */
- char *bufPtr; /* Where to store input read. */
+ char *bufPtr; /* Where to store input read. */
int bytesToRead; /* Maximum number of bytes to read. */
{
Channel *chanPtr = (Channel *) chan;
@@ -3806,17 +4221,23 @@ Tcl_ReadRaw(chan, bufPtr, bytesToRead)
statePtr->flags &= (~(CHANNEL_BLOCKED));
}
- /*
- * Now go to the driver to get as much as is possible to
- * fill the remaining request. Do all the error handling
- * by ourselves. The code was stolen from 'GetInput' and
- * slightly adapted (different return value here).
- *
- * The case of 'bytesToRead == 0' at this point cannot happen.
- */
-
- nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
+ if ((statePtr->flags & CHANNEL_TIMER_FEV) &&
+ (statePtr->flags & CHANNEL_NONBLOCKING)) {
+ nread = -1;
+ result = EWOULDBLOCK;
+ } else {
+ /*
+ * Now go to the driver to get as much as is possible to
+ * fill the remaining request. Do all the error handling
+ * by ourselves. The code was stolen from 'GetInput' and
+ * slightly adapted (different return value here).
+ *
+ * The case of 'bytesToRead == 0' at this point cannot happen.
+ */
+
+ nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
bufPtr + copied, bytesToRead - copied, &result);
+ }
if (nread > 0) {
/*
* If we get a short read, signal up that we may be
@@ -3893,12 +4314,8 @@ Tcl_ReadChars(chan, objPtr, toRead, appendFlag)
* of the object. */
{
- Channel *chanPtr = (Channel *) chan;
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- ChannelBuffer *bufPtr;
- int offset, factor, copied, copiedNow, result;
- Tcl_Encoding encoding;
-#define UTF_EXPANSION_FACTOR 1024
+ Channel* chanPtr = (Channel *) chan;
+ ChannelState* statePtr = chanPtr->state; /* state info for channel */
/*
* This operation should occur at the top of a channel stack.
@@ -3907,12 +4324,64 @@ Tcl_ReadChars(chan, objPtr, toRead, appendFlag)
chanPtr = statePtr->topChanPtr;
if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
- copied = -1;
- goto done;
+ /*
+ * Update the notifier state so we don't block while there is still
+ * data in the buffers.
+ */
+ UpdateInterest(chanPtr);
+ return -1;
}
+ return DoReadChars (chanPtr, objPtr, toRead, appendFlag);
+}
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DoReadChars --
+ *
+ * Reads from the channel until the requested number of characters
+ * have been seen, EOF is seen, or the channel would block. EOL
+ * and EOF translation is done. If reading binary data, the raw
+ * bytes are wrapped in a Tcl byte array object. Otherwise, the raw
+ * bytes are converted to UTF-8 using the channel's current encoding
+ * and stored in a Tcl string object.
+ *
+ * Results:
+ * The number of characters read, or -1 on error. Use Tcl_GetErrno()
+ * to retrieve the error code for the error that occurred.
+ *
+ * Side effects:
+ * May cause input to be buffered.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static int
+DoReadChars(chanPtr, objPtr, toRead, appendFlag)
+ Channel* chanPtr; /* The channel to read. */
+ Tcl_Obj *objPtr; /* Input data is stored in this object. */
+ int toRead; /* Maximum number of characters to store,
+ * or -1 to read all available data (up to EOF
+ * or when channel blocks). */
+ int appendFlag; /* If non-zero, data read from the channel
+ * will be appended to the object. Otherwise,
+ * the data will replace the existing contents
+ * of the object. */
+
+{
+ ChannelState *statePtr = chanPtr->state; /* state info for channel */
+ ChannelBuffer *bufPtr;
+ int offset, factor, copied, copiedNow, result;
+ Tcl_Encoding encoding;
+#define UTF_EXPANSION_FACTOR 1024
+
+ /*
+ * This operation should occur at the top of a channel stack.
+ */
+
+ chanPtr = statePtr->topChanPtr;
encoding = statePtr->encoding;
- factor = UTF_EXPANSION_FACTOR;
+ factor = UTF_EXPANSION_FACTOR;
if (appendFlag == 0) {
if (encoding == NULL) {
@@ -3951,7 +4420,7 @@ Tcl_ReadChars(chan, objPtr, toRead, appendFlag)
RecycleBuffer(statePtr, bufPtr, 0);
statePtr->inQueueHead = nextPtr;
if (nextPtr == NULL) {
- statePtr->inQueueTail = nextPtr;
+ statePtr->inQueueTail = NULL;
}
}
}
@@ -4023,25 +4492,25 @@ Tcl_ReadChars(chan, objPtr, toRead, appendFlag)
static int
ReadBytes(statePtr, objPtr, bytesToRead, offsetPtr)
ChannelState *statePtr; /* State of the channel to read. */
- int bytesToRead; /* Maximum number of characters to store,
- * or < 0 to get all available characters.
- * Characters are obtained from the first
- * buffer in the queue -- even if this number
- * is larger than the number of characters
- * available in the first buffer, only the
- * characters from the first buffer are
- * returned. */
Tcl_Obj *objPtr; /* Input data is appended to this ByteArray
* object. Its length is how much space
* has been allocated to hold data, not how
* many bytes of data have been stored in the
* object. */
+ int bytesToRead; /* Maximum number of bytes to store,
+ * or < 0 to get all available bytes.
+ * Bytes are obtained from the first
+ * buffer in the queue -- even if this number
+ * is larger than the number of bytes
+ * available in the first buffer, only the
+ * bytes from the first buffer are
+ * returned. */
int *offsetPtr; /* On input, contains how many bytes of
* objPtr have been used to hold data. On
* output, filled with how many bytes are now
* being used. */
{
- int toRead, srcLen, srcRead, dstWrote, offset, length;
+ int toRead, srcLen, offset, length, srcRead, dstWrote;
ChannelBuffer *bufPtr;
char *src, *dst;
@@ -4127,6 +4596,10 @@ ReadBytes(statePtr, objPtr, bytesToRead, offsetPtr)
static int
ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr)
ChannelState *statePtr; /* State of channel to read. */
+ Tcl_Obj *objPtr; /* Input data is appended to this object.
+ * objPtr->length is how much space has been
+ * allocated to hold data, not how many bytes
+ * of data have been stored in the object. */
int charsToRead; /* Maximum number of characters to store,
* or -1 to get all available characters.
* Characters are obtained from the first
@@ -4135,10 +4608,6 @@ ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr)
* available in the first buffer, only the
* characters from the first buffer are
* returned. */
- Tcl_Obj *objPtr; /* Input data is appended to this object.
- * objPtr->length is how much space has been
- * allocated to hold data, not how many bytes
- * of data have been stored in the object. */
int *offsetPtr; /* On input, contains how many bytes of
* objPtr have been used to hold data. On
* output, filled with how many bytes are now
@@ -4149,8 +4618,8 @@ ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr)
* UTF-8. On output, contains another guess
* based on the data seen so far. */
{
- int toRead, factor, offset, spaceLeft, length;
- int srcLen, srcRead, dstNeeded, dstRead, dstWrote, numChars;
+ int toRead, factor, offset, spaceLeft, length, srcLen, dstNeeded;
+ int srcRead, dstWrote, numChars, dstRead;
ChannelBuffer *bufPtr;
char *src, *dst;
Tcl_EncodingState oldState;
@@ -4163,7 +4632,7 @@ ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr)
srcLen = bufPtr->nextAdded - bufPtr->nextRemoved;
toRead = charsToRead;
- if ((unsigned) toRead > (unsigned) srcLen) {
+ if ((unsigned)toRead > (unsigned)srcLen) {
toRead = srcLen;
}
@@ -4245,13 +4714,23 @@ ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr)
nextPtr = bufPtr->nextPtr;
if (nextPtr == NULL) {
- /*
- * There isn't enough data in the buffers to complete the next
- * character, so we need to wait for more data before the next
- * file event can be delivered.
- */
+ if (srcLen > 0) {
+ /*
+ * There isn't enough data in the buffers to complete the next
+ * character, so we need to wait for more data before the next
+ * file event can be delivered.
+ *
+ * SF #478856.
+ *
+ * The exception to this is if the input buffer was
+ * completely empty before we tried to convert its
+ * contents. Nothing in, nothing out, and no incomplete
+ * character data. The conversion before the current one
+ * was complete.
+ */
- statePtr->flags |= CHANNEL_NEED_MORE_DATA;
+ statePtr->flags |= CHANNEL_NEED_MORE_DATA;
+ }
return -1;
}
nextPtr->nextRemoved -= srcLen;
@@ -4266,7 +4745,9 @@ ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr)
if (TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead) != 0) {
/*
* Hit EOF char. How many bytes of src correspond to where the
- * EOF was located in dst?
+ * EOF was located in dst? Run the conversion again with an
+ * output buffer just big enough to hold the data so we can
+ * get the correct value for srcRead.
*/
if (dstWrote == 0) {
@@ -4292,7 +4773,7 @@ ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr)
* Got too many chars.
*/
- char *eof;
+ CONST char *eof;
eof = Tcl_UtfAtIndex(dst, toRead);
statePtr->inputEncodingState = oldState;
@@ -4505,7 +4986,7 @@ TranslateInputEOL(statePtr, dstStart, srcStart, dstLenPtr, srcLenPtr)
int
Tcl_Ungets(chan, str, len, atEnd)
Tcl_Channel chan; /* The channel for which to add the input. */
- char *str; /* The input itself. */
+ CONST char *str; /* The input itself. */
int len; /* The length of the input. */
int atEnd; /* If non-zero, add at end of queue; otherwise
* add at head of queue. */
@@ -4754,12 +5235,39 @@ GetInput(chanPtr)
} else {
bufPtr = statePtr->saveInBufPtr;
statePtr->saveInBufPtr = NULL;
+
+ /*
+ * Check the actual buffersize against the requested
+ * buffersize. Buffers which are smaller than requested are
+ * squashed. This is done to honor dynamic changes of the
+ * buffersize made by the user.
+ */
+
+ if ((bufPtr != NULL) && ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize)) {
+ ckfree((char *) bufPtr);
+ bufPtr = NULL;
+ }
+
if (bufPtr == NULL) {
bufPtr = AllocChannelBuffer(statePtr->bufSize);
}
bufPtr->nextPtr = (ChannelBuffer *) NULL;
- toRead = statePtr->bufSize;
+ /* SF #427196: Use the actual size of the buffer to determine
+ * the number of bytes to read from the channel and not the
+ * size for new buffers. They can be different if the
+ * buffersize was changed between reads.
+ *
+ * Note: This affects performance negatively if the buffersize
+ * was extended but this small buffer is reused for all
+ * subsequent reads. The system never uses buffers with the
+ * requested bigger size in that case. An adjunct patch could
+ * try and delete all unused buffers it encounters and which
+ * are smaller than the formally requested buffersize.
+ */
+
+ toRead = bufPtr->bufLength - bufPtr->nextAdded;
+
if (statePtr->inQueueTail == NULL) {
statePtr->inQueueHead = bufPtr;
} else {
@@ -4767,7 +5275,7 @@ GetInput(chanPtr)
}
statePtr->inQueueTail = bufPtr;
}
-
+
/*
* If EOF is set, we should avoid calling the driver because on some
* platforms it is impossible to read from a device after EOF.
@@ -4777,8 +5285,14 @@ GetInput(chanPtr)
return 0;
}
- nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
- bufPtr->buf + bufPtr->nextAdded, toRead, &result);
+ if ((statePtr->flags & CHANNEL_TIMER_FEV) &&
+ (statePtr->flags & CHANNEL_NONBLOCKING)) {
+ nread = -1;
+ result = EWOULDBLOCK;
+ } else {
+ nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
+ bufPtr->buf + bufPtr->nextAdded, toRead, &result);
+ }
if (nread > 0) {
bufPtr->nextAdded += nread;
@@ -4803,7 +5317,7 @@ GetInput(chanPtr)
}
Tcl_SetErrno(result);
return result;
- }
+ }
return 0;
}
@@ -4825,24 +5339,24 @@ GetInput(chanPtr)
*----------------------------------------------------------------------
*/
-int
+Tcl_WideInt
Tcl_Seek(chan, offset, mode)
Tcl_Channel chan; /* The channel on which to seek. */
- int offset; /* Offset to seek to. */
+ Tcl_WideInt offset; /* Offset to seek to. */
int mode; /* Relative to which location to seek? */
{
Channel *chanPtr = (Channel *) chan; /* The real IO channel. */
ChannelState *statePtr = chanPtr->state; /* state info for channel */
- ChannelBuffer *bufPtr;
int inputBuffered, outputBuffered;
+ /* # bytes held in buffers. */
int result; /* Of device driver operations. */
- int curPos; /* Position on the device. */
+ Tcl_WideInt curPos; /* Position on the device. */
int wasAsync; /* Was the channel nonblocking before the
* seek operation? If so, must restore to
* nonblocking mode after the seek. */
if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) {
- return -1;
+ return Tcl_LongAsWide(-1);
}
/*
@@ -4852,7 +5366,9 @@ Tcl_Seek(chan, offset, mode)
* registered in an interpreter.
*/
- if (CheckForDeadChannel(NULL, statePtr)) return -1;
+ if (CheckForDeadChannel(NULL, statePtr)) {
+ return Tcl_LongAsWide(-1);
+ }
/*
* This operation should occur at the top of a channel stack.
@@ -4867,7 +5383,7 @@ Tcl_Seek(chan, offset, mode)
if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
Tcl_SetErrno(EINVAL);
- return -1;
+ return Tcl_LongAsWide(-1);
}
/*
@@ -4875,37 +5391,12 @@ Tcl_Seek(chan, offset, mode)
* output is buffered, cannot compute the current position.
*/
- for (bufPtr = statePtr->inQueueHead, inputBuffered = 0;
- bufPtr != (ChannelBuffer *) NULL;
- bufPtr = bufPtr->nextPtr) {
- inputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
- }
-
- /*
- * Don't forget the bytes in the topmost pushback area.
- */
-
- for (bufPtr = statePtr->topChanPtr->inQueueHead;
- bufPtr != (ChannelBuffer *) NULL;
- bufPtr = bufPtr->nextPtr) {
- inputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
- }
-
- for (bufPtr = statePtr->outQueueHead, outputBuffered = 0;
- bufPtr != (ChannelBuffer *) NULL;
- bufPtr = bufPtr->nextPtr) {
- outputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
- }
- if ((statePtr->curOutPtr != (ChannelBuffer *) NULL) &&
- (statePtr->curOutPtr->nextAdded > statePtr->curOutPtr->nextRemoved)) {
- statePtr->flags |= BUFFER_READY;
- outputBuffered +=
- (statePtr->curOutPtr->nextAdded - statePtr->curOutPtr->nextRemoved);
- }
+ inputBuffered = Tcl_InputBuffered(chan);
+ outputBuffered = Tcl_OutputBuffered(chan);
if ((inputBuffered != 0) && (outputBuffered != 0)) {
Tcl_SetErrno(EFAULT);
- return -1;
+ return Tcl_LongAsWide(-1);
}
/*
@@ -4944,7 +5435,7 @@ Tcl_Seek(chan, offset, mode)
wasAsync = 1;
result = StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING);
if (result != 0) {
- return -1;
+ return Tcl_LongAsWide(-1);
}
statePtr->flags &= (~(CHANNEL_NONBLOCKING));
if (statePtr->flags & BG_FLUSH_SCHEDULED) {
@@ -4966,14 +5457,26 @@ Tcl_Seek(chan, offset, mode)
/*
* Now seek to the new position in the channel as requested by the
- * caller.
+ * caller. Note that we prefer the wideSeekProc if that is
+ * available and non-NULL...
*/
- curPos = (chanPtr->typePtr->seekProc) (chanPtr->instanceData,
- (long) offset, mode, &result);
- if (curPos == -1) {
- Tcl_SetErrno(result);
- }
+ if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) &&
+ chanPtr->typePtr->wideSeekProc != NULL) {
+ curPos = (chanPtr->typePtr->wideSeekProc) (chanPtr->instanceData,
+ offset, mode, &result);
+ } else if (offset < Tcl_LongAsWide(LONG_MIN) ||
+ offset > Tcl_LongAsWide(LONG_MAX)) {
+ Tcl_SetErrno(EOVERFLOW);
+ curPos = Tcl_LongAsWide(-1);
+ } else {
+ curPos = Tcl_LongAsWide((chanPtr->typePtr->seekProc) (
+ chanPtr->instanceData, Tcl_WideAsLong(offset), mode,
+ &result));
+ if (curPos == Tcl_LongAsWide(-1)) {
+ Tcl_SetErrno(result);
+ }
+ }
}
/*
@@ -4987,7 +5490,7 @@ Tcl_Seek(chan, offset, mode)
statePtr->flags |= CHANNEL_NONBLOCKING;
result = StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING);
if (result != 0) {
- return -1;
+ return Tcl_LongAsWide(-1);
}
}
@@ -5013,19 +5516,18 @@ Tcl_Seek(chan, offset, mode)
*----------------------------------------------------------------------
*/
-int
+Tcl_WideInt
Tcl_Tell(chan)
Tcl_Channel chan; /* The channel to return pos for. */
{
Channel *chanPtr = (Channel *) chan; /* The real IO channel. */
ChannelState *statePtr = chanPtr->state; /* state info for channel */
- ChannelBuffer *bufPtr;
- int inputBuffered, outputBuffered;
+ int inputBuffered, outputBuffered; /* # bytes held in buffers. */
int result; /* Of calling device driver. */
- int curPos; /* Position on device. */
+ Tcl_WideInt curPos; /* Position on device. */
if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) {
- return -1;
+ return Tcl_LongAsWide(-1);
}
/*
@@ -5036,7 +5538,7 @@ Tcl_Tell(chan)
*/
if (CheckForDeadChannel(NULL, statePtr)) {
- return -1;
+ return Tcl_LongAsWide(-1);
}
/*
@@ -5052,7 +5554,7 @@ Tcl_Tell(chan)
if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
Tcl_SetErrno(EINVAL);
- return -1;
+ return Tcl_LongAsWide(-1);
}
/*
@@ -5060,43 +5562,78 @@ Tcl_Tell(chan)
* output is buffered, cannot compute the current position.
*/
- for (bufPtr = statePtr->inQueueHead, inputBuffered = 0;
- bufPtr != (ChannelBuffer *) NULL;
- bufPtr = bufPtr->nextPtr) {
- inputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
- }
- for (bufPtr = statePtr->outQueueHead, outputBuffered = 0;
- bufPtr != (ChannelBuffer *) NULL;
- bufPtr = bufPtr->nextPtr) {
- outputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
- }
- if ((statePtr->curOutPtr != (ChannelBuffer *) NULL) &&
- (statePtr->curOutPtr->nextAdded > statePtr->curOutPtr->nextRemoved)) {
- statePtr->flags |= BUFFER_READY;
- outputBuffered +=
- (statePtr->curOutPtr->nextAdded - statePtr->curOutPtr->nextRemoved);
- }
+ inputBuffered = Tcl_InputBuffered(chan);
+ outputBuffered = Tcl_OutputBuffered(chan);
if ((inputBuffered != 0) && (outputBuffered != 0)) {
Tcl_SetErrno(EFAULT);
- return -1;
+ return Tcl_LongAsWide(-1);
}
/*
* Get the current position in the device and compute the position
- * where the next character will be read or written.
+ * where the next character will be read or written. Note that we
+ * prefer the wideSeekProc if that is available and non-NULL...
*/
- curPos = (chanPtr->typePtr->seekProc) (chanPtr->instanceData,
- (long) 0, SEEK_CUR, &result);
- if (curPos == -1) {
+ if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) &&
+ chanPtr->typePtr->wideSeekProc != NULL) {
+ curPos = (chanPtr->typePtr->wideSeekProc) (chanPtr->instanceData,
+ Tcl_LongAsWide(0), SEEK_CUR, &result);
+ } else {
+ curPos = Tcl_LongAsWide((chanPtr->typePtr->seekProc) (
+ chanPtr->instanceData, 0, SEEK_CUR, &result));
+ }
+ if (curPos == Tcl_LongAsWide(-1)) {
Tcl_SetErrno(result);
- return -1;
+ return Tcl_LongAsWide(-1);
}
if (inputBuffered != 0) {
- return (curPos - inputBuffered);
+ return curPos - inputBuffered;
}
- return (curPos + outputBuffered);
+ return curPos + outputBuffered;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_SeekOld, Tcl_TellOld --
+ *
+ * Backward-compatability versions of the seek/tell interface that
+ * do not support 64-bit offsets. This interface is not documented
+ * or expected to be supported indefinitely.
+ *
+ * Results:
+ * As for Tcl_Seek and Tcl_Tell respectively, except truncated to
+ * whatever value will fit in an 'int'.
+ *
+ * Side effects:
+ * As for Tcl_Seek and Tcl_Tell respectively.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Tcl_SeekOld(chan, offset, mode)
+ Tcl_Channel chan; /* The channel on which to seek. */
+ int offset; /* Offset to seek to. */
+ int mode; /* Relative to which location to seek? */
+{
+ Tcl_WideInt wOffset, wResult;
+
+ wOffset = Tcl_LongAsWide((long)offset);
+ wResult = Tcl_Seek(chan, wOffset, mode);
+ return (int)Tcl_WideAsLong(wResult);
+}
+
+int
+Tcl_TellOld(chan)
+ Tcl_Channel chan; /* The channel to return pos for. */
+{
+ Tcl_WideInt wResult;
+
+ wResult = Tcl_Tell(chan);
+ return (int)Tcl_WideAsLong(wResult);
}
/*
@@ -5177,7 +5714,7 @@ CheckChannelErrors(statePtr, flags)
* reading beyond the eofChar). Also, always clear the BLOCKED bit.
* We want to discover these conditions anew in each operation.
*/
-
+
if ((statePtr->flags & CHANNEL_STICKY_EOF) == 0) {
statePtr->flags &= ~CHANNEL_EOF;
}
@@ -5290,6 +5827,48 @@ Tcl_InputBuffered(chan)
/*
*----------------------------------------------------------------------
*
+ * Tcl_OutputBuffered --
+ *
+ * Returns the number of bytes of output currently buffered in the
+ * common internal buffer of a channel.
+ *
+ * Results:
+ * The number of output bytes buffered, or zero if the channel is not
+ * open for writing.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_OutputBuffered(chan)
+ Tcl_Channel chan; /* The channel to query. */
+{
+ ChannelState *statePtr = ((Channel *) chan)->state;
+ /* State of real channel structure. */
+ ChannelBuffer *bufPtr;
+ int bytesBuffered;
+
+ for (bytesBuffered = 0, bufPtr = statePtr->outQueueHead;
+ bufPtr != (ChannelBuffer *) NULL;
+ bufPtr = bufPtr->nextPtr) {
+ bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
+ }
+ if ((statePtr->curOutPtr != (ChannelBuffer *) NULL) &&
+ (statePtr->curOutPtr->nextAdded > statePtr->curOutPtr->nextRemoved)) {
+ statePtr->flags |= BUFFER_READY;
+ bytesBuffered +=
+ (statePtr->curOutPtr->nextAdded - statePtr->curOutPtr->nextRemoved);
+ }
+
+ return bytesBuffered;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_ChannelBuffered --
*
* Returns the number of bytes of input currently buffered in the
@@ -5431,8 +6010,8 @@ Tcl_GetChannelBufferSize(chan)
int
Tcl_BadChannelOption(interp, optionName, optionList)
Tcl_Interp *interp; /* Current interpreter. (can be NULL)*/
- char *optionName; /* 'bad option' name */
- char *optionList; /* Specific options list to append
+ CONST char *optionName; /* 'bad option' name */
+ CONST char *optionList; /* Specific options list to append
* to the standard generic options.
* can be NULL for generic options
* only.
@@ -5441,12 +6020,12 @@ Tcl_BadChannelOption(interp, optionName, optionList)
if (interp) {
CONST char *genericopt =
"blocking buffering buffersize encoding eofchar translation";
- char **argv;
+ CONST char **argv;
int argc, i;
Tcl_DString ds;
Tcl_DStringInit(&ds);
- Tcl_DStringAppend(&ds, (char *) genericopt, -1);
+ Tcl_DStringAppend(&ds, genericopt, -1);
if (optionList && (*optionList)) {
Tcl_DStringAppend(&ds, " ", 1);
Tcl_DStringAppend(&ds, optionList, -1);
@@ -5494,7 +6073,7 @@ int
Tcl_GetChannelOption(interp, chan, optionName, dsPtr)
Tcl_Interp *interp; /* For error reporting - can be NULL. */
Tcl_Channel chan; /* Channel on which to get option. */
- char *optionName; /* Option to get. */
+ CONST char *optionName; /* Option to get. */
Tcl_DString *dsPtr; /* Where to store value(s). */
{
size_t len; /* Length of optionName string. */
@@ -5629,6 +6208,10 @@ Tcl_GetChannelOption(interp, chan, optionName, dsPtr)
Tcl_DStringAppendElement(dsPtr, buf);
}
}
+ if ( !(flags & (TCL_READABLE|TCL_WRITABLE))) {
+ /* Not readable or writable (server socket) */
+ Tcl_DStringAppendElement(dsPtr, "");
+ }
if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
(TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
Tcl_DStringEndSublist(dsPtr);
@@ -5669,6 +6252,10 @@ Tcl_GetChannelOption(interp, chan, optionName, dsPtr)
Tcl_DStringAppendElement(dsPtr, "lf");
}
}
+ if ( !(flags & (TCL_READABLE|TCL_WRITABLE))) {
+ /* Not readable or writable (server socket) */
+ Tcl_DStringAppendElement(dsPtr, "auto");
+ }
if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
(TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
Tcl_DStringEndSublist(dsPtr);
@@ -5718,15 +6305,14 @@ int
Tcl_SetChannelOption(interp, chan, optionName, newValue)
Tcl_Interp *interp; /* For error reporting - can be NULL. */
Tcl_Channel chan; /* Channel on which to set mode. */
- char *optionName; /* Which option to set? */
- char *newValue; /* New value for option. */
+ CONST char *optionName; /* Which option to set? */
+ CONST char *newValue; /* New value for option. */
{
- int newMode; /* New (numeric) mode to sert. */
Channel *chanPtr = (Channel *) chan; /* The real IO channel. */
ChannelState *statePtr = chanPtr->state; /* state info for channel */
size_t len; /* Length of optionName string. */
int argc;
- char **argv;
+ CONST char **argv;
/*
* If the channel is in the middle of a background copy, fail.
@@ -5762,6 +6348,7 @@ Tcl_SetChannelOption(interp, chan, optionName, newValue)
if ((len > 2) && (optionName[1] == 'b') &&
(strncmp(optionName, "-blocking", len) == 0)) {
+ int newMode;
if (Tcl_GetBoolean(interp, newValue, &newMode) == TCL_ERROR) {
return TCL_ERROR;
}
@@ -5812,6 +6399,15 @@ Tcl_SetChannelOption(interp, chan, optionName, newValue)
return TCL_ERROR;
}
}
+ /*
+ * When the channel has an escape sequence driven encoding such as
+ * iso2022, the terminated escape sequence must write to the buffer.
+ */
+ if ((statePtr->encoding != NULL) && (statePtr->curOutPtr != NULL)
+ && (CheckChannelErrors(statePtr, TCL_WRITABLE) == 0)) {
+ statePtr->outputEncodingFlags |= TCL_ENCODING_END;
+ WriteChars(chanPtr, "", 0);
+ }
Tcl_FreeEncoding(statePtr->encoding);
statePtr->encoding = encoding;
statePtr->inputEncodingState = NULL;
@@ -5838,8 +6434,8 @@ Tcl_SetChannelOption(interp, chan, optionName, newValue)
} else if (argc != 2) {
if (interp) {
Tcl_AppendResult(interp,
- "bad value for -eofchar: should be a list of one or",
- " two elements", (char *) NULL);
+ "bad value for -eofchar: should be a list of zero,",
+ " one, or two elements", (char *) NULL);
}
ckfree((char *) argv);
return TCL_ERROR;
@@ -5851,13 +6447,13 @@ Tcl_SetChannelOption(interp, chan, optionName, newValue)
statePtr->outEofChar = (int) argv[1][0];
}
}
- if (argv != (char **) NULL) {
+ if (argv != NULL) {
ckfree((char *) argv);
}
return TCL_OK;
} else if ((len > 1) && (optionName[1] == 't') &&
(strncmp(optionName, "-translation", len) == 0)) {
- char *readMode, *writeMode;
+ CONST char *readMode, *writeMode;
if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
return TCL_ERROR;
@@ -5880,23 +6476,24 @@ Tcl_SetChannelOption(interp, chan, optionName, newValue)
}
if (readMode) {
+ TclEolTranslation translation;
if (*readMode == '\0') {
- newMode = statePtr->inputTranslation;
+ translation = statePtr->inputTranslation;
} else if (strcmp(readMode, "auto") == 0) {
- newMode = TCL_TRANSLATE_AUTO;
+ translation = TCL_TRANSLATE_AUTO;
} else if (strcmp(readMode, "binary") == 0) {
- newMode = TCL_TRANSLATE_LF;
+ translation = TCL_TRANSLATE_LF;
statePtr->inEofChar = 0;
Tcl_FreeEncoding(statePtr->encoding);
statePtr->encoding = NULL;
} else if (strcmp(readMode, "lf") == 0) {
- newMode = TCL_TRANSLATE_LF;
+ translation = TCL_TRANSLATE_LF;
} else if (strcmp(readMode, "cr") == 0) {
- newMode = TCL_TRANSLATE_CR;
+ translation = TCL_TRANSLATE_CR;
} else if (strcmp(readMode, "crlf") == 0) {
- newMode = TCL_TRANSLATE_CRLF;
+ translation = TCL_TRANSLATE_CRLF;
} else if (strcmp(readMode, "platform") == 0) {
- newMode = TCL_PLATFORM_TRANSLATION;
+ translation = TCL_PLATFORM_TRANSLATION;
} else {
if (interp) {
Tcl_AppendResult(interp,
@@ -5914,8 +6511,8 @@ Tcl_SetChannelOption(interp, chan, optionName, newValue)
* complete the line.
*/
- if (newMode != statePtr->inputTranslation) {
- statePtr->inputTranslation = (Tcl_EolTranslation) newMode;
+ if (translation != statePtr->inputTranslation) {
+ statePtr->inputTranslation = translation;
statePtr->flags &= ~(INPUT_SAW_CR);
statePtr->flags &= ~(CHANNEL_NEED_MORE_DATA);
UpdateInterest(chanPtr);
@@ -5932,7 +6529,7 @@ Tcl_SetChannelOption(interp, chan, optionName, newValue)
* coded later.
*/
- if (strcmp(chanPtr->typePtr->typeName, "tcp") == 0) {
+ if (strcmp(Tcl_ChannelName(chanPtr->typePtr), "tcp") == 0) {
statePtr->outputTranslation = TCL_TRANSLATE_CRLF;
} else {
statePtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
@@ -6090,7 +6687,6 @@ Tcl_NotifyChannel(channel, mask)
ChannelHandler *chPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
NextChannelHandler nh;
-#ifdef TCL_CHANNEL_VERSION_2
Channel* upChanPtr;
Tcl_ChannelType* upTypePtr;
@@ -6107,17 +6703,13 @@ Tcl_NotifyChannel(channel, mask)
*/
while (mask && (chanPtr->upChanPtr != ((Channel*) NULL))) {
+ Tcl_DriverHandlerProc* upHandlerProc;
+
upChanPtr = chanPtr->upChanPtr;
upTypePtr = upChanPtr->typePtr;
-
- if ((Tcl_ChannelVersion(upTypePtr) == TCL_CHANNEL_VERSION_2) &&
- (Tcl_ChannelHandlerProc(upTypePtr) !=
- ((Tcl_DriverHandlerProc *) NULL))) {
-
- Tcl_DriverHandlerProc* handlerProc =
- Tcl_ChannelHandlerProc(upTypePtr);
-
- mask = (*handlerProc) (upChanPtr->instanceData, mask);
+ upHandlerProc = Tcl_ChannelHandlerProc(upTypePtr);
+ if (upHandlerProc != NULL) {
+ mask = (*upHandlerProc) (upChanPtr->instanceData, mask);
}
/* ELSE:
@@ -6148,6 +6740,7 @@ Tcl_NotifyChannel(channel, mask)
*/
Tcl_Preserve((ClientData) channel);
+ Tcl_Preserve((ClientData) statePtr);
/*
* If we are flushing in the background, be sure to call FlushChannel
@@ -6157,8 +6750,8 @@ Tcl_NotifyChannel(channel, mask)
*/
if ((statePtr->flags & BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) {
- FlushChannel(NULL, chanPtr, 1);
- mask &= ~TCL_WRITABLE;
+ FlushChannel(NULL, chanPtr, 1);
+ mask &= ~TCL_WRITABLE;
}
/*
@@ -6171,19 +6764,18 @@ Tcl_NotifyChannel(channel, mask)
tsdPtr->nestedHandlerPtr = &nh;
for (chPtr = statePtr->chPtr; chPtr != (ChannelHandler *) NULL; ) {
+ /*
+ * If this channel handler is interested in any of the events that
+ * have occurred on the channel, invoke its procedure.
+ */
- /*
- * If this channel handler is interested in any of the events that
- * have occurred on the channel, invoke its procedure.
- */
-
- if ((chPtr->mask & mask) != 0) {
- nh.nextHandlerPtr = chPtr->nextPtr;
- (*(chPtr->proc))(chPtr->clientData, mask);
- chPtr = nh.nextHandlerPtr;
- } else {
- chPtr = chPtr->nextPtr;
- }
+ if ((chPtr->mask & mask) != 0) {
+ nh.nextHandlerPtr = chPtr->nextPtr;
+ (*(chPtr->proc))(chPtr->clientData, mask);
+ chPtr = nh.nextHandlerPtr;
+ } else {
+ chPtr = chPtr->nextPtr;
+ }
}
/*
@@ -6196,82 +6788,10 @@ Tcl_NotifyChannel(channel, mask)
UpdateInterest(chanPtr);
}
+ Tcl_Release((ClientData) statePtr);
Tcl_Release((ClientData) channel);
tsdPtr->nestedHandlerPtr = nh.nestedHandlerPtr;
-#else
- /* Walk all channels in a stack ! and notify them in order.
- */
-
- while (chanPtr != (Channel *) NULL) {
- /*
- * Preserve the channel struct in case the script closes it.
- */
-
- Tcl_Preserve((ClientData) channel);
-
- /*
- * If we are flushing in the background, be sure to call FlushChannel
- * for writable events. Note that we have to discard the writable
- * event so we don't call any write handlers before the flush is
- * complete.
- */
-
- if ((statePtr->flags & BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) {
- FlushChannel(NULL, chanPtr, 1);
- mask &= ~TCL_WRITABLE;
- }
-
- /*
- * Add this invocation to the list of recursive invocations of
- * ChannelHandlerEventProc.
- */
-
- nh.nextHandlerPtr = (ChannelHandler *) NULL;
- nh.nestedHandlerPtr = tsdPtr->nestedHandlerPtr;
- tsdPtr->nestedHandlerPtr = &nh;
-
- for (chPtr = statePtr->chPtr; chPtr != (ChannelHandler *) NULL; ) {
-
- /*
- * If this channel handler is interested in any of the events that
- * have occurred on the channel, invoke its procedure.
- */
-
- if ((chPtr->mask & mask) != 0) {
- nh.nextHandlerPtr = chPtr->nextPtr;
- (*(chPtr->proc))(chPtr->clientData, mask);
- chPtr = nh.nextHandlerPtr;
- } else {
- chPtr = chPtr->nextPtr;
- }
- }
-
- /*
- * Update the notifier interest, since it may have changed after
- * invoking event handlers. Skip that if the channel was deleted
- * in the call to the channel handler.
- */
-
- if (chanPtr->typePtr != NULL) {
- UpdateInterest(chanPtr);
-
- /* Walk down the stack.
- */
- chanPtr = chanPtr->downChanPtr;
- } else {
- /* Stop walking the chain, the whole stack was destroyed!
- */
- chanPtr = (Channel *) NULL;
- }
-
- Tcl_Release((ClientData) channel);
-
- tsdPtr->nestedHandlerPtr = nh.nestedHandlerPtr;
-
- channel = (Tcl_Channel) chanPtr;
- }
-#endif
}
/*
@@ -6365,8 +6885,23 @@ ChannelTimerProc(clientData)
statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
(ClientData) chanPtr);
+
+ /* Set the TIMER flag to notify the higher levels that the
+ * driver might have no data for us. We do this only if we are
+ * in non-blocking mode and the driver has no BlockModeProc
+ * because only then we really don't know if the driver will
+ * block or not. A similar test is done in "PeekAhead".
+ */
+
+ if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
+ (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL)) {
+ statePtr->flags |= CHANNEL_TIMER_FEV;
+ }
+ Tcl_Preserve((ClientData) statePtr);
Tcl_NotifyChannel((Tcl_Channel)chanPtr, TCL_READABLE);
-
+
+ statePtr->flags &= ~CHANNEL_TIMER_FEV;
+ Tcl_Release((ClientData) statePtr);
} else {
statePtr->timer = NULL;
UpdateInterest(chanPtr);
@@ -6756,7 +7291,7 @@ Tcl_FileEventObjCmd(clientData, interp, objc, objv)
char *chanName;
int modeIndex; /* Index of mode argument. */
int mask;
- static char *modeOptions[] = {"readable", "writable", NULL};
+ static CONST char *modeOptions[] = {"readable", "writable", NULL};
static int maskArray[] = {TCL_READABLE, TCL_WRITABLE};
if ((objc != 3) && (objc != 4)) {
@@ -6889,7 +7424,7 @@ TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr)
if (inPtr != outPtr) {
if (nonBlocking != (writeFlags & CHANNEL_NONBLOCKING)) {
if (SetBlockMode(NULL, outPtr,
- nonBlocking ? TCL_MODE_BLOCKING : TCL_MODE_NONBLOCKING)
+ nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING)
!= TCL_OK) {
if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
SetBlockMode(NULL, inPtr,
@@ -6960,12 +7495,14 @@ CopyData(csPtr, mask)
int mask; /* Current channel event flags. */
{
Tcl_Interp *interp;
- Tcl_Obj *cmdPtr, *errObj = NULL;
+ Tcl_Obj *cmdPtr, *errObj = NULL, *bufObj = NULL;
Tcl_Channel inChan, outChan;
ChannelState *inStatePtr, *outStatePtr;
- int result = TCL_OK;
- int size;
- int total;
+ int result = TCL_OK, size, total, sizeb;
+ char* buffer;
+
+ int inBinary, outBinary, sameEncoding; /* Encoding control */
+ int underflow; /* input underflow */
inChan = (Tcl_Channel) csPtr->readPtr;
outChan = (Tcl_Channel) csPtr->writePtr;
@@ -6982,8 +7519,16 @@ CopyData(csPtr, mask)
* thus gets the bottom of the stack.
*/
- while (csPtr->toRead != 0) {
+ inBinary = (inStatePtr->encoding == NULL);
+ outBinary = (outStatePtr->encoding == NULL);
+ sameEncoding = (inStatePtr->encoding == outStatePtr->encoding);
+
+ if (!(inBinary || sameEncoding)) {
+ bufObj = Tcl_NewObj ();
+ Tcl_IncrRefCount (bufObj);
+ }
+ while (csPtr->toRead != 0) {
/*
* Check for unreported background errors.
*/
@@ -7004,11 +7549,17 @@ CopyData(csPtr, mask)
*/
if ((csPtr->toRead == -1) || (csPtr->toRead > csPtr->bufSize)) {
- size = csPtr->bufSize;
+ sizeb = csPtr->bufSize;
} else {
- size = csPtr->toRead;
+ sizeb = csPtr->toRead;
}
- size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, size);
+
+ if (inBinary || sameEncoding) {
+ size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb);
+ } else {
+ size = DoReadChars(inStatePtr->topChanPtr, bufObj, sizeb, 0 /* No append */);
+ }
+ underflow = (size >= 0) && (size < sizeb); /* input underflow */
if (size < 0) {
readError:
@@ -7017,16 +7568,17 @@ CopyData(csPtr, mask)
Tcl_GetChannelName(inChan), "\": ",
Tcl_PosixError(interp), (char *) NULL);
break;
- } else if (size == 0) {
+ } else if (underflow) {
/*
* We had an underflow on the read side. If we are at EOF,
* then the copying is done, otherwise set up a channel
* handler to detect when the channel becomes readable again.
*/
- if (Tcl_Eof(inChan)) {
+ if ((size == 0) && Tcl_Eof(inChan)) {
break;
- } else if (!(mask & TCL_READABLE)) {
+ }
+ if (! Tcl_Eof(inChan) && !(mask & TCL_READABLE)) {
if (mask & TCL_WRITABLE) {
Tcl_DeleteChannelHandler(outChan, CopyEventProc,
(ClientData) csPtr);
@@ -7034,15 +7586,38 @@ CopyData(csPtr, mask)
Tcl_CreateChannelHandler(inChan, TCL_READABLE,
CopyEventProc, (ClientData) csPtr);
}
- return TCL_OK;
+ if (size == 0) {
+ if (bufObj != (Tcl_Obj*) NULL) {
+ Tcl_DecrRefCount (bufObj);
+ bufObj = (Tcl_Obj*) NULL;
+ }
+ return TCL_OK;
+ }
}
/*
* Now write the buffer out.
*/
- size = DoWrite(outStatePtr->topChanPtr, csPtr->buffer, size);
- if (size < 0) {
+ if (inBinary || sameEncoding) {
+ buffer = csPtr->buffer;
+ sizeb = size;
+ } else {
+ buffer = Tcl_GetStringFromObj (bufObj, &sizeb);
+ }
+
+ if (outBinary || sameEncoding) {
+ sizeb = DoWrite(outStatePtr->topChanPtr, buffer, sizeb);
+ } else {
+ sizeb = DoWriteChars(outStatePtr->topChanPtr, buffer, sizeb);
+ }
+
+ if (inBinary || sameEncoding) {
+ /* Both read and write counted bytes */
+ size = sizeb;
+ } /* else : Read counted characters, write counted bytes, i.e. size != sizeb */
+
+ if (sizeb < 0) {
writeError:
errObj = Tcl_NewObj();
Tcl_AppendStringsToObj(errObj, "error writing \"",
@@ -7052,32 +7627,49 @@ CopyData(csPtr, mask)
}
/*
+ * Update the current byte count. Do it now so the count is
+ * valid before a return or break takes us out of the loop.
+ * The invariant at the top of the loop should be that
+ * csPtr->toRead holds the number of bytes left to copy.
+ */
+
+ if (csPtr->toRead != -1) {
+ csPtr->toRead -= size;
+ }
+ csPtr->total += size;
+
+ /*
+ * Break loop if EOF && (size>0)
+ */
+
+ if (Tcl_Eof(inChan)) {
+ break;
+ }
+
+ /*
* Check to see if the write is happening in the background. If so,
* stop copying and wait for the channel to become writable again.
+ * After input underflow we already installed a readable handler
+ * therefore we don't need a writable handler.
*/
- if (outStatePtr->flags & BG_FLUSH_SCHEDULED) {
+ if ( ! underflow && (outStatePtr->flags & BG_FLUSH_SCHEDULED) ) {
if (!(mask & TCL_WRITABLE)) {
if (mask & TCL_READABLE) {
- Tcl_DeleteChannelHandler(outChan, CopyEventProc,
+ Tcl_DeleteChannelHandler(inChan, CopyEventProc,
(ClientData) csPtr);
}
Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
CopyEventProc, (ClientData) csPtr);
}
+ if (bufObj != (Tcl_Obj*) NULL) {
+ Tcl_DecrRefCount (bufObj);
+ bufObj = (Tcl_Obj*) NULL;
+ }
return TCL_OK;
}
/*
- * Update the current byte count if we care.
- */
-
- if (csPtr->toRead != -1) {
- csPtr->toRead -= size;
- }
- csPtr->total += size;
-
- /*
* For background copies, we only do one buffer per invocation so
* we don't starve the rest of the system.
*/
@@ -7092,8 +7684,17 @@ CopyData(csPtr, mask)
Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
CopyEventProc, (ClientData) csPtr);
}
+ if (bufObj != (Tcl_Obj*) NULL) {
+ Tcl_DecrRefCount (bufObj);
+ bufObj = (Tcl_Obj*) NULL;
+ }
return TCL_OK;
}
+ } /* while */
+
+ if (bufObj != (Tcl_Obj*) NULL) {
+ Tcl_DecrRefCount (bufObj);
+ bufObj = (Tcl_Obj*) NULL;
}
/*
@@ -7144,6 +7745,8 @@ CopyData(csPtr, mask)
*
* Reads a given number of bytes from a channel.
*
+ * No encoding conversions are applied to the bytes being read.
+ *
* Results:
* The number of characters read, or -1 on error. Use Tcl_GetErrno()
* to retrieve the error code for the error that occurred.
@@ -7568,14 +8171,14 @@ CopyBuffer(chanPtr, result, space)
static int
DoWrite(chanPtr, src, srcLen)
Channel *chanPtr; /* The channel to buffer output for. */
- char *src; /* Data to write. */
+ CONST char *src; /* Data to write. */
int srcLen; /* Number of bytes to write. */
{
ChannelState *statePtr = chanPtr->state; /* state info for channel */
ChannelBuffer *outBufPtr; /* Current output buffer. */
int foundNewline; /* Did we find a newline in output? */
char *dPtr;
- char *sPtr; /* Search variables for newline. */
+ CONST char *sPtr; /* Search variables for newline. */
int crsent; /* In CRLF eol translation mode,
* remember the fact that a CR was
* output to the channel without
@@ -7769,6 +8372,7 @@ StopCopy(csPtr)
nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
}
if (csPtr->readPtr != csPtr->writePtr) {
+ nonBlocking = (csPtr->writeFlags & CHANNEL_NONBLOCKING);
if (nonBlocking != (outStatePtr->flags & CHANNEL_NONBLOCKING)) {
SetBlockMode(NULL, csPtr->writePtr,
nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
@@ -7926,17 +8530,32 @@ Tcl_GetChannelNames(interp)
int
Tcl_GetChannelNamesEx(interp, pattern)
Tcl_Interp *interp; /* Interp for error reporting. */
- char *pattern; /* pattern to filter on. */
+ CONST char *pattern; /* pattern to filter on. */
{
- ChannelState *statePtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- char *name;
- Tcl_Obj *resultPtr;
+ ChannelState *statePtr;
+ CONST char *name; /* name for channel */
+ Tcl_Obj *resultPtr; /* pointer to result object */
+ Tcl_HashTable *hTblPtr; /* Hash table of channels. */
+ Tcl_HashEntry *hPtr; /* Search variable. */
+ Tcl_HashSearch hSearch; /* Search variable. */
- resultPtr = Tcl_GetObjResult(interp);
- for (statePtr = tsdPtr->firstCSPtr;
- statePtr != NULL;
- statePtr = statePtr->nextCSPtr) {
+ if (interp == (Tcl_Interp *) NULL) {
+ return TCL_OK;
+ }
+
+ /*
+ * Get the channel table that stores the channels registered
+ * for this interpreter.
+ */
+ hTblPtr = GetChannelTable(interp);
+ resultPtr = Tcl_GetObjResult(interp);
+
+ for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
+ hPtr != (Tcl_HashEntry *) NULL;
+ hPtr = Tcl_NextHashEntry(&hSearch)) {
+
+ statePtr = ((Channel *) Tcl_GetHashValue(hPtr))->state;
if (statePtr->topChanPtr == (Channel *) tsdPtr->stdinChannel) {
name = "stdin";
} else if (statePtr->topChanPtr == (Channel *) tsdPtr->stdoutChannel) {
@@ -7944,8 +8563,13 @@ Tcl_GetChannelNamesEx(interp, pattern)
} else if (statePtr->topChanPtr == (Channel *) tsdPtr->stderrChannel) {
name = "stderr";
} else {
+ /*
+ * This is also stored in Tcl_GetHashKey(hTblPtr, hPtr),
+ * but it's simpler to just grab the name from the statePtr.
+ */
name = statePtr->channelName;
}
+
if (((pattern == NULL) || Tcl_StringMatch(name, pattern)) &&
(Tcl_ListObjAppendElement(interp, resultPtr,
Tcl_NewStringObj(name, -1)) != TCL_OK)) {
@@ -7958,6 +8582,131 @@ Tcl_GetChannelNamesEx(interp, pattern)
/*
*----------------------------------------------------------------------
*
+ * Tcl_IsChannelRegistered --
+ *
+ * Checks whether the channel is associated with the interp.
+ * See also Tcl_RegisterChannel and Tcl_UnregisterChannel.
+ *
+ * Results:
+ * 0 if the channel is not registered in the interpreter, 1 else.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_IsChannelRegistered (interp, chan)
+ Tcl_Interp* interp; /* The interp to query of the channel */
+ Tcl_Channel chan; /* The channel to check */
+{
+ Tcl_HashTable *hTblPtr; /* Hash table of channels. */
+ Tcl_HashEntry *hPtr; /* Search variable. */
+ Channel *chanPtr; /* The real IO channel. */
+ ChannelState *statePtr; /* State of the real channel. */
+
+ /*
+ * Always check bottom-most channel in the stack. This is the one
+ * that gets registered.
+ */
+ chanPtr = ((Channel *) chan)->state->bottomChanPtr;
+ statePtr = chanPtr->state;
+
+ hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
+ if (hTblPtr == (Tcl_HashTable *) NULL) {
+ return 0;
+ }
+ hPtr = Tcl_FindHashEntry(hTblPtr, statePtr->channelName);
+ if (hPtr == (Tcl_HashEntry *) NULL) {
+ return 0;
+ }
+ if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_IsChannelShared --
+ *
+ * Checks whether the channel is shared by multiple interpreters.
+ *
+ * Results:
+ * A boolean value (0 = Not shared, 1 = Shared).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_IsChannelShared (chan)
+ Tcl_Channel chan; /* The channel to query */
+{
+ ChannelState *statePtr = ((Channel *) chan)->state;
+ /* State of real channel structure. */
+
+ return ((statePtr->refCount > 1) ? 1 : 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_IsChannelExisting --
+ *
+ * Checks whether a channel of the given name exists in the
+ * (thread)-global list of all channels.
+ * See Tcl_GetChannelNamesEx for function exposed at the Tcl level.
+ *
+ * Results:
+ * A boolean value (0 = Does not exist, 1 = Does exist).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_IsChannelExisting(chanName)
+ CONST char* chanName; /* The name of the channel to look for. */
+{
+ ChannelState *statePtr;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ CONST char *name;
+ int chanNameLen;
+
+ chanNameLen = strlen(chanName);
+ for (statePtr = tsdPtr->firstCSPtr;
+ statePtr != NULL;
+ statePtr = statePtr->nextCSPtr) {
+ if (statePtr->topChanPtr == (Channel *) tsdPtr->stdinChannel) {
+ name = "stdin";
+ } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stdoutChannel) {
+ name = "stdout";
+ } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stderrChannel) {
+ name = "stderr";
+ } else {
+ name = statePtr->channelName;
+ }
+
+ if ((*chanName == *name) &&
+ (memcmp(name, chanName, (size_t) chanNameLen) == 0)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_ChannelName --
*
* Return the name of the channel type.
@@ -7971,11 +8720,11 @@ Tcl_GetChannelNamesEx(interp, pattern)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_ChannelName(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->typeName);
+ return chanTypePtr->typeName;
}
/*
@@ -7986,7 +8735,7 @@ Tcl_ChannelName(chanTypePtr)
* Return the of version of the channel type.
*
* Results:
- * TCL_CHANNEL_VERSION_2 or TCL_CHANNEL_VERSION_1.
+ * One of the TCL_CHANNEL_VERSION_* constants from tcl.h
*
* Side effects:
* None.
@@ -8000,6 +8749,8 @@ Tcl_ChannelVersion(chanTypePtr)
{
if (chanTypePtr->version == TCL_CHANNEL_VERSION_2) {
return TCL_CHANNEL_VERSION_2;
+ } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_3) {
+ return TCL_CHANNEL_VERSION_3;
} else {
/*
* In <v2 channel versions, the version field is occupied
@@ -8012,6 +8763,33 @@ Tcl_ChannelVersion(chanTypePtr)
/*
*----------------------------------------------------------------------
*
+ * HaveVersion --
+ *
+ * Return whether a channel type is (at least) of a given version.
+ *
+ * Results:
+ * True if the minimum version is exceeded by the version actually
+ * present.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+HaveVersion(chanTypePtr, minimumVersion)
+ Tcl_ChannelType *chanTypePtr;
+ Tcl_ChannelTypeVersion minimumVersion;
+{
+ Tcl_ChannelTypeVersion actualVersion = Tcl_ChannelVersion(chanTypePtr);
+
+ return ((int)actualVersion) >= ((int)minimumVersion);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_ChannelBlockModeProc --
*
* Return the Tcl_DriverBlockModeProc of the channel type.
@@ -8022,16 +8800,18 @@ Tcl_ChannelVersion(chanTypePtr)
* Side effects:
* None.
*
- *----------------------------------------------------------------------
- */
+ *---------------------------------------------------------------------- */
Tcl_DriverBlockModeProc *
Tcl_ChannelBlockModeProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- if (chanTypePtr->version == TCL_CHANNEL_VERSION_2) {
- return (chanTypePtr->blockModeProc);
+ if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
+ return chanTypePtr->blockModeProc;
} else {
+ /*
+ * The v1 structure had the blockModeProc in a different place.
+ */
return (Tcl_DriverBlockModeProc *) (chanTypePtr->version);
}
}
@@ -8056,7 +8836,7 @@ Tcl_DriverCloseProc *
Tcl_ChannelCloseProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->closeProc);
+ return chanTypePtr->closeProc;
}
/*
@@ -8079,7 +8859,7 @@ Tcl_DriverClose2Proc *
Tcl_ChannelClose2Proc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->close2Proc);
+ return chanTypePtr->close2Proc;
}
/*
@@ -8102,7 +8882,7 @@ Tcl_DriverInputProc *
Tcl_ChannelInputProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->inputProc);
+ return chanTypePtr->inputProc;
}
/*
@@ -8125,7 +8905,7 @@ Tcl_DriverOutputProc *
Tcl_ChannelOutputProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->outputProc);
+ return chanTypePtr->outputProc;
}
/*
@@ -8148,7 +8928,7 @@ Tcl_DriverSeekProc *
Tcl_ChannelSeekProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->seekProc);
+ return chanTypePtr->seekProc;
}
/*
@@ -8171,7 +8951,7 @@ Tcl_DriverSetOptionProc *
Tcl_ChannelSetOptionProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->setOptionProc);
+ return chanTypePtr->setOptionProc;
}
/*
@@ -8194,7 +8974,7 @@ Tcl_DriverGetOptionProc *
Tcl_ChannelGetOptionProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->getOptionProc);
+ return chanTypePtr->getOptionProc;
}
/*
@@ -8217,7 +8997,7 @@ Tcl_DriverWatchProc *
Tcl_ChannelWatchProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->watchProc);
+ return chanTypePtr->watchProc;
}
/*
@@ -8240,7 +9020,7 @@ Tcl_DriverGetHandleProc *
Tcl_ChannelGetHandleProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->getHandleProc);
+ return chanTypePtr->getHandleProc;
}
/*
@@ -8263,7 +9043,11 @@ Tcl_DriverFlushProc *
Tcl_ChannelFlushProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->flushProc);
+ if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
+ return chanTypePtr->flushProc;
+ } else {
+ return NULL;
+ }
}
/*
@@ -8286,6 +9070,36 @@ Tcl_DriverHandlerProc *
Tcl_ChannelHandlerProc(chanTypePtr)
Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
{
- return (chanTypePtr->handlerProc);
+ if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
+ return chanTypePtr->handlerProc;
+ } else {
+ return NULL;
+ }
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_ChannelWideSeekProc --
+ *
+ * Return the Tcl_DriverWideSeekProc of the channel type.
+ *
+ * Results:
+ * A pointer to the proc.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_DriverWideSeekProc *
+Tcl_ChannelWideSeekProc(chanTypePtr)
+ Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
+{
+ if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_3)) {
+ return chanTypePtr->wideSeekProc;
+ } else {
+ return NULL;
+ }
+}
diff --git a/tcl/generic/tclIO.h b/tcl/generic/tclIO.h
index 6d93a9c290a..179b56d92b4 100644
--- a/tcl/generic/tclIO.h
+++ b/tcl/generic/tclIO.h
@@ -158,7 +158,7 @@ typedef struct Channel {
*/
typedef struct ChannelState {
- char *channelName; /* The name of the channel instance in Tcl
+ CONST char *channelName; /* The name of the channel instance in Tcl
* commands. Storage is owned by the generic IO
* code, is dynamically allocated. */
int flags; /* ORed combination of the flags defined
@@ -182,10 +182,10 @@ typedef struct ChannelState {
* data bytes. May be TCL_ENCODING_START
* before converting first byte and
* TCL_ENCODING_END when EOF is seen. */
- Tcl_EolTranslation inputTranslation;
+ TclEolTranslation inputTranslation;
/* What translation to apply for end of line
* sequences on input? */
- Tcl_EolTranslation outputTranslation;
+ TclEolTranslation outputTranslation;
/* What translation to use for generating
* end of line sequences in output? */
int inEofChar; /* If nonzero, use this as a signal of EOF
@@ -233,6 +233,8 @@ typedef struct ChannelState {
* long as the channel state. Never NULL. */
struct ChannelState *nextCSPtr;
/* Next in list of channels currently open. */
+ Tcl_ThreadId managingThread; /* TIP #10: Id of the thread managing
+ * this stack of channels. */
} ChannelState;
/*
@@ -294,6 +296,17 @@ typedef struct ChannelState {
* the state of the channel changes. */
#define CHANNEL_RAW_MODE (1<<16) /* When set, notes that the Raw API is
* being used. */
+#define CHANNEL_TIMER_FEV (1<<17) /* When set the event we are
+ * notified by is a fileevent
+ * generated by a timer. We
+ * don't know if the driver
+ * has more data and should
+ * not try to read from it. If
+ * the system needs more than
+ * is in the buffers out read
+ * routines will simulate a
+ * short read (0 characters
+ * read) */
/*
* For each channel handler registered in a call to Tcl_CreateChannelHandler,
diff --git a/tcl/generic/tclIOCmd.c b/tcl/generic/tclIOCmd.c
index 0e6b7bf81a3..76ca6d1662e 100644
--- a/tcl/generic/tclIOCmd.c
+++ b/tcl/generic/tclIOCmd.c
@@ -63,45 +63,62 @@ Tcl_PutsObjCmd(dummy, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
Tcl_Channel chan; /* The channel to puts on. */
- int i; /* Counter. */
+ Tcl_Obj *string; /* String to write. */
int newline; /* Add a newline at end? */
char *channelId; /* Name of channel for puts. */
int result; /* Result of puts operation. */
int mode; /* Mode in which channel is opened. */
- char *arg;
- int length;
- i = 1;
- newline = 1;
- if ((objc >= 2) && (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0)) {
- newline = 0;
- i++;
- }
- if ((i < (objc-3)) || (i >= objc)) {
- Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string");
- return TCL_ERROR;
- }
+ switch (objc) {
+ case 2: /* puts $x */
+ string = objv[1];
+ newline = 1;
+ channelId = "stdout";
+ break;
- /*
- * The code below provides backwards compatibility with an old
- * form of the command that is no longer recommended or documented.
- */
+ case 3: /* puts -nonewline $x or puts $chan $x */
+ if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) {
+ newline = 0;
+ channelId = "stdout";
+ } else {
+ newline = 1;
+ channelId = Tcl_GetString(objv[1]);
+ }
+ string = objv[2];
+ break;
- if (i == (objc-3)) {
- arg = Tcl_GetStringFromObj(objv[i + 2], &length);
- if (strncmp(arg, "nonewline", (size_t) length) != 0) {
- Tcl_AppendResult(interp, "bad argument \"", arg,
- "\": should be \"nonewline\"", (char *) NULL);
- return TCL_ERROR;
+ case 4: /* puts -nonewline $chan $x or puts $chan $x nonewline */
+ if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) {
+ channelId = Tcl_GetString(objv[2]);
+ string = objv[3];
+ } else {
+ /*
+ * The code below provides backwards compatibility with an
+ * old form of the command that is no longer recommended
+ * or documented.
+ */
+
+ char *arg;
+ int length;
+
+ arg = Tcl_GetStringFromObj(objv[3], &length);
+ if (strncmp(arg, "nonewline", (size_t) length) != 0) {
+ Tcl_AppendResult(interp, "bad argument \"", arg,
+ "\": should be \"nonewline\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ channelId = Tcl_GetString(objv[1]);
+ string = objv[2];
}
newline = 0;
+ break;
+
+ default: /* puts or puts some bad number of arguments... */
+ Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string");
+ return TCL_ERROR;
}
- if (i == (objc - 1)) {
- channelId = "stdout";
- } else {
- channelId = Tcl_GetString(objv[i]);
- i++;
- }
+
chan = Tcl_GetChannel(interp, channelId, &mode);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
@@ -112,7 +129,7 @@ Tcl_PutsObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
- result = Tcl_WriteObj(chan, objv[i]);
+ result = Tcl_WriteObj(chan, string);
if (result < 0) {
goto error;
}
@@ -228,22 +245,12 @@ Tcl_GetsObjCmd(dummy, interp, objc, objv)
return TCL_ERROR;
}
- resultPtr = Tcl_GetObjResult(interp);
- linePtr = resultPtr;
- if (objc == 3) {
- /*
- * Variable gets line, interp get bytecount.
- */
-
- linePtr = Tcl_NewObj();
- }
+ linePtr = Tcl_NewObj();
lineLen = Tcl_GetsObj(chan, linePtr);
if (lineLen < 0) {
if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) {
- if (linePtr != resultPtr) {
- Tcl_DecrRefCount(linePtr);
- }
+ Tcl_DecrRefCount(linePtr);
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "error reading \"", name, "\": ",
Tcl_PosixError(interp), (char *) NULL);
@@ -257,8 +264,11 @@ Tcl_GetsObjCmd(dummy, interp, objc, objv)
Tcl_DecrRefCount(linePtr);
return TCL_ERROR;
}
+ resultPtr = Tcl_GetObjResult(interp);
Tcl_SetIntObj(resultPtr, lineLen);
return TCL_OK;
+ } else {
+ Tcl_SetObjResult(interp, linePtr);
}
return TCL_OK;
}
@@ -406,11 +416,14 @@ Tcl_SeekObjCmd(clientData, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
Tcl_Channel chan; /* The channel to tell on. */
- int offset, mode; /* Where to seek? */
- int result; /* Of calling Tcl_Seek. */
+ Tcl_WideInt offset; /* Where to seek? */
+ int mode; /* How to seek? */
+ Tcl_WideInt result; /* Of calling Tcl_Seek. */
char *chanName;
int optionIndex;
- static char *originOptions[] = {"start", "current", "end", (char *) NULL};
+ static CONST char *originOptions[] = {
+ "start", "current", "end", (char *) NULL
+ };
static int modeArray[] = {SEEK_SET, SEEK_CUR, SEEK_END};
if ((objc != 3) && (objc != 4)) {
@@ -422,7 +435,7 @@ Tcl_SeekObjCmd(clientData, interp, objc, objv)
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
- if (Tcl_GetIntFromObj(interp, objv[2], &offset) != TCL_OK) {
+ if (Tcl_GetWideIntFromObj(interp, objv[2], &offset) != TCL_OK) {
return TCL_ERROR;
}
mode = SEEK_SET;
@@ -435,7 +448,7 @@ Tcl_SeekObjCmd(clientData, interp, objc, objv)
}
result = Tcl_Seek(chan, offset, mode);
- if (result == -1) {
+ if (result == Tcl_LongAsWide(-1)) {
Tcl_AppendResult(interp, "error during seek on \"",
chanName, "\": ", Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
@@ -485,7 +498,7 @@ Tcl_TellObjCmd(clientData, interp, objc, objv)
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
- Tcl_SetIntObj(Tcl_GetObjResult(interp), Tcl_Tell(chan));
+ Tcl_SetWideIntObj(Tcl_GetObjResult(interp), Tcl_Tell(chan));
return TCL_OK;
}
@@ -712,12 +725,12 @@ Tcl_ExecObjCmd(dummy, interp, objc, objv)
#define NUM_ARGS 20
Tcl_Obj *resultPtr;
- char **argv;
+ CONST char **argv;
char *string;
Tcl_Channel chan;
- char *argStorage[NUM_ARGS];
+ CONST char *argStorage[NUM_ARGS];
int argc, background, i, index, keepNewline, result, skip, length;
- static char *options[] = {
+ static CONST char *options[] = {
"-keepnewline", "--", NULL
};
enum options {
@@ -770,7 +783,7 @@ Tcl_ExecObjCmd(dummy, interp, objc, objv)
argv = argStorage;
argc = objc - skip;
if ((argc + 1) > sizeof(argv) / sizeof(argv[0])) {
- argv = (char **) ckalloc((unsigned)(argc + 1) * sizeof(char *));
+ argv = (CONST char **) ckalloc((unsigned)(argc + 1) * sizeof(char *));
}
/*
@@ -953,7 +966,7 @@ Tcl_OpenObjCmd(notUsed, interp, objc, objv)
*/
if (!pipeline) {
- chan = Tcl_OpenFileChannel(interp, what, modeString, prot);
+ chan = Tcl_FSOpenFileChannel(interp, objv[1], modeString, prot);
} else {
#ifdef MAC_TCL
Tcl_AppendResult(interp,
@@ -962,7 +975,7 @@ Tcl_OpenObjCmd(notUsed, interp, objc, objv)
return TCL_ERROR;
#else
int mode, seekFlag, cmdObjc;
- char **cmdArgv;
+ CONST char **cmdArgv;
if (Tcl_SplitList(interp, what+1, &cmdObjc, &cmdArgv) != TCL_OK) {
return TCL_ERROR;
@@ -1286,7 +1299,7 @@ Tcl_SocketObjCmd(notUsed, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- static char *socketOptions[] = {
+ static CONST char *socketOptions[] = {
"-async", "-myaddr", "-myport","-server", (char *) NULL
};
enum socketOptions {
@@ -1481,7 +1494,7 @@ Tcl_FcopyObjCmd(dummy, interp, objc, objv)
int mode, i;
int toRead, index;
Tcl_Obj *cmdPtr;
- static char* switches[] = { "-size", "-command", NULL };
+ static CONST char* switches[] = { "-size", "-command", NULL };
enum { FcopySize, FcopyCommand };
if ((objc < 3) || (objc > 7) || (objc == 4) || (objc == 6)) {
diff --git a/tcl/generic/tclIOGT.c b/tcl/generic/tclIOGT.c
index 73a902221f4..e63349aa61d 100644
--- a/tcl/generic/tclIOGT.c
+++ b/tcl/generic/tclIOGT.c
@@ -31,17 +31,17 @@ static int TransformInputProc _ANSI_ARGS_ ((
ClientData instanceData,
char* buf, int toRead, int* errorCodePtr));
static int TransformOutputProc _ANSI_ARGS_ ((
- ClientData instanceData,
- char* buf, int toWrite, int* errorCodePtr));
+ ClientData instanceData, CONST char *buf,
+ int toWrite, int* errorCodePtr));
static int TransformSeekProc _ANSI_ARGS_ ((
ClientData instanceData, long offset,
int mode, int* errorCodePtr));
static int TransformSetOptionProc _ANSI_ARGS_((
ClientData instanceData, Tcl_Interp *interp,
- char *optionName, char *value));
+ CONST char *optionName, CONST char *value));
static int TransformGetOptionProc _ANSI_ARGS_((
ClientData instanceData, Tcl_Interp *interp,
- char *optionName, Tcl_DString *dsPtr));
+ CONST char *optionName, Tcl_DString *dsPtr));
static void TransformWatchProc _ANSI_ARGS_ ((
ClientData instanceData, int mask));
static int TransformGetFileHandleProc _ANSI_ARGS_ ((
@@ -49,6 +49,9 @@ static int TransformGetFileHandleProc _ANSI_ARGS_ ((
ClientData* handlePtr));
static int TransformNotifyProc _ANSI_ARGS_ ((
ClientData instanceData, int mask));
+static Tcl_WideInt TransformWideSeekProc _ANSI_ARGS_ ((
+ ClientData instanceData, Tcl_WideInt offset,
+ int mode, int* errorCodePtr));
/*
* Forward declarations of internal procedures.
@@ -141,6 +144,7 @@ static Tcl_ChannelType transformChannelType = {
TransformBlockModeProc, /* Set blocking/nonblocking mode.*/
NULL, /* Flush proc. */
TransformNotifyProc, /* Handling of events bubbling up */
+ TransformWideSeekProc, /* Wide seek proc */
};
/*
@@ -156,8 +160,8 @@ static Tcl_ChannelType transformChannelType = {
struct ResultBuffer {
unsigned char* buf; /* Reference to the buffer area */
- int allocated; /* Allocated size of the buffer area */
- int used; /* Number of bytes in the buffer, <= allocated */
+ int allocated; /* Allocated size of the buffer area */
+ int used; /* Number of bytes in the buffer, <= allocated */
};
/*
@@ -171,7 +175,7 @@ struct ResultBuffer {
* out information waiting in buffers (fileevent support).
*/
-#define DELAY (5)
+#define FLUSH_DELAY (5)
/*
* Convenience macro to make some casts easier to use.
@@ -357,11 +361,11 @@ TclChannelTransform(interp, chan, cmdObjPtr)
static int
ExecuteCallback (dataPtr, interp, op, buf, bufLen, transmit, preserve)
- TransformChannelData* dataPtr; /* Transformation with the callback */
+ TransformChannelData* dataPtr; /* Transformation with the callback */
Tcl_Interp* interp; /* Current interpreter, possibly NULL */
unsigned char* op; /* Operation invoking the callback */
unsigned char* buf; /* Buffer to give to the script. */
- int bufLen; /* Ands its length */
+ int bufLen; /* Ands its length */
int transmit; /* Flag, determines whether the result
* of the callback is sent to the
* underlying channel or not. */
@@ -377,16 +381,14 @@ ExecuteCallback (dataPtr, interp, op, buf, bufLen, transmit, preserve)
* arguments. Feather's curried commands would come in handy here.
*/
- Tcl_Obj* resObj; /* See below, switch (transmit) */
- int resLen;
- unsigned char* resBuf;
+ Tcl_Obj* resObj; /* See below, switch (transmit) */
+ int resLen;
+ unsigned char* resBuf;
Tcl_SavedResult ciSave;
-
int res = TCL_OK;
Tcl_Obj* command = Tcl_DuplicateObj (dataPtr->command);
Tcl_Obj* temp;
-
if (preserve) {
Tcl_SaveResult (dataPtr->interp, &ciSave);
}
@@ -641,7 +643,7 @@ static int
TransformInputProc (instanceData, buf, toRead, errorCodePtr)
ClientData instanceData;
char* buf;
- int toRead;
+ int toRead;
int* errorCodePtr;
{
TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
@@ -764,8 +766,7 @@ TransformInputProc (instanceData, buf, toRead, errorCodePtr)
*/
res = ExecuteCallback (dataPtr, NO_INTERP, A_READ,
- UCHARP (buf), read, TRANSMIT_IBUF,
- P_PRESERVE);
+ UCHARP (buf), read, TRANSMIT_IBUF, P_PRESERVE);
if (res != TCL_OK) {
*errorCodePtr = EINVAL;
@@ -796,7 +797,7 @@ TransformInputProc (instanceData, buf, toRead, errorCodePtr)
static int
TransformOutputProc (instanceData, buf, toWrite, errorCodePtr)
ClientData instanceData;
- char* buf;
+ CONST char* buf;
int toWrite;
int* errorCodePtr;
{
@@ -848,12 +849,11 @@ TransformOutputProc (instanceData, buf, toWrite, errorCodePtr)
static int
TransformSeekProc (instanceData, offset, mode, errorCodePtr)
- ClientData instanceData; /* The channel to manipulate */
- long offset; /* Size of movement. */
- int mode; /* How to move */
- int* errorCodePtr; /* Location of error flag. */
+ ClientData instanceData; /* The channel to manipulate */
+ long offset; /* Size of movement. */
+ int mode; /* How to move */
+ int* errorCodePtr; /* Location of error flag. */
{
- int result;
TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
Tcl_Channel parent = Tcl_GetStackedChannel(dataPtr->self);
Tcl_ChannelType* parentType = Tcl_GetChannelType(parent);
@@ -864,9 +864,8 @@ TransformSeekProc (instanceData, offset, mode, errorCodePtr)
* location. Simply pass the request down.
*/
- result = (*parentSeekProc) (Tcl_GetChannelInstanceData(parent),
+ return (*parentSeekProc) (Tcl_GetChannelInstanceData(parent),
offset, mode, errorCodePtr);
- return result;
}
/*
@@ -887,9 +886,104 @@ TransformSeekProc (instanceData, offset, mode, errorCodePtr)
dataPtr->readIsFlushed = 0;
}
- result = (*parentSeekProc) (Tcl_GetChannelInstanceData(parent),
+ return (*parentSeekProc) (Tcl_GetChannelInstanceData(parent),
offset, mode, errorCodePtr);
- return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TransformWideSeekProc --
+ *
+ * This procedure is called by the generic IO level to move the
+ * access point in a channel, with a (potentially) 64-bit offset.
+ *
+ * Side effects:
+ * Moves the location at which the channel will be accessed in
+ * future operations. Flushes all transformation buffers, then
+ * forwards it to the underlying channel.
+ *
+ * Result:
+ * -1 if failed, the new position if successful. An output
+ * argument contains the POSIX error code if an error occurred,
+ * or zero.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_WideInt
+TransformWideSeekProc (instanceData, offset, mode, errorCodePtr)
+ ClientData instanceData; /* The channel to manipulate */
+ Tcl_WideInt offset; /* Size of movement. */
+ int mode; /* How to move */
+ int* errorCodePtr; /* Location of error flag. */
+{
+ TransformChannelData* dataPtr =
+ (TransformChannelData*) instanceData;
+ Tcl_Channel parent =
+ Tcl_GetStackedChannel(dataPtr->self);
+ Tcl_ChannelType* parentType =
+ Tcl_GetChannelType(parent);
+ Tcl_DriverSeekProc* parentSeekProc =
+ Tcl_ChannelSeekProc(parentType);
+ Tcl_DriverWideSeekProc* parentWideSeekProc =
+ Tcl_ChannelWideSeekProc(parentType);
+ ClientData parentData =
+ Tcl_GetChannelInstanceData(parent);
+
+ if ((offset == Tcl_LongAsWide(0)) && (mode == SEEK_CUR)) {
+ /*
+ * This is no seek but a request to tell the caller the current
+ * location. Simply pass the request down.
+ */
+
+ if (parentWideSeekProc != NULL) {
+ return (*parentWideSeekProc) (parentData, offset, mode,
+ errorCodePtr);
+ }
+
+ return Tcl_LongAsWide((*parentSeekProc) (parentData, 0, mode,
+ errorCodePtr));
+ }
+
+ /*
+ * It is a real request to change the position. Flush all data waiting
+ * for output and discard everything in the input buffers. Then pass
+ * the request down, unchanged.
+ */
+
+ if (dataPtr->mode & TCL_WRITABLE) {
+ ExecuteCallback (dataPtr, NO_INTERP, A_FLUSH_WRITE,
+ NULL, 0, TRANSMIT_DOWN, P_NO_PRESERVE);
+ }
+
+ if (dataPtr->mode & TCL_READABLE) {
+ ExecuteCallback (dataPtr, NO_INTERP, A_CLEAR_READ,
+ NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
+ ResultClear(&dataPtr->result);
+ dataPtr->readIsFlushed = 0;
+ }
+
+ /*
+ * If we have a wide seek capability, we should stick with that.
+ */
+ if (parentWideSeekProc != NULL) {
+ return (*parentWideSeekProc) (parentData, offset, mode, errorCodePtr);
+ }
+
+ /*
+ * We're transferring to narrow seeks at this point; this is a bit
+ * complex because we have to check whether the seek is possible
+ * first (i.e. whether we are losing information in truncating the
+ * bits of the offset.) Luckily, there's a defined error for what
+ * happens when trying to go out of the representable range.
+ */
+ if (offset<Tcl_LongAsWide(LONG_MIN) || offset>Tcl_LongAsWide(LONG_MAX)) {
+ *errorCodePtr = EOVERFLOW;
+ return Tcl_LongAsWide(-1);
+ }
+ return Tcl_LongAsWide((*parentSeekProc) (parentData,
+ Tcl_WideAsLong(offset), mode, errorCodePtr));
}
/*
@@ -915,8 +1009,8 @@ static int
TransformSetOptionProc (instanceData, interp, optionName, value)
ClientData instanceData;
Tcl_Interp *interp;
- char *optionName;
- char *value;
+ CONST char *optionName;
+ CONST char *value;
{
TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
Tcl_Channel downChan = Tcl_GetStackedChannel(dataPtr->self);
@@ -953,7 +1047,7 @@ static int
TransformGetOptionProc (instanceData, interp, optionName, dsPtr)
ClientData instanceData;
Tcl_Interp* interp;
- char* optionName;
+ CONST char* optionName;
Tcl_DString* dsPtr;
{
TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
@@ -964,7 +1058,7 @@ TransformGetOptionProc (instanceData, interp, optionName, dsPtr)
if (getOptionProc != NULL) {
return (*getOptionProc)(Tcl_GetChannelInstanceData(downChan),
interp, optionName, dsPtr);
- } else if (optionName == (char*) NULL) {
+ } else if (optionName == (CONST char*) NULL) {
/*
* Request is query for all options, this is ok.
*/
@@ -1046,7 +1140,7 @@ TransformWatchProc (instanceData, mask)
* to flush that.
*/
- dataPtr->timer = Tcl_CreateTimerHandler (DELAY,
+ dataPtr->timer = Tcl_CreateTimerHandler (FLUSH_DELAY,
TransformChannelHandlerTimer, (ClientData) dataPtr);
}
}
@@ -1274,7 +1368,7 @@ static int
ResultCopy (r, buf, toRead)
ResultBuffer* r; /* The buffer to read from */
unsigned char* buf; /* The buffer to copy into */
- int toRead; /* Number of requested bytes */
+ int toRead; /* Number of requested bytes */
{
if (r->used == 0) {
/* Nothing to copy in the case of an empty buffer.
@@ -1337,7 +1431,7 @@ static void
ResultAdd (r, buf, toWrite)
ResultBuffer* r; /* The buffer to extend */
unsigned char* buf; /* The buffer to read from */
- int toWrite; /* The number of bytes in 'buf' */
+ int toWrite; /* The number of bytes in 'buf' */
{
if ((r->used + toWrite) > r->allocated) {
/* Extension of the internal buffer is required.
diff --git a/tcl/generic/tclIOSock.c b/tcl/generic/tclIOSock.c
index 031db7856dc..11228793149 100644
--- a/tcl/generic/tclIOSock.c
+++ b/tcl/generic/tclIOSock.c
@@ -43,7 +43,7 @@ TclSockGetPort(interp, string, proto, portPtr)
{
struct servent *sp; /* Protocol info for named services */
Tcl_DString ds;
- char *native;
+ CONST char *native;
if (Tcl_GetInt(NULL, string, portPtr) != TCL_OK) {
/*
@@ -91,10 +91,7 @@ TclSockMinimumBuffers(sock, size)
int size; /* Minimum buffer size */
{
int current;
- /*
- * Should be socklen_t, but HP10.20 (g)cc chokes
- */
- size_t len;
+ socklen_t len;
len = sizeof(int);
getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&current, &len);
@@ -110,4 +107,3 @@ TclSockMinimumBuffers(sock, size)
}
return TCL_OK;
}
-
diff --git a/tcl/generic/tclIOUtil.c b/tcl/generic/tclIOUtil.c
index 445a29d7108..af1bd03a837 100644
--- a/tcl/generic/tclIOUtil.c
+++ b/tcl/generic/tclIOUtil.c
@@ -1,8 +1,12 @@
/*
* tclIOUtil.c --
*
- * This file contains a collection of utility procedures that
- * are shared by the platform specific IO drivers.
+ * This file contains the implementation of Tcl's generic
+ * filesystem code, which supports a pluggable filesystem
+ * architecture allowing both platform specific filesystems and
+ * 'virtual filesystems'. All filesystem access should go through
+ * the functions defined in this file. Most of this code was
+ * contributed by Vince Darley.
*
* Parts of this file are based on code contributed by Karl
* Lehenbauer, Mark Diekhans and Peter da Silva.
@@ -18,7 +22,237 @@
#include "tclInt.h"
#include "tclPort.h"
+#ifdef MAC_TCL
+#include "tclMacInt.h"
+#endif
+#ifdef __WIN32__
+/* for tclWinProcs->useWide */
+#include "tclWinInt.h"
+#endif
+
+/*
+ * Prototypes for procedures defined later in this file.
+ */
+
+static void DupFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
+ Tcl_Obj *copyPtr));
+static void FreeFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *listPtr));
+static int SetFsPathFromAny _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr));
+static Tcl_Obj* FSNormalizeAbsolutePath
+ _ANSI_ARGS_((Tcl_Interp* interp, Tcl_Obj *pathPtr));
+static int TclNormalizeToUniquePath
+ _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *pathPtr));
+static int SetFsPathFromAbsoluteNormalized
+ _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *objPtr));
+static int FindSplitPos _ANSI_ARGS_((char *path, char *separator));
+static Tcl_PathType FSGetPathType _ANSI_ARGS_((Tcl_Obj *pathObjPtr,
+ Tcl_Filesystem **filesystemPtrPtr,
+ int *driveNameLengthPtr));
+static Tcl_PathType GetPathType _ANSI_ARGS_((Tcl_Obj *pathObjPtr,
+ Tcl_Filesystem **filesystemPtrPtr,
+ int *driveNameLengthPtr, Tcl_Obj **driveNameRef));
+
+/*
+ * Define the 'path' object type, which Tcl uses to represent
+ * file paths internally.
+ */
+Tcl_ObjType tclFsPathType = {
+ "path", /* name */
+ FreeFsPathInternalRep, /* freeIntRepProc */
+ DupFsPathInternalRep, /* dupIntRepProc */
+ NULL, /* updateStringProc */
+ SetFsPathFromAny /* setFromAnyProc */
+};
+
+/*
+ * These form part of the native filesystem support. They are needed
+ * here because we have a few native filesystem functions (which are
+ * the same for mac/win/unix) in this file. There is no need to place
+ * them in tclInt.h, because they are not (and should not be) used
+ * anywhere else.
+ */
+extern CONST char * tclpFileAttrStrings[];
+extern CONST TclFileAttrProcs tclpFileAttrProcs[];
+
+/*
+ * The following functions are obsolete string based APIs, and should
+ * be removed in a future release (Tcl 9 would be a good time).
+ */
+/* Obsolete */
+int
+Tcl_Stat(path, oldStyleBuf)
+ CONST char *path; /* Path of file to stat (in current CP). */
+ struct stat *oldStyleBuf; /* Filled with results of stat call. */
+{
+ int ret;
+ Tcl_StatBuf buf;
+ Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1);
+
+ Tcl_IncrRefCount(pathPtr);
+ ret = Tcl_FSStat(pathPtr, &buf);
+ Tcl_DecrRefCount(pathPtr);
+ if (ret != -1) {
+#ifndef TCL_WIDE_INT_IS_LONG
+# define OUT_OF_RANGE(x) \
+ (((Tcl_WideInt)(x)) < Tcl_LongAsWide(LONG_MIN) || \
+ ((Tcl_WideInt)(x)) > Tcl_LongAsWide(LONG_MAX))
+# define OUT_OF_URANGE(x) \
+ (((Tcl_WideUInt)(x)) > (Tcl_WideUInt)ULONG_MAX)
+
+ /*
+ * Perform the result-buffer overflow check manually.
+ *
+ * Note that ino_t/ino64_t is unsigned...
+ */
+
+ if (OUT_OF_URANGE(buf.st_ino) || OUT_OF_RANGE(buf.st_size)
+#ifdef HAVE_ST_BLOCKS
+ || OUT_OF_RANGE(buf.st_blocks)
+#endif
+ ) {
+#ifdef EFBIG
+ errno = EFBIG;
+#else
+# ifdef EOVERFLOW
+ errno = EOVERFLOW;
+# else
+# error "What status should be returned for file size out of range?"
+# endif
+#endif
+ return -1;
+ }
+
+# undef OUT_OF_RANGE
+# undef OUT_OF_URANGE
+#endif /* !TCL_WIDE_INT_IS_LONG */
+
+ /*
+ * Copy across all supported fields, with possible type
+ * coercions on those fields that change between the normal
+ * and lf64 versions of the stat structure (on Solaris at
+ * least.) This is slow when the structure sizes coincide,
+ * but that's what you get for using an obsolete interface.
+ */
+
+ oldStyleBuf->st_mode = buf.st_mode;
+ oldStyleBuf->st_ino = (ino_t) buf.st_ino;
+ oldStyleBuf->st_dev = buf.st_dev;
+ oldStyleBuf->st_rdev = buf.st_rdev;
+ oldStyleBuf->st_nlink = buf.st_nlink;
+ oldStyleBuf->st_uid = buf.st_uid;
+ oldStyleBuf->st_gid = buf.st_gid;
+ oldStyleBuf->st_size = (off_t) buf.st_size;
+ oldStyleBuf->st_atime = buf.st_atime;
+ oldStyleBuf->st_mtime = buf.st_mtime;
+ oldStyleBuf->st_ctime = buf.st_ctime;
+#ifdef HAVE_ST_BLOCKS
+ oldStyleBuf->st_blksize = buf.st_blksize;
+ oldStyleBuf->st_blocks = (blkcnt_t) buf.st_blocks;
+#endif
+ }
+ return ret;
+}
+
+/* Obsolete */
+int
+Tcl_Access(path, mode)
+ CONST char *path; /* Path of file to access (in current CP). */
+ int mode; /* Permission setting. */
+{
+ int ret;
+ Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1);
+ Tcl_IncrRefCount(pathPtr);
+ ret = Tcl_FSAccess(pathPtr,mode);
+ Tcl_DecrRefCount(pathPtr);
+ return ret;
+}
+
+/* Obsolete */
+Tcl_Channel
+Tcl_OpenFileChannel(interp, path, modeString, permissions)
+ Tcl_Interp *interp; /* Interpreter for error reporting;
+ * can be NULL. */
+ CONST char *path; /* Name of file to open. */
+ CONST char *modeString; /* A list of POSIX open modes or
+ * a string such as "rw". */
+ int permissions; /* If the open involves creating a
+ * file, with what modes to create
+ * it? */
+{
+ Tcl_Channel ret;
+ Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1);
+ Tcl_IncrRefCount(pathPtr);
+ ret = Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions);
+ Tcl_DecrRefCount(pathPtr);
+ return ret;
+
+}
+
+/* Obsolete */
+int
+Tcl_Chdir(dirName)
+ CONST char *dirName;
+{
+ int ret;
+ Tcl_Obj *pathPtr = Tcl_NewStringObj(dirName,-1);
+ Tcl_IncrRefCount(pathPtr);
+ ret = Tcl_FSChdir(pathPtr);
+ Tcl_DecrRefCount(pathPtr);
+ return ret;
+}
+
+/* Obsolete */
+char *
+Tcl_GetCwd(interp, cwdPtr)
+ Tcl_Interp *interp;
+ Tcl_DString *cwdPtr;
+{
+ Tcl_Obj *cwd;
+ cwd = Tcl_FSGetCwd(interp);
+ if (cwd == NULL) {
+ return NULL;
+ } else {
+ Tcl_DStringInit(cwdPtr);
+ Tcl_DStringAppend(cwdPtr, Tcl_GetString(cwd), -1);
+ Tcl_DecrRefCount(cwd);
+ return Tcl_DStringValue(cwdPtr);
+ }
+}
+
+/* Obsolete */
+int
+Tcl_EvalFile(interp, fileName)
+ Tcl_Interp *interp; /* Interpreter in which to process file. */
+ CONST char *fileName; /* Name of file to process. Tilde-substitution
+ * will be performed on this name. */
+{
+ int ret;
+ Tcl_Obj *pathPtr = Tcl_NewStringObj(fileName,-1);
+ Tcl_IncrRefCount(pathPtr);
+ ret = Tcl_FSEvalFile(interp, pathPtr);
+ Tcl_DecrRefCount(pathPtr);
+ return ret;
+}
+
+
+/*
+ * The 3 hooks for Stat, Access and OpenFileChannel are obsolete. The
+ * complete, general hooked filesystem APIs should be used instead.
+ * This define decides whether to include the obsolete hooks and
+ * related code. If these are removed, we'll also want to remove them
+ * from stubs/tclInt. The only known users of these APIs are prowrap
+ * and mktclapp. New code/extensions should not use them, since they
+ * do not provide as full support as the full filesystem API.
+ *
+ * As soon as prowrap and mktclapp are updated to use the full
+ * filesystem support, I suggest all these hooks are removed.
+ */
+#define USE_OBSOLETE_FS_HOOKS
+
+
+#ifdef USE_OBSOLETE_FS_HOOKS
/*
* The following typedef declarations allow for hooking into the chain
* of functions maintained for 'Tcl_Stat(...)', 'Tcl_Access(...)' &
@@ -45,10 +279,10 @@ typedef struct OpenFileChannelProc {
} OpenFileChannelProc;
/*
- * For each type of hookable function, a static node is declared to
- * hold the function pointer for the "built-in" routine (e.g.
- * 'TclpStat(...)') and the respective list is initialized as a pointer
- * to that node.
+ * For each type of (obsolete) hookable function, a static node is
+ * declared to hold the function pointer for the "built-in" routine
+ * (e.g. 'TclpStat(...)') and the respective list is initialized as a
+ * pointer to that node.
*
* The "delete" functions (e.g. 'TclStatDeleteProc(...)') ensure that
* these statically declared list entry cannot be inadvertently removed.
@@ -56,26 +290,829 @@ typedef struct OpenFileChannelProc {
* This method avoids the need to call any sort of "initialization"
* function.
*
- * All three lists are protected by a global hookMutex.
+ * All three lists are protected by a global obsoleteFsHookMutex.
*/
-static StatProc defaultStatProc = {
- &TclpStat, NULL
-};
-static StatProc *statProcList = &defaultStatProc;
+static StatProc *statProcList = NULL;
+static AccessProc *accessProcList = NULL;
+static OpenFileChannelProc *openFileChannelProcList = NULL;
+
+TCL_DECLARE_MUTEX(obsoleteFsHookMutex)
+
+#endif /* USE_OBSOLETE_FS_HOOKS */
-static AccessProc defaultAccessProc = {
- &TclpAccess, NULL
+/*
+ * A filesystem record is used to keep track of each
+ * filesystem currently registered with the core,
+ * in a linked list.
+ */
+typedef struct FilesystemRecord {
+ ClientData clientData; /* Client specific data for the new
+ * filesystem (can be NULL) */
+ Tcl_Filesystem *fsPtr; /* Pointer to filesystem dispatch
+ * table. */
+ int fileRefCount; /* How many Tcl_Obj's use this
+ * filesystem. */
+ struct FilesystemRecord *nextPtr;
+ /* The next filesystem registered
+ * to Tcl, or NULL if no more. */
+} FilesystemRecord;
+
+static FilesystemRecord* GetFilesystemRecord
+ _ANSI_ARGS_((Tcl_Filesystem *fromFilesystem, int *epoch));
+
+/*
+ * Declare the native filesystem support. These functions should
+ * be considered private to Tcl, and should really not be called
+ * directly by any code other than this file (i.e. neither by
+ * Tcl's core nor by extensions). Similarly, the old string-based
+ * Tclp... native filesystem functions should not be called.
+ *
+ * The correct API to use now is the Tcl_FS... set of functions,
+ * which ensure correct and complete virtual filesystem support.
+ *
+ * We cannot make all of these static, since some of them
+ * are implemented in the platform-specific directories.
+ */
+static Tcl_FSPathInFilesystemProc NativePathInFilesystem;
+static Tcl_FSFilesystemSeparatorProc NativeFilesystemSeparator;
+static Tcl_FSFreeInternalRepProc NativeFreeInternalRep;
+static Tcl_FSDupInternalRepProc NativeDupInternalRep;
+static Tcl_FSCreateInternalRepProc NativeCreateNativeRep;
+static Tcl_FSFileAttrStringsProc NativeFileAttrStrings;
+static Tcl_FSFileAttrsGetProc NativeFileAttrsGet;
+static Tcl_FSFileAttrsSetProc NativeFileAttrsSet;
+static Tcl_FSUtimeProc NativeUtime;
+
+/*
+ * The only reason these functions are not static is that they
+ * are either called by code in the native (win/unix/mac) directories
+ * or they are actually implemented in those directories. They
+ * should simply not be called by code outside Tcl's native
+ * filesystem core. i.e. they should be considered 'static' to
+ * Tcl's filesystem code (if we ever built the native filesystem
+ * support into a separate code library, this could actually be
+ * enforced).
+ */
+Tcl_FSFilesystemPathTypeProc TclpFilesystemPathType;
+Tcl_FSInternalToNormalizedProc TclpNativeToNormalized;
+Tcl_FSStatProc TclpObjStat;
+Tcl_FSAccessProc TclpObjAccess;
+Tcl_FSMatchInDirectoryProc TclpMatchInDirectory;
+Tcl_FSGetCwdProc TclpObjGetCwd;
+Tcl_FSChdirProc TclpObjChdir;
+Tcl_FSLstatProc TclpObjLstat;
+Tcl_FSCopyFileProc TclpObjCopyFile;
+Tcl_FSDeleteFileProc TclpObjDeleteFile;
+Tcl_FSRenameFileProc TclpObjRenameFile;
+Tcl_FSCreateDirectoryProc TclpObjCreateDirectory;
+Tcl_FSCopyDirectoryProc TclpObjCopyDirectory;
+Tcl_FSRemoveDirectoryProc TclpObjRemoveDirectory;
+Tcl_FSUnloadFileProc TclpUnloadFile;
+Tcl_FSLinkProc TclpObjLink;
+Tcl_FSListVolumesProc TclpObjListVolumes;
+
+/*
+ * Define the native filesystem dispatch table. If necessary, it
+ * is ok to make this non-static, but it should only be accessed
+ * by the functions actually listed within it (or perhaps other
+ * helper functions of them). Anything which is not part of this
+ * 'native filesystem implementation' should not be delving inside
+ * here!
+ */
+static Tcl_Filesystem tclNativeFilesystem = {
+ "native",
+ sizeof(Tcl_Filesystem),
+ TCL_FILESYSTEM_VERSION_1,
+ &NativePathInFilesystem,
+ &NativeDupInternalRep,
+ &NativeFreeInternalRep,
+ &TclpNativeToNormalized,
+ &NativeCreateNativeRep,
+ &TclpObjNormalizePath,
+ &TclpFilesystemPathType,
+ &NativeFilesystemSeparator,
+ &TclpObjStat,
+ &TclpObjAccess,
+ &TclpOpenFileChannel,
+ &TclpMatchInDirectory,
+ &NativeUtime,
+#ifndef S_IFLNK
+ NULL,
+#else
+ &TclpObjLink,
+#endif /* S_IFLNK */
+ &TclpObjListVolumes,
+ &NativeFileAttrStrings,
+ &NativeFileAttrsGet,
+ &NativeFileAttrsSet,
+ &TclpObjCreateDirectory,
+ &TclpObjRemoveDirectory,
+ &TclpObjDeleteFile,
+ &TclpObjCopyFile,
+ &TclpObjRenameFile,
+ &TclpObjCopyDirectory,
+ &TclpObjLstat,
+ &TclpDlopen,
+ &TclpObjGetCwd,
+ &TclpObjChdir
};
-static AccessProc *accessProcList = &defaultAccessProc;
-static OpenFileChannelProc defaultOpenFileChannelProc = {
- &TclpOpenFileChannel, NULL
+/*
+ * Define the tail of the linked list. Note that for unconventional
+ * uses of Tcl without a native filesystem, we may in the future wish
+ * to modify the current approach of hard-coding the native filesystem
+ * in the lookup list 'filesystemList' below.
+ *
+ * We initialize the record so that it thinks one file uses it. This
+ * means it will never be freed.
+ */
+static FilesystemRecord nativeFilesystemRecord = {
+ NULL,
+ &tclNativeFilesystem,
+ 1,
+ NULL
};
-static OpenFileChannelProc *openFileChannelProcList =
- &defaultOpenFileChannelProc;
-TCL_DECLARE_MUTEX(hookMutex)
+/*
+ * The following few variables are protected by the
+ * filesystemMutex just below.
+ */
+
+/*
+ * This is incremented each time we modify the linked list of
+ * filesystems. Any time it changes, all cached filesystem
+ * representations are suspect and must be freed.
+ */
+static int theFilesystemEpoch = 0;
+
+/*
+ * Stores the linked list of filesystems.
+ */
+static FilesystemRecord *filesystemList = &nativeFilesystemRecord;
+
+/*
+ * The number of loops which are currently iterating over the linked
+ * list. If this is greater than zero, we can't modify the list.
+ */
+static int filesystemIteratorsInProgress = 0;
+
+/*
+ * Someone wants to modify the list of filesystems if this is set.
+ */
+static int filesystemWantToModify = 0;
+
+#ifdef TCL_THREADS
+static Tcl_Condition filesystemOkToModify = NULL;
+#endif
+
+TCL_DECLARE_MUTEX(filesystemMutex)
+
+/*
+ * struct FsPath --
+ *
+ * Internal representation of a Tcl_Obj of "path" type. This
+ * can be used to represent relative or absolute paths, and has
+ * certain optimisations when used to represent paths which are
+ * already normalized and absolute.
+ *
+ * Note that 'normPathPtr' can be a circular reference to the
+ * container Tcl_Obj of this FsPath.
+ */
+typedef struct FsPath {
+ Tcl_Obj *translatedPathPtr; /* Name without any ~user sequences.
+ * If this is NULL, then this is a
+ * pure normalized, absolute path
+ * object, in which the parent Tcl_Obj's
+ * string rep is already both translated
+ * and normalized. */
+ Tcl_Obj *normPathPtr; /* Normalized absolute path, without
+ * ., .. or ~user sequences. If the
+ * Tcl_Obj containing
+ * this FsPath is already normalized,
+ * this may be a circular reference back
+ * to the container. If that is NOT the
+ * case, we have a refCount on the object. */
+ Tcl_Obj *cwdPtr; /* If null, path is absolute, else
+ * this points to the cwd object used
+ * for this path. We have a refCount
+ * on the object. */
+ ClientData nativePathPtr; /* Native representation of this path,
+ * which is filesystem dependent. */
+ int filesystemEpoch; /* Used to ensure the path representation
+ * was generated during the correct
+ * filesystem epoch. The epoch changes
+ * when filesystem-mounts are changed. */
+ struct FilesystemRecord *fsRecPtr;
+ /* Pointer to the filesystem record
+ * entry to use for this path. */
+} FsPath;
+
+/*
+ * Used to implement Tcl_FSGetCwd in a file-system independent way.
+ * This is protected by the cwdMutex below.
+ */
+static Tcl_Obj* cwdPathPtr = NULL;
+TCL_DECLARE_MUTEX(cwdMutex)
+
+/*
+ * Declare fallback support function and
+ * information for Tcl_FSLoadFile
+ */
+static Tcl_FSUnloadFileProc FSUnloadTempFile;
+
+/*
+ * One of these structures is used each time we successfully load a
+ * file from a file system by way of making a temporary copy of the
+ * file on the native filesystem. We need to store both the actual
+ * unloadProc/clientData combination which was used, and the original
+ * and modified filenames, so that we can correctly undo the entire
+ * operation when we want to unload the code.
+ */
+typedef struct FsDivertLoad {
+ Tcl_LoadHandle loadHandle;
+ Tcl_FSUnloadFileProc *unloadProcPtr;
+ Tcl_Obj *divertedFile;
+ Tcl_Filesystem *divertedFilesystem;
+ ClientData divertedFileNativeRep;
+} FsDivertLoad;
+
+/* Now move on to the basic filesystem implementation */
+
+
+static int
+FsCwdPointerEquals(objPtr)
+ Tcl_Obj* objPtr;
+{
+ Tcl_MutexLock(&cwdMutex);
+ if (cwdPathPtr == objPtr) {
+ Tcl_MutexUnlock(&cwdMutex);
+ return 1;
+ } else {
+ Tcl_MutexUnlock(&cwdMutex);
+ return 0;
+ }
+}
+
+
+static FilesystemRecord*
+FsGetIterator(void) {
+ Tcl_MutexLock(&filesystemMutex);
+ filesystemIteratorsInProgress++;
+ Tcl_MutexUnlock(&filesystemMutex);
+ /* Now we know the list of filesystems cannot be modified */
+ return filesystemList;
+}
+
+static void
+FsReleaseIterator(void) {
+ Tcl_MutexLock(&filesystemMutex);
+ filesystemIteratorsInProgress--;
+ if (filesystemIteratorsInProgress == 0) {
+ /* Notify any waiting threads that things are ok now */
+ if (filesystemWantToModify > 0) {
+ Tcl_ConditionNotify(&filesystemOkToModify);
+ }
+ }
+ Tcl_MutexUnlock(&filesystemMutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclFinalizeFilesystem --
+ *
+ * Clean up the filesystem. After this, calls to all Tcl_FS...
+ * functions will fail.
+ *
+ * Note that, since 'TclFinalizeLoad' may unload extensions
+ * which implement other filesystems, and which may therefore
+ * contain a 'freeProc' for those filesystems, at this stage
+ * we _must_ have freed all objects of "path" type, or we may
+ * end up with segfaults if we try to free them later.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Frees any memory allocated by the filesystem. Unloads any
+ * extensions which have been loaded.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclFinalizeFilesystem() {
+ /*
+ * Assumption that only one thread is active now. Otherwise
+ * we would need to put various mutexes around this code.
+ */
+
+ if (cwdPathPtr != NULL) {
+ Tcl_DecrRefCount(cwdPathPtr);
+ cwdPathPtr = NULL;
+ }
+
+ /*
+ * We defer unloading of packages until very late
+ * to avoid memory access issues. Both exit callbacks and
+ * synchronization variables may be stored in packages.
+ *
+ * Note that TclFinalizeLoad unloads packages in the reverse
+ * of the order they were loaded in (i.e. last to be loaded
+ * is the first to be unloaded). This can be important for
+ * correct unloading when dependencies exist.
+ */
+
+ TclFinalizeLoad();
+
+ /* Remove all filesystems, freeing any allocated memory */
+ while (filesystemList != NULL) {
+ FilesystemRecord *tmpFsRecPtr = filesystemList->nextPtr;
+ if (filesystemList->fileRefCount > 1) {
+ /*
+ * We are freeing a filesystem which actually has
+ * path objects still around which belong to it.
+ * This is probably bad, but since we are exiting,
+ * we don't do anything about it.
+ */
+ }
+ /* The native filesystem is static, so we don't free it */
+ if (filesystemList != &nativeFilesystemRecord) {
+ ckfree((char *)filesystemList);
+ }
+ filesystemList = tmpFsRecPtr;
+ }
+ /* Now filesystemList is NULL */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSRegister --
+ *
+ * Insert the filesystem function table at the head of the list of
+ * functions which are used during calls to all file-system
+ * operations. The filesystem will be added even if it is
+ * already in the list. (You can use Tcl_FSData to
+ * check if it is in the list, provided the ClientData used was
+ * not NULL).
+ *
+ * Note that the filesystem handling is head-to-tail of the list.
+ * Each filesystem is asked in turn whether it can handle a
+ * particular request, _until_ one of them says 'yes'. At that
+ * point no further filesystems are asked.
+ *
+ * In particular this means if you want to add a diagnostic
+ * filesystem (which simply reports all fs activity), it must be
+ * at the head of the list: i.e. it must be the last registered.
+ *
+ * Results:
+ * Normally TCL_OK; TCL_ERROR if memory for a new node in the list
+ * could not be allocated.
+ *
+ * Side effects:
+ * Memory allocated and modifies the link list for filesystems.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_FSRegister(clientData, fsPtr)
+ ClientData clientData; /* Client specific data for this fs */
+ Tcl_Filesystem *fsPtr; /* The filesystem record for the new fs. */
+{
+ FilesystemRecord *newFilesystemPtr;
+
+ if (fsPtr == NULL) {
+ return TCL_ERROR;
+ }
+
+ newFilesystemPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord));
+
+ newFilesystemPtr->clientData = clientData;
+ newFilesystemPtr->fsPtr = fsPtr;
+ /*
+ * We start with a refCount of 1. If this drops to zero, then
+ * anyone is welcome to ckfree us.
+ */
+ newFilesystemPtr->fileRefCount = 1;
+
+ /*
+ * Is this lock and wait strictly speaking necessary? Since any
+ * iterators out there will have grabbed a copy of the head of
+ * the list and be iterating away from that, if we add a new
+ * element to the head of the list, it can't possibly have any
+ * effect on any of their loops. In fact it could be better not
+ * to wait, since we are adjusting the filesystem epoch, any
+ * cached representations calculated by existing iterators are
+ * going to have to be thrown away anyway.
+ *
+ * However, since registering and unregistering filesystems is
+ * a very rare action, this is not a very important point.
+ */
+ Tcl_MutexLock(&filesystemMutex);
+ if (filesystemIteratorsInProgress) {
+ filesystemWantToModify++;
+ Tcl_ConditionWait(&filesystemOkToModify, &filesystemMutex, NULL);
+ filesystemWantToModify--;
+ }
+
+ newFilesystemPtr->nextPtr = filesystemList;
+ filesystemList = newFilesystemPtr;
+ /*
+ * Increment the filesystem epoch counter, since existing paths
+ * might conceivably now belong to different filesystems.
+ */
+ theFilesystemEpoch++;
+ Tcl_MutexUnlock(&filesystemMutex);
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSUnregister --
+ *
+ * Remove the passed filesystem from the list of filesystem
+ * function tables. It also ensures that the built-in
+ * (native) filesystem is not removable, although we may wish
+ * to change that decision in the future to allow a smaller
+ * Tcl core, in which the native filesystem is not used at
+ * all (we could, say, initialise Tcl completely over a network
+ * connection).
+ *
+ * Results:
+ * TCL_OK if the procedure pointer was successfully removed,
+ * TCL_ERROR otherwise.
+ *
+ * Side effects:
+ * Memory may be deallocated (or will be later, once no "path"
+ * objects refer to this filesystem), but the list of registered
+ * filesystems is updated immediately.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_FSUnregister(fsPtr)
+ Tcl_Filesystem *fsPtr; /* The filesystem record to remove. */
+{
+ int retVal = TCL_ERROR;
+ FilesystemRecord *tmpFsRecPtr;
+ FilesystemRecord *prevFsRecPtr = NULL;
+
+ Tcl_MutexLock(&filesystemMutex);
+ if (filesystemIteratorsInProgress) {
+ filesystemWantToModify++;
+ Tcl_ConditionWait(&filesystemOkToModify, &filesystemMutex, NULL);
+ filesystemWantToModify--;
+ }
+ tmpFsRecPtr = filesystemList;
+ /*
+ * Traverse the 'filesystemList' looking for the particular node
+ * whose 'fsPtr' member matches 'fsPtr' and remove that one from
+ * the list. Ensure that the "default" node cannot be removed.
+ */
+
+ while ((retVal == TCL_ERROR) && (tmpFsRecPtr != &nativeFilesystemRecord)) {
+ if (tmpFsRecPtr->fsPtr == fsPtr) {
+ if (prevFsRecPtr == NULL) {
+ filesystemList = filesystemList->nextPtr;
+ } else {
+ prevFsRecPtr->nextPtr = tmpFsRecPtr->nextPtr;
+ }
+ /*
+ * Increment the filesystem epoch counter, since existing
+ * paths might conceivably now belong to different
+ * filesystems. This should also ensure that paths which
+ * have cached the filesystem which is about to be deleted
+ * do not reference that filesystem (which would of course
+ * lead to memory exceptions).
+ */
+ theFilesystemEpoch++;
+
+ tmpFsRecPtr->fileRefCount--;
+ if (tmpFsRecPtr->fileRefCount <= 0) {
+ ckfree((char *)tmpFsRecPtr);
+ }
+
+ retVal = TCL_OK;
+ } else {
+ prevFsRecPtr = tmpFsRecPtr;
+ tmpFsRecPtr = tmpFsRecPtr->nextPtr;
+ }
+ }
+
+ Tcl_MutexUnlock(&filesystemMutex);
+ return (retVal);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSMountsChanged --
+ *
+ * Notify the filesystem that the available mounted filesystems
+ * (or within any one filesystem type, the number or location of
+ * mount points) have changed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The global filesystem variable 'theFilesystemEpoch' is
+ * incremented. The effect of this is to make all cached
+ * path representations invalid. Clearly it should only therefore
+ * be called when it is really required! There are a few
+ * circumstances when it should be called:
+ *
+ * (1) when a new filesystem is registered or unregistered.
+ * Strictly speaking this is only necessary if the new filesystem
+ * accepts file paths as is (normally the filesystem itself is
+ * really a shell which hasn't yet had any mount points established
+ * and so its 'pathInFilesystem' proc will always fail). However,
+ * for safety, Tcl always calls this for you in these circumstances.
+ *
+ * (2) when additional mount points are established inside any
+ * existing filesystem (except the native fs)
+ *
+ * (3) when any filesystem (except the native fs) changes the list
+ * of available volumes.
+ *
+ * (4) when the mapping from a string representation of a file to
+ * a full, normalized path changes. For example, if 'env(HOME)'
+ * is modified, then any path containing '~' will map to a different
+ * filesystem location. Therefore all such paths need to have
+ * their internal representation invalidated.
+ *
+ * Tcl has no control over (2) and (3), so any registered filesystem
+ * must make sure it calls this function when those situations
+ * occur.
+ *
+ * (Note: the reason for the exception in 2,3 for the native
+ * filesystem is that the native filesystem by default claims all
+ * unknown files even if it really doesn't understand them or if
+ * they don't exist).
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_FSMountsChanged(fsPtr)
+ Tcl_Filesystem *fsPtr;
+{
+ /*
+ * We currently don't do anything with this parameter. We
+ * could in the future only invalidate files for this filesystem
+ * or otherwise take more advanced action.
+ */
+ (void)fsPtr;
+ /*
+ * Increment the filesystem epoch counter, since existing paths
+ * might now belong to different filesystems.
+ */
+ Tcl_MutexLock(&filesystemMutex);
+ theFilesystemEpoch++;
+ Tcl_MutexUnlock(&filesystemMutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSData --
+ *
+ * Retrieve the clientData field for the filesystem given,
+ * or NULL if that filesystem is not registered.
+ *
+ * Results:
+ * A clientData value, or NULL. Note that if the filesystem
+ * was registered with a NULL clientData field, this function
+ * will return that NULL value.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ClientData
+Tcl_FSData(fsPtr)
+ Tcl_Filesystem *fsPtr; /* The filesystem record to query. */
+{
+ ClientData retVal = NULL;
+ FilesystemRecord *tmpFsRecPtr;
+
+ tmpFsRecPtr = FsGetIterator();
+ /*
+ * Traverse the 'filesystemList' looking for the particular node
+ * whose 'fsPtr' member matches 'fsPtr' and remove that one from
+ * the list. Ensure that the "default" node cannot be removed.
+ */
+
+ while ((retVal == NULL) && (tmpFsRecPtr != NULL)) {
+ if (tmpFsRecPtr->fsPtr == fsPtr) {
+ retVal = tmpFsRecPtr->clientData;
+ }
+ tmpFsRecPtr = tmpFsRecPtr->nextPtr;
+ }
+
+ FsReleaseIterator();
+ return (retVal);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FSNormalizeAbsolutePath --
+ *
+ * Description:
+ * Takes an absolute path specification and computes a 'normalized'
+ * path from it.
+ *
+ * A normalized path is one which has all '../', './' removed.
+ * Also it is one which is in the 'standard' format for the native
+ * platform. On MacOS, Unix, this means the path must be free of
+ * symbolic links/aliases, and on Windows it means we want the
+ * long form, with that long form's case-dependence (which gives
+ * us a unique, case-dependent path).
+ *
+ * The behaviour of this function if passed a non-absolute path
+ * is NOT defined.
+ *
+ * Results:
+ * The result is returned in a Tcl_Obj with a refCount of 1,
+ * which is therefore owned by the caller. It must be
+ * freed (with Tcl_DecrRefCount) by the caller when no longer needed.
+ *
+ * Side effects:
+ * None (beyond the memory allocation for the result).
+ *
+ * Special note:
+ * This code is based on code from Matt Newman and Jean-Claude
+ * Wippler, with additions from Vince Darley and is copyright
+ * those respective authors.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Tcl_Obj*
+FSNormalizeAbsolutePath(interp, pathPtr)
+ Tcl_Interp* interp; /* Interpreter to use */
+ Tcl_Obj *pathPtr; /* Absolute path to normalize */
+{
+ int splen = 0, nplen, i;
+ Tcl_Obj *retVal;
+ Tcl_Obj *split;
+
+ /* Split has refCount zero */
+ split = Tcl_FSSplitPath(pathPtr, &splen);
+
+ /*
+ * Modify the list of entries in place, by removing '.', and
+ * removing '..' and the entry before -- unless that entry before
+ * is the top-level entry, i.e. the name of a volume.
+ */
+ nplen = 0;
+ for (i = 0;i < splen;i++) {
+ Tcl_Obj *elt;
+ Tcl_ListObjIndex(NULL, split, nplen, &elt);
+
+ if (strcmp(Tcl_GetString(elt), ".") == 0) {
+ Tcl_ListObjReplace(NULL, split, nplen, 1, 0, NULL);
+ } else if (strcmp(Tcl_GetString(elt), "..") == 0) {
+ if (nplen > 1) {
+ nplen--;
+ Tcl_ListObjReplace(NULL, split, nplen, 2, 0, NULL);
+ } else {
+ Tcl_ListObjReplace(NULL, split, nplen, 1, 0, NULL);
+ }
+ } else {
+ nplen++;
+ }
+ }
+ if (nplen > 0) {
+ retVal = Tcl_FSJoinPath(split, nplen);
+ /*
+ * Now we have an absolute path, with no '..', '.' sequences,
+ * but it still may not be in 'unique' form, depending on the
+ * platform. For instance, Unix is case-sensitive, so the
+ * path is ok. Windows is case-insensitive, and also has the
+ * weird 'longname/shortname' thing (e.g. C:/Program Files/ and
+ * C:/Progra~1/ are equivalent). MacOS is case-insensitive.
+ *
+ * Virtual file systems which may be registered may have
+ * other criteria for normalizing a path.
+ */
+ Tcl_IncrRefCount(retVal);
+ TclNormalizeToUniquePath(interp, retVal);
+ /*
+ * Since we know it is a normalized path, we can
+ * actually convert this object into an FsPath for
+ * greater efficiency
+ */
+ SetFsPathFromAbsoluteNormalized(interp, retVal);
+ } else {
+ /* Init to an empty string */
+ retVal = Tcl_NewStringObj("",0);
+ Tcl_IncrRefCount(retVal);
+ }
+ /*
+ * We increment and then decrement the refCount of split to free
+ * it. We do this right at the end, in case there are
+ * optimisations in Tcl_FSJoinPath(split, nplen) above which would
+ * let it make use of split more effectively if it has a refCount
+ * of zero. Also we can't just decrement the ref count, in case
+ * 'split' was actually returned by the join call above, in a
+ * single-element optimisation when nplen == 1.
+ */
+ Tcl_IncrRefCount(split);
+ Tcl_DecrRefCount(split);
+
+ /* This has a refCount of 1 for the caller */
+ return retVal;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclNormalizeToUniquePath --
+ *
+ * Description:
+ * Takes a path specification containing no ../, ./ sequences,
+ * and converts it into a unique path for the given platform.
+ * On MacOS, Unix, this means the path must be free of
+ * symbolic links/aliases, and on Windows it means we want the
+ * long form, with that long form's case-dependence (which gives
+ * us a unique, case-dependent path).
+ *
+ * Results:
+ * The result is returned in a Tcl_Obj with a refCount of 1,
+ * which is therefore owned by the caller. It must be
+ * freed (with Tcl_DecrRefCount) by the caller when no longer needed.
+ *
+ * Side effects:
+ * None (beyond the memory allocation for the result).
+ *
+ * Special note:
+ * This is only used by the above function. Also if the
+ * filesystem-specific normalizePathProcs can re-introduce
+ * ../, ./ sequences into the path, then this function will
+ * not return the correct result. This may be possible with
+ * symbolic links on unix/macos.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+TclNormalizeToUniquePath(interp, pathPtr)
+ Tcl_Interp *interp;
+ Tcl_Obj *pathPtr;
+{
+ FilesystemRecord *fsRecPtr;
+ int retVal = 0;
+
+ /*
+ * Call each of the "normalise path" functions in succession. This is
+ * a special case, in which if we have a native filesystem handler,
+ * we call it first. This is because the root of Tcl's filesystem
+ * is always a native filesystem (i.e. '/' on unix is native).
+ */
+
+ fsRecPtr = FsGetIterator();
+ while (fsRecPtr != NULL) {
+ if (fsRecPtr == &nativeFilesystemRecord) {
+ Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc;
+ if (proc != NULL) {
+ retVal = (*proc)(interp, pathPtr, retVal);
+ }
+ break;
+ }
+ fsRecPtr = fsRecPtr->nextPtr;
+ }
+ FsReleaseIterator();
+
+ fsRecPtr = FsGetIterator();
+ while (fsRecPtr != NULL) {
+ /* Skip the native system next time through */
+ if (fsRecPtr != &nativeFilesystemRecord) {
+ Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc;
+ if (proc != NULL) {
+ retVal = (*proc)(interp, pathPtr, retVal);
+ }
+ /*
+ * We could add an efficiency check like this:
+ *
+ * if (retVal == length-of(pathPtr)) {break;}
+ *
+ * but there's not much benefit.
+ */
+ }
+ fsRecPtr = fsRecPtr->nextPtr;
+ }
+ FsReleaseIterator();
+
+ return (retVal);
+}
/*
*---------------------------------------------------------------------------
@@ -107,14 +1144,14 @@ int
TclGetOpenMode(interp, string, seekFlagPtr)
Tcl_Interp *interp; /* Interpreter to use for error
* reporting - may be NULL. */
- char *string; /* Mode string, e.g. "r+" or
+ CONST char *string; /* Mode string, e.g. "r+" or
* "RDONLY CREAT". */
int *seekFlagPtr; /* Set this to 1 if the caller
* should seek to EOF during the
* opening of the file. */
{
int mode, modeArgc, c, i, gotRW;
- char **modeArgv, *flag;
+ CONST char **modeArgv, *flag;
#define RW_MODES (O_RDONLY|O_WRONLY|O_RDWR)
/*
@@ -255,7 +1292,7 @@ TclGetOpenMode(interp, string, seekFlagPtr)
/*
*----------------------------------------------------------------------
*
- * Tcl_EvalFile --
+ * Tcl_FSEvalFile --
*
* Read in a file and process the entire file as one gigantic
* Tcl command.
@@ -265,50 +1302,60 @@ TclGetOpenMode(interp, string, seekFlagPtr)
* the file or an error indicating why the file couldn't be read.
*
* Side effects:
- * Depends on the commands in the file.
+ * Depends on the commands in the file. During the evaluation
+ * of the contents of the file, iPtr->scriptFile is made to
+ * point to pathPtr (the old value is cached and replaced when
+ * this function returns).
*
*----------------------------------------------------------------------
*/
int
-Tcl_EvalFile(interp, fileName)
+Tcl_FSEvalFile(interp, pathPtr)
Tcl_Interp *interp; /* Interpreter in which to process file. */
- char *fileName; /* Name of file to process. Tilde-substitution
+ Tcl_Obj *pathPtr; /* Path of file to process. Tilde-substitution
* will be performed on this name. */
{
int result, length;
- struct stat statBuf;
- char *oldScriptFile;
+ Tcl_StatBuf statBuf;
+ Tcl_Obj *oldScriptFile;
Interp *iPtr;
- Tcl_DString nameString;
- char *name, *string;
+ char *string;
Tcl_Channel chan;
Tcl_Obj *objPtr;
- name = Tcl_TranslateFileName(interp, fileName, &nameString);
- if (name == NULL) {
+ if (Tcl_FSGetTranslatedPath(interp, pathPtr) == NULL) {
return TCL_ERROR;
}
result = TCL_ERROR;
objPtr = Tcl_NewObj();
- if (TclStat(name, &statBuf) == -1) {
+ if (Tcl_FSStat(pathPtr, &statBuf) == -1) {
Tcl_SetErrno(errno);
- Tcl_AppendResult(interp, "couldn't read file \"", fileName,
+ Tcl_AppendResult(interp, "couldn't read file \"",
+ Tcl_GetString(pathPtr),
"\": ", Tcl_PosixError(interp), (char *) NULL);
goto end;
}
- chan = Tcl_OpenFileChannel(interp, name, "r", 0644);
+ chan = Tcl_FSOpenFileChannel(interp, pathPtr, "r", 0644);
if (chan == (Tcl_Channel) NULL) {
Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "couldn't read file \"", fileName,
+ Tcl_AppendResult(interp, "couldn't read file \"",
+ Tcl_GetString(pathPtr),
"\": ", Tcl_PosixError(interp), (char *) NULL);
goto end;
}
+ /*
+ * The eofchar is \32 (^Z). This is the usual on Windows, but we
+ * effect this cross-platform to allow for scripted documents.
+ * [Bug: 2040]
+ */
+ Tcl_SetChannelOption(interp, chan, "-eofchar", "\32");
if (Tcl_ReadChars(chan, objPtr, -1, 0) < 0) {
Tcl_Close(interp, chan);
- Tcl_AppendResult(interp, "couldn't read file \"", fileName,
+ Tcl_AppendResult(interp, "couldn't read file \"",
+ Tcl_GetString(pathPtr),
"\": ", Tcl_PosixError(interp), (char *) NULL);
goto end;
}
@@ -318,9 +1365,18 @@ Tcl_EvalFile(interp, fileName)
iPtr = (Interp *) interp;
oldScriptFile = iPtr->scriptFile;
- iPtr->scriptFile = fileName;
+ iPtr->scriptFile = pathPtr;
+ Tcl_IncrRefCount(iPtr->scriptFile);
string = Tcl_GetStringFromObj(objPtr, &length);
result = Tcl_EvalEx(interp, string, length, 0);
+ /*
+ * Now we have to be careful; the script may have changed the
+ * iPtr->scriptFile value, so we must reset it without
+ * assuming it still points to 'pathPtr'.
+ */
+ if (iPtr->scriptFile != NULL) {
+ Tcl_DecrRefCount(iPtr->scriptFile);
+ }
iPtr->scriptFile = oldScriptFile;
if (result == TCL_RETURN) {
@@ -332,14 +1388,13 @@ Tcl_EvalFile(interp, fileName)
* Record information telling where the error occurred.
*/
- sprintf(msg, "\n (file \"%.150s\" line %d)", fileName,
+ sprintf(msg, "\n (file \"%.150s\" line %d)", Tcl_GetString(pathPtr),
interp->errorLine);
Tcl_AddErrorInfo(interp, msg);
}
end:
Tcl_DecrRefCount(objPtr);
- Tcl_DStringFree(&nameString);
return result;
}
@@ -411,12 +1466,12 @@ Tcl_SetErrno(err)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_PosixError(interp)
Tcl_Interp *interp; /* Interpreter whose $errorCode variable
* is to be changed. */
{
- char *id, *msg;
+ CONST char *id, *msg;
msg = Tcl_ErrnoMsg(errno);
id = Tcl_ErrnoId();
@@ -427,12 +1482,12 @@ Tcl_PosixError(interp)
/*
*----------------------------------------------------------------------
*
- * TclStat --
+ * Tcl_FSStat --
*
* This procedure replaces the library version of stat and lsat.
- * The chain of functions that have been "inserted" into the
- * 'statProcList' will be called in succession until either
- * a value of zero is returned, or the entire list is visited.
+ *
+ * The appropriate function for the filesystem to which pathPtr
+ * belongs will be called.
*
* Results:
* See stat documentation.
@@ -444,38 +1499,118 @@ Tcl_PosixError(interp)
*/
int
-TclStat(path, buf)
- CONST char *path; /* Path of file to stat (in current CP). */
- struct stat *buf; /* Filled with results of stat call. */
+Tcl_FSStat(pathPtr, buf)
+ Tcl_Obj *pathPtr; /* Path of file to stat (in current CP). */
+ Tcl_StatBuf *buf; /* Filled with results of stat call. */
{
+ Tcl_Filesystem *fsPtr;
+#ifdef USE_OBSOLETE_FS_HOOKS
StatProc *statProcPtr;
+ struct stat oldStyleStatBuffer;
int retVal = -1;
+ char *path;
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
+ if (transPtr == NULL) {
+ path = NULL;
+ } else {
+ path = Tcl_GetString(transPtr);
+ }
/*
* Call each of the "stat" function in succession. A non-return
* value of -1 indicates the particular function has succeeded.
*/
- Tcl_MutexLock(&hookMutex);
+ Tcl_MutexLock(&obsoleteFsHookMutex);
statProcPtr = statProcList;
while ((retVal == -1) && (statProcPtr != NULL)) {
- retVal = (*statProcPtr->proc)(path, buf);
+ retVal = (*statProcPtr->proc)(path, &oldStyleStatBuffer);
statProcPtr = statProcPtr->nextPtr;
}
- Tcl_MutexUnlock(&hookMutex);
+ Tcl_MutexUnlock(&obsoleteFsHookMutex);
+ if (retVal != -1) {
+ /*
+ * Note that EOVERFLOW is not a problem here, and these
+ * assignments should all be widening (if not identity.)
+ */
+ buf->st_mode = oldStyleStatBuffer.st_mode;
+ buf->st_ino = oldStyleStatBuffer.st_ino;
+ buf->st_dev = oldStyleStatBuffer.st_dev;
+ buf->st_rdev = oldStyleStatBuffer.st_rdev;
+ buf->st_nlink = oldStyleStatBuffer.st_nlink;
+ buf->st_uid = oldStyleStatBuffer.st_uid;
+ buf->st_gid = oldStyleStatBuffer.st_gid;
+ buf->st_size = Tcl_LongAsWide(oldStyleStatBuffer.st_size);
+ buf->st_atime = oldStyleStatBuffer.st_atime;
+ buf->st_mtime = oldStyleStatBuffer.st_mtime;
+ buf->st_ctime = oldStyleStatBuffer.st_ctime;
+#ifdef HAVE_ST_BLOCKS
+ buf->st_blksize = oldStyleStatBuffer.st_blksize;
+ buf->st_blocks = Tcl_LongAsWide(oldStyleStatBuffer.st_blocks);
+#endif
+ return retVal;
+ }
+#endif /* USE_OBSOLETE_FS_HOOKS */
+ fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSStatProc *proc = fsPtr->statProc;
+ if (proc != NULL) {
+ return (*proc)(pathPtr, buf);
+ }
+ }
+ Tcl_SetErrno(ENOENT);
+ return -1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSLstat --
+ *
+ * This procedure replaces the library version of lstat.
+ * The appropriate function for the filesystem to which pathPtr
+ * belongs will be called. If no 'lstat' function is listed,
+ * but a 'stat' function is, then Tcl will fall back on the
+ * stat function.
+ *
+ * Results:
+ * See lstat documentation.
+ *
+ * Side effects:
+ * See lstat documentation.
+ *
+ *----------------------------------------------------------------------
+ */
- return (retVal);
+int
+Tcl_FSLstat(pathPtr, buf)
+ Tcl_Obj *pathPtr; /* Path of file to stat (in current CP). */
+ Tcl_StatBuf *buf; /* Filled with results of stat call. */
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSLstatProc *proc = fsPtr->lstatProc;
+ if (proc != NULL) {
+ return (*proc)(pathPtr, buf);
+ } else {
+ Tcl_FSStatProc *sproc = fsPtr->statProc;
+ if (sproc != NULL) {
+ return (*sproc)(pathPtr, buf);
+ }
+ }
+ }
+ Tcl_SetErrno(ENOENT);
+ return -1;
}
/*
*----------------------------------------------------------------------
*
- * TclAccess --
+ * Tcl_FSAccess --
*
* This procedure replaces the library version of access.
- * The chain of functions that have been "inserted" into the
- * 'accessProcList' will be called in succession until either
- * a value of zero is returned, or the entire list is visited.
+ * The appropriate function for the filesystem to which pathPtr
+ * belongs will be called.
*
* Results:
* See access documentation.
@@ -487,38 +1622,57 @@ TclStat(path, buf)
*/
int
-TclAccess(path, mode)
- CONST char *path; /* Path of file to access (in current CP). */
+Tcl_FSAccess(pathPtr, mode)
+ Tcl_Obj *pathPtr; /* Path of file to access (in current CP). */
int mode; /* Permission setting. */
{
+ Tcl_Filesystem *fsPtr;
+#ifdef USE_OBSOLETE_FS_HOOKS
AccessProc *accessProcPtr;
int retVal = -1;
+ char *path;
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
+ if (transPtr == NULL) {
+ path = NULL;
+ } else {
+ path = Tcl_GetString(transPtr);
+ }
/*
* Call each of the "access" function in succession. A non-return
* value of -1 indicates the particular function has succeeded.
*/
- Tcl_MutexLock(&hookMutex);
+ Tcl_MutexLock(&obsoleteFsHookMutex);
accessProcPtr = accessProcList;
while ((retVal == -1) && (accessProcPtr != NULL)) {
retVal = (*accessProcPtr->proc)(path, mode);
accessProcPtr = accessProcPtr->nextPtr;
}
- Tcl_MutexUnlock(&hookMutex);
+ Tcl_MutexUnlock(&obsoleteFsHookMutex);
+ if (retVal != -1) {
+ return retVal;
+ }
+#endif /* USE_OBSOLETE_FS_HOOKS */
+ fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSAccessProc *proc = fsPtr->accessProc;
+ if (proc != NULL) {
+ return (*proc)(pathPtr, mode);
+ }
+ }
- return (retVal);
+ Tcl_SetErrno(ENOENT);
+ return -1;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_OpenFileChannel --
+ * Tcl_FSOpenFileChannel --
*
- * The chain of functions that have been "inserted" into the
- * 'openFileChannelProcList' will be called in succession until
- * either a valid file channel is returned, or the entire list is
- * visited.
+ * The appropriate function for the filesystem to which pathPtr
+ * belongs will be called.
*
* Results:
* The new channel or NULL, if the named file could not be opened.
@@ -531,18 +1685,32 @@ TclAccess(path, mode)
*/
Tcl_Channel
-Tcl_OpenFileChannel(interp, fileName, modeString, permissions)
+Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions)
Tcl_Interp *interp; /* Interpreter for error reporting;
* can be NULL. */
- char *fileName; /* Name of file to open. */
- char *modeString; /* A list of POSIX open modes or
+ Tcl_Obj *pathPtr; /* Name of file to open. */
+ CONST char *modeString; /* A list of POSIX open modes or
* a string such as "rw". */
int permissions; /* If the open involves creating a
* file, with what modes to create
* it? */
{
+ Tcl_Filesystem *fsPtr;
+#ifdef USE_OBSOLETE_FS_HOOKS
OpenFileChannelProc *openFileChannelProcPtr;
Tcl_Channel retVal = NULL;
+ char *path;
+#endif /* USE_OBSOLETE_FS_HOOKS */
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
+ if (transPtr == NULL) {
+ return NULL;
+ }
+#ifdef USE_OBSOLETE_FS_HOOKS
+ if (transPtr == NULL) {
+ path = NULL;
+ } else {
+ path = Tcl_GetString(transPtr);
+ }
/*
* Call each of the "Tcl_OpenFileChannel" function in succession.
@@ -550,27 +1718,3301 @@ Tcl_OpenFileChannel(interp, fileName, modeString, permissions)
* succeeded.
*/
- Tcl_MutexLock(&hookMutex);
+ Tcl_MutexLock(&obsoleteFsHookMutex);
openFileChannelProcPtr = openFileChannelProcList;
while ((retVal == NULL) && (openFileChannelProcPtr != NULL)) {
- retVal = (*openFileChannelProcPtr->proc)(interp, fileName,
+ retVal = (*openFileChannelProcPtr->proc)(interp, path,
modeString, permissions);
openFileChannelProcPtr = openFileChannelProcPtr->nextPtr;
}
- Tcl_MutexUnlock(&hookMutex);
+ Tcl_MutexUnlock(&obsoleteFsHookMutex);
+ if (retVal != NULL) {
+ return retVal;
+ }
+#endif /* USE_OBSOLETE_FS_HOOKS */
+ fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSOpenFileChannelProc *proc = fsPtr->openFileChannelProc;
+ if (proc != NULL) {
+ int mode, seekFlag;
+ mode = TclGetOpenMode(interp, modeString, &seekFlag);
+ if (mode == -1) {
+ return NULL;
+ }
+ retVal = (*proc)(interp, pathPtr, mode, permissions);
+ if (retVal != NULL) {
+ if (seekFlag) {
+ if (Tcl_Seek(retVal, (Tcl_WideInt)0,
+ SEEK_END) < (Tcl_WideInt)0) {
+ if (interp != (Tcl_Interp *) NULL) {
+ Tcl_AppendResult(interp,
+ "could not seek to end of file while opening \"",
+ Tcl_GetString(pathPtr), "\": ",
+ Tcl_PosixError(interp), (char *) NULL);
+ }
+ Tcl_Close(NULL, retVal);
+ return NULL;
+ }
+ }
+ }
+ return retVal;
+ }
+ }
+ /* File doesn't belong to any filesystem that can open it */
+ Tcl_SetErrno(ENOENT);
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "couldn't open \"",
+ Tcl_GetString(pathPtr), "\": ",
+ Tcl_PosixError(interp), (char *) NULL);
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSMatchInDirectory --
+ *
+ * This routine is used by the globbing code to search a directory
+ * for all files which match a given pattern. The appropriate
+ * function for the filesystem to which pathPtr belongs will be
+ * called. If pathPtr does not belong to any filesystem and if it
+ * is NULL or the empty string, then we assume the pattern is to
+ * be matched in the current working directory. To avoid each
+ * filesystem's Tcl_FSMatchInDirectoryProc having to deal with
+ * this issue, we create a pathPtr on the fly, and then remove it
+ * from the results returned. This makes filesystems easy to
+ * write, since they can assume the pathPtr passed to them
+ * is an ordinary path. In fact this means we could remove such
+ * special case handling from Tcl's native filesystems.
+ *
+ * If 'pattern' is NULL, then pathPtr is assumed to be a fully
+ * specified path of a single file/directory which must be
+ * checked for existence and correct type.
+ *
+ * Results:
+ *
+ * The return value is a standard Tcl result indicating whether an
+ * error occurred in globbing. Error messages are placed in
+ * interp, but good results are placed in the resultPtr given.
+ *
+ * Recursive searches, e.g.
+ *
+ * glob -dir $dir -join * pkgIndex.tcl
+ *
+ * which must recurse through each directory matching '*' are
+ * handled internally by Tcl, by passing specific flags in a
+ * modified 'types' parameter.
+ *
+ * Side effects:
+ * The interpreter may have an error message inserted into it.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tcl_FSMatchInDirectory(interp, result, pathPtr, pattern, types)
+ Tcl_Interp *interp; /* Interpreter to receive error messages. */
+ Tcl_Obj *result; /* List object to receive results. */
+ Tcl_Obj *pathPtr; /* Contains path to directory to search. */
+ CONST char *pattern; /* Pattern to match against. */
+ Tcl_GlobTypeData *types; /* Object containing list of acceptable types.
+ * May be NULL. In particular the directory
+ * flag is very important. */
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSMatchInDirectoryProc *proc = fsPtr->matchInDirectoryProc;
+ if (proc != NULL) {
+ return (*proc)(interp, result, pathPtr, pattern, types);
+ }
+ } else {
+ Tcl_Obj* cwd;
+ int ret = -1;
+ if (pathPtr != NULL) {
+ int len;
+ Tcl_GetStringFromObj(pathPtr,&len);
+ if (len != 0) {
+ /*
+ * We have no idea how to match files in a directory
+ * which belongs to no known filesystem
+ */
+ Tcl_SetErrno(ENOENT);
+ return -1;
+ }
+ }
+ /*
+ * We have an empty or NULL path. This is defined to mean we
+ * must search for files within the current 'cwd'. We
+ * therefore use that, but then since the proc we call will
+ * return results which include the cwd we must then trim it
+ * off the front of each path in the result. We choose to deal
+ * with this here (in the generic code), since if we don't,
+ * every single filesystem's implementation of
+ * Tcl_FSMatchInDirectory will have to deal with it for us.
+ */
+ cwd = Tcl_FSGetCwd(NULL);
+ if (cwd == NULL) {
+ if (interp != NULL) {
+ Tcl_SetResult(interp, "glob couldn't determine "
+ "the current working directory", TCL_STATIC);
+ }
+ return TCL_ERROR;
+ }
+ fsPtr = Tcl_FSGetFileSystemForPath(cwd);
+ if (fsPtr != NULL) {
+ Tcl_FSMatchInDirectoryProc *proc = fsPtr->matchInDirectoryProc;
+ if (proc != NULL) {
+ int cwdLen;
+ Tcl_Obj *cwdDir;
+ char *cwdStr;
+ char sep = 0;
+ Tcl_Obj* tmpResultPtr = Tcl_NewListObj(0, NULL);
+ /*
+ * We know the cwd is a normalised object which does
+ * not end in a directory delimiter, unless the cwd
+ * is the name of a volume, in which case it will
+ * end in a delimiter! We handle this situation here.
+ * A better test than the '!= sep' might be to simply
+ * check if 'cwd' is a root volume.
+ *
+ * Note that if we get this wrong, we will strip off
+ * either too much or too little below, leading to
+ * wrong answers returned by glob.
+ */
+ cwdDir = Tcl_DuplicateObj(cwd);
+ Tcl_IncrRefCount(cwdDir);
+ cwdStr = Tcl_GetStringFromObj(cwdDir, &cwdLen);
+ /*
+ * Should we perhaps use 'Tcl_FSPathSeparator'?
+ * But then what about the Windows special case?
+ * Perhaps we should just check if cwd is a root
+ * volume.
+ */
+ switch (tclPlatform) {
+ case TCL_PLATFORM_UNIX:
+ if (cwdStr[cwdLen-1] != '/') {
+ sep = '/';
+ }
+ break;
+ case TCL_PLATFORM_WINDOWS:
+ if (cwdStr[cwdLen-1] != '/' && cwdStr[cwdLen-1] != '\\') {
+ sep = '/';
+ }
+ break;
+ case TCL_PLATFORM_MAC:
+ if (cwdStr[cwdLen-1] != ':') {
+ sep = ':';
+ }
+ break;
+ }
+ if (sep != 0) {
+ Tcl_AppendToObj(cwdDir, &sep, 1);
+ cwdLen++;
+ /* Note: cwdStr may no longer be a valid pointer now */
+ }
+ ret = (*proc)(interp, tmpResultPtr, cwdDir, pattern, types);
+ Tcl_DecrRefCount(cwdDir);
+ if (ret == TCL_OK) {
+ int resLength;
+
+ ret = Tcl_ListObjLength(interp, tmpResultPtr, &resLength);
+ if (ret == TCL_OK) {
+ Tcl_Obj *elt, *cutElt;
+ char *eltStr;
+ int eltLen, i;
+
+ for (i = 0; i < resLength; i++) {
+ Tcl_ListObjIndex(interp, tmpResultPtr, i, &elt);
+ eltStr = Tcl_GetStringFromObj(elt,&eltLen);
+ cutElt = Tcl_NewStringObj(eltStr + cwdLen,
+ eltLen - cwdLen);
+ Tcl_ListObjAppendElement(interp, result, cutElt);
+ }
+ }
+ }
+ Tcl_DecrRefCount(tmpResultPtr);
+ }
+ }
+ Tcl_DecrRefCount(cwd);
+ return ret;
+ }
+ Tcl_SetErrno(ENOENT);
+ return -1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSGetCwd --
+ *
+ * This function replaces the library version of getcwd().
+ *
+ * Most VFS's will *not* implement a 'cwdProc'. Tcl now maintains
+ * its own record (in a Tcl_Obj) of the cwd, and an attempt
+ * is made to synchronise this with the cwd's containing filesystem,
+ * if that filesystem provides a cwdProc (e.g. the native filesystem).
+ *
+ * Note that if Tcl's cwd is not in the native filesystem, then of
+ * course Tcl's cwd and the native cwd are different: extensions
+ * should therefore ensure they only access the cwd through this
+ * function to avoid confusion.
+ *
+ * If a global cwdPathPtr already exists, it is returned, subject
+ * to a synchronisation attempt in that cwdPathPtr's fs.
+ * Otherwise, the chain of functions that have been "inserted"
+ * into the filesystem will be called in succession until either a
+ * value other than NULL is returned, or the entire list is
+ * visited.
+ *
+ * Results:
+ * The result is a pointer to a Tcl_Obj 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.
+ *
+ * The result already has its refCount incremented for the caller.
+ * When it is no longer needed, that refCount should be decremented.
+ * This is needed for thread-safety purposes, to allow multiple
+ * threads to access this and related functions, while ensuring the
+ * results are always valid.
+ *
+ * Of course it is probably a bad idea for multiple threads to
+ * be *setting* the cwd anyway, but we can at least try to
+ * help the case of multiple reads with occasional sets.
+ *
+ * Side effects:
+ * Various objects may be freed and allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+Tcl_FSGetCwd(interp)
+ Tcl_Interp *interp;
+{
+ Tcl_Obj *cwdToReturn;
+
+ if (FsCwdPointerEquals(NULL)) {
+ FilesystemRecord *fsRecPtr;
+ Tcl_Obj *retVal = NULL;
+
+ /*
+ * We've never been called before, try to find a cwd. Call
+ * each of the "Tcl_GetCwd" function in succession. A non-NULL
+ * return value indicates the particular function has
+ * succeeded.
+ */
+
+ fsRecPtr = FsGetIterator();
+ while ((retVal == NULL) && (fsRecPtr != NULL)) {
+ Tcl_FSGetCwdProc *proc = fsRecPtr->fsPtr->getCwdProc;
+ if (proc != NULL) {
+ retVal = (*proc)(interp);
+ }
+ fsRecPtr = fsRecPtr->nextPtr;
+ }
+ FsReleaseIterator();
+ /*
+ * Now the 'cwd' may NOT be normalized, at least on some
+ * platforms. For the sake of efficiency, we want a completely
+ * normalized cwd at all times.
+ *
+ * Finally, if retVal is NULL, we do not have a cwd, which
+ * could be problematic.
+ */
+ if (retVal != NULL) {
+ Tcl_Obj *norm = FSNormalizeAbsolutePath(interp, retVal);
+ if (norm != NULL) {
+ /*
+ * We found a cwd, which is now in our global storage.
+ * We must make a copy. Norm already has a refCount of
+ * 1.
+ *
+ * Threading issue: note that multiple threads at system
+ * startup could in principle call this procedure
+ * simultaneously. They will therefore each set the
+ * cwdPathPtr independently. That behaviour is a bit
+ * peculiar, but should be fine. Once we have a cwd,
+ * we'll always be in the 'else' branch below which
+ * is simpler.
+ */
+ Tcl_MutexLock(&cwdMutex);
+ /* Just in case the pointer has been set by another
+ * thread between now and the test above */
+ if (cwdPathPtr != NULL) {
+ Tcl_DecrRefCount(cwdPathPtr);
+ }
+ cwdPathPtr = norm;
+ Tcl_MutexUnlock(&cwdMutex);
+ }
+ Tcl_DecrRefCount(retVal);
+ }
+ } else {
+ /*
+ * We already have a cwd cached, but we want to give the
+ * filesystem it is in a chance to check whether that cwd
+ * has changed, or is perhaps no longer accessible. This
+ * allows an error to be thrown if, say, the permissions on
+ * that directory have changed.
+ */
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(cwdPathPtr);
+ /*
+ * If the filesystem couldn't be found, or if no cwd function
+ * exists for this filesystem, then we simply assume the cached
+ * cwd is ok. If we do call a cwd, we must watch for errors
+ * (if the cwd returns NULL). This ensures that, say, on Unix
+ * if the permissions of the cwd change, 'pwd' does actually
+ * throw the correct error in Tcl. (This is tested for in the
+ * test suite on unix).
+ */
+ if (fsPtr != NULL) {
+ Tcl_FSGetCwdProc *proc = fsPtr->getCwdProc;
+ if (proc != NULL) {
+ Tcl_Obj *retVal = (*proc)(interp);
+ if (retVal != NULL) {
+ Tcl_Obj *norm = FSNormalizeAbsolutePath(interp, retVal);
+ /*
+ * Check whether cwd has changed from the value
+ * previously stored in cwdPathPtr. Really 'norm'
+ * shouldn't be null, but we are careful.
+ */
+ if (norm == NULL) {
+ /* Do nothing */
+ } else if (Tcl_FSEqualPaths(cwdPathPtr, norm)) {
+ /*
+ * If the paths were equal, we can be more
+ * efficient and retain the old path object
+ * which will probably already be shared. In
+ * this case we can simply free the normalized
+ * path we just calculated.
+ */
+ Tcl_DecrRefCount(norm);
+ } else {
+ /* The cwd has in fact changed, so we must
+ * lock down the cwdMutex to modify. */
+ Tcl_MutexLock(&cwdMutex);
+ Tcl_DecrRefCount(cwdPathPtr);
+ cwdPathPtr = norm;
+ Tcl_MutexUnlock(&cwdMutex);
+ }
+ Tcl_DecrRefCount(retVal);
+ } else {
+ /* The 'cwd' function returned an error, so we
+ * reset the cwd after locking down the mutex. */
+ Tcl_MutexLock(&cwdMutex);
+ Tcl_DecrRefCount(cwdPathPtr);
+ cwdPathPtr = NULL;
+ Tcl_MutexUnlock(&cwdMutex);
+ }
+ }
+ }
+ }
+
+ /*
+ * The paths all eventually fall through to here. Note that
+ * we use a bunch of separate mutex locks throughout this
+ * code to help prevent deadlocks between threads. Really
+ * the only weirdness will arise if multiple threads are setting
+ * and reading the cwd, and that behaviour is always going to be
+ * a little suspect.
+ */
+ Tcl_MutexLock(&cwdMutex);
+ cwdToReturn = cwdPathPtr;
+ if (cwdToReturn != NULL) {
+ Tcl_IncrRefCount(cwdToReturn);
+ }
+ Tcl_MutexUnlock(&cwdMutex);
+
+ return (cwdToReturn);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSUtime --
+ *
+ * This procedure replaces the library version of utime.
+ * The appropriate function for the filesystem to which pathPtr
+ * belongs will be called.
+ *
+ * Results:
+ * See utime documentation.
+ *
+ * Side effects:
+ * See utime documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_FSUtime (pathPtr, tval)
+ Tcl_Obj *pathPtr; /* File to change access/modification times */
+ struct utimbuf *tval; /* Structure containing access/modification
+ * times to use. Should not be modified. */
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSUtimeProc *proc = fsPtr->utimeProc;
+ if (proc != NULL) {
+ return (*proc)(pathPtr, tval);
+ }
+ }
+ return -1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NativeFileAttrStrings --
+ *
+ * This procedure implements the platform dependent 'file
+ * attributes' subcommand, for the native filesystem, for listing
+ * the set of possible attribute strings. This function is part
+ * of Tcl's native filesystem support, and is placed here because
+ * it is shared by Unix, MacOS and Windows code.
+ *
+ * Results:
+ * An array of strings
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static CONST char**
+NativeFileAttrStrings(pathPtr, objPtrRef)
+ Tcl_Obj *pathPtr;
+ Tcl_Obj** objPtrRef;
+{
+ return tclpFileAttrStrings;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NativeFileAttrsGet --
+ *
+ * This procedure implements the platform dependent
+ * 'file attributes' subcommand, for the native
+ * filesystem, for 'get' operations. This function is part
+ * of Tcl's native filesystem support, and is placed here
+ * because it is shared by Unix, MacOS and Windows code.
+ *
+ * Results:
+ * Standard Tcl return code. The object placed in objPtrRef
+ * (if TCL_OK was returned) is likely to have a refCount of zero.
+ * Either way we must either store it somewhere (e.g. the Tcl
+ * result), or Incr/Decr its refCount to ensure it is properly
+ * freed.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+NativeFileAttrsGet(interp, index, pathPtr, objPtrRef)
+ Tcl_Interp *interp; /* The interpreter for error reporting. */
+ int index; /* index of the attribute command. */
+ Tcl_Obj *pathPtr; /* path of file we are operating on. */
+ Tcl_Obj **objPtrRef; /* for output. */
+{
+ return (*tclpFileAttrProcs[index].getProc)(interp, index,
+ pathPtr, objPtrRef);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NativeFileAttrsSet --
+ *
+ * This procedure implements the platform dependent
+ * 'file attributes' subcommand, for the native
+ * filesystem, for 'set' operations. This function is part
+ * of Tcl's native filesystem support, and is placed here
+ * because it is shared by Unix, MacOS and Windows code.
+ *
+ * Results:
+ * Standard Tcl return code.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+NativeFileAttrsSet(interp, index, pathPtr, objPtr)
+ Tcl_Interp *interp; /* The interpreter for error reporting. */
+ int index; /* index of the attribute command. */
+ Tcl_Obj *pathPtr; /* path of file we are operating on. */
+ Tcl_Obj *objPtr; /* set to this value. */
+{
+ return (*tclpFileAttrProcs[index].setProc)(interp, index,
+ pathPtr, objPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSFileAttrStrings --
+ *
+ * This procedure implements part of the hookable 'file
+ * attributes' subcommand. The appropriate function for the
+ * filesystem to which pathPtr belongs will be called.
+ *
+ * Results:
+ * The called procedure may either return an array of strings,
+ * or may instead return NULL and place a Tcl list into the
+ * given objPtrRef. Tcl will take that list and first increment
+ * its refCount before using it. On completion of that use, Tcl
+ * will decrement its refCount. Hence if the list should be
+ * disposed of by Tcl when done, it should have a refCount of zero,
+ * and if the list should not be disposed of, the filesystem
+ * should ensure it retains a refCount on the object.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CONST char **
+Tcl_FSFileAttrStrings(pathPtr, objPtrRef)
+ Tcl_Obj* pathPtr;
+ Tcl_Obj** objPtrRef;
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSFileAttrStringsProc *proc = fsPtr->fileAttrStringsProc;
+ if (proc != NULL) {
+ return (*proc)(pathPtr, objPtrRef);
+ }
+ }
+ Tcl_SetErrno(ENOENT);
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSFileAttrsGet --
+ *
+ * This procedure implements read access for the hookable 'file
+ * attributes' subcommand. The appropriate function for the
+ * filesystem to which pathPtr belongs will be called.
+ *
+ * Results:
+ * Standard Tcl return code. The object placed in objPtrRef
+ * (if TCL_OK was returned) is likely to have a refCount of zero.
+ * Either way we must either store it somewhere (e.g. the Tcl
+ * result), or Incr/Decr its refCount to ensure it is properly
+ * freed.
+
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_FSFileAttrsGet(interp, index, pathPtr, objPtrRef)
+ Tcl_Interp *interp; /* The interpreter for error reporting. */
+ int index; /* index of the attribute command. */
+ Tcl_Obj *pathPtr; /* filename we are operating on. */
+ Tcl_Obj **objPtrRef; /* for output. */
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSFileAttrsGetProc *proc = fsPtr->fileAttrsGetProc;
+ if (proc != NULL) {
+ return (*proc)(interp, index, pathPtr, objPtrRef);
+ }
+ }
+ Tcl_SetErrno(ENOENT);
+ return -1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSFileAttrsSet --
+ *
+ * This procedure implements write access for the hookable 'file
+ * attributes' subcommand. The appropriate function for the
+ * filesystem to which pathPtr belongs will be called.
+ *
+ * Results:
+ * Standard Tcl return code.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_FSFileAttrsSet(interp, index, pathPtr, objPtr)
+ Tcl_Interp *interp; /* The interpreter for error reporting. */
+ int index; /* index of the attribute command. */
+ Tcl_Obj *pathPtr; /* filename we are operating on. */
+ Tcl_Obj *objPtr; /* Input value. */
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSFileAttrsSetProc *proc = fsPtr->fileAttrsSetProc;
+ if (proc != NULL) {
+ return (*proc)(interp, index, pathPtr, objPtr);
+ }
+ }
+ Tcl_SetErrno(ENOENT);
+ return -1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSChdir --
+ *
+ * This function replaces the library version of chdir().
+ *
+ * The path is normalized and then passed to the filesystem
+ * which claims it.
+ *
+ * Results:
+ * See chdir() documentation. If successful, we keep a
+ * record of the successful path in cwdPathPtr for subsequent
+ * calls to getcwd.
+ *
+ * Side effects:
+ * See chdir() documentation. The global cwdPathPtr may
+ * change value.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tcl_FSChdir(pathPtr)
+ Tcl_Obj *pathPtr;
+{
+ Tcl_Filesystem *fsPtr;
+ int retVal = -1;
+
+ if (Tcl_FSGetNormalizedPath(NULL, pathPtr) == NULL) {
+ return TCL_ERROR;
+ }
+
+ fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSChdirProc *proc = fsPtr->chdirProc;
+ if (proc != NULL) {
+ retVal = (*proc)(pathPtr);
+ } else {
+ /* Fallback on stat-based implementation */
+ Tcl_StatBuf buf;
+ /* If the file can be stat'ed and is a directory and
+ * is readable, then we can chdir. */
+ if ((Tcl_FSStat(pathPtr, &buf) == 0)
+ && (S_ISDIR(buf.st_mode))
+ && (Tcl_FSAccess(pathPtr, R_OK) == 0)) {
+ /* We allow the chdir */
+ retVal = 0;
+ }
+ }
+ }
+
+ if (retVal != -1) {
+ /*
+ * The cwd changed, or an error was thrown. If an error was
+ * thrown, we can just continue (and that will report the error
+ * to the user). If there was no error we must assume that the
+ * cwd was actually changed to the normalized value we
+ * calculated above, and we must therefore cache that
+ * information.
+ */
+ if (retVal == TCL_OK) {
+ /*
+ * Note that this normalized path may be different to what
+ * we found above (or at least a different object), if the
+ * filesystem epoch changed recently. This can actually
+ * happen with scripted documents very easily. Therefore
+ * we ask for the normalized path again (the correct value
+ * will have been cached as a result of the
+ * Tcl_FSGetFileSystemForPath call above anyway).
+ */
+ Tcl_Obj *normDirName = Tcl_FSGetNormalizedPath(NULL, pathPtr);
+ if (normDirName == NULL) {
+ return TCL_ERROR;
+ }
+ /*
+ * We will be adding a reference to this object when
+ * we store it in the cwdPathPtr.
+ */
+ Tcl_IncrRefCount(normDirName);
+ /* Get a lock on the cwd while we modify it */
+ Tcl_MutexLock(&cwdMutex);
+ /* Free up the previous cwd we stored */
+ if (cwdPathPtr != NULL) {
+ Tcl_DecrRefCount(cwdPathPtr);
+ }
+ /* Now remember the current cwd */
+ cwdPathPtr = normDirName;
+ Tcl_MutexUnlock(&cwdMutex);
+ }
+ }
+
return (retVal);
}
/*
*----------------------------------------------------------------------
*
+ * Tcl_FSLoadFile --
+ *
+ * Dynamically loads a binary code file into memory and returns
+ * the addresses of two procedures within that file, if they are
+ * defined. The appropriate function for the filesystem to which
+ * pathPtr belongs will be called.
+ *
+ * Note that the native filesystem doesn't actually assume
+ * 'pathPtr' is a path. Rather it assumes filename is either
+ * a path or just the name of a file which can be found somewhere
+ * in the environment's loadable path. This behaviour is not
+ * very compatible with virtual filesystems (and has other problems
+ * documented in the load man-page), so it is advised that full
+ * paths are always used.
+ *
+ * Results:
+ * A standard Tcl completion code. If an error occurs, an error
+ * message is left in the interp's result.
+ *
+ * Side effects:
+ * New code suddenly appears in memory. This may later be
+ * unloaded by passing the clientData to the unloadProc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_FSLoadFile(interp, pathPtr, sym1, sym2, proc1Ptr, proc2Ptr,
+ handlePtr, unloadProcPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Obj *pathPtr; /* Name of the file containing the desired
+ * code. */
+ CONST char *sym1, *sym2; /* Names of two procedures to look up in
+ * the file's symbol table. */
+ Tcl_PackageInitProc **proc1Ptr, **proc2Ptr;
+ /* Where to return the addresses corresponding
+ * to sym1 and sym2. */
+ Tcl_LoadHandle *handlePtr; /* Filled with token for dynamically loaded
+ * file which will be passed back to
+ * (*unloadProcPtr)() to unload the file. */
+ Tcl_FSUnloadFileProc **unloadProcPtr;
+ /* Filled with address of Tcl_FSUnloadFileProc
+ * function which should be used for
+ * this file. */
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSLoadFileProc *proc = fsPtr->loadFileProc;
+ if (proc != NULL) {
+ int retVal = (*proc)(interp, pathPtr, handlePtr, unloadProcPtr);
+ if (retVal != TCL_OK) {
+ return retVal;
+ }
+ if (*handlePtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (sym1 != NULL) {
+ *proc1Ptr = TclpFindSymbol(interp, *handlePtr, sym1);
+ }
+ if (sym2 != NULL) {
+ *proc2Ptr = TclpFindSymbol(interp, *handlePtr, sym2);
+ }
+ return retVal;
+ } else {
+ Tcl_Filesystem *copyFsPtr;
+ Tcl_Obj *copyToPtr;
+
+ /* First check if it is readable -- and exists! */
+ if (Tcl_FSAccess(pathPtr, R_OK) != 0) {
+ Tcl_AppendResult(interp, "couldn't load library \"",
+ Tcl_GetString(pathPtr), "\": ",
+ Tcl_PosixError(interp), (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Get a temporary filename to use, first to
+ * copy the file into, and then to load.
+ */
+ copyToPtr = TclpTempFileName();
+ if (copyToPtr == NULL) {
+ return -1;
+ }
+ Tcl_IncrRefCount(copyToPtr);
+
+ copyFsPtr = Tcl_FSGetFileSystemForPath(copyToPtr);
+ if ((copyFsPtr == NULL) || (copyFsPtr == fsPtr)) {
+ /*
+ * We already know we can't use Tcl_FSLoadFile from
+ * this filesystem, and we must avoid a possible
+ * infinite loop. Try to delete the file we
+ * probably created, and then exit.
+ */
+ Tcl_FSDeleteFile(copyToPtr);
+ Tcl_DecrRefCount(copyToPtr);
+ return -1;
+ }
+
+ if (TclCrossFilesystemCopy(interp, pathPtr,
+ copyToPtr) == TCL_OK) {
+ /*
+ * Do we need to set appropriate permissions
+ * on the file? This may be required on some
+ * systems. On Unix we could loop over
+ * the file attributes, and set any that are
+ * called "-permissions" to 0777. Or directly:
+ *
+ * Tcl_Obj* perm = Tcl_NewStringObj("0777",-1);
+ * Tcl_IncrRefCount(perm);
+ * Tcl_FSFileAttrsSet(NULL, 2, copyToPtr, perm);
+ * Tcl_DecrRefCount(perm);
+ *
+ */
+ Tcl_LoadHandle newLoadHandle = NULL;
+ Tcl_FSUnloadFileProc *newUnloadProcPtr = NULL;
+ FsDivertLoad *tvdlPtr;
+ int retVal;
+
+ retVal = Tcl_FSLoadFile(interp, copyToPtr, sym1, sym2,
+ proc1Ptr, proc2Ptr,
+ &newLoadHandle,
+ &newUnloadProcPtr);
+ if (retVal != TCL_OK) {
+ /* The file didn't load successfully */
+ Tcl_FSDeleteFile(copyToPtr);
+ Tcl_DecrRefCount(copyToPtr);
+ return retVal;
+ }
+ /*
+ * Try to delete the file immediately -- this is
+ * possible in some OSes, and avoids any worries
+ * about leaving the copy laying around on exit.
+ */
+ if (Tcl_FSDeleteFile(copyToPtr) == TCL_OK) {
+ Tcl_DecrRefCount(copyToPtr);
+ (*handlePtr) = NULL;
+ (*unloadProcPtr) = NULL;
+ return TCL_OK;
+ }
+ /*
+ * When we unload this file, we need to divert the
+ * unloading so we can unload and cleanup the
+ * temporary file correctly.
+ */
+ tvdlPtr = (FsDivertLoad*) ckalloc(sizeof(FsDivertLoad));
+
+ /*
+ * Remember three pieces of information. This allows
+ * us to cleanup the diverted load completely, on
+ * platforms which allow proper unloading of code.
+ */
+ tvdlPtr->loadHandle = newLoadHandle;
+ tvdlPtr->unloadProcPtr = newUnloadProcPtr;
+ /* copyToPtr is already incremented for this reference */
+ tvdlPtr->divertedFile = copyToPtr;
+ /*
+ * This is the filesystem we loaded it into. It is
+ * almost certainly the tclNativeFilesystem, but we don't
+ * want to make that assumption. Since we have a
+ * reference to 'copyToPtr', we already have a refCount
+ * on this filesystem, so we don't need to worry about it
+ * disappearing on us.
+ */
+ tvdlPtr->divertedFilesystem = copyFsPtr;
+ /* Get the native representation of the file path */
+ tvdlPtr->divertedFileNativeRep = Tcl_FSGetInternalRep(copyToPtr,
+ copyFsPtr);
+ copyToPtr = NULL;
+ (*handlePtr) = (Tcl_LoadHandle) tvdlPtr;
+ (*unloadProcPtr) = &FSUnloadTempFile;
+
+ return retVal;
+ } else {
+ /* Cross-platform copy failed */
+ Tcl_FSDeleteFile(copyToPtr);
+ Tcl_DecrRefCount(copyToPtr);
+ return TCL_ERROR;
+ }
+ }
+ }
+ Tcl_SetErrno(ENOENT);
+ return -1;
+}
+/*
+ * This function used to be in the platform specific directories, but it
+ * has now been made to work cross-platform
+ */
+int
+TclpLoadFile(interp, pathPtr, sym1, sym2, proc1Ptr, proc2Ptr,
+ clientDataPtr, unloadProcPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Obj *pathPtr; /* Name of the file containing the desired
+ * code (UTF-8). */
+ CONST char *sym1, *sym2; /* Names of two procedures to look up in
+ * the file's symbol table. */
+ Tcl_PackageInitProc **proc1Ptr, **proc2Ptr;
+ /* Where to return the addresses corresponding
+ * to sym1 and sym2. */
+ ClientData *clientDataPtr; /* Filled with token for dynamically loaded
+ * file which will be passed back to
+ * (*unloadProcPtr)() to unload the file. */
+ Tcl_FSUnloadFileProc **unloadProcPtr;
+ /* Filled with address of Tcl_FSUnloadFileProc
+ * function which should be used for
+ * this file. */
+{
+ Tcl_LoadHandle handle = NULL;
+ int res;
+
+ res = TclpDlopen(interp, pathPtr, &handle, unloadProcPtr);
+
+ if (res != TCL_OK) {
+ return res;
+ }
+
+ if (handle == NULL) {
+ return TCL_ERROR;
+ }
+
+ *clientDataPtr = (ClientData)handle;
+
+ *proc1Ptr = TclpFindSymbol(interp, handle, sym1);
+ *proc2Ptr = TclpFindSymbol(interp, handle, sym2);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FSUnloadTempFile --
+ *
+ * This function is called when we loaded a library of code via
+ * an intermediate temporary file. This function ensures
+ * the library is correctly unloaded and the temporary file
+ * is correctly deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The effects of the 'unload' function called, and of course
+ * the temporary file will be deleted.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+FSUnloadTempFile(loadHandle)
+ Tcl_LoadHandle loadHandle; /* loadHandle returned by a previous call
+ * to Tcl_FSLoadFile(). The loadHandle is
+ * a token that represents the loaded
+ * file. */
+{
+ FsDivertLoad *tvdlPtr = (FsDivertLoad*)loadHandle;
+ /*
+ * This test should never trigger, since we give
+ * the client data in the function above.
+ */
+ if (tvdlPtr == NULL) { return; }
+
+ /*
+ * Call the real 'unloadfile' proc we actually used. It is very
+ * important that we call this first, so that the shared library
+ * is actually unloaded by the OS. Otherwise, the following
+ * 'delete' may well fail because the shared library is still in
+ * use.
+ */
+ if (tvdlPtr->unloadProcPtr != NULL) {
+ (*tvdlPtr->unloadProcPtr)(tvdlPtr->loadHandle);
+ }
+
+ /* Remove the temporary file we created. */
+ if (Tcl_FSDeleteFile(tvdlPtr->divertedFile) != TCL_OK) {
+ /*
+ * The above may have failed because the filesystem, or something
+ * it depends upon (e.g. encodings) are being taken down because
+ * Tcl is exiting.
+ *
+ * Therefore we try to call the filesystem's 'delete file proc'
+ * directly. Note that this call may still cause problems, because
+ * it will ask for the native representation of the divertedFile,
+ * and that may need to be _recalculated_, in which case this
+ * call isn't very different to the above. What we could do
+ * instead is generate a new Tcl_Obj (pure native) by calling:
+ *
+ * Tcl_Obj *tmp = Tcl_FSNewNativePath(tvdlPtr->divertedFile,
+ * tvdlPtr->divertedFileNativeRep);
+ * Tcl_IncrRefCount(tmp);
+ * tvdlPtr->divertedFilesystem->deleteFileProc(tmp);
+ * Tcl_DecrRefCount(tmp);
+ *
+ * and then use that in this call. This approach would potentially
+ * work even if the encodings and everything else have been
+ * deconstructed. For the moment, however, we simply assume
+ * Tcl_FSDeleteFile has worked correctly.
+ */
+ }
+
+ /*
+ * And free up the allocations. This will also of course remove
+ * a refCount from the Tcl_Filesystem to which this file belongs,
+ * which could then free up the filesystem if we are exiting.
+ */
+ Tcl_DecrRefCount(tvdlPtr->divertedFile);
+ ckfree((char*)tvdlPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSLink --
+ *
+ * This function replaces the library version of readlink() and
+ * can also be used to make links. The appropriate function for
+ * the filesystem to which pathPtr belongs will be called.
+ *
+ * Results:
+ * If toPtr is NULL, then the result is a Tcl_Obj specifying the
+ * contents of the symbolic link given by 'pathPtr', or NULL if
+ * the symbolic link could not be read. The result is owned by
+ * the caller, which should call Tcl_DecrRefCount when the result
+ * is no longer needed.
+ *
+ * If toPtr is non-NULL, then the result is toPtr if the link action
+ * was successful, or NULL if not. In this case the result has no
+ * additional reference count, and need not be freed. The actual
+ * action to perform is given by the 'linkAction' flags, which is
+ * an or'd combination of:
+ *
+ * TCL_CREATE_SYMBOLIC_LINK
+ * TCL_CREATE_HARD_LINK
+ *
+ * Note that most filesystems will not support linking across
+ * to different filesystems, so this function will usually
+ * fail unless toPtr is in the same FS as pathPtr.
+ *
+ * Side effects:
+ * See readlink() documentation. A new filesystem link
+ * object may appear
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+Tcl_FSLink(pathPtr, toPtr, linkAction)
+ Tcl_Obj *pathPtr; /* Path of file to readlink or link */
+ Tcl_Obj *toPtr; /* NULL or path to be linked to */
+ int linkAction; /* Action to perform */
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSLinkProc *proc = fsPtr->linkProc;
+ if (proc != NULL) {
+ return (*proc)(pathPtr, toPtr, linkAction);
+ }
+ }
+ /*
+ * If S_IFLNK isn't defined it means that the machine doesn't
+ * support symbolic links, so the file can't possibly be a
+ * symbolic link. Generate an EINVAL error, which is what
+ * happens on machines that do support symbolic links when
+ * you invoke readlink on a file that isn't a symbolic link.
+ */
+#ifndef S_IFLNK
+ errno = EINVAL;
+#else
+ Tcl_SetErrno(ENOENT);
+#endif /* S_IFLNK */
+ return NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSListVolumes --
+ *
+ * Lists the currently mounted volumes. The chain of functions
+ * that have been "inserted" into the filesystem will be called in
+ * succession; each may return a list of volumes, all of which are
+ * added to the result until all mounted file systems are listed.
+ *
+ * Notice that we assume the lists returned by each filesystem
+ * (if non NULL) have been given a refCount for us already.
+ * However, we are NOT allowed to hang on to the list itself
+ * (it belongs to the filesystem we called). Therefore we
+ * quite naturally add its contents to the result we are
+ * building, and then decrement the refCount.
+ *
+ * Results:
+ * The list of volumes, in an object which has refCount 0.
+ *
+ * Side effects:
+ * None
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+Tcl_FSListVolumes(void)
+{
+ FilesystemRecord *fsRecPtr;
+ Tcl_Obj *resultPtr = Tcl_NewObj();
+
+ /*
+ * Call each of the "listVolumes" function in succession.
+ * A non-NULL return value indicates the particular function has
+ * succeeded. We call all the functions registered, since we want
+ * a list of all drives from all filesystems.
+ */
+
+ fsRecPtr = FsGetIterator();
+ while (fsRecPtr != NULL) {
+ Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc;
+ if (proc != NULL) {
+ Tcl_Obj *thisFsVolumes = (*proc)();
+ if (thisFsVolumes != NULL) {
+ Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes);
+ Tcl_DecrRefCount(thisFsVolumes);
+ }
+ }
+ fsRecPtr = fsRecPtr->nextPtr;
+ }
+ FsReleaseIterator();
+
+ return resultPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FSGetPathType --
+ *
+ * Determines whether a given path is relative to the current
+ * directory, relative to the current volume, or absolute.
+ *
+ * Results:
+ * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
+ * TCL_PATH_VOLUME_RELATIVE.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_PathType
+Tcl_FSGetPathType(pathObjPtr)
+ Tcl_Obj *pathObjPtr;
+{
+ return FSGetPathType(pathObjPtr, NULL, NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FSGetPathType --
+ *
+ * Determines whether a given path is relative to the current
+ * directory, relative to the current volume, or absolute. If the
+ * caller wishes to know which filesystem claimed the path (in the
+ * case for which the path is absolute), then a reference to a
+ * filesystem pointer can be passed in (but passing NULL is
+ * acceptable).
+ *
+ * Results:
+ * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
+ * TCL_PATH_VOLUME_RELATIVE. The filesystem reference will
+ * be set if and only if it is non-NULL and the function's
+ * return value is TCL_PATH_ABSOLUTE.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_PathType
+FSGetPathType(pathObjPtr, filesystemPtrPtr, driveNameLengthPtr)
+ Tcl_Obj *pathObjPtr;
+ Tcl_Filesystem **filesystemPtrPtr;
+ int *driveNameLengthPtr;
+{
+ if (Tcl_FSConvertToPathType(NULL, pathObjPtr) != TCL_OK) {
+ return GetPathType(pathObjPtr, filesystemPtrPtr,
+ driveNameLengthPtr, NULL);
+ } else {
+ FsPath *fsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
+ if (fsPathPtr->cwdPtr != NULL) {
+ return TCL_PATH_RELATIVE;
+ } else {
+ return GetPathType(pathObjPtr, filesystemPtrPtr,
+ driveNameLengthPtr, NULL);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSSplitPath --
+ *
+ * This function takes the given Tcl_Obj, which should be a valid
+ * path, and returns a Tcl List object containing each segment of
+ * that path as an element.
+ *
+ * Results:
+ * Returns list object with refCount of zero. If the passed in
+ * lenPtr is non-NULL, we use it to return the number of elements
+ * in the returned list.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+Tcl_FSSplitPath(pathPtr, lenPtr)
+ Tcl_Obj *pathPtr; /* Path to split. */
+ int *lenPtr; /* int to store number of path elements. */
+{
+ Tcl_Obj *result = NULL; /* Needed only to prevent gcc warnings. */
+ Tcl_Filesystem *fsPtr;
+ char separator = '/';
+ int driveNameLength;
+ char *p;
+
+ /*
+ * Perform platform specific splitting.
+ */
+
+ if (FSGetPathType(pathPtr, &fsPtr, &driveNameLength)
+ == TCL_PATH_ABSOLUTE) {
+ if (fsPtr == &tclNativeFilesystem) {
+ return TclpNativeSplitPath(pathPtr, lenPtr);
+ }
+ } else {
+ return TclpNativeSplitPath(pathPtr, lenPtr);
+ }
+
+ /* We assume separators are single characters */
+ if (fsPtr->filesystemSeparatorProc != NULL) {
+ Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(pathPtr);
+ if (sep != NULL) {
+ separator = Tcl_GetString(sep)[0];
+ }
+ }
+
+ /*
+ * Place the drive name as first element of the
+ * result list. The drive name may contain strange
+ * characters, like colons and multiple forward slashes
+ * (for example 'ftp://' is a valid vfs drive name)
+ */
+ result = Tcl_NewObj();
+ p = Tcl_GetString(pathPtr);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(p, driveNameLength));
+ p+= driveNameLength;
+
+ /* Add the remaining path elements to the list */
+ for (;;) {
+ char *elementStart = p;
+ int length;
+ while ((*p != '\0') && (*p != separator)) {
+ p++;
+ }
+ length = p - elementStart;
+ if (length > 0) {
+ Tcl_Obj *nextElt;
+ if (elementStart[0] == '~') {
+ nextElt = Tcl_NewStringObj("./",2);
+ Tcl_AppendToObj(nextElt, elementStart, length);
+ } else {
+ nextElt = Tcl_NewStringObj(elementStart, length);
+ }
+ Tcl_ListObjAppendElement(NULL, result, nextElt);
+ }
+ if (*p++ == '\0') {
+ break;
+ }
+ }
+
+ /*
+ * Compute the number of elements in the result.
+ */
+
+ if (lenPtr != NULL) {
+ Tcl_ListObjLength(NULL, result, lenPtr);
+ }
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSJoinPath --
+ *
+ * This function takes the given Tcl_Obj, which should be a valid
+ * list, and returns the path object given by considering the
+ * first 'elements' elements as valid path segments. If elements < 0,
+ * we use the entire list.
+ *
+ * Results:
+ * Returns object with refCount of zero.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tcl_Obj*
+Tcl_FSJoinPath(listObj, elements)
+ Tcl_Obj *listObj;
+ int elements;
+{
+ Tcl_Obj *res;
+ int i;
+ Tcl_Filesystem *fsPtr = NULL;
+
+ if (elements < 0) {
+ if (Tcl_ListObjLength(NULL, listObj, &elements) != TCL_OK) {
+ return NULL;
+ }
+ } else {
+ /* Just make sure it is a valid list */
+ int listTest;
+ if (Tcl_ListObjLength(NULL, listObj, &listTest) != TCL_OK) {
+ return NULL;
+ }
+ /*
+ * Correct this if it is too large, otherwise we will
+ * waste our timing joining null elements to the path
+ */
+ if (elements > listTest) {
+ elements = listTest;
+ }
+ }
+
+ res = Tcl_NewObj();
+
+ for (i = 0; i < elements; i++) {
+ Tcl_Obj *elt;
+ int driveNameLength;
+ Tcl_PathType type;
+ char *strElt;
+ int strEltLen;
+ int length;
+ char *ptr;
+ Tcl_Obj *driveName = NULL;
+
+ Tcl_ListObjIndex(NULL, listObj, i, &elt);
+ strElt = Tcl_GetStringFromObj(elt, &strEltLen);
+ type = GetPathType(elt, &fsPtr, &driveNameLength, &driveName);
+ if (type != TCL_PATH_RELATIVE) {
+ /* Zero out the current result */
+ Tcl_DecrRefCount(res);
+ if (driveName != NULL) {
+ res = Tcl_DuplicateObj(driveName);
+ Tcl_DecrRefCount(driveName);
+ } else {
+ res = Tcl_NewStringObj(strElt, driveNameLength);
+ }
+ strElt += driveNameLength;
+ }
+
+ ptr = Tcl_GetStringFromObj(res, &length);
+
+ /*
+ * Strip off any './' before a tilde, unless this is the
+ * beginning of the path.
+ */
+ if (length > 0 && strEltLen > 0) {
+ if ((strElt[0] == '.') && (strElt[1] == '/')
+ && (strElt[2] == '~')) {
+ strElt += 2;
+ }
+ }
+
+ /*
+ * A NULL value for fsPtr at this stage basically means
+ * we're trying to join a relative path onto something
+ * which is also relative (or empty). There's nothing
+ * particularly wrong with that.
+ */
+ if (*strElt == '\0') continue;
+
+ if (fsPtr == &tclNativeFilesystem || fsPtr == NULL) {
+ TclpNativeJoinPath(res, strElt);
+ } else {
+ char separator = '/';
+ int needsSep = 0;
+
+ if (fsPtr->filesystemSeparatorProc != NULL) {
+ Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(res);
+ if (sep != NULL) {
+ separator = Tcl_GetString(sep)[0];
+ }
+ }
+
+ if (length > 0 && ptr[length -1] != '/') {
+ Tcl_AppendToObj(res, &separator, 1);
+ length++;
+ }
+ Tcl_SetObjLength(res, length + (int) strlen(strElt));
+
+ ptr = Tcl_GetString(res) + length;
+ for (; *strElt != '\0'; strElt++) {
+ if (*strElt == separator) {
+ while (strElt[1] == separator) {
+ strElt++;
+ }
+ if (strElt[1] != '\0') {
+ if (needsSep) {
+ *ptr++ = separator;
+ }
+ }
+ } else {
+ *ptr++ = *strElt;
+ needsSep = 1;
+ }
+ }
+ length = ptr - Tcl_GetString(res);
+ Tcl_SetObjLength(res, length);
+ }
+ }
+ return res;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetPathType --
+ *
+ * Helper function used by FSGetPathType.
+ *
+ * Results:
+ * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
+ * TCL_PATH_VOLUME_RELATIVE. The filesystem reference will
+ * be set if and only if it is non-NULL and the function's
+ * return value is TCL_PATH_ABSOLUTE.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_PathType
+GetPathType(pathObjPtr, filesystemPtrPtr, driveNameLengthPtr, driveNameRef)
+ Tcl_Obj *pathObjPtr;
+ Tcl_Filesystem **filesystemPtrPtr;
+ int *driveNameLengthPtr;
+ Tcl_Obj **driveNameRef;
+{
+ FilesystemRecord *fsRecPtr;
+ int pathLen;
+ char *path;
+ Tcl_PathType type = TCL_PATH_RELATIVE;
+
+ path = Tcl_GetStringFromObj(pathObjPtr, &pathLen);
+
+ /*
+ * Call each of the "listVolumes" function in succession, checking
+ * whether the given path is an absolute path on any of the volumes
+ * returned (this is done by checking whether the path's prefix
+ * matches).
+ */
+
+ fsRecPtr = FsGetIterator();
+ while (fsRecPtr != NULL) {
+ Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc;
+ /*
+ * We want to skip the native filesystem in this loop because
+ * otherwise we won't necessarily pass all the Tcl testsuite --
+ * this is because some of the tests artificially change the
+ * current platform (between mac, win, unix) but the list
+ * of volumes we get by calling (*proc) will reflect the current
+ * (real) platform only and this may cause some tests to fail.
+ * In particular, on unix '/' will match the beginning of
+ * certain absolute Windows paths starting '//' and those tests
+ * will go wrong.
+ *
+ * Besides these test-suite issues, there is one other reason
+ * to skip the native filesystem --- since the tclFilename.c
+ * code has nice fast 'absolute path' checkers, we don't want
+ * to waste time repeating that effort here, and this
+ * function is actually called quite often, so if we can
+ * save the overhead of the native filesystem returning us
+ * a list of volumes all the time, it is better.
+ */
+ if ((fsRecPtr->fsPtr != &tclNativeFilesystem) && (proc != NULL)) {
+ int numVolumes;
+ Tcl_Obj *thisFsVolumes = (*proc)();
+ if (thisFsVolumes != NULL) {
+ if (Tcl_ListObjLength(NULL, thisFsVolumes,
+ &numVolumes) != TCL_OK) {
+ /*
+ * This is VERY bad; the Tcl_FSListVolumesProc
+ * didn't return a valid list. Set numVolumes to
+ * -1 so that we skip the while loop below and just
+ * return with the current value of 'type'.
+ *
+ * It would be better if we could signal an error
+ * here (but panic seems a bit excessive).
+ */
+ numVolumes = -1;
+ }
+ while (numVolumes > 0) {
+ Tcl_Obj *vol;
+ int len;
+ char *strVol;
+
+ numVolumes--;
+ Tcl_ListObjIndex(NULL, thisFsVolumes, numVolumes, &vol);
+ strVol = Tcl_GetStringFromObj(vol,&len);
+ if (pathLen < len) {
+ continue;
+ }
+ if (strncmp(strVol, path, (size_t) len) == 0) {
+ type = TCL_PATH_ABSOLUTE;
+ if (filesystemPtrPtr != NULL) {
+ *filesystemPtrPtr = fsRecPtr->fsPtr;
+ }
+ if (driveNameLengthPtr != NULL) {
+ *driveNameLengthPtr = len;
+ }
+ if (driveNameRef != NULL) {
+ *driveNameRef = vol;
+ Tcl_IncrRefCount(vol);
+ }
+ break;
+ }
+ }
+ Tcl_DecrRefCount(thisFsVolumes);
+ if (type == TCL_PATH_ABSOLUTE) {
+ /* We don't need to examine any more filesystems */
+ break;
+ }
+ }
+ }
+ fsRecPtr = fsRecPtr->nextPtr;
+ }
+ FsReleaseIterator();
+
+ if (type != TCL_PATH_ABSOLUTE) {
+ type = TclpGetNativePathType(pathObjPtr, driveNameLengthPtr,
+ driveNameRef);
+ if ((type == TCL_PATH_ABSOLUTE) && (filesystemPtrPtr != NULL)) {
+ *filesystemPtrPtr = &tclNativeFilesystem;
+ }
+ }
+ return type;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSRenameFile --
+ *
+ * If the two paths given belong to the same filesystem, we call
+ * that filesystems rename function. Otherwise we simply
+ * return the posix error 'EXDEV', and -1.
+ *
+ * Results:
+ * Standard Tcl error code if a function was called.
+ *
+ * Side effects:
+ * A file may be renamed.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Tcl_FSRenameFile(srcPathPtr, destPathPtr)
+ Tcl_Obj* srcPathPtr; /* Pathname of file or dir to be renamed
+ * (UTF-8). */
+ Tcl_Obj *destPathPtr; /* New pathname of file or directory
+ * (UTF-8). */
+{
+ int retVal = -1;
+ Tcl_Filesystem *fsPtr, *fsPtr2;
+ fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
+ fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
+
+ if (fsPtr == fsPtr2 && fsPtr != NULL) {
+ Tcl_FSRenameFileProc *proc = fsPtr->renameFileProc;
+ if (proc != NULL) {
+ retVal = (*proc)(srcPathPtr, destPathPtr);
+ }
+ }
+ if (retVal == -1) {
+ Tcl_SetErrno(EXDEV);
+ }
+ return retVal;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSCopyFile --
+ *
+ * If the two paths given belong to the same filesystem, we call
+ * that filesystem's copy function. Otherwise we simply
+ * return the posix error 'EXDEV', and -1.
+ *
+ * Note that in the native filesystems, 'copyFileProc' is defined
+ * to copy soft links (i.e. it copies the links themselves, not
+ * the things they point to).
+ *
+ * Results:
+ * Standard Tcl error code if a function was called.
+ *
+ * Side effects:
+ * A file may be copied.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Tcl_FSCopyFile(srcPathPtr, destPathPtr)
+ Tcl_Obj* srcPathPtr; /* Pathname of file to be copied (UTF-8). */
+ Tcl_Obj *destPathPtr; /* Pathname of file to copy to (UTF-8). */
+{
+ int retVal = -1;
+ Tcl_Filesystem *fsPtr, *fsPtr2;
+ fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
+ fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
+
+ if (fsPtr == fsPtr2 && fsPtr != NULL) {
+ Tcl_FSCopyFileProc *proc = fsPtr->copyFileProc;
+ if (proc != NULL) {
+ retVal = (*proc)(srcPathPtr, destPathPtr);
+ }
+ }
+ if (retVal == -1) {
+ Tcl_SetErrno(EXDEV);
+ }
+ return retVal;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclCrossFilesystemCopy --
+ *
+ * Helper for above function, and for Tcl_FSLoadFile, to copy
+ * files from one filesystem to another. This function will
+ * overwrite the target file if it already exists.
+ *
+ * Results:
+ * Standard Tcl error code.
+ *
+ * Side effects:
+ * A file may be created.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+TclCrossFilesystemCopy(interp, source, target)
+ Tcl_Interp *interp; /* For error messages */
+ Tcl_Obj *source; /* Pathname of file to be copied (UTF-8). */
+ Tcl_Obj *target; /* Pathname of file to copy to (UTF-8). */
+{
+ int result = TCL_ERROR;
+ int prot = 0666;
+
+ Tcl_Channel out = Tcl_FSOpenFileChannel(interp, target, "w", prot);
+ if (out != NULL) {
+ /* It looks like we can copy it over */
+ Tcl_Channel in = Tcl_FSOpenFileChannel(interp, source,
+ "r", prot);
+ if (in == NULL) {
+ /* This is very strange, we checked this above */
+ Tcl_Close(interp, out);
+ } else {
+ Tcl_StatBuf sourceStatBuf;
+ struct utimbuf tval;
+ /*
+ * Copy it synchronously. We might wish to add an
+ * asynchronous option to support vfs's which are
+ * slow (e.g. network sockets).
+ */
+ Tcl_SetChannelOption(interp, in, "-translation", "binary");
+ Tcl_SetChannelOption(interp, out, "-translation", "binary");
+
+ if (TclCopyChannel(interp, in, out, -1, NULL) == TCL_OK) {
+ result = TCL_OK;
+ }
+ /*
+ * If the copy failed, assume that copy channel left
+ * a good error message.
+ */
+ Tcl_Close(interp, in);
+ Tcl_Close(interp, out);
+
+ /* Set modification date of copied file */
+ if (Tcl_FSLstat(source, &sourceStatBuf) != 0) {
+ tval.actime = sourceStatBuf.st_atime;
+ tval.modtime = sourceStatBuf.st_mtime;
+ Tcl_FSUtime(source, &tval);
+ }
+ }
+ }
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSDeleteFile --
+ *
+ * The appropriate function for the filesystem to which pathPtr
+ * belongs will be called.
+ *
+ * Results:
+ * Standard Tcl error code.
+ *
+ * Side effects:
+ * A file may be deleted.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Tcl_FSDeleteFile(pathPtr)
+ Tcl_Obj *pathPtr; /* Pathname of file to be removed (UTF-8). */
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSDeleteFileProc *proc = fsPtr->deleteFileProc;
+ if (proc != NULL) {
+ return (*proc)(pathPtr);
+ }
+ }
+ Tcl_SetErrno(ENOENT);
+ return -1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSCreateDirectory --
+ *
+ * The appropriate function for the filesystem to which pathPtr
+ * belongs will be called.
+ *
+ * Results:
+ * Standard Tcl error code.
+ *
+ * Side effects:
+ * A directory may be created.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Tcl_FSCreateDirectory(pathPtr)
+ Tcl_Obj *pathPtr; /* Pathname of directory to create (UTF-8). */
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSCreateDirectoryProc *proc = fsPtr->createDirectoryProc;
+ if (proc != NULL) {
+ return (*proc)(pathPtr);
+ }
+ }
+ Tcl_SetErrno(ENOENT);
+ return -1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSCopyDirectory --
+ *
+ * If the two paths given belong to the same filesystem, we call
+ * that filesystems copy-directory function. Otherwise we simply
+ * return the posix error 'EXDEV', and -1.
+ *
+ * Results:
+ * Standard Tcl error code if a function was called.
+ *
+ * Side effects:
+ * A directory may be copied.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Tcl_FSCopyDirectory(srcPathPtr, destPathPtr, errorPtr)
+ Tcl_Obj* srcPathPtr; /* Pathname of directory to be copied
+ * (UTF-8). */
+ Tcl_Obj *destPathPtr; /* Pathname of target directory (UTF-8). */
+ Tcl_Obj **errorPtr; /* If non-NULL, then will be set to a
+ * new object containing name of file
+ * causing error, with refCount 1. */
+{
+ int retVal = -1;
+ Tcl_Filesystem *fsPtr, *fsPtr2;
+ fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
+ fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
+
+ if (fsPtr == fsPtr2 && fsPtr != NULL) {
+ Tcl_FSCopyDirectoryProc *proc = fsPtr->copyDirectoryProc;
+ if (proc != NULL) {
+ retVal = (*proc)(srcPathPtr, destPathPtr, errorPtr);
+ }
+ }
+ if (retVal == -1) {
+ Tcl_SetErrno(EXDEV);
+ }
+ return retVal;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSRemoveDirectory --
+ *
+ * The appropriate function for the filesystem to which pathPtr
+ * belongs will be called.
+ *
+ * Results:
+ * Standard Tcl error code.
+ *
+ * Side effects:
+ * A directory may be deleted.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Tcl_FSRemoveDirectory(pathPtr, recursive, errorPtr)
+ Tcl_Obj *pathPtr; /* Pathname of directory to be removed
+ * (UTF-8). */
+ int recursive; /* If non-zero, removes directories that
+ * are nonempty. Otherwise, will only remove
+ * empty directories. */
+ Tcl_Obj **errorPtr; /* If non-NULL, then will be set to a
+ * new object containing name of file
+ * causing error, with refCount 1. */
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != NULL) {
+ Tcl_FSRemoveDirectoryProc *proc = fsPtr->removeDirectoryProc;
+ if (proc != NULL) {
+ if (recursive) {
+ /*
+ * We check whether the cwd lies inside this directory
+ * and move it if it does.
+ */
+ Tcl_Obj *cwdPtr = Tcl_FSGetCwd(NULL);
+ if (cwdPtr != NULL) {
+ char *cwdStr, *normPathStr;
+ int cwdLen, normLen;
+ Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathPtr);
+ if (normPath != NULL) {
+ normPathStr = Tcl_GetStringFromObj(normPath, &normLen);
+ cwdStr = Tcl_GetStringFromObj(cwdPtr, &cwdLen);
+ if ((cwdLen >= normLen) && (strncmp(normPathStr,
+ cwdStr, (size_t) normLen) == 0)) {
+ /*
+ * the cwd is inside the directory, so we
+ * perform a 'cd [file dirname $path]'
+ */
+ Tcl_Obj *dirPtr = TclFileDirname(NULL, pathPtr);
+ Tcl_FSChdir(dirPtr);
+ Tcl_DecrRefCount(dirPtr);
+ }
+ }
+ Tcl_DecrRefCount(cwdPtr);
+ }
+ }
+ return (*proc)(pathPtr, recursive, errorPtr);
+ }
+ }
+ Tcl_SetErrno(ENOENT);
+ return -1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSConvertToPathType --
+ *
+ * This function tries to convert the given Tcl_Obj to a valid
+ * Tcl path type, taking account of the fact that the cwd may
+ * have changed even if this object is already supposedly of
+ * the correct type.
+ *
+ * The filename may begin with "~" (to indicate current user's
+ * home directory) or "~<user>" (to indicate any user's home
+ * directory).
+ *
+ * Results:
+ * Standard Tcl error code.
+ *
+ * Side effects:
+ * The old representation may be freed, and new memory allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Tcl_FSConvertToPathType(interp, objPtr)
+ Tcl_Interp *interp; /* Interpreter in which to store error
+ * message (if necessary). */
+ Tcl_Obj *objPtr; /* Object to convert to a valid, current
+ * path type. */
+{
+ /*
+ * While it is bad practice to examine an object's type directly,
+ * this is actually the best thing to do here. The reason is that
+ * if we are converting this object to FsPath type for the first
+ * time, we don't need to worry whether the 'cwd' has changed.
+ * On the other hand, if this object is already of FsPath type,
+ * and is a relative path, we do have to worry about the cwd.
+ * If the cwd has changed, we must recompute the path.
+ */
+ if (objPtr->typePtr == &tclFsPathType) {
+ FsPath *fsPathPtr = (FsPath*) objPtr->internalRep.otherValuePtr;
+ if (fsPathPtr->filesystemEpoch != theFilesystemEpoch) {
+ FreeFsPathInternalRep(objPtr);
+ objPtr->typePtr = NULL;
+ return Tcl_ConvertToType(interp, objPtr, &tclFsPathType);
+ }
+ if (fsPathPtr->cwdPtr == NULL) {
+ return TCL_OK;
+ } else {
+ if (FsCwdPointerEquals(fsPathPtr->cwdPtr)) {
+ return TCL_OK;
+ } else {
+ FreeFsPathInternalRep(objPtr);
+ objPtr->typePtr = NULL;
+ return Tcl_ConvertToType(interp, objPtr, &tclFsPathType);
+ }
+ }
+ } else {
+ return Tcl_ConvertToType(interp, objPtr, &tclFsPathType);
+ }
+}
+
+
+/*
+ * Helper function for SetFsPathFromAny. Returns position of first
+ * directory delimiter in the path.
+ */
+static int
+FindSplitPos(path, separator)
+ char *path;
+ char *separator;
+{
+ int count = 0;
+ switch (tclPlatform) {
+ case TCL_PLATFORM_UNIX:
+ case TCL_PLATFORM_MAC:
+ while (path[count] != 0) {
+ if (path[count] == *separator) {
+ return count;
+ }
+ count++;
+ }
+ break;
+
+ case TCL_PLATFORM_WINDOWS:
+ while (path[count] != 0) {
+ if (path[count] == *separator || path[count] == '\\') {
+ return count;
+ }
+ count++;
+ }
+ break;
+ }
+ return count;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SetFsPathFromAbsoluteNormalized --
+ *
+ * Like SetFsPathFromAny, but assumes the given object is an
+ * absolute normalized path. Only for internal use.
+ *
+ * Results:
+ * Standard Tcl error code.
+ *
+ * Side effects:
+ * The old representation may be freed, and new memory allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static int
+SetFsPathFromAbsoluteNormalized(interp, objPtr)
+ Tcl_Interp *interp; /* Used for error reporting if not NULL. */
+ Tcl_Obj *objPtr; /* The object to convert. */
+{
+ FsPath *fsPathPtr;
+
+ if (objPtr->typePtr == &tclFsPathType) {
+ return TCL_OK;
+ }
+
+ /* Free old representation */
+ if (objPtr->typePtr != NULL) {
+ if (objPtr->bytes == NULL) {
+ if (objPtr->typePtr->updateStringProc == NULL) {
+ if (interp != NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "can't find object",
+ "string representation", (char *) NULL);
+ }
+ return TCL_ERROR;
+ }
+ objPtr->typePtr->updateStringProc(objPtr);
+ }
+ if ((objPtr->typePtr->freeIntRepProc) != NULL) {
+ (*objPtr->typePtr->freeIntRepProc)(objPtr);
+ }
+ }
+
+ fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
+ /* It's a pure normalized absolute path */
+ fsPathPtr->translatedPathPtr = NULL;
+ fsPathPtr->normPathPtr = objPtr;
+ fsPathPtr->cwdPtr = NULL;
+ fsPathPtr->nativePathPtr = NULL;
+ fsPathPtr->fsRecPtr = NULL;
+ fsPathPtr->filesystemEpoch = theFilesystemEpoch;
+
+ objPtr->internalRep.otherValuePtr = (VOID *) fsPathPtr;
+ objPtr->typePtr = &tclFsPathType;
+
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SetFsPathFromAny --
+ *
+ * This function tries to convert the given Tcl_Obj to a valid
+ * Tcl path type.
+ *
+ * The filename may begin with "~" (to indicate current user's
+ * home directory) or "~<user>" (to indicate any user's home
+ * directory).
+ *
+ * Results:
+ * Standard Tcl error code.
+ *
+ * Side effects:
+ * The old representation may be freed, and new memory allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static int
+SetFsPathFromAny(interp, objPtr)
+ Tcl_Interp *interp; /* Used for error reporting if not NULL. */
+ Tcl_Obj *objPtr; /* The object to convert. */
+{
+ int len;
+ FsPath *fsPathPtr;
+ Tcl_Obj *transPtr;
+ char *name;
+
+ if (objPtr->typePtr == &tclFsPathType) {
+ return TCL_OK;
+ }
+
+ /*
+ * First step is to translate the filename. This is similar to
+ * Tcl_TranslateFilename, but shouldn't convert everything to
+ * windows backslashes on that platform. The current
+ * implementation of this piece is a slightly optimised version
+ * of the various Tilde/Split/Join stuff to avoid multiple
+ * split/join operations.
+ *
+ * We remove any trailing directory separator.
+ *
+ * However, the split/join routines are quite complex, and
+ * one has to make sure not to break anything on Unix, Win
+ * or MacOS (fCmd.test, fileName.test and cmdAH.test exercise
+ * most of the code).
+ */
+ name = Tcl_GetStringFromObj(objPtr,&len);
+
+ /*
+ * Handle tilde substitutions, if needed.
+ */
+ if (name[0] == '~') {
+ char *expandedUser;
+ Tcl_DString temp;
+ int split;
+ char separator='/';
+
+ if (tclPlatform==TCL_PLATFORM_MAC) {
+ if (strchr(name, ':') != NULL) separator = ':';
+ }
+
+ split = FindSplitPos(name, &separator);
+ if (split != len) {
+ /* We have multiple pieces '~user/foo/bar...' */
+ name[split] = '\0';
+ }
+ /* Do some tilde substitution */
+ if (name[1] == '\0') {
+ /* We have just '~' */
+ CONST char *dir;
+ Tcl_DString dirString;
+ if (split != len) { name[split] = separator; }
+
+ dir = TclGetEnv("HOME", &dirString);
+ if (dir == NULL) {
+ if (interp) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "couldn't find HOME environment ",
+ "variable to expand path", (char *) NULL);
+ }
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&temp);
+ Tcl_JoinPath(1, &dir, &temp);
+ Tcl_DStringFree(&dirString);
+ } else {
+ /* We have a user name '~user' */
+ Tcl_DStringInit(&temp);
+ if (TclpGetUserHome(name+1, &temp) == NULL) {
+ if (interp != NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "user \"", (name+1),
+ "\" doesn't exist", (char *) NULL);
+ }
+ Tcl_DStringFree(&temp);
+ if (split != len) { name[split] = separator; }
+ return TCL_ERROR;
+ }
+ if (split != len) { name[split] = separator; }
+ }
+
+ expandedUser = Tcl_DStringValue(&temp);
+ transPtr = Tcl_NewStringObj(expandedUser, Tcl_DStringLength(&temp));
+
+ if (split != len) {
+ /* Join up the tilde substitution with the rest */
+ if (name[split+1] == separator) {
+
+ /*
+ * Somewhat tricky case like ~//foo/bar.
+ * Make use of Split/Join machinery to get it right.
+ * Assumes all paths beginning with ~ are part of the
+ * native filesystem.
+ */
+
+ int objc;
+ Tcl_Obj **objv;
+ Tcl_Obj *parts = TclpNativeSplitPath(objPtr, NULL);
+ Tcl_ListObjGetElements(NULL, parts, &objc, &objv);
+ /* Skip '~'. It's replaced by its expansion */
+ objc--; objv++;
+ while (objc--) {
+ TclpNativeJoinPath(transPtr, Tcl_GetString(*objv++));
+ }
+ Tcl_DecrRefCount(parts);
+ } else {
+ /* Simple case. "rest" is relative path. Just join it. */
+ Tcl_Obj *rest = Tcl_NewStringObj(name+split+1,-1);
+ transPtr = Tcl_FSJoinToPath(transPtr, 1, &rest);
+ }
+ }
+ Tcl_DStringFree(&temp);
+ } else {
+ transPtr = Tcl_FSJoinToPath(objPtr,0,NULL);
+ }
+
+ /*
+ * Now we have a translated filename in 'transPtr'. This will have
+ * forward slashes on Windows, and will not contain any ~user
+ * sequences.
+ */
+
+ fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
+ fsPathPtr->translatedPathPtr = transPtr;
+ Tcl_IncrRefCount(fsPathPtr->translatedPathPtr);
+ fsPathPtr->normPathPtr = NULL;
+ fsPathPtr->cwdPtr = NULL;
+ fsPathPtr->nativePathPtr = NULL;
+ fsPathPtr->fsRecPtr = NULL;
+ fsPathPtr->filesystemEpoch = theFilesystemEpoch;
+
+ /*
+ * Free old representation before installing our new one.
+ */
+ if (objPtr->typePtr != NULL && objPtr->typePtr->freeIntRepProc != NULL) {
+ (objPtr->typePtr->freeIntRepProc)(objPtr);
+ }
+ objPtr->internalRep.otherValuePtr = (VOID *) fsPathPtr;
+ objPtr->typePtr = &tclFsPathType;
+
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSNewNativePath --
+ *
+ * This function performs the something like that reverse of the
+ * usual obj->path->nativerep conversions. If some code retrieves
+ * a path in native form (from, e.g. readlink or a native dialog),
+ * and that path is to be used at the Tcl level, then calling
+ * this function is an efficient way of creating the appropriate
+ * path object type.
+ *
+ * Any memory which is allocated for 'clientData' should be retained
+ * until clientData is passed to the filesystem's freeInternalRepProc
+ * when it can be freed. The built in platform-specific filesystems
+ * use 'ckalloc' to allocate clientData, and ckfree to free it.
+ *
+ * Results:
+ * NULL or a valid path object pointer, with refCount zero.
+ *
+ * Side effects:
+ * New memory may be allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+Tcl_FSNewNativePath(fromFilesystem, clientData)
+ Tcl_Filesystem* fromFilesystem;
+ ClientData clientData;
+{
+ Tcl_Obj *objPtr;
+ FsPath *fsPathPtr;
+ FilesystemRecord *fsFromPtr;
+ Tcl_FSInternalToNormalizedProc *proc;
+ int epoch;
+
+ fsFromPtr = GetFilesystemRecord(fromFilesystem, &epoch);
+
+ if (fsFromPtr == NULL) {
+ return NULL;
+ }
+
+ proc = fsFromPtr->fsPtr->internalToNormalizedProc;
+
+ if (proc == NULL) {
+ return NULL;
+ }
+
+ objPtr = (*proc)(clientData);
+ if (objPtr == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Free old representation; shouldn't normally be any,
+ * but best to be safe.
+ */
+ if (objPtr->typePtr != NULL) {
+ if (objPtr->bytes == NULL) {
+ if (objPtr->typePtr->updateStringProc == NULL) {
+ return NULL;
+ }
+ objPtr->typePtr->updateStringProc(objPtr);
+ }
+ if ((objPtr->typePtr->freeIntRepProc) != NULL) {
+ (*objPtr->typePtr->freeIntRepProc)(objPtr);
+ }
+ }
+
+ fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
+ fsPathPtr->translatedPathPtr = NULL;
+ /* Circular reference, by design */
+ fsPathPtr->normPathPtr = objPtr;
+ fsPathPtr->cwdPtr = NULL;
+ fsPathPtr->nativePathPtr = clientData;
+ fsPathPtr->fsRecPtr = fsFromPtr;
+ /* We must increase the refCount for this filesystem. */
+ fsPathPtr->fsRecPtr->fileRefCount++;
+ fsPathPtr->filesystemEpoch = epoch;
+
+ objPtr->internalRep.otherValuePtr = (VOID *) fsPathPtr;
+ objPtr->typePtr = &tclFsPathType;
+ return objPtr;
+}
+
+static void
+FreeFsPathInternalRep(pathObjPtr)
+ Tcl_Obj *pathObjPtr; /* Path object with internal rep to free. */
+{
+ register FsPath* fsPathPtr =
+ (FsPath*) pathObjPtr->internalRep.otherValuePtr;
+
+ if (fsPathPtr->translatedPathPtr != NULL) {
+ Tcl_DecrRefCount(fsPathPtr->translatedPathPtr);
+ }
+ if (fsPathPtr->normPathPtr != NULL) {
+ if (fsPathPtr->normPathPtr != pathObjPtr) {
+ Tcl_DecrRefCount(fsPathPtr->normPathPtr);
+ }
+ fsPathPtr->normPathPtr = NULL;
+ }
+ if (fsPathPtr->cwdPtr != NULL) {
+ Tcl_DecrRefCount(fsPathPtr->cwdPtr);
+ }
+ if (fsPathPtr->nativePathPtr != NULL) {
+ if (fsPathPtr->fsRecPtr != NULL) {
+ if (fsPathPtr->fsRecPtr->fsPtr->freeInternalRepProc != NULL) {
+ (*fsPathPtr->fsRecPtr->fsPtr
+ ->freeInternalRepProc)(fsPathPtr->nativePathPtr);
+ fsPathPtr->nativePathPtr = NULL;
+ }
+ }
+ }
+ if (fsPathPtr->fsRecPtr != NULL) {
+ fsPathPtr->fsRecPtr->fileRefCount--;
+ if (fsPathPtr->fsRecPtr->fileRefCount <= 0) {
+ /* It has been unregistered already */
+ ckfree((char *)fsPathPtr->fsRecPtr);
+ }
+ }
+
+ ckfree((char*) fsPathPtr);
+}
+
+static void
+DupFsPathInternalRep(srcPtr, copyPtr)
+ Tcl_Obj *srcPtr; /* Path obj with internal rep to copy. */
+ Tcl_Obj *copyPtr; /* Path obj with internal rep to set. */
+{
+ register FsPath* srcFsPathPtr =
+ (FsPath*) srcPtr->internalRep.otherValuePtr;
+ register FsPath* copyFsPathPtr =
+ (FsPath*) ckalloc((unsigned)sizeof(FsPath));
+ Tcl_FSDupInternalRepProc *dupProc;
+
+ copyPtr->internalRep.otherValuePtr = (VOID *) copyFsPathPtr;
+
+ if (srcFsPathPtr->translatedPathPtr != NULL) {
+ copyFsPathPtr->translatedPathPtr = srcFsPathPtr->translatedPathPtr;
+ Tcl_IncrRefCount(copyFsPathPtr->translatedPathPtr);
+ } else {
+ copyFsPathPtr->translatedPathPtr = NULL;
+ }
+
+ if (srcFsPathPtr->normPathPtr != NULL) {
+ copyFsPathPtr->normPathPtr = srcFsPathPtr->normPathPtr;
+ if (copyFsPathPtr->normPathPtr != copyPtr) {
+ Tcl_IncrRefCount(copyFsPathPtr->normPathPtr);
+ }
+ } else {
+ copyFsPathPtr->normPathPtr = NULL;
+ }
+
+ if (srcFsPathPtr->cwdPtr != NULL) {
+ copyFsPathPtr->cwdPtr = srcFsPathPtr->cwdPtr;
+ Tcl_IncrRefCount(copyFsPathPtr->cwdPtr);
+ } else {
+ copyFsPathPtr->cwdPtr = NULL;
+ }
+
+ if (srcFsPathPtr->fsRecPtr != NULL
+ && srcFsPathPtr->nativePathPtr != NULL) {
+ dupProc = srcFsPathPtr->fsRecPtr->fsPtr->dupInternalRepProc;
+ if (dupProc != NULL) {
+ copyFsPathPtr->nativePathPtr =
+ (*dupProc)(srcFsPathPtr->nativePathPtr);
+ } else {
+ copyFsPathPtr->nativePathPtr = NULL;
+ }
+ } else {
+ copyFsPathPtr->nativePathPtr = NULL;
+ }
+ copyFsPathPtr->fsRecPtr = srcFsPathPtr->fsRecPtr;
+ copyFsPathPtr->filesystemEpoch = srcFsPathPtr->filesystemEpoch;
+ if (copyFsPathPtr->fsRecPtr != NULL) {
+ copyFsPathPtr->fsRecPtr->fileRefCount++;
+ }
+
+ copyPtr->typePtr = &tclFsPathType;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSGetTranslatedPath --
+ *
+ * This function attempts to extract the translated path
+ * from the given Tcl_Obj. If the translation succeeds (i.e. the
+ * object is a valid path), then it is returned. Otherwise NULL
+ * will be returned, and an error message may be left in the
+ * interpreter (if it is non-NULL)
+ *
+ * Results:
+ * NULL or a valid Tcl_Obj pointer.
+ *
+ * Side effects:
+ * Only those of 'Tcl_FSConvertToPathType'
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+Tcl_FSGetTranslatedPath(interp, pathPtr)
+ Tcl_Interp *interp;
+ Tcl_Obj* pathPtr;
+{
+ register FsPath* srcFsPathPtr;
+ if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
+ return NULL;
+ }
+ srcFsPathPtr = (FsPath*) pathPtr->internalRep.otherValuePtr;
+ if (srcFsPathPtr->translatedPathPtr == NULL) {
+ /*
+ * It is a pure absolute, normalized path object.
+ * This is something like being a 'pure list'. The
+ * object's string, translatedPath and normalizedPath
+ * are all identical.
+ */
+ return srcFsPathPtr->normPathPtr;
+ } else {
+ /* It is an ordinary path object */
+ return srcFsPathPtr->translatedPathPtr;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSGetTranslatedStringPath --
+ *
+ * This function attempts to extract the translated path
+ * from the given Tcl_Obj. If the translation succeeds (i.e. the
+ * object is a valid path), then the path is returned. Otherwise NULL
+ * will be returned, and an error message may be left in the
+ * interpreter (if it is non-NULL)
+ *
+ * Results:
+ * NULL or a valid string.
+ *
+ * Side effects:
+ * Only those of 'Tcl_FSConvertToPathType'
+ *
+ *---------------------------------------------------------------------------
+ */
+CONST char*
+Tcl_FSGetTranslatedStringPath(interp, pathPtr)
+Tcl_Interp *interp;
+Tcl_Obj* pathPtr;
+{
+ Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
+ if (transPtr == NULL) {
+ return NULL;
+ } else {
+ return Tcl_GetString(transPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSGetNormalizedPath --
+ *
+ * This important function attempts to extract from the given Tcl_Obj
+ * a unique normalised path representation, whose string value can
+ * be used as a unique identifier for the file.
+ *
+ * Results:
+ * NULL or a valid path object pointer.
+ *
+ * Side effects:
+ * New memory may be allocated. The Tcl 'errno' may be modified
+ * in the process of trying to examine various path possibilities.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+Tcl_FSGetNormalizedPath(interp, pathObjPtr)
+ Tcl_Interp *interp;
+ Tcl_Obj* pathObjPtr;
+{
+ register FsPath* srcFsPathPtr;
+ if (Tcl_FSConvertToPathType(interp, pathObjPtr) != TCL_OK) {
+ return NULL;
+ }
+ srcFsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
+ if (srcFsPathPtr->normPathPtr == NULL) {
+ int relative = 0;
+ /*
+ * Since normPathPtr is NULL, but this is a valid path
+ * object, we know that the translatedPathPtr cannot be NULL.
+ */
+ Tcl_Obj *absolutePath = srcFsPathPtr->translatedPathPtr;
+ char *path = Tcl_GetString(absolutePath);
+
+ /*
+ * We have to be a little bit careful here to avoid infinite loops
+ * we're asking Tcl_FSGetPathType to return the path's type, but
+ * that call can actually result in a lot of other filesystem
+ * action, which might loop back through here.
+ */
+ if ((path[0] != '\0') &&
+ (Tcl_FSGetPathType(pathObjPtr) == TCL_PATH_RELATIVE)) {
+ Tcl_Obj *cwd = Tcl_FSGetCwd(interp);
+
+ if (cwd == NULL) {
+ return NULL;
+ }
+
+ absolutePath = Tcl_FSJoinToPath(cwd, 1, &absolutePath);
+ Tcl_IncrRefCount(absolutePath);
+ Tcl_DecrRefCount(cwd);
+
+ relative = 1;
+ }
+ /* Already has refCount incremented */
+ srcFsPathPtr->normPathPtr = FSNormalizeAbsolutePath(interp, absolutePath);
+ if (!strcmp(Tcl_GetString(srcFsPathPtr->normPathPtr),
+ Tcl_GetString(pathObjPtr))) {
+ /*
+ * The path was already normalized.
+ * Get rid of the duplicate.
+ */
+ Tcl_DecrRefCount(srcFsPathPtr->normPathPtr);
+ /*
+ * We do *not* increment the refCount for
+ * this circular reference
+ */
+ srcFsPathPtr->normPathPtr = pathObjPtr;
+ }
+ if (relative) {
+ /* This was returned by Tcl_FSJoinToPath above */
+ Tcl_DecrRefCount(absolutePath);
+
+ /* Get a quick, temporary lock on the cwd while we copy it */
+ Tcl_MutexLock(&cwdMutex);
+ srcFsPathPtr->cwdPtr = cwdPathPtr;
+ Tcl_IncrRefCount(srcFsPathPtr->cwdPtr);
+ Tcl_MutexUnlock(&cwdMutex);
+ }
+ }
+ return srcFsPathPtr->normPathPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSGetInternalRep --
+ *
+ * Extract the internal representation of a given path object,
+ * in the given filesystem. If the path object belongs to a
+ * different filesystem, we return NULL.
+ *
+ * If the internal representation is currently NULL, we attempt
+ * to generate it, by calling the filesystem's
+ * 'Tcl_FSCreateInternalRepProc'.
+ *
+ * Results:
+ * NULL or a valid internal representation.
+ *
+ * Side effects:
+ * An attempt may be made to convert the object.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+ClientData
+Tcl_FSGetInternalRep(pathObjPtr, fsPtr)
+ Tcl_Obj* pathObjPtr;
+ Tcl_Filesystem *fsPtr;
+{
+ register FsPath* srcFsPathPtr;
+
+ if (Tcl_FSConvertToPathType(NULL, pathObjPtr) != TCL_OK) {
+ return NULL;
+ }
+ srcFsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
+
+ /*
+ * We will only return the native representation for the caller's
+ * filesystem. Otherwise we will simply return NULL. This means
+ * that there must be a unique bi-directional mapping between paths
+ * and filesystems, and that this mapping will not allow 'remapped'
+ * files -- files which are in one filesystem but mapped into
+ * another. Another way of putting this is that 'stacked'
+ * filesystems are not allowed. We recognise that this is a
+ * potentially useful feature for the future.
+ *
+ * Even something simple like a 'pass through' filesystem which
+ * logs all activity and passes the calls onto the native system
+ * would be nice, but not easily achievable with the current
+ * implementation.
+ */
+ if (srcFsPathPtr->fsRecPtr == NULL) {
+ /*
+ * This only usually happens in wrappers like TclpStat which
+ * create a string object and pass it to TclpObjStat. Code
+ * which calls the Tcl_FS.. functions should always have a
+ * filesystem already set. Whether this code path is legal or
+ * not depends on whether we decide to allow external code to
+ * call the native filesystem directly. It is at least safer
+ * to allow this sub-optimal routing.
+ */
+ Tcl_FSGetFileSystemForPath(pathObjPtr);
+
+ /*
+ * If we fail through here, then the path is probably not a
+ * valid path in the filesystsem, and is most likely to be a
+ * use of the empty path "" via a direct call to one of the
+ * objectified interfaces (e.g. from the Tcl testsuite).
+ */
+ srcFsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
+ if (srcFsPathPtr->fsRecPtr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (fsPtr != srcFsPathPtr->fsRecPtr->fsPtr) {
+ /*
+ * There is still one possibility we should consider; if the
+ * file belongs to a different filesystem, perhaps it is
+ * actually linked through to a file in our own filesystem
+ * which we do care about. The way we can check for this
+ * is we ask what filesystem this path belongs to.
+ */
+ Tcl_Filesystem *actualFs = Tcl_FSGetFileSystemForPath(pathObjPtr);
+ if (actualFs == fsPtr) {
+ return Tcl_FSGetInternalRep(pathObjPtr, fsPtr);
+ }
+ return NULL;
+ }
+
+ if (srcFsPathPtr->nativePathPtr == NULL) {
+ Tcl_FSCreateInternalRepProc *proc;
+ proc = srcFsPathPtr->fsRecPtr->fsPtr->createInternalRepProc;
+
+ if (proc == NULL) {
+ return NULL;
+ }
+ srcFsPathPtr->nativePathPtr = (*proc)(pathObjPtr);
+ }
+ return srcFsPathPtr->nativePathPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSGetNativePath --
+ *
+ * This function is for use by the Win/Unix/MacOS native filesystems,
+ * so that they can easily retrieve the native (char* or TCHAR*)
+ * representation of a path. Other filesystems will probably
+ * want to implement similar functions. They basically act as a
+ * safety net around Tcl_FSGetInternalRep. Normally your file-
+ * system procedures will always be called with path objects
+ * already converted to the correct filesystem, but if for
+ * some reason they are called directly (i.e. by procedures
+ * not in this file), then one cannot necessarily guarantee that
+ * the path object pointer is from the correct filesystem.
+ *
+ * Note: in the future it might be desireable to have separate
+ * versions of this function with different signatures, for
+ * example Tcl_FSGetNativeMacPath, Tcl_FSGetNativeUnixPath etc.
+ * Right now, since native paths are all string based, we use just
+ * one function. On MacOS we could possibly use an FSSpec or
+ * FSRef as the native representation.
+ *
+ * Results:
+ * NULL or a valid native path.
+ *
+ * Side effects:
+ * See Tcl_FSGetInternalRep.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+CONST char *
+Tcl_FSGetNativePath(pathObjPtr)
+ Tcl_Obj *pathObjPtr;
+{
+ return (CONST char *)Tcl_FSGetInternalRep(pathObjPtr, &tclNativeFilesystem);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NativeCreateNativeRep --
+ *
+ * Create a native representation for the given path.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static ClientData
+NativeCreateNativeRep(pathObjPtr)
+ Tcl_Obj* pathObjPtr;
+{
+ char *nativePathPtr;
+ Tcl_DString ds;
+ Tcl_Obj* normPtr;
+ int len;
+ char *str;
+
+ /* Make sure the normalized path is set */
+ normPtr = Tcl_FSGetNormalizedPath(NULL, pathObjPtr);
+
+ str = Tcl_GetStringFromObj(normPtr,&len);
+#ifdef __WIN32__
+ Tcl_WinUtfToTChar(str, len, &ds);
+ if (tclWinProcs->useWide) {
+ nativePathPtr = ckalloc((unsigned)(sizeof(WCHAR)+Tcl_DStringLength(&ds)));
+ memcpy((VOID*)nativePathPtr, (VOID*)Tcl_DStringValue(&ds),
+ (size_t) (sizeof(WCHAR)+Tcl_DStringLength(&ds)));
+ } else {
+ nativePathPtr = ckalloc((unsigned)(sizeof(char)+Tcl_DStringLength(&ds)));
+ memcpy((VOID*)nativePathPtr, (VOID*)Tcl_DStringValue(&ds),
+ (size_t) (sizeof(char)+Tcl_DStringLength(&ds)));
+ }
+#else
+ Tcl_UtfToExternalDString(NULL, str, len, &ds);
+ nativePathPtr = ckalloc((unsigned)(sizeof(char)+Tcl_DStringLength(&ds)));
+ memcpy((VOID*)nativePathPtr, (VOID*)Tcl_DStringValue(&ds),
+ (size_t) (sizeof(char)+Tcl_DStringLength(&ds)));
+#endif
+
+ Tcl_DStringFree(&ds);
+ return (ClientData)nativePathPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclpNativeToNormalized --
+ *
+ * Convert native format to a normalized path object, with refCount
+ * of zero.
+ *
+ * Results:
+ * A valid normalized path.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tcl_Obj*
+TclpNativeToNormalized(clientData)
+ ClientData clientData;
+{
+ Tcl_DString ds;
+ Tcl_Obj *objPtr;
+ CONST char *copy;
+ int len;
+
+#ifdef __WIN32__
+ Tcl_WinTCharToUtf((CONST char*)clientData, -1, &ds);
+#else
+ Tcl_ExternalToUtfDString(NULL, (CONST char*)clientData, -1, &ds);
+#endif
+
+ copy = Tcl_DStringValue(&ds);
+ len = Tcl_DStringLength(&ds);
+
+#ifdef __WIN32__
+ /*
+ * Certain native path representations on Windows have this special
+ * prefix to indicate that they are to be treated specially. For
+ * example extremely long paths, or symlinks
+ */
+ if (*copy == '\\') {
+ if (0 == strncmp(copy,"\\??\\",4)) {
+ copy += 4;
+ len -= 4;
+ } else if (0 == strncmp(copy,"\\\\?\\",4)) {
+ copy += 4;
+ len -= 4;
+ }
+ }
+#endif
+
+ objPtr = Tcl_NewStringObj(copy,len);
+ Tcl_DStringFree(&ds);
+
+ return objPtr;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NativeDupInternalRep --
+ *
+ * Duplicate the native representation.
+ *
+ * Results:
+ * The copied native representation, or NULL if it is not possible
+ * to copy the representation.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static ClientData
+NativeDupInternalRep(clientData)
+ ClientData clientData;
+{
+ ClientData copy;
+ size_t len;
+
+ if (clientData == NULL) {
+ return NULL;
+ }
+
+#ifdef __WIN32__
+ if (tclWinProcs->useWide) {
+ /* unicode representation when running on NT/2K/XP */
+ len = sizeof(WCHAR) + (wcslen((CONST WCHAR*)clientData) * sizeof(WCHAR));
+ } else {
+ /* ansi representation when running on 95/98/ME */
+ len = sizeof(char) + (strlen((CONST char*)clientData) * sizeof(char));
+ }
+#else
+ /* ansi representation when running on Unix/MacOS */
+ len = sizeof(char) + (strlen((CONST char*)clientData) * sizeof(char));
+#endif
+
+ copy = (ClientData) ckalloc(len);
+ memcpy((VOID*)copy, (VOID*)clientData, len);
+ return copy;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NativePathInFilesystem --
+ *
+ * Any path object is acceptable to the native filesystem, by
+ * default (we will throw errors when illegal paths are actually
+ * tried to be used).
+ *
+ * However, this behavior means the native filesystem must be
+ * the last filesystem in the lookup list (otherwise it will
+ * claim all files belong to it, and other filesystems will
+ * never get a look in).
+ *
+ * Results:
+ * TCL_OK, to indicate 'yes', -1 to indicate no.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+NativePathInFilesystem(pathPtr, clientDataPtr)
+ Tcl_Obj *pathPtr;
+ ClientData *clientDataPtr;
+{
+ int len;
+ Tcl_GetStringFromObj(pathPtr,&len);
+ if (len == 0) {
+ return -1;
+ } else {
+ /* We accept any path as valid */
+ return TCL_OK;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NativeFreeInternalRep --
+ *
+ * Free a native internal representation, which will be non-NULL.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is released.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+NativeFreeInternalRep(clientData)
+ ClientData clientData;
+{
+ ckfree((char*)clientData);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSFileSystemInfo --
+ *
+ * This function returns a list of two elements. The first
+ * element is the name of the filesystem (e.g. "native" or "vfs"),
+ * and the second is the particular type of the given path within
+ * that filesystem.
+ *
+ * Results:
+ * A list of two elements.
+ *
+ * Side effects:
+ * The object may be converted to a path type.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tcl_Obj*
+Tcl_FSFileSystemInfo(pathObjPtr)
+ Tcl_Obj* pathObjPtr;
+{
+ Tcl_Obj *resPtr;
+ Tcl_FSFilesystemPathTypeProc *proc;
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathObjPtr);
+
+ if (fsPtr == NULL) {
+ return NULL;
+ }
+
+ resPtr = Tcl_NewListObj(0,NULL);
+
+ Tcl_ListObjAppendElement(NULL, resPtr,
+ Tcl_NewStringObj(fsPtr->typeName,-1));
+
+ proc = fsPtr->filesystemPathTypeProc;
+ if (proc != NULL) {
+ Tcl_Obj *typePtr = (*proc)(pathObjPtr);
+ if (typePtr != NULL) {
+ Tcl_ListObjAppendElement(NULL, resPtr, typePtr);
+ }
+ }
+
+ return resPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSPathSeparator --
+ *
+ * This function returns the separator to be used for a given
+ * path. The object returned should have a refCount of zero
+ *
+ * Results:
+ * A Tcl object, with a refCount of zero. If the caller
+ * needs to retain a reference to the object, it should
+ * call Tcl_IncrRefCount.
+ *
+ * Side effects:
+ * The path object may be converted to a path type.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tcl_Obj*
+Tcl_FSPathSeparator(pathObjPtr)
+ Tcl_Obj* pathObjPtr;
+{
+ Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathObjPtr);
+
+ if (fsPtr == NULL) {
+ return NULL;
+ }
+ if (fsPtr->filesystemSeparatorProc != NULL) {
+ return (*fsPtr->filesystemSeparatorProc)(pathObjPtr);
+ }
+
+ return NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NativeFilesystemSeparator --
+ *
+ * This function is part of the native filesystem support, and
+ * returns the separator for the given path.
+ *
+ * Results:
+ * String object containing the separator character.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Tcl_Obj*
+NativeFilesystemSeparator(pathObjPtr)
+ Tcl_Obj* pathObjPtr;
+{
+ char *separator = NULL; /* lint */
+ switch (tclPlatform) {
+ case TCL_PLATFORM_UNIX:
+ separator = "/";
+ break;
+ case TCL_PLATFORM_WINDOWS:
+ separator = "\\";
+ break;
+ case TCL_PLATFORM_MAC:
+ separator = ":";
+ break;
+ }
+ return Tcl_NewStringObj(separator,1);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSGetFileSystemForPath --
+ *
+ * This function determines which filesystem to use for a
+ * particular path object, and returns the filesystem which
+ * accepts this file. If no filesystem will accept this object
+ * as a valid file path, then NULL is returned.
+ *
+ * Results:
+.* NULL or a filesystem which will accept this path.
+ *
+ * Side effects:
+ * The object may be converted to a path type.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Filesystem*
+Tcl_FSGetFileSystemForPath(pathObjPtr)
+ Tcl_Obj* pathObjPtr;
+{
+ FilesystemRecord *fsRecPtr;
+ Tcl_Filesystem* retVal = NULL;
+ FsPath* srcFsPathPtr;
+
+ /*
+ * If the object has a refCount of zero, we reject it. This
+ * is to avoid possible segfaults or nondeterministic memory
+ * leaks (i.e. the user doesn't know if they should decrement
+ * the ref count on return or not).
+ */
+
+ if (pathObjPtr->refCount == 0) {
+ return NULL;
+ }
+
+ /*
+ * This will ensure the pathObjPtr can be converted into a
+ * "path" type, and that we are able to generate a complete
+ * normalized path which is used to determine the filesystem
+ * match.
+ */
+
+ if (Tcl_FSGetNormalizedPath(NULL, pathObjPtr) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Get a lock on theFilesystemEpoch and the filesystemList
+ *
+ * While we don't need the fsRecPtr until the while loop below, we
+ * do want to make sure the theFilesystemEpoch doesn't change
+ * between the 'if' and 'while' blocks, getting this iterator will
+ * ensure that everything is consistent
+ */
+ fsRecPtr = FsGetIterator();
+
+ /* Make sure pathObjPtr is of the correct epoch */
+
+ srcFsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
+
+ /*
+ * Check if the filesystem has changed in some way since
+ * this object's internal representation was calculated.
+ */
+ if (srcFsPathPtr->filesystemEpoch != theFilesystemEpoch) {
+ /*
+ * We have to discard the stale representation and
+ * recalculate it
+ */
+ FreeFsPathInternalRep(pathObjPtr);
+ pathObjPtr->typePtr = NULL;
+ if (SetFsPathFromAny(NULL, pathObjPtr) != TCL_OK) {
+ goto done;
+ }
+ srcFsPathPtr = (FsPath*) pathObjPtr->internalRep.otherValuePtr;
+ }
+
+ /* Check whether the object is already assigned to a fs */
+ if (srcFsPathPtr->fsRecPtr != NULL) {
+ retVal = srcFsPathPtr->fsRecPtr->fsPtr;
+ goto done;
+ }
+
+ /*
+ * Call each of the "pathInFilesystem" functions in succession. A
+ * non-return value of -1 indicates the particular function has
+ * succeeded.
+ */
+
+ while ((retVal == NULL) && (fsRecPtr != NULL)) {
+ Tcl_FSPathInFilesystemProc *proc = fsRecPtr->fsPtr->pathInFilesystemProc;
+ if (proc != NULL) {
+ ClientData clientData = NULL;
+ int ret = (*proc)(pathObjPtr, &clientData);
+ if (ret != -1) {
+ /*
+ * We assume the srcFsPathPtr hasn't been changed
+ * by the above call to the pathInFilesystemProc.
+ */
+ srcFsPathPtr->fsRecPtr = fsRecPtr;
+ srcFsPathPtr->nativePathPtr = clientData;
+ srcFsPathPtr->filesystemEpoch = theFilesystemEpoch;
+ fsRecPtr->fileRefCount++;
+ retVal = fsRecPtr->fsPtr;
+ }
+ }
+ fsRecPtr = fsRecPtr->nextPtr;
+ }
+
+ done:
+ FsReleaseIterator();
+ return retVal;
+}
+
+/* Simple helper function */
+static FilesystemRecord*
+GetFilesystemRecord(fromFilesystem, epoch)
+ Tcl_Filesystem *fromFilesystem;
+ int *epoch;
+{
+ FilesystemRecord *fsRecPtr = FsGetIterator();
+ while (fsRecPtr != NULL) {
+ if (fsRecPtr->fsPtr == fromFilesystem) {
+ *epoch = theFilesystemEpoch;
+ break;
+ }
+ fsRecPtr = fsRecPtr->nextPtr;
+ }
+ FsReleaseIterator();
+ return fsRecPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tcl_FSEqualPaths --
+ *
+ * This function tests whether the two paths given are equal path
+ * objects. If either or both is NULL, 0 is always returned.
+ *
+ * Results:
+ * 1 or 0.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Tcl_FSEqualPaths(firstPtr, secondPtr)
+ Tcl_Obj* firstPtr;
+ Tcl_Obj* secondPtr;
+{
+ if (firstPtr == secondPtr) {
+ return 1;
+ } else {
+ int tempErrno;
+
+ if (firstPtr == NULL || secondPtr == NULL) {
+ return 0;
+ }
+ if (!(strcmp(Tcl_GetString(firstPtr), Tcl_GetString(secondPtr)))) {
+ return 1;
+ }
+ /*
+ * Try the most thorough, correct method of comparing fully
+ * normalized paths
+ */
+
+ tempErrno = Tcl_GetErrno();
+ firstPtr = Tcl_FSGetNormalizedPath(NULL, firstPtr);
+ secondPtr = Tcl_FSGetNormalizedPath(NULL, secondPtr);
+ Tcl_SetErrno(tempErrno);
+
+ if (firstPtr == NULL || secondPtr == NULL) {
+ return 0;
+ }
+ if (!(strcmp(Tcl_GetString(firstPtr), Tcl_GetString(secondPtr)))) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * utime wants a normalized, NOT native path. I assume a native
+ * version of 'utime' doesn't exist (at least under that name) on NT/2000.
+ * If a native function does exist somewhere, then we could use:
+ *
+ * return native_utime(Tcl_FSGetNativePath(pathPtr),tval);
+ *
+ * This seems rather strange when compared with stat, lstat, access, etc.
+ * all of which want a native path.
+ */
+static int
+NativeUtime(pathPtr, tval)
+ Tcl_Obj *pathPtr;
+ struct utimbuf *tval;
+{
+#ifdef MAC_TCL
+ long gmt_offset=TclpGetGMTOffset();
+ struct utimbuf local_tval;
+ local_tval.actime=tval->actime+gmt_offset;
+ local_tval.modtime=tval->modtime+gmt_offset;
+ return utime(Tcl_GetString(Tcl_FSGetNormalizedPath(NULL,pathPtr)),
+ &local_tval);
+#else
+ return utime(Tcl_GetString(Tcl_FSGetNormalizedPath(NULL,pathPtr)),tval);
+#endif
+}
+
+/* Everything from here on is contained in this obsolete ifdef */
+#ifdef USE_OBSOLETE_FS_HOOKS
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclStatInsertProc --
*
* Insert the passed procedure pointer at the head of the list of
* functions which are used during a call to 'TclStat(...)'. The
- * passed function should be have exactly like 'TclStat' when called
- * during that time (see 'TclStat(...)' for more informatin).
+ * passed function should behave exactly like 'TclStat' when called
+ * during that time (see 'TclStat(...)' for more information).
* The function will be added even if it already in the list.
*
* Results:
@@ -578,7 +5020,7 @@ Tcl_OpenFileChannel(interp, fileName, modeString, permissions)
* could not be allocated.
*
* Side effects:
- * Memory allocataed and modifies the link list for 'TclStat'
+ * Memory allocated and modifies the link list for 'TclStat'
* functions.
*
*----------------------------------------------------------------------
@@ -597,10 +5039,10 @@ TclStatInsertProc (proc)
if (newStatProcPtr != NULL) {
newStatProcPtr->proc = proc;
- Tcl_MutexLock(&hookMutex);
+ Tcl_MutexLock(&obsoleteFsHookMutex);
newStatProcPtr->nextPtr = statProcList;
statProcList = newStatProcPtr;
- Tcl_MutexUnlock(&hookMutex);
+ Tcl_MutexUnlock(&obsoleteFsHookMutex);
retVal = TCL_OK;
}
@@ -636,7 +5078,7 @@ TclStatDeleteProc (proc)
StatProc *tmpStatProcPtr;
StatProc *prevStatProcPtr = NULL;
- Tcl_MutexLock(&hookMutex);
+ Tcl_MutexLock(&obsoleteFsHookMutex);
tmpStatProcPtr = statProcList;
/*
* Traverse the 'statProcList' looking for the particular node
@@ -644,7 +5086,7 @@ TclStatDeleteProc (proc)
* the list. Ensure that the "default" node cannot be removed.
*/
- while ((retVal == TCL_ERROR) && (tmpStatProcPtr != &defaultStatProc)) {
+ while ((retVal == TCL_ERROR) && (tmpStatProcPtr != NULL)) {
if (tmpStatProcPtr->proc == proc) {
if (prevStatProcPtr == NULL) {
statProcList = tmpStatProcPtr->nextPtr;
@@ -652,7 +5094,7 @@ TclStatDeleteProc (proc)
prevStatProcPtr->nextPtr = tmpStatProcPtr->nextPtr;
}
- Tcl_Free((char *)tmpStatProcPtr);
+ ckfree((char *)tmpStatProcPtr);
retVal = TCL_OK;
} else {
@@ -661,7 +5103,7 @@ TclStatDeleteProc (proc)
}
}
- Tcl_MutexUnlock(&hookMutex);
+ Tcl_MutexUnlock(&obsoleteFsHookMutex);
return (retVal);
}
@@ -671,17 +5113,18 @@ TclStatDeleteProc (proc)
* TclAccessInsertProc --
*
* Insert the passed procedure pointer at the head of the list of
- * functions which are used during a call to 'TclAccess(...)'. The
- * passed function should be have exactly like 'TclAccess' when
- * called during that time (see 'TclAccess(...)' for more informatin).
- * The function will be added even if it already in the list.
+ * functions which are used during a call to 'TclAccess(...)'.
+ * The passed function should behave exactly like 'TclAccess' when
+ * called during that time (see 'TclAccess(...)' for more
+ * information). The function will be added even if it already in
+ * the list.
*
* Results:
* Normally TCL_OK; TCL_ERROR if memory for a new node in the list
* could not be allocated.
*
* Side effects:
- * Memory allocataed and modifies the link list for 'TclAccess'
+ * Memory allocated and modifies the link list for 'TclAccess'
* functions.
*
*----------------------------------------------------------------------
@@ -700,10 +5143,10 @@ TclAccessInsertProc(proc)
if (newAccessProcPtr != NULL) {
newAccessProcPtr->proc = proc;
- Tcl_MutexLock(&hookMutex);
+ Tcl_MutexLock(&obsoleteFsHookMutex);
newAccessProcPtr->nextPtr = accessProcList;
accessProcList = newAccessProcPtr;
- Tcl_MutexUnlock(&hookMutex);
+ Tcl_MutexUnlock(&obsoleteFsHookMutex);
retVal = TCL_OK;
}
@@ -745,9 +5188,9 @@ TclAccessDeleteProc(proc)
* the list. Ensure that the "default" node cannot be removed.
*/
- Tcl_MutexLock(&hookMutex);
+ Tcl_MutexLock(&obsoleteFsHookMutex);
tmpAccessProcPtr = accessProcList;
- while ((retVal == TCL_ERROR) && (tmpAccessProcPtr != &defaultAccessProc)) {
+ while ((retVal == TCL_ERROR) && (tmpAccessProcPtr != NULL)) {
if (tmpAccessProcPtr->proc == proc) {
if (prevAccessProcPtr == NULL) {
accessProcList = tmpAccessProcPtr->nextPtr;
@@ -755,7 +5198,7 @@ TclAccessDeleteProc(proc)
prevAccessProcPtr->nextPtr = tmpAccessProcPtr->nextPtr;
}
- Tcl_Free((char *)tmpAccessProcPtr);
+ ckfree((char *)tmpAccessProcPtr);
retVal = TCL_OK;
} else {
@@ -763,7 +5206,7 @@ TclAccessDeleteProc(proc)
tmpAccessProcPtr = tmpAccessProcPtr->nextPtr;
}
}
- Tcl_MutexUnlock(&hookMutex);
+ Tcl_MutexUnlock(&obsoleteFsHookMutex);
return (retVal);
}
@@ -775,9 +5218,9 @@ TclAccessDeleteProc(proc)
*
* Insert the passed procedure pointer at the head of the list of
* functions which are used during a call to
- * 'Tcl_OpenFileChannel(...)'. The passed function should be have
+ * 'Tcl_OpenFileChannel(...)'. The passed function should behave
* exactly like 'Tcl_OpenFileChannel' when called during that time
- * (see 'Tcl_OpenFileChannel(...)' for more informatin). The
+ * (see 'Tcl_OpenFileChannel(...)' for more information). The
* function will be added even if it already in the list.
*
* Results:
@@ -785,7 +5228,7 @@ TclAccessDeleteProc(proc)
* could not be allocated.
*
* Side effects:
- * Memory allocataed and modifies the link list for
+ * Memory allocated and modifies the link list for
* 'Tcl_OpenFileChannel' functions.
*
*----------------------------------------------------------------------
@@ -805,10 +5248,10 @@ TclOpenFileChannelInsertProc(proc)
if (newOpenFileChannelProcPtr != NULL) {
newOpenFileChannelProcPtr->proc = proc;
- Tcl_MutexLock(&hookMutex);
+ Tcl_MutexLock(&obsoleteFsHookMutex);
newOpenFileChannelProcPtr->nextPtr = openFileChannelProcList;
openFileChannelProcList = newOpenFileChannelProcPtr;
- Tcl_MutexUnlock(&hookMutex);
+ Tcl_MutexUnlock(&obsoleteFsHookMutex);
retVal = TCL_OK;
}
@@ -824,7 +5267,7 @@ TclOpenFileChannelInsertProc(proc)
*
* Removed the passed function pointer from the list of
* 'Tcl_OpenFileChannel' functions. Ensures that the built-in
- * open file channel function is not removvable.
+ * open file channel function is not removable.
*
* Results:
* TCL_OK if the procedure pointer was successfully removed,
@@ -847,13 +5290,13 @@ TclOpenFileChannelDeleteProc(proc)
/*
* Traverse the 'openFileChannelProcList' looking for the particular
* node whose 'proc' member matches 'proc' and remove that one from
- * the list. Ensure that the "default" node cannot be removed.
+ * the list.
*/
- Tcl_MutexLock(&hookMutex);
+ Tcl_MutexLock(&obsoleteFsHookMutex);
tmpOpenFileChannelProcPtr = openFileChannelProcList;
while ((retVal == TCL_ERROR) &&
- (tmpOpenFileChannelProcPtr != &defaultOpenFileChannelProc)) {
+ (tmpOpenFileChannelProcPtr != NULL)) {
if (tmpOpenFileChannelProcPtr->proc == proc) {
if (prevOpenFileChannelProcPtr == NULL) {
openFileChannelProcList = tmpOpenFileChannelProcPtr->nextPtr;
@@ -862,7 +5305,7 @@ TclOpenFileChannelDeleteProc(proc)
tmpOpenFileChannelProcPtr->nextPtr;
}
- Tcl_Free((char *)tmpOpenFileChannelProcPtr);
+ ckfree((char *)tmpOpenFileChannelProcPtr);
retVal = TCL_OK;
} else {
@@ -870,7 +5313,8 @@ TclOpenFileChannelDeleteProc(proc)
tmpOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr->nextPtr;
}
}
- Tcl_MutexUnlock(&hookMutex);
+ Tcl_MutexUnlock(&obsoleteFsHookMutex);
return (retVal);
}
+#endif /* USE_OBSOLETE_FS_HOOKS */
diff --git a/tcl/generic/tclIndexObj.c b/tcl/generic/tclIndexObj.c
index 3187de62c0a..b8ebd014bb9 100644
--- a/tcl/generic/tclIndexObj.c
+++ b/tcl/generic/tclIndexObj.c
@@ -14,6 +14,7 @@
*/
#include "tclInt.h"
+#include "tclPort.h"
/*
* Prototypes for procedures defined later in this file:
@@ -21,6 +22,10 @@
static int SetIndexFromAny _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Obj *objPtr));
+static void UpdateStringOfIndex _ANSI_ARGS_((Tcl_Obj *objPtr));
+static void DupIndex _ANSI_ARGS_((Tcl_Obj *srcPtr,
+ Tcl_Obj *dupPtr));
+static void FreeIndex _ANSI_ARGS_((Tcl_Obj *objPtr));
/*
* The structure below defines the index Tcl object type by means of
@@ -29,18 +34,36 @@ static int SetIndexFromAny _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_ObjType tclIndexType = {
"index", /* name */
- (Tcl_FreeInternalRepProc *) NULL, /* freeIntRepProc */
- (Tcl_DupInternalRepProc *) NULL, /* dupIntRepProc */
- (Tcl_UpdateStringProc *) NULL, /* updateStringProc */
+ FreeIndex, /* freeIntRepProc */
+ DupIndex, /* dupIntRepProc */
+ UpdateStringOfIndex, /* updateStringProc */
SetIndexFromAny /* setFromAnyProc */
};
/*
- * Boolean flag indicating whether or not the tclIndexType object
- * type has been registered with the Tcl compiler.
+ * The definition of the internal representation of the "index"
+ * object; The internalRep.otherValuePtr field of an object of "index"
+ * type will be a pointer to one of these structures.
+ *
+ * Keep this structure declaration in sync with tclTestObj.c
+ */
+
+typedef struct {
+ VOID *tablePtr; /* Pointer to the table of strings */
+ int offset; /* Offset between table entries */
+ int index; /* Selected index into table. */
+} IndexRep;
+
+/*
+ * The following macros greatly simplify moving through a table...
*/
+#define STRING_AT(table, offset, index) \
+ (*((CONST char * CONST *)(((char *)(table)) + ((offset) * (index)))))
+#define NEXT_ENTRY(table, offset) \
+ (&(STRING_AT(table, offset, 1)))
+#define EXPAND_OF(indexRep) \
+ STRING_AT((indexRep)->tablePtr, (indexRep)->offset, (indexRep)->index)
-static int indexTypeInitialized = 0;
/*
*----------------------------------------------------------------------
@@ -73,10 +96,10 @@ int
Tcl_GetIndexFromObj(interp, objPtr, tablePtr, msg, flags, indexPtr)
Tcl_Interp *interp; /* Used for error reporting if not NULL. */
Tcl_Obj *objPtr; /* Object containing the string to lookup. */
- char **tablePtr; /* Array of strings to compare against the
+ CONST char **tablePtr; /* Array of strings to compare against the
* value of objPtr; last entry must be NULL
* and there must not be duplicate entries. */
- char *msg; /* Identifying word to use in error messages. */
+ CONST char *msg; /* Identifying word to use in error messages. */
int flags; /* 0 or TCL_EXACT */
int *indexPtr; /* Place to store resulting integer index. */
{
@@ -88,10 +111,17 @@ Tcl_GetIndexFromObj(interp, objPtr, tablePtr, msg, flags, indexPtr)
* is cached).
*/
- if ((objPtr->typePtr == &tclIndexType)
- && (objPtr->internalRep.twoPtrValue.ptr1 == (VOID *) tablePtr)) {
- *indexPtr = (int) objPtr->internalRep.twoPtrValue.ptr2;
- return TCL_OK;
+ if (objPtr->typePtr == &tclIndexType) {
+ IndexRep *indexRep = (IndexRep *) objPtr->internalRep.otherValuePtr;
+ /*
+ * Here's hoping we don't get hit by unfortunate packing
+ * constraints on odd platforms like a Cray PVP...
+ */
+ if (indexRep->tablePtr == (VOID *)tablePtr &&
+ indexRep->offset == sizeof(char *)) {
+ *indexPtr = indexRep->index;
+ return TCL_OK;
+ }
}
return Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, sizeof(char *),
msg, flags, indexPtr);
@@ -131,28 +161,33 @@ Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags,
indexPtr)
Tcl_Interp *interp; /* Used for error reporting if not NULL. */
Tcl_Obj *objPtr; /* Object containing the string to lookup. */
- char **tablePtr; /* The first string in the table. The second
+ CONST VOID *tablePtr; /* The first string in the table. The second
* string will be at this address plus the
* offset, the third plus the offset again,
* etc. The last entry must be NULL
* and there must not be duplicate entries. */
int offset; /* The number of bytes between entries */
- char *msg; /* Identifying word to use in error messages. */
+ CONST char *msg; /* Identifying word to use in error messages. */
int flags; /* 0 or TCL_EXACT */
int *indexPtr; /* Place to store resulting integer index. */
{
int index, length, i, numAbbrev;
- char *key, *p1, *p2, **entryPtr;
+ char *key, *p1;
+ CONST char *p2;
+ CONST char * CONST *entryPtr;
Tcl_Obj *resultPtr;
+ IndexRep *indexRep;
/*
* See if there is a valid cached result from a previous lookup.
*/
- if ((objPtr->typePtr == &tclIndexType)
- && (objPtr->internalRep.twoPtrValue.ptr1 == (VOID *) tablePtr)) {
- *indexPtr = (int) objPtr->internalRep.twoPtrValue.ptr2;
- return TCL_OK;
+ if (objPtr->typePtr == &tclIndexType) {
+ indexRep = (IndexRep *) objPtr->internalRep.otherValuePtr;
+ if (indexRep->tablePtr==tablePtr && indexRep->offset==offset) {
+ *indexPtr = indexRep->index;
+ return TCL_OK;
+ }
}
/*
@@ -160,16 +195,6 @@ Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags,
* abbreviations unless TCL_EXACT is set in flags.
*/
- if (!indexTypeInitialized) {
- /*
- * This is the first time we've done a lookup. Register the
- * tclIndexType.
- */
-
- Tcl_RegisterObjType(&tclIndexType);
- indexTypeInitialized = 1;
- }
-
key = Tcl_GetStringFromObj(objPtr, &length);
index = -1;
numAbbrev = 0;
@@ -182,15 +207,21 @@ Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags,
goto error;
}
+ /*
+ * Scan the table looking for one of:
+ * - An exact match (always preferred)
+ * - A single abbreviation (allowed depending on flags)
+ * - Several abbreviations (never allowed, but overridden by exact match)
+ */
for (entryPtr = tablePtr, i = 0; *entryPtr != NULL;
- entryPtr = (char **) ((long) entryPtr + offset), i++) {
+ entryPtr = NEXT_ENTRY(entryPtr, offset), i++) {
for (p1 = key, p2 = *entryPtr; *p1 == *p2; p1++, p2++) {
- if (*p1 == 0) {
+ if (*p1 == '\0') {
index = i;
goto done;
}
}
- if (*p1 == 0) {
+ if (*p1 == '\0') {
/*
* The value is an abbreviation for this entry. Continue
* checking other entries to make sure it's unique. If we
@@ -203,36 +234,51 @@ Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags,
index = i;
}
}
+ /*
+ * Check if we were instructed to disallow abbreviations.
+ */
if ((flags & TCL_EXACT) || (numAbbrev != 1)) {
goto error;
}
done:
- if ((objPtr->typePtr != NULL)
- && (objPtr->typePtr->freeIntRepProc != NULL)) {
- objPtr->typePtr->freeIntRepProc(objPtr);
- }
- objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) tablePtr;
/*
- * Make sure to account for offsets != sizeof(char *). [Bug 5153]
+ * Cache the found representation. Note that we want to avoid
+ * allocating a new internal-rep if at all possible since that is
+ * potentially a slow operation.
*/
- objPtr->internalRep.twoPtrValue.ptr2 =
- (VOID *) (index * (offset / sizeof(char *)));
- objPtr->typePtr = &tclIndexType;
+ if (objPtr->typePtr == &tclIndexType) {
+ indexRep = (IndexRep *) objPtr->internalRep.otherValuePtr;
+ } else {
+ if ((objPtr->typePtr != NULL)
+ && (objPtr->typePtr->freeIntRepProc != NULL)) {
+ objPtr->typePtr->freeIntRepProc(objPtr);
+ }
+ indexRep = (IndexRep *) ckalloc(sizeof(IndexRep));
+ objPtr->internalRep.otherValuePtr = (VOID *) indexRep;
+ objPtr->typePtr = &tclIndexType;
+ }
+ indexRep->tablePtr = (VOID*) tablePtr;
+ indexRep->offset = offset;
+ indexRep->index = index;
+
*indexPtr = index;
return TCL_OK;
error:
if (interp != NULL) {
+ /*
+ * Produce a fancy error message.
+ */
int count;
resultPtr = Tcl_GetObjResult(interp);
Tcl_AppendStringsToObj(resultPtr,
(numAbbrev > 1) ? "ambiguous " : "bad ", msg, " \"",
- key, "\": must be ", *tablePtr, (char *) NULL);
- for (entryPtr = (char **) ((long) tablePtr + offset), count = 0;
+ key, "\": must be ", STRING_AT(tablePtr,offset,0), (char*)NULL);
+ for (entryPtr = NEXT_ENTRY(tablePtr, offset), count = 0;
*entryPtr != NULL;
- entryPtr = (char **) ((long) entryPtr + offset), count++) {
- if ((*((char **) ((long) entryPtr + offset))) == NULL) {
+ entryPtr = NEXT_ENTRY(entryPtr, offset), count++) {
+ if (*NEXT_ENTRY(entryPtr, offset) == NULL) {
Tcl_AppendStringsToObj(resultPtr,
(count > 0) ? ", or " : " or ", *entryPtr,
(char *) NULL);
@@ -279,6 +325,94 @@ SetIndexFromAny(interp, objPtr)
/*
*----------------------------------------------------------------------
*
+ * UpdateStringOfIndex --
+ *
+ * This procedure is called to convert a Tcl object from index
+ * internal form to its string form. No abbreviation is ever
+ * generated.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The string representation of the object is updated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateStringOfIndex(objPtr)
+ Tcl_Obj *objPtr;
+{
+ IndexRep *indexRep = (IndexRep *) objPtr->internalRep.otherValuePtr;
+ register char *buf;
+ register unsigned len;
+ register CONST char *indexStr = EXPAND_OF(indexRep);
+
+ len = strlen(indexStr);
+ buf = (char *) ckalloc(len + 1);
+ memcpy(buf, indexStr, len+1);
+ objPtr->bytes = buf;
+ objPtr->length = len;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DupIndex --
+ *
+ * This procedure is called to copy the internal rep of an index
+ * Tcl object from to another object.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The internal representation of the target object is updated
+ * and the type is set.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DupIndex(srcPtr, dupPtr)
+ Tcl_Obj *srcPtr, *dupPtr;
+{
+ IndexRep *srcIndexRep = (IndexRep *) srcPtr->internalRep.otherValuePtr;
+ IndexRep *dupIndexRep = (IndexRep *) ckalloc(sizeof(IndexRep));
+
+ memcpy(dupIndexRep, srcIndexRep, sizeof(IndexRep));
+ dupPtr->internalRep.otherValuePtr = (VOID *) dupIndexRep;
+ dupPtr->typePtr = &tclIndexType;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeIndex --
+ *
+ * This procedure is called to delete the internal rep of an index
+ * Tcl object.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The internal representation of the target object is deleted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeIndex(objPtr)
+ Tcl_Obj *objPtr;
+{
+ ckfree((char *) objPtr->internalRep.otherValuePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_WrongNumArgs --
*
* This procedure generates a "wrong # args" error message in an
@@ -308,13 +442,13 @@ Tcl_WrongNumArgs(interp, objc, objv, message)
Tcl_Obj *CONST objv[]; /* Initial argument objects, which
* should be included in the error
* message. */
- char *message; /* Error message to print after the
+ CONST char *message; /* Error message to print after the
* leading objects in objv. The
* message may be NULL. */
{
Tcl_Obj *objPtr;
- char **tablePtr;
int i;
+ register IndexRep *indexRep;
objPtr = Tcl_GetObjResult(interp);
Tcl_AppendToObj(objPtr, "wrong # args: should be \"", -1);
@@ -326,21 +460,24 @@ Tcl_WrongNumArgs(interp, objc, objv, message)
*/
if (objv[i]->typePtr == &tclIndexType) {
- tablePtr = ((char **) objv[i]->internalRep.twoPtrValue.ptr1);
- Tcl_AppendStringsToObj(objPtr,
- tablePtr[(int) objv[i]->internalRep.twoPtrValue.ptr2],
- (char *) NULL);
+ indexRep = (IndexRep *) objv[i]->internalRep.otherValuePtr;
+ Tcl_AppendStringsToObj(objPtr, EXPAND_OF(indexRep), (char *) NULL);
} else {
Tcl_AppendStringsToObj(objPtr, Tcl_GetString(objv[i]),
(char *) NULL);
}
- if (i < (objc - 1)) {
+
+ /*
+ * Append a space character (" ") if there is more text to follow
+ * (either another element from objv, or the message string).
+ */
+ if ((i < (objc - 1)) || message) {
Tcl_AppendStringsToObj(objPtr, " ", (char *) NULL);
}
}
+
if (message) {
- Tcl_AppendStringsToObj(objPtr, " ", message, (char *) NULL);
+ Tcl_AppendStringsToObj(objPtr, message, (char *) NULL);
}
Tcl_AppendStringsToObj(objPtr, "\"", (char *) NULL);
}
-
diff --git a/tcl/generic/tclInitScript.h b/tcl/generic/tclInitScript.h
index 749492361b9..dcb94c6e583 100644
--- a/tcl/generic/tclInitScript.h
+++ b/tcl/generic/tclInitScript.h
@@ -49,8 +49,10 @@ static char initScript[] = "if {[info proc tclInit]==\"\"} {\n\
if {[info exists env(TCL_LIBRARY)]} {\n\
lappend dirs $env(TCL_LIBRARY)\n\
}\n\
- lappend dirs $tclDefaultLibrary\n\
- unset tclDefaultLibrary\n\
+ catch {\n\
+ lappend dirs $tclDefaultLibrary\n\
+ unset tclDefaultLibrary\n\
+ }\n\
set dirs [concat $dirs $tcl_libPath]\n\
}\n\
foreach i $dirs {\n\
@@ -62,7 +64,6 @@ static char initScript[] = "if {[info proc tclInit]==\"\"} {\n\
} else {\n\
append errors \"$tclfile: $msg\n$errorInfo\n\"\n\
}\n\
- set tcl_pkgPath [lreplace $tcl_pkgPath end end]\n\
}\n\
}\n\
set msg \"Can't find a usable init.tcl in the following directories: \n\"\n\
diff --git a/tcl/generic/tclInt.decls b/tcl/generic/tclInt.decls
index 71903e72fe9..e5eb2074d76 100644
--- a/tcl/generic/tclInt.decls
+++ b/tcl/generic/tclInt.decls
@@ -7,6 +7,8 @@
# files
#
# Copyright (c) 1998-1999 by Scriptics Corporation.
+# Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
+#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
@@ -23,9 +25,10 @@ interface tclInt
# Use at your own risk. Note that the position of functions should not
# be changed between versions to avoid gratuitous incompatibilities.
-declare 0 generic {
- int TclAccess(CONST char *path, int mode)
-}
+# Replaced by Tcl_FSAccess in 8.4:
+#declare 0 generic {
+# int TclAccess(CONST char *path, int mode)
+#}
declare 1 generic {
int TclAccessDeleteProc(TclAccessProc_ *proc)
}
@@ -40,7 +43,7 @@ declare 3 generic {
# int TclChdir(Tcl_Interp *interp, char *dirName)
# }
declare 5 {unix win} {
- int TclCleanupChildren(Tcl_Interp *interp, int numPids, Tcl_Pid *pidPtr, \
+ int TclCleanupChildren(Tcl_Interp *interp, int numPids, Tcl_Pid *pidPtr,
Tcl_Channel errorChan)
}
declare 6 generic {
@@ -50,19 +53,20 @@ declare 7 generic {
int TclCopyAndCollapse(int count, CONST char *src, char *dst)
}
declare 8 generic {
- int TclCopyChannel(Tcl_Interp *interp, Tcl_Channel inChan, \
+ int TclCopyChannel(Tcl_Interp *interp, Tcl_Channel inChan,
Tcl_Channel outChan, int toRead, Tcl_Obj *cmdPtr)
}
# TclCreatePipeline unofficially exported for use by BLT.
declare 9 {unix win} {
- int TclCreatePipeline(Tcl_Interp *interp, int argc, char **argv, \
- Tcl_Pid **pidArrayPtr, TclFile *inPipePtr, TclFile *outPipePtr, \
+ int TclCreatePipeline(Tcl_Interp *interp, int argc, CONST char **argv,
+ Tcl_Pid **pidArrayPtr, TclFile *inPipePtr, TclFile *outPipePtr,
TclFile *errFilePtr)
}
declare 10 generic {
- int TclCreateProc(Tcl_Interp *interp, Namespace *nsPtr, char *procName, \
+ int TclCreateProc(Tcl_Interp *interp, Namespace *nsPtr,
+ CONST char *procName,
Tcl_Obj *argsPtr, Tcl_Obj *bodyPtr, Proc **procPtrPtr)
}
declare 11 generic {
@@ -72,8 +76,8 @@ declare 12 generic {
void TclDeleteVars(Interp *iPtr, Tcl_HashTable *tablePtr)
}
declare 13 generic {
- int TclDoGlob(Tcl_Interp *interp, char *separators, \
- Tcl_DString *headPtr, char *tail, GlobTypeData *types)
+ int TclDoGlob(Tcl_Interp *interp, char *separators,
+ Tcl_DString *headPtr, char *tail, Tcl_GlobTypeData *types)
}
declare 14 generic {
void TclDumpMemoryInfo(FILE *outFile)
@@ -85,28 +89,29 @@ declare 14 generic {
declare 16 generic {
void TclExprFloatError(Tcl_Interp *interp, double value)
}
-declare 17 generic {
- int TclFileAttrsCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
-}
-declare 18 generic {
- int TclFileCopyCmd(Tcl_Interp *interp, int argc, char **argv)
-}
-declare 19 generic {
- int TclFileDeleteCmd(Tcl_Interp *interp, int argc, char **argv)
-}
-declare 20 generic {
- int TclFileMakeDirsCmd(Tcl_Interp *interp, int argc, char **argv)
-}
-declare 21 generic {
- int TclFileRenameCmd(Tcl_Interp *interp, int argc, char **argv)
-}
+# Removed in 8.4
+#declare 17 generic {
+# int TclFileAttrsCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+#}
+#declare 18 generic {
+# int TclFileCopyCmd(Tcl_Interp *interp, int argc, char **argv)
+#}
+#declare 19 generic {
+# int TclFileDeleteCmd(Tcl_Interp *interp, int argc, char **argv)
+#}
+#declare 20 generic {
+# int TclFileMakeDirsCmd(Tcl_Interp *interp, int argc, char **argv)
+#}
+#declare 21 generic {
+# int TclFileRenameCmd(Tcl_Interp *interp, int argc, char **argv)
+#}
declare 22 generic {
- int TclFindElement(Tcl_Interp *interp, CONST char *listStr, \
- int listLength, CONST char **elementPtr, CONST char **nextPtr, \
+ int TclFindElement(Tcl_Interp *interp, CONST char *listStr,
+ int listLength, CONST char **elementPtr, CONST char **nextPtr,
int *sizePtr, int *bracePtr)
}
declare 23 generic {
- Proc * TclFindProc(Interp *iPtr, char *procName)
+ Proc * TclFindProc(Interp *iPtr, CONST char *procName)
}
declare 24 generic {
int TclFormatInt(char *buffer, long n)
@@ -119,16 +124,17 @@ declare 25 generic {
# char * TclGetCwd(Tcl_Interp *interp)
# }
declare 27 generic {
- int TclGetDate(char *p, unsigned long now, long zone, \
+ int TclGetDate(char *p, unsigned long now, long zone,
unsigned long *timePtr)
}
declare 28 generic {
Tcl_Channel TclpGetDefaultStdChannel(int type)
}
-declare 29 generic {
- Tcl_Obj * TclGetElementOfIndexedArray(Tcl_Interp *interp, \
- int localIndex, Tcl_Obj *elemPtr, int leaveErrorMsg)
-}
+# Removed in 8.4b2:
+#declare 29 generic {
+# Tcl_Obj * TclGetElementOfIndexedArray(Tcl_Interp *interp,
+# int localIndex, Tcl_Obj *elemPtr, int flags)
+#}
# Replaced by char * TclGetEnv(CONST char *name, Tcl_DString *valuePtr) in 8.1:
# declare 30 generic {
# char * TclGetEnv(CONST char *name)
@@ -137,36 +143,38 @@ declare 31 generic {
char * TclGetExtension(char *name)
}
declare 32 generic {
- int TclGetFrame(Tcl_Interp *interp, char *str, CallFrame **framePtrPtr)
+ int TclGetFrame(Tcl_Interp *interp, CONST char *str,
+ CallFrame **framePtrPtr)
}
declare 33 generic {
TclCmdProcType TclGetInterpProc(void)
}
declare 34 generic {
- int TclGetIntForIndex(Tcl_Interp *interp, Tcl_Obj *objPtr, \
+ int TclGetIntForIndex(Tcl_Interp *interp, Tcl_Obj *objPtr,
int endValue, int *indexPtr)
}
-declare 35 generic {
- Tcl_Obj * TclGetIndexedScalar(Tcl_Interp *interp, int localIndex, \
- int leaveErrorMsg)
-}
+# Removed in 8.4b2:
+#declare 35 generic {
+# Tcl_Obj * TclGetIndexedScalar(Tcl_Interp *interp, int localIndex,
+# int flags)
+#}
declare 36 generic {
- int TclGetLong(Tcl_Interp *interp, char *str, long *longPtr)
+ int TclGetLong(Tcl_Interp *interp, CONST char *str, long *longPtr)
}
declare 37 generic {
int TclGetLoadedPackages(Tcl_Interp *interp, char *targetName)
}
declare 38 generic {
- int TclGetNamespaceForQualName(Tcl_Interp *interp, char *qualName, \
- Namespace *cxtNsPtr, int flags, Namespace **nsPtrPtr, \
- Namespace **altNsPtrPtr, Namespace **actualCxtPtrPtr, \
- char **simpleNamePtr)
+ int TclGetNamespaceForQualName(Tcl_Interp *interp, CONST char *qualName,
+ Namespace *cxtNsPtr, int flags, Namespace **nsPtrPtr,
+ Namespace **altNsPtrPtr, Namespace **actualCxtPtrPtr,
+ CONST char **simpleNamePtr)
}
declare 39 generic {
TclObjCmdProcType TclGetObjInterpProc(void)
}
declare 40 generic {
- int TclGetOpenMode(Tcl_Interp *interp, char *str, int *seekFlagPtr)
+ int TclGetOpenMode(Tcl_Interp *interp, CONST char *str, int *seekFlagPtr)
}
declare 41 generic {
Tcl_Command TclGetOriginalCommand(Tcl_Command command)
@@ -175,10 +183,10 @@ declare 42 generic {
char * TclpGetUserHome(CONST char *name, Tcl_DString *bufferPtr)
}
declare 43 generic {
- int TclGlobalInvoke(Tcl_Interp *interp, int argc, char **argv, int flags)
+ int TclGlobalInvoke(Tcl_Interp *interp, int argc, CONST84 char **argv, int flags)
}
declare 44 generic {
- int TclGuessPackageName(char *fileName, Tcl_DString *bufPtr)
+ int TclGuessPackageName(CONST char *fileName, Tcl_DString *bufPtr)
}
declare 45 generic {
int TclHideUnsafeCommands(Tcl_Interp *interp)
@@ -186,34 +194,36 @@ declare 45 generic {
declare 46 generic {
int TclInExit(void)
}
-declare 47 generic {
- Tcl_Obj * TclIncrElementOfIndexedArray(Tcl_Interp *interp, \
- int localIndex, Tcl_Obj *elemPtr, long incrAmount)
-}
-declare 48 generic {
- Tcl_Obj * TclIncrIndexedScalar(Tcl_Interp *interp, int localIndex, \
- long incrAmount)
-}
+# Removed in 8.4b2:
+#declare 47 generic {
+# Tcl_Obj * TclIncrElementOfIndexedArray(Tcl_Interp *interp,
+# int localIndex, Tcl_Obj *elemPtr, long incrAmount)
+#}
+# Removed in 8.4b2:
+#declare 48 generic {
+# Tcl_Obj * TclIncrIndexedScalar(Tcl_Interp *interp, int localIndex,
+# long incrAmount)
+#}
declare 49 generic {
- Tcl_Obj * TclIncrVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, \
+ Tcl_Obj * TclIncrVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr,
Tcl_Obj *part2Ptr, long incrAmount, int part1NotParsed)
}
declare 50 generic {
- void TclInitCompiledLocals(Tcl_Interp *interp, CallFrame *framePtr, \
+ void TclInitCompiledLocals(Tcl_Interp *interp, CallFrame *framePtr,
Namespace *nsPtr)
}
declare 51 generic {
int TclInterpInit(Tcl_Interp *interp)
}
declare 52 generic {
- int TclInvoke(Tcl_Interp *interp, int argc, char **argv, int flags)
+ int TclInvoke(Tcl_Interp *interp, int argc, CONST84 char **argv, int flags)
}
declare 53 generic {
- int TclInvokeObjectCommand(ClientData clientData, Tcl_Interp *interp, \
- int argc, char **argv)
+ int TclInvokeObjectCommand(ClientData clientData, Tcl_Interp *interp,
+ int argc, CONST84 char **argv)
}
declare 54 generic {
- int TclInvokeStringCommand(ClientData clientData, Tcl_Interp *interp, \
+ int TclInvokeStringCommand(ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
}
declare 55 generic {
@@ -221,8 +231,8 @@ declare 55 generic {
}
# Replaced with TclpLoadFile in 8.1:
# declare 56 generic {
-# int TclLoadFile(Tcl_Interp *interp, char *fileName, char *sym1, \
-# char *sym2, Tcl_PackageInitProc **proc1Ptr, \
+# int TclLoadFile(Tcl_Interp *interp, char *fileName, char *sym1,
+# char *sym2, Tcl_PackageInitProc **proc1Ptr,
# Tcl_PackageInitProc **proc2Ptr)
# }
# Signature changed to take a length in 8.1:
@@ -230,16 +240,17 @@ declare 55 generic {
# int TclLooksLikeInt(char *p)
# }
declare 58 generic {
- Var * TclLookupVar(Tcl_Interp *interp, char *part1, char *part2, \
- int flags, char *msg, int createPart1, int createPart2, \
+ Var * TclLookupVar(Tcl_Interp *interp, CONST char *part1, CONST char *part2,
+ int flags, CONST char *msg, int createPart1, int createPart2,
Var **arrayPtrPtr)
}
-declare 59 generic {
- int TclpMatchFiles(Tcl_Interp *interp, char *separators, \
- Tcl_DString *dirPtr, char *pattern, char *tail)
-}
+# Replaced by Tcl_FSMatchInDirectory in 8.4
+#declare 59 generic {
+# int TclpMatchFiles(Tcl_Interp *interp, char *separators,
+# Tcl_DString *dirPtr, char *pattern, char *tail)
+#}
declare 60 generic {
- int TclNeedSpace(char *start, char *end)
+ int TclNeedSpace(CONST char *start, CONST char *end)
}
declare 61 generic {
Tcl_Obj * TclNewProcBodyObj(Proc *procPtr)
@@ -248,15 +259,15 @@ declare 62 generic {
int TclObjCommandComplete(Tcl_Obj *cmdPtr)
}
declare 63 generic {
- int TclObjInterpProc(ClientData clientData, Tcl_Interp *interp, \
+ int TclObjInterpProc(ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
}
declare 64 generic {
- int TclObjInvoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], \
+ int TclObjInvoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[],
int flags)
}
declare 65 generic {
- int TclObjInvokeGlobal(Tcl_Interp *interp, int objc, \
+ int TclObjInvokeGlobal(Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[], int flags)
}
declare 66 generic {
@@ -265,25 +276,26 @@ declare 66 generic {
declare 67 generic {
int TclOpenFileChannelInsertProc(TclOpenFileChannelProc_ *proc)
}
-declare 68 generic {
- int TclpAccess(CONST char *path, int mode)
-}
+# Replaced by Tcl_FSAccess in 8.4:
+#declare 68 generic {
+# int TclpAccess(CONST char *path, int mode)
+#}
declare 69 generic {
char * TclpAlloc(unsigned int size)
}
-declare 70 generic {
- int TclpCopyFile(CONST char *source, CONST char *dest)
-}
-declare 71 generic {
- int TclpCopyDirectory(CONST char *source, CONST char *dest, \
- Tcl_DString *errorPtr)
-}
-declare 72 generic {
- int TclpCreateDirectory(CONST char *path)
-}
-declare 73 generic {
- int TclpDeleteFile(CONST char *path)
-}
+#declare 70 generic {
+# int TclpCopyFile(CONST char *source, CONST char *dest)
+#}
+#declare 71 generic {
+# int TclpCopyDirectory(CONST char *source, CONST char *dest,
+# Tcl_DString *errorPtr)
+#}
+#declare 72 generic {
+# int TclpCreateDirectory(CONST char *path)
+#}
+#declare 73 generic {
+# int TclpDeleteFile(CONST char *path)
+#}
declare 74 generic {
void TclpFree(char *ptr)
}
@@ -293,51 +305,56 @@ declare 75 generic {
declare 76 generic {
unsigned long TclpGetSeconds(void)
}
+
+# deprecated
declare 77 generic {
void TclpGetTime(Tcl_Time *time)
}
+
declare 78 generic {
int TclpGetTimeZone(unsigned long time)
}
-declare 79 generic {
- int TclpListVolumes(Tcl_Interp *interp)
-}
-declare 80 generic {
- Tcl_Channel TclpOpenFileChannel(Tcl_Interp *interp, char *fileName, \
- char *modeString, int permissions)
-}
+# Replaced by Tcl_FSListVolumes in 8.4:
+#declare 79 generic {
+# int TclpListVolumes(Tcl_Interp *interp)
+#}
+# Replaced by Tcl_FSOpenFileChannel in 8.4:
+#declare 80 generic {
+# Tcl_Channel TclpOpenFileChannel(Tcl_Interp *interp, char *fileName,
+# char *modeString, int permissions)
+#}
declare 81 generic {
char * TclpRealloc(char *ptr, unsigned int size)
}
-declare 82 generic {
- int TclpRemoveDirectory(CONST char *path, int recursive, \
- Tcl_DString *errorPtr)
-}
-declare 83 generic {
- int TclpRenameFile(CONST char *source, CONST char *dest)
-}
+#declare 82 generic {
+# int TclpRemoveDirectory(CONST char *path, int recursive,
+# Tcl_DString *errorPtr)
+#}
+#declare 83 generic {
+# int TclpRenameFile(CONST char *source, CONST char *dest)
+#}
# Removed in 8.1:
# declare 84 generic {
-# int TclParseBraces(Tcl_Interp *interp, char *str, char **termPtr, \
+# int TclParseBraces(Tcl_Interp *interp, char *str, char **termPtr,
# ParseValue *pvPtr)
# }
# declare 85 generic {
-# int TclParseNestedCmd(Tcl_Interp *interp, char *str, int flags, \
+# int TclParseNestedCmd(Tcl_Interp *interp, char *str, int flags,
# char **termPtr, ParseValue *pvPtr)
# }
# declare 86 generic {
-# int TclParseQuotes(Tcl_Interp *interp, char *str, int termChar, \
+# int TclParseQuotes(Tcl_Interp *interp, char *str, int termChar,
# int flags, char **termPtr, ParseValue *pvPtr)
# }
# declare 87 generic {
# void TclPlatformInit(Tcl_Interp *interp)
# }
declare 88 generic {
- char * TclPrecTraceProc(ClientData clientData, Tcl_Interp *interp, \
- char *name1, char *name2, int flags)
+ char * TclPrecTraceProc(ClientData clientData, Tcl_Interp *interp,
+ CONST char *name1, CONST char *name2, int flags)
}
declare 89 generic {
- int TclPreventAliasLoop(Tcl_Interp *interp, Tcl_Interp *cmdInterp, \
+ int TclPreventAliasLoop(Tcl_Interp *interp, Tcl_Interp *cmdInterp,
Tcl_Command cmd)
}
# Removed in 8.1 (only available if compiled with TCL_COMPILE_DEBUG):
@@ -348,20 +365,21 @@ declare 91 generic {
void TclProcCleanupProc(Proc *procPtr)
}
declare 92 generic {
- int TclProcCompileProc(Tcl_Interp *interp, Proc *procPtr, \
- Tcl_Obj *bodyPtr, Namespace *nsPtr, CONST char *description, \
+ int TclProcCompileProc(Tcl_Interp *interp, Proc *procPtr,
+ Tcl_Obj *bodyPtr, Namespace *nsPtr, CONST char *description,
CONST char *procName)
}
declare 93 generic {
void TclProcDeleteProc(ClientData clientData)
}
declare 94 generic {
- int TclProcInterpProc(ClientData clientData, Tcl_Interp *interp, \
- int argc, char **argv)
-}
-declare 95 generic {
- int TclpStat(CONST char *path, struct stat *buf)
+ int TclProcInterpProc(ClientData clientData, Tcl_Interp *interp,
+ int argc, CONST84 char **argv)
}
+# Replaced by Tcl_FSStat in 8.4:
+#declare 95 generic {
+# int TclpStat(CONST char *path, Tcl_StatBuf *buf)
+#}
declare 96 generic {
int TclRenameCommand(Tcl_Interp *interp, char *oldName, char *newName)
}
@@ -371,14 +389,16 @@ declare 97 generic {
declare 98 generic {
int TclServiceIdle(void)
}
-declare 99 generic {
- Tcl_Obj * TclSetElementOfIndexedArray(Tcl_Interp *interp, \
- int localIndex, Tcl_Obj *elemPtr, Tcl_Obj *objPtr, int leaveErrorMsg)
-}
-declare 100 generic {
- Tcl_Obj * TclSetIndexedScalar(Tcl_Interp *interp, int localIndex, \
- Tcl_Obj *objPtr, int leaveErrorMsg)
-}
+# Removed in 8.4b2:
+#declare 99 generic {
+# Tcl_Obj * TclSetElementOfIndexedArray(Tcl_Interp *interp, int localIndex,
+# Tcl_Obj *elemPtr, Tcl_Obj *objPtr, int flags)
+#}
+# Removed in 8.4b2:
+#declare 100 generic {
+# Tcl_Obj * TclSetIndexedScalar(Tcl_Interp *interp, int localIndex,
+# Tcl_Obj *objPtr, int flags)
+#}
declare 101 {unix win} {
char * TclSetPreInitScript(char *string)
}
@@ -386,15 +406,16 @@ declare 102 generic {
void TclSetupEnv(Tcl_Interp *interp)
}
declare 103 generic {
- int TclSockGetPort(Tcl_Interp *interp, char *str, char *proto, \
+ int TclSockGetPort(Tcl_Interp *interp, char *str, char *proto,
int *portPtr)
}
declare 104 {unix win} {
int TclSockMinimumBuffers(int sock, int size)
}
-declare 105 generic {
- int TclStat(CONST char *path, struct stat *buf)
-}
+# Replaced by Tcl_FSStat in 8.4:
+#declare 105 generic {
+# int TclStat(CONST char *path, Tcl_StatBuf *buf)
+#}
declare 106 generic {
int TclStatDeleteProc(TclStatProc_ *proc)
}
@@ -416,54 +437,54 @@ declare 109 generic {
# defined here instead of in tcl.decls since they are not stable yet.
declare 111 generic {
- void Tcl_AddInterpResolvers(Tcl_Interp *interp, char *name, \
- Tcl_ResolveCmdProc *cmdProc, Tcl_ResolveVarProc *varProc, \
+ void Tcl_AddInterpResolvers(Tcl_Interp *interp, CONST char *name,
+ Tcl_ResolveCmdProc *cmdProc, Tcl_ResolveVarProc *varProc,
Tcl_ResolveCompiledVarProc *compiledVarProc)
}
declare 112 generic {
- int Tcl_AppendExportList(Tcl_Interp *interp, Tcl_Namespace *nsPtr, \
+ int Tcl_AppendExportList(Tcl_Interp *interp, Tcl_Namespace *nsPtr,
Tcl_Obj *objPtr)
}
declare 113 generic {
- Tcl_Namespace * Tcl_CreateNamespace(Tcl_Interp *interp, char *name, \
+ Tcl_Namespace * Tcl_CreateNamespace(Tcl_Interp *interp, CONST char *name,
ClientData clientData, Tcl_NamespaceDeleteProc *deleteProc)
}
declare 114 generic {
void Tcl_DeleteNamespace(Tcl_Namespace *nsPtr)
}
declare 115 generic {
- int Tcl_Export(Tcl_Interp *interp, Tcl_Namespace *nsPtr, char *pattern, \
- int resetListFirst)
+ int Tcl_Export(Tcl_Interp *interp, Tcl_Namespace *nsPtr,
+ CONST char *pattern, int resetListFirst)
}
declare 116 generic {
- Tcl_Command Tcl_FindCommand(Tcl_Interp *interp, char *name, \
+ Tcl_Command Tcl_FindCommand(Tcl_Interp *interp, CONST char *name,
Tcl_Namespace *contextNsPtr, int flags)
}
declare 117 generic {
- Tcl_Namespace * Tcl_FindNamespace(Tcl_Interp *interp, char *name, \
+ Tcl_Namespace * Tcl_FindNamespace(Tcl_Interp *interp, CONST char *name,
Tcl_Namespace *contextNsPtr, int flags)
}
declare 118 generic {
- int Tcl_GetInterpResolvers(Tcl_Interp *interp, char *name, \
+ int Tcl_GetInterpResolvers(Tcl_Interp *interp, CONST char *name,
Tcl_ResolverInfo *resInfo)
}
declare 119 generic {
- int Tcl_GetNamespaceResolvers(Tcl_Namespace *namespacePtr, \
+ int Tcl_GetNamespaceResolvers(Tcl_Namespace *namespacePtr,
Tcl_ResolverInfo *resInfo)
}
declare 120 generic {
- Tcl_Var Tcl_FindNamespaceVar(Tcl_Interp *interp, char *name, \
+ Tcl_Var Tcl_FindNamespaceVar(Tcl_Interp *interp, CONST char *name,
Tcl_Namespace *contextNsPtr, int flags)
}
declare 121 generic {
- int Tcl_ForgetImport(Tcl_Interp *interp, Tcl_Namespace *nsPtr, \
- char *pattern)
+ int Tcl_ForgetImport(Tcl_Interp *interp, Tcl_Namespace *nsPtr,
+ CONST char *pattern)
}
declare 122 generic {
Tcl_Command Tcl_GetCommandFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr)
}
declare 123 generic {
- void Tcl_GetCommandFullName(Tcl_Interp *interp, Tcl_Command command, \
+ void Tcl_GetCommandFullName(Tcl_Interp *interp, Tcl_Command command,
Tcl_Obj *objPtr)
}
declare 124 generic {
@@ -473,26 +494,26 @@ declare 125 generic {
Tcl_Namespace * Tcl_GetGlobalNamespace(Tcl_Interp *interp)
}
declare 126 generic {
- void Tcl_GetVariableFullName(Tcl_Interp *interp, Tcl_Var variable, \
+ void Tcl_GetVariableFullName(Tcl_Interp *interp, Tcl_Var variable,
Tcl_Obj *objPtr)
}
declare 127 generic {
- int Tcl_Import(Tcl_Interp *interp, Tcl_Namespace *nsPtr, \
- char *pattern, int allowOverwrite)
+ int Tcl_Import(Tcl_Interp *interp, Tcl_Namespace *nsPtr,
+ CONST char *pattern, int allowOverwrite)
}
declare 128 generic {
void Tcl_PopCallFrame(Tcl_Interp* interp)
}
declare 129 generic {
- int Tcl_PushCallFrame(Tcl_Interp* interp, Tcl_CallFrame *framePtr, \
+ int Tcl_PushCallFrame(Tcl_Interp* interp, Tcl_CallFrame *framePtr,
Tcl_Namespace *nsPtr, int isProcCallFrame)
}
declare 130 generic {
- int Tcl_RemoveInterpResolvers(Tcl_Interp *interp, char *name)
+ int Tcl_RemoveInterpResolvers(Tcl_Interp *interp, CONST char *name)
}
declare 131 generic {
- void Tcl_SetNamespaceResolvers(Tcl_Namespace *namespacePtr, \
- Tcl_ResolveCmdProc *cmdProc, Tcl_ResolveVarProc *varProc, \
+ void Tcl_SetNamespaceResolvers(Tcl_Namespace *namespacePtr,
+ Tcl_ResolveCmdProc *cmdProc, Tcl_ResolveVarProc *varProc,
Tcl_ResolveCompiledVarProc *compiledVarProc)
}
declare 132 generic {
@@ -502,8 +523,8 @@ declare 133 generic {
struct tm * TclpGetDate(TclpTime_t time, int useGMT)
}
declare 134 generic {
- size_t TclpStrftime(char *s, size_t maxsize, CONST char *format, \
- CONST struct tm *t)
+ size_t TclpStrftime(char *s, size_t maxsize, CONST char *format,
+ CONST struct tm *t, int useGMT)
}
declare 135 generic {
int TclpCheckStackSpace(void)
@@ -511,33 +532,34 @@ declare 135 generic {
# Added in 8.1:
-declare 137 generic {
- int TclpChdir(CONST char *dirName)
-}
+#declare 137 generic {
+# int TclpChdir(CONST char *dirName)
+#}
declare 138 generic {
- char * TclGetEnv(CONST char *name, Tcl_DString *valuePtr)
-}
-declare 139 generic {
- int TclpLoadFile(Tcl_Interp *interp, char *fileName, char *sym1, \
- char *sym2, Tcl_PackageInitProc **proc1Ptr, \
- Tcl_PackageInitProc **proc2Ptr, ClientData *clientDataPtr)
+ CONST84_RETURN char * TclGetEnv(CONST char *name, Tcl_DString *valuePtr)
}
+#declare 139 generic {
+# int TclpLoadFile(Tcl_Interp *interp, char *fileName, char *sym1,
+# char *sym2, Tcl_PackageInitProc **proc1Ptr,
+# Tcl_PackageInitProc **proc2Ptr, ClientData *clientDataPtr)
+#}
declare 140 generic {
- int TclLooksLikeInt(char *bytes, int length)
+ int TclLooksLikeInt(CONST char *bytes, int length)
}
+# This is used by TclX, but should otherwise be considered private
declare 141 generic {
- char *TclpGetCwd(Tcl_Interp *interp, Tcl_DString *cwdPtr)
+ CONST84_RETURN char *TclpGetCwd(Tcl_Interp *interp, Tcl_DString *cwdPtr)
}
declare 142 generic {
- int TclSetByteCodeFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr, \
+ int TclSetByteCodeFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr,
CompileHookProc *hookProc, ClientData clientData)
}
declare 143 generic {
- int TclAddLiteralObj(struct CompileEnv *envPtr, Tcl_Obj *objPtr, \
+ int TclAddLiteralObj(struct CompileEnv *envPtr, Tcl_Obj *objPtr,
LiteralEntry **litPtrPtr)
}
declare 144 generic {
- void TclHideLiteral(Tcl_Interp *interp, struct CompileEnv *envPtr, \
+ void TclHideLiteral(Tcl_Interp *interp, struct CompileEnv *envPtr,
int index)
}
declare 145 generic {
@@ -566,7 +588,7 @@ declare 150 generic {
int TclRegAbout(Tcl_Interp *interp, Tcl_RegExp re)
}
declare 151 generic {
- void TclRegExpRangeUniChar(Tcl_RegExp re, int index, int *startPtr, \
+ void TclRegExpRangeUniChar(Tcl_RegExp re, int index, int *startPtr,
int *endPtr)
}
@@ -577,43 +599,93 @@ declare 153 generic {
Tcl_Obj *TclGetLibraryPath(void)
}
-# moved to tclTest.c in 8.3.2/8.4a2
+# moved to tclTest.c (static) in 8.3.2/8.4a2
#declare 154 generic {
# int TclTestChannelCmd(ClientData clientData,
# Tcl_Interp *interp, int argc, char **argv)
#}
#declare 155 generic {
-# int TclTestChannelEventCmd(ClientData clientData, \
+# int TclTestChannelEventCmd(ClientData clientData,
# Tcl_Interp *interp, int argc, char **argv)
#}
declare 156 generic {
- void TclRegError (Tcl_Interp *interp, char *msg, \
+ void TclRegError (Tcl_Interp *interp, CONST char *msg,
int status)
}
declare 157 generic {
- Var * TclVarTraceExists (Tcl_Interp *interp, char *varName)
+ Var * TclVarTraceExists (Tcl_Interp *interp, CONST char *varName)
}
declare 158 generic {
- void TclSetStartupScriptFileName(char *filename)
+ void TclSetStartupScriptFileName(CONST char *filename)
}
declare 159 generic {
- char *TclGetStartupScriptFileName(void)
-}
-declare 160 generic {
- int TclpMatchFilesTypes(Tcl_Interp *interp, char *separators, \
- Tcl_DString *dirPtr, char *pattern, char *tail, GlobTypeData *types)
+ CONST84_RETURN char *TclGetStartupScriptFileName(void)
}
+#declare 160 generic {
+# int TclpMatchFilesTypes(Tcl_Interp *interp, char *separators,
+# Tcl_DString *dirPtr, char *pattern, char *tail, GlobTypeData *types)
+#}
# new in 8.3.2/8.4a2
declare 161 generic {
- int TclChannelTransform(Tcl_Interp *interp, Tcl_Channel chan, \
+ int TclChannelTransform(Tcl_Interp *interp, Tcl_Channel chan,
Tcl_Obj *cmdObjPtr)
}
declare 162 generic {
void TclChannelEventScriptInvoker(ClientData clientData, int flags)
}
+# ALERT: The result of 'TclGetInstructionTable' is actually an
+# "InstructionDesc*" but we do not want to describe this structure in
+# "tclInt.h". It is described in "tclCompile.h". Use a cast to the
+# correct type when calling this procedure.
+
+declare 163 generic {
+ void * TclGetInstructionTable (void)
+}
+
+# ALERT: The argument of 'TclExpandCodeArray' is actually a
+# "CompileEnv*" but we do not want to describe this structure in
+# "tclInt.h". It is described in "tclCompile.h".
+
+declare 164 generic {
+ void TclExpandCodeArray (void *envPtr)
+}
+
+# These functions are vfs aware, but are generally only useful internally.
+declare 165 generic {
+ void TclpSetInitialEncodings(void)
+}
+
+# New function due to TIP #33
+declare 166 generic {
+ int TclListObjSetElement(Tcl_Interp *interp, Tcl_Obj *listPtr,
+ int index, Tcl_Obj *valuePtr)
+}
+
+# VFS-aware versions of Tcl*StartupScriptFileName (158 and 159 above)
+declare 167 generic {
+ void TclSetStartupScriptPath(Tcl_Obj *pathPtr)
+}
+declare 168 generic {
+ Tcl_Obj *TclGetStartupScriptPath(void)
+}
+# variant of Tcl_UtfNCmp that takes n as bytes, not chars
+declare 169 generic {
+ int TclpUtfNcmp2(CONST char *s1, CONST char *s2, unsigned long n)
+}
+declare 170 generic {
+ int TclCheckInterpTraces (Tcl_Interp *interp, CONST char *command, int numChars, \
+ Command *cmdPtr, int result, int traceFlags, int objc, \
+ Tcl_Obj *CONST objv[])
+}
+declare 171 generic {
+ int TclCheckExecutionTraces (Tcl_Interp *interp, CONST char *command, int numChars, \
+ Command *cmdPtr, int result, int traceFlags, int objc, \
+ Tcl_Obj *CONST objv[])
+}
+
##############################################################################
# Define the platform specific internal Tcl interface. These functions are
@@ -646,11 +718,11 @@ declare 5 mac {
int FSpSetDefaultDir(FSSpecPtr theSpec)
}
declare 6 mac {
- OSErr FSpFindFolder(short vRefNum, OSType folderType, \
+ OSErr FSpFindFolder(short vRefNum, OSType folderType,
Boolean createFolder, FSSpec *spec)
}
declare 7 mac {
- void GetGlobalMouse(Point *mouse)
+ void GetGlobalMouseTcl(Point *mouse)
}
# The following routines are utility functions in Tcl. They are exported
@@ -658,15 +730,15 @@ declare 7 mac {
# however. The first set are from the MoreFiles package.
declare 8 mac {
- pascal OSErr FSpGetDirectoryID(CONST FSSpec *spec, long *theDirID, \
+ pascal OSErr FSpGetDirectoryIDTcl(CONST FSSpec *spec, long *theDirID,
Boolean *isDirectory)
}
declare 9 mac {
- pascal short FSpOpenResFileCompat(CONST FSSpec *spec, \
+ pascal short FSpOpenResFileCompatTcl(CONST FSSpec *spec,
SignedByte permission)
}
declare 10 mac {
- pascal void FSpCreateResFileCompat(CONST FSSpec *spec, OSType creator, \
+ pascal void FSpCreateResFileCompatTcl(CONST FSSpec *spec, OSType creator,
OSType fileType, ScriptCode scriptTag)
}
@@ -677,7 +749,7 @@ declare 11 mac {
int FSpLocationFromPath(int length, CONST char *path, FSSpecPtr theSpec)
}
declare 12 mac {
- OSErr FSpPathFromLocation(FSSpecPtr theSpec, int *length, \
+ OSErr FSpPathFromLocation(FSSpecPtr theSpec, int *length,
Handle *fullPath)
}
@@ -705,7 +777,7 @@ declare 19 mac {
int TclMacTimerExpired(void *timerToken)
}
declare 20 mac {
- int TclMacRegisterResourceFork(short fileRef, Tcl_Obj *tokenPtr, \
+ int TclMacRegisterResourceFork(short fileRef, Tcl_Obj *tokenPtr,
int insert)
}
declare 21 mac {
@@ -721,8 +793,15 @@ declare 23 mac {
# declare 24 mac {
# int TclMacReadlink(char *path, char *buf, int size)
# }
+declare 24 mac {
+ char * TclpGetTZName(int isdst)
+}
declare 25 mac {
- int TclMacChmod(char *path, int mode)
+ int TclMacChmod(CONST char *path, int mode)
+}
+# version of FSpLocationFromPath that doesn't resolve the last path component
+declare 26 mac {
+ int FSpLLocationFromPath(int length, CONST char *path, FSSpecPtr theSpec)
}
############################
@@ -735,11 +814,11 @@ declare 1 win {
void TclWinConvertWSAError(DWORD errCode)
}
declare 2 win {
- struct servent * TclWinGetServByName(CONST char *nm, \
+ struct servent * TclWinGetServByName(CONST char *nm,
CONST char *proto)
}
declare 3 win {
- int TclWinGetSockOpt(SOCKET s, int level, int optname, \
+ int TclWinGetSockOpt(SOCKET s, int level, int optname,
char FAR * optval, int FAR *optlen)
}
declare 4 win {
@@ -753,7 +832,7 @@ declare 6 win {
u_short TclWinNToHS(u_short ns)
}
declare 7 win {
- int TclWinSetSockOpt(SOCKET s, int level, int optname, \
+ int TclWinSetSockOpt(SOCKET s, int level, int optname,
CONST char FAR * optval, int optlen)
}
declare 8 win {
@@ -776,15 +855,15 @@ declare 12 win {
int TclpCloseFile(TclFile file)
}
declare 13 win {
- Tcl_Channel TclpCreateCommandChannel(TclFile readFile, \
+ Tcl_Channel TclpCreateCommandChannel(TclFile readFile,
TclFile writeFile, TclFile errorFile, int numPids, Tcl_Pid *pidPtr)
}
declare 14 win {
int TclpCreatePipe(TclFile *readPipe, TclFile *writePipe)
}
declare 15 win {
- int TclpCreateProcess(Tcl_Interp *interp, int argc, char **argv, \
- TclFile inputFile, TclFile outputFile, TclFile errorFile, \
+ int TclpCreateProcess(Tcl_Interp *interp, int argc, CONST char **argv,
+ TclFile inputFile, TclFile outputFile, TclFile errorFile,
Tcl_Pid *pidPtr)
}
# Signature changed in 8.1:
@@ -803,9 +882,11 @@ declare 19 win {
declare 20 win {
void TclWinAddProcess(HANDLE hProcess, DWORD id)
}
-declare 21 win {
- void TclpAsyncMark(Tcl_AsyncHandler async)
-}
+
+# removed permanently for 8.4
+#declare 21 win {
+# void TclpAsyncMark(Tcl_AsyncHandler async)
+#}
# Added in 8.1:
declare 22 win {
@@ -824,6 +905,12 @@ declare 26 win {
void TclWinSetInterfaces(int wide)
}
+# Added in Tcl 8.3.3 / 8.4
+
+declare 27 win {
+ void TclWinFlushDirtyChannels (void)
+}
+
#########################
# Unix specific internals
@@ -836,21 +923,20 @@ declare 1 unix {
int TclpCloseFile(TclFile file)
}
declare 2 unix {
- Tcl_Channel TclpCreateCommandChannel(TclFile readFile, \
+ Tcl_Channel TclpCreateCommandChannel(TclFile readFile,
TclFile writeFile, TclFile errorFile, int numPids, Tcl_Pid *pidPtr)
}
declare 3 unix {
int TclpCreatePipe(TclFile *readPipe, TclFile *writePipe)
}
declare 4 unix {
- int TclpCreateProcess(Tcl_Interp *interp, int argc, char **argv, \
- TclFile inputFile, TclFile outputFile, TclFile errorFile, \
+ int TclpCreateProcess(Tcl_Interp *interp, int argc, CONST char **argv,
+ TclFile inputFile, TclFile outputFile, TclFile errorFile,
Tcl_Pid *pidPtr)
}
# Signature changed in 8.1:
# declare 5 unix {
-# TclFile TclpCreateTempFile(char *contents,
-# Tcl_DString *namePtr)
+# TclFile TclpCreateTempFile(char *contents, Tcl_DString *namePtr)
# }
declare 6 unix {
TclFile TclpMakeFile(Tcl_Channel channel, int direction)
@@ -868,3 +954,21 @@ declare 9 unix {
TclFile TclpCreateTempFile(CONST char *contents)
}
+# Added in 8.4:
+
+declare 10 unix {
+ Tcl_DirEntry * TclpReaddir(DIR * dir)
+}
+
+declare 11 unix {
+ struct tm * TclpLocaltime(time_t * clock)
+}
+
+declare 12 unix {
+ struct tm * TclpGmtime(time_t * clock)
+}
+
+declare 13 unix {
+ char * TclpInetNtoa(struct in_addr addr)
+}
+
diff --git a/tcl/generic/tclInt.h b/tcl/generic/tclInt.h
index 641e9d665a9..7de19273884 100644
--- a/tcl/generic/tclInt.h
+++ b/tcl/generic/tclInt.h
@@ -7,6 +7,7 @@
* Copyright (c) 1993-1997 Lucent Technologies.
* Copyright (c) 1994-1998 Sun Microsystems, Inc.
* Copyright (c) 1998-1999 by Scriptics Corporation.
+ * Copyright (c) 2001, 2002 by Kevin B. Kenny. All rights reserved.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -27,12 +28,12 @@
* needed by stdlib.h in some configurations.
*/
-#include <stdio.h>
-
#ifndef _TCL
#include "tcl.h"
#endif
+#include <stdio.h>
+
#include <ctype.h>
#ifdef NO_LIMITS_H
# include "../compat/limits.h"
@@ -90,15 +91,15 @@ typedef struct Tcl_ResolvedVarInfo {
typedef int (Tcl_ResolveCompiledVarProc) _ANSI_ARGS_((
- Tcl_Interp* interp, char* name, int length,
+ Tcl_Interp* interp, CONST84 char* name, int length,
Tcl_Namespace *context, Tcl_ResolvedVarInfo **rPtr));
typedef int (Tcl_ResolveVarProc) _ANSI_ARGS_((
- Tcl_Interp* interp, char* name, Tcl_Namespace *context,
+ Tcl_Interp* interp, CONST84 char* name, Tcl_Namespace *context,
int flags, Tcl_Var *rPtr));
typedef int (Tcl_ResolveCmdProc) _ANSI_ARGS_((Tcl_Interp* interp,
- char* name, Tcl_Namespace *context, int flags,
+ CONST84 char* name, Tcl_Namespace *context, int flags,
Tcl_Command *rPtr));
typedef struct Tcl_ResolverInfo {
@@ -184,11 +185,13 @@ typedef struct Namespace {
* namespace has already cached a Command *
* pointer; this causes all its cached
* Command* pointers to be invalidated. */
- int resolverEpoch; /* Incremented whenever the name resolution
- * rules change for this namespace; this
- * invalidates all byte codes compiled in
- * the namespace, causing the code to be
- * recompiled under the new rules. */
+ int resolverEpoch; /* Incremented whenever (a) the name resolution
+ * rules change for this namespace or (b) a
+ * newly added command shadows a command that
+ * is compiled to bytecodes.
+ * This invalidates all byte codes compiled
+ * in the namespace, causing the code to be
+ * recompiled under the new rules.*/
Tcl_ResolveCmdProc *cmdResProc;
/* If non-null, this procedure overrides
* the usual command resolution mechanism
@@ -270,6 +273,43 @@ typedef struct VarTrace {
} VarTrace;
/*
+ * The following structure defines a command trace, which is used to
+ * invoke a specific C procedure whenever certain operations are performed
+ * on a command.
+ */
+
+typedef struct CommandTrace {
+ Tcl_CommandTraceProc *traceProc;/* Procedure to call when operations given
+ * by flags are performed on command. */
+ ClientData clientData; /* Argument to pass to proc. */
+ int flags; /* What events the trace procedure is
+ * interested in: OR-ed combination of
+ * TCL_TRACE_RENAME, TCL_TRACE_DELETE. */
+ struct CommandTrace *nextPtr; /* Next in list of traces associated with
+ * a particular command. */
+} CommandTrace;
+
+/*
+ * When a command trace is active (i.e. its associated procedure is
+ * executing), one of the following structures is linked into a list
+ * associated with the command's interpreter. The information in
+ * the structure is needed in order for Tcl to behave reasonably
+ * if traces are deleted while traces are active.
+ */
+
+typedef struct ActiveCommandTrace {
+ struct Command *cmdPtr; /* Command that's being traced. */
+ struct ActiveCommandTrace *nextPtr;
+ /* Next in list of all active command
+ * traces for the interpreter, or NULL
+ * if no more. */
+ CommandTrace *nextTracePtr; /* Next trace to check after current
+ * trace procedure returns; if this
+ * trace gets deleted, must update pointer
+ * to avoid using free'd memory. */
+} ActiveCommandTrace;
+
+/*
* When a variable trace is active (i.e. its associated procedure is
* executing), one of the following structures is linked into a list
* associated with the variable's interpreter. The information in
@@ -614,12 +654,35 @@ typedef struct Proc {
typedef struct Trace {
int level; /* Only trace commands at nesting level
* less than or equal to this. */
- Tcl_CmdTraceProc *proc; /* Procedure to call to trace command. */
+ Tcl_CmdObjTraceProc *proc; /* Procedure to call to trace command. */
ClientData clientData; /* Arbitrary value to pass to proc. */
struct Trace *nextPtr; /* Next in list of traces for this interp. */
+ int flags; /* Flags governing the trace - see
+ * Tcl_CreateObjTrace for details */
+ Tcl_CmdObjTraceDeleteProc* delProc;
+ /* Procedure to call when trace is deleted */
} Trace;
/*
+ * When an interpreter trace is active (i.e. its associated procedure
+ * is executing), one of the following structures is linked into a list
+ * associated with the interpreter. The information in the structure
+ * is needed in order for Tcl to behave reasonably if traces are
+ * deleted while traces are active.
+ */
+
+typedef struct ActiveInterpTrace {
+ struct ActiveInterpTrace *nextPtr;
+ /* Next in list of all active command
+ * traces for the interpreter, or NULL
+ * if no more. */
+ Trace *nextTracePtr; /* Next trace to check after current
+ * trace procedure returns; if this
+ * trace gets deleted, must update pointer
+ * to avoid using free'd memory. */
+} ActiveInterpTrace;
+
+/*
* The structure below defines an entry in the assocData hash table which
* is associated with an interpreter. The entry contains a pointer to a
* function to call when the interpreter is deleted, and a pointer to
@@ -701,11 +764,6 @@ typedef struct CallFrame {
typedef VOID **TclHandle;
-EXTERN TclHandle TclHandleCreate _ANSI_ARGS_((VOID *ptr));
-EXTERN void TclHandleFree _ANSI_ARGS_((TclHandle handle));
-EXTERN TclHandle TclHandlePreserve _ANSI_ARGS_((TclHandle handle));
-EXTERN void TclHandleRelease _ANSI_ARGS_((TclHandle handle));
-
/*
*----------------------------------------------------------------
* Data structures related to history. These are used primarily
@@ -852,6 +910,8 @@ typedef struct ExecEnv {
int stackTop; /* Index of current top of stack; -1 when
* the stack is empty. */
int stackEnd; /* Index of last usable item in stack. */
+ Tcl_Obj *errorInfo;
+ Tcl_Obj *errorCode;
} ExecEnv;
/*
@@ -1020,10 +1080,8 @@ typedef struct Command {
/* Procedure invoked when deleting command
* to, e.g., free all client data. */
ClientData deleteData; /* Arbitrary value passed to deleteProc. */
- int deleted; /* Means that the command is in the process
- * of being deleted (its deleteProc is
- * currently executing). Other attempts to
- * delete the command should be ignored. */
+ int flags; /* Miscellaneous bits of information about
+ * command. See below for definitions. */
ImportRef *importRefPtr; /* List of each imported Command created in
* another namespace when this command is
* imported. These imported commands
@@ -1031,9 +1089,35 @@ typedef struct Command {
* command. The list is used to remove all
* those imported commands when deleting
* this "real" command. */
+ CommandTrace *tracePtr; /* First in list of all traces set for this
+ * command. */
} Command;
/*
+ * Flag bits for commands.
+ *
+ * CMD_IS_DELETED - Means that the command is in the process
+ * of being deleted (its deleteProc is
+ * currently executing). Other attempts to
+ * delete the command should be ignored.
+ * CMD_TRACE_ACTIVE - 1 means that trace processing is currently
+ * underway for a rename/delete change.
+ * See the two flags below for which is
+ * currently being processed.
+ * CMD_HAS_EXEC_TRACES - 1 means that this command has at least
+ * one execution trace (as opposed to simple
+ * delete/rename traces) in its tracePtr list.
+ * TCL_TRACE_RENAME - A rename trace is in progress. Further
+ * recursive renames will not be traced.
+ * TCL_TRACE_DELETE - A delete trace is in progress. Further
+ * recursive deletes will not be traced.
+ * (these last two flags are defined in tcl.h)
+ */
+#define CMD_IS_DELETED 0x1
+#define CMD_TRACE_ACTIVE 0x2
+#define CMD_HAS_EXEC_TRACES 0x4
+
+/*
*----------------------------------------------------------------
* Data structures related to name resolution procedures.
*----------------------------------------------------------------
@@ -1134,7 +1218,7 @@ typedef struct Interp {
/*
* Information related to procedures and variables. See tclProc.c
- * and tclvar.c for usage.
+ * and tclVar.c for usage.
*/
int numLevels; /* Keeps track of how many nested calls to
@@ -1153,7 +1237,7 @@ typedef struct Interp {
* unless an "uplevel" command is
* executing). NULL means no procedure is
* active or "uplevel 0" is executing. */
- ActiveVarTrace *activeTracePtr;
+ ActiveVarTrace *activeVarTracePtr;
/* First in list of active traces for
* interp, or NULL if no active traces. */
int returnCode; /* Completion code to return if current
@@ -1223,11 +1307,9 @@ typedef struct Interp {
* are added/removed by calling
* Tcl_AddInterpResolvers and
* Tcl_RemoveInterpResolver. */
- char *scriptFile; /* NULL means there is no nested source
+ Tcl_Obj *scriptFile; /* NULL means there is no nested source
* command active; otherwise this points to
- * the name of the file being sourced (it's
- * not malloc-ed: it points to an argument
- * to Tcl_EvalFile. */
+ * pathPtr of the file being sourced. */
int flags; /* Various flag bits. See below. */
long randSeed; /* Seed used for rand() function. */
Trace *tracePtr; /* List of traces for this interpreter. */
@@ -1248,6 +1330,16 @@ typedef struct Interp {
* accessed directly; see comment above. */
Tcl_ThreadId threadId; /* ID of thread that owns the interpreter */
+ ActiveCommandTrace *activeCmdTracePtr;
+ /* First in list of active command traces for
+ * interp, or NULL if no active traces. */
+ ActiveInterpTrace *activeInterpTracePtr;
+ /* First in list of active traces for
+ * interp, or NULL if no active traces. */
+
+ int tracesForbiddingInline; /* Count of traces (in the list headed by
+ * tracePtr) that forbid inline bytecode
+ * compilation */
/*
* Statistical information about the bytecode compiler and interpreter's
* operation.
@@ -1306,6 +1398,9 @@ typedef struct Interp {
* interpreter; instead, have Tcl_EvalObj call
* Tcl_EvalEx. Used primarily for testing the
* new parser.
+ * INTERP_TRACE_IN_PROGRESS: Non-zero means that an interp trace is currently
+ * active; so no further trace callbacks should be
+ * invoked.
*/
#define DELETED 1
@@ -1317,6 +1412,7 @@ typedef struct Interp {
#define RAND_SEED_INITIALIZED 0x40
#define SAFE_INTERP 0x80
#define USE_EVAL_DIRECT 0x100
+#define INTERP_TRACE_IN_PROGRESS 0x200
/*
*----------------------------------------------------------------
@@ -1379,7 +1475,7 @@ typedef struct ParseValue {
#define TCL_ALIGN(x) (((int)(x) + 7) & ~7)
/*
- * The following macros are used to specify the runtime platform
+ * The following enum values are used to specify the runtime platform
* setting of the tclPlatform variable.
*/
@@ -1390,6 +1486,19 @@ typedef enum {
} TclPlatformType;
/*
+ * The following enum values are used to indicate the translation
+ * of a Tcl channel. Declared here so that each platform can define
+ * TCL_PLATFORM_TRANSLATION to the native translation on that platform
+ */
+
+typedef enum TclEolTranslation {
+ TCL_TRANSLATE_AUTO, /* Eol == \r, \n and \r\n. */
+ TCL_TRANSLATE_CR, /* Eol == \r. */
+ TCL_TRANSLATE_LF, /* Eol == \n. */
+ TCL_TRANSLATE_CRLF /* Eol == \r\n. */
+} TclEolTranslation;
+
+/*
* Flags for TclInvoke:
*
* TCL_INVOKE_HIDDEN Invoke a hidden command; if not set,
@@ -1434,9 +1543,9 @@ typedef struct List {
*/
typedef int (TclGetFileAttrProc) _ANSI_ARGS_((Tcl_Interp *interp,
- int objIndex, CONST char *fileName, Tcl_Obj **attrObjPtrPtr));
+ int objIndex, Tcl_Obj *fileName, Tcl_Obj **attrObjPtrPtr));
typedef int (TclSetFileAttrProc) _ANSI_ARGS_((Tcl_Interp *interp,
- int objIndex, CONST char *fileName, Tcl_Obj *attrObjPtr));
+ int objIndex, Tcl_Obj *fileName, Tcl_Obj *attrObjPtr));
typedef struct TclFileAttrProcs {
TclGetFileAttrProc *getProc; /* The procedure for getting attrs. */
@@ -1451,63 +1560,42 @@ typedef struct TclFileAttrProcs {
typedef struct TclFile_ *TclFile;
/*
+ * Opaque names for platform specific types.
+ */
+
+typedef struct TclpTime_t_ *TclpTime_t;
+
+/*
+ * The "globParameters" argument of the function TclGlob is an
+ * or'ed combination of the following values:
+ */
+
+#define TCL_GLOBMODE_NO_COMPLAIN 1
+#define TCL_GLOBMODE_JOIN 2
+#define TCL_GLOBMODE_DIR 4
+#define TCL_GLOBMODE_TAILS 8
+
+/*
*----------------------------------------------------------------
- * Data structures related to hooking 'TclStat(...)' and
- * 'TclAccess(...)'.
+ * Data structures related to obsolete filesystem hooks
*----------------------------------------------------------------
*/
typedef int (TclStatProc_) _ANSI_ARGS_((CONST char *path, struct stat *buf));
typedef int (TclAccessProc_) _ANSI_ARGS_((CONST char *path, int mode));
typedef Tcl_Channel (TclOpenFileChannelProc_) _ANSI_ARGS_((Tcl_Interp *interp,
- char *fileName, char *modeString,
+ CONST char *fileName, CONST char *modeString,
int permissions));
-typedef int (*TclCmdProcType) _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char *argv[]));
-typedef int (*TclObjCmdProcType) _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int objc, struct Tcl_Obj * CONST objv[]));
/*
- * Opaque names for platform specific types.
- */
-
-typedef struct TclpTime_t_ *TclpTime_t;
-
-/*
- * The following structure is used to pass glob type data amongst
- * the various glob routines and TclpMatchFilesTypes. Currently
- * most of the fields are ignored. However they will be used in
- * a future release to implement glob's ability to find files
- * of particular types/permissions/etc only.
- */
-typedef struct GlobTypeData {
- /* Corresponds to bcdpfls as in 'find -t' */
- int type;
- /* Corresponds to file permissions */
- int perm;
- /* Acceptable mac type */
- Tcl_Obj* macType;
- /* Acceptable mac creator */
- Tcl_Obj* macCreator;
-} GlobTypeData;
-
-/*
- * type and permission definitions for glob command
+ *----------------------------------------------------------------
+ * Data structures related to procedures
+ *----------------------------------------------------------------
*/
-#define TCL_GLOB_TYPE_BLOCK (1<<0)
-#define TCL_GLOB_TYPE_CHAR (1<<1)
-#define TCL_GLOB_TYPE_DIR (1<<2)
-#define TCL_GLOB_TYPE_PIPE (1<<3)
-#define TCL_GLOB_TYPE_FILE (1<<4)
-#define TCL_GLOB_TYPE_LINK (1<<5)
-#define TCL_GLOB_TYPE_SOCK (1<<6)
-#define TCL_GLOB_PERM_RONLY (1<<0)
-#define TCL_GLOB_PERM_HIDDEN (1<<1)
-#define TCL_GLOB_PERM_R (1<<2)
-#define TCL_GLOB_PERM_W (1<<3)
-#define TCL_GLOB_PERM_X (1<<4)
+typedef Tcl_CmdProc *TclCmdProcType;
+typedef Tcl_ObjCmdProc *TclObjCmdProcType;
/*
*----------------------------------------------------------------
@@ -1523,8 +1611,6 @@ extern char * tclDefaultEncodingDir;
extern Tcl_ChannelType tclFileChannelType;
extern char * tclMemDumpFileName;
extern TclPlatformType tclPlatform;
-extern char * tclpFileAttrStrings[];
-extern CONST TclFileAttrProcs tclpFileAttrProcs[];
/*
* Variables denoting the Tcl object types defined in the core.
@@ -1534,10 +1620,26 @@ extern Tcl_ObjType tclBooleanType;
extern Tcl_ObjType tclByteArrayType;
extern Tcl_ObjType tclByteCodeType;
extern Tcl_ObjType tclDoubleType;
+extern Tcl_ObjType tclEndOffsetType;
extern Tcl_ObjType tclIntType;
extern Tcl_ObjType tclListType;
extern Tcl_ObjType tclProcBodyType;
extern Tcl_ObjType tclStringType;
+extern Tcl_ObjType tclArraySearchType;
+extern Tcl_ObjType tclIndexType;
+extern Tcl_ObjType tclNsNameType;
+#ifndef TCL_WIDE_INT_IS_LONG
+extern Tcl_ObjType tclWideIntType;
+#endif
+
+/*
+ * Variables denoting the hash key types defined in the core.
+ */
+
+extern Tcl_HashKeyType tclArrayHashKeyType;
+extern Tcl_HashKeyType tclOneWordHashKeyType;
+extern Tcl_HashKeyType tclStringHashKeyType;
+extern Tcl_HashKeyType tclObjHashKeyType;
/*
* The head of the list of free Tcl objects, and the total number of Tcl
@@ -1549,6 +1651,8 @@ extern Tcl_Obj * tclFreeObjList;
#ifdef TCL_COMPILE_STATS
extern long tclObjsAlloced;
extern long tclObjsFreed;
+#define TCL_MAX_SHARED_OBJ_STATS 5
+extern long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS];
#endif /* TCL_COMPILE_STATS */
/*
@@ -1558,6 +1662,7 @@ extern long tclObjsFreed;
*/
extern char * tclEmptyStringRep;
+extern char tclEmptyString;
/*
*----------------------------------------------------------------
@@ -1566,55 +1671,22 @@ extern char * tclEmptyStringRep;
*----------------------------------------------------------------
*/
-EXTERN int TclAccess _ANSI_ARGS_((CONST char *path,
- int mode));
-EXTERN int TclAccessDeleteProc _ANSI_ARGS_((TclAccessProc_ *proc));
-EXTERN int TclAccessInsertProc _ANSI_ARGS_((TclAccessProc_ *proc));
-EXTERN void TclAllocateFreeObjects _ANSI_ARGS_((void));
EXTERN int TclArraySet _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Obj *arrayNameObj, Tcl_Obj *arrayElemObj));
EXTERN int TclCheckBadOctal _ANSI_ARGS_((Tcl_Interp *interp,
- char *value));
-EXTERN int TclCleanupChildren _ANSI_ARGS_((Tcl_Interp *interp,
- int numPids, Tcl_Pid *pidPtr,
- Tcl_Channel errorChan));
-EXTERN void TclCleanupCommand _ANSI_ARGS_((Command *cmdPtr));
-EXTERN int TclCopyChannel _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Channel inChan, Tcl_Channel outChan,
- int toRead, Tcl_Obj *cmdPtr));
-/*
- * TclCreatePipeline unofficially exported for use by BLT.
- */
-EXTERN int TclCreatePipeline _ANSI_ARGS_((Tcl_Interp *interp,
- int argc, char **argv, Tcl_Pid **pidArrayPtr,
- TclFile *inPipePtr, TclFile *outPipePtr,
- TclFile *errFilePtr));
-EXTERN int TclCreateProc _ANSI_ARGS_((Tcl_Interp *interp,
- Namespace *nsPtr, char *procName,
- Tcl_Obj *argsPtr, Tcl_Obj *bodyPtr,
- Proc **procPtrPtr));
-EXTERN void TclDeleteCompiledLocalVars _ANSI_ARGS_((
- Interp *iPtr, CallFrame *framePtr));
-EXTERN void TclDeleteVars _ANSI_ARGS_((Interp *iPtr,
- Tcl_HashTable *tablePtr));
-EXTERN int TclDoGlob _ANSI_ARGS_((Tcl_Interp *interp,
- char *separators, Tcl_DString *headPtr,
- char *tail, GlobTypeData *types));
-EXTERN void TclDumpMemoryInfo _ANSI_ARGS_((FILE *outFile));
+ CONST char *value));
EXTERN void TclExpandTokenArray _ANSI_ARGS_((
Tcl_Parse *parsePtr));
-EXTERN void TclExprFloatError _ANSI_ARGS_((Tcl_Interp *interp,
- double value));
EXTERN int TclFileAttrsCmd _ANSI_ARGS_((Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[]));
EXTERN int TclFileCopyCmd _ANSI_ARGS_((Tcl_Interp *interp,
- int argc, char **argv)) ;
+ int objc, Tcl_Obj *CONST objv[])) ;
EXTERN int TclFileDeleteCmd _ANSI_ARGS_((Tcl_Interp *interp,
- int argc, char **argv));
+ int objc, Tcl_Obj *CONST objv[]));
EXTERN int TclFileMakeDirsCmd _ANSI_ARGS_((Tcl_Interp *interp,
- int argc, char **argv)) ;
+ int objc, Tcl_Obj *CONST objv[])) ;
EXTERN int TclFileRenameCmd _ANSI_ARGS_((Tcl_Interp *interp,
- int argc, char **argv)) ;
+ int objc, Tcl_Obj *CONST objv[])) ;
EXTERN void TclFinalizeAllocSubsystem _ANSI_ARGS_((void));
EXTERN void TclFinalizeCompExecEnv _ANSI_ARGS_((void));
EXTERN void TclFinalizeCompilation _ANSI_ARGS_((void));
@@ -1622,68 +1694,18 @@ EXTERN void TclFinalizeEncodingSubsystem _ANSI_ARGS_((void));
EXTERN void TclFinalizeEnvironment _ANSI_ARGS_((void));
EXTERN void TclFinalizeExecution _ANSI_ARGS_((void));
EXTERN void TclFinalizeIOSubsystem _ANSI_ARGS_((void));
+EXTERN void TclFinalizeFilesystem _ANSI_ARGS_((void));
EXTERN void TclFinalizeLoad _ANSI_ARGS_((void));
EXTERN void TclFinalizeMemorySubsystem _ANSI_ARGS_((void));
EXTERN void TclFinalizeNotifier _ANSI_ARGS_((void));
+EXTERN void TclFinalizeAsync _ANSI_ARGS_((void));
EXTERN void TclFinalizeSynchronization _ANSI_ARGS_((void));
EXTERN void TclFinalizeThreadData _ANSI_ARGS_((void));
EXTERN void TclFindEncodings _ANSI_ARGS_((CONST char *argv0));
-EXTERN Proc * TclFindProc _ANSI_ARGS_((Interp *iPtr,
- char *procName));
-EXTERN int TclFormatInt _ANSI_ARGS_((char *buffer, long n));
-EXTERN void TclFreePackageInfo _ANSI_ARGS_((Interp *iPtr));
-EXTERN int TclGetDate _ANSI_ARGS_((char *p,
- unsigned long now, long zone,
- unsigned long *timePtr));
-EXTERN Tcl_Obj * TclGetElementOfIndexedArray _ANSI_ARGS_((
- Tcl_Interp *interp, int localIndex,
- Tcl_Obj *elemPtr, int leaveErrorMsg));
-EXTERN char * TclGetExtension _ANSI_ARGS_((char *name));
-EXTERN int TclGetFrame _ANSI_ARGS_((Tcl_Interp *interp,
- char *string, CallFrame **framePtrPtr));
-EXTERN TclCmdProcType TclGetInterpProc _ANSI_ARGS_((void));
-EXTERN int TclGetIntForIndex _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Obj *objPtr, int endValue, int *indexPtr));
-EXTERN Tcl_Obj * TclGetIndexedScalar _ANSI_ARGS_((Tcl_Interp *interp,
- int localIndex, int leaveErrorMsg));
-EXTERN int TclGetLong _ANSI_ARGS_((Tcl_Interp *interp,
- char *string, long *longPtr));
-EXTERN int TclGetLoadedPackages _ANSI_ARGS_((
- Tcl_Interp *interp, char *targetName));
-EXTERN int TclGetNamespaceForQualName _ANSI_ARGS_((
- Tcl_Interp *interp, char *qualName,
- Namespace *cxtNsPtr, int flags,
- Namespace **nsPtrPtr, Namespace **altNsPtrPtr,
- Namespace **actualCxtPtrPtr,
- char **simpleNamePtr));
-EXTERN TclObjCmdProcType TclGetObjInterpProc _ANSI_ARGS_((void));
-EXTERN int TclGetOpenMode _ANSI_ARGS_((Tcl_Interp *interp,
- char *string, int *seekFlagPtr));
-EXTERN Tcl_Command TclGetOriginalCommand _ANSI_ARGS_((
- Tcl_Command command));
EXTERN int TclGlob _ANSI_ARGS_((Tcl_Interp *interp,
- char *pattern, char *unquotedPrefix,
- int globFlags, GlobTypeData* types));
-EXTERN int TclGlobalInvoke _ANSI_ARGS_((Tcl_Interp *interp,
- int argc, char **argv, int flags));
-EXTERN int TclGuessPackageName _ANSI_ARGS_((char *fileName,
- Tcl_DString *bufPtr));
-EXTERN int TclHideUnsafeCommands _ANSI_ARGS_((
- Tcl_Interp *interp));
-EXTERN int TclInExit _ANSI_ARGS_((void));
-EXTERN Tcl_Obj * TclIncrElementOfIndexedArray _ANSI_ARGS_((
- Tcl_Interp *interp, int localIndex,
- Tcl_Obj *elemPtr, long incrAmount));
-EXTERN Tcl_Obj * TclIncrIndexedScalar _ANSI_ARGS_((
- Tcl_Interp *interp, int localIndex,
- long incrAmount));
-EXTERN Tcl_Obj * TclIncrVar2 _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr,
- long incrAmount, int flags));
+ char *pattern, Tcl_Obj *unquotedPrefix,
+ int globFlags, Tcl_GlobTypeData* types));
EXTERN void TclInitAlloc _ANSI_ARGS_((void));
-EXTERN void TclInitCompiledLocals _ANSI_ARGS_((
- Tcl_Interp *interp, CallFrame *framePtr,
- Namespace *nsPtr));
EXTERN void TclInitDbCkalloc _ANSI_ARGS_((void));
EXTERN void TclInitEncodingSubsystem _ANSI_ARGS_((void));
EXTERN void TclInitIOSubsystem _ANSI_ARGS_((void));
@@ -1691,47 +1713,43 @@ EXTERN void TclInitNamespaceSubsystem _ANSI_ARGS_((void));
EXTERN void TclInitNotifier _ANSI_ARGS_((void));
EXTERN void TclInitObjSubsystem _ANSI_ARGS_((void));
EXTERN void TclInitSubsystems _ANSI_ARGS_((CONST char *argv0));
-EXTERN int TclInvoke _ANSI_ARGS_((Tcl_Interp *interp,
- int argc, char **argv, int flags));
-EXTERN int TclInvokeObjectCommand _ANSI_ARGS_((
- ClientData clientData, Tcl_Interp *interp,
- int argc, char **argv));
-EXTERN int TclInvokeStringCommand _ANSI_ARGS_((
- ClientData clientData, Tcl_Interp *interp,
- int objc, Tcl_Obj *CONST objv[]));
EXTERN int TclIsLocalScalar _ANSI_ARGS_((CONST char *src,
int len));
-EXTERN Proc * TclIsProc _ANSI_ARGS_((Command *cmdPtr));
-EXTERN Var * TclLookupVar _ANSI_ARGS_((Tcl_Interp *interp,
- char *part1, char *part2, int flags, char *msg,
- int createPart1, int createPart2,
- Var **arrayPtrPtr));
-EXTERN int TclMathInProgress _ANSI_ARGS_((void));
-EXTERN int TclNeedSpace _ANSI_ARGS_((char *start, char *end));
-EXTERN Tcl_Obj * TclNewProcBodyObj _ANSI_ARGS_((Proc *procPtr));
-EXTERN int TclObjCommandComplete _ANSI_ARGS_((Tcl_Obj *cmdPtr));
-EXTERN int TclObjInterpProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int objc,
- Tcl_Obj *CONST objv[]));
-EXTERN int TclObjInvoke _ANSI_ARGS_((Tcl_Interp *interp,
- int objc, Tcl_Obj *CONST objv[], int flags));
-EXTERN int TclObjInvokeGlobal _ANSI_ARGS_((Tcl_Interp *interp,
- int objc, Tcl_Obj *CONST objv[], int flags));
-EXTERN int TclOpenFileChannelDeleteProc _ANSI_ARGS_((
- TclOpenFileChannelProc_ *proc));
-EXTERN int TclOpenFileChannelInsertProc _ANSI_ARGS_((
- TclOpenFileChannelProc_ *proc));
-EXTERN int TclpAccess _ANSI_ARGS_((CONST char *filename,
+EXTERN int TclJoinThread _ANSI_ARGS_((Tcl_ThreadId id,
+ int* result));
+EXTERN Tcl_Obj * TclLindexList _ANSI_ARGS_((Tcl_Interp* interp,
+ Tcl_Obj* listPtr,
+ Tcl_Obj* argPtr ));
+EXTERN Tcl_Obj * TclLindexFlat _ANSI_ARGS_((Tcl_Interp* interp,
+ Tcl_Obj* listPtr,
+ int indexCount,
+ Tcl_Obj *CONST indexArray[]
+ ));
+EXTERN Tcl_Obj * TclLsetList _ANSI_ARGS_((Tcl_Interp* interp,
+ Tcl_Obj* listPtr,
+ Tcl_Obj* indexPtr,
+ Tcl_Obj* valuePtr
+ ));
+EXTERN Tcl_Obj * TclLsetFlat _ANSI_ARGS_((Tcl_Interp* interp,
+ Tcl_Obj* listPtr,
+ int indexCount,
+ Tcl_Obj *CONST indexArray[],
+ Tcl_Obj* valuePtr
+ ));
+EXTERN int TclParseBackslash _ANSI_ARGS_((CONST char *src,
+ int numBytes, int *readPtr, char *dst));
+EXTERN int TclParseHex _ANSI_ARGS_((CONST char *src, int numBytes,
+ Tcl_UniChar *resultPtr));
+EXTERN int TclParseInteger _ANSI_ARGS_((CONST char *string,
+ int numBytes));
+EXTERN int TclParseWhiteSpace _ANSI_ARGS_((CONST char *src,
+ int numBytes, Tcl_Parse *parsePtr, char *typePtr));
+EXTERN int TclpObjAccess _ANSI_ARGS_((Tcl_Obj *filename,
int mode));
-EXTERN char * TclpAlloc _ANSI_ARGS_((unsigned int size));
+EXTERN int TclpObjLstat _ANSI_ARGS_((Tcl_Obj *pathPtr,
+ Tcl_StatBuf *buf));
EXTERN int TclpCheckStackSpace _ANSI_ARGS_((void));
-EXTERN int TclpCopyFile _ANSI_ARGS_((CONST char *source,
- CONST char *dest));
-EXTERN int TclpCopyDirectory _ANSI_ARGS_((CONST char *source,
- CONST char *dest, Tcl_DString *errorPtr));
-EXTERN int TclpCreateDirectory _ANSI_ARGS_((CONST char *path));
-EXTERN int TclpDeleteFile _ANSI_ARGS_((CONST char *path));
-EXTERN void TclpExit _ANSI_ARGS_((int status));
+EXTERN Tcl_Obj* TclpTempFileName _ANSI_ARGS_((void));
EXTERN void TclpFinalizeCondition _ANSI_ARGS_((
Tcl_Condition *condPtr));
EXTERN void TclpFinalizeMutex _ANSI_ARGS_((Tcl_Mutex *mutexPtr));
@@ -1743,56 +1761,63 @@ EXTERN char * TclpFindExecutable _ANSI_ARGS_((
CONST char *argv0));
EXTERN int TclpFindVariable _ANSI_ARGS_((CONST char *name,
int *lengthPtr));
-EXTERN void TclpFree _ANSI_ARGS_((char *ptr));
-EXTERN unsigned long TclpGetClicks _ANSI_ARGS_((void));
-EXTERN Tcl_Channel TclpGetDefaultStdChannel _ANSI_ARGS_((int type));
-EXTERN unsigned long TclpGetSeconds _ANSI_ARGS_((void));
-EXTERN void TclpGetTime _ANSI_ARGS_((Tcl_Time *time));
-EXTERN int TclpGetTimeZone _ANSI_ARGS_((unsigned long time));
-EXTERN char * TclpGetUserHome _ANSI_ARGS_((CONST char *name,
- Tcl_DString *bufferPtr));
-EXTERN int TclpHasSockets _ANSI_ARGS_((Tcl_Interp *interp));
EXTERN void TclpInitLibraryPath _ANSI_ARGS_((CONST char *argv0));
EXTERN void TclpInitLock _ANSI_ARGS_((void));
EXTERN void TclpInitPlatform _ANSI_ARGS_((void));
EXTERN void TclpInitUnlock _ANSI_ARGS_((void));
-EXTERN int TclpListVolumes _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int TclpLoadFile _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *pathPtr,
+ CONST char *sym1, CONST char *sym2,
+ Tcl_PackageInitProc **proc1Ptr,
+ Tcl_PackageInitProc **proc2Ptr,
+ ClientData *clientDataPtr,
+ Tcl_FSUnloadFileProc **unloadProcPtr));
+EXTERN Tcl_Obj* TclpObjListVolumes _ANSI_ARGS_((void));
EXTERN void TclpMasterLock _ANSI_ARGS_((void));
EXTERN void TclpMasterUnlock _ANSI_ARGS_((void));
EXTERN int TclpMatchFiles _ANSI_ARGS_((Tcl_Interp *interp,
char *separators, Tcl_DString *dirPtr,
char *pattern, char *tail));
+EXTERN int TclpObjNormalizePath _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *pathPtr, int nextCheckpoint));
+EXTERN int TclpObjCreateDirectory _ANSI_ARGS_((Tcl_Obj *pathPtr));
+EXTERN void TclpNativeJoinPath _ANSI_ARGS_((Tcl_Obj *prefix,
+ char *joining));
+EXTERN Tcl_Obj* TclpNativeSplitPath _ANSI_ARGS_((Tcl_Obj *pathPtr,
+ int *lenPtr));
+EXTERN Tcl_PathType TclpGetNativePathType _ANSI_ARGS_((Tcl_Obj *pathObjPtr,
+ int *driveNameLengthPtr, Tcl_Obj **driveNameRef));
+EXTERN int TclCrossFilesystemCopy _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *source, Tcl_Obj *target));
+EXTERN int TclpObjDeleteFile _ANSI_ARGS_((Tcl_Obj *pathPtr));
+EXTERN int TclpObjCopyDirectory _ANSI_ARGS_((Tcl_Obj *srcPathPtr,
+ Tcl_Obj *destPathPtr, Tcl_Obj **errorPtr));
+EXTERN int TclpObjCopyFile _ANSI_ARGS_((Tcl_Obj *srcPathPtr,
+ Tcl_Obj *destPathPtr));
+EXTERN int TclpObjRemoveDirectory _ANSI_ARGS_((Tcl_Obj *pathPtr,
+ int recursive, Tcl_Obj **errorPtr));
+EXTERN int TclpObjRenameFile _ANSI_ARGS_((Tcl_Obj *srcPathPtr,
+ Tcl_Obj *destPathPtr));
+EXTERN int TclpMatchInDirectory _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *resultPtr, Tcl_Obj *pathPtr,
+ CONST char *pattern, Tcl_GlobTypeData *types));
+EXTERN Tcl_Obj* TclpObjGetCwd _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN Tcl_Obj* TclpObjLink _ANSI_ARGS_((Tcl_Obj *pathPtr,
+ Tcl_Obj *toPtr, int linkType));
+EXTERN int TclpObjChdir _ANSI_ARGS_((Tcl_Obj *pathPtr));
+EXTERN Tcl_Obj* TclFileDirname _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj*pathPtr));
+EXTERN int TclpObjStat _ANSI_ARGS_((Tcl_Obj *pathPtr, Tcl_StatBuf *buf));
EXTERN Tcl_Channel TclpOpenFileChannel _ANSI_ARGS_((Tcl_Interp *interp,
- char *fileName, char *modeString,
+ Tcl_Obj *pathPtr, int mode,
int permissions));
+EXTERN void TclpPanic _ANSI_ARGS_(TCL_VARARGS(CONST char *,
+ format));
EXTERN char * TclpReadlink _ANSI_ARGS_((CONST char *fileName,
Tcl_DString *linkPtr));
-EXTERN char * TclpRealloc _ANSI_ARGS_((char *ptr,
- unsigned int size));
EXTERN void TclpReleaseFile _ANSI_ARGS_((TclFile file));
-EXTERN int TclpRemoveDirectory _ANSI_ARGS_((CONST char *path,
- int recursive, Tcl_DString *errorPtr));
-EXTERN int TclpRenameFile _ANSI_ARGS_((CONST char *source,
- CONST char *dest));
-EXTERN void TclpSetInitialEncodings _ANSI_ARGS_((void));
EXTERN void TclpSetVariables _ANSI_ARGS_((Tcl_Interp *interp));
-EXTERN VOID * TclpSysAlloc _ANSI_ARGS_((long size, int isBin));
-EXTERN void TclpSysFree _ANSI_ARGS_((VOID *ptr));
-EXTERN VOID * TclpSysRealloc _ANSI_ARGS_((VOID *cp,
- unsigned int size));
-EXTERN void TclpUnloadFile _ANSI_ARGS_((ClientData clientData));
-EXTERN char * TclPrecTraceProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, char *name1, char *name2,
- int flags));
-EXTERN int TclPreventAliasLoop _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Interp *cmdInterp, Tcl_Command cmd));
-EXTERN void TclProcCleanupProc _ANSI_ARGS_((Proc *procPtr));
-EXTERN int TclProcCompileProc _ANSI_ARGS_((Tcl_Interp *interp,
- Proc *procPtr, Tcl_Obj *bodyPtr, Namespace *nsPtr,
- CONST char *description, CONST char *procName));
-EXTERN void TclProcDeleteProc _ANSI_ARGS_((ClientData clientData));
-EXTERN int TclProcInterpProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char **argv));
+EXTERN void TclpUnloadFile _ANSI_ARGS_((Tcl_LoadHandle loadHandle));
EXTERN VOID * TclpThreadDataKeyGet _ANSI_ARGS_((
Tcl_ThreadDataKey *keyPtr));
EXTERN void TclpThreadDataKeyInit _ANSI_ARGS_((
@@ -1802,33 +1827,22 @@ EXTERN void TclpThreadDataKeySet _ANSI_ARGS_((
EXTERN void TclpThreadExit _ANSI_ARGS_((int status));
EXTERN void TclRememberCondition _ANSI_ARGS_((Tcl_Condition *mutex));
EXTERN void TclRememberDataKey _ANSI_ARGS_((Tcl_ThreadDataKey *mutex));
+EXTERN VOID TclRememberJoinableThread _ANSI_ARGS_((Tcl_ThreadId id));
EXTERN void TclRememberMutex _ANSI_ARGS_((Tcl_Mutex *mutex));
-EXTERN int TclRenameCommand _ANSI_ARGS_((Tcl_Interp *interp,
- char *oldName, char *newName)) ;
-EXTERN void TclResetShadowedCmdRefs _ANSI_ARGS_((
- Tcl_Interp *interp, Command *newCmdPtr));
-EXTERN int TclServiceIdle _ANSI_ARGS_((void));
-EXTERN Tcl_Obj * TclSetElementOfIndexedArray _ANSI_ARGS_((
- Tcl_Interp *interp, int localIndex,
- Tcl_Obj *elemPtr, Tcl_Obj *objPtr,
- int leaveErrorMsg));
-EXTERN Tcl_Obj * TclSetIndexedScalar _ANSI_ARGS_((Tcl_Interp *interp,
- int localIndex, Tcl_Obj *objPtr,
- int leaveErrorMsg));
-EXTERN char * TclSetPreInitScript _ANSI_ARGS_((char *string));
-EXTERN void TclSetupEnv _ANSI_ARGS_((Tcl_Interp *interp));
-EXTERN int TclSockGetPort _ANSI_ARGS_((Tcl_Interp *interp,
- char *string, char *proto, int *portPtr));
-EXTERN int TclSockMinimumBuffers _ANSI_ARGS_((int sock,
- int size));
-EXTERN int TclStat _ANSI_ARGS_((CONST char *path,
- struct stat *buf));
-EXTERN int TclStatDeleteProc _ANSI_ARGS_((TclStatProc_ *proc));
-EXTERN int TclStatInsertProc _ANSI_ARGS_((TclStatProc_ *proc));
-EXTERN void TclTeardownNamespace _ANSI_ARGS_((Namespace *nsPtr));
+EXTERN VOID TclSignalExitThread _ANSI_ARGS_((Tcl_ThreadId id,
+ int result));
EXTERN void TclTransferResult _ANSI_ARGS_((Tcl_Interp *sourceInterp,
int result, Tcl_Interp *targetInterp));
-EXTERN int TclUpdateReturnInfo _ANSI_ARGS_((Interp *iPtr));
+EXTERN Tcl_Obj* TclpNativeToNormalized
+ _ANSI_ARGS_((ClientData clientData));
+EXTERN Tcl_Obj* TclpFilesystemPathType
+ _ANSI_ARGS_((Tcl_Obj* pathObjPtr));
+EXTERN Tcl_PackageInitProc* TclpFindSymbol _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_LoadHandle loadHandle, CONST char *symbol));
+EXTERN int TclpDlopen _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *pathPtr,
+ Tcl_LoadHandle *loadHandle,
+ Tcl_FSUnloadFileProc **unloadProcPtr));
/*
*----------------------------------------------------------------
@@ -1926,6 +1940,8 @@ EXTERN int Tcl_LreplaceObjCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
EXTERN int Tcl_LsearchObjCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+EXTERN int Tcl_LsetObjCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp* interp, int objc, Tcl_Obj *CONST objv[]));
EXTERN int Tcl_LsortObjCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
EXTERN int Tcl_NamespaceObjCmd _ANSI_ARGS_((ClientData clientData,
@@ -1997,7 +2013,7 @@ EXTERN int Tcl_WhileObjCmd _ANSI_ARGS_((ClientData clientData,
#ifdef MAC_TCL
EXTERN int Tcl_EchoCmd _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST84 char **argv));
EXTERN int Tcl_LsObjCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
EXTERN int Tcl_BeepObjCmd _ANSI_ARGS_((ClientData clientData,
@@ -2014,6 +2030,8 @@ EXTERN int Tcl_ResourceObjCmd _ANSI_ARGS_((ClientData clientData,
*----------------------------------------------------------------
*/
+EXTERN int TclCompileAppendCmd _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
EXTERN int TclCompileBreakCmd _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
EXTERN int TclCompileCatchCmd _ANSI_ARGS_((Tcl_Interp *interp,
@@ -2030,12 +2048,52 @@ EXTERN int TclCompileIfCmd _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
EXTERN int TclCompileIncrCmd _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
+EXTERN int TclCompileLappendCmd _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
+EXTERN int TclCompileLindexCmd _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
+EXTERN int TclCompileListCmd _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
+EXTERN int TclCompileLlengthCmd _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
+EXTERN int TclCompileLsetCmd _ANSI_ARGS_((Tcl_Interp* interp,
+ Tcl_Parse* parsePtr, struct CompileEnv* envPtr));
+EXTERN int TclCompileRegexpCmd _ANSI_ARGS_((Tcl_Interp* interp,
+ Tcl_Parse* parsePtr, struct CompileEnv* envPtr));
+EXTERN int TclCompileReturnCmd _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
EXTERN int TclCompileSetCmd _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
+EXTERN int TclCompileStringCmd _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
EXTERN int TclCompileWhileCmd _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
/*
+ * Functions defined in generic/tclVar.c and currenttly exported only
+ * for use by the bytecode compiler and engine. Some of these could later
+ * be placed in the public interface.
+ */
+
+EXTERN Var * TclLookupArrayElement _ANSI_ARGS_((Tcl_Interp *interp,
+ CONST char *arrayName, CONST char *elName, CONST int flags,
+ CONST char *msg, CONST int createPart1,
+ CONST int createPart2, Var *arrayPtr));
+EXTERN Var * TclObjLookupVar _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *part1Ptr, CONST char *part2, int flags,
+ CONST char *msg, CONST int createPart1,
+ CONST int createPart2, Var **arrayPtrPtr));
+EXTERN Tcl_Obj *TclPtrGetVar _ANSI_ARGS_((Tcl_Interp *interp, Var *varPtr,
+ Var *arrayPtr, CONST char *part1, CONST char *part2,
+ CONST int flags));
+EXTERN Tcl_Obj *TclPtrSetVar _ANSI_ARGS_((Tcl_Interp *interp, Var *varPtr,
+ Var *arrayPtr, CONST char *part1, CONST char *part2,
+ Tcl_Obj *newValuePtr, CONST int flags));
+EXTERN Tcl_Obj *TclPtrIncrVar _ANSI_ARGS_((Tcl_Interp *interp, Var *varPtr,
+ Var *arrayPtr, CONST char *part1, CONST char *part2,
+ CONST long i, CONST int flags));
+
+/*
*----------------------------------------------------------------
* Macros used by the Tcl core to create and release Tcl objects.
* TclNewObj(objPtr) creates a new object denoting an empty string.
@@ -2050,6 +2108,10 @@ EXTERN int TclCompileWhileCmd _ANSI_ARGS_((Tcl_Interp *interp,
*
* EXTERN void TclNewObj _ANSI_ARGS_((Tcl_Obj *objPtr));
* EXTERN void TclDecrRefCount _ANSI_ARGS_((Tcl_Obj *objPtr));
+ *
+ * These macros are defined in terms of two macros that depend on
+ * memory allocator in use: TclAllocObjStorage, TclFreeObjStorage.
+ * They are defined below.
*----------------------------------------------------------------
*/
@@ -2063,78 +2125,102 @@ EXTERN int TclCompileWhileCmd _ANSI_ARGS_((Tcl_Interp *interp,
# define TclIncrObjsFreed()
#endif /* TCL_COMPILE_STATS */
-#ifdef TCL_MEM_DEBUG
-# define TclNewObj(objPtr) \
- (objPtr) = (Tcl_Obj *) \
- Tcl_DbCkalloc(sizeof(Tcl_Obj), __FILE__, __LINE__); \
- (objPtr)->refCount = 0; \
- (objPtr)->bytes = tclEmptyStringRep; \
- (objPtr)->length = 0; \
- (objPtr)->typePtr = NULL; \
- TclIncrObjsAllocated()
-
-# define TclDbNewObj(objPtr, file, line) \
- (objPtr) = (Tcl_Obj *) Tcl_DbCkalloc(sizeof(Tcl_Obj), (file), (line)); \
+#define TclNewObj(objPtr) \
+ TclAllocObjStorage(objPtr); \
+ TclIncrObjsAllocated(); \
(objPtr)->refCount = 0; \
(objPtr)->bytes = tclEmptyStringRep; \
(objPtr)->length = 0; \
- (objPtr)->typePtr = NULL; \
- TclIncrObjsAllocated()
-
-# define TclDecrRefCount(objPtr) \
+ (objPtr)->typePtr = NULL
+
+#define TclDecrRefCount(objPtr) \
if (--(objPtr)->refCount <= 0) { \
- if ((objPtr)->refCount < -1) \
- panic("Reference count for %lx was negative: %s line %d", \
- (objPtr), __FILE__, __LINE__); \
- if (((objPtr)->bytes != NULL) \
- && ((objPtr)->bytes != tclEmptyStringRep)) { \
- ckfree((char *) (objPtr)->bytes); \
- } \
if (((objPtr)->typePtr != NULL) \
&& ((objPtr)->typePtr->freeIntRepProc != NULL)) { \
(objPtr)->typePtr->freeIntRepProc(objPtr); \
} \
- ckfree((char *) (objPtr)); \
+ if (((objPtr)->bytes != NULL) \
+ && ((objPtr)->bytes != tclEmptyStringRep)) { \
+ ckfree((char *) (objPtr)->bytes); \
+ } \
+ TclFreeObjStorage(objPtr); \
TclIncrObjsFreed(); \
}
+#ifdef TCL_MEM_DEBUG
+# define TclAllocObjStorage(objPtr) \
+ (objPtr) = (Tcl_Obj *) \
+ Tcl_DbCkalloc(sizeof(Tcl_Obj), __FILE__, __LINE__)
+
+# define TclFreeObjStorage(objPtr) \
+ if ((objPtr)->refCount < -1) { \
+ panic("Reference count for %lx was negative: %s line %d", \
+ (objPtr), __FILE__, __LINE__); \
+ } \
+ ckfree((char *) (objPtr))
+
+# define TclDbNewObj(objPtr, file, line) \
+ (objPtr) = (Tcl_Obj *) Tcl_DbCkalloc(sizeof(Tcl_Obj), (file), (line)); \
+ (objPtr)->refCount = 0; \
+ (objPtr)->bytes = tclEmptyStringRep; \
+ (objPtr)->length = 0; \
+ (objPtr)->typePtr = NULL; \
+ TclIncrObjsAllocated()
+
+#elif defined(PURIFY)
+
+/*
+ * The PURIFY mode is like the regular mode, but instead of doing block
+ * Tcl_Obj allocation and keeping a freed list for efficiency, it always
+ * allocates and frees a single Tcl_Obj so that tools like Purify can
+ * better track memory leaks
+ */
+
+# define TclAllocObjStorage(objPtr) \
+ (objPtr) = (Tcl_Obj *) Tcl_Ckalloc(sizeof(Tcl_Obj))
+
+# define TclFreeObjStorage(objPtr) \
+ ckfree((char *) (objPtr))
+
+#elif defined(TCL_THREADS) && defined(USE_THREAD_ALLOC)
+
+/*
+ * The TCL_THREADS mode is like the regular mode but allocates Tcl_Obj's
+ * from per-thread caches.
+ */
+
+EXTERN Tcl_Obj *TclThreadAllocObj _ANSI_ARGS_((void));
+EXTERN void TclThreadFreeObj _ANSI_ARGS_((Tcl_Obj *));
+
+# define TclAllocObjStorage(objPtr) \
+ (objPtr) = TclThreadAllocObj()
+
+# define TclFreeObjStorage(objPtr) \
+ TclThreadFreeObj((objPtr))
+
#else /* not TCL_MEM_DEBUG */
#ifdef TCL_THREADS
+/* declared in tclObj.c */
extern Tcl_Mutex tclObjMutex;
#endif
-# define TclNewObj(objPtr) \
- Tcl_MutexLock(&tclObjMutex); \
- if (tclFreeObjList == NULL) { \
- TclAllocateFreeObjects(); \
- } \
- (objPtr) = tclFreeObjList; \
- tclFreeObjList = (Tcl_Obj *) \
- tclFreeObjList->internalRep.otherValuePtr; \
- (objPtr)->refCount = 0; \
- (objPtr)->bytes = tclEmptyStringRep; \
- (objPtr)->length = 0; \
- (objPtr)->typePtr = NULL; \
- TclIncrObjsAllocated(); \
- Tcl_MutexUnlock(&tclObjMutex)
+# define TclAllocObjStorage(objPtr) \
+ Tcl_MutexLock(&tclObjMutex); \
+ if (tclFreeObjList == NULL) { \
+ TclAllocateFreeObjects(); \
+ } \
+ (objPtr) = tclFreeObjList; \
+ tclFreeObjList = (Tcl_Obj *) \
+ tclFreeObjList->internalRep.otherValuePtr; \
+ Tcl_MutexUnlock(&tclObjMutex)
+
+# define TclFreeObjStorage(objPtr) \
+ Tcl_MutexLock(&tclObjMutex); \
+ (objPtr)->internalRep.otherValuePtr = (VOID *) tclFreeObjList; \
+ tclFreeObjList = (objPtr); \
+ Tcl_MutexUnlock(&tclObjMutex)
-# define TclDecrRefCount(objPtr) \
- if (--(objPtr)->refCount <= 0) { \
- if (((objPtr)->bytes != NULL) \
- && ((objPtr)->bytes != tclEmptyStringRep)) { \
- ckfree((char *) (objPtr)->bytes); \
- } \
- if (((objPtr)->typePtr != NULL) \
- && ((objPtr)->typePtr->freeIntRepProc != NULL)) { \
- (objPtr)->typePtr->freeIntRepProc(objPtr); \
- } \
- Tcl_MutexLock(&tclObjMutex); \
- (objPtr)->internalRep.otherValuePtr = (VOID *) tclFreeObjList; \
- tclFreeObjList = (objPtr); \
- TclIncrObjsFreed(); \
- Tcl_MutexUnlock(&tclObjMutex); \
- }
#endif /* TCL_MEM_DEBUG */
/*
@@ -2179,6 +2265,23 @@ extern Tcl_Mutex tclObjMutex;
#define TclGetString(objPtr) \
((objPtr)->bytes? (objPtr)->bytes : Tcl_GetString((objPtr)))
+/*
+ *----------------------------------------------------------------
+ * Macro used by the Tcl core to compare Unicode strings. On
+ * big-endian systems we can use the more efficient memcmp, but
+ * this would not be lexically correct on little-endian systems.
+ * The ANSI C "prototype" for this macro is:
+ *
+ * EXTERN int TclUniCharNcmp _ANSI_ARGS_((CONST Tcl_UniChar *cs,
+ * CONST Tcl_UniChar *ct, unsigned long n));
+ *----------------------------------------------------------------
+ */
+#ifdef WORDS_BIGENDIAN
+# define TclUniCharNcmp(cs,ct,n) memcmp((cs),(ct),(n)*sizeof(Tcl_UniChar))
+#else /* !WORDS_BIGENDIAN */
+# define TclUniCharNcmp Tcl_UniCharNcmp
+#endif /* WORDS_BIGENDIAN */
+
#include "tclIntDecls.h"
# undef TCL_STORAGE_CLASS
@@ -2186,4 +2289,3 @@ extern Tcl_Mutex tclObjMutex;
#endif /* _TCLINT */
-
diff --git a/tcl/generic/tclIntDecls.h b/tcl/generic/tclIntDecls.h
index 900fc2e9f00..adea7f58f25 100644
--- a/tcl/generic/tclIntDecls.h
+++ b/tcl/generic/tclIntDecls.h
@@ -29,8 +29,7 @@
* Exported function declarations:
*/
-/* 0 */
-EXTERN int TclAccess _ANSI_ARGS_((CONST char * path, int mode));
+/* Slot 0 is reserved */
/* 1 */
EXTERN int TclAccessDeleteProc _ANSI_ARGS_((
TclAccessProc_ * proc));
@@ -64,20 +63,20 @@ EXTERN int TclCopyChannel _ANSI_ARGS_((Tcl_Interp * interp,
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
/* 9 */
EXTERN int TclCreatePipeline _ANSI_ARGS_((Tcl_Interp * interp,
- int argc, char ** argv,
+ int argc, CONST char ** argv,
Tcl_Pid ** pidArrayPtr, TclFile * inPipePtr,
TclFile * outPipePtr, TclFile * errFilePtr));
#endif /* UNIX */
#ifdef __WIN32__
/* 9 */
EXTERN int TclCreatePipeline _ANSI_ARGS_((Tcl_Interp * interp,
- int argc, char ** argv,
+ int argc, CONST char ** argv,
Tcl_Pid ** pidArrayPtr, TclFile * inPipePtr,
TclFile * outPipePtr, TclFile * errFilePtr));
#endif /* __WIN32__ */
/* 10 */
EXTERN int TclCreateProc _ANSI_ARGS_((Tcl_Interp * interp,
- Namespace * nsPtr, char * procName,
+ Namespace * nsPtr, CONST char * procName,
Tcl_Obj * argsPtr, Tcl_Obj * bodyPtr,
Proc ** procPtrPtr));
/* 11 */
@@ -89,28 +88,18 @@ EXTERN void TclDeleteVars _ANSI_ARGS_((Interp * iPtr,
/* 13 */
EXTERN int TclDoGlob _ANSI_ARGS_((Tcl_Interp * interp,
char * separators, Tcl_DString * headPtr,
- char * tail, GlobTypeData * types));
+ char * tail, Tcl_GlobTypeData * types));
/* 14 */
EXTERN void TclDumpMemoryInfo _ANSI_ARGS_((FILE * outFile));
/* Slot 15 is reserved */
/* 16 */
EXTERN void TclExprFloatError _ANSI_ARGS_((Tcl_Interp * interp,
double value));
-/* 17 */
-EXTERN int TclFileAttrsCmd _ANSI_ARGS_((Tcl_Interp * interp,
- int objc, Tcl_Obj *CONST objv[]));
-/* 18 */
-EXTERN int TclFileCopyCmd _ANSI_ARGS_((Tcl_Interp * interp,
- int argc, char ** argv));
-/* 19 */
-EXTERN int TclFileDeleteCmd _ANSI_ARGS_((Tcl_Interp * interp,
- int argc, char ** argv));
-/* 20 */
-EXTERN int TclFileMakeDirsCmd _ANSI_ARGS_((Tcl_Interp * interp,
- int argc, char ** argv));
-/* 21 */
-EXTERN int TclFileRenameCmd _ANSI_ARGS_((Tcl_Interp * interp,
- int argc, char ** argv));
+/* Slot 17 is reserved */
+/* Slot 18 is reserved */
+/* Slot 19 is reserved */
+/* Slot 20 is reserved */
+/* Slot 21 is reserved */
/* 22 */
EXTERN int TclFindElement _ANSI_ARGS_((Tcl_Interp * interp,
CONST char * listStr, int listLength,
@@ -119,7 +108,7 @@ EXTERN int TclFindElement _ANSI_ARGS_((Tcl_Interp * interp,
int * bracePtr));
/* 23 */
EXTERN Proc * TclFindProc _ANSI_ARGS_((Interp * iPtr,
- char * procName));
+ CONST char * procName));
/* 24 */
EXTERN int TclFormatInt _ANSI_ARGS_((char * buffer, long n));
/* 25 */
@@ -130,44 +119,39 @@ EXTERN int TclGetDate _ANSI_ARGS_((char * p, unsigned long now,
long zone, unsigned long * timePtr));
/* 28 */
EXTERN Tcl_Channel TclpGetDefaultStdChannel _ANSI_ARGS_((int type));
-/* 29 */
-EXTERN Tcl_Obj * TclGetElementOfIndexedArray _ANSI_ARGS_((
- Tcl_Interp * interp, int localIndex,
- Tcl_Obj * elemPtr, int leaveErrorMsg));
+/* Slot 29 is reserved */
/* Slot 30 is reserved */
/* 31 */
EXTERN char * TclGetExtension _ANSI_ARGS_((char * name));
/* 32 */
EXTERN int TclGetFrame _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, CallFrame ** framePtrPtr));
+ CONST char * str, CallFrame ** framePtrPtr));
/* 33 */
EXTERN TclCmdProcType TclGetInterpProc _ANSI_ARGS_((void));
/* 34 */
EXTERN int TclGetIntForIndex _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * objPtr, int endValue,
int * indexPtr));
-/* 35 */
-EXTERN Tcl_Obj * TclGetIndexedScalar _ANSI_ARGS_((Tcl_Interp * interp,
- int localIndex, int leaveErrorMsg));
+/* Slot 35 is reserved */
/* 36 */
EXTERN int TclGetLong _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, long * longPtr));
+ CONST char * str, long * longPtr));
/* 37 */
EXTERN int TclGetLoadedPackages _ANSI_ARGS_((
Tcl_Interp * interp, char * targetName));
/* 38 */
EXTERN int TclGetNamespaceForQualName _ANSI_ARGS_((
- Tcl_Interp * interp, char * qualName,
+ Tcl_Interp * interp, CONST char * qualName,
Namespace * cxtNsPtr, int flags,
Namespace ** nsPtrPtr,
Namespace ** altNsPtrPtr,
Namespace ** actualCxtPtrPtr,
- char ** simpleNamePtr));
+ CONST char ** simpleNamePtr));
/* 39 */
EXTERN TclObjCmdProcType TclGetObjInterpProc _ANSI_ARGS_((void));
/* 40 */
EXTERN int TclGetOpenMode _ANSI_ARGS_((Tcl_Interp * interp,
- char * str, int * seekFlagPtr));
+ CONST char * str, int * seekFlagPtr));
/* 41 */
EXTERN Tcl_Command TclGetOriginalCommand _ANSI_ARGS_((
Tcl_Command command));
@@ -176,23 +160,17 @@ EXTERN char * TclpGetUserHome _ANSI_ARGS_((CONST char * name,
Tcl_DString * bufferPtr));
/* 43 */
EXTERN int TclGlobalInvoke _ANSI_ARGS_((Tcl_Interp * interp,
- int argc, char ** argv, int flags));
+ int argc, CONST84 char ** argv, int flags));
/* 44 */
-EXTERN int TclGuessPackageName _ANSI_ARGS_((char * fileName,
- Tcl_DString * bufPtr));
+EXTERN int TclGuessPackageName _ANSI_ARGS_((
+ CONST char * fileName, Tcl_DString * bufPtr));
/* 45 */
EXTERN int TclHideUnsafeCommands _ANSI_ARGS_((
Tcl_Interp * interp));
/* 46 */
EXTERN int TclInExit _ANSI_ARGS_((void));
-/* 47 */
-EXTERN Tcl_Obj * TclIncrElementOfIndexedArray _ANSI_ARGS_((
- Tcl_Interp * interp, int localIndex,
- Tcl_Obj * elemPtr, long incrAmount));
-/* 48 */
-EXTERN Tcl_Obj * TclIncrIndexedScalar _ANSI_ARGS_((
- Tcl_Interp * interp, int localIndex,
- long incrAmount));
+/* Slot 47 is reserved */
+/* Slot 48 is reserved */
/* 49 */
EXTERN Tcl_Obj * TclIncrVar2 _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * part1Ptr, Tcl_Obj * part2Ptr,
@@ -205,11 +183,11 @@ EXTERN void TclInitCompiledLocals _ANSI_ARGS_((
EXTERN int TclInterpInit _ANSI_ARGS_((Tcl_Interp * interp));
/* 52 */
EXTERN int TclInvoke _ANSI_ARGS_((Tcl_Interp * interp, int argc,
- char ** argv, int flags));
+ CONST84 char ** argv, int flags));
/* 53 */
EXTERN int TclInvokeObjectCommand _ANSI_ARGS_((
ClientData clientData, Tcl_Interp * interp,
- int argc, char ** argv));
+ int argc, CONST84 char ** argv));
/* 54 */
EXTERN int TclInvokeStringCommand _ANSI_ARGS_((
ClientData clientData, Tcl_Interp * interp,
@@ -220,15 +198,13 @@ EXTERN Proc * TclIsProc _ANSI_ARGS_((Command * cmdPtr));
/* Slot 57 is reserved */
/* 58 */
EXTERN Var * TclLookupVar _ANSI_ARGS_((Tcl_Interp * interp,
- char * part1, char * part2, int flags,
- char * msg, int createPart1, int createPart2,
- Var ** arrayPtrPtr));
-/* 59 */
-EXTERN int TclpMatchFiles _ANSI_ARGS_((Tcl_Interp * interp,
- char * separators, Tcl_DString * dirPtr,
- char * pattern, char * tail));
+ CONST char * part1, CONST char * part2,
+ int flags, CONST char * msg, int createPart1,
+ int createPart2, Var ** arrayPtrPtr));
+/* Slot 59 is reserved */
/* 60 */
-EXTERN int TclNeedSpace _ANSI_ARGS_((char * start, char * end));
+EXTERN int TclNeedSpace _ANSI_ARGS_((CONST char * start,
+ CONST char * end));
/* 61 */
EXTERN Tcl_Obj * TclNewProcBodyObj _ANSI_ARGS_((Proc * procPtr));
/* 62 */
@@ -249,20 +225,13 @@ EXTERN int TclOpenFileChannelDeleteProc _ANSI_ARGS_((
/* 67 */
EXTERN int TclOpenFileChannelInsertProc _ANSI_ARGS_((
TclOpenFileChannelProc_ * proc));
-/* 68 */
-EXTERN int TclpAccess _ANSI_ARGS_((CONST char * path, int mode));
+/* Slot 68 is reserved */
/* 69 */
EXTERN char * TclpAlloc _ANSI_ARGS_((unsigned int size));
-/* 70 */
-EXTERN int TclpCopyFile _ANSI_ARGS_((CONST char * source,
- CONST char * dest));
-/* 71 */
-EXTERN int TclpCopyDirectory _ANSI_ARGS_((CONST char * source,
- CONST char * dest, Tcl_DString * errorPtr));
-/* 72 */
-EXTERN int TclpCreateDirectory _ANSI_ARGS_((CONST char * path));
-/* 73 */
-EXTERN int TclpDeleteFile _ANSI_ARGS_((CONST char * path));
+/* Slot 70 is reserved */
+/* Slot 71 is reserved */
+/* Slot 72 is reserved */
+/* Slot 73 is reserved */
/* 74 */
EXTERN void TclpFree _ANSI_ARGS_((char * ptr));
/* 75 */
@@ -273,29 +242,21 @@ EXTERN unsigned long TclpGetSeconds _ANSI_ARGS_((void));
EXTERN void TclpGetTime _ANSI_ARGS_((Tcl_Time * time));
/* 78 */
EXTERN int TclpGetTimeZone _ANSI_ARGS_((unsigned long time));
-/* 79 */
-EXTERN int TclpListVolumes _ANSI_ARGS_((Tcl_Interp * interp));
-/* 80 */
-EXTERN Tcl_Channel TclpOpenFileChannel _ANSI_ARGS_((Tcl_Interp * interp,
- char * fileName, char * modeString,
- int permissions));
+/* Slot 79 is reserved */
+/* Slot 80 is reserved */
/* 81 */
EXTERN char * TclpRealloc _ANSI_ARGS_((char * ptr,
unsigned int size));
-/* 82 */
-EXTERN int TclpRemoveDirectory _ANSI_ARGS_((CONST char * path,
- int recursive, Tcl_DString * errorPtr));
-/* 83 */
-EXTERN int TclpRenameFile _ANSI_ARGS_((CONST char * source,
- CONST char * dest));
+/* Slot 82 is reserved */
+/* Slot 83 is reserved */
/* Slot 84 is reserved */
/* Slot 85 is reserved */
/* Slot 86 is reserved */
/* Slot 87 is reserved */
/* 88 */
EXTERN char * TclPrecTraceProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp * interp, char * name1,
- char * name2, int flags));
+ Tcl_Interp * interp, CONST char * name1,
+ CONST char * name2, int flags));
/* 89 */
EXTERN int TclPreventAliasLoop _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Interp * cmdInterp, Tcl_Command cmd));
@@ -311,10 +272,9 @@ EXTERN int TclProcCompileProc _ANSI_ARGS_((Tcl_Interp * interp,
EXTERN void TclProcDeleteProc _ANSI_ARGS_((ClientData clientData));
/* 94 */
EXTERN int TclProcInterpProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp * interp, int argc, char ** argv));
-/* 95 */
-EXTERN int TclpStat _ANSI_ARGS_((CONST char * path,
- struct stat * buf));
+ Tcl_Interp * interp, int argc,
+ CONST84 char ** argv));
+/* Slot 95 is reserved */
/* 96 */
EXTERN int TclRenameCommand _ANSI_ARGS_((Tcl_Interp * interp,
char * oldName, char * newName));
@@ -323,15 +283,8 @@ EXTERN void TclResetShadowedCmdRefs _ANSI_ARGS_((
Tcl_Interp * interp, Command * newCmdPtr));
/* 98 */
EXTERN int TclServiceIdle _ANSI_ARGS_((void));
-/* 99 */
-EXTERN Tcl_Obj * TclSetElementOfIndexedArray _ANSI_ARGS_((
- Tcl_Interp * interp, int localIndex,
- Tcl_Obj * elemPtr, Tcl_Obj * objPtr,
- int leaveErrorMsg));
-/* 100 */
-EXTERN Tcl_Obj * TclSetIndexedScalar _ANSI_ARGS_((Tcl_Interp * interp,
- int localIndex, Tcl_Obj * objPtr,
- int leaveErrorMsg));
+/* Slot 99 is reserved */
+/* Slot 100 is reserved */
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
/* 101 */
EXTERN char * TclSetPreInitScript _ANSI_ARGS_((char * string));
@@ -355,9 +308,7 @@ EXTERN int TclSockMinimumBuffers _ANSI_ARGS_((int sock,
EXTERN int TclSockMinimumBuffers _ANSI_ARGS_((int sock,
int size));
#endif /* __WIN32__ */
-/* 105 */
-EXTERN int TclStat _ANSI_ARGS_((CONST char * path,
- struct stat * buf));
+/* Slot 105 is reserved */
/* 106 */
EXTERN int TclStatDeleteProc _ANSI_ARGS_((TclStatProc_ * proc));
/* 107 */
@@ -369,7 +320,7 @@ EXTERN int TclUpdateReturnInfo _ANSI_ARGS_((Interp * iPtr));
/* Slot 110 is reserved */
/* 111 */
EXTERN void Tcl_AddInterpResolvers _ANSI_ARGS_((
- Tcl_Interp * interp, char * name,
+ Tcl_Interp * interp, CONST char * name,
Tcl_ResolveCmdProc * cmdProc,
Tcl_ResolveVarProc * varProc,
Tcl_ResolveCompiledVarProc * compiledVarProc));
@@ -379,26 +330,26 @@ EXTERN int Tcl_AppendExportList _ANSI_ARGS_((
Tcl_Obj * objPtr));
/* 113 */
EXTERN Tcl_Namespace * Tcl_CreateNamespace _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, ClientData clientData,
+ CONST char * name, ClientData clientData,
Tcl_NamespaceDeleteProc * deleteProc));
/* 114 */
EXTERN void Tcl_DeleteNamespace _ANSI_ARGS_((
Tcl_Namespace * nsPtr));
/* 115 */
EXTERN int Tcl_Export _ANSI_ARGS_((Tcl_Interp * interp,
- Tcl_Namespace * nsPtr, char * pattern,
+ Tcl_Namespace * nsPtr, CONST char * pattern,
int resetListFirst));
/* 116 */
EXTERN Tcl_Command Tcl_FindCommand _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, Tcl_Namespace * contextNsPtr,
- int flags));
+ CONST char * name,
+ Tcl_Namespace * contextNsPtr, int flags));
/* 117 */
EXTERN Tcl_Namespace * Tcl_FindNamespace _ANSI_ARGS_((Tcl_Interp * interp,
- char * name, Tcl_Namespace * contextNsPtr,
- int flags));
+ CONST char * name,
+ Tcl_Namespace * contextNsPtr, int flags));
/* 118 */
EXTERN int Tcl_GetInterpResolvers _ANSI_ARGS_((
- Tcl_Interp * interp, char * name,
+ Tcl_Interp * interp, CONST char * name,
Tcl_ResolverInfo * resInfo));
/* 119 */
EXTERN int Tcl_GetNamespaceResolvers _ANSI_ARGS_((
@@ -406,11 +357,11 @@ EXTERN int Tcl_GetNamespaceResolvers _ANSI_ARGS_((
Tcl_ResolverInfo * resInfo));
/* 120 */
EXTERN Tcl_Var Tcl_FindNamespaceVar _ANSI_ARGS_((
- Tcl_Interp * interp, char * name,
+ Tcl_Interp * interp, CONST char * name,
Tcl_Namespace * contextNsPtr, int flags));
/* 121 */
EXTERN int Tcl_ForgetImport _ANSI_ARGS_((Tcl_Interp * interp,
- Tcl_Namespace * nsPtr, char * pattern));
+ Tcl_Namespace * nsPtr, CONST char * pattern));
/* 122 */
EXTERN Tcl_Command Tcl_GetCommandFromObj _ANSI_ARGS_((
Tcl_Interp * interp, Tcl_Obj * objPtr));
@@ -430,7 +381,7 @@ EXTERN void Tcl_GetVariableFullName _ANSI_ARGS_((
Tcl_Obj * objPtr));
/* 127 */
EXTERN int Tcl_Import _ANSI_ARGS_((Tcl_Interp * interp,
- Tcl_Namespace * nsPtr, char * pattern,
+ Tcl_Namespace * nsPtr, CONST char * pattern,
int allowOverwrite));
/* 128 */
EXTERN void Tcl_PopCallFrame _ANSI_ARGS_((Tcl_Interp* interp));
@@ -440,7 +391,7 @@ EXTERN int Tcl_PushCallFrame _ANSI_ARGS_((Tcl_Interp* interp,
Tcl_Namespace * nsPtr, int isProcCallFrame));
/* 130 */
EXTERN int Tcl_RemoveInterpResolvers _ANSI_ARGS_((
- Tcl_Interp * interp, char * name));
+ Tcl_Interp * interp, CONST char * name));
/* 131 */
EXTERN void Tcl_SetNamespaceResolvers _ANSI_ARGS_((
Tcl_Namespace * namespacePtr,
@@ -453,26 +404,21 @@ EXTERN int TclpHasSockets _ANSI_ARGS_((Tcl_Interp * interp));
EXTERN struct tm * TclpGetDate _ANSI_ARGS_((TclpTime_t time, int useGMT));
/* 134 */
EXTERN size_t TclpStrftime _ANSI_ARGS_((char * s, size_t maxsize,
- CONST char * format, CONST struct tm * t));
+ CONST char * format, CONST struct tm * t,
+ int useGMT));
/* 135 */
EXTERN int TclpCheckStackSpace _ANSI_ARGS_((void));
/* Slot 136 is reserved */
-/* 137 */
-EXTERN int TclpChdir _ANSI_ARGS_((CONST char * dirName));
+/* Slot 137 is reserved */
/* 138 */
-EXTERN char * TclGetEnv _ANSI_ARGS_((CONST char * name,
+EXTERN CONST84_RETURN char * TclGetEnv _ANSI_ARGS_((CONST char * name,
Tcl_DString * valuePtr));
-/* 139 */
-EXTERN int TclpLoadFile _ANSI_ARGS_((Tcl_Interp * interp,
- char * fileName, char * sym1, char * sym2,
- Tcl_PackageInitProc ** proc1Ptr,
- Tcl_PackageInitProc ** proc2Ptr,
- ClientData * clientDataPtr));
+/* Slot 139 is reserved */
/* 140 */
-EXTERN int TclLooksLikeInt _ANSI_ARGS_((char * bytes,
+EXTERN int TclLooksLikeInt _ANSI_ARGS_((CONST char * bytes,
int length));
/* 141 */
-EXTERN char * TclpGetCwd _ANSI_ARGS_((Tcl_Interp * interp,
+EXTERN CONST84_RETURN char * TclpGetCwd _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_DString * cwdPtr));
/* 142 */
EXTERN int TclSetByteCodeFromAny _ANSI_ARGS_((
@@ -510,32 +456,58 @@ EXTERN Tcl_Obj * TclGetLibraryPath _ANSI_ARGS_((void));
/* Slot 155 is reserved */
/* 156 */
EXTERN void TclRegError _ANSI_ARGS_((Tcl_Interp * interp,
- char * msg, int status));
+ CONST char * msg, int status));
/* 157 */
EXTERN Var * TclVarTraceExists _ANSI_ARGS_((Tcl_Interp * interp,
- char * varName));
+ CONST char * varName));
/* 158 */
EXTERN void TclSetStartupScriptFileName _ANSI_ARGS_((
- char * filename));
+ CONST char * filename));
/* 159 */
-EXTERN char * TclGetStartupScriptFileName _ANSI_ARGS_((void));
-/* 160 */
-EXTERN int TclpMatchFilesTypes _ANSI_ARGS_((Tcl_Interp * interp,
- char * separators, Tcl_DString * dirPtr,
- char * pattern, char * tail,
- GlobTypeData * types));
+EXTERN CONST84_RETURN char * TclGetStartupScriptFileName _ANSI_ARGS_((void));
+/* Slot 160 is reserved */
/* 161 */
EXTERN int TclChannelTransform _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Channel chan, Tcl_Obj * cmdObjPtr));
/* 162 */
EXTERN void TclChannelEventScriptInvoker _ANSI_ARGS_((
ClientData clientData, int flags));
+/* 163 */
+EXTERN void * TclGetInstructionTable _ANSI_ARGS_((void));
+/* 164 */
+EXTERN void TclExpandCodeArray _ANSI_ARGS_((void * envPtr));
+/* 165 */
+EXTERN void TclpSetInitialEncodings _ANSI_ARGS_((void));
+/* 166 */
+EXTERN int TclListObjSetElement _ANSI_ARGS_((
+ Tcl_Interp * interp, Tcl_Obj * listPtr,
+ int index, Tcl_Obj * valuePtr));
+/* 167 */
+EXTERN void TclSetStartupScriptPath _ANSI_ARGS_((
+ Tcl_Obj * pathPtr));
+/* 168 */
+EXTERN Tcl_Obj * TclGetStartupScriptPath _ANSI_ARGS_((void));
+/* 169 */
+EXTERN int TclpUtfNcmp2 _ANSI_ARGS_((CONST char * s1,
+ CONST char * s2, unsigned long n));
+/* 170 */
+EXTERN int TclCheckInterpTraces _ANSI_ARGS_((
+ Tcl_Interp * interp, CONST char * command,
+ int numChars, Command * cmdPtr, int result,
+ int traceFlags, int objc,
+ Tcl_Obj *CONST objv[]));
+/* 171 */
+EXTERN int TclCheckExecutionTraces _ANSI_ARGS_((
+ Tcl_Interp * interp, CONST char * command,
+ int numChars, Command * cmdPtr, int result,
+ int traceFlags, int objc,
+ Tcl_Obj *CONST objv[]));
typedef struct TclIntStubs {
int magic;
struct TclIntStubHooks *hooks;
- int (*tclAccess) _ANSI_ARGS_((CONST char * path, int mode)); /* 0 */
+ void *reserved0;
int (*tclAccessDeleteProc) _ANSI_ARGS_((TclAccessProc_ * proc)); /* 1 */
int (*tclAccessInsertProc) _ANSI_ARGS_((TclAccessProc_ * proc)); /* 2 */
void (*tclAllocateFreeObjects) _ANSI_ARGS_((void)); /* 3 */
@@ -553,65 +525,65 @@ typedef struct TclIntStubs {
int (*tclCopyAndCollapse) _ANSI_ARGS_((int count, CONST char * src, char * dst)); /* 7 */
int (*tclCopyChannel) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel inChan, Tcl_Channel outChan, int toRead, Tcl_Obj * cmdPtr)); /* 8 */
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
- int (*tclCreatePipeline) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv, Tcl_Pid ** pidArrayPtr, TclFile * inPipePtr, TclFile * outPipePtr, TclFile * errFilePtr)); /* 9 */
+ int (*tclCreatePipeline) _ANSI_ARGS_((Tcl_Interp * interp, int argc, CONST char ** argv, Tcl_Pid ** pidArrayPtr, TclFile * inPipePtr, TclFile * outPipePtr, TclFile * errFilePtr)); /* 9 */
#endif /* UNIX */
#ifdef __WIN32__
- int (*tclCreatePipeline) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv, Tcl_Pid ** pidArrayPtr, TclFile * inPipePtr, TclFile * outPipePtr, TclFile * errFilePtr)); /* 9 */
+ int (*tclCreatePipeline) _ANSI_ARGS_((Tcl_Interp * interp, int argc, CONST char ** argv, Tcl_Pid ** pidArrayPtr, TclFile * inPipePtr, TclFile * outPipePtr, TclFile * errFilePtr)); /* 9 */
#endif /* __WIN32__ */
#ifdef MAC_TCL
void *reserved9;
#endif /* MAC_TCL */
- int (*tclCreateProc) _ANSI_ARGS_((Tcl_Interp * interp, Namespace * nsPtr, char * procName, Tcl_Obj * argsPtr, Tcl_Obj * bodyPtr, Proc ** procPtrPtr)); /* 10 */
+ int (*tclCreateProc) _ANSI_ARGS_((Tcl_Interp * interp, Namespace * nsPtr, CONST char * procName, Tcl_Obj * argsPtr, Tcl_Obj * bodyPtr, Proc ** procPtrPtr)); /* 10 */
void (*tclDeleteCompiledLocalVars) _ANSI_ARGS_((Interp * iPtr, CallFrame * framePtr)); /* 11 */
void (*tclDeleteVars) _ANSI_ARGS_((Interp * iPtr, Tcl_HashTable * tablePtr)); /* 12 */
- int (*tclDoGlob) _ANSI_ARGS_((Tcl_Interp * interp, char * separators, Tcl_DString * headPtr, char * tail, GlobTypeData * types)); /* 13 */
+ int (*tclDoGlob) _ANSI_ARGS_((Tcl_Interp * interp, char * separators, Tcl_DString * headPtr, char * tail, Tcl_GlobTypeData * types)); /* 13 */
void (*tclDumpMemoryInfo) _ANSI_ARGS_((FILE * outFile)); /* 14 */
void *reserved15;
void (*tclExprFloatError) _ANSI_ARGS_((Tcl_Interp * interp, double value)); /* 16 */
- int (*tclFileAttrsCmd) _ANSI_ARGS_((Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[])); /* 17 */
- int (*tclFileCopyCmd) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv)); /* 18 */
- int (*tclFileDeleteCmd) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv)); /* 19 */
- int (*tclFileMakeDirsCmd) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv)); /* 20 */
- int (*tclFileRenameCmd) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv)); /* 21 */
+ void *reserved17;
+ void *reserved18;
+ void *reserved19;
+ void *reserved20;
+ void *reserved21;
int (*tclFindElement) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * listStr, int listLength, CONST char ** elementPtr, CONST char ** nextPtr, int * sizePtr, int * bracePtr)); /* 22 */
- Proc * (*tclFindProc) _ANSI_ARGS_((Interp * iPtr, char * procName)); /* 23 */
+ Proc * (*tclFindProc) _ANSI_ARGS_((Interp * iPtr, CONST char * procName)); /* 23 */
int (*tclFormatInt) _ANSI_ARGS_((char * buffer, long n)); /* 24 */
void (*tclFreePackageInfo) _ANSI_ARGS_((Interp * iPtr)); /* 25 */
void *reserved26;
int (*tclGetDate) _ANSI_ARGS_((char * p, unsigned long now, long zone, unsigned long * timePtr)); /* 27 */
Tcl_Channel (*tclpGetDefaultStdChannel) _ANSI_ARGS_((int type)); /* 28 */
- Tcl_Obj * (*tclGetElementOfIndexedArray) _ANSI_ARGS_((Tcl_Interp * interp, int localIndex, Tcl_Obj * elemPtr, int leaveErrorMsg)); /* 29 */
+ void *reserved29;
void *reserved30;
char * (*tclGetExtension) _ANSI_ARGS_((char * name)); /* 31 */
- int (*tclGetFrame) _ANSI_ARGS_((Tcl_Interp * interp, char * str, CallFrame ** framePtrPtr)); /* 32 */
+ int (*tclGetFrame) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, CallFrame ** framePtrPtr)); /* 32 */
TclCmdProcType (*tclGetInterpProc) _ANSI_ARGS_((void)); /* 33 */
int (*tclGetIntForIndex) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, int endValue, int * indexPtr)); /* 34 */
- Tcl_Obj * (*tclGetIndexedScalar) _ANSI_ARGS_((Tcl_Interp * interp, int localIndex, int leaveErrorMsg)); /* 35 */
- int (*tclGetLong) _ANSI_ARGS_((Tcl_Interp * interp, char * str, long * longPtr)); /* 36 */
+ void *reserved35;
+ int (*tclGetLong) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, long * longPtr)); /* 36 */
int (*tclGetLoadedPackages) _ANSI_ARGS_((Tcl_Interp * interp, char * targetName)); /* 37 */
- int (*tclGetNamespaceForQualName) _ANSI_ARGS_((Tcl_Interp * interp, char * qualName, Namespace * cxtNsPtr, int flags, Namespace ** nsPtrPtr, Namespace ** altNsPtrPtr, Namespace ** actualCxtPtrPtr, char ** simpleNamePtr)); /* 38 */
+ int (*tclGetNamespaceForQualName) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * qualName, Namespace * cxtNsPtr, int flags, Namespace ** nsPtrPtr, Namespace ** altNsPtrPtr, Namespace ** actualCxtPtrPtr, CONST char ** simpleNamePtr)); /* 38 */
TclObjCmdProcType (*tclGetObjInterpProc) _ANSI_ARGS_((void)); /* 39 */
- int (*tclGetOpenMode) _ANSI_ARGS_((Tcl_Interp * interp, char * str, int * seekFlagPtr)); /* 40 */
+ int (*tclGetOpenMode) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * str, int * seekFlagPtr)); /* 40 */
Tcl_Command (*tclGetOriginalCommand) _ANSI_ARGS_((Tcl_Command command)); /* 41 */
char * (*tclpGetUserHome) _ANSI_ARGS_((CONST char * name, Tcl_DString * bufferPtr)); /* 42 */
- int (*tclGlobalInvoke) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv, int flags)); /* 43 */
- int (*tclGuessPackageName) _ANSI_ARGS_((char * fileName, Tcl_DString * bufPtr)); /* 44 */
+ int (*tclGlobalInvoke) _ANSI_ARGS_((Tcl_Interp * interp, int argc, CONST84 char ** argv, int flags)); /* 43 */
+ int (*tclGuessPackageName) _ANSI_ARGS_((CONST char * fileName, Tcl_DString * bufPtr)); /* 44 */
int (*tclHideUnsafeCommands) _ANSI_ARGS_((Tcl_Interp * interp)); /* 45 */
int (*tclInExit) _ANSI_ARGS_((void)); /* 46 */
- Tcl_Obj * (*tclIncrElementOfIndexedArray) _ANSI_ARGS_((Tcl_Interp * interp, int localIndex, Tcl_Obj * elemPtr, long incrAmount)); /* 47 */
- Tcl_Obj * (*tclIncrIndexedScalar) _ANSI_ARGS_((Tcl_Interp * interp, int localIndex, long incrAmount)); /* 48 */
+ void *reserved47;
+ void *reserved48;
Tcl_Obj * (*tclIncrVar2) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * part1Ptr, Tcl_Obj * part2Ptr, long incrAmount, int part1NotParsed)); /* 49 */
void (*tclInitCompiledLocals) _ANSI_ARGS_((Tcl_Interp * interp, CallFrame * framePtr, Namespace * nsPtr)); /* 50 */
int (*tclInterpInit) _ANSI_ARGS_((Tcl_Interp * interp)); /* 51 */
- int (*tclInvoke) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv, int flags)); /* 52 */
- int (*tclInvokeObjectCommand) _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp, int argc, char ** argv)); /* 53 */
+ int (*tclInvoke) _ANSI_ARGS_((Tcl_Interp * interp, int argc, CONST84 char ** argv, int flags)); /* 52 */
+ int (*tclInvokeObjectCommand) _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp, int argc, CONST84 char ** argv)); /* 53 */
int (*tclInvokeStringCommand) _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[])); /* 54 */
Proc * (*tclIsProc) _ANSI_ARGS_((Command * cmdPtr)); /* 55 */
void *reserved56;
void *reserved57;
- Var * (*tclLookupVar) _ANSI_ARGS_((Tcl_Interp * interp, char * part1, char * part2, int flags, char * msg, int createPart1, int createPart2, Var ** arrayPtrPtr)); /* 58 */
- int (*tclpMatchFiles) _ANSI_ARGS_((Tcl_Interp * interp, char * separators, Tcl_DString * dirPtr, char * pattern, char * tail)); /* 59 */
- int (*tclNeedSpace) _ANSI_ARGS_((char * start, char * end)); /* 60 */
+ Var * (*tclLookupVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * part1, CONST char * part2, int flags, CONST char * msg, int createPart1, int createPart2, Var ** arrayPtrPtr)); /* 58 */
+ void *reserved59;
+ int (*tclNeedSpace) _ANSI_ARGS_((CONST char * start, CONST char * end)); /* 60 */
Tcl_Obj * (*tclNewProcBodyObj) _ANSI_ARGS_((Proc * procPtr)); /* 61 */
int (*tclObjCommandComplete) _ANSI_ARGS_((Tcl_Obj * cmdPtr)); /* 62 */
int (*tclObjInterpProc) _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[])); /* 63 */
@@ -619,39 +591,39 @@ typedef struct TclIntStubs {
int (*tclObjInvokeGlobal) _ANSI_ARGS_((Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[], int flags)); /* 65 */
int (*tclOpenFileChannelDeleteProc) _ANSI_ARGS_((TclOpenFileChannelProc_ * proc)); /* 66 */
int (*tclOpenFileChannelInsertProc) _ANSI_ARGS_((TclOpenFileChannelProc_ * proc)); /* 67 */
- int (*tclpAccess) _ANSI_ARGS_((CONST char * path, int mode)); /* 68 */
+ void *reserved68;
char * (*tclpAlloc) _ANSI_ARGS_((unsigned int size)); /* 69 */
- int (*tclpCopyFile) _ANSI_ARGS_((CONST char * source, CONST char * dest)); /* 70 */
- int (*tclpCopyDirectory) _ANSI_ARGS_((CONST char * source, CONST char * dest, Tcl_DString * errorPtr)); /* 71 */
- int (*tclpCreateDirectory) _ANSI_ARGS_((CONST char * path)); /* 72 */
- int (*tclpDeleteFile) _ANSI_ARGS_((CONST char * path)); /* 73 */
+ void *reserved70;
+ void *reserved71;
+ void *reserved72;
+ void *reserved73;
void (*tclpFree) _ANSI_ARGS_((char * ptr)); /* 74 */
unsigned long (*tclpGetClicks) _ANSI_ARGS_((void)); /* 75 */
unsigned long (*tclpGetSeconds) _ANSI_ARGS_((void)); /* 76 */
void (*tclpGetTime) _ANSI_ARGS_((Tcl_Time * time)); /* 77 */
int (*tclpGetTimeZone) _ANSI_ARGS_((unsigned long time)); /* 78 */
- int (*tclpListVolumes) _ANSI_ARGS_((Tcl_Interp * interp)); /* 79 */
- Tcl_Channel (*tclpOpenFileChannel) _ANSI_ARGS_((Tcl_Interp * interp, char * fileName, char * modeString, int permissions)); /* 80 */
+ void *reserved79;
+ void *reserved80;
char * (*tclpRealloc) _ANSI_ARGS_((char * ptr, unsigned int size)); /* 81 */
- int (*tclpRemoveDirectory) _ANSI_ARGS_((CONST char * path, int recursive, Tcl_DString * errorPtr)); /* 82 */
- int (*tclpRenameFile) _ANSI_ARGS_((CONST char * source, CONST char * dest)); /* 83 */
+ void *reserved82;
+ void *reserved83;
void *reserved84;
void *reserved85;
void *reserved86;
void *reserved87;
- char * (*tclPrecTraceProc) _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp, char * name1, char * name2, int flags)); /* 88 */
+ char * (*tclPrecTraceProc) _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp, CONST char * name1, CONST char * name2, int flags)); /* 88 */
int (*tclPreventAliasLoop) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Interp * cmdInterp, Tcl_Command cmd)); /* 89 */
void *reserved90;
void (*tclProcCleanupProc) _ANSI_ARGS_((Proc * procPtr)); /* 91 */
int (*tclProcCompileProc) _ANSI_ARGS_((Tcl_Interp * interp, Proc * procPtr, Tcl_Obj * bodyPtr, Namespace * nsPtr, CONST char * description, CONST char * procName)); /* 92 */
void (*tclProcDeleteProc) _ANSI_ARGS_((ClientData clientData)); /* 93 */
- int (*tclProcInterpProc) _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp, int argc, char ** argv)); /* 94 */
- int (*tclpStat) _ANSI_ARGS_((CONST char * path, struct stat * buf)); /* 95 */
+ int (*tclProcInterpProc) _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp, int argc, CONST84 char ** argv)); /* 94 */
+ void *reserved95;
int (*tclRenameCommand) _ANSI_ARGS_((Tcl_Interp * interp, char * oldName, char * newName)); /* 96 */
void (*tclResetShadowedCmdRefs) _ANSI_ARGS_((Tcl_Interp * interp, Command * newCmdPtr)); /* 97 */
int (*tclServiceIdle) _ANSI_ARGS_((void)); /* 98 */
- Tcl_Obj * (*tclSetElementOfIndexedArray) _ANSI_ARGS_((Tcl_Interp * interp, int localIndex, Tcl_Obj * elemPtr, Tcl_Obj * objPtr, int leaveErrorMsg)); /* 99 */
- Tcl_Obj * (*tclSetIndexedScalar) _ANSI_ARGS_((Tcl_Interp * interp, int localIndex, Tcl_Obj * objPtr, int leaveErrorMsg)); /* 100 */
+ void *reserved99;
+ void *reserved100;
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
char * (*tclSetPreInitScript) _ANSI_ARGS_((char * string)); /* 101 */
#endif /* UNIX */
@@ -672,43 +644,43 @@ typedef struct TclIntStubs {
#ifdef MAC_TCL
void *reserved104;
#endif /* MAC_TCL */
- int (*tclStat) _ANSI_ARGS_((CONST char * path, struct stat * buf)); /* 105 */
+ void *reserved105;
int (*tclStatDeleteProc) _ANSI_ARGS_((TclStatProc_ * proc)); /* 106 */
int (*tclStatInsertProc) _ANSI_ARGS_((TclStatProc_ * proc)); /* 107 */
void (*tclTeardownNamespace) _ANSI_ARGS_((Namespace * nsPtr)); /* 108 */
int (*tclUpdateReturnInfo) _ANSI_ARGS_((Interp * iPtr)); /* 109 */
void *reserved110;
- void (*tcl_AddInterpResolvers) _ANSI_ARGS_((Tcl_Interp * interp, char * name, Tcl_ResolveCmdProc * cmdProc, Tcl_ResolveVarProc * varProc, Tcl_ResolveCompiledVarProc * compiledVarProc)); /* 111 */
+ void (*tcl_AddInterpResolvers) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, Tcl_ResolveCmdProc * cmdProc, Tcl_ResolveVarProc * varProc, Tcl_ResolveCompiledVarProc * compiledVarProc)); /* 111 */
int (*tcl_AppendExportList) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Namespace * nsPtr, Tcl_Obj * objPtr)); /* 112 */
- Tcl_Namespace * (*tcl_CreateNamespace) _ANSI_ARGS_((Tcl_Interp * interp, char * name, ClientData clientData, Tcl_NamespaceDeleteProc * deleteProc)); /* 113 */
+ Tcl_Namespace * (*tcl_CreateNamespace) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, ClientData clientData, Tcl_NamespaceDeleteProc * deleteProc)); /* 113 */
void (*tcl_DeleteNamespace) _ANSI_ARGS_((Tcl_Namespace * nsPtr)); /* 114 */
- int (*tcl_Export) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Namespace * nsPtr, char * pattern, int resetListFirst)); /* 115 */
- Tcl_Command (*tcl_FindCommand) _ANSI_ARGS_((Tcl_Interp * interp, char * name, Tcl_Namespace * contextNsPtr, int flags)); /* 116 */
- Tcl_Namespace * (*tcl_FindNamespace) _ANSI_ARGS_((Tcl_Interp * interp, char * name, Tcl_Namespace * contextNsPtr, int flags)); /* 117 */
- int (*tcl_GetInterpResolvers) _ANSI_ARGS_((Tcl_Interp * interp, char * name, Tcl_ResolverInfo * resInfo)); /* 118 */
+ int (*tcl_Export) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Namespace * nsPtr, CONST char * pattern, int resetListFirst)); /* 115 */
+ Tcl_Command (*tcl_FindCommand) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, Tcl_Namespace * contextNsPtr, int flags)); /* 116 */
+ Tcl_Namespace * (*tcl_FindNamespace) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, Tcl_Namespace * contextNsPtr, int flags)); /* 117 */
+ int (*tcl_GetInterpResolvers) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, Tcl_ResolverInfo * resInfo)); /* 118 */
int (*tcl_GetNamespaceResolvers) _ANSI_ARGS_((Tcl_Namespace * namespacePtr, Tcl_ResolverInfo * resInfo)); /* 119 */
- Tcl_Var (*tcl_FindNamespaceVar) _ANSI_ARGS_((Tcl_Interp * interp, char * name, Tcl_Namespace * contextNsPtr, int flags)); /* 120 */
- int (*tcl_ForgetImport) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Namespace * nsPtr, char * pattern)); /* 121 */
+ Tcl_Var (*tcl_FindNamespaceVar) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name, Tcl_Namespace * contextNsPtr, int flags)); /* 120 */
+ int (*tcl_ForgetImport) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Namespace * nsPtr, CONST char * pattern)); /* 121 */
Tcl_Command (*tcl_GetCommandFromObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr)); /* 122 */
void (*tcl_GetCommandFullName) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Command command, Tcl_Obj * objPtr)); /* 123 */
Tcl_Namespace * (*tcl_GetCurrentNamespace) _ANSI_ARGS_((Tcl_Interp * interp)); /* 124 */
Tcl_Namespace * (*tcl_GetGlobalNamespace) _ANSI_ARGS_((Tcl_Interp * interp)); /* 125 */
void (*tcl_GetVariableFullName) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Var variable, Tcl_Obj * objPtr)); /* 126 */
- int (*tcl_Import) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Namespace * nsPtr, char * pattern, int allowOverwrite)); /* 127 */
+ int (*tcl_Import) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Namespace * nsPtr, CONST char * pattern, int allowOverwrite)); /* 127 */
void (*tcl_PopCallFrame) _ANSI_ARGS_((Tcl_Interp* interp)); /* 128 */
int (*tcl_PushCallFrame) _ANSI_ARGS_((Tcl_Interp* interp, Tcl_CallFrame * framePtr, Tcl_Namespace * nsPtr, int isProcCallFrame)); /* 129 */
- int (*tcl_RemoveInterpResolvers) _ANSI_ARGS_((Tcl_Interp * interp, char * name)); /* 130 */
+ int (*tcl_RemoveInterpResolvers) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * name)); /* 130 */
void (*tcl_SetNamespaceResolvers) _ANSI_ARGS_((Tcl_Namespace * namespacePtr, Tcl_ResolveCmdProc * cmdProc, Tcl_ResolveVarProc * varProc, Tcl_ResolveCompiledVarProc * compiledVarProc)); /* 131 */
int (*tclpHasSockets) _ANSI_ARGS_((Tcl_Interp * interp)); /* 132 */
struct tm * (*tclpGetDate) _ANSI_ARGS_((TclpTime_t time, int useGMT)); /* 133 */
- size_t (*tclpStrftime) _ANSI_ARGS_((char * s, size_t maxsize, CONST char * format, CONST struct tm * t)); /* 134 */
+ size_t (*tclpStrftime) _ANSI_ARGS_((char * s, size_t maxsize, CONST char * format, CONST struct tm * t, int useGMT)); /* 134 */
int (*tclpCheckStackSpace) _ANSI_ARGS_((void)); /* 135 */
void *reserved136;
- int (*tclpChdir) _ANSI_ARGS_((CONST char * dirName)); /* 137 */
- char * (*tclGetEnv) _ANSI_ARGS_((CONST char * name, Tcl_DString * valuePtr)); /* 138 */
- int (*tclpLoadFile) _ANSI_ARGS_((Tcl_Interp * interp, char * fileName, char * sym1, char * sym2, Tcl_PackageInitProc ** proc1Ptr, Tcl_PackageInitProc ** proc2Ptr, ClientData * clientDataPtr)); /* 139 */
- int (*tclLooksLikeInt) _ANSI_ARGS_((char * bytes, int length)); /* 140 */
- char * (*tclpGetCwd) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_DString * cwdPtr)); /* 141 */
+ void *reserved137;
+ CONST84_RETURN char * (*tclGetEnv) _ANSI_ARGS_((CONST char * name, Tcl_DString * valuePtr)); /* 138 */
+ void *reserved139;
+ int (*tclLooksLikeInt) _ANSI_ARGS_((CONST char * bytes, int length)); /* 140 */
+ CONST84_RETURN char * (*tclpGetCwd) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_DString * cwdPtr)); /* 141 */
int (*tclSetByteCodeFromAny) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, CompileHookProc * hookProc, ClientData clientData)); /* 142 */
int (*tclAddLiteralObj) _ANSI_ARGS_((struct CompileEnv * envPtr, Tcl_Obj * objPtr, LiteralEntry ** litPtrPtr)); /* 143 */
void (*tclHideLiteral) _ANSI_ARGS_((Tcl_Interp * interp, struct CompileEnv * envPtr, int index)); /* 144 */
@@ -723,13 +695,22 @@ typedef struct TclIntStubs {
Tcl_Obj * (*tclGetLibraryPath) _ANSI_ARGS_((void)); /* 153 */
void *reserved154;
void *reserved155;
- void (*tclRegError) _ANSI_ARGS_((Tcl_Interp * interp, char * msg, int status)); /* 156 */
- Var * (*tclVarTraceExists) _ANSI_ARGS_((Tcl_Interp * interp, char * varName)); /* 157 */
- void (*tclSetStartupScriptFileName) _ANSI_ARGS_((char * filename)); /* 158 */
- char * (*tclGetStartupScriptFileName) _ANSI_ARGS_((void)); /* 159 */
- int (*tclpMatchFilesTypes) _ANSI_ARGS_((Tcl_Interp * interp, char * separators, Tcl_DString * dirPtr, char * pattern, char * tail, GlobTypeData * types)); /* 160 */
+ void (*tclRegError) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * msg, int status)); /* 156 */
+ Var * (*tclVarTraceExists) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * varName)); /* 157 */
+ void (*tclSetStartupScriptFileName) _ANSI_ARGS_((CONST char * filename)); /* 158 */
+ CONST84_RETURN char * (*tclGetStartupScriptFileName) _ANSI_ARGS_((void)); /* 159 */
+ void *reserved160;
int (*tclChannelTransform) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel chan, Tcl_Obj * cmdObjPtr)); /* 161 */
void (*tclChannelEventScriptInvoker) _ANSI_ARGS_((ClientData clientData, int flags)); /* 162 */
+ void * (*tclGetInstructionTable) _ANSI_ARGS_((void)); /* 163 */
+ void (*tclExpandCodeArray) _ANSI_ARGS_((void * envPtr)); /* 164 */
+ void (*tclpSetInitialEncodings) _ANSI_ARGS_((void)); /* 165 */
+ int (*tclListObjSetElement) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * listPtr, int index, Tcl_Obj * valuePtr)); /* 166 */
+ void (*tclSetStartupScriptPath) _ANSI_ARGS_((Tcl_Obj * pathPtr)); /* 167 */
+ Tcl_Obj * (*tclGetStartupScriptPath) _ANSI_ARGS_((void)); /* 168 */
+ int (*tclpUtfNcmp2) _ANSI_ARGS_((CONST char * s1, CONST char * s2, unsigned long n)); /* 169 */
+ int (*tclCheckInterpTraces) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * command, int numChars, Command * cmdPtr, int result, int traceFlags, int objc, Tcl_Obj *CONST objv[])); /* 170 */
+ int (*tclCheckExecutionTraces) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * command, int numChars, Command * cmdPtr, int result, int traceFlags, int objc, Tcl_Obj *CONST objv[])); /* 171 */
} TclIntStubs;
#ifdef __cplusplus
@@ -746,10 +727,7 @@ extern TclIntStubs *tclIntStubsPtr;
* Inline function declarations:
*/
-#ifndef TclAccess
-#define TclAccess \
- (tclIntStubsPtr->tclAccess) /* 0 */
-#endif
+/* Slot 0 is reserved */
#ifndef TclAccessDeleteProc
#define TclAccessDeleteProc \
(tclIntStubsPtr->tclAccessDeleteProc) /* 1 */
@@ -824,26 +802,11 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclExprFloatError \
(tclIntStubsPtr->tclExprFloatError) /* 16 */
#endif
-#ifndef TclFileAttrsCmd
-#define TclFileAttrsCmd \
- (tclIntStubsPtr->tclFileAttrsCmd) /* 17 */
-#endif
-#ifndef TclFileCopyCmd
-#define TclFileCopyCmd \
- (tclIntStubsPtr->tclFileCopyCmd) /* 18 */
-#endif
-#ifndef TclFileDeleteCmd
-#define TclFileDeleteCmd \
- (tclIntStubsPtr->tclFileDeleteCmd) /* 19 */
-#endif
-#ifndef TclFileMakeDirsCmd
-#define TclFileMakeDirsCmd \
- (tclIntStubsPtr->tclFileMakeDirsCmd) /* 20 */
-#endif
-#ifndef TclFileRenameCmd
-#define TclFileRenameCmd \
- (tclIntStubsPtr->tclFileRenameCmd) /* 21 */
-#endif
+/* Slot 17 is reserved */
+/* Slot 18 is reserved */
+/* Slot 19 is reserved */
+/* Slot 20 is reserved */
+/* Slot 21 is reserved */
#ifndef TclFindElement
#define TclFindElement \
(tclIntStubsPtr->tclFindElement) /* 22 */
@@ -869,10 +832,7 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclpGetDefaultStdChannel \
(tclIntStubsPtr->tclpGetDefaultStdChannel) /* 28 */
#endif
-#ifndef TclGetElementOfIndexedArray
-#define TclGetElementOfIndexedArray \
- (tclIntStubsPtr->tclGetElementOfIndexedArray) /* 29 */
-#endif
+/* Slot 29 is reserved */
/* Slot 30 is reserved */
#ifndef TclGetExtension
#define TclGetExtension \
@@ -890,10 +850,7 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclGetIntForIndex \
(tclIntStubsPtr->tclGetIntForIndex) /* 34 */
#endif
-#ifndef TclGetIndexedScalar
-#define TclGetIndexedScalar \
- (tclIntStubsPtr->tclGetIndexedScalar) /* 35 */
-#endif
+/* Slot 35 is reserved */
#ifndef TclGetLong
#define TclGetLong \
(tclIntStubsPtr->tclGetLong) /* 36 */
@@ -938,14 +895,8 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclInExit \
(tclIntStubsPtr->tclInExit) /* 46 */
#endif
-#ifndef TclIncrElementOfIndexedArray
-#define TclIncrElementOfIndexedArray \
- (tclIntStubsPtr->tclIncrElementOfIndexedArray) /* 47 */
-#endif
-#ifndef TclIncrIndexedScalar
-#define TclIncrIndexedScalar \
- (tclIntStubsPtr->tclIncrIndexedScalar) /* 48 */
-#endif
+/* Slot 47 is reserved */
+/* Slot 48 is reserved */
#ifndef TclIncrVar2
#define TclIncrVar2 \
(tclIntStubsPtr->tclIncrVar2) /* 49 */
@@ -980,10 +931,7 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclLookupVar \
(tclIntStubsPtr->tclLookupVar) /* 58 */
#endif
-#ifndef TclpMatchFiles
-#define TclpMatchFiles \
- (tclIntStubsPtr->tclpMatchFiles) /* 59 */
-#endif
+/* Slot 59 is reserved */
#ifndef TclNeedSpace
#define TclNeedSpace \
(tclIntStubsPtr->tclNeedSpace) /* 60 */
@@ -1016,30 +964,15 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclOpenFileChannelInsertProc \
(tclIntStubsPtr->tclOpenFileChannelInsertProc) /* 67 */
#endif
-#ifndef TclpAccess
-#define TclpAccess \
- (tclIntStubsPtr->tclpAccess) /* 68 */
-#endif
+/* Slot 68 is reserved */
#ifndef TclpAlloc
#define TclpAlloc \
(tclIntStubsPtr->tclpAlloc) /* 69 */
#endif
-#ifndef TclpCopyFile
-#define TclpCopyFile \
- (tclIntStubsPtr->tclpCopyFile) /* 70 */
-#endif
-#ifndef TclpCopyDirectory
-#define TclpCopyDirectory \
- (tclIntStubsPtr->tclpCopyDirectory) /* 71 */
-#endif
-#ifndef TclpCreateDirectory
-#define TclpCreateDirectory \
- (tclIntStubsPtr->tclpCreateDirectory) /* 72 */
-#endif
-#ifndef TclpDeleteFile
-#define TclpDeleteFile \
- (tclIntStubsPtr->tclpDeleteFile) /* 73 */
-#endif
+/* Slot 70 is reserved */
+/* Slot 71 is reserved */
+/* Slot 72 is reserved */
+/* Slot 73 is reserved */
#ifndef TclpFree
#define TclpFree \
(tclIntStubsPtr->tclpFree) /* 74 */
@@ -1060,26 +993,14 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclpGetTimeZone \
(tclIntStubsPtr->tclpGetTimeZone) /* 78 */
#endif
-#ifndef TclpListVolumes
-#define TclpListVolumes \
- (tclIntStubsPtr->tclpListVolumes) /* 79 */
-#endif
-#ifndef TclpOpenFileChannel
-#define TclpOpenFileChannel \
- (tclIntStubsPtr->tclpOpenFileChannel) /* 80 */
-#endif
+/* Slot 79 is reserved */
+/* Slot 80 is reserved */
#ifndef TclpRealloc
#define TclpRealloc \
(tclIntStubsPtr->tclpRealloc) /* 81 */
#endif
-#ifndef TclpRemoveDirectory
-#define TclpRemoveDirectory \
- (tclIntStubsPtr->tclpRemoveDirectory) /* 82 */
-#endif
-#ifndef TclpRenameFile
-#define TclpRenameFile \
- (tclIntStubsPtr->tclpRenameFile) /* 83 */
-#endif
+/* Slot 82 is reserved */
+/* Slot 83 is reserved */
/* Slot 84 is reserved */
/* Slot 85 is reserved */
/* Slot 86 is reserved */
@@ -1109,10 +1030,7 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclProcInterpProc \
(tclIntStubsPtr->tclProcInterpProc) /* 94 */
#endif
-#ifndef TclpStat
-#define TclpStat \
- (tclIntStubsPtr->tclpStat) /* 95 */
-#endif
+/* Slot 95 is reserved */
#ifndef TclRenameCommand
#define TclRenameCommand \
(tclIntStubsPtr->tclRenameCommand) /* 96 */
@@ -1125,14 +1043,8 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclServiceIdle \
(tclIntStubsPtr->tclServiceIdle) /* 98 */
#endif
-#ifndef TclSetElementOfIndexedArray
-#define TclSetElementOfIndexedArray \
- (tclIntStubsPtr->tclSetElementOfIndexedArray) /* 99 */
-#endif
-#ifndef TclSetIndexedScalar
-#define TclSetIndexedScalar \
- (tclIntStubsPtr->tclSetIndexedScalar) /* 100 */
-#endif
+/* Slot 99 is reserved */
+/* Slot 100 is reserved */
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
#ifndef TclSetPreInitScript
#define TclSetPreInitScript \
@@ -1165,10 +1077,7 @@ extern TclIntStubs *tclIntStubsPtr;
(tclIntStubsPtr->tclSockMinimumBuffers) /* 104 */
#endif
#endif /* __WIN32__ */
-#ifndef TclStat
-#define TclStat \
- (tclIntStubsPtr->tclStat) /* 105 */
-#endif
+/* Slot 105 is reserved */
#ifndef TclStatDeleteProc
#define TclStatDeleteProc \
(tclIntStubsPtr->tclStatDeleteProc) /* 106 */
@@ -1287,18 +1196,12 @@ extern TclIntStubs *tclIntStubsPtr;
(tclIntStubsPtr->tclpCheckStackSpace) /* 135 */
#endif
/* Slot 136 is reserved */
-#ifndef TclpChdir
-#define TclpChdir \
- (tclIntStubsPtr->tclpChdir) /* 137 */
-#endif
+/* Slot 137 is reserved */
#ifndef TclGetEnv
#define TclGetEnv \
(tclIntStubsPtr->tclGetEnv) /* 138 */
#endif
-#ifndef TclpLoadFile
-#define TclpLoadFile \
- (tclIntStubsPtr->tclpLoadFile) /* 139 */
-#endif
+/* Slot 139 is reserved */
#ifndef TclLooksLikeInt
#define TclLooksLikeInt \
(tclIntStubsPtr->tclLooksLikeInt) /* 140 */
@@ -1373,10 +1276,7 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclGetStartupScriptFileName \
(tclIntStubsPtr->tclGetStartupScriptFileName) /* 159 */
#endif
-#ifndef TclpMatchFilesTypes
-#define TclpMatchFilesTypes \
- (tclIntStubsPtr->tclpMatchFilesTypes) /* 160 */
-#endif
+/* Slot 160 is reserved */
#ifndef TclChannelTransform
#define TclChannelTransform \
(tclIntStubsPtr->tclChannelTransform) /* 161 */
@@ -1385,10 +1285,45 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclChannelEventScriptInvoker \
(tclIntStubsPtr->tclChannelEventScriptInvoker) /* 162 */
#endif
+#ifndef TclGetInstructionTable
+#define TclGetInstructionTable \
+ (tclIntStubsPtr->tclGetInstructionTable) /* 163 */
+#endif
+#ifndef TclExpandCodeArray
+#define TclExpandCodeArray \
+ (tclIntStubsPtr->tclExpandCodeArray) /* 164 */
+#endif
+#ifndef TclpSetInitialEncodings
+#define TclpSetInitialEncodings \
+ (tclIntStubsPtr->tclpSetInitialEncodings) /* 165 */
+#endif
+#ifndef TclListObjSetElement
+#define TclListObjSetElement \
+ (tclIntStubsPtr->tclListObjSetElement) /* 166 */
+#endif
+#ifndef TclSetStartupScriptPath
+#define TclSetStartupScriptPath \
+ (tclIntStubsPtr->tclSetStartupScriptPath) /* 167 */
+#endif
+#ifndef TclGetStartupScriptPath
+#define TclGetStartupScriptPath \
+ (tclIntStubsPtr->tclGetStartupScriptPath) /* 168 */
+#endif
+#ifndef TclpUtfNcmp2
+#define TclpUtfNcmp2 \
+ (tclIntStubsPtr->tclpUtfNcmp2) /* 169 */
+#endif
+#ifndef TclCheckInterpTraces
+#define TclCheckInterpTraces \
+ (tclIntStubsPtr->tclCheckInterpTraces) /* 170 */
+#endif
+#ifndef TclCheckExecutionTraces
+#define TclCheckExecutionTraces \
+ (tclIntStubsPtr->tclCheckExecutionTraces) /* 171 */
+#endif
#endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */
/* !END!: Do not edit above this line. */
#endif /* _TCLINTDECLS */
-
diff --git a/tcl/generic/tclIntPlatDecls.h b/tcl/generic/tclIntPlatDecls.h
index b985bb0987e..fb6f7d1c90c 100644
--- a/tcl/generic/tclIntPlatDecls.h
+++ b/tcl/generic/tclIntPlatDecls.h
@@ -43,9 +43,9 @@ EXTERN int TclpCreatePipe _ANSI_ARGS_((TclFile * readPipe,
TclFile * writePipe));
/* 4 */
EXTERN int TclpCreateProcess _ANSI_ARGS_((Tcl_Interp * interp,
- int argc, char ** argv, TclFile inputFile,
- TclFile outputFile, TclFile errorFile,
- Tcl_Pid * pidPtr));
+ int argc, CONST char ** argv,
+ TclFile inputFile, TclFile outputFile,
+ TclFile errorFile, Tcl_Pid * pidPtr));
/* Slot 5 is reserved */
/* 6 */
EXTERN TclFile TclpMakeFile _ANSI_ARGS_((Tcl_Channel channel,
@@ -59,6 +59,14 @@ EXTERN int TclUnixWaitForFile _ANSI_ARGS_((int fd, int mask,
/* 9 */
EXTERN TclFile TclpCreateTempFile _ANSI_ARGS_((
CONST char * contents));
+/* 10 */
+EXTERN Tcl_DirEntry * TclpReaddir _ANSI_ARGS_((DIR * dir));
+/* 11 */
+EXTERN struct tm * TclpLocaltime _ANSI_ARGS_((time_t * clock));
+/* 12 */
+EXTERN struct tm * TclpGmtime _ANSI_ARGS_((time_t * clock));
+/* 13 */
+EXTERN char * TclpInetNtoa _ANSI_ARGS_((struct in_addr addr));
#endif /* UNIX */
#ifdef __WIN32__
/* 0 */
@@ -101,9 +109,9 @@ EXTERN int TclpCreatePipe _ANSI_ARGS_((TclFile * readPipe,
TclFile * writePipe));
/* 15 */
EXTERN int TclpCreateProcess _ANSI_ARGS_((Tcl_Interp * interp,
- int argc, char ** argv, TclFile inputFile,
- TclFile outputFile, TclFile errorFile,
- Tcl_Pid * pidPtr));
+ int argc, CONST char ** argv,
+ TclFile inputFile, TclFile outputFile,
+ TclFile errorFile, Tcl_Pid * pidPtr));
/* Slot 16 is reserved */
/* Slot 17 is reserved */
/* 18 */
@@ -115,8 +123,7 @@ EXTERN TclFile TclpOpenFile _ANSI_ARGS_((CONST char * fname,
/* 20 */
EXTERN void TclWinAddProcess _ANSI_ARGS_((HANDLE hProcess,
DWORD id));
-/* 21 */
-EXTERN void TclpAsyncMark _ANSI_ARGS_((Tcl_AsyncHandler async));
+/* Slot 21 is reserved */
/* 22 */
EXTERN TclFile TclpCreateTempFile _ANSI_ARGS_((
CONST char * contents));
@@ -128,6 +135,8 @@ EXTERN char * TclWinNoBackslash _ANSI_ARGS_((char * path));
EXTERN TclPlatformType * TclWinGetPlatform _ANSI_ARGS_((void));
/* 26 */
EXTERN void TclWinSetInterfaces _ANSI_ARGS_((int wide));
+/* 27 */
+EXTERN void TclWinFlushDirtyChannels _ANSI_ARGS_((void));
#endif /* __WIN32__ */
#ifdef MAC_TCL
/* 0 */
@@ -148,15 +157,16 @@ EXTERN OSErr FSpFindFolder _ANSI_ARGS_((short vRefNum,
OSType folderType, Boolean createFolder,
FSSpec * spec));
/* 7 */
-EXTERN void GetGlobalMouse _ANSI_ARGS_((Point * mouse));
+EXTERN void GetGlobalMouseTcl _ANSI_ARGS_((Point * mouse));
/* 8 */
-EXTERN pascal OSErr FSpGetDirectoryID _ANSI_ARGS_((CONST FSSpec * spec,
- long * theDirID, Boolean * isDirectory));
+EXTERN pascal OSErr FSpGetDirectoryIDTcl _ANSI_ARGS_((
+ CONST FSSpec * spec, long * theDirID,
+ Boolean * isDirectory));
/* 9 */
-EXTERN pascal short FSpOpenResFileCompat _ANSI_ARGS_((
+EXTERN pascal short FSpOpenResFileCompatTcl _ANSI_ARGS_((
CONST FSSpec * spec, SignedByte permission));
/* 10 */
-EXTERN pascal void FSpCreateResFileCompat _ANSI_ARGS_((
+EXTERN pascal void FSpCreateResFileCompatTcl _ANSI_ARGS_((
CONST FSSpec * spec, OSType creator,
OSType fileType, ScriptCode scriptTag));
/* 11 */
@@ -192,9 +202,13 @@ EXTERN int TclMacCreateEnv _ANSI_ARGS_((void));
/* 23 */
EXTERN FILE * TclMacFOpenHack _ANSI_ARGS_((CONST char * path,
CONST char * mode));
-/* Slot 24 is reserved */
+/* 24 */
+EXTERN char * TclpGetTZName _ANSI_ARGS_((int isdst));
/* 25 */
-EXTERN int TclMacChmod _ANSI_ARGS_((char * path, int mode));
+EXTERN int TclMacChmod _ANSI_ARGS_((CONST char * path, int mode));
+/* 26 */
+EXTERN int FSpLLocationFromPath _ANSI_ARGS_((int length,
+ CONST char * path, FSSpecPtr theSpec));
#endif /* MAC_TCL */
typedef struct TclIntPlatStubs {
@@ -206,12 +220,16 @@ typedef struct TclIntPlatStubs {
int (*tclpCloseFile) _ANSI_ARGS_((TclFile file)); /* 1 */
Tcl_Channel (*tclpCreateCommandChannel) _ANSI_ARGS_((TclFile readFile, TclFile writeFile, TclFile errorFile, int numPids, Tcl_Pid * pidPtr)); /* 2 */
int (*tclpCreatePipe) _ANSI_ARGS_((TclFile * readPipe, TclFile * writePipe)); /* 3 */
- int (*tclpCreateProcess) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv, TclFile inputFile, TclFile outputFile, TclFile errorFile, Tcl_Pid * pidPtr)); /* 4 */
+ int (*tclpCreateProcess) _ANSI_ARGS_((Tcl_Interp * interp, int argc, CONST char ** argv, TclFile inputFile, TclFile outputFile, TclFile errorFile, Tcl_Pid * pidPtr)); /* 4 */
void *reserved5;
TclFile (*tclpMakeFile) _ANSI_ARGS_((Tcl_Channel channel, int direction)); /* 6 */
TclFile (*tclpOpenFile) _ANSI_ARGS_((CONST char * fname, int mode)); /* 7 */
int (*tclUnixWaitForFile) _ANSI_ARGS_((int fd, int mask, int timeout)); /* 8 */
TclFile (*tclpCreateTempFile) _ANSI_ARGS_((CONST char * contents)); /* 9 */
+ Tcl_DirEntry * (*tclpReaddir) _ANSI_ARGS_((DIR * dir)); /* 10 */
+ struct tm * (*tclpLocaltime) _ANSI_ARGS_((time_t * clock)); /* 11 */
+ struct tm * (*tclpGmtime) _ANSI_ARGS_((time_t * clock)); /* 12 */
+ char * (*tclpInetNtoa) _ANSI_ARGS_((struct in_addr addr)); /* 13 */
#endif /* UNIX */
#ifdef __WIN32__
void (*tclWinConvertError) _ANSI_ARGS_((DWORD errCode)); /* 0 */
@@ -229,18 +247,19 @@ typedef struct TclIntPlatStubs {
int (*tclpCloseFile) _ANSI_ARGS_((TclFile file)); /* 12 */
Tcl_Channel (*tclpCreateCommandChannel) _ANSI_ARGS_((TclFile readFile, TclFile writeFile, TclFile errorFile, int numPids, Tcl_Pid * pidPtr)); /* 13 */
int (*tclpCreatePipe) _ANSI_ARGS_((TclFile * readPipe, TclFile * writePipe)); /* 14 */
- int (*tclpCreateProcess) _ANSI_ARGS_((Tcl_Interp * interp, int argc, char ** argv, TclFile inputFile, TclFile outputFile, TclFile errorFile, Tcl_Pid * pidPtr)); /* 15 */
+ int (*tclpCreateProcess) _ANSI_ARGS_((Tcl_Interp * interp, int argc, CONST char ** argv, TclFile inputFile, TclFile outputFile, TclFile errorFile, Tcl_Pid * pidPtr)); /* 15 */
void *reserved16;
void *reserved17;
TclFile (*tclpMakeFile) _ANSI_ARGS_((Tcl_Channel channel, int direction)); /* 18 */
TclFile (*tclpOpenFile) _ANSI_ARGS_((CONST char * fname, int mode)); /* 19 */
void (*tclWinAddProcess) _ANSI_ARGS_((HANDLE hProcess, DWORD id)); /* 20 */
- void (*tclpAsyncMark) _ANSI_ARGS_((Tcl_AsyncHandler async)); /* 21 */
+ void *reserved21;
TclFile (*tclpCreateTempFile) _ANSI_ARGS_((CONST char * contents)); /* 22 */
char * (*tclpGetTZName) _ANSI_ARGS_((int isdst)); /* 23 */
char * (*tclWinNoBackslash) _ANSI_ARGS_((char * path)); /* 24 */
TclPlatformType * (*tclWinGetPlatform) _ANSI_ARGS_((void)); /* 25 */
void (*tclWinSetInterfaces) _ANSI_ARGS_((int wide)); /* 26 */
+ void (*tclWinFlushDirtyChannels) _ANSI_ARGS_((void)); /* 27 */
#endif /* __WIN32__ */
#ifdef MAC_TCL
VOID * (*tclpSysAlloc) _ANSI_ARGS_((long size, int isBin)); /* 0 */
@@ -250,10 +269,10 @@ typedef struct TclIntPlatStubs {
int (*fSpGetDefaultDir) _ANSI_ARGS_((FSSpecPtr theSpec)); /* 4 */
int (*fSpSetDefaultDir) _ANSI_ARGS_((FSSpecPtr theSpec)); /* 5 */
OSErr (*fSpFindFolder) _ANSI_ARGS_((short vRefNum, OSType folderType, Boolean createFolder, FSSpec * spec)); /* 6 */
- void (*getGlobalMouse) _ANSI_ARGS_((Point * mouse)); /* 7 */
- pascal OSErr (*fSpGetDirectoryID) _ANSI_ARGS_((CONST FSSpec * spec, long * theDirID, Boolean * isDirectory)); /* 8 */
- pascal short (*fSpOpenResFileCompat) _ANSI_ARGS_((CONST FSSpec * spec, SignedByte permission)); /* 9 */
- pascal void (*fSpCreateResFileCompat) _ANSI_ARGS_((CONST FSSpec * spec, OSType creator, OSType fileType, ScriptCode scriptTag)); /* 10 */
+ void (*getGlobalMouseTcl) _ANSI_ARGS_((Point * mouse)); /* 7 */
+ pascal OSErr (*fSpGetDirectoryIDTcl) _ANSI_ARGS_((CONST FSSpec * spec, long * theDirID, Boolean * isDirectory)); /* 8 */
+ pascal short (*fSpOpenResFileCompatTcl) _ANSI_ARGS_((CONST FSSpec * spec, SignedByte permission)); /* 9 */
+ pascal void (*fSpCreateResFileCompatTcl) _ANSI_ARGS_((CONST FSSpec * spec, OSType creator, OSType fileType, ScriptCode scriptTag)); /* 10 */
int (*fSpLocationFromPath) _ANSI_ARGS_((int length, CONST char * path, FSSpecPtr theSpec)); /* 11 */
OSErr (*fSpPathFromLocation) _ANSI_ARGS_((FSSpecPtr theSpec, int * length, Handle * fullPath)); /* 12 */
void (*tclMacExitHandler) _ANSI_ARGS_((void)); /* 13 */
@@ -267,8 +286,9 @@ typedef struct TclIntPlatStubs {
short (*tclMacUnRegisterResourceFork) _ANSI_ARGS_((char * tokenPtr, Tcl_Obj * resultPtr)); /* 21 */
int (*tclMacCreateEnv) _ANSI_ARGS_((void)); /* 22 */
FILE * (*tclMacFOpenHack) _ANSI_ARGS_((CONST char * path, CONST char * mode)); /* 23 */
- void *reserved24;
- int (*tclMacChmod) _ANSI_ARGS_((char * path, int mode)); /* 25 */
+ char * (*tclpGetTZName) _ANSI_ARGS_((int isdst)); /* 24 */
+ int (*tclMacChmod) _ANSI_ARGS_((CONST char * path, int mode)); /* 25 */
+ int (*fSpLLocationFromPath) _ANSI_ARGS_((int length, CONST char * path, FSSpecPtr theSpec)); /* 26 */
#endif /* MAC_TCL */
} TclIntPlatStubs;
@@ -324,6 +344,22 @@ extern TclIntPlatStubs *tclIntPlatStubsPtr;
#define TclpCreateTempFile \
(tclIntPlatStubsPtr->tclpCreateTempFile) /* 9 */
#endif
+#ifndef TclpReaddir
+#define TclpReaddir \
+ (tclIntPlatStubsPtr->tclpReaddir) /* 10 */
+#endif
+#ifndef TclpLocaltime
+#define TclpLocaltime \
+ (tclIntPlatStubsPtr->tclpLocaltime) /* 11 */
+#endif
+#ifndef TclpGmtime
+#define TclpGmtime \
+ (tclIntPlatStubsPtr->tclpGmtime) /* 12 */
+#endif
+#ifndef TclpInetNtoa
+#define TclpInetNtoa \
+ (tclIntPlatStubsPtr->tclpInetNtoa) /* 13 */
+#endif
#endif /* UNIX */
#ifdef __WIN32__
#ifndef TclWinConvertError
@@ -398,10 +434,7 @@ extern TclIntPlatStubs *tclIntPlatStubsPtr;
#define TclWinAddProcess \
(tclIntPlatStubsPtr->tclWinAddProcess) /* 20 */
#endif
-#ifndef TclpAsyncMark
-#define TclpAsyncMark \
- (tclIntPlatStubsPtr->tclpAsyncMark) /* 21 */
-#endif
+/* Slot 21 is reserved */
#ifndef TclpCreateTempFile
#define TclpCreateTempFile \
(tclIntPlatStubsPtr->tclpCreateTempFile) /* 22 */
@@ -422,6 +455,10 @@ extern TclIntPlatStubs *tclIntPlatStubsPtr;
#define TclWinSetInterfaces \
(tclIntPlatStubsPtr->tclWinSetInterfaces) /* 26 */
#endif
+#ifndef TclWinFlushDirtyChannels
+#define TclWinFlushDirtyChannels \
+ (tclIntPlatStubsPtr->tclWinFlushDirtyChannels) /* 27 */
+#endif
#endif /* __WIN32__ */
#ifdef MAC_TCL
#ifndef TclpSysAlloc
@@ -452,21 +489,21 @@ extern TclIntPlatStubs *tclIntPlatStubsPtr;
#define FSpFindFolder \
(tclIntPlatStubsPtr->fSpFindFolder) /* 6 */
#endif
-#ifndef GetGlobalMouse
-#define GetGlobalMouse \
- (tclIntPlatStubsPtr->getGlobalMouse) /* 7 */
+#ifndef GetGlobalMouseTcl
+#define GetGlobalMouseTcl \
+ (tclIntPlatStubsPtr->getGlobalMouseTcl) /* 7 */
#endif
-#ifndef FSpGetDirectoryID
-#define FSpGetDirectoryID \
- (tclIntPlatStubsPtr->fSpGetDirectoryID) /* 8 */
+#ifndef FSpGetDirectoryIDTcl
+#define FSpGetDirectoryIDTcl \
+ (tclIntPlatStubsPtr->fSpGetDirectoryIDTcl) /* 8 */
#endif
-#ifndef FSpOpenResFileCompat
-#define FSpOpenResFileCompat \
- (tclIntPlatStubsPtr->fSpOpenResFileCompat) /* 9 */
+#ifndef FSpOpenResFileCompatTcl
+#define FSpOpenResFileCompatTcl \
+ (tclIntPlatStubsPtr->fSpOpenResFileCompatTcl) /* 9 */
#endif
-#ifndef FSpCreateResFileCompat
-#define FSpCreateResFileCompat \
- (tclIntPlatStubsPtr->fSpCreateResFileCompat) /* 10 */
+#ifndef FSpCreateResFileCompatTcl
+#define FSpCreateResFileCompatTcl \
+ (tclIntPlatStubsPtr->fSpCreateResFileCompatTcl) /* 10 */
#endif
#ifndef FSpLocationFromPath
#define FSpLocationFromPath \
@@ -520,11 +557,18 @@ extern TclIntPlatStubs *tclIntPlatStubsPtr;
#define TclMacFOpenHack \
(tclIntPlatStubsPtr->tclMacFOpenHack) /* 23 */
#endif
-/* Slot 24 is reserved */
+#ifndef TclpGetTZName
+#define TclpGetTZName \
+ (tclIntPlatStubsPtr->tclpGetTZName) /* 24 */
+#endif
#ifndef TclMacChmod
#define TclMacChmod \
(tclIntPlatStubsPtr->tclMacChmod) /* 25 */
#endif
+#ifndef FSpLLocationFromPath
+#define FSpLLocationFromPath \
+ (tclIntPlatStubsPtr->fSpLLocationFromPath) /* 26 */
+#endif
#endif /* MAC_TCL */
#endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */
@@ -532,4 +576,3 @@ extern TclIntPlatStubs *tclIntPlatStubsPtr;
/* !END!: Do not edit above this line. */
#endif /* _TCLINTPLATDECLS */
-
diff --git a/tcl/generic/tclInterp.c b/tcl/generic/tclInterp.c
index 96d2e27d418..f8626b5773c 100644
--- a/tcl/generic/tclInterp.c
+++ b/tcl/generic/tclInterp.c
@@ -12,9 +12,9 @@
* RCS: @(#) $Id$
*/
-#include <stdio.h>
#include "tclInt.h"
#include "tclPort.h"
+#include <stdio.h>
/*
* Counter for how many aliases were created (global)
@@ -35,12 +35,6 @@ typedef struct Alias {
Tcl_Obj *namePtr; /* Name of alias command in slave interp. */
Tcl_Interp *targetInterp; /* Interp in which target command will be
* invoked. */
- Tcl_Obj *prefixPtr; /* Tcl list making up the prefix of the
- * target command to be invoked in the target
- * interpreter. Additional arguments
- * specified when calling the alias in the
- * slave interp will be appended to the prefix
- * before the command is invoked. */
Tcl_Command slaveCmd; /* Source command in slave interpreter,
* bound to command that invokes the target
* command in the target interpreter. */
@@ -56,6 +50,16 @@ typedef struct Alias {
* redirecting to it. Random access to this
* hash table is never required - we are using
* a hash table only for convenience. */
+ int objc; /* Count of Tcl_Obj in the prefix of the
+ * target command to be invoked in the
+ * target interpreter. Additional arguments
+ * specified when calling the alias in the
+ * slave interp will be appended to the prefix
+ * before the command is invoked. */
+ Tcl_Obj *objPtr; /* The first actual prefix object - the target
+ * command name; this has to be at the end of the
+ * structure, which will be extended to accomodate
+ * the remaining objects in the prefix. */
} Alias;
/*
@@ -190,6 +194,10 @@ static int SlaveObjCmd _ANSI_ARGS_((ClientData dummy,
Tcl_Obj *CONST objv[]));
static void SlaveObjCmdDeleteProc _ANSI_ARGS_((
ClientData clientData));
+static int SlaveRecursionLimit _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Interp *slaveInterp, int objc,
+ Tcl_Obj *CONST objv[]));
+
/*
*---------------------------------------------------------------------------
@@ -347,18 +355,20 @@ Tcl_InterpObjCmd(clientData, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
int index;
- static char *options[] = {
+ static CONST char *options[] = {
"alias", "aliases", "create", "delete",
"eval", "exists", "expose", "hide",
"hidden", "issafe", "invokehidden", "marktrusted",
- "slaves", "share", "target", "transfer",
+ "recursionlimit", "slaves", "share",
+ "target", "transfer",
NULL
};
enum option {
OPT_ALIAS, OPT_ALIASES, OPT_CREATE, OPT_DELETE,
OPT_EVAL, OPT_EXISTS, OPT_EXPOSE, OPT_HIDE,
OPT_HIDDEN, OPT_ISSAFE, OPT_INVOKEHID, OPT_MARKTRUSTED,
- OPT_SLAVES, OPT_SHARE, OPT_TARGET, OPT_TRANSFER
+ OPT_RECLIMIT, OPT_SLAVES, OPT_SHARE,
+ OPT_TARGET, OPT_TRANSFER
};
@@ -419,7 +429,7 @@ Tcl_InterpObjCmd(clientData, interp, objc, objv)
int i, last, safe;
Tcl_Obj *slavePtr;
char buf[16 + TCL_INTEGER_SPACE];
- static char *options[] = {
+ static CONST char *options[] = {
"-safe", "--", NULL
};
enum option {
@@ -582,7 +592,7 @@ Tcl_InterpObjCmd(clientData, interp, objc, objv)
case OPT_INVOKEHID: {
int i, index, global;
Tcl_Interp *slaveInterp;
- static char *hiddenOptions[] = {
+ static CONST char *hiddenOptions[] = {
"-global", "--", NULL
};
enum hiddenOption {
@@ -630,6 +640,19 @@ Tcl_InterpObjCmd(clientData, interp, objc, objv)
}
return SlaveMarkTrusted(interp, slaveInterp);
}
+ case OPT_RECLIMIT: {
+ Tcl_Interp *slaveInterp;
+
+ if (objc != 3 && objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "path ?newlimit?");
+ return TCL_ERROR;
+ }
+ slaveInterp = GetInterp(interp, objv[2]);
+ if (slaveInterp == NULL) {
+ return TCL_ERROR;
+ }
+ return SlaveRecursionLimit(interp, slaveInterp, objc - 3, objv + 3);
+ }
case OPT_SLAVES: {
Tcl_Interp *slaveInterp;
InterpInfo *iiPtr;
@@ -808,11 +831,11 @@ GetInterp2(interp, objc, objv)
int
Tcl_CreateAlias(slaveInterp, slaveCmd, targetInterp, targetCmd, argc, argv)
Tcl_Interp *slaveInterp; /* Interpreter for source command. */
- char *slaveCmd; /* Command to install in slave. */
+ CONST char *slaveCmd; /* Command to install in slave. */
Tcl_Interp *targetInterp; /* Interpreter for target command. */
- char *targetCmd; /* Name of target command. */
+ CONST char *targetCmd; /* Name of target command. */
int argc; /* How many additional arguments? */
- char **argv; /* These are the additional args. */
+ CONST char * CONST *argv; /* These are the additional args. */
{
Tcl_Obj *slaveObjPtr, *targetObjPtr;
Tcl_Obj **objv;
@@ -863,9 +886,9 @@ Tcl_CreateAlias(slaveInterp, slaveCmd, targetInterp, targetCmd, argc, argv)
int
Tcl_CreateAliasObj(slaveInterp, slaveCmd, targetInterp, targetCmd, objc, objv)
Tcl_Interp *slaveInterp; /* Interpreter for source command. */
- char *slaveCmd; /* Command to install in slave. */
+ CONST char *slaveCmd; /* Command to install in slave. */
Tcl_Interp *targetInterp; /* Interpreter for target command. */
- char *targetCmd; /* Name of target command. */
+ CONST char *targetCmd; /* Name of target command. */
int objc; /* How many additional arguments? */
Tcl_Obj *CONST objv[]; /* Argument vector. */
{
@@ -906,11 +929,11 @@ int
Tcl_GetAlias(interp, aliasName, targetInterpPtr, targetNamePtr, argcPtr,
argvPtr)
Tcl_Interp *interp; /* Interp to start search from. */
- char *aliasName; /* Name of alias to find. */
+ CONST char *aliasName; /* Name of alias to find. */
Tcl_Interp **targetInterpPtr; /* (Return) target interpreter. */
- char **targetNamePtr; /* (Return) name of target command. */
+ CONST char **targetNamePtr; /* (Return) name of target command. */
int *argcPtr; /* (Return) count of addnl args. */
- char ***argvPtr; /* (Return) additional arguments. */
+ CONST char ***argvPtr; /* (Return) additional arguments. */
{
InterpInfo *iiPtr;
Tcl_HashEntry *hPtr;
@@ -926,7 +949,8 @@ Tcl_GetAlias(interp, aliasName, targetInterpPtr, targetNamePtr, argcPtr,
return TCL_ERROR;
}
aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);
- Tcl_ListObjGetElements(NULL, aliasPtr->prefixPtr, &objc, &objv);
+ objc = aliasPtr->objc;
+ objv = &aliasPtr->objPtr;
if (targetInterpPtr != NULL) {
*targetInterpPtr = aliasPtr->targetInterp;
@@ -938,7 +962,8 @@ Tcl_GetAlias(interp, aliasName, targetInterpPtr, targetNamePtr, argcPtr,
*argcPtr = objc - 1;
}
if (argvPtr != NULL) {
- *argvPtr = (char **) ckalloc((unsigned) sizeof(char *) * (objc - 1));
+ *argvPtr = (CONST char **)
+ ckalloc((unsigned) sizeof(CONST char *) * (objc - 1));
for (i = 1; i < objc; i++) {
*argvPtr[i - 1] = Tcl_GetString(objv[i]);
}
@@ -949,7 +974,7 @@ Tcl_GetAlias(interp, aliasName, targetInterpPtr, targetNamePtr, argcPtr,
/*
*----------------------------------------------------------------------
*
- * Tcl_ObjGetAlias --
+ * Tcl_GetAliasObj --
*
* Object version: Gets information about an alias.
*
@@ -966,9 +991,9 @@ int
Tcl_GetAliasObj(interp, aliasName, targetInterpPtr, targetNamePtr, objcPtr,
objvPtr)
Tcl_Interp *interp; /* Interp to start search from. */
- char *aliasName; /* Name of alias to find. */
+ CONST char *aliasName; /* Name of alias to find. */
Tcl_Interp **targetInterpPtr; /* (Return) target interpreter. */
- char **targetNamePtr; /* (Return) name of target command. */
+ CONST char **targetNamePtr; /* (Return) name of target command. */
int *objcPtr; /* (Return) count of addnl args. */
Tcl_Obj ***objvPtr; /* (Return) additional args. */
{
@@ -986,12 +1011,13 @@ Tcl_GetAliasObj(interp, aliasName, targetInterpPtr, targetNamePtr, objcPtr,
return TCL_ERROR;
}
aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);
- Tcl_ListObjGetElements(NULL, aliasPtr->prefixPtr, &objc, &objv);
+ objc = aliasPtr->objc;
+ objv = &aliasPtr->objPtr;
if (targetInterpPtr != (Tcl_Interp **) NULL) {
*targetInterpPtr = aliasPtr->targetInterp;
}
- if (targetNamePtr != (char **) NULL) {
+ if (targetNamePtr != (CONST char **) NULL) {
*targetNamePtr = Tcl_GetString(objv[0]);
}
if (objcPtr != (int *) NULL) {
@@ -1056,17 +1082,16 @@ TclPreventAliasLoop(interp, cmdInterp, cmd)
aliasPtr = (Alias *) cmdPtr->objClientData;
nextAliasPtr = aliasPtr;
while (1) {
- int objc;
- Tcl_Obj **objv;
+ Tcl_Obj *cmdNamePtr;
/*
* If the target of the next alias in the chain is the same as
* the source alias, we have a loop.
*/
- Tcl_ListObjGetElements(NULL, nextAliasPtr->prefixPtr, &objc, &objv);
+ cmdNamePtr = nextAliasPtr->objPtr;
aliasCmd = Tcl_FindCommand(nextAliasPtr->targetInterp,
- Tcl_GetString(objv[0]),
+ Tcl_GetString(cmdNamePtr),
Tcl_GetGlobalNamespace(nextAliasPtr->targetInterp),
/*flags*/ 0);
if (aliasCmd == (Tcl_Command) NULL) {
@@ -1132,14 +1157,24 @@ AliasCreate(interp, slaveInterp, masterInterp, namePtr, targetNamePtr,
Target *targetPtr;
Slave *slavePtr;
Master *masterPtr;
+ int i;
+ Tcl_Obj **prefv;
- aliasPtr = (Alias *) ckalloc((unsigned) sizeof(Alias));
+ aliasPtr = (Alias *) ckalloc((unsigned) (sizeof(Alias)
+ + objc * sizeof(Tcl_Obj *)));
aliasPtr->namePtr = namePtr;
Tcl_IncrRefCount(aliasPtr->namePtr);
aliasPtr->targetInterp = masterInterp;
- aliasPtr->prefixPtr = Tcl_NewListObj(1, &targetNamePtr);
- Tcl_ListObjReplace(NULL, aliasPtr->prefixPtr, 1, 0, objc, objv);
- Tcl_IncrRefCount(aliasPtr->prefixPtr);
+
+ aliasPtr->objc = objc + 1;
+ prefv = &aliasPtr->objPtr;
+
+ *prefv = targetNamePtr;
+ Tcl_IncrRefCount(targetNamePtr);
+ for (i = 0; i < objc; i++) {
+ *(++prefv) = objv[i];
+ Tcl_IncrRefCount(objv[i]);
+ }
aliasPtr->slaveCmd = Tcl_CreateObjCommand(slaveInterp,
Tcl_GetString(namePtr), AliasObjCmd, (ClientData) aliasPtr,
@@ -1156,7 +1191,10 @@ AliasCreate(interp, slaveInterp, masterInterp, namePtr, targetNamePtr,
Command *cmdPtr;
Tcl_DecrRefCount(aliasPtr->namePtr);
- Tcl_DecrRefCount(aliasPtr->prefixPtr);
+ Tcl_DecrRefCount(targetNamePtr);
+ for (i = 0; i < objc; i++) {
+ Tcl_DecrRefCount(objv[i]);
+ }
cmdPtr = (Command *) aliasPtr->slaveCmd;
cmdPtr->clientData = NULL;
@@ -1245,7 +1283,7 @@ static int
AliasDelete(interp, slaveInterp, namePtr)
Tcl_Interp *interp; /* Interpreter for result & errors. */
Tcl_Interp *slaveInterp; /* Interpreter containing alias. */
- Tcl_Obj *namePtr; /* Name of alias to describe. */
+ Tcl_Obj *namePtr; /* Name of alias to delete. */
{
Slave *slavePtr;
Alias *aliasPtr;
@@ -1297,6 +1335,7 @@ AliasDescribe(interp, slaveInterp, namePtr)
Slave *slavePtr;
Tcl_HashEntry *hPtr;
Alias *aliasPtr;
+ Tcl_Obj *prefixPtr;
/*
* If the alias has been renamed in the slave, the master can still use
@@ -1310,7 +1349,8 @@ AliasDescribe(interp, slaveInterp, namePtr)
return TCL_OK;
}
aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);
- Tcl_SetObjResult(interp, aliasPtr->prefixPtr);
+ prefixPtr = Tcl_NewListObj(aliasPtr->objc, &aliasPtr->objPtr);
+ Tcl_SetObjResult(interp, prefixPtr);
return TCL_OK;
}
@@ -1381,71 +1421,51 @@ AliasObjCmd(clientData, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument vector. */
{
+#define ALIAS_CMDV_PREALLOC 10
Tcl_Interp *targetInterp;
Alias *aliasPtr;
int result, prefc, cmdc;
- Tcl_Obj *cmdPtr;
Tcl_Obj **prefv, **cmdv;
-
+ Tcl_Obj *cmdArr[ALIAS_CMDV_PREALLOC];
aliasPtr = (Alias *) clientData;
targetInterp = aliasPtr->targetInterp;
- Tcl_Preserve((ClientData) targetInterp);
-
- ((Interp *) targetInterp)->numLevels++;
-
- Tcl_ResetResult(targetInterp);
- Tcl_AllowExceptions(targetInterp);
-
/*
* Append the arguments to the command prefix and invoke the command
* in the target interp's global namespace.
*/
- Tcl_ListObjGetElements(NULL, aliasPtr->prefixPtr, &prefc, &prefv);
- cmdPtr = Tcl_NewListObj(prefc, prefv);
- Tcl_ListObjReplace(NULL, cmdPtr, prefc, 0, objc - 1, objv + 1);
- Tcl_ListObjGetElements(NULL, cmdPtr, &cmdc, &cmdv);
- result = TclObjInvoke(targetInterp, cmdc, cmdv,
- TCL_INVOKE_NO_TRACEBACK);
- Tcl_DecrRefCount(cmdPtr);
-
- ((Interp *) targetInterp)->numLevels--;
-
- /*
- * Check if we are at the bottom of the stack for the target interpreter.
- * If so, check for special return codes.
- */
-
- if (((Interp *) targetInterp)->numLevels == 0) {
- if (result == TCL_RETURN) {
- result = TclUpdateReturnInfo((Interp *) targetInterp);
- }
- if ((result != TCL_OK) && (result != TCL_ERROR)) {
- Tcl_ResetResult(targetInterp);
- if (result == TCL_BREAK) {
- Tcl_SetObjResult(targetInterp,
- Tcl_NewStringObj("invoked \"break\" outside of a loop",
- -1));
- } else if (result == TCL_CONTINUE) {
- Tcl_SetObjResult(targetInterp,
- Tcl_NewStringObj(
- "invoked \"continue\" outside of a loop",
- -1));
- } else {
- char buf[32 + TCL_INTEGER_SPACE];
-
- sprintf(buf, "command returned bad code: %d", result);
- Tcl_SetObjResult(targetInterp, Tcl_NewStringObj(buf, -1));
- }
- result = TCL_ERROR;
- }
+ prefc = aliasPtr->objc;
+ prefv = &aliasPtr->objPtr;
+ cmdc = prefc + objc - 1;
+ if (cmdc <= ALIAS_CMDV_PREALLOC) {
+ cmdv = cmdArr;
+ } else {
+ cmdv = (Tcl_Obj **) ckalloc((unsigned) (cmdc * sizeof(Tcl_Obj *)));
}
- TclTransferResult(targetInterp, result, interp);
+ prefv = &aliasPtr->objPtr;
+ memcpy((VOID *) cmdv, (VOID *) prefv,
+ (size_t) (prefc * sizeof(Tcl_Obj *)));
+ memcpy((VOID *) (cmdv+prefc), (VOID *) (objv+1),
+ (size_t) ((objc-1) * sizeof(Tcl_Obj *)));
+
+ Tcl_ResetResult(targetInterp);
- Tcl_Release((ClientData) targetInterp);
+ if (targetInterp != interp) {
+ Tcl_Preserve((ClientData) targetInterp);
+ result = Tcl_EvalObjv(targetInterp, cmdc, cmdv, TCL_EVAL_INVOKE);
+ TclTransferResult(targetInterp, result, interp);
+ Tcl_Release((ClientData) targetInterp);
+ } else {
+ result = Tcl_EvalObjv(targetInterp, cmdc, cmdv, TCL_EVAL_INVOKE);
+ }
+
+ if (cmdv != cmdArr) {
+ ckfree((char *) cmdv);
+ }
return result;
+#undef ALIAS_CMDV_PREALLOC
}
/*
@@ -1472,11 +1492,16 @@ AliasObjCmdDeleteProc(clientData)
{
Alias *aliasPtr;
Target *targetPtr;
+ int i;
+ Tcl_Obj **objv;
aliasPtr = (Alias *) clientData;
Tcl_DecrRefCount(aliasPtr->namePtr);
- Tcl_DecrRefCount(aliasPtr->prefixPtr);
+ objv = &aliasPtr->objPtr;
+ for (i = 0; i < aliasPtr->objc; i++) {
+ Tcl_DecrRefCount(objv[i]);
+ }
Tcl_DeleteHashEntry(aliasPtr->aliasEntryPtr);
targetPtr = (Target *) Tcl_GetHashValue(aliasPtr->targetEntryPtr);
@@ -1512,7 +1537,7 @@ AliasObjCmdDeleteProc(clientData)
Tcl_Interp *
Tcl_CreateSlave(interp, slavePath, isSafe)
Tcl_Interp *interp; /* Interpreter to start search at. */
- char *slavePath; /* Name of slave to create. */
+ CONST char *slavePath; /* Name of slave to create. */
int isSafe; /* Should new slave be "safe" ? */
{
Tcl_Obj *pathPtr;
@@ -1545,7 +1570,7 @@ Tcl_CreateSlave(interp, slavePath, isSafe)
Tcl_Interp *
Tcl_GetSlave(interp, slavePath)
Tcl_Interp *interp; /* Interpreter to start search from. */
- char *slavePath; /* Path of slave to find. */
+ CONST char *slavePath; /* Path of slave to find. */
{
Tcl_Obj *pathPtr;
Tcl_Interp *slaveInterp;
@@ -1780,6 +1805,11 @@ SlaveCreate(interp, pathPtr, safe)
if (Tcl_Init(slaveInterp) == TCL_ERROR) {
goto error;
}
+ /*
+ * This will create the "memory" command in slave interpreters
+ * if we compiled with TCL_MEM_DEBUG, otherwise it does nothing.
+ */
+ Tcl_InitMemory(slaveInterp);
}
return slaveInterp;
@@ -1816,15 +1846,15 @@ SlaveObjCmd(clientData, interp, objc, objv)
{
Tcl_Interp *slaveInterp;
int index;
- static char *options[] = {
+ static CONST char *options[] = {
"alias", "aliases", "eval", "expose",
"hide", "hidden", "issafe", "invokehidden",
- "marktrusted", NULL
+ "marktrusted", "recursionlimit", NULL
};
enum options {
OPT_ALIAS, OPT_ALIASES, OPT_EVAL, OPT_EXPOSE,
OPT_HIDE, OPT_HIDDEN, OPT_ISSAFE, OPT_INVOKEHIDDEN,
- OPT_MARKTRUSTED
+ OPT_MARKTRUSTED, OPT_RECLIMIT
};
slaveInterp = (Tcl_Interp *) clientData;
@@ -1843,22 +1873,28 @@ SlaveObjCmd(clientData, interp, objc, objv)
switch ((enum options) index) {
case OPT_ALIAS: {
- if (objc == 3) {
- return AliasDescribe(interp, slaveInterp, objv[2]);
- }
- if (Tcl_GetString(objv[3])[0] == '\0') {
- if (objc == 4) {
- return AliasDelete(interp, slaveInterp, objv[2]);
+ if (objc > 2) {
+ if (objc == 3) {
+ return AliasDescribe(interp, slaveInterp, objv[2]);
+ }
+ if (Tcl_GetString(objv[3])[0] == '\0') {
+ if (objc == 4) {
+ return AliasDelete(interp, slaveInterp, objv[2]);
+ }
+ } else {
+ return AliasCreate(interp, slaveInterp, interp, objv[2],
+ objv[3], objc - 4, objv + 4);
}
- } else {
- return AliasCreate(interp, slaveInterp, interp, objv[2],
- objv[3], objc - 4, objv + 4);
}
Tcl_WrongNumArgs(interp, 2, objv,
"aliasName ?targetName? ?args..?");
return TCL_ERROR;
}
case OPT_ALIASES: {
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
+ return TCL_ERROR;
+ }
return AliasList(interp, slaveInterp);
}
case OPT_EVAL: {
@@ -1890,12 +1926,16 @@ SlaveObjCmd(clientData, interp, objc, objv)
return SlaveHidden(interp, slaveInterp);
}
case OPT_ISSAFE: {
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
+ return TCL_ERROR;
+ }
Tcl_SetIntObj(Tcl_GetObjResult(interp), Tcl_IsSafe(slaveInterp));
return TCL_OK;
}
case OPT_INVOKEHIDDEN: {
int global, i, index;
- static char *hiddenOptions[] = {
+ static CONST char *hiddenOptions[] = {
"-global", "--", NULL
};
enum hiddenOption {
@@ -1932,6 +1972,13 @@ SlaveObjCmd(clientData, interp, objc, objv)
}
return SlaveMarkTrusted(interp, slaveInterp);
}
+ case OPT_RECLIMIT: {
+ if (objc != 2 && objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?newlimit?");
+ return TCL_ERROR;
+ }
+ return SlaveRecursionLimit(interp, slaveInterp, objc - 2, objv + 2);
+ }
}
return TCL_ERROR;
@@ -2074,6 +2121,65 @@ SlaveExpose(interp, slaveInterp, objc, objv)
/*
*----------------------------------------------------------------------
*
+ * SlaveRecursionLimit --
+ *
+ * Helper function to set/query the Recursion limit of an interp
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * When (objc == 1), slaveInterp will be set to a new recursion
+ * limit of objv[0].
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SlaveRecursionLimit(interp, slaveInterp, objc, objv)
+ Tcl_Interp *interp; /* Interp for error return. */
+ Tcl_Interp *slaveInterp; /* Interp in which limit is set/queried. */
+ int objc; /* Set or Query. */
+ Tcl_Obj *CONST objv[]; /* Argument strings. */
+{
+ Interp *iPtr;
+ int limit;
+
+ if (objc) {
+ if (Tcl_IsSafe(interp)) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "permission denied: ",
+ "safe interpreters cannot change recursion limit",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[0], &limit) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (limit <= 0) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "recursion limit must be > 0", -1));
+ return TCL_ERROR;
+ }
+ Tcl_SetRecursionLimit(slaveInterp, limit);
+ iPtr = (Interp *) slaveInterp;
+ if (interp == slaveInterp && iPtr->numLevels > limit) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "falling back due to new recursion limit", -1));
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, objv[0]);
+ return TCL_OK;
+ } else {
+ limit = Tcl_SetRecursionLimit(slaveInterp, 0);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(limit));
+ return TCL_OK;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* SlaveHide --
*
* Helper function to hide a command in a slave interpreter.
diff --git a/tcl/generic/tclLink.c b/tcl/generic/tclLink.c
index 13c5691a863..6edba23678a 100644
--- a/tcl/generic/tclLink.c
+++ b/tcl/generic/tclLink.c
@@ -26,7 +26,7 @@
typedef struct Link {
Tcl_Interp *interp; /* Interpreter containing Tcl variable. */
- char *varName; /* Name of variable (must be global). This
+ Tcl_Obj *varName; /* Name of variable (must be global). This
* is needed during trace callbacks, since
* the actual variable may be aliased at
* that time via upvar. */
@@ -35,6 +35,7 @@ typedef struct Link {
union {
int i;
double d;
+ Tcl_WideInt w;
} lastValue; /* Last known value of C variable; used to
* avoid string conversions. */
int flags; /* Miscellaneous one-bit values; see below
@@ -59,10 +60,9 @@ typedef struct Link {
*/
static char * LinkTraceProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, char *name1, char *name2,
- int flags));
-static char * StringValue _ANSI_ARGS_((Link *linkPtr,
- char *buffer));
+ Tcl_Interp *interp, CONST char *name1,
+ CONST char *name2, int flags));
+static Tcl_Obj * ObjValue _ANSI_ARGS_((Link *linkPtr));
/*
*----------------------------------------------------------------------
@@ -88,21 +88,21 @@ static char * StringValue _ANSI_ARGS_((Link *linkPtr,
int
Tcl_LinkVar(interp, varName, addr, type)
Tcl_Interp *interp; /* Interpreter in which varName exists. */
- char *varName; /* Name of a global variable in interp. */
+ CONST char *varName; /* Name of a global variable in interp. */
char *addr; /* Address of a C variable to be linked
* to varName. */
int type; /* Type of C variable: TCL_LINK_INT, etc.
* Also may have TCL_LINK_READ_ONLY
* OR'ed in. */
{
+ Tcl_Obj *objPtr;
Link *linkPtr;
- char buffer[TCL_DOUBLE_SPACE];
int code;
linkPtr = (Link *) ckalloc(sizeof(Link));
linkPtr->interp = interp;
- linkPtr->varName = (char *) ckalloc((unsigned) (strlen(varName) + 1));
- strcpy(linkPtr->varName, varName);
+ linkPtr->varName = Tcl_NewStringObj(varName, -1);
+ Tcl_IncrRefCount(linkPtr->varName);
linkPtr->addr = addr;
linkPtr->type = type & ~TCL_LINK_READ_ONLY;
if (type & TCL_LINK_READ_ONLY) {
@@ -110,9 +110,11 @@ Tcl_LinkVar(interp, varName, addr, type)
} else {
linkPtr->flags = 0;
}
- if (Tcl_SetVar(interp, varName, StringValue(linkPtr, buffer),
+ objPtr = ObjValue(linkPtr);
+ if (Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, objPtr,
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
- ckfree(linkPtr->varName);
+ Tcl_DecrRefCount(linkPtr->varName);
+ Tcl_DecrRefCount(objPtr);
ckfree((char *) linkPtr);
return TCL_ERROR;
}
@@ -120,7 +122,7 @@ Tcl_LinkVar(interp, varName, addr, type)
|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, LinkTraceProc,
(ClientData) linkPtr);
if (code != TCL_OK) {
- ckfree(linkPtr->varName);
+ Tcl_DecrRefCount(linkPtr->varName);
ckfree((char *) linkPtr);
}
return code;
@@ -147,7 +149,7 @@ Tcl_LinkVar(interp, varName, addr, type)
void
Tcl_UnlinkVar(interp, varName)
Tcl_Interp *interp; /* Interpreter containing variable to unlink. */
- char *varName; /* Global variable in interp to unlink. */
+ CONST char *varName; /* Global variable in interp to unlink. */
{
Link *linkPtr;
@@ -159,7 +161,7 @@ Tcl_UnlinkVar(interp, varName)
Tcl_UntraceVar(interp, varName,
TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
LinkTraceProc, (ClientData) linkPtr);
- ckfree(linkPtr->varName);
+ Tcl_DecrRefCount(linkPtr->varName);
ckfree((char *) linkPtr);
}
@@ -185,10 +187,9 @@ Tcl_UnlinkVar(interp, varName)
void
Tcl_UpdateLinkedVar(interp, varName)
Tcl_Interp *interp; /* Interpreter containing variable. */
- char *varName; /* Name of global variable that is linked. */
+ CONST char *varName; /* Name of global variable that is linked. */
{
Link *linkPtr;
- char buffer[TCL_DOUBLE_SPACE];
int savedFlag;
linkPtr = (Link *) Tcl_VarTraceInfo(interp, varName, TCL_GLOBAL_ONLY,
@@ -198,7 +199,7 @@ Tcl_UpdateLinkedVar(interp, varName)
}
savedFlag = linkPtr->flags & LINK_BEING_UPDATED;
linkPtr->flags |= LINK_BEING_UPDATED;
- Tcl_SetVar(interp, linkPtr->varName, StringValue(linkPtr, buffer),
+ Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
TCL_GLOBAL_ONLY);
linkPtr->flags = (linkPtr->flags & ~LINK_BEING_UPDATED) | savedFlag;
}
@@ -228,15 +229,15 @@ static char *
LinkTraceProc(clientData, interp, name1, name2, flags)
ClientData clientData; /* Contains information about the link. */
Tcl_Interp *interp; /* Interpreter containing Tcl variable. */
- char *name1; /* First part of variable name. */
- char *name2; /* Second part of variable name. */
+ CONST char *name1; /* First part of variable name. */
+ CONST char *name2; /* Second part of variable name. */
int flags; /* Miscellaneous additional information. */
{
Link *linkPtr = (Link *) clientData;
- int changed;
- char buffer[TCL_DOUBLE_SPACE];
- char *value, **pp, *result;
- Tcl_Obj *objPtr;
+ int changed, valueLength;
+ CONST char *value;
+ char **pp, *result;
+ Tcl_Obj *objPtr, *valueObj;
/*
* If the variable is being unset, then just re-create it (with a
@@ -245,14 +246,14 @@ LinkTraceProc(clientData, interp, name1, name2, flags)
if (flags & TCL_TRACE_UNSETS) {
if (flags & TCL_INTERP_DESTROYED) {
- ckfree(linkPtr->varName);
+ Tcl_DecrRefCount(linkPtr->varName);
ckfree((char *) linkPtr);
} else if (flags & TCL_TRACE_DESTROYED) {
- Tcl_SetVar(interp, linkPtr->varName, StringValue(linkPtr, buffer),
+ Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
TCL_GLOBAL_ONLY);
- Tcl_TraceVar(interp, linkPtr->varName, TCL_GLOBAL_ONLY
- |TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
- LinkTraceProc, (ClientData) linkPtr);
+ Tcl_TraceVar(interp, Tcl_GetString(linkPtr->varName),
+ TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES
+ |TCL_TRACE_UNSETS, LinkTraceProc, (ClientData) linkPtr);
}
return NULL;
}
@@ -275,21 +276,24 @@ LinkTraceProc(clientData, interp, name1, name2, flags)
if (flags & TCL_TRACE_READS) {
switch (linkPtr->type) {
- case TCL_LINK_INT:
- case TCL_LINK_BOOLEAN:
- changed = *(int *)(linkPtr->addr) != linkPtr->lastValue.i;
- break;
- case TCL_LINK_DOUBLE:
- changed = *(double *)(linkPtr->addr) != linkPtr->lastValue.d;
- break;
- case TCL_LINK_STRING:
- changed = 1;
- break;
- default:
- return "internal error: bad linked variable type";
+ case TCL_LINK_INT:
+ case TCL_LINK_BOOLEAN:
+ changed = *(int *)(linkPtr->addr) != linkPtr->lastValue.i;
+ break;
+ case TCL_LINK_DOUBLE:
+ changed = *(double *)(linkPtr->addr) != linkPtr->lastValue.d;
+ break;
+ case TCL_LINK_WIDE_INT:
+ changed = *(Tcl_WideInt *)(linkPtr->addr) != linkPtr->lastValue.w;
+ break;
+ case TCL_LINK_STRING:
+ changed = 1;
+ break;
+ default:
+ return "internal error: bad linked variable type";
}
if (changed) {
- Tcl_SetVar(interp, linkPtr->varName, StringValue(linkPtr, buffer),
+ Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
TCL_GLOBAL_ONLY);
}
return NULL;
@@ -305,12 +309,12 @@ LinkTraceProc(clientData, interp, name1, name2, flags)
*/
if (linkPtr->flags & LINK_READ_ONLY) {
- Tcl_SetVar(interp, linkPtr->varName, StringValue(linkPtr, buffer),
+ Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
TCL_GLOBAL_ONLY);
return "linked variable is read-only";
}
- value = Tcl_GetVar(interp, linkPtr->varName, TCL_GLOBAL_ONLY);
- if (value == NULL) {
+ valueObj = Tcl_ObjGetVar2(interp, linkPtr->varName,NULL, TCL_GLOBAL_ONLY);
+ if (valueObj == NULL) {
/*
* This shouldn't ever happen.
*/
@@ -323,48 +327,67 @@ LinkTraceProc(clientData, interp, name1, name2, flags)
result = NULL;
switch (linkPtr->type) {
- case TCL_LINK_INT:
- if (Tcl_GetInt(interp, value, &linkPtr->lastValue.i) != TCL_OK) {
- Tcl_SetObjResult(interp, objPtr);
- Tcl_SetVar(interp, linkPtr->varName,
- StringValue(linkPtr, buffer), TCL_GLOBAL_ONLY);
- result = "variable must have integer value";
- goto end;
- }
- *(int *)(linkPtr->addr) = linkPtr->lastValue.i;
- break;
- case TCL_LINK_DOUBLE:
- if (Tcl_GetDouble(interp, value, &linkPtr->lastValue.d)
- != TCL_OK) {
- Tcl_SetObjResult(interp, objPtr);
- Tcl_SetVar(interp, linkPtr->varName,
- StringValue(linkPtr, buffer), TCL_GLOBAL_ONLY);
- result = "variable must have real value";
- goto end;
- }
- *(double *)(linkPtr->addr) = linkPtr->lastValue.d;
- break;
- case TCL_LINK_BOOLEAN:
- if (Tcl_GetBoolean(interp, value, &linkPtr->lastValue.i)
- != TCL_OK) {
- Tcl_SetObjResult(interp, objPtr);
- Tcl_SetVar(interp, linkPtr->varName,
- StringValue(linkPtr, buffer), TCL_GLOBAL_ONLY);
- result = "variable must have boolean value";
- goto end;
- }
- *(int *)(linkPtr->addr) = linkPtr->lastValue.i;
- break;
- case TCL_LINK_STRING:
- pp = (char **)(linkPtr->addr);
- if (*pp != NULL) {
- ckfree(*pp);
- }
- *pp = (char *) ckalloc((unsigned) (strlen(value) + 1));
- strcpy(*pp, value);
- break;
- default:
- return "internal error: bad linked variable type";
+ case TCL_LINK_INT:
+ if (Tcl_GetIntFromObj(interp, valueObj, &linkPtr->lastValue.i)
+ != TCL_OK) {
+ Tcl_SetObjResult(interp, objPtr);
+ Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+ TCL_GLOBAL_ONLY);
+ result = "variable must have integer value";
+ goto end;
+ }
+ *(int *)(linkPtr->addr) = linkPtr->lastValue.i;
+ break;
+
+ case TCL_LINK_WIDE_INT:
+ if (Tcl_GetWideIntFromObj(interp, valueObj, &linkPtr->lastValue.w)
+ != TCL_OK) {
+ Tcl_SetObjResult(interp, objPtr);
+ Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+ TCL_GLOBAL_ONLY);
+ result = "variable must have integer value";
+ goto end;
+ }
+ *(Tcl_WideInt *)(linkPtr->addr) = linkPtr->lastValue.w;
+ break;
+
+ case TCL_LINK_DOUBLE:
+ if (Tcl_GetDoubleFromObj(interp, valueObj, &linkPtr->lastValue.d)
+ != TCL_OK) {
+ Tcl_SetObjResult(interp, objPtr);
+ Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+ TCL_GLOBAL_ONLY);
+ result = "variable must have real value";
+ goto end;
+ }
+ *(double *)(linkPtr->addr) = linkPtr->lastValue.d;
+ break;
+
+ case TCL_LINK_BOOLEAN:
+ if (Tcl_GetBooleanFromObj(interp, valueObj, &linkPtr->lastValue.i)
+ != TCL_OK) {
+ Tcl_SetObjResult(interp, objPtr);
+ Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+ TCL_GLOBAL_ONLY);
+ result = "variable must have boolean value";
+ goto end;
+ }
+ *(int *)(linkPtr->addr) = linkPtr->lastValue.i;
+ break;
+
+ case TCL_LINK_STRING:
+ value = Tcl_GetStringFromObj(valueObj, &valueLength);
+ valueLength++;
+ pp = (char **)(linkPtr->addr);
+ if (*pp != NULL) {
+ ckfree(*pp);
+ }
+ *pp = (char *) ckalloc((unsigned) valueLength);
+ memcpy(*pp, value, (unsigned) valueLength);
+ break;
+
+ default:
+ return "internal error: bad linked variable type";
}
end:
Tcl_DecrRefCount(objPtr);
@@ -374,13 +397,13 @@ LinkTraceProc(clientData, interp, name1, name2, flags)
/*
*----------------------------------------------------------------------
*
- * StringValue --
+ * ObjValue --
*
- * Converts the value of a C variable to a string for use in a
+ * Converts the value of a C variable to a Tcl_Obj* for use in a
* Tcl variable to which it is linked.
*
* Results:
- * The return value is a pointer to a string that represents
+ * The return value is a pointer to a Tcl_Obj that represents
* the value of the C variable given by linkPtr.
*
* Side effects:
@@ -389,42 +412,37 @@ LinkTraceProc(clientData, interp, name1, name2, flags)
*----------------------------------------------------------------------
*/
-static char *
-StringValue(linkPtr, buffer)
+static Tcl_Obj *
+ObjValue(linkPtr)
Link *linkPtr; /* Structure describing linked variable. */
- char *buffer; /* Small buffer to use for converting
- * values. Must have TCL_DOUBLE_SPACE
- * bytes or more. */
{
char *p;
switch (linkPtr->type) {
- case TCL_LINK_INT:
- linkPtr->lastValue.i = *(int *)(linkPtr->addr);
- TclFormatInt(buffer, linkPtr->lastValue.i);
- return buffer;
- case TCL_LINK_DOUBLE:
- linkPtr->lastValue.d = *(double *)(linkPtr->addr);
- Tcl_PrintDouble((Tcl_Interp *) NULL, linkPtr->lastValue.d, buffer);
- return buffer;
- case TCL_LINK_BOOLEAN:
- linkPtr->lastValue.i = *(int *)(linkPtr->addr);
- if (linkPtr->lastValue.i != 0) {
- return "1";
- }
- return "0";
- case TCL_LINK_STRING:
- p = *(char **)(linkPtr->addr);
- if (p == NULL) {
- return "NULL";
- }
- return p;
- }
+ case TCL_LINK_INT:
+ linkPtr->lastValue.i = *(int *)(linkPtr->addr);
+ return Tcl_NewIntObj(linkPtr->lastValue.i);
+ case TCL_LINK_WIDE_INT:
+ linkPtr->lastValue.w = *(Tcl_WideInt *)(linkPtr->addr);
+ return Tcl_NewWideIntObj(linkPtr->lastValue.w);
+ case TCL_LINK_DOUBLE:
+ linkPtr->lastValue.d = *(double *)(linkPtr->addr);
+ return Tcl_NewDoubleObj(linkPtr->lastValue.d);
+ case TCL_LINK_BOOLEAN:
+ linkPtr->lastValue.i = *(int *)(linkPtr->addr);
+ return Tcl_NewBooleanObj(linkPtr->lastValue.i != 0);
+ case TCL_LINK_STRING:
+ p = *(char **)(linkPtr->addr);
+ if (p == NULL) {
+ return Tcl_NewStringObj("NULL", 4);
+ }
+ return Tcl_NewStringObj(p, -1);
/*
* This code only gets executed if the link type is unknown
* (shouldn't ever happen).
*/
-
- return "??";
+ default:
+ return Tcl_NewStringObj("??", 2);
+ }
}
diff --git a/tcl/generic/tclListObj.c b/tcl/generic/tclListObj.c
index 0e22a6020ac..88619f4c158 100644
--- a/tcl/generic/tclListObj.c
+++ b/tcl/generic/tclListObj.c
@@ -6,6 +6,7 @@
*
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
* Copyright (c) 1998 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -29,6 +30,15 @@ static void UpdateStringOfList _ANSI_ARGS_((Tcl_Obj *listPtr));
/*
* The structure below defines the list Tcl object type by means of
* procedures that can be invoked by generic object code.
+ *
+ * The internal representation of a list object is a two-pointer
+ * representation. The first pointer designates a List structure that
+ * contains an array of pointers to the element objects, together with
+ * integers that represent the current element count and the allocated
+ * size of the array. The second pointer is normally NULL; during
+ * execution of functions in this file that operate on nested sublists,
+ * it is occasionally used as working storage to avoid an auxiliary
+ * stack.
*/
Tcl_ObjType tclListType = {
@@ -105,7 +115,8 @@ Tcl_NewListObj(objc, objv)
listRepPtr->elemCount = objc;
listRepPtr->elements = elemPtrs;
- listPtr->internalRep.otherValuePtr = (VOID *) listRepPtr;
+ listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+ listPtr->internalRep.twoPtrValue.ptr2 = NULL;
listPtr->typePtr = &tclListType;
}
return listPtr;
@@ -121,9 +132,9 @@ Tcl_NewListObj(objc, objv)
* TCL_MEM_DEBUG is defined. It creates new list objects. It is the
* same as the Tcl_NewListObj procedure above except that it calls
* Tcl_DbCkalloc directly with the file name and line number from its
- * caller. This simplifies debugging since then the checkmem command
- * will report the correct file name and line number when reporting
- * objects that haven't been freed.
+ * caller. This simplifies debugging since then the [memory active]
+ * command will report the correct file name and line number when
+ * reporting objects that haven't been freed.
*
* When TCL_MEM_DEBUG is not defined, this procedure just returns the
* result of calling Tcl_NewListObj.
@@ -147,7 +158,7 @@ Tcl_Obj *
Tcl_DbNewListObj(objc, objv, file, line)
int objc; /* Count of objects referenced by objv. */
Tcl_Obj *CONST objv[]; /* An array of pointers to Tcl objects. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -174,7 +185,8 @@ Tcl_DbNewListObj(objc, objv, file, line)
listRepPtr->elemCount = objc;
listRepPtr->elements = elemPtrs;
- listPtr->internalRep.otherValuePtr = (VOID *) listRepPtr;
+ listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+ listPtr->internalRep.twoPtrValue.ptr2 = NULL;
listPtr->typePtr = &tclListType;
}
return listPtr;
@@ -186,7 +198,7 @@ Tcl_Obj *
Tcl_DbNewListObj(objc, objv, file, line)
int objc; /* Count of objects referenced by objv. */
Tcl_Obj *CONST objv[]; /* An array of pointers to Tcl objects. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -261,10 +273,12 @@ Tcl_SetListObj(objPtr, objc, objv)
listRepPtr->elemCount = objc;
listRepPtr->elements = elemPtrs;
- objPtr->internalRep.otherValuePtr = (VOID *) listRepPtr;
+ objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+ objPtr->internalRep.twoPtrValue.ptr2 = NULL;
objPtr->typePtr = &tclListType;
} else {
objPtr->bytes = tclEmptyStringRep;
+ objPtr->length = 0;
}
}
@@ -316,7 +330,7 @@ Tcl_ListObjGetElements(interp, listPtr, objcPtr, objvPtr)
return result;
}
}
- listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+ listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
*objcPtr = listRepPtr->elemCount;
*objvPtr = listRepPtr->elements;
return TCL_OK;
@@ -367,7 +381,7 @@ Tcl_ListObjAppendList(interp, listPtr, elemListPtr)
return result;
}
}
- listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+ listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
listLen = listRepPtr->elemCount;
result = Tcl_ListObjGetElements(interp, elemListPtr, &objc, &objv);
@@ -430,7 +444,7 @@ Tcl_ListObjAppendElement(interp, listPtr, objPtr)
}
}
- listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+ listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
elemPtrs = listRepPtr->elements;
numElems = listRepPtr->elemCount;
numRequired = numElems + 1 ;
@@ -514,7 +528,7 @@ Tcl_ListObjIndex(interp, listPtr, index, objPtrPtr)
}
}
- listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+ listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
if ((index < 0) || (index >= listRepPtr->elemCount)) {
*objPtrPtr = NULL;
} else {
@@ -561,7 +575,7 @@ Tcl_ListObjLength(interp, listPtr, intPtr)
}
}
- listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+ listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
*intPtr = listRepPtr->elemCount;
return TCL_OK;
}
@@ -629,7 +643,7 @@ Tcl_ListObjReplace(interp, listPtr, first, count, objc, objv)
return result;
}
}
- listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+ listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
elemPtrs = listRepPtr->elements;
numElems = listRepPtr->elemCount;
@@ -762,6 +776,586 @@ Tcl_ListObjReplace(interp, listPtr, first, count, objc, objv)
/*
*----------------------------------------------------------------------
*
+ * TclLsetList --
+ *
+ * Core of the 'lset' command when objc == 4. Objv[2] may be
+ * either a scalar index or a list of indices.
+ *
+ * Results:
+ * Returns the new value of the list variable, or NULL if an
+ * error occurs.
+ *
+ * Side effects:
+ * Surgery is performed on the list value to produce the
+ * result.
+ *
+ * On entry, the reference count of the variable value does not reflect
+ * any references held on the stack. The first action of this function
+ * is to determine whether the object is shared, and to duplicate it if
+ * it is. The reference count of the duplicate is incremented.
+ * At this point, the reference count will be 1 for either case, so that
+ * the object will appear to be unshared.
+ *
+ * If an error occurs, and the object has been duplicated, the reference
+ * count on the duplicate is decremented so that it is now 0: this dismisses
+ * any memory that was allocated by this procedure.
+ *
+ * If no error occurs, the reference count of the original object is
+ * incremented if the object has not been duplicated, and nothing is
+ * done to a reference count of the duplicate. Now the reference count
+ * of an unduplicated object is 2 (the returned pointer, plus the one
+ * stored in the variable). The reference count of a duplicate object
+ * is 1, reflecting that the returned pointer is the only active
+ * reference. The caller is expected to store the returned value back
+ * in the variable and decrement its reference count. (INST_STORE_*
+ * does exactly this.)
+ *
+ * Tcl_LsetFlat and related functions maintain a linked list of
+ * Tcl_Obj's whose string representations must be spoilt by threading
+ * via 'ptr2' of the two-pointer internal representation. On entry
+ * to Tcl_LsetList, the values of 'ptr2' are immaterial; on exit,
+ * the 'ptr2' field of any Tcl_Obj that has been modified is set to
+ * NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+TclLsetList( interp, listPtr, indexArgPtr, valuePtr )
+ Tcl_Interp* interp; /* Tcl interpreter */
+ Tcl_Obj* listPtr; /* Pointer to the list being modified */
+ Tcl_Obj* indexArgPtr; /* Index or index-list arg to 'lset' */
+ Tcl_Obj* valuePtr; /* Value arg to 'lset' */
+{
+ int indexCount; /* Number of indices in the index list */
+ Tcl_Obj** indices; /* Vector of indices in the index list*/
+
+ int duplicated; /* Flag == 1 if the obj has been
+ * duplicated, 0 otherwise */
+ Tcl_Obj* retValuePtr; /* Pointer to the list to be returned */
+ int index; /* Current index in the list - discarded */
+ int result; /* Status return from library calls */
+ Tcl_Obj* subListPtr; /* Pointer to the current sublist */
+ int elemCount; /* Count of elements in the current sublist */
+ Tcl_Obj** elemPtrs; /* Pointers to elements of current sublist */
+ Tcl_Obj* chainPtr; /* Pointer to the enclosing sublist
+ * of the current sublist */
+ int i;
+
+
+ /*
+ * Determine whether the index arg designates a list or a single
+ * index. We have to be careful about the order of the checks to
+ * avoid repeated shimmering; see TIP #22 and #23 for details.
+ */
+
+ if ( indexArgPtr->typePtr != &tclListType
+ && TclGetIntForIndex( NULL, indexArgPtr, 0, &index ) == TCL_OK ) {
+
+ /*
+ * indexArgPtr designates a single index.
+ */
+
+ return TclLsetFlat( interp, listPtr, 1, &indexArgPtr, valuePtr );
+
+ } else if ( Tcl_ListObjGetElements( NULL, indexArgPtr,
+ &indexCount, &indices ) != TCL_OK ) {
+
+ /*
+ * indexArgPtr designates something that is neither an index nor a
+ * well formed list. Report the error via TclLsetFlat.
+ */
+
+ return TclLsetFlat( interp, listPtr, 1, &indexArgPtr, valuePtr );
+
+ }
+
+ /*
+ * At this point, we know that argPtr designates a well formed list,
+ * and the 'else if' above has parsed it into indexCount and indices.
+ * If there are no indices, simply return 'valuePtr', counting the
+ * returned pointer as a reference.
+ */
+
+ if ( indexCount == 0 ) {
+ Tcl_IncrRefCount( valuePtr );
+ return valuePtr;
+ }
+
+ /*
+ * Duplicate the list arg if necessary.
+ */
+
+ if ( Tcl_IsShared( listPtr ) ) {
+ duplicated = 1;
+ listPtr = Tcl_DuplicateObj( listPtr );
+ Tcl_IncrRefCount( listPtr );
+ } else {
+ duplicated = 0;
+ }
+
+ /*
+ * It would be tempting simply to go off to TclLsetFlat to finish the
+ * processing. Alas, it is also incorrect! The problem is that
+ * 'indexArgPtr' may designate a sublist of 'listPtr' whose value
+ * is to be manipulated. The fact that 'listPtr' is itself unshared
+ * does not guarantee that no sublist is. Therefore, it's necessary
+ * to replicate all the work here, expanding the index list on each
+ * trip through the loop.
+ */
+
+ /*
+ * Anchor the linked list of Tcl_Obj's whose string reps must be
+ * invalidated if the operation succeeds.
+ */
+
+ retValuePtr = listPtr;
+ chainPtr = NULL;
+
+ /*
+ * Handle each index arg by diving into the appropriate sublist
+ */
+
+ for ( i = 0; ; ++i ) {
+
+ /*
+ * Take the sublist apart.
+ */
+
+ result = Tcl_ListObjGetElements( interp, listPtr,
+ &elemCount, &elemPtrs );
+ if ( result != TCL_OK ) {
+ break;
+ }
+ listPtr->internalRep.twoPtrValue.ptr2 = (VOID *) chainPtr;
+
+ /*
+ * Reconstitute the index array
+ */
+
+ result = Tcl_ListObjGetElements( interp, indexArgPtr,
+ &indexCount, &indices );
+ if ( result != TCL_OK ) {
+ /*
+ * Shouldn't be able to get here, because we already
+ * parsed the thing successfully once.
+ */
+ break;
+ }
+
+ /*
+ * Determine the index of the requested element.
+ */
+
+ result = TclGetIntForIndex( interp, indices[ i ],
+ (elemCount - 1), &index );
+ if ( result != TCL_OK ) {
+ break;
+ }
+
+ /*
+ * Check that the index is in range.
+ */
+
+ if ( ( index < 0 ) || ( index >= elemCount ) ) {
+ Tcl_SetObjResult( interp,
+ Tcl_NewStringObj( "list index out of range",
+ -1 ) );
+ result = TCL_ERROR;
+ break;
+ }
+
+ /*
+ * Break the loop after extracting the innermost sublist
+ */
+
+ if ( i >= indexCount-1 ) {
+ result = TCL_OK;
+ break;
+ }
+
+ /*
+ * Extract the appropriate sublist, and make sure that it is unshared.
+ */
+
+ subListPtr = elemPtrs[ index ];
+ if ( Tcl_IsShared( subListPtr ) ) {
+ subListPtr = Tcl_DuplicateObj( subListPtr );
+ result = TclListObjSetElement( interp, listPtr, index,
+ subListPtr );
+ if ( result != TCL_OK ) {
+ /*
+ * We actually shouldn't be able to get here, because
+ * we've already checked everything that TclListObjSetElement
+ * checks. If we were to get here, it would result in leaking
+ * subListPtr.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Chain the current sublist onto the linked list of Tcl_Obj's
+ * whose string reps must be spoilt.
+ */
+
+ chainPtr = listPtr;
+ listPtr = subListPtr;
+
+ }
+
+ /*
+ * Store the new element into the correct slot in the innermost sublist.
+ */
+
+ if ( result == TCL_OK ) {
+ result = TclListObjSetElement( interp, listPtr, index, valuePtr );
+ }
+
+ if ( result == TCL_OK ) {
+
+ listPtr->internalRep.twoPtrValue.ptr2 = (VOID *) chainPtr;
+
+ /* Spoil all the string reps */
+
+ while ( listPtr != NULL ) {
+ subListPtr = (Tcl_Obj *) listPtr->internalRep.twoPtrValue.ptr2;
+ Tcl_InvalidateStringRep( listPtr );
+ listPtr->internalRep.twoPtrValue.ptr2 = NULL;
+ listPtr = subListPtr;
+ }
+
+ /* Return the new list if everything worked. */
+
+ if ( !duplicated ) {
+ Tcl_IncrRefCount( retValuePtr );
+ }
+ return retValuePtr;
+ }
+
+ /* Clean up the one dangling reference otherwise */
+
+ if ( duplicated ) {
+ Tcl_DecrRefCount( retValuePtr );
+ }
+ return NULL;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclLsetFlat --
+ *
+ * Core of the 'lset' command when objc>=5. Objv[2], ... ,
+ * objv[objc-2] contain scalar indices.
+ *
+ * Results:
+ * Returns the new value of the list variable, or NULL if an
+ * error occurs.
+ *
+ * Side effects:
+ * Surgery is performed on the list value to produce the
+ * result.
+ *
+ * On entry, the reference count of the variable value does not reflect
+ * any references held on the stack. The first action of this function
+ * is to determine whether the object is shared, and to duplicate it if
+ * it is. The reference count of the duplicate is incremented.
+ * At this point, the reference count will be 1 for either case, so that
+ * the object will appear to be unshared.
+ *
+ * If an error occurs, and the object has been duplicated, the reference
+ * count on the duplicate is decremented so that it is now 0: this dismisses
+ * any memory that was allocated by this procedure.
+ *
+ * If no error occurs, the reference count of the original object is
+ * incremented if the object has not been duplicated, and nothing is
+ * done to a reference count of the duplicate. Now the reference count
+ * of an unduplicated object is 2 (the returned pointer, plus the one
+ * stored in the variable). The reference count of a duplicate object
+ * is 1, reflecting that the returned pointer is the only active
+ * reference. The caller is expected to store the returned value back
+ * in the variable and decrement its reference count. (INST_STORE_*
+ * does exactly this.)
+ *
+ * Tcl_LsetList and related functions maintain a linked list of
+ * Tcl_Obj's whose string representations must be spoilt by threading
+ * via 'ptr2' of the two-pointer internal representation. On entry
+ * to Tcl_LsetList, the values of 'ptr2' are immaterial; on exit,
+ * the 'ptr2' field of any Tcl_Obj that has been modified is set to
+ * NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+TclLsetFlat( interp, listPtr, indexCount, indexArray, valuePtr )
+ Tcl_Interp* interp; /* Tcl interpreter */
+ Tcl_Obj* listPtr; /* Pointer to the list being modified */
+ int indexCount; /* Number of index args */
+ Tcl_Obj *CONST indexArray[];
+ /* Index args */
+ Tcl_Obj* valuePtr; /* Value arg to 'lset' */
+{
+
+ int duplicated; /* Flag == 1 if the obj has been
+ * duplicated, 0 otherwise */
+ Tcl_Obj* retValuePtr; /* Pointer to the list to be returned */
+
+ int elemCount; /* Length of one sublist being changed */
+ Tcl_Obj** elemPtrs; /* Pointers to the elements of a sublist */
+
+ Tcl_Obj* subListPtr; /* Pointer to the current sublist */
+
+ int index; /* Index of the element to replace in the
+ * current sublist */
+ Tcl_Obj* chainPtr; /* Pointer to the enclosing list of
+ * the current sublist. */
+
+ int result; /* Status return from library calls */
+
+
+
+ int i;
+
+ /*
+ * If there are no indices, then simply return the new value,
+ * counting the returned pointer as a reference
+ */
+
+ if ( indexCount == 0 ) {
+ Tcl_IncrRefCount( valuePtr );
+ return valuePtr;
+ }
+
+ /*
+ * If the list is shared, make a private copy.
+ */
+
+ if ( Tcl_IsShared( listPtr ) ) {
+ duplicated = 1;
+ listPtr = Tcl_DuplicateObj( listPtr );
+ Tcl_IncrRefCount( listPtr );
+ } else {
+ duplicated = 0;
+ }
+
+ /*
+ * Anchor the linked list of Tcl_Obj's whose string reps must be
+ * invalidated if the operation succeeds.
+ */
+
+ retValuePtr = listPtr;
+ chainPtr = NULL;
+
+ /*
+ * Handle each index arg by diving into the appropriate sublist
+ */
+
+ for ( i = 0; ; ++i ) {
+
+ /*
+ * Take the sublist apart.
+ */
+
+ result = Tcl_ListObjGetElements( interp, listPtr,
+ &elemCount, &elemPtrs );
+ if ( result != TCL_OK ) {
+ break;
+ }
+ listPtr->internalRep.twoPtrValue.ptr2 = (VOID *) chainPtr;
+
+ /*
+ * Determine the index of the requested element.
+ */
+
+ result = TclGetIntForIndex( interp, indexArray[ i ],
+ (elemCount - 1), &index );
+ if ( result != TCL_OK ) {
+ break;
+ }
+
+ /*
+ * Check that the index is in range.
+ */
+
+ if ( ( index < 0 ) || ( index >= elemCount ) ) {
+ Tcl_SetObjResult( interp,
+ Tcl_NewStringObj( "list index out of range",
+ -1 ) );
+ result = TCL_ERROR;
+ break;
+ }
+
+ /*
+ * Break the loop after extracting the innermost sublist
+ */
+
+ if ( i >= indexCount-1 ) {
+ result = TCL_OK;
+ break;
+ }
+
+ /*
+ * Extract the appropriate sublist, and make sure that it is unshared.
+ */
+
+ subListPtr = elemPtrs[ index ];
+ if ( Tcl_IsShared( subListPtr ) ) {
+ subListPtr = Tcl_DuplicateObj( subListPtr );
+ result = TclListObjSetElement( interp, listPtr, index,
+ subListPtr );
+ if ( result != TCL_OK ) {
+ /*
+ * We actually shouldn't be able to get here.
+ * If we do, it would result in leaking subListPtr,
+ * but everything's been validated already; the error
+ * exit from TclListObjSetElement should never happen.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Chain the current sublist onto the linked list of Tcl_Obj's
+ * whose string reps must be spoilt.
+ */
+
+ chainPtr = listPtr;
+ listPtr = subListPtr;
+
+ }
+
+ /* Store the result in the list element */
+
+ if ( result == TCL_OK ) {
+ result = TclListObjSetElement( interp, listPtr, index, valuePtr );
+ }
+
+ if ( result == TCL_OK ) {
+
+ listPtr->internalRep.twoPtrValue.ptr2 = (VOID *) chainPtr;
+
+ /* Spoil all the string reps */
+
+ while ( listPtr != NULL ) {
+ subListPtr = (Tcl_Obj *) listPtr->internalRep.twoPtrValue.ptr2;
+ Tcl_InvalidateStringRep( listPtr );
+ listPtr->internalRep.twoPtrValue.ptr2 = NULL;
+ listPtr = subListPtr;
+ }
+
+ /* Return the new list if everything worked. */
+
+ if ( !duplicated ) {
+ Tcl_IncrRefCount( retValuePtr );
+ }
+ return retValuePtr;
+ }
+
+ /* Clean up the one dangling reference otherwise */
+
+ if ( duplicated ) {
+ Tcl_DecrRefCount( retValuePtr );
+ }
+ return NULL;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclListObjSetElement --
+ *
+ * Set a single element of a list to a specified value
+ *
+ * Results:
+ *
+ * The return value is normally TCL_OK. If listPtr does not
+ * refer to a list object and cannot be converted to one, TCL_ERROR
+ * is returned and an error message will be left in the interpreter
+ * result if interp is not NULL. Similarly, if index designates
+ * an element outside the range [0..listLength-1], where
+ * listLength is the count of elements in the list object designated
+ * by listPtr, TCL_ERROR is returned and an error message is left
+ * in the interpreter result.
+ *
+ * Side effects:
+ *
+ * Panics if listPtr designates a shared object. Otherwise, attempts
+ * to convert it to a list. Decrements the ref count of the object
+ * at the specified index within the list, replaces with the
+ * object designated by valuePtr, and increments the ref count
+ * of the replacement object.
+ *
+ * It is the caller's responsibility to invalidate the string
+ * representation of the object.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclListObjSetElement( interp, listPtr, index, valuePtr )
+ Tcl_Interp* interp; /* Tcl interpreter; used for error reporting
+ * if not NULL */
+ Tcl_Obj* listPtr; /* List object in which element should be
+ * stored */
+ int index; /* Index of element to store */
+ Tcl_Obj* valuePtr; /* Tcl object to store in the designated
+ * list element */
+{
+ int result; /* Return value from this function */
+ List* listRepPtr; /* Internal representation of the list
+ * being modified */
+ Tcl_Obj** elemPtrs; /* Pointers to elements of the list */
+ int elemCount; /* Number of elements in the list */
+
+ /* Ensure that the listPtr parameter designates an unshared list */
+
+ if ( Tcl_IsShared( listPtr ) ) {
+ panic( "Tcl_ListObjSetElement called with shared object" );
+ }
+ if ( listPtr->typePtr != &tclListType ) {
+ result = SetListFromAny( interp, listPtr );
+ if ( result != TCL_OK ) {
+ return result;
+ }
+ }
+ listRepPtr = (List*) listPtr->internalRep.twoPtrValue.ptr1;
+ elemPtrs = listRepPtr->elements;
+ elemCount = listRepPtr->elemCount;
+
+ /* Ensure that the index is in bounds */
+
+ if ( index < 0 || index >= elemCount ) {
+ if ( interp != NULL ) {
+ Tcl_SetObjResult( interp,
+ Tcl_NewStringObj( "list index out of range",
+ -1 ) );
+ return TCL_ERROR;
+ }
+ }
+
+ /* Add a reference to the new list element */
+
+ Tcl_IncrRefCount( valuePtr );
+
+ /* Remove a reference from the old list element */
+
+ Tcl_DecrRefCount( elemPtrs[ index ] );
+
+ /* Stash the new object in the list */
+
+ elemPtrs[ index ] = valuePtr;
+
+ return TCL_OK;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* FreeListInternalRep --
*
* Deallocate the storage associated with a list object's internal
@@ -772,7 +1366,7 @@ Tcl_ListObjReplace(interp, listPtr, first, count, objc, objv)
*
* Side effects:
* Frees listPtr's List* internal representation and sets listPtr's
- * internalRep.otherValuePtr to NULL. Decrements the ref counts
+ * internalRep.twoPtrValue.ptr1 to NULL. Decrements the ref counts
* of all element objects, which may free them.
*
*----------------------------------------------------------------------
@@ -782,7 +1376,7 @@ static void
FreeListInternalRep(listPtr)
Tcl_Obj *listPtr; /* List object with internal rep to free. */
{
- register List *listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+ register List *listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
register Tcl_Obj **elemPtrs = listRepPtr->elements;
register Tcl_Obj *objPtr;
int numElems = listRepPtr->elemCount;
@@ -794,6 +1388,9 @@ FreeListInternalRep(listPtr)
}
ckfree((char *) elemPtrs);
ckfree((char *) listRepPtr);
+
+ listPtr->internalRep.twoPtrValue.ptr1 = NULL;
+ listPtr->internalRep.twoPtrValue.ptr2 = NULL;
}
/*
@@ -823,7 +1420,7 @@ DupListInternalRep(srcPtr, copyPtr)
Tcl_Obj *srcPtr; /* Object with internal rep to copy. */
Tcl_Obj *copyPtr; /* Object with internal rep to set. */
{
- List *srcListRepPtr = (List *) srcPtr->internalRep.otherValuePtr;
+ List *srcListRepPtr = (List *) srcPtr->internalRep.twoPtrValue.ptr1;
int numElems = srcListRepPtr->elemCount;
int maxElems = srcListRepPtr->maxElemCount;
register Tcl_Obj **srcElemPtrs = srcListRepPtr->elements;
@@ -849,7 +1446,8 @@ DupListInternalRep(srcPtr, copyPtr)
copyListRepPtr->elemCount = numElems;
copyListRepPtr->elements = copyElemPtrs;
- copyPtr->internalRep.otherValuePtr = (VOID *) copyListRepPtr;
+ copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) copyListRepPtr;
+ copyPtr->internalRep.twoPtrValue.ptr2 = NULL;
copyPtr->typePtr = &tclListType;
}
@@ -975,7 +1573,8 @@ SetListFromAny(interp, objPtr)
oldTypePtr->freeIntRepProc(objPtr);
}
- objPtr->internalRep.otherValuePtr = (VOID *) listRepPtr;
+ objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+ objPtr->internalRep.twoPtrValue.ptr2 = NULL;
objPtr->typePtr = &tclListType;
return TCL_OK;
}
@@ -1007,7 +1606,7 @@ UpdateStringOfList(listPtr)
{
# define LOCAL_SIZE 20
int localFlags[LOCAL_SIZE], *flagPtr;
- List *listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+ List *listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
int numElems = listRepPtr->elemCount;
register int i;
char *elem, *dst;
diff --git a/tcl/generic/tclLiteral.c b/tcl/generic/tclLiteral.c
index 37b1d33aa84..bee26f4251a 100644
--- a/tcl/generic/tclLiteral.c
+++ b/tcl/generic/tclLiteral.c
@@ -696,31 +696,10 @@ TclReleaseLiteral(interp, objPtr)
entryPtr->refCount--;
/*
- * We found the matching LiteralEntry. Check if it's only being
- * kept alive only by a circular reference from a ByteCode
- * stored as its internal rep.
- */
-
- if ((entryPtr->refCount == 1)
- && (objPtr->typePtr == &tclByteCodeType)) {
- codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
- if ((codePtr->numLitObjects == 1)
- && (codePtr->objArrayPtr[0] == objPtr)) {
- entryPtr->refCount = 0;
-
- /*
- * Set the ByteCode object array entry NULL to signal
- * to TclCleanupByteCode to not try to release this
- * about to be freed literal again.
- */
-
- codePtr->objArrayPtr[0] = NULL;
- }
- }
-
- /*
* If the literal is no longer being used by any ByteCode,
- * delete the entry then decrement the ref count of its object.
+ * delete the entry then remove the reference corresponding
+ * to the global literal table entry (decrement the ref count
+ * of the object).
*/
if (entryPtr->refCount == 0) {
@@ -729,27 +708,40 @@ TclReleaseLiteral(interp, objPtr)
} else {
prevPtr->nextPtr = entryPtr->nextPtr;
}
-#ifdef TCL_COMPILE_STATS
- iPtr->stats.currentLitStringBytes -= (double) (length + 1);
-#endif /*TCL_COMPILE_STATS*/
ckfree((char *) entryPtr);
globalTablePtr->numEntries--;
+ TclDecrRefCount(objPtr);
+
/*
- * Remove the reference corresponding to the global
- * literal table entry.
+ * Check if the LiteralEntry is only being kept alive by
+ * a circular reference from a ByteCode stored as its
+ * internal rep. In that case, set the ByteCode object array
+ * entry NULL to signal to TclCleanupByteCode to not try to
+ * release this about to be freed literal again.
*/
+
+ if (objPtr->typePtr == &tclByteCodeType) {
+ codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr;
+ if ((codePtr->numLitObjects == 1)
+ && (codePtr->objArrayPtr[0] == objPtr)) {
+ codePtr->objArrayPtr[0] = NULL;
+ }
+ }
- TclDecrRefCount(objPtr);
+#ifdef TCL_COMPILE_STATS
+ iPtr->stats.currentLitStringBytes -= (double) (length + 1);
+#endif /*TCL_COMPILE_STATS*/
}
break;
}
}
-
+
/*
* Remove the reference corresponding to the local literal table
* entry.
*/
+
Tcl_DecrRefCount(objPtr);
}
diff --git a/tcl/generic/tclLoad.c b/tcl/generic/tclLoad.c
index 81e963a6e86..eb3dbefeddf 100644
--- a/tcl/generic/tclLoad.c
+++ b/tcl/generic/tclLoad.c
@@ -19,7 +19,8 @@
* either dynamically (with the "load" command) or statically (as
* indicated by a call to TclGetLoadedPackages). All such packages
* are linked together into a single list for the process. Packages
- * are never unloaded, so these structures are never freed.
+ * are never unloaded, until the application exits, when
+ * TclFinalizeLoad is called, and these structures are freed.
*/
typedef struct LoadedPackage {
@@ -31,8 +32,8 @@ typedef struct LoadedPackage {
* properly capitalized (first letter UC,
* others LC), no "_", as in "Net".
* Malloc-ed. */
- ClientData clientData; /* Token for the loaded file which should be
- * passed to TclpUnloadFile() when the file
+ Tcl_LoadHandle loadHandle; /* Token for the loaded file which should be
+ * passed to (*unLoadProcPtr)() when the file
* is no longer needed. If fileName is NULL,
* then this field is irrelevant. */
Tcl_PackageInitProc *initProc;
@@ -46,6 +47,11 @@ typedef struct LoadedPackage {
* untrusted scripts). NULL means the
* package can't be used in unsafe
* interpreters. */
+ Tcl_FSUnloadFileProc *unLoadProcPtr;
+ /* Procedure to use to unload this package.
+ * If NULL, then we do not attempt to unload
+ * the package. If fileName is NULL, then
+ * this field is irrelevant. */
struct LoadedPackage *nextPtr;
/* Next in list of all packages loaded into
* this application process. NULL means
@@ -113,12 +119,13 @@ Tcl_LoadObjCmd(dummy, interp, objc, objv)
{
Tcl_Interp *target;
LoadedPackage *pkgPtr, *defaultPtr;
- Tcl_DString pkgName, tmp, initName, safeInitName, fileName;
+ Tcl_DString pkgName, tmp, initName, safeInitName;
Tcl_PackageInitProc *initProc, *safeInitProc;
InterpPackage *ipFirstPtr, *ipPtr;
int code, namesMatch, filesMatch;
- char *p, *tempString, *fullFileName, *packageName;
- ClientData clientData;
+ char *p, *fullFileName, *packageName;
+ Tcl_LoadHandle loadHandle;
+ Tcl_FSUnloadFileProc *unLoadProcPtr = NULL;
Tcl_UniChar ch;
int offset;
@@ -126,11 +133,11 @@ Tcl_LoadObjCmd(dummy, interp, objc, objv)
Tcl_WrongNumArgs(interp, 1, objv, "fileName ?packageName? ?interp?");
return TCL_ERROR;
}
- tempString = Tcl_GetString(objv[1]);
- fullFileName = Tcl_TranslateFileName(interp, tempString, &fileName);
- if (fullFileName == NULL) {
+ if (Tcl_FSConvertToPathType(interp, objv[1]) != TCL_OK) {
return TCL_ERROR;
}
+ fullFileName = Tcl_GetString(objv[1]);
+
Tcl_DStringInit(&pkgName);
Tcl_DStringInit(&initName);
Tcl_DStringInit(&safeInitName);
@@ -265,8 +272,10 @@ Tcl_LoadObjCmd(dummy, interp, objc, objv)
*/
retc = TclGuessPackageName(fullFileName, &pkgName);
if (!retc) {
- int pargc;
- char **pargv, *pkgGuess;
+ Tcl_Obj *splitPtr;
+ Tcl_Obj *pkgGuessPtr;
+ int pElements;
+ char *pkgGuess;
/*
* The platform-specific code couldn't figure out the
@@ -276,8 +285,9 @@ Tcl_LoadObjCmd(dummy, interp, objc, objv)
* characters that follow that.
*/
- Tcl_SplitPath(fullFileName, &pargc, &pargv);
- pkgGuess = pargv[pargc-1];
+ splitPtr = Tcl_FSSplitPath(objv[1], &pElements);
+ Tcl_ListObjIndex(NULL, splitPtr, pElements -1, &pkgGuessPtr);
+ pkgGuess = Tcl_GetString(pkgGuessPtr);
if ((pkgGuess[0] == 'l') && (pkgGuess[1] == 'i')
&& (pkgGuess[2] == 'b')) {
pkgGuess += 3;
@@ -291,7 +301,7 @@ Tcl_LoadObjCmd(dummy, interp, objc, objv)
}
}
if (p == pkgGuess) {
- ckfree((char *)pargv);
+ Tcl_DecrRefCount(splitPtr);
Tcl_AppendResult(interp,
"couldn't figure out package name for ",
fullFileName, (char *) NULL);
@@ -299,7 +309,7 @@ Tcl_LoadObjCmd(dummy, interp, objc, objv)
goto done;
}
Tcl_DStringAppend(&pkgName, pkgGuess, (p - pkgGuess));
- ckfree((char *)pargv);
+ Tcl_DecrRefCount(splitPtr);
}
}
@@ -328,9 +338,9 @@ Tcl_LoadObjCmd(dummy, interp, objc, objv)
*/
Tcl_MutexLock(&packageMutex);
- code = TclpLoadFile(interp, fullFileName, Tcl_DStringValue(&initName),
+ code = Tcl_FSLoadFile(interp, objv[1], Tcl_DStringValue(&initName),
Tcl_DStringValue(&safeInitName), &initProc, &safeInitProc,
- &clientData);
+ &loadHandle,&unLoadProcPtr);
Tcl_MutexUnlock(&packageMutex);
if (code != TCL_OK) {
goto done;
@@ -338,7 +348,9 @@ Tcl_LoadObjCmd(dummy, interp, objc, objv)
if (initProc == NULL) {
Tcl_AppendResult(interp, "couldn't find procedure ",
Tcl_DStringValue(&initName), (char *) NULL);
- TclpUnloadFile(clientData);
+ if (unLoadProcPtr != NULL) {
+ (*unLoadProcPtr)(loadHandle);
+ }
code = TCL_ERROR;
goto done;
}
@@ -354,7 +366,8 @@ Tcl_LoadObjCmd(dummy, interp, objc, objv)
pkgPtr->packageName = (char *) ckalloc((unsigned)
(Tcl_DStringLength(&pkgName) + 1));
strcpy(pkgPtr->packageName, Tcl_DStringValue(&pkgName));
- pkgPtr->clientData = clientData;
+ pkgPtr->loadHandle = loadHandle;
+ pkgPtr->unLoadProcPtr = unLoadProcPtr;
pkgPtr->initProc = initProc;
pkgPtr->safeInitProc = safeInitProc;
Tcl_MutexLock(&packageMutex);
@@ -410,7 +423,6 @@ Tcl_LoadObjCmd(dummy, interp, objc, objv)
Tcl_DStringFree(&pkgName);
Tcl_DStringFree(&initName);
Tcl_DStringFree(&safeInitName);
- Tcl_DStringFree(&fileName);
Tcl_DStringFree(&tmp);
return code;
}
@@ -439,7 +451,7 @@ Tcl_StaticPackage(interp, pkgName, initProc, safeInitProc)
* package has already been loaded
* into the given interpreter by
* calling the appropriate init proc. */
- char *pkgName; /* Name of package (must be properly
+ CONST char *pkgName; /* Name of package (must be properly
* capitalized: first letter upper
* case, others lower case). */
Tcl_PackageInitProc *initProc; /* Procedure to call to incorporate
@@ -478,7 +490,7 @@ Tcl_StaticPackage(interp, pkgName, initProc, safeInitProc)
pkgPtr->packageName = (char *) ckalloc((unsigned)
(strlen(pkgName) + 1));
strcpy(pkgPtr->packageName, pkgName);
- pkgPtr->clientData = NULL;
+ pkgPtr->loadHandle = NULL;
pkgPtr->initProc = initProc;
pkgPtr->safeInitProc = safeInitProc;
Tcl_MutexLock(&packageMutex);
@@ -653,7 +665,10 @@ TclFinalizeLoad()
* call a function in the dll after it's been unloaded.
*/
if (pkgPtr->fileName[0] != '\0') {
- TclpUnloadFile(pkgPtr->clientData);
+ Tcl_FSUnloadFileProc *unLoadProcPtr = pkgPtr->unLoadProcPtr;
+ if (unLoadProcPtr != NULL) {
+ (*unLoadProcPtr)(pkgPtr->loadHandle);
+ }
}
#endif
ckfree(pkgPtr->fileName);
diff --git a/tcl/generic/tclLoadNone.c b/tcl/generic/tclLoadNone.c
index 35180f5ff52..480331b3e34 100644
--- a/tcl/generic/tclLoadNone.c
+++ b/tcl/generic/tclLoadNone.c
@@ -18,7 +18,7 @@
/*
*----------------------------------------------------------------------
*
- * TclpLoadFile --
+ * TclpDlopen --
*
* This procedure is called to carry out dynamic loading of binary
* code; it is intended for use only on systems that don't support
@@ -35,18 +35,17 @@
*/
int
-TclpLoadFile(interp, fileName, sym1, sym2, proc1Ptr, proc2Ptr, clientDataPtr)
+TclpDlopen(interp, pathPtr, loadHandle, unloadProcPtr)
Tcl_Interp *interp; /* Used for error reporting. */
- char *fileName; /* Name of the file containing the desired
- * code. */
- char *sym1, *sym2; /* Names of two procedures to look up in
- * the file's symbol table. */
- Tcl_PackageInitProc **proc1Ptr, **proc2Ptr;
- /* Where to return the addresses corresponding
- * to sym1 and sym2. */
- ClientData *clientDataPtr; /* Filled with token for dynamically loaded
+ Tcl_Obj *pathPtr; /* Name of the file containing the desired
+ * code (UTF-8). */
+ Tcl_LoadHandle *loadHandle; /* Filled with token for dynamically loaded
* file which will be passed back to
- * TclpUnloadFile() to unload the file. */
+ * (*unloadProcPtr)() to unload the file. */
+ Tcl_FSUnloadFileProc **unloadProcPtr;
+ /* Filled with address of Tcl_FSUnloadFileProc
+ * function which should be used for
+ * this file. */
{
Tcl_SetResult(interp,
"dynamic loading is not currently available on this system",
@@ -57,6 +56,30 @@ TclpLoadFile(interp, fileName, sym1, sym2, proc1Ptr, proc2Ptr, clientDataPtr)
/*
*----------------------------------------------------------------------
*
+ * TclpFindSymbol --
+ *
+ * Looks up a symbol, by name, through a handle associated with
+ * a previously loaded piece of code (shared library).
+ *
+ * Results:
+ * Returns a pointer to the function associated with 'symbol' if
+ * it is found. Otherwise returns NULL and may leave an error
+ * message in the interp's result.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_PackageInitProc*
+TclpFindSymbol(interp, loadHandle, symbol)
+ Tcl_Interp *interp;
+ Tcl_LoadHandle loadHandle;
+ CONST char *symbol;
+{
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclGuessPackageName --
*
* If the "load" command is invoked without providing a package
@@ -76,7 +99,7 @@ TclpLoadFile(interp, fileName, sym1, sym2, proc1Ptr, proc2Ptr, clientDataPtr)
int
TclGuessPackageName(fileName, bufPtr)
- char *fileName; /* Name of file containing package (already
+ CONST char *fileName; /* Name of file containing package (already
* translated to local form if needed). */
Tcl_DString *bufPtr; /* Initialized empty dstring. Append
* package name to this if possible. */
@@ -103,10 +126,10 @@ TclGuessPackageName(fileName, bufPtr)
*/
void
-TclpUnloadFile(clientData)
- ClientData clientData; /* ClientData returned by a previous call
- * to TclpLoadFile(). The clientData is
- * a token that represents the loaded
- * file. */
+TclpUnloadFile(loadHandle)
+ Tcl_LoadHandle loadHandle; /* loadHandle returned by a previous call
+ * to TclpDlopen(). The loadHandle is
+ * a token that represents the loaded
+ * file. */
{
}
diff --git a/tcl/generic/tclMain.c b/tcl/generic/tclMain.c
index a89d0caf3f3..eedbd8d4a44 100644
--- a/tcl/generic/tclMain.c
+++ b/tcl/generic/tclMain.c
@@ -5,6 +5,7 @@
*
* Copyright (c) 1988-1994 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 2000 Ajuba Solutions.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -19,35 +20,111 @@
# define TCL_STORAGE_CLASS DLLEXPORT
/*
- * The following code ensures that tclLink.c is linked whenever
- * Tcl is linked. Without this code there's no reference to the
- * code in that file from anywhere in Tcl, so it may not be
- * linked into the application.
- */
-
-EXTERN int Tcl_LinkVar();
-int (*tclDummyLinkVarPtr)() = Tcl_LinkVar;
-
-/*
* Declarations for various library procedures and variables (don't want
* to include tclPort.h here, because people might copy this file out of
* the Tcl source directory to make their own modified versions).
- * Note: "exit" should really be declared here, but there's no way to
- * declare it without causing conflicts with other definitions elsewher
- * on some systems, so it's better just to leave it out.
*/
+#if !defined(MAC_TCL)
extern int isatty _ANSI_ARGS_((int fd));
-extern char * strcpy _ANSI_ARGS_((char *dst, CONST char *src));
+#else
+#include <unistd.h>
+#endif
+
+static Tcl_Obj *tclStartupScriptPath = NULL;
-static char *tclStartupScriptFileName = NULL;
+static Tcl_MainLoopProc *mainLoopProc = NULL;
+/*
+ * Structure definition for information used to keep the state of
+ * an interactive command processor that reads lines from standard
+ * input and writes prompts and results to standard output.
+ */
+
+typedef enum {
+ PROMPT_NONE, /* Print no prompt */
+ PROMPT_START, /* Print prompt for command start */
+ PROMPT_CONTINUE /* Print prompt for command continuation */
+} PromptType;
+
+typedef struct InteractiveState {
+ Tcl_Channel input; /* The standard input channel from which
+ * lines are read. */
+ int tty; /* Non-zero means standard input is a
+ * terminal-like device. Zero means it's
+ * a file. */
+ Tcl_Obj *commandPtr; /* Used to assemble lines of input into
+ * Tcl commands. */
+ PromptType prompt; /* Next prompt to print */
+ Tcl_Interp *interp; /* Interpreter that evaluates interactive
+ * commands. */
+} InteractiveState;
+
+/*
+ * Forward declarations for procedures defined later in this file.
+ */
+static void Prompt _ANSI_ARGS_((Tcl_Interp *interp,
+ PromptType *promptPtr));
+static void StdinProc _ANSI_ARGS_((ClientData clientData,
+ int mask));
/*
*----------------------------------------------------------------------
*
+ * TclSetStartupScriptPath --
+ *
+ * Primes the startup script VFS path, used to override the
+ * command line processing.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * This procedure initializes the VFS path of the Tcl script to
+ * run at startup.
+ *
+ *----------------------------------------------------------------------
+ */
+void TclSetStartupScriptPath(pathPtr)
+ Tcl_Obj *pathPtr;
+{
+ if (tclStartupScriptPath != NULL) {
+ Tcl_DecrRefCount(tclStartupScriptPath);
+ }
+ tclStartupScriptPath = pathPtr;
+ if (tclStartupScriptPath != NULL) {
+ Tcl_IncrRefCount(tclStartupScriptPath);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclGetStartupScriptPath --
+ *
+ * Gets the startup script VFS path, used to override the
+ * command line processing.
+ *
+ * Results:
+ * The startup script VFS path, NULL if none has been set.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Obj *TclGetStartupScriptPath()
+{
+ return tclStartupScriptPath;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclSetStartupScriptFileName --
*
* Primes the startup script file name, used to override the
@@ -63,9 +140,10 @@ static char *tclStartupScriptFileName = NULL;
*----------------------------------------------------------------------
*/
void TclSetStartupScriptFileName(fileName)
- char *fileName;
+ CONST char *fileName;
{
- tclStartupScriptFileName = fileName;
+ Tcl_Obj *pathPtr = Tcl_NewStringObj(fileName,-1);
+ TclSetStartupScriptPath(pathPtr);
}
@@ -85,9 +163,14 @@ void TclSetStartupScriptFileName(fileName)
*
*----------------------------------------------------------------------
*/
-char *TclGetStartupScriptFileName()
+CONST char *TclGetStartupScriptFileName()
{
- return tclStartupScriptFileName;
+ Tcl_Obj *pathPtr = TclGetStartupScriptPath();
+
+ if (pathPtr == NULL) {
+ return NULL;
+ }
+ return Tcl_GetString(pathPtr);
}
@@ -101,7 +184,7 @@ char *TclGetStartupScriptFileName()
*
* Results:
* None. This procedure never returns (it exits the process when
- * it's done.
+ * it's done).
*
* Side effects:
* This procedure initializes the Tcl world and then starts
@@ -123,18 +206,18 @@ Tcl_Main(argc, argv, appInitProc)
{
Tcl_Obj *resultPtr;
Tcl_Obj *commandPtr = NULL;
- char buffer[1000], *args;
- int code, gotPartial, tty, length;
+ char buffer[TCL_INTEGER_SPACE + 5], *args;
+ PromptType prompt = PROMPT_START;
+ int code, length, tty;
int exitCode = 0;
Tcl_Channel inChannel, outChannel, errChannel;
Tcl_Interp *interp;
Tcl_DString argString;
Tcl_FindExecutable(argv[0]);
+
interp = Tcl_CreateInterp();
-#ifdef TCL_MEM_DEBUG
Tcl_InitMemory(interp);
-#endif
/*
* Make command-line arguments available in the Tcl variables "argc"
@@ -142,27 +225,34 @@ Tcl_Main(argc, argv, appInitProc)
* strip it off and use it as the name of a script file to process.
*/
- if (tclStartupScriptFileName == NULL) {
+ if (TclGetStartupScriptPath() == NULL) {
if ((argc > 1) && (argv[1][0] != '-')) {
- tclStartupScriptFileName = argv[1];
+ TclSetStartupScriptFileName(argv[1]);
argc--;
argv++;
}
}
- args = Tcl_Merge(argc-1, argv+1);
+
+ /*
+ * The CONST casting is safe, and better we do it here than force
+ * all callers of Tcl_Main to do it. (Those callers are likely
+ * in a main() that can't easily change its signature.)
+ */
+
+ args = Tcl_Merge(argc-1, (CONST char **)argv+1);
Tcl_ExternalToUtfDString(NULL, args, -1, &argString);
Tcl_SetVar(interp, "argv", Tcl_DStringValue(&argString), TCL_GLOBAL_ONLY);
Tcl_DStringFree(&argString);
ckfree(args);
- if (tclStartupScriptFileName == NULL) {
+ if (TclGetStartupScriptPath() == NULL) {
Tcl_ExternalToUtfDString(NULL, argv[0], -1, &argString);
} else {
- tclStartupScriptFileName = Tcl_ExternalToUtfDString(NULL,
- tclStartupScriptFileName, -1, &argString);
+ TclSetStartupScriptFileName(Tcl_ExternalToUtfDString(NULL,
+ TclGetStartupScriptFileName(), -1, &argString));
}
- TclFormatInt(buffer, argc-1);
+ TclFormatInt(buffer, (long) argc-1);
Tcl_SetVar(interp, "argc", buffer, TCL_GLOBAL_ONLY);
Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&argString), TCL_GLOBAL_ONLY);
@@ -172,13 +262,14 @@ Tcl_Main(argc, argv, appInitProc)
tty = isatty(0);
Tcl_SetVar(interp, "tcl_interactive",
- ((tclStartupScriptFileName == NULL) && tty) ? "1" : "0",
+ ((TclGetStartupScriptPath() == NULL) && tty) ? "1" : "0",
TCL_GLOBAL_ONLY);
/*
* Invoke application-specific initialization.
*/
+ Tcl_Preserve((ClientData) interp);
if ((*appInitProc)(interp) != TCL_OK) {
errChannel = Tcl_GetStdChannel(TCL_STDERR);
if (errChannel) {
@@ -188,17 +279,21 @@ Tcl_Main(argc, argv, appInitProc)
Tcl_WriteChars(errChannel, "\n", 1);
}
}
+ if (Tcl_InterpDeleted(interp)) {
+ goto done;
+ }
/*
* If a script file was specified then just source that file
* and quit.
*/
- if (tclStartupScriptFileName != NULL) {
- code = Tcl_EvalFile(interp, tclStartupScriptFileName);
+ if (TclGetStartupScriptPath() != NULL) {
+ code = Tcl_FSEvalFile(interp, TclGetStartupScriptPath());
if (code != TCL_OK) {
errChannel = Tcl_GetStdChannel(TCL_STDERR);
if (errChannel) {
+
/*
* The following statement guarantees that the errorInfo
* variable is set properly.
@@ -231,63 +326,68 @@ Tcl_Main(argc, argv, appInitProc)
commandPtr = Tcl_NewObj();
Tcl_IncrRefCount(commandPtr);
+ /*
+ * Get a new value for tty if anyone writes to ::tcl_interactive
+ */
+ Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty, TCL_LINK_BOOLEAN);
inChannel = Tcl_GetStdChannel(TCL_STDIN);
outChannel = Tcl_GetStdChannel(TCL_STDOUT);
- gotPartial = 0;
- while (1) {
+ while ((inChannel != (Tcl_Channel) NULL) && !Tcl_InterpDeleted(interp)) {
if (tty) {
- Tcl_Obj *promptCmdPtr;
-
- promptCmdPtr = Tcl_GetVar2Ex(interp,
- (gotPartial ? "tcl_prompt2" : "tcl_prompt1"),
- NULL, TCL_GLOBAL_ONLY);
- if (promptCmdPtr == NULL) {
- defaultPrompt:
- if (!gotPartial && outChannel) {
- Tcl_WriteChars(outChannel, "% ", 2);
- }
- } else {
- code = Tcl_EvalObjEx(interp, promptCmdPtr, 0);
- inChannel = Tcl_GetStdChannel(TCL_STDIN);
- outChannel = Tcl_GetStdChannel(TCL_STDOUT);
- errChannel = Tcl_GetStdChannel(TCL_STDERR);
- if (code != TCL_OK) {
- if (errChannel) {
- Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
- Tcl_WriteChars(errChannel, "\n", 1);
- }
- Tcl_AddErrorInfo(interp,
- "\n (script that generates prompt)");
- goto defaultPrompt;
- }
+ Prompt(interp, &prompt);
+ if (Tcl_InterpDeleted(interp)) {
+ break;
}
- if (outChannel) {
- Tcl_Flush(outChannel);
+ inChannel = Tcl_GetStdChannel(TCL_STDIN);
+ if (inChannel == (Tcl_Channel) NULL) {
+ break;
}
}
- if (!inChannel) {
- goto done;
+ if (Tcl_IsShared(commandPtr)) {
+ Tcl_DecrRefCount(commandPtr);
+ commandPtr = Tcl_DuplicateObj(commandPtr);
+ Tcl_IncrRefCount(commandPtr);
}
length = Tcl_GetsObj(inChannel, commandPtr);
if (length < 0) {
- goto done;
- }
- if ((length == 0) && Tcl_Eof(inChannel) && (!gotPartial)) {
- goto done;
+ if (Tcl_InputBlocked(inChannel)) {
+
+ /*
+ * This can only happen if stdin has been set to
+ * non-blocking. In that case cycle back and try
+ * again. This sets up a tight polling loop (since
+ * we have no event loop running). If this causes
+ * bad CPU hogging, we might try toggling the blocking
+ * on stdin instead.
+ */
+
+ continue;
+ }
+
+ /*
+ * Either EOF, or an error on stdin; we're done
+ */
+
+ break;
}
/*
* Add the newline removed by Tcl_GetsObj back to the string.
*/
+ if (Tcl_IsShared(commandPtr)) {
+ Tcl_DecrRefCount(commandPtr);
+ commandPtr = Tcl_DuplicateObj(commandPtr);
+ Tcl_IncrRefCount(commandPtr);
+ }
Tcl_AppendToObj(commandPtr, "\n", 1);
if (!TclObjCommandComplete(commandPtr)) {
- gotPartial = 1;
+ prompt = PROMPT_CONTINUE;
continue;
}
- gotPartial = 0;
- code = Tcl_RecordAndEvalObj(interp, commandPtr, 0);
+ prompt = PROMPT_START;
+ code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL);
inChannel = Tcl_GetStdChannel(TCL_STDIN);
outChannel = Tcl_GetStdChannel(TCL_STDOUT);
errChannel = Tcl_GetStdChannel(TCL_STDERR);
@@ -301,31 +401,325 @@ Tcl_Main(argc, argv, appInitProc)
}
} else if (tty) {
resultPtr = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(resultPtr);
Tcl_GetStringFromObj(resultPtr, &length);
if ((length > 0) && outChannel) {
Tcl_WriteObj(outChannel, resultPtr);
Tcl_WriteChars(outChannel, "\n", 1);
}
+ Tcl_DecrRefCount(resultPtr);
+ }
+ if (mainLoopProc != NULL) {
+
+ /*
+ * If a main loop has been defined while running interactively,
+ * we want to start a fileevent based prompt by establishing a
+ * channel handler for stdin.
+ */
+
+ InteractiveState *isPtr = NULL;
+
+ if (inChannel) {
+ if (tty) {
+ Prompt(interp, &prompt);
+ }
+ isPtr = (InteractiveState *)
+ ckalloc((int) sizeof(InteractiveState));
+ isPtr->input = inChannel;
+ isPtr->tty = tty;
+ isPtr->commandPtr = commandPtr;
+ isPtr->prompt = prompt;
+ isPtr->interp = interp;
+
+ Tcl_UnlinkVar(interp, "tcl_interactive");
+ Tcl_LinkVar(interp, "tcl_interactive", (char *) &(isPtr->tty),
+ TCL_LINK_BOOLEAN);
+
+ Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc,
+ (ClientData) isPtr);
+ }
+
+ (*mainLoopProc)();
+ mainLoopProc = NULL;
+
+ if (inChannel) {
+ tty = isPtr->tty;
+ Tcl_UnlinkVar(interp, "tcl_interactive");
+ Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty,
+ TCL_LINK_BOOLEAN);
+ prompt = isPtr->prompt;
+ commandPtr = isPtr->commandPtr;
+ if (isPtr->input != (Tcl_Channel) NULL) {
+ Tcl_DeleteChannelHandler(isPtr->input, StdinProc,
+ (ClientData) isPtr);
+ }
+ ckfree((char *)isPtr);
+ }
+ inChannel = Tcl_GetStdChannel(TCL_STDIN);
+ outChannel = Tcl_GetStdChannel(TCL_STDOUT);
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
}
#ifdef TCL_MEM_DEBUG
+
+ /*
+ * This code here only for the (unsupported and deprecated)
+ * [checkmem] command.
+ */
+
if (tclMemDumpFileName != NULL) {
- Tcl_DecrRefCount(commandPtr);
+ mainLoopProc = NULL;
Tcl_DeleteInterp(interp);
- Tcl_Exit(0);
}
#endif
}
+ done:
+ if ((exitCode == 0) && (mainLoopProc != NULL)) {
+
+ /*
+ * If everything has gone OK so far, call the main loop proc,
+ * if it exists. Packages (like Tk) can set it to start processing
+ * events at this point.
+ */
+
+ (*mainLoopProc)();
+ mainLoopProc = NULL;
+ }
+ if (commandPtr != NULL) {
+ Tcl_DecrRefCount(commandPtr);
+ }
+
/*
* Rather than calling exit, invoke the "exit" command so that
* users can replace "exit" with some other command to do additional
* cleanup on exit. The Tcl_Eval call should never return.
*/
- done:
- if (commandPtr != NULL) {
+ if (!Tcl_InterpDeleted(interp)) {
+ sprintf(buffer, "exit %d", exitCode);
+ Tcl_Eval(interp, buffer);
+
+ /*
+ * If Tcl_Eval returns, trying to eval [exit], something
+ * unusual is happening. Maybe interp has been deleted;
+ * maybe [exit] was redefined. We still want to cleanup
+ * and exit.
+ */
+
+ if (!Tcl_InterpDeleted(interp)) {
+ Tcl_DeleteInterp(interp);
+ }
+ }
+ TclSetStartupScriptPath(NULL);
+
+ /*
+ * If we get here, the master interp has been deleted. Allow
+ * its destruction with the last matching Tcl_Release.
+ */
+
+ Tcl_Release((ClientData) interp);
+ Tcl_Exit(exitCode);
+}
+
+/*
+ *---------------------------------------------------------------
+ *
+ * Tcl_SetMainLoop --
+ *
+ * Sets an alternative main loop procedure.
+ *
+ * Results:
+ * Returns the previously defined main loop procedure.
+ *
+ * Side effects:
+ * This procedure will be called before Tcl exits, allowing for
+ * the creation of an event loop.
+ *
+ *---------------------------------------------------------------
+ */
+
+void
+Tcl_SetMainLoop(proc)
+ Tcl_MainLoopProc *proc;
+{
+ mainLoopProc = proc;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StdinProc --
+ *
+ * This procedure is invoked by the event dispatcher whenever
+ * standard input becomes readable. It grabs the next line of
+ * input characters, adds them to a command being assembled, and
+ * executes the command if it's complete.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Could be almost arbitrary, depending on the command that's
+ * typed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+StdinProc(clientData, mask)
+ ClientData clientData; /* The state of interactive cmd line */
+ int mask; /* Not used. */
+{
+ InteractiveState *isPtr = (InteractiveState *) clientData;
+ Tcl_Channel chan = isPtr->input;
+ Tcl_Obj *commandPtr = isPtr->commandPtr;
+ Tcl_Interp *interp = isPtr->interp;
+ int code, length;
+
+ if (Tcl_IsShared(commandPtr)) {
+ Tcl_DecrRefCount(commandPtr);
+ commandPtr = Tcl_DuplicateObj(commandPtr);
+ Tcl_IncrRefCount(commandPtr);
+ }
+ length = Tcl_GetsObj(chan, commandPtr);
+ if (length < 0) {
+ if (Tcl_InputBlocked(chan)) {
+ return;
+ }
+ if (isPtr->tty) {
+ /*
+ * Would be better to find a way to exit the mainLoop?
+ * Or perhaps evaluate [exit]? Leaving as is for now due
+ * to compatibility concerns.
+ */
+ Tcl_Exit(0);
+ }
+ Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) isPtr);
+ return;
+ }
+
+ if (Tcl_IsShared(commandPtr)) {
Tcl_DecrRefCount(commandPtr);
+ commandPtr = Tcl_DuplicateObj(commandPtr);
+ Tcl_IncrRefCount(commandPtr);
+ }
+ Tcl_AppendToObj(commandPtr, "\n", 1);
+ if (!TclObjCommandComplete(commandPtr)) {
+ isPtr->prompt = PROMPT_CONTINUE;
+ goto prompt;
+ }
+ isPtr->prompt = PROMPT_START;
+
+ /*
+ * Disable the stdin channel handler while evaluating the command;
+ * otherwise if the command re-enters the event loop we might
+ * process commands from stdin before the current command is
+ * finished. Among other things, this will trash the text of the
+ * command being evaluated.
+ */
+
+ Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) isPtr);
+ code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL);
+ isPtr->input = chan = Tcl_GetStdChannel(TCL_STDIN);
+ Tcl_DecrRefCount(commandPtr);
+ isPtr->commandPtr = commandPtr = Tcl_NewObj();
+ Tcl_IncrRefCount(commandPtr);
+ if (chan != (Tcl_Channel) NULL) {
+ Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc,
+ (ClientData) isPtr);
+ }
+ if (code != TCL_OK) {
+ Tcl_Channel errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel != (Tcl_Channel) NULL) {
+ Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
+ Tcl_WriteChars(errChannel, "\n", 1);
+ }
+ } else if (isPtr->tty) {
+ Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
+ Tcl_Channel outChannel = Tcl_GetStdChannel(TCL_STDOUT);
+ Tcl_IncrRefCount(resultPtr);
+ Tcl_GetStringFromObj(resultPtr, &length);
+ if ((length >0) && (outChannel != (Tcl_Channel) NULL)) {
+ Tcl_WriteObj(outChannel, resultPtr);
+ Tcl_WriteChars(outChannel, "\n", 1);
+ }
+ Tcl_DecrRefCount(resultPtr);
+ }
+
+ /*
+ * If a tty stdin is still around, output a prompt.
+ */
+
+ prompt:
+ if (isPtr->tty && (isPtr->input != (Tcl_Channel) NULL)) {
+ Prompt(interp, &(isPtr->prompt));
+ isPtr->input = Tcl_GetStdChannel(TCL_STDIN);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Prompt --
+ *
+ * Issue a prompt on standard output, or invoke a script
+ * to issue the prompt.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A prompt gets output, and a Tcl script may be evaluated
+ * in interp.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Prompt(interp, promptPtr)
+ Tcl_Interp *interp; /* Interpreter to use for prompting. */
+ PromptType *promptPtr; /* Points to type of prompt to print.
+ * Filled with PROMPT_NONE after a
+ * prompt is printed. */
+{
+ Tcl_Obj *promptCmdPtr;
+ int code;
+ Tcl_Channel outChannel, errChannel;
+
+ if (*promptPtr == PROMPT_NONE) {
+ return;
+ }
+
+ promptCmdPtr = Tcl_GetVar2Ex(interp,
+ ((*promptPtr == PROMPT_CONTINUE) ? "tcl_prompt2" : "tcl_prompt1"),
+ NULL, TCL_GLOBAL_ONLY);
+ if (Tcl_InterpDeleted(interp)) {
+ return;
+ }
+ if (promptCmdPtr == NULL) {
+ defaultPrompt:
+ outChannel = Tcl_GetStdChannel(TCL_STDOUT);
+ if ((*promptPtr == PROMPT_START)
+ && (outChannel != (Tcl_Channel) NULL)) {
+ Tcl_WriteChars(outChannel, "% ", 2);
+ }
+ } else {
+ code = Tcl_EvalObjEx(interp, promptCmdPtr, TCL_EVAL_GLOBAL);
+ if (code != TCL_OK) {
+ Tcl_AddErrorInfo(interp,
+ "\n (script that generates prompt)");
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel != (Tcl_Channel) NULL) {
+ Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
+ Tcl_WriteChars(errChannel, "\n", 1);
+ }
+ goto defaultPrompt;
+ }
+ }
+ outChannel = Tcl_GetStdChannel(TCL_STDOUT);
+ if (outChannel != (Tcl_Channel) NULL) {
+ Tcl_Flush(outChannel);
}
- sprintf(buffer, "exit %d", exitCode);
- Tcl_Eval(interp, buffer);
+ *promptPtr = PROMPT_NONE;
}
diff --git a/tcl/generic/tclNamesp.c b/tcl/generic/tclNamesp.c
index 38f7d2a794c..b628a35de72 100644
--- a/tcl/generic/tclNamesp.c
+++ b/tcl/generic/tclNamesp.c
@@ -104,6 +104,9 @@ static int NamespaceDeleteCmd _ANSI_ARGS_((
static int NamespaceEvalCmd _ANSI_ARGS_((
ClientData dummy, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[]));
+static int NamespaceExistsCmd _ANSI_ARGS_((
+ ClientData dummy, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]));
static int NamespaceExportCmd _ANSI_ARGS_((
ClientData dummy, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[]));
@@ -163,7 +166,7 @@ Tcl_ObjType tclNsNameType = {
* None.
*
* Side effects:
- * The namespace object type is registered with the Tcl compiler.
+ * None.
*
*----------------------------------------------------------------------
*/
@@ -171,7 +174,9 @@ Tcl_ObjType tclNsNameType = {
void
TclInitNamespaceSubsystem()
{
- Tcl_RegisterObjType(&tclNsNameType);
+ /*
+ * Does nothing for now.
+ */
}
/*
@@ -427,7 +432,7 @@ Tcl_CreateNamespace(interp, name, clientData, deleteProc)
Tcl_Interp *interp; /* Interpreter in which a new namespace
* is being created. Also used for
* error reporting. */
- char *name; /* Name for the new namespace. May be a
+ CONST char *name; /* Name for the new namespace. May be a
* qualified name with names of ancestor
* namespaces separated by "::"s. */
ClientData clientData; /* One-word value to store with
@@ -442,7 +447,7 @@ Tcl_CreateNamespace(interp, name, clientData, deleteProc)
register Namespace *nsPtr, *ancestorPtr;
Namespace *parentPtr, *dummy1Ptr, *dummy2Ptr;
Namespace *globalNsPtr = iPtr->globalNsPtr;
- char *simpleName;
+ CONST char *simpleName;
Tcl_HashEntry *entryPtr;
Tcl_DString buffer1, buffer2;
int newEntry;
@@ -715,7 +720,8 @@ TclTeardownNamespace(nsPtr)
* variables, in case they had any traces on them.
*/
- char *str, *errorInfoStr, *errorCodeStr;
+ CONST char *str;
+ char *errorInfoStr, *errorCodeStr;
str = Tcl_GetVar((Tcl_Interp *) iPtr, "errorInfo", TCL_GLOBAL_ONLY);
if (str != NULL) {
@@ -896,7 +902,7 @@ Tcl_Export(interp, namespacePtr, pattern, resetListFirst)
Tcl_Namespace *namespacePtr; /* Points to the namespace from which
* commands are to be exported. NULL for
* the current namespace. */
- char *pattern; /* String pattern indicating which commands
+ CONST char *pattern; /* String pattern indicating which commands
* to export. This pattern may not include
* any namespace qualifiers; only commands
* in the specified namespace may be
@@ -909,7 +915,8 @@ Tcl_Export(interp, namespacePtr, pattern, resetListFirst)
#define INIT_EXPORT_PATTERNS 5
Namespace *nsPtr, *exportNsPtr, *dummyPtr;
Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
- char *simplePattern, *patternCpy;
+ CONST char *simplePattern;
+ char *patternCpy;
int neededElems, len, i;
/*
@@ -1096,7 +1103,7 @@ Tcl_Import(interp, namespacePtr, pattern, allowOverwrite)
Tcl_Namespace *namespacePtr; /* Points to the namespace into which the
* commands are to be imported. NULL for
* the current namespace. */
- char *pattern; /* String pattern indicating which commands
+ CONST char *pattern; /* String pattern indicating which commands
* to import. This pattern should be
* qualified by the name of the namespace
* from which to import the command(s). */
@@ -1108,7 +1115,8 @@ Tcl_Import(interp, namespacePtr, pattern, allowOverwrite)
Interp *iPtr = (Interp *) interp;
Namespace *nsPtr, *importNsPtr, *dummyPtr;
Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
- char *simplePattern, *cmdName;
+ CONST char *simplePattern;
+ char *cmdName;
register Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
Command *cmdPtr, *realCmdPtr;
@@ -1265,6 +1273,7 @@ Tcl_Import(interp, namespacePtr, pattern, allowOverwrite)
"import pattern \"", pattern,
"\" would create a loop containing command \"",
Tcl_DStringValue(&ds), "\"", (char *) NULL);
+ Tcl_DStringFree(&ds);
return TCL_ERROR;
}
}
@@ -1277,6 +1286,7 @@ Tcl_Import(interp, namespacePtr, pattern, allowOverwrite)
dataPtr->realCmdPtr = cmdPtr;
dataPtr->selfPtr = (Command *) importedCmd;
dataPtr->selfPtr->compileProc = cmdPtr->compileProc;
+ Tcl_DStringFree(&ds);
/*
* Create an ImportRef structure describing this new import
@@ -1328,14 +1338,15 @@ Tcl_ForgetImport(interp, namespacePtr, pattern)
Tcl_Namespace *namespacePtr; /* Points to the namespace from which
* previously imported commands should be
* removed. NULL for current namespace. */
- char *pattern; /* String pattern indicating which imported
+ CONST char *pattern; /* String pattern indicating which imported
* commands to remove. This pattern should
* be qualified by the name of the
* namespace from which the command(s) were
* imported. */
{
Namespace *nsPtr, *importNsPtr, *dummyPtr, *actualCtxPtr;
- char *simplePattern, *cmdName;
+ CONST char *simplePattern;
+ char *cmdName;
register Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
Command *cmdPtr;
@@ -1605,7 +1616,7 @@ TclGetNamespaceForQualName(interp, qualName, cxtNsPtr, flags,
nsPtrPtr, altNsPtrPtr, actualCxtPtrPtr, simpleNamePtr)
Tcl_Interp *interp; /* Interpreter in which to find the
* namespace containing qualName. */
- register char *qualName; /* A namespace-qualified name of an
+ CONST char *qualName; /* A namespace-qualified name of an
* command, variable, or namespace. */
Namespace *cxtNsPtr; /* The namespace in which to start the
* search for qualName's namespace. If NULL
@@ -1637,7 +1648,7 @@ TclGetNamespaceForQualName(interp, qualName, cxtNsPtr, flags,
* the :: namespace if TCL_GLOBAL_ONLY was
* specified, or the current namespace if
* cxtNsPtr was NULL. */
- char **simpleNamePtr; /* Address where procedure stores the
+ CONST char **simpleNamePtr; /* Address where procedure stores the
* simple name at end of the qualName, or
* NULL if qualName is "::" or the flag
* FIND_ONLY_NS was specified. */
@@ -1646,8 +1657,8 @@ TclGetNamespaceForQualName(interp, qualName, cxtNsPtr, flags,
Namespace *nsPtr = cxtNsPtr;
Namespace *altNsPtr;
Namespace *globalNsPtr = iPtr->globalNsPtr;
- register char *start, *end;
- char *nsName;
+ CONST char *start, *end;
+ CONST char *nsName;
Tcl_HashEntry *entryPtr;
Tcl_DString buffer;
int len;
@@ -1870,7 +1881,7 @@ Tcl_Namespace *
Tcl_FindNamespace(interp, name, contextNsPtr, flags)
Tcl_Interp *interp; /* The interpreter in which to find the
* namespace. */
- char *name; /* Namespace name. If it starts with "::",
+ CONST char *name; /* Namespace name. If it starts with "::",
* will be looked up in global namespace.
* Else, looked up first in contextNsPtr
* (current namespace if contextNsPtr is
@@ -1885,7 +1896,7 @@ Tcl_FindNamespace(interp, name, contextNsPtr, flags)
* TCL_LEAVE_ERR_MSG flags. */
{
Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr;
- char *dummy;
+ CONST char *dummy;
/*
* Find the namespace(s) that contain the specified namespace name.
@@ -1929,7 +1940,7 @@ Tcl_Command
Tcl_FindCommand(interp, name, contextNsPtr, flags)
Tcl_Interp *interp; /* The interpreter in which to find the
* command and to report errors. */
- char *name; /* Command's name. If it starts with "::",
+ CONST char *name; /* Command's name. If it starts with "::",
* will be looked up in global namespace.
* Else, looked up first in contextNsPtr
* (current namespace if contextNsPtr is
@@ -1952,7 +1963,7 @@ Tcl_FindCommand(interp, name, contextNsPtr, flags)
ResolverScheme *resPtr;
Namespace *nsPtr[2], *cxtNsPtr;
- char *simpleName;
+ CONST char *simpleName;
register Tcl_HashEntry *entryPtr;
register Command *cmdPtr;
register int search;
@@ -2061,7 +2072,7 @@ Tcl_Var
Tcl_FindNamespaceVar(interp, name, contextNsPtr, flags)
Tcl_Interp *interp; /* The interpreter in which to find the
* variable. */
- char *name; /* Variable's name. If it starts with "::",
+ CONST char *name; /* Variable's name. If it starts with "::",
* will be looked up in global namespace.
* Else, looked up first in contextNsPtr
* (current namespace if contextNsPtr is
@@ -2083,7 +2094,7 @@ Tcl_FindNamespaceVar(interp, name, contextNsPtr, flags)
Interp *iPtr = (Interp*)interp;
ResolverScheme *resPtr;
Namespace *nsPtr[2], *cxtNsPtr;
- char *simpleName;
+ CONST char *simpleName;
Tcl_HashEntry *entryPtr;
Var *varPtr;
register int search;
@@ -2275,6 +2286,17 @@ TclResetShadowedCmdRefs(interp, newCmdPtr)
hPtr = Tcl_FindHashEntry(&shadowNsPtr->cmdTable, cmdName);
if (hPtr != NULL) {
nsPtr->cmdRefEpoch++;
+
+ /*
+ * If the shadowed command was compiled to bytecodes, we
+ * invalidate all the bytecodes in nsPtr, to force a new
+ * compilation. We use the resolverEpoch to signal the need
+ * for a fresh compilation of every bytecode.
+ */
+
+ if ((((Command *) Tcl_GetHashValue(hPtr))->compileProc) != NULL) {
+ nsPtr->resolverEpoch++;
+ }
}
}
@@ -2342,12 +2364,29 @@ GetNamespaceFromObj(interp, objPtr, nsPtrPtr)
* of a namespace. */
Tcl_Namespace **nsPtrPtr; /* Result namespace pointer goes here. */
{
+ Interp *iPtr = (Interp *) interp;
register ResolvedNsName *resNamePtr;
register Namespace *nsPtr;
- Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
- int result;
+ Namespace *currNsPtr;
+ CallFrame *savedFramePtr;
+ int result = TCL_OK;
+ char *name;
/*
+ * If the namespace name is fully qualified, do as if the lookup were
+ * done from the global namespace; this helps avoid repeated lookups
+ * of fully qualified names.
+ */
+
+ savedFramePtr = iPtr->varFramePtr;
+ name = Tcl_GetString(objPtr);
+ if ((*name++ == ':') && (*name == ':')) {
+ iPtr->varFramePtr = NULL;
+ }
+
+ currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
+
+ /*
* Get the internal representation, converting to a namespace type if
* needed. The internal representation is a ResolvedNsName that points
* to the actual namespace.
@@ -2356,7 +2395,7 @@ GetNamespaceFromObj(interp, objPtr, nsPtrPtr)
if (objPtr->typePtr != &tclNsNameType) {
result = tclNsNameType.setFromAnyProc(interp, objPtr);
if (result != TCL_OK) {
- return TCL_ERROR;
+ goto done;
}
}
resNamePtr = (ResolvedNsName *) objPtr->internalRep.otherValuePtr;
@@ -2382,7 +2421,7 @@ GetNamespaceFromObj(interp, objPtr, nsPtrPtr)
if (nsPtr == NULL) { /* try again */
result = tclNsNameType.setFromAnyProc(interp, objPtr);
if (result != TCL_OK) {
- return TCL_ERROR;
+ goto done;
}
resNamePtr = (ResolvedNsName *) objPtr->internalRep.otherValuePtr;
if (resNamePtr != NULL) {
@@ -2393,7 +2432,10 @@ GetNamespaceFromObj(interp, objPtr, nsPtrPtr)
}
}
*nsPtrPtr = (Tcl_Namespace *) nsPtr;
- return TCL_OK;
+
+ done:
+ iPtr->varFramePtr = savedFramePtr;
+ return result;
}
/*
@@ -2409,6 +2451,7 @@ GetNamespaceFromObj(interp, objPtr, nsPtrPtr)
* namespace current
* namespace delete ?name name...?
* namespace eval name arg ?arg...?
+ * namespace exists name
* namespace export ?-clear? ?pattern pattern...?
* namespace forget ?pattern pattern...?
* namespace import ?-force? ?pattern pattern...?
@@ -2442,16 +2485,17 @@ Tcl_NamespaceObjCmd(clientData, interp, objc, objv)
register int objc; /* Number of arguments. */
register Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- static char *subCmds[] = {
- "children", "code", "current", "delete",
- "eval", "export", "forget", "import",
- "inscope", "origin", "parent", "qualifiers",
- "tail", "which", (char *) NULL};
+ static CONST char *subCmds[] = {
+ "children", "code", "current", "delete",
+ "eval", "exists", "export", "forget", "import",
+ "inscope", "origin", "parent", "qualifiers",
+ "tail", "which", (char *) NULL
+ };
enum NSSubCmdIdx {
- NSChildrenIdx, NSCodeIdx, NSCurrentIdx, NSDeleteIdx,
- NSEvalIdx, NSExportIdx, NSForgetIdx, NSImportIdx,
- NSInscopeIdx, NSOriginIdx, NSParentIdx, NSQualifiersIdx,
- NSTailIdx, NSWhichIdx
+ NSChildrenIdx, NSCodeIdx, NSCurrentIdx, NSDeleteIdx,
+ NSEvalIdx, NSExistsIdx, NSExportIdx, NSForgetIdx, NSImportIdx,
+ NSInscopeIdx, NSOriginIdx, NSParentIdx, NSQualifiersIdx,
+ NSTailIdx, NSWhichIdx
};
int index, result;
@@ -2486,6 +2530,9 @@ Tcl_NamespaceObjCmd(clientData, interp, objc, objv)
case NSEvalIdx:
result = NamespaceEvalCmd(clientData, interp, objc, objv);
break;
+ case NSExistsIdx:
+ result = NamespaceExistsCmd(clientData, interp, objc, objv);
+ break;
case NSExportIdx:
result = NamespaceExportCmd(clientData, interp, objc, objv);
break;
@@ -2631,10 +2678,10 @@ NamespaceChildrenCmd(dummy, interp, objc, objv)
* Here "arg" can be a list. "namespace code arg" produces a result
* equivalent to that produced by the command
*
- * list namespace inscope [namespace current] $arg
+ * list ::namespace inscope [namespace current] $arg
*
* However, if "arg" is itself a scoped value starting with
- * "namespace inscope", then the result is just "arg".
+ * "::namespace inscope", then the result is just "arg".
*
* Results:
* Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong.
@@ -2668,6 +2715,10 @@ NamespaceCodeCmd(dummy, interp, objc, objv)
*/
arg = Tcl_GetStringFromObj(objv[2], &length);
+ while (*arg == ':') {
+ arg++;
+ length--;
+ }
if ((*arg == 'n') && (length > 17)
&& (strncmp(arg, "namespace", 9) == 0)) {
for (p = (arg + 9); (*p == ' '); p++) {
@@ -2690,7 +2741,7 @@ NamespaceCodeCmd(dummy, interp, objc, objv)
listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
Tcl_ListObjAppendElement(interp, listPtr,
- Tcl_NewStringObj("namespace", -1));
+ Tcl_NewStringObj("::namespace", -1));
Tcl_ListObjAppendElement(interp, listPtr,
Tcl_NewStringObj("inscope", -1));
@@ -2877,7 +2928,7 @@ NamespaceEvalCmd(dummy, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
Tcl_Namespace *namespacePtr;
- Tcl_CallFrame frame;
+ CallFrame frame;
Tcl_Obj *objPtr;
char *name;
int length, result;
@@ -2915,11 +2966,13 @@ NamespaceEvalCmd(dummy, interp, objc, objv)
* the command(s).
*/
- result = Tcl_PushCallFrame(interp, &frame, namespacePtr,
- /*isProcCallFrame*/ 0);
+ result = Tcl_PushCallFrame(interp, (Tcl_CallFrame *) &frame,
+ namespacePtr, /*isProcCallFrame*/ 0);
if (result != TCL_OK) {
return TCL_ERROR;
}
+ frame.objc = objc;
+ frame.objv = objv; /* ref counts do not need to be incremented here */
if (objc == 4) {
result = Tcl_EvalObjEx(interp, objv[3], 0);
@@ -2951,6 +3004,53 @@ NamespaceEvalCmd(dummy, interp, objc, objv)
/*
*----------------------------------------------------------------------
*
+ * NamespaceExistsCmd --
+ *
+ * Invoked to implement the "namespace exists" command that returns
+ * true if the given namespace currently exists, and false otherwise.
+ * Handles the following syntax:
+ *
+ * namespace exists name
+ *
+ * Results:
+ * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong.
+ *
+ * Side effects:
+ * Returns a result in the interpreter's result object. If anything
+ * goes wrong, the result is an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+NamespaceExistsCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ Tcl_Namespace *namespacePtr;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "name");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Check whether the given namespace exists
+ */
+
+ if (GetNamespaceFromObj(interp, objv[2], &namespacePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (namespacePtr != NULL));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* NamespaceExportCmd --
*
* Invoked to implement the "namespace export" command that specifies
@@ -3768,7 +3868,8 @@ SetNsNameFromAny(interp, objPtr)
register Tcl_Obj *objPtr; /* The object to convert. */
{
register Tcl_ObjType *oldTypePtr = objPtr->typePtr;
- char *name, *dummy;
+ char *name;
+ CONST char *dummy;
Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr;
register ResolvedNsName *resNamePtr;
@@ -3880,4 +3981,3 @@ UpdateStringOfNsName(objPtr)
}
objPtr->length = length;
}
-
diff --git a/tcl/generic/tclNotify.c b/tcl/generic/tclNotify.c
index 2c386ab9e7c..9e68a6b5879 100644
--- a/tcl/generic/tclNotify.c
+++ b/tcl/generic/tclNotify.c
@@ -116,7 +116,7 @@ TclInitNotifier()
Tcl_MutexLock(&listLock);
tsdPtr->threadId = Tcl_GetCurrentThread();
- tsdPtr->clientData = Tcl_InitNotifier();
+ tsdPtr->clientData = tclStubs.tcl_InitNotifier();
tsdPtr->nextPtr = firstNotifierPtr;
firstNotifierPtr = tsdPtr;
@@ -146,10 +146,21 @@ TclFinalizeNotifier()
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
ThreadSpecificData **prevPtrPtr;
+ Tcl_Event *evPtr, *hold;
+
+ Tcl_MutexLock(&(tsdPtr->queueMutex));
+ for (evPtr = tsdPtr->firstEventPtr; evPtr != (Tcl_Event *) NULL; ) {
+ hold = evPtr;
+ evPtr = evPtr->nextPtr;
+ ckfree((char *) hold);
+ }
+ tsdPtr->firstEventPtr = NULL;
+ tsdPtr->lastEventPtr = NULL;
+ Tcl_MutexUnlock(&(tsdPtr->queueMutex));
Tcl_MutexLock(&listLock);
- Tcl_FinalizeNotifier(tsdPtr->clientData);
+ tclStubs.tcl_FinalizeNotifier(tsdPtr->clientData);
Tcl_MutexFinalize(&(tsdPtr->queueMutex));
for (prevPtrPtr = &firstNotifierPtr; *prevPtrPtr != NULL;
prevPtrPtr = &((*prevPtrPtr)->nextPtr)) {
@@ -192,6 +203,10 @@ Tcl_SetNotifier(notifierProcPtr)
#endif
tclStubs.tcl_SetTimer = notifierProcPtr->setTimerProc;
tclStubs.tcl_WaitForEvent = notifierProcPtr->waitForEventProc;
+ tclStubs.tcl_InitNotifier = notifierProcPtr->initNotifierProc;
+ tclStubs.tcl_FinalizeNotifier = notifierProcPtr->finalizeNotifierProc;
+ tclStubs.tcl_AlertNotifier = notifierProcPtr->alertNotifierProc;
+ tclStubs.tcl_ServiceModeHook = notifierProcPtr->serviceModeHookProc;
}
/*
@@ -706,7 +721,7 @@ Tcl_SetServiceMode(mode)
oldMode = tsdPtr->serviceMode;
tsdPtr->serviceMode = mode;
- Tcl_ServiceModeHook(mode);
+ tclStubs.tcl_ServiceModeHook(mode);
return oldMode;
}
@@ -1072,10 +1087,9 @@ Tcl_ThreadAlert(threadId)
Tcl_MutexLock(&listLock);
for (tsdPtr = firstNotifierPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
if (tsdPtr->threadId == threadId) {
- Tcl_AlertNotifier(tsdPtr->clientData);
+ tclStubs.tcl_AlertNotifier(tsdPtr->clientData);
break;
}
}
Tcl_MutexUnlock(&listLock);
}
-
diff --git a/tcl/generic/tclObj.c b/tcl/generic/tclObj.c
index 581c6b0aaa5..6af1b59d002 100644
--- a/tcl/generic/tclObj.c
+++ b/tcl/generic/tclObj.c
@@ -6,6 +6,7 @@
*
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
* Copyright (c) 1999 by Scriptics Corporation.
+ * Copyright (c) 2001 by ActiveState Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -14,6 +15,7 @@
*/
#include "tclInt.h"
+#include "tclCompile.h"
#include "tclPort.h"
/*
@@ -45,18 +47,8 @@ Tcl_Mutex tclObjMutex;
* is shared by all new objects allocated by Tcl_NewObj.
*/
-static char emptyString;
-char *tclEmptyStringRep = &emptyString;
-
-/*
- * The number of Tcl objects ever allocated (by Tcl_NewObj) and freed
- * (by TclFreeObj).
- */
-
-#ifdef TCL_COMPILE_STATS
-long tclObjsAlloced = 0;
-long tclObjsFreed = 0;
-#endif /* TCL_COMPILE_STATS */
+char tclEmptyString = '\0';
+char *tclEmptyStringRep = &tclEmptyString;
/*
* Prototypes for procedures defined later in this file:
@@ -71,6 +63,37 @@ static int SetIntFromAny _ANSI_ARGS_((Tcl_Interp *interp,
static void UpdateStringOfBoolean _ANSI_ARGS_((Tcl_Obj *objPtr));
static void UpdateStringOfDouble _ANSI_ARGS_((Tcl_Obj *objPtr));
static void UpdateStringOfInt _ANSI_ARGS_((Tcl_Obj *objPtr));
+#ifndef TCL_WIDE_INT_IS_LONG
+static int SetWideIntFromAny _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr));
+static void UpdateStringOfWideInt _ANSI_ARGS_((Tcl_Obj *objPtr));
+#endif
+
+/*
+ * Prototypes for the array hash key methods.
+ */
+
+static Tcl_HashEntry * AllocObjEntry _ANSI_ARGS_((
+ Tcl_HashTable *tablePtr, VOID *keyPtr));
+static int CompareObjKeys _ANSI_ARGS_((
+ VOID *keyPtr, Tcl_HashEntry *hPtr));
+static void FreeObjEntry _ANSI_ARGS_((
+ Tcl_HashEntry *hPtr));
+static unsigned int HashObjKey _ANSI_ARGS_((
+ Tcl_HashTable *tablePtr,
+ VOID *keyPtr));
+
+/*
+ * Prototypes for the CommandName object type.
+ */
+
+static void DupCmdNameInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr,
+ Tcl_Obj *copyPtr));
+static void FreeCmdNameInternalRep _ANSI_ARGS_((
+ Tcl_Obj *objPtr));
+static int SetCmdNameFromAny _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr));
+
/*
* The structures below defines the Tcl object types defined in this file by
@@ -102,6 +125,81 @@ Tcl_ObjType tclIntType = {
UpdateStringOfInt, /* updateStringProc */
SetIntFromAny /* setFromAnyProc */
};
+
+#ifndef TCL_WIDE_INT_IS_LONG
+Tcl_ObjType tclWideIntType = {
+ "wideInt", /* name */
+ (Tcl_FreeInternalRepProc *) NULL, /* freeIntRepProc */
+ (Tcl_DupInternalRepProc *) NULL, /* dupIntRepProc */
+ UpdateStringOfWideInt, /* updateStringProc */
+ SetWideIntFromAny /* setFromAnyProc */
+};
+#endif
+
+/*
+ * The structure below defines the Tcl obj hash key type.
+ */
+Tcl_HashKeyType tclObjHashKeyType = {
+ TCL_HASH_KEY_TYPE_VERSION, /* version */
+ 0, /* flags */
+ HashObjKey, /* hashKeyProc */
+ CompareObjKeys, /* compareKeysProc */
+ AllocObjEntry, /* allocEntryProc */
+ FreeObjEntry /* freeEntryProc */
+};
+
+/*
+ * The structure below defines the command name Tcl object type by means of
+ * procedures that can be invoked by generic object code. Objects of this
+ * type cache the Command pointer that results from looking up command names
+ * in the command hashtable. Such objects appear as the zeroth ("command
+ * name") argument in a Tcl command.
+ */
+
+static Tcl_ObjType tclCmdNameType = {
+ "cmdName", /* name */
+ FreeCmdNameInternalRep, /* freeIntRepProc */
+ DupCmdNameInternalRep, /* dupIntRepProc */
+ (Tcl_UpdateStringProc *) NULL, /* updateStringProc */
+ SetCmdNameFromAny /* setFromAnyProc */
+};
+
+
+/*
+ * Structure containing a cached pointer to a command that is the result
+ * of resolving the command's name in some namespace. It is the internal
+ * representation for a cmdName object. It contains the pointer along
+ * with some information that is used to check the pointer's validity.
+ */
+
+typedef struct ResolvedCmdName {
+ Command *cmdPtr; /* A cached Command pointer. */
+ Namespace *refNsPtr; /* Points to the namespace containing the
+ * reference (not the namespace that
+ * contains the referenced command). */
+ long refNsId; /* refNsPtr's unique namespace id. Used to
+ * verify that refNsPtr is still valid
+ * (e.g., it's possible that the cmd's
+ * containing namespace was deleted and a
+ * new one created at the same address). */
+ int refNsCmdEpoch; /* Value of the referencing namespace's
+ * cmdRefEpoch when the pointer was cached.
+ * Before using the cached pointer, we check
+ * if the namespace's epoch was incremented;
+ * if so, this cached pointer is invalid. */
+ int cmdEpoch; /* Value of the command's cmdEpoch when this
+ * pointer was cached. Before using the
+ * cached pointer, we check if the cmd's
+ * epoch was incremented; if so, the cmd was
+ * renamed, deleted, hidden, or exposed, and
+ * so the pointer is invalid. */
+ int refCount; /* Reference count: 1 for each cmdName
+ * object that has a pointer to this
+ * ResolvedCmdName structure as its internal
+ * rep. This structure can be freed when
+ * refCount becomes zero. */
+} ResolvedCmdName;
+
/*
*-------------------------------------------------------------------------
@@ -133,16 +231,30 @@ TclInitObjSubsystem()
Tcl_RegisterObjType(&tclBooleanType);
Tcl_RegisterObjType(&tclByteArrayType);
Tcl_RegisterObjType(&tclDoubleType);
+ Tcl_RegisterObjType(&tclEndOffsetType);
Tcl_RegisterObjType(&tclIntType);
+#ifndef TCL_WIDE_INT_IS_LONG
+ Tcl_RegisterObjType(&tclWideIntType);
+#endif
Tcl_RegisterObjType(&tclStringType);
Tcl_RegisterObjType(&tclListType);
Tcl_RegisterObjType(&tclByteCodeType);
Tcl_RegisterObjType(&tclProcBodyType);
+ Tcl_RegisterObjType(&tclArraySearchType);
+ Tcl_RegisterObjType(&tclIndexType);
+ Tcl_RegisterObjType(&tclNsNameType);
+ Tcl_RegisterObjType(&tclCmdNameType);
#ifdef TCL_COMPILE_STATS
Tcl_MutexLock(&tclObjMutex);
tclObjsAlloced = 0;
tclObjsFreed = 0;
+ {
+ int i;
+ for (i = 0; i < TCL_MAX_SHARED_OBJ_STATS; i++) {
+ tclObjsShared[i] = 0;
+ }
+ }
Tcl_MutexUnlock(&tclObjMutex);
#endif
}
@@ -306,7 +418,7 @@ Tcl_AppendAllObjTypes(interp, objPtr)
Tcl_ObjType *
Tcl_GetObjType(typeName)
- char *typeName; /* Name of Tcl object type to look up. */
+ CONST char *typeName; /* Name of Tcl object type to look up. */
{
register Tcl_HashEntry *hPtr;
Tcl_ObjType *typePtr;
@@ -404,25 +516,11 @@ Tcl_NewObj()
register Tcl_Obj *objPtr;
/*
- * Allocate the object using the list of free Tcl_Obj structs
- * we maintain.
+ * Use the macro defined in tclInt.h - it will use the
+ * correct allocator.
*/
- Tcl_MutexLock(&tclObjMutex);
- if (tclFreeObjList == NULL) {
- TclAllocateFreeObjects();
- }
- objPtr = tclFreeObjList;
- tclFreeObjList = (Tcl_Obj *) tclFreeObjList->internalRep.otherValuePtr;
-
- objPtr->refCount = 0;
- objPtr->bytes = tclEmptyStringRep;
- objPtr->length = 0;
- objPtr->typePtr = NULL;
-#ifdef TCL_COMPILE_STATS
- tclObjsAlloced++;
-#endif /* TCL_COMPILE_STATS */
- Tcl_MutexUnlock(&tclObjMutex);
+ TclNewObj(objPtr);
return objPtr;
}
#endif /* TCL_MEM_DEBUG */
@@ -437,7 +535,7 @@ Tcl_NewObj()
* empty string. It is the same as the Tcl_NewObj procedure above
* except that it calls Tcl_DbCkalloc directly with the file name and
* line number from its caller. This simplifies debugging since then
- * the checkmem command will report the correct file name and line
+ * the [memory active] command will report the correct file name and line
* number when reporting objects that haven't been freed.
*
* When TCL_MEM_DEBUG is not defined, this procedure just returns the
@@ -458,7 +556,7 @@ Tcl_NewObj()
Tcl_Obj *
Tcl_DbNewObj(file, line)
- register char *file; /* The name of the source file calling this
+ register CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
register int line; /* Line number in the source file; used
* for debugging. */
@@ -466,29 +564,18 @@ Tcl_DbNewObj(file, line)
register Tcl_Obj *objPtr;
/*
- * If debugging Tcl's memory usage, allocate the object using ckalloc.
- * Otherwise, allocate it using the list of free Tcl_Obj structs we
- * maintain.
+ * Use the macro defined in tclInt.h - it will use the
+ * correct allocator.
*/
- objPtr = (Tcl_Obj *) Tcl_DbCkalloc(sizeof(Tcl_Obj), file, line);
- objPtr->refCount = 0;
- objPtr->bytes = tclEmptyStringRep;
- objPtr->length = 0;
- objPtr->typePtr = NULL;
-#ifdef TCL_COMPILE_STATS
- Tcl_MutexLock(&tclObjMutex);
- tclObjsAlloced++;
- Tcl_MutexUnlock(&tclObjMutex);
-#endif /* TCL_COMPILE_STATS */
+ TclDbNewObj(objPtr, file, line);
return objPtr;
}
-
#else /* if not TCL_MEM_DEBUG */
Tcl_Obj *
Tcl_DbNewObj(file, line)
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -523,23 +610,27 @@ Tcl_DbNewObj(file, line)
void
TclAllocateFreeObjects()
{
- Tcl_Obj tmp[2];
- size_t objSizePlusPadding = /* NB: this assumes byte addressing. */
- ((int)(&(tmp[1])) - (int)(&(tmp[0])));
- size_t bytesToAlloc = (OBJS_TO_ALLOC_EACH_TIME * objSizePlusPadding);
+ size_t bytesToAlloc = (OBJS_TO_ALLOC_EACH_TIME * sizeof(Tcl_Obj));
char *basePtr;
register Tcl_Obj *prevPtr, *objPtr;
register int i;
+ /*
+ * This has been noted by Purify to be a potential leak. The problem is
+ * that Tcl, when not TCL_MEM_DEBUG compiled, keeps around all allocated
+ * Tcl_Obj's, pointed to by tclFreeObjList, when freed instead of
+ * actually freeing the memory. These never do get freed properly.
+ */
+
basePtr = (char *) ckalloc(bytesToAlloc);
memset(basePtr, 0, bytesToAlloc);
prevPtr = NULL;
objPtr = (Tcl_Obj *) basePtr;
- for (i = 0; i < OBJS_TO_ALLOC_EACH_TIME; i++) {
+ for (i = 0; i < OBJS_TO_ALLOC_EACH_TIME; i++) {
objPtr->internalRep.otherValuePtr = (VOID *) prevPtr;
prevPtr = objPtr;
- objPtr = (Tcl_Obj *) (((char *)objPtr) + objSizePlusPadding);
+ objPtr++;
}
tclFreeObjList = prevPtr;
}
@@ -593,18 +684,22 @@ TclFreeObj(objPtr)
* Tcl_Obj structs we maintain.
*/
+#if defined(TCL_MEM_DEBUG) || defined(PURIFY)
Tcl_MutexLock(&tclObjMutex);
-#ifdef TCL_MEM_DEBUG
ckfree((char *) objPtr);
-#else
+ Tcl_MutexUnlock(&tclObjMutex);
+#elif defined(TCL_THREADS) && defined(USE_THREAD_ALLOC)
+ TclThreadFreeObj(objPtr);
+#else
+ Tcl_MutexLock(&tclObjMutex);
objPtr->internalRep.otherValuePtr = (VOID *) tclFreeObjList;
tclFreeObjList = objPtr;
+ Tcl_MutexUnlock(&tclObjMutex);
#endif /* TCL_MEM_DEBUG */
#ifdef TCL_COMPILE_STATS
tclObjsFreed++;
#endif /* TCL_COMPILE_STATS */
- Tcl_MutexUnlock(&tclObjMutex);
}
/*
@@ -648,15 +743,7 @@ Tcl_DuplicateObj(objPtr)
if (objPtr->bytes == NULL) {
dupPtr->bytes = NULL;
} else if (objPtr->bytes != tclEmptyStringRep) {
- int len = objPtr->length;
-
- dupPtr->bytes = (char *) ckalloc((unsigned) len+1);
- if (len > 0) {
- memcpy((VOID *) dupPtr->bytes, (VOID *) objPtr->bytes,
- (unsigned) len);
- }
- dupPtr->bytes[len] = '\0';
- dupPtr->length = len;
+ TclInitStringRep(dupPtr, objPtr->bytes, objPtr->length);
}
if (typePtr != NULL) {
@@ -733,24 +820,20 @@ Tcl_GetString(objPtr)
char *
Tcl_GetStringFromObj(objPtr, lengthPtr)
- register Tcl_Obj *objPtr; /* Object whose string rep byte pointer
- * should be returned. */
- register int *lengthPtr; /* If non-NULL, the location where the
- * string rep's byte array length should be
- * stored. If NULL, no length is stored. */
+ register Tcl_Obj *objPtr; /* Object whose string rep byte pointer should
+ * be returned. */
+ register int *lengthPtr; /* If non-NULL, the location where the string
+ * rep's byte array length should * be stored.
+ * If NULL, no length is stored. */
{
- if (objPtr->bytes != NULL) {
- if (lengthPtr != NULL) {
- *lengthPtr = objPtr->length;
+ if (objPtr->bytes == NULL) {
+ if (objPtr->typePtr->updateStringProc == NULL) {
+ panic("UpdateStringProc should not be invoked for type %s",
+ objPtr->typePtr->name);
}
- return objPtr->bytes;
+ (*objPtr->typePtr->updateStringProc)(objPtr);
}
- if (objPtr->typePtr->updateStringProc == NULL) {
- panic("UpdateStringProc should not be invoked for type %s",
- objPtr->typePtr->name);
- }
- (*objPtr->typePtr->updateStringProc)(objPtr);
if (lengthPtr != NULL) {
*lengthPtr = objPtr->length;
}
@@ -847,9 +930,9 @@ Tcl_NewBooleanObj(boolValue)
* TCL_MEM_DEBUG is defined. It creates new boolean objects. It is the
* same as the Tcl_NewBooleanObj procedure above except that it calls
* Tcl_DbCkalloc directly with the file name and line number from its
- * caller. This simplifies debugging since then the checkmem command
- * will report the correct file name and line number when reporting
- * objects that haven't been freed.
+ * caller. This simplifies debugging since then the [memory active]
+ * command will report the correct file name and line number when
+ * reporting objects that haven't been freed.
*
* When TCL_MEM_DEBUG is not defined, this procedure just returns the
* result of calling Tcl_NewBooleanObj.
@@ -869,7 +952,7 @@ Tcl_NewBooleanObj(boolValue)
Tcl_Obj *
Tcl_DbNewBooleanObj(boolValue, file, line)
register int boolValue; /* Boolean used to initialize new object. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -889,7 +972,7 @@ Tcl_DbNewBooleanObj(boolValue, file, line)
Tcl_Obj *
Tcl_DbNewBooleanObj(boolValue, file, line)
register int boolValue; /* Boolean used to initialize new object. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -965,7 +1048,12 @@ Tcl_GetBooleanFromObj(interp, objPtr, boolPtr)
{
register int result;
- result = SetBooleanFromAny(interp, objPtr);
+ if (objPtr->typePtr == &tclBooleanType) {
+ result = TCL_OK;
+ } else {
+ result = SetBooleanFromAny(interp, objPtr);
+ }
+
if (result == TCL_OK) {
*boolPtr = (int) objPtr->internalRep.longValue;
}
@@ -1003,88 +1091,142 @@ SetBooleanFromAny(interp, objPtr)
char lowerCase[10];
int newBool, length;
register int i;
- double dbl;
/*
* Get the string representation. Make it up-to-date if necessary.
*/
-
+
string = Tcl_GetStringFromObj(objPtr, &length);
/*
- * Copy the string converting its characters to lower case.
- */
-
- for (i = 0; (i < 9) && (i < length); i++) {
- c = string[i];
- /*
- * Weed out international characters so we can safely operate
- * on single bytes.
- */
-
- if (c & 0x80) {
- goto badBoolean;
- }
- if (Tcl_UniCharIsUpper(UCHAR(c))) {
- c = (char) Tcl_UniCharToLower(UCHAR(c));
- }
- lowerCase[i] = c;
- }
- lowerCase[i] = 0;
-
- /*
- * Parse the string as a boolean. We use an implementation here that
- * doesn't report errors in interp if interp is NULL.
+ * Use the obvious shortcuts for numerical values; if objPtr is not
+ * of numerical type, parse its string rep.
*/
-
- c = lowerCase[0];
- if ((c == '0') && (lowerCase[1] == '\0')) {
- newBool = 0;
- } else if ((c == '1') && (lowerCase[1] == '\0')) {
- newBool = 1;
- } else if ((c == 'y') && (strncmp(lowerCase, "yes", (size_t) length) == 0)) {
- newBool = 1;
- } else if ((c == 'n') && (strncmp(lowerCase, "no", (size_t) length) == 0)) {
- newBool = 0;
- } else if ((c == 't') && (strncmp(lowerCase, "true", (size_t) length) == 0)) {
- newBool = 1;
- } else if ((c == 'f') && (strncmp(lowerCase, "false", (size_t) length) == 0)) {
- newBool = 0;
- } else if ((c == 'o') && (length >= 2)) {
- if (strncmp(lowerCase, "on", (size_t) length) == 0) {
- newBool = 1;
- } else if (strncmp(lowerCase, "off", (size_t) length) == 0) {
- newBool = 0;
- } else {
- goto badBoolean;
- }
+
+ if (objPtr->typePtr == &tclIntType) {
+ newBool = (objPtr->internalRep.longValue != 0);
+ } else if (objPtr->typePtr == &tclDoubleType) {
+ newBool = (objPtr->internalRep.doubleValue != 0.0);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (objPtr->typePtr == &tclWideIntType) {
+ newBool = (objPtr->internalRep.wideValue != Tcl_LongAsWide(0));
+#endif /* TCL_WIDE_INT_IS_LONG */
} else {
- /*
- * Still might be a string containing the characters representing an
- * int or double that wasn't handled above. This would be a string
- * like "27" or "1.0" that is non-zero and not "1". Such a string
- * whould result in the boolean value true. We try converting to
- * double. If that succeeds and the resulting double is non-zero, we
- * have a "true". Note that numbers can't have embedded NULLs.
+ /*
+ * Copy the string converting its characters to lower case.
*/
-
- dbl = strtod(string, &end);
- if (end == string) {
- goto badBoolean;
+
+ for (i = 0; (i < 9) && (i < length); i++) {
+ c = string[i];
+ /*
+ * Weed out international characters so we can safely operate
+ * on single bytes.
+ */
+
+ if (c & 0x80) {
+ goto badBoolean;
+ }
+ if (Tcl_UniCharIsUpper(UCHAR(c))) {
+ c = (char) Tcl_UniCharToLower(UCHAR(c));
+ }
+ lowerCase[i] = c;
}
-
+ lowerCase[i] = 0;
+
/*
- * Make sure the string has no garbage after the end of the double.
+ * Parse the string as a boolean. We use an implementation here that
+ * doesn't report errors in interp if interp is NULL.
*/
- while ((end < (string+length))
- && isspace(UCHAR(*end))) { /* INTL: ISO only */
- end++;
- }
- if (end != (string+length)) {
- goto badBoolean;
+ c = lowerCase[0];
+ if ((c == '0') && (lowerCase[1] == '\0')) {
+ newBool = 0;
+ } else if ((c == '1') && (lowerCase[1] == '\0')) {
+ newBool = 1;
+ } else if ((c == 'y') && (strncmp(lowerCase, "yes", (size_t) length) == 0)) {
+ newBool = 1;
+ } else if ((c == 'n') && (strncmp(lowerCase, "no", (size_t) length) == 0)) {
+ newBool = 0;
+ } else if ((c == 't') && (strncmp(lowerCase, "true", (size_t) length) == 0)) {
+ newBool = 1;
+ } else if ((c == 'f') && (strncmp(lowerCase, "false", (size_t) length) == 0)) {
+ newBool = 0;
+ } else if ((c == 'o') && (length >= 2)) {
+ if (strncmp(lowerCase, "on", (size_t) length) == 0) {
+ newBool = 1;
+ } else if (strncmp(lowerCase, "off", (size_t) length) == 0) {
+ newBool = 0;
+ } else {
+ goto badBoolean;
+ }
+ } else {
+ double dbl;
+ /*
+ * Boolean values can be extracted from ints or doubles. Note
+ * that we don't use strtoul or strtoull here because we don't
+ * care about what the value is, just whether it is equal to
+ * zero or not.
+ */
+#ifdef TCL_WIDE_INT_IS_LONG
+ newBool = strtol(string, &end, 0);
+ if (end != string) {
+ /*
+ * Make sure the string has no garbage after the end of
+ * the int.
+ */
+ while ((end < (string+length))
+ && isspace(UCHAR(*end))) { /* INTL: ISO only */
+ end++;
+ }
+ if (end == (string+length)) {
+ newBool = (newBool != 0);
+ goto goodBoolean;
+ }
+ }
+#else /* !TCL_WIDE_INT_IS_LONG */
+ Tcl_WideInt wide = strtoll(string, &end, 0);
+ if (end != string) {
+ /*
+ * Make sure the string has no garbage after the end of
+ * the wide int.
+ */
+ while ((end < (string+length))
+ && isspace(UCHAR(*end))) { /* INTL: ISO only */
+ end++;
+ }
+ if (end == (string+length)) {
+ newBool = (wide != Tcl_LongAsWide(0));
+ goto goodBoolean;
+ }
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
+ /*
+ * Still might be a string containing the characters representing an
+ * int or double that wasn't handled above. This would be a string
+ * like "27" or "1.0" that is non-zero and not "1". Such a string
+ * would result in the boolean value true. We try converting to
+ * double. If that succeeds and the resulting double is non-zero, we
+ * have a "true". Note that numbers can't have embedded NULLs.
+ */
+
+ dbl = strtod(string, &end);
+ if (end == string) {
+ goto badBoolean;
+ }
+
+ /*
+ * Make sure the string has no garbage after the end of the double.
+ */
+
+ while ((end < (string+length))
+ && isspace(UCHAR(*end))) { /* INTL: ISO only */
+ end++;
+ }
+ if (end != (string+length)) {
+ goto badBoolean;
+ }
+ newBool = (dbl != 0.0);
}
- newBool = (dbl != 0.0);
}
/*
@@ -1093,6 +1235,7 @@ SetBooleanFromAny(interp, objPtr)
* Tcl_GetStringFromObj, to use that old internalRep.
*/
+ goodBoolean:
if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
oldTypePtr->freeIntRepProc(objPtr);
}
@@ -1205,9 +1348,9 @@ Tcl_NewDoubleObj(dblValue)
* TCL_MEM_DEBUG is defined. It creates new double objects. It is the
* same as the Tcl_NewDoubleObj procedure above except that it calls
* Tcl_DbCkalloc directly with the file name and line number from its
- * caller. This simplifies debugging since then the checkmem command
- * will report the correct file name and line number when reporting
- * objects that haven't been freed.
+ * caller. This simplifies debugging since then the [memory active]
+ * command will report the correct file name and line number when
+ * reporting objects that haven't been freed.
*
* When TCL_MEM_DEBUG is not defined, this procedure just returns the
* result of calling Tcl_NewDoubleObj.
@@ -1227,7 +1370,7 @@ Tcl_NewDoubleObj(dblValue)
Tcl_Obj *
Tcl_DbNewDoubleObj(dblValue, file, line)
register double dblValue; /* Double used to initialize the object. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -1247,7 +1390,7 @@ Tcl_DbNewDoubleObj(dblValue, file, line)
Tcl_Obj *
Tcl_DbNewDoubleObj(dblValue, file, line)
register double dblValue; /* Double used to initialize the object. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -1836,8 +1979,8 @@ Tcl_NewLongObj(longValue)
* When the core is compiled with TCL_MEM_DEBUG defined,
* Tcl_DbNewLongObj calls Tcl_DbCkalloc directly with the file name and
* line number from its caller. This simplifies debugging since then
- * the checkmem command will report the caller's file name and line
- * number when reporting objects that haven't been freed.
+ * the [memory active] command will report the caller's file name and
+ * line number when reporting objects that haven't been freed.
*
* Otherwise, when the core is compiled without TCL_MEM_DEBUG defined,
* this procedure just returns the result of calling Tcl_NewLongObj.
@@ -1859,7 +2002,7 @@ Tcl_Obj *
Tcl_DbNewLongObj(longValue, file, line)
register long longValue; /* Long integer used to initialize the
* new object. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -1880,7 +2023,7 @@ Tcl_Obj *
Tcl_DbNewLongObj(longValue, file, line)
register long longValue; /* Long integer used to initialize the
* new object. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -1971,6 +2114,380 @@ Tcl_GetLongFromObj(interp, objPtr, longPtr)
/*
*----------------------------------------------------------------------
*
+ * SetWideIntFromAny --
+ *
+ * Attempt to generate an integer internal form for the Tcl object
+ * "objPtr".
+ *
+ * Results:
+ * The return value is a standard object Tcl result. If an error occurs
+ * during conversion, an error message is left in the interpreter's
+ * result unless "interp" is NULL.
+ *
+ * Side effects:
+ * If no error occurs, an int is stored as "objPtr"s internal
+ * representation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifndef TCL_WIDE_INT_IS_LONG
+static int
+SetWideIntFromAny(interp, objPtr)
+ Tcl_Interp *interp; /* Used for error reporting if not NULL. */
+ register Tcl_Obj *objPtr; /* The object to convert. */
+{
+ Tcl_ObjType *oldTypePtr = objPtr->typePtr;
+ char *string, *end;
+ int length;
+ register char *p;
+ Tcl_WideInt newWide;
+
+ /*
+ * Get the string representation. Make it up-to-date if necessary.
+ */
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+
+ /*
+ * Now parse "objPtr"s string as an int. We use an implementation here
+ * that doesn't report errors in interp if interp is NULL. Note: use
+ * strtoull instead of strtoll for integer conversions to allow full-size
+ * unsigned numbers, but don't depend on strtoull to handle sign
+ * characters; it won't in some implementations.
+ */
+
+ errno = 0;
+ for (p = string; isspace(UCHAR(*p)); p++) { /* INTL: ISO space. */
+ /* Empty loop body. */
+ }
+ if (*p == '-') {
+ p++;
+ newWide = -((Tcl_WideInt)strtoull(p, &end, 0));
+ } else if (*p == '+') {
+ p++;
+ newWide = strtoull(p, &end, 0);
+ } else {
+ newWide = strtoull(p, &end, 0);
+ }
+ if (end == p) {
+ badInteger:
+ if (interp != NULL) {
+ /*
+ * Must copy string before resetting the result in case a caller
+ * is trying to convert the interpreter's result to an int.
+ */
+
+ char buf[100];
+ sprintf(buf, "expected integer but got \"%.50s\"", string);
+ Tcl_ResetResult(interp);
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), buf, -1);
+ TclCheckBadOctal(interp, string);
+ }
+ return TCL_ERROR;
+ }
+ if (errno == ERANGE) {
+ if (interp != NULL) {
+ char *s = "integer value too large to represent";
+ Tcl_ResetResult(interp);
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1);
+ Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s, (char *) NULL);
+ }
+ return TCL_ERROR;
+ }
+
+ /*
+ * Make sure that the string has no garbage after the end of the int.
+ */
+
+ while ((end < (string+length))
+ && isspace(UCHAR(*end))) { /* INTL: ISO space. */
+ end++;
+ }
+ if (end != (string+length)) {
+ goto badInteger;
+ }
+
+ /*
+ * The conversion to int succeeded. Free the old internalRep before
+ * setting the new one. We do this as late as possible to allow the
+ * conversion code, in particular Tcl_GetStringFromObj, to use that old
+ * internalRep.
+ */
+
+ if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
+ oldTypePtr->freeIntRepProc(objPtr);
+ }
+
+ objPtr->internalRep.wideValue = newWide;
+ objPtr->typePtr = &tclWideIntType;
+ return TCL_OK;
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateStringOfWideInt --
+ *
+ * Update the string representation for a wide integer object.
+ * Note: This procedure does not free an existing old string rep
+ * so storage will be lost if this has not already been done.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The object's string is set to a valid string that results from
+ * the wideInt-to-string conversion.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifndef TCL_WIDE_INT_IS_LONG
+static void
+UpdateStringOfWideInt(objPtr)
+ register Tcl_Obj *objPtr; /* Int object whose string rep to update. */
+{
+ char buffer[TCL_INTEGER_SPACE+2];
+ register unsigned len;
+ register Tcl_WideInt wideVal = objPtr->internalRep.wideValue;
+
+ sprintf(buffer, "%" TCL_LL_MODIFIER "d", wideVal);
+ len = strlen(buffer);
+ objPtr->bytes = ckalloc((unsigned) len + 1);
+ memcpy(objPtr->bytes, buffer, len + 1);
+ objPtr->length = len;
+}
+#endif /* TCL_WIDE_INT_IS_LONG */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_NewWideIntObj --
+ *
+ * If a client is compiled with TCL_MEM_DEBUG defined, calls to
+ * Tcl_NewWideIntObj to create a new 64-bit integer object end up calling
+ * the debugging procedure Tcl_DbNewWideIntObj instead.
+ *
+ * Otherwise, if the client is compiled without TCL_MEM_DEBUG defined,
+ * calls to Tcl_NewWideIntObj result in a call to one of the two
+ * Tcl_NewWideIntObj implementations below. We provide two implementations
+ * so that the Tcl core can be compiled to do memory debugging of the
+ * core even if a client does not request it for itself.
+ *
+ * Results:
+ * The newly created object is returned. This object will have an
+ * invalid string representation. The returned object has ref count 0.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifdef TCL_MEM_DEBUG
+#undef Tcl_NewWideIntObj
+
+Tcl_Obj *
+Tcl_NewWideIntObj(wideValue)
+ register Tcl_WideInt wideValue; /* Wide integer used to initialize
+ * the new object. */
+{
+ return Tcl_DbNewWideIntObj(wideValue, "unknown", 0);
+}
+
+#else /* if not TCL_MEM_DEBUG */
+
+Tcl_Obj *
+Tcl_NewWideIntObj(wideValue)
+ register Tcl_WideInt wideValue; /* Wide integer used to initialize
+ * the new object. */
+{
+#ifdef TCL_WIDE_INT_IS_LONG
+ return Tcl_NewLongObj(wideValue);
+#else
+ register Tcl_Obj *objPtr;
+
+ TclNewObj(objPtr);
+ objPtr->bytes = NULL;
+
+ objPtr->internalRep.wideValue = wideValue;
+ objPtr->typePtr = &tclWideIntType;
+ return objPtr;
+#endif /* TCL_WIDE_INT_IS_LONG */
+}
+#endif /* if TCL_MEM_DEBUG */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_DbNewWideIntObj --
+ *
+ * If a client is compiled with TCL_MEM_DEBUG defined, calls to
+ * Tcl_NewWideIntObj to create new wide integer end up calling
+ * the debugging procedure Tcl_DbNewWideIntObj instead. We
+ * provide two implementations of Tcl_DbNewWideIntObj so that
+ * whether the Tcl core is compiled to do memory debugging of the
+ * core is independent of whether a client requests debugging for
+ * itself.
+ *
+ * When the core is compiled with TCL_MEM_DEBUG defined,
+ * Tcl_DbNewWideIntObj calls Tcl_DbCkalloc directly with the file
+ * name and line number from its caller. This simplifies
+ * debugging since then the checkmem command will report the
+ * caller's file name and line number when reporting objects that
+ * haven't been freed.
+ *
+ * Otherwise, when the core is compiled without TCL_MEM_DEBUG defined,
+ * this procedure just returns the result of calling Tcl_NewWideIntObj.
+ *
+ * Results:
+ * The newly created wide integer object is returned. This object
+ * will have an invalid string representation. The returned object has
+ * ref count 0.
+ *
+ * Side effects:
+ * Allocates memory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifdef TCL_MEM_DEBUG
+
+Tcl_Obj *
+Tcl_DbNewWideIntObj(wideValue, file, line)
+ register Tcl_WideInt wideValue; /* Wide integer used to initialize
+ * the new object. */
+ CONST char *file; /* The name of the source file
+ * calling this procedure; used for
+ * debugging. */
+ int line; /* Line number in the source file;
+ * used for debugging. */
+{
+#ifdef TCL_WIDE_INT_IS_LONG
+ return Tcl_DbNewLongObj(wideValue, file, line);
+#else
+ register Tcl_Obj *objPtr;
+
+ TclDbNewObj(objPtr, file, line);
+ objPtr->bytes = NULL;
+
+ objPtr->internalRep.wideValue = wideValue;
+ objPtr->typePtr = &tclWideIntType;
+ return objPtr;
+#endif
+}
+
+#else /* if not TCL_MEM_DEBUG */
+
+Tcl_Obj *
+Tcl_DbNewWideIntObj(wideValue, file, line)
+ register Tcl_WideInt wideValue; /* Long integer used to initialize
+ * the new object. */
+ CONST char *file; /* The name of the source file
+ * calling this procedure; used for
+ * debugging. */
+ int line; /* Line number in the source file;
+ * used for debugging. */
+{
+ return Tcl_NewWideIntObj(wideValue);
+}
+#endif /* TCL_MEM_DEBUG */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_SetWideIntObj --
+ *
+ * Modify an object to be a wide integer object and to have the
+ * specified wide integer value.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The object's old string rep, if any, is freed. Also, any old
+ * internal rep is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_SetWideIntObj(objPtr, wideValue)
+ register Tcl_Obj *objPtr; /* Object w. internal rep to init. */
+ register Tcl_WideInt wideValue; /* Wide integer used to initialize
+ * the object's value. */
+{
+#ifdef TCL_WIDE_INT_IS_LONG
+ Tcl_SetLongObj(objPtr, wideValue);
+#else
+ register Tcl_ObjType *oldTypePtr = objPtr->typePtr;
+
+ if (Tcl_IsShared(objPtr)) {
+ panic("Tcl_SetWideIntObj called with shared object");
+ }
+
+ if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
+ oldTypePtr->freeIntRepProc(objPtr);
+ }
+
+ objPtr->internalRep.wideValue = wideValue;
+ objPtr->typePtr = &tclWideIntType;
+ Tcl_InvalidateStringRep(objPtr);
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_GetWideIntFromObj --
+ *
+ * Attempt to return a wide integer from the Tcl object "objPtr". If
+ * the object is not already a wide int object, an attempt will be made
+ * to convert it to one.
+ *
+ * Results:
+ * The return value is a standard Tcl object result. If an error occurs
+ * during conversion, an error message is left in the interpreter's
+ * result unless "interp" is NULL.
+ *
+ * Side effects:
+ * If the object is not already an int object, the conversion will free
+ * any old internal representation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_GetWideIntFromObj(interp, objPtr, wideIntPtr)
+ Tcl_Interp *interp; /* Used for error reporting if not NULL. */
+ register Tcl_Obj *objPtr; /* Object from which to get a wide int. */
+ register Tcl_WideInt *wideIntPtr; /* Place to store resulting long. */
+{
+#ifdef TCL_WIDE_INT_IS_LONG
+ /*
+ * Next line is type-safe because we only do this when long = Tcl_WideInt
+ */
+ return Tcl_GetLongFromObj(interp, objPtr, wideIntPtr);
+#else
+ register int result;
+
+ if (objPtr->typePtr == &tclWideIntType) {
+ *wideIntPtr = objPtr->internalRep.wideValue;
+ return TCL_OK;
+ }
+ result = SetWideIntFromAny(interp, objPtr);
+ if (result == TCL_OK) {
+ *wideIntPtr = objPtr->internalRep.wideValue;
+ }
+ return result;
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_DbIncrRefCount --
*
* This procedure is normally called when debugging: i.e., when
@@ -1993,7 +2510,7 @@ void
Tcl_DbIncrRefCount(objPtr, file, line)
register Tcl_Obj *objPtr; /* The object we are registering a
* reference to. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -2033,7 +2550,7 @@ void
Tcl_DbDecrRefCount(objPtr, file, line)
register Tcl_Obj *objPtr; /* The object we are releasing a reference
* to. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -2074,7 +2591,7 @@ Tcl_DbDecrRefCount(objPtr, file, line)
int
Tcl_DbIsShared(objPtr, file, line)
register Tcl_Obj *objPtr; /* The object to test for being shared. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -2086,5 +2603,578 @@ Tcl_DbIsShared(objPtr, file, line)
panic("Trying to check whether previously disposed object is shared.");
}
#endif
+#ifdef TCL_COMPILE_STATS
+ Tcl_MutexLock(&tclObjMutex);
+ if ((objPtr)->refCount <= 1) {
+ tclObjsShared[1]++;
+ } else if ((objPtr)->refCount < TCL_MAX_SHARED_OBJ_STATS) {
+ tclObjsShared[(objPtr)->refCount]++;
+ } else {
+ tclObjsShared[0]++;
+ }
+ Tcl_MutexUnlock(&tclObjMutex);
+#endif
return ((objPtr)->refCount > 1);
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_InitObjHashTable --
+ *
+ * Given storage for a hash table, set up the fields to prepare
+ * the hash table for use, the keys are Tcl_Obj *.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * TablePtr is now ready to be passed to Tcl_FindHashEntry and
+ * Tcl_CreateHashEntry.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_InitObjHashTable(tablePtr)
+ register Tcl_HashTable *tablePtr; /* Pointer to table record, which
+ * is supplied by the caller. */
+{
+ Tcl_InitCustomHashTable(tablePtr, TCL_CUSTOM_PTR_KEYS,
+ &tclObjHashKeyType);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AllocObjEntry --
+ *
+ * Allocate space for a Tcl_HashEntry containing the Tcl_Obj * key.
+ *
+ * Results:
+ * The return value is a pointer to the created entry.
+ *
+ * Side effects:
+ * Increments the reference count on the object.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_HashEntry *
+AllocObjEntry(tablePtr, keyPtr)
+ Tcl_HashTable *tablePtr; /* Hash table. */
+ VOID *keyPtr; /* Key to store in the hash table entry. */
+{
+ Tcl_Obj *objPtr = (Tcl_Obj *) keyPtr;
+ Tcl_HashEntry *hPtr;
+
+ hPtr = (Tcl_HashEntry *) ckalloc((unsigned) (sizeof(Tcl_HashEntry)));
+ hPtr->key.oneWordValue = (char *) objPtr;
+ Tcl_IncrRefCount (objPtr);
+
+ return hPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompareObjKeys --
+ *
+ * Compares two Tcl_Obj * keys.
+ *
+ * Results:
+ * The return value is 0 if they are different and 1 if they are
+ * the same.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CompareObjKeys(keyPtr, hPtr)
+ VOID *keyPtr; /* New key to compare. */
+ Tcl_HashEntry *hPtr; /* Existing key to compare. */
+{
+ Tcl_Obj *objPtr1 = (Tcl_Obj *) keyPtr;
+ Tcl_Obj *objPtr2 = (Tcl_Obj *) hPtr->key.oneWordValue;
+ register CONST char *p1, *p2;
+ register int l1, l2;
+
+ /*
+ * If the object pointers are the same then they match.
+ */
+ if (objPtr1 == objPtr2) {
+ return 1;
+ }
+
+ /*
+ * Don't use Tcl_GetStringFromObj as it would prevent l1 and l2 being
+ * in a register.
+ */
+ p1 = Tcl_GetString (objPtr1);
+ l1 = objPtr1->length;
+ p2 = Tcl_GetString (objPtr2);
+ l2 = objPtr2->length;
+
+ /*
+ * Only compare if the string representations are of the same length.
+ */
+ if (l1 == l2) {
+ for (;; p1++, p2++, l1--) {
+ if (*p1 != *p2) {
+ break;
+ }
+ if (l1 == 0) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeObjEntry --
+ *
+ * Frees space for a Tcl_HashEntry containing the Tcl_Obj * key.
+ *
+ * Results:
+ * The return value is a pointer to the created entry.
+ *
+ * Side effects:
+ * Decrements the reference count of the object.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeObjEntry(hPtr)
+ Tcl_HashEntry *hPtr; /* Hash entry to free. */
+{
+ Tcl_Obj *objPtr = (Tcl_Obj *) hPtr->key.oneWordValue;
+
+ Tcl_DecrRefCount (objPtr);
+ ckfree ((char *) hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HashObjKey --
+ *
+ * Compute a one-word summary of the string representation of the
+ * Tcl_Obj, which can be used to generate a hash index.
+ *
+ * Results:
+ * The return value is a one-word summary of the information in
+ * the string representation of the Tcl_Obj.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static unsigned int
+HashObjKey(tablePtr, keyPtr)
+ Tcl_HashTable *tablePtr; /* Hash table. */
+ VOID *keyPtr; /* Key from which to compute hash value. */
+{
+ Tcl_Obj *objPtr = (Tcl_Obj *) keyPtr;
+ register CONST char *string;
+ register int length;
+ register unsigned int result;
+ register int c;
+
+ string = Tcl_GetString (objPtr);
+ length = objPtr->length;
+
+ /*
+ * I tried a zillion different hash functions and asked many other
+ * people for advice. Many people had their own favorite functions,
+ * all different, but no-one had much idea why they were good ones.
+ * I chose the one below (multiply by 9 and add new character)
+ * because of the following reasons:
+ *
+ * 1. Multiplying by 10 is perfect for keys that are decimal strings,
+ * and multiplying by 9 is just about as good.
+ * 2. Times-9 is (shift-left-3) plus (old). This means that each
+ * character's bits hang around in the low-order bits of the
+ * hash value for ever, plus they spread fairly rapidly up to
+ * the high-order bits to fill out the hash value. This seems
+ * works well both for decimal and non-decimal strings.
+ */
+
+ result = 0;
+ while (length) {
+ c = *string;
+ string++;
+ length--;
+ if (length == 0) {
+ break;
+ }
+ result += (result<<3) + c;
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_GetCommandFromObj --
+ *
+ * Returns the command specified by the name in a Tcl_Obj.
+ *
+ * Results:
+ * Returns a token for the command if it is found. Otherwise, if it
+ * can't be found or there is an error, returns NULL.
+ *
+ * Side effects:
+ * May update the internal representation for the object, caching
+ * the command reference so that the next time this procedure is
+ * called with the same object, the command can be found quickly.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Command
+Tcl_GetCommandFromObj(interp, objPtr)
+ Tcl_Interp *interp; /* The interpreter in which to resolve the
+ * command and to report errors. */
+ register Tcl_Obj *objPtr; /* The object containing the command's
+ * name. If the name starts with "::", will
+ * be looked up in global namespace. Else,
+ * looked up first in the current namespace,
+ * then in global namespace. */
+{
+ Interp *iPtr = (Interp *) interp;
+ register ResolvedCmdName *resPtr;
+ register Command *cmdPtr;
+ Namespace *currNsPtr;
+ int result;
+ CallFrame *savedFramePtr;
+ char *name;
+
+ /*
+ * If the variable name is fully qualified, do as if the lookup were
+ * done from the global namespace; this helps avoid repeated lookups
+ * of fully qualified names. It costs close to nothing, and may be very
+ * helpful for OO applications which pass along a command name ("this"),
+ * [Patch 456668]
+ */
+
+ savedFramePtr = iPtr->varFramePtr;
+ name = Tcl_GetString(objPtr);
+ if ((*name++ == ':') && (*name == ':')) {
+ iPtr->varFramePtr = NULL;
+ }
+
+ /*
+ * Get the internal representation, converting to a command type if
+ * needed. The internal representation is a ResolvedCmdName that points
+ * to the actual command.
+ */
+
+ if (objPtr->typePtr != &tclCmdNameType) {
+ result = tclCmdNameType.setFromAnyProc(interp, objPtr);
+ if (result != TCL_OK) {
+ iPtr->varFramePtr = savedFramePtr;
+ return (Tcl_Command) NULL;
+ }
+ }
+ resPtr = (ResolvedCmdName *) objPtr->internalRep.otherValuePtr;
+
+ /*
+ * Get the current namespace.
+ */
+
+ if (iPtr->varFramePtr != NULL) {
+ currNsPtr = iPtr->varFramePtr->nsPtr;
+ } else {
+ currNsPtr = iPtr->globalNsPtr;
+ }
+
+ /*
+ * Check the context namespace and the namespace epoch of the resolved
+ * symbol to make sure that it is fresh. If not, then force another
+ * conversion to the command type, to discard the old rep and create a
+ * new one. Note that we verify that the namespace id of the context
+ * namespace is the same as the one we cached; this insures that the
+ * namespace wasn't deleted and a new one created at the same address
+ * with the same command epoch.
+ */
+
+ cmdPtr = NULL;
+ if ((resPtr != NULL)
+ && (resPtr->refNsPtr == currNsPtr)
+ && (resPtr->refNsId == currNsPtr->nsId)
+ && (resPtr->refNsCmdEpoch == currNsPtr->cmdRefEpoch)) {
+ cmdPtr = resPtr->cmdPtr;
+ if (cmdPtr->cmdEpoch != resPtr->cmdEpoch) {
+ cmdPtr = NULL;
+ }
+ }
+
+ if (cmdPtr == NULL) {
+ result = tclCmdNameType.setFromAnyProc(interp, objPtr);
+ if (result != TCL_OK) {
+ iPtr->varFramePtr = savedFramePtr;
+ return (Tcl_Command) NULL;
+ }
+ resPtr = (ResolvedCmdName *) objPtr->internalRep.otherValuePtr;
+ if (resPtr != NULL) {
+ cmdPtr = resPtr->cmdPtr;
+ }
+ }
+ iPtr->varFramePtr = savedFramePtr;
+ return (Tcl_Command) cmdPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclSetCmdNameObj --
+ *
+ * Modify an object to be an CmdName object that refers to the argument
+ * Command structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The object's old internal rep is freed. It's string rep is not
+ * changed. The refcount in the Command structure is incremented to
+ * keep it from being freed if the command is later deleted until
+ * TclExecuteByteCode has a chance to recognize that it was deleted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclSetCmdNameObj(interp, objPtr, cmdPtr)
+ Tcl_Interp *interp; /* Points to interpreter containing command
+ * that should be cached in objPtr. */
+ register Tcl_Obj *objPtr; /* Points to Tcl object to be changed to
+ * a CmdName object. */
+ Command *cmdPtr; /* Points to Command structure that the
+ * CmdName object should refer to. */
+{
+ Interp *iPtr = (Interp *) interp;
+ register ResolvedCmdName *resPtr;
+ Tcl_ObjType *oldTypePtr = objPtr->typePtr;
+ register Namespace *currNsPtr;
+
+ if (oldTypePtr == &tclCmdNameType) {
+ return;
+ }
+
+ /*
+ * Get the current namespace.
+ */
+
+ if (iPtr->varFramePtr != NULL) {
+ currNsPtr = iPtr->varFramePtr->nsPtr;
+ } else {
+ currNsPtr = iPtr->globalNsPtr;
+ }
+
+ cmdPtr->refCount++;
+ resPtr = (ResolvedCmdName *) ckalloc(sizeof(ResolvedCmdName));
+ resPtr->cmdPtr = cmdPtr;
+ resPtr->refNsPtr = currNsPtr;
+ resPtr->refNsId = currNsPtr->nsId;
+ resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch;
+ resPtr->cmdEpoch = cmdPtr->cmdEpoch;
+ resPtr->refCount = 1;
+
+ if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
+ oldTypePtr->freeIntRepProc(objPtr);
+ }
+ objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr;
+ objPtr->internalRep.twoPtrValue.ptr2 = NULL;
+ objPtr->typePtr = &tclCmdNameType;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeCmdNameInternalRep --
+ *
+ * Frees the resources associated with a cmdName object's internal
+ * representation.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Decrements the ref count of any cached ResolvedCmdName structure
+ * pointed to by the cmdName's internal representation. If this is
+ * the last use of the ResolvedCmdName, it is freed. This in turn
+ * decrements the ref count of the Command structure pointed to by
+ * the ResolvedSymbol, which may free the Command structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeCmdNameInternalRep(objPtr)
+ register Tcl_Obj *objPtr; /* CmdName object with internal
+ * representation to free. */
+{
+ register ResolvedCmdName *resPtr =
+ (ResolvedCmdName *) objPtr->internalRep.otherValuePtr;
+
+ if (resPtr != NULL) {
+ /*
+ * Decrement the reference count of the ResolvedCmdName structure.
+ * If there are no more uses, free the ResolvedCmdName structure.
+ */
+
+ resPtr->refCount--;
+ if (resPtr->refCount == 0) {
+ /*
+ * Now free the cached command, unless it is still in its
+ * hash table or if there are other references to it
+ * from other cmdName objects.
+ */
+
+ Command *cmdPtr = resPtr->cmdPtr;
+ TclCleanupCommand(cmdPtr);
+ ckfree((char *) resPtr);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DupCmdNameInternalRep --
+ *
+ * Initialize the internal representation of an cmdName Tcl_Obj to a
+ * copy of the internal representation of an existing cmdName object.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * "copyPtr"s internal rep is set to point to the ResolvedCmdName
+ * structure corresponding to "srcPtr"s internal rep. Increments the
+ * ref count of the ResolvedCmdName structure pointed to by the
+ * cmdName's internal representation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DupCmdNameInternalRep(srcPtr, copyPtr)
+ Tcl_Obj *srcPtr; /* Object with internal rep to copy. */
+ register Tcl_Obj *copyPtr; /* Object with internal rep to set. */
+{
+ register ResolvedCmdName *resPtr =
+ (ResolvedCmdName *) srcPtr->internalRep.otherValuePtr;
+
+ copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr;
+ copyPtr->internalRep.twoPtrValue.ptr2 = NULL;
+ if (resPtr != NULL) {
+ resPtr->refCount++;
+ }
+ copyPtr->typePtr = &tclCmdNameType;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetCmdNameFromAny --
+ *
+ * Generate an cmdName internal form for the Tcl object "objPtr".
+ *
+ * Results:
+ * The return value is a standard Tcl result. The conversion always
+ * succeeds and TCL_OK is returned.
+ *
+ * Side effects:
+ * A pointer to a ResolvedCmdName structure that holds a cached pointer
+ * to the command with a name that matches objPtr's string rep is
+ * stored as objPtr's internal representation. This ResolvedCmdName
+ * pointer will be NULL if no matching command was found. The ref count
+ * of the cached Command's structure (if any) is also incremented.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SetCmdNameFromAny(interp, objPtr)
+ Tcl_Interp *interp; /* Used for error reporting if not NULL. */
+ register Tcl_Obj *objPtr; /* The object to convert. */
+{
+ Interp *iPtr = (Interp *) interp;
+ char *name;
+ Tcl_Command cmd;
+ register Command *cmdPtr;
+ Namespace *currNsPtr;
+ register ResolvedCmdName *resPtr;
+
+ /*
+ * Get "objPtr"s string representation. Make it up-to-date if necessary.
+ */
+
+ name = objPtr->bytes;
+ if (name == NULL) {
+ name = Tcl_GetString(objPtr);
+ }
+
+ /*
+ * Find the Command structure, if any, that describes the command called
+ * "name". Build a ResolvedCmdName that holds a cached pointer to this
+ * Command, and bump the reference count in the referenced Command
+ * structure. A Command structure will not be deleted as long as it is
+ * referenced from a CmdName object.
+ */
+
+ cmd = Tcl_FindCommand(interp, name, (Tcl_Namespace *) NULL,
+ /*flags*/ 0);
+ cmdPtr = (Command *) cmd;
+ if (cmdPtr != NULL) {
+ /*
+ * Get the current namespace.
+ */
+
+ if (iPtr->varFramePtr != NULL) {
+ currNsPtr = iPtr->varFramePtr->nsPtr;
+ } else {
+ currNsPtr = iPtr->globalNsPtr;
+ }
+
+ cmdPtr->refCount++;
+ resPtr = (ResolvedCmdName *) ckalloc(sizeof(ResolvedCmdName));
+ resPtr->cmdPtr = cmdPtr;
+ resPtr->refNsPtr = currNsPtr;
+ resPtr->refNsId = currNsPtr->nsId;
+ resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch;
+ resPtr->cmdEpoch = cmdPtr->cmdEpoch;
+ resPtr->refCount = 1;
+ } else {
+ resPtr = NULL; /* no command named "name" was found */
+ }
+
+ /*
+ * Free the old internalRep before setting the new one. We do this as
+ * late as possible to allow the conversion code, in particular
+ * GetStringFromObj, to use that old internalRep. If no Command
+ * structure was found, leave NULL as the cached value.
+ */
+
+ if ((objPtr->typePtr != NULL)
+ && (objPtr->typePtr->freeIntRepProc != NULL)) {
+ objPtr->typePtr->freeIntRepProc(objPtr);
+ }
+
+ objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr;
+ objPtr->internalRep.twoPtrValue.ptr2 = NULL;
+ objPtr->typePtr = &tclCmdNameType;
+ return TCL_OK;
+}
diff --git a/tcl/generic/tclPanic.c b/tcl/generic/tclPanic.c
index 4e8cc1e2365..4f446b329cf 100644
--- a/tcl/generic/tclPanic.c
+++ b/tcl/generic/tclPanic.c
@@ -2,8 +2,8 @@
* tclPanic.c --
*
* Source code for the "Tcl_Panic" library procedure for Tcl;
- * individual applications will probably override this with
- * an application-specific panic procedure.
+ * individual applications will probably call Tcl_SetPanicProc()
+ * to set an application-specific panic procedure.
*
* Copyright (c) 1988-1993 The Regents of the University of California.
* Copyright (c) 1994 Sun Microsystems, Inc.
@@ -16,13 +16,23 @@
*/
#include "tclInt.h"
+#include "tclPort.h"
/*
* The panicProc variable contains a pointer to an application
* specific panic procedure.
*/
-void (*panicProc) _ANSI_ARGS_(TCL_VARARGS(char *,format)) = NULL;
+static Tcl_PanicProc *panicProc = NULL;
+
+/*
+ * The platformPanicProc variable contains a pointer to a platform
+ * specific panic procedure, if any. ( TclpPanic may be NULL via
+ * a macro. )
+ */
+
+static Tcl_PanicProc * CONST platformPanicProc = TclpPanic;
+
/*
*----------------------------------------------------------------------
@@ -42,7 +52,7 @@ void (*panicProc) _ANSI_ARGS_(TCL_VARARGS(char *,format)) = NULL;
void
Tcl_SetPanicProc(proc)
- void (*proc) _ANSI_ARGS_(TCL_VARARGS(char *,format));
+ Tcl_PanicProc *proc;
{
panicProc = proc;
}
@@ -65,7 +75,7 @@ Tcl_SetPanicProc(proc)
void
Tcl_PanicVA (format, argList)
- char *format; /* Format string, suitable for passing to
+ CONST char *format; /* Format string, suitable for passing to
* fprintf. */
va_list argList; /* Variable argument list. */
{
@@ -85,6 +95,9 @@ Tcl_PanicVA (format, argList)
if (panicProc != NULL) {
(void) (*panicProc)(format, arg1, arg2, arg3, arg4,
arg5, arg6, arg7, arg8);
+ } else if (platformPanicProc != NULL) {
+ (void) (*platformPanicProc)(format, arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7, arg8);
} else {
(void) fprintf(stderr, format, arg1, arg2, arg3, arg4, arg5, arg6,
arg7, arg8);
@@ -97,7 +110,7 @@ Tcl_PanicVA (format, argList)
/*
*----------------------------------------------------------------------
*
- * panic --
+ * Tcl_Panic --
*
* Print an error message and kill the process.
*
@@ -112,12 +125,12 @@ Tcl_PanicVA (format, argList)
/* VARARGS ARGSUSED */
void
-panic TCL_VARARGS_DEF(char *,arg1)
+Tcl_Panic TCL_VARARGS_DEF(CONST char *,arg1)
{
va_list argList;
- char *format;
+ CONST char *format;
- format = TCL_VARARGS_START(char *,arg1,argList);
+ format = TCL_VARARGS_START(CONST char *,arg1,argList);
Tcl_PanicVA(format, argList);
va_end (argList);
}
diff --git a/tcl/generic/tclParse.c b/tcl/generic/tclParse.c
index 1422cd02336..c39f8f57156 100644
--- a/tcl/generic/tclParse.c
+++ b/tcl/generic/tclParse.c
@@ -4,12 +4,11 @@
* This file contains procedures that parse Tcl scripts. They
* do so in a general-purpose fashion that can be used for many
* different purposes, including compilation, direct execution,
- * code analysis, etc. This file also includes a few additional
- * procedures such as Tcl_EvalObjv, Tcl_Eval, and Tcl_EvalEx, which
- * allow scripts to be evaluated directly, without compiling.
+ * code analysis, etc.
*
* Copyright (c) 1997 Sun Microsystems, Inc.
- * Copyright (c) 1998 by Scriptics Corporation.
+ * Copyright (c) 1998-2000 Ajuba Solutions.
+ * Contributions from Don Porter, NIST, 2002. (not subject to US copyright)
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -33,32 +32,32 @@
* information about its character argument. The following return
* values are defined.
*
- * TYPE_NORMAL - All characters that don't have special significance
- * to the Tcl parser.
- * TYPE_SPACE - The character is a whitespace character other
- * than newline.
- * TYPE_COMMAND_END - Character is newline or semicolon.
- * TYPE_SUBS - Character begins a substitution or has other
- * special meaning in ParseTokens: backslash, dollar
- * sign, open bracket, or null.
- * TYPE_QUOTE - Character is a double quote.
- * TYPE_CLOSE_PAREN - Character is a right parenthesis.
- * TYPE_CLOSE_BRACK - Character is a right square bracket.
- * TYPE_BRACE - Character is a curly brace (either left or right).
+ * TYPE_NORMAL - All characters that don't have special significance
+ * to the Tcl parser.
+ * TYPE_SPACE - The character is a whitespace character other
+ * than newline.
+ * TYPE_COMMAND_END - Character is newline or semicolon.
+ * TYPE_SUBS - Character begins a substitution or has other
+ * special meaning in ParseTokens: backslash, dollar
+ * sign, or open bracket.
+ * TYPE_QUOTE - Character is a double quote.
+ * TYPE_CLOSE_PAREN - Character is a right parenthesis.
+ * TYPE_CLOSE_BRACK - Character is a right square bracket.
+ * TYPE_BRACE - Character is a curly brace (either left or right).
*/
-#define TYPE_NORMAL 0
-#define TYPE_SPACE 0x1
-#define TYPE_COMMAND_END 0x2
-#define TYPE_SUBS 0x4
-#define TYPE_QUOTE 0x8
-#define TYPE_CLOSE_PAREN 0x10
-#define TYPE_CLOSE_BRACK 0x20
-#define TYPE_BRACE 0x40
+#define TYPE_NORMAL 0
+#define TYPE_SPACE 0x1
+#define TYPE_COMMAND_END 0x2
+#define TYPE_SUBS 0x4
+#define TYPE_QUOTE 0x8
+#define TYPE_CLOSE_PAREN 0x10
+#define TYPE_CLOSE_BRACK 0x20
+#define TYPE_BRACE 0x40
-#define CHAR_TYPE(c) (typeTable+128)[(int)(c)]
+#define CHAR_TYPE(c) (charTypeTable+128)[(int)(c)]
-char typeTable[] = {
+static CONST char charTypeTable[] = {
/*
* Negative character values, from -128 to -1:
*/
@@ -175,14 +174,13 @@ char typeTable[] = {
* Prototypes for local procedures defined in this file:
*/
-static int CommandComplete _ANSI_ARGS_((char *script,
- int length));
-static int ParseTokens _ANSI_ARGS_((char *src, int mask,
+static int CommandComplete _ANSI_ARGS_((CONST char *script,
+ int numBytes));
+static int ParseComment _ANSI_ARGS_((CONST char *src, int numBytes,
Tcl_Parse *parsePtr));
-static int EvalObjv _ANSI_ARGS_((Tcl_Interp *interp, int objc,
- Tcl_Obj *CONST objv[], char *command, int length,
- int flags));
-
+static int ParseTokens _ANSI_ARGS_((CONST char *src, int numBytes,
+ int mask, Tcl_Parse *parsePtr));
+
/*
*----------------------------------------------------------------------
*
@@ -214,14 +212,9 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
Tcl_Interp *interp; /* Interpreter to use for error reporting;
* if NULL, then no error message is
* provided. */
- char *string; /* First character of string containing
- * one or more Tcl commands. The string
- * must be in writable memory and must
- * have one additional byte of space at
- * string[length] where we can
- * temporarily store a 0 sentinel
- * character. */
- int numBytes; /* Total number of bytes in string. If < 0,
+ CONST char *string; /* First character of string containing
+ * one or more Tcl commands. */
+ register int numBytes; /* Total number of bytes in string. If < 0,
* the script consists of all bytes up to
* the first null character. */
int nested; /* Non-zero means this is a nested command:
@@ -234,21 +227,25 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
* information in the structure is
* ignored. */
{
- register char *src; /* Points to current character
+ register CONST char *src; /* Points to current character
* in the command. */
- int type; /* Result returned by CHAR_TYPE(*src). */
+ char type; /* Result returned by CHAR_TYPE(*src). */
Tcl_Token *tokenPtr; /* Pointer to token being filled in. */
int wordIndex; /* Index of word token for current word. */
- char utfBytes[TCL_UTF_MAX]; /* Holds result of backslash substitution. */
int terminators; /* CHAR_TYPE bits that indicate the end
* of a command. */
- char *termPtr; /* Set by Tcl_ParseBraces/QuotedString to
+ CONST char *termPtr; /* Set by Tcl_ParseBraces/QuotedString to
* point to char after terminating one. */
- int length, savedChar;
-
-
+ int scanned;
+
+ if ((string == NULL) && (numBytes>0)) {
+ if (interp != NULL) {
+ Tcl_SetResult(interp, "can't parse a NULL pointer", TCL_STATIC);
+ }
+ return TCL_ERROR;
+ }
if (numBytes < 0) {
- numBytes = (string? strlen(string) : 0);
+ numBytes = strlen(string);
}
parsePtr->commentStart = NULL;
parsePtr->commentSize = 0;
@@ -271,66 +268,15 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
}
/*
- * Temporarily overwrite the character just after the end of the
- * string with a 0 byte. This acts as a sentinel and reduces the
- * number of places where we have to check for the end of the
- * input string. The original value of the byte is restored at
- * the end of the parse.
- */
-
- savedChar = string[numBytes];
- if (savedChar != 0) {
- string[numBytes] = 0;
- }
-
- /*
* Parse any leading space and comments before the first word of the
* command.
*/
- src = string;
- while (1) {
- while ((CHAR_TYPE(*src) == TYPE_SPACE) || (*src == '\n')) {
- src++;
- }
- if ((*src == '\\') && (src[1] == '\n')) {
- /*
- * Skip backslash-newline sequence: it should be treated
- * just like white space.
- */
-
- if ((src + 2) == parsePtr->end) {
- parsePtr->incomplete = 1;
- }
- src += 2;
- continue;
- }
- if (*src != '#') {
- break;
- }
- if (parsePtr->commentStart == NULL) {
- parsePtr->commentStart = src;
- }
- while (1) {
- if (src == parsePtr->end) {
- if (nested) {
- parsePtr->incomplete = nested;
- }
- parsePtr->commentSize = src - parsePtr->commentStart;
- break;
- } else if (*src == '\\') {
- if ((src[1] == '\n') && ((src + 2) == parsePtr->end)) {
- parsePtr->incomplete = 1;
- }
- Tcl_UtfBackslash(src, &length, utfBytes);
- src += length;
- } else if (*src == '\n') {
- src++;
- parsePtr->commentSize = src - parsePtr->commentStart;
- break;
- } else {
- src++;
- }
+ scanned = ParseComment(string, numBytes, parsePtr);
+ src = (string + scanned); numBytes -= scanned;
+ if (numBytes == 0) {
+ if (nested) {
+ parsePtr->incomplete = nested;
}
}
@@ -357,19 +303,9 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
* sequence: it should be treated just like white space.
*/
- while (1) {
- type = CHAR_TYPE(*src);
- if (type == TYPE_SPACE) {
- src++;
- continue;
- } else if ((*src == '\\') && (src[1] == '\n')) {
- if ((src + 2) == parsePtr->end) {
- parsePtr->incomplete = 1;
- }
- Tcl_UtfBackslash(src, &length, utfBytes);
- src += length;
- continue;
- }
+ scanned = TclParseWhiteSpace(src, numBytes, parsePtr, &type);
+ src += scanned; numBytes -= scanned;
+ if (numBytes == 0) {
break;
}
if ((type & terminators) != 0) {
@@ -377,9 +313,6 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
src++;
break;
}
- if (src == parsePtr->end) {
- break;
- }
tokenPtr->start = src;
parsePtr->numTokens++;
parsePtr->numWords++;
@@ -391,28 +324,28 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
*/
if (*src == '"') {
- if (Tcl_ParseQuotedString(interp, src, (parsePtr->end - src),
- parsePtr, 1, &termPtr) != TCL_OK) {
+ if (Tcl_ParseQuotedString(interp, src, numBytes,
+ parsePtr, 1, &termPtr) != TCL_OK) {
goto error;
}
- src = termPtr;
+ src = termPtr; numBytes = parsePtr->end - src;
} else if (*src == '{') {
- if (Tcl_ParseBraces(interp, src, (parsePtr->end - src),
- parsePtr, 1, &termPtr) != TCL_OK) {
+ if (Tcl_ParseBraces(interp, src, numBytes,
+ parsePtr, 1, &termPtr) != TCL_OK) {
goto error;
}
- src = termPtr;
+ src = termPtr; numBytes = parsePtr->end - src;
} else {
/*
* This is an unquoted word. Call ParseTokens and let it do
* all of the work.
*/
- if (ParseTokens(src, TYPE_SPACE|terminators,
+ if (ParseTokens(src, numBytes, TYPE_SPACE|terminators,
parsePtr) != TCL_OK) {
goto error;
}
- src = parsePtr->term;
+ src = parsePtr->term; numBytes = parsePtr->end - src;
}
/*
@@ -436,32 +369,18 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
* command.
*/
- type = CHAR_TYPE(*src);
- if (type == TYPE_SPACE) {
- src++;
+ scanned = TclParseWhiteSpace(src, numBytes, parsePtr, &type);
+ if (scanned) {
+ src += scanned; numBytes -= scanned;
continue;
- } else {
- /*
- * Backslash-newline (and any following white space) must be
- * treated as if it were a space character.
- */
-
- if ((*src == '\\') && (src[1] == '\n')) {
- if ((src + 2) == parsePtr->end) {
- parsePtr->incomplete = 1;
- }
- Tcl_UtfBackslash(src, &length, utfBytes);
- src += length;
- continue;
- }
}
- if ((type & terminators) != 0) {
- parsePtr->term = src;
- src++;
+ if (numBytes == 0) {
break;
}
- if (src == parsePtr->end) {
+ if ((type & terminators) != 0) {
+ parsePtr->term = src;
+ src++;
break;
}
if (src[-1] == '"') {
@@ -481,17 +400,10 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
goto error;
}
-
parsePtr->commandSize = src - parsePtr->commandStart;
- if (savedChar != 0) {
- string[numBytes] = (char) savedChar;
- }
return TCL_OK;
error:
- if (savedChar != 0) {
- string[numBytes] = (char) savedChar;
- }
Tcl_FreeParse(parsePtr);
if (parsePtr->commandStart == NULL) {
parsePtr->commandStart = string;
@@ -499,17 +411,361 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
parsePtr->commandSize = parsePtr->term - parsePtr->commandStart;
return TCL_ERROR;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclParseWhiteSpace --
+ *
+ * Scans up to numBytes bytes starting at src, consuming white
+ * space as defined by Tcl's parsing rules.
+ *
+ * Results:
+ * Returns the number of bytes recognized as white space. Records
+ * at parsePtr, information about the parse. Records at typePtr
+ * the character type of the non-whitespace character that terminated
+ * the scan.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclParseWhiteSpace(src, numBytes, parsePtr, typePtr)
+ CONST char *src; /* First character to parse. */
+ register int numBytes; /* Max number of bytes to scan. */
+ Tcl_Parse *parsePtr; /* Information about parse in progress.
+ * Updated if parsing indicates
+ * an incomplete command. */
+ char *typePtr; /* Points to location to store character
+ * type of character that ends run
+ * of whitespace */
+{
+ register char type = TYPE_NORMAL;
+ register CONST char *p = src;
+
+ while (1) {
+ while (numBytes && ((type = CHAR_TYPE(*p)) & TYPE_SPACE)) {
+ numBytes--; p++;
+ }
+ if (numBytes && (type & TYPE_SUBS)) {
+ if (*p != '\\') {
+ break;
+ }
+ if (--numBytes == 0) {
+ break;
+ }
+ if (p[1] != '\n') {
+ break;
+ }
+ p+=2;
+ if (--numBytes == 0) {
+ parsePtr->incomplete = 1;
+ break;
+ }
+ continue;
+ }
+ break;
+ }
+ *typePtr = type;
+ return (p - src);
+}
/*
*----------------------------------------------------------------------
*
+ * TclParseHex --
+ *
+ * Scans a hexadecimal number as a Tcl_UniChar value.
+ * (e.g., for parsing \x and \u escape sequences).
+ * At most numBytes bytes are scanned.
+ *
+ * Results:
+ * The numeric value is stored in *resultPtr.
+ * Returns the number of bytes consumed.
+ *
+ * Notes:
+ * Relies on the following properties of the ASCII
+ * character set, with which UTF-8 is compatible:
+ *
+ * The digits '0' .. '9' and the letters 'A' .. 'Z' and 'a' .. 'z'
+ * occupy consecutive code points, and '0' < 'A' < 'a'.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclParseHex(src, numBytes, resultPtr)
+ CONST char *src; /* First character to parse. */
+ int numBytes; /* Max number of byes to scan */
+ Tcl_UniChar *resultPtr; /* Points to storage provided by
+ * caller where the Tcl_UniChar
+ * resulting from the conversion is
+ * to be written. */
+{
+ Tcl_UniChar result = 0;
+ register CONST char *p = src;
+
+ while (numBytes--) {
+ unsigned char digit = UCHAR(*p);
+
+ if (!isxdigit(digit))
+ break;
+
+ ++p;
+ result <<= 4;
+
+ if (digit >= 'a') {
+ result |= (10 + digit - 'a');
+ } else if (digit >= 'A') {
+ result |= (10 + digit - 'A');
+ } else {
+ result |= (digit - '0');
+ }
+ }
+
+ *resultPtr = result;
+ return (p - src);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclParseBackslash --
+ *
+ * Scans up to numBytes bytes starting at src, consuming a
+ * backslash sequence as defined by Tcl's parsing rules.
+ *
+ * Results:
+ * Records at readPtr the number of bytes making up the backslash
+ * sequence. Records at dst the UTF-8 encoded equivalent of
+ * that backslash sequence. Returns the number of bytes written
+ * to dst, at most TCL_UTF_MAX. Either readPtr or dst may be
+ * NULL, if the results are not needed, but the return value is
+ * the same either way.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclParseBackslash(src, numBytes, readPtr, dst)
+ CONST char * src; /* Points to the backslash character of a
+ * a backslash sequence */
+ int numBytes; /* Max number of bytes to scan */
+ int *readPtr; /* NULL, or points to storage where the
+ * number of bytes scanned should be written. */
+ char *dst; /* NULL, or points to buffer where the UTF-8
+ * encoding of the backslash sequence is to be
+ * written. At most TCL_UTF_MAX bytes will be
+ * written there. */
+{
+ register CONST char *p = src+1;
+ Tcl_UniChar result;
+ int count;
+ char buf[TCL_UTF_MAX];
+
+ if (numBytes == 0) {
+ if (readPtr != NULL) {
+ *readPtr = 0;
+ }
+ return 0;
+ }
+
+ if (dst == NULL) {
+ dst = buf;
+ }
+
+ if (numBytes == 1) {
+ /* Can only scan the backslash. Return it. */
+ result = '\\';
+ count = 1;
+ goto done;
+ }
+
+ count = 2;
+ switch (*p) {
+ /*
+ * Note: in the conversions below, use absolute values (e.g.,
+ * 0xa) rather than symbolic values (e.g. \n) that get converted
+ * by the compiler. It's possible that compilers on some
+ * platforms will do the symbolic conversions differently, which
+ * could result in non-portable Tcl scripts.
+ */
+
+ case 'a':
+ result = 0x7;
+ break;
+ case 'b':
+ result = 0x8;
+ break;
+ case 'f':
+ result = 0xc;
+ break;
+ case 'n':
+ result = 0xa;
+ break;
+ case 'r':
+ result = 0xd;
+ break;
+ case 't':
+ result = 0x9;
+ break;
+ case 'v':
+ result = 0xb;
+ break;
+ case 'x':
+ count += TclParseHex(p+1, numBytes-1, &result);
+ if (count == 2) {
+ /* No hexadigits -> This is just "x". */
+ result = 'x';
+ } else {
+ /* Keep only the last byte (2 hex digits) */
+ result = (unsigned char) result;
+ }
+ break;
+ case 'u':
+ count += TclParseHex(p+1, (numBytes > 5) ? 4 : numBytes-1, &result);
+ if (count == 2) {
+ /* No hexadigits -> This is just "u". */
+ result = 'u';
+ }
+ break;
+ case '\n':
+ count--;
+ do {
+ p++; count++;
+ } while ((count < numBytes) && ((*p == ' ') || (*p == '\t')));
+ result = ' ';
+ break;
+ case 0:
+ result = '\\';
+ count = 1;
+ break;
+ default:
+ /*
+ * Check for an octal number \oo?o?
+ */
+ if (isdigit(UCHAR(*p)) && (UCHAR(*p) < '8')) { /* INTL: digit */
+ result = (unsigned char)(*p - '0');
+ p++;
+ if ((numBytes == 2) || !isdigit(UCHAR(*p)) /* INTL: digit */
+ || (UCHAR(*p) >= '8')) {
+ break;
+ }
+ count = 3;
+ result = (unsigned char)((result << 3) + (*p - '0'));
+ p++;
+ if ((numBytes == 3) || !isdigit(UCHAR(*p)) /* INTL: digit */
+ || (UCHAR(*p) >= '8')) {
+ break;
+ }
+ count = 4;
+ result = (unsigned char)((result << 3) + (*p - '0'));
+ break;
+ }
+ /*
+ * We have to convert here in case the user has put a
+ * backslash in front of a multi-byte utf-8 character.
+ * While this means nothing special, we shouldn't break up
+ * a correct utf-8 character. [Bug #217987] test subst-3.2
+ */
+ if (Tcl_UtfCharComplete(p, numBytes - 1)) {
+ count = Tcl_UtfToUniChar(p, &result) + 1; /* +1 for '\' */
+ } else {
+ char utfBytes[TCL_UTF_MAX];
+ memcpy(utfBytes, p, (size_t) (numBytes - 1));
+ utfBytes[numBytes - 1] = '\0';
+ count = Tcl_UtfToUniChar(utfBytes, &result) + 1;
+ }
+ break;
+ }
+
+ done:
+ if (readPtr != NULL) {
+ *readPtr = count;
+ }
+ return Tcl_UniCharToUtf((int) result, dst);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParseComment --
+ *
+ * Scans up to numBytes bytes starting at src, consuming a
+ * Tcl comment as defined by Tcl's parsing rules.
+ *
+ * Results:
+ * Records in parsePtr information about the parse. Returns the
+ * number of bytes consumed.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ParseComment(src, numBytes, parsePtr)
+ CONST char *src; /* First character to parse. */
+ register int numBytes; /* Max number of bytes to scan. */
+ Tcl_Parse *parsePtr; /* Information about parse in progress.
+ * Updated if parsing indicates
+ * an incomplete command. */
+{
+ register CONST char *p = src;
+ while (numBytes) {
+ char type;
+ int scanned;
+ do {
+ scanned = TclParseWhiteSpace(p, numBytes, parsePtr, &type);
+ p += scanned; numBytes -= scanned;
+ } while (numBytes && (*p == '\n') && (p++,numBytes--));
+ if ((numBytes == 0) || (*p != '#')) {
+ break;
+ }
+ if (parsePtr->commentStart == NULL) {
+ parsePtr->commentStart = p;
+ }
+ while (numBytes) {
+ if (*p == '\\') {
+ scanned = TclParseWhiteSpace(p, numBytes, parsePtr, &type);
+ if (scanned) {
+ p += scanned; numBytes -= scanned;
+ } else {
+ /*
+ * General backslash substitution in comments isn't
+ * part of the formal spec, but test parse-15.47
+ * and history indicate that it has been the de facto
+ * rule. Don't change it now.
+ */
+ TclParseBackslash(p, numBytes, &scanned, NULL);
+ p += scanned; numBytes -= scanned;
+ }
+ } else {
+ p++; numBytes--;
+ if (p[-1] == '\n') {
+ break;
+ }
+ }
+ }
+ parsePtr->commentSize = p - parsePtr->commentStart;
+ }
+ return (p - src);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* ParseTokens --
*
* This procedure forms the heart of the Tcl parser. It parses one
* or more tokens from a string, up to a termination point
* specified by the caller. This procedure is used to parse
* unquoted command words (those not in quotes or braces), words in
- * quotes, and array indices for variables.
+ * quotes, and array indices for variables. No more than numBytes
+ * bytes will be scanned.
*
* Results:
* Tokens are added to parsePtr and parsePtr->term is filled in
@@ -527,8 +783,9 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
*/
static int
-ParseTokens(src, mask, parsePtr)
- register char *src; /* First character to parse. */
+ParseTokens(src, numBytes, mask, parsePtr)
+ register CONST char *src; /* First character to parse. */
+ register int numBytes; /* Max number of bytes to scan. */
int mask; /* Specifies when to stop parsing. The
* parse stops at the first unquoted
* character whose CHAR_TYPE contains
@@ -537,8 +794,8 @@ ParseTokens(src, mask, parsePtr)
* Updated with additional tokens and
* termination information. */
{
- int type, originalTokens, varToken;
- char utfBytes[TCL_UTF_MAX];
+ char type;
+ int originalTokens, varToken;
Tcl_Token *tokenPtr;
Tcl_Parse nested;
@@ -550,7 +807,7 @@ ParseTokens(src, mask, parsePtr)
*/
originalTokens = parsePtr->numTokens;
- while (1) {
+ while (numBytes && !((type = CHAR_TYPE(*src)) & mask)) {
if (parsePtr->numTokens == parsePtr->tokensAvailable) {
TclExpandTokenArray(parsePtr);
}
@@ -558,22 +815,15 @@ ParseTokens(src, mask, parsePtr)
tokenPtr->start = src;
tokenPtr->numComponents = 0;
- type = CHAR_TYPE(*src);
- if (type & mask) {
- break;
- }
-
if ((type & TYPE_SUBS) == 0) {
/*
* This is a simple range of characters. Scan to find the end
* of the range.
*/
- while (1) {
- src++;
- if (CHAR_TYPE(*src) & (mask | TYPE_SUBS)) {
- break;
- }
+ while ((++src, --numBytes)
+ && !(CHAR_TYPE(*src) & (mask | TYPE_SUBS))) {
+ /* empty loop */
}
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->size = src - tokenPtr->start;
@@ -585,11 +835,12 @@ ParseTokens(src, mask, parsePtr)
*/
varToken = parsePtr->numTokens;
- if (Tcl_ParseVarName(parsePtr->interp, src, parsePtr->end - src,
+ if (Tcl_ParseVarName(parsePtr->interp, src, numBytes,
parsePtr, 1) != TCL_OK) {
return TCL_ERROR;
}
src += parsePtr->tokenPtr[varToken].size;
+ numBytes -= parsePtr->tokenPtr[varToken].size;
} else if (*src == '[') {
/*
* Command substitution. Call Tcl_ParseCommand recursively
@@ -597,23 +848,24 @@ ParseTokens(src, mask, parsePtr)
* throw away the parse information.
*/
- src++;
+ src++; numBytes--;
while (1) {
if (Tcl_ParseCommand(parsePtr->interp, src,
- parsePtr->end - src, 1, &nested) != TCL_OK) {
+ numBytes, 1, &nested) != TCL_OK) {
parsePtr->errorType = nested.errorType;
parsePtr->term = nested.term;
parsePtr->incomplete = nested.incomplete;
return TCL_ERROR;
}
src = nested.commandStart + nested.commandSize;
+ numBytes = parsePtr->end - src;
if (nested.tokenPtr != nested.staticTokens) {
ckfree((char *) nested.tokenPtr);
}
if ((*nested.term == ']') && !nested.incomplete) {
break;
}
- if (src == parsePtr->end) {
+ if (numBytes == 0) {
if (parsePtr->interp != NULL) {
Tcl_SetResult(parsePtr->interp,
"missing close-bracket", TCL_STATIC);
@@ -631,9 +883,18 @@ ParseTokens(src, mask, parsePtr)
/*
* Backslash substitution.
*/
+ TclParseBackslash(src, numBytes, &tokenPtr->size, NULL);
+
+ if (tokenPtr->size == 1) {
+ /* Just a backslash, due to end of string */
+ tokenPtr->type = TCL_TOKEN_TEXT;
+ parsePtr->numTokens++;
+ src++; numBytes--;
+ continue;
+ }
if (src[1] == '\n') {
- if ((src + 2) == parsePtr->end) {
+ if (numBytes == 2) {
parsePtr->incomplete = 1;
}
@@ -644,28 +905,22 @@ ParseTokens(src, mask, parsePtr)
*/
if (mask & TYPE_SPACE) {
+ if (parsePtr->numTokens == originalTokens) {
+ goto finishToken;
+ }
break;
}
}
+
tokenPtr->type = TCL_TOKEN_BS;
- Tcl_UtfBackslash(src, &tokenPtr->size, utfBytes);
parsePtr->numTokens++;
src += tokenPtr->size;
+ numBytes -= tokenPtr->size;
} else if (*src == 0) {
- /*
- * We encountered a null character. If it is the null
- * character at the end of the string, then return.
- * Otherwise generate a text token for the single
- * character.
- */
-
- if (src == parsePtr->end) {
- break;
- }
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->size = 1;
parsePtr->numTokens++;
- src++;
+ src++; numBytes--;
} else {
panic("ParseTokens encountered unknown character");
}
@@ -676,7 +931,14 @@ ParseTokens(src, mask, parsePtr)
* for the empty range, so that there is always at least one
* token added.
*/
+ if (parsePtr->numTokens == parsePtr->tokensAvailable) {
+ TclExpandTokenArray(parsePtr);
+ }
+ tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
+ tokenPtr->start = src;
+ tokenPtr->numComponents = 0;
+ finishToken:
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->size = 0;
parsePtr->numTokens++;
@@ -684,7 +946,7 @@ ParseTokens(src, mask, parsePtr)
parsePtr->term = src;
return TCL_OK;
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -713,7 +975,7 @@ Tcl_FreeParse(parsePtr)
parsePtr->tokenPtr = parsePtr->staticTokens;
}
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -751,819 +1013,15 @@ TclExpandTokenArray(parsePtr)
parsePtr->tokenPtr = newPtr;
parsePtr->tokensAvailable = newCount;
}
-
-/*
- *----------------------------------------------------------------------
- *
- * EvalObjv --
- *
- * This procedure evaluates a Tcl command that has already been
- * parsed into words, with one Tcl_Obj holding each word.
- *
- * Results:
- * The return value is a standard Tcl completion code such as
- * TCL_OK or TCL_ERROR. A result or error message is left in
- * interp's result. If an error occurs, this procedure does
- * NOT add any information to the errorInfo variable.
- *
- * Side effects:
- * Depends on the command.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-EvalObjv(interp, objc, objv, command, length, flags)
- Tcl_Interp *interp; /* Interpreter in which to evaluate the
- * command. Also used for error
- * reporting. */
- int objc; /* Number of words in command. */
- Tcl_Obj *CONST objv[]; /* An array of pointers to objects that are
- * the words that make up the command. */
- char *command; /* Points to the beginning of the string
- * representation of the command; this
- * is used for traces. If the string
- * representation of the command is
- * unknown, an empty string should be
- * supplied. */
- int length; /* Number of bytes in command; if -1, all
- * characters up to the first null byte are
- * used. */
- int flags; /* Collection of OR-ed bits that control
- * the evaluation of the script. Only
- * TCL_EVAL_GLOBAL is currently
- * supported. */
-
-{
- Command *cmdPtr;
- Interp *iPtr = (Interp *) interp;
- Tcl_Obj **newObjv;
- int i, code;
- Trace *tracePtr, *nextPtr;
- char **argv, *commandCopy;
- CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr
- * in case TCL_EVAL_GLOBAL was set. */
- Tcl_ResetResult(interp);
- if (objc == 0) {
- return TCL_OK;
- }
-
- /*
- * If the interpreter was deleted, return an error.
- */
-
- if (iPtr->flags & DELETED) {
- Tcl_AppendToObj(Tcl_GetObjResult(interp),
- "attempt to call eval in deleted interpreter", -1);
- Tcl_SetErrorCode(interp, "CORE", "IDELETE",
- "attempt to call eval in deleted interpreter",
- (char *) NULL);
- return TCL_ERROR;
- }
-
- /*
- * Check depth of nested calls to Tcl_Eval: if this gets too large,
- * it's probably because of an infinite loop somewhere.
- */
-
- if (iPtr->numLevels >= iPtr->maxNestingDepth) {
- iPtr->result = "too many nested calls to Tcl_Eval (infinite loop?)";
- return TCL_ERROR;
- }
- iPtr->numLevels++;
-
- /*
- * On the Mac, we will never reach the default recursion limit before
- * blowing the stack. So we need to do a check here.
- */
-
- if (TclpCheckStackSpace() == 0) {
- /*NOTREACHED*/
- iPtr->numLevels--;
- iPtr->result = "too many nested calls to Tcl_Eval (infinite loop?)";
- return TCL_ERROR;
- }
-
- /*
- * Find the procedure to execute this command. If there isn't one,
- * then see if there is a command "unknown". If so, create a new
- * word array with "unknown" as the first word and the original
- * command words as arguments. Then call ourselves recursively
- * to execute it.
- */
-
- cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]);
- if (cmdPtr == NULL) {
- newObjv = (Tcl_Obj **) ckalloc((unsigned)
- ((objc + 1) * sizeof (Tcl_Obj *)));
- for (i = objc-1; i >= 0; i--) {
- newObjv[i+1] = objv[i];
- }
- newObjv[0] = Tcl_NewStringObj("unknown", -1);
- Tcl_IncrRefCount(newObjv[0]);
- cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, newObjv[0]);
- if (cmdPtr == NULL) {
- Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "invalid command name \"", Tcl_GetString(objv[0]), "\"",
- (char *) NULL);
- code = TCL_ERROR;
- } else {
- code = EvalObjv(interp, objc+1, newObjv, command, length, 0);
- }
- Tcl_DecrRefCount(newObjv[0]);
- ckfree((char *) newObjv);
- goto done;
- }
-
- /*
- * Call trace procedures if needed.
- */
-
- argv = NULL;
- commandCopy = command;
-
- for (tracePtr = iPtr->tracePtr; tracePtr != NULL; tracePtr = nextPtr) {
- nextPtr = tracePtr->nextPtr;
- if (iPtr->numLevels > tracePtr->level) {
- continue;
- }
-
- /*
- * This is a bit messy because we have to emulate the old trace
- * interface, which uses strings for everything.
- */
-
- if (argv == NULL) {
- argv = (char **) ckalloc((unsigned) (objc + 1) * sizeof(char *));
- for (i = 0; i < objc; i++) {
- argv[i] = Tcl_GetString(objv[i]);
- }
- argv[objc] = 0;
-
- if (length < 0) {
- length = strlen(command);
- } else if ((size_t)length < strlen(command)) {
- commandCopy = (char *) ckalloc((unsigned) (length + 1));
- strncpy(commandCopy, command, (size_t) length);
- commandCopy[length] = 0;
- }
- }
- (*tracePtr->proc)(tracePtr->clientData, interp, iPtr->numLevels,
- commandCopy, cmdPtr->proc, cmdPtr->clientData,
- objc, argv);
- }
- if (argv != NULL) {
- ckfree((char *) argv);
- }
- if (commandCopy != command) {
- ckfree((char *) commandCopy);
- }
-
- /*
- * Finally, invoke the command's Tcl_ObjCmdProc.
- */
-
- iPtr->cmdCount++;
- savedVarFramePtr = iPtr->varFramePtr;
- if (flags & TCL_EVAL_GLOBAL) {
- iPtr->varFramePtr = NULL;
- }
- code = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, objc, objv);
- iPtr->varFramePtr = savedVarFramePtr;
- if (Tcl_AsyncReady()) {
- code = Tcl_AsyncInvoke(interp, code);
- }
-
- /*
- * If the interpreter has a non-empty string result, the result
- * object is either empty or stale because some procedure set
- * interp->result directly. If so, move the string result to the
- * result object, then reset the string result.
- */
-
- if (*(iPtr->result) != 0) {
- (void) Tcl_GetObjResult(interp);
- }
-
- done:
- iPtr->numLevels--;
- return code;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_EvalObjv --
- *
- * This procedure evaluates a Tcl command that has already been
- * parsed into words, with one Tcl_Obj holding each word.
- *
- * Results:
- * The return value is a standard Tcl completion code such as
- * TCL_OK or TCL_ERROR. A result or error message is left in
- * interp's result.
- *
- * Side effects:
- * Depends on the command.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_EvalObjv(interp, objc, objv, flags)
- Tcl_Interp *interp; /* Interpreter in which to evaluate the
- * command. Also used for error
- * reporting. */
- int objc; /* Number of words in command. */
- Tcl_Obj *CONST objv[]; /* An array of pointers to objects that are
- * the words that make up the command. */
- int flags; /* Collection of OR-ed bits that control
- * the evaluation of the script. Only
- * TCL_EVAL_GLOBAL is currently
- * supported. */
-{
- Interp *iPtr = (Interp *)interp;
- Trace *tracePtr;
- Tcl_DString cmdBuf;
- char *cmdString = "";
- int cmdLen = 0;
- int code = TCL_OK;
-
- for (tracePtr = iPtr->tracePtr; tracePtr; tracePtr = tracePtr->nextPtr) {
- /*
- * EvalObjv will increment numLevels so use "<" rather than "<="
- */
- if (iPtr->numLevels < tracePtr->level) {
- int i;
- /*
- * The command will be needed for an execution trace or stack trace
- * generate a command string.
- */
- cmdtraced:
- Tcl_DStringInit(&cmdBuf);
- for (i = 0; i < objc; i++) {
- Tcl_DStringAppendElement(&cmdBuf, Tcl_GetString(objv[i]));
- }
- cmdString = Tcl_DStringValue(&cmdBuf);
- cmdLen = Tcl_DStringLength(&cmdBuf);
- break;
- }
- }
-
- /*
- * Execute the command if we have not done so already
- */
- switch (code) {
- case TCL_OK:
- code = EvalObjv(interp, objc, objv, cmdString, cmdLen, flags);
- if (code == TCL_ERROR && cmdLen == 0)
- goto cmdtraced;
- break;
- case TCL_ERROR:
- Tcl_LogCommandInfo(interp, cmdString, cmdString, cmdLen);
- break;
- default:
- /*NOTREACHED*/
- break;
- }
-
- if (cmdLen != 0) {
- Tcl_DStringFree(&cmdBuf);
- }
- return code;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_LogCommandInfo --
- *
- * This procedure is invoked after an error occurs in an interpreter.
- * It adds information to the "errorInfo" variable to describe the
- * command that was being executed when the error occurred.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Information about the command is added to errorInfo and the
- * line number stored internally in the interpreter is set. If this
- * is the first call to this procedure or Tcl_AddObjErrorInfo since
- * an error occurred, then old information in errorInfo is
- * deleted.
- *
- *----------------------------------------------------------------------
- */
-
-void
-Tcl_LogCommandInfo(interp, script, command, length)
- Tcl_Interp *interp; /* Interpreter in which to log information. */
- char *script; /* First character in script containing
- * command (must be <= command). */
- char *command; /* First character in command that
- * generated the error. */
- int length; /* Number of bytes in command (-1 means
- * use all bytes up to first null byte). */
-{
- char buffer[200];
- register char *p;
- char *ellipsis = "";
- Interp *iPtr = (Interp *) interp;
-
- if (iPtr->flags & ERR_ALREADY_LOGGED) {
- /*
- * Someone else has already logged error information for this
- * command; we shouldn't add anything more.
- */
-
- return;
- }
-
- /*
- * Compute the line number where the error occurred.
- */
-
- iPtr->errorLine = 1;
- for (p = script; p != command; p++) {
- if (*p == '\n') {
- iPtr->errorLine++;
- }
- }
-
- /*
- * Create an error message to add to errorInfo, including up to a
- * maximum number of characters of the command.
- */
-
- if (length < 0) {
- length = strlen(command);
- }
- if (length > 150) {
- length = 150;
- ellipsis = "...";
- }
- if (!(iPtr->flags & ERR_IN_PROGRESS)) {
- sprintf(buffer, "\n while executing\n\"%.*s%s\"",
- length, command, ellipsis);
- } else {
- sprintf(buffer, "\n invoked from within\n\"%.*s%s\"",
- length, command, ellipsis);
- }
- Tcl_AddObjErrorInfo(interp, buffer, -1);
- iPtr->flags &= ~ERR_ALREADY_LOGGED;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_EvalTokens --
- *
- * Given an array of tokens parsed from a Tcl command (e.g., the
- * tokens that make up a word or the index for an array variable)
- * this procedure evaluates the tokens and concatenates their
- * values to form a single result value.
- *
- * Results:
- * The return value is a pointer to a newly allocated Tcl_Obj
- * containing the value of the array of tokens. The reference
- * count of the returned object has been incremented. If an error
- * occurs in evaluating the tokens then a NULL value is returned
- * and an error message is left in interp's result.
- *
- * Side effects:
- * A new object is allocated to hold the result.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Obj *
-Tcl_EvalTokens(interp, tokenPtr, count)
- Tcl_Interp *interp; /* Interpreter in which to lookup
- * variables, execute nested commands,
- * and report errors. */
- Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens
- * to evaluate and concatenate. */
- int count; /* Number of tokens to consider at tokenPtr.
- * Must be at least 1. */
-{
- Tcl_Obj *resultPtr, *indexPtr, *valuePtr, *newPtr;
- char buffer[TCL_UTF_MAX];
-#ifdef TCL_MEM_DEBUG
-# define MAX_VAR_CHARS 5
-#else
-# define MAX_VAR_CHARS 30
-#endif
- char nameBuffer[MAX_VAR_CHARS+1];
- char *varName, *index;
- char *p = NULL; /* Initialized to avoid compiler warning. */
- int length, code;
-
- /*
- * The only tricky thing about this procedure is that it attempts to
- * avoid object creation and string copying whenever possible. For
- * example, if the value is just a nested command, then use the
- * command's result object directly.
- */
-
- resultPtr = NULL;
- for ( ; count > 0; count--, tokenPtr++) {
- valuePtr = NULL;
-
- /*
- * The switch statement below computes the next value to be
- * concat to the result, as either a range of text or an
- * object.
- */
-
- switch (tokenPtr->type) {
- case TCL_TOKEN_TEXT:
- p = tokenPtr->start;
- length = tokenPtr->size;
- break;
-
- case TCL_TOKEN_BS:
- length = Tcl_UtfBackslash(tokenPtr->start, (int *) NULL,
- buffer);
- p = buffer;
- break;
-
- case TCL_TOKEN_COMMAND:
- code = Tcl_EvalEx(interp, tokenPtr->start+1, tokenPtr->size-2,
- 0);
- if (code != TCL_OK) {
- goto error;
- }
- valuePtr = Tcl_GetObjResult(interp);
- break;
-
- case TCL_TOKEN_VARIABLE:
- if (tokenPtr->numComponents == 1) {
- indexPtr = NULL;
- } else {
- indexPtr = Tcl_EvalTokens(interp, tokenPtr+2,
- tokenPtr->numComponents - 1);
- if (indexPtr == NULL) {
- goto error;
- }
- }
-
- /*
- * We have to make a copy of the variable name in order
- * to have a null-terminated string. We can't make a
- * temporary modification to the script to null-terminate
- * the name, because a trace callback might potentially
- * reuse the script and be affected by the null character.
- */
-
- if (tokenPtr[1].size <= MAX_VAR_CHARS) {
- varName = nameBuffer;
- } else {
- varName = ckalloc((unsigned) (tokenPtr[1].size + 1));
- }
- strncpy(varName, tokenPtr[1].start, (size_t) tokenPtr[1].size);
- varName[tokenPtr[1].size] = 0;
- if (indexPtr != NULL) {
- index = TclGetString(indexPtr);
- } else {
- index = NULL;
- }
- valuePtr = Tcl_GetVar2Ex(interp, varName, index,
- TCL_LEAVE_ERR_MSG);
- if (varName != nameBuffer) {
- ckfree(varName);
- }
- if (indexPtr != NULL) {
- Tcl_DecrRefCount(indexPtr);
- }
- if (valuePtr == NULL) {
- goto error;
- }
- count -= tokenPtr->numComponents;
- tokenPtr += tokenPtr->numComponents;
- break;
-
- default:
- panic("unexpected token type in Tcl_EvalTokens");
- }
-
- /*
- * If valuePtr isn't NULL, the next piece of text comes from that
- * object; otherwise, take length bytes starting at p.
- */
-
- if (resultPtr == NULL) {
- if (valuePtr != NULL) {
- resultPtr = valuePtr;
- } else {
- resultPtr = Tcl_NewStringObj(p, length);
- }
- Tcl_IncrRefCount(resultPtr);
- } else {
- if (Tcl_IsShared(resultPtr)) {
- newPtr = Tcl_DuplicateObj(resultPtr);
- Tcl_DecrRefCount(resultPtr);
- resultPtr = newPtr;
- Tcl_IncrRefCount(resultPtr);
- }
- if (valuePtr != NULL) {
- p = Tcl_GetStringFromObj(valuePtr, &length);
- }
- Tcl_AppendToObj(resultPtr, p, length);
- }
- }
- return resultPtr;
-
- error:
- if (resultPtr != NULL) {
- Tcl_DecrRefCount(resultPtr);
- }
- return NULL;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_EvalEx --
- *
- * This procedure evaluates a Tcl script without using the compiler
- * or byte-code interpreter. It just parses the script, creates
- * values for each word of each command, then calls EvalObjv
- * to execute each command.
- *
- * Results:
- * The return value is a standard Tcl completion code such as
- * TCL_OK or TCL_ERROR. A result or error message is left in
- * interp's result.
- *
- * Side effects:
- * Depends on the script.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_EvalEx(interp, script, numBytes, flags)
- Tcl_Interp *interp; /* Interpreter in which to evaluate the
- * script. Also used for error reporting. */
- char *script; /* First character of script to evaluate. */
- int numBytes; /* Number of bytes in script. If < 0, the
- * script consists of all bytes up to the
- * first null character. */
- int flags; /* Collection of OR-ed bits that control
- * the evaluation of the script. Only
- * TCL_EVAL_GLOBAL is currently
- * supported. */
-{
- Interp *iPtr = (Interp *) interp;
- char *p, *next;
- Tcl_Parse parse;
-#define NUM_STATIC_OBJS 20
- Tcl_Obj *staticObjArray[NUM_STATIC_OBJS], **objv;
- Tcl_Token *tokenPtr;
- int i, code, commandLength, bytesLeft, nested;
- CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr
- * in case TCL_EVAL_GLOBAL was set. */
-
- /*
- * The variables below keep track of how much state has been
- * allocated while evaluating the script, so that it can be freed
- * properly if an error occurs.
- */
-
- int gotParse = 0, objectsUsed = 0;
-
- if (numBytes < 0) {
- numBytes = strlen(script);
- }
- Tcl_ResetResult(interp);
-
- savedVarFramePtr = iPtr->varFramePtr;
- if (flags & TCL_EVAL_GLOBAL) {
- iPtr->varFramePtr = NULL;
- }
-
- /*
- * Each iteration through the following loop parses the next
- * command from the script and then executes it.
- */
-
- objv = staticObjArray;
- p = script;
- bytesLeft = numBytes;
- if (iPtr->evalFlags & TCL_BRACKET_TERM) {
- nested = 1;
- } else {
- nested = 0;
- }
- iPtr->evalFlags = 0;
- do {
- if (Tcl_ParseCommand(interp, p, bytesLeft, nested, &parse)
- != TCL_OK) {
- code = TCL_ERROR;
- goto error;
- }
- gotParse = 1;
- if (parse.numWords > 0) {
- /*
- * Generate an array of objects for the words of the command.
- */
-
- if (parse.numWords <= NUM_STATIC_OBJS) {
- objv = staticObjArray;
- } else {
- objv = (Tcl_Obj **) ckalloc((unsigned)
- (parse.numWords * sizeof (Tcl_Obj *)));
- }
- for (objectsUsed = 0, tokenPtr = parse.tokenPtr;
- objectsUsed < parse.numWords;
- objectsUsed++, tokenPtr += (tokenPtr->numComponents + 1)) {
- objv[objectsUsed] = Tcl_EvalTokens(interp, tokenPtr+1,
- tokenPtr->numComponents);
- if (objv[objectsUsed] == NULL) {
- code = TCL_ERROR;
- goto error;
- }
- }
-
- /*
- * Execute the command and free the objects for its words.
- */
-
- code = EvalObjv(interp, objectsUsed, objv, p, bytesLeft, 0);
- if (code != TCL_OK) {
- goto error;
- }
- for (i = 0; i < objectsUsed; i++) {
- Tcl_DecrRefCount(objv[i]);
- }
- objectsUsed = 0;
- if (objv != staticObjArray) {
- ckfree((char *) objv);
- objv = staticObjArray;
- }
- }
-
- /*
- * Advance to the next command in the script.
- */
-
- next = parse.commandStart + parse.commandSize;
- bytesLeft -= next - p;
- p = next;
- Tcl_FreeParse(&parse);
- gotParse = 0;
- if ((nested != 0) && (p > script) && (p[-1] == ']')) {
- /*
- * We get here in the special case where the TCL_BRACKET_TERM
- * flag was set in the interpreter and we reached a close
- * bracket in the script. Return immediately.
- */
-
- iPtr->termOffset = (p - 1) - script;
- iPtr->varFramePtr = savedVarFramePtr;
- return TCL_OK;
- }
- } while (bytesLeft > 0);
- iPtr->termOffset = p - script;
- iPtr->varFramePtr = savedVarFramePtr;
- return TCL_OK;
-
- error:
- /*
- * Generate various pieces of error information, such as the line
- * number where the error occurred and information to add to the
- * errorInfo variable. Then free resources that had been allocated
- * to the command.
- */
-
- if ((code == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
- commandLength = parse.commandSize;
- if ((parse.commandStart + commandLength) != (script + numBytes)) {
- /*
- * The command where the error occurred didn't end at the end
- * of the script (i.e. it ended at a terminator character such
- * as ";". Reduce the length by one so that the error message
- * doesn't include the terminator character.
- */
-
- commandLength -= 1;
- }
- Tcl_LogCommandInfo(interp, script, parse.commandStart, commandLength);
- }
-
- for (i = 0; i < objectsUsed; i++) {
- Tcl_DecrRefCount(objv[i]);
- }
- if (gotParse) {
- p = parse.commandStart + parse.commandSize;
- Tcl_FreeParse(&parse);
- if ((nested != 0) && (p > script) && (p[-1] == ']')) {
- /*
- * We get here in the special case where the TCL_BRACKET_TERM
- * flag was set in the interpreter and we reached a close
- * bracket in the script. Return immediately.
- */
-
- iPtr->termOffset = (p - 1) - script;
- } else {
- iPtr->termOffset = p - script;
- }
- }
- if (objv != staticObjArray) {
- ckfree((char *) objv);
- }
- iPtr->varFramePtr = savedVarFramePtr;
- return code;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_Eval --
- *
- * Execute a Tcl command in a string. This procedure executes the
- * script directly, rather than compiling it to bytecodes. Before
- * the arrival of the bytecode compiler in Tcl 8.0 Tcl_Eval was
- * the main procedure used for executing Tcl commands, but nowadays
- * it isn't used much.
- *
- * Results:
- * The return value is one of the return codes defined in tcl.h
- * (such as TCL_OK), and interp's result contains a value
- * to supplement the return code. The value of the result
- * will persist only until the next call to Tcl_Eval or Tcl_EvalObj:
- * you must copy it or lose it!
- *
- * Side effects:
- * Can be almost arbitrary, depending on the commands in the script.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_Eval(interp, string)
- Tcl_Interp *interp; /* Token for command interpreter (returned
- * by previous call to Tcl_CreateInterp). */
- char *string; /* Pointer to TCL command to execute. */
-{
- int code;
-
- code = Tcl_EvalEx(interp, string, -1, 0);
-
- /*
- * For backwards compatibility with old C code that predates the
- * object system in Tcl 8.0, we have to mirror the object result
- * back into the string result (some callers may expect it there).
- */
-
- Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)),
- TCL_VOLATILE);
- return code;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_EvalObj, Tcl_GlobalEvalObj --
- *
- * These functions are deprecated but we keep them around for backwards
- * compatibility reasons.
- *
- * Results:
- * See the functions they call.
- *
- * Side effects:
- * See the functions they call.
- *
- *----------------------------------------------------------------------
- */
-
-#undef Tcl_EvalObj
-int
-Tcl_EvalObj(interp, objPtr)
- Tcl_Interp * interp;
- Tcl_Obj * objPtr;
-{
- return Tcl_EvalObjEx(interp, objPtr, 0);
-}
-
-#undef Tcl_GlobalEvalObj
-int
-Tcl_GlobalEvalObj(interp, objPtr)
- Tcl_Interp * interp;
- Tcl_Obj * objPtr;
-{
- return Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_GLOBAL);
-}
-
/*
*----------------------------------------------------------------------
*
* Tcl_ParseVarName --
*
* Given a string starting with a $ sign, parse off a variable
- * name and return information about the parse.
+ * name and return information about the parse. No more than
+ * numBytes bytes will be scanned.
*
* Results:
* The return value is TCL_OK if the command was parsed
@@ -1590,9 +1048,9 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
Tcl_Interp *interp; /* Interpreter to use for error reporting;
* if NULL, then no error message is
* provided. */
- char *string; /* String containing variable name. First
+ CONST char *string; /* String containing variable name. First
* character must be "$". */
- int numBytes; /* Total number of bytes in string. If < 0,
+ register int numBytes; /* Total number of bytes in string. If < 0,
* the string consists of all bytes up to the
* first null character. */
Tcl_Parse *parsePtr; /* Structure to fill in with information
@@ -1603,16 +1061,17 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
* it. */
{
Tcl_Token *tokenPtr;
- char *end, *src;
+ register CONST char *src;
unsigned char c;
int varIndex, offset;
Tcl_UniChar ch;
unsigned array;
- if (numBytes >= 0) {
- end = string + numBytes;
- } else {
- end = string + strlen(string);
+ if ((numBytes == 0) || (string == NULL)) {
+ return TCL_ERROR;
+ }
+ if (numBytes < 0) {
+ numBytes = strlen(string);
}
if (!append) {
@@ -1621,7 +1080,7 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
parsePtr->numTokens = 0;
parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
parsePtr->string = string;
- parsePtr->end = end;
+ parsePtr->end = (string + numBytes);
parsePtr->interp = interp;
parsePtr->errorType = TCL_PARSE_SUCCESS;
parsePtr->incomplete = 0;
@@ -1643,8 +1102,8 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
varIndex = parsePtr->numTokens;
parsePtr->numTokens++;
tokenPtr++;
- src++;
- if (src >= end) {
+ src++; numBytes--;
+ if (numBytes == 0) {
goto justADollarSign;
}
tokenPtr->type = TCL_TOKEN_TEXT;
@@ -1669,26 +1128,23 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
*/
if (*src == '{') {
- src++;
+ src++; numBytes--;
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->start = src;
tokenPtr->numComponents = 0;
- while (1) {
- if (src == end) {
- if (interp != NULL) {
- Tcl_SetResult(interp,
- "missing close-brace for variable name",
+
+ while (numBytes && (*src != '}')) {
+ numBytes--; src++;
+ }
+ if (numBytes == 0) {
+ if (interp != NULL) {
+ Tcl_SetResult(interp, "missing close-brace for variable name",
TCL_STATIC);
- }
- parsePtr->errorType = TCL_PARSE_MISSING_VAR_BRACE;
- parsePtr->term = tokenPtr->start-1;
- parsePtr->incomplete = 1;
- goto error;
}
- if (*src == '}') {
- break;
- }
- src++;
+ parsePtr->errorType = TCL_PARSE_MISSING_VAR_BRACE;
+ parsePtr->term = tokenPtr->start-1;
+ parsePtr->incomplete = 1;
+ goto error;
}
tokenPtr->size = src - tokenPtr->start;
tokenPtr[-1].size = src - tokenPtr[-1].start;
@@ -1698,17 +1154,24 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->start = src;
tokenPtr->numComponents = 0;
- while (src != end) {
- offset = Tcl_UtfToUniChar(src, &ch);
+ while (numBytes) {
+ if (Tcl_UtfCharComplete(src, numBytes)) {
+ offset = Tcl_UtfToUniChar(src, &ch);
+ } else {
+ char utfBytes[TCL_UTF_MAX];
+ memcpy(utfBytes, src, (size_t) numBytes);
+ utfBytes[numBytes] = '\0';
+ offset = Tcl_UtfToUniChar(utfBytes, &ch);
+ }
c = UCHAR(ch);
if (isalnum(c) || (c == '_')) { /* INTL: ISO only, UCHAR. */
- src += offset;
+ src += offset; numBytes -= offset;
continue;
}
- if ((c == ':') && (((src+1) != end) && (src[1] == ':'))) {
- src += 2;
- while ((src != end) && (*src == ':')) {
- src += 1;
+ if ((c == ':') && (numBytes != 1) && (src[1] == ':')) {
+ src += 2; numBytes -= 2;
+ while (numBytes && (*src == ':')) {
+ src++; numBytes--;
}
continue;
}
@@ -1718,9 +1181,9 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
/*
* Support for empty array names here.
*/
- array = ((src != end) && (*src == '('));
+ array = (numBytes && (*src == '('));
tokenPtr->size = src - tokenPtr->start;
- if (tokenPtr->size == 0 && !array) {
+ if ((tokenPtr->size == 0) && !array) {
goto justADollarSign;
}
parsePtr->numTokens++;
@@ -1731,11 +1194,12 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
* since it could contain any number of substitutions.
*/
- if (ParseTokens(src+1, TYPE_CLOSE_PAREN, parsePtr)
+ if (ParseTokens(src+1, numBytes-1, TYPE_CLOSE_PAREN, parsePtr)
!= TCL_OK) {
goto error;
}
- if ((parsePtr->term == end) || (*parsePtr->term != ')')) {
+ if ((parsePtr->term == (src + numBytes))
+ || (*parsePtr->term != ')')) {
if (parsePtr->interp != NULL) {
Tcl_SetResult(parsePtr->interp, "missing )",
TCL_STATIC);
@@ -1770,7 +1234,7 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
Tcl_FreeParse(parsePtr);
return TCL_ERROR;
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -1793,18 +1257,19 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_ParseVar(interp, string, termPtr)
Tcl_Interp *interp; /* Context for looking up variable. */
- register char *string; /* String containing variable name.
+ register CONST char *string; /* String containing variable name.
* First character must be "$". */
- char **termPtr; /* If non-NULL, points to word to fill
+ CONST char **termPtr; /* If non-NULL, points to word to fill
* in with character just after last
* one in the variable specifier. */
{
Tcl_Parse parse;
register Tcl_Obj *objPtr;
+ int code;
if (Tcl_ParseVarName(interp, string, -1, &parse, 0) != TCL_OK) {
return NULL;
@@ -1821,25 +1286,30 @@ Tcl_ParseVar(interp, string, termPtr)
return "$";
}
- objPtr = Tcl_EvalTokens(interp, parse.tokenPtr, parse.numTokens);
- if (objPtr == NULL) {
+ code = Tcl_EvalTokensStandard(interp, parse.tokenPtr, parse.numTokens);
+ if (code != TCL_OK) {
return NULL;
}
+ objPtr = Tcl_GetObjResult(interp);
/*
* At this point we should have an object containing the value of
* a variable. Just return the string from that object.
+ *
+ * This should have returned the object for the user to manage, but
+ * instead we have some weak reference to the string value in the
+ * object, which is why we make sure the object exists after resetting
+ * the result. This isn't ideal, but it's the best we can do with the
+ * current documented interface. -- hobbs
*/
-#ifdef TCL_COMPILE_DEBUG
- if (objPtr->refCount < 2) {
- panic("Tcl_ParseVar got temporary object from Tcl_EvalTokens");
+ if (!Tcl_IsShared(objPtr)) {
+ Tcl_IncrRefCount(objPtr);
}
-#endif /*TCL_COMPILE_DEBUG*/
- TclDecrRefCount(objPtr);
+ Tcl_ResetResult(interp);
return TclGetString(objPtr);
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -1847,7 +1317,8 @@ Tcl_ParseVar(interp, string, termPtr)
*
* Given a string in braces such as a Tcl command argument or a string
* value in a Tcl expression, this procedure parses the string and
- * returns information about the parse.
+ * returns information about the parse. No more than numBytes bytes
+ * will be scanned.
*
* Results:
* The return value is TCL_OK if the string was parsed successfully and
@@ -1873,9 +1344,9 @@ Tcl_ParseBraces(interp, string, numBytes, parsePtr, append, termPtr)
Tcl_Interp *interp; /* Interpreter to use for error reporting;
* if NULL, then no error message is
* provided. */
- char *string; /* String containing the string in braces.
+ CONST char *string; /* String containing the string in braces.
* The first character must be '{'. */
- int numBytes; /* Total number of bytes in string. If < 0,
+ register int numBytes; /* Total number of bytes in string. If < 0,
* the string consists of all bytes up to
* the first null character. */
register Tcl_Parse *parsePtr;
@@ -1885,35 +1356,35 @@ Tcl_ParseBraces(interp, string, numBytes, parsePtr, append, termPtr)
* information in parsePtr; zero means
* ignore existing tokens in parsePtr and
* reinitialize it. */
- char **termPtr; /* If non-NULL, points to word in which to
+ CONST char **termPtr; /* If non-NULL, points to word in which to
* store a pointer to the character just
* after the terminating '}' if the parse
* was successful. */
{
- char utfBytes[TCL_UTF_MAX]; /* For result of backslash substitution. */
Tcl_Token *tokenPtr;
- register char *src, *end;
+ register CONST char *src;
int startIndex, level, length;
- if ((numBytes >= 0) || (string == NULL)) {
- end = string + numBytes;
- } else {
- end = string + strlen(string);
+ if ((numBytes == 0) || (string == NULL)) {
+ return TCL_ERROR;
}
-
+ if (numBytes < 0) {
+ numBytes = strlen(string);
+ }
+
if (!append) {
parsePtr->numWords = 0;
parsePtr->tokenPtr = parsePtr->staticTokens;
parsePtr->numTokens = 0;
parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
parsePtr->string = string;
- parsePtr->end = end;
+ parsePtr->end = (string + numBytes);
parsePtr->interp = interp;
parsePtr->errorType = TCL_PARSE_SUCCESS;
}
- src = string+1;
+ src = string;
startIndex = parsePtr->numTokens;
if (parsePtr->numTokens == parsePtr->tokensAvailable) {
@@ -1921,130 +1392,135 @@ Tcl_ParseBraces(interp, string, numBytes, parsePtr, append, termPtr)
}
tokenPtr = &parsePtr->tokenPtr[startIndex];
tokenPtr->type = TCL_TOKEN_TEXT;
- tokenPtr->start = src;
+ tokenPtr->start = src+1;
tokenPtr->numComponents = 0;
level = 1;
while (1) {
- while (CHAR_TYPE(*src) == TYPE_NORMAL) {
- src++;
- }
- if (*src == '}') {
- level--;
- if (level == 0) {
+ while (++src, --numBytes) {
+ if (CHAR_TYPE(*src) != TYPE_NORMAL) {
break;
}
- src++;
- } else if (*src == '{') {
- level++;
- src++;
- } else if (*src == '\\') {
- Tcl_UtfBackslash(src, &length, utfBytes);
- if (src[1] == '\n') {
+ }
+ if (numBytes == 0) {
+ register int openBrace = 0;
+
+ parsePtr->errorType = TCL_PARSE_MISSING_BRACE;
+ parsePtr->term = string;
+ parsePtr->incomplete = 1;
+ if (interp == NULL) {
/*
- * A backslash-newline sequence must be collapsed, even
- * inside braces, so we have to split the word into
- * multiple tokens so that the backslash-newline can be
- * represented explicitly.
+ * Skip straight to the exit code since we have no
+ * interpreter to put error message in.
*/
-
- if ((src + 2) == end) {
- parsePtr->incomplete = 1;
- }
- tokenPtr->size = (src - tokenPtr->start);
- if (tokenPtr->size != 0) {
- parsePtr->numTokens++;
- }
- if ((parsePtr->numTokens+1) >= parsePtr->tokensAvailable) {
- TclExpandTokenArray(parsePtr);
- }
- tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
- tokenPtr->type = TCL_TOKEN_BS;
- tokenPtr->start = src;
- tokenPtr->size = length;
- tokenPtr->numComponents = 0;
- parsePtr->numTokens++;
-
- src += length;
- tokenPtr++;
- tokenPtr->type = TCL_TOKEN_TEXT;
- tokenPtr->start = src;
- tokenPtr->numComponents = 0;
- } else {
- src += length;
+ goto error;
}
- } else if (src == end) {
- int openBrace;
- if (interp != NULL) {
- Tcl_SetResult(interp, "missing close-brace", TCL_STATIC);
- }
+ Tcl_SetResult(interp, "missing close-brace", TCL_STATIC);
+
/*
- * Search the source string for a possible open
- * brace within the context of a comment. Since we
- * aren't performing a full Tcl parse, just look for
- * an open brace preceeded by a '<whitspace>#' on
- * the same line.
+ * Guess if the problem is due to comments by searching
+ * the source string for a possible open brace within the
+ * context of a comment. Since we aren't performing a
+ * full Tcl parse, just look for an open brace preceded
+ * by a '<whitespace>#' on the same line.
*/
- openBrace = 0;
- while (src > string ) {
+
+ for (; src > string; src--) {
switch (*src) {
- case '{':
- openBrace = 1;
+ case '{':
+ openBrace = 1;
break;
case '\n':
- openBrace = 0;
+ openBrace = 0;
break;
- case '#':
- if ((openBrace == 1) && (isspace(UCHAR(src[-1])))) {
- if (interp != NULL) {
- Tcl_AppendResult(interp,
- ": possible unbalanced brace in comment",
- (char *) NULL);
- }
- openBrace = -1;
- break;
+ case '#' :
+ if (openBrace && (isspace(UCHAR(src[-1])))) {
+ Tcl_AppendResult(interp,
+ ": possible unbalanced brace in comment",
+ (char *) NULL);
+ goto error;
}
break;
}
- if (openBrace == -1) {
- break;
- }
- src--;
}
- parsePtr->errorType = TCL_PARSE_MISSING_BRACE;
- parsePtr->term = string;
- parsePtr->incomplete = 1;
- goto error;
- } else {
- src++;
- }
- }
- /*
- * Decide if we need to finish emitting a partially-finished token.
- * There are 3 cases:
- * {abc \newline xyz} or {xyz} - finish emitting "xyz" token
- * {abc \newline} - don't emit token after \newline
- * {} - finish emitting zero-sized token
- * The last case ensures that there is a token (even if empty) that
- * describes the braced string.
- */
+ error:
+ Tcl_FreeParse(parsePtr);
+ return TCL_ERROR;
+ }
+ switch (*src) {
+ case '{':
+ level++;
+ break;
+ case '}':
+ if (--level == 0) {
+
+ /*
+ * Decide if we need to finish emitting a
+ * partially-finished token. There are 3 cases:
+ * {abc \newline xyz} or {xyz}
+ * - finish emitting "xyz" token
+ * {abc \newline}
+ * - don't emit token after \newline
+ * {} - finish emitting zero-sized token
+ *
+ * The last case ensures that there is a token
+ * (even if empty) that describes the braced string.
+ */
- if ((src != tokenPtr->start)
- || (parsePtr->numTokens == startIndex)) {
- tokenPtr->size = (src - tokenPtr->start);
- parsePtr->numTokens++;
- }
- if (termPtr != NULL) {
- *termPtr = src+1;
+ if ((src != tokenPtr->start)
+ || (parsePtr->numTokens == startIndex)) {
+ tokenPtr->size = (src - tokenPtr->start);
+ parsePtr->numTokens++;
+ }
+ if (termPtr != NULL) {
+ *termPtr = src+1;
+ }
+ return TCL_OK;
+ }
+ break;
+ case '\\':
+ TclParseBackslash(src, numBytes, &length, NULL);
+ if ((length > 1) && (src[1] == '\n')) {
+ /*
+ * A backslash-newline sequence must be collapsed, even
+ * inside braces, so we have to split the word into
+ * multiple tokens so that the backslash-newline can be
+ * represented explicitly.
+ */
+
+ if (numBytes == 2) {
+ parsePtr->incomplete = 1;
+ }
+ tokenPtr->size = (src - tokenPtr->start);
+ if (tokenPtr->size != 0) {
+ parsePtr->numTokens++;
+ }
+ if ((parsePtr->numTokens+1) >= parsePtr->tokensAvailable) {
+ TclExpandTokenArray(parsePtr);
+ }
+ tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
+ tokenPtr->type = TCL_TOKEN_BS;
+ tokenPtr->start = src;
+ tokenPtr->size = length;
+ tokenPtr->numComponents = 0;
+ parsePtr->numTokens++;
+
+ src += length - 1;
+ numBytes -= length - 1;
+ tokenPtr++;
+ tokenPtr->type = TCL_TOKEN_TEXT;
+ tokenPtr->start = src + 1;
+ tokenPtr->numComponents = 0;
+ } else {
+ src += length - 1;
+ numBytes -= length - 1;
+ }
+ break;
+ }
}
- return TCL_OK;
-
- error:
- Tcl_FreeParse(parsePtr);
- return TCL_ERROR;
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -2052,7 +1528,8 @@ Tcl_ParseBraces(interp, string, numBytes, parsePtr, append, termPtr)
*
* Given a double-quoted string such as a quoted Tcl command argument
* or a quoted value in a Tcl expression, this procedure parses the
- * string and returns information about the parse.
+ * string and returns information about the parse. No more than
+ * numBytes bytes will be scanned.
*
* Results:
* The return value is TCL_OK if the string was parsed successfully and
@@ -2078,9 +1555,9 @@ Tcl_ParseQuotedString(interp, string, numBytes, parsePtr, append, termPtr)
Tcl_Interp *interp; /* Interpreter to use for error reporting;
* if NULL, then no error message is
* provided. */
- char *string; /* String containing the quoted string.
+ CONST char *string; /* String containing the quoted string.
* The first character must be '"'. */
- int numBytes; /* Total number of bytes in string. If < 0,
+ register int numBytes; /* Total number of bytes in string. If < 0,
* the string consists of all bytes up to
* the first null character. */
register Tcl_Parse *parsePtr;
@@ -2090,31 +1567,30 @@ Tcl_ParseQuotedString(interp, string, numBytes, parsePtr, append, termPtr)
* information in parsePtr; zero means
* ignore existing tokens in parsePtr and
* reinitialize it. */
- char **termPtr; /* If non-NULL, points to word in which to
+ CONST char **termPtr; /* If non-NULL, points to word in which to
* store a pointer to the character just
* after the quoted string's terminating
* close-quote if the parse succeeds. */
{
- char *end;
-
- if ((numBytes >= 0) || (string == NULL)) {
- end = string + numBytes;
- } else {
- end = string + strlen(string);
+ if ((numBytes == 0) || (string == NULL)) {
+ return TCL_ERROR;
}
-
+ if (numBytes < 0) {
+ numBytes = strlen(string);
+ }
+
if (!append) {
parsePtr->numWords = 0;
parsePtr->tokenPtr = parsePtr->staticTokens;
parsePtr->numTokens = 0;
parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
parsePtr->string = string;
- parsePtr->end = end;
+ parsePtr->end = (string + numBytes);
parsePtr->interp = interp;
parsePtr->errorType = TCL_PARSE_SUCCESS;
}
- if (ParseTokens(string+1, TYPE_QUOTE, parsePtr) != TCL_OK) {
+ if (ParseTokens(string+1, numBytes-1, TYPE_QUOTE, parsePtr) != TCL_OK) {
goto error;
}
if (*parsePtr->term != '"') {
@@ -2135,7 +1611,7 @@ Tcl_ParseQuotedString(interp, string, numBytes, parsePtr, append, termPtr)
Tcl_FreeParse(parsePtr);
return TCL_ERROR;
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -2157,16 +1633,16 @@ Tcl_ParseQuotedString(interp, string, numBytes, parsePtr, append, termPtr)
*/
static int
-CommandComplete(script, length)
- char *script; /* Script to check. */
- int length; /* Number of bytes in script. */
+CommandComplete(script, numBytes)
+ CONST char *script; /* Script to check. */
+ int numBytes; /* Number of bytes in script. */
{
Tcl_Parse parse;
- char *p, *end;
+ CONST char *p, *end;
int result;
p = script;
- end = p + length;
+ end = p + numBytes;
while (Tcl_ParseCommand((Tcl_Interp *) NULL, p, end - p, 0, &parse)
== TCL_OK) {
p = parse.commandStart + parse.commandSize;
@@ -2183,7 +1659,7 @@ CommandComplete(script, length)
Tcl_FreeParse(&parse);
return result;
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -2206,11 +1682,11 @@ CommandComplete(script, length)
int
Tcl_CommandComplete(script)
- char *script; /* Script to check. */
+ CONST char *script; /* Script to check. */
{
return CommandComplete(script, (int) strlen(script));
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -2234,13 +1710,13 @@ TclObjCommandComplete(objPtr)
Tcl_Obj *objPtr; /* Points to object holding script
* to check. */
{
- char *script;
+ CONST char *script;
int length;
script = Tcl_GetStringFromObj(objPtr, &length);
return CommandComplete(script, length);
}
-
+
/*
*----------------------------------------------------------------------
*
diff --git a/tcl/generic/tclParseExpr.c b/tcl/generic/tclParseExpr.c
index 00612db7efa..cde02d2898b 100644
--- a/tcl/generic/tclParseExpr.c
+++ b/tcl/generic/tclParseExpr.c
@@ -7,6 +7,8 @@
* code analysis, etc.
*
* Copyright (c) 1997 Sun Microsystems, Inc.
+ * Copyright (c) 1998-2000 by Scriptics Corporation.
+ * Contributions from Don Porter, NIST, 2002. (not subject to US copyright)
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -15,7 +17,6 @@
*/
#include "tclInt.h"
-#include "tclCompile.h"
/*
* The stuff below is a bit of a hack so that this file can be used in
@@ -55,22 +56,24 @@ typedef struct ParseInfo {
int lexeme; /* Type of last lexeme scanned in expr.
* See below for definitions. Corresponds to
* size characters beginning at start. */
- char *start; /* First character in lexeme. */
+ CONST char *start; /* First character in lexeme. */
int size; /* Number of bytes in lexeme. */
- char *next; /* Position of the next character to be
+ CONST char *next; /* Position of the next character to be
* scanned in the expression string. */
- char *prevEnd; /* Points to the character just after the
+ CONST char *prevEnd; /* Points to the character just after the
* last one in the previous lexeme. Used to
* compute size of subexpression tokens. */
- char *originalExpr; /* Points to the start of the expression
+ CONST char *originalExpr; /* Points to the start of the expression
* originally passed to Tcl_ParseExpr. */
- char *lastChar; /* Points just after last byte of expr. */
+ CONST char *lastChar; /* Points just after last byte of expr. */
} ParseInfo;
/*
* Definitions of the different lexemes that appear in expressions. The
* order of these must match the corresponding entries in the
* operatorStrings array below.
+ *
+ * Basic lexemes:
*/
#define LITERAL 0
@@ -84,62 +87,69 @@ typedef struct ParseInfo {
#define COMMA 8
#define END 9
#define UNKNOWN 10
+#define UNKNOWN_CHAR 11
/*
- * Binary operators:
+ * Binary numeric operators:
*/
-#define MULT 11
-#define DIVIDE 12
-#define MOD 13
-#define PLUS 14
-#define MINUS 15
-#define LEFT_SHIFT 16
-#define RIGHT_SHIFT 17
-#define LESS 18
-#define GREATER 19
-#define LEQ 20
-#define GEQ 21
-#define EQUAL 22
-#define NEQ 23
-#define BIT_AND 24
-#define BIT_XOR 25
-#define BIT_OR 26
-#define AND 27
-#define OR 28
-#define QUESTY 29
-#define COLON 30
+#define MULT 12
+#define DIVIDE 13
+#define MOD 14
+#define PLUS 15
+#define MINUS 16
+#define LEFT_SHIFT 17
+#define RIGHT_SHIFT 18
+#define LESS 19
+#define GREATER 20
+#define LEQ 21
+#define GEQ 22
+#define EQUAL 23
+#define NEQ 24
+#define BIT_AND 25
+#define BIT_XOR 26
+#define BIT_OR 27
+#define AND 28
+#define OR 29
+#define QUESTY 30
+#define COLON 31
/*
* Unary operators. Unary minus and plus are represented by the (binary)
* lexemes MINUS and PLUS.
*/
-#define NOT 31
-#define BIT_NOT 32
+#define NOT 32
+#define BIT_NOT 33
+
+/*
+ * Binary string operators:
+ */
+
+#define STREQ 34
+#define STRNEQ 35
/*
* Mapping from lexemes to strings; used for debugging messages. These
* entries must match the order and number of the lexeme definitions above.
*/
-#ifdef TCL_COMPILE_DEBUG
static char *lexemeStrings[] = {
"LITERAL", "FUNCNAME",
- "[", "{", "(", ")", "$", "\"", ",", "END", "UNKNOWN",
+ "[", "{", "(", ")", "$", "\"", ",", "END", "UNKNOWN", "UNKNOWN_CHAR",
"*", "/", "%", "+", "-",
"<<", ">>", "<", ">", "<=", ">=", "==", "!=",
"&", "^", "|", "&&", "||", "?", ":",
- "!", "~"
+ "!", "~", "eq", "ne",
};
-#endif /* TCL_COMPILE_DEBUG */
/*
* Declarations for local procedures to this file:
*/
static int GetLexeme _ANSI_ARGS_((ParseInfo *infoPtr));
-static void LogSyntaxError _ANSI_ARGS_((ParseInfo *infoPtr));
+static void LogSyntaxError _ANSI_ARGS_((ParseInfo *infoPtr,
+ CONST char *extraInfo));
static int ParseAddExpr _ANSI_ARGS_((ParseInfo *infoPtr));
static int ParseBitAndExpr _ANSI_ARGS_((ParseInfo *infoPtr));
static int ParseBitOrExpr _ANSI_ARGS_((ParseInfo *infoPtr));
@@ -148,14 +158,16 @@ static int ParseCondExpr _ANSI_ARGS_((ParseInfo *infoPtr));
static int ParseEqualityExpr _ANSI_ARGS_((ParseInfo *infoPtr));
static int ParseLandExpr _ANSI_ARGS_((ParseInfo *infoPtr));
static int ParseLorExpr _ANSI_ARGS_((ParseInfo *infoPtr));
+static int ParseMaxDoubleLength _ANSI_ARGS_((CONST char *string,
+ CONST char *end));
static int ParseMultiplyExpr _ANSI_ARGS_((ParseInfo *infoPtr));
static int ParsePrimaryExpr _ANSI_ARGS_((ParseInfo *infoPtr));
static int ParseRelationalExpr _ANSI_ARGS_((ParseInfo *infoPtr));
static int ParseShiftExpr _ANSI_ARGS_((ParseInfo *infoPtr));
static int ParseUnaryExpr _ANSI_ARGS_((ParseInfo *infoPtr));
-static void PrependSubExprTokens _ANSI_ARGS_((char *op,
- int opBytes, char *src, int srcBytes,
- int firstIndex, ParseInfo *infoPtr));
+static void PrependSubExprTokens _ANSI_ARGS_((CONST char *op,
+ int opBytes, CONST char *src, int srcBytes,
+ int firstIndex, ParseInfo *infoPtr));
/*
* Macro used to debug the execution of the recursive descent parser used
@@ -181,7 +193,8 @@ static void PrependSubExprTokens _ANSI_ARGS_((char *op,
* Given a string, this procedure parses the first Tcl expression
* in the string and returns information about the structure of
* the expression. This procedure is the top-level interface to the
- * the expression parsing module.
+ * the expression parsing module. No more that numBytes bytes will
+ * be scanned.
*
* Results:
* The return value is TCL_OK if the command was parsed successfully
@@ -203,7 +216,7 @@ static void PrependSubExprTokens _ANSI_ARGS_((char *op,
int
Tcl_ParseExpr(interp, string, numBytes, parsePtr)
Tcl_Interp *interp; /* Used for error reporting. */
- char *string; /* The source string to parse. */
+ CONST char *string; /* The source string to parse. */
int numBytes; /* Number of bytes in string. If < 0, the
* string consists of all bytes up to the
* first null character. */
@@ -214,7 +227,6 @@ Tcl_ParseExpr(interp, string, numBytes, parsePtr)
{
ParseInfo info;
int code;
- char savedChar;
if (numBytes < 0) {
numBytes = (string? strlen(string) : 0);
@@ -241,17 +253,6 @@ Tcl_ParseExpr(interp, string, numBytes, parsePtr)
parsePtr->incomplete = 0;
/*
- * Temporarily overwrite the character just after the end of the
- * string with a 0 byte. This acts as a sentinel and reduces the
- * number of places where we have to check for the end of the
- * input string. The original value of the byte is restored at
- * the end of the parse.
- */
-
- savedChar = string[numBytes];
- string[numBytes] = 0;
-
- /*
* Initialize the ParseInfo structure that holds state while parsing
* the expression.
*/
@@ -278,14 +279,12 @@ Tcl_ParseExpr(interp, string, numBytes, parsePtr)
goto error;
}
if (info.lexeme != END) {
- LogSyntaxError(&info);
+ LogSyntaxError(&info, "extra tokens at end of expression");
goto error;
}
- string[numBytes] = (char) savedChar;
return TCL_OK;
error:
- string[numBytes] = (char) savedChar;
if (parsePtr->tokenPtr != parsePtr->staticTokens) {
ckfree((char *) parsePtr->tokenPtr);
}
@@ -301,7 +300,7 @@ Tcl_ParseExpr(interp, string, numBytes, parsePtr)
* condExpr ::= lorExpr ['?' condExpr ':' condExpr]
*
* Note that this is the topmost recursive-descent parsing routine used
- * by TclParseExpr to parse expressions. This avoids an extra procedure
+ * by Tcl_ParseExpr to parse expressions. This avoids an extra procedure
* call since such a procedure would only return the result of calling
* ParseCondExpr. Other recursive-descent procedures that need to parse
* complete expressions also call ParseCondExpr.
@@ -327,7 +326,7 @@ ParseCondExpr(infoPtr)
Tcl_Parse *parsePtr = infoPtr->parsePtr;
Tcl_Token *tokenPtr, *firstTokenPtr, *condTokenPtr;
int firstIndex, numToMove, code;
- char *srcStart;
+ CONST char *srcStart;
HERE("condExpr", 1);
srcStart = infoPtr->start;
@@ -384,7 +383,7 @@ ParseCondExpr(infoPtr)
return code;
}
if (infoPtr->lexeme != COLON) {
- LogSyntaxError(infoPtr);
+ LogSyntaxError(infoPtr, "missing colon from ternary conditional");
return TCL_ERROR;
}
code = GetLexeme(infoPtr); /* skip over the ':' */
@@ -440,7 +439,7 @@ ParseLorExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("lorExpr", 2);
srcStart = infoPtr->start;
@@ -500,7 +499,7 @@ ParseLandExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("landExpr", 3);
srcStart = infoPtr->start;
@@ -560,7 +559,7 @@ ParseBitOrExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("bitOrExpr", 4);
srcStart = infoPtr->start;
@@ -621,7 +620,7 @@ ParseBitXorExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("bitXorExpr", 5);
srcStart = infoPtr->start;
@@ -682,7 +681,7 @@ ParseBitAndExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("bitAndExpr", 6);
srcStart = infoPtr->start;
@@ -720,7 +719,8 @@ ParseBitAndExpr(infoPtr)
* ParseEqualityExpr --
*
* This procedure parses a Tcl equality (inequality) expression:
- * equalityExpr ::= relationalExpr {('==' | '!=') relationalExpr}
+ * equalityExpr ::= relationalExpr
+ * {('==' | '!=' | 'ne' | 'eq') relationalExpr}
*
* Results:
* The return value is TCL_OK on a successful parse and TCL_ERROR
@@ -742,7 +742,7 @@ ParseEqualityExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, lexeme, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("equalityExpr", 7);
srcStart = infoPtr->start;
@@ -754,9 +754,10 @@ ParseEqualityExpr(infoPtr)
}
lexeme = infoPtr->lexeme;
- while ((lexeme == EQUAL) || (lexeme == NEQ)) {
+ while ((lexeme == EQUAL) || (lexeme == NEQ)
+ || (lexeme == STREQ) || (lexeme == STRNEQ)) {
operator = infoPtr->start;
- code = GetLexeme(infoPtr); /* skip over == or != */
+ code = GetLexeme(infoPtr); /* skip over ==, !=, 'eq' or 'ne' */
if (code != TCL_OK) {
return code;
}
@@ -766,7 +767,8 @@ ParseEqualityExpr(infoPtr)
}
/*
- * Generate tokens for the subexpression and '==' or '!=' operator.
+ * Generate tokens for the subexpression and '==', '!=', 'eq' or 'ne'
+ * operator.
*/
PrependSubExprTokens(operator, 2, srcStart,
@@ -804,7 +806,7 @@ ParseRelationalExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, lexeme, operatorSize, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("relationalExpr", 8);
srcStart = infoPtr->start;
@@ -872,7 +874,7 @@ ParseShiftExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, lexeme, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("shiftExpr", 9);
srcStart = infoPtr->start;
@@ -934,7 +936,7 @@ ParseAddExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, lexeme, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("addExpr", 10);
srcStart = infoPtr->start;
@@ -996,7 +998,7 @@ ParseMultiplyExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, lexeme, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("multiplyExpr", 11);
srcStart = infoPtr->start;
@@ -1058,7 +1060,7 @@ ParseUnaryExpr(infoPtr)
{
Tcl_Parse *parsePtr = infoPtr->parsePtr;
int firstIndex, lexeme, code;
- char *srcStart, *operator;
+ CONST char *srcStart, *operator;
HERE("unaryExpr", 12);
srcStart = infoPtr->start;
@@ -1123,7 +1125,7 @@ ParsePrimaryExpr(infoPtr)
Tcl_Interp *interp = parsePtr->interp;
Tcl_Token *tokenPtr, *exprTokenPtr;
Tcl_Parse nested;
- char *dollarPtr, *stringStart, *termPtr, *src;
+ CONST char *dollarPtr, *stringStart, *termPtr, *src;
int lexeme, exprIndex, firstIndex, numToMove, code;
/*
@@ -1142,7 +1144,8 @@ ParsePrimaryExpr(infoPtr)
return code;
}
if (infoPtr->lexeme != CLOSE_PAREN) {
- goto syntaxError;
+ LogSyntaxError(infoPtr, "looking for close parenthesis");
+ return TCL_ERROR;
}
code = GetLexeme(infoPtr); /* skip over the ')' */
if (code != TCL_OK) {
@@ -1192,7 +1195,7 @@ ParsePrimaryExpr(infoPtr)
exprTokenPtr->size = infoPtr->size;
exprTokenPtr->numComponents = 1;
break;
-
+
case DOLLAR:
/*
* $var variable reference.
@@ -1372,7 +1375,43 @@ ParsePrimaryExpr(infoPtr)
return code;
}
if (infoPtr->lexeme != OPEN_PAREN) {
- goto syntaxError;
+ /*
+ * Guess what kind of error we have by trying to tell
+ * whether we have a function or variable name here.
+ * Alas, this makes the parser more tightly bound with the
+ * rest of the interpreter, but that is the only way to
+ * give a sensible message here. Still, it is not too
+ * serious as this is only done when generating an error.
+ */
+ Interp *iPtr = (Interp *) infoPtr->parsePtr->interp;
+ Tcl_DString functionName;
+ Tcl_HashEntry *hPtr;
+
+ /*
+ * Look up the name as a function name. We need a writable
+ * copy (DString) so we can terminate it with a NULL for
+ * the benefit of Tcl_FindHashEntry which operates on
+ * NULL-terminated string keys.
+ */
+ Tcl_DStringInit(&functionName);
+ hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable,
+ Tcl_DStringAppend(&functionName, tokenPtr->start,
+ tokenPtr->size));
+ Tcl_DStringFree(&functionName);
+
+ /*
+ * Assume that we have an attempted variable reference
+ * unless we've got a function name, as the set of
+ * potential function names is typically much smaller.
+ */
+ if (hPtr != NULL) {
+ LogSyntaxError(infoPtr,
+ "expected parenthesis enclosing function arguments");
+ } else {
+ LogSyntaxError(infoPtr,
+ "variable references require preceding $");
+ }
+ return TCL_ERROR;
}
code = GetLexeme(infoPtr); /* skip over '(' */
if (code != TCL_OK) {
@@ -1391,7 +1430,9 @@ ParsePrimaryExpr(infoPtr)
return code;
}
} else if (infoPtr->lexeme != CLOSE_PAREN) {
- goto syntaxError;
+ LogSyntaxError(infoPtr,
+ "missing close parenthesis at end of function call");
+ return TCL_ERROR;
}
}
@@ -1399,9 +1440,37 @@ ParsePrimaryExpr(infoPtr)
exprTokenPtr->size = (infoPtr->next - exprTokenPtr->start);
exprTokenPtr->numComponents = parsePtr->numTokens - firstIndex;
break;
-
- default:
- goto syntaxError;
+
+ case COMMA:
+ LogSyntaxError(infoPtr,
+ "commas can only separate function arguments");
+ return TCL_ERROR;
+ case END:
+ LogSyntaxError(infoPtr, "premature end of expression");
+ return TCL_ERROR;
+ case UNKNOWN:
+ LogSyntaxError(infoPtr, "single equality character not legal in expressions");
+ return TCL_ERROR;
+ case UNKNOWN_CHAR:
+ LogSyntaxError(infoPtr, "character not legal in expressions");
+ return TCL_ERROR;
+ case QUESTY:
+ LogSyntaxError(infoPtr, "unexpected ternary 'then' separator");
+ return TCL_ERROR;
+ case COLON:
+ LogSyntaxError(infoPtr, "unexpected ternary 'else' separator");
+ return TCL_ERROR;
+ case CLOSE_PAREN:
+ LogSyntaxError(infoPtr, "unexpected close parenthesis");
+ return TCL_ERROR;
+
+ default: {
+ char buf[64];
+
+ sprintf(buf, "unexpected operator %s", lexemeStrings[lexeme]);
+ LogSyntaxError(infoPtr, buf);
+ return TCL_ERROR;
+ }
}
/*
@@ -1414,10 +1483,6 @@ ParsePrimaryExpr(infoPtr)
}
parsePtr->term = infoPtr->next;
return TCL_OK;
-
- syntaxError:
- LogSyntaxError(infoPtr);
- return TCL_ERROR;
}
/*
@@ -1453,11 +1518,9 @@ GetLexeme(infoPtr)
ParseInfo *infoPtr; /* Holds state needed to parse the expr,
* including the resulting lexeme. */
{
- register char *src; /* Points to current source char. */
- char *termPtr; /* Points to char terminating a literal. */
- double doubleValue; /* Value of a scanned double literal. */
+ register CONST char *src; /* Points to current source char. */
char c;
- int startsWithDigit, offset;
+ int offset, length, numBytes;
Tcl_Parse *parsePtr = infoPtr->parsePtr;
Tcl_Interp *interp = parsePtr->interp;
Tcl_UniChar ch;
@@ -1471,26 +1534,18 @@ GetLexeme(infoPtr)
infoPtr->prevEnd = infoPtr->next;
/*
- * Scan over leading white space at the start of a lexeme. Note that a
- * backslash-newline is treated as a space.
+ * Scan over leading white space at the start of a lexeme.
*/
src = infoPtr->next;
- c = *src;
- while (isspace(UCHAR(c)) || (c == '\\')) { /* INTL: ISO space */
- if (c == '\\') {
- if (src[1] == '\n') {
- src += 2;
- } else {
- break; /* no longer white space */
- }
- } else {
- src++;
- }
- c = *src;
- }
+ numBytes = parsePtr->end - src;
+ do {
+ char type;
+ int scanned = TclParseWhiteSpace(src, numBytes, parsePtr, &type);
+ src += scanned; numBytes -= scanned;
+ } while (numBytes && (*src == '\n') && (src++,numBytes--));
parsePtr->term = src;
- if (src >= infoPtr->lastChar) {
+ if (numBytes == 0) {
infoPtr->lexeme = END;
infoPtr->next = src;
return TCL_OK;
@@ -1503,59 +1558,48 @@ GetLexeme(infoPtr)
* by mistake, which would eventually cause a syntax error.
*/
+ c = *src;
if ((c != '+') && (c != '-')) {
- startsWithDigit = isdigit(UCHAR(c)); /* INTL: digit */
- if (startsWithDigit && TclLooksLikeInt(src, -1)) {
- errno = 0;
- (void) strtoul(src, &termPtr, 0);
- if (errno == ERANGE) {
- if (interp != NULL) {
- char *s = "integer value too large to represent";
- Tcl_ResetResult(interp);
- Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1);
- Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s,
- (char *) NULL);
- }
+ CONST char *end = infoPtr->lastChar;
+ if ((length = TclParseInteger(src, (end - src)))) {
+ /*
+ * First length bytes look like an integer. Verify by
+ * attempting the conversion to the largest integer we have.
+ */
+ int code;
+ Tcl_WideInt wide;
+ Tcl_Obj *value = Tcl_NewStringObj(src, length);
+
+ Tcl_IncrRefCount(value);
+ code = Tcl_GetWideIntFromObj(interp, value, &wide);
+ Tcl_DecrRefCount(value);
+ if (code == TCL_ERROR) {
parsePtr->errorType = TCL_PARSE_BAD_NUMBER;
return TCL_ERROR;
}
- if (termPtr != src) {
- /*
- * src was the start of a valid integer, but was it
- * a bad octal? Stopping at a digit would cause that.
- */
- if (isdigit(UCHAR(*termPtr))) { /* INTL: digit. */
- /*
- * We only want to report an error for the number,
- * but we may have something like "08+1"
- */
- if (interp != NULL) {
- while (isdigit(UCHAR(*(++termPtr)))) {} /* INTL: digit. */
- Tcl_ResetResult(interp);
- offset = termPtr - src;
- c = src[offset];
- src[offset] = 0;
- Tcl_AppendResult(interp, "\"", src,
- "\" is an invalid octal number",
- (char *) NULL);
- src[offset] = c;
- }
- parsePtr->errorType = TCL_PARSE_BAD_NUMBER;
- return TCL_ERROR;
- }
+ infoPtr->lexeme = LITERAL;
+ infoPtr->start = src;
+ infoPtr->size = length;
+ infoPtr->next = (src + length);
+ parsePtr->term = infoPtr->next;
+ return TCL_OK;
+ } else if ((length = ParseMaxDoubleLength(src, end))) {
+ /*
+ * There are length characters that could be a double.
+ * Let strtod() tells us for sure. Need a writable copy
+ * so we can set an terminating NULL to keep strtod from
+ * scanning too far.
+ */
+ char *startPtr, *termPtr;
+ double doubleValue;
+ Tcl_DString toParse;
- infoPtr->lexeme = LITERAL;
- infoPtr->start = src;
- infoPtr->size = (termPtr - src);
- infoPtr->next = termPtr;
- parsePtr->term = termPtr;
- return TCL_OK;
- }
- } else if (startsWithDigit || (c == '.')
- || (c == 'n') || (c == 'N')) {
errno = 0;
- doubleValue = strtod(src, &termPtr);
- if (termPtr != src) {
+ Tcl_DStringInit(&toParse);
+ startPtr = Tcl_DStringAppend(&toParse, src, length);
+ doubleValue = strtod(startPtr, &termPtr);
+ Tcl_DStringFree(&toParse);
+ if (termPtr != startPtr) {
if (errno != 0) {
if (interp != NULL) {
TclExprFloatError(interp, doubleValue);
@@ -1565,14 +1609,19 @@ GetLexeme(infoPtr)
}
/*
- * src was the start of a valid double.
+ * startPtr was the start of a valid double, copied
+ * from src.
*/
infoPtr->lexeme = LITERAL;
infoPtr->start = src;
- infoPtr->size = (termPtr - src);
- infoPtr->next = termPtr;
- parsePtr->term = termPtr;
+ if ((termPtr - startPtr) > length) {
+ infoPtr->size = length;
+ } else {
+ infoPtr->size = (termPtr - startPtr);
+ }
+ infoPtr->next = src + infoPtr->size;
+ parsePtr->term = infoPtr->next;
return TCL_OK;
}
}
@@ -1646,72 +1695,69 @@ GetLexeme(infoPtr)
return TCL_OK;
case '<':
- switch (src[1]) {
- case '<':
- infoPtr->lexeme = LEFT_SHIFT;
- infoPtr->size = 2;
- infoPtr->next = src+2;
- break;
- case '=':
- infoPtr->lexeme = LEQ;
- infoPtr->size = 2;
- infoPtr->next = src+2;
- break;
- default:
- infoPtr->lexeme = LESS;
- break;
+ infoPtr->lexeme = LESS;
+ if ((infoPtr->lastChar - src) > 1) {
+ switch (src[1]) {
+ case '<':
+ infoPtr->lexeme = LEFT_SHIFT;
+ infoPtr->size = 2;
+ infoPtr->next = src+2;
+ break;
+ case '=':
+ infoPtr->lexeme = LEQ;
+ infoPtr->size = 2;
+ infoPtr->next = src+2;
+ break;
+ }
}
parsePtr->term = infoPtr->next;
return TCL_OK;
case '>':
- switch (src[1]) {
- case '>':
- infoPtr->lexeme = RIGHT_SHIFT;
- infoPtr->size = 2;
- infoPtr->next = src+2;
- break;
- case '=':
- infoPtr->lexeme = GEQ;
- infoPtr->size = 2;
- infoPtr->next = src+2;
- break;
- default:
- infoPtr->lexeme = GREATER;
- break;
+ infoPtr->lexeme = GREATER;
+ if ((infoPtr->lastChar - src) > 1) {
+ switch (src[1]) {
+ case '>':
+ infoPtr->lexeme = RIGHT_SHIFT;
+ infoPtr->size = 2;
+ infoPtr->next = src+2;
+ break;
+ case '=':
+ infoPtr->lexeme = GEQ;
+ infoPtr->size = 2;
+ infoPtr->next = src+2;
+ break;
+ }
}
parsePtr->term = infoPtr->next;
return TCL_OK;
case '=':
- if (src[1] == '=') {
+ infoPtr->lexeme = UNKNOWN;
+ if ((src[1] == '=') && ((infoPtr->lastChar - src) > 1)) {
infoPtr->lexeme = EQUAL;
infoPtr->size = 2;
infoPtr->next = src+2;
- } else {
- infoPtr->lexeme = UNKNOWN;
}
parsePtr->term = infoPtr->next;
return TCL_OK;
case '!':
- if (src[1] == '=') {
+ infoPtr->lexeme = NOT;
+ if ((src[1] == '=') && ((infoPtr->lastChar - src) > 1)) {
infoPtr->lexeme = NEQ;
infoPtr->size = 2;
infoPtr->next = src+2;
- } else {
- infoPtr->lexeme = NOT;
}
parsePtr->term = infoPtr->next;
return TCL_OK;
case '&':
- if (src[1] == '&') {
+ infoPtr->lexeme = BIT_AND;
+ if ((src[1] == '&') && ((infoPtr->lastChar - src) > 1)) {
infoPtr->lexeme = AND;
infoPtr->size = 2;
infoPtr->next = src+2;
- } else {
- infoPtr->lexeme = BIT_AND;
}
parsePtr->term = infoPtr->next;
return TCL_OK;
@@ -1721,12 +1767,11 @@ GetLexeme(infoPtr)
return TCL_OK;
case '|':
- if (src[1] == '|') {
+ infoPtr->lexeme = BIT_OR;
+ if ((src[1] == '|') && ((infoPtr->lastChar - src) > 1)) {
infoPtr->lexeme = OR;
infoPtr->size = 2;
infoPtr->next = src+2;
- } else {
- infoPtr->lexeme = BIT_OR;
}
parsePtr->term = infoPtr->next;
return TCL_OK;
@@ -1735,22 +1780,104 @@ GetLexeme(infoPtr)
infoPtr->lexeme = BIT_NOT;
return TCL_OK;
+ case 'e':
+ if ((src[1] == 'q') && ((infoPtr->lastChar - src) > 1)) {
+ infoPtr->lexeme = STREQ;
+ infoPtr->size = 2;
+ infoPtr->next = src+2;
+ parsePtr->term = infoPtr->next;
+ return TCL_OK;
+ } else {
+ goto checkFuncName;
+ }
+
+ case 'n':
+ if ((src[1] == 'e') && ((infoPtr->lastChar - src) > 1)) {
+ infoPtr->lexeme = STRNEQ;
+ infoPtr->size = 2;
+ infoPtr->next = src+2;
+ parsePtr->term = infoPtr->next;
+ return TCL_OK;
+ } else {
+ goto checkFuncName;
+ }
+
default:
- offset = Tcl_UtfToUniChar(src, &ch);
+ checkFuncName:
+ length = (infoPtr->lastChar - src);
+ if (Tcl_UtfCharComplete(src, length)) {
+ offset = Tcl_UtfToUniChar(src, &ch);
+ } else {
+ char utfBytes[TCL_UTF_MAX];
+ memcpy(utfBytes, src, (size_t) length);
+ utfBytes[length] = '\0';
+ offset = Tcl_UtfToUniChar(utfBytes, &ch);
+ }
c = UCHAR(ch);
if (isalpha(UCHAR(c))) { /* INTL: ISO only. */
infoPtr->lexeme = FUNC_NAME;
while (isalnum(UCHAR(c)) || (c == '_')) { /* INTL: ISO only. */
- src += offset;
- offset = Tcl_UtfToUniChar(src, &ch);
+ src += offset; length -= offset;
+ if (Tcl_UtfCharComplete(src, length)) {
+ offset = Tcl_UtfToUniChar(src, &ch);
+ } else {
+ char utfBytes[TCL_UTF_MAX];
+ memcpy(utfBytes, src, (size_t) length);
+ utfBytes[length] = '\0';
+ offset = Tcl_UtfToUniChar(utfBytes, &ch);
+ }
c = UCHAR(ch);
}
infoPtr->size = (src - infoPtr->start);
infoPtr->next = src;
parsePtr->term = infoPtr->next;
+ /*
+ * Check for boolean literals (true, false, yes, no, on, off)
+ */
+ switch (infoPtr->start[0]) {
+ case 'f':
+ if (infoPtr->size == 5 &&
+ strncmp("false", infoPtr->start, 5) == 0) {
+ infoPtr->lexeme = LITERAL;
+ return TCL_OK;
+ }
+ break;
+ case 'n':
+ if (infoPtr->size == 2 &&
+ strncmp("no", infoPtr->start, 2) == 0) {
+ infoPtr->lexeme = LITERAL;
+ return TCL_OK;
+ }
+ break;
+ case 'o':
+ if (infoPtr->size == 3 &&
+ strncmp("off", infoPtr->start, 3) == 0) {
+ infoPtr->lexeme = LITERAL;
+ return TCL_OK;
+ } else if (infoPtr->size == 2 &&
+ strncmp("on", infoPtr->start, 2) == 0) {
+ infoPtr->lexeme = LITERAL;
+ return TCL_OK;
+ }
+ break;
+ case 't':
+ if (infoPtr->size == 4 &&
+ strncmp("true", infoPtr->start, 4) == 0) {
+ infoPtr->lexeme = LITERAL;
+ return TCL_OK;
+ }
+ break;
+ case 'y':
+ if (infoPtr->size == 3 &&
+ strncmp("yes", infoPtr->start, 3) == 0) {
+ infoPtr->lexeme = LITERAL;
+ return TCL_OK;
+ }
+ break;
+ }
return TCL_OK;
}
- infoPtr->lexeme = UNKNOWN;
+ infoPtr->lexeme = UNKNOWN_CHAR;
return TCL_OK;
}
}
@@ -1758,6 +1885,107 @@ GetLexeme(infoPtr)
/*
*----------------------------------------------------------------------
*
+ * TclParseInteger --
+ *
+ * Scans up to numBytes bytes starting at src, and checks whether
+ * the leading bytes look like an integer's string representation.
+ *
+ * Results:
+ * Returns 0 if the leading bytes do not look like an integer.
+ * Otherwise, returns the number of bytes examined that look
+ * like an integer. This may be less than numBytes if the integer
+ * is only the leading part of the string.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclParseInteger(string, numBytes)
+ register CONST char *string;/* The string to examine. */
+ register int numBytes; /* Max number of bytes to scan. */
+{
+ register CONST char *p = string;
+
+ /* Take care of introductory "0x" */
+ if ((numBytes > 1) && (p[0] == '0') && ((p[1] == 'x') || (p[1] == 'X'))) {
+ int scanned;
+ Tcl_UniChar ch;
+ p+=2; numBytes -= 2;
+ scanned = TclParseHex(p, numBytes, &ch);
+ if (scanned) {
+ return scanned + 2;
+ }
+ return 0;
+ }
+ while (numBytes && isdigit(UCHAR(*p))) { /* INTL: digit */
+ numBytes--; p++;
+ }
+ if (numBytes == 0) {
+ return (p - string);
+ }
+ if ((*p != '.') && (*p != 'e') && (*p != 'E')) {
+ return (p - string);
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParseMaxDoubleLength --
+ *
+ * Scans a sequence of bytes checking that the characters could
+ * be in a string rep of a double.
+ *
+ * Results:
+ * Returns the number of bytes starting with string, runing to, but
+ * not including end, all of which could be part of a string rep.
+ * of a double. Only character identity is used, no actual
+ * parsing is done.
+ *
+ * The legal bytes are '0' - '9', 'A' - 'F', 'a' - 'f',
+ * '.', '+', '-', 'i', 'I', 'n', 'N', 'p', 'P', 'x', and 'X'.
+ * This covers the values "Inf" and "Nan" as well as the
+ * decimal and hexadecimal representations recognized by a
+ * C99-compliant strtod().
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ParseMaxDoubleLength(string, end)
+ register CONST char *string;/* The string to examine. */
+ CONST char *end; /* Point to the first character past the end
+ * of the string we are examining. */
+{
+ CONST char *p = string;
+ while (p < end) {
+ switch (*p) {
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case 'A': case 'B':
+ case 'C': case 'D': case 'E': case 'F': case 'I': case 'N':
+ case 'P': case 'X': case 'a': case 'b': case 'c': case 'd':
+ case 'e': case 'f': case 'i': case 'n': case 'p': case 'x':
+ case '.': case '+': case '-':
+ p++;
+ break;
+ default:
+ goto done;
+ }
+ }
+ done:
+ return (p - string);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* PrependSubExprTokens --
*
* This procedure is called after the operands of an subexpression have
@@ -1777,10 +2005,10 @@ GetLexeme(infoPtr)
static void
PrependSubExprTokens(op, opBytes, src, srcBytes, firstIndex, infoPtr)
- char *op; /* Points to first byte of the operator
+ CONST char *op; /* Points to first byte of the operator
* in the source script. */
int opBytes; /* Number of bytes in the operator. */
- char *src; /* Points to first byte of the subexpression
+ CONST char *src; /* Points to first byte of the subexpression
* in the source script. */
int srcBytes; /* Number of bytes in subexpression's
* source. */
@@ -1830,23 +2058,32 @@ PrependSubExprTokens(op, opBytes, src, srcBytes, firstIndex, infoPtr)
*
* Side effects:
* Sets the interpreter result to an error message describing the
- * expression that was being parsed when the error occurred.
+ * expression that was being parsed when the error occurred, and why
+ * the parser considers that to be a syntax error at all.
*
*----------------------------------------------------------------------
*/
static void
-LogSyntaxError(infoPtr)
+LogSyntaxError(infoPtr, extraInfo)
ParseInfo *infoPtr; /* Holds the parse state for the
* expression being parsed. */
+ CONST char *extraInfo; /* String to provide extra information
+ * about the syntax error. */
{
int numBytes = (infoPtr->lastChar - infoPtr->originalExpr);
char buffer[100];
- sprintf(buffer, "syntax error in expression \"%.*s\"",
- ((numBytes > 60)? 60 : numBytes), infoPtr->originalExpr);
+ if (numBytes > 60) {
+ sprintf(buffer, "syntax error in expression \"%.60s...\"",
+ infoPtr->originalExpr);
+ } else {
+ sprintf(buffer, "syntax error in expression \"%.*s\"",
+ numBytes, infoPtr->originalExpr);
+ }
+ Tcl_ResetResult(infoPtr->parsePtr->interp);
Tcl_AppendStringsToObj(Tcl_GetObjResult(infoPtr->parsePtr->interp),
- buffer, (char *) NULL);
+ buffer, ": ", extraInfo, (char *) NULL);
infoPtr->parsePtr->errorType = TCL_PARSE_SYNTAX;
infoPtr->parsePtr->term = infoPtr->start;
}
diff --git a/tcl/generic/tclPipe.c b/tcl/generic/tclPipe.c
index 09bcb486671..e47648fe887 100644
--- a/tcl/generic/tclPipe.c
+++ b/tcl/generic/tclPipe.c
@@ -39,8 +39,9 @@ TCL_DECLARE_MUTEX(pipeMutex) /* Guard access to detList. */
*/
static TclFile FileForRedirect _ANSI_ARGS_((Tcl_Interp *interp,
- char *spec, int atOk, char *arg, char *nextArg,
- int flags, int *skipPtr, int *closePtr, int *releasePtr));
+ CONST char *spec, int atOk, CONST char *arg,
+ CONST char *nextArg, int flags, int *skipPtr,
+ int *closePtr, int *releasePtr));
/*
*----------------------------------------------------------------------
@@ -67,14 +68,14 @@ static TclFile
FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr,
releasePtr)
Tcl_Interp *interp; /* Intepreter to use for error reporting. */
- char *spec; /* Points to character just after
+ CONST char *spec; /* Points to character just after
* redirection character. */
- char *arg; /* Pointer to entire argument containing
+ CONST char *arg; /* Pointer to entire argument containing
* spec: used for error reporting. */
int atOK; /* Non-zero means that '@' notation can be
* used to specify a channel, zero means that
* it isn't. */
- char *nextArg; /* Next argument in argc/argv array, if needed
+ CONST char *nextArg; /* Next argument in argc/argv array, if needed
* for file name or channel name. May be
* NULL. */
int flags; /* Flags to use for opening file or to
@@ -123,7 +124,7 @@ FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr,
Tcl_Flush(chan);
}
} else {
- char *name;
+ CONST char *name;
Tcl_DString nameString;
if (*spec == '\0') {
@@ -278,7 +279,7 @@ TclCleanupChildren(interp, numPids, pidPtr, errorChan)
int i, abnormalExit, anyErrorInfo;
Tcl_Pid pid;
WAIT_STATUS_TYPE waitStatus;
- char *msg;
+ CONST char *msg;
abnormalExit = 0;
for (i = 0; i < numPids; i++) {
@@ -324,7 +325,7 @@ TclCleanupChildren(interp, numPids, pidPtr, errorChan)
abnormalExit = 1;
} else if (WIFSIGNALED(waitStatus)) {
if (interp != (Tcl_Interp *) NULL) {
- char *p;
+ CONST char *p;
p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
@@ -335,7 +336,7 @@ TclCleanupChildren(interp, numPids, pidPtr, errorChan)
}
} else if (WIFSTOPPED(waitStatus)) {
if (interp != (Tcl_Interp *) NULL) {
- char *p;
+ CONST char *p;
p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
@@ -371,7 +372,7 @@ TclCleanupChildren(interp, numPids, pidPtr, errorChan)
int count;
Tcl_Obj *objPtr;
- Tcl_Seek(errorChan, 0L, SEEK_SET);
+ Tcl_Seek(errorChan, (Tcl_WideInt)0, SEEK_SET);
objPtr = Tcl_NewObj();
count = Tcl_ReadChars(errorChan, objPtr, -1, 0);
if (count < 0) {
@@ -439,7 +440,7 @@ TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
outPipePtr, errFilePtr)
Tcl_Interp *interp; /* Interpreter to use for error reporting. */
int argc; /* Number of entries in argv. */
- char **argv; /* Array of strings describing commands in
+ CONST char **argv; /* Array of strings describing commands in
* pipeline plus I/O redirection with <,
* <<, >, etc. Argv[argc] must be NULL. */
Tcl_Pid **pidArrayPtr; /* Word at *pidArrayPtr gets filled in with
@@ -476,7 +477,7 @@ TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
* at *pidPtr right now. */
int cmdCount; /* Count of number of distinct commands
* found in argc/argv. */
- char *inputLiteral = NULL; /* If non-null, then this points to a
+ CONST char *inputLiteral = NULL; /* If non-null, then this points to a
* string containing input data (specified
* via <<) to be piped to the first process
* in the pipeline. */
@@ -498,7 +499,7 @@ TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
int errorClose = 0; /* If non-zero, then errorFile should be
* closed when cleaning up. */
int errorRelease = 0;
- char *p;
+ CONST char *p;
int skip, lastBar, lastArg, i, j, atOK, flags, errorToOutput;
Tcl_DString execBuffer;
TclFile pipeIn;
@@ -802,7 +803,7 @@ TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
for (i = 0; i < argc; i = lastArg + 1) {
int result, joinThisError;
Tcl_Pid pid;
- char *oldName;
+ CONST char *oldName;
/*
* Convert the program name into native form.
@@ -992,7 +993,7 @@ Tcl_OpenCommandChannel(interp, argc, argv, flags)
Tcl_Interp *interp; /* Interpreter for error reporting. Can
* NOT be NULL. */
int argc; /* How many arguments. */
- char **argv; /* Array of arguments for command pipe. */
+ CONST char **argv; /* Array of arguments for command pipe. */
int flags; /* Or'ed combination of TCL_STDIN, TCL_STDOUT,
* TCL_STDERR, and TCL_ENFORCE_MODE. */
{
diff --git a/tcl/generic/tclPkg.c b/tcl/generic/tclPkg.c
index 1906e8dbafb..1bdfe18991a 100644
--- a/tcl/generic/tclPkg.c
+++ b/tcl/generic/tclPkg.c
@@ -51,11 +51,12 @@ typedef struct Package {
*/
static int CheckVersion _ANSI_ARGS_((Tcl_Interp *interp,
- char *string));
-static int ComparePkgVersions _ANSI_ARGS_((char *v1, char *v2,
+ CONST char *string));
+static int ComparePkgVersions _ANSI_ARGS_((CONST char *v1,
+ CONST char *v2,
int *satPtr));
static Package * FindPackage _ANSI_ARGS_((Tcl_Interp *interp,
- char *name));
+ CONST char *name));
/*
*----------------------------------------------------------------------
@@ -84,8 +85,8 @@ int
Tcl_PkgProvide(interp, name, version)
Tcl_Interp *interp; /* Interpreter in which package is now
* available. */
- char *name; /* Name of package. */
- char *version; /* Version string for package. */
+ CONST char *name; /* Name of package. */
+ CONST char *version; /* Version string for package. */
{
return Tcl_PkgProvideEx(interp, name, version, (ClientData) NULL);
}
@@ -94,8 +95,8 @@ int
Tcl_PkgProvideEx(interp, name, version, clientData)
Tcl_Interp *interp; /* Interpreter in which package is now
* available. */
- char *name; /* Name of package. */
- char *version; /* Version string for package. */
+ CONST char *name; /* Name of package. */
+ CONST char *version; /* Version string for package. */
ClientData clientData; /* clientdata for this package (normally
* used for C callback function table) */
{
@@ -148,12 +149,12 @@ Tcl_PkgProvideEx(interp, name, version, clientData)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_PkgRequire(interp, name, version, exact)
Tcl_Interp *interp; /* Interpreter in which package is now
* available. */
- char *name; /* Name of desired package. */
- char *version; /* Version string for desired version;
+ CONST char *name; /* Name of desired package. */
+ CONST char *version; /* Version string for desired version;
* NULL means use the latest version
* available. */
int exact; /* Non-zero means that only the particular
@@ -163,12 +164,12 @@ Tcl_PkgRequire(interp, name, version, exact)
return Tcl_PkgRequireEx(interp, name, version, exact, (ClientData *) NULL);
}
-char *
+CONST char *
Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr)
Tcl_Interp *interp; /* Interpreter in which package is now
* available. */
- char *name; /* Name of desired package. */
- char *version; /* Version string for desired version;
+ CONST char *name; /* Name of desired package. */
+ CONST char *version; /* Version string for desired version;
* NULL means use the latest version
* available. */
int exact; /* Non-zero means that only the particular
@@ -186,7 +187,7 @@ Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr)
Tcl_DString command;
/*
- * If an attempt is being made to load this into a standalong executable
+ * If an attempt is being made to load this into a standalone executable
* on a platform where backlinking is not supported then this must be
* a shared version of Tcl (Otherwise the load would have failed).
* Detect this situation by checking that this library has been correctly
@@ -194,7 +195,67 @@ Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr)
* work.
*/
- if (!tclEmptyStringRep) {
+ if (tclEmptyStringRep == NULL) {
+
+ /*
+ * OK, so what's going on here?
+ *
+ * First, what are we doing? We are performing a check on behalf of
+ * one particular caller, Tcl_InitStubs(). When a package is
+ * stub-enabled, it is statically linked to libtclstub.a, which
+ * contains a copy of Tcl_InitStubs(). When a stub-enabled package
+ * is loaded, its *_Init() function is supposed to call
+ * Tcl_InitStubs() before calling any other functions in the Tcl
+ * library. The first Tcl function called by Tcl_InitStubs() through
+ * the stub table is Tcl_PkgRequireEx(), so this code right here is
+ * the first code that is part of the original Tcl library in the
+ * executable that gets executed on behalf of a newly loaded
+ * stub-enabled package.
+ *
+ * One easy error for the developer/builder of a stub-enabled package
+ * to make is to forget to define USE_TCL_STUBS when compiling the
+ * package. When that happens, the package will contain symbols
+ * that are references to the Tcl library, rather than function
+ * pointers referencing the stub table. On platforms that lack
+ * backlinking, those unresolved references may cause the loading
+ * of the package to also load a second copy of the Tcl library,
+ * leading to all kinds of trouble. We would like to catch that
+ * error and report a useful message back to the user. That's
+ * what we're doing.
+ *
+ * Second, how does this work? If we reach this point, then the
+ * global variable tclEmptyStringRep has the value NULL. Compare
+ * that with the definition of tclEmptyStringRep near the top of
+ * the file generic/tclObj.c. It clearly should not have the value
+ * NULL; it should point to the char tclEmptyString. If we see it
+ * having the value NULL, then somehow we are seeing a Tcl library
+ * that isn't completely initialized, and that's an indicator for the
+ * error condition described above. (Further explanation is welcome.)
+ *
+ * Third, so what do we do about it? This situation indicates
+ * the package we just loaded wasn't properly compiled to be
+ * stub-enabled, yet it thinks it is stub-enabled (it called
+ * Tcl_InitStubs()). We want to report that the package just
+ * loaded is broken, so we want to place an error message in
+ * the interpreter result and return NULL to indicate failure
+ * to Tcl_InitStubs() so that it will also fail. (Further
+ * explanation why we don't want to Tcl_Panic() is welcome.
+ * After all, two Tcl libraries can't be a good thing!)
+ *
+ * Trouble is that's going to be tricky. We're now using a Tcl
+ * library that's not fully initialized. In particular, it
+ * doesn't have a proper value for tclEmptyStringRep. The
+ * Tcl_Obj system heavily depends on the value of tclEmptyStringRep
+ * and all of Tcl depends (increasingly) on the Tcl_Obj system, we
+ * need to correct that flaw before making the calls to set the
+ * interpreter result to the error message. That's the only flaw
+ * corrected; other problems with initialization of the Tcl library
+ * are not remedied, so be very careful about adding any other calls
+ * here without checking how they behave when initialization is
+ * incomplete.
+ */
+
+ tclEmptyStringRep = &tclEmptyString;
Tcl_AppendResult(interp, "Cannot load package \"", name,
"\" in standalone executable: This package is not ",
"compiled with stub support", NULL);
@@ -350,12 +411,12 @@ Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_PkgPresent(interp, name, version, exact)
Tcl_Interp *interp; /* Interpreter in which package is now
* available. */
- char *name; /* Name of desired package. */
- char *version; /* Version string for desired version;
+ CONST char *name; /* Name of desired package. */
+ CONST char *version; /* Version string for desired version;
* NULL means use the latest version
* available. */
int exact; /* Non-zero means that only the particular
@@ -365,12 +426,12 @@ Tcl_PkgPresent(interp, name, version, exact)
return Tcl_PkgPresentEx(interp, name, version, exact, (ClientData *) NULL);
}
-char *
+CONST char *
Tcl_PkgPresentEx(interp, name, version, exact, clientDataPtr)
Tcl_Interp *interp; /* Interpreter in which package is now
* available. */
- char *name; /* Name of desired package. */
- char *version; /* Version string for desired version;
+ CONST char *name; /* Name of desired package. */
+ CONST char *version; /* Version string for desired version;
* NULL means use the latest version
* available. */
int exact; /* Non-zero means that only the particular
@@ -386,22 +447,6 @@ Tcl_PkgPresentEx(interp, name, version, exact, clientDataPtr)
Package *pkgPtr;
int satisfies, result;
- /*
- * If an attempt is being made to load this into a standalone executable
- * on a platform where backlinking is not supported then this must be
- * a shared version of Tcl (Otherwise the load would have failed).
- * Detect this situation by checking that this library has been correctly
- * initialised. If it has not been then return immediately as nothing will
- * work.
- */
-
- if (!tclEmptyStringRep) {
- Tcl_AppendResult(interp, "Cannot load package \"", name,
- "\" in standalone executable: This package is not ",
- "compiled with stub support", NULL);
- return NULL;
- }
-
hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name);
if (hPtr) {
pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
@@ -469,7 +514,7 @@ Tcl_PackageObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- static char *pkgOptions[] = {
+ static CONST char *pkgOptions[] = {
"forget", "ifneeded", "names", "present", "provide", "require",
"unknown", "vcompare", "versions", "vsatisfies", (char *) NULL
};
@@ -485,7 +530,8 @@ Tcl_PackageObjCmd(dummy, interp, objc, objv)
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
Tcl_HashTable *tablePtr;
- char *version, *argv2, *argv3, *argv4;
+ CONST char *version;
+ char *argv2, *argv3, *argv4;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
@@ -503,7 +549,7 @@ Tcl_PackageObjCmd(dummy, interp, objc, objv)
keyString = Tcl_GetString(objv[i]);
hPtr = Tcl_FindHashEntry(&iPtr->packageTable, keyString);
if (hPtr == NULL) {
- return TCL_OK;
+ continue;
}
pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
Tcl_DeleteHashEntry(hPtr);
@@ -619,7 +665,7 @@ Tcl_PackageObjCmd(dummy, interp, objc, objv)
if (version == NULL) {
return TCL_ERROR;
}
- Tcl_SetResult(interp, version, TCL_VOLATILE);
+ Tcl_SetObjResult( interp, Tcl_NewStringObj( version, -1 ) );
break;
}
case PKG_PROVIDE: {
@@ -674,7 +720,7 @@ Tcl_PackageObjCmd(dummy, interp, objc, objv)
if (version == NULL) {
return TCL_ERROR;
}
- Tcl_SetResult(interp, version, TCL_VOLATILE);
+ Tcl_SetObjResult( interp, Tcl_NewStringObj( version, -1 ) );
break;
}
case PKG_UNKNOWN: {
@@ -776,7 +822,7 @@ Tcl_PackageObjCmd(dummy, interp, objc, objv)
static Package *
FindPackage(interp, name)
Tcl_Interp *interp; /* Interpreter to use for package lookup. */
- char *name; /* Name of package to fine. */
+ CONST char *name; /* Name of package to fine. */
{
Interp *iPtr = (Interp *) interp;
Tcl_HashEntry *hPtr;
@@ -866,11 +912,11 @@ TclFreePackageInfo(iPtr)
static int
CheckVersion(interp, string)
Tcl_Interp *interp; /* Used for error reporting. */
- char *string; /* Supposedly a version number, which is
+ CONST char *string; /* Supposedly a version number, which is
* groups of decimal digits separated
* by dots. */
{
- char *p = string;
+ CONST char *p = string;
char prevChar;
if (!isdigit(UCHAR(*p))) { /* INTL: digit */
@@ -915,7 +961,8 @@ CheckVersion(interp, string)
static int
ComparePkgVersions(v1, v2, satPtr)
- char *v1, *v2; /* Versions strings, of form 2.1.3 (any
+ CONST char *v1;
+ CONST char *v2; /* Versions strings, of form 2.1.3 (any
* number of version numbers). */
int *satPtr; /* If non-null, the word pointed to is
* filled in with a 0/1 value. 1 means
diff --git a/tcl/generic/tclPlatDecls.h b/tcl/generic/tclPlatDecls.h
index 2aff8ad1b9f..a7e5e174866 100644
--- a/tcl/generic/tclPlatDecls.h
+++ b/tcl/generic/tclPlatDecls.h
@@ -12,6 +12,22 @@
#ifndef _TCLPLATDECLS
#define _TCLPLATDECLS
+/*
+ * Pull in the typedef of TCHAR for windows.
+ */
+#if defined(__WIN32__) && !defined(_TCHAR_DEFINED)
+# include <tchar.h>
+# ifndef _TCHAR_DEFINED
+ /* Borland seems to forget to set this. */
+ typedef _TCHAR TCHAR;
+# define _TCHAR_DEFINED
+# endif
+# if defined(_MSC_VER) && defined(__STDC__)
+ /* MSVC++ misses this. */
+ typedef _TCHAR TCHAR;
+# endif
+#endif
+
/* !BEGIN!: Do not edit below this line. */
/*
@@ -35,12 +51,12 @@ EXTERN char * Tcl_MacConvertTextResource _ANSI_ARGS_((
Handle resource));
/* 2 */
EXTERN int Tcl_MacEvalResource _ANSI_ARGS_((Tcl_Interp * interp,
- char * resourceName, int resourceNumber,
- char * fileName));
+ CONST char * resourceName,
+ int resourceNumber, CONST char * fileName));
/* 3 */
EXTERN Handle Tcl_MacFindResource _ANSI_ARGS_((Tcl_Interp * interp,
- long resourceType, char * resourceName,
- int resourceNumber, char * resFileRef,
+ long resourceType, CONST char * resourceName,
+ int resourceNumber, CONST char * resFileRef,
int * releaseIt));
/* 4 */
EXTERN int Tcl_GetOSTypeFromObj _ANSI_ARGS_((
@@ -58,6 +74,13 @@ EXTERN int strncasecmp _ANSI_ARGS_((CONST char * s1,
EXTERN int strcasecmp _ANSI_ARGS_((CONST char * s1,
CONST char * s2));
#endif /* MAC_TCL */
+#ifdef MAC_OSX_TCL
+/* 0 */
+EXTERN int Tcl_MacOSXOpenBundleResources _ANSI_ARGS_((
+ Tcl_Interp * interp, CONST char * bundleName,
+ int hasResourceFile, int maxPathLen,
+ char * libraryPath));
+#endif /* MAC_OSX_TCL */
typedef struct TclPlatStubs {
int magic;
@@ -70,14 +93,17 @@ typedef struct TclPlatStubs {
#ifdef MAC_TCL
void (*tcl_MacSetEventProc) _ANSI_ARGS_((Tcl_MacConvertEventPtr procPtr)); /* 0 */
char * (*tcl_MacConvertTextResource) _ANSI_ARGS_((Handle resource)); /* 1 */
- int (*tcl_MacEvalResource) _ANSI_ARGS_((Tcl_Interp * interp, char * resourceName, int resourceNumber, char * fileName)); /* 2 */
- Handle (*tcl_MacFindResource) _ANSI_ARGS_((Tcl_Interp * interp, long resourceType, char * resourceName, int resourceNumber, char * resFileRef, int * releaseIt)); /* 3 */
+ int (*tcl_MacEvalResource) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * resourceName, int resourceNumber, CONST char * fileName)); /* 2 */
+ Handle (*tcl_MacFindResource) _ANSI_ARGS_((Tcl_Interp * interp, long resourceType, CONST char * resourceName, int resourceNumber, CONST char * resFileRef, int * releaseIt)); /* 3 */
int (*tcl_GetOSTypeFromObj) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * objPtr, OSType * osTypePtr)); /* 4 */
void (*tcl_SetOSTypeObj) _ANSI_ARGS_((Tcl_Obj * objPtr, OSType osType)); /* 5 */
Tcl_Obj * (*tcl_NewOSTypeObj) _ANSI_ARGS_((OSType osType)); /* 6 */
int (*strncasecmp) _ANSI_ARGS_((CONST char * s1, CONST char * s2, size_t n)); /* 7 */
int (*strcasecmp) _ANSI_ARGS_((CONST char * s1, CONST char * s2)); /* 8 */
#endif /* MAC_TCL */
+#ifdef MAC_OSX_TCL
+ int (*tcl_MacOSXOpenBundleResources) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * bundleName, int hasResourceFile, int maxPathLen, char * libraryPath)); /* 0 */
+#endif /* MAC_OSX_TCL */
} TclPlatStubs;
#ifdef __cplusplus
@@ -142,6 +168,12 @@ extern TclPlatStubs *tclPlatStubsPtr;
(tclPlatStubsPtr->strcasecmp) /* 8 */
#endif
#endif /* MAC_TCL */
+#ifdef MAC_OSX_TCL
+#ifndef Tcl_MacOSXOpenBundleResources
+#define Tcl_MacOSXOpenBundleResources \
+ (tclPlatStubsPtr->tcl_MacOSXOpenBundleResources) /* 0 */
+#endif
+#endif /* MAC_OSX_TCL */
#endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */
diff --git a/tcl/generic/tclPort.h b/tcl/generic/tclPort.h
index 4c719340b0e..3e9ea2ed34c 100644
--- a/tcl/generic/tclPort.h
+++ b/tcl/generic/tclPort.h
@@ -22,10 +22,22 @@
# include "../win/tclWinPort.h"
#else
# if defined(MAC_TCL)
-# include "tclMacPort.h"
-# else
-# include "../unix/tclUnixPort.h"
-# endif
+# include "tclMacPort.h"
+# else
+# include "../unix/tclUnixPort.h"
+# endif
#endif
+#if !defined(TCL_WIDE_INT_IS_LONG) && !defined(LLONG_MIN)
+# ifdef LLONG_BIT
+# define LLONG_MIN ((Tcl_WideInt)(Tcl_LongAsWide(1)<<(LLONG_BIT-1)))
+# else
+/* Assume we're on a system with a 64-bit 'long long' type */
+# define LLONG_MIN ((Tcl_WideInt)(Tcl_LongAsWide(1)<<63))
+# endif
+/* Assume that if LLONG_MIN is undefined, then so is LLONG_MAX */
+# define LLONG_MAX (~LLONG_MIN)
+#endif
+
+
#endif /* _TCLPORT */
diff --git a/tcl/generic/tclPosixStr.c b/tcl/generic/tclPosixStr.c
index 2055f19eb48..54ddcd74fe4 100644
--- a/tcl/generic/tclPosixStr.c
+++ b/tcl/generic/tclPosixStr.c
@@ -35,7 +35,7 @@
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_ErrnoId()
{
switch (errno) {
@@ -339,6 +339,9 @@ Tcl_ErrnoId()
#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP))
case EOPNOTSUPP: return "EOPNOTSUPP";
#endif
+#if defined(EOVERFLOW) && ( !defined(EFBIG) || (EOVERFLOW != EFBIG) ) && ( !defined(EINVAL) || (EOVERFLOW != EINVAL) )
+ case EOVERFLOW: return "EOVERFLOW";
+#endif
#ifdef EPERM
case EPERM: return "EPERM";
#endif
@@ -480,7 +483,7 @@ Tcl_ErrnoId()
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_ErrnoMsg(err)
int err; /* Error number (such as in errno variable). */
{
@@ -786,6 +789,9 @@ Tcl_ErrnoMsg(err)
#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP))
case EOPNOTSUPP: return "operation not supported on socket";
#endif
+#if defined(EOVERFLOW) && ( !defined(EFBIG) || (EOVERFLOW != EFBIG) ) && ( !defined(EINVAL) || (EOVERFLOW != EINVAL) )
+ case EOVERFLOW: return "file too big";
+#endif
#ifdef EPERM
case EPERM: return "not owner";
#endif
@@ -927,7 +933,7 @@ Tcl_ErrnoMsg(err)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_SignalId(sig)
int sig; /* Number of signal. */
{
@@ -1059,7 +1065,7 @@ Tcl_SignalId(sig)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_SignalMsg(sig)
int sig; /* Number of signal. */
{
@@ -1172,4 +1178,3 @@ Tcl_SignalMsg(sig)
}
return "unknown signal";
}
-
diff --git a/tcl/generic/tclProc.c b/tcl/generic/tclProc.c
index f9d19696ebe..cf5438f690d 100644
--- a/tcl/generic/tclProc.c
+++ b/tcl/generic/tclProc.c
@@ -27,6 +27,8 @@ static int ProcBodySetFromAny _ANSI_ARGS_((Tcl_Interp *interp,
static void ProcBodyUpdateString _ANSI_ARGS_((Tcl_Obj *objPtr));
static int ProcessProcResultCode _ANSI_ARGS_((Tcl_Interp *interp,
char *procName, int nameLen, int returnCode));
+static int TclCompileNoOp _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
/*
* The ProcBodyObjType type
@@ -67,7 +69,8 @@ Tcl_ProcObjCmd(dummy, interp, objc, objv)
{
register Interp *iPtr = (Interp *) interp;
Proc *procPtr;
- char *fullName, *procName;
+ char *fullName;
+ CONST char *procName, *procArgs, *procBody;
Namespace *nsPtr, *altNsPtr, *cxtNsPtr;
Tcl_Command cmd;
Tcl_DString ds;
@@ -145,6 +148,57 @@ Tcl_ProcObjCmd(dummy, interp, objc, objv)
procPtr->cmdPtr = (Command *) cmd;
+
+ /*
+ * Optimize for noop procs: if the argument list is just "args"
+ * and the body is empty, define a compileProc.
+ *
+ * Notes:
+ * - cannot be done for any argument list without having different
+ * compiled/not-compiled behaviour in the "wrong argument #" case,
+ * or making this code much more complicated. In any case, it doesn't
+ * seem to make a lot of sense to verify the number of arguments we
+ * are about to ignore ...
+ * - could be enhanced to handle also non-empty bodies that contain
+ * only comments; however, parsing the body will slow down the
+ * compilation of all procs whose argument list is just _args_
+ */
+
+ procArgs = Tcl_GetString(objv[2]);
+
+ while(*procArgs == ' ') {
+ procArgs++;
+ }
+
+ if ((procArgs[0] == 'a') && (strncmp(procArgs, "args", 4) == 0)) {
+ procArgs +=4;
+ while(*procArgs != '\0') {
+ if (*procArgs != ' ') {
+ goto done;
+ }
+ procArgs++;
+ }
+
+ /*
+ * The argument list is just "args"; check the body
+ */
+
+ procBody = Tcl_GetString(objv[3]);
+ while(*procBody != '\0') {
+ if (!isspace(UCHAR(*procBody))) {
+ goto done;
+ }
+ procBody++;
+ }
+
+ /*
+ * The body is just spaces: link the compileProc
+ */
+
+ ((Command *) cmd)->compileProc = TclCompileNoOp;
+ }
+
+ done:
return TCL_OK;
}
@@ -175,17 +229,17 @@ int
TclCreateProc(interp, nsPtr, procName, argsPtr, bodyPtr, procPtrPtr)
Tcl_Interp *interp; /* interpreter containing proc */
Namespace *nsPtr; /* namespace containing this proc */
- char *procName; /* unqualified name of this proc */
+ CONST char *procName; /* unqualified name of this proc */
Tcl_Obj *argsPtr; /* description of arguments */
Tcl_Obj *bodyPtr; /* command body */
Proc **procPtrPtr; /* returns: pointer to proc data */
{
Interp *iPtr = (Interp*)interp;
- char **argArray = NULL;
+ CONST char **argArray = NULL;
register Proc *procPtr;
int i, length, result, numArgs;
- char *args, *bytes, *p;
+ CONST char *args, *bytes, *p;
register CompiledLocal *localPtr = NULL;
Tcl_Obj *defPtr;
int precompiled = 0;
@@ -281,7 +335,7 @@ TclCreateProc(interp, nsPtr, procName, argsPtr, bodyPtr, procPtrPtr)
}
for (i = 0; i < numArgs; i++) {
int fieldCount, nameLength, valueLength;
- char **fieldValues;
+ CONST char **fieldValues;
/*
* Now divide the specifier up into name and default.
@@ -321,7 +375,7 @@ TclCreateProc(interp, nsPtr, procName, argsPtr, bodyPtr, procPtrPtr)
p = fieldValues[0];
while (*p != '\0') {
if (*p == '(') {
- char *q = p;
+ CONST char *q = p;
do {
q++;
} while (*q != '\0');
@@ -335,6 +389,14 @@ TclCreateProc(interp, nsPtr, procName, argsPtr, bodyPtr, procPtrPtr)
ckfree((char *) fieldValues);
goto procError;
}
+ } else if ((*p == ':') && (*(p+1) == ':')) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "procedure \"", procName,
+ "\" has formal parameter \"", fieldValues[0],
+ "\" that is not a simple name",
+ (char *) NULL);
+ ckfree((char *) fieldValues);
+ goto procError;
}
p++;
}
@@ -415,6 +477,7 @@ TclCreateProc(interp, nsPtr, procName, argsPtr, bodyPtr, procPtrPtr)
}
strcpy(localPtr->name, fieldValues[0]);
}
+
ckfree((char *) fieldValues);
}
@@ -481,7 +544,7 @@ procError:
int
TclGetFrame(interp, string, framePtrPtr)
Tcl_Interp *interp; /* Interpreter in which to find frame. */
- char *string; /* String describing frame. */
+ CONST char *string; /* String describing frame. */
CallFrame **framePtrPtr; /* Store pointer to frame here (or NULL
* if global frame indicated). */
{
@@ -653,7 +716,7 @@ Tcl_UplevelObjCmd(dummy, interp, objc, objv)
Proc *
TclFindProc(iPtr, procName)
Interp *iPtr; /* Interpreter in which to look. */
- char *procName; /* Name of desired procedure. */
+ CONST char *procName; /* Name of desired procedure. */
{
Tcl_Command cmd;
Tcl_Command origCmd;
@@ -735,7 +798,7 @@ TclProcInterpProc(clientData, interp, argc, argv)
* invoked. */
int argc; /* Count of number of arguments to this
* procedure. */
- register char **argv; /* Argument values. */
+ register CONST char **argv; /* Argument values. */
{
register Tcl_Obj *objPtr;
register int i;
@@ -839,6 +902,7 @@ TclObjInterpProc(clientData, interp, objc, objv)
register CompiledLocal *localPtr;
char *procName;
int nameLen, localCt, numArgs, argCt, i, result;
+ Tcl_Obj *objResult = Tcl_GetObjResult(interp);
/*
* This procedure generates an array "compiledLocals" that holds the
@@ -943,36 +1007,48 @@ TclObjInterpProc(clientData, interp, objc, objv)
Tcl_Obj *listPtr = Tcl_NewListObj(argCt, &(objv[i]));
varPtr->value.objPtr = listPtr;
Tcl_IncrRefCount(listPtr); /* local var is a reference */
- varPtr->flags &= ~VAR_UNDEFINED;
+ TclClearVarUndefined(varPtr);
argCt = 0;
break; /* done processing args */
} else if (argCt > 0) {
Tcl_Obj *objPtr = objv[i];
varPtr->value.objPtr = objPtr;
- varPtr->flags &= ~VAR_UNDEFINED;
+ TclClearVarUndefined(varPtr);
Tcl_IncrRefCount(objPtr); /* since the local variable now has
* another reference to object. */
} else if (localPtr->defValuePtr != NULL) {
Tcl_Obj *objPtr = localPtr->defValuePtr;
varPtr->value.objPtr = objPtr;
- varPtr->flags &= ~VAR_UNDEFINED;
+ TclClearVarUndefined(varPtr);
Tcl_IncrRefCount(objPtr); /* since the local variable now has
* another reference to object. */
} else {
- Tcl_ResetResult(interp);
- Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "no value given for parameter \"", localPtr->name,
- "\" to \"", Tcl_GetString(objv[0]), "\"", (char *) NULL);
- result = TCL_ERROR;
- goto procDone;
+ goto incorrectArgs;
}
varPtr++;
localPtr = localPtr->nextPtr;
}
if (argCt > 0) {
- Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "called \"", Tcl_GetString(objv[0]),
- "\" with too many arguments", (char *) NULL);
+ incorrectArgs:
+ /*
+ * Build up equivalent to Tcl_WrongNumArgs message for proc
+ */
+ Tcl_ResetResult(interp);
+ Tcl_AppendStringsToObj(objResult,
+ "wrong # args: should be \"", procName, (char *) NULL);
+ localPtr = procPtr->firstLocalPtr;
+ for (i = 1; i <= numArgs; i++) {
+ if (localPtr->defValuePtr != NULL) {
+ Tcl_AppendStringsToObj(objResult,
+ " ?", localPtr->name, "?", (char *) NULL);
+ } else {
+ Tcl_AppendStringsToObj(objResult,
+ " ", localPtr->name, (char *) NULL);
+ }
+ localPtr = localPtr->nextPtr;
+ }
+ Tcl_AppendStringsToObj(objResult, "\"", (char *) NULL);
+
result = TCL_ERROR;
goto procDone;
}
@@ -981,23 +1057,21 @@ TclObjInterpProc(clientData, interp, objc, objv)
* Invoke the commands in the procedure's body.
*/
- if (tclTraceExec >= 1) {
#ifdef TCL_COMPILE_DEBUG
+ if (tclTraceExec >= 1) {
fprintf(stdout, "Calling proc ");
for (i = 0; i < objc; i++) {
TclPrintObject(stdout, objv[i], 15);
fprintf(stdout, " ");
}
fprintf(stdout, "\n");
-#else /* TCL_COMPILE_DEBUG */
- fprintf(stdout, "Calling proc %.*s\n", nameLen, procName);
-#endif /*TCL_COMPILE_DEBUG*/
fflush(stdout);
}
+#endif /*TCL_COMPILE_DEBUG*/
iPtr->returnCode = TCL_OK;
procPtr->refCount++;
- result = Tcl_EvalObjEx(interp, procPtr->bodyPtr, 0);
+ result = TclCompEvalObj(interp, procPtr->bodyPtr);
procPtr->refCount--;
if (procPtr->refCount <= 0) {
TclProcCleanupProc(procPtr);
@@ -1095,6 +1169,7 @@ TclProcCompileProc(interp, procPtr, bodyPtr, nsPtr, description, procName)
int numChars;
char *ellipsis;
+#ifdef TCL_COMPILE_DEBUG
if (tclTraceCompile >= 1) {
/*
* Display a line summarizing the top level command we
@@ -1110,6 +1185,7 @@ TclProcCompileProc(interp, procPtr, bodyPtr, nsPtr, description, procName)
fprintf(stdout, "Compiling %s \"%.*s%s\"\n",
description, numChars, procName, ellipsis);
}
+#endif
/*
* Plug the current procPtr into the interpreter and coerce
@@ -1207,33 +1283,32 @@ ProcessProcResultCode(interp, procName, nameLen, returnCode)
int returnCode; /* The unexpected result code. */
{
Interp *iPtr = (Interp *) interp;
+ char msg[100 + TCL_INTEGER_SPACE];
+ char *ellipsis = "";
+ if (returnCode == TCL_OK) {
+ return TCL_OK;
+ }
+ if (returnCode > TCL_CONTINUE) {
+ return returnCode;
+ }
if (returnCode == TCL_RETURN) {
- returnCode = TclUpdateReturnInfo(iPtr);
- } else if (returnCode == TCL_ERROR) {
- char msg[100 + TCL_INTEGER_SPACE];
- char *ellipsis = "";
- int numChars = nameLen;
-
- if (numChars > 60) {
- numChars = 60;
- ellipsis = "...";
- }
- sprintf(msg, "\n (procedure \"%.*s%s\" line %d)",
- numChars, procName, ellipsis, iPtr->errorLine);
- Tcl_AddObjErrorInfo(interp, msg, -1);
- } else if (returnCode == TCL_BREAK) {
- Tcl_ResetResult(interp);
- Tcl_AppendToObj(Tcl_GetObjResult(interp),
- "invoked \"break\" outside of a loop", -1);
- returnCode = TCL_ERROR;
- } else if (returnCode == TCL_CONTINUE) {
+ return TclUpdateReturnInfo(iPtr);
+ }
+ if (returnCode != TCL_ERROR) {
Tcl_ResetResult(interp);
- Tcl_AppendToObj(Tcl_GetObjResult(interp),
- "invoked \"continue\" outside of a loop", -1);
- returnCode = TCL_ERROR;
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), ((returnCode == TCL_BREAK)
+ ? "invoked \"break\" outside of a loop"
+ : "invoked \"continue\" outside of a loop"), -1);
}
- return returnCode;
+ if (nameLen > 60) {
+ nameLen = 60;
+ ellipsis = "...";
+ }
+ sprintf(msg, "\n (procedure \"%.*s%s\" line %d)", nameLen, procName,
+ ellipsis, iPtr->errorLine);
+ Tcl_AddObjErrorInfo(interp, msg, -1);
+ return TCL_ERROR;
}
/*
@@ -1346,17 +1421,20 @@ TclUpdateReturnInfo(iPtr)
* exception is being processed. */
{
int code;
+ char *errorCode;
code = iPtr->returnCode;
iPtr->returnCode = TCL_OK;
if (code == TCL_ERROR) {
- Tcl_SetVar2((Tcl_Interp *) iPtr, "errorCode", (char *) NULL,
- (iPtr->errorCode != NULL) ? iPtr->errorCode : "NONE",
+ errorCode = ((iPtr->errorCode != NULL) ? iPtr->errorCode : "NONE");
+ Tcl_ObjSetVar2((Tcl_Interp *) iPtr, iPtr->execEnvPtr->errorCode,
+ NULL, Tcl_NewStringObj(errorCode, -1),
TCL_GLOBAL_ONLY);
iPtr->flags |= ERROR_CODE_SET;
if (iPtr->errorInfo != NULL) {
- Tcl_SetVar2((Tcl_Interp *) iPtr, "errorInfo", (char *) NULL,
- iPtr->errorInfo, TCL_GLOBAL_ONLY);
+ Tcl_ObjSetVar2((Tcl_Interp *) iPtr, iPtr->execEnvPtr->errorInfo,
+ NULL, Tcl_NewStringObj(iPtr->errorInfo, -1),
+ TCL_GLOBAL_ONLY);
iPtr->flags |= ERR_IN_PROGRESS;
}
}
@@ -1568,3 +1646,53 @@ ProcBodyUpdateString(objPtr)
{
panic("called ProcBodyUpdateString");
}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileNoOp --
+ *
+ * Procedure called to compile noOp's
+ *
+ * Results:
+ * The return value is TCL_OK, indicating successful compilation.
+ *
+ * Side effects:
+ * Instructions are added to envPtr to execute a noOp at runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TclCompileNoOp(interp, parsePtr, envPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Parse *parsePtr; /* Points to a parse structure for the
+ * command created by Tcl_ParseCommand. */
+ CompileEnv *envPtr; /* Holds resulting instructions. */
+{
+ Tcl_Token *tokenPtr;
+ int i, code;
+ int savedStackDepth = envPtr->currStackDepth;
+
+ tokenPtr = parsePtr->tokenPtr;
+ for(i = 1; i < parsePtr->numWords; i++) {
+ tokenPtr = tokenPtr + tokenPtr->numComponents + 1;
+ envPtr->currStackDepth = savedStackDepth;
+
+ if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+ code = TclCompileTokens(interp, tokenPtr+1,
+ tokenPtr->numComponents, envPtr);
+ if (code != TCL_OK) {
+ return code;
+ }
+ TclEmitOpcode(INST_POP, envPtr);
+ }
+ }
+ envPtr->currStackDepth = savedStackDepth;
+ TclEmitPush(TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0), envPtr);
+ return TCL_OK;
+}
+
+
+
diff --git a/tcl/generic/tclRegexp.c b/tcl/generic/tclRegexp.c
index 47254712ced..6fc4d0484c0 100644
--- a/tcl/generic/tclRegexp.c
+++ b/tcl/generic/tclRegexp.c
@@ -88,7 +88,7 @@ static Tcl_ThreadDataKey dataKey;
*/
static TclRegexp * CompileRegexp _ANSI_ARGS_((Tcl_Interp *interp,
- char *pattern, int length, int flags));
+ CONST char *pattern, int length, int flags));
static void DupRegexpInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
Tcl_Obj *copyPtr));
static void FinalizeRegexp _ANSI_ARGS_((ClientData clientData));
@@ -141,7 +141,7 @@ Tcl_RegExp
Tcl_RegExpCompile(interp, string)
Tcl_Interp *interp; /* For use in error reporting and
* to access the interp regexp cache. */
- char *string; /* String for which to produce
+ CONST char *string; /* String for which to produce
* compiled regular expression. */
{
return (Tcl_RegExp) CompileRegexp(interp, string, (int) strlen(string),
@@ -183,7 +183,7 @@ Tcl_RegExpExec(interp, re, string, start)
int flags, result, numChars;
TclRegexp *regexp = (TclRegexp *)re;
Tcl_DString ds;
- Tcl_UniChar *ustr;
+ CONST Tcl_UniChar *ustr;
/*
* If the starting point is offset from the beginning of the buffer,
@@ -243,9 +243,9 @@ Tcl_RegExpRange(re, index, startPtr, endPtr)
int index; /* 0 means give the range of the entire
* match, > 0 means give the range of
* a matching subrange. */
- char **startPtr; /* Store address of first character in
+ CONST char **startPtr; /* Store address of first character in
* (sub-) range here. */
- char **endPtr; /* Store address of character just after last
+ CONST char **endPtr; /* Store address of character just after last
* in (sub-) range here. */
{
TclRegexp *regexpPtr = (TclRegexp *) re;
@@ -398,8 +398,8 @@ TclRegExpRangeUniChar(re, index, startPtr, endPtr)
int
Tcl_RegExpMatch(interp, string, pattern)
Tcl_Interp *interp; /* Used for error reporting. May be NULL. */
- char *string; /* String. */
- char *pattern; /* Regular expression to match against
+ CONST char *string; /* String. */
+ CONST char *pattern; /* Regular expression to match against
* string. */
{
Tcl_RegExp re;
@@ -455,8 +455,7 @@ Tcl_RegExpExecObj(interp, re, objPtr, offset, nmatches, flags)
regexpPtr->string = NULL;
regexpPtr->objPtr = objPtr;
- udata = Tcl_GetUnicode(objPtr);
- length = Tcl_GetCharLength(objPtr);
+ udata = Tcl_GetUnicodeFromObj(objPtr, &length);
if (offset > length) {
offset = length;
@@ -697,7 +696,7 @@ TclRegAbout(interp, re)
void
TclRegError(interp, msg, status)
Tcl_Interp *interp; /* Interpreter for error reporting. */
- char *msg; /* Message to prepend to error. */
+ CONST char *msg; /* Message to prepend to error. */
int status; /* Status code to report. */
{
char buf[100]; /* ample in practice */
@@ -832,12 +831,12 @@ SetRegexpFromAny(interp, objPtr)
static TclRegexp *
CompileRegexp(interp, string, length, flags)
Tcl_Interp *interp; /* Used for error reporting if not NULL. */
- char *string; /* The regexp to compile (UTF-8). */
+ CONST char *string; /* The regexp to compile (UTF-8). */
int length; /* The length of the string in bytes. */
int flags; /* Compilation flags. */
{
TclRegexp *regexpPtr;
- Tcl_UniChar *uniString;
+ CONST Tcl_UniChar *uniString;
int numChars;
Tcl_DString stringBuf;
int status, i;
diff --git a/tcl/generic/tclResolve.c b/tcl/generic/tclResolve.c
index 7fea4acffea..c2235475507 100644
--- a/tcl/generic/tclResolve.c
+++ b/tcl/generic/tclResolve.c
@@ -63,7 +63,7 @@ Tcl_AddInterpResolvers(interp, name, cmdProc, varProc, compiledVarProc)
Tcl_Interp *interp; /* Interpreter whose name resolution
* rules are being modified. */
- char *name; /* Name of this resolution scheme. */
+ CONST char *name; /* Name of this resolution scheme. */
Tcl_ResolveCmdProc *cmdProc; /* New procedure for command
* resolution */
Tcl_ResolveVarProc *varProc; /* Procedure for variable resolution
@@ -142,7 +142,7 @@ Tcl_GetInterpResolvers(interp, name, resInfoPtr)
Tcl_Interp *interp; /* Interpreter whose name resolution
* rules are being queried. */
- char *name; /* Look for a scheme with this name. */
+ CONST char *name; /* Look for a scheme with this name. */
Tcl_ResolverInfo *resInfoPtr; /* Returns pointers to the procedures,
* if found */
{
@@ -194,7 +194,7 @@ Tcl_RemoveInterpResolvers(interp, name)
Tcl_Interp *interp; /* Interpreter whose name resolution
* rules are being modified. */
- char *name; /* Name of the scheme to be removed. */
+ CONST char *name; /* Name of the scheme to be removed. */
{
Interp *iPtr = (Interp*)interp;
ResolverScheme **prevPtrPtr, *resPtr;
@@ -291,7 +291,7 @@ BumpCmdRefEpochs(nsPtr)
* type:
*
* typedef int (Tcl_ResolveCmdProc) _ANSI_ARGS_((
- * Tcl_Interp* interp, char* name, Tcl_Namespace *context,
+ * Tcl_Interp* interp, CONST char* name, Tcl_Namespace *context,
* int flags, Tcl_Command *rPtr));
*
* Whenever a command is executed or Tcl_FindCommand is invoked
@@ -308,7 +308,7 @@ BumpCmdRefEpochs(nsPtr)
* time:
*
* typedef int (Tcl_ResolveCompiledVarProc) _ANSI_ARGS_((
- * Tcl_Interp* interp, char* name, Tcl_Namespace *context,
+ * Tcl_Interp* interp, CONST char* name, Tcl_Namespace *context,
* Tcl_ResolvedVarInfo *rPtr));
*
* If this procedure is able to resolve the name, it should return
@@ -325,7 +325,7 @@ BumpCmdRefEpochs(nsPtr)
* Tcl_FindNamespaceVar.) This procedure has the following type:
*
* typedef int (Tcl_ResolveVarProc) _ANSI_ARGS_((
- * Tcl_Interp* interp, char* name, Tcl_Namespace *context,
+ * Tcl_Interp* interp, CONST char* name, Tcl_Namespace *context,
* int flags, Tcl_Var *rPtr));
*
* This procedure is quite similar to the compile-time version.
diff --git a/tcl/generic/tclResult.c b/tcl/generic/tclResult.c
index 2b537b73e7a..15e07558b8b 100644
--- a/tcl/generic/tclResult.c
+++ b/tcl/generic/tclResult.c
@@ -297,7 +297,7 @@ Tcl_SetResult(interp, string, freeProc)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_GetStringResult(interp)
register Tcl_Interp *interp; /* Interpreter whose result to return. */
{
diff --git a/tcl/generic/tclScan.c b/tcl/generic/tclScan.c
index c5d4784dfe3..7d4e560e59c 100644
--- a/tcl/generic/tclScan.c
+++ b/tcl/generic/tclScan.c
@@ -12,6 +12,10 @@
*/
#include "tclInt.h"
+/*
+ * For strtoll() and strtoull() declarations on some platforms...
+ */
+#include "tclPort.h"
/*
* Flag values used by Tcl_ScanObjCmd.
@@ -29,6 +33,7 @@
#define SCAN_PTOK 0x100 /* Decimal point is allowed. */
#define SCAN_EXPOK 0x200 /* An exponent is allowed. */
+#define SCAN_LONGER 0x400 /* Asked for a wide value. */
/*
* The following structure contains the information associated with
@@ -270,6 +275,7 @@ ValidateFormat(interp, format, numVars, totalSubs)
int staticAssign[STATIC_LIST_SIZE];
int *nassign = staticAssign;
int objIndex, xpgSize, nspace = STATIC_LIST_SIZE;
+ char buf[TCL_UTF_MAX+1];
/*
* Initialize an array that records the number of times a variable
@@ -359,10 +365,16 @@ ValidateFormat(interp, format, numVars, totalSubs)
}
/*
- * Ignore size specifier.
+ * Handle any size specifier.
*/
- if ((ch == 'l') || (ch == 'L') || (ch == 'h')) {
+ switch (ch) {
+ case 'l':
+ case 'L':
+#ifndef TCL_WIDE_INT_IS_LONG
+ flags |= SCAN_LONGER;
+#endif
+ case 'h':
format += Tcl_UtfToUniChar(format, &ch);
}
@@ -375,24 +387,45 @@ ValidateFormat(interp, format, numVars, totalSubs)
*/
switch (ch) {
+ case 'c':
+ if (flags & SCAN_WIDTH) {
+ Tcl_SetResult(interp,
+ "field width may not be specified in %c conversion",
+ TCL_STATIC);
+ goto error;
+ }
+ /*
+ * Fall through!
+ */
case 'n':
+ case 's':
+ if (flags & SCAN_LONGER) {
+ invalidLonger:
+ buf[Tcl_UniCharToUtf(ch, buf)] = '\0';
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "'l' modifier may not be specified in %", buf,
+ " conversion", NULL);
+ goto error;
+ }
+ /*
+ * Fall through!
+ */
case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
case 'i':
case 'o':
- case 'x':
case 'u':
- case 'f':
- case 'e':
- case 'g':
- case 's':
- break;
- case 'c':
- if (flags & SCAN_WIDTH) {
- Tcl_SetResult(interp, "field width may not be specified in %c conversion", TCL_STATIC);
- goto error;
- }
- break;
+ case 'x':
+ break;
+ /*
+ * Bracket terms need special checking
+ */
case '[':
+ if (flags & SCAN_LONGER) {
+ goto invalidLonger;
+ }
if (*format == '\0') {
goto badSet;
}
@@ -539,13 +572,18 @@ Tcl_ScanObjCmd(dummy, interp, objc, objv)
{
char *format;
int numVars, nconversions, totalVars = -1;
- int objIndex, offset, i, value, result, code;
+ int objIndex, offset, i, result, code;
+ long value;
char *string, *end, *baseString;
char op = 0;
int base = 0;
int underflow = 0;
size_t width;
long (*fn)() = NULL;
+#ifndef TCL_WIDE_INT_IS_LONG
+ Tcl_WideInt (*lfn)() = NULL;
+ Tcl_WideInt wideValue;
+#endif
Tcl_UniChar ch, sch;
Tcl_Obj **objs = NULL, *objPtr = NULL;
int flags;
@@ -644,7 +682,7 @@ Tcl_ScanObjCmd(dummy, interp, objc, objv)
if (*end == '$') {
format = end+1;
format += Tcl_UtfToUniChar(format, &ch);
- objIndex = value - 1;
+ objIndex = (int) value - 1;
}
}
@@ -660,10 +698,19 @@ Tcl_ScanObjCmd(dummy, interp, objc, objv)
}
/*
- * Ignore size specifier.
+ * Handle any size specifier.
*/
- if ((ch == 'l') || (ch == 'L') || (ch == 'h')) {
+ switch (ch) {
+ case 'l':
+ case 'L':
+#ifndef TCL_WIDE_INT_IS_LONG
+ flags |= SCAN_LONGER;
+#endif
+ /*
+ * Fall through so we skip to the next character.
+ */
+ case 'h':
format += Tcl_UtfToUniChar(format, &ch);
}
@@ -685,27 +732,42 @@ Tcl_ScanObjCmd(dummy, interp, objc, objv)
op = 'i';
base = 10;
fn = (long (*)())strtol;
+#ifndef TCL_WIDE_INT_IS_LONG
+ lfn = (Tcl_WideInt (*)())strtoll;
+#endif
break;
case 'i':
op = 'i';
base = 0;
fn = (long (*)())strtol;
+#ifndef TCL_WIDE_INT_IS_LONG
+ lfn = (Tcl_WideInt (*)())strtoll;
+#endif
break;
case 'o':
op = 'i';
base = 8;
- fn = (long (*)())strtol;
+ fn = (long (*)())strtoul;
+#ifndef TCL_WIDE_INT_IS_LONG
+ lfn = (Tcl_WideInt (*)())strtoull;
+#endif
break;
case 'x':
op = 'i';
base = 16;
- fn = (long (*)())strtol;
+ fn = (long (*)())strtoul;
+#ifndef TCL_WIDE_INT_IS_LONG
+ lfn = (Tcl_WideInt (*)())strtoull;
+#endif
break;
case 'u':
op = 'i';
base = 10;
flags |= SCAN_UNSIGNED;
fn = (long (*)())strtoul;
+#ifndef TCL_WIDE_INT_IS_LONG
+ lfn = (Tcl_WideInt (*)())strtoull;
+#endif
break;
case 'f':
@@ -854,12 +916,19 @@ Tcl_ScanObjCmd(dummy, interp, objc, objv)
* a number. If we are unsure of the base, it
* indicates that we are in base 8 or base 16 (if it is
* followed by an 'x').
+ *
+ * 8.1 - 8.3.4 incorrectly handled 0x... base-16
+ * cases for %x by not reading the 0x as the
+ * auto-prelude for base-16. [Bug #495213]
*/
case '0':
if (base == 0) {
base = 8;
flags |= SCAN_XOK;
}
+ if (base == 16) {
+ flags |= SCAN_XOK;
+ }
if (flags & SCAN_NOZERO) {
flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS
| SCAN_NOZERO);
@@ -954,13 +1023,33 @@ Tcl_ScanObjCmd(dummy, interp, objc, objv)
if (!(flags & SCAN_SUPPRESS)) {
*end = '\0';
- value = (int) (*fn)(buf, NULL, base);
- if ((flags & SCAN_UNSIGNED) && (value < 0)) {
- sprintf(buf, "%u", value); /* INTL: ISO digit */
- objPtr = Tcl_NewStringObj(buf, -1);
+#ifndef TCL_WIDE_INT_IS_LONG
+ if (flags & SCAN_LONGER) {
+ wideValue = (Tcl_WideInt) (*lfn)(buf, NULL, base);
+ if ((flags & SCAN_UNSIGNED) && (wideValue < 0)) {
+ /* INTL: ISO digit */
+ sprintf(buf, "%" TCL_LL_MODIFIER "u",
+ (Tcl_WideUInt)wideValue);
+ objPtr = Tcl_NewStringObj(buf, -1);
+ } else {
+ objPtr = Tcl_NewWideIntObj(wideValue);
+ }
} else {
- objPtr = Tcl_NewIntObj(value);
+#endif /* !TCL_WIDE_INT_IS_LONG */
+ value = (long) (*fn)(buf, NULL, base);
+ if ((flags & SCAN_UNSIGNED) && (value < 0)) {
+ sprintf(buf, "%lu", value); /* INTL: ISO digit */
+ objPtr = Tcl_NewStringObj(buf, -1);
+ } else {
+ if ((unsigned long) value > UINT_MAX) {
+ objPtr = Tcl_NewLongObj(value);
+ } else {
+ objPtr = Tcl_NewIntObj(value);
+ }
+ }
+#ifndef TCL_WIDE_INT_IS_LONG
}
+#endif
Tcl_IncrRefCount(objPtr);
objs[objIndex++] = objPtr;
}
@@ -975,6 +1064,7 @@ Tcl_ScanObjCmd(dummy, interp, objc, objv)
if ((width == 0) || (width > sizeof(buf) - 1)) {
width = sizeof(buf) - 1;
}
+ flags &= ~SCAN_LONGER;
flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_PTOK | SCAN_EXPOK;
for (end = buf; width > 0; width--) {
switch (*string) {
@@ -1112,7 +1202,9 @@ Tcl_ScanObjCmd(dummy, interp, objc, objv)
}
}
}
- ckfree((char*) objs);
+ if (objs != NULL) {
+ ckfree((char*) objs);
+ }
if (code == TCL_OK) {
if (underflow && (nconversions == 0)) {
if (numVars) {
diff --git a/tcl/generic/tclStringObj.c b/tcl/generic/tclStringObj.c
index 7c435b508f6..c532e01afcd 100644
--- a/tcl/generic/tclStringObj.c
+++ b/tcl/generic/tclStringObj.c
@@ -33,8 +33,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id$
- */
+ * RCS: @(#) $Id$ */
#include "tclInt.h"
@@ -43,15 +42,15 @@
*/
static void AppendUnicodeToUnicodeRep _ANSI_ARGS_((
- Tcl_Obj *objPtr, Tcl_UniChar *unicode,
+ Tcl_Obj *objPtr, CONST Tcl_UniChar *unicode,
int appendNumChars));
static void AppendUnicodeToUtfRep _ANSI_ARGS_((
- Tcl_Obj *objPtr, Tcl_UniChar *unicode,
+ Tcl_Obj *objPtr, CONST Tcl_UniChar *unicode,
int numChars));
static void AppendUtfToUnicodeRep _ANSI_ARGS_((Tcl_Obj *objPtr,
- char *bytes, int numBytes));
+ CONST char *bytes, int numBytes));
static void AppendUtfToUtfRep _ANSI_ARGS_((Tcl_Obj *objPtr,
- char *bytes, int numBytes));
+ CONST char *bytes, int numBytes));
static void FillUnicodeRep _ANSI_ARGS_((Tcl_Obj *objPtr));
@@ -109,6 +108,44 @@ typedef struct String {
#define SET_STRING(objPtr, stringPtr) \
(objPtr)->internalRep.otherValuePtr = (VOID *) (stringPtr)
+/*
+ * TCL STRING GROWTH ALGORITHM
+ *
+ * When growing strings (during an append, for example), the following growth
+ * algorithm is used:
+ *
+ * Attempt to allocate 2 * (originalLength + appendLength)
+ * On failure:
+ * attempt to allocate originalLength + 2*appendLength +
+ * TCL_GROWTH_MIN_ALLOC
+ *
+ * This algorithm allows very good performance, as it rapidly increases the
+ * memory allocated for a given string, which minimizes the number of
+ * reallocations that must be performed. However, using only the doubling
+ * algorithm can lead to a significant waste of memory. In particular, it
+ * may fail even when there is sufficient memory available to complete the
+ * append request (but there is not 2 * totalLength memory available). So when
+ * the doubling fails (because there is not enough memory available), the
+ * algorithm requests a smaller amount of memory, which is still enough to
+ * cover the request, but which hopefully will be less than the total available
+ * memory.
+ *
+ * The addition of TCL_GROWTH_MIN_ALLOC allows for efficient handling
+ * of very small appends. Without this extra slush factor, a sequence
+ * of several small appends would cause several memory allocations.
+ * As long as TCL_GROWTH_MIN_ALLOC is a reasonable size, we can
+ * avoid that behavior.
+ *
+ * The growth algorithm can be tuned by adjusting the following parameters:
+ *
+ * TCL_GROWTH_MIN_ALLOC Additional space, in bytes, to allocate when
+ * the double allocation has failed.
+ * Default is 1024 (1 kilobyte).
+ */
+#ifndef TCL_GROWTH_MIN_ALLOC
+#define TCL_GROWTH_MIN_ALLOC 1024
+#endif
+
/*
*----------------------------------------------------------------------
@@ -182,9 +219,9 @@ Tcl_NewStringObj(bytes, length)
* TCL_MEM_DEBUG is defined. It creates new string objects. It is the
* same as the Tcl_NewStringObj procedure above except that it calls
* Tcl_DbCkalloc directly with the file name and line number from its
- * caller. This simplifies debugging since then the checkmem command
- * will report the correct file name and line number when reporting
- * objects that haven't been freed.
+ * caller. This simplifies debugging since then the [memory active]
+ * command will report the correct file name and line number when
+ * reporting objects that haven't been freed.
*
* When TCL_MEM_DEBUG is not defined, this procedure just returns the
* result of calling Tcl_NewStringObj.
@@ -213,7 +250,7 @@ Tcl_DbNewStringObj(bytes, length, file, line)
* when initializing the new object. If
* negative, use bytes up to the first
* NULL byte. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -238,7 +275,7 @@ Tcl_DbNewStringObj(bytes, length, file, line)
* when initializing the new object. If
* negative, use bytes up to the first
* NULL byte. */
- char *file; /* The name of the source file calling this
+ CONST char *file; /* The name of the source file calling this
* procedure; used for debugging. */
int line; /* Line number in the source file; used
* for debugging. */
@@ -250,10 +287,10 @@ Tcl_DbNewStringObj(bytes, length, file, line)
/*
*---------------------------------------------------------------------------
*
- * TclNewUnicodeObj --
+ * Tcl_NewUnicodeObj --
*
* This procedure is creates a new String object and initializes
- * it from the given Utf String. If the Utf String is the same size
+ * it from the given Unicode String. If the Utf String is the same size
* as the Unicode string, don't duplicate the data.
*
* Results:
@@ -269,7 +306,7 @@ Tcl_DbNewStringObj(bytes, length, file, line)
Tcl_Obj *
Tcl_NewUnicodeObj(unicode, numChars)
- Tcl_UniChar *unicode; /* The unicode string used to initialize
+ CONST Tcl_UniChar *unicode; /* The unicode string used to initialize
* the new object. */
int numChars; /* Number of characters in the unicode
* string. */
@@ -483,6 +520,63 @@ Tcl_GetUnicode(objPtr)
/*
*----------------------------------------------------------------------
*
+ * Tcl_GetUnicodeFromObj --
+ *
+ * Get the Unicode form of the String object with length. If
+ * the object is not already a String object, it will be converted
+ * to one. If the String object does not have a Unicode rep, then
+ * one is create from the UTF string format.
+ *
+ * Results:
+ * Returns a pointer to the object's internal Unicode string.
+ *
+ * Side effects:
+ * Converts the object to have the String internal rep.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_UniChar *
+Tcl_GetUnicodeFromObj(objPtr, lengthPtr)
+ Tcl_Obj *objPtr; /* The object to find the unicode string for. */
+ int *lengthPtr; /* If non-NULL, the location where the
+ * string rep's unichar length should be
+ * stored. If NULL, no length is stored. */
+{
+ String *stringPtr;
+
+ SetStringFromAny(NULL, objPtr);
+ stringPtr = GET_STRING(objPtr);
+
+ if ((stringPtr->numChars == -1) || (stringPtr->uallocated == 0)) {
+
+ /*
+ * We haven't yet calculated the length, or all of the characters
+ * in the Utf string are 1 byte chars (so we didn't store the
+ * unicode str). Since this function must return a unicode string,
+ * and one has not yet been stored, force the Unicode to be
+ * calculated and stored now.
+ */
+
+ FillUnicodeRep(objPtr);
+
+ /*
+ * We need to fetch the pointer again because we have just
+ * reallocated the structure to make room for the Unicode data.
+ */
+
+ stringPtr = GET_STRING(objPtr);
+ }
+
+ if (lengthPtr != NULL) {
+ *lengthPtr = stringPtr->numChars;
+ }
+ return stringPtr->unicode;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_GetRange --
*
* Create a Tcl Object that contains the chars between first and last
@@ -499,10 +593,9 @@ Tcl_GetUnicode(objPtr)
*----------------------------------------------------------------------
*/
-Tcl_Obj*
+Tcl_Obj *
Tcl_GetRange(objPtr, first, last)
-
- Tcl_Obj *objPtr; /* The Tcl object to find the range of. */
+ Tcl_Obj *objPtr; /* The Tcl object to find the range of. */
int first; /* First index of the range. */
int last; /* Last index of the range. */
{
@@ -580,7 +673,7 @@ Tcl_GetRange(objPtr, first, last)
void
Tcl_SetStringObj(objPtr, bytes, length)
register Tcl_Obj *objPtr; /* Object whose internal rep to init. */
- char *bytes; /* Points to the first of the length bytes
+ CONST char *bytes; /* Points to the first of the length bytes
* used to initialize the object. */
register int length; /* The number of bytes to copy from "bytes"
* when initializing the object. If
@@ -668,12 +761,97 @@ Tcl_SetObjLength(objPtr, length)
* Not enough space in current string. Reallocate the string
* space and free the old string.
*/
+ if (objPtr->bytes != tclEmptyStringRep) {
+ new = (char *) ckrealloc((char *)objPtr->bytes,
+ (unsigned)(length+1));
+ } else {
+ new = (char *) ckalloc((unsigned) (length+1));
+ if (objPtr->bytes != NULL && objPtr->length != 0) {
+ memcpy((VOID *) new, (VOID *) objPtr->bytes,
+ (size_t) objPtr->length);
+ Tcl_InvalidateStringRep(objPtr);
+ }
+ }
+ objPtr->bytes = new;
+ stringPtr->allocated = length;
+ }
+
+ objPtr->length = length;
+ if ((objPtr->bytes != NULL) && (objPtr->bytes != tclEmptyStringRep)) {
+ objPtr->bytes[length] = 0;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AttemptSetObjLength --
+ *
+ * This procedure changes the length of the string representation
+ * of an object. It uses the attempt* (non-panic'ing) memory allocators.
+ *
+ * Results:
+ * 1 if the requested memory was allocated, 0 otherwise.
+ *
+ * Side effects:
+ * If the size of objPtr's string representation is greater than
+ * length, then it is reduced to length and a new terminating null
+ * byte is stored in the strength. If the length of the string
+ * representation is greater than length, the storage space is
+ * reallocated to the given length; a null byte is stored at the
+ * end, but other bytes past the end of the original string
+ * representation are undefined. The object's internal
+ * representation is changed to "expendable string".
+ *
+ *----------------------------------------------------------------------
+ */
- new = (char *) ckalloc((unsigned) (length+1));
- if (objPtr->bytes != NULL) {
- memcpy((VOID *) new, (VOID *) objPtr->bytes,
- (size_t) objPtr->length);
- Tcl_InvalidateStringRep(objPtr);
+int
+Tcl_AttemptSetObjLength(objPtr, length)
+ register Tcl_Obj *objPtr; /* Pointer to object. This object must
+ * not currently be shared. */
+ register int length; /* Number of bytes desired for string
+ * representation of object, not including
+ * terminating null byte. */
+{
+ char *new;
+ String *stringPtr;
+
+ if (Tcl_IsShared(objPtr)) {
+ panic("Tcl_AttemptSetObjLength called with shared object");
+ }
+ SetStringFromAny(NULL, objPtr);
+
+ /*
+ * Invalidate the unicode data.
+ */
+
+ stringPtr = GET_STRING(objPtr);
+ stringPtr->numChars = -1;
+ stringPtr->uallocated = 0;
+
+ if (length > (int) stringPtr->allocated) {
+
+ /*
+ * Not enough space in current string. Reallocate the string
+ * space and free the old string.
+ */
+ if (objPtr->bytes != tclEmptyStringRep) {
+ new = (char *) attemptckrealloc((char *)objPtr->bytes,
+ (unsigned)(length+1));
+ if (new == NULL) {
+ return 0;
+ }
+ } else {
+ new = (char *) attemptckalloc((unsigned) (length+1));
+ if (new == NULL) {
+ return 0;
+ }
+ if (objPtr->bytes != NULL && objPtr->length != 0) {
+ memcpy((VOID *) new, (VOID *) objPtr->bytes,
+ (size_t) objPtr->length);
+ Tcl_InvalidateStringRep(objPtr);
+ }
}
objPtr->bytes = new;
stringPtr->allocated = length;
@@ -683,6 +861,7 @@ Tcl_SetObjLength(objPtr, length)
if ((objPtr->bytes != NULL) && (objPtr->bytes != tclEmptyStringRep)) {
objPtr->bytes[length] = 0;
}
+ return 1;
}
/*
@@ -704,7 +883,7 @@ Tcl_SetObjLength(objPtr, length)
void
Tcl_SetUnicodeObj(objPtr, unicode, numChars)
Tcl_Obj *objPtr; /* The object to set the string of. */
- Tcl_UniChar *unicode; /* The unicode string used to initialize
+ CONST Tcl_UniChar *unicode; /* The unicode string used to initialize
* the object. */
int numChars; /* Number of characters in the unicode
* string. */
@@ -766,7 +945,7 @@ Tcl_SetUnicodeObj(objPtr, unicode, numChars)
void
Tcl_AppendToObj(objPtr, bytes, length)
register Tcl_Obj *objPtr; /* Points to the object to append to. */
- char *bytes; /* Points to the bytes to append to the
+ CONST char *bytes; /* Points to the bytes to append to the
* object. */
register int length; /* The number of bytes to append from
* "bytes". If < 0, then append all bytes
@@ -823,7 +1002,7 @@ Tcl_AppendToObj(objPtr, bytes, length)
void
Tcl_AppendUnicodeToObj(objPtr, unicode, length)
register Tcl_Obj *objPtr; /* Points to the object to append to. */
- Tcl_UniChar *unicode; /* The unicode string to append to the
+ CONST Tcl_UniChar *unicode; /* The unicode string to append to the
* object. */
int length; /* Number of chars in "unicode". */
{
@@ -838,15 +1017,7 @@ Tcl_AppendUnicodeToObj(objPtr, unicode, length)
}
SetStringFromAny(NULL, objPtr);
-
- /*
- * TEMPORARY!!! This is terribly inefficient, but it works, and Don
- * needs for me to check this stuff in ASAP. -Melissa
- */
-
-/* UpdateStringOfString(objPtr); */
-/* AppendUnicodeToUtfRep(objPtr, unicode, length); */
-/* return; */
+ stringPtr = GET_STRING(objPtr);
/*
* If objPtr has a valid Unicode rep, then append the "unicode"
@@ -854,7 +1025,6 @@ Tcl_AppendUnicodeToObj(objPtr, unicode, length)
* "unicode" to objPtr's string rep.
*/
- stringPtr = GET_STRING(objPtr);
if (stringPtr->uallocated > 0) {
AppendUnicodeToUnicodeRep(objPtr, unicode, length);
} else {
@@ -970,13 +1140,12 @@ Tcl_AppendObjToObj(objPtr, appendObjPtr)
static void
AppendUnicodeToUnicodeRep(objPtr, unicode, appendNumChars)
- Tcl_Obj *objPtr; /* Points to the object to append to. */
- Tcl_UniChar *unicode; /* String to append. */
- int appendNumChars; /* Number of chars of "unicode" to append. */
+ Tcl_Obj *objPtr; /* Points to the object to append to. */
+ CONST Tcl_UniChar *unicode; /* String to append. */
+ int appendNumChars; /* Number of chars of "unicode" to append. */
{
- String *stringPtr;
- int numChars;
- size_t newSize;
+ String *stringPtr, *tmpString;
+ size_t numChars;
if (appendNumChars < 0) {
appendNumChars = 0;
@@ -990,21 +1159,28 @@ AppendUnicodeToUnicodeRep(objPtr, unicode, appendNumChars)
SetStringFromAny(NULL, objPtr);
stringPtr = GET_STRING(objPtr);
-
+
/*
* If not enough space has been allocated for the unicode rep,
- * reallocate the internal rep object with double the amount of
- * space needed, so the unicode string can grow without being
- * reallocated.
+ * reallocate the internal rep object with additional space. First try to
+ * double the required allocation; if that fails, try a more modest
+ * increase. See the "TCL STRING GROWTH ALGORITHM" comment at the top of
+ * this file for an explanation of this growth algorithm.
*/
numChars = stringPtr->numChars + appendNumChars;
- newSize = (numChars + 1) * sizeof(Tcl_UniChar);
- if (newSize > stringPtr->uallocated) {
- stringPtr->uallocated = newSize * 2;
- stringPtr = (String *) ckrealloc((char*)stringPtr,
+ if (numChars >= stringPtr->uallocated) {
+ stringPtr->uallocated = 2 * numChars;
+ tmpString = (String *) attemptckrealloc((char *)stringPtr,
STRING_SIZE(stringPtr->uallocated));
+ if (tmpString == NULL) {
+ stringPtr->uallocated =
+ numChars + appendNumChars + TCL_GROWTH_MIN_ALLOC;
+ tmpString = (String *) ckrealloc((char *)stringPtr,
+ STRING_SIZE(stringPtr->uallocated));
+ }
+ stringPtr = tmpString;
SET_STRING(objPtr, stringPtr);
}
@@ -1018,7 +1194,6 @@ AppendUnicodeToUnicodeRep(objPtr, unicode, appendNumChars)
stringPtr->unicode[numChars] = 0;
stringPtr->numChars = numChars;
- SET_STRING(objPtr, stringPtr);
Tcl_InvalidateStringRep(objPtr);
}
@@ -1041,12 +1216,12 @@ AppendUnicodeToUnicodeRep(objPtr, unicode, appendNumChars)
static void
AppendUnicodeToUtfRep(objPtr, unicode, numChars)
- Tcl_Obj *objPtr; /* Points to the object to append to. */
- Tcl_UniChar *unicode; /* String to convert to UTF. */
- int numChars; /* Number of chars of "unicode" to convert. */
+ Tcl_Obj *objPtr; /* Points to the object to append to. */
+ CONST Tcl_UniChar *unicode; /* String to convert to UTF. */
+ int numChars; /* Number of chars of "unicode" to convert. */
{
Tcl_DString dsPtr;
- char *bytes;
+ CONST char *bytes;
if (numChars < 0) {
numChars = 0;
@@ -1059,7 +1234,7 @@ AppendUnicodeToUtfRep(objPtr, unicode, numChars)
}
Tcl_DStringInit(&dsPtr);
- bytes = (char *)Tcl_UniCharToUtfDString(unicode, numChars, &dsPtr);
+ bytes = Tcl_UniCharToUtfDString(unicode, numChars, &dsPtr);
AppendUtfToUtfRep(objPtr, bytes, Tcl_DStringLength(&dsPtr));
Tcl_DStringFree(&dsPtr);
}
@@ -1085,7 +1260,7 @@ AppendUnicodeToUtfRep(objPtr, unicode, numChars)
static void
AppendUtfToUnicodeRep(objPtr, bytes, numBytes)
Tcl_Obj *objPtr; /* Points to the object to append to. */
- char *bytes; /* String to convert to Unicode. */
+ CONST char *bytes; /* String to convert to Unicode. */
int numBytes; /* Number of bytes of "bytes" to convert. */
{
Tcl_DString dsPtr;
@@ -1126,7 +1301,7 @@ AppendUtfToUnicodeRep(objPtr, bytes, numBytes)
static void
AppendUtfToUtfRep(objPtr, bytes, numBytes)
Tcl_Obj *objPtr; /* Points to the object to append to. */
- char *bytes; /* String to append. */
+ CONST char *bytes; /* String to append. */
int numBytes; /* Number of bytes of "bytes" to append. */
{
String *stringPtr;
@@ -1151,13 +1326,17 @@ AppendUtfToUtfRep(objPtr, bytes, numBytes)
if (newLength > (int) stringPtr->allocated) {
/*
- * There isn't currently enough space in the string
- * representation so allocate additional space. Overallocate the
- * space by doubling it so that we won't have to do as much
- * reallocation in the future.
+ * There isn't currently enough space in the string representation
+ * so allocate additional space. First, try to double the length
+ * required. If that fails, try a more modest allocation. See the
+ * "TCL STRING GROWTH ALGORITHM" comment at the top of this file for an
+ * explanation of this growth algorithm.
*/
- Tcl_SetObjLength(objPtr, 2*newLength);
+ if (Tcl_AttemptSetObjLength(objPtr, 2 * newLength) == 0) {
+ Tcl_SetObjLength(objPtr,
+ newLength + numBytes + TCL_GROWTH_MIN_ALLOC);
+ }
} else {
/*
@@ -1199,7 +1378,7 @@ Tcl_AppendStringsToObjVA (objPtr, argList)
{
#define STATIC_LIST_SIZE 16
String *stringPtr;
- int newLength, oldLength;
+ int newLength, oldLength, attemptLength;
register char *string, *dst;
char *static_list[STATIC_LIST_SIZE];
char **args = static_list;
@@ -1220,7 +1399,8 @@ Tcl_AppendStringsToObjVA (objPtr, argList)
*/
nargs = 0;
- newLength = oldLength = objPtr->length;
+ newLength = 0;
+ oldLength = objPtr->length;
while (1) {
string = va_arg(argList, char *);
if (string == NULL) {
@@ -1244,23 +1424,35 @@ Tcl_AppendStringsToObjVA (objPtr, argList)
newLength += strlen(string);
args[nargs++] = string;
}
- if (newLength == oldLength) {
+ if (newLength == 0) {
goto done;
}
stringPtr = GET_STRING(objPtr);
- if (newLength > (int) stringPtr->allocated) {
+ if (oldLength + newLength > (int) stringPtr->allocated) {
/*
* There isn't currently enough space in the string
- * representation so allocate additional space. If the current
+ * representation, so allocate additional space. If the current
* string representation isn't empty (i.e. it looks like we're
- * doing a series of appends) then overallocate the space so
- * that we won't have to do as much reallocation in the future.
+ * doing a series of appends) then try to allocate extra space to
+ * accomodate future growth: first try to double the required memory;
+ * if that fails, try a more modest allocation. See the "TCL STRING
+ * GROWTH ALGORITHM" comment at the top of this file for an explanation
+ * of this growth algorithm. Otherwise, if the current string
+ * representation is empty, exactly enough memory is allocated.
*/
- Tcl_SetObjLength(objPtr,
- (objPtr->length == 0) ? newLength : 2*newLength);
+ if (oldLength == 0) {
+ Tcl_SetObjLength(objPtr, newLength);
+ } else {
+ attemptLength = 2 * (oldLength + newLength);
+ if (Tcl_AttemptSetObjLength(objPtr, attemptLength) == 0) {
+ attemptLength = oldLength + (2 * newLength) +
+ TCL_GROWTH_MIN_ALLOC;
+ Tcl_SetObjLength(objPtr, attemptLength);
+ }
+ }
}
/*
@@ -1291,7 +1483,7 @@ Tcl_AppendStringsToObjVA (objPtr, argList)
if (dst != NULL) {
*dst = 0;
}
- objPtr->length = newLength;
+ objPtr->length = oldLength + newLength;
done:
/*
@@ -1486,10 +1678,8 @@ DupStringInternalRep(srcPtr, copyPtr)
static int
SetStringFromAny(interp, objPtr)
Tcl_Interp *interp; /* Used for error reporting if not NULL. */
- Tcl_Obj *objPtr; /* The object to convert. */
+ register Tcl_Obj *objPtr; /* The object to convert. */
{
- String *stringPtr;
-
/*
* The Unicode object is opitmized for the case where each UTF char
* in a string is only one byte. In this case, we store the value of
@@ -1497,6 +1687,7 @@ SetStringFromAny(interp, objPtr)
*/
if (objPtr->typePtr != &tclStringType) {
+ String *stringPtr;
if (objPtr->typePtr != NULL) {
if (objPtr->bytes == NULL) {
diff --git a/tcl/generic/tclStubInit.c b/tcl/generic/tclStubInit.c
index fec95ec45f4..ae18983f7a7 100644
--- a/tcl/generic/tclStubInit.c
+++ b/tcl/generic/tclStubInit.c
@@ -31,6 +31,10 @@
#undef Tcl_NewStringObj
#undef Tcl_DumpActiveMemory
#undef Tcl_ValidateAllMemory
+#if TCL_PRESERVE_BINARY_COMPATABILITY
+# undef Tcl_FindHashEntry
+# undef Tcl_CreateHashEntry
+#endif
/*
* WARNING: The contents of this file is automatically generated by the
@@ -43,7 +47,7 @@
TclIntStubs tclIntStubs = {
TCL_STUB_MAGIC,
NULL,
- TclAccess, /* 0 */
+ NULL, /* 0 */
TclAccessDeleteProc, /* 1 */
TclAccessInsertProc, /* 2 */
TclAllocateFreeObjects, /* 3 */
@@ -76,11 +80,11 @@ TclIntStubs tclIntStubs = {
TclDumpMemoryInfo, /* 14 */
NULL, /* 15 */
TclExprFloatError, /* 16 */
- TclFileAttrsCmd, /* 17 */
- TclFileCopyCmd, /* 18 */
- TclFileDeleteCmd, /* 19 */
- TclFileMakeDirsCmd, /* 20 */
- TclFileRenameCmd, /* 21 */
+ NULL, /* 17 */
+ NULL, /* 18 */
+ NULL, /* 19 */
+ NULL, /* 20 */
+ NULL, /* 21 */
TclFindElement, /* 22 */
TclFindProc, /* 23 */
TclFormatInt, /* 24 */
@@ -88,13 +92,13 @@ TclIntStubs tclIntStubs = {
NULL, /* 26 */
TclGetDate, /* 27 */
TclpGetDefaultStdChannel, /* 28 */
- TclGetElementOfIndexedArray, /* 29 */
+ NULL, /* 29 */
NULL, /* 30 */
TclGetExtension, /* 31 */
TclGetFrame, /* 32 */
TclGetInterpProc, /* 33 */
TclGetIntForIndex, /* 34 */
- TclGetIndexedScalar, /* 35 */
+ NULL, /* 35 */
TclGetLong, /* 36 */
TclGetLoadedPackages, /* 37 */
TclGetNamespaceForQualName, /* 38 */
@@ -106,8 +110,8 @@ TclIntStubs tclIntStubs = {
TclGuessPackageName, /* 44 */
TclHideUnsafeCommands, /* 45 */
TclInExit, /* 46 */
- TclIncrElementOfIndexedArray, /* 47 */
- TclIncrIndexedScalar, /* 48 */
+ NULL, /* 47 */
+ NULL, /* 48 */
TclIncrVar2, /* 49 */
TclInitCompiledLocals, /* 50 */
TclInterpInit, /* 51 */
@@ -118,7 +122,7 @@ TclIntStubs tclIntStubs = {
NULL, /* 56 */
NULL, /* 57 */
TclLookupVar, /* 58 */
- TclpMatchFiles, /* 59 */
+ NULL, /* 59 */
TclNeedSpace, /* 60 */
TclNewProcBodyObj, /* 61 */
TclObjCommandComplete, /* 62 */
@@ -127,22 +131,22 @@ TclIntStubs tclIntStubs = {
TclObjInvokeGlobal, /* 65 */
TclOpenFileChannelDeleteProc, /* 66 */
TclOpenFileChannelInsertProc, /* 67 */
- TclpAccess, /* 68 */
+ NULL, /* 68 */
TclpAlloc, /* 69 */
- TclpCopyFile, /* 70 */
- TclpCopyDirectory, /* 71 */
- TclpCreateDirectory, /* 72 */
- TclpDeleteFile, /* 73 */
+ NULL, /* 70 */
+ NULL, /* 71 */
+ NULL, /* 72 */
+ NULL, /* 73 */
TclpFree, /* 74 */
TclpGetClicks, /* 75 */
TclpGetSeconds, /* 76 */
TclpGetTime, /* 77 */
TclpGetTimeZone, /* 78 */
- TclpListVolumes, /* 79 */
- TclpOpenFileChannel, /* 80 */
+ NULL, /* 79 */
+ NULL, /* 80 */
TclpRealloc, /* 81 */
- TclpRemoveDirectory, /* 82 */
- TclpRenameFile, /* 83 */
+ NULL, /* 82 */
+ NULL, /* 83 */
NULL, /* 84 */
NULL, /* 85 */
NULL, /* 86 */
@@ -154,12 +158,12 @@ TclIntStubs tclIntStubs = {
TclProcCompileProc, /* 92 */
TclProcDeleteProc, /* 93 */
TclProcInterpProc, /* 94 */
- TclpStat, /* 95 */
+ NULL, /* 95 */
TclRenameCommand, /* 96 */
TclResetShadowedCmdRefs, /* 97 */
TclServiceIdle, /* 98 */
- TclSetElementOfIndexedArray, /* 99 */
- TclSetIndexedScalar, /* 100 */
+ NULL, /* 99 */
+ NULL, /* 100 */
#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
TclSetPreInitScript, /* 101 */
#endif /* UNIX */
@@ -180,7 +184,7 @@ TclIntStubs tclIntStubs = {
#ifdef MAC_TCL
NULL, /* 104 */
#endif /* MAC_TCL */
- TclStat, /* 105 */
+ NULL, /* 105 */
TclStatDeleteProc, /* 106 */
TclStatInsertProc, /* 107 */
TclTeardownNamespace, /* 108 */
@@ -212,9 +216,9 @@ TclIntStubs tclIntStubs = {
TclpStrftime, /* 134 */
TclpCheckStackSpace, /* 135 */
NULL, /* 136 */
- TclpChdir, /* 137 */
+ NULL, /* 137 */
TclGetEnv, /* 138 */
- TclpLoadFile, /* 139 */
+ NULL, /* 139 */
TclLooksLikeInt, /* 140 */
TclpGetCwd, /* 141 */
TclSetByteCodeFromAny, /* 142 */
@@ -235,9 +239,18 @@ TclIntStubs tclIntStubs = {
TclVarTraceExists, /* 157 */
TclSetStartupScriptFileName, /* 158 */
TclGetStartupScriptFileName, /* 159 */
- TclpMatchFilesTypes, /* 160 */
+ NULL, /* 160 */
TclChannelTransform, /* 161 */
TclChannelEventScriptInvoker, /* 162 */
+ TclGetInstructionTable, /* 163 */
+ TclExpandCodeArray, /* 164 */
+ TclpSetInitialEncodings, /* 165 */
+ TclListObjSetElement, /* 166 */
+ TclSetStartupScriptPath, /* 167 */
+ TclGetStartupScriptPath, /* 168 */
+ TclpUtfNcmp2, /* 169 */
+ TclCheckInterpTraces, /* 170 */
+ TclCheckExecutionTraces, /* 171 */
};
TclIntPlatStubs tclIntPlatStubs = {
@@ -254,6 +267,10 @@ TclIntPlatStubs tclIntPlatStubs = {
TclpOpenFile, /* 7 */
TclUnixWaitForFile, /* 8 */
TclpCreateTempFile, /* 9 */
+ TclpReaddir, /* 10 */
+ TclpLocaltime, /* 11 */
+ TclpGmtime, /* 12 */
+ TclpInetNtoa, /* 13 */
#endif /* UNIX */
#ifdef __WIN32__
TclWinConvertError, /* 0 */
@@ -277,12 +294,13 @@ TclIntPlatStubs tclIntPlatStubs = {
TclpMakeFile, /* 18 */
TclpOpenFile, /* 19 */
TclWinAddProcess, /* 20 */
- TclpAsyncMark, /* 21 */
+ NULL, /* 21 */
TclpCreateTempFile, /* 22 */
TclpGetTZName, /* 23 */
TclWinNoBackslash, /* 24 */
TclWinGetPlatform, /* 25 */
TclWinSetInterfaces, /* 26 */
+ TclWinFlushDirtyChannels, /* 27 */
#endif /* __WIN32__ */
#ifdef MAC_TCL
TclpSysAlloc, /* 0 */
@@ -292,10 +310,10 @@ TclIntPlatStubs tclIntPlatStubs = {
FSpGetDefaultDir, /* 4 */
FSpSetDefaultDir, /* 5 */
FSpFindFolder, /* 6 */
- GetGlobalMouse, /* 7 */
- FSpGetDirectoryID, /* 8 */
- FSpOpenResFileCompat, /* 9 */
- FSpCreateResFileCompat, /* 10 */
+ GetGlobalMouseTcl, /* 7 */
+ FSpGetDirectoryIDTcl, /* 8 */
+ FSpOpenResFileCompatTcl, /* 9 */
+ FSpCreateResFileCompatTcl, /* 10 */
FSpLocationFromPath, /* 11 */
FSpPathFromLocation, /* 12 */
TclMacExitHandler, /* 13 */
@@ -309,8 +327,9 @@ TclIntPlatStubs tclIntPlatStubs = {
TclMacUnRegisterResourceFork, /* 21 */
TclMacCreateEnv, /* 22 */
TclMacFOpenHack, /* 23 */
- NULL, /* 24 */
+ TclpGetTZName, /* 24 */
TclMacChmod, /* 25 */
+ FSpLLocationFromPath, /* 26 */
#endif /* MAC_TCL */
};
@@ -332,6 +351,9 @@ TclPlatStubs tclPlatStubs = {
strncasecmp, /* 7 */
strcasecmp, /* 8 */
#endif /* MAC_TCL */
+#ifdef MAC_OSX_TCL
+ Tcl_MacOSXOpenBundleResources, /* 0 */
+#endif /* MAC_OSX_TCL */
};
static TclStubHooks tclStubHooks = {
@@ -611,7 +633,7 @@ TclStubs tclStubs = {
Tcl_ResetResult, /* 217 */
Tcl_ScanElement, /* 218 */
Tcl_ScanCountedElement, /* 219 */
- Tcl_Seek, /* 220 */
+ Tcl_SeekOld, /* 220 */
Tcl_ServiceAll, /* 221 */
Tcl_ServiceEvent, /* 222 */
Tcl_SetAssocData, /* 223 */
@@ -637,7 +659,7 @@ TclStubs tclStubs = {
Tcl_SplitPath, /* 243 */
Tcl_StaticPackage, /* 244 */
Tcl_StringMatch, /* 245 */
- Tcl_Tell, /* 246 */
+ Tcl_TellOld, /* 246 */
Tcl_TraceVar, /* 247 */
Tcl_TraceVar2, /* 248 */
Tcl_TranslateFileName, /* 249 */
@@ -669,21 +691,13 @@ TclStubs tclStubs = {
Tcl_SetErrorCodeVA, /* 275 */
Tcl_VarEvalVA, /* 276 */
Tcl_WaitPid, /* 277 */
-#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
- Tcl_PanicVA, /* 278 */
-#endif /* UNIX */
-#ifdef __WIN32__
Tcl_PanicVA, /* 278 */
-#endif /* __WIN32__ */
-#ifdef MAC_TCL
- NULL, /* 278 */
-#endif /* MAC_TCL */
Tcl_GetVersion, /* 279 */
Tcl_InitMemory, /* 280 */
Tcl_StackChannel, /* 281 */
Tcl_UnstackChannel, /* 282 */
Tcl_GetStackedChannel, /* 283 */
- NULL, /* 284 */
+ Tcl_SetMainLoop, /* 284 */
NULL, /* 285 */
Tcl_AppendObjToObj, /* 286 */
Tcl_CreateEncoding, /* 287 */
@@ -811,7 +825,88 @@ TclStubs tclStubs = {
Tcl_ChannelGetHandleProc, /* 409 */
Tcl_ChannelFlushProc, /* 410 */
Tcl_ChannelHandlerProc, /* 411 */
+ Tcl_JoinThread, /* 412 */
+ Tcl_IsChannelShared, /* 413 */
+ Tcl_IsChannelRegistered, /* 414 */
+ Tcl_CutChannel, /* 415 */
+ Tcl_SpliceChannel, /* 416 */
+ Tcl_ClearChannelHandlers, /* 417 */
+ Tcl_IsChannelExisting, /* 418 */
+ Tcl_UniCharNcasecmp, /* 419 */
+ Tcl_UniCharCaseMatch, /* 420 */
+ Tcl_FindHashEntry, /* 421 */
+ Tcl_CreateHashEntry, /* 422 */
+ Tcl_InitCustomHashTable, /* 423 */
+ Tcl_InitObjHashTable, /* 424 */
+ Tcl_CommandTraceInfo, /* 425 */
+ Tcl_TraceCommand, /* 426 */
+ Tcl_UntraceCommand, /* 427 */
+ Tcl_AttemptAlloc, /* 428 */
+ Tcl_AttemptDbCkalloc, /* 429 */
+ Tcl_AttemptRealloc, /* 430 */
+ Tcl_AttemptDbCkrealloc, /* 431 */
+ Tcl_AttemptSetObjLength, /* 432 */
+ Tcl_GetChannelThread, /* 433 */
+ Tcl_GetUnicodeFromObj, /* 434 */
+ Tcl_GetMathFuncInfo, /* 435 */
+ Tcl_ListMathFuncs, /* 436 */
+ Tcl_SubstObj, /* 437 */
+ Tcl_DetachChannel, /* 438 */
+ Tcl_IsStandardChannel, /* 439 */
+ Tcl_FSCopyFile, /* 440 */
+ Tcl_FSCopyDirectory, /* 441 */
+ Tcl_FSCreateDirectory, /* 442 */
+ Tcl_FSDeleteFile, /* 443 */
+ Tcl_FSLoadFile, /* 444 */
+ Tcl_FSMatchInDirectory, /* 445 */
+ Tcl_FSLink, /* 446 */
+ Tcl_FSRemoveDirectory, /* 447 */
+ Tcl_FSRenameFile, /* 448 */
+ Tcl_FSLstat, /* 449 */
+ Tcl_FSUtime, /* 450 */
+ Tcl_FSFileAttrsGet, /* 451 */
+ Tcl_FSFileAttrsSet, /* 452 */
+ Tcl_FSFileAttrStrings, /* 453 */
+ Tcl_FSStat, /* 454 */
+ Tcl_FSAccess, /* 455 */
+ Tcl_FSOpenFileChannel, /* 456 */
+ Tcl_FSGetCwd, /* 457 */
+ Tcl_FSChdir, /* 458 */
+ Tcl_FSConvertToPathType, /* 459 */
+ Tcl_FSJoinPath, /* 460 */
+ Tcl_FSSplitPath, /* 461 */
+ Tcl_FSEqualPaths, /* 462 */
+ Tcl_FSGetNormalizedPath, /* 463 */
+ Tcl_FSJoinToPath, /* 464 */
+ Tcl_FSGetInternalRep, /* 465 */
+ Tcl_FSGetTranslatedPath, /* 466 */
+ Tcl_FSEvalFile, /* 467 */
+ Tcl_FSNewNativePath, /* 468 */
+ Tcl_FSGetNativePath, /* 469 */
+ Tcl_FSFileSystemInfo, /* 470 */
+ Tcl_FSPathSeparator, /* 471 */
+ Tcl_FSListVolumes, /* 472 */
+ Tcl_FSRegister, /* 473 */
+ Tcl_FSUnregister, /* 474 */
+ Tcl_FSData, /* 475 */
+ Tcl_FSGetTranslatedStringPath, /* 476 */
+ Tcl_FSGetFileSystemForPath, /* 477 */
+ Tcl_FSGetPathType, /* 478 */
+ Tcl_OutputBuffered, /* 479 */
+ Tcl_FSMountsChanged, /* 480 */
+ Tcl_EvalTokensStandard, /* 481 */
+ Tcl_GetTime, /* 482 */
+ Tcl_CreateObjTrace, /* 483 */
+ Tcl_GetCommandInfoFromToken, /* 484 */
+ Tcl_SetCommandInfoFromToken, /* 485 */
+ Tcl_DbNewWideIntObj, /* 486 */
+ Tcl_GetWideIntFromObj, /* 487 */
+ Tcl_NewWideIntObj, /* 488 */
+ Tcl_SetWideIntObj, /* 489 */
+ Tcl_AllocStatBuf, /* 490 */
+ Tcl_Seek, /* 491 */
+ Tcl_Tell, /* 492 */
+ Tcl_ChannelWideSeekProc, /* 493 */
};
/* !END!: Do not edit above this line. */
-
diff --git a/tcl/generic/tclStubLib.c b/tcl/generic/tclStubLib.c
index 048fdd4d85f..b00211d2e77 100644
--- a/tcl/generic/tclStubLib.c
+++ b/tcl/generic/tclStubLib.c
@@ -80,13 +80,13 @@ HasStubSupport (interp)
#undef Tcl_InitStubs
#endif
-char *
+CONST char *
Tcl_InitStubs (interp, version, exact)
Tcl_Interp *interp;
- char *version;
+ CONST char *version;
int exact;
{
- char *actualVersion;
+ CONST char *actualVersion;
TclStubs *tmp;
if (!tclStubsPtr) {
diff --git a/tcl/generic/tclTest.c b/tcl/generic/tclTest.c
index 99f80d68bf1..20071d3d008 100644
--- a/tcl/generic/tclTest.c
+++ b/tcl/generic/tclTest.c
@@ -17,14 +17,25 @@
*/
#define TCL_TEST
-
#include "tclInt.h"
#include "tclPort.h"
+
+/*
+ * Required for Testregexp*Cmd
+ */
#include "tclRegexp.h"
-#include "tclIO.h"
+
+/*
+ * Required for TestlocaleCmd
+ */
#include <locale.h>
/*
+ * Required for the TestChannelCmd and TestChannelEventCmd
+ */
+#include "tclIO.h"
+
+/*
* Declare external functions used in Windows tests.
*/
@@ -95,6 +106,12 @@ typedef struct TclEncoding {
static int freeCount;
/*
+ * Boolean flag used by the "testsetmainloop" and "testexitmainloop"
+ * commands.
+ */
+static int exitMainLoop = 0;
+
+/*
* Forward declarations for procedures defined later in this file:
*/
@@ -106,9 +123,9 @@ static void CleanupTestSetassocdataTests _ANSI_ARGS_((
static void CmdDelProc1 _ANSI_ARGS_((ClientData clientData));
static void CmdDelProc2 _ANSI_ARGS_((ClientData clientData));
static int CmdProc1 _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int CmdProc2 _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static void CmdTraceDeleteProc _ANSI_ARGS_((
ClientData clientData, Tcl_Interp *interp,
int level, char *command, Tcl_CmdProc *cmdProc,
@@ -120,14 +137,14 @@ static void CmdTraceProc _ANSI_ARGS_((ClientData clientData,
int argc, char **argv));
static int CreatedCommandProc _ANSI_ARGS_((
ClientData clientData, Tcl_Interp *interp,
- int argc, char **argv));
+ int argc, CONST char **argv));
static int CreatedCommandProc2 _ANSI_ARGS_((
ClientData clientData, Tcl_Interp *interp,
- int argc, char **argv));
+ int argc, CONST char **argv));
static void DelCallbackProc _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp));
static int DelCmdProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static void DelDeleteProc _ANSI_ARGS_((ClientData clientData));
static void EncodingFreeProc _ANSI_ARGS_((ClientData clientData));
static int EncodingToUtfProc _ANSI_ARGS_((ClientData clientData,
@@ -143,18 +160,29 @@ static int EncodingFromUtfProc _ANSI_ARGS_((ClientData clientData,
static void ExitProcEven _ANSI_ARGS_((ClientData clientData));
static void ExitProcOdd _ANSI_ARGS_((ClientData clientData));
static int GetTimesCmd _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
+static void MainLoop _ANSI_ARGS_((void));
static int NoopCmd _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int NoopObjCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
+static int ObjTraceProc _ANSI_ARGS_(( ClientData clientData,
+ Tcl_Interp* interp,
+ int level,
+ CONST char* command,
+ Tcl_Command commandToken,
+ int objc,
+ Tcl_Obj *CONST objv[] ));
+static void ObjTraceDeleteProc _ANSI_ARGS_(( ClientData ));
static void PrintParse _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Parse *parsePtr));
+ Tcl_Parse *parsePtr));
static void SpecialFree _ANSI_ARGS_((char *blockPtr));
static int StaticInitProc _ANSI_ARGS_((Tcl_Interp *interp));
static int TestaccessprocCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
+static int PretendTclpAccess _ANSI_ARGS_((CONST char *path,
+ int mode));
static int TestAccessProc1 _ANSI_ARGS_((CONST char *path,
int mode));
static int TestAccessProc2 _ANSI_ARGS_((CONST char *path,
@@ -162,25 +190,25 @@ static int TestAccessProc2 _ANSI_ARGS_((CONST char *path,
static int TestAccessProc3 _ANSI_ARGS_((CONST char *path,
int mode));
static int TestasyncCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestcmdinfoCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestcmdtokenCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestcmdtraceCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestchmodCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestcreatecommandCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestdcallCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestdelCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestdelassocdataCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestdstringCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestencodingObjCmd _ANSI_ARGS_((ClientData dummy,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
@@ -191,29 +219,31 @@ static int TestevalobjvObjCmd _ANSI_ARGS_((ClientData dummy,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static int TestexithandlerCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestexprlongCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestexprparserObjCmd _ANSI_ARGS_((ClientData dummy,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static int TestexprstringCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestfileCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+static int TestfilelinkCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
static int TestfeventCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestgetassocdataCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestgetplatformCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestgetvarfullnameCmd _ANSI_ARGS_((
ClientData dummy, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[]));
static int TestinterpdeleteCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestlinkCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestlocaleCmd _ANSI_ARGS_((ClientData dummy,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
@@ -223,14 +253,26 @@ static int TestMathFunc _ANSI_ARGS_((ClientData clientData,
static int TestMathFunc2 _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, Tcl_Value *args,
Tcl_Value *resultPtr));
-static Tcl_Channel TestOpenFileChannelProc1 _ANSI_ARGS_((Tcl_Interp *interp,
- char *filename, char *modeString, int permissions));
-static Tcl_Channel TestOpenFileChannelProc2 _ANSI_ARGS_((Tcl_Interp *interp,
- char *filename, char *modeString, int permissions));
-static Tcl_Channel TestOpenFileChannelProc3 _ANSI_ARGS_((Tcl_Interp *interp,
- char *filename, char *modeString, int permissions));
+static int TestmainthreadCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int argc, CONST char **argv));
+static int TestsetmainloopCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int argc, CONST char **argv));
+static int TestexitmainloopCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int argc, CONST char **argv));
+static Tcl_Channel PretendTclpOpenFileChannel _ANSI_ARGS_((
+ Tcl_Interp *interp, CONST char *fileName,
+ CONST char *modeString, int permissions));
+static Tcl_Channel TestOpenFileChannelProc1 _ANSI_ARGS_((
+ Tcl_Interp *interp, CONST char *fileName,
+ CONST char *modeString, int permissions));
+static Tcl_Channel TestOpenFileChannelProc2 _ANSI_ARGS_((
+ Tcl_Interp *interp, CONST char *fileName,
+ CONST char *modeString, int permissions));
+static Tcl_Channel TestOpenFileChannelProc3 _ANSI_ARGS_((
+ Tcl_Interp *interp, CONST char *fileName,
+ CONST char *modeString, int permissions));
static int TestpanicCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestparserObjCmd _ANSI_ARGS_((ClientData dummy,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
@@ -250,21 +292,21 @@ static int TestsaveresultCmd _ANSI_ARGS_((ClientData dummy,
Tcl_Obj *CONST objv[]));
static void TestsaveresultFree _ANSI_ARGS_((char *blockPtr));
static int TestsetassocdataCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestsetCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestsetobjerrorcodeCmd _ANSI_ARGS_((
ClientData dummy, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[]));
-static int TestopenfilechannelprocCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+static int TestopenfilechannelprocCmd _ANSI_ARGS_((
+ ClientData dummy, Tcl_Interp *interp, int argc,
+ CONST char **argv));
static int TestsetplatformCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
-static int TestsetrecursionlimitCmd _ANSI_ARGS_((
- ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *CONST objv[]));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TeststaticpkgCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
+static int PretendTclpStat _ANSI_ARGS_((CONST char *path,
+ struct stat *buf));
static int TestStatProc1 _ANSI_ARGS_((CONST char *path,
struct stat *buf));
static int TestStatProc2 _ANSI_ARGS_((CONST char *path,
@@ -272,16 +314,111 @@ static int TestStatProc2 _ANSI_ARGS_((CONST char *path,
static int TestStatProc3 _ANSI_ARGS_((CONST char *path,
struct stat *buf));
static int TeststatprocCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TesttranslatefilenameCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
static int TestupvarCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
-static int TestChannelCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
-static int TestChannelEventCmd _ANSI_ARGS_((ClientData dummy,
- Tcl_Interp *interp, int argc, char **argv));
+ Tcl_Interp *interp, int argc, CONST char **argv));
+static int TestWrongNumArgsObjCmd _ANSI_ARGS_((
+ ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]));
+static int TestGetIndexFromObjStructObjCmd _ANSI_ARGS_((
+ ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]));
+static int TestChannelCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, CONST char **argv));
+static int TestChannelEventCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, CONST char **argv));
+/* Filesystem testing */
+
+static int TestFilesystemObjCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+
+static void TestReport _ANSI_ARGS_((CONST char* cmd, Tcl_Obj* arg1, Tcl_Obj* arg2));
+
+static Tcl_Obj *TestReportGetNativePath(Tcl_Obj* pathObjPtr);
+
+static int TestReportStat _ANSI_ARGS_ ((Tcl_Obj *path,
+ Tcl_StatBuf *buf));
+static int TestReportAccess _ANSI_ARGS_ ((Tcl_Obj *path,
+ int mode));
+static Tcl_Channel TestReportOpenFileChannel _ANSI_ARGS_ ((
+ Tcl_Interp *interp, Tcl_Obj *fileName,
+ int mode, int permissions));
+static int TestReportMatchInDirectory _ANSI_ARGS_ ((
+ Tcl_Interp *interp, Tcl_Obj *resultPtr,
+ Tcl_Obj *dirPtr, CONST char *pattern,
+ Tcl_GlobTypeData *types));
+static int TestReportChdir _ANSI_ARGS_ ((Tcl_Obj *dirName));
+static int TestReportLstat _ANSI_ARGS_ ((Tcl_Obj *path,
+ Tcl_StatBuf *buf));
+static int TestReportCopyFile _ANSI_ARGS_ ((Tcl_Obj *src,
+ Tcl_Obj *dst));
+static int TestReportDeleteFile _ANSI_ARGS_ ((Tcl_Obj *path));
+static int TestReportRenameFile _ANSI_ARGS_ ((Tcl_Obj *src,
+ Tcl_Obj *dst));
+static int TestReportCreateDirectory _ANSI_ARGS_ ((Tcl_Obj *path));
+static int TestReportCopyDirectory _ANSI_ARGS_ ((Tcl_Obj *src,
+ Tcl_Obj *dst, Tcl_Obj **errorPtr));
+static int TestReportRemoveDirectory _ANSI_ARGS_ ((Tcl_Obj *path,
+ int recursive, Tcl_Obj **errorPtr));
+static int TestReportLoadFile _ANSI_ARGS_ ((Tcl_Interp *interp,
+ Tcl_Obj *fileName,
+ Tcl_LoadHandle *handlePtr,
+ Tcl_FSUnloadFileProc **unloadProcPtr));
+static Tcl_Obj * TestReportLink _ANSI_ARGS_ ((Tcl_Obj *path,
+ Tcl_Obj *to, int linkType));
+static CONST char** TestReportFileAttrStrings _ANSI_ARGS_ ((
+ Tcl_Obj *fileName, Tcl_Obj **objPtrRef));
+static int TestReportFileAttrsGet _ANSI_ARGS_ ((Tcl_Interp *interp,
+ int index, Tcl_Obj *fileName, Tcl_Obj **objPtrRef));
+static int TestReportFileAttrsSet _ANSI_ARGS_ ((Tcl_Interp *interp,
+ int index, Tcl_Obj *fileName, Tcl_Obj *objPtr));
+static int TestReportUtime _ANSI_ARGS_ ((Tcl_Obj *fileName,
+ struct utimbuf *tval));
+static int TestReportNormalizePath _ANSI_ARGS_ ((
+ Tcl_Interp *interp, Tcl_Obj *pathPtr,
+ int nextCheckpoint));
+static int TestReportInFilesystem _ANSI_ARGS_ ((Tcl_Obj *pathPtr, ClientData *clientDataPtr));
+static void TestReportFreeInternalRep _ANSI_ARGS_ ((ClientData clientData));
+static ClientData TestReportDupInternalRep _ANSI_ARGS_ ((ClientData clientData));
+
+static Tcl_Filesystem testReportingFilesystem = {
+ "reporting",
+ sizeof(Tcl_Filesystem),
+ TCL_FILESYSTEM_VERSION_1,
+ &TestReportInFilesystem, /* path in */
+ &TestReportDupInternalRep,
+ &TestReportFreeInternalRep,
+ NULL, /* native to norm */
+ NULL, /* convert to native */
+ &TestReportNormalizePath,
+ NULL, /* path type */
+ NULL, /* separator */
+ &TestReportStat,
+ &TestReportAccess,
+ &TestReportOpenFileChannel,
+ &TestReportMatchInDirectory,
+ &TestReportUtime,
+ &TestReportLink,
+ NULL /* list volumes */,
+ &TestReportFileAttrStrings,
+ &TestReportFileAttrsGet,
+ &TestReportFileAttrsSet,
+ &TestReportCreateDirectory,
+ &TestReportRemoveDirectory,
+ &TestReportDeleteFile,
+ &TestReportCopyFile,
+ &TestReportRenameFile,
+ &TestReportCopyDirectory,
+ &TestReportLstat,
+ &TestReportLoadFile,
+ NULL /* cwd */,
+ &TestReportChdir
+};
+
/*
* External (platform specific) initialization routine, these declarations
* explicitly don't use EXTERN since this code does not get compiled
@@ -315,7 +452,15 @@ Tcltest_Init(interp)
Tcl_Interp *interp; /* Interpreter for application. */
{
Tcl_ValueType t3ArgTypes[2];
-
+
+ Tcl_Obj *listPtr;
+ Tcl_Obj **objv;
+ int objc, index;
+ static CONST char *specialOptions[] = {
+ "-appinitprocerror", "-appinitprocdeleteinterp",
+ "-appinitprocclosestderr", "-appinitprocsetrcfile", (char *) NULL
+ };
+
if (Tcl_PkgProvide(interp, "Tcltest", TCL_VERSION) == TCL_ERROR) {
return TCL_ERROR;
}
@@ -330,6 +475,13 @@ Tcltest_Init(interp)
(Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "noop", NoopObjCmd, (ClientData) 0,
(Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateObjCommand(interp, "testwrongnumargs", TestWrongNumArgsObjCmd,
+ (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateObjCommand(interp, "testfilesystem", TestFilesystemObjCmd,
+ (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateObjCommand(interp, "testgetindexfromobjstruct",
+ TestGetIndexFromObjStructObjCmd, (ClientData) 0,
+ (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, "testaccessproc", TestaccessprocCmd, (ClientData) 0,
(Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, "testasync", TestasyncCmd, (ClientData) 0,
@@ -373,7 +525,9 @@ Tcltest_Init(interp)
(ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, "testfevent", TestfeventCmd, (ClientData) 0,
(Tcl_CmdDeleteProc *) NULL);
- Tcl_CreateCommand(interp, "testfile", TestfileCmd,
+ Tcl_CreateObjCommand(interp, "testfilelink", TestfilelinkCmd,
+ (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateObjCommand(interp, "testfile", TestfileCmd,
(ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, "testgetassocdata", TestgetassocdataCmd,
(ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
@@ -414,9 +568,6 @@ Tcltest_Init(interp)
(Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, "testsetplatform", TestsetplatformCmd,
(ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
- Tcl_CreateObjCommand(interp, "testsetrecursionlimit",
- TestsetrecursionlimitCmd,
- (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, "teststaticpkg", TeststaticpkgCmd,
(ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, "testtranslatefilename",
@@ -430,6 +581,12 @@ Tcltest_Init(interp)
(ClientData) 345);
Tcl_CreateCommand(interp, "teststatproc", TeststatprocCmd, (ClientData) 0,
(Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateCommand(interp, "testmainthread", TestmainthreadCmd, (ClientData) 0,
+ (Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateCommand(interp, "testsetmainloop", TestsetmainloopCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateCommand(interp, "testexitmainloop", TestexitmainloopCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
t3ArgTypes[0] = TCL_EITHER;
t3ArgTypes[1] = TCL_EITHER;
Tcl_CreateMathFunc(interp, "T3", 2, t3ArgTypes, TestMathFunc2,
@@ -442,6 +599,42 @@ Tcltest_Init(interp)
#endif
/*
+ * Check for special options used in ../tests/main.test
+ */
+
+ listPtr = Tcl_GetVar2Ex(interp, "argv", NULL, TCL_GLOBAL_ONLY);
+ if (listPtr != NULL) {
+ if (Tcl_ListObjGetElements(interp, listPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc && (Tcl_GetIndexFromObj(NULL, objv[0], specialOptions, NULL,
+ TCL_EXACT, &index) == TCL_OK)) {
+ switch (index) {
+ case 0: {
+ return TCL_ERROR;
+ }
+ case 1: {
+ Tcl_DeleteInterp(interp);
+ return TCL_ERROR;
+ }
+ case 2: {
+ int mode;
+ Tcl_UnregisterChannel(interp,
+ Tcl_GetChannel(interp, "stderr", &mode));
+ return TCL_ERROR;
+ }
+ case 3: {
+ if (objc-1) {
+ Tcl_SetVar2Ex(interp, "tcl_rcFileName", NULL,
+ objv[1], TCL_GLOBAL_ONLY);
+ }
+ return TCL_ERROR;
+ }
+ }
+ }
+ }
+
+ /*
* And finally add any platform specific test commands.
*/
@@ -471,7 +664,7 @@ TestasyncCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
TestAsyncHandler *asyncPtr, *prevPtr;
int id, code;
@@ -545,7 +738,7 @@ TestasyncCmd(dummy, interp, argc, argv)
break;
}
}
- Tcl_SetResult(interp, argv[3], TCL_VOLATILE);
+ Tcl_SetResult(interp, (char *)argv[3], TCL_VOLATILE);
return code;
} else {
Tcl_AppendResult(interp, "bad option \"", argv[1],
@@ -564,17 +757,25 @@ AsyncHandlerProc(clientData, interp, code)
int code; /* Current return code from command. */
{
TestAsyncHandler *asyncPtr = (TestAsyncHandler *) clientData;
- char *listArgv[4];
- char string[TCL_INTEGER_SPACE], *cmd;
+ CONST char *listArgv[4], *cmd;
+ char string[TCL_INTEGER_SPACE];
TclFormatInt(string, code);
listArgv[0] = asyncPtr->command;
- listArgv[1] = Tcl_GetStringResult(interp);
+ listArgv[1] = Tcl_GetString(Tcl_GetObjResult(interp));
listArgv[2] = string;
listArgv[3] = NULL;
cmd = Tcl_Merge(3, listArgv);
- code = Tcl_Eval(interp, cmd);
- ckfree(cmd);
+ if (interp != NULL) {
+ code = Tcl_Eval(interp, cmd);
+ } else {
+ /*
+ * this should not happen, but by definition of how async
+ * handlers are invoked, it's possible. Better error
+ * checking is needed here.
+ */
+ }
+ ckfree((char *)cmd);
return code;
}
@@ -602,7 +803,7 @@ TestcmdinfoCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
Tcl_CmdInfo info;
@@ -675,7 +876,7 @@ CmdProc1(clientData, interp, argc, argv)
ClientData clientData; /* String to return. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
Tcl_AppendResult(interp, "CmdProc1 ", (char *) clientData,
(char *) NULL);
@@ -688,7 +889,7 @@ CmdProc2(clientData, interp, argc, argv)
ClientData clientData; /* String to return. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
Tcl_AppendResult(interp, "CmdProc2 ", (char *) clientData,
(char *) NULL);
@@ -737,10 +938,10 @@ TestcmdtokenCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
Tcl_Command token;
- long int l;
+ int *l;
char buf[30];
if (argc != 3) {
@@ -751,12 +952,12 @@ TestcmdtokenCmd(dummy, interp, argc, argv)
if (strcmp(argv[1], "create") == 0) {
token = Tcl_CreateCommand(interp, argv[2], CmdProc1,
(ClientData) "original", (Tcl_CmdDeleteProc *) NULL);
- sprintf(buf, "%lx", (long int) token);
+ sprintf(buf, "%p", (VOID *)token);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
} else if (strcmp(argv[1], "name") == 0) {
Tcl_Obj *objPtr;
-
- if (sscanf(argv[2], "%lx", &l) != 1) {
+
+ if (sscanf(argv[2], "%p", &l) != 1) {
Tcl_AppendResult(interp, "bad command token \"", argv[2],
"\"", (char *) NULL);
return TCL_ERROR;
@@ -764,7 +965,7 @@ TestcmdtokenCmd(dummy, interp, argc, argv)
objPtr = Tcl_NewObj();
Tcl_GetCommandFullName(interp, (Tcl_Command) l, objPtr);
-
+
Tcl_AppendElement(interp,
Tcl_GetCommandName(interp, (Tcl_Command) l));
Tcl_AppendElement(interp, Tcl_GetString(objPtr));
@@ -801,7 +1002,7 @@ TestcmdtraceCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
Tcl_DString buffer;
int result;
@@ -834,9 +1035,30 @@ TestcmdtraceCmd(dummy, interp, argc, argv)
cmdTrace = Tcl_CreateTrace(interp, 50000,
(Tcl_CmdTraceProc *) CmdTraceDeleteProc, (ClientData) NULL);
Tcl_Eval(interp, argv[2]);
+ } else if ( strcmp(argv[1], "resulttest" ) == 0 ) {
+ /* Create an object-based trace, then eval a script. This is used
+ * to test return codes other than TCL_OK from the trace engine.
+ */
+ static int deleteCalled;
+ deleteCalled = 0;
+ cmdTrace = Tcl_CreateObjTrace( interp, 50000,
+ TCL_ALLOW_INLINE_COMPILATION,
+ ObjTraceProc,
+ (ClientData) &deleteCalled,
+ ObjTraceDeleteProc );
+ result = Tcl_Eval( interp, argv[ 2 ] );
+ Tcl_DeleteTrace( interp, cmdTrace );
+ if ( !deleteCalled ) {
+ Tcl_SetResult( interp, "Delete wasn't called", TCL_STATIC );
+ return TCL_ERROR;
+ } else {
+ return result;
+ }
+
} else {
Tcl_AppendResult(interp, "bad option \"", argv[1],
- "\": must be tracetest or deletetest", (char *) NULL);
+ "\": must be tracetest, deletetest or resulttest",
+ (char *) NULL);
return TCL_ERROR;
}
return TCL_OK;
@@ -893,6 +1115,41 @@ CmdTraceDeleteProc(clientData, interp, level, command, cmdProc,
Tcl_DeleteTrace(interp, cmdTrace);
}
+static int
+ObjTraceProc( clientData, interp, level, command, token, objc, objv )
+ ClientData clientData; /* unused */
+ Tcl_Interp* interp; /* Tcl interpreter */
+ int level; /* Execution level */
+ CONST char* command; /* Command being executed */
+ Tcl_Command token; /* Command information */
+ int objc; /* Parameter count */
+ Tcl_Obj *CONST objv[]; /* Parameter list */
+{
+ CONST char* word = Tcl_GetString( objv[ 0 ] );
+ if ( !strcmp( word, "Error" ) ) {
+ Tcl_SetObjResult( interp, Tcl_NewStringObj( command, -1 ) );
+ return TCL_ERROR;
+ } else if ( !strcmp( word, "Break" ) ) {
+ return TCL_BREAK;
+ } else if ( !strcmp( word, "Continue" ) ) {
+ return TCL_CONTINUE;
+ } else if ( !strcmp( word, "Return" ) ) {
+ return TCL_RETURN;
+ } else if ( !strcmp( word, "OtherStatus" ) ) {
+ return 6;
+ } else {
+ return TCL_OK;
+ }
+}
+
+static void
+ObjTraceDeleteProc( clientData )
+ ClientData clientData;
+{
+ int * intPtr = (int *) clientData;
+ *intPtr = 1; /* Record that the trace was deleted */
+}
+
/*
*----------------------------------------------------------------------
*
@@ -919,7 +1176,7 @@ TestcreatecommandCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
@@ -952,7 +1209,7 @@ CreatedCommandProc(clientData, interp, argc, argv)
ClientData clientData; /* String to return. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
Tcl_CmdInfo info;
int found;
@@ -974,7 +1231,7 @@ CreatedCommandProc2(clientData, interp, argc, argv)
ClientData clientData; /* String to return. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
Tcl_CmdInfo info;
int found;
@@ -1013,7 +1270,7 @@ TestdcallCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
int i, id;
@@ -1079,7 +1336,7 @@ TestdelCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
DelCmd *dPtr;
Tcl_Interp *slave;
@@ -1109,7 +1366,7 @@ DelCmdProc(clientData, interp, argc, argv)
ClientData clientData; /* String result to return. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
DelCmd *dPtr = (DelCmd *) clientData;
@@ -1154,7 +1411,7 @@ TestdelassocdataCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
@@ -1188,7 +1445,7 @@ TestdstringCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
int count;
@@ -1323,7 +1580,7 @@ TestencodingObjCmd(dummy, interp, objc, objv)
int index, length;
char *string;
TclEncoding *encodingPtr;
- static char *optionStrings[] = {
+ static CONST char *optionStrings[] = {
"create", "delete", "path",
NULL
};
@@ -1595,7 +1852,7 @@ TestexithandlerCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
int value;
@@ -1663,7 +1920,7 @@ TestexprlongCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
long exprResult;
char buf[4 + TCL_INTEGER_SPACE];
@@ -1700,7 +1957,7 @@ TestexprstringCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
@@ -1713,6 +1970,74 @@ TestexprstringCmd(clientData, interp, argc, argv)
/*
*----------------------------------------------------------------------
*
+ * TestfilelinkCmd --
+ *
+ * This procedure implements the "testfilelink" command. It is used
+ * to test the effects of creating and manipulating filesystem links
+ * in Tcl.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * May create a link on disk.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TestfilelinkCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* The argument objects. */
+{
+ Tcl_Obj *contents;
+
+ if (objc < 2 || objc > 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "source ?target?");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_FSConvertToPathType(interp, objv[1]) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (objc == 3) {
+ /* Create link from source to target */
+ contents = Tcl_FSLink(objv[1], objv[2],
+ TCL_CREATE_SYMBOLIC_LINK|TCL_CREATE_HARD_LINK);
+ if (contents == NULL) {
+ Tcl_AppendResult(interp, "could not create link from \"",
+ Tcl_GetString(objv[1]), "\" to \"",
+ Tcl_GetString(objv[2]), "\": ",
+ Tcl_PosixError(interp), (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ /* Read link */
+ contents = Tcl_FSLink(objv[1], NULL, 0);
+ if (contents == NULL) {
+ Tcl_AppendResult(interp, "could not read link \"",
+ Tcl_GetString(objv[1]), "\": ",
+ Tcl_PosixError(interp), (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ Tcl_SetObjResult(interp, contents);
+ if (objc == 2) {
+ /*
+ * If we are creating a link, this will actually just
+ * be objv[3], and we don't own it
+ */
+ Tcl_DecrRefCount(contents);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TestgetassocdataCmd --
*
* This procedure implements the "testgetassocdata" command. It is
@@ -1732,7 +2057,7 @@ TestgetassocdataCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
char *res;
@@ -1770,9 +2095,9 @@ TestgetplatformCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
- static char *platformStrings[] = { "unix", "mac", "windows" };
+ static CONST char *platformStrings[] = { "unix", "mac", "windows" };
TclPlatformType *platform;
#ifdef __WIN32__
@@ -1815,7 +2140,7 @@ TestinterpdeleteCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
Tcl_Interp *slaveToDelete;
@@ -1824,11 +2149,6 @@ TestinterpdeleteCmd(dummy, interp, argc, argv)
" path\"", (char *) NULL);
return TCL_ERROR;
}
- if (argv[1][0] == '\0') {
- Tcl_AppendResult(interp, "cannot delete current interpreter",
- (char *) NULL);
- return TCL_ERROR;
- }
slaveToDelete = Tcl_GetSlave(interp, argv[1]);
if (slaveToDelete == (Tcl_Interp *) NULL) {
return TCL_ERROR;
@@ -1861,27 +2181,36 @@ TestlinkCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
static int intVar = 43;
static int boolVar = 4;
static double realVar = 1.23;
+ static Tcl_WideInt wideVar = Tcl_LongAsWide(79);
static char *stringVar = NULL;
static int created = 0;
- char buffer[TCL_DOUBLE_SPACE];
+ char buffer[2*TCL_DOUBLE_SPACE];
int writable, flag;
+ Tcl_Obj *tmp;
if (argc < 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " option ?arg arg arg?\"", (char *) NULL);
+ " option ?arg arg arg arg arg?\"", (char *) NULL);
return TCL_ERROR;
}
if (strcmp(argv[1], "create") == 0) {
+ if (argc != 7) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ", argv[1],
+ " intRO realRO boolRO stringRO wideRO\"", (char *) NULL);
+ return TCL_ERROR;
+ }
if (created) {
Tcl_UnlinkVar(interp, "int");
Tcl_UnlinkVar(interp, "real");
Tcl_UnlinkVar(interp, "bool");
Tcl_UnlinkVar(interp, "string");
+ Tcl_UnlinkVar(interp, "wide");
}
created = 1;
if (Tcl_GetBoolean(interp, argv[2], &writable) != TCL_OK) {
@@ -1916,11 +2245,20 @@ TestlinkCmd(dummy, interp, argc, argv)
TCL_LINK_STRING | flag) != TCL_OK) {
return TCL_ERROR;
}
+ if (Tcl_GetBoolean(interp, argv[6], &writable) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flag = (writable != 0) ? 0 : TCL_LINK_READ_ONLY;
+ if (Tcl_LinkVar(interp, "wide", (char *) &wideVar,
+ TCL_LINK_WIDE_INT | flag) != TCL_OK) {
+ return TCL_ERROR;
+ }
} else if (strcmp(argv[1], "delete") == 0) {
Tcl_UnlinkVar(interp, "int");
Tcl_UnlinkVar(interp, "real");
Tcl_UnlinkVar(interp, "bool");
Tcl_UnlinkVar(interp, "string");
+ Tcl_UnlinkVar(interp, "wide");
created = 0;
} else if (strcmp(argv[1], "get") == 0) {
TclFormatInt(buffer, intVar);
@@ -1930,11 +2268,18 @@ TestlinkCmd(dummy, interp, argc, argv)
TclFormatInt(buffer, boolVar);
Tcl_AppendElement(interp, buffer);
Tcl_AppendElement(interp, (stringVar == NULL) ? "-" : stringVar);
+ /*
+ * Wide ints only have an object-based interface.
+ */
+ tmp = Tcl_NewWideIntObj(wideVar);
+ Tcl_AppendElement(interp, Tcl_GetString(tmp));
+ Tcl_DecrRefCount(tmp);
} else if (strcmp(argv[1], "set") == 0) {
- if (argc != 6) {
+ if (argc != 7) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " ", argv[1],
- "intValue realValue boolValue stringValue\"", (char *) NULL);
+ argv[0], " ", argv[1],
+ " intValue realValue boolValue stringValue wideValue\"",
+ (char *) NULL);
return TCL_ERROR;
}
if (argv[2][0] != 0) {
@@ -1963,11 +2308,20 @@ TestlinkCmd(dummy, interp, argc, argv)
strcpy(stringVar, argv[5]);
}
}
+ if (argv[6][0] != 0) {
+ tmp = Tcl_NewStringObj(argv[6], -1);
+ if (Tcl_GetWideIntFromObj(interp, tmp, &wideVar) != TCL_OK) {
+ Tcl_DecrRefCount(tmp);
+ return TCL_ERROR;
+ }
+ Tcl_DecrRefCount(tmp);
+ }
} else if (strcmp(argv[1], "update") == 0) {
- if (argc != 6) {
+ if (argc != 7) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " ", argv[1],
- "intValue realValue boolValue stringValue\"", (char *) NULL);
+ argv[0], " ", argv[1],
+ "intValue realValue boolValue stringValue wideValue\"",
+ (char *) NULL);
return TCL_ERROR;
}
if (argv[2][0] != 0) {
@@ -2000,6 +2354,15 @@ TestlinkCmd(dummy, interp, argc, argv)
}
Tcl_UpdateLinkedVar(interp, "string");
}
+ if (argv[6][0] != 0) {
+ tmp = Tcl_NewStringObj(argv[6], -1);
+ if (Tcl_GetWideIntFromObj(interp, tmp, &wideVar) != TCL_OK) {
+ Tcl_DecrRefCount(tmp);
+ return TCL_ERROR;
+ }
+ Tcl_DecrRefCount(tmp);
+ Tcl_UpdateLinkedVar(interp, "wide");
+ }
} else {
Tcl_AppendResult(interp, "bad option \"", argv[1],
"\": should be create, delete, get, set, or update",
@@ -2036,7 +2399,7 @@ TestlocaleCmd(clientData, interp, objc, objv)
int index;
char *locale;
- static char *optionStrings[] = {
+ static CONST char *optionStrings[] = {
"ctype", "numeric", "time", "collate", "monetary",
"all", NULL
};
@@ -2148,8 +2511,16 @@ TestMathFunc2(clientData, interp, args, resultPtr)
resultPtr->type = TCL_DOUBLE;
resultPtr->doubleValue = ((d0 > d1)? d0 : d1);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (args[1].type == TCL_WIDE_INT) {
+ Tcl_WideInt w0 = Tcl_LongAsWide(i0);
+ Tcl_WideInt w1 = args[1].wideValue;
+
+ resultPtr->type = TCL_WIDE_INT;
+ resultPtr->wideValue = ((w0 > w1)? w0 : w1);
+#endif
} else {
- Tcl_SetResult(interp, "T2: wrong type for arg 2", TCL_STATIC);
+ Tcl_SetResult(interp, "T3: wrong type for arg 2", TCL_STATIC);
result = TCL_ERROR;
}
} else if (args[0].type == TCL_DOUBLE) {
@@ -2165,12 +2536,44 @@ TestMathFunc2(clientData, interp, args, resultPtr)
resultPtr->type = TCL_DOUBLE;
resultPtr->doubleValue = ((d0 > d1)? d0 : d1);
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (args[1].type == TCL_WIDE_INT) {
+ double d1 = Tcl_WideAsDouble(args[1].wideValue);
+
+ resultPtr->type = TCL_DOUBLE;
+ resultPtr->doubleValue = ((d0 > d1)? d0 : d1);
+#endif
+ } else {
+ Tcl_SetResult(interp, "T3: wrong type for arg 2", TCL_STATIC);
+ result = TCL_ERROR;
+ }
+#ifndef TCL_WIDE_INT_IS_LONG
+ } else if (args[0].type == TCL_WIDE_INT) {
+ Tcl_WideInt w0 = args[0].wideValue;
+
+ if (args[1].type == TCL_INT) {
+ Tcl_WideInt w1 = Tcl_LongAsWide(args[1].intValue);
+
+ resultPtr->type = TCL_WIDE_INT;
+ resultPtr->wideValue = ((w0 > w1)? w0 : w1);
+ } else if (args[1].type == TCL_DOUBLE) {
+ double d0 = Tcl_WideAsDouble(w0);
+ double d1 = args[1].doubleValue;
+
+ resultPtr->type = TCL_DOUBLE;
+ resultPtr->doubleValue = ((d0 > d1)? d0 : d1);
+ } else if (args[1].type == TCL_WIDE_INT) {
+ Tcl_WideInt w1 = args[1].wideValue;
+
+ resultPtr->type = TCL_WIDE_INT;
+ resultPtr->wideValue = ((w0 > w1)? w0 : w1);
} else {
- Tcl_SetResult(interp, "T2: wrong type for arg 2", TCL_STATIC);
+ Tcl_SetResult(interp, "T3: wrong type for arg 2", TCL_STATIC);
result = TCL_ERROR;
}
+#endif
} else {
- Tcl_SetResult(interp, "T2: wrong type for arg 1", TCL_STATIC);
+ Tcl_SetResult(interp, "T3: wrong type for arg 1", TCL_STATIC);
result = TCL_ERROR;
}
return result;
@@ -2422,7 +2825,8 @@ TestparsevarObjCmd(clientData, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* The argument objects. */
{
- char *name, *value, *termPtr;
+ CONST char *value;
+ CONST char *name, *termPtr;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "varName");
@@ -2521,7 +2925,7 @@ TestparsevarnameObjCmd(clientData, interp, objc, objv)
*/
/* ARGSUSED */
-int
+static int
TestregexpObjCmd(dummy, interp, objc, objv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
@@ -2534,7 +2938,7 @@ TestregexpObjCmd(dummy, interp, objc, objv)
char *string;
Tcl_Obj *objPtr;
Tcl_RegExpInfo info;
- static char *options[] = {
+ static CONST char *options[] = {
"-indices", "-nocase", "-about", "-expanded",
"-line", "-linestop", "-lineanchor",
"-xflags",
@@ -2648,7 +3052,8 @@ TestregexpObjCmd(dummy, interp, objc, objv)
Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
if (objc > 2 && (cflags&REG_EXPECT) && indices) {
- char *varName, *value;
+ char *varName;
+ CONST char *value;
int start, end;
char info[TCL_INTEGER_SPACE * 2];
@@ -2858,7 +3263,7 @@ TestsetassocdataCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
char *buf;
char *oldData;
@@ -2911,7 +3316,7 @@ TestsetplatformCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
size_t length;
TclPlatformType *platform;
@@ -2946,47 +3351,6 @@ TestsetplatformCmd(clientData, interp, argc, argv)
/*
*----------------------------------------------------------------------
*
- * TestsetrecursionlimitCmd --
- *
- * This procedure implements the "testsetrecursionlimit" command. It is
- * used to change the interp recursion limit (to test the effects
- * of Tcl_SetRecursionLimit).
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Sets the interp's recursion limit.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-TestsetrecursionlimitCmd(dummy, interp, objc, objv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int objc; /* Number of arguments. */
- Tcl_Obj *CONST objv[]; /* The argument objects. */
-{
- int value;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "integer");
- return TCL_ERROR;
- }
- if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) {
- return TCL_ERROR;
- }
- value = Tcl_SetRecursionLimit(interp, value);
- Tcl_SetIntObj(Tcl_GetObjResult(interp), value);
- return TCL_OK;
-}
-
-
-
-/*
- *----------------------------------------------------------------------
- *
* TeststaticpkgCmd --
*
* This procedure implements the "teststaticpkg" command.
@@ -3007,7 +3371,7 @@ TeststaticpkgCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
int safe, loaded;
@@ -3058,10 +3422,10 @@ TesttranslatefilenameCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
Tcl_DString buffer;
- char *result;
+ CONST char *result;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # arguments: should be \"",
@@ -3100,7 +3464,7 @@ TestupvarCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
int flags = 0;
@@ -3192,7 +3556,7 @@ TestfeventCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
static Tcl_Interp *interp2 = NULL;
int code;
@@ -3224,7 +3588,7 @@ TestfeventCmd(clientData, interp, argc, argv)
Tcl_DeleteInterp(interp2);
}
interp2 = Tcl_CreateInterp();
- return TCL_OK;
+ return Tcl_Init(interp2);
} else if (strcmp(argv[1], "delete") == 0) {
if (interp2 != NULL) {
Tcl_DeleteInterp(interp2);
@@ -3264,9 +3628,9 @@ TestpanicCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
- char *argString;
+ CONST char *argString;
/*
* Put the arguments into a var args structure
@@ -3275,7 +3639,7 @@ TestpanicCmd(dummy, interp, argc, argv)
argString = Tcl_Merge(argc-1, argv+1);
panic(argString);
- ckfree(argString);
+ ckfree((char *)argString);
return TCL_OK;
}
@@ -3304,7 +3668,7 @@ TestchmodCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
int i, mode;
char *rest;
@@ -3323,13 +3687,14 @@ TestchmodCmd(dummy, interp, argc, argv)
for (i = 2; i < argc; i++) {
Tcl_DString buffer;
+ CONST char *translated;
- argv[i] = Tcl_TranslateFileName(interp, argv[i], &buffer);
- if (argv[i] == NULL) {
+ translated = Tcl_TranslateFileName(interp, argv[i], &buffer);
+ if (translated == NULL) {
return TCL_ERROR;
}
- if (chmod(argv[i], (unsigned) mode) != 0) {
- Tcl_AppendResult(interp, argv[i], ": ", Tcl_PosixError(interp),
+ if (chmod(translated, (unsigned) mode) != 0) {
+ Tcl_AppendResult(interp, translated, ": ", Tcl_PosixError(interp),
(char *) NULL);
return TCL_ERROR;
}
@@ -3342,11 +3707,12 @@ static int
TestfileCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ int argc; /* Number of arguments. */
+ Tcl_Obj *CONST argv[]; /* The argument objects. */
{
int force, i, j, result;
- Tcl_DString error, name[2];
+ Tcl_Obj *error = NULL;
+ char *subcmd;
if (argc < 3) {
return TCL_ERROR;
@@ -3354,54 +3720,51 @@ TestfileCmd(dummy, interp, argc, argv)
force = 0;
i = 2;
- if (strcmp(argv[2], "-force") == 0) {
+ if (strcmp(Tcl_GetString(argv[2]), "-force") == 0) {
force = 1;
i = 3;
}
- Tcl_DStringInit(&name[0]);
- Tcl_DStringInit(&name[1]);
- Tcl_DStringInit(&error);
-
if (argc - i > 2) {
return TCL_ERROR;
}
for (j = i; j < argc; j++) {
- argv[j] = Tcl_TranslateFileName(interp, argv[j], &name[j - i]);
- if (argv[j] == NULL) {
+ if (Tcl_FSGetTranslatedPath(interp, argv[j]) == NULL) {
return TCL_ERROR;
}
}
- if (strcmp(argv[1], "mv") == 0) {
- result = TclpRenameFile(argv[i], argv[i + 1]);
- } else if (strcmp(argv[1], "cp") == 0) {
- result = TclpCopyFile(argv[i], argv[i + 1]);
- } else if (strcmp(argv[1], "rm") == 0) {
- result = TclpDeleteFile(argv[i]);
- } else if (strcmp(argv[1], "mkdir") == 0) {
- result = TclpCreateDirectory(argv[i]);
- } else if (strcmp(argv[1], "cpdir") == 0) {
- result = TclpCopyDirectory(argv[i], argv[i + 1], &error);
- } else if (strcmp(argv[1], "rmdir") == 0) {
- result = TclpRemoveDirectory(argv[i], force, &error);
+ subcmd = Tcl_GetString(argv[1]);
+
+ if (strcmp(subcmd, "mv") == 0) {
+ result = TclpObjRenameFile(argv[i], argv[i + 1]);
+ } else if (strcmp(subcmd, "cp") == 0) {
+ result = TclpObjCopyFile(argv[i], argv[i + 1]);
+ } else if (strcmp(subcmd, "rm") == 0) {
+ result = TclpObjDeleteFile(argv[i]);
+ } else if (strcmp(subcmd, "mkdir") == 0) {
+ result = TclpObjCreateDirectory(argv[i]);
+ } else if (strcmp(subcmd, "cpdir") == 0) {
+ result = TclpObjCopyDirectory(argv[i], argv[i + 1], &error);
+ } else if (strcmp(subcmd, "rmdir") == 0) {
+ result = TclpObjRemoveDirectory(argv[i], force, &error);
} else {
result = TCL_ERROR;
goto end;
}
if (result != TCL_OK) {
- if (Tcl_DStringValue(&error)[0] != '\0') {
- Tcl_AppendResult(interp, Tcl_DStringValue(&error), " ", NULL);
+ if (error != NULL) {
+ if (Tcl_GetString(error)[0] != '\0') {
+ Tcl_AppendResult(interp, Tcl_GetString(error), " ", NULL);
+ }
+ Tcl_DecrRefCount(error);
}
Tcl_AppendResult(interp, Tcl_ErrnoId(), (char *) NULL);
}
end:
- Tcl_DStringFree(&error);
- Tcl_DStringFree(&name[0]);
- Tcl_DStringFree(&name[1]);
return result;
}
@@ -3508,7 +3871,7 @@ GetTimesCmd(unused, interp, argc, argv)
ClientData unused; /* Unused. */
Tcl_Interp *interp; /* The current interpreter. */
int argc; /* The number of arguments. */
- char **argv; /* The argument strings. */
+ CONST char **argv; /* The argument strings. */
{
Interp *iPtr = (Interp *) interp;
int i, n;
@@ -3516,59 +3879,59 @@ GetTimesCmd(unused, interp, argc, argv)
Tcl_Time start, stop;
Tcl_Obj *objPtr;
Tcl_Obj **objv;
- char *s;
+ CONST char *s;
char newString[TCL_INTEGER_SPACE];
/* alloc & free 100000 times */
fprintf(stderr, "alloc & free 100000 6 word items\n");
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 100000; i++) {
objPtr = (Tcl_Obj *) ckalloc(sizeof(Tcl_Obj));
ckfree((char *) objPtr);
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per alloc+free\n", timePer/100000);
/* alloc 5000 times */
fprintf(stderr, "alloc 5000 6 word items\n");
objv = (Tcl_Obj **) ckalloc(5000 * sizeof(Tcl_Obj *));
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 5000; i++) {
objv[i] = (Tcl_Obj *) ckalloc(sizeof(Tcl_Obj));
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per alloc\n", timePer/5000);
/* free 5000 times */
fprintf(stderr, "free 5000 6 word items\n");
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 5000; i++) {
ckfree((char *) objv[i]);
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per free\n", timePer/5000);
/* Tcl_NewObj 5000 times */
fprintf(stderr, "Tcl_NewObj 5000 times\n");
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 5000; i++) {
objv[i] = Tcl_NewObj();
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per Tcl_NewObj\n", timePer/5000);
/* Tcl_DecrRefCount 5000 times */
fprintf(stderr, "Tcl_DecrRefCount 5000 times\n");
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 5000; i++) {
objPtr = objv[i];
Tcl_DecrRefCount(objPtr);
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per Tcl_DecrRefCount\n", timePer/5000);
ckfree((char *) objv);
@@ -3576,24 +3939,24 @@ GetTimesCmd(unused, interp, argc, argv)
/* TclGetString 100000 times */
fprintf(stderr, "TclGetStringFromObj of \"12345\" 100000 times\n");
objPtr = Tcl_NewStringObj("12345", -1);
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 100000; i++) {
(void) TclGetString(objPtr);
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per TclGetStringFromObj of \"12345\"\n",
timePer/100000);
/* Tcl_GetIntFromObj 100000 times */
fprintf(stderr, "Tcl_GetIntFromObj of \"12345\" 100000 times\n");
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 100000; i++) {
if (Tcl_GetIntFromObj(interp, objPtr, &n) != TCL_OK) {
return TCL_ERROR;
}
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per Tcl_GetIntFromObj of \"12345\"\n",
timePer/100000);
@@ -3601,63 +3964,63 @@ GetTimesCmd(unused, interp, argc, argv)
/* Tcl_GetInt 100000 times */
fprintf(stderr, "Tcl_GetInt of \"12345\" 100000 times\n");
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 100000; i++) {
if (Tcl_GetInt(interp, "12345", &n) != TCL_OK) {
return TCL_ERROR;
}
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per Tcl_GetInt of \"12345\"\n",
timePer/100000);
/* sprintf 100000 times */
fprintf(stderr, "sprintf of 12345 100000 times\n");
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 100000; i++) {
sprintf(newString, "%d", 12345);
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per sprintf of 12345\n",
timePer/100000);
/* hashtable lookup 100000 times */
fprintf(stderr, "hashtable lookup of \"gettimes\" 100000 times\n");
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 100000; i++) {
(void) Tcl_FindHashEntry(&iPtr->globalNsPtr->cmdTable, "gettimes");
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per hashtable lookup of \"gettimes\"\n",
timePer/100000);
/* Tcl_SetVar 100000 times */
fprintf(stderr, "Tcl_SetVar of \"12345\" 100000 times\n");
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 100000; i++) {
s = Tcl_SetVar(interp, "a", "12345", TCL_LEAVE_ERR_MSG);
if (s == NULL) {
return TCL_ERROR;
}
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per Tcl_SetVar of a to \"12345\"\n",
timePer/100000);
/* Tcl_GetVar 100000 times */
fprintf(stderr, "Tcl_GetVar of a==\"12345\" 100000 times\n");
- TclpGetTime(&start);
+ Tcl_GetTime(&start);
for (i = 0; i < 100000; i++) {
s = Tcl_GetVar(interp, "a", TCL_LEAVE_ERR_MSG);
if (s == NULL) {
return TCL_ERROR;
}
}
- TclpGetTime(&stop);
+ Tcl_GetTime(&stop);
timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
fprintf(stderr, " %.3f usec per Tcl_GetVar of a==\"12345\"\n",
timePer/100000);
@@ -3688,7 +4051,7 @@ NoopCmd(unused, interp, argc, argv)
ClientData unused; /* Unused. */
Tcl_Interp *interp; /* The current interpreter. */
int argc; /* The number of arguments. */
- char **argv; /* The argument strings. */
+ CONST char **argv; /* The argument strings. */
{
return TCL_OK;
}
@@ -3743,10 +4106,10 @@ TestsetCmd(data, interp, argc, argv)
ClientData data; /* Additional flags for Get/SetVar2. */
register Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
int flags = (int) data;
- char *value;
+ CONST char *value;
if (argc == 2) {
Tcl_SetResult(interp, "before get", TCL_STATIC);
@@ -3800,7 +4163,7 @@ TestsaveresultCmd(dummy, interp, objc, objv)
int discard, result, index;
Tcl_SavedResult state;
Tcl_Obj *objPtr;
- static char *optionStrings[] = {
+ static CONST char *optionStrings[] = {
"append", "dynamic", "free", "object", "small", NULL
};
enum options {
@@ -3925,7 +4288,7 @@ TeststatprocCmd (dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
register Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
TclStatProc_ *proc;
int retVal;
@@ -3937,7 +4300,7 @@ TeststatprocCmd (dummy, interp, argc, argv)
}
if (strcmp(argv[2], "TclpStat") == 0) {
- proc = TclpStat;
+ proc = PretendTclpStat;
} else if (strcmp(argv[2], "TestStatProc1") == 0) {
proc = TestStatProc1;
} else if (strcmp(argv[2], "TestStatProc2") == 0) {
@@ -3953,7 +4316,7 @@ TeststatprocCmd (dummy, interp, argc, argv)
}
if (strcmp(argv[1], "insert") == 0) {
- if (proc == TclpStat) {
+ if (proc == PretendTclpStat) {
Tcl_AppendResult(interp, "bad arg \"", argv[1], "\": ",
"must be ",
"TestStatProc1, TestStatProc2, or TestStatProc3",
@@ -3977,16 +4340,94 @@ TeststatprocCmd (dummy, interp, argc, argv)
return retVal;
}
+static int PretendTclpStat(path, buf)
+ CONST char *path;
+ struct stat *buf;
+{
+ int ret;
+ Tcl_Obj *pathPtr = Tcl_NewStringObj(path, -1);
+#ifdef TCL_WIDE_INT_IS_LONG
+ Tcl_IncrRefCount(pathPtr);
+ ret = TclpObjStat(pathPtr, buf);
+ Tcl_DecrRefCount(pathPtr);
+ return ret;
+#else /* TCL_WIDE_INT_IS_LONG */
+ Tcl_StatBuf realBuf;
+ Tcl_IncrRefCount(pathPtr);
+ ret = TclpObjStat(pathPtr, &realBuf);
+ Tcl_DecrRefCount(pathPtr);
+ if (ret != -1) {
+# define OUT_OF_RANGE(x) \
+ (((Tcl_WideInt)(x)) < Tcl_LongAsWide(LONG_MIN) || \
+ ((Tcl_WideInt)(x)) > Tcl_LongAsWide(LONG_MAX))
+# define OUT_OF_URANGE(x) \
+ (((Tcl_WideUInt)(x)) > (Tcl_WideUInt)ULONG_MAX)
+
+ /*
+ * Perform the result-buffer overflow check manually.
+ *
+ * Note that ino_t/ino64_t is unsigned...
+ */
+
+ if (OUT_OF_URANGE(realBuf.st_ino) || OUT_OF_RANGE(realBuf.st_size)
+# ifdef HAVE_ST_BLOCKS
+ || OUT_OF_RANGE(realBuf.st_blocks)
+# endif
+ ) {
+# ifdef EOVERFLOW
+ errno = EOVERFLOW;
+# else
+# ifdef EFBIG
+ errno = EFBIG;
+# else
+# error "what error should be returned for a value out of range?"
+# endif
+# endif
+ return -1;
+ }
+
+# undef OUT_OF_RANGE
+# undef OUT_OF_URANGE
+
+ /*
+ * Copy across all supported fields, with possible type
+ * coercions on those fields that change between the normal
+ * and lf64 versions of the stat structure (on Solaris at
+ * least.) This is slow when the structure sizes coincide,
+ * but that's what you get for mixing interfaces...
+ */
+
+ buf->st_mode = realBuf.st_mode;
+ buf->st_ino = (ino_t) realBuf.st_ino;
+ buf->st_dev = realBuf.st_dev;
+ buf->st_rdev = realBuf.st_rdev;
+ buf->st_nlink = realBuf.st_nlink;
+ buf->st_uid = realBuf.st_uid;
+ buf->st_gid = realBuf.st_gid;
+ buf->st_size = (off_t) realBuf.st_size;
+ buf->st_atime = realBuf.st_atime;
+ buf->st_mtime = realBuf.st_mtime;
+ buf->st_ctime = realBuf.st_ctime;
+# ifdef HAVE_ST_BLOCKS
+ buf->st_blksize = realBuf.st_blksize;
+ buf->st_blocks = (blkcnt_t) realBuf.st_blocks;
+# endif
+ }
+ return ret;
+#endif /* TCL_WIDE_INT_IS_LONG */
+}
+
/* Be careful in the compares in these tests, since the Macintosh puts a
* leading : in the beginning of non-absolute paths before passing them
* into the file command procedures.
*/
-
+
static int
TestStatProc1(path, buf)
CONST char *path;
struct stat *buf;
{
+ memset(buf, 0, sizeof(struct stat));
buf->st_size = 1234;
return ((strstr(path, "testStat1%.fil") == NULL) ? -1 : 0);
}
@@ -3997,6 +4438,7 @@ TestStatProc2(path, buf)
CONST char *path;
struct stat *buf;
{
+ memset(buf, 0, sizeof(struct stat));
buf->st_size = 2345;
return ((strstr(path, "testStat2%.fil") == NULL) ? -1 : 0);
}
@@ -4007,6 +4449,7 @@ TestStatProc3(path, buf)
CONST char *path;
struct stat *buf;
{
+ memset(buf, 0, sizeof(struct stat));
buf->st_size = 3456;
return ((strstr(path, "testStat3%.fil") == NULL) ? -1 : 0);
}
@@ -4014,6 +4457,123 @@ TestStatProc3(path, buf)
/*
*----------------------------------------------------------------------
*
+ * TestmainthreadCmd --
+ *
+ * Implements the "testmainthread" cmd that is used to test the
+ * 'Tcl_GetCurrentThread' API.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TestmainthreadCmd (dummy, interp, argc, argv)
+ ClientData dummy; /* Not used. */
+ register Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ CONST char **argv; /* Argument strings. */
+{
+ if (argc == 1) {
+ Tcl_Obj *idObj = Tcl_NewLongObj((long)Tcl_GetCurrentThread());
+ Tcl_SetObjResult(interp, idObj);
+ return TCL_OK;
+ } else {
+ Tcl_SetResult(interp, "wrong # args", TCL_STATIC);
+ return TCL_ERROR;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MainLoop --
+ *
+ * A main loop set by TestsetmainloopCmd below.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Event handlers could do anything.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MainLoop(void)
+{
+ while (!exitMainLoop) {
+ Tcl_DoOneEvent(0);
+ }
+ fprintf(stdout,"Exit MainLoop\n");
+ fflush(stdout);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TestsetmainloopCmd --
+ *
+ * Implements the "testsetmainloop" cmd that is used to test the
+ * 'Tcl_SetMainLoop' API.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TestsetmainloopCmd (dummy, interp, argc, argv)
+ ClientData dummy; /* Not used. */
+ register Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ CONST char **argv; /* Argument strings. */
+{
+ exitMainLoop = 0;
+ Tcl_SetMainLoop(MainLoop);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TestexitmainloopCmd --
+ *
+ * Implements the "testexitmainloop" cmd that is used to test the
+ * 'Tcl_SetMainLoop' API.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TestexitmainloopCmd (dummy, interp, argc, argv)
+ ClientData dummy; /* Not used. */
+ register Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ CONST char **argv; /* Argument strings. */
+{
+ exitMainLoop = 1;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TestaccessprocCmd --
*
* Implements the "testTclAccessProc" cmd that is used to test the
@@ -4033,7 +4593,7 @@ TestaccessprocCmd (dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
register Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
TclAccessProc_ *proc;
int retVal;
@@ -4045,7 +4605,7 @@ TestaccessprocCmd (dummy, interp, argc, argv)
}
if (strcmp(argv[2], "TclpAccess") == 0) {
- proc = TclpAccess;
+ proc = PretendTclpAccess;
} else if (strcmp(argv[2], "TestAccessProc1") == 0) {
proc = TestAccessProc1;
} else if (strcmp(argv[2], "TestAccessProc2") == 0) {
@@ -4061,7 +4621,7 @@ TestaccessprocCmd (dummy, interp, argc, argv)
}
if (strcmp(argv[1], "insert") == 0) {
- if (proc == TclpAccess) {
+ if (proc == PretendTclpAccess) {
Tcl_AppendResult(interp, "bad arg \"", argv[1], "\": ",
"must be ",
"TestAccessProc1, TestAccessProc2, or TestAccessProc3",
@@ -4085,6 +4645,17 @@ TestaccessprocCmd (dummy, interp, argc, argv)
return retVal;
}
+static int PretendTclpAccess(path, mode)
+ CONST char *path;
+ int mode;
+{
+ int ret;
+ Tcl_Obj *pathPtr = Tcl_NewStringObj(path, -1);
+ Tcl_IncrRefCount(pathPtr);
+ ret = TclpObjAccess(pathPtr, mode);
+ Tcl_DecrRefCount(pathPtr);
+ return ret;
+}
static int
TestAccessProc1(path, mode)
@@ -4134,7 +4705,7 @@ TestopenfilechannelprocCmd (dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
register Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
TclOpenFileChannelProc_ *proc;
int retVal;
@@ -4146,7 +4717,7 @@ TestopenfilechannelprocCmd (dummy, interp, argc, argv)
}
if (strcmp(argv[2], "TclpOpenFileChannel") == 0) {
- proc = TclpOpenFileChannel;
+ proc = PretendTclpOpenFileChannel;
} else if (strcmp(argv[2], "TestOpenFileChannelProc1") == 0) {
proc = TestOpenFileChannelProc1;
} else if (strcmp(argv[2], "TestOpenFileChannelProc2") == 0) {
@@ -4163,7 +4734,7 @@ TestopenfilechannelprocCmd (dummy, interp, argc, argv)
}
if (strcmp(argv[1], "insert") == 0) {
- if (proc == TclpOpenFileChannel) {
+ if (proc == PretendTclpOpenFileChannel) {
Tcl_AppendResult(interp, "bad arg \"", argv[1], "\": ",
"must be ",
"TestOpenFileChannelProc1, TestOpenFileChannelProc2, or ",
@@ -4188,22 +4759,68 @@ TestopenfilechannelprocCmd (dummy, interp, argc, argv)
return retVal;
}
+static Tcl_Channel
+PretendTclpOpenFileChannel(interp, fileName, modeString, permissions)
+ Tcl_Interp *interp; /* Interpreter for error reporting;
+ * can be NULL. */
+ CONST char *fileName; /* Name of file to open. */
+ CONST char *modeString; /* A list of POSIX open modes or
+ * a string such as "rw". */
+ int permissions; /* If the open involves creating a
+ * file, with what modes to create
+ * it? */
+{
+ Tcl_Channel ret;
+ int mode, seekFlag;
+ Tcl_Obj *pathPtr;
+ mode = TclGetOpenMode(interp, modeString, &seekFlag);
+ if (mode == -1) {
+ return NULL;
+ }
+ pathPtr = Tcl_NewStringObj(fileName, -1);
+ Tcl_IncrRefCount(pathPtr);
+ ret = TclpOpenFileChannel(interp, pathPtr, mode, permissions);
+ Tcl_DecrRefCount(pathPtr);
+ if (ret != NULL) {
+ if (seekFlag) {
+ if (Tcl_Seek(ret, (Tcl_WideInt)0, SEEK_END) < (Tcl_WideInt)0) {
+ if (interp != (Tcl_Interp *) NULL) {
+ Tcl_AppendResult(interp,
+ "could not seek to end of file while opening \"",
+ fileName, "\": ",
+ Tcl_PosixError(interp), (char *) NULL);
+ }
+ Tcl_Close(NULL, ret);
+ return NULL;
+ }
+ }
+ }
+ return ret;
+}
static Tcl_Channel
TestOpenFileChannelProc1(interp, fileName, modeString, permissions)
Tcl_Interp *interp; /* Interpreter for error reporting;
* can be NULL. */
- char *fileName; /* Name of file to open. */
- char *modeString; /* A list of POSIX open modes or
+ CONST char *fileName; /* Name of file to open. */
+ CONST char *modeString; /* A list of POSIX open modes or
* a string such as "rw". */
int permissions; /* If the open involves creating a
* file, with what modes to create
* it? */
{
- if (!strcmp("testOpenFileChannel1%.fil", fileName)) {
- return (TclpOpenFileChannel(interp, "__testOpenFileChannel1%__.fil",
+ CONST char *expectname="testOpenFileChannel1%.fil";
+ Tcl_DString ds;
+
+ Tcl_DStringInit(&ds);
+ Tcl_JoinPath(1, &expectname, &ds);
+
+ if (!strcmp(Tcl_DStringValue(&ds), fileName)) {
+ Tcl_DStringFree(&ds);
+ return (PretendTclpOpenFileChannel(interp, "__testOpenFileChannel1%__.fil",
modeString, permissions));
} else {
+ Tcl_DStringFree(&ds);
return (NULL);
}
}
@@ -4213,17 +4830,25 @@ static Tcl_Channel
TestOpenFileChannelProc2(interp, fileName, modeString, permissions)
Tcl_Interp *interp; /* Interpreter for error reporting;
* can be NULL. */
- char *fileName; /* Name of file to open. */
- char *modeString; /* A list of POSIX open modes or
+ CONST char *fileName; /* Name of file to open. */
+ CONST char *modeString; /* A list of POSIX open modes or
* a string such as "rw". */
int permissions; /* If the open involves creating a
* file, with what modes to create
* it? */
{
- if (!strcmp("testOpenFileChannel2%.fil", fileName)) {
- return (TclpOpenFileChannel(interp, "__testOpenFileChannel2%__.fil",
+ CONST char *expectname="testOpenFileChannel2%.fil";
+ Tcl_DString ds;
+
+ Tcl_DStringInit(&ds);
+ Tcl_JoinPath(1, &expectname, &ds);
+
+ if (!strcmp(Tcl_DStringValue(&ds), fileName)) {
+ Tcl_DStringFree(&ds);
+ return (PretendTclpOpenFileChannel(interp, "__testOpenFileChannel2%__.fil",
modeString, permissions));
} else {
+ Tcl_DStringFree(&ds);
return (NULL);
}
}
@@ -4233,17 +4858,25 @@ static Tcl_Channel
TestOpenFileChannelProc3(interp, fileName, modeString, permissions)
Tcl_Interp *interp; /* Interpreter for error reporting;
* can be NULL. */
- char *fileName; /* Name of file to open. */
- char *modeString; /* A list of POSIX open modes or
+ CONST char *fileName; /* Name of file to open. */
+ CONST char *modeString; /* A list of POSIX open modes or
* a string such as "rw". */
int permissions; /* If the open involves creating a
* file, with what modes to create
* it? */
{
- if (!strcmp("testOpenFileChannel3%.fil", fileName)) {
- return (TclpOpenFileChannel(interp, "__testOpenFileChannel3%__.fil",
+ CONST char *expectname="testOpenFileChannel3%.fil";
+ Tcl_DString ds;
+
+ Tcl_DStringInit(&ds);
+ Tcl_JoinPath(1, &expectname, &ds);
+
+ if (!strcmp(Tcl_DStringValue(&ds), fileName)) {
+ Tcl_DStringFree(&ds);
+ return (PretendTclpOpenFileChannel(interp, "__testOpenFileChannel3%__.fil",
modeString, permissions));
} else {
+ Tcl_DStringFree(&ds);
return (NULL);
}
}
@@ -4266,14 +4899,14 @@ TestOpenFileChannelProc3(interp, fileName, modeString, permissions)
*/
/* ARGSUSED */
-int
+static int
TestChannelCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Interpreter for result. */
int argc; /* Count of additional args. */
- char **argv; /* Additional arg strings. */
+ CONST char **argv; /* Additional arg strings. */
{
- char *cmdName; /* Sub command. */
+ CONST char *cmdName; /* Sub command. */
Tcl_HashTable *hTblPtr; /* Hash table of channels. */
Tcl_HashSearch hSearch; /* Search variable. */
Tcl_HashEntry *hPtr; /* Search variable. */
@@ -4311,6 +4944,27 @@ TestChannelCmd(clientData, interp, argc, argv)
chan = NULL;
}
+ if ((cmdName[0] == 'c') && (strncmp(cmdName, "cut", len) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " cut channelName\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tcl_CutChannel(chan);
+ return TCL_OK;
+ }
+
+ if ((cmdName[0] == 'c') &&
+ (strncmp(cmdName, "clearchannelhandlers", len) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " clearchannelhandlers channelName\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tcl_ClearChannelHandlers(chan);
+ return TCL_OK;
+ }
+
if ((cmdName[0] == 'i') && (strncmp(cmdName, "info", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
@@ -4318,7 +4972,7 @@ TestChannelCmd(clientData, interp, argc, argv)
return TCL_ERROR;
}
Tcl_AppendElement(interp, argv[2]);
- Tcl_AppendElement(interp, chanPtr->typePtr->typeName);
+ Tcl_AppendElement(interp, Tcl_ChannelName(chanPtr->typePtr));
if (statePtr->flags & TCL_READABLE) {
Tcl_AppendElement(interp, "read");
} else {
@@ -4407,7 +5061,7 @@ TestChannelCmd(clientData, interp, argc, argv)
TclFormatInt(buf, IOQueued);
Tcl_AppendElement(interp, buf);
- TclFormatInt(buf, Tcl_Tell((Tcl_Channel) chanPtr));
+ TclFormatInt(buf, (int)Tcl_Tell((Tcl_Channel) chanPtr));
Tcl_AppendElement(interp, buf);
TclFormatInt(buf, statePtr->refCount);
@@ -4434,6 +5088,28 @@ TestChannelCmd(clientData, interp, argc, argv)
return TCL_OK;
}
+ if ((cmdName[0] == 'i') && (strncmp(cmdName, "isshared", len) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "channel name required", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ TclFormatInt(buf, Tcl_IsChannelShared(chan));
+ Tcl_AppendResult(interp, buf, (char *) NULL);
+ return TCL_OK;
+ }
+
+ if ((cmdName[0] == 'i') && (strncmp(cmdName, "isstandard", len) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "channel name required", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ TclFormatInt(buf, Tcl_IsStandardChannel(chan));
+ Tcl_AppendResult(interp, buf, (char *) NULL);
+ return TCL_OK;
+ }
+
if ((cmdName[0] == 'm') && (strncmp(cmdName, "mode", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "channel name required",
@@ -4454,6 +5130,18 @@ TestChannelCmd(clientData, interp, argc, argv)
return TCL_OK;
}
+ if ((cmdName[0] == 'm') && (strncmp(cmdName, "mthread", len) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "channel name required",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ TclFormatInt(buf, (long) Tcl_GetChannelThread(chan));
+ Tcl_AppendResult(interp, buf, (char *) NULL);
+ return TCL_OK;
+ }
+
if ((cmdName[0] == 'n') && (strncmp(cmdName, "name", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "channel name required",
@@ -4543,13 +5231,23 @@ TestChannelCmd(clientData, interp, argc, argv)
return TCL_OK;
}
+ if ((cmdName[0] == 's') && (strncmp(cmdName, "splice", len) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "channel name required", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ Tcl_SpliceChannel(chan);
+ return TCL_OK;
+ }
+
if ((cmdName[0] == 't') && (strncmp(cmdName, "type", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "channel name required",
(char *) NULL);
return TCL_ERROR;
}
- Tcl_AppendResult(interp, chanPtr->typePtr->typeName,
+ Tcl_AppendResult(interp, Tcl_ChannelName(chanPtr->typePtr),
(char *) NULL);
return TCL_OK;
}
@@ -4605,7 +5303,8 @@ TestChannelCmd(clientData, interp, argc, argv)
}
Tcl_AppendResult(interp, "bad option \"", cmdName, "\": should be ",
- "info, open, readable, writable, transform, unstack",
+ "cut, clearchannelhandlers, info, isshared, mode, open, "
+ "readable, splice, writable, transform, unstack",
(char *) NULL);
return TCL_ERROR;
}
@@ -4628,18 +5327,18 @@ TestChannelCmd(clientData, interp, argc, argv)
*/
/* ARGSUSED */
-int
+static int
TestChannelEventCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ CONST char **argv; /* Argument strings. */
{
Tcl_Obj *resultListPtr;
Channel *chanPtr;
ChannelState *statePtr; /* state info for channel */
EventScriptRecord *esPtr, *prevEsPtr, *nextEsPtr;
- char *cmd;
+ CONST char *cmd;
int index, i, mask, len;
if ((argc < 3) || (argc > 5)) {
@@ -4823,4 +5522,440 @@ TestChannelEventCmd(dummy, interp, argc, argv)
"add, delete, list, set, or removeall", (char *) NULL);
return TCL_ERROR;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TestWrongNumArgsObjCmd --
+ *
+ * Test the Tcl_WrongNumArgs function.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Sets interpreter result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TestWrongNumArgsObjCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ int i, length;
+ char *msg;
+
+ if (objc < 3) {
+ /*
+ * Don't use Tcl_WrongNumArgs here, as that is the function
+ * we want to test!
+ */
+ Tcl_SetResult(interp, "insufficient arguments", TCL_STATIC);
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIntFromObj(interp, objv[1], &i) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ msg = Tcl_GetStringFromObj(objv[2], &length);
+ if (length == 0) {
+ msg = NULL;
+ }
+
+ if (i > objc - 3) {
+ /*
+ * Asked for more arguments than were given.
+ */
+ Tcl_SetResult(interp, "insufficient arguments", TCL_STATIC);
+ return TCL_ERROR;
+ }
+
+ Tcl_WrongNumArgs(interp, i, &(objv[3]), msg);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TestGetIndexFromObjStructObjCmd --
+ *
+ * Test the Tcl_GetIndexFromObjStruct function.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Sets interpreter result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TestGetIndexFromObjStructObjCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *ary[] = {
+ "a", "b", "c", "d", "e", "f", (char *)NULL,(char *)NULL
+ };
+ int idx,target;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "argument targetvalue");
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIndexFromObjStruct(interp, objv[1], ary, 2*sizeof(char *),
+ "dummy", 0, &idx) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[2], &target) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (idx != target) {
+ char buffer[64];
+ sprintf(buffer, "%d", idx);
+ Tcl_AppendResult(interp, "index value comparison failed: got ",
+ buffer, NULL);
+ sprintf(buffer, "%d", target);
+ Tcl_AppendResult(interp, " when ", buffer, " expected", NULL);
+ return TCL_ERROR;
+ }
+ Tcl_WrongNumArgs(interp, 3, objv, NULL);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TestFilesystemObjCmd --
+ *
+ * This procedure implements the "testfilesystem" command. It is
+ * used to test Tcl_FSRegister, Tcl_FSUnregister, and can be used
+ * to test that the pluggable filesystem works.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Inserts or removes a filesystem from Tcl's stack.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TestFilesystemObjCmd(dummy, interp, objc, objv)
+ ClientData dummy;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+{
+ int res, boolVal;
+ char *msg;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "boolean");
+ return TCL_ERROR;
+ }
+ if (Tcl_GetBooleanFromObj(interp, objv[1], &boolVal) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (boolVal) {
+ res = Tcl_FSRegister((ClientData)interp, &testReportingFilesystem);
+ msg = (res == TCL_OK) ? "registered" : "failed";
+ } else {
+ res = Tcl_FSUnregister(&testReportingFilesystem);
+ msg = (res == TCL_OK) ? "unregistered" : "failed";
+ }
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ return res;
+}
+
+static int
+TestReportInFilesystem(Tcl_Obj *pathPtr, ClientData *clientDataPtr)
+{
+ static Tcl_Obj* lastPathPtr = NULL;
+
+ if (pathPtr == lastPathPtr) {
+ /* Reject all files second time around */
+ return -1;
+ } else {
+ Tcl_Obj * newPathPtr;
+ /* Try to claim all files first time around */
+
+ newPathPtr = Tcl_DuplicateObj(pathPtr);
+ lastPathPtr = newPathPtr;
+ Tcl_IncrRefCount(newPathPtr);
+ if (Tcl_FSGetFileSystemForPath(newPathPtr) == NULL) {
+ /* Nothing claimed it. Therefore we don't either */
+ Tcl_DecrRefCount(newPathPtr);
+ lastPathPtr = NULL;
+ return -1;
+ } else {
+ lastPathPtr = NULL;
+ *clientDataPtr = (ClientData) newPathPtr;
+ return TCL_OK;
+ }
+ }
+}
+
+/*
+ * Simple helper function to extract the native vfs representation of a
+ * path object, or NULL if no such representation exists.
+ */
+static Tcl_Obj*
+TestReportGetNativePath(Tcl_Obj* pathObjPtr) {
+ return (Tcl_Obj*) Tcl_FSGetInternalRep(pathObjPtr, &testReportingFilesystem);
+}
+
+static void
+TestReportFreeInternalRep(ClientData clientData) {
+ Tcl_Obj *nativeRep = (Tcl_Obj*)clientData;
+ if (nativeRep != NULL) {
+ /* Free the path */
+ Tcl_DecrRefCount(nativeRep);
+ }
+}
+
+static ClientData
+TestReportDupInternalRep(ClientData clientData) {
+ Tcl_Obj *original = (Tcl_Obj*)clientData;
+ Tcl_IncrRefCount(original);
+ return clientData;
+}
+static void
+TestReport(cmd, path, arg2)
+ CONST char* cmd;
+ Tcl_Obj* path;
+ Tcl_Obj* arg2;
+{
+ Tcl_Interp* interp = (Tcl_Interp*) Tcl_FSData(&testReportingFilesystem);
+ if (interp == NULL) {
+ /* This is bad, but not much we can do about it */
+ } else {
+ /*
+ * No idea why I decided to program this up using the
+ * old string-based API, but there you go. We should
+ * convert it to objects.
+ */
+ Tcl_SavedResult savedResult;
+ Tcl_DString ds;
+ Tcl_DStringInit(&ds);
+ Tcl_DStringAppend(&ds, "lappend filesystemReport ",-1);
+ Tcl_DStringStartSublist(&ds);
+ Tcl_DStringAppendElement(&ds, cmd);
+ if (path != NULL) {
+ Tcl_DStringAppendElement(&ds, Tcl_GetString(path));
+ }
+ if (arg2 != NULL) {
+ Tcl_DStringAppendElement(&ds, Tcl_GetString(arg2));
+ }
+ Tcl_DStringEndSublist(&ds);
+ Tcl_SaveResult(interp, &savedResult);
+ Tcl_Eval(interp, Tcl_DStringValue(&ds));
+ Tcl_DStringFree(&ds);
+ Tcl_RestoreResult(interp, &savedResult);
+ }
+}
+
+static int
+TestReportStat(path, buf)
+ Tcl_Obj *path; /* Path of file to stat (in current CP). */
+ Tcl_StatBuf *buf; /* Filled with results of stat call. */
+{
+ TestReport("stat",path, NULL);
+ return Tcl_FSStat(TestReportGetNativePath(path),buf);
+}
+static int
+TestReportLstat(path, buf)
+ Tcl_Obj *path; /* Path of file to stat (in current CP). */
+ Tcl_StatBuf *buf; /* Filled with results of stat call. */
+{
+ TestReport("lstat",path, NULL);
+ return Tcl_FSLstat(TestReportGetNativePath(path),buf);
+}
+static int
+TestReportAccess(path, mode)
+ Tcl_Obj *path; /* Path of file to access (in current CP). */
+ int mode; /* Permission setting. */
+{
+ TestReport("access",path,NULL);
+ return Tcl_FSAccess(TestReportGetNativePath(path),mode);
+}
+static Tcl_Channel
+TestReportOpenFileChannel(interp, fileName, mode, permissions)
+ Tcl_Interp *interp; /* Interpreter for error reporting;
+ * can be NULL. */
+ Tcl_Obj *fileName; /* Name of file to open. */
+ int mode; /* POSIX open mode. */
+ int permissions; /* If the open involves creating a
+ * file, with what modes to create
+ * it? */
+{
+ TestReport("open",fileName, NULL);
+ return TclpOpenFileChannel(interp, TestReportGetNativePath(fileName),
+ mode, permissions);
+}
+
+static int
+TestReportMatchInDirectory(interp, resultPtr, dirPtr, pattern, types)
+ Tcl_Interp *interp; /* Interpreter to receive results. */
+ Tcl_Obj *resultPtr; /* Directory separators to pass to TclDoGlob. */
+ Tcl_Obj *dirPtr; /* Contains path to directory to search. */
+ CONST char *pattern; /* Pattern to match against. */
+ Tcl_GlobTypeData *types; /* Object containing list of acceptable types.
+ * May be NULL. */
+{
+ TestReport("matchindirectory",dirPtr, NULL);
+ return Tcl_FSMatchInDirectory(interp, resultPtr,
+ TestReportGetNativePath(dirPtr), pattern,
+ types);
+}
+static int
+TestReportChdir(dirName)
+ Tcl_Obj *dirName;
+{
+ TestReport("chdir",dirName,NULL);
+ return Tcl_FSChdir(TestReportGetNativePath(dirName));
+}
+static int
+TestReportLoadFile(interp, fileName,
+ handlePtr, unloadProcPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Obj *fileName; /* Name of the file containing the desired
+ * code. */
+ Tcl_LoadHandle *handlePtr; /* Filled with token for dynamically loaded
+ * file which will be passed back to
+ * (*unloadProcPtr)() to unload the file. */
+ Tcl_FSUnloadFileProc **unloadProcPtr;
+ /* Filled with address of Tcl_FSUnloadFileProc
+ * function which should be used for
+ * this file. */
+{
+ TestReport("loadfile",fileName,NULL);
+ return Tcl_FSLoadFile(interp, TestReportGetNativePath(fileName), NULL, NULL,
+ NULL, NULL, handlePtr, unloadProcPtr);
+}
+static Tcl_Obj *
+TestReportLink(path, to, linkType)
+ Tcl_Obj *path; /* Path of file to readlink or link */
+ Tcl_Obj *to; /* Path of file to link to, or NULL */
+ int linkType;
+{
+ TestReport("link",path,to);
+ return Tcl_FSLink(TestReportGetNativePath(path), to, linkType);
+}
+static int
+TestReportRenameFile(src, dst)
+ Tcl_Obj *src; /* Pathname of file or dir to be renamed
+ * (UTF-8). */
+ Tcl_Obj *dst; /* New pathname of file or directory
+ * (UTF-8). */
+{
+ TestReport("renamefile",src,dst);
+ return Tcl_FSRenameFile(TestReportGetNativePath(src),
+ TestReportGetNativePath(dst));
+}
+static int
+TestReportCopyFile(src, dst)
+ Tcl_Obj *src; /* Pathname of file to be copied (UTF-8). */
+ Tcl_Obj *dst; /* Pathname of file to copy to (UTF-8). */
+{
+ TestReport("copyfile",src,dst);
+ return Tcl_FSCopyFile(TestReportGetNativePath(src),
+ TestReportGetNativePath(dst));
+}
+static int
+TestReportDeleteFile(path)
+ Tcl_Obj *path; /* Pathname of file to be removed (UTF-8). */
+{
+ TestReport("deletefile",path,NULL);
+ return Tcl_FSDeleteFile(TestReportGetNativePath(path));
+}
+static int
+TestReportCreateDirectory(path)
+ Tcl_Obj *path; /* Pathname of directory to create (UTF-8). */
+{
+ TestReport("createdirectory",path,NULL);
+ return Tcl_FSCreateDirectory(TestReportGetNativePath(path));
+}
+static int
+TestReportCopyDirectory(src, dst, errorPtr)
+ Tcl_Obj *src; /* Pathname of directory to be copied
+ * (UTF-8). */
+ Tcl_Obj *dst; /* Pathname of target directory (UTF-8). */
+ Tcl_Obj **errorPtr; /* If non-NULL, to be filled with UTF-8 name
+ * of file causing error. */
+{
+ TestReport("copydirectory",src,dst);
+ return Tcl_FSCopyDirectory(TestReportGetNativePath(src),
+ TestReportGetNativePath(dst), errorPtr);
+}
+static int
+TestReportRemoveDirectory(path, recursive, errorPtr)
+ Tcl_Obj *path; /* Pathname of directory to be removed
+ * (UTF-8). */
+ int recursive; /* If non-zero, removes directories that
+ * are nonempty. Otherwise, will only remove
+ * empty directories. */
+ Tcl_Obj **errorPtr; /* If non-NULL, to be filled with UTF-8 name
+ * of file causing error. */
+{
+ TestReport("removedirectory",path,NULL);
+ return Tcl_FSRemoveDirectory(TestReportGetNativePath(path), recursive,
+ errorPtr);
+}
+static CONST char**
+TestReportFileAttrStrings(fileName, objPtrRef)
+ Tcl_Obj* fileName;
+ Tcl_Obj** objPtrRef;
+{
+ TestReport("fileattributestrings",fileName,NULL);
+ return Tcl_FSFileAttrStrings(TestReportGetNativePath(fileName), objPtrRef);
+}
+static int
+TestReportFileAttrsGet(interp, index, fileName, objPtrRef)
+ Tcl_Interp *interp; /* The interpreter for error reporting. */
+ int index; /* index of the attribute command. */
+ Tcl_Obj *fileName; /* filename we are operating on. */
+ Tcl_Obj **objPtrRef; /* for output. */
+{
+ TestReport("fileattributesget",fileName,NULL);
+ return Tcl_FSFileAttrsGet(interp, index,
+ TestReportGetNativePath(fileName), objPtrRef);
+}
+static int
+TestReportFileAttrsSet(interp, index, fileName, objPtr)
+ Tcl_Interp *interp; /* The interpreter for error reporting. */
+ int index; /* index of the attribute command. */
+ Tcl_Obj *fileName; /* filename we are operating on. */
+ Tcl_Obj *objPtr; /* for input. */
+{
+ TestReport("fileattributesset",fileName,objPtr);
+ return Tcl_FSFileAttrsSet(interp, index,
+ TestReportGetNativePath(fileName), objPtr);
+}
+static int
+TestReportUtime (fileName, tval)
+ Tcl_Obj* fileName;
+ struct utimbuf *tval;
+{
+ TestReport("utime",fileName,NULL);
+ return Tcl_FSUtime(TestReportGetNativePath(fileName), tval);
+}
+static int
+TestReportNormalizePath(interp, pathPtr, nextCheckpoint)
+ Tcl_Interp *interp;
+ Tcl_Obj *pathPtr;
+ int nextCheckpoint;
+{
+ TestReport("normalizepath",pathPtr,NULL);
+ return nextCheckpoint;
+}
diff --git a/tcl/generic/tclTestObj.c b/tcl/generic/tclTestObj.c
index 3f583ff9af3..1724730c4ee 100644
--- a/tcl/generic/tclTestObj.c
+++ b/tcl/generic/tclTestObj.c
@@ -404,8 +404,17 @@ TestindexobjCmd(clientData, interp, objc, objv)
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
int allowAbbrev, index, index2, setError, i, result;
- char **argv;
- static char *tablePtr[] = {"a", "b", "check", (char *) NULL};
+ CONST char **argv;
+ static CONST char *tablePtr[] = {"a", "b", "check", (char *) NULL};
+ /*
+ * Keep this structure declaration in sync with tclIndexObj.c
+ */
+ struct IndexRep {
+ VOID *tablePtr; /* Pointer to the table of strings */
+ int offset; /* Offset between table entries */
+ int index; /* Selected index into table. */
+ };
+ struct IndexRep *indexRep;
if ((objc == 3) && (strcmp(Tcl_GetString(objv[1]),
"check") == 0)) {
@@ -415,12 +424,14 @@ TestindexobjCmd(clientData, interp, objc, objv)
* returned on subsequent lookups.
*/
- Tcl_GetIndexFromObj((Tcl_Interp *) NULL, objv[1], tablePtr,
- "token", 0, &index);
if (Tcl_GetIntFromObj(interp, objv[2], &index2) != TCL_OK) {
return TCL_ERROR;
}
- objv[1]->internalRep.twoPtrValue.ptr2 = (VOID *) index2;
+
+ Tcl_GetIndexFromObj((Tcl_Interp *) NULL, objv[1], tablePtr,
+ "token", 0, &index);
+ indexRep = (struct IndexRep *) objv[1]->internalRep.otherValuePtr;
+ indexRep->index = index2;
result = Tcl_GetIndexFromObj((Tcl_Interp *) NULL, objv[1],
tablePtr, "token", 0, &index);
if (result == TCL_OK) {
@@ -441,7 +452,7 @@ TestindexobjCmd(clientData, interp, objc, objv)
return TCL_ERROR;
}
- argv = (char **) ckalloc((unsigned) ((objc-3) * sizeof(char *)));
+ argv = (CONST char **) ckalloc((unsigned) ((objc-3) * sizeof(char *)));
for (i = 4; i < objc; i++) {
argv[i-4] = Tcl_GetString(objv[i]);
}
@@ -454,9 +465,13 @@ TestindexobjCmd(clientData, interp, objc, objv)
* the index object, clear out the object's cached state.
*/
- if ((objv[3]->typePtr == Tcl_GetObjType("index"))
- && (objv[3]->internalRep.twoPtrValue.ptr1 == (VOID *) argv)) {
- objv[3]->typePtr = NULL;
+ if ( objv[3]->typePtr != NULL
+ && !strcmp( "index", objv[3]->typePtr->name ) ) {
+ indexRep = (struct IndexRep *) objv[3]->internalRep.otherValuePtr;
+ if (indexRep->tablePtr == (VOID *) argv) {
+ objv[3]->typePtr->freeIntRepProc(objv[3]);
+ objv[3]->typePtr = NULL;
+ }
}
result = Tcl_GetIndexFromObj((setError? interp : NULL), objv[3],
@@ -773,6 +788,19 @@ TestobjCmd(clientData, interp, objc, objv)
varPtr[i] = NULL;
}
}
+ } else if ( strcmp ( subCmd, "invalidateStringRep" ) == 0 ) {
+ if ( objc != 3 ) {
+ goto wrongNumArgs;
+ }
+ index = Tcl_GetString( objv[2] );
+ if ( GetVariableIndex( interp, index, &varIndex ) != TCL_OK ) {
+ return TCL_ERROR;
+ }
+ if (CheckIfVarUnset(interp, varIndex)) {
+ return TCL_ERROR;
+ }
+ Tcl_InvalidateStringRep( varPtr[varIndex] );
+ Tcl_SetObjResult( interp, varPtr[varIndex] );
} else if (strcmp(subCmd, "newobj") == 0) {
if (objc != 3) {
goto wrongNumArgs;
@@ -881,7 +909,7 @@ TeststringobjCmd(clientData, interp, objc, objv)
#define MAX_STRINGS 11
char *index, *string, *strings[MAX_STRINGS+1];
TestString *strPtr;
- static char *options[] = {
+ static CONST char *options[] = {
"append", "appendstrings", "get", "get2", "length", "length2",
"set", "set2", "setlength", "ualloc", (char *) NULL
};
diff --git a/tcl/generic/tclThread.c b/tcl/generic/tclThread.c
index f7c3a39b786..bd7c569716a 100644
--- a/tcl/generic/tclThread.c
+++ b/tcl/generic/tclThread.c
@@ -577,4 +577,3 @@ Tcl_MutexUnlock(mutexPtr)
{
}
#endif
-
diff --git a/tcl/generic/tclThreadAlloc.c b/tcl/generic/tclThreadAlloc.c
new file mode 100644
index 00000000000..54dbfde588f
--- /dev/null
+++ b/tcl/generic/tclThreadAlloc.c
@@ -0,0 +1,955 @@
+/*
+ * tclThreadAlloc.c --
+ *
+ * This is a very fast storage allocator for used with threads (designed
+ * avoid lock contention). The basic strategy is to allocate memory in
+ * fixed size blocks from block caches.
+ *
+ * The Initial Developer of the Original Code is America Online, Inc.
+ * Portions created by AOL are Copyright (C) 1999 America Online, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$ */
+
+#if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC)
+
+#include "tclInt.h"
+
+#ifdef WIN32
+#include "tclWinInt.h"
+#else
+extern Tcl_Mutex *TclpNewAllocMutex(void);
+extern void *TclpGetAllocCache(void);
+extern void TclpSetAllocCache(void *);
+#endif
+
+/*
+ * If range checking is enabled, an additional byte will be allocated
+ * to store the magic number at the end of the requested memory.
+ */
+
+#ifndef RCHECK
+#ifdef NDEBUG
+#define RCHECK 0
+#else
+#define RCHECK 1
+#endif
+#endif
+
+/*
+ * The following define the number of Tcl_Obj's to allocate/move
+ * at a time and the high water mark to prune a per-thread cache.
+ * On a 32 bit system, sizeof(Tcl_Obj) = 24 so 800 * 24 = ~16k.
+ *
+ */
+
+#define NOBJALLOC 800
+#define NOBJHIGH 1200
+
+/*
+ * The following defines the number of buckets in the bucket
+ * cache and those block sizes from (1<<4) to (1<<(3+NBUCKETS))
+ */
+
+#define NBUCKETS 11
+#define MAXALLOC 16284
+
+/*
+ * The following union stores accounting information for
+ * each block including two small magic numbers and
+ * a bucket number when in use or a next pointer when
+ * free. The original requested size (not including
+ * the Block overhead) is also maintained.
+ */
+
+typedef struct Block {
+ union {
+ struct Block *next; /* Next in free list. */
+ struct {
+ unsigned char magic1; /* First magic number. */
+ unsigned char bucket; /* Bucket block allocated from. */
+ unsigned char unused; /* Padding. */
+ unsigned char magic2; /* Second magic number. */
+ } b_s;
+ } b_u;
+ size_t b_reqsize; /* Requested allocation size. */
+} Block;
+#define b_next b_u.next
+#define b_bucket b_u.b_s.bucket
+#define b_magic1 b_u.b_s.magic1
+#define b_magic2 b_u.b_s.magic2
+#define MAGIC 0xef
+
+/*
+ * The following structure defines a bucket of blocks with
+ * various accounting and statistics information.
+ */
+
+typedef struct Bucket {
+ Block *firstPtr;
+ int nfree;
+ int nget;
+ int nput;
+ int nwait;
+ int nlock;
+ int nrequest;
+} Bucket;
+
+/*
+ * The following structure defines a cache of buckets and objs.
+ */
+
+typedef struct Cache {
+ struct Cache *nextPtr;
+ Tcl_ThreadId owner;
+ Tcl_Obj *firstObjPtr;
+ int nobjs;
+ int nsysalloc;
+ Bucket buckets[NBUCKETS];
+} Cache;
+
+/*
+ * The following array specifies various per-bucket
+ * limits and locks. The values are statically initialized
+ * to avoid calculating them repeatedly.
+ */
+
+struct binfo {
+ size_t blocksize; /* Bucket blocksize. */
+ int maxblocks; /* Max blocks before move to share. */
+ int nmove; /* Num blocks to move to share. */
+ Tcl_Mutex *lockPtr; /* Share bucket lock. */
+} binfo[NBUCKETS] = {
+ { 16, 1024, 512, NULL},
+ { 32, 512, 256, NULL},
+ { 64, 256, 128, NULL},
+ { 128, 128, 64, NULL},
+ { 256, 64, 32, NULL},
+ { 512, 32, 16, NULL},
+ { 1024, 16, 8, NULL},
+ { 2048, 8, 4, NULL},
+ { 4096, 4, 2, NULL},
+ { 8192, 2, 1, NULL},
+ {16284, 1, 1, NULL},
+};
+
+/*
+ * Static functions defined in this file.
+ */
+
+static void LockBucket(Cache *cachePtr, int bucket);
+static void UnlockBucket(Cache *cachePtr, int bucket);
+static void PutBlocks(Cache *cachePtr, int bucket, int nmove);
+static int GetBlocks(Cache *cachePtr, int bucket);
+static Block *Ptr2Block(char *ptr);
+static char *Block2Ptr(Block *blockPtr, int bucket, unsigned int reqsize);
+static void MoveObjs(Cache *fromPtr, Cache *toPtr, int nmove);
+
+/*
+ * Local variables defined in this file and initialized at
+ * startup.
+ */
+
+static Tcl_Mutex *listLockPtr;
+static Tcl_Mutex *objLockPtr;
+static Cache sharedCache;
+static Cache *sharedPtr = &sharedCache;
+static Cache *firstCachePtr = &sharedCache;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetCache ---
+ *
+ * Gets per-thread memory cache, allocating it if necessary.
+ *
+ * Results:
+ * Pointer to cache.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Cache *
+GetCache(void)
+{
+ Cache *cachePtr;
+
+ /*
+ * Check for first-time initialization.
+ */
+
+ if (listLockPtr == NULL) {
+ Tcl_Mutex *initLockPtr;
+ int i;
+
+ initLockPtr = Tcl_GetAllocMutex();
+ Tcl_MutexLock(initLockPtr);
+ if (listLockPtr == NULL) {
+ listLockPtr = TclpNewAllocMutex();
+ objLockPtr = TclpNewAllocMutex();
+ for (i = 0; i < NBUCKETS; ++i) {
+ binfo[i].lockPtr = TclpNewAllocMutex();
+ }
+ }
+ Tcl_MutexUnlock(initLockPtr);
+ }
+
+ /*
+ * Get this thread's cache, allocating if necessary.
+ */
+
+ cachePtr = TclpGetAllocCache();
+ if (cachePtr == NULL) {
+ cachePtr = calloc(1, sizeof(Cache));
+ if (cachePtr == NULL) {
+ panic("alloc: could not allocate new cache");
+ }
+ Tcl_MutexLock(listLockPtr);
+ cachePtr->nextPtr = firstCachePtr;
+ firstCachePtr = cachePtr;
+ Tcl_MutexUnlock(listLockPtr);
+ cachePtr->owner = Tcl_GetCurrentThread();
+ TclpSetAllocCache(cachePtr);
+ }
+ return cachePtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclFreeAllocCache --
+ *
+ * Flush and delete a cache, removing from list of caches.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclFreeAllocCache(void *arg)
+{
+ Cache *cachePtr = arg;
+ Cache **nextPtrPtr;
+ register int bucket;
+
+ /*
+ * Flush blocks.
+ */
+
+ for (bucket = 0; bucket < NBUCKETS; ++bucket) {
+ if (cachePtr->buckets[bucket].nfree > 0) {
+ PutBlocks(cachePtr, bucket, cachePtr->buckets[bucket].nfree);
+ }
+ }
+
+ /*
+ * Flush objs.
+ */
+
+ if (cachePtr->nobjs > 0) {
+ Tcl_MutexLock(objLockPtr);
+ MoveObjs(cachePtr, sharedPtr, cachePtr->nobjs);
+ Tcl_MutexUnlock(objLockPtr);
+ }
+
+ /*
+ * Remove from pool list.
+ */
+
+ Tcl_MutexLock(listLockPtr);
+ nextPtrPtr = &firstCachePtr;
+ while (*nextPtrPtr != cachePtr) {
+ nextPtrPtr = &(*nextPtrPtr)->nextPtr;
+ }
+ *nextPtrPtr = cachePtr->nextPtr;
+ cachePtr->nextPtr = NULL;
+ Tcl_MutexUnlock(listLockPtr);
+#ifdef WIN32
+ TlsFree((DWORD) cachePtr);
+#else
+ free(cachePtr);
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpAlloc --
+ *
+ * Allocate memory.
+ *
+ * Results:
+ * Pointer to memory just beyond Block pointer.
+ *
+ * Side effects:
+ * May allocate more blocks for a bucket.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+TclpAlloc(unsigned int reqsize)
+{
+ Cache *cachePtr = TclpGetAllocCache();
+ Block *blockPtr;
+ register int bucket;
+ size_t size;
+
+ if (cachePtr == NULL) {
+ cachePtr = GetCache();
+ }
+
+ /*
+ * Increment the requested size to include room for
+ * the Block structure. Call malloc() directly if the
+ * required amount is greater than the largest block,
+ * otherwise pop the smallest block large enough,
+ * allocating more blocks if necessary.
+ */
+
+ blockPtr = NULL;
+ size = reqsize + sizeof(Block);
+#if RCHECK
+ ++size;
+#endif
+ if (size > MAXALLOC) {
+ bucket = NBUCKETS;
+ blockPtr = malloc(size);
+ if (blockPtr != NULL) {
+ cachePtr->nsysalloc += reqsize;
+ }
+ } else {
+ bucket = 0;
+ while (binfo[bucket].blocksize < size) {
+ ++bucket;
+ }
+ if (cachePtr->buckets[bucket].nfree || GetBlocks(cachePtr, bucket)) {
+ blockPtr = cachePtr->buckets[bucket].firstPtr;
+ cachePtr->buckets[bucket].firstPtr = blockPtr->b_next;
+ --cachePtr->buckets[bucket].nfree;
+ ++cachePtr->buckets[bucket].nget;
+ cachePtr->buckets[bucket].nrequest += reqsize;
+ }
+ }
+ if (blockPtr == NULL) {
+ return NULL;
+ }
+ return Block2Ptr(blockPtr, bucket, reqsize);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpFree --
+ *
+ * Return blocks to the thread block cache.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May move blocks to shared cache.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpFree(char *ptr)
+{
+ if (ptr != NULL) {
+ Cache *cachePtr = TclpGetAllocCache();
+ Block *blockPtr;
+ int bucket;
+
+ if (cachePtr == NULL) {
+ cachePtr = GetCache();
+ }
+
+ /*
+ * Get the block back from the user pointer and
+ * call system free directly for large blocks.
+ * Otherwise, push the block back on the bucket and
+ * move blocks to the shared cache if there are now
+ * too many free.
+ */
+
+ blockPtr = Ptr2Block(ptr);
+ bucket = blockPtr->b_bucket;
+ if (bucket == NBUCKETS) {
+ cachePtr->nsysalloc -= blockPtr->b_reqsize;
+ free(blockPtr);
+ } else {
+ cachePtr->buckets[bucket].nrequest -= blockPtr->b_reqsize;
+ blockPtr->b_next = cachePtr->buckets[bucket].firstPtr;
+ cachePtr->buckets[bucket].firstPtr = blockPtr;
+ ++cachePtr->buckets[bucket].nfree;
+ ++cachePtr->buckets[bucket].nput;
+ if (cachePtr != sharedPtr &&
+ cachePtr->buckets[bucket].nfree > binfo[bucket].maxblocks) {
+ PutBlocks(cachePtr, bucket, binfo[bucket].nmove);
+ }
+ }
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpRealloc --
+ *
+ * Re-allocate memory to a larger or smaller size.
+ *
+ * Results:
+ * Pointer to memory just beyond Block pointer.
+ *
+ * Side effects:
+ * Previous memory, if any, may be freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+TclpRealloc(char *ptr, unsigned int reqsize)
+{
+ Cache *cachePtr = TclpGetAllocCache();
+ Block *blockPtr;
+ void *new;
+ size_t size, min;
+ int bucket;
+
+ if (ptr == NULL) {
+ return TclpAlloc(reqsize);
+ }
+
+ if (cachePtr == NULL) {
+ cachePtr = GetCache();
+ }
+
+ /*
+ * If the block is not a system block and fits in place,
+ * simply return the existing pointer. Otherwise, if the block
+ * is a system block and the new size would also require a system
+ * block, call realloc() directly.
+ */
+
+ blockPtr = Ptr2Block(ptr);
+ size = reqsize + sizeof(Block);
+#if RCHECK
+ ++size;
+#endif
+ bucket = blockPtr->b_bucket;
+ if (bucket != NBUCKETS) {
+ if (bucket > 0) {
+ min = binfo[bucket-1].blocksize;
+ } else {
+ min = 0;
+ }
+ if (size > min && size <= binfo[bucket].blocksize) {
+ cachePtr->buckets[bucket].nrequest -= blockPtr->b_reqsize;
+ cachePtr->buckets[bucket].nrequest += reqsize;
+ return Block2Ptr(blockPtr, bucket, reqsize);
+ }
+ } else if (size > MAXALLOC) {
+ cachePtr->nsysalloc -= blockPtr->b_reqsize;
+ cachePtr->nsysalloc += reqsize;
+ blockPtr = realloc(blockPtr, size);
+ if (blockPtr == NULL) {
+ return NULL;
+ }
+ return Block2Ptr(blockPtr, NBUCKETS, reqsize);
+ }
+
+ /*
+ * Finally, perform an expensive malloc/copy/free.
+ */
+
+ new = TclpAlloc(reqsize);
+ if (new != NULL) {
+ if (reqsize > blockPtr->b_reqsize) {
+ reqsize = blockPtr->b_reqsize;
+ }
+ memcpy(new, ptr, reqsize);
+ TclpFree(ptr);
+ }
+ return new;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclThreadAllocObj --
+ *
+ * Allocate a Tcl_Obj from the per-thread cache.
+ *
+ * Results:
+ * Pointer to uninitialized Tcl_Obj.
+ *
+ * Side effects:
+ * May move Tcl_Obj's from shared cached or allocate new Tcl_Obj's
+ * if list is empty.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TclThreadAllocObj(void)
+{
+ register Cache *cachePtr = TclpGetAllocCache();
+ register int nmove;
+ register Tcl_Obj *objPtr;
+ Tcl_Obj *newObjsPtr;
+
+ if (cachePtr == NULL) {
+ cachePtr = GetCache();
+ }
+
+ /*
+ * Get this thread's obj list structure and move
+ * or allocate new objs if necessary.
+ */
+
+ if (cachePtr->nobjs == 0) {
+ Tcl_MutexLock(objLockPtr);
+ nmove = sharedPtr->nobjs;
+ if (nmove > 0) {
+ if (nmove > NOBJALLOC) {
+ nmove = NOBJALLOC;
+ }
+ MoveObjs(sharedPtr, cachePtr, nmove);
+ }
+ Tcl_MutexUnlock(objLockPtr);
+ if (cachePtr->nobjs == 0) {
+ cachePtr->nobjs = nmove = NOBJALLOC;
+ newObjsPtr = malloc(sizeof(Tcl_Obj) * nmove);
+ if (newObjsPtr == NULL) {
+ panic("alloc: could not allocate %d new objects", nmove);
+ }
+ while (--nmove >= 0) {
+ objPtr = &newObjsPtr[nmove];
+ objPtr->internalRep.otherValuePtr = cachePtr->firstObjPtr;
+ cachePtr->firstObjPtr = objPtr;
+ }
+ }
+ }
+
+ /*
+ * Pop the first object.
+ */
+
+ objPtr = cachePtr->firstObjPtr;
+ cachePtr->firstObjPtr = objPtr->internalRep.otherValuePtr;
+ --cachePtr->nobjs;
+ return objPtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclThreadFreeObj --
+ *
+ * Return a free Tcl_Obj to the per-thread cache.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May move free Tcl_Obj's to shared list upon hitting high
+ * water mark.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclThreadFreeObj(Tcl_Obj *objPtr)
+{
+ Cache *cachePtr = TclpGetAllocCache();
+
+ if (cachePtr == NULL) {
+ cachePtr = GetCache();
+ }
+
+ /*
+ * Get this thread's list and push on the free Tcl_Obj.
+ */
+
+ objPtr->internalRep.otherValuePtr = cachePtr->firstObjPtr;
+ cachePtr->firstObjPtr = objPtr;
+ ++cachePtr->nobjs;
+
+ /*
+ * If the number of free objects has exceeded the high
+ * water mark, move some blocks to the shared list.
+ */
+
+ if (cachePtr->nobjs > NOBJHIGH) {
+ Tcl_MutexLock(objLockPtr);
+ MoveObjs(cachePtr, sharedPtr, NOBJALLOC);
+ Tcl_MutexUnlock(objLockPtr);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_GetMemoryInfo --
+ *
+ * Return a list-of-lists of memory stats.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * List appended to given dstring.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_GetMemoryInfo(Tcl_DString *dsPtr)
+{
+ Cache *cachePtr;
+ char buf[200];
+ int n;
+
+ Tcl_MutexLock(listLockPtr);
+ cachePtr = firstCachePtr;
+ while (cachePtr != NULL) {
+ Tcl_DStringStartSublist(dsPtr);
+ if (cachePtr == sharedPtr) {
+ Tcl_DStringAppendElement(dsPtr, "shared");
+ } else {
+ sprintf(buf, "thread%d", (int) cachePtr->owner);
+ Tcl_DStringAppendElement(dsPtr, buf);
+ }
+ for (n = 0; n < NBUCKETS; ++n) {
+ sprintf(buf, "%d %d %d %d %d %d %d",
+ (int) binfo[n].blocksize,
+ cachePtr->buckets[n].nfree,
+ cachePtr->buckets[n].nget,
+ cachePtr->buckets[n].nput,
+ cachePtr->buckets[n].nrequest,
+ cachePtr->buckets[n].nlock,
+ cachePtr->buckets[n].nwait);
+ Tcl_DStringAppendElement(dsPtr, buf);
+ }
+ Tcl_DStringEndSublist(dsPtr);
+ cachePtr = cachePtr->nextPtr;
+ }
+ Tcl_MutexUnlock(listLockPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MoveObjs --
+ *
+ * Move Tcl_Obj's between caches.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MoveObjs(Cache *fromPtr, Cache *toPtr, int nmove)
+{
+ register Tcl_Obj *objPtr = fromPtr->firstObjPtr;
+ Tcl_Obj *fromFirstObjPtr = objPtr;
+
+ toPtr->nobjs += nmove;
+ fromPtr->nobjs -= nmove;
+
+ /*
+ * Find the last object to be moved; set the next one
+ * (the first one not to be moved) as the first object
+ * in the 'from' cache.
+ */
+
+ while (--nmove) {
+ objPtr = objPtr->internalRep.otherValuePtr;
+ }
+ fromPtr->firstObjPtr = objPtr->internalRep.otherValuePtr;
+
+ /*
+ * Move all objects as a block - they are already linked to
+ * each other, we just have to update the first and last.
+ */
+
+ objPtr->internalRep.otherValuePtr = toPtr->firstObjPtr;
+ toPtr->firstObjPtr = fromFirstObjPtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Block2Ptr, Ptr2Block --
+ *
+ * Convert between internal blocks and user pointers.
+ *
+ * Results:
+ * User pointer or internal block.
+ *
+ * Side effects:
+ * Invalid blocks will abort the server.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+Block2Ptr(Block *blockPtr, int bucket, unsigned int reqsize)
+{
+ register void *ptr;
+
+ blockPtr->b_magic1 = blockPtr->b_magic2 = MAGIC;
+ blockPtr->b_bucket = bucket;
+ blockPtr->b_reqsize = reqsize;
+ ptr = ((void *) (blockPtr + 1));
+#if RCHECK
+ ((unsigned char *)(ptr))[reqsize] = MAGIC;
+#endif
+ return (char *) ptr;
+}
+
+static Block *
+Ptr2Block(char *ptr)
+{
+ register Block *blockPtr;
+
+ blockPtr = (((Block *) ptr) - 1);
+ if (blockPtr->b_magic1 != MAGIC
+#if RCHECK
+ || ((unsigned char *) ptr)[blockPtr->b_reqsize] != MAGIC
+#endif
+ || blockPtr->b_magic2 != MAGIC) {
+ panic("alloc: invalid block: %p: %x %x %x\n",
+ blockPtr, blockPtr->b_magic1, blockPtr->b_magic2,
+ ((unsigned char *) ptr)[blockPtr->b_reqsize]);
+ }
+ return blockPtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LockBucket, UnlockBucket --
+ *
+ * Set/unset the lock to access a bucket in the shared cache.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Lock activity and contention are monitored globally and on
+ * a per-cache basis.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+LockBucket(Cache *cachePtr, int bucket)
+{
+#if 0
+ if (Tcl_MutexTryLock(binfo[bucket].lockPtr) != TCL_OK) {
+ Tcl_MutexLock(binfo[bucket].lockPtr);
+ ++cachePtr->buckets[bucket].nwait;
+ ++sharedPtr->buckets[bucket].nwait;
+ }
+#else
+ Tcl_MutexLock(binfo[bucket].lockPtr);
+#endif
+ ++cachePtr->buckets[bucket].nlock;
+ ++sharedPtr->buckets[bucket].nlock;
+}
+
+
+static void
+UnlockBucket(Cache *cachePtr, int bucket)
+{
+ Tcl_MutexUnlock(binfo[bucket].lockPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PutBlocks --
+ *
+ * Return unused blocks to the shared cache.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+PutBlocks(Cache *cachePtr, int bucket, int nmove)
+{
+ register Block *lastPtr, *firstPtr;
+ register int n = nmove;
+
+ /*
+ * Before acquiring the lock, walk the block list to find
+ * the last block to be moved.
+ */
+
+ firstPtr = lastPtr = cachePtr->buckets[bucket].firstPtr;
+ while (--n > 0) {
+ lastPtr = lastPtr->b_next;
+ }
+ cachePtr->buckets[bucket].firstPtr = lastPtr->b_next;
+ cachePtr->buckets[bucket].nfree -= nmove;
+
+ /*
+ * Aquire the lock and place the list of blocks at the front
+ * of the shared cache bucket.
+ */
+
+ LockBucket(cachePtr, bucket);
+ lastPtr->b_next = sharedPtr->buckets[bucket].firstPtr;
+ sharedPtr->buckets[bucket].firstPtr = firstPtr;
+ sharedPtr->buckets[bucket].nfree += nmove;
+ UnlockBucket(cachePtr, bucket);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetBlocks --
+ *
+ * Get more blocks for a bucket.
+ *
+ * Results:
+ * 1 if blocks where allocated, 0 otherwise.
+ *
+ * Side effects:
+ * Cache may be filled with available blocks.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GetBlocks(Cache *cachePtr, int bucket)
+{
+ register Block *blockPtr;
+ register int n;
+ register size_t size;
+
+ /*
+ * First, atttempt to move blocks from the shared cache. Note
+ * the potentially dirty read of nfree before acquiring the lock
+ * which is a slight performance enhancement. The value is
+ * verified after the lock is actually acquired.
+ */
+
+ if (cachePtr != sharedPtr && sharedPtr->buckets[bucket].nfree > 0) {
+ LockBucket(cachePtr, bucket);
+ if (sharedPtr->buckets[bucket].nfree > 0) {
+
+ /*
+ * Either move the entire list or walk the list to find
+ * the last block to move.
+ */
+
+ n = binfo[bucket].nmove;
+ if (n >= sharedPtr->buckets[bucket].nfree) {
+ cachePtr->buckets[bucket].firstPtr =
+ sharedPtr->buckets[bucket].firstPtr;
+ cachePtr->buckets[bucket].nfree =
+ sharedPtr->buckets[bucket].nfree;
+ sharedPtr->buckets[bucket].firstPtr = NULL;
+ sharedPtr->buckets[bucket].nfree = 0;
+ } else {
+ blockPtr = sharedPtr->buckets[bucket].firstPtr;
+ cachePtr->buckets[bucket].firstPtr = blockPtr;
+ sharedPtr->buckets[bucket].nfree -= n;
+ cachePtr->buckets[bucket].nfree = n;
+ while (--n > 0) {
+ blockPtr = blockPtr->b_next;
+ }
+ sharedPtr->buckets[bucket].firstPtr = blockPtr->b_next;
+ blockPtr->b_next = NULL;
+ }
+ }
+ UnlockBucket(cachePtr, bucket);
+ }
+
+ if (cachePtr->buckets[bucket].nfree == 0) {
+
+ /*
+ * If no blocks could be moved from shared, first look for a
+ * larger block in this cache to split up.
+ */
+
+ blockPtr = NULL;
+ n = NBUCKETS;
+ size = 0; /* lint */
+ while (--n > bucket) {
+ if (cachePtr->buckets[n].nfree > 0) {
+ size = binfo[n].blocksize;
+ blockPtr = cachePtr->buckets[n].firstPtr;
+ cachePtr->buckets[n].firstPtr = blockPtr->b_next;
+ --cachePtr->buckets[n].nfree;
+ break;
+ }
+ }
+
+ /*
+ * Otherwise, allocate a big new block directly.
+ */
+
+ if (blockPtr == NULL) {
+ size = MAXALLOC;
+ blockPtr = malloc(size);
+ if (blockPtr == NULL) {
+ return 0;
+ }
+ }
+
+ /*
+ * Split the larger block into smaller blocks for this bucket.
+ */
+
+ n = size / binfo[bucket].blocksize;
+ cachePtr->buckets[bucket].nfree = n;
+ cachePtr->buckets[bucket].firstPtr = blockPtr;
+ while (--n > 0) {
+ blockPtr->b_next = (Block *)
+ ((char *) blockPtr + binfo[bucket].blocksize);
+ blockPtr = blockPtr->b_next;
+ }
+ blockPtr->b_next = NULL;
+ }
+ return 1;
+}
+
+#endif /* TCL_THREADS */
diff --git a/tcl/generic/tclThreadJoin.c b/tcl/generic/tclThreadJoin.c
new file mode 100644
index 00000000000..d06c4dee016
--- /dev/null
+++ b/tcl/generic/tclThreadJoin.c
@@ -0,0 +1,311 @@
+/*
+ * tclThreadJoin.c --
+ *
+ * This file implements a platform independent emulation layer for
+ * the handling of joinable threads. The Mac and Windows platforms
+ * use this code to provide the functionality of joining threads.
+ * This code is currently not necessary on Unix.
+ *
+ * Copyright (c) 2000 by Scriptics Corporation
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#include "tclInt.h"
+
+#if defined(WIN32) || defined(MAC_TCL)
+
+/* The information about each joinable thread is remembered in a
+ * structure as defined below.
+ */
+
+typedef struct JoinableThread {
+ Tcl_ThreadId id; /* The id of the joinable thread */
+ int result; /* A place for the result after the
+ * demise of the thread */
+ int done; /* Boolean flag. Initialized to 0
+ * and set to 1 after the exit of
+ * the thread. This allows a thread
+ * requesting a join to detect when
+ * waiting is not necessary. */
+ int waitedUpon; /* Boolean flag. Initialized to 0
+ * and set to 1 by the thread waiting
+ * for this one via Tcl_JoinThread.
+ * Used to lock any other thread
+ * trying to wait on this one.
+ */
+ Tcl_Mutex threadMutex; /* The mutex used to serialize access
+ * to this structure. */
+ Tcl_Condition cond; /* This is the condition a thread has
+ * to wait upon to get notified of the
+ * end of the described thread. It is
+ * signaled indirectly by
+ * Tcl_ExitThread. */
+ struct JoinableThread* nextThreadPtr; /* Reference to the next thread in the
+ * list of joinable threads */
+} JoinableThread;
+
+/* The following variable is used to maintain the global list of all
+ * joinable threads. Usage by a thread is allowed only if the
+ * thread acquired the 'joinMutex'.
+ */
+
+TCL_DECLARE_MUTEX(joinMutex)
+
+static JoinableThread* firstThreadPtr;
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclJoinThread --
+ *
+ * This procedure waits for the exit of the thread with the specified
+ * id and returns its result.
+ *
+ * Results:
+ * A standard tcl result signaling the overall success/failure of the
+ * operation and an integer result delivered by the thread which was
+ * waited upon.
+ *
+ * Side effects:
+ * Deallocates the memory allocated by TclRememberJoinableThread.
+ * Removes the data associated to the thread waited upon from the
+ * list of joinable threads.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclJoinThread(id, result)
+ Tcl_ThreadId id; /* The id of the thread to wait upon. */
+ int* result; /* Reference to a location for the result
+ * of the thread we are waiting upon. */
+{
+ /* Steps done here:
+ * i. Acquire the joinMutex and search for the thread.
+ * ii. Error out if it could not be found.
+ * iii. If found, switch from exclusive access to the list to exclusive
+ * access to the thread structure.
+ * iv. Error out if some other is already waiting.
+ * v. Skip the waiting part of the thread is already done.
+ * vi. Wait for the thread to exit, mark it as waited upon too.
+ * vii. Get the result form the structure,
+ * viii. switch to exclusive access of the list,
+ * ix. remove the structure from the list,
+ * x. then switch back to exclusive access to the structure
+ * xi. and delete it.
+ */
+
+ JoinableThread* threadPtr;
+
+ Tcl_MutexLock (&joinMutex);
+
+ for (threadPtr = firstThreadPtr;
+ (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);
+ threadPtr = threadPtr->nextThreadPtr)
+ /* empty body */
+ ;
+
+ if (threadPtr == (JoinableThread*) NULL) {
+ /* Thread not found. Either not joinable, or already waited
+ * upon and exited. Whatever, an error is in order.
+ */
+
+ Tcl_MutexUnlock (&joinMutex);
+ return TCL_ERROR;
+ }
+
+ /* [1] If we don't lock the structure before giving up exclusive access
+ * to the list some other thread just completing its wait on the same
+ * thread can delete the structure from under us, leaving us with a
+ * dangling pointer.
+ */
+
+ Tcl_MutexLock (&threadPtr->threadMutex);
+ Tcl_MutexUnlock (&joinMutex);
+
+ /* [2] Now that we have the structure mutex any other thread that just
+ * tries to delete structure will wait at location [3] until we are
+ * done with the structure. And in that case we are done with it
+ * rather quickly as 'waitedUpon' will be set and we will have to
+ * error out.
+ */
+
+ if (threadPtr->waitedUpon) {
+ Tcl_MutexUnlock (&threadPtr->threadMutex);
+ return TCL_ERROR;
+ }
+
+ /* We are waiting now, let other threads recognize this
+ */
+
+ threadPtr->waitedUpon = 1;
+
+ while (!threadPtr->done) {
+ Tcl_ConditionWait (&threadPtr->cond, &threadPtr->threadMutex, NULL);
+ }
+
+ /* We have to release the structure before trying to access the list
+ * again or we can run into deadlock with a thread at [1] (see above)
+ * because of us holding the structure and the other holding the list.
+ * There is no problem with dangling pointers here as 'waitedUpon == 1'
+ * is still valid and any other thread will error out and not come to
+ * this place. IOW, the fact that we are here also means that no other
+ * thread came here before us and is able to delete the structure.
+ */
+
+ Tcl_MutexUnlock (&threadPtr->threadMutex);
+ Tcl_MutexLock (&joinMutex);
+
+ /* We have to search the list again as its structure may (may, almost
+ * certainly) have changed while we were waiting. Especially now is the
+ * time to compute the predecessor in the list. Any earlier result can
+ * be dangling by now.
+ */
+
+ if (firstThreadPtr == threadPtr) {
+ firstThreadPtr = threadPtr->nextThreadPtr;
+ } else {
+ JoinableThread* prevThreadPtr;
+
+ for (prevThreadPtr = firstThreadPtr;
+ prevThreadPtr->nextThreadPtr != threadPtr;
+ prevThreadPtr = prevThreadPtr->nextThreadPtr)
+ /* empty body */
+ ;
+
+ prevThreadPtr->nextThreadPtr = threadPtr->nextThreadPtr;
+ }
+
+ Tcl_MutexUnlock (&joinMutex);
+
+ /* [3] Now that the structure is not part of the list anymore no other
+ * thread can acquire its mutex from now on. But it is possible that
+ * another thread is still holding the mutex though, see location [2].
+ * So we have to acquire the mutex one more time to wait for that thread
+ * to finish. We can (and have to) release the mutex immediately.
+ */
+
+ Tcl_MutexLock (&threadPtr->threadMutex);
+ Tcl_MutexUnlock (&threadPtr->threadMutex);
+
+ /* Copy the result to us, finalize the synchronisation objects, then
+ * free the structure and return.
+ */
+
+ *result = threadPtr->result;
+
+ Tcl_ConditionFinalize (&threadPtr->cond);
+ Tcl_MutexFinalize (&threadPtr->threadMutex);
+ ckfree ((VOID*) threadPtr);
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclRememberJoinableThread --
+ *
+ * This procedure remebers a thread as joinable. Only a call to
+ * TclJoinThread will remove the structre created (and initialized)
+ * here. IOW, not waiting upon a joinable thread will cause memory
+ * leaks.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Allocates memory, adds it to the global list of all joinable
+ * threads.
+ *
+ *----------------------------------------------------------------------
+ */
+
+VOID
+TclRememberJoinableThread(id)
+ Tcl_ThreadId id; /* The thread to remember as joinable */
+{
+ JoinableThread* threadPtr;
+
+ threadPtr = (JoinableThread*) ckalloc (sizeof (JoinableThread));
+ threadPtr->id = id;
+ threadPtr->done = 0;
+ threadPtr->waitedUpon = 0;
+ threadPtr->threadMutex = (Tcl_Mutex) NULL;
+ threadPtr->cond = (Tcl_Condition) NULL;
+
+ Tcl_MutexLock (&joinMutex);
+
+ threadPtr->nextThreadPtr = firstThreadPtr;
+ firstThreadPtr = threadPtr;
+
+ Tcl_MutexUnlock (&joinMutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclSignalExitThread --
+ *
+ * This procedure signals that the specified thread is done with
+ * its work. If the thread is joinable this signal is propagated
+ * to the thread waiting upon it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Modifies the associated structure to hold the result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+VOID
+TclSignalExitThread(id,result)
+ Tcl_ThreadId id; /* Id of the thread signaling its exit */
+ int result; /* The result from the thread */
+{
+ JoinableThread* threadPtr;
+
+ Tcl_MutexLock (&joinMutex);
+
+ for (threadPtr = firstThreadPtr;
+ (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);
+ threadPtr = threadPtr->nextThreadPtr)
+ /* empty body */
+ ;
+
+ if (threadPtr == (JoinableThread*) NULL) {
+ /* Thread not found. Not joinable. No problem, nothing to do.
+ */
+
+ Tcl_MutexUnlock (&joinMutex);
+ return;
+ }
+
+ /* Switch over the exclusive access from the list to the structure,
+ * then store the result, set the flag and notify the waiting thread,
+ * provided that it exists. The order of lock/unlock ensures that a
+ * thread entering 'TclJoinThread' will not interfere with us.
+ */
+
+ Tcl_MutexLock (&threadPtr->threadMutex);
+ Tcl_MutexUnlock (&joinMutex);
+
+ threadPtr->done = 1;
+ threadPtr->result = result;
+
+ if (threadPtr->waitedUpon) {
+ Tcl_ConditionNotify (&threadPtr->cond);
+ }
+
+ Tcl_MutexUnlock (&threadPtr->threadMutex);
+}
+
+#endif /* WIN32 || MAC_TCL */
diff --git a/tcl/generic/tclThreadTest.c b/tcl/generic/tclThreadTest.c
index 25a3938a009..4f73ce7c55d 100644
--- a/tcl/generic/tclThreadTest.c
+++ b/tcl/generic/tclThreadTest.c
@@ -118,7 +118,7 @@ EXTERN int TclThread_Init _ANSI_ARGS_((Tcl_Interp *interp));
EXTERN int Tcl_ThreadObjCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
EXTERN int TclCreateThread _ANSI_ARGS_((Tcl_Interp *interp,
- CONST char *script));
+ char *script, int joinable));
EXTERN int TclThreadList _ANSI_ARGS_((Tcl_Interp *interp));
EXTERN int TclThreadSend _ANSI_ARGS_((Tcl_Interp *interp, Tcl_ThreadId id,
char *script, int wait));
@@ -126,7 +126,7 @@ EXTERN int TclThreadSend _ANSI_ARGS_((Tcl_Interp *interp, Tcl_ThreadId id,
#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLIMPORT
-Tcl_ThreadCreateType NewThread _ANSI_ARGS_((ClientData clientData));
+Tcl_ThreadCreateType NewTestThread _ANSI_ARGS_((ClientData clientData));
static void ListRemove _ANSI_ARGS_((ThreadSpecificData *tsdPtr));
static void ListUpdateInner _ANSI_ARGS_((ThreadSpecificData *tsdPtr));
static int ThreadEventProc _ANSI_ARGS_((Tcl_Event *evPtr, int mask));
@@ -175,13 +175,14 @@ TclThread_Init(interp)
* This procedure is invoked to process the "testthread" Tcl command.
* See the user documentation for details on what it does.
*
- * thread create
+ * thread create ?-joinable? ?script?
* thread send id ?-async? script
* thread exit
* thread info id
* thread names
* thread wait
* thread errorproc proc
+ * thread join id
*
* Results:
* A standard Tcl result.
@@ -202,10 +203,11 @@ Tcl_ThreadObjCmd(dummy, interp, objc, objv)
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
int option;
- static char *threadOptions[] = {"create", "exit", "id", "names",
- "send", "wait", "errorproc", (char *) NULL};
- enum options {THREAD_CREATE, THREAD_EXIT, THREAD_ID, THREAD_NAMES,
- THREAD_SEND, THREAD_WAIT, THREAD_ERRORPROC};
+ static CONST char *threadOptions[] = {"create", "exit", "id", "join", "names",
+ "send", "wait", "errorproc",
+ (char *) NULL};
+ enum options {THREAD_CREATE, THREAD_EXIT, THREAD_ID, THREAD_JOIN,
+ THREAD_NAMES, THREAD_SEND, THREAD_WAIT, THREAD_ERRORPROC};
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option ?args?");
@@ -231,15 +233,51 @@ Tcl_ThreadObjCmd(dummy, interp, objc, objv)
switch ((enum options)option) {
case THREAD_CREATE: {
char *script;
+ int joinable, len;
+
if (objc == 2) {
- script = "testthread wait"; /* Just enter the event loop */
+ /* Neither joinable nor special script
+ */
+
+ joinable = 0;
+ script = "testthread wait"; /* Just enter the event loop */
+
} else if (objc == 3) {
- script = Tcl_GetString(objv[2]);
+ /* Possibly -joinable, then no special script,
+ * no joinable, then its a script.
+ */
+
+ script = Tcl_GetString(objv[2]);
+ len = strlen (script);
+
+ if ((len > 1) &&
+ (script [0] == '-') && (script [1] == 'j') &&
+ (0 == strncmp (script, "-joinable", (size_t) len))) {
+ joinable = 1;
+ script = "testthread wait"; /* Just enter the event loop
+ */
+ } else {
+ /* Remember the script */
+ joinable = 0;
+ }
+ } else if (objc == 4) {
+ /* Definitely a script available, but is the flag
+ * -joinable ?
+ */
+
+ script = Tcl_GetString(objv[2]);
+ len = strlen (script);
+
+ joinable = ((len > 1) &&
+ (script [0] == '-') && (script [1] == 'j') &&
+ (0 == strncmp (script, "-joinable", (size_t) len)));
+
+ script = Tcl_GetString(objv[3]);
} else {
- Tcl_WrongNumArgs(interp, 2, objv, "?script?");
+ Tcl_WrongNumArgs(interp, 2, objv, "?-joinable? ?script?");
return TCL_ERROR;
}
- return TclCreateThread(interp, script);
+ return TclCreateThread(interp, script, joinable);
}
case THREAD_EXIT: {
if (objc > 2) {
@@ -259,6 +297,28 @@ Tcl_ThreadObjCmd(dummy, interp, objc, objv)
Tcl_WrongNumArgs(interp, 2, objv, NULL);
return TCL_ERROR;
}
+ case THREAD_JOIN: {
+ long id;
+ int result, status;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "join id");
+ return TCL_ERROR;
+ }
+ if (Tcl_GetLongFromObj(interp, objv[2], &id) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ result = Tcl_JoinThread ((Tcl_ThreadId) id, &status);
+ if (result == TCL_OK) {
+ Tcl_SetIntObj (Tcl_GetObjResult (interp), status);
+ } else {
+ char buf [20];
+ sprintf (buf, "%ld", id);
+ Tcl_AppendResult (interp, "cannot join thread ", buf, NULL);
+ }
+ return result;
+ }
case THREAD_NAMES: {
if (objc > 2) {
Tcl_WrongNumArgs(interp, 2, objv, NULL);
@@ -343,20 +403,23 @@ Tcl_ThreadObjCmd(dummy, interp, objc, objv)
/* ARGSUSED */
int
-TclCreateThread(interp, script)
+TclCreateThread(interp, script, joinable)
Tcl_Interp *interp; /* Current interpreter. */
- CONST char *script; /* Script to execute */
+ char *script; /* Script to execute */
+ int joinable; /* Flag, joinable thread or not */
{
ThreadCtrl ctrl;
Tcl_ThreadId id;
- ctrl.script = (char *) script;
+ ctrl.script = script;
ctrl.condWait = NULL;
ctrl.flags = 0;
+ joinable = joinable ? TCL_THREAD_JOINABLE : TCL_THREAD_NOFLAGS;
+
Tcl_MutexLock(&threadMutex);
- if (Tcl_CreateThread(&id, NewThread, (ClientData) &ctrl,
- TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS) != TCL_OK) {
+ if (Tcl_CreateThread(&id, NewTestThread, (ClientData) &ctrl,
+ TCL_THREAD_STACK_DEFAULT, joinable) != TCL_OK) {
Tcl_MutexUnlock(&threadMutex);
Tcl_AppendResult(interp,"can't create a new thread",0);
ckfree((void*)ctrl.script);
@@ -377,7 +440,7 @@ TclCreateThread(interp, script)
/*
*------------------------------------------------------------------------
*
- * NewThread --
+ * NewTestThread --
*
* This routine is the "main()" for a new thread whose task is to
* execute a single TCL script. The argument to this function is
@@ -403,7 +466,7 @@ TclCreateThread(interp, script)
*------------------------------------------------------------------------
*/
Tcl_ThreadCreateType
-NewThread(clientData)
+NewTestThread(clientData)
ClientData clientData;
{
ThreadCtrl *ctrlPtr = (ThreadCtrl*)clientData;
@@ -483,8 +546,8 @@ ThreadErrorProc(interp)
Tcl_Interp *interp; /* Interp that failed */
{
Tcl_Channel errChannel;
- char *errorInfo, *script;
- char *argv[3];
+ CONST char *errorInfo, *argv[3];
+ char *script;
char buf[TCL_DOUBLE_SPACE+1];
sprintf(buf, "%ld", (long) Tcl_GetCurrentThread());
@@ -780,7 +843,7 @@ TclThreadSend(interp, id, script, wait)
*
*------------------------------------------------------------------------
*/
-int
+static int
ThreadEventProc(evPtr, mask)
Tcl_Event *evPtr; /* Really ThreadEvent */
int mask;
@@ -790,7 +853,7 @@ ThreadEventProc(evPtr, mask)
ThreadEventResult *resultPtr = threadEventPtr->resultPtr;
Tcl_Interp *interp = tsdPtr->interp;
int code;
- char *result, *errorCode, *errorInfo;
+ CONST char *result, *errorCode, *errorInfo;
if (interp == NULL) {
code = TCL_ERROR;
@@ -853,7 +916,7 @@ ThreadEventProc(evPtr, mask)
*------------------------------------------------------------------------
*/
/* ARGSUSED */
-void
+static void
ThreadFreeProc(clientData)
ClientData clientData;
{
@@ -879,7 +942,7 @@ ThreadFreeProc(clientData)
*------------------------------------------------------------------------
*/
/* ARGSUSED */
-int
+static int
ThreadDeleteEvent(eventPtr, clientData)
Tcl_Event *eventPtr; /* Really ThreadEvent */
ClientData clientData; /* dummy */
@@ -912,7 +975,7 @@ ThreadDeleteEvent(eventPtr, clientData)
*------------------------------------------------------------------------
*/
/* ARGSUSED */
-void
+static void
ThreadExitProc(clientData)
ClientData clientData;
{
@@ -964,4 +1027,3 @@ ThreadExitProc(clientData)
}
#endif /* TCL_THREADS */
-
diff --git a/tcl/generic/tclTimer.c b/tcl/generic/tclTimer.c
index 4c39fe23c2a..84be1bbf37f 100644
--- a/tcl/generic/tclTimer.c
+++ b/tcl/generic/tclTimer.c
@@ -174,7 +174,7 @@ InitTimer()
* None.
*
* Side effects:
- * Removes the timer and idle event sources.
+ * Removes the timer and idle event sources and remaining events.
*
*----------------------------------------------------------------------
*/
@@ -183,7 +183,19 @@ static void
TimerExitProc(clientData)
ClientData clientData; /* Not used. */
{
+ ThreadSpecificData *tsdPtr =
+ (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
+
Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, NULL);
+ if (tsdPtr != NULL) {
+ register TimerHandler *timerHandlerPtr;
+ timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
+ while (timerHandlerPtr != NULL) {
+ tsdPtr->firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
+ ckfree((char *) timerHandlerPtr);
+ timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
+ }
+ }
}
/*
@@ -224,7 +236,7 @@ Tcl_CreateTimerHandler(milliseconds, proc, clientData)
* Compute when the event should fire.
*/
- TclpGetTime(&time);
+ Tcl_GetTime(&time);
timerHandlerPtr->time.sec = time.sec + milliseconds/1000;
timerHandlerPtr->time.usec = time.usec + (milliseconds%1000)*1000;
if (timerHandlerPtr->time.usec >= 1000000) {
@@ -350,7 +362,7 @@ TimerSetupProc(data, flags)
* Compute the timeout for the next timer on the list.
*/
- TclpGetTime(&blockTime);
+ Tcl_GetTime(&blockTime);
blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
blockTime.usec;
@@ -401,7 +413,7 @@ TimerCheckProc(data, flags)
* Compute the timeout for the next timer on the list.
*/
- TclpGetTime(&blockTime);
+ Tcl_GetTime(&blockTime);
blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
blockTime.usec;
@@ -500,7 +512,7 @@ TimerHandlerEventProc(evPtr, flags)
tsdPtr->timerPending = 0;
currentTimerId = tsdPtr->lastTimerId;
- TclpGetTime(&time);
+ Tcl_GetTime(&time);
while (1) {
nextPtrPtr = &tsdPtr->firstTimerHandlerPtr;
timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
@@ -735,7 +747,9 @@ Tcl_AfterObjCmd(clientData, interp, objc, objv)
char *argString;
int index;
char buf[16 + TCL_INTEGER_SPACE];
- static char *afterSubCmds[] = {"cancel", "idle", "info", (char *) NULL};
+ static CONST char *afterSubCmds[] = {
+ "cancel", "idle", "info", (char *) NULL
+ };
enum afterSubCmds {AFTER_CANCEL, AFTER_IDLE, AFTER_INFO};
ThreadSpecificData *tsdPtr = InitTimer();
diff --git a/tcl/generic/tclUniData.c b/tcl/generic/tclUniData.c
index 612aba8e864..9f0c6e05ae0 100644
--- a/tcl/generic/tclUniData.c
+++ b/tcl/generic/tclUniData.c
@@ -1,5 +1,5 @@
/*
- * tclUtfData.c --
+ * tclUniData.c --
*
* Declarations of Unicode character information tables. This file is
* automatically generated by the tools/uniParse.tcl script. Do not
@@ -26,44 +26,33 @@
*/
static unsigned char pageMap[] = {
- 0, 1, 2, 3, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 7, 33,
- 7, 34, 35, 16, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
- 49, 50, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
- 55, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 77, 78,
- 81, 82, 77, 16, 16, 16, 16, 83, 84, 85, 16, 86, 87, 88, 16, 89, 90,
- 91, 92, 93, 94, 16, 16, 16, 16, 16, 16, 16, 95, 96, 97, 47, 47, 98,
- 47, 47, 99, 47, 100, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 7,
- 7, 7, 7, 101, 7, 7, 102, 103, 104, 105, 106, 104, 107, 108, 109, 110,
- 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
- 125, 126, 126, 126, 126, 126, 126, 126, 127, 128, 129, 123, 130, 16,
- 16, 16, 16, 123, 131, 125, 132, 133, 134, 135, 136, 123, 123, 123,
- 123, 137, 123, 123, 138, 139, 123, 123, 138, 16, 16, 16, 16, 140, 141,
- 142, 143, 144, 145, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 146, 147, 83, 47, 148, 83, 47, 149, 150, 151, 47, 47, 152,
- 16, 16, 16, 153, 154, 155, 156, 154, 157, 158, 159, 123, 123, 123,
- 160, 123, 123, 161, 159, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 47, 47, 47, 47, 47, 47, 47,
+ 0, 1, 2, 3, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 7, 15, 16, 17,
+ 18, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 7, 32,
+ 7, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 47,
+ 48, 49, 50, 51, 52, 35, 47, 53, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 58, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 80, 81,
+ 84, 85, 80, 86, 87, 88, 89, 90, 91, 92, 35, 93, 94, 95, 35, 96, 97,
+ 98, 99, 100, 101, 102, 35, 47, 103, 104, 35, 35, 105, 106, 107, 47,
+ 47, 108, 47, 47, 109, 47, 110, 111, 47, 112, 47, 113, 114, 115, 116,
+ 114, 47, 117, 118, 35, 47, 47, 119, 90, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 120, 121, 47, 47, 122,
+ 35, 35, 35, 35, 47, 123, 124, 125, 126, 47, 127, 128, 47, 129, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 7, 7, 7, 7, 130, 7, 7, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156, 156, 156, 156, 156, 156, 156,
+ 157, 158, 159, 160, 161, 162, 35, 35, 35, 160, 163, 164, 165, 166,
+ 167, 168, 169, 160, 160, 160, 160, 170, 171, 172, 173, 174, 160, 160,
+ 175, 35, 35, 35, 35, 176, 177, 178, 179, 180, 181, 35, 35, 160, 160,
+ 160, 160, 160, 160, 160, 160, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 182, 160, 160, 155, 160, 160, 160, 160, 160, 160, 170, 183, 184, 185,
+ 90, 47, 186, 90, 47, 187, 188, 189, 47, 47, 190, 128, 35, 35, 191,
+ 192, 193, 194, 192, 195, 196, 197, 160, 160, 160, 198, 160, 160, 199,
+ 197, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
@@ -75,6 +64,7 @@ static unsigned char pageMap[] = {
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 200, 35, 35, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
@@ -102,12 +92,6 @@ static unsigned char pageMap[] = {
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
- 162, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
@@ -118,8 +102,13 @@ static unsigned char pageMap[] = {
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 201, 35, 35, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 202, 203, 204, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
@@ -128,29 +117,269 @@ static unsigned char pageMap[] = {
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
- 47, 47, 47, 47, 47, 47, 163, 16, 16, 164, 164, 164, 164, 164, 164,
- 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
- 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
- 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
- 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
- 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 47, 47, 47, 47, 47, 47, 47, 47, 47, 166,
- 16, 16, 16, 16, 16, 16, 167, 168, 169, 47, 47, 170, 171, 47, 47, 47,
- 47, 47, 47, 47, 47, 47, 47, 172, 173, 47, 174, 47, 175, 176, 16, 177,
- 178, 179, 47, 47, 47, 180, 181, 2, 182, 183, 184, 185, 186, 187
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 205, 35, 35, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 47, 47, 47, 47, 47, 47, 47, 47, 47, 208, 35, 35, 35, 35,
+ 35, 35, 209, 210, 211, 47, 47, 212, 213, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 214, 215, 47, 216, 47, 217, 218, 35, 219, 220, 221, 47,
+ 47, 47, 222, 223, 2, 224, 225, 226, 227, 228, 229, 230, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 231, 35, 232, 233,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 208, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 47, 234, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 235, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 236, 207, 207, 207, 207, 207, 207, 207, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35
};
/*
@@ -167,326 +396,413 @@ static unsigned char groupMap[] = {
10, 10, 10, 10, 10, 10, 5, 3, 6, 11, 12, 11, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 5, 7, 6, 7, 1, 2, 3, 4, 4, 4, 4, 14, 14, 11, 14, 15, 16,
- 7, 8, 14, 11, 14, 7, 17, 17, 11, 15, 14, 3, 11, 17, 15, 18, 17, 17,
+ 7, 8, 14, 11, 14, 7, 17, 17, 11, 18, 14, 3, 11, 17, 15, 19, 17, 17,
17, 3, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 7, 10, 10, 10, 10, 10, 10, 10, 15,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 7, 13, 13, 13, 13, 13, 13, 13, 19, 20, 21,
- 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20,
- 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21,
- 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 22, 23, 20, 21, 20,
- 21, 20, 21, 15, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20,
- 21, 20, 21, 15, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20,
- 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21,
- 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 24,
- 20, 21, 20, 21, 20, 21, 25, 15, 26, 20, 21, 20, 21, 27, 20, 21, 28,
- 28, 20, 21, 15, 29, 30, 31, 20, 21, 28, 32, 15, 33, 34, 20, 21, 15,
- 15, 33, 35, 15, 36, 20, 21, 20, 21, 20, 21, 37, 20, 21, 37, 38, 15,
- 20, 21, 37, 20, 21, 39, 39, 20, 21, 20, 21, 40, 20, 21, 15, 38, 20,
- 21, 38, 38, 38, 38, 38, 38, 41, 42, 43, 41, 42, 43, 41, 42, 43, 20,
- 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 44, 20,
- 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21,
- 15, 41, 42, 43, 20, 21, 0, 0, 0, 0, 20, 21, 20, 21, 20, 21, 20, 21,
- 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20,
- 21, 20, 21, 20, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 45,
- 46, 15, 47, 47, 15, 48, 15, 49, 15, 15, 15, 15, 47, 15, 15, 50, 15,
- 15, 15, 15, 51, 52, 15, 15, 15, 15, 15, 52, 15, 15, 53, 15, 15, 54,
- 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 55, 15, 15, 55, 15, 15, 15,
- 15, 55, 15, 56, 56, 15, 15, 15, 15, 15, 15, 57, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
- 0, 0, 0, 0, 0, 0, 0, 58, 58, 58, 58, 58, 58, 58, 58, 58, 11, 11, 58,
- 58, 58, 58, 58, 58, 58, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 11, 11, 58, 58, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 0, 58, 58, 58, 58, 58, 11, 11, 11, 11, 11, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 59, 59, 59, 59, 59,
- 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
- 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 59, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11,
- 0, 0, 0, 0, 58, 0, 0, 0, 3, 0, 0, 0, 0, 0, 11, 11, 61, 3, 62, 62, 62,
- 0, 63, 0, 64, 64, 15, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
- 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 65, 66,
- 66, 66, 15, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 67, 13, 13, 13, 13, 13, 13, 13, 13, 13, 68, 69, 69, 0,
- 70, 71, 72, 72, 72, 73, 74, 0, 0, 0, 72, 0, 72, 0, 72, 0, 72, 0, 20,
- 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 75, 76, 44, 38,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 77, 77, 77, 77, 77, 77,
- 77, 77, 77, 77, 77, 0, 77, 77, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 13, 13, 13, 13, 13, 13, 7, 13, 13, 13, 13, 13, 13, 13, 20, 21, 22,
+ 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21,
+ 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22,
+ 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 23, 24, 21, 22, 21,
+ 22, 21, 22, 15, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21,
+ 22, 21, 22, 15, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21,
+ 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22,
+ 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 25,
+ 21, 22, 21, 22, 21, 22, 26, 15, 27, 21, 22, 21, 22, 28, 21, 22, 29,
+ 29, 21, 22, 15, 30, 31, 32, 21, 22, 29, 33, 34, 35, 36, 21, 22, 15,
+ 15, 35, 37, 15, 38, 21, 22, 21, 22, 21, 22, 39, 21, 22, 39, 15, 15,
+ 21, 22, 39, 21, 22, 40, 40, 21, 22, 21, 22, 41, 21, 22, 15, 42, 21,
+ 22, 15, 43, 42, 42, 42, 42, 44, 45, 46, 44, 45, 46, 44, 45, 46, 21,
+ 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 47, 21,
+ 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22,
+ 15, 44, 45, 46, 21, 22, 48, 49, 21, 22, 21, 22, 21, 22, 21, 22, 0,
+ 0, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22,
+ 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 50, 51, 15, 52, 52, 15, 53, 15,
+ 54, 15, 15, 15, 15, 52, 15, 15, 55, 15, 15, 15, 15, 56, 57, 15, 15,
+ 15, 15, 15, 57, 15, 15, 58, 15, 15, 59, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 60, 15, 15, 60, 15, 15, 15, 15, 60, 15, 61, 61, 15, 15,
+ 15, 15, 15, 15, 62, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 11, 11, 63, 63, 63, 63, 63, 63, 63,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 63, 63, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 63, 63, 63, 63,
+ 63, 11, 11, 11, 11, 11, 11, 11, 11, 11, 63, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64,
+ 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11,
+ 0, 0, 0, 0, 63, 0, 0, 0, 3, 0, 0, 0, 0, 0, 11, 11, 66, 3, 67, 67, 67,
+ 0, 68, 0, 69, 69, 15, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 70, 71,
+ 71, 71, 15, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 72, 13, 13, 13, 13, 13, 13, 13, 13, 13, 73, 74, 74, 0,
+ 75, 76, 77, 77, 77, 78, 79, 15, 0, 0, 21, 22, 21, 22, 21, 22, 21, 22,
+ 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 80, 81, 47,
+ 15, 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84, 84, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
- 10, 10, 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 10, 10, 10, 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 0, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
- 0, 76, 76, 20, 21, 14, 59, 59, 59, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20,
- 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 38, 20,
- 21, 20, 21, 0, 0, 20, 21, 0, 0, 20, 21, 0, 0, 0, 20, 21, 20, 21, 20,
- 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21,
- 20, 21, 20, 21, 20, 21, 0, 0, 20, 21, 20, 21, 20, 21, 20, 21, 0, 0,
- 20, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
- 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
- 78, 78, 78, 78, 78, 78, 0, 0, 58, 3, 3, 3, 3, 3, 3, 0, 79, 79, 79,
- 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
- 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
- 79, 15, 0, 3, 0, 0, 0, 0, 0, 0, 0, 59, 59, 59, 59, 59, 59, 59, 59,
- 59, 59, 59, 59, 59, 59, 59, 59, 59, 0, 59, 59, 59, 59, 59, 59, 59,
- 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 0,
- 59, 59, 59, 3, 59, 3, 59, 59, 3, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 0, 0, 0, 0, 38, 38,
- 38, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
- 3, 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 0, 0, 0, 0, 58, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 59, 59, 59, 59, 59, 59, 59, 59, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3,
- 3, 3, 3, 0, 0, 59, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 0, 0, 38, 38, 38, 38, 38, 0, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 38, 38, 38,
- 38, 3, 38, 59, 59, 59, 59, 59, 59, 59, 80, 80, 59, 59, 59, 59, 59,
- 59, 58, 58, 59, 59, 14, 59, 59, 59, 59, 0, 0, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 59, 59, 81, 0, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 0, 59, 38, 81,
- 81, 81, 59, 59, 59, 59, 59, 59, 59, 59, 81, 81, 81, 81, 59, 0, 0, 38,
- 59, 59, 59, 59, 0, 0, 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 59,
- 59, 3, 3, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 59, 81, 81, 0, 38, 38, 38, 38, 38, 38, 38,
- 38, 0, 0, 38, 38, 0, 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 38, 38, 38, 38, 38,
- 38, 38, 0, 38, 0, 0, 0, 38, 38, 38, 38, 0, 0, 59, 0, 81, 81, 81, 59,
- 59, 59, 59, 0, 0, 81, 81, 0, 0, 81, 81, 59, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 81, 0, 0, 0, 0, 38, 38, 0, 38, 38, 38, 59, 59, 0, 0, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 38, 38, 4, 4, 17, 17, 17, 17, 17, 17, 14, 0, 0, 0,
- 0, 0, 0, 0, 59, 0, 0, 38, 38, 38, 38, 38, 38, 0, 0, 0, 0, 38, 38, 0,
- 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 0, 38, 38, 38, 38, 38, 38, 38, 0, 38, 38, 0,
- 38, 38, 0, 38, 38, 0, 0, 59, 0, 81, 81, 81, 59, 59, 0, 0, 0, 0, 59,
- 59, 0, 0, 59, 59, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 38, 38,
- 38, 0, 38, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 59, 59,
- 38, 38, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 59, 81, 0, 38,
- 38, 38, 38, 38, 38, 38, 0, 38, 0, 38, 38, 38, 0, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 0, 38, 38, 38, 38, 38, 38, 38, 0, 38, 38, 0, 38, 38, 38, 38, 38, 0,
- 0, 59, 38, 81, 81, 81, 59, 59, 59, 59, 59, 0, 59, 59, 81, 0, 81, 81,
- 59, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0,
- 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 38, 38, 38,
- 38, 38, 38, 38, 0, 38, 38, 0, 0, 38, 38, 38, 38, 0, 0, 59, 38, 81,
- 59, 81, 59, 59, 59, 0, 0, 0, 81, 81, 0, 0, 81, 81, 59, 0, 0, 0, 0,
- 0, 0, 0, 0, 59, 81, 0, 0, 0, 0, 38, 38, 0, 38, 38, 38, 0, 0, 0, 0,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 59, 81, 0, 38, 38, 38, 38, 38, 38, 0, 0, 0, 38, 38,
- 38, 0, 38, 38, 38, 38, 0, 0, 0, 38, 38, 0, 38, 0, 38, 38, 0, 0, 0,
- 38, 38, 0, 0, 0, 38, 38, 38, 0, 0, 0, 38, 38, 38, 38, 38, 38, 38, 38,
- 0, 38, 38, 38, 0, 0, 0, 0, 81, 81, 59, 81, 81, 0, 0, 0, 81, 81, 81,
- 0, 81, 81, 81, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 17, 17, 17, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 81, 81, 0, 38, 38, 38, 38,
- 38, 38, 38, 38, 0, 38, 38, 38, 0, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 0, 38, 38, 38, 38, 38, 0, 0, 0, 0,
- 59, 59, 59, 81, 81, 81, 81, 0, 59, 59, 59, 0, 59, 59, 59, 59, 0, 0,
- 0, 0, 0, 0, 0, 59, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 38, 0, 0, 0,
- 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 81, 81, 0, 38, 38, 38, 38, 38, 38, 38, 38, 0, 38,
- 38, 38, 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 0, 38, 38, 38, 38, 38, 0, 0, 0, 0, 81, 59, 81, 81, 81,
- 81, 81, 0, 59, 81, 81, 0, 81, 81, 59, 59, 0, 0, 0, 0, 0, 0, 0, 81,
- 81, 0, 0, 0, 0, 0, 0, 0, 38, 0, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 0, 0, 0, 0, 81, 81, 81, 59, 59, 59, 0, 0, 81, 81, 81, 0, 81, 81, 81,
- 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 59, 38, 38, 59, 59,
- 59, 59, 59, 59, 59, 0, 0, 0, 0, 4, 38, 38, 38, 38, 38, 38, 58, 59,
- 59, 59, 59, 59, 59, 59, 59, 14, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 3,
- 0, 0, 0, 0, 0, 38, 38, 0, 38, 0, 0, 38, 38, 0, 38, 0, 0, 38, 0, 0,
- 0, 0, 0, 0, 38, 38, 38, 38, 0, 38, 38, 38, 38, 38, 38, 38, 0, 38, 38,
- 38, 0, 38, 0, 38, 0, 0, 38, 38, 0, 38, 38, 38, 38, 59, 38, 38, 59,
- 59, 59, 59, 59, 59, 0, 59, 59, 38, 0, 0, 38, 38, 38, 38, 38, 0, 58,
- 0, 59, 59, 59, 59, 59, 59, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0,
- 38, 38, 0, 0, 38, 14, 14, 14, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 14, 14, 14, 14, 14, 59, 59, 14, 14, 14, 14, 14, 14, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 14, 59,
- 14, 59, 14, 59, 5, 6, 5, 6, 81, 81, 38, 38, 38, 38, 38, 38, 38, 38,
- 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 0, 0, 0, 0, 0, 0, 0, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
- 59, 59, 81, 59, 59, 59, 59, 59, 3, 59, 59, 38, 38, 38, 38, 0, 0, 0,
- 0, 59, 59, 59, 59, 59, 59, 0, 59, 0, 59, 59, 59, 59, 59, 59, 59, 59,
- 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 0, 0, 0, 59, 59,
- 59, 59, 59, 59, 59, 0, 59, 0, 0, 0, 0, 0, 0, 78, 78, 78, 78, 78, 78,
- 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
- 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 3, 0, 0, 0, 0,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 0, 0, 0, 0, 38, 38, 38, 38,
- 0, 0, 0, 0, 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 0, 0, 0, 0, 0, 0, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21,
- 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 15, 15, 15, 15, 15,
- 82, 0, 0, 0, 0, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20,
- 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 20, 21, 0, 0, 0, 0, 0,
- 0, 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, 84,
- 83, 83, 83, 83, 83, 83, 0, 0, 84, 84, 84, 84, 84, 84, 0, 0, 83, 83,
- 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, 84, 83, 83, 83,
- 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83,
- 83, 83, 0, 0, 84, 84, 84, 84, 84, 84, 0, 0, 15, 83, 15, 83, 15, 83,
- 15, 83, 0, 84, 0, 84, 0, 84, 0, 84, 83, 83, 83, 83, 83, 83, 83, 83,
- 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 86, 86, 86, 86, 87, 87, 88,
- 88, 89, 89, 90, 90, 0, 0, 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84,
- 84, 84, 84, 84, 84, 83, 83, 15, 91, 15, 0, 15, 15, 84, 84, 92, 92,
- 93, 11, 94, 11, 11, 11, 15, 91, 15, 0, 15, 15, 95, 95, 95, 95, 93,
- 11, 11, 11, 83, 83, 15, 15, 0, 0, 15, 15, 84, 84, 96, 96, 0, 11, 11,
- 11, 83, 83, 15, 15, 15, 97, 15, 15, 84, 84, 98, 98, 99, 11, 11, 11,
- 0, 0, 15, 91, 15, 0, 15, 15, 100, 100, 101, 101, 93, 11, 11, 0, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 102, 102, 102, 102, 8, 8, 8, 8, 8,
- 8, 3, 3, 16, 18, 5, 16, 16, 18, 5, 16, 3, 3, 3, 3, 3, 3, 3, 3, 103,
- 104, 102, 102, 102, 102, 102, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 16, 18,
- 3, 3, 3, 3, 12, 12, 3, 3, 3, 7, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 13, 13, 13, 13, 13, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 21, 22, 14, 64, 64, 64, 64, 0, 85, 85, 0, 0, 21, 22,
+ 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21,
+ 22, 77, 21, 22, 21, 22, 0, 0, 21, 22, 0, 0, 21, 22, 0, 0, 0, 21, 22,
+ 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21,
+ 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22,
+ 21, 22, 0, 0, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 102, 102, 102, 102, 102, 102, 17, 0, 0, 0, 17, 17, 17, 17, 17,
- 17, 7, 7, 7, 5, 6, 15, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 7, 7,
- 7, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59,
- 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 80, 80, 80, 80, 59,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
+ 0, 0, 63, 3, 3, 3, 3, 3, 3, 0, 87, 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 15, 0, 3, 8, 0, 0,
+ 0, 0, 0, 0, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 0, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 64, 64, 64, 3, 64, 3, 64,
+ 64, 3, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 0, 0, 0, 0, 0, 42, 42, 42, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 0, 0, 0, 0, 0, 63, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 3, 3, 3, 0, 0, 64, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 42, 64,
+ 64, 64, 64, 64, 64, 64, 85, 85, 64, 64, 64, 64, 64, 64, 63, 63, 64,
+ 64, 14, 64, 64, 64, 64, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 42, 42,
+ 42, 14, 14, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 88, 42,
+ 64, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64,
+ 64, 89, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 0, 0, 64, 42, 89, 89, 89, 64, 64, 64, 64, 64, 64,
+ 64, 64, 89, 89, 89, 89, 64, 0, 0, 42, 64, 64, 64, 64, 0, 0, 0, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 64, 64, 3, 3, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64,
+ 89, 89, 0, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 42, 42, 0, 0, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 0, 42, 0, 0, 0, 42,
+ 42, 42, 42, 0, 0, 64, 0, 89, 89, 89, 64, 64, 64, 64, 0, 0, 89, 89,
+ 0, 0, 89, 89, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 0, 0, 42, 42,
+ 0, 42, 42, 42, 64, 64, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 42, 42,
+ 4, 4, 17, 17, 17, 17, 17, 17, 14, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 42,
+ 42, 42, 42, 42, 42, 0, 0, 0, 0, 42, 42, 0, 0, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0,
+ 42, 42, 42, 42, 42, 42, 42, 0, 42, 42, 0, 42, 42, 0, 42, 42, 0, 0,
+ 64, 0, 89, 89, 89, 64, 64, 0, 0, 0, 0, 64, 64, 0, 0, 64, 64, 64, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 0, 42, 0, 0, 0, 0, 0,
+ 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 64, 64, 42, 42, 42, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 64, 64, 89, 0, 42, 42, 42, 42, 42, 42, 42,
+ 0, 42, 0, 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 42, 42, 42, 42, 42,
+ 42, 42, 0, 42, 42, 0, 42, 42, 42, 42, 42, 0, 0, 64, 42, 89, 89, 89,
+ 64, 64, 64, 64, 64, 0, 64, 64, 89, 0, 89, 89, 64, 0, 0, 42, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 0, 42,
+ 42, 0, 0, 42, 42, 42, 42, 0, 0, 64, 42, 89, 64, 89, 64, 64, 64, 0,
+ 0, 0, 89, 89, 0, 0, 89, 89, 64, 0, 0, 0, 0, 0, 0, 0, 0, 64, 89, 0,
+ 0, 0, 0, 42, 42, 0, 42, 42, 42, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 89,
+ 0, 42, 42, 42, 42, 42, 42, 0, 0, 0, 42, 42, 42, 0, 42, 42, 42, 42,
+ 0, 0, 0, 42, 42, 0, 42, 0, 42, 42, 0, 0, 0, 42, 42, 0, 0, 0, 42, 42,
+ 42, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 0, 42, 42, 42, 0, 0, 0,
+ 0, 89, 89, 64, 89, 89, 0, 0, 0, 89, 89, 89, 0, 89, 89, 89, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 89, 89, 89, 0, 42, 42, 42, 42, 42, 42, 42, 42, 0, 42,
+ 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 0, 42, 42, 42, 42, 42, 0, 0, 0, 0, 64, 64, 64, 89, 89,
+ 89, 89, 0, 64, 64, 64, 0, 64, 64, 64, 64, 0, 0, 0, 0, 0, 0, 0, 64,
+ 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89,
+ 89, 0, 42, 42, 42, 42, 42, 42, 42, 42, 0, 42, 42, 42, 0, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 42, 42, 42,
+ 42, 42, 0, 0, 0, 0, 89, 64, 89, 89, 89, 89, 89, 0, 64, 89, 89, 0, 89,
+ 89, 64, 64, 0, 0, 0, 0, 0, 0, 0, 89, 89, 0, 0, 0, 0, 0, 0, 0, 42, 0,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 89, 89, 89, 64, 64,
+ 64, 0, 0, 89, 89, 89, 0, 89, 89, 89, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 89, 0, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 42, 0, 0,
+ 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 64, 0, 0, 0, 0, 89, 89, 89, 64,
+ 64, 64, 0, 64, 0, 89, 89, 89, 89, 89, 89, 89, 89, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 89, 3, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 64, 42, 42, 64, 64, 64, 64, 64, 64, 64, 0, 0, 0, 0, 4, 42, 42,
+ 42, 42, 42, 42, 63, 64, 64, 64, 64, 64, 64, 64, 64, 3, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 3, 3, 0, 0, 0, 0, 0, 42, 42, 0, 42, 0, 0, 42, 42,
+ 0, 42, 0, 0, 42, 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 0, 42, 42, 42, 42,
+ 42, 42, 42, 0, 42, 42, 42, 0, 42, 0, 42, 0, 0, 42, 42, 0, 42, 42, 42,
+ 42, 64, 42, 42, 64, 64, 64, 64, 64, 64, 0, 64, 64, 42, 0, 0, 42, 42,
+ 42, 42, 42, 0, 63, 0, 64, 64, 64, 64, 64, 64, 0, 0, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 0, 0, 42, 42, 0, 0, 42, 14, 14, 14, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 14, 14, 14, 14, 14, 64, 64, 14, 14, 14,
+ 14, 14, 14, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 14, 64, 14, 64, 14, 64, 5, 6, 5, 6, 89, 89, 42, 42, 42,
+ 42, 42, 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 0, 0, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 89, 64, 64, 64, 64, 64, 3, 64, 64, 42,
+ 42, 42, 42, 0, 0, 0, 0, 64, 64, 64, 64, 64, 64, 64, 64, 0, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 0, 14, 14, 14, 14, 14, 14, 14, 14, 64, 14, 14, 14, 14, 14, 14, 0, 0,
+ 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 0, 42,
+ 42, 42, 42, 42, 0, 42, 42, 0, 89, 64, 64, 64, 64, 89, 64, 0, 0, 0,
+ 64, 64, 89, 64, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3,
+ 3, 3, 3, 3, 3, 42, 42, 42, 42, 42, 42, 89, 89, 64, 64, 0, 0, 0, 0,
+ 0, 0, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 0, 0, 0, 0, 3, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0,
+ 0, 0, 0, 0, 42, 42, 42, 42, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 0, 0, 42, 42, 42,
+ 42, 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 0, 42, 0, 42, 42, 42, 42, 0, 0, 42, 42, 42, 42, 42, 42, 42,
+ 0, 42, 0, 42, 42, 42, 42, 0, 0, 42, 42, 42, 42, 42, 42, 42, 0, 42,
+ 0, 42, 42, 42, 42, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 0, 42, 0, 42, 42, 42, 42, 0, 0, 42, 42, 42, 42, 42, 42,
+ 42, 0, 42, 0, 42, 42, 42, 42, 0, 0, 42, 42, 42, 42, 42, 42, 42, 0,
+ 42, 42, 42, 42, 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 9, 9, 9, 9, 9, 9, 9, 9, 9, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 3, 42, 42, 42, 42, 42,
+ 42, 42, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 5, 6, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 3, 3, 3, 90, 90, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 89, 89, 89, 64, 64, 64, 64, 64, 64, 64, 89, 89, 89, 89, 89,
+ 89, 89, 89, 64, 89, 89, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 3, 3, 3, 3, 3, 3, 3, 4, 3, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3,
+ 3, 3, 3, 3, 8, 3, 3, 3, 3, 88, 88, 88, 88, 0, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 0, 0, 0, 0, 0, 0, 42, 42, 42, 63, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 0, 0, 0,
+ 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 64, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 22, 21, 22, 21, 22, 21,
+ 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 15, 15,
+ 15, 15, 15, 91, 0, 0, 0, 0, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22,
+ 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 0,
+ 0, 0, 0, 0, 0, 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93,
+ 93, 93, 93, 92, 92, 92, 92, 92, 92, 0, 0, 93, 93, 93, 93, 93, 93, 0,
+ 0, 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 93, 93,
+ 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, 92,
+ 92, 92, 92, 92, 92, 0, 0, 93, 93, 93, 93, 93, 93, 0, 0, 15, 92, 15,
+ 92, 15, 92, 15, 92, 0, 93, 0, 93, 0, 93, 0, 93, 92, 92, 92, 92, 92,
+ 92, 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 95, 95, 95, 95,
+ 96, 96, 97, 97, 98, 98, 99, 99, 0, 0, 92, 92, 92, 92, 92, 92, 92, 92,
+ 100, 100, 100, 100, 100, 100, 100, 100, 92, 92, 92, 92, 92, 92, 92,
+ 92, 100, 100, 100, 100, 100, 100, 100, 100, 92, 92, 92, 92, 92, 92,
+ 92, 92, 100, 100, 100, 100, 100, 100, 100, 100, 92, 92, 15, 101, 15,
+ 0, 15, 15, 93, 93, 102, 102, 103, 11, 104, 11, 11, 11, 15, 101, 15,
+ 0, 15, 15, 105, 105, 105, 105, 103, 11, 11, 11, 92, 92, 15, 15, 0,
+ 0, 15, 15, 93, 93, 106, 106, 0, 11, 11, 11, 92, 92, 15, 15, 15, 107,
+ 15, 15, 93, 93, 108, 108, 109, 11, 11, 11, 0, 0, 15, 101, 15, 0, 15,
+ 15, 110, 110, 111, 111, 103, 11, 11, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 88, 88, 88, 88, 8, 8, 8, 8, 8, 8, 3, 3, 16, 19, 5, 16, 16,
+ 19, 5, 16, 3, 3, 3, 3, 3, 3, 3, 3, 112, 113, 88, 88, 88, 88, 88, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 16, 19, 3, 3, 3, 3, 12, 12, 3, 3, 3, 7,
+ 5, 6, 0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 88, 88, 88, 88, 88, 17,
+ 0, 0, 0, 17, 17, 17, 17, 17, 17, 7, 7, 7, 5, 6, 15, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 7, 7, 7, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 14, 14, 72, 14, 14, 14, 14, 72, 14, 14, 15, 72,
- 72, 72, 15, 15, 72, 72, 72, 15, 14, 72, 14, 14, 15, 72, 72, 72, 72,
- 72, 14, 14, 14, 14, 14, 14, 72, 14, 72, 14, 72, 14, 72, 72, 72, 72,
- 15, 15, 72, 72, 14, 72, 15, 38, 38, 38, 38, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17,
- 17, 17, 17, 17, 17, 17, 17, 17, 17, 105, 105, 105, 105, 105, 105, 105,
- 105, 105, 105, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106,
- 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 14, 14, 14, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 85, 85, 85, 85, 64, 85, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 77,
+ 14, 14, 14, 14, 77, 14, 14, 15, 77, 77, 77, 15, 15, 77, 77, 77, 15,
+ 14, 77, 14, 14, 14, 77, 77, 77, 77, 77, 14, 14, 14, 14, 14, 14, 77,
+ 14, 114, 14, 77, 14, 115, 116, 77, 77, 14, 15, 77, 77, 14, 77, 15,
+ 42, 42, 42, 42, 15, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 118, 118, 118, 118, 118, 118, 118, 118, 118,
+ 118, 118, 118, 118, 118, 118, 118, 90, 90, 90, 90, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 14, 14, 14, 14, 14, 7, 7, 14, 14,
+ 14, 14, 7, 14, 14, 7, 14, 14, 7, 14, 14, 14, 14, 14, 14, 14, 7, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 7, 7, 14, 14, 7,
+ 14, 7, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 7, 7, 7, 7, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 7, 7, 14, 14, 14, 14, 14, 14, 14, 5, 6, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 7, 14, 7, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 14, 14, 14, 14, 14, 14, 7,
- 7, 7, 7, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 7, 7, 14, 14, 14, 14, 14, 14, 14, 5, 6, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 14,
- 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 14, 14, 14, 14, 14, 14, 14, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
- 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 108, 108, 108, 108, 108, 108, 108,
- 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
- 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 109,
- 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109,
- 109, 109, 109, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 119, 119, 119, 119, 119, 119,
+ 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119,
+ 119, 119, 119, 119, 119, 119, 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0,
- 0, 14, 14, 14, 14, 14, 14, 0, 14, 14, 14, 14, 0, 14, 14, 14, 14, 0,
- 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 14, 0, 14,
- 14, 14, 14, 0, 0, 0, 14, 0, 14, 14, 14, 14, 14, 14, 14, 0, 0, 14, 14,
- 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17,
+ 14, 14, 14, 14, 14, 7, 14, 14, 14, 14, 14, 14, 14, 14, 14, 7, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 7, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14,
+ 14, 14, 14, 0, 14, 14, 14, 14, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 0, 14, 0, 14, 14, 14, 14, 0, 0, 0, 14, 0, 14, 14,
+ 14, 14, 14, 14, 14, 0, 0, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
- 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 14, 0, 0, 0, 14, 14, 14,
+ 17, 17, 17, 14, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 0, 2, 3, 3, 3, 14, 58, 38, 107, 5, 6, 5, 6, 5, 6, 5, 6, 5,
- 6, 14, 14, 5, 6, 5, 6, 5, 6, 5, 6, 8, 5, 6, 6, 14, 107, 107, 107, 107,
- 107, 107, 107, 107, 107, 59, 59, 59, 59, 59, 59, 8, 58, 58, 58, 58,
- 58, 14, 14, 0, 0, 0, 0, 0, 0, 0, 14, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 0, 0, 0, 59,
- 59, 11, 11, 58, 58, 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 12,
- 58, 58, 58, 0, 0, 0, 0, 0, 0, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 0, 0, 0, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 14, 14, 17, 17,
- 17, 17, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 0, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0,
+ 0, 0, 0, 2, 3, 3, 3, 14, 63, 42, 90, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6,
+ 14, 14, 5, 6, 5, 6, 5, 6, 5, 6, 8, 5, 6, 6, 14, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 64, 64, 64, 64, 64, 64, 8, 63, 63, 63, 63, 63, 14,
+ 14, 90, 90, 90, 0, 0, 0, 14, 14, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 64, 64,
+ 11, 11, 63, 63, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 12, 63,
+ 63, 63, 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 14, 14, 17, 17, 17,
+ 17, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17,
- 17, 17, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14,
+ 14, 14, 14, 14, 14, 14, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 38, 38, 38, 38, 38,
- 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 38, 38, 38, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 110, 110, 110,
- 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
- 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
- 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
- 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
- 111, 111, 111, 111, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15,
- 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15,
- 15, 15, 0, 0, 0, 0, 0, 0, 59, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 7, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 38, 38, 38,
- 38, 38, 0, 38, 0, 38, 38, 0, 38, 38, 0, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 0, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 0, 0, 0, 59, 59, 59, 59,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 14, 14, 14, 0, 14, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 42, 42, 42, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 121, 121, 121, 121, 121, 121,
+ 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+ 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 122, 122, 122,
+ 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+ 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+ 122, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15,
+ 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 0,
+ 0, 0, 0, 0, 42, 64, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 7, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 42, 42, 42, 42,
+ 42, 0, 42, 0, 42, 42, 0, 42, 42, 0, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 64, 64, 64, 64,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 8, 8, 12, 12, 5, 6, 5, 6, 5,
6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 0, 0, 0, 0, 3, 3, 3, 3, 12, 12, 12,
3, 3, 3, 0, 3, 3, 3, 3, 8, 5, 6, 5, 6, 5, 6, 3, 3, 3, 7, 8, 7, 7, 7,
- 0, 3, 4, 3, 3, 0, 0, 0, 0, 38, 38, 38, 0, 38, 0, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 0, 0, 102, 0, 3, 3, 3, 4, 3, 3, 3, 5, 6, 3, 7, 3, 8, 3, 3, 9, 9, 9,
+ 0, 3, 4, 3, 3, 0, 0, 0, 0, 42, 42, 42, 0, 42, 0, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 0, 0, 88, 0, 3, 3, 3, 4, 3, 3, 3, 5, 6, 3, 7, 3, 8, 3, 3, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 3, 3, 7, 7, 7, 3, 11, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 5, 7, 6, 7, 0, 0, 3, 5, 6, 3, 12, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 58, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 58,
- 58, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 0, 0, 0,
- 38, 38, 38, 38, 38, 38, 0, 0, 38, 38, 38, 38, 38, 38, 0, 0, 38, 38,
- 38, 38, 38, 38, 0, 0, 38, 38, 38, 0, 0, 0, 4, 4, 7, 11, 14, 4, 4, 0,
- 14, 7, 7, 7, 7, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14,
- 14, 0, 0
+ 13, 13, 13, 5, 7, 6, 7, 0, 0, 3, 5, 6, 3, 12, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 63, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 63,
+ 63, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0,
+ 42, 42, 42, 42, 42, 42, 0, 0, 42, 42, 42, 42, 42, 42, 0, 0, 42, 42,
+ 42, 42, 42, 42, 0, 0, 42, 42, 42, 0, 0, 0, 4, 4, 7, 11, 14, 4, 4, 0,
+ 14, 7, 7, 7, 7, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 88, 88, 14,
+ 14, 42, 17, 42, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 123, 123,
+ 126, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 89, 64, 14, 14, 14,
+ 14, 14, 0, 0, 77, 77, 15, 15, 77, 15, 15, 77, 77, 15, 77, 77, 15, 77,
+ 77, 15, 15, 77, 15, 15, 77, 77, 15, 77, 77, 15, 77, 77, 15, 15, 77,
+ 15, 15, 77, 77, 15, 77, 77, 15, 77, 77, 15, 15, 77, 77, 15, 15, 77,
+ 15, 15, 77, 77, 15, 15, 77, 15, 15, 77, 77, 15, 15, 9, 9, 9, 42, 42,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 88, 0, 88, 88, 88, 88, 88, 88, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 122,
+ 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+ 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+ 122
};
/*
@@ -510,21 +826,23 @@ static unsigned char groupMap[] = {
static int groups[] = {
0, 15, 12, 25, 27, 21, 22, 26, 20, 9, 134217793, 28, 19, 134217858,
- 29, 2, 23, 11, 24, -507510654, 4194369, 4194434, -834666431, 973078658,
- -507510719, 1258291330, 880803905, 864026689, 859832385, 331350081,
- 847249473, 851443777, 868220993, 884998209, 876609601, 893386817,
- 897581121, 914358337, 5, 910164033, 918552641, 8388705, 4194499,
- 8388770, 331350146, 880803970, 864026754, 859832450, 847249538,
- 851443842, 868221058, 876609666, 884998274, 893386882, 897581186,
- 914358402, 910164098, 918552706, 4, 6, -352321338, 159383617,
- 155189313, 268435521, 264241217, 159383682, 155189378, 130023554,
- 268435586, 264241282, 260046978, 239075458, 1, 197132418, 226492546,
- 360710274, 335544450, 335544385, 201326657, 201326722, 7, 8, 247464066,
- -33554302, -33554367, -310378366, -360710014, -419430270, -536870782,
- -469761918, -528482174, -37748606, -310378431, -37748671, 155189442,
- -360710079, -419430335, -29359998, -469761983, -29360063, -536870847,
- -528482239, 16, 13, 14, 67108938, 67109002, 10, 109051997, 109052061,
- 18, 17
+ 29, 2, 23, 11, 1178599554, 24, -507510654, 4194369, 4194434, -834666431,
+ 973078658, -507510719, 1258291330, 880803905, 864026689, 859832385,
+ 331350081, 847249473, 851443777, 868220993, -406847358, 884998209,
+ 876609601, 893386817, 897581121, 914358337, 910164033, 918552641,
+ 5, -234880894, 8388705, 4194499, 8388770, 331350146, -406847423,
+ -234880959, 880803970, 864026754, 859832450, 847249538, 851443842,
+ 868221058, 876609666, 884998274, 893386882, 897581186, 914358402,
+ 910164098, 918552706, 4, 6, -352321402, 159383617, 155189313,
+ 268435521, 264241217, 159383682, 155189378, 130023554, 268435586,
+ 264241282, 260046978, 239075458, 1, 197132418, 226492546, 360710274,
+ 335544450, -251658175, 402653314, 335544385, 7, 201326657, 201326722,
+ 16, 8, 10, 247464066, -33554302, -33554367, -310378366, -360710014,
+ -419430270, -536870782, -469761918, -528482174, -33554365, -37748606,
+ -310378431, -37748669, 155189378, -360710079, -419430335, -29359998,
+ -469761983, -29360063, -536870847, -528482239, 13, 14, -1463812031,
+ -801111999, -293601215, 67108938, 67109002, 109051997, 109052061,
+ 18, 17, 8388673, 12582977, 8388738, 12583042
};
/*
@@ -575,7 +893,7 @@ enum {
#define GetCaseType(info) (((info) & 0xE0) >> 5)
#define GetCategory(info) ((info) & 0x1F)
-#define GetDelta(infO) (((info) > 0) ? ((info) >> 22) : (~(~((info)) >> 22)))
+#define GetDelta(info) (((info) > 0) ? ((info) >> 22) : (~(~((info)) >> 22)))
/*
* This macro extracts the information about a character from the
diff --git a/tcl/generic/tclUtf.c b/tcl/generic/tclUtf.c
index 5f6826ddf01..8ba7bb56655 100644
--- a/tcl/generic/tclUtf.c
+++ b/tcl/generic/tclUtf.c
@@ -61,8 +61,8 @@
* The following structures are used when mapping between Unicode (UCS-2)
* and UTF-8.
*/
-
-CONST unsigned char totalBytes[256] = {
+
+static CONST unsigned char totalBytes[256] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
@@ -111,7 +111,7 @@ static int UtfCount _ANSI_ARGS_((int ch));
*---------------------------------------------------------------------------
*/
-static int
+INLINE static int
UtfCount(ch)
int ch; /* The Tcl_UniChar whose size is returned. */
{
@@ -309,7 +309,7 @@ Tcl_UtfToUniChar(str, chPtr)
* Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
* characters representing themselves.
*/
-
+
*chPtr = (Tcl_UniChar) byte;
return 1;
} else if (byte < 0xE0) {
@@ -317,7 +317,7 @@ Tcl_UtfToUniChar(str, chPtr)
/*
* Two-byte-character lead-byte followed by a trail-byte.
*/
-
+
*chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6) | (str[1] & 0x3F));
return 2;
}
@@ -325,7 +325,7 @@ Tcl_UtfToUniChar(str, chPtr)
* A two-byte-character lead-byte not followed by trail-byte
* represents itself.
*/
-
+
*chPtr = (Tcl_UniChar) byte;
return 1;
} else if (byte < 0xF0) {
@@ -536,7 +536,7 @@ Tcl_NumUtfChars(str, len)
*
*---------------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_UtfFindFirst(string, ch)
CONST char *string; /* The UTF-8 string to be searched. */
int ch; /* The Tcl_UniChar to search for. */
@@ -547,7 +547,7 @@ Tcl_UtfFindFirst(string, ch)
while (1) {
len = Tcl_UtfToUniChar(string, &find);
if (find == ch) {
- return (char *) string;
+ return string;
}
if (*string == '\0') {
return NULL;
@@ -576,7 +576,7 @@ Tcl_UtfFindFirst(string, ch)
*---------------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_UtfFindLast(string, ch)
CONST char *string; /* The UTF-8 string to be searched. */
int ch; /* The Tcl_UniChar to search for. */
@@ -596,7 +596,7 @@ Tcl_UtfFindLast(string, ch)
}
string += len;
}
- return (char *) last;
+ return last;
}
/*
@@ -619,13 +619,13 @@ Tcl_UtfFindLast(string, ch)
*---------------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_UtfNext(str)
CONST char *str; /* The current location in the string. */
{
Tcl_UniChar ch;
- return (char *) str + Tcl_UtfToUniChar(str, &ch);
+ return str + Tcl_UtfToUniChar(str, &ch);
}
/*
@@ -634,7 +634,8 @@ Tcl_UtfNext(str)
* Tcl_UtfPrev --
*
* Given a pointer to some current location in a UTF-8 string,
- * move backwards one character.
+ * move backwards one character. This works correctly when the
+ * pointer is in the middle of a UTF-8 character.
*
* Results:
* The return value is a pointer to the previous character in the
@@ -648,7 +649,7 @@ Tcl_UtfNext(str)
*---------------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_UtfPrev(str, start)
CONST char *str; /* The current location in the string. */
CONST char *start; /* Pointer to the beginning of the
@@ -670,16 +671,13 @@ Tcl_UtfPrev(str, start)
byte = *((unsigned char *) look);
if (byte < 0x80) {
break;
- }
+ }
if (byte >= 0xC0) {
- if (totalBytes[byte] != i + 1) {
- break;
- }
- return (char *) look;
+ return look;
}
look--;
}
- return (char *) str;
+ return str;
}
/*
@@ -730,7 +728,7 @@ Tcl_UniCharAtIndex(src, index)
*---------------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_UtfAtIndex(src, index)
register CONST char *src; /* The UTF-8 string. */
register int index; /* The position of the desired character. */
@@ -741,7 +739,7 @@ Tcl_UtfAtIndex(src, index)
index--;
src += Tcl_UtfToUniChar(src, &ch);
}
- return (char *) src;
+ return src;
}
/*
@@ -780,118 +778,19 @@ Tcl_UtfBackslash(src, readPtr, dst)
char *dst; /* Filled with the bytes represented by the
* backslash sequence. */
{
- register CONST char *p = src+1;
- int result, count, n;
- char buf[TCL_UTF_MAX];
-
- if (dst == NULL) {
- dst = buf;
+#define LINE_LENGTH 128
+ int numRead;
+ int result;
+
+ result = TclParseBackslash(src, LINE_LENGTH, &numRead, dst);
+ if (numRead == LINE_LENGTH) {
+ /* We ate a whole line. Pay the price of a strlen() */
+ result = TclParseBackslash(src, (int)strlen(src), &numRead, dst);
}
-
- count = 2;
- switch (*p) {
- /*
- * Note: in the conversions below, use absolute values (e.g.,
- * 0xa) rather than symbolic values (e.g. \n) that get converted
- * by the compiler. It's possible that compilers on some
- * platforms will do the symbolic conversions differently, which
- * could result in non-portable Tcl scripts.
- */
-
- case 'a':
- result = 0x7;
- break;
- case 'b':
- result = 0x8;
- break;
- case 'f':
- result = 0xc;
- break;
- case 'n':
- result = 0xa;
- break;
- case 'r':
- result = 0xd;
- break;
- case 't':
- result = 0x9;
- break;
- case 'v':
- result = 0xb;
- break;
- case 'x':
- if (isxdigit(UCHAR(p[1]))) { /* INTL: digit */
- char *end;
-
- result = (unsigned char) strtoul(p+1, &end, 16);
- count = end - src;
- } else {
- count = 2;
- result = 'x';
- }
- break;
- case 'u':
- result = 0;
- for (count = 0; count < 4; count++) {
- p++;
- if (!isxdigit(UCHAR(*p))) { /* INTL: digit */
- break;
- }
- n = *p - '0';
- if (n > 9) {
- n = n + '0' + 10 - 'A';
- }
- if (n > 16) {
- n = n + 'A' - 'a';
- }
- result = (result << 4) + n;
- }
- if (count == 0) {
- result = 'u';
- }
- count += 2;
- break;
-
- case '\n':
- do {
- p++;
- } while ((*p == ' ') || (*p == '\t'));
- result = ' ';
- count = p - src;
- break;
- case 0:
- result = '\\';
- count = 1;
- break;
- default:
- /*
- * Check for an octal number \oo?o?
- */
- if (isdigit(UCHAR(*p)) && (UCHAR(*p) < '8')) { /* INTL: digit */
- result = (unsigned char)(*p - '0');
- p++;
- if (!isdigit(UCHAR(*p)) || (UCHAR(*p) >= '8')) { /* INTL: digit */
- break;
- }
- count = 3;
- result = (unsigned char)((result << 3) + (*p - '0'));
- p++;
- if (!isdigit(UCHAR(*p)) || (UCHAR(*p) >= '8')) { /* INTL: digit */
- break;
- }
- count = 4;
- result = (unsigned char)((result << 3) + (*p - '0'));
- break;
- }
- result = *p;
- count = 2;
- break;
- }
-
if (readPtr != NULL) {
- *readPtr = count;
+ *readPtr = numRead;
}
- return Tcl_UniCharToUtf(result, dst);
+ return result;
}
/*
@@ -1065,6 +964,51 @@ Tcl_UtfToTitle(str)
/*
*----------------------------------------------------------------------
*
+ * TclpUtfNcmp2 --
+ *
+ * Compare at most n bytes of utf-8 strings cs and ct. Both cs
+ * and ct are assumed to be at least n bytes long.
+ *
+ * Results:
+ * Return <0 if cs < ct, 0 if cs == ct, or >0 if cs > ct.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclpUtfNcmp2(cs, ct, n)
+ CONST char *cs; /* UTF string to compare to ct. */
+ CONST char *ct; /* UTF string cs is compared to. */
+ unsigned long n; /* Number of *bytes* to compare. */
+{
+ /*
+ * We can't simply call 'memcmp(cs, ct, n);' because we need to check
+ * for Tcl's \xC0\x80 non-utf-8 null encoding.
+ * Otherwise utf-8 lexes fine in the strcmp manner.
+ */
+ register int result = 0;
+
+ for ( ; n != 0; n--, cs++, ct++) {
+ if (*cs != *ct) {
+ result = UCHAR(*cs) - UCHAR(*ct);
+ break;
+ }
+ }
+ if (n && ((UCHAR(*cs) == 0xC0) || (UCHAR(*ct) == 0xC0))) {
+ unsigned char c1, c2;
+ c1 = ((UCHAR(*cs) == 0xC0) && (UCHAR(cs[1]) == 0x80)) ? 0 : UCHAR(*cs);
+ c2 = ((UCHAR(*ct) == 0xC0) && (UCHAR(ct[1]) == 0x80)) ? 0 : UCHAR(*ct);
+ result = (c1 - c2);
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_UtfNcmp --
*
* Compare at most n UTF chars of string cs to string ct. Both cs
@@ -1087,11 +1031,9 @@ Tcl_UtfNcmp(cs, ct, n)
{
Tcl_UniChar ch1, ch2;
/*
- * Another approach that should work is:
- * return memcmp(cs, ct, (unsigned) (Tcl_UtfAtIndex(cs, n) - cs));
- * That assumes that ct is a properly formed UTF, so we will just
- * be comparing the bytes that compromise those strings to the
- * char length n.
+ * Cannot use 'memcmp(cs, ct, n);' as byte representation of
+ * \u0000 (the pair of bytes 0xc0,0x80) is larger than byte
+ * representation of \u0001 (the byte 0x01.)
*/
while (n-- > 0) {
/*
@@ -1265,7 +1207,7 @@ Tcl_UniCharToTitle(ch)
int
Tcl_UniCharLen(str)
- Tcl_UniChar *str; /* Unicode string to find length of. */
+ CONST Tcl_UniChar *str; /* Unicode string to find length of. */
{
int len = 0;
@@ -1299,12 +1241,53 @@ Tcl_UniCharNcmp(cs, ct, n)
CONST Tcl_UniChar *ct; /* Unicode string cs is compared to. */
unsigned long n; /* Number of unichars to compare. */
{
- for ( ; n != 0; n--, cs++, ct++) {
+#ifdef WORDS_BIGENDIAN
+ /*
+ * We are definitely on a big-endian machine; memcmp() is safe
+ */
+ return memcmp(cs, ct, n*sizeof(Tcl_UniChar));
+
+#else /* !WORDS_BIGENDIAN */
+ /*
+ * We can't simply call memcmp() because that is not lexically correct.
+ */
+ for ( ; n != 0; cs++, ct++, n--) {
if (*cs != *ct) {
- return *cs - *ct;
+ return (*cs - *ct);
}
- if (*cs == '\0') {
- break;
+ }
+ return 0;
+#endif /* WORDS_BIGENDIAN */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_UniCharNcasecmp --
+ *
+ * Compare at most n unichars of string cs to string ct case
+ * insensitive. Both cs and ct are assumed to be at least n
+ * unichars long.
+ *
+ * Results:
+ * Return <0 if cs < ct, 0 if cs == ct, or >0 if cs > ct.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_UniCharNcasecmp(cs, ct, n)
+ CONST Tcl_UniChar *cs; /* Unicode string to compare to ct. */
+ CONST Tcl_UniChar *ct; /* Unicode string cs is compared to. */
+ unsigned long n; /* Number of unichars to compare. */
+{
+ for ( ; n != 0; n--, cs++, ct++) {
+ if ((*cs != *ct) &&
+ (Tcl_UniCharToLower(*cs) != Tcl_UniCharToLower(*ct))) {
+ return (*cs - *ct);
}
}
return 0;
@@ -1584,3 +1567,182 @@ Tcl_UniCharIsWordChar(ch)
return (((ALPHA_BITS | DIGIT_BITS | CONNECTOR_BITS) >> category) & 1);
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_UniCharCaseMatch --
+ *
+ * See if a particular Unicode string matches a particular pattern.
+ * Allows case insensitivity. This is the Unicode equivalent of
+ * the char* Tcl_StringCaseMatch.
+ *
+ * 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_UniCharCaseMatch(string, pattern, nocase)
+ CONST Tcl_UniChar *string; /* Unicode String. */
+ CONST Tcl_UniChar *pattern; /* Pattern, which may contain special
+ * characters. */
+ int nocase; /* 0 for case sensitive, 1 for insensitive */
+{
+ Tcl_UniChar ch1, p;
+
+ while (1) {
+ p = *pattern;
+
+ /*
+ * 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 (*string == 0);
+ }
+ if ((*string == 0) && (p != '*')) {
+ return 0;
+ }
+
+ /*
+ * Check for a "*" as the next pattern character. It matches any
+ * substring. We handle this by skipping all the characters up to the
+ * next matching one in the pattern, and then calling ourselves
+ * recursively for each postfix of string, until either we match or we
+ * reach the end of the string.
+ */
+
+ if (p == '*') {
+ /*
+ * Skip all successive *'s in the pattern
+ */
+ while (*(++pattern) == '*') {}
+ p = *pattern;
+ if (p == 0) {
+ return 1;
+ }
+ if (nocase) {
+ p = Tcl_UniCharToLower(p);
+ }
+ while (1) {
+ /*
+ * Optimization for matching - cruise through the string
+ * quickly if the next char in the pattern isn't a special
+ * character
+ */
+ if ((p != '[') && (p != '?') && (p != '\\')) {
+ if (nocase) {
+ while (*string && (p != *string)
+ && (p != Tcl_UniCharToLower(*string))) {
+ string++;
+ }
+ } else {
+ while (*string && (p != *string)) { string++; }
+ }
+ }
+ if (Tcl_UniCharCaseMatch(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++;
+ 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++;
+ ch1 = (nocase ? Tcl_UniCharToLower(*string) : *string);
+ string++;
+ while (1) {
+ if ((*pattern == ']') || (*pattern == 0)) {
+ return 0;
+ }
+ startChar = (nocase ? Tcl_UniCharToLower(*pattern) : *pattern);
+ pattern++;
+ if (*pattern == '-') {
+ pattern++;
+ if (*pattern == 0) {
+ return 0;
+ }
+ endChar = (nocase ? Tcl_UniCharToLower(*pattern)
+ : *pattern);
+ pattern++;
+ 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--;
+ 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 == '\\') {
+ if (*(++pattern) == '\0') {
+ return 0;
+ }
+ }
+
+ /*
+ * There's no special character. Just make sure that the next
+ * bytes of each string match.
+ */
+
+ if (nocase) {
+ if (Tcl_UniCharToLower(*string) != Tcl_UniCharToLower(*pattern)) {
+ return 0;
+ }
+ } else if (*string != *pattern) {
+ return 0;
+ }
+ string++;
+ pattern++;
+ }
+}
diff --git a/tcl/generic/tclUtil.c b/tcl/generic/tclUtil.c
index 041036b80fd..4e71f668f96 100644
--- a/tcl/generic/tclUtil.c
+++ b/tcl/generic/tclUtil.c
@@ -6,6 +6,7 @@
*
* Copyright (c) 1987-1993 The Regents of the University of California.
* Copyright (c) 1994-1998 Sun Microsystems, Inc.
+ * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -62,6 +63,30 @@ static char precisionFormat[10] = "%.12g";
* to sprintf. */
TCL_DECLARE_MUTEX(precisionMutex)
+/*
+ * Prototypes for procedures defined later in this file.
+ */
+
+static void UpdateStringOfEndOffset _ANSI_ARGS_((Tcl_Obj* objPtr));
+static int SetEndOffsetFromAny _ANSI_ARGS_((Tcl_Interp* interp,
+ Tcl_Obj* objPtr));
+
+/*
+ * The following is the Tcl object type definition for an object
+ * that represents a list index in the form, "end-offset". It is
+ * used as a performance optimization in TclGetIntForIndex. The
+ * internal rep is an integer, so no memory management is required
+ * for it.
+ */
+
+Tcl_ObjType tclEndOffsetType = {
+ "end-offset", /* name */
+ (Tcl_FreeInternalRepProc*) NULL, /* freeIntRepProc */
+ (Tcl_DupInternalRepProc*) NULL, /* dupIntRepProc */
+ UpdateStringOfEndOffset, /* updateStringProc */
+ SetEndOffsetFromAny
+};
+
/*
*----------------------------------------------------------------------
@@ -318,11 +343,11 @@ TclFindElement(interp, list, listLength, elementPtr, nextPtr, sizePtr,
* 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.
+ * 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.
@@ -395,10 +420,10 @@ Tcl_SplitList(interp, list, argcPtr, argvPtr)
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
+ CONST char ***argvPtr; /* Pointer to place to store pointer to
* array of pointers to list elements. */
{
- char **argv;
+ CONST char **argv;
CONST char *l;
char *p;
int length, size, i, result, elSize, brace;
@@ -417,7 +442,7 @@ Tcl_SplitList(interp, list, argcPtr, argvPtr)
}
}
size++; /* Leave space for final NULL pointer. */
- argv = (char **) ckalloc((unsigned)
+ argv = (CONST char **) ckalloc((unsigned)
((size * sizeof(char *)) + (l - list) + 1));
length = strlen(list);
for (i = 0, p = ((char *) argv) + size*sizeof(char *);
@@ -822,7 +847,7 @@ Tcl_ConvertCountedElement(src, length, dst, flags)
char *
Tcl_Merge(argc, argv)
int argc; /* How many strings to merge. */
- char **argv; /* Array of string values. */
+ CONST char * CONST *argv; /* Array of string values. */
{
# define LOCAL_SIZE 20
int localFlags[LOCAL_SIZE], *flagPtr;
@@ -925,7 +950,7 @@ Tcl_Backslash(src, readPtr)
char *
Tcl_Concat(argc, argv)
int argc; /* Number of strings to concatenate. */
- char **argv; /* Array of strings to concatenate. */
+ CONST char * CONST *argv; /* Array of strings to concatenate. */
{
int totalSize, i;
char *p;
@@ -940,7 +965,7 @@ Tcl_Concat(argc, argv)
return result;
}
for (p = result, i = 0; i < argc; i++) {
- char *element;
+ CONST char *element;
int length;
/*
@@ -1071,8 +1096,8 @@ Tcl_ConcatObj(objc, objv)
for (i = 0; i < objc; i++) {
objPtr = objv[i];
element = Tcl_GetStringFromObj(objPtr, &elemLength);
- while ((elemLength > 0)
- && (isspace(UCHAR(*element)))) { /* INTL: ISO space. */
+ while ((elemLength > 0) && (UCHAR(*element) < 127)
+ && isspace(UCHAR(*element))) { /* INTL: ISO C space. */
element++;
elemLength--;
}
@@ -1083,8 +1108,8 @@ Tcl_ConcatObj(objc, objv)
* this case it could be significant.
*/
- while ((elemLength > 0)
- && isspace(UCHAR(element[elemLength-1])) /* INTL: ISO space. */
+ while ((elemLength > 0) && (UCHAR(element[elemLength-1]) < 127)
+ && isspace(UCHAR(element[elemLength-1])) /* INTL: ISO C space. */
&& ((elemLength < 2) || (element[elemLength-2] != '\\'))) {
elemLength--;
}
@@ -1136,131 +1161,7 @@ Tcl_StringMatch(string, pattern)
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++;
- }
+ return Tcl_StringCaseMatch(string, pattern, 0);
}
/*
@@ -1290,13 +1191,12 @@ Tcl_StringCaseMatch(string, pattern, nocase)
* characters. */
int nocase; /* 0 for case sensitive, 1 for insensitive */
{
- int p, s;
+ int p;
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
@@ -1305,35 +1205,74 @@ Tcl_StringCaseMatch(string, pattern, nocase)
*/
if (p == '\0') {
- return (s == '\0');
+ return (*string == '\0');
}
- if ((s == '\0') && (p != '*')) {
+ if ((*string == '\0') && (p != '*')) {
return 0;
}
- /* Check for a "*" as the next pattern character. It matches
+ /*
+ * 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') {
+ /*
+ * Skip all successive *'s in the pattern
+ */
+ while (*(++pattern) == '*') {}
+ p = *pattern;
+ if (p == '\0') {
return 1;
}
+ Tcl_UtfToUniChar(pattern, &ch2);
+ if (nocase) {
+ ch2 = Tcl_UniCharToLower(ch2);
+ }
while (1) {
+ /*
+ * Optimization for matching - cruise through the string
+ * quickly if the next char in the pattern isn't a special
+ * character
+ */
+ if ((p != '[') && (p != '?') && (p != '\\')) {
+ if (nocase) {
+ while (*string) {
+ int charLen = Tcl_UtfToUniChar(string, &ch1);
+ if (ch2==ch1 || ch2==Tcl_UniCharToLower(ch1)) {
+ break;
+ }
+ string += charLen;
+ }
+ } else {
+ /*
+ * There's no point in trying to make this code
+ * shorter, as the number of bytes you want to
+ * compare each time is non-constant.
+ */
+ while (*string) {
+ int charLen = Tcl_UtfToUniChar(string, &ch1);
+ if (ch2 == ch1) {
+ break;
+ }
+ string += charLen;
+ }
+ }
+ }
if (Tcl_StringCaseMatch(string, pattern, nocase)) {
return 1;
}
if (*string == '\0') {
return 0;
}
- string++;
+ string += Tcl_UtfToUniChar(string, &ch1);
}
}
- /* Check for a "?" as the next pattern character. It matches
+ /*
+ * Check for a "?" as the next pattern character. It matches
* any single character.
*/
@@ -1343,11 +1282,12 @@ Tcl_StringCaseMatch(string, pattern, nocase)
continue;
}
- /* Check for a "[" as the next pattern character. It is followed
+ /*
+ * 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;
@@ -1396,22 +1336,23 @@ Tcl_StringCaseMatch(string, pattern, nocase)
continue;
}
- /* If the next pattern character is '\', just strip off the '\'
+ /*
+ * 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') {
+ if (*pattern == '\0') {
return 0;
}
}
- /* There's no special character. Just make sure that the next
+ /*
+ * 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) {
@@ -1547,10 +1488,12 @@ Tcl_DStringAppendElement(dsPtr, string)
CONST char *string; /* String to append. Must be
* null-terminated. */
{
- int newSize, flags;
+ int newSize, flags, strSize;
char *dst;
- newSize = Tcl_ScanElement(string, &flags) + dsPtr->length + 1;
+ strSize = ((string == NULL) ? 0 : strlen(string));
+ newSize = Tcl_ScanCountedElement(string, strSize, &flags)
+ + dsPtr->length + 1;
/*
* Allocate a larger buffer for the string if the current one isn't
@@ -1587,7 +1530,7 @@ Tcl_DStringAppendElement(dsPtr, string)
dst++;
dsPtr->length++;
}
- dsPtr->length += Tcl_ConvertElement(string, dst, flags);
+ dsPtr->length += Tcl_ConvertCountedElement(string, strSize, dst, flags);
return dsPtr->string;
}
@@ -1935,11 +1878,12 @@ 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. */
+ CONST char *name1; /* Name of variable. */
+ CONST char *name2; /* Second part of variable name. */
int flags; /* Information about what happened. */
{
- char *value, *end;
+ CONST char *value;
+ char *end;
int prec;
/*
@@ -2022,10 +1966,12 @@ TclPrecTraceProc(clientData, interp, name1, name2, flags)
int
TclNeedSpace(start, end)
- char *start; /* First character in string. */
- char *end; /* End of string (place where space will
+ CONST char *start; /* First character in string. */
+ CONST char *end; /* End of string (place where space will
* be added, if appropriate). */
{
+ Tcl_UniChar ch;
+
/*
* A space is needed unless either
* (a) we're at the start of the string, or
@@ -2039,10 +1985,14 @@ TclNeedSpace(start, end)
if (end == start) {
return 0;
}
- end--;
+ end = Tcl_UtfPrev(end, start);
if (*end != '{') {
- if (isspace(UCHAR(*end)) /* INTL: ISO space. */
- && ((end == start) || (end[-1] != '\\'))) {
+ Tcl_UtfToUniChar(end, &ch);
+ /*
+ * Direct char comparison on next line is safe as it is with
+ * a character in the ASCII subset, and so single-byte in UTF8.
+ */
+ if (Tcl_UniCharIsSpace(ch) && ((end == start) || (end[-1] != '\\'))) {
return 0;
}
return 1;
@@ -2051,9 +2001,10 @@ TclNeedSpace(start, end)
if (end == start) {
return 0;
}
- end--;
+ end = Tcl_UtfPrev(end, start);
} while (*end == '{');
- if (isspace(UCHAR(*end))) { /* INTL: ISO space. */
+ Tcl_UtfToUniChar(end, &ch);
+ if (Tcl_UniCharIsSpace(ch)) {
return 0;
}
return 1;
@@ -2167,44 +2118,34 @@ TclFormatInt(buffer, n)
int
TclLooksLikeInt(bytes, length)
- register char *bytes; /* Points to first byte of the string. */
+ register CONST 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;
+ register CONST char *p;
+
+ if ((bytes == NULL) && (length > 0)) {
+ Tcl_Panic("TclLooksLikeInt: cannot scan %d bytes from NULL", length);
+ }
if (length < 0) {
- length = (bytes? strlen(bytes) : 0);
+ length = (bytes? strlen(bytes) : 0);
}
- end = (bytes + length);
p = bytes;
- while ((p < end) && isspace(UCHAR(*p))) { /* INTL: ISO space. */
- p++;
+ while (length && isspace(UCHAR(*p))) { /* INTL: ISO space. */
+ length--; p++;
}
- if (p == end) {
- return 0;
+ if (length == 0) {
+ 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;
+ p++; length--;
}
- return 0;
+
+ return (0 != TclParseInteger(p, length));
}
/*
@@ -2228,7 +2169,7 @@ TclLooksLikeInt(bytes, length)
*
* Side effects:
* The object referenced by "objPtr" might be converted to an
- * integer object.
+ * integer, wide integer, or end-based-index object.
*
*----------------------------------------------------------------------
*/
@@ -2246,26 +2187,193 @@ TclGetIntForIndex(interp, objPtr, endValue, indexPtr)
* representing an index. */
{
char *bytes;
- int length, offset;
+ int offset;
+#ifndef TCL_WIDE_INT_IS_LONG
+ Tcl_WideInt wideOffset;
+#endif
+
+ /*
+ * If the object is already an integer, use it.
+ */
if (objPtr->typePtr == &tclIntType) {
*indexPtr = (int)objPtr->internalRep.longValue;
return TCL_OK;
}
- bytes = Tcl_GetStringFromObj(objPtr, &length);
+ /*
+ * If the object is already a wide-int, and it is not out of range
+ * for an integer, use it. [Bug #526717]
+ */
+#ifndef TCL_WIDE_INT_IS_LONG
+ if (objPtr->typePtr == &tclWideIntType) {
+ Tcl_WideInt wideOffset = objPtr->internalRep.wideValue;
+ if (wideOffset >= Tcl_LongAsWide(INT_MIN)
+ && wideOffset <= Tcl_LongAsWide(INT_MAX)) {
+ *indexPtr = (int) Tcl_WideAsLong(wideOffset);
+ return TCL_OK;
+ }
+ }
+#endif /* TCL_WIDE_INT_IS_LONG */
- if ((*bytes != 'e') || (strncmp(bytes, "end",
- (size_t)((length > 3) ? 3 : length)) != 0)) {
- if (Tcl_GetIntFromObj(NULL, objPtr, &offset) != TCL_OK) {
- goto intforindex_error;
+ if (SetEndOffsetFromAny(NULL, objPtr) == TCL_OK) {
+ /*
+ * If the object is already an offset from the end of the
+ * list, or can be converted to one, use it.
+ */
+
+ *indexPtr = endValue + objPtr->internalRep.longValue;
+
+#ifdef TCL_WIDE_INT_IS_LONG
+ } else if (Tcl_GetIntFromObj(NULL, objPtr, &offset) == TCL_OK) {
+ /*
+ * If the object can be converted to an integer, use that.
+ */
+
+ *indexPtr = offset;
+
+#else /* !TCL_WIDE_INT_IS_LONG */
+ } else if (Tcl_GetWideIntFromObj(NULL, objPtr, &wideOffset) == TCL_OK) {
+ /*
+ * If the object can be converted to a wide integer, use
+ * that. [Bug #526717]
+ */
+
+ offset = (int) Tcl_WideAsLong(wideOffset);
+ if (Tcl_LongAsWide(offset) == wideOffset) {
+ /*
+ * But it is representable as a narrow integer, so we
+ * prefer that (so preserving old behaviour in the
+ * majority of cases.)
+ */
+ objPtr->typePtr = &tclIntType;
+ objPtr->internalRep.longValue = offset;
}
*indexPtr = offset;
+
+#endif /* TCL_WIDE_INT_IS_LONG */
+ } else {
+ /*
+ * Report a parse error.
+ */
+
+ if (interp != NULL) {
+ bytes = Tcl_GetString(objPtr);
+ /*
+ * The result might not be empty; this resets it which
+ * should be both a cheap operation, and of little problem
+ * because this is an error-generation path anyway.
+ */
+ Tcl_ResetResult(interp);
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "bad index \"", bytes,
+ "\": must be integer or end?-integer?",
+ (char *) NULL);
+ if (!strncmp(bytes, "end-", 3)) {
+ bytes += 3;
+ }
+ TclCheckBadOctal(interp, bytes);
+ }
+
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateStringOfEndOffset --
+ *
+ * Update the string rep of a Tcl object holding an "end-offset"
+ * expression.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Stores a valid string in the object's string rep.
+ *
+ * This procedure does NOT free any earlier string rep. If it is
+ * called on an object that already has a valid string rep, it will
+ * leak memory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateStringOfEndOffset(objPtr)
+ register Tcl_Obj* objPtr;
+{
+ char buffer[TCL_INTEGER_SPACE + sizeof("end") + 1];
+ register int len;
+
+ strcpy(buffer, "end");
+ len = sizeof("end") - 1;
+ if (objPtr->internalRep.longValue != 0) {
+ buffer[len++] = '-';
+ len += TclFormatInt(buffer+len, -(objPtr->internalRep.longValue));
+ }
+ objPtr->bytes = ckalloc((unsigned) (len+1));
+ strcpy(objPtr->bytes, buffer);
+ objPtr->length = len;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetEndOffsetFromAny --
+ *
+ * Look for a string of the form "end-offset" and convert it
+ * to an internal representation holding the offset.
+ *
+ * Results:
+ * Returns TCL_OK if ok, TCL_ERROR if the string was badly formed.
+ *
+ * Side effects:
+ * If interp is not NULL, stores an error message in the
+ * interpreter result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SetEndOffsetFromAny(interp, objPtr)
+ Tcl_Interp* interp; /* Tcl interpreter or NULL */
+ Tcl_Obj* objPtr; /* Pointer to the object to parse */
+{
+ int offset; /* Offset in the "end-offset" expression */
+ Tcl_ObjType* oldTypePtr = objPtr->typePtr;
+ /* Old internal rep type of the object */
+ register char* bytes; /* String rep of the object */
+ int length; /* Length of the object's string rep */
+
+ /* If it's already the right type, we're fine. */
+
+ if (objPtr->typePtr == &tclEndOffsetType) {
return TCL_OK;
}
+ /* Check for a string rep of the right form. */
+
+ bytes = Tcl_GetStringFromObj(objPtr, &length);
+ if ((*bytes != 'e') || (strncmp(bytes, "end",
+ (size_t)((length > 3) ? 3 : length)) != 0)) {
+ if (interp != NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "bad index \"", bytes,
+ "\": must be end?-integer?",
+ (char*) NULL);
+ }
+ return TCL_ERROR;
+ }
+
+ /* Convert the string rep */
+
if (length <= 3) {
- *indexPtr = endValue;
+ offset = 0;
} else if (bytes[3] == '-') {
/*
* This is our limited string expression evaluator
@@ -2273,19 +2381,35 @@ TclGetIntForIndex(interp, objPtr, endValue, indexPtr)
if (Tcl_GetInt(interp, bytes+3, &offset) != TCL_OK) {
return TCL_ERROR;
}
- *indexPtr = endValue + offset;
+
} else {
- intforindex_error:
- if ((Interp *)interp != NULL) {
+ /*
+ * Conversion failed. Report the error.
+ */
+ if (interp != NULL) {
+ Tcl_ResetResult(interp);
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "bad index \"", bytes,
- "\": must be integer or end?-integer?", (char *) NULL);
- TclCheckBadOctal(interp, bytes);
+ "bad index \"", bytes,
+ "\": must be integer or end?-integer?",
+ (char *) NULL);
}
return TCL_ERROR;
}
+
+ /*
+ * The conversion succeeded. Free the old internal rep and set
+ * the new one.
+ */
+
+ if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
+ oldTypePtr->freeIntRepProc(objPtr);
+ }
+
+ objPtr->internalRep.longValue = offset;
+ objPtr->typePtr = &tclEndOffsetType;
+
return TCL_OK;
-}
+}
/*
*----------------------------------------------------------------------
@@ -2309,9 +2433,9 @@ 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. */
+ CONST char *value; /* String to check. */
{
- register char *p = value;
+ register CONST char *p = value;
/*
* A frequent mistake is invalid octal values due to an unwanted
@@ -2334,6 +2458,10 @@ TclCheckBadOctal(interp, value)
if (*p == '\0') {
/* Reached end of string */
if (interp != NULL) {
+ /*
+ * Don't reset the result here because we want this result
+ * to be added to an existing error message as extra info.
+ */
Tcl_AppendResult(interp, " (looks like invalid octal number)",
(char *) NULL);
}
@@ -2367,105 +2495,31 @@ TclCheckBadOctal(interp, value)
CONST char *
Tcl_GetNameOfExecutable()
{
- return (tclExecutableName);
+ return tclExecutableName;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_GetCwd --
+ * TclpGetTime --
*
- * This function replaces the library version of getcwd().
+ * Deprecated synonym for Tcl_GetTime.
*
* 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.
+ * Stores current time in the buffer designated by "timePtr"
*
- *----------------------------------------------------------------------
- */
-
-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.
+ * This procedure is provided for the benefit of extensions written
+ * before Tcl_GetTime was exported from the library.
*
*----------------------------------------------------------------------
*/
-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. */
+void
+TclpGetTime(timePtr)
+ Tcl_Time* timePtr;
{
- return TclStat(path, bufPtr);
+ Tcl_GetTime(timePtr);
}
diff --git a/tcl/generic/tclVar.c b/tcl/generic/tclVar.c
index fce00ab6138..3bbbcc3ac25 100644
--- a/tcl/generic/tclVar.c
+++ b/tcl/generic/tclVar.c
@@ -10,6 +10,7 @@
* Copyright (c) 1987-1994 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
* Copyright (c) 1998-1999 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -25,46 +26,130 @@
* variable access is denied.
*/
-static char *noSuchVar = "no such variable";
-static char *isArray = "variable is array";
-static char *needArray = "variable isn't array";
-static char *noSuchElement = "no such element in array";
-static char *danglingElement = "upvar refers to element in deleted array";
-static char *danglingVar = "upvar refers to variable in deleted namespace";
-static char *badNamespace = "parent namespace doesn't exist";
-static char *missingName = "missing variable name";
-static char *isArrayElement = "name refers to an element in an array";
+static CONST char *noSuchVar = "no such variable";
+static CONST char *isArray = "variable is array";
+static CONST char *needArray = "variable isn't array";
+static CONST char *noSuchElement = "no such element in array";
+static CONST char *danglingElement =
+ "upvar refers to element in deleted array";
+static CONST char *danglingVar =
+ "upvar refers to variable in deleted namespace";
+static CONST char *badNamespace = "parent namespace doesn't exist";
+static CONST char *missingName = "missing variable name";
+static CONST char *isArrayElement = "name refers to an element in an array";
/*
* Forward references to procedures defined later in this file:
*/
-static char * CallTraces _ANSI_ARGS_((Interp *iPtr, Var *arrayPtr,
- Var *varPtr, char *part1, char *part2,
- int flags));
+static int CallVarTraces _ANSI_ARGS_((Interp *iPtr, Var *arrayPtr,
+ Var *varPtr, CONST char *part1, CONST char *part2,
+ int flags, CONST int leaveErrMsg));
static void CleanupVar _ANSI_ARGS_((Var *varPtr,
Var *arrayPtr));
static void DeleteSearches _ANSI_ARGS_((Var *arrayVarPtr));
static void DeleteArray _ANSI_ARGS_((Interp *iPtr,
- char *arrayName, Var *varPtr, int flags));
-static int MakeUpvar _ANSI_ARGS_((
- Interp *iPtr, CallFrame *framePtr,
- char *otherP1, char *otherP2, int otherFlags,
- char *myName, int myFlags));
+ CONST char *arrayName, Var *varPtr, int flags));
+static void DisposeTraceResult _ANSI_ARGS_((int flags,
+ char *result));
+static int ObjMakeUpvar _ANSI_ARGS_((Tcl_Interp *interp,
+ CallFrame *framePtr, Tcl_Obj *otherP1Ptr,
+ CONST char *otherP2, CONST int otherFlags,
+ CONST char *myName, CONST int myFlags, int index));
static Var * NewVar _ANSI_ARGS_((void));
static ArraySearch * ParseSearchId _ANSI_ARGS_((Tcl_Interp *interp,
- Var *varPtr, char *varName, char *string));
+ CONST Var *varPtr, CONST char *varName,
+ Tcl_Obj *handleObj));
static void VarErrMsg _ANSI_ARGS_((Tcl_Interp *interp,
- char *part1, char *part2, char *operation,
- char *reason));
+ CONST char *part1, CONST char *part2,
+ CONST char *operation, CONST char *reason));
+static int SetArraySearchObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr));
+
+
+/*
+ * Functions defined in this file that may be exported in the future
+ * for use by the bytecode compiler and engine or to the public interface.
+ */
+
+Var * TclLookupSimpleVar _ANSI_ARGS_((Tcl_Interp *interp,
+ CONST char *varName, int flags, CONST int create,
+ CONST char **errMsgPtr, int *indexPtr));
+int TclObjUnsetVar2 _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *part1Ptr, CONST char *part2, int flags));
+
+static Tcl_FreeInternalRepProc FreeLocalVarName;
+static Tcl_DupInternalRepProc DupLocalVarName;
+static Tcl_UpdateStringProc UpdateLocalVarName;
+static Tcl_FreeInternalRepProc FreeNsVarName;
+static Tcl_DupInternalRepProc DupNsVarName;
+static Tcl_FreeInternalRepProc FreeParsedVarName;
+static Tcl_DupInternalRepProc DupParsedVarName;
+static Tcl_UpdateStringProc UpdateParsedVarName;
+
+/*
+ * Types of Tcl_Objs used to cache variable lookups.
+ *
+ *
+ * localVarName - INTERNALREP DEFINITION:
+ * twoPtrValue.ptr1 = pointer to the corresponding Proc
+ * twoPtrValue.ptr2 = index into locals table
+ *
+ * nsVarName - INTERNALREP DEFINITION:
+ * twoPtrValue.ptr1: pointer to the namespace containing the
+ * reference
+ * twoPtrValue.ptr2: pointer to the corresponding Var
+ *
+ * parsedVarName - INTERNALREP DEFINITION:
+ * twoPtrValue.ptr1 = pointer to the array name Tcl_Obj,
+ * or NULL if it is a scalar variable
+ * twoPtrValue.ptr2 = pointer to the element name string
+ * (owned by this Tcl_Obj), or NULL if
+ * it is a scalar variable
+ */
+
+Tcl_ObjType tclLocalVarNameType = {
+ "localVarName",
+ FreeLocalVarName, DupLocalVarName, UpdateLocalVarName, NULL
+};
+
+Tcl_ObjType tclNsVarNameType = {
+ "namespaceVarName",
+ FreeNsVarName, DupNsVarName, NULL, NULL
+};
+
+Tcl_ObjType tclParsedVarNameType = {
+ "parsedVarName",
+ FreeParsedVarName, DupParsedVarName, UpdateParsedVarName, NULL
+};
+
+/*
+ * Type of Tcl_Objs used to speed up array searches.
+ *
+ * INTERNALREP DEFINITION:
+ * twoPtrValue.ptr1 = searchIdNumber as offset from (char*)NULL
+ * twoPtrValue.ptr2 = variableNameStartInString as offset from (char*)NULL
+ *
+ * Note that the value stored in ptr2 is the offset into the string of
+ * the start of the variable name and not the address of the variable
+ * name itself, as this can be safely copied.
+ */
+Tcl_ObjType tclArraySearchType = {
+ "array search",
+ NULL, NULL, NULL, SetArraySearchObj
+};
+
/*
*----------------------------------------------------------------------
*
* TclLookupVar --
*
- * This procedure is used by virtually all of the variable code to
- * locate a variable given its name(s).
+ * This procedure is used to locate a variable given its name(s). It
+ * has been mostly superseded by TclObjLookupVar, it is now only used
+ * by the string-based interfaces. It is kept in tcl8.4 mainly because
+ * it is in the internal stubs table, so that some extension may be
+ * calling it.
*
* Results:
* The return value is a pointer to the variable structure indicated by
@@ -93,19 +178,18 @@ static void VarErrMsg _ANSI_ARGS_((Tcl_Interp *interp,
*
*----------------------------------------------------------------------
*/
-
Var *
TclLookupVar(interp, part1, part2, flags, msg, createPart1, createPart2,
arrayPtrPtr)
Tcl_Interp *interp; /* Interpreter to use for lookup. */
- register char *part1; /* If part2 isn't NULL, this is the name of
+ CONST char *part1; /* If part2 isn't NULL, this is the name of
* an array. Otherwise, this
* is a full variable name that could
* include a parenthesized array element. */
- char *part2; /* Name of element within array, or NULL. */
+ CONST char *part2; /* Name of element within array, or NULL. */
int flags; /* Only TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
* and TCL_LEAVE_ERR_MSG bits matter. */
- char *msg; /* Verb to use in error messages, e.g.
+ CONST char *msg; /* Verb to use in error messages, e.g.
* "read" or "set". Only needed if
* TCL_LEAVE_ERR_MSG is set in flags. */
int createPart1; /* If 1, create hash table entry for part 1
@@ -119,35 +203,24 @@ TclLookupVar(interp, part1, part2, flags, msg, createPart1, createPart2,
* address of array variable. Otherwise
* this is set to NULL. */
{
- Interp *iPtr = (Interp *) interp;
- CallFrame *varFramePtr = iPtr->varFramePtr;
- /* Points to the procedure call frame whose
- * variables are currently in use. Same as
- * the current procedure's frame, if any,
- * unless an "uplevel" is executing. */
- Tcl_HashTable *tablePtr; /* Points to the hashtable, if any, in which
- * to look up the variable. */
- Tcl_Var var; /* Used to search for global names. */
- Var *varPtr; /* Points to the Var structure returned for
- * the variable. */
- char *elName; /* Name of array element or NULL; may be
+ Var *varPtr;
+ CONST char *elName; /* Name of array element or NULL; may be
* same as part2, or may be openParen+1. */
- char *openParen, *closeParen;
+ int openParen, closeParen;
/* If this procedure parses a name into
- * array and index, these point to the
- * parens around the index. Otherwise they
- * are NULL. These are needed to restore
- * the parens after parsing the name. */
- Namespace *varNsPtr, *cxtNsPtr, *dummy1Ptr, *dummy2Ptr;
- ResolverScheme *resPtr;
- Tcl_HashEntry *hPtr;
- register char *p;
- int new, i, result;
+ * array and index, these are the offsets to
+ * the parens around the index. Otherwise
+ * they are -1. */
+ register CONST char *p;
+ CONST char *errMsg = NULL;
+ int index;
+#define VAR_NAME_BUF_SIZE 26
+ char buffer[VAR_NAME_BUF_SIZE];
+ char *newVarName = buffer;
varPtr = NULL;
*arrayPtrPtr = NULL;
- openParen = closeParen = NULL;
- varNsPtr = NULL; /* set non-NULL if a nonlocal variable */
+ openParen = closeParen = -1;
/*
* Parse part1 into array name and index.
@@ -162,28 +235,439 @@ TclLookupVar(interp, part1, part2, flags, msg, createPart1, createPart2,
elName = part2;
for (p = part1; *p ; p++) {
if (*p == '(') {
- openParen = p;
+ openParen = p - part1;
do {
p++;
} while (*p != '\0');
p--;
if (*p == ')') {
if (part2 != NULL) {
- openParen = NULL;
if (flags & TCL_LEAVE_ERR_MSG) {
VarErrMsg(interp, part1, part2, msg, needArray);
}
- goto done;
+ return NULL;
}
- closeParen = p;
- *openParen = 0;
- elName = openParen+1;
+ closeParen = p - part1;
} else {
- openParen = NULL;
+ openParen = -1;
}
break;
}
}
+ if (openParen != -1) {
+ if (closeParen >= VAR_NAME_BUF_SIZE) {
+ newVarName = ckalloc((unsigned int) (closeParen+1));
+ }
+ memcpy(newVarName, part1, (unsigned int) closeParen);
+ newVarName[openParen] = '\0';
+ newVarName[closeParen] = '\0';
+ part1 = newVarName;
+ elName = newVarName + openParen + 1;
+ }
+
+ varPtr = TclLookupSimpleVar(interp, part1, flags,
+ createPart1, &errMsg, &index);
+ if (varPtr == NULL) {
+ if ((errMsg != NULL) && (flags & TCL_LEAVE_ERR_MSG)) {
+ VarErrMsg(interp, part1, elName, msg, errMsg);
+ }
+ } else {
+ while (TclIsVarLink(varPtr)) {
+ varPtr = varPtr->value.linkPtr;
+ }
+ if (elName != NULL) {
+ *arrayPtrPtr = varPtr;
+ varPtr = TclLookupArrayElement(interp, part1, elName, flags,
+ msg, createPart1, createPart2, varPtr);
+ }
+ }
+ if (newVarName != buffer) {
+ ckfree(newVarName);
+ }
+
+ return varPtr;
+
+#undef VAR_NAME_BUF_SIZE
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclObjLookupVar --
+ *
+ * This procedure is used by virtually all of the variable code to
+ * locate a variable given its name(s). The parsing into array/element
+ * components and (if possible) the lookup results are cached in
+ * part1Ptr, which is converted to one of the varNameTypes.
+ *
+ * Results:
+ * The return value is a pointer to the variable structure indicated by
+ * part1Ptr and part2, or NULL if the variable couldn't be found. If
+ * the variable is found, *arrayPtrPtr is filled with the address of the
+ * variable structure for the array that contains the variable (or NULL
+ * if the variable is a scalar). If the variable can't be found and
+ * either createPart1 or createPart2 are 1, a new as-yet-undefined
+ * (VAR_UNDEFINED) variable structure is created, entered into a hash
+ * table, and returned.
+ *
+ * If the variable isn't found and creation wasn't specified, or some
+ * other error occurs, NULL is returned and an error message is left in
+ * the interp's result if TCL_LEAVE_ERR_MSG is set in flags.
+ *
+ * Note: it's possible for the variable returned to be VAR_UNDEFINED
+ * even if createPart1 or createPart2 are 1 (these only cause the hash
+ * table entry or array to be created). For example, the variable might
+ * be a global that has been unset but is still referenced by a
+ * procedure, or a variable that has been unset but it only being kept
+ * in existence (if VAR_UNDEFINED) by a trace.
+ *
+ * Side effects:
+ * New hashtable entries may be created if createPart1 or createPart2
+ * are 1.
+ * The object part1Ptr is converted to one of tclLocalVarNameType,
+ * tclNsVarNameType or tclParsedVarNameType and caches as much of the
+ * lookup as it can.
+ *
+ *----------------------------------------------------------------------
+ */
+Var *
+TclObjLookupVar(interp, part1Ptr, part2, flags, msg, createPart1, createPart2,
+ arrayPtrPtr)
+ Tcl_Interp *interp; /* Interpreter to use for lookup. */
+ register Tcl_Obj *part1Ptr; /* If part2 isn't NULL, this is the name
+ * of an array. Otherwise, this is a full
+ * variable name that could include a parenthesized
+ * array element. */
+ CONST char *part2; /* Name of element within array, or NULL. */
+ int flags; /* Only TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
+ * and TCL_LEAVE_ERR_MSG bits matter. */
+ CONST char *msg; /* Verb to use in error messages, e.g.
+ * "read" or "set". Only needed if
+ * TCL_LEAVE_ERR_MSG is set in flags. */
+ CONST int createPart1; /* If 1, create hash table entry for part 1
+ * of name, if it doesn't already exist. If
+ * 0, return error if it doesn't exist. */
+ CONST int createPart2; /* If 1, create hash table entry for part 2
+ * of name, if it doesn't already exist. If
+ * 0, return error if it doesn't exist. */
+ Var **arrayPtrPtr; /* If the name refers to an element of an
+ * array, *arrayPtrPtr gets filled in with
+ * address of array variable. Otherwise
+ * this is set to NULL. */
+{
+ Interp *iPtr = (Interp *) interp;
+ register Var *varPtr; /* Points to the variable's in-frame Var
+ * structure. */
+ char *part1;
+ int index, len1, len2;
+ int parsed = 0;
+ Tcl_Obj *objPtr;
+ Tcl_ObjType *typePtr = part1Ptr->typePtr;
+ CONST char *errMsg = NULL;
+ CallFrame *varFramePtr = iPtr->varFramePtr;
+ Namespace *nsPtr;
+
+ /*
+ * If part1Ptr is a tclParsedVarNameType, separate it into the
+ * pre-parsed parts.
+ */
+
+ *arrayPtrPtr = NULL;
+ if (typePtr == &tclParsedVarNameType) {
+ if (part1Ptr->internalRep.twoPtrValue.ptr1 != NULL) {
+ if (part2 != NULL) {
+ /*
+ * ERROR: part1Ptr is already an array element, cannot
+ * specify a part2.
+ */
+
+ if (flags & TCL_LEAVE_ERR_MSG) {
+ part1 = TclGetString(part1Ptr);
+ VarErrMsg(interp, part1, part2, msg, needArray);
+ }
+ return NULL;
+ }
+ part2 = (char *) part1Ptr->internalRep.twoPtrValue.ptr2;
+ part1Ptr = (Tcl_Obj *) part1Ptr->internalRep.twoPtrValue.ptr1;
+ typePtr = part1Ptr->typePtr;
+ }
+ parsed = 1;
+ }
+ part1 = Tcl_GetStringFromObj(part1Ptr, &len1);
+
+ nsPtr = ((varFramePtr == NULL)? iPtr->globalNsPtr : varFramePtr->nsPtr);
+ if (nsPtr->varResProc != NULL || iPtr->resolverPtr != NULL) {
+ goto doParse;
+ }
+
+ if (typePtr == &tclLocalVarNameType) {
+ Proc *procPtr = (Proc *) part1Ptr->internalRep.twoPtrValue.ptr1;
+ int localIndex = (int) part1Ptr->internalRep.twoPtrValue.ptr2;
+ int useLocal;
+
+ useLocal = ((varFramePtr != NULL) && varFramePtr->isProcCallFrame
+ && !(flags & (TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY)));
+ if (useLocal && (procPtr == varFramePtr->procPtr)) {
+ /*
+ * part1Ptr points to an indexed local variable of the
+ * correct procedure: use the cached value.
+ */
+
+ varPtr = &(varFramePtr->compiledLocals[localIndex]);
+ goto donePart1;
+ }
+ goto doneParsing;
+ } else if (typePtr == &tclNsVarNameType) {
+ Namespace *cachedNsPtr;
+ int useGlobal, useReference;
+
+ varPtr = (Var *) part1Ptr->internalRep.twoPtrValue.ptr2;
+ cachedNsPtr = (Namespace *) part1Ptr->internalRep.twoPtrValue.ptr1;
+ useGlobal = (cachedNsPtr == iPtr->globalNsPtr)
+ && ((flags & TCL_GLOBAL_ONLY)
+ || ((*part1 == ':') && (*(part1+1) == ':'))
+ || (varFramePtr == NULL)
+ || (!varFramePtr->isProcCallFrame
+ && (nsPtr == iPtr->globalNsPtr)));
+ useReference = useGlobal || ((cachedNsPtr == nsPtr)
+ && ((flags & TCL_NAMESPACE_ONLY)
+ || (varFramePtr && !varFramePtr->isProcCallFrame
+ && !(flags & TCL_GLOBAL_ONLY)
+ /* careful: an undefined ns variable could
+ * be hiding a valid global reference. */
+ && !(varPtr->flags & VAR_UNDEFINED))));
+ if (useReference && (varPtr->hPtr != NULL)) {
+ /*
+ * A straight global or namespace reference, use it. It isn't
+ * so simple to deal with 'implicit' namespace references, i.e.,
+ * those where the reference could be to either a namespace
+ * or a global variable. Those we lookup again.
+ *
+ * If (varPtr->hPtr == NULL), this might be a reference to a
+ * variable in a deleted namespace, kept alive by e.g. part1Ptr.
+ * We could conceivably be so unlucky that a new namespace was
+ * created at the same address as the deleted one, so to be
+ * safe we test for a valid hPtr.
+ */
+ goto donePart1;
+ }
+ goto doneParsing;
+ }
+
+ doParse:
+ if (!parsed && (*(part1 + len1 - 1) == ')')) {
+ /*
+ * part1Ptr is possibly an unparsed array element.
+ */
+ register int i;
+ char *newPart2;
+ len2 = -1;
+ for (i = 0; i < len1; i++) {
+ if (*(part1 + i) == '(') {
+ if (part2 != NULL) {
+ if (flags & TCL_LEAVE_ERR_MSG) {
+ VarErrMsg(interp, part1, part2, msg, needArray);
+ }
+ }
+
+ /*
+ * part1Ptr points to an array element; first copy
+ * the element name to a new string part2.
+ */
+
+ part2 = part1 + i + 1;
+ len2 = len1 - i - 2;
+ len1 = i;
+
+ newPart2 = ckalloc((unsigned int) (len2+1));
+ memcpy(newPart2, part2, (unsigned int) len2);
+ *(newPart2+len2) = '\0';
+ part2 = newPart2;
+
+ /*
+ * Free the internal rep of the original part1Ptr, now
+ * renamed objPtr, and set it to tclParsedVarNameType.
+ */
+
+ objPtr = part1Ptr;
+ if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
+ typePtr->freeIntRepProc(objPtr);
+ }
+ objPtr->typePtr = &tclParsedVarNameType;
+
+ /*
+ * Define a new string object to hold the new part1Ptr, i.e.,
+ * the array name. Set the internal rep of objPtr, reset
+ * typePtr and part1 to contain the references to the
+ * array name.
+ */
+
+ part1Ptr = Tcl_NewStringObj(part1, len1);
+ Tcl_IncrRefCount(part1Ptr);
+
+ objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) part1Ptr;
+ objPtr->internalRep.twoPtrValue.ptr2 = (VOID *) part2;
+
+ typePtr = part1Ptr->typePtr;
+ part1 = TclGetString(part1Ptr);
+ break;
+ }
+ }
+ }
+
+ doneParsing:
+ /*
+ * part1Ptr is not an array element; look it up, and convert
+ * it to one of the cached types if possible.
+ */
+
+ if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
+ typePtr->freeIntRepProc(part1Ptr);
+ part1Ptr->typePtr = NULL;
+ }
+
+ varPtr = TclLookupSimpleVar(interp, part1, flags,
+ createPart1, &errMsg, &index);
+ if (varPtr == NULL) {
+ if ((errMsg != NULL) && (flags & TCL_LEAVE_ERR_MSG)) {
+ VarErrMsg(interp, part1, part2, msg, errMsg);
+ }
+ return NULL;
+ }
+
+ /*
+ * Cache the newly found variable if possible.
+ */
+
+ if (index >= 0) {
+ /*
+ * An indexed local variable.
+ */
+
+ Proc *procPtr = ((Interp *) interp)->varFramePtr->procPtr;
+
+ part1Ptr->typePtr = &tclLocalVarNameType;
+ procPtr->refCount++;
+ part1Ptr->internalRep.twoPtrValue.ptr1 = (VOID *) procPtr;
+ part1Ptr->internalRep.twoPtrValue.ptr2 = (VOID *) index;
+ } else if (index > -3) {
+ Namespace *nsPtr;
+
+ nsPtr = ((index == -1)? iPtr->globalNsPtr : varFramePtr->nsPtr);
+ varPtr->refCount++;
+ part1Ptr->typePtr = &tclNsVarNameType;
+ part1Ptr->internalRep.twoPtrValue.ptr1 = (VOID *) nsPtr;
+ part1Ptr->internalRep.twoPtrValue.ptr2 = (VOID *) varPtr;
+ } else {
+ /*
+ * At least mark part1Ptr as already parsed.
+ */
+ part1Ptr->typePtr = &tclParsedVarNameType;
+ part1Ptr->internalRep.twoPtrValue.ptr1 = NULL;
+ part1Ptr->internalRep.twoPtrValue.ptr2 = NULL;
+ }
+
+ donePart1:
+#if 0
+ if (varPtr == NULL) {
+ if (flags & TCL_LEAVE_ERR_MSG) {
+ part1 = TclGetString(part1Ptr);
+ VarErrMsg(interp, part1, part2, msg,
+ "Cached variable reference is NULL.");
+ }
+ return NULL;
+ }
+#endif
+ while (TclIsVarLink(varPtr)) {
+ varPtr = varPtr->value.linkPtr;
+ }
+
+ if (part2 != NULL) {
+ /*
+ * Array element sought: look it up.
+ */
+
+ part1 = TclGetString(part1Ptr);
+ *arrayPtrPtr = varPtr;
+ varPtr = TclLookupArrayElement(interp, part1, part2,
+ flags, msg, createPart1, createPart2, varPtr);
+ }
+ return varPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclLookupSimpleVar --
+ *
+ * This procedure is used by to locate a simple variable (i.e., not
+ * an array element) given its name.
+ *
+ * Results:
+ * The return value is a pointer to the variable structure indicated by
+ * varName, or NULL if the variable couldn't be found. If the variable
+ * can't be found and create is 1, a new as-yet-undefined (VAR_UNDEFINED)
+ * variable structure is created, entered into a hash table, and returned.
+ *
+ * If the current CallFrame corresponds to a proc and the variable found is
+ * one of the compiledLocals, its index is placed in *indexPtr. Otherwise,
+ * *indexPtr will be set to (according to the needs of TclObjLookupVar):
+ * -1 a global reference
+ * -2 a reference to a namespace variable
+ * -3 a non-cachable reference, i.e., one of:
+ * . non-indexed local var
+ * . a reference of unknown origin;
+ * . resolution by a namespace or interp resolver
+ *
+ * If the variable isn't found and creation wasn't specified, or some
+ * other error occurs, NULL is returned and the corresponding error
+ * message is left in *errMsgPtr.
+ *
+ * Note: it's possible for the variable returned to be VAR_UNDEFINED
+ * even if create is 1 (this only causes the hash table entry to be
+ * created). For example, the variable might be a global that has been
+ * unset but is still referenced by a procedure, or a variable that has
+ * been unset but it only being kept in existence (if VAR_UNDEFINED) by
+ * a trace.
+ *
+ * Side effects:
+ * A new hashtable entry may be created if create is 1.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Var *
+TclLookupSimpleVar(interp, varName, flags, create, errMsgPtr, indexPtr)
+ Tcl_Interp *interp; /* Interpreter to use for lookup. */
+ CONST char *varName; /* This is a simple variable name that could
+ * representa scalar or an array. */
+ int flags; /* Only TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
+ * and TCL_LEAVE_ERR_MSG bits matter. */
+ CONST int create; /* If 1, create hash table entry for varname,
+ * if it doesn't already exist. If 0, return
+ * error if it doesn't exist. */
+ CONST char **errMsgPtr;
+ int *indexPtr;
+{
+ Interp *iPtr = (Interp *) interp;
+ CallFrame *varFramePtr = iPtr->varFramePtr;
+ /* Points to the procedure call frame whose
+ * variables are currently in use. Same as
+ * the current procedure's frame, if any,
+ * unless an "uplevel" is executing. */
+ Tcl_HashTable *tablePtr; /* Points to the hashtable, if any, in which
+ * to look up the variable. */
+ Tcl_Var var; /* Used to search for global names. */
+ Var *varPtr; /* Points to the Var structure returned for
+ * the variable. */
+ Namespace *varNsPtr, *cxtNsPtr, *dummy1Ptr, *dummy2Ptr;
+ ResolverScheme *resPtr;
+ Tcl_HashEntry *hPtr;
+ int new, i, result;
+
+ varPtr = NULL;
+ varNsPtr = NULL; /* set non-NULL if a nonlocal variable */
+ *indexPtr = -3;
/*
* If this namespace has a variable resolver, then give it first
@@ -191,7 +675,7 @@ TclLookupVar(interp, part1, part2, flags, msg, createPart1, createPart2,
* value, it may signal to continue onward, or it may signal
* an error.
*/
- if ((flags & TCL_GLOBAL_ONLY) != 0 || iPtr->varFramePtr == NULL) {
+ if ((flags & TCL_GLOBAL_ONLY) || iPtr->varFramePtr == NULL) {
cxtNsPtr = iPtr->globalNsPtr;
} else {
cxtNsPtr = iPtr->varFramePtr->nsPtr;
@@ -201,7 +685,7 @@ TclLookupVar(interp, part1, part2, flags, msg, createPart1, createPart2,
resPtr = iPtr->resolverPtr;
if (cxtNsPtr->varResProc) {
- result = (*cxtNsPtr->varResProc)(interp, part1,
+ result = (*cxtNsPtr->varResProc)(interp, varName,
(Tcl_Namespace *) cxtNsPtr, flags, &var);
} else {
result = TCL_CONTINUE;
@@ -209,7 +693,7 @@ TclLookupVar(interp, part1, part2, flags, msg, createPart1, createPart2,
while (result == TCL_CONTINUE && resPtr) {
if (resPtr->varResProc) {
- result = (*resPtr->varResProc)(interp, part1,
+ result = (*resPtr->varResProc)(interp, varName,
(Tcl_Namespace *) cxtNsPtr, flags, &var);
}
resPtr = resPtr->nextPtr;
@@ -217,71 +701,85 @@ TclLookupVar(interp, part1, part2, flags, msg, createPart1, createPart2,
if (result == TCL_OK) {
varPtr = (Var *) var;
- goto lookupVarPart2;
+ return varPtr;
} else if (result != TCL_CONTINUE) {
- return (Var *) NULL;
+ return NULL;
}
}
/*
- * Look up part1. Look it up as either a namespace variable or as a
+ * Look up varName. Look it up as either a namespace variable or as a
* local variable in a procedure call frame (varFramePtr).
- * Interpret part1 as a namespace variable if:
+ * Interpret varName as a namespace variable if:
* 1) so requested by a TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY flag,
* 2) there is no active frame (we're at the global :: scope),
* 3) the active frame was pushed to define the namespace context
* for a "namespace eval" or "namespace inscope" command,
* 4) the name has namespace qualifiers ("::"s).
- * Otherwise, if part1 is a local variable, search first in the
+ * Otherwise, if varName is a local variable, search first in the
* frame's array of compiler-allocated local variables, then in its
* hashtable for runtime-created local variables.
*
- * If createPart1 and the variable isn't found, create the variable and,
+ * If create and the variable isn't found, create the variable and,
* if necessary, create varFramePtr's local var hashtable.
*/
if (((flags & (TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY)) != 0)
|| (varFramePtr == NULL)
|| !varFramePtr->isProcCallFrame
- || (strstr(part1, "::") != NULL)) {
- char *tail;
+ || (strstr(varName, "::") != NULL)) {
+ CONST char *tail;
+ int lookGlobal;
+ lookGlobal = (flags & TCL_GLOBAL_ONLY)
+ || (cxtNsPtr == iPtr->globalNsPtr)
+ || ((*varName == ':') && (*(varName+1) == ':'));
+ if (lookGlobal) {
+ *indexPtr = -1;
+ flags = (flags | TCL_GLOBAL_ONLY) & ~TCL_NAMESPACE_ONLY;
+ } else if (flags & TCL_NAMESPACE_ONLY) {
+ *indexPtr = -2;
+ }
+
/*
* Don't pass TCL_LEAVE_ERR_MSG, we may yet create the variable,
* or otherwise generate our own error!
*/
- var = Tcl_FindNamespaceVar(interp, part1, (Tcl_Namespace *) NULL,
+ var = Tcl_FindNamespaceVar(interp, varName, (Tcl_Namespace *) cxtNsPtr,
flags & ~TCL_LEAVE_ERR_MSG);
if (var != (Tcl_Var) NULL) {
varPtr = (Var *) var;
}
if (varPtr == NULL) {
- if (createPart1) { /* var wasn't found so create it */
- TclGetNamespaceForQualName(interp, part1, (Namespace *) NULL,
+ if (create) { /* var wasn't found so create it */
+ TclGetNamespaceForQualName(interp, varName, cxtNsPtr,
flags, &varNsPtr, &dummy1Ptr, &dummy2Ptr, &tail);
-
if (varNsPtr == NULL) {
- if (flags & TCL_LEAVE_ERR_MSG) {
- VarErrMsg(interp, part1, part2, msg, badNamespace);
- }
- goto done;
+ *errMsgPtr = badNamespace;
+ return NULL;
}
if (tail == NULL) {
- if (flags & TCL_LEAVE_ERR_MSG) {
- VarErrMsg(interp, part1, part2, msg, missingName);
- }
- goto done;
+ *errMsgPtr = missingName;
+ return NULL;
}
hPtr = Tcl_CreateHashEntry(&varNsPtr->varTable, tail, &new);
varPtr = NewVar();
Tcl_SetHashValue(hPtr, varPtr);
varPtr->hPtr = hPtr;
varPtr->nsPtr = varNsPtr;
- } else { /* var wasn't found and not to create it */
- if (flags & TCL_LEAVE_ERR_MSG) {
- VarErrMsg(interp, part1, part2, msg, noSuchVar);
+ if ((lookGlobal) || (varNsPtr == NULL)) {
+ /*
+ * The variable was created starting from the global
+ * namespace: a global reference is returned even if
+ * it wasn't explicitly requested.
+ */
+ *indexPtr = -1;
+ } else {
+ *indexPtr = -2;
}
- goto done;
+ } else { /* var wasn't found and not to create it */
+ *errMsgPtr = noSuchVar;
+ return NULL;
}
}
} else { /* local var: look in frame varFramePtr */
@@ -289,156 +787,170 @@ TclLookupVar(interp, part1, part2, flags, msg, createPart1, createPart2,
int localCt = procPtr->numCompiledLocals;
CompiledLocal *localPtr = procPtr->firstLocalPtr;
Var *localVarPtr = varFramePtr->compiledLocals;
- int part1Len = strlen(part1);
+ int varNameLen = strlen(varName);
for (i = 0; i < localCt; i++) {
if (!TclIsVarTemporary(localPtr)) {
register char *localName = localVarPtr->name;
- if ((part1[0] == localName[0])
- && (part1Len == localPtr->nameLength)
- && (strcmp(part1, localName) == 0)) {
- varPtr = localVarPtr;
- break;
+ if ((varName[0] == localName[0])
+ && (varNameLen == localPtr->nameLength)
+ && (strcmp(varName, localName) == 0)) {
+ *indexPtr = i;
+ return localVarPtr;
}
}
localVarPtr++;
localPtr = localPtr->nextPtr;
}
- if (varPtr == NULL) { /* look in the frame's var hash table */
- tablePtr = varFramePtr->varTablePtr;
- if (createPart1) {
- if (tablePtr == NULL) {
- tablePtr = (Tcl_HashTable *)
- ckalloc(sizeof(Tcl_HashTable));
- Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS);
- varFramePtr->varTablePtr = tablePtr;
- }
- hPtr = Tcl_CreateHashEntry(tablePtr, part1, &new);
- if (new) {
- varPtr = NewVar();
- Tcl_SetHashValue(hPtr, varPtr);
- varPtr->hPtr = hPtr;
- varPtr->nsPtr = NULL; /* a local variable */
- } else {
- varPtr = (Var *) Tcl_GetHashValue(hPtr);
- }
+ tablePtr = varFramePtr->varTablePtr;
+ if (create) {
+ if (tablePtr == NULL) {
+ tablePtr = (Tcl_HashTable *)
+ ckalloc(sizeof(Tcl_HashTable));
+ Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS);
+ varFramePtr->varTablePtr = tablePtr;
+ }
+ hPtr = Tcl_CreateHashEntry(tablePtr, varName, &new);
+ if (new) {
+ varPtr = NewVar();
+ Tcl_SetHashValue(hPtr, varPtr);
+ varPtr->hPtr = hPtr;
+ varPtr->nsPtr = NULL; /* a local variable */
} else {
- hPtr = NULL;
- if (tablePtr != NULL) {
- hPtr = Tcl_FindHashEntry(tablePtr, part1);
- }
- if (hPtr == NULL) {
- if (flags & TCL_LEAVE_ERR_MSG) {
- VarErrMsg(interp, part1, part2, msg, noSuchVar);
- }
- goto done;
- }
varPtr = (Var *) Tcl_GetHashValue(hPtr);
}
+ } else {
+ hPtr = NULL;
+ if (tablePtr != NULL) {
+ hPtr = Tcl_FindHashEntry(tablePtr, varName);
+ }
+ if (hPtr == NULL) {
+ *errMsgPtr = noSuchVar;
+ return NULL;
+ }
+ varPtr = (Var *) Tcl_GetHashValue(hPtr);
}
}
+ return varPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclLookupArrayElement --
+ *
+ * This procedure is used to locate a variable which is in an array's
+ * hashtable given a pointer to the array's Var structure and the
+ * element's name.
+ *
+ * Results:
+ * The return value is a pointer to the variable structure , or NULL if
+ * the variable couldn't be found.
+ *
+ * If arrayPtr points to a variable that isn't an array and createPart1
+ * is 1, the corresponding variable will be converted to an array.
+ * Otherwise, NULL is returned and an error message is left in
+ * the interp's result if TCL_LEAVE_ERR_MSG is set in flags.
+ *
+ * If the variable is not found and createPart2 is 1, the variable is
+ * created. Otherwise, NULL is returned and an error message is left in
+ * the interp's result if TCL_LEAVE_ERR_MSG is set in flags.
+ *
+ * Note: it's possible for the variable returned to be VAR_UNDEFINED
+ * even if createPart1 or createPart2 are 1 (these only cause the hash
+ * table entry or array to be created). For example, the variable might
+ * be a global that has been unset but is still referenced by a
+ * procedure, or a variable that has been unset but it only being kept
+ * in existence (if VAR_UNDEFINED) by a trace.
+ *
+ * Side effects:
+ * The variable at arrayPtr may be converted to be an array if
+ * createPart1 is 1. A new hashtable entry may be created if createPart2
+ * is 1.
+ *
+ *----------------------------------------------------------------------
+ */
- lookupVarPart2:
- if (openParen != NULL) {
- *openParen = '(';
- openParen = NULL;
- }
-
- /*
- * If varPtr is a link variable, we have a reference to some variable
- * that was created through an "upvar" or "global" command. Traverse
- * through any links until we find the referenced variable.
- */
-
- while (TclIsVarLink(varPtr)) {
- varPtr = varPtr->value.linkPtr;
- }
-
- /*
- * If we're not dealing with an array element, return varPtr.
- */
-
- if (elName == NULL) {
- goto done;
- }
+Var *
+TclLookupArrayElement(interp, arrayName, elName, flags, msg, createArray, createElem, arrayPtr)
+ Tcl_Interp *interp; /* Interpreter to use for lookup. */
+ CONST char *arrayName; /* This is the name of the array. */
+ CONST char *elName; /* Name of element within array. */
+ CONST int flags; /* Only TCL_LEAVE_ERR_MSG bit matters. */
+ CONST char *msg; /* Verb to use in error messages, e.g.
+ * "read" or "set". Only needed if
+ * TCL_LEAVE_ERR_MSG is set in flags. */
+ CONST int createArray; /* If 1, transform arrayName to be an array
+ * if it isn't one yet and the transformation
+ * is possible. If 0, return error if it
+ * isn't already an array. */
+ CONST int createElem; /* If 1, create hash table entry for the
+ * element, if it doesn't already exist. If
+ * 0, return error if it doesn't exist. */
+ Var *arrayPtr; /* Pointer to the array's Var structure. */
+{
+ Tcl_HashEntry *hPtr;
+ int new;
+ Var *varPtr;
/*
* We're dealing with an array element. Make sure the variable is an
* array and look up the element (create the element if desired).
*/
- if (TclIsVarUndefined(varPtr) && !TclIsVarArrayElement(varPtr)) {
- if (!createPart1) {
+ if (TclIsVarUndefined(arrayPtr) && !TclIsVarArrayElement(arrayPtr)) {
+ if (!createArray) {
if (flags & TCL_LEAVE_ERR_MSG) {
- VarErrMsg(interp, part1, part2, msg, noSuchVar);
+ VarErrMsg(interp, arrayName, elName, msg, noSuchVar);
}
- varPtr = NULL;
- goto done;
+ return NULL;
}
/*
* Make sure we are not resurrecting a namespace variable from a
* deleted namespace!
*/
- if ((varPtr->flags & VAR_IN_HASHTABLE) && (varPtr->hPtr == NULL)) {
+ if ((arrayPtr->flags & VAR_IN_HASHTABLE) && (arrayPtr->hPtr == NULL)) {
if (flags & TCL_LEAVE_ERR_MSG) {
- VarErrMsg(interp, part1, part2, msg, danglingVar);
+ VarErrMsg(interp, arrayName, elName, msg, danglingVar);
}
- varPtr = NULL;
- goto done;
+ return NULL;
}
- TclSetVarArray(varPtr);
- TclClearVarUndefined(varPtr);
- varPtr->value.tablePtr =
+ TclSetVarArray(arrayPtr);
+ TclClearVarUndefined(arrayPtr);
+ arrayPtr->value.tablePtr =
(Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
- Tcl_InitHashTable(varPtr->value.tablePtr, TCL_STRING_KEYS);
- } else if (!TclIsVarArray(varPtr)) {
+ Tcl_InitHashTable(arrayPtr->value.tablePtr, TCL_STRING_KEYS);
+ } else if (!TclIsVarArray(arrayPtr)) {
if (flags & TCL_LEAVE_ERR_MSG) {
- VarErrMsg(interp, part1, part2, msg, needArray);
+ VarErrMsg(interp, arrayName, elName, msg, needArray);
}
- varPtr = NULL;
- goto done;
- }
- *arrayPtrPtr = varPtr;
- if (closeParen != NULL) {
- *closeParen = 0;
+ return NULL;
}
- if (createPart2) {
- hPtr = Tcl_CreateHashEntry(varPtr->value.tablePtr, elName, &new);
- if (closeParen != NULL) {
- *closeParen = ')';
- }
+
+ if (createElem) {
+ hPtr = Tcl_CreateHashEntry(arrayPtr->value.tablePtr, elName, &new);
if (new) {
- if (varPtr->searchPtr != NULL) {
- DeleteSearches(varPtr);
+ if (arrayPtr->searchPtr != NULL) {
+ DeleteSearches(arrayPtr);
}
varPtr = NewVar();
Tcl_SetHashValue(hPtr, varPtr);
varPtr->hPtr = hPtr;
- varPtr->nsPtr = varNsPtr;
+ varPtr->nsPtr = arrayPtr->nsPtr;
TclSetVarArrayElement(varPtr);
}
} else {
- hPtr = Tcl_FindHashEntry(varPtr->value.tablePtr, elName);
- if (closeParen != NULL) {
- *closeParen = ')';
- }
+ hPtr = Tcl_FindHashEntry(arrayPtr->value.tablePtr, elName);
if (hPtr == NULL) {
if (flags & TCL_LEAVE_ERR_MSG) {
- VarErrMsg(interp, part1, part2, msg, noSuchElement);
+ VarErrMsg(interp, arrayName, elName, msg, noSuchElement);
}
- varPtr = NULL;
- goto done;
+ return NULL;
}
}
- varPtr = (Var *) Tcl_GetHashValue(hPtr);
-
- done:
- if (openParen != NULL) {
- *openParen = '(';
- }
- return varPtr;
+ return (Var *) Tcl_GetHashValue(hPtr);
}
/*
@@ -463,11 +975,11 @@ TclLookupVar(interp, part1, part2, flags, msg, createPart1, createPart2,
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_GetVar(interp, varName, flags)
Tcl_Interp *interp; /* Command interpreter in which varName is
* to be looked up. */
- char *varName; /* Name of a variable in interp. */
+ CONST char *varName; /* Name of a variable in interp. */
int flags; /* OR-ed combination of TCL_GLOBAL_ONLY,
* TCL_NAMESPACE_ONLY or TCL_LEAVE_ERR_MSG
* bits. */
@@ -498,13 +1010,13 @@ Tcl_GetVar(interp, varName, flags)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_GetVar2(interp, part1, part2, flags)
Tcl_Interp *interp; /* Command interpreter in which variable is
* to be looked up. */
- char *part1; /* Name of an array (if part2 is non-NULL)
+ CONST char *part1; /* Name of an array (if part2 is non-NULL)
* or the name of a variable. */
- char *part2; /* If non-NULL, gives the name of an element
+ CONST char *part2; /* If non-NULL, gives the name of an element
* in the array part1. */
int flags; /* OR-ed combination of TCL_GLOBAL_ONLY,
* TCL_NAMESPACE_ONLY and TCL_LEAVE_ERR_MSG
@@ -518,54 +1030,6 @@ Tcl_GetVar2(interp, part1, part2, flags)
}
return TclGetString(objPtr);
}
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_ObjGetVar2 --
- *
- * Return the value of a Tcl variable as a Tcl object, given a
- * two-part name consisting of array name and element within array.
- *
- * Results:
- * The return value points to the current object value of the variable
- * given by part1Ptr and part2Ptr. If the specified variable doesn't
- * exist, or if there is a clash in array usage, then NULL is returned
- * and a message will be left in the interpreter's result if the
- * TCL_LEAVE_ERR_MSG flag is set.
- *
- * Side effects:
- * The ref count for the returned object is _not_ incremented to
- * reflect the returned reference; if you want to keep a reference to
- * the object you must increment its ref count yourself.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Obj *
-Tcl_ObjGetVar2(interp, part1Ptr, part2Ptr, flags)
- Tcl_Interp *interp; /* Command interpreter in which variable is
- * to be looked up. */
- register Tcl_Obj *part1Ptr; /* Points to an object holding the name of
- * an array (if part2 is non-NULL) or the
- * name of a variable. */
- register Tcl_Obj *part2Ptr; /* If non-null, points to an object holding
- * the name of an element in the array
- * part1Ptr. */
- int flags; /* OR-ed combination of TCL_GLOBAL_ONLY,
- * TCL_LEAVE_ERR_MSG, and
- * TCL_PARSE_PART1 bits. */
-{
- char *part1, *part2;
-
- part1 = Tcl_GetString(part1Ptr);
- if (part2Ptr != NULL) {
- part2 = Tcl_GetString(part2Ptr);
- } else {
- part2 = NULL;
- }
-
- return Tcl_GetVar2Ex(interp, part1, part2, flags);
-}
/*
*----------------------------------------------------------------------
@@ -594,87 +1058,44 @@ Tcl_Obj *
Tcl_GetVar2Ex(interp, part1, part2, flags)
Tcl_Interp *interp; /* Command interpreter in which variable is
* to be looked up. */
- char *part1; /* Name of an array (if part2 is non-NULL)
+ CONST char *part1; /* Name of an array (if part2 is non-NULL)
* or the name of a variable. */
- char *part2; /* If non-NULL, gives the name of an element
+ CONST char *part2; /* If non-NULL, gives the name of an element
* in the array part1. */
int flags; /* OR-ed combination of TCL_GLOBAL_ONLY,
* and TCL_LEAVE_ERR_MSG bits. */
{
- Interp *iPtr = (Interp *) interp;
- register Var *varPtr;
- Var *arrayPtr;
- char *msg;
+ Var *varPtr, *arrayPtr;
+ /*
+ * We need a special flag check to see if we want to create part 1,
+ * because commands like lappend require read traces to trigger for
+ * previously non-existent values.
+ */
varPtr = TclLookupVar(interp, part1, part2, flags, "read",
- /*createPart1*/ 0, /*createPart2*/ 1, &arrayPtr);
+ /*createPart1*/ (flags & TCL_TRACE_READS),
+ /*createPart2*/ 1, &arrayPtr);
if (varPtr == NULL) {
return NULL;
}
- /*
- * Invoke any traces that have been set for the variable.
- */
-
- if ((varPtr->tracePtr != NULL)
- || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
- msg = CallTraces(iPtr, arrayPtr, varPtr, part1, part2,
- (flags & (TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY)) | TCL_TRACE_READS);
- if (msg != NULL) {
- if (flags & TCL_LEAVE_ERR_MSG) {
- VarErrMsg(interp, part1, part2, "read", msg);
- }
- goto errorReturn;
- }
- }
-
- /*
- * Return the element if it's an existing scalar variable.
- */
-
- if (TclIsVarScalar(varPtr) && !TclIsVarUndefined(varPtr)) {
- return varPtr->value.objPtr;
- }
-
- if (flags & TCL_LEAVE_ERR_MSG) {
- if (TclIsVarUndefined(varPtr) && (arrayPtr != NULL)
- && !TclIsVarUndefined(arrayPtr)) {
- msg = noSuchElement;
- } else if (TclIsVarArray(varPtr)) {
- msg = isArray;
- } else {
- msg = noSuchVar;
- }
- VarErrMsg(interp, part1, part2, "read", msg);
- }
-
- /*
- * An error. If the variable doesn't exist anymore and no-one's using
- * it, then free up the relevant structures and hash table entries.
- */
-
- errorReturn:
- if (TclIsVarUndefined(varPtr)) {
- CleanupVar(varPtr, arrayPtr);
- }
- return NULL;
+ return TclPtrGetVar(interp, varPtr, arrayPtr, part1, part2, flags);
}
/*
*----------------------------------------------------------------------
*
- * TclGetIndexedScalar --
+ * Tcl_ObjGetVar2 --
*
- * Return the Tcl object value of a local scalar variable in the active
- * procedure, given its index in the procedure's array of compiler
- * allocated local variables.
+ * Return the value of a Tcl variable as a Tcl object, given a
+ * two-part name consisting of array name and element within array.
*
* Results:
* The return value points to the current object value of the variable
- * given by localIndex. If the specified variable doesn't exist, or
- * there is a clash in array usage, or an error occurs while executing
- * variable traces, then NULL is returned and a message will be left in
- * the interpreter's result if leaveErrorMsg is 1.
+ * given by part1Ptr and part2Ptr. If the specified variable doesn't
+ * exist, or if there is a clash in array usage, then NULL is returned
+ * and a message will be left in the interpreter's result if the
+ * TCL_LEAVE_ERR_MSG flag is set.
*
* Side effects:
* The ref count for the returned object is _not_ incremented to
@@ -685,109 +1106,53 @@ Tcl_GetVar2Ex(interp, part1, part2, flags)
*/
Tcl_Obj *
-TclGetIndexedScalar(interp, localIndex, leaveErrorMsg)
+Tcl_ObjGetVar2(interp, part1Ptr, part2Ptr, flags)
Tcl_Interp *interp; /* Command interpreter in which variable is
* to be looked up. */
- register int localIndex; /* Index of variable in procedure's array
- * of local variables. */
- int leaveErrorMsg; /* 1 if to leave an error message in
- * interpreter's result on an error.
- * Otherwise no error message is left. */
+ register Tcl_Obj *part1Ptr; /* Points to an object holding the name of
+ * an array (if part2 is non-NULL) or the
+ * name of a variable. */
+ register Tcl_Obj *part2Ptr; /* If non-null, points to an object holding
+ * the name of an element in the array
+ * part1Ptr. */
+ int flags; /* OR-ed combination of TCL_GLOBAL_ONLY and
+ * TCL_LEAVE_ERR_MSG bits. */
{
- Interp *iPtr = (Interp *) interp;
- CallFrame *varFramePtr = iPtr->varFramePtr;
- /* Points to the procedure call frame whose
- * variables are currently in use. Same as
- * the current procedure's frame, if any,
- * unless an "uplevel" is executing. */
- Var *compiledLocals = varFramePtr->compiledLocals;
- register Var *varPtr; /* Points to the variable's in-frame Var
- * structure. */
- char *varName; /* Name of the local variable. */
- char *msg;
-
-#ifdef TCL_COMPILE_DEBUG
- int localCt = varFramePtr->procPtr->numCompiledLocals;
+ Var *varPtr, *arrayPtr;
+ char *part1, *part2;
- if (compiledLocals == NULL) {
- fprintf(stderr, "\nTclGetIndexedScalar: can't get local %i in frame 0x%x, no compiled locals\n",
- localIndex, (unsigned int) varFramePtr);
- panic("TclGetIndexedScalar: no compiled locals in frame 0x%x",
- (unsigned int) varFramePtr);
- }
- if ((localIndex < 0) || (localIndex >= localCt)) {
- fprintf(stderr, "\nTclGetIndexedScalar: can't get local %i in frame 0x%x with %i locals\n",
- localIndex, (unsigned int) varFramePtr, localCt);
- panic("TclGetIndexedScalar: bad local index %i in frame 0x%x",
- localIndex, (unsigned int) varFramePtr);
- }
-#endif /* TCL_COMPILE_DEBUG */
+ part1 = Tcl_GetString(part1Ptr);
+ part2 = ((part2Ptr == NULL) ? NULL : Tcl_GetString(part2Ptr));
- varPtr = &(compiledLocals[localIndex]);
- varName = varPtr->name;
-
- /*
- * If varPtr is a link variable, we have a reference to some variable
- * that was created through an "upvar" or "global" command, or we have a
- * reference to a variable in an enclosing namespace. Traverse through
- * any links until we find the referenced variable.
- */
-
- while (TclIsVarLink(varPtr)) {
- varPtr = varPtr->value.linkPtr;
- }
-
/*
- * Invoke any traces that have been set for the variable.
+ * We need a special flag check to see if we want to create part 1,
+ * because commands like lappend require read traces to trigger for
+ * previously non-existent values.
*/
-
- if (varPtr->tracePtr != NULL) {
- msg = CallTraces(iPtr, /*arrayPtr*/ NULL, varPtr, varName, NULL,
- TCL_TRACE_READS);
- if (msg != NULL) {
- if (leaveErrorMsg) {
- VarErrMsg(interp, varName, NULL, "read", msg);
- }
- return NULL;
- }
- }
-
- /*
- * Make sure we're dealing with a scalar variable and not an array, and
- * that the variable exists (isn't undefined).
- */
-
- if (!TclIsVarScalar(varPtr) || TclIsVarUndefined(varPtr)) {
- if (leaveErrorMsg) {
- if (TclIsVarArray(varPtr)) {
- msg = isArray;
- } else {
- msg = noSuchVar;
- }
- VarErrMsg(interp, varName, NULL, "read", msg);
-
- }
+ varPtr = TclObjLookupVar(interp, part1Ptr, part2, flags, "read",
+ /*createPart1*/ (flags & TCL_TRACE_READS),
+ /*createPart2*/ 1, &arrayPtr);
+ if (varPtr == NULL) {
return NULL;
}
- return varPtr->value.objPtr;
+
+ return TclPtrGetVar(interp, varPtr, arrayPtr, part1, part2, flags);
}
/*
*----------------------------------------------------------------------
*
- * TclGetElementOfIndexedArray --
+ * TclPtrGetVar --
*
- * Return the Tcl object value for an element in a local array
- * variable. The element is named by the object elemPtr while the
- * array is specified by its index in the active procedure's array
- * of compiler allocated local variables.
+ * Return the value of a Tcl variable as a Tcl object, given the
+ * pointers to the variable's (and possibly containing array's)
+ * VAR structure.
*
* Results:
- * The return value points to the current object value of the
- * element. If the specified array or element doesn't exist, or there
- * is a clash in array usage, or an error occurs while executing
- * variable traces, then NULL is returned and a message will be left in
- * the interpreter's result if leaveErrorMsg is 1.
+ * The return value points to the current object value of the variable
+ * given by varPtr. If the specified variable doesn't exist, or if there
+ * is a clash in array usage, then NULL is returned and a message will be
+ * left in the interpreter's result if the TCL_LEAVE_ERR_MSG flag is set.
*
* Side effects:
* The ref count for the returned object is _not_ incremented to
@@ -798,114 +1163,31 @@ TclGetIndexedScalar(interp, localIndex, leaveErrorMsg)
*/
Tcl_Obj *
-TclGetElementOfIndexedArray(interp, localIndex, elemPtr, leaveErrorMsg)
+TclPtrGetVar(interp, varPtr, arrayPtr, part1, part2, flags)
Tcl_Interp *interp; /* Command interpreter in which variable is
* to be looked up. */
- int localIndex; /* Index of array variable in procedure's
- * array of local variables. */
- Tcl_Obj *elemPtr; /* Points to an object holding the name of
- * an element to get in the array. */
- int leaveErrorMsg; /* 1 if to leave an error message in
- * the interpreter's result on an error.
- * Otherwise no error message is left. */
+ register Var *varPtr; /* The variable to be read.*/
+ Var *arrayPtr; /* NULL for scalar variables, pointer to
+ * the containing array otherwise. */
+ CONST char *part1; /* Name of an array (if part2 is non-NULL)
+ * or the name of a variable. */
+ CONST char *part2; /* If non-NULL, gives the name of an element
+ * in the array part1. */
+ CONST int flags; /* OR-ed combination of TCL_GLOBAL_ONLY,
+ * and TCL_LEAVE_ERR_MSG bits. */
{
Interp *iPtr = (Interp *) interp;
- CallFrame *varFramePtr = iPtr->varFramePtr;
- /* Points to the procedure call frame whose
- * variables are currently in use. Same as
- * the current procedure's frame, if any,
- * unless an "uplevel" is executing. */
- Var *compiledLocals = varFramePtr->compiledLocals;
- Var *arrayPtr; /* Points to the array's in-frame Var
- * structure. */
- char *arrayName; /* Name of the local array. */
- Tcl_HashEntry *hPtr;
- Var *varPtr = NULL; /* Points to the element's Var structure
- * that we return. Initialized to avoid
- * compiler warning. */
- char *elem, *msg;
- int new;
-
-#ifdef TCL_COMPILE_DEBUG
- Proc *procPtr = varFramePtr->procPtr;
- int localCt = procPtr->numCompiledLocals;
-
- if (compiledLocals == NULL) {
- fprintf(stderr, "\nTclGetElementOfIndexedArray: can't get element of local %i in frame 0x%x, no compiled locals\n",
- localIndex, (unsigned int) varFramePtr);
- panic("TclGetIndexedScalar: no compiled locals in frame 0x%x",
- (unsigned int) varFramePtr);
- }
- if ((localIndex < 0) || (localIndex >= localCt)) {
- fprintf(stderr, "\nTclGetIndexedScalar: can't get element of local %i in frame 0x%x with %i locals\n",
- localIndex, (unsigned int) varFramePtr, localCt);
- panic("TclGetElementOfIndexedArray: bad local index %i in frame 0x%x",
- localIndex, (unsigned int) varFramePtr);
- }
-#endif /* TCL_COMPILE_DEBUG */
-
- elem = TclGetString(elemPtr);
- arrayPtr = &(compiledLocals[localIndex]);
- arrayName = arrayPtr->name;
+ CONST char *msg;
/*
- * If arrayPtr is a link variable, we have a reference to some variable
- * that was created through an "upvar" or "global" command, or we have a
- * reference to a variable in an enclosing namespace. Traverse through
- * any links until we find the referenced variable.
- */
-
- while (TclIsVarLink(arrayPtr)) {
- arrayPtr = arrayPtr->value.linkPtr;
- }
-
- /*
- * Make sure we're dealing with an array and that the array variable
- * exists (isn't undefined).
- */
-
- if (!TclIsVarArray(arrayPtr) || TclIsVarUndefined(arrayPtr)) {
- if (leaveErrorMsg) {
- VarErrMsg(interp, arrayName, elem, "read", noSuchVar);
- }
- goto errorReturn;
- }
-
- /*
- * Look up the element. Note that we must create the element (but leave
- * it marked undefined) if it does not already exist. This allows a
- * trace to create new array elements "on the fly" that did not exist
- * before. A trace is always passed a variable for the array element. If
- * the trace does not define the variable, it will be deleted below (at
- * errorReturn) and an error returned.
- */
-
- hPtr = Tcl_CreateHashEntry(arrayPtr->value.tablePtr, elem, &new);
- if (new) {
- if (arrayPtr->searchPtr != NULL) {
- DeleteSearches(arrayPtr);
- }
- varPtr = NewVar();
- Tcl_SetHashValue(hPtr, varPtr);
- varPtr->hPtr = hPtr;
- varPtr->nsPtr = varFramePtr->nsPtr;
- TclSetVarArrayElement(varPtr);
- } else {
- varPtr = (Var *) Tcl_GetHashValue(hPtr);
- }
-
- /*
- * Invoke any traces that have been set for the element variable.
+ * Invoke any traces that have been set for the variable.
*/
if ((varPtr->tracePtr != NULL)
- || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
- msg = CallTraces(iPtr, arrayPtr, varPtr, arrayName, elem,
- TCL_TRACE_READS);
- if (msg != NULL) {
- if (leaveErrorMsg) {
- VarErrMsg(interp, arrayName, elem, "read", msg);
- }
+ || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
+ if (TCL_ERROR == CallVarTraces(iPtr, arrayPtr, varPtr, part1, part2,
+ (flags & (TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY))
+ | TCL_TRACE_READS, (flags & TCL_LEAVE_ERR_MSG))) {
goto errorReturn;
}
}
@@ -918,13 +1200,16 @@ TclGetElementOfIndexedArray(interp, localIndex, elemPtr, leaveErrorMsg)
return varPtr->value.objPtr;
}
- if (leaveErrorMsg) {
- if (TclIsVarArray(varPtr)) {
+ if (flags & TCL_LEAVE_ERR_MSG) {
+ if (TclIsVarUndefined(varPtr) && (arrayPtr != NULL)
+ && !TclIsVarUndefined(arrayPtr)) {
+ msg = noSuchElement;
+ } else if (TclIsVarArray(varPtr)) {
msg = isArray;
} else {
msg = noSuchVar;
}
- VarErrMsg(interp, arrayName, elem, "read", msg);
+ VarErrMsg(interp, part1, part2, "read", msg);
}
/*
@@ -933,8 +1218,8 @@ TclGetElementOfIndexedArray(interp, localIndex, elemPtr, leaveErrorMsg)
*/
errorReturn:
- if ((varPtr != NULL) && TclIsVarUndefined(varPtr)) {
- CleanupVar(varPtr, NULL); /* the array is not in a hashtable */
+ if (TclIsVarUndefined(varPtr)) {
+ CleanupVar(varPtr, arrayPtr);
}
return NULL;
}
@@ -1012,12 +1297,12 @@ Tcl_SetObjCmd(dummy, interp, objc, objv)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_SetVar(interp, varName, newValue, flags)
Tcl_Interp *interp; /* Command interpreter in which varName is
* to be looked up. */
- char *varName; /* Name of a variable in interp. */
- char *newValue; /* New value for varName. */
+ CONST char *varName; /* Name of a variable in interp. */
+ CONST char *newValue; /* New value for varName. */
int flags; /* Various flags that tell how to set value:
* any of TCL_GLOBAL_ONLY,
* TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
@@ -1053,16 +1338,16 @@ Tcl_SetVar(interp, varName, newValue, flags)
*----------------------------------------------------------------------
*/
-char *
+CONST char *
Tcl_SetVar2(interp, part1, part2, newValue, flags)
Tcl_Interp *interp; /* Command interpreter in which variable is
* to be looked up. */
- char *part1; /* If part2 is NULL, this is name of scalar
+ CONST char *part1; /* If part2 is NULL, this is name of scalar
* variable. Otherwise it is the name of
* an array. */
- char *part2; /* Name of an element within an array, or
+ CONST char *part2; /* Name of an element within an array, or
* NULL. */
- char *newValue; /* New value for variable. */
+ CONST char *newValue; /* New value for variable. */
int flags; /* Various flags that tell how to set value:
* any of TCL_GLOBAL_ONLY,
* TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
@@ -1091,9 +1376,73 @@ Tcl_SetVar2(interp, part1, part2, newValue, flags)
/*
*----------------------------------------------------------------------
*
+ * Tcl_SetVar2Ex --
+ *
+ * Given a two-part variable name, which may refer either to a scalar
+ * variable or an element of an array, change the value of the variable
+ * to a new Tcl object value. If the named scalar or array or element
+ * doesn't exist then create one.
+ *
+ * Results:
+ * Returns a pointer to the Tcl_Obj holding the new value of the
+ * variable. If the write operation was disallowed because an array was
+ * expected but not found (or vice versa), then NULL is returned; if
+ * the TCL_LEAVE_ERR_MSG flag is set, then an explanatory message will
+ * be left in the interpreter's result. Note that the returned object
+ * may not be the same one referenced by newValuePtr; this is because
+ * variable traces may modify the variable's value.
+ *
+ * Side effects:
+ * The value of the given variable is set. If either the array or the
+ * entry didn't exist then a new variable is created.
+ *
+ * The reference count is decremented for any old value of the variable
+ * and incremented for its new value. If the new value for the variable
+ * is not the same one referenced by newValuePtr (perhaps as a result
+ * of a variable trace), then newValuePtr's ref count is left unchanged
+ * by Tcl_SetVar2Ex. newValuePtr's ref count is also left unchanged if
+ * we are appending it as a string value: that is, if "flags" includes
+ * TCL_APPEND_VALUE but not TCL_LIST_ELEMENT.
+ *
+ * The reference count for the returned object is _not_ incremented: if
+ * you want to keep a reference to the object you must increment its
+ * ref count yourself.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+Tcl_SetVar2Ex(interp, part1, part2, newValuePtr, flags)
+ Tcl_Interp *interp; /* Command interpreter in which variable is
+ * to be found. */
+ CONST char *part1; /* Name of an array (if part2 is non-NULL)
+ * or the name of a variable. */
+ CONST char *part2; /* If non-NULL, gives the name of an element
+ * in the array part1. */
+ Tcl_Obj *newValuePtr; /* New value for variable. */
+ int flags; /* Various flags that tell how to set value:
+ * any of TCL_GLOBAL_ONLY,
+ * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
+ * TCL_LIST_ELEMENT or TCL_LEAVE_ERR_MSG. */
+{
+ Var *varPtr, *arrayPtr;
+
+ varPtr = TclLookupVar(interp, part1, part2, flags, "set",
+ /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
+ if (varPtr == NULL) {
+ return NULL;
+ }
+
+ return TclPtrSetVar(interp, varPtr, arrayPtr, part1, part2,
+ newValuePtr, flags);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_ObjSetVar2 --
*
- * This function is the same as Tcl_SetVar2Ex below, except the
+ * This function is the same as Tcl_SetVar2Ex above, except the
* variable names are passed in Tcl object instead of strings.
*
* Results:
@@ -1108,7 +1457,6 @@ Tcl_SetVar2(interp, part1, part2, newValue, flags)
* Side effects:
* The value of the given variable is set. If either the array or the
* entry didn't exist then a new variable is created.
-
*
*----------------------------------------------------------------------
*/
@@ -1127,30 +1475,33 @@ Tcl_ObjSetVar2(interp, part1Ptr, part2Ptr, newValuePtr, flags)
int flags; /* Various flags that tell how to set value:
* any of TCL_GLOBAL_ONLY,
* TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
- * TCL_LIST_ELEMENT, TCL_LEAVE_ERR_MSG, or
- * TCL_PARSE_PART1. */
+ * TCL_LIST_ELEMENT, or TCL_LEAVE_ERR_MSG. */
{
+ Var *varPtr, *arrayPtr;
char *part1, *part2;
- part1 = Tcl_GetString(part1Ptr);
- if (part2Ptr != NULL) {
- part2 = Tcl_GetString(part2Ptr);
- } else {
- part2 = NULL;
+ part1 = TclGetString(part1Ptr);
+ part2 = ((part2Ptr == NULL) ? NULL : Tcl_GetString(part2Ptr));
+
+ varPtr = TclObjLookupVar(interp, part1Ptr, part2, flags, "set",
+ /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
+ if (varPtr == NULL) {
+ return NULL;
}
-
- return Tcl_SetVar2Ex(interp, part1, part2, newValuePtr, flags);
+
+ return TclPtrSetVar(interp, varPtr, arrayPtr, part1, part2,
+ newValuePtr, flags);
}
+
/*
*----------------------------------------------------------------------
*
- * Tcl_SetVar2Ex --
+ * TclPtrSetVar --
*
- * Given a two-part variable name, which may refer either to a scalar
- * variable or an element of an array, change the value of the variable
- * to a new Tcl object value. If the named scalar or array or element
- * doesn't exist then create one.
+ * This function is the same as Tcl_SetVar2Ex above, except that
+ * it requires pointers to the variable's Var structs in addition
+ * to the variable names.
*
* Results:
* Returns a pointer to the Tcl_Obj holding the new value of the
@@ -1164,49 +1515,29 @@ Tcl_ObjSetVar2(interp, part1Ptr, part2Ptr, newValuePtr, flags)
* Side effects:
* The value of the given variable is set. If either the array or the
* entry didn't exist then a new variable is created.
- *
- * The reference count is decremented for any old value of the variable
- * and incremented for its new value. If the new value for the variable
- * is not the same one referenced by newValuePtr (perhaps as a result
- * of a variable trace), then newValuePtr's ref count is left unchanged
- * by Tcl_SetVar2Ex. newValuePtr's ref count is also left unchanged if
- * we are appending it as a string value: that is, if "flags" includes
- * TCL_APPEND_VALUE but not TCL_LIST_ELEMENT.
- *
- * The reference count for the returned object is _not_ incremented: if
- * you want to keep a reference to the object you must increment its
- * ref count yourself.
+
*
*----------------------------------------------------------------------
*/
Tcl_Obj *
-Tcl_SetVar2Ex(interp, part1, part2, newValuePtr, flags)
+TclPtrSetVar(interp, varPtr, arrayPtr, part1, part2, newValuePtr, flags)
Tcl_Interp *interp; /* Command interpreter in which variable is
- * to be found. */
- char *part1; /* Name of an array (if part2 is non-NULL)
+ * to be looked up. */
+ register Var *varPtr;
+ Var *arrayPtr;
+ CONST char *part1; /* Name of an array (if part2 is non-NULL)
* or the name of a variable. */
- char *part2; /* If non-NULL, gives the name of an element
+ CONST char *part2; /* If non-NULL, gives the name of an element
* in the array part1. */
Tcl_Obj *newValuePtr; /* New value for variable. */
- int flags; /* Various flags that tell how to set value:
- * any of TCL_GLOBAL_ONLY,
- * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
- * TCL_LIST_ELEMENT or TCL_LEAVE_ERR_MSG. */
+ CONST int flags; /* OR-ed combination of TCL_GLOBAL_ONLY,
+ * and TCL_LEAVE_ERR_MSG bits. */
{
Interp *iPtr = (Interp *) interp;
- register Var *varPtr;
- Var *arrayPtr;
Tcl_Obj *oldValuePtr;
Tcl_Obj *resultPtr = NULL;
- char *bytes;
- int length, result;
-
- varPtr = TclLookupVar(interp, part1, part2, flags, "set",
- /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
- if (varPtr == NULL) {
- return NULL;
- }
+ int result;
/*
* If the variable is in a hashtable and its hPtr field is NULL, then we
@@ -1239,12 +1570,18 @@ Tcl_SetVar2Ex(interp, part1, part2, newValuePtr, flags)
}
/*
- * At this point, if we were appending, we used to call read traces: we
- * treated append as a read-modify-write. However, it seemed unlikely to
- * us that a real program would be interested in such reads being done
- * during a set operation.
+ * Invoke any read traces that have been set for the variable if it
+ * is requested; this is only done in the core when lappending.
*/
+ if ((flags & TCL_TRACE_READS) && ((varPtr->tracePtr != NULL)
+ || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL)))) {
+ if (TCL_ERROR == CallVarTraces(iPtr, arrayPtr, varPtr, part1, part2,
+ TCL_TRACE_READS, (flags & TCL_LEAVE_ERR_MSG))) {
+ return NULL;
+ }
+ }
+
/*
* Set the variable's new value. If appending, append the new value to
* the variable, either as a list element or as a string. Also, if
@@ -1281,10 +1618,9 @@ Tcl_SetVar2Ex(interp, part1, part2, newValuePtr, flags)
* We append newValuePtr's bytes but don't change its ref count.
*/
- bytes = Tcl_GetStringFromObj(newValuePtr, &length);
if (oldValuePtr == NULL) {
- varPtr->value.objPtr = Tcl_NewStringObj(bytes, length);
- Tcl_IncrRefCount(varPtr->value.objPtr);
+ varPtr->value.objPtr = newValuePtr;
+ Tcl_IncrRefCount(newValuePtr);
} else {
if (Tcl_IsShared(oldValuePtr)) { /* append to copy */
varPtr->value.objPtr = Tcl_DuplicateObj(oldValuePtr);
@@ -1295,34 +1631,16 @@ Tcl_SetVar2Ex(interp, part1, part2, newValuePtr, flags)
Tcl_AppendObjToObj(oldValuePtr, newValuePtr);
}
}
- } else {
- if (flags & TCL_LIST_ELEMENT) { /* set var to list element */
- int neededBytes, listFlags;
-
- /*
- * We set the variable to the result of converting newValuePtr's
- * string rep to a list element. We do not change newValuePtr's
- * ref count.
- */
+ } else if (newValuePtr != oldValuePtr) {
+ /*
+ * In this case we are replacing the value, so we don't need to
+ * do more than swap the objects.
+ */
- if (oldValuePtr != NULL) {
- Tcl_DecrRefCount(oldValuePtr); /* discard old value */
- }
- bytes = Tcl_GetStringFromObj(newValuePtr, &length);
- neededBytes = Tcl_ScanElement(bytes, &listFlags);
- oldValuePtr = Tcl_NewObj();
- oldValuePtr->bytes = (char *)
- ckalloc((unsigned) (neededBytes + 1));
- oldValuePtr->length = Tcl_ConvertElement(bytes,
- oldValuePtr->bytes, listFlags);
- varPtr->value.objPtr = oldValuePtr;
- Tcl_IncrRefCount(varPtr->value.objPtr);
- } else if (newValuePtr != oldValuePtr) {
- varPtr->value.objPtr = newValuePtr;
- Tcl_IncrRefCount(newValuePtr); /* var is another ref */
- if (oldValuePtr != NULL) {
- TclDecrRefCount(oldValuePtr); /* discard old value */
- }
+ varPtr->value.objPtr = newValuePtr;
+ Tcl_IncrRefCount(newValuePtr); /* var is another ref */
+ if (oldValuePtr != NULL) {
+ TclDecrRefCount(oldValuePtr); /* discard old value */
}
}
TclSetVarScalar(varPtr);
@@ -1337,12 +1655,9 @@ Tcl_SetVar2Ex(interp, part1, part2, newValuePtr, flags)
if ((varPtr->tracePtr != NULL)
|| ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
- char *msg = CallTraces(iPtr, arrayPtr, varPtr, part1, part2,
- (flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY)) | TCL_TRACE_WRITES);
- if (msg != NULL) {
- if (flags & TCL_LEAVE_ERR_MSG) {
- VarErrMsg(interp, part1, part2, "set", msg);
- }
+ if (TCL_ERROR == CallVarTraces(iPtr, arrayPtr, varPtr, part1, part2,
+ (flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY))
+ | TCL_TRACE_WRITES, (flags & TCL_LEAVE_ERR_MSG))) {
goto cleanup;
}
}
@@ -1379,403 +1694,6 @@ Tcl_SetVar2Ex(interp, part1, part2, newValuePtr, flags)
/*
*----------------------------------------------------------------------
*
- * TclSetIndexedScalar --
- *
- * Change the Tcl object value of a local scalar variable in the active
- * procedure, given its compile-time allocated index in the procedure's
- * array of local variables.
- *
- * Results:
- * Returns a pointer to the Tcl_Obj holding the new value of the
- * variable given by localIndex. If the specified variable doesn't
- * exist, or there is a clash in array usage, or an error occurs while
- * executing variable traces, then NULL is returned and a message will
- * be left in the interpreter's result if leaveErrorMsg is 1. Note
- * that the returned object may not be the same one referenced by
- * newValuePtr; this is because variable traces may modify the
- * variable's value.
- *
- * Side effects:
- * The value of the given variable is set. The reference count is
- * decremented for any old value of the variable and incremented for
- * its new value. If as a result of a variable trace the new value for
- * the variable is not the same one referenced by newValuePtr, then
- * newValuePtr's ref count is left unchanged. The ref count for the
- * returned object is _not_ incremented to reflect the returned
- * reference; if you want to keep a reference to the object you must
- * increment its ref count yourself. This procedure does not create
- * new variables, but only sets those recognized at compile time.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Obj *
-TclSetIndexedScalar(interp, localIndex, newValuePtr, leaveErrorMsg)
- Tcl_Interp *interp; /* Command interpreter in which variable is
- * to be found. */
- int localIndex; /* Index of variable in procedure's array
- * of local variables. */
- Tcl_Obj *newValuePtr; /* New value for variable. */
- int leaveErrorMsg; /* 1 if to leave an error message in
- * the interpreter's result on an error.
- * Otherwise no error message is left. */
-{
- Interp *iPtr = (Interp *) interp;
- CallFrame *varFramePtr = iPtr->varFramePtr;
- /* Points to the procedure call frame whose
- * variables are currently in use. Same as
- * the current procedure's frame, if any,
- * unless an "uplevel" is executing. */
- Var *compiledLocals = varFramePtr->compiledLocals;
- register Var *varPtr; /* Points to the variable's in-frame Var
- * structure. */
- char *varName; /* Name of the local variable. */
- Tcl_Obj *oldValuePtr;
- Tcl_Obj *resultPtr = NULL;
-
-#ifdef TCL_COMPILE_DEBUG
- Proc *procPtr = varFramePtr->procPtr;
- int localCt = procPtr->numCompiledLocals;
-
- if (compiledLocals == NULL) {
- fprintf(stderr, "\nTclSetIndexedScalar: can't set local %i in frame 0x%x, no compiled locals\n",
- localIndex, (unsigned int) varFramePtr);
- panic("TclSetIndexedScalar: no compiled locals in frame 0x%x",
- (unsigned int) varFramePtr);
- }
- if ((localIndex < 0) || (localIndex >= localCt)) {
- fprintf(stderr, "\nTclSetIndexedScalar: can't set local %i in frame 0x%x with %i locals\n",
- localIndex, (unsigned int) varFramePtr, localCt);
- panic("TclSetIndexedScalar: bad local index %i in frame 0x%x",
- localIndex, (unsigned int) varFramePtr);
- }
-#endif /* TCL_COMPILE_DEBUG */
-
- varPtr = &(compiledLocals[localIndex]);
- varName = varPtr->name;
-
- /*
- * If varPtr is a link variable, we have a reference to some variable
- * that was created through an "upvar" or "global" command, or we have a
- * reference to a variable in an enclosing namespace. Traverse through
- * any links until we find the referenced variable.
- */
-
- while (TclIsVarLink(varPtr)) {
- varPtr = varPtr->value.linkPtr;
- }
-
- /*
- * If the variable is in a hashtable and its hPtr field is NULL, then we
- * may have an upvar to an array element where the array was deleted
- * or an upvar to a namespace variable whose namespace was deleted.
- * Generate an error (allowing the variable to be reset would screw up
- * our storage allocation and is meaningless anyway).
- */
-
- if ((varPtr->flags & VAR_IN_HASHTABLE) && (varPtr->hPtr == NULL)) {
- if (leaveErrorMsg) {
- if (TclIsVarArrayElement(varPtr)) {
- VarErrMsg(interp, varName, NULL, "set", danglingElement);
- } else {
- VarErrMsg(interp, varName, NULL, "set", danglingVar);
- }
- }
- return NULL;
- }
-
- /*
- * It's an error to try to set an array variable itself.
- */
-
- if (TclIsVarArray(varPtr) && !TclIsVarUndefined(varPtr)) {
- if (leaveErrorMsg) {
- VarErrMsg(interp, varName, NULL, "set", isArray);
- }
- return NULL;
- }
-
- /*
- * Set the variable's new value and discard its old value. We don't
- * append with this "set" procedure so the old value isn't needed.
- */
-
- oldValuePtr = varPtr->value.objPtr;
- if (newValuePtr != oldValuePtr) { /* set new value */
- varPtr->value.objPtr = newValuePtr;
- Tcl_IncrRefCount(newValuePtr); /* var is another ref to obj */
- if (oldValuePtr != NULL) {
- TclDecrRefCount(oldValuePtr); /* discard old value */
- }
- }
- TclSetVarScalar(varPtr);
- TclClearVarUndefined(varPtr);
-
- /*
- * Invoke any write traces for the variable.
- */
-
- if (varPtr->tracePtr != NULL) {
- char *msg = CallTraces(iPtr, /*arrayPtr*/ NULL, varPtr,
- varName, (char *) NULL, TCL_TRACE_WRITES);
- if (msg != NULL) {
- if (leaveErrorMsg) {
- VarErrMsg(interp, varName, NULL, "set", msg);
- }
- goto cleanup;
- }
- }
-
- /*
- * Return the variable's value unless the variable was changed in some
- * gross way by a trace (e.g. it was unset and then recreated as an
- * array). If it was changed is a gross way, just return an empty string
- * object.
- */
-
- if (TclIsVarScalar(varPtr) && !TclIsVarUndefined(varPtr)) {
- return varPtr->value.objPtr;
- }
-
- resultPtr = Tcl_NewObj();
-
- /*
- * If the variable doesn't exist anymore and no-one's using it, then
- * free up the relevant structures and hash table entries.
- */
-
- cleanup:
- if (TclIsVarUndefined(varPtr)) {
- CleanupVar(varPtr, NULL);
- }
- return resultPtr;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclSetElementOfIndexedArray --
- *
- * Change the Tcl object value of an element in a local array
- * variable. The element is named by the object elemPtr while the array
- * is specified by its index in the active procedure's array of
- * compiler allocated local variables.
- *
- * Results:
- * Returns a pointer to the Tcl_Obj holding the new value of the
- * element. If the specified array or element doesn't exist, or there
- * is a clash in array usage, or an error occurs while executing
- * variable traces, then NULL is returned and a message will be left in
- * the interpreter's result if leaveErrorMsg is 1. Note that the
- * returned object may not be the same one referenced by newValuePtr;
- * this is because variable traces may modify the variable's value.
- *
- * Side effects:
- * The value of the given array element is set. The reference count is
- * decremented for any old value of the element and incremented for its
- * new value. If as a result of a variable trace the new value for the
- * element is not the same one referenced by newValuePtr, then
- * newValuePtr's ref count is left unchanged. The ref count for the
- * returned object is _not_ incremented to reflect the returned
- * reference; if you want to keep a reference to the object you must
- * increment its ref count yourself. This procedure will not create new
- * array variables, but only sets elements of those arrays recognized
- * at compile time. However, if the entry doesn't exist then a new
- * variable is created.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Obj *
-TclSetElementOfIndexedArray(interp, localIndex, elemPtr, newValuePtr,
- leaveErrorMsg)
- Tcl_Interp *interp; /* Command interpreter in which the array is
- * to be found. */
- int localIndex; /* Index of array variable in procedure's
- * array of local variables. */
- Tcl_Obj *elemPtr; /* Points to an object holding the name of
- * an element to set in the array. */
- Tcl_Obj *newValuePtr; /* New value for variable. */
- int leaveErrorMsg; /* 1 if to leave an error message in
- * the interpreter's result on an error.
- * Otherwise no error message is left. */
-{
- Interp *iPtr = (Interp *) interp;
- CallFrame *varFramePtr = iPtr->varFramePtr;
- /* Points to the procedure call frame whose
- * variables are currently in use. Same as
- * the current procedure's frame, if any,
- * unless an "uplevel" is executing. */
- Var *compiledLocals = varFramePtr->compiledLocals;
- Var *arrayPtr; /* Points to the array's in-frame Var
- * structure. */
- char *arrayName; /* Name of the local array. */
- char *elem;
- Tcl_HashEntry *hPtr;
- Var *varPtr = NULL; /* Points to the element's Var structure
- * that we return. */
- Tcl_Obj *resultPtr = NULL;
- Tcl_Obj *oldValuePtr;
- int new;
-
-#ifdef TCL_COMPILE_DEBUG
- Proc *procPtr = varFramePtr->procPtr;
- int localCt = procPtr->numCompiledLocals;
-
- if (compiledLocals == NULL) {
- fprintf(stderr, "\nTclSetElementOfIndexedArray: can't set element of local %i in frame 0x%x, no compiled locals\n",
- localIndex, (unsigned int) varFramePtr);
- panic("TclSetIndexedScalar: no compiled locals in frame 0x%x",
- (unsigned int) varFramePtr);
- }
- if ((localIndex < 0) || (localIndex >= localCt)) {
- fprintf(stderr, "\nTclSetIndexedScalar: can't set elememt of local %i in frame 0x%x with %i locals\n",
- localIndex, (unsigned int) varFramePtr, localCt);
- panic("TclSetElementOfIndexedArray: bad local index %i in frame 0x%x",
- localIndex, (unsigned int) varFramePtr);
- }
-#endif /* TCL_COMPILE_DEBUG */
-
- elem = TclGetString(elemPtr);
- arrayPtr = &(compiledLocals[localIndex]);
- arrayName = arrayPtr->name;
-
- /*
- * If arrayPtr is a link variable, we have a reference to some variable
- * that was created through an "upvar" or "global" command, or we have a
- * reference to a variable in an enclosing namespace. Traverse through
- * any links until we find the referenced variable.
- */
-
- while (TclIsVarLink(arrayPtr)) {
- arrayPtr = arrayPtr->value.linkPtr;
- }
-
- /*
- * If the variable is in a hashtable and its hPtr field is NULL, then we
- * may have an upvar to an array element where the array was deleted
- * or an upvar to a namespace variable whose namespace was deleted.
- * Generate an error (allowing the variable to be reset would screw up
- * our storage allocation and is meaningless anyway).
- */
-
- if ((arrayPtr->flags & VAR_IN_HASHTABLE) && (arrayPtr->hPtr == NULL)) {
- if (leaveErrorMsg) {
- if (TclIsVarArrayElement(arrayPtr)) {
- VarErrMsg(interp, arrayName, elem, "set", danglingElement);
- } else {
- VarErrMsg(interp, arrayName, elem, "set", danglingVar);
- }
- }
- goto errorReturn;
- }
-
- /*
- * Make sure we're dealing with an array.
- */
-
- if (TclIsVarUndefined(arrayPtr) && !TclIsVarArrayElement(arrayPtr)) {
- TclSetVarArray(arrayPtr);
- arrayPtr->value.tablePtr =
- (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
- Tcl_InitHashTable(arrayPtr->value.tablePtr, TCL_STRING_KEYS);
- TclClearVarUndefined(arrayPtr);
- } else if (!TclIsVarArray(arrayPtr)) {
- if (leaveErrorMsg) {
- VarErrMsg(interp, arrayName, elem, "set", needArray);
- }
- goto errorReturn;
- }
-
- /*
- * Look up the element.
- */
-
- hPtr = Tcl_CreateHashEntry(arrayPtr->value.tablePtr, elem, &new);
- if (new) {
- if (arrayPtr->searchPtr != NULL) {
- DeleteSearches(arrayPtr);
- }
- varPtr = NewVar();
- Tcl_SetHashValue(hPtr, varPtr);
- varPtr->hPtr = hPtr;
- varPtr->nsPtr = varFramePtr->nsPtr;
- TclSetVarArrayElement(varPtr);
- }
- varPtr = (Var *) Tcl_GetHashValue(hPtr);
-
- /*
- * It's an error to try to set an array variable itself.
- */
-
- if (TclIsVarArray(varPtr)) {
- if (leaveErrorMsg) {
- VarErrMsg(interp, arrayName, elem, "set", isArray);
- }
- goto errorReturn;
- }
-
- /*
- * Set the variable's new value and discard the old one. We don't
- * append with this "set" procedure so the old value isn't needed.
- */
-
- oldValuePtr = varPtr->value.objPtr;
- if (newValuePtr != oldValuePtr) { /* set new value */
- varPtr->value.objPtr = newValuePtr;
- Tcl_IncrRefCount(newValuePtr); /* var is another ref to obj */
- if (oldValuePtr != NULL) {
- TclDecrRefCount(oldValuePtr); /* discard old value */
- }
- }
- TclSetVarScalar(varPtr);
- TclClearVarUndefined(varPtr);
-
- /*
- * Invoke any write traces for the element variable.
- */
-
- if ((varPtr->tracePtr != NULL)
- || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
- char *msg = CallTraces(iPtr, arrayPtr, varPtr, arrayName, elem,
- TCL_TRACE_WRITES);
- if (msg != NULL) {
- if (leaveErrorMsg) {
- VarErrMsg(interp, arrayName, elem, "set", msg);
- }
- goto errorReturn;
- }
- }
-
- /*
- * Return the element's value unless it was changed in some gross way by
- * a trace (e.g. it was unset and then recreated as an array). If it was
- * changed is a gross way, just return an empty string object.
- */
-
- if (TclIsVarScalar(varPtr) && !TclIsVarUndefined(varPtr)) {
- return varPtr->value.objPtr;
- }
-
- resultPtr = Tcl_NewObj();
-
- /*
- * An error. If the variable doesn't exist anymore and no-one's using
- * it, then free up the relevant structures and hash table entries.
- */
-
- errorReturn:
- if (varPtr != NULL) {
- if (TclIsVarUndefined(varPtr)) {
- CleanupVar(varPtr, NULL); /* note: array isn't in hashtable */
- }
- }
- return resultPtr;
-}
-
-/*
- *----------------------------------------------------------------------
- *
* TclIncrVar2 --
*
* Given a two-part variable name, which may refer either to a scalar
@@ -1815,96 +1733,75 @@ TclIncrVar2(interp, part1Ptr, part2Ptr, incrAmount, flags)
* TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
* TCL_LIST_ELEMENT, TCL_LEAVE_ERR_MSG. */
{
- register Tcl_Obj *varValuePtr;
- Tcl_Obj *resultPtr;
- int createdNewObj; /* Set 1 if var's value object is shared
- * so we must increment a copy (i.e. copy
- * on write). */
- long i;
- int result;
+ Var *varPtr, *arrayPtr;
+ char *part1, *part2;
- varValuePtr = Tcl_ObjGetVar2(interp, part1Ptr, part2Ptr, flags);
- if (varValuePtr == NULL) {
+ part1 = TclGetString(part1Ptr);
+ part2 = ((part2Ptr == NULL)? NULL : TclGetString(part2Ptr));
+
+ varPtr = TclObjLookupVar(interp, part1Ptr, part2, flags, "read",
+ 0, 1, &arrayPtr);
+ if (varPtr == NULL) {
Tcl_AddObjErrorInfo(interp,
"\n (reading value of variable to increment)", -1);
return NULL;
}
-
- /*
- * Increment the variable's value. If the object is unshared we can
- * modify it directly, otherwise we must create a new copy to modify:
- * this is "copy on write". Then free the variable's old string
- * representation, if any, since it will no longer be valid.
- */
-
- createdNewObj = 0;
- if (Tcl_IsShared(varValuePtr)) {
- varValuePtr = Tcl_DuplicateObj(varValuePtr);
- createdNewObj = 1;
- }
- result = Tcl_GetLongFromObj(interp, varValuePtr, &i);
- if (result != TCL_OK) {
- if (createdNewObj) {
- Tcl_DecrRefCount(varValuePtr); /* free unneeded copy */
- }
- return NULL;
- }
- Tcl_SetLongObj(varValuePtr, (i + incrAmount));
-
- /*
- * Store the variable's new value and run any write traces.
- */
-
- resultPtr = Tcl_ObjSetVar2(interp, part1Ptr, part2Ptr, varValuePtr, flags);
- if (resultPtr == NULL) {
- return NULL;
- }
- return resultPtr;
+ return TclPtrIncrVar(interp, varPtr, arrayPtr, part1, part2,
+ incrAmount, flags);
}
/*
*----------------------------------------------------------------------
*
- * TclIncrIndexedScalar --
+ * TclPtrIncrVar --
*
- * Increments the Tcl object value of a local scalar variable in the
- * active procedure, given its compile-time allocated index in the
- * procedure's array of local variables.
+ * Given the pointers to a variable and possible containing array,
+ * increment the Tcl object value of the variable by a specified
+ * amount.
*
* Results:
* Returns a pointer to the Tcl_Obj holding the new value of the
- * variable given by localIndex. If the specified variable doesn't
- * exist, or there is a clash in array usage, or an error occurs while
- * executing variable traces, then NULL is returned and a message will
- * be left in the interpreter's result.
+ * variable. If the specified variable doesn't exist, or there is a
+ * clash in array usage, or an error occurs while executing variable
+ * traces, then NULL is returned and a message will be left in
+ * the interpreter's result.
*
* Side effects:
* The value of the given variable is incremented by the specified
- * amount. The ref count for the returned object is _not_ incremented
- * to reflect the returned reference; if you want to keep a reference
- * to the object you must increment its ref count yourself.
+ * amount. If either the array or the entry didn't exist then a new
+ * variable is created. The ref count for the returned object is _not_
+ * incremented to reflect the returned reference; if you want to keep a
+ * reference to the object you must increment its ref count yourself.
*
*----------------------------------------------------------------------
*/
Tcl_Obj *
-TclIncrIndexedScalar(interp, localIndex, incrAmount)
+TclPtrIncrVar(interp, varPtr, arrayPtr, part1, part2, incrAmount, flags)
Tcl_Interp *interp; /* Command interpreter in which variable is
* to be found. */
- int localIndex; /* Index of variable in procedure's array
- * of local variables. */
- long incrAmount; /* Amount to be added to variable. */
+ Var *varPtr;
+ Var *arrayPtr;
+ CONST char *part1; /* Points to an object holding the name of
+ * an array (if part2 is non-NULL) or the
+ * name of a variable. */
+ CONST char *part2; /* If non-null, points to an object holding
+ * the name of an element in the array
+ * part1Ptr. */
+ CONST long incrAmount; /* Amount to be added to variable. */
+ CONST int flags; /* Various flags that tell how to incr value:
+ * any of TCL_GLOBAL_ONLY,
+ * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
+ * TCL_LIST_ELEMENT, TCL_LEAVE_ERR_MSG. */
{
register Tcl_Obj *varValuePtr;
- Tcl_Obj *resultPtr;
int createdNewObj; /* Set 1 if var's value object is shared
* so we must increment a copy (i.e. copy
* on write). */
long i;
- int result;
- varValuePtr = TclGetIndexedScalar(interp, localIndex,
- /*leaveErrorMsg*/ 1);
+ varValuePtr = TclPtrGetVar(interp, varPtr, arrayPtr, part1, part2, flags);
+
if (varValuePtr == NULL) {
Tcl_AddObjErrorInfo(interp,
"\n (reading value of variable to increment)", -1);
@@ -1912,125 +1809,58 @@ TclIncrIndexedScalar(interp, localIndex, incrAmount)
}
/*
- * Reach into the object's representation to extract and increment the
- * variable's value. If the object is unshared we can modify it
- * directly, otherwise we must create a new copy to modify: this is
- * "copy on write". Then free the variable's old string representation,
- * if any, since it will no longer be valid.
+ * Increment the variable's value. If the object is unshared we can
+ * modify it directly, otherwise we must create a new copy to modify:
+ * this is "copy on write". Then free the variable's old string
+ * representation, if any, since it will no longer be valid.
*/
createdNewObj = 0;
if (Tcl_IsShared(varValuePtr)) {
- createdNewObj = 1;
varValuePtr = Tcl_DuplicateObj(varValuePtr);
+ createdNewObj = 1;
}
- result = Tcl_GetLongFromObj(interp, varValuePtr, &i);
- if (result != TCL_OK) {
+#ifdef TCL_WIDE_INT_IS_LONG
+ if (Tcl_GetLongFromObj(interp, varValuePtr, &i) != TCL_OK) {
if (createdNewObj) {
Tcl_DecrRefCount(varValuePtr); /* free unneeded copy */
}
return NULL;
}
Tcl_SetLongObj(varValuePtr, (i + incrAmount));
-
- /*
- * Store the variable's new value and run any write traces.
- */
-
- resultPtr = TclSetIndexedScalar(interp, localIndex, varValuePtr,
- /*leaveErrorMsg*/ 1);
- if (resultPtr == NULL) {
- return NULL;
- }
- return resultPtr;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclIncrElementOfIndexedArray --
- *
- * Increments the Tcl object value of an element in a local array
- * variable. The element is named by the object elemPtr while the array
- * is specified by its index in the active procedure's array of
- * compiler allocated local variables.
- *
- * Results:
- * Returns a pointer to the Tcl_Obj holding the new value of the
- * element. If the specified array or element doesn't exist, or there
- * is a clash in array usage, or an error occurs while executing
- * variable traces, then NULL is returned and a message will be left in
- * the interpreter's result.
- *
- * Side effects:
- * The value of the given array element is incremented by the specified
- * amount. The ref count for the returned object is _not_ incremented
- * to reflect the returned reference; if you want to keep a reference
- * to the object you must increment its ref count yourself. If the
- * entry doesn't exist then a new variable is created.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Obj *
-TclIncrElementOfIndexedArray(interp, localIndex, elemPtr, incrAmount)
- Tcl_Interp *interp; /* Command interpreter in which the array is
- * to be found. */
- int localIndex; /* Index of array variable in procedure's
- * array of local variables. */
- Tcl_Obj *elemPtr; /* Points to an object holding the name of
- * an element to increment in the array. */
- long incrAmount; /* Amount to be added to variable. */
-{
- register Tcl_Obj *varValuePtr;
- Tcl_Obj *resultPtr;
- int createdNewObj; /* Set 1 if var's value object is shared
- * so we must increment a copy (i.e. copy
- * on write). */
- long i;
- int result;
-
- varValuePtr = TclGetElementOfIndexedArray(interp, localIndex, elemPtr,
- /*leaveErrorMsg*/ 1);
- if (varValuePtr == NULL) {
- Tcl_AddObjErrorInfo(interp,
- "\n (reading value of variable to increment)", -1);
- return NULL;
- }
-
- /*
- * Reach into the object's representation to extract and increment the
- * variable's value. If the object is unshared we can modify it
- * directly, otherwise we must create a new copy to modify: this is
- * "copy on write". Then free the variable's old string representation,
- * if any, since it will no longer be valid.
- */
-
- createdNewObj = 0;
- if (Tcl_IsShared(varValuePtr)) {
- createdNewObj = 1;
- varValuePtr = Tcl_DuplicateObj(varValuePtr);
- }
- result = Tcl_GetLongFromObj(interp, varValuePtr, &i);
- if (result != TCL_OK) {
- if (createdNewObj) {
- Tcl_DecrRefCount(varValuePtr); /* free unneeded copy */
+#else
+ if (varValuePtr->typePtr == &tclWideIntType) {
+ Tcl_WideInt wide = varValuePtr->internalRep.wideValue;
+ Tcl_SetWideIntObj(varValuePtr, wide + Tcl_LongAsWide(incrAmount));
+ } else if (varValuePtr->typePtr == &tclIntType) {
+ i = varValuePtr->internalRep.longValue;
+ Tcl_SetIntObj(varValuePtr, i + incrAmount);
+ } else {
+ /*
+ * Not an integer or wide internal-rep...
+ */
+ Tcl_WideInt wide;
+ if (Tcl_GetWideIntFromObj(interp, varValuePtr, &wide) != TCL_OK) {
+ if (createdNewObj) {
+ Tcl_DecrRefCount(varValuePtr); /* free unneeded copy */
+ }
+ return NULL;
+ }
+ if (wide <= Tcl_LongAsWide(LONG_MAX)
+ && wide >= Tcl_LongAsWide(LONG_MIN)) {
+ Tcl_SetLongObj(varValuePtr, Tcl_WideAsLong(wide) + incrAmount);
+ } else {
+ Tcl_SetWideIntObj(varValuePtr, wide + Tcl_LongAsWide(incrAmount));
}
- return NULL;
}
- Tcl_SetLongObj(varValuePtr, (i + incrAmount));
-
+#endif
+
/*
* Store the variable's new value and run any write traces.
*/
- resultPtr = TclSetElementOfIndexedArray(interp, localIndex, elemPtr,
- varValuePtr,
- /*leaveErrorMsg*/ 1);
- if (resultPtr == NULL) {
- return NULL;
- }
- return resultPtr;
+ return TclPtrSetVar(interp, varPtr, arrayPtr, part1, part2,
+ varValuePtr, flags);
}
/*
@@ -2057,7 +1887,7 @@ int
Tcl_UnsetVar(interp, varName, flags)
Tcl_Interp *interp; /* Command interpreter in which varName is
* to be looked up. */
- char *varName; /* Name of a variable in interp. May be
+ CONST char *varName; /* Name of a variable in interp. May be
* either a scalar name or an array name
* or an element in an array. */
int flags; /* OR-ed combination of any of
@@ -2092,8 +1922,51 @@ int
Tcl_UnsetVar2(interp, part1, part2, flags)
Tcl_Interp *interp; /* Command interpreter in which varName is
* to be looked up. */
- char *part1; /* Name of variable or array. */
- char *part2; /* Name of element within array or NULL. */
+ CONST char *part1; /* Name of variable or array. */
+ CONST char *part2; /* Name of element within array or NULL. */
+ int flags; /* OR-ed combination of any of
+ * TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
+ * TCL_LEAVE_ERR_MSG. */
+{
+ int result;
+ Tcl_Obj *part1Ptr;
+
+ part1Ptr = Tcl_NewStringObj(part1, -1);
+ Tcl_IncrRefCount(part1Ptr);
+ result = TclObjUnsetVar2(interp, part1Ptr, part2, flags);
+ TclDecrRefCount(part1Ptr);
+
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclObjUnsetVar2 --
+ *
+ * Delete a variable, given a 2-object name.
+ *
+ * Results:
+ * Returns TCL_OK if the variable was successfully deleted, TCL_ERROR
+ * if the variable can't be unset. In the event of an error,
+ * if the TCL_LEAVE_ERR_MSG flag is set then an error message
+ * is left in the interp's result.
+ *
+ * Side effects:
+ * If part1ptr and part2Ptr indicate a local or global variable in interp,
+ * it is deleted. If part1Ptr is an array name and part2Ptr is NULL, then
+ * the whole array is deleted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclObjUnsetVar2(interp, part1Ptr, part2, flags)
+ Tcl_Interp *interp; /* Command interpreter in which varName is
+ * to be looked up. */
+ Tcl_Obj *part1Ptr; /* Name of variable or array. */
+ CONST char *part2; /* Name of element within array or NULL. */
int flags; /* OR-ed combination of any of
* TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
* TCL_LEAVE_ERR_MSG. */
@@ -2105,12 +1978,15 @@ Tcl_UnsetVar2(interp, part1, part2, flags)
ActiveVarTrace *activePtr;
Tcl_Obj *objPtr;
int result;
+ char *part1;
- varPtr = TclLookupVar(interp, part1, part2, flags, "unset",
+ part1 = TclGetString(part1Ptr);
+ varPtr = TclObjLookupVar(interp, part1Ptr, part2, flags, "unset",
/*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
if (varPtr == NULL) {
return TCL_ERROR;
}
+
result = (TclIsVarUndefined(varPtr)? TCL_ERROR : TCL_OK);
if ((arrayPtr != NULL) && (arrayPtr->searchPtr != NULL)) {
@@ -2141,7 +2017,7 @@ Tcl_UnsetVar2(interp, part1, part2, flags)
* Call trace procedures for the variable being deleted. Then delete
* its traces. Be sure to abort any other traces for the variable
* that are still pending. Special tricks:
- * 1. We need to increment varPtr's refCount around this: CallTraces
+ * 1. We need to increment varPtr's refCount around this: CallVarTraces
* will use dummyVar so it won't increment varPtr's refCount itself.
* 2. Turn off the VAR_TRACE_ACTIVE flag in dummyVar: we want to
* call unset traces even if other traces are pending.
@@ -2151,14 +2027,15 @@ Tcl_UnsetVar2(interp, part1, part2, flags)
|| ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
varPtr->refCount++;
dummyVar.flags &= ~VAR_TRACE_ACTIVE;
- (void) CallTraces(iPtr, arrayPtr, &dummyVar, part1, part2,
- (flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY)) | TCL_TRACE_UNSETS);
+ CallVarTraces(iPtr, arrayPtr, &dummyVar, part1, part2,
+ (flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY))
+ | TCL_TRACE_UNSETS, /* leaveErrMsg */ 0);
while (dummyVar.tracePtr != NULL) {
VarTrace *tracePtr = dummyVar.tracePtr;
dummyVar.tracePtr = tracePtr->nextPtr;
- ckfree((char *) tracePtr);
+ Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
}
- for (activePtr = iPtr->activeTracePtr; activePtr != NULL;
+ for (activePtr = iPtr->activeVarTracePtr; activePtr != NULL;
activePtr = activePtr->nextPtr) {
if (activePtr->varPtr == varPtr) {
activePtr->nextTracePtr = NULL;
@@ -2190,7 +2067,8 @@ Tcl_UnsetVar2(interp, part1, part2, flags)
*/
varPtr->refCount++;
DeleteArray(iPtr, part1, dummyVarPtr,
- (flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY)) | TCL_TRACE_UNSETS);
+ (flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY))
+ | TCL_TRACE_UNSETS);
/* Decr ref count */
varPtr->refCount--;
}
@@ -2256,7 +2134,7 @@ int
Tcl_TraceVar(interp, varName, flags, proc, clientData)
Tcl_Interp *interp; /* Interpreter in which variable is
* to be traced. */
- char *varName; /* Name of variable; may end with "(index)"
+ CONST char *varName; /* Name of variable; may end with "(index)"
* to signify an array reference. */
int flags; /* OR-ed collection of bits, including any
* of TCL_TRACE_READS, TCL_TRACE_WRITES,
@@ -2295,8 +2173,8 @@ int
Tcl_TraceVar2(interp, part1, part2, flags, proc, clientData)
Tcl_Interp *interp; /* Interpreter in which variable is
* to be traced. */
- char *part1; /* Name of scalar variable or array. */
- char *part2; /* Name of element within array; NULL means
+ CONST char *part1; /* Name of scalar variable or array. */
+ CONST char *part2; /* Name of element within array; NULL means
* trace applies to scalar variable or array
* as-a-whole. */
int flags; /* OR-ed collection of bits, including any
@@ -2309,25 +2187,46 @@ Tcl_TraceVar2(interp, part1, part2, flags, proc, clientData)
{
Var *varPtr, *arrayPtr;
register VarTrace *tracePtr;
-
- varPtr = TclLookupVar(interp, part1, part2, (flags | TCL_LEAVE_ERR_MSG),
+ int flagMask;
+
+ /*
+ * We strip 'flags' down to just the parts which are relevant to
+ * TclLookupVar, to avoid conflicts between trace flags and
+ * internal namespace flags such as 'FIND_ONLY_NS'. This can
+ * now occur since we have trace flags with values 0x1000 and higher.
+ */
+ flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
+ varPtr = TclLookupVar(interp, part1, part2,
+ (flags & flagMask) | TCL_LEAVE_ERR_MSG,
"trace", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
if (varPtr == NULL) {
return TCL_ERROR;
}
/*
+ * Check for a nonsense flag combination. Note that this is a
+ * panic() because there should be no code path that ever sets
+ * both flags.
+ */
+ if ((flags&TCL_TRACE_RESULT_DYNAMIC) && (flags&TCL_TRACE_RESULT_OBJECT)) {
+ panic("bad result flag combination");
+ }
+
+ /*
* Set up trace information.
*/
+ flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
+ TCL_TRACE_ARRAY | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
+#ifndef TCL_REMOVE_OBSOLETE_TRACES
+ flagMask |= TCL_TRACE_OLD_STYLE;
+#endif
tracePtr = (VarTrace *) ckalloc(sizeof(VarTrace));
- tracePtr->traceProc = proc;
- tracePtr->clientData = clientData;
- tracePtr->flags =
- flags & (TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
- TCL_TRACE_ARRAY);
- tracePtr->nextPtr = varPtr->tracePtr;
- varPtr->tracePtr = tracePtr;
+ tracePtr->traceProc = proc;
+ tracePtr->clientData = clientData;
+ tracePtr->flags = flags & flagMask;
+ tracePtr->nextPtr = varPtr->tracePtr;
+ varPtr->tracePtr = tracePtr;
return TCL_OK;
}
@@ -2352,7 +2251,7 @@ Tcl_TraceVar2(interp, part1, part2, flags, proc, clientData)
void
Tcl_UntraceVar(interp, varName, flags, proc, clientData)
Tcl_Interp *interp; /* Interpreter containing variable. */
- char *varName; /* Name of variable; may end with "(index)"
+ CONST char *varName; /* Name of variable; may end with "(index)"
* to signify an array reference. */
int flags; /* OR-ed collection of bits describing
* current trace, including any of
@@ -2386,8 +2285,8 @@ Tcl_UntraceVar(interp, varName, flags, proc, clientData)
void
Tcl_UntraceVar2(interp, part1, part2, flags, proc, clientData)
Tcl_Interp *interp; /* Interpreter containing variable. */
- char *part1; /* Name of variable or array. */
- char *part2; /* Name of element within array; NULL means
+ CONST char *part1; /* Name of variable or array. */
+ CONST char *part2; /* Name of element within array; NULL means
* trace applies to scalar variable or array
* as-a-whole. */
int flags; /* OR-ed collection of bits describing
@@ -2403,17 +2302,31 @@ Tcl_UntraceVar2(interp, part1, part2, flags, proc, clientData)
Var *varPtr, *arrayPtr;
Interp *iPtr = (Interp *) interp;
ActiveVarTrace *activePtr;
-
- varPtr = TclLookupVar(interp, part1, part2,
- flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY),
+ int flagMask;
+
+ /*
+ * Set up a mask to mask out the parts of the flags that we are not
+ * interested in now.
+ */
+ flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
+ varPtr = TclLookupVar(interp, part1, part2, flags & flagMask,
/*msg*/ (char *) NULL,
/*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
if (varPtr == NULL) {
return;
}
- flags &= (TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
- TCL_TRACE_ARRAY);
+
+ /*
+ * Set up a mask to mask out the parts of the flags that we are not
+ * interested in now.
+ */
+ flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
+ TCL_TRACE_ARRAY | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
+#ifndef TCL_REMOVE_OBSOLETE_TRACES
+ flagMask |= TCL_TRACE_OLD_STYLE;
+#endif
+ flags &= flagMask;
for (tracePtr = varPtr->tracePtr, prevPtr = NULL; ;
prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
if (tracePtr == NULL) {
@@ -2428,10 +2341,10 @@ Tcl_UntraceVar2(interp, part1, part2, flags, proc, clientData)
/*
* The code below makes it possible to delete traces while traces
* are active: it makes sure that the deleted trace won't be
- * processed by CallTraces.
+ * processed by CallVarTraces.
*/
- for (activePtr = iPtr->activeTracePtr; activePtr != NULL;
+ for (activePtr = iPtr->activeVarTracePtr; activePtr != NULL;
activePtr = activePtr->nextPtr) {
if (activePtr->nextTracePtr == tracePtr) {
activePtr->nextTracePtr = tracePtr->nextPtr;
@@ -2442,7 +2355,7 @@ Tcl_UntraceVar2(interp, part1, part2, flags, proc, clientData)
} else {
prevPtr->nextPtr = tracePtr->nextPtr;
}
- ckfree((char *) tracePtr);
+ Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
/*
* If this is the last trace on the variable, and the variable is
@@ -2483,7 +2396,7 @@ Tcl_UntraceVar2(interp, part1, part2, flags, proc, clientData)
ClientData
Tcl_VarTraceInfo(interp, varName, flags, proc, prevClientData)
Tcl_Interp *interp; /* Interpreter containing variable. */
- char *varName; /* Name of variable; may end with "(index)"
+ CONST char *varName; /* Name of variable; may end with "(index)"
* to signify an array reference. */
int flags; /* OR-ed combo or TCL_GLOBAL_ONLY,
* TCL_NAMESPACE_ONLY (can be 0). */
@@ -2518,8 +2431,8 @@ Tcl_VarTraceInfo(interp, varName, flags, proc, prevClientData)
ClientData
Tcl_VarTraceInfo2(interp, part1, part2, flags, proc, prevClientData)
Tcl_Interp *interp; /* Interpreter containing variable. */
- char *part1; /* Name of variable or array. */
- char *part2; /* Name of element within array; NULL means
+ CONST char *part1; /* Name of variable or array. */
+ CONST char *part2; /* Name of element within array; NULL means
* trace applies to scalar variable or array
* as-a-whole. */
int flags; /* OR-ed combination of TCL_GLOBAL_ONLY,
@@ -2589,18 +2502,45 @@ Tcl_UnsetObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- register int i;
+ register int i, flags = TCL_LEAVE_ERR_MSG;
register char *name;
- if (objc < 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "varName ?varName ...?");
+ if (objc < 1) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "?-nocomplain? ?--? ?varName varName ...?");
return TCL_ERROR;
+ } else if (objc == 1) {
+ /*
+ * Do nothing if no arguments supplied, so as to match
+ * command documentation.
+ */
+ return TCL_OK;
}
-
- for (i = 1; i < objc; i++) {
- name = TclGetString(objv[i]);
- if (Tcl_UnsetVar2(interp, name, (char *) NULL,
- TCL_LEAVE_ERR_MSG) != TCL_OK) {
+
+ /*
+ * Simple, restrictive argument parsing. The only options are --
+ * and -nocomplain (which must come first and be given exactly to
+ * be an option).
+ */
+ i = 1;
+ name = TclGetString(objv[i]);
+ if (name[0] == '-') {
+ if (strcmp("-nocomplain", name) == 0) {
+ i++;
+ if (i == objc) {
+ return TCL_OK;
+ }
+ flags = 0;
+ name = TclGetString(objv[i]);
+ }
+ if (strcmp("--", name) == 0) {
+ i++;
+ }
+ }
+
+ for (; i < objc; i++) {
+ if ((TclObjUnsetVar2(interp, objv[i], NULL, flags) != TCL_OK)
+ && (flags == TCL_LEAVE_ERR_MSG)) {
return TCL_ERROR;
}
}
@@ -2632,6 +2572,9 @@ Tcl_AppendObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
+ Var *varPtr, *arrayPtr;
+ char *part1;
+
register Tcl_Obj *varValuePtr = NULL;
/* Initialized to avoid compiler
* warning. */
@@ -2641,15 +2584,29 @@ Tcl_AppendObjCmd(dummy, interp, objc, objv)
Tcl_WrongNumArgs(interp, 1, objv, "varName ?value value ...?");
return TCL_ERROR;
}
+
if (objc == 2) {
varValuePtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
if (varValuePtr == NULL) {
return TCL_ERROR;
}
} else {
- for (i = 2; i < objc; i++) {
- varValuePtr = Tcl_ObjSetVar2(interp, objv[1], (Tcl_Obj *) NULL,
- objv[i], (TCL_APPEND_VALUE | TCL_LEAVE_ERR_MSG));
+ varPtr = TclObjLookupVar(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG,
+ "set", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
+ part1 = TclGetString(objv[1]);
+ if (varPtr == NULL) {
+ return TCL_ERROR;
+ }
+ for (i = 2; i < objc; i++) {
+ /*
+ * Note that we do not need to increase the refCount of
+ * the Var pointers: should a trace delete the variable,
+ * the return value of TclPtrSetVar will be NULL, and we
+ * will not access the variable again.
+ */
+
+ varValuePtr = TclPtrSetVar(interp, varPtr, arrayPtr, part1, NULL,
+ objv[i], (TCL_APPEND_VALUE | TCL_LEAVE_ERR_MSG));
if (varValuePtr == NULL) {
return TCL_ERROR;
}
@@ -2688,25 +2645,26 @@ Tcl_LappendObjCmd(dummy, interp, objc, objv)
register List *listRepPtr;
register Tcl_Obj **elemPtrs;
int numElems, numRequired, createdNewObj, createVar, i, j;
+ Var *varPtr, *arrayPtr;
+ char *part1;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "varName ?value value ...?");
return TCL_ERROR;
}
if (objc == 2) {
- newValuePtr = Tcl_ObjGetVar2(interp, objv[1], (Tcl_Obj *) NULL,
- (TCL_LEAVE_ERR_MSG));
+ newValuePtr = Tcl_ObjGetVar2(interp, objv[1], (Tcl_Obj *) NULL, 0);
if (newValuePtr == NULL) {
/*
* The variable doesn't exist yet. Just create it with an empty
* initial value.
*/
- Tcl_Obj *nullObjPtr = Tcl_NewObj();
- newValuePtr = Tcl_ObjSetVar2(interp, objv[1], NULL,
- nullObjPtr, TCL_LEAVE_ERR_MSG);
+ varValuePtr = Tcl_NewObj();
+ newValuePtr = Tcl_ObjSetVar2(interp, objv[1], NULL, varValuePtr,
+ TCL_LEAVE_ERR_MSG);
if (newValuePtr == NULL) {
- Tcl_DecrRefCount(nullObjPtr); /* free unneeded object */
+ Tcl_DecrRefCount(varValuePtr); /* free unneeded object */
return TCL_ERROR;
}
}
@@ -2723,27 +2681,41 @@ Tcl_LappendObjCmd(dummy, interp, objc, objv)
createdNewObj = 0;
createVar = 1;
- varValuePtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
+
+ /*
+ * Use the TCL_TRACE_READS flag to ensure that if we have an
+ * array with no elements set yet, but with a read trace on it,
+ * we will create the variable and get read traces triggered.
+ * Note that you have to protect the variable pointers around
+ * the TclPtrGetVar call to insure that they remain valid
+ * even if the variable was undefined and unused.
+ */
+
+ varPtr = TclObjLookupVar(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG,
+ "set", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
+ if (varPtr == NULL) {
+ return TCL_ERROR;
+ }
+ varPtr->refCount++;
+ if (arrayPtr != NULL) {
+ arrayPtr->refCount++;
+ }
+ part1 = TclGetString(objv[1]);
+ varValuePtr = TclPtrGetVar(interp, varPtr, arrayPtr, part1, NULL,
+ (TCL_TRACE_READS | TCL_LEAVE_ERR_MSG));
+ varPtr->refCount--;
+ if (arrayPtr != NULL) {
+ arrayPtr->refCount--;
+ }
+
if (varValuePtr == NULL) {
/*
* We couldn't read the old value: either the var doesn't yet
- * exist or it's an array element. If it's new, we will try to
+ * exist or it's an array element. If it's new, we will try to
* create it with Tcl_ObjSetVar2 below.
*/
- char *p, *varName;
- int nameBytes, i;
-
- varName = Tcl_GetStringFromObj(objv[1], &nameBytes);
- for (i = 0, p = varName; i < nameBytes; i++, p++) {
- if (*p == '(') {
- p = (varName + nameBytes-1);
- if (*p == ')') { /* last char is ')' => array ref */
- createVar = 0;
- }
- break;
- }
- }
+ createVar = (TclIsVarUndefined(varPtr));
varValuePtr = Tcl_NewObj();
createdNewObj = 1;
} else if (Tcl_IsShared(varValuePtr)) {
@@ -2764,7 +2736,7 @@ Tcl_LappendObjCmd(dummy, interp, objc, objv)
return result;
}
}
- listRepPtr = (List *) varValuePtr->internalRep.otherValuePtr;
+ listRepPtr = (List *) varValuePtr->internalRep.twoPtrValue.ptr1;
elemPtrs = listRepPtr->elements;
numElems = listRepPtr->elemCount;
@@ -2810,8 +2782,8 @@ Tcl_LappendObjCmd(dummy, interp, objc, objv)
* was new and we didn't create the variable.
*/
- newValuePtr = Tcl_ObjSetVar2(interp, objv[1], NULL, varValuePtr,
- TCL_LEAVE_ERR_MSG);
+ newValuePtr = TclPtrSetVar(interp, varPtr, arrayPtr, part1, NULL,
+ varValuePtr, TCL_LEAVE_ERR_MSG);
if (newValuePtr == NULL) {
if (createdNewObj && !createVar) {
Tcl_DecrRefCount(varValuePtr); /* free unneeded obj */
@@ -2861,18 +2833,18 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
enum {ARRAY_ANYMORE, ARRAY_DONESEARCH, ARRAY_EXISTS, ARRAY_GET,
ARRAY_NAMES, ARRAY_NEXTELEMENT, ARRAY_SET, ARRAY_SIZE,
- ARRAY_STARTSEARCH, ARRAY_UNSET};
- static char *arrayOptions[] = {
+ ARRAY_STARTSEARCH, ARRAY_STATISTICS, ARRAY_UNSET};
+ static CONST char *arrayOptions[] = {
"anymore", "donesearch", "exists", "get", "names", "nextelement",
- "set", "size", "startsearch", "unset", (char *) NULL
+ "set", "size", "startsearch", "statistics", "unset", (char *) NULL
};
Interp *iPtr = (Interp *) interp;
Var *varPtr, *arrayPtr;
Tcl_HashEntry *hPtr;
- Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
+ Tcl_Obj *resultPtr, *varNamePtr;
int notArray;
- char *varName, *msg;
+ char *varName;
int index, result;
@@ -2887,38 +2859,50 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
}
/*
- * Locate the array variable (and it better be an array).
+ * Locate the array variable
*/
- varName = TclGetString(objv[2]);
- varPtr = TclLookupVar(interp, varName, (char *) NULL, /*flags*/ 0,
+ varNamePtr = objv[2];
+ varName = TclGetString(varNamePtr);
+ varPtr = TclObjLookupVar(interp, varNamePtr, NULL, /*flags*/ 0,
/*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
- notArray = 0;
- if ((varPtr == NULL) || !TclIsVarArray(varPtr)
- || TclIsVarUndefined(varPtr)) {
- notArray = 1;
- }
-
/*
* Special array trace used to keep the env array in sync for
* array names, array get, etc.
*/
- if (varPtr != NULL && varPtr->tracePtr != NULL) {
- msg = CallTraces(iPtr, arrayPtr, varPtr, varName, NULL,
+ if (varPtr != NULL && varPtr->tracePtr != NULL
+ && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) {
+ if (TCL_ERROR == CallVarTraces(iPtr, arrayPtr, varPtr, varName, NULL,
(TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY|
- TCL_TRACE_ARRAY));
- if (msg != NULL) {
- VarErrMsg(interp, varName, NULL, "trace array", msg);
+ TCL_TRACE_ARRAY), /* leaveErrMsg */ 1)) {
return TCL_ERROR;
}
}
+ /*
+ * Verify that it is indeed an array variable. This test comes after
+ * the traces - the variable may actually become an array as an effect
+ * of said traces.
+ */
+
+ notArray = 0;
+ if ((varPtr == NULL) || !TclIsVarArray(varPtr)
+ || TclIsVarUndefined(varPtr)) {
+ notArray = 1;
+ }
+
+ /*
+ * We have to wait to get the resultPtr until here because
+ * CallVarTraces can affect the result.
+ */
+
+ resultPtr = Tcl_GetObjResult(interp);
+
switch (index) {
case ARRAY_ANYMORE: {
ArraySearch *searchPtr;
- char *searchId;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv,
@@ -2928,8 +2912,7 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
if (notArray) {
goto error;
}
- searchId = Tcl_GetString(objv[3]);
- searchPtr = ParseSearchId(interp, varPtr, varName, searchId);
+ searchPtr = ParseSearchId(interp, varPtr, varName, objv[3]);
if (searchPtr == NULL) {
return TCL_ERROR;
}
@@ -2953,7 +2936,6 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
}
case ARRAY_DONESEARCH: {
ArraySearch *searchPtr, *prevPtr;
- char *searchId;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv,
@@ -2963,8 +2945,7 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
if (notArray) {
goto error;
}
- searchId = Tcl_GetString(objv[3]);
- searchPtr = ParseSearchId(interp, varPtr, varName, searchId);
+ searchPtr = ParseSearchId(interp, varPtr, varName, objv[3]);
if (searchPtr == NULL) {
return TCL_ERROR;
}
@@ -2995,7 +2976,8 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
Var *varPtr2;
char *pattern = NULL;
char *name;
- Tcl_Obj *namePtr, *valuePtr;
+ Tcl_Obj *namePtr, *valuePtr, *nameLstPtr, *tmpResPtr, **namePtrPtr;
+ int i, count;
if ((objc != 3) && (objc != 4)) {
Tcl_WrongNumArgs(interp, 2, objv, "arrayName ?pattern?");
@@ -3007,6 +2989,14 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
if (objc == 4) {
pattern = TclGetString(objv[3]);
}
+
+ /*
+ * Store the array names in a new object.
+ */
+
+ nameLstPtr = Tcl_NewObj();
+ Tcl_IncrRefCount(nameLstPtr);
+
for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr, &search);
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
varPtr2 = (Var *) Tcl_GetHashValue(hPtr);
@@ -3019,27 +3009,75 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
}
namePtr = Tcl_NewStringObj(name, -1);
- result = Tcl_ListObjAppendElement(interp, resultPtr,
+ result = Tcl_ListObjAppendElement(interp, nameLstPtr,
namePtr);
if (result != TCL_OK) {
Tcl_DecrRefCount(namePtr); /* free unneeded name obj */
+ Tcl_DecrRefCount(nameLstPtr);
return result;
}
+ }
+
+ /*
+ * Make sure the Var structure of the array is not removed by
+ * a trace while we're working.
+ */
+
+ varPtr->refCount++;
+
+ /*
+ * Get the array values corresponding to each element name
+ */
+ tmpResPtr = Tcl_NewObj();
+ result = Tcl_ListObjGetElements(interp, nameLstPtr,
+ &count, &namePtrPtr);
+ if (result != TCL_OK) {
+ goto errorInArrayGet;
+ }
+
+ for (i = 0; i < count; i++) {
+ namePtr = *namePtrPtr++;
valuePtr = Tcl_ObjGetVar2(interp, objv[2], namePtr,
TCL_LEAVE_ERR_MSG);
if (valuePtr == NULL) {
- Tcl_DecrRefCount(namePtr); /* free unneeded name obj */
- return result;
+ /*
+ * Some trace played a trick on us; we need to diagnose to
+ * adapt our behaviour: was the array element unset, or did
+ * the modification modify the complete array?
+ */
+
+ if (TclIsVarArray(varPtr) && !TclIsVarUndefined(varPtr)) {
+ /*
+ * The array itself looks OK, the variable was
+ * undefined: forget it.
+ */
+
+ continue;
+ } else {
+ result = TCL_ERROR;
+ goto errorInArrayGet;
+ }
}
- result = Tcl_ListObjAppendElement(interp, resultPtr,
- valuePtr);
+ result = Tcl_ListObjAppendElement(interp, tmpResPtr, namePtr);
if (result != TCL_OK) {
- Tcl_DecrRefCount(namePtr); /* free unneeded name obj */
- return result;
+ goto errorInArrayGet;
+ }
+ result = Tcl_ListObjAppendElement(interp, tmpResPtr, valuePtr);
+ if (result != TCL_OK) {
+ goto errorInArrayGet;
}
}
+ varPtr->refCount--;
+ Tcl_SetObjResult(interp, tmpResPtr);
+ Tcl_DecrRefCount(nameLstPtr);
break;
+
+ errorInArrayGet:
+ varPtr->refCount--;
+ Tcl_DecrRefCount(nameLstPtr);
+ Tcl_DecrRefCount(tmpResPtr); /* free unneeded temp result obj */
+ return result;
}
case ARRAY_NAMES: {
Tcl_HashSearch search;
@@ -3047,9 +3085,17 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
char *pattern = NULL;
char *name;
Tcl_Obj *namePtr;
+ int mode, matched = 0;
+ static CONST char *options[] = {
+ "-exact", "-glob", "-regexp", (char *) NULL
+ };
+ enum options { OPT_EXACT, OPT_GLOB, OPT_REGEXP };
+
+ mode = OPT_GLOB;
- if ((objc != 3) && (objc != 4)) {
- Tcl_WrongNumArgs(interp, 2, objv, "arrayName ?pattern?");
+ if ((objc < 3) && (objc > 5)) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "arrayName ?mode? ?pattern?");
return TCL_ERROR;
}
if (notArray) {
@@ -3057,7 +3103,13 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
}
if (objc == 4) {
pattern = Tcl_GetString(objv[3]);
- }
+ } else if (objc == 5) {
+ pattern = Tcl_GetString(objv[4]);
+ if (Tcl_GetIndexFromObj(interp, objv[3], options, "option",
+ 0, &mode) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr, &search);
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
varPtr2 = (Var *) Tcl_GetHashValue(hPtr);
@@ -3065,8 +3117,25 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
continue;
}
name = Tcl_GetHashKey(varPtr->value.tablePtr, hPtr);
- if ((objc == 4) && !Tcl_StringMatch(name, pattern)) {
- continue; /* element name doesn't match pattern */
+ if (objc > 3) {
+ switch ((enum options) mode) {
+ case OPT_EXACT:
+ matched = (strcmp(name, pattern) == 0);
+ break;
+ case OPT_GLOB:
+ matched = Tcl_StringMatch(name, pattern);
+ break;
+ case OPT_REGEXP:
+ matched = Tcl_RegExpMatch(interp, name,
+ pattern);
+ if (matched < 0) {
+ return TCL_ERROR;
+ }
+ break;
+ }
+ if (matched == 0) {
+ continue;
+ }
}
namePtr = Tcl_NewStringObj(name, -1);
@@ -3080,7 +3149,6 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
}
case ARRAY_NEXTELEMENT: {
ArraySearch *searchPtr;
- char *searchId;
Tcl_HashEntry *hPtr;
if (objc != 4) {
@@ -3091,8 +3159,7 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
if (notArray) {
goto error;
}
- searchId = Tcl_GetString(objv[3]);
- searchPtr = ParseSearchId(interp, varPtr, varName, searchId);
+ searchPtr = ParseSearchId(interp, varPtr, varName, objv[3]);
if (searchPtr == NULL) {
return TCL_ERROR;
}
@@ -3178,7 +3245,27 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
varPtr->searchPtr = searchPtr;
break;
}
- case ARRAY_UNSET: {
+
+ case ARRAY_STATISTICS: {
+ CONST char *stats;
+
+ if (notArray) {
+ goto error;
+ }
+
+ stats = Tcl_HashStats(varPtr->value.tablePtr);
+ if (stats != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), stats, -1);
+ ckfree((void *)stats);
+ } else {
+ Tcl_SetResult(interp, "error reading array statistics",
+ TCL_STATIC);
+ return TCL_ERROR;
+ }
+ break;
+ }
+
+ case ARRAY_UNSET: {
Tcl_HashSearch search;
Var *varPtr2;
char *pattern = NULL;
@@ -3195,7 +3282,7 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
/*
* When no pattern is given, just unset the whole array
*/
- if (Tcl_UnsetVar2(interp, varName, (char *) NULL, 0)
+ if (TclObjUnsetVar2(interp, varNamePtr, NULL, 0)
!= TCL_OK) {
return TCL_ERROR;
}
@@ -3210,7 +3297,7 @@ Tcl_ArrayObjCmd(dummy, interp, objc, objv)
}
name = Tcl_GetHashKey(varPtr->value.tablePtr, hPtr);
if (Tcl_StringMatch(name, pattern) &&
- (Tcl_UnsetVar2(interp, varName, name, 0)
+ (TclObjUnsetVar2(interp, varNamePtr, name, 0)
!= TCL_OK)) {
return TCL_ERROR;
}
@@ -3254,26 +3341,26 @@ TclArraySet(interp, arrayNameObj, arrayElemObj)
{
Var *varPtr, *arrayPtr;
Tcl_Obj **elemPtrs;
- int result, elemLen, i;
+ int result, elemLen, i, nameLen;
char *varName, *p;
- varName = TclGetString(arrayNameObj);
- for (p = varName; *p ; p++) {
- if (*p == '(') {
- do {
- p++;
- } while (*p != '\0');
- p--;
- if (*p == ')') {
+ varName = Tcl_GetStringFromObj(arrayNameObj, &nameLen);
+ p = varName + nameLen - 1;
+ if (*p == ')') {
+ while (--p >= varName) {
+ if (*p == '(') {
VarErrMsg(interp, varName, NULL, "set", needArray);
return TCL_ERROR;
}
- break;
}
}
- varPtr = TclLookupVar(interp, varName, (char *) NULL, /*flags*/ 0,
- /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
+ varPtr = TclObjLookupVar(interp, arrayNameObj, NULL,
+ /*flags*/ TCL_LEAVE_ERR_MSG, /*msg*/ "set", /*createPart1*/ 1,
+ /*createPart2*/ 0, &arrayPtr);
+ if (varPtr == NULL) {
+ return TCL_ERROR;
+ }
if (arrayElemObj != NULL) {
result = Tcl_ListObjGetElements(interp, arrayElemObj,
@@ -3288,9 +3375,19 @@ TclArraySet(interp, arrayNameObj, arrayElemObj)
return TCL_ERROR;
}
if (elemLen > 0) {
+ /*
+ * We needn't worry about traces invalidating arrayPtr:
+ * should that be the case, TclPtrSetVar will return NULL
+ * so that we break out of the loop and return an error.
+ */
+
for (i = 0; i < elemLen; i += 2) {
- if (Tcl_ObjSetVar2(interp, arrayNameObj, elemPtrs[i],
- elemPtrs[i+1], TCL_LEAVE_ERR_MSG) == NULL) {
+ char *part2 = TclGetString(elemPtrs[i]);
+ Var *elemVarPtr = TclLookupArrayElement(interp, varName,
+ part2, TCL_LEAVE_ERR_MSG, "set", 1, 1, varPtr);
+ if ((elemVarPtr == NULL) ||
+ (TclPtrSetVar(interp, elemVarPtr, varPtr, varName,
+ part2, elemPtrs[i+1], TCL_LEAVE_ERR_MSG) == NULL)) {
result = TCL_ERROR;
break;
}
@@ -3320,22 +3417,6 @@ TclArraySet(interp, arrayNameObj, arrayElemObj)
VarErrMsg(interp, varName, (char *)NULL, "array set", needArray);
return TCL_ERROR;
}
- } else {
- /*
- * Create variable for new array.
- */
-
- varPtr = TclLookupVar(interp, varName, (char *) NULL,
- TCL_LEAVE_ERR_MSG, "set",
- /*createPart1*/ 1, /*createPart2*/ 0, &arrayPtr);
-
- /*
- * Still couldn't do it - this can occur if a non-existent
- * namespace was specified
- */
- if (varPtr == NULL) {
- return TCL_ERROR;
- }
}
TclSetVarArray(varPtr);
TclClearVarUndefined(varPtr);
@@ -3348,7 +3429,7 @@ TclArraySet(interp, arrayNameObj, arrayElemObj)
/*
*----------------------------------------------------------------------
*
- * MakeUpvar --
+ * ObjMakeUpvar --
*
* This procedure does all of the work of the "global" and "upvar"
* commands.
@@ -3366,158 +3447,101 @@ TclArraySet(interp, arrayNameObj, arrayElemObj)
*/
static int
-MakeUpvar(iPtr, framePtr, otherP1, otherP2, otherFlags, myName, myFlags)
- Interp *iPtr; /* Interpreter containing variables. Used
- * for error messages, too. */
+ObjMakeUpvar(interp, framePtr, otherP1Ptr, otherP2, otherFlags, myName, myFlags, index)
+ Tcl_Interp *interp; /* Interpreter containing variables. Used
+ * for error messages, too. */
CallFrame *framePtr; /* Call frame containing "other" variable.
* NULL means use global :: context. */
- char *otherP1, *otherP2; /* Two-part name of variable in framePtr. */
- int otherFlags; /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY:
+ Tcl_Obj *otherP1Ptr;
+ CONST char *otherP2; /* Two-part name of variable in framePtr. */
+ CONST int otherFlags; /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY:
* indicates scope of "other" variable. */
- char *myName; /* Name of variable which will refer to
+ CONST char *myName; /* Name of variable which will refer to
* otherP1/otherP2. Must be a scalar. */
- int myFlags; /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY:
+ CONST int myFlags; /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY:
* indicates scope of myName. */
+ int index; /* If the variable to be linked is an indexed
+ * scalar, this is its index. Otherwise, -1. */
{
- Tcl_HashEntry *hPtr;
+ Interp *iPtr = (Interp *) interp;
Var *otherPtr, *varPtr, *arrayPtr;
CallFrame *varFramePtr;
- CallFrame *savedFramePtr = NULL; /* Init. to avoid compiler warning. */
- Tcl_HashTable *tablePtr;
- Namespace *nsPtr, *altNsPtr, *dummyNsPtr;
- char *tail;
- int new;
+ CONST char *errMsg;
/*
* Find "other" in "framePtr". If not looking up other in just the
* current namespace, temporarily replace the current var frame
- * pointer in the interpreter in order to use TclLookupVar.
+ * pointer in the interpreter in order to use TclObjLookupVar.
*/
+ varFramePtr = iPtr->varFramePtr;
if (!(otherFlags & TCL_NAMESPACE_ONLY)) {
- savedFramePtr = iPtr->varFramePtr;
iPtr->varFramePtr = framePtr;
}
- otherPtr = TclLookupVar((Tcl_Interp *) iPtr, otherP1, otherP2,
+ otherPtr = TclObjLookupVar(interp, otherP1Ptr, otherP2,
(otherFlags | TCL_LEAVE_ERR_MSG), "access",
/*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
if (!(otherFlags & TCL_NAMESPACE_ONLY)) {
- iPtr->varFramePtr = savedFramePtr;
+ iPtr->varFramePtr = varFramePtr;
}
if (otherPtr == NULL) {
return TCL_ERROR;
}
- /*
- * Now create a hashtable entry for "myName". Create it as either a
- * namespace variable or as a local variable in a procedure call
- * frame. Interpret myName as a namespace variable if:
- * 1) so requested by a TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY flag,
- * 2) there is no active frame (we're at the global :: scope),
- * 3) the active frame was pushed to define the namespace context
- * for a "namespace eval" or "namespace inscope" command,
- * 4) the name has namespace qualifiers ("::"s).
- * If creating myName in the active procedure, look first in the
- * frame's array of compiler-allocated local variables, then in its
- * hashtable for runtime-created local variables. Create that
- * procedure's local variable hashtable if necessary.
- */
-
- varFramePtr = iPtr->varFramePtr;
- if ((myFlags & (TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY))
- || (varFramePtr == NULL)
- || !varFramePtr->isProcCallFrame
- || (strstr(myName, "::") != NULL)) {
- TclGetNamespaceForQualName((Tcl_Interp *) iPtr, myName,
- (Namespace *) NULL, myFlags, &nsPtr, &altNsPtr, &dummyNsPtr, &tail);
-
- if (nsPtr == NULL) {
- nsPtr = altNsPtr;
- }
- if (nsPtr == NULL) {
- Tcl_AppendResult((Tcl_Interp *) iPtr, "bad variable name \"",
- myName, "\": unknown namespace", (char *) NULL);
- return TCL_ERROR;
- }
-
+ if (index >= 0) {
+ if (!varFramePtr->isProcCallFrame) {
+ panic("ObjMakeUpVar called with an index outside from a proc.\n");
+ }
+ varPtr = &(varFramePtr->compiledLocals[index]);
+ } else {
/*
* Check that we are not trying to create a namespace var linked to
* a local variable in a procedure. If we allowed this, the local
* variable in the shorter-lived procedure frame could go away
* leaving the namespace var's reference invalid.
*/
-
- if ((otherP2 ? arrayPtr->nsPtr : otherPtr->nsPtr) == NULL) {
+
+ if (((otherP2 ? arrayPtr->nsPtr : otherPtr->nsPtr) == NULL)
+ && ((myFlags & (TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY))
+ || (varFramePtr == NULL)
+ || !varFramePtr->isProcCallFrame
+ || (strstr(myName, "::") != NULL))) {
Tcl_AppendResult((Tcl_Interp *) iPtr, "bad variable name \"",
- myName, "\": upvar won't create namespace variable that refers to procedure variable",
- (char *) NULL);
- return TCL_ERROR;
- }
+ myName, "\": upvar won't create namespace variable that ",
+ "refers to procedure variable", (char *) NULL);
+ return TCL_ERROR;
+ }
- hPtr = Tcl_CreateHashEntry(&nsPtr->varTable, tail, &new);
- if (new) {
- varPtr = NewVar();
- Tcl_SetHashValue(hPtr, varPtr);
- varPtr->hPtr = hPtr;
- varPtr->nsPtr = nsPtr;
- } else {
- varPtr = (Var *) Tcl_GetHashValue(hPtr);
+ /*
+ * Lookup and eventually create the new variable.
+ */
+
+ varPtr = TclLookupSimpleVar(interp, myName, myFlags, /*create*/ 1,
+ &errMsg, &index);
+ if (varPtr == NULL) {
+ VarErrMsg(interp, myName, NULL, "create", errMsg);
+ return TCL_ERROR;
}
- } else { /* look in the call frame */
- Proc *procPtr = varFramePtr->procPtr;
- int localCt = procPtr->numCompiledLocals;
- CompiledLocal *localPtr = procPtr->firstLocalPtr;
- Var *localVarPtr = varFramePtr->compiledLocals;
- int nameLen = strlen(myName);
- int i;
+ }
- varPtr = NULL;
- for (i = 0; i < localCt; i++) {
- if (!TclIsVarTemporary(localPtr)) {
- char *localName = localVarPtr->name;
- if ((myName[0] == localName[0])
- && (nameLen == localPtr->nameLength)
- && (strcmp(myName, localName) == 0)) {
- varPtr = localVarPtr;
- new = 0;
- break;
- }
- }
- localVarPtr++;
- localPtr = localPtr->nextPtr;
- }
- if (varPtr == NULL) { /* look in frame's local var hashtable */
- tablePtr = varFramePtr->varTablePtr;
- if (tablePtr == NULL) {
- tablePtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
- Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS);
- varFramePtr->varTablePtr = tablePtr;
- }
- hPtr = Tcl_CreateHashEntry(tablePtr, myName, &new);
- if (new) {
- varPtr = NewVar();
- Tcl_SetHashValue(hPtr, varPtr);
- varPtr->hPtr = hPtr;
- varPtr->nsPtr = varFramePtr->nsPtr;
- } else {
- varPtr = (Var *) Tcl_GetHashValue(hPtr);
- }
- }
+ if (varPtr == otherPtr) {
+ Tcl_SetResult((Tcl_Interp *) iPtr,
+ "can't upvar from variable to itself", TCL_STATIC);
+ return TCL_ERROR;
}
- if (!new) {
+ if (varPtr->tracePtr != NULL) {
+ Tcl_AppendResult((Tcl_Interp *) iPtr, "variable \"", myName,
+ "\" has traces: can't use for upvar", (char *) NULL);
+ return TCL_ERROR;
+ } else if (!TclIsVarUndefined(varPtr)) {
/*
- * The variable already exists. Make sure this variable "varPtr"
+ * The variable already existed. Make sure this variable "varPtr"
* isn't the same as "otherPtr" (avoid circular links). Also, if
* it's not an upvar then it's an error. If it is an upvar, then
* just disconnect it from the thing it currently refers to.
*/
- if (varPtr == otherPtr) {
- Tcl_SetResult((Tcl_Interp *) iPtr,
- "can't upvar from variable to itself", TCL_STATIC);
- return TCL_ERROR;
- }
if (TclIsVarLink(varPtr)) {
Var *linkPtr = varPtr->value.linkPtr;
if (linkPtr == otherPtr) {
@@ -3527,14 +3551,10 @@ MakeUpvar(iPtr, framePtr, otherP1, otherP2, otherFlags, myName, myFlags)
if (TclIsVarUndefined(linkPtr)) {
CleanupVar(linkPtr, (Var *) NULL);
}
- } else if (!TclIsVarUndefined(varPtr)) {
+ } else {
Tcl_AppendResult((Tcl_Interp *) iPtr, "variable \"", myName,
"\" already exists", (char *) NULL);
return TCL_ERROR;
- } else if (varPtr->tracePtr != NULL) {
- Tcl_AppendResult((Tcl_Interp *) iPtr, "variable \"", myName,
- "\" has traces: can't use for upvar", (char *) NULL);
- return TCL_ERROR;
}
}
TclSetVarLink(varPtr);
@@ -3569,52 +3589,16 @@ int
Tcl_UpVar(interp, frameName, varName, localName, flags)
Tcl_Interp *interp; /* Command interpreter in which varName is
* to be looked up. */
- char *frameName; /* Name of the frame containing the source
+ CONST char *frameName; /* Name of the frame containing the source
* variable, such as "1" or "#0". */
- char *varName; /* Name of a variable in interp to link to.
+ CONST char *varName; /* Name of a variable in interp to link to.
* May be either a scalar name or an
* element in an array. */
- char *localName; /* Name of link variable. */
+ CONST char *localName; /* Name of link variable. */
int flags; /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY:
* indicates scope of localName. */
{
- int result;
- CallFrame *framePtr;
- register char *p;
-
- result = TclGetFrame(interp, frameName, &framePtr);
- if (result == -1) {
- return TCL_ERROR;
- }
-
- /*
- * Figure out whether varName is an array reference, then call
- * MakeUpvar to do all the real work.
- */
-
- for (p = varName; *p != '\0'; p++) {
- if (*p == '(') {
- char *openParen = p;
- do {
- p++;
- } while (*p != '\0');
- p--;
- if (*p != ')') {
- goto scalar;
- }
- *openParen = '\0';
- *p = '\0';
- result = MakeUpvar((Interp *) interp, framePtr, varName,
- openParen+1, 0, localName, flags);
- *openParen = '(';
- *p = ')';
- return result;
- }
- }
-
- scalar:
- return MakeUpvar((Interp *) interp, framePtr, varName, (char *) NULL,
- 0, localName, flags);
+ return Tcl_UpVar2(interp, frameName, varName, NULL, localName, flags);
}
/*
@@ -3642,23 +3626,30 @@ int
Tcl_UpVar2(interp, frameName, part1, part2, localName, flags)
Tcl_Interp *interp; /* Interpreter containing variables. Used
* for error messages too. */
- char *frameName; /* Name of the frame containing the source
+ CONST char *frameName; /* Name of the frame containing the source
* variable, such as "1" or "#0". */
- char *part1, *part2; /* Two parts of source variable name to
+ CONST char *part1;
+ CONST char *part2; /* Two parts of source variable name to
* link to. */
- char *localName; /* Name of link variable. */
+ CONST char *localName; /* Name of link variable. */
int flags; /* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY:
* indicates scope of localName. */
{
int result;
CallFrame *framePtr;
+ Tcl_Obj *part1Ptr;
- result = TclGetFrame(interp, frameName, &framePtr);
- if (result == -1) {
+ if (TclGetFrame(interp, frameName, &framePtr) == -1) {
return TCL_ERROR;
}
- return MakeUpvar((Interp *) interp, framePtr, part1, part2, 0,
- localName, flags);
+
+ part1Ptr = Tcl_NewStringObj(part1, -1);
+ Tcl_IncrRefCount(part1Ptr);
+ result = ObjMakeUpvar(interp, framePtr, part1Ptr, part2, 0,
+ localName, flags, -1);
+ TclDecrRefCount(part1Ptr);
+
+ return result;
}
/*
@@ -3779,7 +3770,7 @@ Tcl_GlobalObjCmd(dummy, interp, objc, objv)
while ((tail > varName) && ((*tail != ':') || (*(tail-1) != ':'))) {
tail--;
}
- if (*tail == ':') {
+ if ((*tail == ':') && (tail > varName)) {
tail++;
}
@@ -3787,9 +3778,9 @@ Tcl_GlobalObjCmd(dummy, interp, objc, objv)
* Link to the variable "varName" in the global :: namespace.
*/
- result = MakeUpvar(iPtr, (CallFrame *) NULL,
- varName, (char *) NULL, /*otherFlags*/ TCL_GLOBAL_ONLY,
- /*myName*/ tail, /*myFlags*/ 0);
+ result = ObjMakeUpvar(interp, (CallFrame *) NULL,
+ objPtr, NULL, /*otherFlags*/ TCL_GLOBAL_ONLY,
+ /*myName*/ tail, /*myFlags*/ 0, -1);
if (result != TCL_OK) {
return result;
}
@@ -3844,6 +3835,12 @@ Tcl_VariableObjCmd(dummy, interp, objc, objv)
Var *varPtr, *arrayPtr;
Tcl_Obj *varValuePtr;
int i, result;
+ Tcl_Obj *varNamePtr;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "?name value...? name ?value?");
+ return TCL_ERROR;
+ }
for (i = 1; i < objc; i = i+2) {
/*
@@ -3851,8 +3848,9 @@ Tcl_VariableObjCmd(dummy, interp, objc, objv)
* it if necessary.
*/
- varName = TclGetString(objv[i]);
- varPtr = TclLookupVar(interp, varName, (char *) NULL,
+ varNamePtr = objv[i];
+ varName = TclGetString(varNamePtr);
+ varPtr = TclObjLookupVar(interp, varNamePtr, NULL,
(TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG), "define",
/*createPart1*/ 1, /*createPart2*/ 0, &arrayPtr);
@@ -3889,8 +3887,8 @@ Tcl_VariableObjCmd(dummy, interp, objc, objv)
*/
if (i+1 < objc) { /* a value was specified */
- varValuePtr = Tcl_ObjSetVar2(interp, objv[i], NULL, objv[i+1],
- (TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG));
+ varValuePtr = TclPtrSetVar(interp, varPtr, arrayPtr, varName, NULL,
+ objv[i+1], (TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG));
if (varValuePtr == NULL) {
return TCL_ERROR;
}
@@ -3924,10 +3922,10 @@ Tcl_VariableObjCmd(dummy, interp, objc, objv)
* current namespace.
*/
- result = MakeUpvar(iPtr, (CallFrame *) NULL,
- /*otherP1*/ varName, /*otherP2*/ (char *) NULL,
+ result = ObjMakeUpvar(interp, (CallFrame *) NULL,
+ /*otherP1*/ varNamePtr, /*otherP2*/ NULL,
/*otherFlags*/ TCL_NAMESPACE_ONLY,
- /*myName*/ tail, /*myFlags*/ 0);
+ /*myName*/ tail, /*myFlags*/ 0, -1);
if (result != TCL_OK) {
return result;
}
@@ -3961,10 +3959,8 @@ Tcl_UpvarObjCmd(dummy, interp, objc, objv)
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- register Interp *iPtr = (Interp *) interp;
CallFrame *framePtr;
- char *frameSpec, *otherVarName, *myVarName;
- register char *p;
+ char *frameSpec, *localName;
int result;
if (objc < 3) {
@@ -3997,34 +3993,9 @@ Tcl_UpvarObjCmd(dummy, interp, objc, objv)
*/
for ( ; objc > 0; objc -= 2, objv += 2) {
- myVarName = TclGetString(objv[1]);
- otherVarName = TclGetString(objv[0]);
- for (p = otherVarName; *p != 0; p++) {
- if (*p == '(') {
- char *openParen = p;
-
- do {
- p++;
- } while (*p != '\0');
- p--;
- if (*p != ')') {
- goto scalar;
- }
- *openParen = '\0';
- *p = '\0';
- result = MakeUpvar(iPtr, framePtr,
- otherVarName, openParen+1, /*otherFlags*/ 0,
- myVarName, /*flags*/ 0);
- *openParen = '(';
- *p = ')';
- goto checkResult;
- }
- }
- scalar:
- result = MakeUpvar(iPtr, framePtr, otherVarName, (char *) NULL, 0,
- myVarName, /*flags*/ 0);
-
- checkResult:
+ localName = TclGetString(objv[1]);
+ result = ObjMakeUpvar(interp, framePtr, /* othervarName */ objv[0],
+ NULL, 0, /* myVarName */ localName, /*flags*/ 0, -1);
if (result != TCL_OK) {
return TCL_ERROR;
}
@@ -4035,7 +4006,39 @@ Tcl_UpvarObjCmd(dummy, interp, objc, objv)
/*
*----------------------------------------------------------------------
*
- * CallTraces --
+ * DisposeTraceResult--
+ *
+ * This procedure is called to dispose of the result returned from
+ * a trace procedure. The disposal method appropriate to the type
+ * of result is determined by flags.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The memory allocated for the trace result may be freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+DisposeTraceResult(flags, result)
+ int flags; /* Indicates type of result to determine
+ * proper disposal method */
+ char *result; /* The result returned from a trace
+ * procedure to be disposed */
+{
+ if (flags & TCL_TRACE_RESULT_DYNAMIC) {
+ ckfree(result);
+ } else if (flags & TCL_TRACE_RESULT_OBJECT) {
+ Tcl_DecrRefCount((Tcl_Obj *) result);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CallVarTraces --
*
* This procedure is invoked to find and invoke relevant
* trace procedures associated with a particular operation on
@@ -4043,12 +4046,11 @@ Tcl_UpvarObjCmd(dummy, interp, objc, objv)
* variable and on its containing array (where relevant).
*
* Results:
- * The return value is NULL if no trace procedures were invoked, or
- * if all the invoked trace procedures returned successfully.
- * The return value is non-NULL if a trace procedure returned an
- * error (in this case no more trace procedures were invoked after
- * the error was returned). In this case the return value is a
- * pointer to a static string describing the error.
+ * Returns TCL_OK to indicate normal operation. Returns TCL_ERROR
+ * if invocation of a trace procedure indicated an error. When
+ * TCL_ERROR is returned and leaveErrMsg is true, then the
+ * ::errorInfo variable of iPtr has information about the error
+ * appended to it.
*
* Side effects:
* Almost anything can happen, depending on trace; this procedure
@@ -4057,26 +4059,33 @@ Tcl_UpvarObjCmd(dummy, interp, objc, objv)
*----------------------------------------------------------------------
*/
-static char *
-CallTraces(iPtr, arrayPtr, varPtr, part1, part2, flags)
+int
+CallVarTraces(iPtr, arrayPtr, varPtr, part1, part2, flags, leaveErrMsg)
Interp *iPtr; /* Interpreter containing variable. */
register Var *arrayPtr; /* Pointer to array variable that contains
* the variable, or NULL if the variable
* isn't an element of an array. */
Var *varPtr; /* Variable whose traces are to be
* invoked. */
- char *part1, *part2; /* Variable's two-part name. */
+ CONST char *part1;
+ CONST char *part2; /* Variable's two-part name. */
int flags; /* Flags passed to trace procedures:
* indicates what's happening to variable,
* plus other stuff like TCL_GLOBAL_ONLY,
* TCL_NAMESPACE_ONLY, and
* TCL_INTERP_DESTROYED. */
+ CONST int leaveErrMsg; /* If true, and one of the traces indicates an
+ * error, then leave an error message and stack
+ * trace information in *iPTr. */
{
register VarTrace *tracePtr;
ActiveVarTrace active;
- char *result, *openParen, *p;
+ char *result;
+ CONST char *openParen, *p;
Tcl_DString nameCopy;
int copiedName;
+ int code = TCL_OK;
+ int disposeFlags = 0;
/*
* If there are already similar trace procedures active for the
@@ -4084,10 +4093,13 @@ CallTraces(iPtr, arrayPtr, varPtr, part1, part2, flags)
*/
if (varPtr->flags & VAR_TRACE_ACTIVE) {
- return NULL;
+ return code;
}
varPtr->flags |= VAR_TRACE_ACTIVE;
varPtr->refCount++;
+ if (arrayPtr != NULL) {
+ arrayPtr->refCount++;
+ }
/*
* If the variable name hasn't been parsed into array name and
@@ -4108,12 +4120,14 @@ CallTraces(iPtr, arrayPtr, varPtr, part1, part2, flags)
} while (*p != '\0');
p--;
if (*p == ')') {
+ int offset = (openParen - part1);
+ char *newPart1;
Tcl_DStringInit(&nameCopy);
Tcl_DStringAppend(&nameCopy, part1, (p-part1));
- part2 = Tcl_DStringValue(&nameCopy)
- + (openParen + 1 - part1);
- part2[-1] = 0;
- part1 = Tcl_DStringValue(&nameCopy);
+ newPart1 = Tcl_DStringValue(&nameCopy);
+ newPart1[offset] = 0;
+ part1 = newPart1;
+ part2 = newPart1 + offset + 1;
copiedName = 1;
}
break;
@@ -4126,10 +4140,10 @@ CallTraces(iPtr, arrayPtr, varPtr, part1, part2, flags)
*/
result = NULL;
- active.nextPtr = iPtr->activeTracePtr;
- iPtr->activeTracePtr = &active;
- if (arrayPtr != NULL) {
- arrayPtr->refCount++;
+ active.nextPtr = iPtr->activeVarTracePtr;
+ iPtr->activeVarTracePtr = &active;
+ Tcl_Preserve((ClientData) iPtr);
+ if (arrayPtr != NULL && !(arrayPtr->flags & VAR_TRACE_ACTIVE)) {
active.varPtr = arrayPtr;
for (tracePtr = arrayPtr->tracePtr; tracePtr != NULL;
tracePtr = active.nextTracePtr) {
@@ -4137,15 +4151,22 @@ CallTraces(iPtr, arrayPtr, varPtr, part1, part2, flags)
if (!(tracePtr->flags & flags)) {
continue;
}
+ Tcl_Preserve((ClientData) tracePtr);
result = (*tracePtr->traceProc)(tracePtr->clientData,
(Tcl_Interp *) iPtr, part1, part2, flags);
if (result != NULL) {
if (flags & TCL_TRACE_UNSETS) {
- result = NULL;
+ /* Ignore errors in unset traces */
+ DisposeTraceResult(tracePtr->flags, result);
} else {
- goto done;
+ disposeFlags = tracePtr->flags;
+ code = TCL_ERROR;
}
}
+ Tcl_Release((ClientData) tracePtr);
+ if (code == TCL_ERROR) {
+ goto done;
+ }
}
}
@@ -4163,15 +4184,22 @@ CallTraces(iPtr, arrayPtr, varPtr, part1, part2, flags)
if (!(tracePtr->flags & flags)) {
continue;
}
+ Tcl_Preserve((ClientData) tracePtr);
result = (*tracePtr->traceProc)(tracePtr->clientData,
(Tcl_Interp *) iPtr, part1, part2, flags);
if (result != NULL) {
if (flags & TCL_TRACE_UNSETS) {
- result = NULL;
+ /* Ignore errors in unset traces */
+ DisposeTraceResult(tracePtr->flags, result);
} else {
- goto done;
+ disposeFlags = tracePtr->flags;
+ code = TCL_ERROR;
}
}
+ Tcl_Release((ClientData) tracePtr);
+ if (code == TCL_ERROR) {
+ goto done;
+ }
}
/*
@@ -4180,6 +4208,33 @@ CallTraces(iPtr, arrayPtr, varPtr, part1, part2, flags)
*/
done:
+ if (code == TCL_ERROR) {
+ if (leaveErrMsg) {
+ CONST char *type = "";
+ switch (flags&(TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_ARRAY)) {
+ case TCL_TRACE_READS: {
+ type = "read";
+ break;
+ }
+ case TCL_TRACE_WRITES: {
+ type = "set";
+ break;
+ }
+ case TCL_TRACE_ARRAY: {
+ type = "trace array";
+ break;
+ }
+ }
+ if (disposeFlags & TCL_TRACE_RESULT_OBJECT) {
+ VarErrMsg((Tcl_Interp *) iPtr, part1, part2, type,
+ Tcl_GetString((Tcl_Obj *) result));
+ } else {
+ VarErrMsg((Tcl_Interp *) iPtr, part1, part2, type, result);
+ }
+ }
+ DisposeTraceResult(disposeFlags,result);
+ }
+
if (arrayPtr != NULL) {
arrayPtr->refCount--;
}
@@ -4188,8 +4243,9 @@ CallTraces(iPtr, arrayPtr, varPtr, part1, part2, flags)
}
varPtr->flags &= ~VAR_TRACE_ACTIVE;
varPtr->refCount--;
- iPtr->activeTracePtr = active.nextPtr;
- return result;
+ iPtr->activeVarTracePtr = active.nextPtr;
+ Tcl_Release((ClientData) iPtr);
+ return code;
}
/*
@@ -4233,9 +4289,75 @@ NewVar()
/*
*----------------------------------------------------------------------
*
+ * SetArraySearchObj --
+ *
+ * This function converts the given tcl object into one that
+ * has the "array search" internal type.
+ *
+ * Results:
+ * TCL_OK if the conversion succeeded, and TCL_ERROR if it failed
+ * (when an error message will be placed in the interpreter's
+ * result.)
+ *
+ * Side effects:
+ * Updates the internal type and representation of the object to
+ * make this an array-search object. See the tclArraySearchType
+ * declaration above for details of the internal representation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SetArraySearchObj(interp, objPtr)
+ Tcl_Interp *interp;
+ Tcl_Obj *objPtr;
+{
+ char *string;
+ char *end;
+ int id;
+ size_t offset;
+
+ /*
+ * Get the string representation. Make it up-to-date if necessary.
+ */
+
+ string = Tcl_GetString(objPtr);
+
+ /*
+ * Parse the id into the three parts separated by dashes.
+ */
+ if ((string[0] != 's') || (string[1] != '-')) {
+ syntax:
+ Tcl_AppendResult(interp, "illegal search identifier \"", string,
+ "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ id = strtoul(string+2, &end, 10);
+ if ((end == (string+2)) || (*end != '-')) {
+ goto syntax;
+ }
+ /*
+ * Can't perform value check in this context, so place reference
+ * to place in string to use for the check in the object instead.
+ */
+ end++;
+ offset = end - string;
+
+ if (objPtr->typePtr != NULL && objPtr->typePtr->freeIntRepProc != NULL) {
+ objPtr->typePtr->freeIntRepProc(objPtr);
+ }
+ objPtr->typePtr = &tclArraySearchType;
+ objPtr->internalRep.twoPtrValue.ptr1 = (VOID *)(((char *)NULL)+id);
+ objPtr->internalRep.twoPtrValue.ptr2 = (VOID *)(((char *)NULL)+offset);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* ParseSearchId --
*
- * This procedure translates from a string to a pointer to an
+ * This procedure translates from a tcl object to a pointer to an
* active array search (if there is one that matches the string).
*
* Results:
@@ -4244,41 +4366,47 @@ NewVar()
* the interp's result contains an error message.
*
* Side effects:
- * None.
+ * The tcl object might have its internal type and representation
+ * modified.
*
*----------------------------------------------------------------------
*/
static ArraySearch *
-ParseSearchId(interp, varPtr, varName, string)
+ParseSearchId(interp, varPtr, varName, handleObj)
Tcl_Interp *interp; /* Interpreter containing variable. */
- Var *varPtr; /* Array variable search is for. */
- char *varName; /* Name of array variable that search is
+ CONST Var *varPtr; /* Array variable search is for. */
+ CONST char *varName; /* Name of array variable that search is
* supposed to be for. */
- char *string; /* String containing id of search. Must have
+ Tcl_Obj *handleObj; /* Object containing id of search. Must have
* form "search-num-var" where "num" is a
* decimal number and "var" is a variable
* name. */
{
- char *end;
+ register char *string;
+ register size_t offset;
int id;
ArraySearch *searchPtr;
/*
- * Parse the id into the three parts separated by dashes.
+ * Parse the id.
*/
-
- if ((string[0] != 's') || (string[1] != '-')) {
- syntax:
- Tcl_AppendResult(interp, "illegal search identifier \"", string,
- "\"", (char *) NULL);
+ if (Tcl_ConvertToType(interp, handleObj, &tclArraySearchType) != TCL_OK) {
return NULL;
}
- id = strtoul(string+2, &end, 10);
- if ((end == (string+2)) || (*end != '-')) {
- goto syntax;
- }
- if (strcmp(end+1, varName) != 0) {
+ /*
+ * Cast is safe, since always came from an int in the first place.
+ */
+ id = (int)(((char*)handleObj->internalRep.twoPtrValue.ptr1) -
+ ((char*)NULL));
+ string = Tcl_GetString(handleObj);
+ offset = (((char*)handleObj->internalRep.twoPtrValue.ptr2) -
+ ((char*)NULL));
+ /*
+ * This test cannot be placed inside the Tcl_Obj machinery, since
+ * it is dependent on the variable context.
+ */
+ if (strcmp(string+offset, varName) != 0) {
Tcl_AppendResult(interp, "search identifier \"", string,
"\" isn't for variable \"", varName, "\"", (char *) NULL);
return NULL;
@@ -4287,6 +4415,10 @@ ParseSearchId(interp, varPtr, varName, string)
/*
* Search through the list of active searches on the interpreter
* to see if the desired one exists.
+ *
+ * Note that we cannot store the searchPtr directly in the Tcl_Obj
+ * as that would run into trouble when DeleteSearches() was called
+ * so we must scan this list every time.
*/
for (searchPtr = varPtr->searchPtr; searchPtr != NULL;
@@ -4374,10 +4506,13 @@ TclDeleteVars(iPtr, tablePtr)
flags = TCL_TRACE_UNSETS;
if (tablePtr == &iPtr->globalNsPtr->varTable) {
- flags |= (TCL_INTERP_DESTROYED | TCL_GLOBAL_ONLY);
+ flags |= TCL_GLOBAL_ONLY;
} else if (tablePtr == &currNsPtr->varTable) {
flags |= TCL_NAMESPACE_ONLY;
}
+ if (Tcl_InterpDeleted(interp)) {
+ flags |= TCL_INTERP_DESTROYED;
+ }
for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL;
hPtr = Tcl_NextHashEntry(&search)) {
@@ -4411,7 +4546,7 @@ TclDeleteVars(iPtr, tablePtr)
* free up the variable's space (no need to free the hash entry
* here, unless we're dealing with a global variable: the
* hash entries will be deleted automatically when the whole
- * table is deleted). Note that we give CallTraces the variable's
+ * table is deleted). Note that we give CallVarTraces the variable's
* fully-qualified name so that any called trace procedures can
* refer to these variables being deleted.
*/
@@ -4420,16 +4555,16 @@ TclDeleteVars(iPtr, tablePtr)
objPtr = Tcl_NewObj();
Tcl_IncrRefCount(objPtr); /* until done with traces */
Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr, objPtr);
- (void) CallTraces(iPtr, (Var *) NULL, varPtr,
- Tcl_GetString(objPtr), (char *) NULL, flags);
+ CallVarTraces(iPtr, (Var *) NULL, varPtr, Tcl_GetString(objPtr),
+ NULL, flags, /* leaveErrMsg */ 0);
Tcl_DecrRefCount(objPtr); /* free no longer needed obj */
while (varPtr->tracePtr != NULL) {
VarTrace *tracePtr = varPtr->tracePtr;
varPtr->tracePtr = tracePtr->nextPtr;
- ckfree((char *) tracePtr);
+ Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
}
- for (activePtr = iPtr->activeTracePtr; activePtr != NULL;
+ for (activePtr = iPtr->activeVarTracePtr; activePtr != NULL;
activePtr = activePtr->nextPtr) {
if (activePtr->varPtr == varPtr) {
activePtr->nextTracePtr = NULL;
@@ -4546,14 +4681,14 @@ TclDeleteCompiledLocalVars(iPtr, framePtr)
*/
if (varPtr->tracePtr != NULL) {
- (void) CallTraces(iPtr, (Var *) NULL, varPtr,
- varPtr->name, (char *) NULL, flags);
+ CallVarTraces(iPtr, (Var *) NULL, varPtr, varPtr->name, NULL,
+ flags, /* leaveErrMsg */ 0);
while (varPtr->tracePtr != NULL) {
VarTrace *tracePtr = varPtr->tracePtr;
varPtr->tracePtr = tracePtr->nextPtr;
- ckfree((char *) tracePtr);
+ Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
}
- for (activePtr = iPtr->activeTracePtr; activePtr != NULL;
+ for (activePtr = iPtr->activeVarTracePtr; activePtr != NULL;
activePtr = activePtr->nextPtr) {
if (activePtr->varPtr == varPtr) {
activePtr->nextTracePtr = NULL;
@@ -4607,10 +4742,10 @@ TclDeleteCompiledLocalVars(iPtr, framePtr)
static void
DeleteArray(iPtr, arrayName, varPtr, flags)
Interp *iPtr; /* Interpreter containing array. */
- char *arrayName; /* Name of array (used for trace
+ CONST char *arrayName; /* Name of array (used for trace
* callbacks). */
Var *varPtr; /* Pointer to variable structure. */
- int flags; /* Flags to pass to CallTraces:
+ int flags; /* Flags to pass to CallVarTraces:
* TCL_TRACE_UNSETS and sometimes
* TCL_INTERP_DESTROYED,
* TCL_NAMESPACE_ONLY, or
@@ -4634,14 +4769,15 @@ DeleteArray(iPtr, arrayName, varPtr, flags)
elPtr->hPtr = NULL;
if (elPtr->tracePtr != NULL) {
elPtr->flags &= ~VAR_TRACE_ACTIVE;
- (void) CallTraces(iPtr, (Var *) NULL, elPtr, arrayName,
- Tcl_GetHashKey(varPtr->value.tablePtr, hPtr), flags);
+ CallVarTraces(iPtr, (Var *) NULL, elPtr, arrayName,
+ Tcl_GetHashKey(varPtr->value.tablePtr, hPtr), flags,
+ /* leaveErrMsg */ 0);
while (elPtr->tracePtr != NULL) {
VarTrace *tracePtr = elPtr->tracePtr;
elPtr->tracePtr = tracePtr->nextPtr;
- ckfree((char *) tracePtr);
+ Tcl_EventuallyFree((ClientData) tracePtr,TCL_DYNAMIC);
}
- for (activePtr = iPtr->activeTracePtr; activePtr != NULL;
+ for (activePtr = iPtr->activeVarTracePtr; activePtr != NULL;
activePtr = activePtr->nextPtr) {
if (activePtr->varPtr == elPtr) {
activePtr->nextTracePtr = NULL;
@@ -4650,6 +4786,19 @@ DeleteArray(iPtr, arrayName, varPtr, flags)
}
TclSetVarUndefined(elPtr);
TclSetVarScalar(elPtr);
+
+ /*
+ * Even though array elements are not supposed to be namespace
+ * variables, some combinations of [upvar] and [variable] may
+ * create such beasts - see [Bug 604239]. This is necessary to
+ * avoid leaking the corresponding Var struct, and is otherwise
+ * harmless.
+ */
+
+ if (elPtr->flags & VAR_NAMESPACE_VAR) {
+ elPtr->flags &= ~VAR_NAMESPACE_VAR;
+ elPtr->refCount--;
+ }
if (elPtr->refCount == 0) {
ckfree((char *) elPtr); /* element Vars are VAR_IN_HASHTABLE */
}
@@ -4729,10 +4878,11 @@ CleanupVar(varPtr, arrayPtr)
static void
VarErrMsg(interp, part1, part2, operation, reason)
Tcl_Interp *interp; /* Interpreter in which to record message. */
- char *part1, *part2; /* Variable's two-part name. */
- char *operation; /* String describing operation that failed,
+ CONST char *part1;
+ CONST char *part2; /* Variable's two-part name. */
+ CONST char *operation; /* String describing operation that failed,
* e.g. "read", "set", or "unset". */
- char *reason; /* String describing why operation failed. */
+ CONST char *reason; /* String describing why operation failed. */
{
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "can't ", operation, " \"", part1,
@@ -4742,7 +4892,6 @@ VarErrMsg(interp, part1, part2, operation, reason)
}
Tcl_AppendResult(interp, "\": ", reason, (char *) NULL);
}
-
/*
*----------------------------------------------------------------------
@@ -4765,11 +4914,10 @@ VarErrMsg(interp, part1, part2, operation, reason)
Var *
TclVarTraceExists(interp, varName)
Tcl_Interp *interp; /* The interpreter */
- char *varName; /* The variable name */
+ CONST char *varName; /* The variable name */
{
Var *varPtr;
Var *arrayPtr;
- char *msg;
/*
* The choice of "create" flag values is delicate here, and
@@ -4782,27 +4930,223 @@ TclVarTraceExists(interp, varName)
*/
varPtr = TclLookupVar(interp, varName, (char *) NULL,
- 0, "access",
- /*createPart1*/ 0, /*createPart2*/ 1, &arrayPtr);
+ 0, "access", /*createPart1*/ 0, /*createPart2*/ 1, &arrayPtr);
+
if (varPtr == NULL) {
return NULL;
}
- if ((varPtr != NULL) &&
- ((varPtr->tracePtr != NULL)
- || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL)))) {
- msg = CallTraces((Interp *)interp, arrayPtr, varPtr, varName,
- (char *) NULL, TCL_TRACE_READS);
- if (msg != NULL) {
- /*
- * If the variable doesn't exist anymore and no-one's using
- * it, then free up the relevant structures and hash table entries.
- */
- if (TclIsVarUndefined(varPtr)) {
- CleanupVar(varPtr, arrayPtr);
+ if ((varPtr->tracePtr != NULL)
+ || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
+ CallVarTraces((Interp *)interp, arrayPtr, varPtr, varName, NULL,
+ TCL_TRACE_READS, /* leaveErrMsg */ 0);
+ }
+
+ /*
+ * If the variable doesn't exist anymore and no-one's using
+ * it, then free up the relevant structures and hash table entries.
+ */
+
+ if (TclIsVarUndefined(varPtr)) {
+ CleanupVar(varPtr, arrayPtr);
+ return NULL;
+ }
+
+ return varPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Internal functions for variable name object types --
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*
+ * localVarName -
+ *
+ * INTERNALREP DEFINITION:
+ * twoPtrValue.ptr1 = pointer to the corresponding Proc
+ * twoPtrValue.ptr2 = index into locals table
+*/
+
+static void
+FreeLocalVarName(objPtr)
+ Tcl_Obj *objPtr;
+{
+ register Proc *procPtr = (Proc *) objPtr->internalRep.twoPtrValue.ptr1;
+ procPtr->refCount--;
+ if (procPtr->refCount <= 0) {
+ TclProcCleanupProc(procPtr);
+ }
+}
+
+static void
+DupLocalVarName(srcPtr, dupPtr)
+ Tcl_Obj *srcPtr;
+ Tcl_Obj *dupPtr;
+{
+ register Proc *procPtr = (Proc *) srcPtr->internalRep.twoPtrValue.ptr1;
+
+ dupPtr->internalRep.twoPtrValue.ptr1 = (VOID *) procPtr;
+ dupPtr->internalRep.twoPtrValue.ptr2 = srcPtr->internalRep.twoPtrValue.ptr2;
+ procPtr->refCount++;
+ dupPtr->typePtr = &tclLocalVarNameType;
+}
+
+static void
+UpdateLocalVarName(objPtr)
+ Tcl_Obj *objPtr;
+{
+ Proc *procPtr = (Proc *) objPtr->internalRep.twoPtrValue.ptr1;
+ unsigned int index = (unsigned int) objPtr->internalRep.twoPtrValue.ptr2;
+ CompiledLocal *localPtr = procPtr->firstLocalPtr;
+ unsigned int nameLen;
+
+ if (localPtr == NULL) {
+ goto emptyName;
+ }
+ while (index--) {
+ localPtr = localPtr->nextPtr;
+ if (localPtr == NULL) {
+ goto emptyName;
+ }
+ }
+
+ nameLen = (unsigned int) localPtr->nameLength;
+ objPtr->bytes = ckalloc(nameLen + 1);
+ memcpy(objPtr->bytes, localPtr->name, nameLen + 1);
+ objPtr->length = nameLen;
+ return;
+
+ emptyName:
+ objPtr->bytes = ckalloc(1);
+ *(objPtr->bytes) = '\0';
+ objPtr->length = 0;
+}
+
+/*
+ * nsVarName -
+ *
+ * INTERNALREP DEFINITION:
+ * twoPtrValue.ptr1: pointer to the namespace containing the
+ * reference.
+ * twoPtrValue.ptr2: pointer to the corresponding Var
+*/
+
+static void
+FreeNsVarName(objPtr)
+ Tcl_Obj *objPtr;
+{
+ register Var *varPtr = (Var *) objPtr->internalRep.twoPtrValue.ptr2;
+
+ varPtr->refCount--;
+ if (TclIsVarUndefined(varPtr) && (varPtr->refCount <= 0)) {
+ if (TclIsVarLink(varPtr)) {
+ Var *linkPtr = varPtr->value.linkPtr;
+ linkPtr->refCount--;
+ if (TclIsVarUndefined(linkPtr) && (linkPtr->refCount <= 0)) {
+ CleanupVar(linkPtr, (Var *) NULL);
}
- return NULL;
}
+ CleanupVar(varPtr, NULL);
}
- return varPtr;
+}
+
+static void
+DupNsVarName(srcPtr, dupPtr)
+ Tcl_Obj *srcPtr;
+ Tcl_Obj *dupPtr;
+{
+ Namespace *nsPtr = (Namespace *) srcPtr->internalRep.twoPtrValue.ptr1;
+ register Var *varPtr = (Var *) srcPtr->internalRep.twoPtrValue.ptr2;
+
+ dupPtr->internalRep.twoPtrValue.ptr1 = (VOID *) nsPtr;
+ dupPtr->internalRep.twoPtrValue.ptr2 = (VOID *) varPtr;
+ varPtr->refCount++;
+ dupPtr->typePtr = &tclNsVarNameType;
+}
+
+/*
+ * parsedVarName -
+ *
+ * INTERNALREP DEFINITION:
+ * twoPtrValue.ptr1 = pointer to the array name Tcl_Obj
+ * (NULL if scalar)
+ * twoPtrValue.ptr2 = pointer to the element name string
+ * (owned by this Tcl_Obj), or NULL if
+ * it is a scalar variable
+ */
+
+static void
+FreeParsedVarName(objPtr)
+ Tcl_Obj *objPtr;
+{
+ register Tcl_Obj *arrayPtr =
+ (Tcl_Obj *) objPtr->internalRep.twoPtrValue.ptr1;
+ register char *elem = (char *) objPtr->internalRep.twoPtrValue.ptr2;
+
+ if (arrayPtr != NULL) {
+ TclDecrRefCount(arrayPtr);
+ ckfree(elem);
+ }
+}
+
+static void
+DupParsedVarName(srcPtr, dupPtr)
+ Tcl_Obj *srcPtr;
+ Tcl_Obj *dupPtr;
+{
+ register Tcl_Obj *arrayPtr =
+ (Tcl_Obj *) srcPtr->internalRep.twoPtrValue.ptr1;
+ register char *elem = (char *) srcPtr->internalRep.twoPtrValue.ptr2;
+ char *elemCopy;
+ unsigned int elemLen;
+
+ if (arrayPtr != NULL) {
+ Tcl_IncrRefCount(arrayPtr);
+ elemLen = strlen(elem);
+ elemCopy = ckalloc(elemLen+1);
+ memcpy(elemCopy, elem, elemLen);
+ *(elemCopy + elemLen) = '\0';
+ elem = elemCopy;
+ }
+
+ dupPtr->internalRep.twoPtrValue.ptr1 = (VOID *) arrayPtr;
+ dupPtr->internalRep.twoPtrValue.ptr2 = (VOID *) elem;
+ dupPtr->typePtr = &tclParsedVarNameType;
+}
+
+static void
+UpdateParsedVarName(objPtr)
+ Tcl_Obj *objPtr;
+{
+ Tcl_Obj *arrayPtr = (Tcl_Obj *) objPtr->internalRep.twoPtrValue.ptr1;
+ char *part2 = (char *) objPtr->internalRep.twoPtrValue.ptr2;
+ char *part1, *p;
+ int len1, len2, totalLen;
+
+ if (arrayPtr == NULL) {
+ /*
+ * This is a parsed scalar name: what is it
+ * doing here?
+ */
+ panic("ERROR: scalar parsedVarName without a string rep.\n");
+ }
+ part1 = Tcl_GetStringFromObj(arrayPtr, &len1);
+ len2 = strlen(part2);
+
+ totalLen = len1 + len2 + 2;
+ p = ckalloc((unsigned int) totalLen + 1);
+ objPtr->bytes = p;
+ objPtr->length = totalLen;
+
+ memcpy(p, part1, (unsigned int) len1);
+ p += len1;
+ *p++ = '(';
+ memcpy(p, part2, (unsigned int) len2);
+ p += len2;
+ *p++ = ')';
+ *p = '\0';
}