summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compat/stdlib.h14
-rw-r--r--doc/Notifier.32
-rw-r--r--doc/encoding.n4
-rw-r--r--generic/regexec.c66
-rw-r--r--generic/tcl.decls4
-rw-r--r--generic/tcl.h11
-rw-r--r--generic/tclBasic.c45
-rw-r--r--generic/tclBinary.c22
-rw-r--r--generic/tclClock.c4
-rw-r--r--generic/tclCmdIL.c6
-rw-r--r--generic/tclDecls.h8
-rw-r--r--generic/tclEnsemble.c332
-rw-r--r--generic/tclExecute.c37
-rw-r--r--generic/tclIO.c9
-rw-r--r--generic/tclInt.h12
-rw-r--r--generic/tclInterp.c2
-rw-r--r--generic/tclLink.c26
-rw-r--r--generic/tclListObj.c647
-rw-r--r--generic/tclNotify.c38
-rw-r--r--generic/tclOO.c16
-rw-r--r--generic/tclOO.decls14
-rw-r--r--generic/tclOO.h29
-rw-r--r--generic/tclOOCall.c6
-rw-r--r--generic/tclOODecls.h23
-rw-r--r--generic/tclOODefineCmds.c6
-rw-r--r--generic/tclOOInt.h21
-rw-r--r--generic/tclOOMethod.c140
-rw-r--r--generic/tclOOStubInit.c3
-rw-r--r--generic/tclProc.c17
-rw-r--r--generic/tclStrToD.c88
-rw-r--r--generic/tclStringObj.c39
-rw-r--r--generic/tclStubInit.c1
-rw-r--r--generic/tclTest.c127
-rw-r--r--generic/tclUtf.c6
-rw-r--r--library/manifest.txt2
-rw-r--r--library/tcltest/pkgIndex.tcl2
-rw-r--r--library/tcltest/tcltest.tcl4
-rw-r--r--macosx/tclMacOSXFCmd.c12
-rw-r--r--tests/apply.test11
-rw-r--r--tests/io.test102
-rw-r--r--tests/oo.test2
-rw-r--r--tests/ooNext2.test2
-rw-r--r--tests/ooUtil.test2
-rw-r--r--tests/string.test12
-rw-r--r--tests/winConsole.test343
-rw-r--r--unix/Makefile.in14
-rw-r--r--unix/tclAppInit.c21
-rw-r--r--unix/tclLoadDl.c10
-rw-r--r--unix/tclLoadDyld.c10
-rw-r--r--unix/tclLoadNext.c2
-rw-r--r--unix/tclLoadOSF.c2
-rw-r--r--unix/tclLoadShl.c4
-rw-r--r--unix/tclUnixChan.c8
-rw-r--r--unix/tclUnixFCmd.c22
-rw-r--r--unix/tclUnixFile.c28
-rw-r--r--unix/tclUnixInit.c36
-rw-r--r--unix/tclUnixPipe.c10
-rw-r--r--unix/tclUnixSock.c10
-rw-r--r--unix/tclooConfig.sh2
-rw-r--r--win/Makefile.in4
-rw-r--r--win/nmakehlp.c9
-rw-r--r--win/rules.vc2
-rw-r--r--win/tclAppInit.c31
-rw-r--r--win/tclWinConsole.c2331
-rw-r--r--win/tclWinDde.c4
-rw-r--r--win/tclWinFCmd.c22
-rw-r--r--win/tclWinFile.c22
-rw-r--r--win/tclWinInit.c10
-rw-r--r--win/tclWinLoad.c7
-rw-r--r--win/tclWinPipe.c12
-rw-r--r--win/tclWinSerial.c10
-rw-r--r--win/tclWinSock.c4
-rw-r--r--win/tclooConfig.sh2
73 files changed, 3292 insertions, 1676 deletions
diff --git a/compat/stdlib.h b/compat/stdlib.h
index bb0f133..2f7eaf4 100644
--- a/compat/stdlib.h
+++ b/compat/stdlib.h
@@ -21,14 +21,18 @@ extern void abort(void);
extern double atof(const char *string);
extern int atoi(const char *string);
extern long atol(const char *string);
-extern char * calloc(unsigned int numElements, unsigned int size);
+extern void * calloc(unsigned long numElements, unsigned long size);
extern void exit(int status);
-extern int free(char *blockPtr);
+extern void free(void *blockPtr);
extern char * getenv(const char *name);
-extern char * malloc(unsigned int numBytes);
-extern void qsort(void *base, int n, int size, int (*compar)(
+extern void * malloc(unsigned long numBytes);
+extern void qsort(void *base, unsigned long n, unsigned long size, int (*compar)(
const void *element1, const void *element2));
-extern char * realloc(char *ptr, unsigned int numBytes);
+extern void * realloc(void *ptr, unsigned long numBytes);
+extern char * realpath(const char *path, char *resolved_path);
+extern int mkstemps(char *templ, int suffixlen);
+extern int mkstemp(char *templ);
+extern char * mkdtemp(char *templ);
extern long strtol(const char *string, char **endPtr, int base);
extern unsigned long strtoul(const char *string, char **endPtr, int base);
diff --git a/doc/Notifier.3 b/doc/Notifier.3
index 3b547ff..7cb02f6 100644
--- a/doc/Notifier.3
+++ b/doc/Notifier.3
@@ -90,7 +90,7 @@ necessary.
.AP Tcl_Event *evPtr in
An event to add to the event queue. The storage for the event must
have been allocated by the caller using \fBTcl_Alloc\fR or \fBckalloc\fR.
-.AP int flags in
+.AP int position in
Where to add the new event in the queue: \fBTCL_QUEUE_TAIL\fR,
\fBTCL_QUEUE_HEAD\fR, \fBTCL_QUEUE_MARK\fR, and whether to do
an alert if the queue is empty: \fBTCL_QUEUE_ALERT_IF_EMPTY\fR.
diff --git a/doc/encoding.n b/doc/encoding.n
index 2277f9d..c1dbf27 100644
--- a/doc/encoding.n
+++ b/doc/encoding.n
@@ -117,7 +117,7 @@ which is the Hiragana letter HA.
The following example detects the error location in an incomplete UTF-8 sequence:
.PP
.CS
-% set s [\fBencoding convertfrom\fR -failindex i utf-8 "A\xc3"]
+% set s [\fBencoding convertfrom\fR -failindex i utf-8 "A\exC3"]
A
% set i
1
@@ -127,7 +127,7 @@ The following example detects the error location while transforming to ISO8859-1
(ISO-Latin 1):
.PP
.CS
-% set s [\fBencoding convertto\fR -failindex i utf-8 "A\u0141"]
+% set s [\fBencoding convertto\fR -failindex i utf-8 "A\eu0141"]
A
% set i
1
diff --git a/generic/regexec.c b/generic/regexec.c
index 54cb905..7ef048e 100644
--- a/generic/regexec.c
+++ b/generic/regexec.c
@@ -236,13 +236,15 @@ exec(
v->err = 0;
assert(v->g->ntree >= 0);
n = v->g->ntree;
- if (n <= LOCALDFAS)
+ if (n <= LOCALDFAS) {
v->subdfas = subdfas;
- else
+ } else {
v->subdfas = (struct dfa **) MALLOC(n * sizeof(struct dfa *));
+ }
if (v->subdfas == NULL) {
- if (v->pmatch != pmatch && v->pmatch != mat)
+ if (v->pmatch != pmatch && v->pmatch != mat) {
FREE(v->pmatch);
+ }
FreeVars(v);
return REG_ESPACE;
}
@@ -279,11 +281,13 @@ exec(
}
n = v->g->ntree;
for (i = 0; i < n; i++) {
- if (v->subdfas[i] != NULL)
+ if (v->subdfas[i] != NULL) {
freeDFA(v->subdfas[i]);
+ }
}
- if (v->subdfas != subdfas)
+ if (v->subdfas != subdfas) {
FREE(v->subdfas);
+ }
FreeVars(v);
return st;
}
@@ -299,8 +303,9 @@ getsubdfa(struct vars * v,
{
if (v->subdfas[t->id] == NULL) {
v->subdfas[t->id] = newDFA(v, &t->cnfa, &v->g->cmap, NULL);
- if (ISERR())
+ if (ISERR()) {
return NULL;
+ }
}
return v->subdfas[t->id];
}
@@ -640,10 +645,11 @@ cdissect(
break;
case '.': /* concatenation */
assert(t->left != NULL && t->right != NULL);
- if (t->left->flags & SHORTER) /* reverse scan */
+ if (t->left->flags & SHORTER) {/* reverse scan */
er = crevcondissect(v, t, begin, end);
- else
+ } else {
er = ccondissect(v, t, begin, end);
+ }
break;
case '|': /* alternation */
assert(t->left != NULL);
@@ -651,10 +657,11 @@ cdissect(
break;
case '*': /* iteration */
assert(t->left != NULL);
- if (t->left->flags & SHORTER) /* reverse scan */
+ if (t->left->flags & SHORTER) {/* reverse scan */
er = creviterdissect(v, t, begin, end);
- else
+ } else {
er = citerdissect(v, t, begin, end);
+ }
break;
case '(': /* capturing */
assert(t->left != NULL && t->right == NULL);
@@ -920,17 +927,20 @@ cbrdissect(
assert(end > begin);
tlen = end - begin;
- if (tlen % brlen != 0)
+ if (tlen % brlen != 0) {
return REG_NOMATCH;
+ }
numreps = tlen / brlen;
- if (numreps < (size_t)min || (numreps > (size_t)max && max != DUPINF))
+ if (numreps < (size_t)min || (numreps > (size_t)max && max != DUPINF)) {
return REG_NOMATCH;
+ }
/* okay, compare the actual string contents */
p = begin;
while (numreps-- > 0) {
- if ((*v->g->compare) (brstring, p, brlen) != 0)
+ if ((*v->g->compare) (brstring, p, brlen) != 0) {
return REG_NOMATCH;
+ }
p += brlen;
}
@@ -1007,8 +1017,9 @@ citerdissect(struct vars * v,
*/
min_matches = t->min;
if (min_matches <= 0) {
- if (begin == end)
+ if (begin == end) {
return REG_OKAY;
+ }
min_matches = 1;
}
@@ -1022,8 +1033,9 @@ citerdissect(struct vars * v,
* sub-match endpoints in endpts[1..max_matches].
*/
max_matches = end - begin;
- if (max_matches > (size_t)t->max && t->max != DUPINF)
+ if (max_matches > (size_t)t->max && t->max != DUPINF) {
max_matches = t->max;
+ }
if (max_matches < (size_t)min_matches)
max_matches = min_matches;
endpts = (chr **) MALLOC((max_matches + 1) * sizeof(chr *));
@@ -1066,8 +1078,9 @@ citerdissect(struct vars * v,
t->id, k, LOFF(endpts[k])));
/* k'th sub-match can no longer be considered verified */
- if (nverified >= k)
+ if (nverified >= k) {
nverified = k - 1;
+ }
if (endpts[k] != end) {
/* haven't reached end yet, try another iteration if allowed */
@@ -1093,8 +1106,9 @@ citerdissect(struct vars * v,
* number of matches, start the slow part: recurse to verify each
* sub-match. We always have k <= max_matches, needn't check that.
*/
- if (k < min_matches)
+ if (k < min_matches) {
goto backtrack;
+ }
MDEBUG(("%d: verifying %d..%d\n", t->id, nverified + 1, k));
@@ -1105,8 +1119,9 @@ citerdissect(struct vars * v,
nverified = i;
continue;
}
- if (er == REG_NOMATCH)
+ if (er == REG_NOMATCH) {
break;
+ }
/* oops, something failed */
FREE(endpts);
return er;
@@ -1180,8 +1195,9 @@ creviterdissect(struct vars * v,
*/
min_matches = t->min;
if (min_matches <= 0) {
- if (begin == end)
+ if (begin == end) {
return REG_OKAY;
+ }
min_matches = 1;
}
@@ -1235,8 +1251,9 @@ creviterdissect(struct vars * v,
limit++;
/* if this is the last allowed sub-match, it must reach to the end */
- if ((size_t)k >= max_matches)
+ if ((size_t)k >= max_matches) {
limit = end;
+ }
/* try to find an endpoint for the k'th sub-match */
endpts[k] = shortest(v, d, endpts[k - 1], limit, end,
@@ -1250,8 +1267,9 @@ creviterdissect(struct vars * v,
t->id, k, LOFF(endpts[k])));
/* k'th sub-match can no longer be considered verified */
- if (nverified >= k)
+ if (nverified >= k) {
nverified = k - 1;
+ }
if (endpts[k] != end) {
/* haven't reached end yet, try another iteration if allowed */
@@ -1272,8 +1290,9 @@ creviterdissect(struct vars * v,
* number of matches, start the slow part: recurse to verify each
* sub-match. We always have k <= max_matches, needn't check that.
*/
- if (k < min_matches)
+ if (k < min_matches) {
goto backtrack;
+ }
MDEBUG(("%d: verifying %d..%d\n", t->id, nverified + 1, k));
@@ -1284,8 +1303,9 @@ creviterdissect(struct vars * v,
nverified = i;
continue;
}
- if (er == REG_NOMATCH)
+ if (er == REG_NOMATCH) {
break;
+ }
/* oops, something failed */
FREE(endpts);
return er;
diff --git a/generic/tcl.decls b/generic/tcl.decls
index fc3c8cb..d08ba0a 100644
--- a/generic/tcl.decls
+++ b/generic/tcl.decls
@@ -734,7 +734,7 @@ declare 204 {
const char *Tcl_PosixError(Tcl_Interp *interp)
}
declare 205 {
- void Tcl_QueueEvent(Tcl_Event *evPtr, int flags)
+ void Tcl_QueueEvent(Tcl_Event *evPtr, int position)
}
declare 206 {
int Tcl_Read(Tcl_Channel chan, char *bufPtr, int toRead)
@@ -1144,7 +1144,7 @@ declare 318 {
}
declare 319 {
void Tcl_ThreadQueueEvent(Tcl_ThreadId threadId, Tcl_Event *evPtr,
- int flags)
+ int position)
}
declare 320 {
int Tcl_UniCharAtIndex(const char *src, int index)
diff --git a/generic/tcl.h b/generic/tcl.h
index 94196a2..101ae0b 100644
--- a/generic/tcl.h
+++ b/generic/tcl.h
@@ -926,13 +926,8 @@ typedef struct Tcl_CmdInfo {
* change a command's namespace; use
* TclRenameCommand or Tcl_Eval (of 'rename')
* to do that. */
-#if (TCL_MAJOR_VERSION > 8) || defined(TCL_NO_DEPRECATED)
- Tcl_ObjCmdProc2 *objProc2; /* Command's object-based function. */
- void *objClientData2; /* ClientData for object proc. */
-#else
- void *reserved1;
- void *reserved2;
-#endif
+ Tcl_ObjCmdProc2 *objProc2; /* Not used in Tcl 8.7. */
+ void *objClientData2; /* Not used in Tcl 8.7. */
} Tcl_CmdInfo;
/*
@@ -2391,7 +2386,7 @@ const char * TclTomMathInitializeStubs(Tcl_Interp *interp,
#if defined(_WIN32)
TCL_NORETURN void Tcl_ConsolePanic(const char *format, ...);
#else
-# define Tcl_ConsolePanic ((Tcl_PanicProc *)0)
+# define Tcl_ConsolePanic ((Tcl_PanicProc *)NULL)
#endif
#ifdef USE_TCL_STUBS
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index d0af547..f7f6ed8 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -1350,11 +1350,11 @@ TclRegisterCommandTypeName(
int isNew;
hPtr = Tcl_CreateHashEntry(&commandTypeTable,
- (void *) implementationProc, &isNew);
+ implementationProc, &isNew);
Tcl_SetHashValue(hPtr, (void *) nameStr);
} else {
hPtr = Tcl_FindHashEntry(&commandTypeTable,
- (void *) implementationProc);
+ implementationProc);
if (hPtr != NULL) {
Tcl_DeleteHashEntry(hPtr);
}
@@ -1865,7 +1865,7 @@ DeleteInterpProc(
*/
Tcl_MutexLock(&cancelLock);
- hPtr = Tcl_FindHashEntry(&cancelTable, (char *) iPtr);
+ hPtr = Tcl_FindHashEntry(&cancelTable, iPtr);
if (hPtr != NULL) {
CancelInfo *cancelInfo = (CancelInfo *)Tcl_GetHashValue(hPtr);
@@ -3473,17 +3473,6 @@ Tcl_GetCommandInfoFromToken(
infoPtr->deleteProc = cmdPtr->deleteProc;
infoPtr->deleteData = cmdPtr->deleteData;
infoPtr->namespacePtr = (Tcl_Namespace *) cmdPtr->nsPtr;
-#if TCL_MAJOR_VERSION > 8 || defined(TCL_NO_DEPRECATED)
- if (infoPtr->objProc == cmdWrapperProc) {
- CmdWrapperInfo *info = (CmdWrapperInfo *)cmdPtr->objClientData;
- infoPtr->objProc2 = info->proc;
- infoPtr->objClientData2 = info->clientData;
- infoPtr->isNativeObjectProc = 2;
- } else {
- infoPtr->objProc2 = cmdWrapper2Proc;
- infoPtr->objClientData2 = cmdPtr;
- }
-#endif
return 1;
}
@@ -4664,7 +4653,7 @@ Tcl_CancelEval(
goto done;
}
- hPtr = Tcl_FindHashEntry(&cancelTable, (char *) interp);
+ hPtr = Tcl_FindHashEntry(&cancelTable, interp);
if (hPtr == NULL) {
/*
* No CancelInfo record for this interpreter.
@@ -5354,8 +5343,8 @@ TEOV_RunEnterTraces(
{
Interp *iPtr = (Interp *) interp;
Command *cmdPtr = *cmdPtrPtr;
- int newEpoch, cmdEpoch = cmdPtr->cmdEpoch;
- int length, traceCode = TCL_OK;
+ int length, newEpoch, cmdEpoch = cmdPtr->cmdEpoch;
+ int traceCode = TCL_OK;
const char *command = TclGetStringFromObj(commandPtr, &length);
/*
@@ -5625,7 +5614,7 @@ TclEvalEx(
* TCL_EVAL_GLOBAL was set. */
int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS);
int gotParse = 0;
- unsigned int i, objectsUsed = 0;
+ TCL_HASH_TYPE i, objectsUsed = 0;
/* These variables keep track of how much
* state has been allocated while evaluating
* the script, so that it can be freed
@@ -5797,7 +5786,7 @@ TclEvalEx(
wordStart = tokenPtr->start;
lines[objectsUsed] = TclWordKnownAtCompileTime(tokenPtr, NULL)
- ? wordLine : TCL_INDEX_NONE;
+ ? wordLine : -1;
if (eeFramePtr->type == TCL_LOCATION_SOURCE) {
iPtr->evalFlags |= TCL_EVAL_FILE;
@@ -6230,7 +6219,7 @@ TclArgumentRelease(
for (i = 1; i < objc; i++) {
CFWord *cfwPtr;
Tcl_HashEntry *hPtr =
- Tcl_FindHashEntry(iPtr->lineLAPtr, (char *) objv[i]);
+ Tcl_FindHashEntry(iPtr->lineLAPtr, objv[i]);
if (!hPtr) {
continue;
@@ -6282,7 +6271,7 @@ TclArgumentBCEnter(
CFWordBC *lastPtr = NULL;
Interp *iPtr = (Interp *) interp;
Tcl_HashEntry *hePtr =
- Tcl_FindHashEntry(iPtr->lineBCPtr, (char *) codePtr);
+ Tcl_FindHashEntry(iPtr->lineBCPtr, codePtr);
if (!hePtr) {
return;
@@ -6388,7 +6377,7 @@ TclArgumentBCRelease(
while (cfwPtr) {
CFWordBC *nextPtr = cfwPtr->nextPtr;
Tcl_HashEntry *hPtr =
- Tcl_FindHashEntry(iPtr->lineLABCPtr, (char *) cfwPtr->obj);
+ Tcl_FindHashEntry(iPtr->lineLABCPtr, cfwPtr->obj);
CFWordBC *xPtr = (CFWordBC *)Tcl_GetHashValue(hPtr);
if (xPtr != cfwPtr) {
@@ -6453,7 +6442,7 @@ TclArgumentGet(
* stack. That is nearest.
*/
- hPtr = Tcl_FindHashEntry(iPtr->lineLAPtr, (char *) obj);
+ hPtr = Tcl_FindHashEntry(iPtr->lineLAPtr, obj);
if (hPtr) {
CFWord *cfwPtr = (CFWord *)Tcl_GetHashValue(hPtr);
@@ -6467,7 +6456,7 @@ TclArgumentGet(
* that stack.
*/
- hPtr = Tcl_FindHashEntry(iPtr->lineLABCPtr, (char *) obj);
+ hPtr = Tcl_FindHashEntry(iPtr->lineLABCPtr, obj);
if (hPtr) {
CFWordBC *cfwPtr = (CFWordBC *)Tcl_GetHashValue(hPtr);
@@ -6510,7 +6499,7 @@ Tcl_Eval(
* previous call to Tcl_CreateInterp). */
const char *script) /* Pointer to TCL command to execute. */
{
- int code = Tcl_EvalEx(interp, script, -1, 0);
+ int code = Tcl_EvalEx(interp, script, TCL_INDEX_NONE, 0);
/*
* For backwards compatibility with old C code that predates the object
@@ -7359,10 +7348,11 @@ Tcl_AppendObjToErrorInfo(
* pertains. */
Tcl_Obj *objPtr) /* Message to record. */
{
- const char *message = TclGetString(objPtr);
+ int length;
+ const char *message = TclGetStringFromObj(objPtr, &length);
Tcl_IncrRefCount(objPtr);
- Tcl_AddObjErrorInfo(interp, message, objPtr->length);
+ Tcl_AddObjErrorInfo(interp, message, length);
Tcl_DecrRefCount(objPtr);
}
@@ -7534,6 +7524,7 @@ Tcl_VarEvalVA(
*
*----------------------------------------------------------------------
*/
+
int
Tcl_VarEval(
Tcl_Interp *interp,
diff --git a/generic/tclBinary.c b/generic/tclBinary.c
index bf40924..8b974c1 100644
--- a/generic/tclBinary.c
+++ b/generic/tclBinary.c
@@ -649,7 +649,7 @@ SetByteArrayFromAny(
TCL_UNUSED(Tcl_Interp *),
Tcl_Obj *objPtr) /* The object to convert to type ByteArray. */
{
- size_t length, bad;
+ int length, bad;
const char *src, *srcEnd;
unsigned char *dst;
Tcl_UniChar ch = 0;
@@ -663,8 +663,8 @@ SetByteArrayFromAny(
return TCL_OK;
}
- src = TclGetString(objPtr);
- length = bad = objPtr->length;
+ src = TclGetStringFromObj(objPtr, &length);
+ bad = length;
srcEnd = src + length;
/* Note the allocation is over-sized, possibly by a factor of four,
@@ -1001,7 +1001,7 @@ TclInitBinaryCmd(
static int
BinaryFormatCmd(
- TCL_UNUSED(ClientData),
+ TCL_UNUSED(void *),
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
@@ -1506,7 +1506,7 @@ BinaryFormatCmd(
static int
BinaryScanCmd(
- TCL_UNUSED(ClientData),
+ TCL_UNUSED(void *),
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
@@ -2583,7 +2583,7 @@ DeleteScanNumberCache(
static int
BinaryEncodeHex(
- TCL_UNUSED(ClientData),
+ TCL_UNUSED(void *),
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[])
@@ -2627,7 +2627,7 @@ BinaryEncodeHex(
static int
BinaryDecodeHex(
- TCL_UNUSED(ClientData),
+ TCL_UNUSED(void *),
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[])
@@ -2751,7 +2751,7 @@ BinaryDecodeHex(
static int
BinaryEncode64(
- TCL_UNUSED(ClientData),
+ TCL_UNUSED(void *),
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[])
@@ -2873,7 +2873,7 @@ BinaryEncode64(
static int
BinaryEncodeUu(
- TCL_UNUSED(ClientData),
+ TCL_UNUSED(void *),
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[])
@@ -3022,7 +3022,7 @@ BinaryEncodeUu(
static int
BinaryDecodeUu(
- TCL_UNUSED(ClientData),
+ TCL_UNUSED(void *),
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[])
@@ -3195,7 +3195,7 @@ BinaryDecodeUu(
static int
BinaryDecode64(
- TCL_UNUSED(ClientData),
+ TCL_UNUSED(void *),
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[])
diff --git a/generic/tclClock.c b/generic/tclClock.c
index 0669ffe..86eed73 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -1520,9 +1520,9 @@ GetJulianDayFromEraYearMonthDay(
* Have to make sure quotient is truncated towards 0 when negative.
* See above bug for details. The casts are necessary.
*/
- if (ym1 >= 0)
+ if (ym1 >= 0) {
ym1o4 = ym1 / 4;
- else {
+ } else {
ym1o4 = - (int) (((unsigned int) -ym1) / 4);
}
#endif
diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c
index f32fd98..1197b92 100644
--- a/generic/tclCmdIL.c
+++ b/generic/tclCmdIL.c
@@ -2901,7 +2901,7 @@ Tcl_LrepeatObjCmd(
List *listRepPtr = ListRepPtr(listPtr);
listRepPtr->elemCount = elementCount*objc;
- dataArray = &listRepPtr->elements;
+ dataArray = listRepPtr->elements;
}
/*
@@ -3088,7 +3088,7 @@ Tcl_LreverseObjCmd(
resultObj = Tcl_NewListObj(elemc, NULL);
listRepPtr = ListRepPtr(resultObj);
listRepPtr->elemCount = elemc;
- dataArray = &listRepPtr->elements;
+ dataArray = listRepPtr->elements;
for (i=0,j=elemc-1 ; i<elemc ; i++,j--) {
dataArray[j] = elemv[i];
@@ -4414,7 +4414,7 @@ Tcl_LsortObjCmd(
resultPtr = Tcl_NewListObj(sortInfo.numElements * groupSize, NULL);
listRepPtr = ListRepPtr(resultPtr);
- newArray = &listRepPtr->elements;
+ newArray = listRepPtr->elements;
if (group) {
for (i=0; elementPtr!=NULL ; elementPtr=elementPtr->nextPtr) {
idx = elementPtr->payload.index;
diff --git a/generic/tclDecls.h b/generic/tclDecls.h
index 58eb1d0..3917d0f 100644
--- a/generic/tclDecls.h
+++ b/generic/tclDecls.h
@@ -642,7 +642,7 @@ EXTERN int Tcl_PutEnv(const char *assignment);
/* 204 */
EXTERN const char * Tcl_PosixError(Tcl_Interp *interp);
/* 205 */
-EXTERN void Tcl_QueueEvent(Tcl_Event *evPtr, int flags);
+EXTERN void Tcl_QueueEvent(Tcl_Event *evPtr, int position);
/* 206 */
EXTERN int Tcl_Read(Tcl_Channel chan, char *bufPtr, int toRead);
/* 207 */
@@ -980,7 +980,7 @@ EXTERN Tcl_Obj * Tcl_SetVar2Ex(Tcl_Interp *interp, const char *part1,
EXTERN void Tcl_ThreadAlert(Tcl_ThreadId threadId);
/* 319 */
EXTERN void Tcl_ThreadQueueEvent(Tcl_ThreadId threadId,
- Tcl_Event *evPtr, int flags);
+ Tcl_Event *evPtr, int position);
/* 320 */
EXTERN int Tcl_UniCharAtIndex(const char *src, int index);
/* 321 */
@@ -2236,7 +2236,7 @@ typedef struct TclStubs {
void (*tcl_PrintDouble) (Tcl_Interp *interp, double value, char *dst); /* 202 */
int (*tcl_PutEnv) (const char *assignment); /* 203 */
const char * (*tcl_PosixError) (Tcl_Interp *interp); /* 204 */
- void (*tcl_QueueEvent) (Tcl_Event *evPtr, int flags); /* 205 */
+ void (*tcl_QueueEvent) (Tcl_Event *evPtr, int position); /* 205 */
int (*tcl_Read) (Tcl_Channel chan, char *bufPtr, int toRead); /* 206 */
void (*tcl_ReapDetachedProcs) (void); /* 207 */
int (*tcl_RecordAndEval) (Tcl_Interp *interp, const char *cmd, int flags); /* 208 */
@@ -2350,7 +2350,7 @@ typedef struct TclStubs {
int (*tcl_SetSystemEncoding) (Tcl_Interp *interp, const char *name); /* 316 */
Tcl_Obj * (*tcl_SetVar2Ex) (Tcl_Interp *interp, const char *part1, const char *part2, Tcl_Obj *newValuePtr, int flags); /* 317 */
void (*tcl_ThreadAlert) (Tcl_ThreadId threadId); /* 318 */
- void (*tcl_ThreadQueueEvent) (Tcl_ThreadId threadId, Tcl_Event *evPtr, int flags); /* 319 */
+ void (*tcl_ThreadQueueEvent) (Tcl_ThreadId threadId, Tcl_Event *evPtr, int position); /* 319 */
int (*tcl_UniCharAtIndex) (const char *src, int index); /* 320 */
int (*tcl_UniCharToLower) (int ch); /* 321 */
int (*tcl_UniCharToTitle) (int ch); /* 322 */
diff --git a/generic/tclEnsemble.c b/generic/tclEnsemble.c
index 5c30a0b..7a295ba 100644
--- a/generic/tclEnsemble.c
+++ b/generic/tclEnsemble.c
@@ -21,12 +21,12 @@ static inline Tcl_Obj * NewNsObj(Tcl_Namespace *namespacePtr);
static inline int EnsembleUnknownCallback(Tcl_Interp *interp,
EnsembleConfig *ensemblePtr, int objc,
Tcl_Obj *const objv[], Tcl_Obj **prefixObjPtr);
-static int NsEnsembleImplementationCmdNR(ClientData clientData,
+static int NsEnsembleImplementationCmdNR(void *clientData,
Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]);
static void BuildEnsembleConfig(EnsembleConfig *ensemblePtr);
static int NsEnsembleStringOrder(const void *strPtr1,
const void *strPtr2);
-static void DeleteEnsembleConfig(ClientData clientData);
+static void DeleteEnsembleConfig(void *clientData);
static void MakeCachedEnsembleCommand(Tcl_Obj *objPtr,
EnsembleConfig *ensemblePtr, Tcl_HashEntry *hPtr,
Tcl_Obj *fix);
@@ -70,8 +70,8 @@ enum EnsConfigOpts {
};
/*
- * This structure defines a Tcl object type that contains a reference to an
- * ensemble subcommand (e.g. the "length" in [string length ab]). It is used
+ * ensembleCmdType is a Tcl object type that contains a reference to an
+ * ensemble subcommand, e.g. the "length" in [string length ab]. It is used
* to cache the mapping between the subcommand itself and the real command
* that implements it.
*/
@@ -151,7 +151,7 @@ NewNsObj(
int
TclNamespaceEnsembleCmd(
- TCL_UNUSED(ClientData),
+ TCL_UNUSED(void *),
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[])
@@ -163,7 +163,8 @@ TclNamespaceEnsembleCmd(
Tcl_DictSearch search;
Tcl_Obj *listObj;
const char *simpleName;
- int index, done;
+ int index;
+ int done;
if (nsPtr == NULL || nsPtr->flags & NS_DEAD) {
if (!Tcl_InterpDeleted(interp)) {
@@ -187,7 +188,8 @@ TclNamespaceEnsembleCmd(
switch ((enum EnsSubcmds) index) {
case ENS_CREATE: {
const char *name;
- int len, allocatedMapFlag = 0;
+ int len;
+ int allocatedMapFlag = 0;
/*
* Defaults
*/
@@ -498,7 +500,8 @@ TclNamespaceEnsembleCmd(
Tcl_SetObjResult(interp, resultObj);
} else {
- int len, allocatedMapFlag = 0;
+ int len;
+ int allocatedMapFlag = 0;
Tcl_Obj *subcmdObj = NULL, *mapObj = NULL, *paramObj = NULL,
*unknownObj = NULL; /* Defaults, silence gcc 4 warnings */
int permitPrefix, flags = 0; /* silence gcc 4 warning */
@@ -940,7 +943,8 @@ Tcl_SetEnsembleMappingDict(
return TCL_ERROR;
}
if (mapDict != NULL) {
- int size, done;
+ int size;
+ int done;
Tcl_DictSearch search;
Tcl_Obj *valuePtr;
@@ -1523,7 +1527,8 @@ TclMakeEnsemble(
Tcl_DString buf, hiddenBuf;
const char **nameParts = NULL;
const char *cmdName = NULL;
- int i, nameCount = 0, ensembleFlags = 0, hiddenLen;
+ int i, nameCount = 0;
+ int ensembleFlags = 0, hiddenLen;
/*
* Construct the path for the ensemble namespace and create it.
@@ -1674,7 +1679,7 @@ TclMakeEnsemble(
int
TclEnsembleImplementationCmd(
- ClientData clientData,
+ void *clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[])
@@ -1685,7 +1690,7 @@ TclEnsembleImplementationCmd(
static int
NsEnsembleImplementationCmdNR(
- ClientData clientData,
+ void *clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[])
@@ -1704,7 +1709,7 @@ NsEnsembleImplementationCmdNR(
int subIdx;
/*
- * Must recheck objc, since numParameters might have changed. Cf. test
+ * Must recheck objc since numParameters might have changed. See test
* namespace-53.9.
*/
@@ -1712,7 +1717,7 @@ NsEnsembleImplementationCmdNR(
subIdx = 1 + ensemblePtr->numParameters;
if (objc < subIdx + 1) {
/*
- * We don't have a subcommand argument. Make error message.
+ * No subcommand argument. Make error message.
*/
Tcl_DString buf; /* Message being built */
@@ -1744,18 +1749,16 @@ NsEnsembleImplementationCmdNR(
}
/*
- * Determine if the table of subcommands is right. If so, we can just look
- * up in there and go straight to dispatch.
+ * If the table of subcommands is valid just lookup up the command there
+ * and go to dispatch.
*/
subObj = objv[subIdx];
if (ensemblePtr->epoch == ensemblePtr->nsPtr->exportLookupEpoch) {
/*
- * Table of subcommands is still valid; therefore there might be a
- * valid cache of discovered information which we can reuse. Do the
- * check here, and if we're still valid, we can jump straight to the
- * part where we do the invocation of the subcommand.
+ * Table of subcommands is still valid so if the internal representtion
+ * is an ensembleCmd, just call it.
*/
EnsembleCmdRep *ensembleCmd;
@@ -1777,8 +1780,8 @@ NsEnsembleImplementationCmdNR(
}
/*
- * Look in the hashtable for the subcommand name; this is the fastest way
- * of all if there is no cache in operation.
+ * Look in the hashtable for the named subcommand. This is the fastest
+ * path if there is no cache in operation.
*/
hPtr = Tcl_FindHashEntry(&ensemblePtr->subcommandTable,
@@ -1786,26 +1789,25 @@ NsEnsembleImplementationCmdNR(
if (hPtr != NULL) {
/*
- * Cache for later in the subcommand object.
+ * Cache ensemble in the subcommand object for later.
*/
MakeCachedEnsembleCommand(subObj, ensemblePtr, hPtr, NULL);
} else if (!(ensemblePtr->flags & TCL_ENSEMBLE_PREFIX)) {
/*
- * Could not map, no prefixing, go to unknown/error handling.
+ * Could not map. No prefixing. Go to unknown/error handling.
*/
goto unknownOrAmbiguousSubcommand;
} else {
/*
- * If we've not already confirmed the command with the hash as part of
- * building our export table, we need to scan the sorted array for
- * matches.
+ * If the command isn't yet confirmed with the hash as part of building
+ * the export table, scan the sorted array for matches.
*/
- const char *subcmdName; /* Name of the subcommand, or unique prefix of
- * it (will be an error for a non-unique
- * prefix). */
+ const char *subcmdName; /* Name of the subcommand or unique prefix of
+ * it (a non-unique prefix produces an error).
+ */
char *fullName = NULL; /* Full name of the subcommand. */
int stringLength, i;
int tableLength = ensemblePtr->subcommandTable.numEntries;
@@ -1820,10 +1822,10 @@ NsEnsembleImplementationCmdNR(
if (cmp == 0) {
if (fullName != NULL) {
/*
- * Since there's never the exact-match case to worry about
- * (hash search filters this), getting here indicates that
- * our subcommand is an ambiguous prefix of (at least) two
- * exported subcommands, which is an error case.
+ * Hash search filters out the exact-match case, so getting
+ * here indicates that the subcommand is an ambiguous
+ * prefix of at least two exported subcommands, which is an
+ * error case.
*/
goto unknownOrAmbiguousSubcommand;
@@ -1831,9 +1833,8 @@ NsEnsembleImplementationCmdNR(
fullName = ensemblePtr->subcommandArrayPtr[i];
} else if (cmp < 0) {
/*
- * Because we are searching a sorted table, we can now stop
- * searching because we have gone past anything that could
- * possibly match.
+ * The table is sorted so stop searching because a match would
+ * have been found already.
*/
break;
@@ -1841,7 +1842,7 @@ NsEnsembleImplementationCmdNR(
}
if (fullName == NULL) {
/*
- * The subcommand is not a prefix of anything, so bail out!
+ * The subcommand is not a prefix of anything. Bail out!
*/
goto unknownOrAmbiguousSubcommand;
@@ -1871,21 +1872,19 @@ NsEnsembleImplementationCmdNR(
runResultingSubcommand:
/*
- * Do the real work of execution of the subcommand by building an array of
- * objects (note that this is potentially not the same length as the
- * number of arguments to this ensemble command), populating it and then
- * feeding it back through the main command-lookup engine. In theory, we
- * could look up the command in the namespace ourselves, as we already
- * have the namespace in which it is guaranteed to exist,
+ * Execute the subcommand by populating an array of objects, which might
+ * not be the same length as the number of arguments to this ensemble
+ * command, and then handing it to the main command-lookup engine. In
+ * theory, the command could be looked up right here using the namespace in
+ * which it is guaranteed to exist,
*
* ((Q: That's not true if the -map option is used, is it?))
*
- * but we don't do that (the cacheing of the command object used should
- * help with that.)
+ * but don't do that because cacheing of the command object should help.
*/
{
- Tcl_Obj *copyPtr; /* The actual list of words to dispatch to.
+ Tcl_Obj *copyPtr; /* The list of words to dispatch on.
* Will be freed by the dispatch engine. */
Tcl_Obj **copyObjv;
int copyObjc, prefixObjc;
@@ -1908,8 +1907,8 @@ NsEnsembleImplementationCmdNR(
TclDecrRefCount(prefixObj);
/*
- * Record what arguments the script sent in so that things like
- * Tcl_WrongNumArgs can give the correct error message. Parameters
+ * Record the words of the command as given so that routines like
+ * Tcl_WrongNumArgs can produce the correct error message. Parameters
* count both as inserted and removed arguments.
*/
@@ -1931,10 +1930,9 @@ NsEnsembleImplementationCmdNR(
unknownOrAmbiguousSubcommand:
/*
- * Have not been able to match the subcommand asked for with a real
- * subcommand that we export. See whether a handler has been registered
- * for dealing with this situation. Will only call (at most) once for any
- * particular ensemble invocation.
+ * The named subcommand did not match any exported command. If there is a
+ * handler registered unknown subcommands, call it, but not more than once
+ * for this call.
*/
if (ensemblePtr->unknownHandler != NULL && reparseCount++ < 1) {
@@ -1950,10 +1948,10 @@ NsEnsembleImplementationCmdNR(
}
/*
- * We cannot determine what subcommand to hand off to, so generate a
- * (standard) failure message. Note the one odd case compared with
- * standard ensemble-like command, which is where a namespace has no
- * exported commands at all...
+ * Could not find a routine for the named subcommand so generate a standard
+ * failure message. The one odd case compared with a standard
+ * ensemble-like command is where a namespace has no exported commands at
+ * all...
*/
Tcl_ResetResult(interp);
@@ -1987,7 +1985,7 @@ NsEnsembleImplementationCmdNR(
int
TclClearRootEnsemble(
- TCL_UNUSED(ClientData *),
+ TCL_UNUSED(void **),
Tcl_Interp *interp,
int result)
{
@@ -2000,8 +1998,8 @@ TclClearRootEnsemble(
*
* TclInitRewriteEnsemble --
*
- * Applies a rewrite of arguments so that an ensemble subcommand will
- * report error messages correctly for the overall command.
+ * Applies a rewrite of arguments so that an ensemble subcommand
+ * correctly reports any error messages for the overall command.
*
* Results:
* Whether this is the first rewrite applied, a value which must be
@@ -2079,7 +2077,7 @@ TclResetRewriteEnsemble(
*
* TclSpellFix --
*
- * Record a spelling correction that needs making in the generation of
+ * Records a spelling correction that needs making in the generation of
* the WrongNumArgs usage message.
*
* Results:
@@ -2093,7 +2091,7 @@ TclResetRewriteEnsemble(
static int
FreeER(
- ClientData data[],
+ void *data[],
TCL_UNUSED(Tcl_Interp *),
int result)
{
@@ -2144,8 +2142,8 @@ TclSpellFix(
if (badIdx < iPtr->ensembleRewrite.numInsertedObjs) {
/*
- * Misspelled value was inserted. We cannot directly jump to the bad
- * value, but have to search.
+ * Misspelled value was inserted. Cannot directly jump to the bad
+ * value. Must search.
*/
idx = 1;
@@ -2257,22 +2255,22 @@ TclFetchEnsembleRoot(
/*
* ----------------------------------------------------------------------
*
- * EnsmebleUnknownCallback --
+ * EnsembleUnknownCallback --
*
- * Helper for the ensemble engine that handles the procesing of unknown
- * callbacks. See the user documentation of the ensemble unknown handler
- * for details; this function is only ever called when such a function is
- * defined, and is only ever called once per ensemble dispatch (i.e. if a
- * reparse still fails, this isn't called again).
+ * Helper for the ensemble engine. Calls the routine registered for
+ * "ensemble unknown" case. See the user documentation of the
+ * ensemble unknown handler for details. Only called when such a
+ * function is defined, and is only called once per ensemble dispatch.
+ * I.e. even if a reparse still fails, this isn't called again.
*
* Results:
* TCL_OK - *prefixObjPtr contains the command words to dispatch
* to.
- * TCL_CONTINUE - Need to reparse (*prefixObjPtr is invalid).
- * TCL_ERROR - Something went wrong! Error message in interpreter.
+ * TCL_CONTINUE - Need to reparse, i.e. *prefixObjPtr is invalid
+ * TCL_ERROR - Something went wrong. Error message in interpreter.
*
* Side effects:
- * Calls the Tcl interpreter, so arbitrary.
+ * Arbitrary, due to evaluation of script provided by client.
*
* ----------------------------------------------------------------------
*/
@@ -2285,28 +2283,28 @@ EnsembleUnknownCallback(
Tcl_Obj *const objv[],
Tcl_Obj **prefixObjPtr)
{
- int paramc, i, result, prefixObjc;
+ int paramc, i, prefixObjc;
+ int result;
Tcl_Obj **paramv, *unknownCmd, *ensObj;
/*
- * Create the unknown command callback to determine what to do.
+ * Create the "unknown" command callback to determine what to do.
*/
unknownCmd = Tcl_DuplicateObj(ensemblePtr->unknownHandler);
TclNewObj(ensObj);
Tcl_GetCommandFullName(interp, ensemblePtr->token, ensObj);
Tcl_ListObjAppendElement(NULL, unknownCmd, ensObj);
- for (i=1 ; i<objc ; i++) {
+ for (i = 1 ; i < objc ; i++) {
Tcl_ListObjAppendElement(NULL, unknownCmd, objv[i]);
}
TclListObjGetElementsM(NULL, unknownCmd, &paramc, &paramv);
Tcl_IncrRefCount(unknownCmd);
/*
- * Now call the unknown handler. (We don't bother NRE-enabling this; deep
- * recursing through unknown handlers is horribly perverse.) Note that it
- * is always an error for an unknown handler to delete its ensemble; don't
- * do that!
+ * Call the "unknown" handler. No attempt to NRE-enable this as deep
+ * recursion through unknown handlers is perverse. It is always an error
+ * for an unknown handler to delete its ensemble. Don't do that.
*/
Tcl_Preserve(ensemblePtr);
@@ -2324,10 +2322,9 @@ EnsembleUnknownCallback(
Tcl_Release(ensemblePtr);
/*
- * If we succeeded, we should either have a list of words that form the
- * command to be executed, or an empty list. In the empty-list case, the
- * ensemble is believed to be updated so we should ask the ensemble engine
- * to reparse the original command.
+ * On success the result is a list of words that form the command to be
+ * executed. If the list is empty, the ensemble should have been updated,
+ * so ask the ensemble engine to reparse the original command.
*/
if (result == TCL_OK) {
@@ -2336,11 +2333,7 @@ EnsembleUnknownCallback(
TclDecrRefCount(unknownCmd);
Tcl_ResetResult(interp);
- /*
- * Namespace is still there. Check if the result is a valid list. If
- * it is, and it is non-empty, that list is what we are using as our
- * replacement.
- */
+ /* A non-empty list is the replacement command. */
if (TclListObjLengthM(interp, *prefixObjPtr, &prefixObjc) != TCL_OK) {
TclDecrRefCount(*prefixObjPtr);
@@ -2353,7 +2346,7 @@ EnsembleUnknownCallback(
}
/*
- * Namespace alive & empty result => reparse.
+ * Empty result => reparse.
*/
TclDecrRefCount(*prefixObjPtr);
@@ -2361,7 +2354,7 @@ EnsembleUnknownCallback(
}
/*
- * Oh no! An exceptional result. Convert to an error.
+ * Convert exceptional result to an error.
*/
if (!Tcl_InterpDeleted(interp)) {
@@ -2401,16 +2394,16 @@ EnsembleUnknownCallback(
*
* MakeCachedEnsembleCommand --
*
- * Cache what we've computed so far; it's not nice to repeatedly copy
- * strings about. Note that to do this, we start by deleting any old
- * representation that there was (though if it was an out of date
- * ensemble rep, we can skip some of the deallocation process.)
+ * Caches what has been computed so far to minimize string copying.
+ * Starts by deleting any existing representation but reusing the existing
+ * structure if it is an ensembleCmd.
*
* Results:
- * None
+ * None.
*
* Side effects:
- * Alters the internal representation of the first object parameter.
+ * Converts the internal representation of the given object to an
+ * ensembleCmd.
*
*----------------------------------------------------------------------
*/
@@ -2432,8 +2425,7 @@ MakeCachedEnsembleCommand(
}
} else {
/*
- * Kill the old internal rep, and replace it with a brand new one of
- * our own.
+ * Replace any old internal representation with a new one.
*/
ensembleCmd = (EnsembleCmdRep *)ckalloc(sizeof(EnsembleCmdRep));
@@ -2459,17 +2451,16 @@ MakeCachedEnsembleCommand(
*
* DeleteEnsembleConfig --
*
- * Destroys the data structure used to represent an ensemble. This is
- * called when the ensemble's command is deleted (which happens
- * automatically if the ensemble's namespace is deleted.) Maintainers
- * should note that ensembles should be deleted by deleting their
- * commands.
+ * Destroys the data structure used to represent an ensemble. Called when
+ * the procedure for the ensemble is deleted, which happens automatically
+ * if the namespace for the ensemble is deleted. Deleting the procedure
+ * for an ensemble is the right way to initiate cleanup.
*
* Results:
* None.
*
* Side effects:
- * Memory is (eventually) deallocated.
+ * Memory is eventually deallocated.
*
*----------------------------------------------------------------------
*/
@@ -2496,15 +2487,12 @@ ClearTable(
static void
DeleteEnsembleConfig(
- ClientData clientData)
+ void *clientData)
{
EnsembleConfig *ensemblePtr = (EnsembleConfig *)clientData;
Namespace *nsPtr = ensemblePtr->nsPtr;
- /*
- * Unlink from the ensemble chain if it has not been marked as having been
- * done already.
- */
+ /* Unlink from the ensemble chain if it not already marked as unlinked. */
if (ensemblePtr->next != ensemblePtr) {
EnsembleConfig *ensPtr = (EnsembleConfig *) nsPtr->ensembles;
@@ -2530,7 +2518,7 @@ DeleteEnsembleConfig(
ensemblePtr->flags |= ENSEMBLE_DEAD;
/*
- * Kill the pointer-containing fields.
+ * Release the fields that contain pointers.
*/
ClearTable(ensemblePtr);
@@ -2548,10 +2536,9 @@ DeleteEnsembleConfig(
}
/*
- * Arrange for the structure to be reclaimed. Note that this is complex
- * because we have to make sure that we can react sensibly when an
- * ensemble is deleted during the process of initialising the ensemble
- * (especially the unknown callback.)
+ * Arrange for the structure to be reclaimed. This is complex because it is
+ * necessary to react sensibly when an ensemble is deleted during its
+ * initialisation, particularly in the case of an unknown callback.
*/
Tcl_EventuallyFree(ensemblePtr, TCL_DYNAMIC);
@@ -2562,11 +2549,11 @@ DeleteEnsembleConfig(
*
* BuildEnsembleConfig --
*
- * Create the internal data structures that describe how an ensemble
- * looks, being a hash mapping from the full command name to the Tcl list
- * that describes the implementation prefix words, and a sorted array of
- * all the full command names to allow for reasonably efficient
- * unambiguous prefix handling.
+ * Creates the internal data structures that describe how an ensemble
+ * looks. The structures are a hash map from the full command name to the
+ * Tcl list that describes the implementation prefix words, and a sorted
+ * array of all the full command names to allow for reasonably efficient
+ * handling of an unambiguous prefix.
*
* Results:
* None.
@@ -2574,7 +2561,7 @@ DeleteEnsembleConfig(
* Side effects:
* Reallocates and rebuilds the hash table and array stored at the
* ensemblePtr argument. For large ensembles or large namespaces, this is
- * a potentially expensive operation.
+ * may be an expensive operation.
*
*----------------------------------------------------------------------
*/
@@ -2583,10 +2570,10 @@ static void
BuildEnsembleConfig(
EnsembleConfig *ensemblePtr)
{
- Tcl_HashSearch search; /* Used for scanning the set of commands in
- * the namespace that backs up this
- * ensemble. */
- int i, j, isNew;
+ Tcl_HashSearch search; /* Used for scanning the commands in
+ * the namespace for this ensemble. */
+ int i, j;
+ int isNew;
Tcl_HashTable *hash = &ensemblePtr->subcommandTable;
Tcl_HashEntry *hPtr;
Tcl_Obj *mapDict = ensemblePtr->subcommandDict;
@@ -2602,13 +2589,13 @@ BuildEnsembleConfig(
/*
* There is a list of exactly what subcommands go in the table.
- * Must determine the target for each.
+ * Determine the target for each.
*/
TclListObjGetElementsM(NULL, subList, &subc, &subv);
if (subList == mapDict) {
/*
- * Strange case where explicit list of subcommands is same value
+ * Unusual case where explicit list of subcommands is same value
* as the dict mapping to targets.
*/
@@ -2657,10 +2644,10 @@ BuildEnsembleConfig(
}
/*
- * target was not in the dictionary so map onto the namespace.
- * Note in this case that we do not guarantee that the command
- * is actually there; that is the programmer's responsibility
- * (or [::unknown] of course).
+ * Target was not in the dictionary. Map onto the namespace.
+ * In this case there is no guarantee that the command
+ * is actually there. It is the responsibility of the
+ * programmer (or [::unknown] of course) to provide the procedure.
*/
cmdObj = Tcl_NewStringObj(name, -1);
@@ -2671,9 +2658,9 @@ BuildEnsembleConfig(
}
} else if (mapDict) {
/*
- * No subcmd list, but we do have a mapping dictionary so we should
- * use the keys of that. Convert the dictionary's contents into the
- * form required for the ensemble's internal hashtable.
+ * No subcmd list, but there is a mapping dictionary, so
+ * use the keys of that. Convert the contents of the dictionary into the
+ * form required for the internal hashtable of the ensemble.
*/
Tcl_DictSearch dictSearch;
@@ -2692,18 +2679,15 @@ BuildEnsembleConfig(
}
} else {
/*
- * Discover what commands are actually exported by the namespace.
- * What we have is an array of patterns and a hash table whose keys
- * are the command names exported by the namespace (the contents do
- * not matter here.) We must find out what commands are actually
- * exported by filtering each command in the namespace against each of
- * the patterns in the export list. Note that we use an intermediate
- * hash table to make memory management easier, and because that makes
- * exact matching far easier too.
+ * Use the array of patterns and the hash table whose keys are the
+ * commands exported by the namespace. The corresponding values do not
+ * matter here. Filter the commands in the namespace against the
+ * patterns in the export list to find out what commands are actually
+ * exported. Use an intermediate hash table to make memory management
+ * easier and to make exact matching much easier.
*
- * Suggestion for future enhancement: compute the unique prefixes and
- * place them in the hash too, which should make for even faster
- * matching.
+ * Suggestion for future enhancement: Compute the unique prefixes and
+ * place them in the hash too for even faster matching.
*/
hPtr = Tcl_FirstHashEntry(&ensemblePtr->nsPtr->cmdTable, &search);
@@ -2748,22 +2732,22 @@ BuildEnsembleConfig(
/*
* Create a sorted array of all subcommands in the ensemble; hash tables
* are all very well for a quick look for an exact match, but they can't
- * determine things like whether a string is a prefix of another (not
- * without lots of preparation anyway) and they're no good for when we're
- * generating the error message either.
+ * determine things like whether a string is a prefix of another, at least
+ * not without a lot of preparation, and they're not useful for generating
+ * the error message either.
*
- * We do this by filling an array with the names (we use the hash keys
- * directly to save a copy, since any time we change the array we change
- * the hash too, and vice versa) and running quicksort over the array.
+ * Do this by filling an array with the names: Use the hash keys
+ * directly to save a copy since any time we change the array we change
+ * the hash too, and vice versa, and run quicksort over the array.
*/
ensemblePtr->subcommandArrayPtr =
(char **)ckalloc(sizeof(char *) * hash->numEntries);
/*
- * Fill array from both ends as this makes us less likely to end up with
- * performance problems in qsort(), which is good. Note that doing this
- * makes this code much more opaque, but the naive alternatve:
+ * Fill the array from both ends as this reduces the likelihood of
+ * performance problems in qsort(). This makes this code much more opaque,
+ * but the naive alternatve:
*
* for (hPtr=Tcl_FirstHashEntry(hash,&search),i=0 ;
* hPtr!=NULL ; hPtr=Tcl_NextHashEntry(&search),i++) {
@@ -2771,11 +2755,11 @@ BuildEnsembleConfig(
* }
*
* can produce long runs of precisely ordered table entries when the
- * commands in the namespace are declared in a sorted fashion (an ordering
- * some people like) and the hashing functions (or the command names
- * themselves) are fairly unfortunate. By filling from both ends, it
- * requires active malice (and probably a debugger) to get qsort() to have
- * awful runtime behaviour.
+ * commands in the namespace are declared in a sorted fashion, which is an
+ * ordering some people like, and the hashing functions or the command
+ * names themselves are fairly unfortunate. Filling from both ends means
+ * that it requires active malice, and probably a debugger, to get qsort()
+ * to have awful runtime behaviour.
*/
i = 0;
@@ -2801,8 +2785,7 @@ BuildEnsembleConfig(
*
* NsEnsembleStringOrder --
*
- * Helper function to compare two pointers to two strings for use with
- * qsort().
+ * Helper to for uset with sort() that compares two string pointers.
*
* Results:
* -1 if the first string is smaller, 1 if the second string is smaller,
@@ -2930,14 +2913,15 @@ TclCompileEnsemble(
Tcl_Obj *replaced, *replacement;
Tcl_Command ensemble = (Tcl_Command) cmdPtr;
Command *oldCmdPtr = cmdPtr, *newCmdPtr;
- int len, result, flags = 0, i, depth = 1, invokeAnyway = 0;
+ int result, flags = 0, depth = 1, invokeAnyway = 0;
int ourResult = TCL_ERROR;
- unsigned numBytes;
+ int i, len;
+ TCL_HASH_TYPE numBytes;
const char *word;
TclNewObj(replaced);
Tcl_IncrRefCount(replaced);
- if (parsePtr->numWords < depth + 1) {
+ if (parsePtr->numWords <= depth) {
goto failed;
}
if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
@@ -3197,7 +3181,7 @@ TclCompileEnsemble(
* Throw out any line information generated by the failed compile attempt.
*/
- while (mapPtr->nuloc - 1 > eclIndex) {
+ while (mapPtr->nuloc > eclIndex + 1) {
mapPtr->nuloc--;
ckfree(mapPtr->loc[mapPtr->nuloc].line);
mapPtr->loc[mapPtr->nuloc].line = NULL;
@@ -3264,10 +3248,11 @@ TclAttemptCompileProc(
CompileEnv *envPtr) /* Holds resulting instructions. */
{
DefineLineInformation;
- int result, i;
+ int result;
+ int i;
Tcl_Token *saveTokenPtr = parsePtr->tokenPtr;
int savedStackDepth = envPtr->currStackDepth;
- unsigned savedCodeNext = envPtr->codeNext - envPtr->codeStart;
+ TCL_HASH_TYPE savedCodeNext = envPtr->codeNext - envPtr->codeStart;
int savedAuxDataArrayNext = envPtr->auxDataArrayNext;
int savedExceptArrayNext = envPtr->exceptArrayNext;
#ifdef TCL_COMPILE_DEBUG
@@ -3400,7 +3385,8 @@ CompileToInvokedCommand(
Tcl_Token *tokPtr;
Tcl_Obj *objPtr, **words;
const char *bytes;
- int i, numWords, cmdLit, extraLiteralFlags = LITERAL_CMD_NAME;
+ int cmdLit, extraLiteralFlags = LITERAL_CMD_NAME;
+ int i, numWords, length;
/*
* Push the words of the command. Take care; the command words may be
@@ -3411,9 +3397,9 @@ CompileToInvokedCommand(
TclListObjGetElementsM(NULL, replacements, &numWords, &words);
for (i = 0, tokPtr = parsePtr->tokenPtr; i < parsePtr->numWords;
i++, tokPtr = TokenAfter(tokPtr)) {
- if (i > 0 && i < numWords+1) {
- bytes = TclGetString(words[i-1]);
- PushLiteral(envPtr, bytes, words[i-1]->length);
+ if (i > 0 && i <= numWords) {
+ bytes = TclGetStringFromObj(words[i-1], &length);
+ PushLiteral(envPtr, bytes, length);
continue;
}
@@ -3441,11 +3427,11 @@ CompileToInvokedCommand(
TclNewObj(objPtr);
Tcl_GetCommandFullName(interp, (Tcl_Command) cmdPtr, objPtr);
- bytes = TclGetString(objPtr);
+ bytes = TclGetStringFromObj(objPtr, &length);
if ((cmdPtr != NULL) && (cmdPtr->flags & CMD_VIA_RESOLVER)) {
extraLiteralFlags |= LITERAL_UNSHARED;
}
- cmdLit = TclRegisterLiteral(envPtr, bytes, objPtr->length, extraLiteralFlags);
+ cmdLit = TclRegisterLiteral(envPtr, bytes, length, extraLiteralFlags);
TclSetCmdNameObj(interp, TclFetchLiteral(envPtr, cmdLit), cmdPtr);
TclEmitPush(cmdLit, envPtr);
TclDecrRefCount(objPtr);
diff --git a/generic/tclExecute.c b/generic/tclExecute.c
index 923aae3..dd50be0 100644
--- a/generic/tclExecute.c
+++ b/generic/tclExecute.c
@@ -511,13 +511,13 @@ VarHashCreateVar(
#define GetNumberFromObj(interp, objPtr, ptrPtr, tPtr) \
((TclHasInternalRep((objPtr), &tclIntType)) \
? (*(tPtr) = TCL_NUMBER_INT, \
- *(ptrPtr) = (ClientData) \
+ *(ptrPtr) = (void *) \
(&((objPtr)->internalRep.wideValue)), TCL_OK) : \
TclHasInternalRep((objPtr), &tclDoubleType) \
? (((isnan((objPtr)->internalRep.doubleValue)) \
? (*(tPtr) = TCL_NUMBER_NAN) \
: (*(tPtr) = TCL_NUMBER_DOUBLE)), \
- *(ptrPtr) = (ClientData) \
+ *(ptrPtr) = (void *) \
(&((objPtr)->internalRep.doubleValue)), TCL_OK) : \
(((objPtr)->bytes != NULL) && ((objPtr)->length == 0)) \
? TCL_ERROR : \
@@ -1348,7 +1348,7 @@ int
Tcl_ExprObj(
Tcl_Interp *interp, /* Context in which to evaluate the
* expression. */
- Tcl_Obj *objPtr, /* Points to Tcl object containing expression
+ 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. */
@@ -1494,10 +1494,11 @@ CompileExprObj(
* TIP #280: No invoker (yet) - Expression compilation.
*/
- const char *string = TclGetString(objPtr);
+ int length;
+ const char *string = TclGetStringFromObj(objPtr, &length);
- TclInitCompileEnv(interp, &compEnv, string, objPtr->length, NULL, 0);
- TclCompileExpr(interp, string, objPtr->length, &compEnv, 0);
+ TclInitCompileEnv(interp, &compEnv, string, length, NULL, 0);
+ TclCompileExpr(interp, string, length, &compEnv, 0);
/*
* Successful compilation. If the expression yielded no instructions,
@@ -2105,8 +2106,8 @@ TEBCresume(
Tcl_Obj *objPtr, *valuePtr, *value2Ptr, *part1Ptr, *part2Ptr, *tmpPtr;
Tcl_Obj **objv = NULL;
- int objc = 0;
- int opnd, length, pcAdjustment;
+ int length, objc = 0;
+ int opnd, pcAdjustment;
Var *varPtr, *arrayPtr;
#ifdef TCL_COMPILE_DEBUG
char cmdNameBuf[21];
@@ -3184,7 +3185,8 @@ TEBCresume(
*/
{
- int storeFlags, len;
+ int storeFlags;
+ int len;
case INST_STORE_ARRAY4:
opnd = TclGetUInt4AtPtr(pc+1);
@@ -4660,7 +4662,7 @@ TEBCresume(
TRACE_APPEND(("ERROR: \"%.30s\" not on reachable chain\n",
O2S(valuePtr)));
- for (i=contextPtr->index ; i>=0 ; i--) {
+ for (i = contextPtr->index ; i >= 0 ; i--) {
miPtr = contextPtr->callPtr->chain + i;
if (miPtr->isFilter
|| miPtr->mPtr->declaringClassPtr != classPtr) {
@@ -4787,7 +4789,11 @@ TEBCresume(
Method *const mPtr =
contextPtr->callPtr->chain[newDepth].mPtr;
- return mPtr->typePtr->callProc(mPtr->clientData, interp,
+ if (mPtr->typePtr->version < TCL_OO_METHOD_VERSION_2) {
+ return mPtr->typePtr->callProc(mPtr->clientData, interp,
+ (Tcl_ObjectContext) contextPtr, opnd, objv);
+ }
+ return ((Tcl_MethodCallProc2 *)(void *)(mPtr->typePtr->callProc))(mPtr->clientData, interp,
(Tcl_ObjectContext) contextPtr, opnd, objv);
}
@@ -4829,8 +4835,8 @@ TEBCresume(
*/
{
- int index, numIndices, fromIdx, toIdx;
- int nocase, match, length2, cflags, s1len, s2len;
+ int numIndices, nocase, match, cflags;
+ int length2, fromIdx, toIdx, index, s1len, s2len;
const char *s1, *s2;
case INST_LIST:
@@ -6866,7 +6872,8 @@ TEBCresume(
*/
{
- int opnd2, allocateDict, done, i, allocdict;
+ int opnd2, allocateDict, done, allocdict;
+ int i;
Tcl_Obj *dictPtr, *statePtr, *keyPtr, *listPtr, *varNamePtr, *keysPtr;
Tcl_Obj *emptyPtr, **keyPtrPtr;
Tcl_DictSearch *searchPtr;
@@ -10046,7 +10053,7 @@ EvalStatsCmd(
#ifdef TCL_MEM_DEBUG
Tcl_AppendPrintfToObj(objPtr, "\nHeap Statistics:\n");
- TclDumpMemoryInfo((ClientData) objPtr, 1);
+ TclDumpMemoryInfo(objPtr, 1);
#endif
Tcl_AppendPrintfToObj(objPtr, "\n----------------------------------------------------------------\n");
diff --git a/generic/tclIO.c b/generic/tclIO.c
index 585dc7b..5313eed 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -4488,8 +4488,8 @@ Write(
}
}
}
- if ((flushed < total) && (GotFlag(statePtr, CHANNEL_UNBUFFERED) ||
- (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED)))) {
+ if (((flushed < total) && GotFlag(statePtr, CHANNEL_UNBUFFERED)) ||
+ (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED))) {
if (FlushChannel(NULL, chanPtr, 0) != 0) {
return -1;
}
@@ -4749,7 +4749,6 @@ Tcl_GetsObj(
eol = dst;
skip = 1;
if (GotFlag(statePtr, INPUT_SAW_CR)) {
- ResetFlag(statePtr, INPUT_SAW_CR);
if ((eol < dstEnd) && (*eol == '\n')) {
/*
* Skip the raw bytes that make up the '\n'.
@@ -4799,8 +4798,10 @@ Tcl_GetsObj(
skip++;
}
eol--;
+ ResetFlag(statePtr, INPUT_SAW_CR);
goto gotEOL;
} else if (*eol == '\n') {
+ ResetFlag(statePtr, INPUT_SAW_CR);
goto gotEOL;
}
}
@@ -4829,7 +4830,7 @@ Tcl_GetsObj(
Tcl_SetObjLength(objPtr, oldLength);
CommonGetsCleanup(chanPtr);
copiedTotal = -1;
- ResetFlag(statePtr, CHANNEL_BLOCKED);
+ ResetFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR);
goto done;
}
goto gotEOL;
diff --git a/generic/tclInt.h b/generic/tclInt.h
index ee3dbf8..ac6fb54 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -913,7 +913,9 @@ typedef struct VarInHash {
*----------------------------------------------------------------
*/
-#if defined(__GNUC__) && (__GNUC__ > 2)
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define TCLFLEXARRAY
+#elif defined(__GNUC__) && (__GNUC__ > 2)
# define TCLFLEXARRAY 0
#else
# define TCLFLEXARRAY 1
@@ -2438,14 +2440,14 @@ typedef struct List {
* derived from the list representation. May
* be ignored if there is no string rep at
* all.*/
- Tcl_Obj *elements; /* First list element; the struct is grown to
+ Tcl_Obj *elements[TCLFLEXARRAY]; /* First list element; the struct is grown to
* accommodate all elements. */
} List;
#define LIST_MAX \
- (1 + (int)(((size_t)UINT_MAX - sizeof(List))/sizeof(Tcl_Obj *)))
+ ((int)(((size_t)UINT_MAX - offsetof(List, elements))/sizeof(Tcl_Obj *)))
#define LIST_SIZE(numElems) \
- (unsigned)(sizeof(List) + (((numElems) - 1) * sizeof(Tcl_Obj *)))
+ (TCL_HASH_TYPE)(offsetof(List, elements) + ((numElems) * sizeof(Tcl_Obj *)))
/*
* Macro used to get the elements of a list object.
@@ -2455,7 +2457,7 @@ typedef struct List {
((List *) (listPtr)->internalRep.twoPtrValue.ptr1)
#define ListObjGetElements(listPtr, objc, objv) \
- ((objv) = &(ListRepPtr(listPtr)->elements), \
+ ((objv) = ListRepPtr(listPtr)->elements, \
(objc) = ListRepPtr(listPtr)->elemCount)
#define ListObjLength(listPtr, len) \
diff --git a/generic/tclInterp.c b/generic/tclInterp.c
index b87bf7c..4ce2f31 100644
--- a/generic/tclInterp.c
+++ b/generic/tclInterp.c
@@ -1837,7 +1837,7 @@ AliasNRCmd(
listPtr = Tcl_NewListObj(cmdc, NULL);
listRep = ListRepPtr(listPtr);
listRep->elemCount = cmdc;
- cmdv = &listRep->elements;
+ cmdv = listRep->elements;
prefv = &aliasPtr->objPtr;
memcpy(cmdv, prefv, prefc * sizeof(Tcl_Obj *));
diff --git a/generic/tclLink.c b/generic/tclLink.c
index 384fcf3..6bd65fa 100644
--- a/generic/tclLink.c
+++ b/generic/tclLink.c
@@ -95,7 +95,7 @@ typedef struct Link {
* Forward references to functions defined later in this file:
*/
-static char * LinkTraceProc(ClientData clientData,Tcl_Interp *interp,
+static char * LinkTraceProc(void *clientData,Tcl_Interp *interp,
const char *name1, const char *name2, int flags);
static Tcl_Obj * ObjValue(Link *linkPtr);
static void LinkFree(Link *linkPtr);
@@ -527,7 +527,7 @@ GetUWide(
Tcl_WideUInt *uwidePtr)
{
Tcl_WideInt *widePtr = (Tcl_WideInt *) uwidePtr;
- ClientData clientData;
+ void *clientData;
int type, intValue;
if (TclGetNumberFromObj(NULL, objPtr, &clientData, &type) == TCL_OK) {
@@ -633,14 +633,15 @@ SetInvalidRealFromAny(
{
const char *str;
const char *endPtr;
+ int length;
- str = TclGetString(objPtr);
- if ((objPtr->length == 1) && (str[0] == '.')) {
+ str = TclGetStringFromObj(objPtr, &length);
+ if ((length == 1) && (str[0] == '.')) {
objPtr->typePtr = &invalidRealType;
objPtr->internalRep.doubleValue = 0.0;
return TCL_OK;
}
- if (TclParseNumber(NULL, objPtr, NULL, str, objPtr->length, &endPtr,
+ if (TclParseNumber(NULL, objPtr, NULL, str, length, &endPtr,
TCL_PARSE_DECIMAL_ONLY) == TCL_OK) {
/*
* If number is followed by [eE][+-]?, then it is an invalid
@@ -678,13 +679,14 @@ GetInvalidIntFromObj(
Tcl_Obj *objPtr,
int *intPtr)
{
- const char *str = TclGetString(objPtr);
+ int length;
+ const char *str = TclGetStringFromObj(objPtr, &length);
- if ((objPtr->length == 0) || ((objPtr->length == 2) && (str[0] == '0')
+ if ((length == 0) || ((length == 2) && (str[0] == '0')
&& strchr("xXbBoOdD", str[1]))) {
*intPtr = 0;
return TCL_OK;
- } else if ((objPtr->length == 1) && strchr("+-", str[0])) {
+ } else if ((length == 1) && strchr("+-", str[0])) {
*intPtr = (str[0] == '+');
return TCL_OK;
}
@@ -743,7 +745,7 @@ GetInvalidDoubleFromObj(
static char *
LinkTraceProc(
- ClientData clientData, /* Contains information about the link. */
+ void *clientData, /* Contains information about the link. */
Tcl_Interp *interp, /* Interpreter containing Tcl variable. */
TCL_UNUSED(const char *) /*name1*/,
TCL_UNUSED(const char *) /*name2*/,
@@ -896,8 +898,8 @@ LinkTraceProc(
switch (linkPtr->type) {
case TCL_LINK_STRING:
- value = TclGetString(valueObj);
- valueLength = valueObj->length + 1;
+ value = TclGetStringFromObj(valueObj, &valueLength);
+ valueLength++; /* include end of string char */
pp = (char **) linkPtr->addr;
*pp = (char *)ckrealloc(*pp, valueLength);
@@ -905,7 +907,7 @@ LinkTraceProc(
return NULL;
case TCL_LINK_CHARS:
- value = (char *) Tcl_GetStringFromObj(valueObj, &valueLength);
+ value = (char *) TclGetStringFromObj(valueObj, &valueLength);
valueLength++; /* include end of string char */
if (valueLength > linkPtr->bytes) {
return (char *) "wrong size of char* value";
diff --git a/generic/tclListObj.c b/generic/tclListObj.c
index a7f723d..c24809e 100644
--- a/generic/tclListObj.c
+++ b/generic/tclListObj.c
@@ -77,20 +77,22 @@ const Tcl_ObjType tclListType = {
*
* NewListInternalRep --
*
- * Creates a list internal rep with space for objc elements. objc
- * must be > 0. If objv!=NULL, initializes with the first objc values
- * in that array. If objv==NULL, initalize list internal rep to have
- * 0 elements, with space to add objc more. Flag value "p" indicates
+ * Creates a 'List' structure with space for 'objc' elements. 'objc' must
+ * be > 0. If 'objv' is not NULL, The list is initialized with first
+ * 'objc' values in that array. Otherwise the list is initialized to have
+ * 0 elements, with space to add 'objc' more. Flag value 'p' indicates
* how to behave on failure.
*
- * Results:
- * A new List struct with refCount 0 is returned. If some failure
- * prevents this then if p=0, NULL is returned and otherwise the
- * routine panics.
+ * Value
*
- * Side effects:
- * The ref counts of the elements in objv are incremented since the
- * resulting list now refers to them.
+ * A new 'List' structure with refCount 0. If some failure
+ * prevents this NULL is returned if 'p' is 0 , and 'Tcl_Panic'
+ * is called if it is not.
+ *
+ * Effect
+ *
+ * The refCount of each value in 'objv' is incremented as it is added
+ * to the list.
*
*----------------------------------------------------------------------
*/
@@ -140,7 +142,7 @@ NewListInternalRep(
int i;
listRepPtr->elemCount = objc;
- elemPtrs = &listRepPtr->elements;
+ elemPtrs = listRepPtr->elements;
for (i = 0; i < objc; i++) {
elemPtrs[i] = objv[i];
Tcl_IncrRefCount(elemPtrs[i]);
@@ -154,21 +156,9 @@ NewListInternalRep(
/*
*----------------------------------------------------------------------
*
- * AttemptNewList --
+ * AttemptNewList --
*
- * Creates a list internal rep with space for objc elements. objc
- * must be > 0. If objv!=NULL, initializes with the first objc values
- * in that array. If objv==NULL, initalize list internal rep to have
- * 0 elements, with space to add objc more.
- *
- * Results:
- * A new List struct with refCount 0 is returned. If some failure
- * prevents this then NULL is returned, and an error message is left
- * in the interp result, unless interp is NULL.
- *
- * Side effects:
- * The ref counts of the elements in objv are incremented since the
- * resulting list now refers to them.
+ * Like NewListInternalRep, but additionally sets an error message on failure.
*
*----------------------------------------------------------------------
*/
@@ -201,23 +191,20 @@ AttemptNewList(
*
* Tcl_NewListObj --
*
- * This function is normally called when not debugging: i.e., when
- * TCL_MEM_DEBUG is not defined. It creates a new list object from an
- * (objc,objv) array: that is, each of the objc elements of the array
- * referenced by objv is inserted as an element into a new Tcl object.
+ * Creates a new list object and adds values to it. When TCL_MEM_DEBUG is
+ * defined, 'Tcl_DbNewListObj' is called instead.
*
- * When TCL_MEM_DEBUG is defined, this function just returns the result
- * of calling the debugging version Tcl_DbNewListObj.
+ * Value
*
- * Results:
- * A new list object is returned that is initialized from the object
- * pointers in objv. If objc is less than or equal to zero, an empty
- * object is returned. The new object's string representation is left
- * NULL. The resulting new list object has ref count 0.
+ * A new list 'Tcl_Obj' to which is appended values from 'objv', or if
+ * 'objc' is less than or equal to zero, a list 'Tcl_Obj' having no
+ * elements. The string representation of the new 'Tcl_Obj' is set to
+ * NULL. The refCount of the list is 0.
*
- * Side effects:
- * The ref counts of the elements in objv are incremented since the
- * resulting list now refers to them.
+ * Effect
+ *
+ * The refCount of each elements in 'objv' is incremented as it is added
+ * to the list.
*
*----------------------------------------------------------------------
*/
@@ -268,28 +255,14 @@ Tcl_NewListObj(
/*
*----------------------------------------------------------------------
*
- * Tcl_DbNewListObj --
- *
- * This function is normally called when debugging: i.e., when
- * TCL_MEM_DEBUG is defined. It creates new list objects. It is the same
- * as the Tcl_NewListObj function above except that it calls
- * Tcl_DbCkalloc directly with the file name and line number from its
- * 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.
+ * Tcl_DbNewListObj --
*
- * When TCL_MEM_DEBUG is not defined, this function just returns the
- * result of calling Tcl_NewListObj.
+ * Like 'Tcl_NewListObj', but it calls Tcl_DbCkalloc directly with the
+ * file name and line number from its caller. This simplifies debugging
+ * since the [memory active] command will report the correct file
+ * name and line number when reporting objects that haven't been freed.
*
- * Results:
- * A new list object is returned that is initialized from the object
- * pointers in objv. If objc is less than or equal to zero, an empty
- * object is returned. The new object's string representation is left
- * NULL. The new list object has ref count 0.
- *
- * Side effects:
- * The ref counts of the elements in objv are incremented since the
- * resulting list now refers to them.
+ * When TCL_MEM_DEBUG is not defined, 'Tcl_NewListObj' is called instead.
*
*----------------------------------------------------------------------
*/
@@ -348,19 +321,8 @@ Tcl_DbNewListObj(
*
* Tcl_SetListObj --
*
- * Modify an object to be a list containing each of the objc elements of
- * the object array referenced by objv.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The object is made a list object and is initialized from the object
- * pointers in objv. If objc is less than or equal to zero, an empty
- * object is returned. The new object's string representation is left
- * NULL. The ref counts of the elements in objv are incremented since the
- * list now refers to them. The object's old string and internal
- * representations are freed and its type is set NULL.
+ * Like 'Tcl_NewListObj', but operates on an existing 'Tcl_Obj'instead of
+ * creating a new one.
*
*----------------------------------------------------------------------
*/
@@ -403,18 +365,20 @@ Tcl_SetListObj(
*
* TclListObjCopy --
*
- * Makes a "pure list" copy of a list value. This provides for the C
- * level a counterpart of the [lrange $list 0 end] command, while using
- * internals details to be as efficient as possible.
+ * Creates a new 'Tcl_Obj' which is a pure copy of a list value. This
+ * provides for the C level a counterpart of the [lrange $list 0 end]
+ * command, while using internals details to be as efficient as possible.
*
- * Results:
- * Normally returns a pointer to a new Tcl_Obj, that contains the same
- * list value as *listPtr does. The returned Tcl_Obj has a refCount of
- * zero. If *listPtr does not hold a list, NULL is returned, and if
- * interp is non-NULL, an error message is recorded there.
+ * Value
*
- * Side effects:
- * None.
+ * The address of the new 'Tcl_Obj' which shares its internal
+ * representation with 'listPtr', and whose refCount is 0. If 'listPtr'
+ * is not actually a list, the value is NULL, and an error message is left
+ * in 'interp' if it is not NULL.
+ *
+ * Effect
+ *
+ * 'listPtr' is converted to a list if it isn't one already.
*
*----------------------------------------------------------------------
*/
@@ -529,27 +493,30 @@ TclListObjRange(
*
* Tcl_ListObjGetElements --
*
- * This function returns an (objc,objv) array of the elements in a list
- * object.
+ * Retreive the elements in a list 'Tcl_Obj'.
*
- * Results:
- * The return value is normally TCL_OK; in this case *objcPtr is set to
- * the count of list elements and *objvPtr is set to a pointer to an
- * array of (*objcPtr) pointers to each list element. If listPtr does not
- * refer to a list object and the object can not be converted to one,
- * TCL_ERROR is returned and an error message will be left in the
- * interpreter's result if interp is not NULL.
- *
- * The objects referenced by the returned array should be treated as
- * readonly and their ref counts are _not_ incremented; the caller must
- * do that if it holds on to a reference. Furthermore, the pointer and
- * length returned by this function may change as soon as any function is
- * called on the list object; be careful about retaining the pointer in a
- * local data structure.
+ * Value
*
- * Side effects:
- * The possible conversion of the object referenced by listPtr
- * to a list object.
+ * TCL_OK
+ *
+ * A count of list elements is stored, 'objcPtr', And a pointer to the
+ * array of elements in the list is stored in 'objvPtr'.
+ *
+ * The elements accessible via 'objvPtr' should be treated as readonly
+ * and the refCount for each object is _not_ incremented; the caller
+ * must do that if it holds on to a reference. Furthermore, the
+ * pointer and length returned by this function may change as soon as
+ * any function is called on the list object. Be careful about
+ * retaining the pointer in a local data structure.
+ *
+ * TCL_ERROR
+ *
+ * 'listPtr' is not a valid list. An error message is left in the
+ * interpreter's result if 'interp' is not NULL.
+ *
+ * Effect
+ *
+ * 'listPtr' is converted to a list object if it isn't one already.
*
*----------------------------------------------------------------------
*/
@@ -570,7 +537,8 @@ Tcl_ListObjGetElements(
ListGetInternalRep(listPtr, listRepPtr);
if (listRepPtr == NULL) {
- int result, length;
+ int result;
+ int length;
(void) Tcl_GetStringFromObj(listPtr, &length);
if (length == 0) {
@@ -585,7 +553,7 @@ Tcl_ListObjGetElements(
ListGetInternalRep(listPtr, listRepPtr);
}
*objcPtr = listRepPtr->elemCount;
- *objvPtr = &listRepPtr->elements;
+ *objvPtr = listRepPtr->elements;
return TCL_OK;
}
@@ -594,20 +562,27 @@ Tcl_ListObjGetElements(
*
* Tcl_ListObjAppendList --
*
- * This function appends the elements in the list value referenced by
- * elemListPtr to the list value referenced by listPtr.
+ * Appends the elements of elemListPtr to those of listPtr.
*
- * Results:
- * The return value is normally TCL_OK. If listPtr or elemListPtr do not
- * refer to list values, TCL_ERROR is returned and an error message is
- * left in the interpreter's result if interp is not NULL.
+ * Value
*
- * Side effects:
- * The reference counts of the elements in elemListPtr are incremented
- * since the list now refers to them. listPtr and elemListPtr are
- * converted, if necessary, to list objects. Also, appending the new
- * elements may cause listObj's array of element pointers to grow.
- * listPtr's old string representation, if any, is invalidated.
+ * TCL_OK
+ *
+ * Success.
+ *
+ * TCL_ERROR
+ *
+ * 'listPtr' or 'elemListPtr' are not valid lists. An error
+ * message is left in the interpreter's result if 'interp' is not NULL.
+ *
+ * Effect
+ *
+ * The reference count of each element of 'elemListPtr' as it is added to
+ * 'listPtr'. 'listPtr' and 'elemListPtr' are converted to 'tclListType'
+ * if they are not already. Appending the new elements may cause the
+ * array of element pointers in 'listObj' to grow. If any objects are
+ * appended to 'listPtr'. Any preexisting string representation of
+ * 'listPtr' is invalidated.
*
*----------------------------------------------------------------------
*/
@@ -646,24 +621,27 @@ Tcl_ListObjAppendList(
*
* Tcl_ListObjAppendElement --
*
- * This function is a special purpose version of Tcl_ListObjAppendList:
- * it appends a single object referenced by objPtr to the list object
- * referenced by listPtr. If listPtr is not already a list object, an
- * attempt will be made to convert it to one.
+ * Like 'Tcl_ListObjAppendList', but Appends a single value to a list.
*
- * Results:
- * The return value is normally TCL_OK; in this case objPtr is added to
- * the end of listPtr's list. If listPtr does not refer to a list object
- * and the object can not be converted to one, TCL_ERROR is returned and
- * an error message will be left in the interpreter's result if interp is
- * not NULL.
+ * Value
*
- * Side effects:
- * The ref count of objPtr is incremented since the list now refers to
- * it. listPtr will be converted, if necessary, to a list object. Also,
- * appending the new element may cause listObj's array of element
- * pointers to grow. listPtr's old string representation, if any, is
- * invalidated.
+ * TCL_OK
+ *
+ * 'objPtr' is appended to the elements of 'listPtr'.
+ *
+ * TCL_ERROR
+ *
+ * listPtr does not refer to a list object and the object can not be
+ * converted to one. An error message will be left in the
+ * interpreter's result if interp is not NULL.
+ *
+ * Effect
+ *
+ * If 'listPtr' is not already of type 'tclListType', it is converted.
+ * The 'refCount' of 'objPtr' is incremented as it is added to 'listPtr'.
+ * Appending the new element may cause the the array of element pointers
+ * in 'listObj' to grow. Any preexisting string representation of
+ * 'listPtr' is invalidated.
*
*----------------------------------------------------------------------
*/
@@ -675,7 +653,8 @@ Tcl_ListObjAppendElement(
Tcl_Obj *objPtr) /* Object to append to listPtr's list. */
{
List *listRepPtr, *newPtr = NULL;
- int numElems, numRequired, needGrow, isShared, attempt;
+ int numElems, numRequired;
+ int needGrow, isShared, attempt;
if (Tcl_IsShared(listPtr)) {
Tcl_Panic("%s called with shared object", "Tcl_ListObjAppendElement");
@@ -683,7 +662,8 @@ Tcl_ListObjAppendElement(
ListGetInternalRep(listPtr, listRepPtr);
if (listRepPtr == NULL) {
- int result, length;
+ int result;
+ int length;
(void) Tcl_GetStringFromObj(listPtr, &length);
if (length == 0) {
@@ -739,7 +719,7 @@ Tcl_ListObjAppendElement(
}
}
if (isShared || needGrow) {
- Tcl_Obj **dst, **src = &listRepPtr->elements;
+ Tcl_Obj **dst, **src = listRepPtr->elements;
/*
* Either we have a shared internalrep and we must copy to write, or we
@@ -767,7 +747,7 @@ Tcl_ListObjAppendElement(
return TCL_ERROR;
}
- dst = &newPtr->elements;
+ dst = newPtr->elements;
newPtr->refCount++;
newPtr->canonicalFlag = listRepPtr->canonicalFlag;
newPtr->elemCount = listRepPtr->elemCount;
@@ -803,7 +783,7 @@ Tcl_ListObjAppendElement(
* the ref count for the (now shared) objPtr.
*/
- *(&listRepPtr->elements + listRepPtr->elemCount) = objPtr;
+ listRepPtr->elements[listRepPtr->elemCount] = objPtr;
Tcl_IncrRefCount(objPtr);
listRepPtr->elemCount++;
@@ -821,23 +801,27 @@ Tcl_ListObjAppendElement(
*
* Tcl_ListObjIndex --
*
- * This function returns a pointer to the index'th object from the list
- * referenced by listPtr. The first element has index 0. If index is
- * negative or greater than or equal to the number of elements in the
- * list, a NULL is returned. If listPtr is not a list object, an attempt
- * will be made to convert it to a list.
+ * Retrieve a pointer to the element of 'listPtr' at 'index'. The index
+ * of the first element is 0.
*
- * Results:
- * The return value is normally TCL_OK; in this case objPtrPtr is set to
- * the Tcl_Obj pointer for the index'th list element or NULL if index is
- * out of range. This object should be treated as readonly and its ref
- * count is _not_ incremented; the caller must do that if it holds on to
- * the reference. If listPtr does not refer to a list and can't be
- * converted to one, TCL_ERROR is returned and an error message is left
- * in the interpreter's result if interp is not NULL.
+ * Value
*
- * Side effects:
- * listPtr will be converted, if necessary, to a list object.
+ * TCL_OK
+ *
+ * A pointer to the element at 'index' is stored in 'objPtrPtr'. If
+ * 'index' is out of range, NULL is stored in 'objPtrPtr'. This
+ * object should be treated as readonly and its 'refCount' is _not_
+ * incremented. The caller must do that if it holds on to the
+ * reference.
+ *
+ * TCL_ERROR
+ *
+ * 'listPtr' is not a valid list. An an error message is left in the
+ * interpreter's result if 'interp' is not NULL.
+ *
+ * Effect
+ *
+ * If 'listPtr' is not already of type 'tclListType', it is converted.
*
*----------------------------------------------------------------------
*/
@@ -853,7 +837,8 @@ Tcl_ListObjIndex(
ListGetInternalRep(listPtr, listRepPtr);
if (listRepPtr == NULL) {
- int result, length;
+ int result;
+ int length;
(void) Tcl_GetStringFromObj(listPtr, &length);
if (length == 0) {
@@ -870,7 +855,7 @@ Tcl_ListObjIndex(
if ((index < 0) || (index >= listRepPtr->elemCount)) {
*objPtrPtr = NULL;
} else {
- *objPtrPtr = (&listRepPtr->elements)[index];
+ *objPtrPtr = listRepPtr->elements[index];
}
return TCL_OK;
@@ -881,19 +866,20 @@ Tcl_ListObjIndex(
*
* Tcl_ListObjLength --
*
- * This function returns the number of elements in a list object. If the
- * object is not already a list object, an attempt will be made to
- * convert it to one.
+ * Retrieve the number of elements in a list.
*
- * Results:
- * The return value is normally TCL_OK; in this case *intPtr will be set
- * to the integer count of list elements. If listPtr does not refer to a
- * list object and the object can not be converted to one, TCL_ERROR is
- * returned and an error message will be left in the interpreter's result
- * if interp is not NULL.
+ * Value
*
- * Side effects:
- * The possible conversion of the argument object to a list object.
+ * TCL_OK
+ *
+ * A count of list elements is stored at the address provided by
+ * 'intPtr'. If 'listPtr' is not already of type 'tclListPtr', it is
+ * converted.
+ *
+ * TCL_ERROR
+ *
+ * 'listPtr' is not a valid list. An error message will be left in
+ * the interpreter's result if 'interp' is not NULL.
*
*----------------------------------------------------------------------
*/
@@ -903,13 +889,14 @@ int
Tcl_ListObjLength(
Tcl_Interp *interp, /* Used to report errors if not NULL. */
Tcl_Obj *listPtr, /* List object whose #elements to return. */
- int *intPtr) /* The resulting int is stored here. */
+ int *intPtr) /* The resulting length is stored here. */
{
List *listRepPtr;
ListGetInternalRep(listPtr, listRepPtr);
if (listRepPtr == NULL) {
- int result, length;
+ int result;
+ int length;
(void) Tcl_GetStringFromObj(listPtr, &length);
if (length == 0) {
@@ -932,35 +919,36 @@ Tcl_ListObjLength(
*
* Tcl_ListObjReplace --
*
- * This function replaces zero or more elements of the list referenced by
- * listPtr with the objects from an (objc,objv) array. The objc elements
- * of the array referenced by objv replace the count elements in listPtr
- * starting at first.
+ * Replace values in a list.
*
- * If the argument first is zero or negative, it refers to the first
- * element. If first is greater than or equal to the number of elements
- * in the list, then no elements are deleted; the new elements are
- * appended to the list. Count gives the number of elements to replace.
- * If count is zero or negative then no elements are deleted; the new
- * elements are simply inserted before first.
+ * If 'first' is zero or TCL_INDEX_NONE, it refers to the first element. If
+ * 'first' outside the range of elements in the list, no elements are
+ * deleted.
*
- * The argument objv refers to an array of objc pointers to the new
- * elements to be added to listPtr in place of those that were deleted.
- * If objv is NULL, no new elements are added. If listPtr is not a list
- * object, an attempt will be made to convert it to one.
+ * If 'count' is zero or TCL_INDEX_NONE no elements are deleted, and any new
+ * elements are inserted at the beginning of the list.
*
- * Results:
- * The return value is normally TCL_OK. If listPtr does not refer to a
- * list object and can not be converted to one, TCL_ERROR is returned and
- * an error message will be left in the interpreter's result if interp is
- * not NULL.
+ * Value
*
- * Side effects:
- * The ref counts of the objc elements in objv are incremented since the
- * resulting list now refers to them. Similarly, the ref counts for
- * replaced objects are decremented. listPtr is converted, if necessary,
- * to a list object. listPtr's old string representation, if any, is
- * freed.
+ * TCL_OK
+ *
+ * The first 'objc' values of 'objv' replaced 'count' elements in 'listPtr'
+ * starting at 'first'. If 'objc' 0, no new elements are added.
+ *
+ * TCL_ERROR
+ *
+ * 'listPtr' is not a valid list. An error message is left in the
+ * interpreter's result if 'interp' is not NULL.
+ *
+ * Effect
+ *
+ * If 'listPtr' is not of type 'tclListType', it is converted if possible.
+ *
+ * The 'refCount' of each element appended to the list is incremented.
+ * Similarly, the 'refCount' for each replaced element is decremented.
+ *
+ * If 'listPtr' is modified, any previous string representation is
+ * invalidated.
*
*----------------------------------------------------------------------
*/
@@ -977,7 +965,8 @@ Tcl_ListObjReplace(
{
List *listRepPtr;
Tcl_Obj **elemPtrs;
- int needGrow, numElems, numRequired, numAfterLast, start, i, j, isShared;
+ int numElems, numRequired, numAfterLast, start, i, j;
+ int needGrow, isShared;
if (Tcl_IsShared(listPtr)) {
Tcl_Panic("%s called with shared object", "Tcl_ListObjReplace");
@@ -1011,7 +1000,7 @@ Tcl_ListObjReplace(
* Resist any temptation to optimize this case.
*/
- elemPtrs = &listRepPtr->elements;
+ elemPtrs = listRepPtr->elements;
numElems = listRepPtr->elemCount;
if (first < 0) {
@@ -1065,7 +1054,7 @@ Tcl_ListObjReplace(
if (newPtr) {
listRepPtr = newPtr;
ListResetInternalRep(listPtr, listRepPtr);
- elemPtrs = &listRepPtr->elements;
+ elemPtrs = listRepPtr->elements;
listRepPtr->maxElemCount = attempt;
needGrow = numRequired > listRepPtr->maxElemCount;
}
@@ -1140,7 +1129,7 @@ Tcl_ListObjReplace(
ListResetInternalRep(listPtr, listRepPtr);
listRepPtr->refCount++;
- elemPtrs = &listRepPtr->elements;
+ elemPtrs = listRepPtr->elements;
if (isShared) {
/*
@@ -1228,22 +1217,19 @@ Tcl_ListObjReplace(
*
* TclLindexList --
*
- * This procedure handles the 'lindex' command when objc==3.
+ * Implements the 'lindex' command when objc==3.
*
- * Results:
- * Returns a pointer to the object extracted, or NULL if an error
- * occurred. The returned object already includes one reference count for
- * the pointer returned.
+ * Implemented entirely as a wrapper around 'TclLindexFlat'. Reconfigures
+ * the argument format into required form while taking care to manage
+ * shimmering so as to tend to keep the most useful internalreps
+ * and/or avoid the most expensive conversions.
*
- * Side effects:
- * None.
+ * Value
*
- * Notes:
- * This procedure is implemented entirely as a wrapper around
- * TclLindexFlat. All it does is reconfigure the argument format into the
- * form required by TclLindexFlat, while taking care to manage shimmering
- * in such a way that we tend to keep the most useful internalreps and/or
- * avoid the most expensive conversions.
+ * A pointer to the specified element, with its 'refCount' incremented, or
+ * NULL if an error occurred.
+ *
+ * Notes
*
*----------------------------------------------------------------------
*/
@@ -1302,7 +1288,7 @@ TclLindexList(
assert(listRepPtr != NULL);
listPtr = TclLindexFlat(interp, listPtr, listRepPtr->elemCount,
- &listRepPtr->elements);
+ listRepPtr->elements);
Tcl_DecrRefCount(indexListCopy);
return listPtr;
}
@@ -1310,25 +1296,20 @@ TclLindexList(
/*
*----------------------------------------------------------------------
*
- * TclLindexFlat --
+ * TclLindexFlat --
*
- * This procedure is the core of the 'lindex' command, with all index
- * arguments presented as a flat list.
+ * The core of the 'lindex' command, with all index
+ * arguments presented as a flat list.
*
- * Results:
- * Returns a pointer to the object extracted, or NULL if an error
- * occurred. The returned object already includes one reference count for
- * the pointer returned.
+ * Value
*
- * Side effects:
- * None.
+ * A pointer to the object extracted, with its 'refCount' incremented, or
+ * NULL if an error occurred. Thus, the calling code will usually do
+ * something like:
+ *
+ * Tcl_SetObjResult(interp, result);
+ * Tcl_DecrRefCount(result);
*
- * Notes:
- * 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);
*
*----------------------------------------------------------------------
*/
@@ -1404,24 +1385,17 @@ TclLindexFlat(
*
* TclLsetList --
*
- * Core of the 'lset' command when objc == 4. Objv[2] may be either a
+ * The core of [lset] when objc == 4. Objv[2] may be either a
* scalar index or a list of indices.
* It also handles 'lpop' when given a NULL value.
*
- * Results:
- * Returns the new value of the list variable, or NULL if there was an
- * error. The returned object includes one reference count for the
- * pointer returned.
+ * Implemented entirely as a wrapper around 'TclLindexFlat', as described
+ * for 'TclLindexList'.
*
- * Side effects:
- * None.
+ * Value
*
- * Notes:
- * This procedure is implemented entirely as a wrapper around
- * TclLsetFlat. All it does is reconfigure the argument format into the
- * form required by TclLsetFlat, while taking care to manage shimmering
- * in such a way that we tend to keep the most useful internalreps and/or
- * avoid the most expensive conversions.
+ * The new list, with the 'refCount' of 'valuPtr' incremented, or NULL if
+ * there was an error.
*
*----------------------------------------------------------------------
*/
@@ -1486,36 +1460,39 @@ TclLsetList(
* Core engine of the 'lset' command.
* It also handles 'lpop' when given a NULL value.
*
- * Results:
- * Returns the new value of the list variable, or NULL if an error
- * occurred. The returned object includes one reference count for the
- * pointer returned.
+ * Value
*
- * Side effects:
- * 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 function.
- *
- * 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.)
- *
- * Surgery is performed on the unshared list value to produce the result.
- * TclLsetFlat maintains a linked list of Tcl_Obj's whose string
+ * The resulting list
+ *
+ * The 'refCount' of 'valuePtr' is incremented. If 'listPtr' was not
+ * duplicated, its 'refCount' is incremented. The reference count of
+ * an unduplicated object is therefore 2 (one for the returned pointer
+ * and one for the variable that holds it). The reference count of a
+ * duplicate object is 1, reflecting that result is the only active
+ * reference. The caller is expected to store the result in the
+ * variable and decrement its reference count. (INST_STORE_* does
+ * exactly this.)
+ *
+ * NULL
+ *
+ * An error occurred. If 'listPtr' was duplicated, the reference
+ * count on the duplicate is decremented so that it is 0, causing any
+ * memory allocated by this function to be freed.
+ *
+ *
+ * Effect
+ *
+ * On entry, the reference count of 'listPtr' does not reflect any
+ * references held on the stack. The first action of this function is to
+ * determine whether 'listPtr' is shared and to create a duplicate
+ * unshared copy if it is. The reference count of the duplicate is
+ * incremented. At this point, the reference count is 1 in either case so
+ * that the object is considered unshared.
+ *
+ * The unshared list is altered directly to produce the result.
+ * 'TclLsetFlat' maintains a linked list of 'Tcl_Obj' values whose string
* representations must be spoilt by threading via 'ptr2' of the
- * two-pointer internal representation. On entry to TclLsetFlat, the
+ * two-pointer internal representation. On entry to 'TclLsetFlat', the
* values of 'ptr2' are immaterial; on exit, the 'ptr2' field of any
* Tcl_Obj that has been modified is set to NULL.
*
@@ -1531,7 +1508,8 @@ TclLsetFlat(
/* Index args. */
Tcl_Obj *valuePtr) /* Value arg to 'lset' or NULL to 'lpop'. */
{
- int index, result, len;
+ int index, len;
+ int result;
Tcl_Obj *subListPtr, *retValuePtr, *chainPtr;
Tcl_ObjInternalRep *irPtr;
@@ -1724,12 +1702,12 @@ TclLsetFlat(
}
/*
- * Store valuePtr in proper sublist and return. The -1 is to avoid a
- * compiler warning (not a problem because we checked that we have a
- * proper list - or something convertible to one - above).
+ * Store valuePtr in proper sublist and return. The TCL_INDEX_NONE is
+ * to avoid a compiler warning (not a problem because we checked that
+ * we have a proper list - or something convertible to one - above).
*/
- len = -1;
+ len = TCL_INDEX_NONE;
TclListObjLengthM(NULL, subListPtr, &len);
if (valuePtr == NULL) {
Tcl_ListObjReplace(NULL, subListPtr, index, 1, 0, NULL);
@@ -1748,26 +1726,38 @@ TclLsetFlat(
*
* TclListObjSetElement --
*
- * Set a single element of a list to a specified value
+ * 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.
+ * It is the caller's responsibility to invalidate the string
+ * representation of the 'listPtr'.
*
- * Side effects:
- * Tcl_Panic if listPtr designates a shared object. Otherwise, attempts
- * to convert it to a list with a non-shared internal rep. 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.
+ * Value
+ *
+ * TCL_OK
+ *
+ * Success.
+ *
+ * TCL_ERROR
+ *
+ * 'listPtr' does not refer to a list object and cannot be converted
+ * to one. An error message will be left in the interpreter result if
+ * interp is not NULL.
+ *
+ * TCL_ERROR
+ *
+ * An index designates an element outside the range [0..listLength-1],
+ * where 'listLength' is the count of elements in the list object
+ * designated by 'listPtr'. An error message is left in the
+ * interpreter result.
+ *
+ * Effect
+ *
+ * If 'listPtr' designates a shared object, 'Tcl_Panic' is called. If
+ * 'listPtr' is not already of type 'tclListType', it is converted and the
+ * internal representation is unshared. The 'refCount' of the element at
+ * 'index' is decremented and replaced in the list with the 'valuePtr',
+ * whose 'refCount' in turn is incremented.
*
- * It is the caller's responsibility to invalidate the string
- * representation of the object.
*
*----------------------------------------------------------------------
*/
@@ -1797,7 +1787,8 @@ TclListObjSetElement(
ListGetInternalRep(listPtr, listRepPtr);
if (listRepPtr == NULL) {
- int result, length;
+ int result;
+ int length;
(void) Tcl_GetStringFromObj(listPtr, &length);
if (length == 0) {
@@ -1837,7 +1828,7 @@ TclListObjSetElement(
*/
if (listRepPtr->refCount > 1) {
- Tcl_Obj **dst, **src = &listRepPtr->elements;
+ Tcl_Obj **dst, **src = listRepPtr->elements;
List *newPtr = AttemptNewList(NULL, listRepPtr->maxElemCount, NULL);
if (newPtr == NULL) {
@@ -1850,7 +1841,7 @@ TclListObjSetElement(
newPtr->elemCount = elemCount;
newPtr->canonicalFlag = listRepPtr->canonicalFlag;
- dst = &newPtr->elements;
+ dst = newPtr->elements;
while (elemCount--) {
*dst = *src++;
Tcl_IncrRefCount(*dst++);
@@ -1861,7 +1852,7 @@ TclListObjSetElement(
listRepPtr = newPtr;
ListResetInternalRep(listPtr, listRepPtr);
}
- elemPtrs = &listRepPtr->elements;
+ elemPtrs = listRepPtr->elements;
/*
* Add a reference to the new list element.
@@ -1901,13 +1892,11 @@ TclListObjSetElement(
*
* FreeListInternalRep --
*
- * Deallocate the storage associated with a list object's internal
- * representation.
+ * Deallocate the storage associated with the internal representation of a
+ * a list object.
*
- * Results:
- * None.
+ * Effect
*
- * Side effects:
* Frees listPtr's List* internal representation, if no longer shared.
* May decrement the ref counts of element objects, which may free them.
*
@@ -1924,7 +1913,7 @@ FreeListInternalRep(
assert(listRepPtr != NULL);
if (listRepPtr->refCount-- <= 1) {
- Tcl_Obj **elemPtrs = &listRepPtr->elements;
+ Tcl_Obj **elemPtrs = listRepPtr->elements;
int i, numElems = listRepPtr->elemCount;
for (i = 0; i < numElems; i++) {
@@ -1939,14 +1928,12 @@ FreeListInternalRep(
*
* DupListInternalRep --
*
- * Initialize the internal representation of a list Tcl_Obj to share the
+ * Initialize the internal representation of a list 'Tcl_Obj' to share the
* internal representation of an existing list object.
*
- * Results:
- * None.
+ * Effect
*
- * Side effects:
- * The reference count of the List internal rep is incremented.
+ * The 'refCount' of the List internal rep is incremented.
*
*----------------------------------------------------------------------
*/
@@ -1968,16 +1955,20 @@ DupListInternalRep(
*
* SetListFromAny --
*
- * Attempt to generate a list internal form for the Tcl object "objPtr".
+ * Convert any object to a list.
*
- * Results:
- * The return value is TCL_OK or TCL_ERROR. If an error occurs during
- * conversion, an error message is left in the interpreter's result
- * unless "interp" is NULL.
+ * Value
+ *
+ * TCL_OK
+ *
+ * Success. The internal representation of 'objPtr' is set, and the type
+ * of 'objPtr' is 'tclListType'.
+ *
+ * TCL_ERROR
+ *
+ * An error occured during conversion. An error message is left in the
+ * interpreter's result if 'interp' is not NULL.
*
- * Side effects:
- * If no error occurs, a list is stored as "objPtr"s internal
- * representation.
*
*----------------------------------------------------------------------
*/
@@ -2001,7 +1992,8 @@ SetListFromAny(
if (!TclHasStringRep(objPtr) && TclHasInternalRep(objPtr, &tclDictType)) {
Tcl_Obj *keyPtr, *valuePtr;
Tcl_DictSearch search;
- int done, size;
+ int done;
+ int size;
/*
* Create the new list representation. Note that we do not need to do
@@ -2023,7 +2015,7 @@ SetListFromAny(
* Populate the list representation.
*/
- elemPtrs = &listRepPtr->elements;
+ elemPtrs = listRepPtr->elements;
Tcl_DictObjFirst(NULL, objPtr, &search, &keyPtr, &valuePtr, &done);
while (!done) {
*elemPtrs++ = keyPtr;
@@ -2048,7 +2040,7 @@ SetListFromAny(
if (listRepPtr == NULL) {
return TCL_ERROR;
}
- elemPtrs = &listRepPtr->elements;
+ elemPtrs = listRepPtr->elements;
/*
* Each iteration, parse and store a list element.
@@ -2057,12 +2049,13 @@ SetListFromAny(
while (nextElem < limit) {
const char *elemStart;
char *check;
- int elemSize, literal;
+ int elemSize;
+ int literal;
if (TCL_OK != TclFindElement(interp, nextElem, limit - nextElem,
&elemStart, &nextElem, &elemSize, &literal)) {
fail:
- while (--elemPtrs >= &listRepPtr->elements) {
+ while (--elemPtrs >= listRepPtr->elements) {
Tcl_DecrRefCount(*elemPtrs);
}
ckfree(listRepPtr);
@@ -2092,7 +2085,7 @@ SetListFromAny(
Tcl_IncrRefCount(*elemPtrs++);/* Since list now holds ref to it. */
}
- listRepPtr->elemCount = elemPtrs - &listRepPtr->elements;
+ listRepPtr->elemCount = elemPtrs - listRepPtr->elements;
}
/*
@@ -2110,18 +2103,16 @@ SetListFromAny(
*
* UpdateStringOfList --
*
- * Update the string representation for a list object. Note: This
- * function does not invalidate an existing old string rep so storage
- * will be lost if this has not already been done.
+ * Update the string representation for a list object.
*
- * Results:
- * None.
+ * Any previously-exising string representation is not invalidated, so
+ * storage is lost if this has not been taken care of.
*
- * Side effects:
- * The object's string is set to a valid string that results from the
- * list-to-string conversion. This string will be empty if the list has
- * no elements. The list internal representation should not be NULL and
- * we assume it is not NULL.
+ * Effect
+ *
+ * The string representation of 'listPtr' is set to the resulting string.
+ * This string will be empty if the list has no elements. It is assumed
+ * that the list internal representation is not NULL.
*
*----------------------------------------------------------------------
*/
@@ -2174,7 +2165,7 @@ UpdateStringOfList(
flagPtr = (char *)ckalloc(numElems);
}
- elemPtrs = &listRepPtr->elements;
+ elemPtrs = listRepPtr->elements;
for (i = 0; i < numElems; i++) {
flagPtr[i] = (i ? TCL_DONT_QUOTE_HASH : 0);
elem = TclGetStringFromObj(elemPtrs[i], &length);
diff --git a/generic/tclNotify.c b/generic/tclNotify.c
index 8613e98..e17819e 100644
--- a/generic/tclNotify.c
+++ b/generic/tclNotify.c
@@ -96,7 +96,7 @@ TCL_DECLARE_MUTEX(listLock)
*/
static int QueueEvent(ThreadSpecificData *tsdPtr,
- Tcl_Event *evPtr, int flags);
+ Tcl_Event *evPtr, int position);
/*
*----------------------------------------------------------------------
@@ -175,8 +175,7 @@ TclFinalizeNotifier(void)
Tcl_Event *evPtr, *hold;
if (!tsdPtr->initialized) {
- return; /* Notifier not initialized for the current
- * thread. */
+ return; /* Notifier not initialized for the current thread */
}
Tcl_MutexLock(&(tsdPtr->queueMutex));
@@ -310,7 +309,7 @@ Tcl_CreateEventSource(
* checkProc. */
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- EventSource *sourcePtr = (EventSource *) ckalloc(sizeof(EventSource));
+ EventSource *sourcePtr = (EventSource *)ckalloc(sizeof(EventSource));
sourcePtr->setupProc = setupProc;
sourcePtr->checkProc = checkProc;
@@ -392,12 +391,12 @@ Tcl_QueueEvent(
* malloc (ckalloc), and it becomes the
* property of the event queue. It will be
* freed after the event has been handled. */
- int flags) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
- * TCL_QUEUE_MARK, possibly combined with TCL_QUEUE_ALERT_IF_EMPTY. */
+ int position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, TCL_QUEUE_MARK,
+ * possibly combined with TCL_QUEUE_ALERT_IF_EMPTY. */
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- (void) QueueEvent(tsdPtr, evPtr, flags);
+ QueueEvent(tsdPtr, evPtr, position);
}
/*
@@ -424,8 +423,8 @@ Tcl_ThreadQueueEvent(
* malloc (ckalloc), and it becomes the
* property of the event queue. It will be
* freed after the event has been handled. */
- int flags) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
- * TCL_QUEUE_MARK, possibly combined with TCL_QUEUE_ALERT_IF_EMPTY. */
+ int position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, TCL_QUEUE_MARK,
+ * possibly combined with TCL_QUEUE_ALERT_IF_EMPTY. */
{
ThreadSpecificData *tsdPtr;
@@ -444,7 +443,7 @@ Tcl_ThreadQueueEvent(
*/
if (tsdPtr) {
- if (QueueEvent(tsdPtr, evPtr, flags)) {
+ if (QueueEvent(tsdPtr, evPtr, position)) {
Tcl_AlertNotifier(tsdPtr->clientData);
}
} else {
@@ -484,15 +483,14 @@ QueueEvent(
* malloc (ckalloc), and it becomes the
* property of the event queue. It will be
* freed after the event has been handled. */
- int flags)
- /* One of TCL_QUEUE_TAIL_EX,
- * TCL_QUEUE_HEAD_EX, TCL_QUEUE_MARK_EX,
+ int position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, TCL_QUEUE_MARK,
* possibly combined with TCL_QUEUE_ALERT_IF_EMPTY */
{
- int wasEmpty = 0;
-
Tcl_MutexLock(&(tsdPtr->queueMutex));
- if ((flags & 3) == TCL_QUEUE_TAIL) {
+ if (tsdPtr->firstEventPtr != NULL) {
+ position &= ~TCL_QUEUE_ALERT_IF_EMPTY;
+ }
+ if ((position & 3) == TCL_QUEUE_TAIL) {
/*
* Append the event on the end of the queue.
*/
@@ -500,12 +498,11 @@ QueueEvent(
evPtr->nextPtr = NULL;
if (tsdPtr->firstEventPtr == NULL) {
tsdPtr->firstEventPtr = evPtr;
- wasEmpty = (flags & TCL_QUEUE_ALERT_IF_EMPTY) ? 1 : 0;
} else {
tsdPtr->lastEventPtr->nextPtr = evPtr;
}
tsdPtr->lastEventPtr = evPtr;
- } else if ((flags & 3) == TCL_QUEUE_HEAD) {
+ } else if ((position & 3) == TCL_QUEUE_HEAD) {
/*
* Push the event on the head of the queue.
*/
@@ -513,10 +510,9 @@ QueueEvent(
evPtr->nextPtr = tsdPtr->firstEventPtr;
if (tsdPtr->firstEventPtr == NULL) {
tsdPtr->lastEventPtr = evPtr;
- wasEmpty = (flags & TCL_QUEUE_ALERT_IF_EMPTY) ? 1 : 0;
}
tsdPtr->firstEventPtr = evPtr;
- } else if ((flags & 3) == TCL_QUEUE_MARK) {
+ } else if ((position & 3) == TCL_QUEUE_MARK) {
/*
* Insert the event after the current marker event and advance the
* marker to the new event.
@@ -535,7 +531,7 @@ QueueEvent(
}
}
Tcl_MutexUnlock(&(tsdPtr->queueMutex));
- return wasEmpty;
+ return position & TCL_QUEUE_ALERT_IF_EMPTY;
}
/*
diff --git a/generic/tclOO.c b/generic/tclOO.c
index 0cd08d2..5bd687a 100644
--- a/generic/tclOO.c
+++ b/generic/tclOO.c
@@ -391,9 +391,9 @@ InitFoundation(
*/
TclNewLiteralStringObj(namePtr, "new");
- Tcl_NewInstanceMethod(interp, (Tcl_Object) fPtr->classCls->thisPtr,
+ TclNewInstanceMethod(interp, (Tcl_Object) fPtr->classCls->thisPtr,
namePtr /* keeps ref */, 0 /* private */, NULL, NULL);
- fPtr->classCls->constructorPtr = (Method *) Tcl_NewMethod(interp,
+ fPtr->classCls->constructorPtr = (Method *) TclNewMethod(interp,
(Tcl_Class) fPtr->classCls, NULL, 0, &classConstructor, NULL);
/*
@@ -2246,7 +2246,7 @@ CloneObjectMethod(
Tcl_Obj *namePtr)
{
if (mPtr->typePtr == NULL) {
- Tcl_NewInstanceMethod(interp, (Tcl_Object) oPtr, namePtr,
+ TclNewInstanceMethod(interp, (Tcl_Object) oPtr, namePtr,
mPtr->flags & PUBLIC_METHOD, NULL, NULL);
} else if (mPtr->typePtr->cloneProc) {
ClientData newClientData;
@@ -2255,10 +2255,10 @@ CloneObjectMethod(
&newClientData) != TCL_OK) {
return TCL_ERROR;
}
- Tcl_NewInstanceMethod(interp, (Tcl_Object) oPtr, namePtr,
+ TclNewInstanceMethod(interp, (Tcl_Object) oPtr, namePtr,
mPtr->flags & PUBLIC_METHOD, mPtr->typePtr, newClientData);
} else {
- Tcl_NewInstanceMethod(interp, (Tcl_Object) oPtr, namePtr,
+ TclNewInstanceMethod(interp, (Tcl_Object) oPtr, namePtr,
mPtr->flags & PUBLIC_METHOD, mPtr->typePtr, mPtr->clientData);
}
return TCL_OK;
@@ -2275,7 +2275,7 @@ CloneClassMethod(
Method *m2Ptr;
if (mPtr->typePtr == NULL) {
- m2Ptr = (Method *) Tcl_NewMethod(interp, (Tcl_Class) clsPtr,
+ m2Ptr = (Method *) TclNewMethod(interp, (Tcl_Class) clsPtr,
namePtr, mPtr->flags & PUBLIC_METHOD, NULL, NULL);
} else if (mPtr->typePtr->cloneProc) {
ClientData newClientData;
@@ -2284,11 +2284,11 @@ CloneClassMethod(
&newClientData) != TCL_OK) {
return TCL_ERROR;
}
- m2Ptr = (Method *) Tcl_NewMethod(interp, (Tcl_Class) clsPtr,
+ m2Ptr = (Method *) TclNewMethod(interp, (Tcl_Class) clsPtr,
namePtr, mPtr->flags & PUBLIC_METHOD, mPtr->typePtr,
newClientData);
} else {
- m2Ptr = (Method *) Tcl_NewMethod(interp, (Tcl_Class) clsPtr,
+ m2Ptr = (Method *) TclNewMethod(interp, (Tcl_Class) clsPtr,
namePtr, mPtr->flags & PUBLIC_METHOD, mPtr->typePtr,
mPtr->clientData);
}
diff --git a/generic/tclOO.decls b/generic/tclOO.decls
index c6ffccd..c933872 100644
--- a/generic/tclOO.decls
+++ b/generic/tclOO.decls
@@ -135,6 +135,20 @@ declare 30 {
declare 31 {
Tcl_Obj *Tcl_GetObjectClassName(Tcl_Interp *interp, Tcl_Object object)
}
+declare 32 {
+ int Tcl_MethodIsType2(Tcl_Method method, const Tcl_MethodType2 *typePtr,
+ void **clientDataPtr)
+}
+declare 33 {
+ Tcl_Method Tcl_NewInstanceMethod2(Tcl_Interp *interp, Tcl_Object object,
+ Tcl_Obj *nameObj, int flags, const Tcl_MethodType2 *typePtr,
+ void *clientData)
+}
+declare 34 {
+ Tcl_Method Tcl_NewMethod2(Tcl_Interp *interp, Tcl_Class cls,
+ Tcl_Obj *nameObj, int flags, const Tcl_MethodType2 *typePtr,
+ void *clientData)
+}
######################################################################
# Private API, exposed to support advanced OO systems that plug in on top of
diff --git a/generic/tclOO.h b/generic/tclOO.h
index 4a3398f..6f18491 100644
--- a/generic/tclOO.h
+++ b/generic/tclOO.h
@@ -24,8 +24,8 @@
* win/tclooConfig.sh
*/
-#define TCLOO_VERSION "1.2.0"
-#define TCLOO_PATCHLEVEL TCLOO_VERSION
+#define TCLOO_VERSION "1.3"
+#define TCLOO_PATCHLEVEL TCLOO_VERSION ".0"
#include "tcl.h"
@@ -40,7 +40,7 @@ extern "C" {
extern const char *TclOOInitializeStubs(
Tcl_Interp *, const char *version);
#define Tcl_OOInitStubs(interp) \
- TclOOInitializeStubs((interp), TCLOO_VERSION)
+ TclOOInitializeStubs((interp), TCLOO_PATCHLEVEL)
#ifndef USE_TCL_STUBS
# define TclOOInitializeStubs(interp, version) (TCLOO_PATCHLEVEL)
#endif
@@ -62,6 +62,8 @@ typedef struct Tcl_ObjectContext_ *Tcl_ObjectContext;
typedef int (Tcl_MethodCallProc)(void *clientData, Tcl_Interp *interp,
Tcl_ObjectContext objectContext, int objc, Tcl_Obj *const *objv);
+typedef int (Tcl_MethodCallProc2)(void *clientData, Tcl_Interp *interp,
+ Tcl_ObjectContext objectContext, size_t objc, Tcl_Obj *const *objv);
typedef void (Tcl_MethodDeleteProc)(void *clientData);
typedef int (Tcl_CloneProc)(Tcl_Interp *interp, void *oldClientData,
void **newClientData);
@@ -77,7 +79,7 @@ typedef int (Tcl_ObjectMapMethodNameProc)(Tcl_Interp *interp,
typedef struct {
int version; /* Structure version field. Always to be equal
- * to TCL_OO_METHOD_VERSION_CURRENT in
+ * to TCL_OO_METHOD_VERSION_(1|CURRENT) in
* declarations. */
const char *name; /* Name of this type of method, mostly for
* debugging purposes. */
@@ -92,12 +94,31 @@ typedef struct {
* be copied directly. */
} Tcl_MethodType;
+typedef struct {
+ int version; /* Structure version field. Always to be equal
+ * to TCL_OO_METHOD_VERSION_2 in
+ * declarations. */
+ const char *name; /* Name of this type of method, mostly for
+ * debugging purposes. */
+ Tcl_MethodCallProc2 *callProc;
+ /* How to invoke this method. */
+ Tcl_MethodDeleteProc *deleteProc;
+ /* How to delete this method's type-specific
+ * data, or NULL if the type-specific data
+ * does not need deleting. */
+ Tcl_CloneProc *cloneProc; /* How to copy this method's type-specific
+ * data, or NULL if the type-specific data can
+ * be copied directly. */
+} Tcl_MethodType2;
+
/*
* The correct value for the version field of the Tcl_MethodType structure.
* This allows new versions of the structure to be introduced without breaking
* binary compatibility.
*/
+#define TCL_OO_METHOD_VERSION_1 1
+#define TCL_OO_METHOD_VERSION_2 2
#define TCL_OO_METHOD_VERSION_CURRENT 1
/*
diff --git a/generic/tclOOCall.c b/generic/tclOOCall.c
index d265c1a..a9ed6bf 100644
--- a/generic/tclOOCall.c
+++ b/generic/tclOOCall.c
@@ -369,7 +369,11 @@ TclOOInvokeContext(
* Run the method implementation.
*/
- return mPtr->typePtr->callProc(mPtr->clientData, interp,
+ if (mPtr->typePtr->version < TCL_OO_METHOD_VERSION_2) {
+ return (mPtr->typePtr->callProc)(mPtr->clientData, interp,
+ (Tcl_ObjectContext) contextPtr, objc, objv);
+ }
+ return ((Tcl_MethodCallProc2 *)(void *)(mPtr->typePtr->callProc))(mPtr->clientData, interp,
(Tcl_ObjectContext) contextPtr, objc, objv);
}
diff --git a/generic/tclOODecls.h b/generic/tclOODecls.h
index 6ba5d14..13e07ec 100644
--- a/generic/tclOODecls.h
+++ b/generic/tclOODecls.h
@@ -123,6 +123,20 @@ TCLAPI Tcl_Class Tcl_GetClassOfObject(Tcl_Object object);
/* 31 */
TCLAPI Tcl_Obj * Tcl_GetObjectClassName(Tcl_Interp *interp,
Tcl_Object object);
+/* 32 */
+TCLAPI int Tcl_MethodIsType2(Tcl_Method method,
+ const Tcl_MethodType2 *typePtr,
+ void **clientDataPtr);
+/* 33 */
+TCLAPI Tcl_Method Tcl_NewInstanceMethod2(Tcl_Interp *interp,
+ Tcl_Object object, Tcl_Obj *nameObj,
+ int flags, const Tcl_MethodType2 *typePtr,
+ void *clientData);
+/* 34 */
+TCLAPI Tcl_Method Tcl_NewMethod2(Tcl_Interp *interp, Tcl_Class cls,
+ Tcl_Obj *nameObj, int flags,
+ const Tcl_MethodType2 *typePtr,
+ void *clientData);
typedef struct {
const struct TclOOIntStubs *tclOOIntStubs;
@@ -164,6 +178,9 @@ typedef struct TclOOStubs {
int (*tcl_MethodIsPrivate) (Tcl_Method method); /* 29 */
Tcl_Class (*tcl_GetClassOfObject) (Tcl_Object object); /* 30 */
Tcl_Obj * (*tcl_GetObjectClassName) (Tcl_Interp *interp, Tcl_Object object); /* 31 */
+ int (*tcl_MethodIsType2) (Tcl_Method method, const Tcl_MethodType2 *typePtr, void **clientDataPtr); /* 32 */
+ Tcl_Method (*tcl_NewInstanceMethod2) (Tcl_Interp *interp, Tcl_Object object, Tcl_Obj *nameObj, int flags, const Tcl_MethodType2 *typePtr, void *clientData); /* 33 */
+ Tcl_Method (*tcl_NewMethod2) (Tcl_Interp *interp, Tcl_Class cls, Tcl_Obj *nameObj, int flags, const Tcl_MethodType2 *typePtr, void *clientData); /* 34 */
} TclOOStubs;
extern const TclOOStubs *tclOOStubsPtr;
@@ -242,6 +259,12 @@ extern const TclOOStubs *tclOOStubsPtr;
(tclOOStubsPtr->tcl_GetClassOfObject) /* 30 */
#define Tcl_GetObjectClassName \
(tclOOStubsPtr->tcl_GetObjectClassName) /* 31 */
+#define Tcl_MethodIsType2 \
+ (tclOOStubsPtr->tcl_MethodIsType2) /* 32 */
+#define Tcl_NewInstanceMethod2 \
+ (tclOOStubsPtr->tcl_NewInstanceMethod2) /* 33 */
+#define Tcl_NewMethod2 \
+ (tclOOStubsPtr->tcl_NewMethod2) /* 34 */
#endif /* defined(USE_TCLOO_STUBS) */
diff --git a/generic/tclOODefineCmds.c b/generic/tclOODefineCmds.c
index 42c6637..686fd00 100644
--- a/generic/tclOODefineCmds.c
+++ b/generic/tclOODefineCmds.c
@@ -2286,12 +2286,12 @@ TclOODefineSlots(
if (slotObject == NULL) {
continue;
}
- Tcl_NewInstanceMethod(fPtr->interp, slotObject, getName, 0,
+ TclNewInstanceMethod(fPtr->interp, slotObject, getName, 0,
&slotInfoPtr->getterType, NULL);
- Tcl_NewInstanceMethod(fPtr->interp, slotObject, setName, 0,
+ TclNewInstanceMethod(fPtr->interp, slotObject, setName, 0,
&slotInfoPtr->setterType, NULL);
if (slotInfoPtr->resolverType.callProc) {
- Tcl_NewInstanceMethod(fPtr->interp, slotObject, resolveName, 0,
+ TclNewInstanceMethod(fPtr->interp, slotObject, resolveName, 0,
&slotInfoPtr->resolverType, NULL);
}
}
diff --git a/generic/tclOOInt.h b/generic/tclOOInt.h
index 9488271..725c4ce 100644
--- a/generic/tclOOInt.h
+++ b/generic/tclOOInt.h
@@ -235,14 +235,14 @@ typedef struct Object {
* other spots). */
#define FORCE_UNKNOWN 0x10000 /* States that we are *really* looking up the
* unknown method handler at that point. */
-#define HAS_PRIVATE_METHODS 0x20000
- /* Object/class has (or had) private methods,
- * and so shouldn't be cached so
- * aggressively. */
-#define DONT_DELETE 0x40000 /* Inhibit deletion of this object. Used
+#define DONT_DELETE 0x20000 /* Inhibit deletion of this object. Used
* during fundamental object type mutation to
* make sure that the object actually survives
* to the end of the operation. */
+#define HAS_PRIVATE_METHODS 0x40000
+ /* Object/class has (or had) private methods,
+ * and so shouldn't be cached so
+ * aggressively. */
/*
* And the definition of a class. Note that every class also has an associated
@@ -492,6 +492,17 @@ MODULE_SCOPE void TclOOAddToMixinSubs(Class *subPtr, Class *mixinPtr);
MODULE_SCOPE void TclOOAddToSubclasses(Class *subPtr, Class *superPtr);
MODULE_SCOPE Class * TclOOAllocClass(Tcl_Interp *interp,
Object *useThisObj);
+MODULE_SCOPE int TclMethodIsType(Tcl_Method method,
+ const Tcl_MethodType *typePtr,
+ void **clientDataPtr);
+MODULE_SCOPE Tcl_Method TclNewInstanceMethod(Tcl_Interp *interp,
+ Tcl_Object object, Tcl_Obj *nameObj,
+ int flags, const Tcl_MethodType *typePtr,
+ void *clientData);
+MODULE_SCOPE Tcl_Method TclNewMethod(Tcl_Interp *interp, Tcl_Class cls,
+ Tcl_Obj *nameObj, int flags,
+ const Tcl_MethodType *typePtr,
+ void *clientData);
MODULE_SCOPE int TclNRNewObjectInstance(Tcl_Interp *interp,
Tcl_Class cls, const char *nameStr,
const char *nsNameStr, int objc,
diff --git a/generic/tclOOMethod.c b/generic/tclOOMethod.c
index ae1f3bd..73368e4 100644
--- a/generic/tclOOMethod.c
+++ b/generic/tclOOMethod.c
@@ -126,7 +126,7 @@ static const Tcl_MethodType fwdMethodType = {
*/
Tcl_Method
-Tcl_NewInstanceMethod(
+TclNewInstanceMethod(
TCL_UNUSED(Tcl_Interp *),
Tcl_Object object, /* The object that has the method attached to
* it. */
@@ -187,6 +187,50 @@ Tcl_NewInstanceMethod(
oPtr->epoch++;
return (Tcl_Method) mPtr;
}
+Tcl_Method
+Tcl_NewInstanceMethod(
+ TCL_UNUSED(Tcl_Interp *),
+ Tcl_Object object, /* The object that has the method attached to
+ * it. */
+ Tcl_Obj *nameObj, /* The name of the method. May be NULL; if so,
+ * up to caller to manage storage (e.g., when
+ * it is a constructor or destructor). */
+ int flags, /* Whether this is a public method. */
+ const Tcl_MethodType *typePtr,
+ /* The type of method this is, which defines
+ * how to invoke, delete and clone the
+ * method. */
+ void *clientData) /* Some data associated with the particular
+ * method to be created. */
+{
+ if (typePtr->version > TCL_OO_METHOD_VERSION_1) {
+ Tcl_Panic("%s: Wrong version in typePtr->version, should be TCL_OO_METHOD_VERSION_1", "Tcl_NewInstanceMethod");
+ }
+ return TclNewInstanceMethod(NULL, object, nameObj, flags,
+ (const Tcl_MethodType *)typePtr, clientData);
+}
+Tcl_Method
+Tcl_NewInstanceMethod2(
+ TCL_UNUSED(Tcl_Interp *),
+ Tcl_Object object, /* The object that has the method attached to
+ * it. */
+ Tcl_Obj *nameObj, /* The name of the method. May be NULL; if so,
+ * up to caller to manage storage (e.g., when
+ * it is a constructor or destructor). */
+ int flags, /* Whether this is a public method. */
+ const Tcl_MethodType2 *typePtr,
+ /* The type of method this is, which defines
+ * how to invoke, delete and clone the
+ * method. */
+ void *clientData) /* Some data associated with the particular
+ * method to be created. */
+{
+ if (typePtr->version < TCL_OO_METHOD_VERSION_2) {
+ Tcl_Panic("%s: Wrong version in typePtr->version, should be TCL_OO_METHOD_VERSION_2", "Tcl_NewInstanceMethod2");
+ }
+ return TclNewInstanceMethod(NULL, object, nameObj, flags,
+ (const Tcl_MethodType *)typePtr, clientData);
+}
/*
* ----------------------------------------------------------------------
@@ -199,7 +243,7 @@ Tcl_NewInstanceMethod(
*/
Tcl_Method
-Tcl_NewMethod(
+TclNewMethod(
TCL_UNUSED(Tcl_Interp *),
Tcl_Class cls, /* The class to attach the method to. */
Tcl_Obj *nameObj, /* The name of the object. May be NULL (e.g.,
@@ -255,6 +299,48 @@ Tcl_NewMethod(
return (Tcl_Method) mPtr;
}
+
+Tcl_Method
+Tcl_NewMethod(
+ TCL_UNUSED(Tcl_Interp *),
+ Tcl_Class cls, /* The class to attach the method to. */
+ Tcl_Obj *nameObj, /* The name of the object. May be NULL (e.g.,
+ * for constructors or destructors); if so, up
+ * to caller to manage storage. */
+ int flags, /* Whether this is a public method. */
+ const Tcl_MethodType *typePtr,
+ /* The type of method this is, which defines
+ * how to invoke, delete and clone the
+ * method. */
+ void *clientData) /* Some data associated with the particular
+ * method to be created. */
+{
+ if (typePtr->version > TCL_OO_METHOD_VERSION_1) {
+ Tcl_Panic("%s: Wrong version in typePtr->version, should be TCL_OO_METHOD_VERSION_1", "Tcl_NewMethod");
+ }
+ return TclNewMethod(NULL, cls, nameObj, flags, typePtr, clientData);
+}
+
+Tcl_Method
+Tcl_NewMethod2(
+ TCL_UNUSED(Tcl_Interp *),
+ Tcl_Class cls, /* The class to attach the method to. */
+ Tcl_Obj *nameObj, /* The name of the object. May be NULL (e.g.,
+ * for constructors or destructors); if so, up
+ * to caller to manage storage. */
+ int flags, /* Whether this is a public method. */
+ const Tcl_MethodType2 *typePtr,
+ /* The type of method this is, which defines
+ * how to invoke, delete and clone the
+ * method. */
+ void *clientData) /* Some data associated with the particular
+ * method to be created. */
+{
+ if (typePtr->version < TCL_OO_METHOD_VERSION_2) {
+ Tcl_Panic("%s: Wrong version in typePtr->version, should be TCL_OO_METHOD_VERSION_2", "Tcl_NewMethod2");
+ }
+ return TclNewMethod(NULL, cls, nameObj, flags, (const Tcl_MethodType *)typePtr, clientData);
+}
/*
* ----------------------------------------------------------------------
@@ -304,7 +390,7 @@ TclOONewBasicMethod(
Tcl_Obj *namePtr = Tcl_NewStringObj(dcm->name, -1);
Tcl_IncrRefCount(namePtr);
- Tcl_NewMethod(interp, (Tcl_Class) clsPtr, namePtr,
+ TclNewMethod(interp, (Tcl_Class) clsPtr, namePtr,
(dcm->isPublic ? PUBLIC_METHOD : 0), &dcm->definition, NULL);
Tcl_DecrRefCount(namePtr);
}
@@ -529,7 +615,7 @@ TclOOMakeProcInstanceMethod(
}
}
- return Tcl_NewInstanceMethod(interp, (Tcl_Object) oPtr, nameObj, flags,
+ return TclNewInstanceMethod(interp, (Tcl_Object) oPtr, nameObj, flags,
typePtr, clientData);
}
@@ -642,7 +728,7 @@ TclOOMakeProcMethod(
}
}
- return Tcl_NewMethod(interp, (Tcl_Class) clsPtr, nameObj, flags, typePtr,
+ return TclNewMethod(interp, (Tcl_Class) clsPtr, nameObj, flags, typePtr,
clientData);
}
@@ -1402,7 +1488,7 @@ TclOONewForwardInstanceMethod(
fmPtr = (ForwardMethod *)ckalloc(sizeof(ForwardMethod));
fmPtr->prefixObj = prefixObj;
Tcl_IncrRefCount(prefixObj);
- return (Method *) Tcl_NewInstanceMethod(interp, (Tcl_Object) oPtr,
+ return (Method *) TclNewInstanceMethod(interp, (Tcl_Object) oPtr,
nameObj, flags, &fwdMethodType, fmPtr);
}
@@ -1441,7 +1527,7 @@ TclOONewForwardMethod(
fmPtr = (ForwardMethod *)ckalloc(sizeof(ForwardMethod));
fmPtr->prefixObj = prefixObj;
Tcl_IncrRefCount(prefixObj);
- return (Method *) Tcl_NewMethod(interp, (Tcl_Class) clsPtr, nameObj,
+ return (Method *) TclNewMethod(interp, (Tcl_Class) clsPtr, nameObj,
flags, &fwdMethodType, fmPtr);
}
@@ -1672,6 +1758,23 @@ Tcl_MethodName(
}
int
+TclMethodIsType(
+ Tcl_Method method,
+ const Tcl_MethodType *typePtr,
+ void **clientDataPtr)
+{
+ Method *mPtr = (Method *) method;
+
+ if (mPtr->typePtr == typePtr) {
+ if (clientDataPtr != NULL) {
+ *clientDataPtr = mPtr->clientData;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int
Tcl_MethodIsType(
Tcl_Method method,
const Tcl_MethodType *typePtr,
@@ -1679,6 +1782,9 @@ Tcl_MethodIsType(
{
Method *mPtr = (Method *) method;
+ if (typePtr->version > TCL_OO_METHOD_VERSION_1) {
+ Tcl_Panic("%s: Wrong version in typePtr->version, should be TCL_OO_METHOD_VERSION_1", "Tcl_MethodIsType");
+ }
if (mPtr->typePtr == typePtr) {
if (clientDataPtr != NULL) {
*clientDataPtr = mPtr->clientData;
@@ -1689,6 +1795,26 @@ Tcl_MethodIsType(
}
int
+Tcl_MethodIsType2(
+ Tcl_Method method,
+ const Tcl_MethodType2 *typePtr,
+ void **clientDataPtr)
+{
+ Method *mPtr = (Method *) method;
+
+ if (typePtr->version < TCL_OO_METHOD_VERSION_2) {
+ Tcl_Panic("%s: Wrong version in typePtr->version, should be TCL_OO_METHOD_VERSION_2", "Tcl_MethodIsType2");
+ }
+ if (mPtr->typePtr == (const Tcl_MethodType *)typePtr) {
+ if (clientDataPtr != NULL) {
+ *clientDataPtr = mPtr->clientData;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int
Tcl_MethodIsPublic(
Tcl_Method method)
{
diff --git a/generic/tclOOStubInit.c b/generic/tclOOStubInit.c
index b9034f0..7b653cb 100644
--- a/generic/tclOOStubInit.c
+++ b/generic/tclOOStubInit.c
@@ -76,6 +76,9 @@ const TclOOStubs tclOOStubs = {
Tcl_MethodIsPrivate, /* 29 */
Tcl_GetClassOfObject, /* 30 */
Tcl_GetObjectClassName, /* 31 */
+ Tcl_MethodIsType2, /* 32 */
+ Tcl_NewInstanceMethod2, /* 33 */
+ Tcl_NewMethod2, /* 34 */
};
/* !END!: Do not edit above this line. */
diff --git a/generic/tclProc.c b/generic/tclProc.c
index 17635e7..e6b1372 100644
--- a/generic/tclProc.c
+++ b/generic/tclProc.c
@@ -1587,12 +1587,15 @@ TclPushProcCallFrame(
* is up-to-date), the namespace must match (so variable handling
* is right) and the resolverEpoch must match (so that new shadowed
* commands and/or resolver changes are considered).
+ * Ensure the ByteCode's procPtr is the same (or it's precompiled).
*/
if (((Interp *) *codePtr->interpHandle != iPtr)
|| (codePtr->compileEpoch != iPtr->compileEpoch)
|| (codePtr->nsPtr != nsPtr)
- || (codePtr->nsEpoch != nsPtr->resolverEpoch)) {
+ || (codePtr->nsEpoch != nsPtr->resolverEpoch)
+ || ((codePtr->procPtr != procPtr) && procPtr->bodyPtr->bytes)
+ ) {
goto doCompilation;
}
} else {
@@ -1932,6 +1935,7 @@ TclProcCompileProc(
* procPtr->numCompiledLocals if new local variables are found while
* compiling.
*
+ * Ensure the ByteCode's procPtr is the same (or it is pure precompiled).
* Precompiled procedure bodies, however, are immutable and therefore they
* are not recompiled, even if things have changed.
*/
@@ -1940,7 +1944,9 @@ TclProcCompileProc(
if (((Interp *) *codePtr->interpHandle == iPtr)
&& (codePtr->compileEpoch == iPtr->compileEpoch)
&& (codePtr->nsPtr == nsPtr)
- && (codePtr->nsEpoch == nsPtr->resolverEpoch)) {
+ && (codePtr->nsEpoch == nsPtr->resolverEpoch)
+ && ((codePtr->procPtr == procPtr) || !bodyPtr->bytes)
+ ) {
return TCL_OK;
}
@@ -2155,6 +2161,13 @@ TclProcCleanupProc(
Interp *iPtr = procPtr->iPtr;
if (bodyPtr != NULL) {
+ /* procPtr is stored in body's ByteCode, so ensure to reset it. */
+ ByteCode *codePtr;
+
+ ByteCodeGetInternalRep(bodyPtr, &tclByteCodeType, codePtr);
+ if (codePtr != NULL && codePtr->procPtr == procPtr) {
+ codePtr->procPtr = NULL;
+ }
Tcl_DecrRefCount(bodyPtr);
}
for (localPtr = procPtr->firstLocalPtr; localPtr != NULL; ) {
diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c
index cda840d..3b40f96 100644
--- a/generic/tclStrToD.c
+++ b/generic/tclStrToD.c
@@ -49,44 +49,43 @@
* file exists only on Linux; it is missing on Cygwin and MinGW. Most gcc-isms
* and ix86-isms are factored out here.
*/
-
-#if defined(__GNUC__)
+# if defined(__GNUC__)
typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__HI__)));
-#define _FPU_GETCW(cw) __asm__ __volatile__ ("fnstcw %0" : "=m" (*&cw))
-#define _FPU_SETCW(cw) __asm__ __volatile__ ("fldcw %0" : : "m" (*&cw))
-# define FPU_IEEE_ROUNDING 0x027F
-# define ADJUST_FPU_CONTROL_WORD
-#define TCL_IEEE_DOUBLE_ROUNDING \
+# define _FPU_GETCW(cw) __asm__ __volatile__ ("fnstcw %0" : "=m" (*&cw))
+# define _FPU_SETCW(cw) __asm__ __volatile__ ("fldcw %0" : : "m" (*&cw))
+# define FPU_IEEE_ROUNDING 0x027F
+# define ADJUST_FPU_CONTROL_WORD
+# define TCL_IEEE_DOUBLE_ROUNDING_DECL \
fpu_control_t roundTo53Bits = FPU_IEEE_ROUNDING; \
- fpu_control_t oldRoundingMode; \
+ fpu_control_t oldRoundingMode;
+# define TCL_IEEE_DOUBLE_ROUNDING \
_FPU_GETCW(oldRoundingMode); \
_FPU_SETCW(roundTo53Bits)
-#define TCL_DEFAULT_DOUBLE_ROUNDING \
+# define TCL_DEFAULT_DOUBLE_ROUNDING \
_FPU_SETCW(oldRoundingMode)
/*
* Sun ProC needs sunmath for rounding control on x86 like gcc above.
*/
-#elif defined(__sun)
-#include <sunmath.h>
-#define TCL_IEEE_DOUBLE_ROUNDING \
+# elif defined(__sun)
+# include <sunmath.h>
+# define TCL_IEEE_DOUBLE_ROUNDING_DECL
+# define TCL_IEEE_DOUBLE_ROUNDING \
ieee_flags("set","precision","double",NULL)
-#define TCL_DEFAULT_DOUBLE_ROUNDING \
+# define TCL_DEFAULT_DOUBLE_ROUNDING \
ieee_flags("clear","precision",NULL,NULL)
+# endif
+#endif
/*
* Other platforms are assumed to always operate in full IEEE mode, so we make
* the macros to go in and out of that mode do nothing.
*/
-
-#else /* !__GNUC__ && !__sun */
-#define TCL_IEEE_DOUBLE_ROUNDING ((void) 0)
-#define TCL_DEFAULT_DOUBLE_ROUNDING ((void) 0)
-#endif
-#else /* !__i386 */
-#define TCL_IEEE_DOUBLE_ROUNDING ((void) 0)
-#define TCL_DEFAULT_DOUBLE_ROUNDING ((void) 0)
+#ifndef TCL_IEEE_DOUBLE_ROUNDING /* !__i386 || (!__GNUC__ && !__sun) */
+# define TCL_IEEE_DOUBLE_ROUNDING_DECL
+# define TCL_IEEE_DOUBLE_ROUNDING ((void) 0)
+# define TCL_DEFAULT_DOUBLE_ROUNDING ((void) 0)
#endif
/*
@@ -1273,7 +1272,6 @@ TclParseNumber(
acceptPoint = p;
acceptLen = len;
goto endgame;
-
}
p++;
len--;
@@ -1746,7 +1744,8 @@ MakeLowPrecisionDouble(
int numSigDigs, /* Number of digits in the significand */
long exponent) /* Power of ten */
{
- double retval; /* Value of the number. */
+ TCL_IEEE_DOUBLE_ROUNDING_DECL
+
mp_int significandBig; /* Significand expressed as a bignum. */
/*
@@ -1754,18 +1753,25 @@ MakeLowPrecisionDouble(
* This causes the result of double-precision calculations to be rounded
* twice: once to the precision of double-extended and then again to the
* precision of double. Double-rounding introduces gratuitous errors of 1
- * ulp, so we need to change rounding mode to 53-bits.
+ * ulp, so we need to change rounding mode to 53-bits. We also make
+ * 'retval' volatile, so that it doesn't get promoted to a register.
*/
-
- TCL_IEEE_DOUBLE_ROUNDING;
+ volatile double retval; /* Value of the number. */
/*
- * Test for the easy cases.
+ * Test for zero significand, which requires explicit construction
+ * of -0.0. (Unary minus returns a positive zero.)
*/
-
if (significand == 0) {
return copysign(0.0, -signum);
}
+
+ /*
+ * Set the FP control word for 53 bits, WARNING: It must be reset
+ * before returning.
+ */
+ TCL_IEEE_DOUBLE_ROUNDING;
+
if (numSigDigs <= QUICK_MAX) {
if (exponent >= 0) {
if (exponent <= mmaxpow) {
@@ -1865,7 +1871,8 @@ MakeHighPrecisionDouble(
int numSigDigs, /* Number of significant digits */
long exponent) /* Power of 10 by which to multiply */
{
- double retval;
+ TCL_IEEE_DOUBLE_ROUNDING_DECL
+
int machexp = 0; /* Machine exponent of a power of 10. */
/*
@@ -1873,19 +1880,30 @@ MakeHighPrecisionDouble(
* This causes the result of double-precision calculations to be rounded
* twice: once to the precision of double-extended and then again to the
* precision of double. Double-rounding introduces gratuitous errors of 1
- * ulp, so we need to change rounding mode to 53-bits.
+ * ulp, so we need to change rounding mode to 53-bits. We also make
+ * 'retval' volatile to make sure that it doesn't get promoted to a
+ * register.
*/
-
- TCL_IEEE_DOUBLE_ROUNDING;
+ volatile double retval;
/*
- * Quick checks for zero, and over/underflow. Be careful to avoid
- * integer overflow when calculating with 'exponent'.
+ * A zero significand requires explicit construction of -0.0.
+ * (Unary minus returns positive zero.)
*/
-
if (mp_iszero(significand)) {
return copysign(0.0, -signum);
}
+
+ /*
+ * Set the 53-bit rounding mode. WARNING: It must be reset before
+ * returning.
+ */
+ TCL_IEEE_DOUBLE_ROUNDING;
+
+ /*
+ * Make quick checks for over/underflow. Be careful to avoid
+ * integer overflow when calculating with 'exponent'.
+ */
if (exponent >= 0 && exponent-1 > maxDigits-numSigDigs) {
retval = HUGE_VAL;
goto returnValue;
diff --git a/generic/tclStringObj.c b/generic/tclStringObj.c
index 86b3937..7ce1cdc 100644
--- a/generic/tclStringObj.c
+++ b/generic/tclStringObj.c
@@ -68,6 +68,9 @@ static int SetStringFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
static void SetUnicodeObj(Tcl_Obj *objPtr,
const Tcl_UniChar *unicode, int numChars);
static int UnicodeLength(const Tcl_UniChar *unicode);
+#if !defined(TCL_NO_DEPRECATED)
+static int UTF16Length(const unsigned short *unicode);
+#endif
static void UpdateStringOfString(Tcl_Obj *objPtr);
#if (TCL_UTF_MAX) > 3 && !defined(TCL_NO_DEPRECATED)
static void DupUTF16StringInternalRep(Tcl_Obj *objPtr,
@@ -562,6 +565,10 @@ Tcl_NewUnicodeObj(
TclNewObj(objPtr);
TclInvalidateStringRep(objPtr);
+ if (numChars < 0) {
+ numChars = UTF16Length(unicode);
+ }
+
String *stringPtr = (String *)ckalloc((offsetof(String, unicode)
+ sizeof(unsigned short)) + numChars * sizeof(unsigned short));
memcpy(stringPtr->unicode, unicode, numChars * sizeof(unsigned short));
@@ -974,6 +981,7 @@ Tcl_GetUnicodeFromObj(
}
#endif
+#if !defined(TCL_NO_DEPRECATED)
unsigned short *
TclGetUnicodeFromObj(
Tcl_Obj *objPtr, /* The object to find the unicode string
@@ -984,7 +992,11 @@ TclGetUnicodeFromObj(
{
String *stringPtr;
+#if TCL_UTF_MAX > 3
+ SetUTF16StringFromAny(NULL, objPtr);
+#else
SetStringFromAny(NULL, objPtr);
+#endif
stringPtr = GET_STRING(objPtr);
if (lengthPtr != NULL) {
@@ -992,6 +1004,7 @@ TclGetUnicodeFromObj(
}
return stringPtr->unicode;
}
+#endif
/*
*----------------------------------------------------------------------
@@ -1451,14 +1464,7 @@ Tcl_SetUnicodeObj(
String *stringPtr;
if (numChars < 0) {
- numChars = 0;
-
- if (unicode) {
- while (numChars >= 0 && unicode[numChars] != 0) {
- numChars++;
- }
- }
- stringCheckLimits(numChars);
+ numChars = UTF16Length(unicode);
}
/*
@@ -1479,6 +1485,21 @@ Tcl_SetUnicodeObj(
TclInvalidateStringRep(objPtr);
stringPtr->allocated = numChars;
}
+
+static int
+UTF16Length(
+ const unsigned short *ucs2Ptr)
+{
+ int numChars = 0;
+
+ if (ucs2Ptr) {
+ while (numChars >= 0 && ucs2Ptr[numChars] != 0) {
+ numChars++;
+ }
+ }
+ stringCheckLimits(numChars);
+ return numChars;
+}
#endif
static int
@@ -1723,7 +1744,7 @@ Tcl_AppendUnicodeToObj(
return;
}
- SetStringFromAny(NULL, objPtr);
+ SetUTF16StringFromAny(NULL, objPtr);
stringPtr = GET_STRING(objPtr);
stringPtr = stringAttemptRealloc(stringPtr, stringPtr->numChars + length);
memcpy(&stringPtr->unicode[stringPtr->numChars], unicode, length);
diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c
index 0052682..4941348 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -96,6 +96,7 @@ static void uniCodePanic(void) {
}
# define Tcl_GetUnicode (unsigned short *(*)(Tcl_Obj *))(void *)uniCodePanic
# define Tcl_GetUnicodeFromObj (unsigned short *(*)(Tcl_Obj *, int *))(void *)uniCodePanic
+# define TclGetUnicodeFromObj (unsigned short *(*)(Tcl_Obj *, size_t *))(void *)uniCodePanic
# define Tcl_NewUnicodeObj (Tcl_Obj *(*)(const unsigned short *, int))(void *)uniCodePanic
# define Tcl_SetUnicodeObj (void(*)(Tcl_Obj *, const unsigned short *, int))(void *)uniCodePanic
# define Tcl_AppendUnicodeToObj (void(*)(Tcl_Obj *, const unsigned short *, int))(void *)uniCodePanic
diff --git a/generic/tclTest.c b/generic/tclTest.c
index 5d65b36..3db70fc 100644
--- a/generic/tclTest.c
+++ b/generic/tclTest.c
@@ -222,6 +222,7 @@ static Tcl_ObjCmdProc2 TestbytestringObjCmd;
static Tcl_ObjCmdProc2 TestsetbytearraylengthObjCmd;
static Tcl_ObjCmdProc2 TestpurebytesobjObjCmd;
static Tcl_ObjCmdProc2 TeststringbytesObjCmd;
+static Tcl_ObjCmdProc Testutf16stringObjCmd;
static Tcl_CmdProc TestcmdinfoCmd;
static Tcl_CmdProc TestcmdtokenCmd;
static Tcl_CmdProc TestcmdtraceCmd;
@@ -341,6 +342,7 @@ static Tcl_ObjCmdProc2 TestInterpResolverCmd;
#if defined(HAVE_CPUID) && !defined(MAC_OSX_TCL)
static Tcl_ObjCmdProc2 TestcpuidCmd;
#endif
+static Tcl_ObjCmdProc TestApplyLambdaObjCmd;
static const Tcl_Filesystem testReportingFilesystem = {
"reporting",
@@ -560,6 +562,7 @@ Tcltest_Init(
Tcl_CreateObjCommand2(interp, "testsetbytearraylength", TestsetbytearraylengthObjCmd, NULL, NULL);
Tcl_CreateObjCommand2(interp, "testbytestring", TestbytestringObjCmd, NULL, NULL);
Tcl_CreateObjCommand2(interp, "teststringbytes", TeststringbytesObjCmd, NULL, NULL);
+ Tcl_CreateObjCommand2(interp, "testutf16string", Testutf16stringObjCmd, NULL, NULL);
Tcl_CreateObjCommand2(interp, "testwrongnumargs", TestWrongNumArgsObjCmd,
NULL, NULL);
Tcl_CreateObjCommand2(interp, "testfilesystem", TestFilesystemObjCmd,
@@ -713,6 +716,8 @@ Tcltest_Init(
NULL, NULL);
Tcl_CreateObjCommand2(interp, "testsetencpath", TestsetencpathObjCmd,
NULL, NULL);
+ Tcl_CreateObjCommand(interp, "testapplylambda", TestApplyLambdaObjCmd,
+ NULL, NULL);
if (TclObjTest_Init(interp) != TCL_OK) {
return TCL_ERROR;
@@ -1114,10 +1119,6 @@ TestcmdinfoCmd(
info.clientData = (void *) "new_command_data";
info.objProc = NULL;
info.objClientData = NULL;
-#if TCL_MAJOR_VERSION > 8 || defined(TCL_NO_DEPRECATED)
- info.objProc2 = NULL;
- info.objClientData2 = NULL;
-#endif
info.deleteProc = CmdDelProc2;
info.deleteData = (void *) "new_delete_data";
if (Tcl_SetCommandInfo(interp, argv[2], &info) == 0) {
@@ -5185,6 +5186,43 @@ TestbytestringObjCmd(
/*
*----------------------------------------------------------------------
*
+ * Testutf16stringObjCmd --
+ *
+ * This specifically tests the Tcl_GetUnicode and Tcl_NewUnicodeObj
+ * C functions which broke in Tcl 8.7 and were undetected by the
+ * existing test suite. Bug [b79df322a9]
+ *
+ * Results:
+ * Returns the TCL_OK result code.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+Testutf16stringObjCmd(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp, /* Current interpreter. */
+ size_t objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* The argument objects. */
+{
+ const unsigned short *p;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "string");
+ return TCL_ERROR;
+ }
+
+ p = Tcl_GetUnicode(objv[1]);
+ Tcl_SetObjResult(interp, Tcl_NewUnicodeObj(p, -1));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TestsetCmd --
*
* Implements the "testset{err,noerr}" cmds that are used when testing
@@ -8091,7 +8129,85 @@ TestInterpResolverCmd(
}
return TCL_OK;
}
-
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * TestApplyLambdaObjCmd --
+ *
+ * Implements the Tcl command testapplylambda. This tests the apply
+ * implementation handling of a lambda where the lambda has a list
+ * internal representation where the second element's internal
+ * representation is already a byte code object.
+ *
+ * Results:
+ * TCL_OK - Success. Caller should check result is 42
+ * TCL_ERROR - Error.
+ *
+ * Side effects:
+ * In the presence of the apply bug, may panic. Otherwise
+ * Interpreter result holds result or error message.
+ *
+ *------------------------------------------------------------------------
+ */
+int TestApplyLambdaObjCmd (
+ TCL_UNUSED(void*),
+ Tcl_Interp *interp, /* Current interpreter. */
+ TCL_UNUSED(int), /* objc. */
+ TCL_UNUSED(Tcl_Obj *const *)) /* objv. */
+{
+ Tcl_Obj *lambdaObjs[2];
+ Tcl_Obj *evalObjs[2];
+ Tcl_Obj *lambdaObj;
+ int result;
+
+ /* Create a lambda {{} {set a 42}} */
+ lambdaObjs[0] = Tcl_NewObj(); /* No parameters */
+ lambdaObjs[1] = Tcl_NewStringObj("set a 42", -1); /* Body */
+ lambdaObj = Tcl_NewListObj(2, lambdaObjs);
+ Tcl_IncrRefCount(lambdaObj);
+
+ /* Create the command "apply {{} {set a 42}" */
+ evalObjs[0] = Tcl_NewStringObj("apply", -1);
+ Tcl_IncrRefCount(evalObjs[0]);
+ /*
+ * NOTE: IMPORTANT TO EXHIBIT THE BUG. We duplicate the lambda because
+ * it will get shimmered to a Lambda internal representation but we
+ * want to hold on to our list representation.
+ */
+ evalObjs[1] = Tcl_DuplicateObj(lambdaObj);
+ Tcl_IncrRefCount(evalObjs[1]);
+
+ /* Evaluate it */
+ result = Tcl_EvalObjv(interp, 2, evalObjs, TCL_EVAL_GLOBAL);
+ if (result != TCL_OK) {
+ Tcl_DecrRefCount(evalObjs[0]);
+ Tcl_DecrRefCount(evalObjs[1]);
+ return result;
+ }
+ /*
+ * So far so good. At this point,
+ * - evalObjs[1] has an internal representation of Lambda
+ * - lambdaObj[1] ({set a 42}) has been shimmered to
+ * an internal representation of ByteCode.
+ */
+ Tcl_DecrRefCount(evalObjs[1]); /* Don't need this anymore */
+ /*
+ * The bug trigger. Repeating the command but:
+ * - we are calling apply with a lambda that is a list (as BEFORE),
+ * BUT
+ * - The body of the lambda (lambdaObjs[1]) ALREADY has internal
+ * representation of ByteCode and thus will not be compiled again
+ */
+ evalObjs[1] = lambdaObj; /* lambdaObj already has a ref count so
+ no need for IncrRef */
+ result = Tcl_EvalObjv(interp, 2, evalObjs, TCL_EVAL_GLOBAL);
+ Tcl_DecrRefCount(evalObjs[0]);
+ Tcl_DecrRefCount(lambdaObj);
+
+ return result;
+}
+
/*
* Local Variables:
* mode: c
@@ -8101,3 +8217,4 @@ TestInterpResolverCmd(
* indent-tabs-mode: nil
* End:
*/
+
diff --git a/generic/tclUtf.c b/generic/tclUtf.c
index 82adf65..87216c2 100644
--- a/generic/tclUtf.c
+++ b/generic/tclUtf.c
@@ -495,8 +495,7 @@ Tcl_UtfToUniChar(
* A three-byte-character lead-byte not followed by two trail-bytes
* represents itself.
*/
- }
- else if (byte < 0xF5) {
+ } else if (byte < 0xF5) {
if (((src[1] & 0xC0) == 0x80) && ((src[2] & 0xC0) == 0x80) && ((src[3] & 0xC0) == 0x80)) {
/*
* Four-byte-character lead byte followed by three trail bytes.
@@ -591,8 +590,7 @@ Tcl_UtfToChar16(
* A three-byte-character lead-byte not followed by two trail-bytes
* represents itself.
*/
- }
- else if (byte < 0xF5) {
+ } else if (byte < 0xF5) {
if (((src[1] & 0xC0) == 0x80) && ((src[2] & 0xC0) == 0x80)) {
/*
* Four-byte-character lead byte followed by at least two trail bytes.
diff --git a/library/manifest.txt b/library/manifest.txt
index 6b70b24..b425920 100644
--- a/library/manifest.txt
+++ b/library/manifest.txt
@@ -12,7 +12,7 @@ apply {{dir} {
0 tcl::idna 1.0.1 {cookiejar idna.tcl}
0 platform 1.0.18 {platform platform.tcl}
0 platform::shell 1.1.4 {platform shell.tcl}
- 1 tcltest 2.5.4 {tcltest tcltest.tcl}
+ 1 tcltest 2.5.5 {tcltest tcltest.tcl}
} {
if {$isafe && !$safe} continue
package ifneeded $package $version [list source [file join $dir {*}$file]]
diff --git a/library/tcltest/pkgIndex.tcl b/library/tcltest/pkgIndex.tcl
index da78df0..18b05e5 100644
--- a/library/tcltest/pkgIndex.tcl
+++ b/library/tcltest/pkgIndex.tcl
@@ -9,4 +9,4 @@
# full path name of this file's directory.
if {![package vsatisfies [package provide Tcl] 8.5-]} {return}
-package ifneeded tcltest 2.5.4 [list source [file join $dir tcltest.tcl]]
+package ifneeded tcltest 2.5.5 [list source [file join $dir tcltest.tcl]]
diff --git a/library/tcltest/tcltest.tcl b/library/tcltest/tcltest.tcl
index 72c7b94..7344f9f 100644
--- a/library/tcltest/tcltest.tcl
+++ b/library/tcltest/tcltest.tcl
@@ -22,7 +22,7 @@ namespace eval tcltest {
# When the version number changes, be sure to update the pkgIndex.tcl file,
# and the install directory in the Makefiles. When the minor version
# changes (new feature) be sure to update the man page as well.
- variable Version 2.5.4
+ variable Version 2.5.5
# Compatibility support for dumb variables defined in tcltest 1
# Do not use these. Call [package provide Tcl] and [info patchlevel]
@@ -2141,7 +2141,7 @@ proc tcltest::test {name description args} {
if {[IsVerbose msec] || [IsVerbose usec]} {
set t [expr {[clock microseconds] - $timeStart}]
if {[IsVerbose usec]} {
- puts [outputChannel] "++++ $name took $t μs"
+ puts [outputChannel] "++++ $name took $t \xB5s"
}
if {[IsVerbose msec]} {
puts [outputChannel] "++++ $name took [expr {round($t/1000.)}] ms"
diff --git a/macosx/tclMacOSXFCmd.c b/macosx/tclMacOSXFCmd.c
index 1717c3c..02e57f1 100644
--- a/macosx/tclMacOSXFCmd.c
+++ b/macosx/tclMacOSXFCmd.c
@@ -344,8 +344,8 @@ TclMacOSXSetFileAttribute(
*/
Tcl_DStringInit(&ds);
- Tcl_DStringAppend(&ds, native, -1);
- Tcl_DStringAppend(&ds, _PATH_RSRCFORKSPEC, -1);
+ Tcl_DStringAppend(&ds, native, TCL_INDEX_NONE);
+ Tcl_DStringAppend(&ds, _PATH_RSRCFORKSPEC, TCL_INDEX_NONE);
result = truncate(Tcl_DStringValue(&ds), 0);
if (result != 0) {
@@ -459,11 +459,11 @@ TclMacOSXCopyFileAttributes(
*/
Tcl_DStringInit(&srcBuf);
- Tcl_DStringAppend(&srcBuf, src, -1);
- Tcl_DStringAppend(&srcBuf, _PATH_RSRCFORKSPEC, -1);
+ Tcl_DStringAppend(&srcBuf, src, TCL_INDEX_NONE);
+ Tcl_DStringAppend(&srcBuf, _PATH_RSRCFORKSPEC, TCL_INDEX_NONE);
Tcl_DStringInit(&dstBuf);
- Tcl_DStringAppend(&dstBuf, dst, -1);
- Tcl_DStringAppend(&dstBuf, _PATH_RSRCFORKSPEC, -1);
+ Tcl_DStringAppend(&dstBuf, dst, TCL_INDEX_NONE);
+ Tcl_DStringAppend(&dstBuf, _PATH_RSRCFORKSPEC, TCL_INDEX_NONE);
/*
* Do the copy.
diff --git a/tests/apply.test b/tests/apply.test
index e2be172..a5f1f8f 100644
--- a/tests/apply.test
+++ b/tests/apply.test
@@ -16,12 +16,16 @@ if {"::tcltest" ni [namespace children]} {
package require tcltest 2.5
namespace import -force ::tcltest::*
}
+::tcltest::loadTestedCommands
+catch [list package require -exact tcl::test [info patchlevel]]
if {[info commands ::apply] eq {}} {
return
}
testConstraint memory [llength [info commands memory]]
+testConstraint applylambda [llength [info commands testapplylambda]]
+
# Tests for wrong number of arguments
@@ -306,6 +310,13 @@ test apply-9.3 {leaking internal rep} -setup {
unset -nocomplain end i x tmp leakedBytes
} -result 0
+# Tests for specific bugs
+test apply-10.1 {Test for precompiled bytecode body} -constraints {
+ applylambda
+} -body {
+ testapplylambda
+} -result 42
+
# Tests for the avoidance of recompilation
# cleanup
diff --git a/tests/io.test b/tests/io.test
index f07fa8d..6314ace 100644
--- a/tests/io.test
+++ b/tests/io.test
@@ -336,6 +336,15 @@ test io-3.8 {WriteChars: reset sawLF after each buffer} {
close $f
lappend x [contents $path(test1)]
} [list "abcdefg\nhijklmno" "abcdefg\nhijklmnopqrstuvwxyz"]
+test io-3.9 {Write: flush line-buffered channels when crlf is split over two buffers} -body {
+ # https://core.tcl-lang.org/tcllib/tktedit?name=c9d8a52fe
+ set f [open $path(test1) w]
+ fconfigure $f -buffering line -translation crlf -buffersize 8
+ puts $f "1234567"
+ string map {"\r" "<cr>" "\n" "<lf>"} [contents $path(test1)]
+} -cleanup {
+ close $f
+} -result "1234567<cr><lf>"
test io-4.1 {TranslateOutputEOL: lf} {
# search for \n
@@ -3067,6 +3076,99 @@ test io-29.35 {Tcl_Close vs fileevent vs multiple interpreters} {socket tempNotM
interp delete y
} ""
+test io-29.36.1 {gets on translation auto with "\r" in QA communication mode, possible regression, bug [b3977d199b]} -constraints {
+ socket tempNotMac fileevent
+} -setup {
+ set s [open "|[list [interpreter] << {
+ proc accept {so args} {
+ fconfigure $so -translation binary
+ puts -nonewline $so "who are you?\r"; flush $so
+ set a [gets $so]
+ puts -nonewline $so "really $a?\r"; flush $so
+ set a [gets $so]
+ close $so
+ set ::done $a
+ }
+ set s [socket -server [namespace code accept] -myaddr 127.0.0.1 0]
+ puts [lindex [fconfigure $s -sockname] 2]
+ foreach c {1 2} {
+ vwait ::done
+ puts $::done
+ }
+ }]" r]
+ set c {}
+ set result {}
+} -body {
+ set port [gets $s]
+ foreach t {{cr lf} {auto lf}} {
+ set c [socket 127.0.0.1 $port]
+ fconfigure $c -buffering line -translation $t
+ lappend result $t
+ while {1} {
+ set q [gets $c]
+ switch -- $q {
+ "who are you?" {puts $c "client"}
+ "really client?" {puts $c "yes"; lappend result $q; break}
+ default {puts $c "wrong"; lappend result "unexpected input \"$q\""; break}
+ }
+ }
+ lappend result [gets $s]
+ close $c; set c {}
+ }
+ set result
+} -cleanup {
+ close $s
+ if {$c ne {}} { close $c }
+ unset -nocomplain s c port t q
+} -result [list {cr lf} "really client?" yes {auto lf} "really client?" yes]
+test io-29.36.2 {gets on translation auto with "\r\n" in different buffers, bug [b3977d199b]} -constraints {
+ socket tempNotMac fileevent
+} -setup {
+ set s [socket -server [namespace code accept] -myaddr 127.0.0.1 0]
+ set c {}
+} -body {
+ set ::cnt 0
+ proc accept {so args} {
+ fconfigure $so -translation binary
+ puts -nonewline $so "1 line\r"
+ puts -nonewline $so "\n2 li"
+ flush $so
+ # now force separate packets
+ puts -nonewline $so "ne\r"
+ flush $so
+ if {$::cnt & 1} {
+ vwait ::cli; # simulate short delay (so client can process events, just wait for it)
+ } else {
+ # we don't have a delay, so client would get the lines as single chunk
+ }
+ # we'll try with "\r" and without "\r" (to cover both branches, where "\r" and "eof" causes exit from [gets] by 3rd line)
+ puts -nonewline $so "\n3 line"
+ if {!($::cnt % 3)} {
+ puts -nonewline $so "\r"
+ }
+ flush $so
+ close $so
+ }
+ while {$::cnt < 6} { incr ::cnt
+ set c [socket 127.0.0.1 [lindex [fconfigure $s -sockname] 2]]
+ fconfigure $c -blocking 0 -buffering line -translation auto
+ fileevent $c readable [list apply {c {
+ if {[gets $c line] >= 0} {
+ lappend ::cli <$line>
+ } elseif {[eof $c]} {
+ set ::done 1
+ }
+ }} $c]
+ vwait ::done
+ close $c; set c {}
+ }
+ set ::cli
+} -cleanup {
+ close $s
+ if {$c ne {}} { close $c }
+ unset -nocomplain ::done ::cli ::cnt s c
+} -result [lrepeat 6 {<1 line>} {<2 line>} {<3 line>}]
+
# Test end of line translations. Procedures tested are Tcl_Write, Tcl_Read.
test io-30.1 {Tcl_Write lf, Tcl_Read lf} {
diff --git a/tests/oo.test b/tests/oo.test
index 105c492..ff67cc1 100644
--- a/tests/oo.test
+++ b/tests/oo.test
@@ -7,7 +7,7 @@
# See the file "license.terms" for information on usage and redistribution of
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
-package require tcl::oo 1.2.0
+package require tcl::oo 1.3.0
if {"::tcltest" ni [namespace children]} {
package require tcltest 2.5
namespace import -force ::tcltest::*
diff --git a/tests/ooNext2.test b/tests/ooNext2.test
index ce4acdf..746f9a5 100644
--- a/tests/ooNext2.test
+++ b/tests/ooNext2.test
@@ -7,7 +7,7 @@
# See the file "license.terms" for information on usage and redistribution of
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
-package require tcl::oo 1.2.0
+package require tcl::oo 1.3.0
if {"::tcltest" ni [namespace children]} {
package require tcltest 2.5
namespace import -force ::tcltest::*
diff --git a/tests/ooUtil.test b/tests/ooUtil.test
index 4db971e..c8be9c8 100644
--- a/tests/ooUtil.test
+++ b/tests/ooUtil.test
@@ -9,7 +9,7 @@
# See the file "license.terms" for information on usage and redistribution of
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
-package require tcl::oo 1.2.0
+package require tcl::oo 1.3.0
if {"::tcltest" ni [namespace children]} {
package require tcltest 2.5
namespace import -force ::tcltest::*
diff --git a/tests/string.test b/tests/string.test
index d497b42..ba5be14 100644
--- a/tests/string.test
+++ b/tests/string.test
@@ -34,6 +34,7 @@ testConstraint testindexobj [expr {[info commands testindexobj] ne {}}]
testConstraint testevalex [expr {[info commands testevalex] ne {}}]
testConstraint utf16 [expr {[string length \U010000] == 2}]
testConstraint testbytestring [llength [info commands testbytestring]]
+testConstraint testutf16string [llength [info commands testutf16string]]
# Used for constraining memory leak tests
testConstraint memory [llength [info commands memory]]
@@ -2635,6 +2636,17 @@ test string-32.17.$noComp {string is dict, valid dict packed in invalid dict} {
} 0
}; # foreach noComp {0 1}
+
+test string-bug-b79df322a9 {Tcl_GetUnicode/Tcl_NewUnicodeObj api} -constraints {
+ testutf16string deprecated
+} -body {
+ # This simple test suffices because the bug has nothing to do with
+ # the actual encoding conversion. The test was added because these
+ # functions are no longer called within the Tcl core and thus
+ # not tested by either `string`, not `encoding` tests.
+ testutf16string "abcde"
+} -result abcde
+
# cleanup
rename MemStress {}
diff --git a/tests/winConsole.test b/tests/winConsole.test
index 8ca1457..821a143 100644
--- a/tests/winConsole.test
+++ b/tests/winConsole.test
@@ -14,34 +14,361 @@ if {"::tcltest" ni [namespace children]} {
namespace import -force ::tcltest::*
}
+catch {package require twapi} ;# Only to bring window to foreground. Not critical
-test winConsole-1.1 {Console file channel: non-blocking gets} {win interactive} {
- set oldmode [fconfigure stdin]
+::tcltest::ConstraintInitializer haveThread { expr {![catch {package require Thread}]} }
+
+# Prompt user for a yes/no response
+proc yesno {question {default "Y"}} {
+ set answer ""
+ # Make sure we are seen but catch because ui and console
+ # packages may not be available
+ catch {twapi::set_foreground_window [twapi::get_console_window]}
+ while {![string is boolean -strict $answer]} {
+ puts -nonewline stdout "$question Type Y/N followed by Enter \[$default\] : "
+ flush stdout
+ set answer [string trim [gets stdin]]
+ if {$answer eq ""} {
+ set answer $default
+ }
+ }
+ return [expr {!! $answer}]
+}
- puts stdout "Enter abcdef<return> now: " nonewline
+proc prompt {prompt} {
+ # Make sure we are seen but catch because twapi ui and console
+ # packages may not be available
+ catch {twapi::set_foreground_window [twapi::get_console_window]}
+ puts -nonewline stdout "$prompt"
flush stdout
+}
+
+# Input tests
+
+test console-input-1.0 {Console blocking gets} -constraints {win interactive} -body {
+ prompt "Type \"xyz\" and hit Enter: "
+ gets stdin
+} -result xyz
+
+test console-input-1.1 {Console file channel: non-blocking gets} -constraints {
+ win interactive
+} -setup {
+ unset -nocomplain result
+ unset -nocomplain result2
+} -body {
+ set oldmode [fconfigure stdin]
+
+ prompt "Type \"abc\" and hit Enter: "
fileevent stdin readable {
if {[gets stdin line] >= 0} {
- set result $line
- } else {
+ lappend result2 $line
+ if {[llength $result2] > 1} {
+ set result $result2
+ } else {
+ prompt "Type \"def\" and hit Enter: "
+ }
+ } elseif {[eof stdin]} {
set result "gets failed"
}
}
fconfigure stdin -blocking 0 -buffering line
- set result {}
vwait result
#cleanup the fileevent
fileevent stdin readable {}
fconfigure stdin {*}$oldmode
+ set result
+
+} -result {abc def}
+
+test console-input-1.1.1 {Bug baa51423c28a: Console file channel: fileevent with blocking gets} -constraints {
+ win interactive
+} -setup {
+ unset -nocomplain result
+ unset -nocomplain result2
+} -body {
+ prompt "Type \"abc\" and hit Enter: "
+ fileevent stdin readable {
+ if {[gets stdin line] >= 0} {
+ lappend result2 $line
+ if {[llength $result2] > 1} {
+ set result $result2
+ } else {
+ prompt "Type \"def\" and hit Enter: "
+ }
+ } elseif {[eof stdin]} {
+ set result "gets failed"
+ }
+ }
+
+ vwait result
+ #cleanup the fileevent
+ fileevent stdin readable {}
+ set result
+
+} -result {abc def}
+
+test console-input-2.0 {Console blocking read} -constraints {win interactive} -setup {
+ set oldmode [fconfigure stdin]
+ fconfigure stdin -inputmode raw
+} -cleanup {
+ fconfigure stdin {*}$oldmode
+} -body {
+ prompt "Type the key \"a\". Do NOT hit Enter. You will NOT see characters echoed."
+ set c [read stdin 1]
+ puts ""
+ set c
+} -result a
+
+test console-input-2.1 {Console file channel: non-blocking read} -constraints {
+ win interactive
+} -setup {
+ set oldmode [fconfigure stdin]
+} -cleanup {
+ fconfigure stdin {*}$oldmode
+ puts ""; # Because CRLF also would not have been echoed
+} -body {
+ set input ""
+ fconfigure stdin -blocking 0 -buffering line -inputmode raw
+ prompt "Type \"abc\". Do NOT hit Enter. You will NOT see characters echoed."
+
+ fileevent stdin readable {
+ set c [read stdin 1]
+ if {$c eq ""} {
+ if {[eof stdin]} {
+ set result "read eof"
+ }
+ } else {
+ append input $c
+ if {[string length $input] == 3} {
+ set result $input
+ }
+ }
+ }
+
+ set result {}
+ vwait result
+ fileevent stdin readable {}
set result
+} -result abc
+
+# Output tests
+
+test console-output-1.0 {Console blocking puts stdout} -constraints {win interactive} -body {
+ puts stdout "123"
+ yesno "Did you see the string \"123\"?"
+} -result 1
+
+test console-output-1.1 {Console non-blocking puts stdout} -constraints {
+ win interactive
+} -setup {
+ set oldmode [fconfigure stdout]
+ dict unset oldmode -winsize
+} -cleanup {
+ fconfigure stdout {*}$oldmode
+} -body {
+ fconfigure stdout -blocking 0 -buffering line
+ set count 0
+ fileevent stdout writable {
+ if {[incr count] < 4} {
+ puts "$count"
+ } else {
+ fileevent stdout writable {}
+ set done 1
+ }
+ }
+ vwait done
+ yesno "Did you see 1, 2, 3 printed on consecutive lines?"
+} -result 1
+
+test console-output-2.0 {Console blocking puts stderr} -constraints {win interactive} -body {
+ puts stderr "456"
+ yesno "Did you see the string \"456\"?"
+} -result 1
+
+
+# fconfigure get tests
+
+## fconfigure get stdin
+
+test console-fconfigure-get-1.0 {
+ Console get stdin configuration
+} -constraints {win interactive} -body {
+ lsort [dict keys [fconfigure stdin]]
+} -result {-blocking -buffering -buffersize -encoding -eofchar -inputmode -translation}
+
+set testnum 0
+foreach {opt result} {
+ -blocking 1
+ -buffering line
+ -buffersize 4096
+ -encoding utf-16
+ -inputmode normal
+ -translation auto
+} {
+ test console-fconfigure-get-1.[incr testnum] "Console get stdin option $opt" \
+ -constraints {win interactive} -body {
+ fconfigure stdin $opt
+ } -result $result
+}
+test console-fconfigure-get-1.[incr testnum] {
+ Console get stdin option -eofchar
+} -constraints {win interactive} -body {
+ fconfigure stdin -eofchar
+} -result \x1a
+
+test console-fconfigure-get-1.[incr testnum] {
+ fconfigure -winsize
+} -constraints {win interactive} -body {
+ fconfigure stdin -winsize
+} -result {bad option "-winsize": should be one of -blocking, -buffering, -buffersize, -encoding, -eofchar, -translation, or -inputmode} -returnCodes error
+
+## fconfigure get stdout/stderr
+foreach chan {stdout stderr} major {2 3} {
+ test console-fconfigure-get-$major.0 "Console get $chan configuration" -constraints {
+ win interactive
+ } -body {
+ lsort [dict keys [fconfigure $chan]]
+ } -result {-blocking -buffering -buffersize -encoding -eofchar -translation -winsize}
+ set testnum 0
+ foreach {opt result} {
+ -blocking 1
+ -buffersize 4096
+ -encoding utf-16
+ -translation crlf
+ } {
+ test console-fconfigure-get-$major.[incr testnum] "Console get $chan option $opt" \
+ -constraints {win interactive} -body {
+ fconfigure $chan $opt
+ } -result $result
+ }
+
+ test console-fconfigure-get-$major.[incr testnum] "Console get $chan option -winsize" \
+ -constraints {win interactive} -body {
+ fconfigure $chan -winsize
+ } -result {\d+ \d+} -match regexp
+
+ test console-fconfigure-get-$major.[incr testnum] "Console get $chan option -buffering" \
+ -constraints {win interactive} -body {
+ fconfigure $chan -buffering
+ } -result [expr {$chan eq "stdout" ? "line" : "none"}]
+
+ test console-fconfigure-get-$major.[incr testnum] {
+ fconfigure -inputmode
+ } -constraints {win interactive} -body {
+ fconfigure $chan -inputmode
+ } -result {bad option "-inputmode": should be one of -blocking, -buffering, -buffersize, -encoding, -eofchar, -translation, or -winsize} -returnCodes error
+
+}
+
+## fconfigure set stdin
+
+test console-fconfigure-set-1.0 {
+ fconfigure -inputmode password
+} -constraints {win interactive} -body {
+ set result {}
+
+ prompt "Type \"pass\" and hit Enter. You should NOT see characters echoed: "
+ fconfigure stdin -inputmode password
+ lappend result [gets stdin]
+ lappend result [fconfigure stdin -inputmode]
+ fconfigure stdin -inputmode normal
+ lappend result [yesno "\nWere the characters echoed?"]
+
+ prompt "Type \"norm\" and hit Enter. You should see characters echoed: "
+ lappend result [gets stdin]
+ lappend result [fconfigure stdin -inputmode]
+ lappend result [yesno "Were the characters echoed?"]
+
+ set result
+} -result [list pass password 0 norm normal 1]
+
+test console-fconfigure-set-1.1 {
+ fconfigure -inputmode raw
+} -constraints {win interactive} -body {
+ set result {}
+
+ prompt "Type the keys \"a\", Ctrl-H, \"b\". Do NOT hit Enter. You should NOT see characters echoed: "
+ fconfigure stdin -inputmode raw
+ lappend result [read stdin 3]
+ lappend result [fconfigure stdin -inputmode]
+ fconfigure stdin -inputmode normal
+ lappend result [yesno "\nWere the characters echoed?"]
+
+ prompt "Type the keys \"c\", Ctrl-H, \"d\" and hit Enter. You should see characters echoed: "
+ lappend result [gets stdin]
+ lappend result [fconfigure stdin -inputmode]
+ lappend result [yesno "Were the characters echoed (c replaced by d)?"]
+
+ set result
+} -result [list a\x08b raw 0 d normal 1]
+
+test console-fconfigure-set-1.2 {
+ fconfigure -inputmode reset
+} -constraints {win interactive} -body {
+ set result {}
+
+ prompt "Type \"pass\" and hit Enter. You should NOT see characters echoed: "
+ fconfigure stdin -inputmode password
+ lappend result [gets stdin]
+ lappend result [fconfigure stdin -inputmode]
+ fconfigure stdin -inputmode reset
+ lappend result [yesno "\nWere the characters echoed?"]
+
+ prompt "Type \"reset\" and hit Enter. You should see characters echoed: "
+ lappend result [gets stdin]
+ lappend result [fconfigure stdin -inputmode]
+ lappend result [yesno "Were the characters echoed?"]
+
+ set result
+} -result [list pass password 0 reset normal 1]
+
+test console-fconfigure-set-1.3 {
+ fconfigure stdin -winsize
+} -constraints {win interactive} -body {
+ fconfigure stdin -winsize {10 30}
+} -result {bad option "-winsize": should be one of -blocking, -buffering, -buffersize, -encoding, -eofchar, -translation, or -inputmode} -returnCodes error
+
+## fconfigure set stdout,stderr
+
+test console-fconfigure-set-2.0 {
+ fconfigure stdout -winsize
+} -constraints {win interactive} -body {
+ fconfigure stdout -winsize {10 30}
+} -result {bad option "-winsize": should be one of -blocking, -buffering, -buffersize, -encoding, -eofchar, or -translation} -returnCodes error
+
+test console-fconfigure-set-3.0 {
+ fconfigure stderr -winsize
+} -constraints {win interactive} -body {
+ fconfigure stderr -winsize {10 30}
+} -result {bad option "-winsize": should be one of -blocking, -buffering, -buffersize, -encoding, -eofchar, or -translation} -returnCodes error
+
+# Multiple threads
-} "abcdef"
+test console-thread-input-1.0 {Get input in thread} -constraints {
+ win interactive haveThread
+} -setup {
+ set tid [thread::create]
+} -cleanup {
+ thread::release $tid
+} -body {
+ prompt "Type \"xyz\" and hit Enter: "
+ thread::send $tid {gets stdin}
+} -result xyz
-#cleanup
+test console-thread-output-1.0 {Output from thread} -constraints {
+ win interactive haveThread
+} -setup {
+ set tid [thread::create]
+} -cleanup {
+ thread::release $tid
+} -body {
+ thread::send $tid {puts [thread::id]}
+ yesno "Did you see $tid printed?"
+} -result 1
::tcltest::cleanupTests
return
diff --git a/unix/Makefile.in b/unix/Makefile.in
index d0a9d86..30d9462 100644
--- a/unix/Makefile.in
+++ b/unix/Makefile.in
@@ -176,7 +176,7 @@ NATIVE_TCLSH = @TCLSH_PROG@
STLIB_LD = @STLIB_LD@
SHLIB_LD = @SHLIB_LD@
-SHLIB_CFLAGS = @SHLIB_CFLAGS@ -DBUILD_tcl
+SHLIB_CFLAGS = @SHLIB_CFLAGS@
SHLIB_LD_LIBS = @SHLIB_LD_LIBS@
SHLIB_LD_FLAGS = @SHLIB_LD_FLAGS@
TCL_SHLIB_LD_EXTRAS = @TCL_SHLIB_LD_EXTRAS@
@@ -278,12 +278,12 @@ VALGRINDARGS = --tool=memcheck --num-callers=24 \
STUB_CC_SWITCHES = -I"${BUILD_DIR}" -I${UNIX_DIR} -I${GENERIC_DIR} -I${TOMMATH_DIR} \
${CFLAGS} ${CFLAGS_WARNING} ${SHLIB_CFLAGS} \
- ${AC_FLAGS} ${ENV_FLAGS} ${EXTRA_CFLAGS} \
- @EXTRA_CC_SWITCHES@
+ ${AC_FLAGS} ${ENV_FLAGS} ${EXTRA_CFLAGS} @EXTRA_CC_SWITCHES@ \
+ ${NO_DEPRECATED_FLAGS} -DMP_FIXED_CUTOFFS -DMP_NO_STDINT
-CC_SWITCHES = $(STUB_CC_SWITCHES) ${NO_DEPRECATED_FLAGS} -DMP_FIXED_CUTOFFS -DMP_NO_STDINT
+CC_SWITCHES = $(STUB_CC_SWITCHES) -DBUILD_tcl
-APP_CC_SWITCHES = $(CC_SWITCHES) @EXTRA_APP_CC_SWITCHES@
+APP_CC_SWITCHES = $(STUB_CC_SWITCHES) @EXTRA_APP_CC_SWITCHES@
LIBS = @TCL_LIBS@
@@ -1049,9 +1049,9 @@ install-libraries: libraries
@echo "Installing package msgcat 1.7.1 as a Tcl Module"
@$(INSTALL_DATA) $(TOP_DIR)/library/msgcat/msgcat.tcl \
"$(MODULE_INSTALL_DIR)/8.7/msgcat-1.7.1.tm"
- @echo "Installing package tcltest 2.5.4 as a Tcl Module"
+ @echo "Installing package tcltest 2.5.5 as a Tcl Module"
@$(INSTALL_DATA) $(TOP_DIR)/library/tcltest/tcltest.tcl \
- "$(MODULE_INSTALL_DIR)/8.5/tcltest-2.5.4.tm"
+ "$(MODULE_INSTALL_DIR)/8.5/tcltest-2.5.5.tm"
@echo "Installing package platform 1.0.18 as a Tcl Module"
@$(INSTALL_DATA) $(TOP_DIR)/library/platform/platform.tcl \
"$(MODULE_INSTALL_DIR)/8.4/platform-1.0.18.tm"
diff --git a/unix/tclAppInit.c b/unix/tclAppInit.c
index 552f9e4..05d25de 100644
--- a/unix/tclAppInit.c
+++ b/unix/tclAppInit.c
@@ -12,12 +12,15 @@
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
-#undef BUILD_tcl
-#undef STATIC_BUILD
#include "tcl.h"
-#if TCL_MAJOR_VERSION < 9 && TCL_MINOR_VERSION < 7
+#if TCL_MAJOR_VERSION < 9
+# if defined(USE_TCL_STUBS)
+# error "Don't build with USE_TCL_STUBS!"
+# endif
+# if TCL_MINOR_VERSION < 7
# define Tcl_LibraryInitProc Tcl_PackageInitProc
# define Tcl_StaticLibrary Tcl_StaticPackage
+# endif
#endif
#ifdef TCL_TEST
@@ -88,7 +91,7 @@ main(
TclZipfs_AppHook(&argc, &argv);
#endif
- Tcl_Main(argc, argv, TCL_LOCAL_APPINIT);
+ Tcl_Main((size_t)argc, argv, TCL_LOCAL_APPINIT);
return 0; /* Needed only to prevent compiler warning. */
}
@@ -115,7 +118,7 @@ int
Tcl_AppInit(
Tcl_Interp *interp) /* Interpreter for application. */
{
- if ((Tcl_Init)(interp) == TCL_ERROR) {
+ if (Tcl_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
@@ -157,11 +160,11 @@ Tcl_AppInit(
*/
#ifdef DJGPP
- (Tcl_ObjSetVar2)(interp, Tcl_NewStringObj("tcl_rcFileName", -1), NULL,
- Tcl_NewStringObj("~/tclsh.rc", -1), TCL_GLOBAL_ONLY);
+ Tcl_ObjSetVar2(interp, Tcl_NewStringObj("tcl_rcFileName", TCL_INDEX_NONE), NULL,
+ Tcl_NewStringObj("~/tclsh.rc", TCL_INDEX_NONE), TCL_GLOBAL_ONLY);
#else
- (Tcl_ObjSetVar2)(interp, Tcl_NewStringObj("tcl_rcFileName", -1), NULL,
- Tcl_NewStringObj("~/.tclshrc", -1), TCL_GLOBAL_ONLY);
+ Tcl_ObjSetVar2(interp, Tcl_NewStringObj("tcl_rcFileName", TCL_INDEX_NONE), NULL,
+ Tcl_NewStringObj("~/.tclshrc", TCL_INDEX_NONE), TCL_GLOBAL_ONLY);
#endif
return TCL_OK;
diff --git a/unix/tclLoadDl.c b/unix/tclLoadDl.c
index 342dff6..5c19ea3 100644
--- a/unix/tclLoadDl.c
+++ b/unix/tclLoadDl.c
@@ -108,7 +108,7 @@ TclpDlopen(
Tcl_DString ds;
const char *fileName = Tcl_GetString(pathPtr);
- native = Tcl_UtfToExternalDString(NULL, fileName, -1, &ds);
+ native = Tcl_UtfToExternalDString(NULL, fileName, TCL_INDEX_NONE, &ds);
/*
* Use (RTLD_NOW|RTLD_LOCAL) as default, see [Bug #3216070]
*/
@@ -179,12 +179,12 @@ FindSymbol(
* the underscore.
*/
- native = Tcl_UtfToExternalDString(NULL, symbol, -1, &ds);
+ native = Tcl_UtfToExternalDString(NULL, symbol, TCL_INDEX_NONE, &ds);
proc = dlsym(handle, native); /* INTL: Native. */
if (proc == NULL) {
Tcl_DStringInit(&newName);
TclDStringAppendLiteral(&newName, "_");
- native = Tcl_DStringAppend(&newName, native, -1);
+ native = Tcl_DStringAppend(&newName, native, TCL_INDEX_NONE);
proc = dlsym(handle, native); /* INTL: Native. */
Tcl_DStringFree(&newName);
}
@@ -194,8 +194,8 @@ FindSymbol(
sprintf(buf, "%d", Tcl_DStringLength(&ds));
Tcl_DStringInit(&newName);
TclDStringAppendLiteral(&newName, "__Z");
- Tcl_DStringAppend(&newName, buf, -1);
- Tcl_DStringAppend(&newName, Tcl_DStringValue(&ds), -1);
+ Tcl_DStringAppend(&newName, buf, TCL_INDEX_NONE);
+ Tcl_DStringAppend(&newName, Tcl_DStringValue(&ds), TCL_INDEX_NONE);
TclDStringAppendLiteral(&newName, "P10Tcl_Interp");
native = Tcl_DStringValue(&newName);
proc = dlsym(handle, native + 1); /* INTL: Native. */
diff --git a/unix/tclLoadDyld.c b/unix/tclLoadDyld.c
index 7cd48f2..854d4bd 100644
--- a/unix/tclLoadDyld.c
+++ b/unix/tclLoadDyld.c
@@ -185,7 +185,7 @@ TclpDlopen(
nativePath = (const char *)Tcl_FSGetNativePath(pathPtr);
nativeFileName = Tcl_UtfToExternalDString(NULL, Tcl_GetString(pathPtr),
- -1, &ds);
+ TCL_INDEX_NONE, &ds);
#if TCL_DYLD_USE_DLFCN
/*
@@ -296,7 +296,7 @@ TclpDlopen(
TclNewObj(errObj);
if (errMsg != NULL) {
- Tcl_AppendToObj(errObj, errMsg, -1);
+ Tcl_AppendToObj(errObj, errMsg, TCL_INDEX_NONE);
}
#if TCL_DYLD_USE_NSMODULE
if (objFileImageErrMsg) {
@@ -341,7 +341,7 @@ FindSymbol(
Tcl_DString ds;
const char *native;
- native = Tcl_UtfToExternalDString(NULL, symbol, -1, &ds);
+ native = Tcl_UtfToExternalDString(NULL, symbol, TCL_INDEX_NONE, &ds);
if (dyldLoadHandle->dlHandle) {
#if TCL_DYLD_USE_DLFCN
proc = (Tcl_LibraryInitProc *)dlsym(dyldLoadHandle->dlHandle, native);
@@ -360,7 +360,7 @@ FindSymbol(
Tcl_DStringInit(&newName);
TclDStringAppendLiteral(&newName, "_");
- native = Tcl_DStringAppend(&newName, native, -1);
+ native = Tcl_DStringAppend(&newName, native, TCL_INDEX_NONE);
if (dyldLoadHandle->dyldLibHeader) {
nsSymbol = NSLookupSymbolInImage(dyldLoadHandle->dyldLibHeader,
native, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW |
@@ -656,7 +656,7 @@ TclpLoadMemory(
const char *errorName, *errMsg;
NSLinkEditError(&editError, &errorNumber, &errorName, &errMsg);
- Tcl_SetObjResult(interp, Tcl_NewStringObj(errMsg, -1));
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(errMsg, TCL_INDEX_NONE));
return TCL_ERROR;
}
diff --git a/unix/tclLoadNext.c b/unix/tclLoadNext.c
index 2055210..dc827fc 100644
--- a/unix/tclLoadNext.c
+++ b/unix/tclLoadNext.c
@@ -83,7 +83,7 @@ TclpDlopen(
Tcl_DString ds;
- native = Tcl_UtfToExternalDString(NULL, fileName, -1, &ds);
+ native = Tcl_UtfToExternalDString(NULL, fileName, TCL_INDEX_NONE, &ds);
files = {native,NULL};
result = rld_load(errorStream, &header, files, NULL);
Tcl_DStringFree(&ds);
diff --git a/unix/tclLoadOSF.c b/unix/tclLoadOSF.c
index bb58871..03698fa 100644
--- a/unix/tclLoadOSF.c
+++ b/unix/tclLoadOSF.c
@@ -100,7 +100,7 @@ TclpDlopen(
Tcl_DString ds;
- native = Tcl_UtfToExternalDString(NULL, fileName, -1, &ds);
+ native = Tcl_UtfToExternalDString(NULL, fileName, TCL_INDEX_NONE, &ds);
lm = (Tcl_LibraryInitProc *) load(native, LDR_NOFLAGS);
Tcl_DStringFree(&ds);
}
diff --git a/unix/tclLoadShl.c b/unix/tclLoadShl.c
index 5bf97eb..5cde183 100644
--- a/unix/tclLoadShl.c
+++ b/unix/tclLoadShl.c
@@ -86,7 +86,7 @@ TclpDlopen(
Tcl_DString ds;
- native = Tcl_UtfToExternalDString(NULL, fileName, -1, &ds);
+ native = Tcl_UtfToExternalDString(NULL, fileName, TCL_INDEX_NONE, &ds);
handle = shl_load(native, BIND_DEFERRED|BIND_VERBOSE|DYNAMIC_PATH, 0L);
Tcl_DStringFree(&ds);
}
@@ -140,7 +140,7 @@ FindSymbol(
(void *) &proc) != 0) {
Tcl_DStringInit(&newName);
TclDStringAppendLiteral(&newName, "_");
- Tcl_DStringAppend(&newName, symbol, -1);
+ Tcl_DStringAppend(&newName, symbol, TCL_INDEX_NONE);
if (shl_findsym(&handle, Tcl_DStringValue(&newName),
(short) TYPE_PROCEDURE, (void *) &proc) != 0) {
proc = NULL;
diff --git a/unix/tclUnixChan.c b/unix/tclUnixChan.c
index 4cb9af0..22e9876 100644
--- a/unix/tclUnixChan.c
+++ b/unix/tclUnixChan.c
@@ -1860,12 +1860,11 @@ TclpGetDefaultStdChannel(
* Some #def's to make the code a little clearer!
*/
-#define ZERO_OFFSET ((Tcl_SeekOffset) 0)
#define ERROR_OFFSET ((Tcl_SeekOffset) -1)
switch (type) {
case TCL_STDIN:
- if ((TclOSseek(0, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
+ if ((TclOSseek(0, 0, SEEK_CUR) == ERROR_OFFSET)
&& (errno == EBADF)) {
return NULL;
}
@@ -1874,7 +1873,7 @@ TclpGetDefaultStdChannel(
bufMode = "line";
break;
case TCL_STDOUT:
- if ((TclOSseek(1, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
+ if ((TclOSseek(1, 0, SEEK_CUR) == ERROR_OFFSET)
&& (errno == EBADF)) {
return NULL;
}
@@ -1883,7 +1882,7 @@ TclpGetDefaultStdChannel(
bufMode = "line";
break;
case TCL_STDERR:
- if ((TclOSseek(2, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
+ if ((TclOSseek(2, 0, SEEK_CUR) == ERROR_OFFSET)
&& (errno == EBADF)) {
return NULL;
}
@@ -1896,7 +1895,6 @@ TclpGetDefaultStdChannel(
break;
}
-#undef ZERO_OFFSET
#undef ERROR_OFFSET
channel = Tcl_MakeFileChannel(INT2PTR(fd), mode);
diff --git a/unix/tclUnixFCmd.c b/unix/tclUnixFCmd.c
index a5d6a87..818209d 100644
--- a/unix/tclUnixFCmd.c
+++ b/unix/tclUnixFCmd.c
@@ -779,7 +779,7 @@ TclpObjCopyDirectory(
Tcl_DStringFree(&dstString);
if (ret != TCL_OK) {
- *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
+ *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), TCL_INDEX_NONE);
Tcl_DStringFree(&ds);
Tcl_IncrRefCount(*errorPtr);
}
@@ -833,7 +833,7 @@ TclpObjRemoveDirectory(
Tcl_DStringFree(&pathString);
if (ret != TCL_OK) {
- *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
+ *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), TCL_INDEX_NONE);
Tcl_DStringFree(&ds);
Tcl_IncrRefCount(*errorPtr);
}
@@ -883,7 +883,7 @@ DoRemoveDirectory(
result = TCL_OK;
if ((errno != EEXIST) || (recursive == 0)) {
if (errorPtr != NULL) {
- Tcl_ExternalToUtfDString(NULL, path, -1, errorPtr);
+ Tcl_ExternalToUtfDString(NULL, path, TCL_INDEX_NONE, errorPtr);
}
result = TCL_ERROR;
}
@@ -1015,9 +1015,9 @@ TraverseUnixTree(
* Append name after slash, and recurse on the file.
*/
- Tcl_DStringAppend(sourcePtr, dirEntPtr->d_name, -1);
+ Tcl_DStringAppend(sourcePtr, dirEntPtr->d_name, TCL_INDEX_NONE);
if (targetPtr != NULL) {
- Tcl_DStringAppend(targetPtr, dirEntPtr->d_name, -1);
+ Tcl_DStringAppend(targetPtr, dirEntPtr->d_name, TCL_INDEX_NONE);
}
result = TraverseUnixTree(traverseProc, sourcePtr, targetPtr,
errorPtr, doRewind);
@@ -1132,7 +1132,7 @@ TraverseUnixTree(
end:
if (errfile != NULL) {
if (errorPtr != NULL) {
- Tcl_ExternalToUtfDString(NULL, errfile, -1, errorPtr);
+ Tcl_ExternalToUtfDString(NULL, errfile, TCL_INDEX_NONE, errorPtr);
}
result = TCL_ERROR;
}
@@ -1368,8 +1368,8 @@ GetGroupAttribute(
Tcl_DString ds;
const char *utf;
- utf = Tcl_ExternalToUtfDString(NULL, groupPtr->gr_name, -1, &ds);
- *attributePtrPtr = Tcl_NewStringObj(utf, -1);
+ utf = Tcl_ExternalToUtfDString(NULL, groupPtr->gr_name, TCL_INDEX_NONE, &ds);
+ *attributePtrPtr = Tcl_NewStringObj(utf, TCL_INDEX_NONE);
Tcl_DStringFree(&ds);
}
return TCL_OK;
@@ -1421,7 +1421,7 @@ GetOwnerAttribute(
} else {
Tcl_DString ds;
- (void) Tcl_ExternalToUtfDString(NULL, pwPtr->pw_name, -1, &ds);
+ (void) Tcl_ExternalToUtfDString(NULL, pwPtr->pw_name, TCL_INDEX_NONE, &ds);
*attributePtrPtr = TclDStringToObj(&ds);
}
return TCL_OK;
@@ -2176,7 +2176,7 @@ TclUnixOpenTemporaryFile(
Tcl_UtfToExternalDString(NULL, string, dirObj->length, &templ);
} else {
Tcl_DStringInit(&templ);
- Tcl_DStringAppend(&templ, DefaultTempDir(), -1); /* INTL: native */
+ Tcl_DStringAppend(&templ, DefaultTempDir(), TCL_INDEX_NONE); /* INTL: native */
}
TclDStringAppendLiteral(&templ, "/");
@@ -2301,7 +2301,7 @@ TclpCreateTemporaryDirectory(
Tcl_UtfToExternalDString(NULL, string, dirObj->length, &templ);
} else {
Tcl_DStringInit(&templ);
- Tcl_DStringAppend(&templ, DefaultTempDir(), -1); /* INTL: native */
+ Tcl_DStringAppend(&templ, DefaultTempDir(), TCL_INDEX_NONE); /* INTL: native */
}
if (Tcl_DStringValue(&templ)[Tcl_DStringLength(&templ) - 1] != '/') {
diff --git a/unix/tclUnixFile.c b/unix/tclUnixFile.c
index 998614d..d1b656b 100644
--- a/unix/tclUnixFile.c
+++ b/unix/tclUnixFile.c
@@ -119,7 +119,7 @@ TclpFindExecutable(
TclDStringAppendLiteral(&buffer, "/");
}
}
- name = Tcl_DStringAppend(&buffer, argv0, -1);
+ name = Tcl_DStringAppend(&buffer, argv0, TCL_INDEX_NONE);
/*
* INTL: The following calls to access() and stat() should not be
@@ -155,9 +155,9 @@ TclpFindExecutable(
#endif
{
encoding = Tcl_GetEncoding(NULL, NULL);
- Tcl_ExternalToUtfDString(encoding, name, -1, &utfName);
+ Tcl_ExternalToUtfDString(encoding, name, TCL_INDEX_NONE, &utfName);
TclSetObjNameOfExecutable(
- Tcl_NewStringObj(Tcl_DStringValue(&utfName), -1), encoding);
+ Tcl_NewStringObj(Tcl_DStringValue(&utfName), TCL_INDEX_NONE), encoding);
Tcl_DStringFree(&utfName);
goto done;
}
@@ -178,7 +178,7 @@ TclpFindExecutable(
}
Tcl_DStringInit(&nameString);
- Tcl_DStringAppend(&nameString, name, -1);
+ Tcl_DStringAppend(&nameString, name, TCL_INDEX_NONE);
Tcl_DStringFree(&buffer);
Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&cwd),
@@ -191,10 +191,10 @@ TclpFindExecutable(
Tcl_DStringFree(&nameString);
encoding = Tcl_GetEncoding(NULL, NULL);
- Tcl_ExternalToUtfDString(encoding, Tcl_DStringValue(&buffer), -1,
+ Tcl_ExternalToUtfDString(encoding, Tcl_DStringValue(&buffer), TCL_INDEX_NONE,
&utfName);
TclSetObjNameOfExecutable(
- Tcl_NewStringObj(Tcl_DStringValue(&utfName), -1), encoding);
+ Tcl_NewStringObj(Tcl_DStringValue(&utfName), TCL_INDEX_NONE), encoding);
Tcl_DStringFree(&utfName);
done:
@@ -307,7 +307,7 @@ TclpMatchInDirectory(
* Now open the directory for reading and iterate over the contents.
*/
- native = Tcl_UtfToExternalDString(NULL, dirName, -1, &ds);
+ native = Tcl_UtfToExternalDString(NULL, dirName, TCL_INDEX_NONE, &ds);
if ((TclOSstat(native, &statBuf) != 0) /* INTL: Native. */
|| !S_ISDIR(statBuf.st_mode)) {
@@ -371,14 +371,14 @@ TclpMatchInDirectory(
* and pattern. If so, add the file to the result.
*/
- utfname = Tcl_ExternalToUtfDString(NULL, entryPtr->d_name, -1,
+ utfname = Tcl_ExternalToUtfDString(NULL, entryPtr->d_name, TCL_INDEX_NONE,
&utfDs);
if (Tcl_StringCaseMatch(utfname, pattern, 0)) {
int typeOk = 1;
if (types != NULL) {
Tcl_DStringSetLength(&ds, nativeDirLen);
- native = Tcl_DStringAppend(&ds, entryPtr->d_name, -1);
+ native = Tcl_DStringAppend(&ds, entryPtr->d_name, TCL_INDEX_NONE);
matchResult = NativeMatchType(interp, native,
entryPtr->d_name, types);
typeOk = (matchResult == 1);
@@ -598,7 +598,7 @@ TclpGetUserHome(
{
struct passwd *pwPtr;
Tcl_DString ds;
- const char *native = Tcl_UtfToExternalDString(NULL, name, -1, &ds);
+ const char *native = Tcl_UtfToExternalDString(NULL, name, TCL_INDEX_NONE, &ds);
pwPtr = TclpGetPwNam(native); /* INTL: Native. */
Tcl_DStringFree(&ds);
@@ -606,7 +606,7 @@ TclpGetUserHome(
if (pwPtr == NULL) {
return NULL;
}
- Tcl_ExternalToUtfDString(NULL, pwPtr->pw_dir, -1, bufferPtr);
+ Tcl_ExternalToUtfDString(NULL, pwPtr->pw_dir, TCL_INDEX_NONE, bufferPtr);
return Tcl_DStringValue(bufferPtr);
}
@@ -785,7 +785,7 @@ TclpGetCwd(
}
return NULL;
}
- return Tcl_ExternalToUtfDString(NULL, buffer, -1, bufferPtr);
+ return Tcl_ExternalToUtfDString(NULL, buffer, TCL_INDEX_NONE, bufferPtr);
}
/*
@@ -820,7 +820,7 @@ TclpReadlink(
const char *native;
Tcl_DString ds;
- native = Tcl_UtfToExternalDString(NULL, path, -1, &ds);
+ native = Tcl_UtfToExternalDString(NULL, path, TCL_INDEX_NONE, &ds);
length = readlink(native, link, sizeof(link)); /* INTL: Native. */
Tcl_DStringFree(&ds);
@@ -1061,7 +1061,7 @@ TclpNativeToNormalized(
{
Tcl_DString ds;
- Tcl_ExternalToUtfDString(NULL, (const char *) clientData, -1, &ds);
+ Tcl_ExternalToUtfDString(NULL, (const char *) clientData, TCL_INDEX_NONE, &ds);
return TclDStringToObj(&ds);
}
diff --git a/unix/tclUnixInit.c b/unix/tclUnixInit.c
index c480a56..21910e1 100644
--- a/unix/tclUnixInit.c
+++ b/unix/tclUnixInit.c
@@ -369,13 +369,13 @@ TclpInitPlatform(void)
* Make sure, that the standard FDs exist. [Bug 772288]
*/
- if (TclOSseek(0, (Tcl_SeekOffset) 0, SEEK_CUR) == -1 && errno == EBADF) {
+ if (TclOSseek(0, 0, SEEK_CUR) == -1 && errno == EBADF) {
open("/dev/null", O_RDONLY);
}
- if (TclOSseek(1, (Tcl_SeekOffset) 0, SEEK_CUR) == -1 && errno == EBADF) {
+ if (TclOSseek(1, 0, SEEK_CUR) == -1 && errno == EBADF) {
open("/dev/null", O_WRONLY);
}
- if (TclOSseek(2, (Tcl_SeekOffset) 0, SEEK_CUR) == -1 && errno == EBADF) {
+ if (TclOSseek(2, 0, SEEK_CUR) == -1 && errno == EBADF) {
open("/dev/null", O_WRONLY);
}
@@ -473,7 +473,7 @@ TclpInitLibraryPath(
*/
str = getenv("TCL_LIBRARY"); /* INTL: Native. */
- Tcl_ExternalToUtfDString(NULL, str, -1, &buffer);
+ Tcl_ExternalToUtfDString(NULL, str, TCL_INDEX_NONE, &buffer);
str = Tcl_DStringValue(&buffer);
if ((str != NULL) && (str[0] != '\0')) {
@@ -496,7 +496,7 @@ TclpInitLibraryPath(
* If TCL_LIBRARY is set, search there.
*/
- Tcl_ListObjAppendElement(NULL, pathPtr, Tcl_NewStringObj(str, -1));
+ Tcl_ListObjAppendElement(NULL, pathPtr, Tcl_NewStringObj(str, TCL_INDEX_NONE));
Tcl_SplitPath(str, &pathc, &pathv);
if ((pathc > 0) && (strcasecmp(installLib + 4, pathv[pathc-1]) != 0)) {
@@ -537,7 +537,7 @@ TclpInitLibraryPath(
str = defaultLibraryDir;
}
if (str[0] != '\0') {
- objPtr = Tcl_NewStringObj(str, -1);
+ objPtr = Tcl_NewStringObj(str, TCL_INDEX_NONE);
Tcl_ListObjAppendElement(NULL, pathPtr, objPtr);
}
}
@@ -635,13 +635,13 @@ Tcl_GetEncodingNameFromEnvironment(
*/
Tcl_DStringInit(&ds);
- encoding = Tcl_DStringAppend(&ds, nl_langinfo(CODESET), -1);
+ encoding = Tcl_DStringAppend(&ds, nl_langinfo(CODESET), TCL_INDEX_NONE);
Tcl_UtfToLower(Tcl_DStringValue(&ds));
knownEncoding = SearchKnownEncodings(encoding);
if (knownEncoding != NULL) {
- Tcl_DStringAppend(bufPtr, knownEncoding, -1);
+ Tcl_DStringAppend(bufPtr, knownEncoding, TCL_INDEX_NONE);
} else if (NULL != Tcl_GetEncoding(NULL, encoding)) {
- Tcl_DStringAppend(bufPtr, encoding, -1);
+ Tcl_DStringAppend(bufPtr, encoding, TCL_INDEX_NONE);
}
Tcl_DStringFree(&ds);
if (Tcl_DStringLength(bufPtr)) {
@@ -673,14 +673,14 @@ Tcl_GetEncodingNameFromEnvironment(
Tcl_DStringInit(&ds);
p = encoding;
- encoding = Tcl_DStringAppend(&ds, p, -1);
+ encoding = Tcl_DStringAppend(&ds, p, TCL_INDEX_NONE);
Tcl_UtfToLower(Tcl_DStringValue(&ds));
knownEncoding = SearchKnownEncodings(encoding);
if (knownEncoding != NULL) {
- Tcl_DStringAppend(bufPtr, knownEncoding, -1);
+ Tcl_DStringAppend(bufPtr, knownEncoding, TCL_INDEX_NONE);
} else if (NULL != Tcl_GetEncoding(NULL, encoding)) {
- Tcl_DStringAppend(bufPtr, encoding, -1);
+ Tcl_DStringAppend(bufPtr, encoding, TCL_INDEX_NONE);
}
if (Tcl_DStringLength(bufPtr)) {
Tcl_DStringFree(&ds);
@@ -701,9 +701,9 @@ Tcl_GetEncodingNameFromEnvironment(
if (*p != '\0') {
knownEncoding = SearchKnownEncodings(p);
if (knownEncoding != NULL) {
- Tcl_DStringAppend(bufPtr, knownEncoding, -1);
+ Tcl_DStringAppend(bufPtr, knownEncoding, TCL_INDEX_NONE);
} else if (NULL != Tcl_GetEncoding(NULL, p)) {
- Tcl_DStringAppend(bufPtr, p, -1);
+ Tcl_DStringAppend(bufPtr, p, TCL_INDEX_NONE);
}
}
Tcl_DStringFree(&ds);
@@ -711,7 +711,7 @@ Tcl_GetEncodingNameFromEnvironment(
return Tcl_DStringValue(bufPtr);
}
}
- return Tcl_DStringAppend(bufPtr, TCL_DEFAULT_ENCODING, -1);
+ return Tcl_DStringAppend(bufPtr, TCL_DEFAULT_ENCODING, TCL_INDEX_NONE);
}
/*
@@ -901,7 +901,7 @@ TclpSetVariables(
unameOK = 1;
- native = Tcl_ExternalToUtfDString(NULL, name.sysname, -1, &ds);
+ native = Tcl_ExternalToUtfDString(NULL, name.sysname, TCL_INDEX_NONE, &ds);
Tcl_SetVar2(interp, "tcl_platform", "os", native, TCL_GLOBAL_ONLY);
Tcl_DStringFree(&ds);
@@ -964,7 +964,7 @@ TclpSetVariables(
user = "";
Tcl_DStringInit(&ds); /* ensure cleanliness */
} else {
- user = Tcl_ExternalToUtfDString(NULL, pwEnt->pw_name, -1, &ds);
+ user = Tcl_ExternalToUtfDString(NULL, pwEnt->pw_name, TCL_INDEX_NONE, &ds);
}
Tcl_SetVar2(interp, "tcl_platform", "user", user, TCL_GLOBAL_ONLY);
@@ -1013,7 +1013,7 @@ TclpFindVariable(
Tcl_DStringInit(&envString);
for (i = 0, env = environ[i]; env != NULL; i++, env = environ[i]) {
- p1 = Tcl_ExternalToUtfDString(NULL, env, -1, &envString);
+ p1 = Tcl_ExternalToUtfDString(NULL, env, TCL_INDEX_NONE, &envString);
p2 = name;
for (; *p2 == *p1; p1++, p2++) {
diff --git a/unix/tclUnixPipe.c b/unix/tclUnixPipe.c
index e7199bc..c53360a 100644
--- a/unix/tclUnixPipe.c
+++ b/unix/tclUnixPipe.c
@@ -141,7 +141,7 @@ TclpOpenFile(
const char *native;
Tcl_DString ds;
- native = Tcl_UtfToExternalDString(NULL, fname, -1, &ds);
+ native = Tcl_UtfToExternalDString(NULL, fname, TCL_INDEX_NONE, &ds);
fd = TclOSopen(native, mode, 0666); /* INTL: Native. */
Tcl_DStringFree(&ds);
if (fd != -1) {
@@ -153,7 +153,7 @@ TclpOpenFile(
*/
if ((mode & O_WRONLY) && !(mode & O_APPEND)) {
- TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_END);
+ TclOSseek(fd, 0, SEEK_END);
}
/*
@@ -198,14 +198,14 @@ TclpCreateTempFile(
Tcl_DString dstring;
char *native;
- native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring);
+ native = Tcl_UtfToExternalDString(NULL, contents, TCL_INDEX_NONE, &dstring);
if (write(fd, native, Tcl_DStringLength(&dstring)) == -1) {
close(fd);
Tcl_DStringFree(&dstring);
return NULL;
}
Tcl_DStringFree(&dstring);
- TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_SET);
+ TclOSseek(fd, 0, SEEK_SET);
}
return MakeFile(fd);
}
@@ -436,7 +436,7 @@ TclpCreateProcess(
newArgv = (char **)TclStackAlloc(interp, (argc+1) * sizeof(char *));
newArgv[argc] = NULL;
for (i = 0; i < argc; i++) {
- newArgv[i] = Tcl_UtfToExternalDString(NULL, argv[i], -1, &dsArray[i]);
+ newArgv[i] = Tcl_UtfToExternalDString(NULL, argv[i], TCL_INDEX_NONE, &dsArray[i]);
}
#ifdef USE_VFORK
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c
index 91d84f3..d2068c3 100644
--- a/unix/tclUnixSock.c
+++ b/unix/tclUnixSock.c
@@ -873,7 +873,7 @@ TcpGetOptionProc(
errno = err;
}
if (errno != 0) {
- Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(errno), -1);
+ Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(errno), TCL_INDEX_NONE);
}
return TCL_OK;
}
@@ -881,7 +881,7 @@ TcpGetOptionProc(
if ((len > 1) && (optionName[1] == 'c') &&
(strncmp(optionName, "-connecting", len) == 0)) {
Tcl_DStringAppend(dsPtr,
- GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT) ? "1" : "0", -1);
+ GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT) ? "1" : "0", TCL_INDEX_NONE);
return TCL_OK;
}
@@ -1769,13 +1769,13 @@ Tcl_OpenTcpServerEx(
return statePtr->channel;
}
if (interp != NULL) {
- Tcl_Obj *errorObj = Tcl_NewStringObj("couldn't open socket: ", -1);
+ Tcl_Obj *errorObj = Tcl_NewStringObj("couldn't open socket: ", TCL_INDEX_NONE);
if (errorMsg == NULL) {
errno = my_errno;
- Tcl_AppendToObj(errorObj, Tcl_PosixError(interp), -1);
+ Tcl_AppendToObj(errorObj, Tcl_PosixError(interp), TCL_INDEX_NONE);
} else {
- Tcl_AppendToObj(errorObj, errorMsg, -1);
+ Tcl_AppendToObj(errorObj, errorMsg, TCL_INDEX_NONE);
}
Tcl_SetObjResult(interp, errorObj);
}
diff --git a/unix/tclooConfig.sh b/unix/tclooConfig.sh
index 4c2068c..a400b5b 100644
--- a/unix/tclooConfig.sh
+++ b/unix/tclooConfig.sh
@@ -16,4 +16,4 @@ TCLOO_STUB_LIB_SPEC=""
TCLOO_INCLUDE_SPEC=""
TCLOO_PRIVATE_INCLUDE_SPEC=""
TCLOO_CFLAGS=""
-TCLOO_VERSION=1.2.0
+TCLOO_VERSION=1.3
diff --git a/win/Makefile.in b/win/Makefile.in
index cf1ea7b..4e14ddc 100644
--- a/win/Makefile.in
+++ b/win/Makefile.in
@@ -886,8 +886,8 @@ install-libraries: libraries install-tzdata install-msgs
done;
@echo "Installing package msgcat 1.7.1 as a Tcl Module";
@$(COPY) $(ROOT_DIR)/library/msgcat/msgcat.tcl "$(MODULE_INSTALL_DIR)/8.7/msgcat-1.7.1.tm";
- @echo "Installing package tcltest 2.5.4 as a Tcl Module";
- @$(COPY) $(ROOT_DIR)/library/tcltest/tcltest.tcl "$(MODULE_INSTALL_DIR)/8.5/tcltest-2.5.4.tm";
+ @echo "Installing package tcltest 2.5.5 as a Tcl Module";
+ @$(COPY) $(ROOT_DIR)/library/tcltest/tcltest.tcl "$(MODULE_INSTALL_DIR)/8.5/tcltest-2.5.5.tm";
@echo "Installing package platform 1.0.18 as a Tcl Module";
@$(COPY) $(ROOT_DIR)/library/platform/platform.tcl "$(MODULE_INSTALL_DIR)/8.4/platform-1.0.18.tm";
@echo "Installing package platform::shell 1.1.4 as a Tcl Module";
diff --git a/win/nmakehlp.c b/win/nmakehlp.c
index 71d727f..fc40da4 100644
--- a/win/nmakehlp.c
+++ b/win/nmakehlp.c
@@ -718,11 +718,13 @@ static int LocateDependencyHelper(const char *dir, const char *keypath)
int keylen, ret;
WIN32_FIND_DATA finfo;
- if (dir == NULL || keypath == NULL)
+ if (dir == NULL || keypath == NULL) {
return 2; /* Have no real error reporting mechanism into nmake */
+ }
dirlen = strlen(dir);
- if ((dirlen + 3) > sizeof(path))
+ if ((dirlen + 3) > sizeof(path)) {
return 2;
+ }
strncpy(path, dir, dirlen);
strncpy(path+dirlen, "\\*", 3); /* Including terminating \0 */
keylen = strlen(keypath);
@@ -788,8 +790,9 @@ static int LocateDependency(const char *keypath)
for (i = 0; i < (sizeof(paths)/sizeof(paths[0])); ++i) {
ret = LocateDependencyHelper(paths[i], keypath);
- if (ret == 0)
+ if (ret == 0) {
return ret;
+ }
}
return ret;
}
diff --git a/win/rules.vc b/win/rules.vc
index db65ce7..fdc68e0 100644
--- a/win/rules.vc
+++ b/win/rules.vc
@@ -1418,7 +1418,7 @@ OPTDEFINES = $(OPTDEFINES) /DTCL_CFG_DO64BIT
OPTDEFINES = $(OPTDEFINES) /DNO_STRTOI64=1
!endif
-!if "$(TCL_MAJOR_VERSION)" == "8"
+!if $(TCL_MAJOR_VERSION) == 8
!if "$(_USE_64BIT_TIME_T)" == "1"
OPTDEFINES = $(OPTDEFINES) /D_USE_64BIT_TIME_T=1
!endif
diff --git a/win/tclAppInit.c b/win/tclAppInit.c
index be70492..27eb164 100644
--- a/win/tclAppInit.c
+++ b/win/tclAppInit.c
@@ -15,17 +15,14 @@
*/
#include "tcl.h"
-#define WIN32_LEAN_AND_MEAN
-#define STRICT /* See MSDN Article Q83456 */
-#include <windows.h>
-#undef STRICT
-#undef WIN32_LEAN_AND_MEAN
-#include <locale.h>
-#include <stdlib.h>
-#include <tchar.h>
-#if TCL_MAJOR_VERSION < 9 && TCL_MINOR_VERSION < 7
+#if TCL_MAJOR_VERSION < 9
+# if defined(USE_TCL_STUBS)
+# error "Don't build with USE_TCL_STUBS!"
+# endif
+# if TCL_MINOR_VERSION < 7
# define Tcl_LibraryInitProc Tcl_PackageInitProc
# define Tcl_StaticLibrary Tcl_StaticPackage
+# endif
#endif
#ifdef TCL_TEST
@@ -39,6 +36,14 @@ extern Tcl_LibraryInitProc Dde_Init;
extern Tcl_LibraryInitProc Dde_SafeInit;
#endif
+#define WIN32_LEAN_AND_MEAN
+#define STRICT /* See MSDN Article Q83456 */
+#include <windows.h>
+#undef STRICT
+#undef WIN32_LEAN_AND_MEAN
+#include <locale.h>
+#include <stdlib.h>
+#include <tchar.h>
#if defined(__GNUC__) || defined(TCL_BROKEN_MAINARGS)
int _CRT_glob = 0;
#endif /* __GNUC__ || TCL_BROKEN_MAINARGS */
@@ -136,7 +141,7 @@ _tmain(
TclZipfs_AppHook(&argc, &argv);
#endif
- Tcl_Main(argc, argv, TCL_LOCAL_APPINIT);
+ Tcl_Main((size_t)argc, argv, TCL_LOCAL_APPINIT);
return 0; /* Needed only to prevent compiler warning. */
}
@@ -163,7 +168,7 @@ int
Tcl_AppInit(
Tcl_Interp *interp) /* Interpreter for application. */
{
- if ((Tcl_Init)(interp) == TCL_ERROR) {
+ if (Tcl_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
@@ -210,8 +215,8 @@ Tcl_AppInit(
* user-specific startup file will be run under any conditions.
*/
- (Tcl_ObjSetVar2)(interp, Tcl_NewStringObj("tcl_rcFileName", -1), NULL,
- Tcl_NewStringObj("~/tclshrc.tcl", -1), TCL_GLOBAL_ONLY);
+ Tcl_ObjSetVar2(interp, Tcl_NewStringObj("tcl_rcFileName", TCL_INDEX_NONE), NULL,
+ Tcl_NewStringObj("~/tclshrc.tcl", TCL_INDEX_NONE), TCL_GLOBAL_ONLY);
return TCL_OK;
}
diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c
index c3ba814..4b2d1d3 100644
--- a/win/tclWinConsole.c
+++ b/win/tclWinConsole.c
@@ -2,123 +2,195 @@
* tclWinConsole.c --
*
* This file implements the Windows-specific console functions, and the
- * "console" channel driver.
+ * "console" channel driver. Windows 7 or later required.
*
- * Copyright © 1999 Scriptics Corp.
+ * Copyright © 2022 Ashok P. Nadkarni
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
+#ifdef TCL_CONSOLE_DEBUG
+#undef NDEBUG /* Enable asserts */
+#endif
+
#include "tclWinInt.h"
+#include <assert.h>
+#include <ctype.h>
/*
- * The following variable is used to tell whether this module has been
- * initialized.
+ * A general note on the design: The console channel driver differs from most
+ * other drivers in the following respects:
+ *
+ * - There can be at most 3 console handles at any time since Windows does
+ * support allocation of more than one console (with three handles
+ * corresponding to stdin, stdout, stderr)
+ *
+ * - Consoles are created / inherited at process startup. There is currently
+ * no way in Tcl to programmatically create a console. Even if these were
+ * added the above Windows limitation would still apply.
+ *
+ * - Unlike files, sockets etc. where there is a one-to-one
+ * correspondence between Tcl channels and operating system handles,
+ * std* channels are shared amongst threads which means there can be
+ * multiple Tcl channels corresponding to a single console handle.
+ *
+ * - Even with multiple threads, more than one file event handler is unlikely.
+ * It does not make sense for multiple threads to register handlers for
+ * stdin because the input would be randomly fragmented amongst the threads.
+ *
+ * Various design factors are driven by the above, e.g. use of lists instead
+ * of hash tables (at most 3 console handles) and use of global instead of
+ * per thread queues which simplifies lock management particularly because
+ * thread-console relation is not one-one and is likely more performant as
+ * well with fewer locks needing to be obtained.
+ *
+ * Some additional design notes/reminders for the future:
+ *
+ * Aligned, synchronous reads are done directly by interpreter thread.
+ * Unaligned or asynchronous reads are done through the reader thread.
+ *
+ * The reader thread does not read ahead. That is, it will not post a read
+ * until some interpreter thread is actually requesting a read. This is
+ * because an interpreter may (for example) turn off echo for passwords and
+ * the read ahead would come in the way of that.
+ *
+ * If multiple threads are reading from stdin, the input is sprayed in random
+ * fashion. This is not good application design and hence no plan to address
+ * this (not clear what should be done even in theory)
+ *
+ * For output, we do not restrict all output to the console writer threads.
+ * See ConsoleOutputProc for the conditions.
+ *
+ * Locks are never held when calling the ReadConsole/WriteConsole API's
+ * since they may block.
*/
-static int initialized = 0;
+static int gInitialized = 0;
/*
- * The consoleMutex locks around access to the initialized variable, and it is
- * used to protect background threads from being terminated while they are
- * using APIs that hold locks.
+ * Permit CONSOLE_BUFFER_SIZE to be defined on build command for stress test.
+ *
+ * In theory, at least sizeof(WCHAR) but note the Tcl channel bug
+ * https://core.tcl-lang.org/tcl/tktview/b3977d199b08e3979a8da970553d5209b3042e9c
+ * will cause failures in test suite if close to max input line in the suite.
*/
-
-TCL_DECLARE_MUTEX(consoleMutex)
+#ifndef CONSOLE_BUFFER_SIZE
+#define CONSOLE_BUFFER_SIZE 8000 /* In bytes */
+#endif
/*
- * Bit masks used in the flags field of the ConsoleInfo structure below.
+ * Ring buffer for storing data. Actual data is from bufPtr[start]:bufPtr[size-1]
+ * and bufPtr[0]:bufPtr[length - (size-start)].
*/
-
-#define CONSOLE_PENDING (1<<0) /* Message is pending in the queue. */
-#define CONSOLE_ASYNC (1<<1) /* Channel is non-blocking. */
-#define CONSOLE_READ_OPS (1<<4) /* Channel supports read-related ops. */
-#define CONSOLE_RESET (1<<5) /* Console mode needs to be reset. */
+#if TCL_MAJOR_VERSION > 8
+typedef ptrdiff_t RingSizeT; /* Tcl9 TODO */
+#define RingSizeT_MAX PTRDIFF_MAX
+#else
+typedef int RingSizeT;
+#define RingSizeT_MAX INT_MAX
+#endif
+typedef struct RingBuffer {
+ char *bufPtr; /* Pointer to buffer storage */
+ RingSizeT capacity; /* Size of the buffer in RingBufferChar */
+ RingSizeT start; /* Start of the data within the buffer. */
+ RingSizeT length; /* Number of RingBufferChar*/
+} RingBuffer;
+#define RingBufferLength(ringPtr_) ((ringPtr_)->length)
+#define RingBufferHasFreeSpace(ringPtr_) ((ringPtr_)->length < (ringPtr_)->capacity)
+#define RINGBUFFER_ASSERT(ringPtr_) assert(RingBufferCheck(ringPtr_))
/*
- * Bit masks used in the sharedFlags field of the ConsoleInfo structure below.
+ * The Win32 console API does not support non-blocking I/O in any form. Thus
+ * the actual calls are made on a separate thread. Moreover, separate
+ * threads are needed for each handle because (for example) blocking on user
+ * input on stdin should not prevent output to stdout when non-blocking i/o
+ * is configured at the script level.
+ *
+ * In the input (e.g. stdin) case, the console stdin thread is the producer
+ * writing to the buffer ring buffer. The Tcl interpreter threads are the
+ * consumer. For the output (e.g. stdout/stderr) case, the Tcl interpreter
+ * are the producers while the console stdout/stderr threads are the
+ * consumers.
+ *
+ * Consoles are identified purely by handles and multiple threads may open
+ * them (as stdin/stdout/stderr are shared).
+ *
+ * Note on reference counting - a ConsoleHandleInfo instance has multiple
+ * references to it - one each from every channel that is attached to it
+ * plus one from the console thread itself which also serves as the reference
+ * from gConsoleHandleInfoList.
*/
-
-#define CONSOLE_EOF (1<<2) /* Console has reached EOF. */
-#define CONSOLE_BUFFERED (1<<3) /* Data was read into a buffer by the reader
- * thread. */
-
-#define CONSOLE_BUFFER_SIZE (8*1024)
-
-/*
- * Structure containing handles associated with one of the special console
- * threads.
- */
-
-typedef struct {
- HANDLE thread; /* Handle to reader or writer thread. */
- HANDLE readyEvent; /* Manual-reset event to signal _to_ the main
- * thread when the worker thread has finished
- * waiting for its normal work to happen. */
- TclPipeThreadInfo *TI; /* Thread info structure of writer and reader. */
-} ConsoleThreadInfo;
+typedef struct ConsoleHandleInfo {
+ struct ConsoleHandleInfo *nextPtr; /* Process-global list of consoles */
+ HANDLE console; /* Console handle */
+ HANDLE consoleThread; /* Handle to thread doing actual i/o on the console */
+ SRWLOCK lock; /* Controls access to this structure.
+ * Cheaper than CRITICAL_SECTION but note does not
+ * support recursive locks or Try* style attempts.*/
+ CONDITION_VARIABLE consoleThreadCV;/* For awakening console thread */
+ CONDITION_VARIABLE interpThreadCV; /* For awakening interpthread(s) */
+ RingBuffer buffer; /* Buffer for data transferred between console
+ * threads and Tcl threads. For input consoles,
+ * written by the console thread and read by Tcl
+ * threads. The converse for output threads */
+ DWORD initMode; /* Initial console mode. */
+ DWORD lastError; /* An error caused by the last background
+ * operation. Set to 0 if no error has been
+ * detected. */
+ int numRefs; /* See comments above */
+ int permissions; /* TCL_READABLE for input consoles, TCL_WRITABLE
+ * for output. Only one or the other can be set. */
+ int flags;
+#define CONSOLE_DATA_AWAITED 0x0001 /* An interpreter is awaiting data */
+} ConsoleHandleInfo;
/*
* This structure describes per-instance data for a console based channel.
+ *
+ * Note on locking - this structure has no locks because it is accessed
+ * only from the thread owning channel EXCEPT when a console traverses it
+ * looking for a channel that is watching for events on the console. Even
+ * in that case, no locking is required because that access is only under
+ * the consoleLock lock which prevents the channel from being removed from
+ * the gWatchingChannelList which in turn means it will not be deallocated
+ * from under the console thread. Access to individual fields does not need
+ * to be controlled because
+ * - the console thread does not write to any fields
+ * - changes to the nextWatchingChannelPtr field
+ * - changes to other fields do not matter because after being read for
+ * queueing events, they are verified again when the event is received
+ * in the interpreter thread (since they could have changed anyways while
+ * the event was in-flight on the event queue)
+ *
+ * Note on reference counting - a structure instance may be referenced from
+ * three places:
+ * - the Tcl channel subsystem. This reference is created when on channel
+ * opening and dropped on channel close. This also covers the reference
+ * from gWatchingChannelList since queueing / dequeuing from that list
+ * happens in conjunction with channel operations.
+ * - the Tcl event queue entries. This reference is added when the event
+ * is queued and dropped on receipt.
*/
-
-typedef struct ConsoleInfo {
- HANDLE handle;
- int type;
- struct ConsoleInfo *nextPtr;/* Pointer to next registered console. */
+typedef struct ConsoleChannelInfo {
+ HANDLE handle; /* Console handle */
+ Tcl_ThreadId threadId; /* Id of owning thread */
+ struct ConsoleChannelInfo
+ *nextWatchingChannelPtr; /* Pointer to next channel watching events. */
Tcl_Channel channel; /* Pointer to channel structure. */
- int validMask; /* OR'ed combination of TCL_READABLE,
+ DWORD initMode; /* Initial console mode. */
+ int numRefs; /* See comments above */
+ int permissions; /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
* which operations are valid on the file. */
int watchMask; /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
* which events should be reported. */
- int flags; /* State flags, see above for a list. */
- Tcl_ThreadId threadId; /* Thread to which events should be reported.
- * This value is used by the reader/writer
- * threads. */
- ConsoleThreadInfo writer; /* A specialized thread for handling
- * asynchronous writes to the console; the
- * waiting starts when a control event is sent,
- * and a reset event is sent back to the main
- * thread when the write is done. */
- ConsoleThreadInfo reader; /* A specialized thread for handling
- * asynchronous reads from the console; the
- * waiting starts when a control event is sent,
- * and a reset event is sent back to the main
- * thread when input is available. */
- DWORD writeError; /* An error caused by the last background
- * write. Set to 0 if no error has been
- * detected. This word is shared with the
- * writer thread so access must be
- * synchronized with the writable object. */
- char *writeBuf; /* Current background output buffer. Access is
- * synchronized with the writable object. */
- int writeBufLen; /* Size of write buffer. Access is
- * synchronized with the writable object. */
- int toWrite; /* Current amount to be written. Access is
- * synchronized with the writable object. */
- int readFlags; /* Flags that are shared with the reader
- * thread. Access is synchronized with the
- * readable object. */
- int bytesRead; /* Number of bytes in the buffer. */
- int offset; /* Number of bytes read out of the buffer. */
- DWORD initMode; /* Initial console mode. */
- char buffer[CONSOLE_BUFFER_SIZE];
- /* Data consumed by reader thread. */
-} ConsoleInfo;
-
-typedef struct {
- /*
- * The following pointer refers to the head of the list of consoles that
- * are being watched for file events.
- */
-
- ConsoleInfo *firstConsolePtr;
-} ThreadSpecificData;
-
-static Tcl_ThreadDataKey dataKey;
+ int flags; /* State flags */
+#define CONSOLE_EVENT_QUEUED 0x0001 /* Notification event already queued */
+#define CONSOLE_ASYNC 0x0002 /* Channel is non-blocking. */
+#define CONSOLE_READ_OPS 0x0004 /* Channel supports read-related ops. */
+} ConsoleChannelInfo;
/*
* The following structure is what is added to the Tcl event queue when
@@ -126,51 +198,96 @@ static Tcl_ThreadDataKey dataKey;
*/
typedef struct {
- Tcl_Event header; /* Information that is standard for all
- * events. */
- ConsoleInfo *infoPtr; /* Pointer to console info structure. Note
- * that we still have to verify that the
- * console exists before dereferencing this
- * pointer. */
+ Tcl_Event header; /* Information that is standard for all events. */
+ ConsoleChannelInfo *chanInfoPtr; /* Pointer to console info structure. Note
+ * that we still have to verify that the
+ * console exists before dereferencing this
+ * pointer. */
} ConsoleEvent;
/*
* Declarations for functions used only in this file.
*/
-static int ConsoleBlockModeProc(ClientData instanceData,
- int mode);
-static void ConsoleCheckProc(ClientData clientData, int flags);
-static int ConsoleCloseProc(ClientData instanceData,
- Tcl_Interp *interp, int flags);
-static int ConsoleEventProc(Tcl_Event *evPtr, int flags);
-static void ConsoleExitHandler(ClientData clientData);
-static int ConsoleGetHandleProc(ClientData instanceData,
- int direction, ClientData *handlePtr);
-static int ConsoleGetOptionProc(ClientData instanceData,
- Tcl_Interp *interp, const char *optionName,
- Tcl_DString *dsPtr);
-static void ConsoleInit(void);
-static int ConsoleInputProc(ClientData instanceData, char *buf,
- int toRead, int *errorCode);
-static int ConsoleOutputProc(ClientData instanceData,
- const char *buf, int toWrite, int *errorCode);
+static int ConsoleBlockModeProc(ClientData instanceData, int mode);
+static void ConsoleCheckProc(ClientData clientData, int flags);
+static int ConsoleCloseProc(ClientData instanceData,
+ Tcl_Interp *interp, int flags);
+static int ConsoleEventProc(Tcl_Event *evPtr, int flags);
+static void ConsoleExitHandler(ClientData clientData);
+static int ConsoleGetHandleProc(ClientData instanceData,
+ int direction, ClientData *handlePtr);
+static int ConsoleGetOptionProc(ClientData instanceData,
+ Tcl_Interp *interp, const char *optionName,
+ Tcl_DString *dsPtr);
+static void ConsoleInit(void);
+static int ConsoleInputProc(ClientData instanceData, char *buf,
+ int toRead, int *errorCode);
+static int ConsoleOutputProc(ClientData instanceData,
+ const char *buf, int toWrite, int *errorCode);
+static int ConsoleSetOptionProc(ClientData instanceData,
+ Tcl_Interp *interp, const char *optionName,
+ const char *value);
+static void ConsoleSetupProc(ClientData clientData, int flags);
+static void ConsoleWatchProc(ClientData instanceData, int mask);
+static void ProcExitHandler(ClientData clientData);
+static void ConsoleThreadActionProc(ClientData instanceData, int action);
+static DWORD ReadConsoleChars(HANDLE hConsole, WCHAR *lpBuffer,
+ RingSizeT nChars, RingSizeT *nCharsReadPtr);
+static DWORD WriteConsoleChars(HANDLE hConsole,
+ const WCHAR *lpBuffer, RingSizeT nChars,
+ RingSizeT *nCharsWritten);
+static void RingBufferInit(RingBuffer *ringPtr, RingSizeT capacity);
+static void RingBufferClear(RingBuffer *ringPtr);
+static RingSizeT RingBufferIn(RingBuffer *ringPtr, const char *srcPtr,
+ RingSizeT srcLen, int partialCopyOk);
+static RingSizeT RingBufferOut(RingBuffer *ringPtr, char *dstPtr,
+ RingSizeT dstCapacity, int partialCopyOk);
+static ConsoleHandleInfo *AllocateConsoleHandleInfo(HANDLE consoleHandle,
+ int permissions);
+static ConsoleHandleInfo *FindConsoleInfo(const ConsoleChannelInfo *);
static DWORD WINAPI ConsoleReaderThread(LPVOID arg);
-static int ConsoleSetOptionProc(ClientData instanceData,
- Tcl_Interp *interp, const char *optionName,
- const char *value);
-static void ConsoleSetupProc(ClientData clientData, int flags);
-static void ConsoleWatchProc(ClientData instanceData, int mask);
static DWORD WINAPI ConsoleWriterThread(LPVOID arg);
-static void ProcExitHandler(ClientData clientData);
-static int WaitForRead(ConsoleInfo *infoPtr, int blocking);
-static void ConsoleThreadActionProc(ClientData instanceData,
- int action);
-static BOOL ReadConsoleBytes(HANDLE hConsole, LPVOID lpBuffer,
- DWORD nbytes, LPDWORD nbytesread);
-static BOOL WriteConsoleBytes(HANDLE hConsole,
- const void *lpBuffer, DWORD nbytes,
- LPDWORD nbyteswritten);
+static void NudgeWatchers(HANDLE consoleHandle);
+#ifndef NDEBUG
+static int RingBufferCheck(const RingBuffer *ringPtr);
+#endif
+
+/*
+ * Static data.
+ */
+
+typedef struct {
+ /* Currently this struct is only used to detect thread initialization */
+ int notUsed; /* Dummy field */
+} ThreadSpecificData;
+static Tcl_ThreadDataKey dataKey;
+
+/*
+ * All access to static data is controlled through a single process-wide
+ * lock. A process can have only a single console at a time, with three
+ * handles for stdin, stdout and stderr. Creation/destruction of consoles is
+ * a relatively rare event (currently only possible during process start),
+ * the number of consoles (as opposed to channels) is small (only stdin,
+ * stdout and stderr), and contention low. More finer-grained locking would
+ * likely not only complicate implementation but be slower due to multiple
+ * locks being held. Note console channels also differ from other Tcl
+ * channel types in that the channel<->OS descriptor mapping is not one-to-one.
+ */
+SRWLOCK gConsoleLock;
+
+
+/* Process-wide list of console handles. Access control through gConsoleLock */
+static ConsoleHandleInfo *gConsoleHandleInfoList;
+
+/*
+ * Process-wide list of channels that are listening for events. Again access
+ * control through gConsoleLock. Common list for all threads is simplifies
+ * locking and bookkeeping and is workable because in practice multiple
+ * threads are very unlikely to be all waiting on stdin (not workable
+ * because input would be randomly distributed to threads)
+ */
+static ConsoleChannelInfo *gWatchingChannelList;
/*
* This structure describes the channel type structure for command console
@@ -178,82 +295,317 @@ static BOOL WriteConsoleBytes(HANDLE hConsole,
*/
static const Tcl_ChannelType consoleChannelType = {
- "console", /* Type name. */
- TCL_CHANNEL_VERSION_5, /* v5 channel */
- TCL_CLOSE2PROC, /* Close proc. */
- ConsoleInputProc, /* Input proc. */
- ConsoleOutputProc, /* Output proc. */
- NULL, /* Seek proc. */
- ConsoleSetOptionProc, /* Set option proc. */
- ConsoleGetOptionProc, /* Get option proc. */
- ConsoleWatchProc, /* Set up notifier to watch the channel. */
- ConsoleGetHandleProc, /* Get an OS handle from channel. */
- ConsoleCloseProc, /* close2proc. */
- ConsoleBlockModeProc, /* Set blocking or non-blocking mode. */
- NULL, /* Flush proc. */
- NULL, /* Handler proc. */
- NULL, /* Wide seek proc. */
- ConsoleThreadActionProc, /* Thread action proc. */
- NULL /* Truncation proc. */
+ "console", /* Type name. */
+ TCL_CHANNEL_VERSION_5, /* v5 channel */
+ TCL_CLOSE2PROC, /* Close proc. */
+ ConsoleInputProc, /* Input proc. */
+ ConsoleOutputProc, /* Output proc. */
+ NULL, /* Seek proc. */
+ ConsoleSetOptionProc, /* Set option proc. */
+ ConsoleGetOptionProc, /* Get option proc. */
+ ConsoleWatchProc, /* Set up notifier to watch the channel. */
+ ConsoleGetHandleProc, /* Get an OS handle from channel. */
+ ConsoleCloseProc, /* close2proc. */
+ ConsoleBlockModeProc, /* Set blocking or non-blocking mode. */
+ NULL, /* Flush proc. */
+ NULL, /* Handler proc. */
+ NULL, /* Wide seek proc. */
+ ConsoleThreadActionProc, /* Thread action proc. */
+ NULL /* Truncation proc. */
};
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * RingBufferInit --
+ *
+ * Initializes the ring buffer to a given size.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Panics on allocation failure.
+ *
+ *------------------------------------------------------------------------
+ */
+static void
+RingBufferInit(RingBuffer *ringPtr, RingSizeT capacity)
+{
+ if (capacity <= 0 || capacity > RingSizeT_MAX) {
+ Tcl_Panic("Internal error: invalid ring buffer capacity requested.");
+ }
+ ringPtr->bufPtr = (char *)ckalloc(capacity);
+ ringPtr->capacity = capacity;
+ ringPtr->start = 0;
+ ringPtr->length = 0;
+}
/*
- *----------------------------------------------------------------------
+ *------------------------------------------------------------------------
*
- * ReadConsoleBytes, WriteConsoleBytes --
+ * RingBufferClear
*
- * Wrapper for ReadConsoleW, that takes and returns number of bytes
- * instead of number of WCHARS.
+ * Clears the contents of a ring buffer.
*
- *----------------------------------------------------------------------
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The allocated internal buffer is freed.
+ *
+ *------------------------------------------------------------------------
*/
+static void
+RingBufferClear(RingBuffer *ringPtr)
+{
+ if (ringPtr->bufPtr) {
+ ckfree(ringPtr->bufPtr);
+ ringPtr->bufPtr = NULL;
+ }
+ ringPtr->capacity = 0;
+ ringPtr->start = 0;
+ ringPtr->length = 0;
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * RingBufferIn --
+ *
+ * Appends data to the ring buffer.
+ *
+ * Results:
+ * Returns number of bytes copied.
+ *
+ * Side effects:
+ * Internal buffer is updated.
+ *
+ *------------------------------------------------------------------------
+ */
+static RingSizeT
+RingBufferIn(
+ RingBuffer *ringPtr,
+ const char *srcPtr, /* Source to be copied */
+ RingSizeT srcLen, /* Length of source */
+ int partialCopyOk /* If true, partial copy is permitted */
+ )
+{
+ RingSizeT freeSpace;
+
+ RINGBUFFER_ASSERT(ringPtr);
-static BOOL
-ReadConsoleBytes(
+ freeSpace = ringPtr->capacity - ringPtr->length;
+ if (freeSpace < srcLen) {
+ if (!partialCopyOk) {
+ return 0;
+ }
+ /* Copy only as much as free space allows */
+ srcLen = freeSpace;
+ }
+
+ if (ringPtr->capacity - ringPtr->start > ringPtr->length) {
+ /* There is room at the back */
+ RingSizeT endSpaceStart = ringPtr->start + ringPtr->length;
+ RingSizeT endSpace = ringPtr->capacity - endSpaceStart;
+ if (endSpace >= srcLen) {
+ /* Everything fits at the back */
+ memmove(endSpaceStart + ringPtr->bufPtr, srcPtr, srcLen);
+ } else {
+ /* srcLen > endSpace */
+ memmove(endSpaceStart + ringPtr->bufPtr, srcPtr, endSpace);
+ memmove(ringPtr->bufPtr, endSpace + srcPtr, srcLen - endSpace);
+ }
+ } else {
+ /* No room at the back. Existing data wrap to front. */
+ RingSizeT wrapLen =
+ ringPtr->start + ringPtr->length - ringPtr->capacity;
+ memmove(wrapLen + ringPtr->bufPtr, srcPtr, srcLen);
+ }
+
+ ringPtr->length += srcLen;
+
+ RINGBUFFER_ASSERT(ringPtr);
+
+ return srcLen;
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * RingBufferOut --
+ *
+ * Moves data out of the ring buffer. If dstPtr is NULL, the data
+ * is simply removed.
+ *
+ * Results:
+ * Returns number of bytes copied or removed.
+ *
+ * Side effects:
+ * Internal buffer is updated.
+ *
+ *------------------------------------------------------------------------
+ */
+static RingSizeT
+RingBufferOut(RingBuffer *ringPtr,
+ char *dstPtr, /* Buffer for output data. May be NULL */
+ RingSizeT dstCapacity, /* Size of buffer */
+ int partialCopyOk) /* If true, return what's available */
+{
+ RingSizeT leadLen;
+
+ RINGBUFFER_ASSERT(ringPtr);
+
+ if (dstCapacity > ringPtr->length) {
+ if (dstPtr && !partialCopyOk) {
+ return 0;
+ }
+ dstCapacity = ringPtr->length;
+ }
+
+ if (ringPtr->start <= (ringPtr->capacity - ringPtr->length)) {
+ /* No content wrap around. So leadLen is entire content */
+ leadLen = ringPtr->length;
+ } else {
+ /* Content wraps around so lead segment stretches to end of buffer */
+ leadLen = ringPtr->capacity - ringPtr->start;
+ }
+ if (leadLen >= dstCapacity) {
+ if (dstPtr) {
+ memmove(dstPtr, ringPtr->start + ringPtr->bufPtr, dstCapacity);
+ }
+ ringPtr->start += dstCapacity;
+ } else {
+ RingSizeT wrapLen = dstCapacity - leadLen;
+ if (dstPtr) {
+ memmove(dstPtr,
+ ringPtr->start + ringPtr->bufPtr,
+ leadLen);
+ memmove(
+ leadLen + dstPtr, ringPtr->bufPtr, wrapLen);
+ }
+ ringPtr->start = wrapLen;
+ }
+
+ ringPtr->length -= dstCapacity;
+ if (ringPtr->start == ringPtr->capacity || ringPtr->length == 0) {
+ ringPtr->start = 0;
+ }
+
+ RINGBUFFER_ASSERT(ringPtr);
+
+ return dstCapacity;
+}
+
+#ifndef NDEBUG
+static int
+RingBufferCheck(const RingBuffer *ringPtr)
+{
+ return (ringPtr->bufPtr != NULL && ringPtr->capacity == CONSOLE_BUFFER_SIZE
+ && ringPtr->start < ringPtr->capacity
+ && ringPtr->length <= ringPtr->capacity);
+}
+#endif
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * ReadConsoleChars --
+ *
+ * Wrapper for ReadConsoleW.
+ *
+ * Results:
+ * Returns 0 on success, else Windows error code.
+ *
+ * Side effects:
+ * On success the number of characters (not bytes) read is stored in
+ * *nCharsReadPtr. This will be 0 if the operation was interrupted by
+ * a Ctrl-C or a CancelIo call.
+ *
+ *------------------------------------------------------------------------
+ */
+static DWORD
+ReadConsoleChars(
HANDLE hConsole,
- LPVOID lpBuffer,
- DWORD nbytes,
- LPDWORD nbytesread)
+ WCHAR *lpBuffer,
+ RingSizeT nChars,
+ RingSizeT *nCharsReadPtr)
{
- DWORD ntchars;
+ DWORD nRead;
BOOL result;
/*
- * If user types a Ctrl-Break or Ctrl-C, ReadConsole will return
- * success with ntchars == 0 and GetLastError() will be
- * ERROR_OPERATION_ABORTED. We do not want to treat this case
- * as EOF so we will loop around again. If no Ctrl signal handlers
- * have been established, the default signal OS handler in a separate
- * thread will terminate the program. If a Ctrl signal handler
- * has been established (through an extension for example), it
- * will run and take whatever action it deems appropriate.
+ * If user types a Ctrl-Break or Ctrl-C, ReadConsole will return success
+ * with ntchars == 0 and GetLastError() will be ERROR_OPERATION_ABORTED.
+ * If no Ctrl signal handlers have been established, the default signal
+ * OS handler in a separate thread will terminate the program. If a Ctrl
+ * signal handler has been established (through an extension for
+ * example), it will run and take whatever action it deems appropriate.
+ *
+ * If one thread closes its channel, it calls CancelSynchronousIo on the
+ * console handle which results again in success being returned and
+ * GetLastError() being ERROR_OPERATION_ABORTED but ntchars in
+ * unmodified.
+ *
+ * In both cases above we will return success but with nbytesread as 0.
+ * This allows caller to check for thread termination etc.
+ *
+ * See https://bugs.python.org/issue30237
+ * or https://github.com/microsoft/terminal/issues/12143
*/
- do {
- result = ReadConsoleW(hConsole, lpBuffer, nbytes / sizeof(WCHAR), &ntchars,
- NULL);
- } while (result && ntchars == 0 && GetLastError() == ERROR_OPERATION_ABORTED);
- if (nbytesread != NULL) {
- *nbytesread = ntchars * sizeof(WCHAR);
- }
- return result;
+ nRead = (DWORD)-1;
+ result = ReadConsoleW(hConsole, lpBuffer, nChars, &nRead, NULL);
+ if (result) {
+ if ((nRead == 0 || nRead == (DWORD)-1)
+ && GetLastError() == ERROR_OPERATION_ABORTED) {
+ nRead = 0;
+ }
+ *nCharsReadPtr = nRead;
+ return 0;
+ } else
+ return GetLastError();
}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * WriteConsoleChars --
+ *
+ * Wrapper for WriteConsoleW.
+ *
+ * Results:
+ * Returns 0 on success, Windows error code on failure.
+ *
+ * Side effects:
+ * On success the number of characters (not bytes) written is stored in
+ * *nCharsWrittenPtr. This will be 0 if the operation was interrupted by
+ * a Ctrl-C or a CancelIo call.
+ *
+ *------------------------------------------------------------------------
+ */
-static BOOL
-WriteConsoleBytes(
+static DWORD
+WriteConsoleChars(
HANDLE hConsole,
- const void *lpBuffer,
- DWORD nbytes,
- LPDWORD nbyteswritten)
+ const WCHAR *lpBuffer,
+ RingSizeT nChars,
+ RingSizeT *nCharsWrittenPtr)
{
- DWORD ntchars;
+ DWORD nCharsWritten;
BOOL result;
- result = WriteConsoleW(hConsole, lpBuffer, nbytes / sizeof(WCHAR), &ntchars,
- NULL);
- if (nbyteswritten != NULL) {
- *nbyteswritten = ntchars * sizeof(WCHAR);
+ /* See comments in ReadConsoleChars, not sure that applies here */
+ nCharsWritten = (DWORD)-1;
+ result = WriteConsoleW(hConsole, lpBuffer, nChars, &nCharsWritten, NULL);
+ if (result) {
+ if (nCharsWritten == (DWORD) -1) {
+ nCharsWritten = 0;
+ }
+ *nCharsWrittenPtr = nCharsWritten;
+ return 0;
+ } else {
+ return GetLastError();
}
- return result;
}
/*
@@ -280,19 +632,19 @@ ConsoleInit(void)
* is a speed enhancement.
*/
- if (!initialized) {
- Tcl_MutexLock(&consoleMutex);
- if (!initialized) {
- initialized = 1;
+ if (!gInitialized) {
+ AcquireSRWLockExclusive(&gConsoleLock);
+ if (!gInitialized) {
+ gInitialized = 1;
Tcl_CreateExitHandler(ProcExitHandler, NULL);
}
- Tcl_MutexUnlock(&consoleMutex);
+ ReleaseSRWLockExclusive(&gConsoleLock);
}
if (TclThreadDataKeyGet(&dataKey) == NULL) {
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- tsdPtr->firstConsolePtr = NULL;
+ tsdPtr->notUsed = 0;
Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
Tcl_CreateThreadExitHandler(ConsoleExitHandler, NULL);
}
@@ -343,9 +695,46 @@ static void
ProcExitHandler(
TCL_UNUSED(ClientData))
{
- Tcl_MutexLock(&consoleMutex);
- initialized = 0;
- Tcl_MutexUnlock(&consoleMutex);
+ AcquireSRWLockExclusive(&gConsoleLock);
+ gInitialized = 0;
+ ReleaseSRWLockExclusive(&gConsoleLock);
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * NudgeWatchers --
+ *
+ * Wakes up all threads which have file event watchers on the passed
+ * console handle.
+ *
+ * The function locks and releases gConsoleLock.
+ * Caller must not be holding locks that will violate lock hierarchy.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * As above.
+ *------------------------------------------------------------------------
+ */
+void NudgeWatchers (HANDLE consoleHandle)
+{
+ ConsoleChannelInfo *chanInfoPtr;
+ AcquireSRWLockShared(&gConsoleLock); /* Shared-read lock */
+ for (chanInfoPtr = gWatchingChannelList; chanInfoPtr;
+ chanInfoPtr = chanInfoPtr->nextWatchingChannelPtr) {
+ /*
+ * Notify channels interested in our handle AND that have
+ * a thread attached.
+ * No lock needed for chanInfoPtr. See ConsoleChannelInfo.
+ */
+ if (chanInfoPtr->handle == consoleHandle
+ && chanInfoPtr->threadId != NULL) {
+ Tcl_ThreadAlert(chanInfoPtr->threadId);
+ }
+ }
+ ReleaseSRWLockShared(&gConsoleLock);
}
/*
@@ -354,7 +743,9 @@ ProcExitHandler(
* ConsoleSetupProc --
*
* This procedure is invoked before Tcl_DoOneEvent blocks waiting for an
- * event.
+ * event. It walks the channel list and if any input channel has data
+ * available or output channel has space for data, sets the event loop
+ * blocking time to 0 so that it will poll immediately.
*
* Results:
* None.
@@ -370,34 +761,45 @@ ConsoleSetupProc(
TCL_UNUSED(ClientData),
int flags) /* Event flags as passed to Tcl_DoOneEvent. */
{
- ConsoleInfo *infoPtr;
+ ConsoleChannelInfo *chanInfoPtr;
Tcl_Time blockTime = { 0, 0 };
int block = 1;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
if (!(flags & TCL_FILE_EVENTS)) {
return;
}
/*
- * Look to see if any events are already pending. If they are, poll.
+ * Walk the list of channels. See general comments for struct
+ * ConsoleChannelInfo with regard to locking and field access.
*/
-
- for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
- infoPtr = infoPtr->nextPtr) {
- if (infoPtr->watchMask & TCL_WRITABLE) {
- if (WaitForSingleObject(infoPtr->writer.readyEvent,
- 0) != WAIT_TIMEOUT) {
- block = 0;
- }
- }
- if (infoPtr->watchMask & TCL_READABLE) {
- if (WaitForRead(infoPtr, 0) >= 0) {
- block = 0;
+ AcquireSRWLockShared(&gConsoleLock); /* READ lock - no data modification */
+
+ for (chanInfoPtr = gWatchingChannelList; block && chanInfoPtr != NULL;
+ chanInfoPtr = chanInfoPtr->nextWatchingChannelPtr) {
+ ConsoleHandleInfo *handleInfoPtr;
+ handleInfoPtr = FindConsoleInfo(chanInfoPtr);
+ if (handleInfoPtr != NULL) {
+ AcquireSRWLockShared(&handleInfoPtr->lock);
+ /* Remember at most one of READABLE, WRITABLE set */
+ if (chanInfoPtr->watchMask & TCL_READABLE) {
+ if (RingBufferLength(&handleInfoPtr->buffer) > 0
+ || handleInfoPtr->lastError != ERROR_SUCCESS) {
+ block = 0; /* Input data available */
+ }
+ } else if (chanInfoPtr->watchMask & TCL_WRITABLE) {
+ if (RingBufferHasFreeSpace(&handleInfoPtr->buffer)) {
+ /* TCL_WRITABLE */
+ block = 0; /* Output space available */
+ }
}
+ ReleaseSRWLockShared(&handleInfoPtr->lock);
}
}
+ ReleaseSRWLockShared(&gConsoleLock);
+
if (!block) {
+ /* At least one channel is readable/writable. Set block time to 0 */
Tcl_SetMaxBlockTime(&blockTime);
}
}
@@ -424,54 +826,85 @@ ConsoleCheckProc(
TCL_UNUSED(ClientData),
int flags) /* Event flags as passed to Tcl_DoOneEvent. */
{
- ConsoleInfo *infoPtr;
+ ConsoleChannelInfo *chanInfoPtr;
+ Tcl_ThreadId me;
int needEvent;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
if (!(flags & TCL_FILE_EVENTS)) {
return;
}
+ me = Tcl_GetCurrentThread();
+
/*
- * Queue events for any ready consoles that don't already have events
- * queued.
+ * Acquire a shared lock. Note this is ok even though we potentially
+ * modify the chanInfoPtr->flags because chanInfoPtr is only modified
+ * when it belongs to this thread and no other thread will write to it.
+ * THe shared lock is intended to protect the global gWatchingChannelList
+ * as we traverse it.
*/
+ AcquireSRWLockShared(&gConsoleLock);
+
+ for (chanInfoPtr = gWatchingChannelList; chanInfoPtr != NULL;
+ chanInfoPtr = chanInfoPtr->nextWatchingChannelPtr) {
+ ConsoleHandleInfo *handleInfoPtr;
- for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
- infoPtr = infoPtr->nextPtr) {
- if (infoPtr->flags & CONSOLE_PENDING) {
+ if (chanInfoPtr->threadId != me) {
+ /* Some other thread owns the channel */
+ continue;
+ }
+ if (chanInfoPtr->flags & CONSOLE_EVENT_QUEUED) {
+ /* A notification event already queued. No point in another. */
continue;
}
- /*
- * Queue an event if the console is signaled for reading or writing.
- */
+ handleInfoPtr = FindConsoleInfo(chanInfoPtr);
+ /* Pointer is safe to access as we are holding gConsoleLock */
+
+ if (handleInfoPtr == NULL) {
+ /* Stale event */
+ continue;
+ }
needEvent = 0;
- if (infoPtr->watchMask & TCL_WRITABLE) {
- if (WaitForSingleObject(infoPtr->writer.readyEvent,
- 0) != WAIT_TIMEOUT) {
- needEvent = 1;
+ AcquireSRWLockShared(&handleInfoPtr->lock);
+ /* Rememeber channel is read or write, never both */
+ if (chanInfoPtr->watchMask & TCL_READABLE) {
+ if (RingBufferLength(&handleInfoPtr->buffer) > 0
+ || handleInfoPtr->lastError != ERROR_SUCCESS) {
+ needEvent = 1; /* Input data available or error/EOF */
}
+ /*
+ * TCL_READABLE watch means someone is looking out for data being
+ * available, let reader thread know. Note channel need not be
+ * ASYNC! (Bug [baa51423c2])
+ */
+ handleInfoPtr->flags |= CONSOLE_DATA_AWAITED;
+ WakeConditionVariable(&handleInfoPtr->consoleThreadCV);
}
-
- if (infoPtr->watchMask & TCL_READABLE) {
- if (WaitForRead(infoPtr, 0) >= 0) {
- needEvent = 1;
+ else if (chanInfoPtr->watchMask & TCL_WRITABLE) {
+ if (RingBufferHasFreeSpace(&handleInfoPtr->buffer)) {
+ needEvent = 1; /* Output space available */
}
}
+ ReleaseSRWLockShared(&handleInfoPtr->lock);
if (needEvent) {
ConsoleEvent *evPtr = (ConsoleEvent *)ckalloc(sizeof(ConsoleEvent));
- infoPtr->flags |= CONSOLE_PENDING;
+ /* See note above loop why this can be accessed without locks */
+ chanInfoPtr->flags |= CONSOLE_EVENT_QUEUED;
+ chanInfoPtr->numRefs += 1; /* So it does not go away while event
+ is in queue */
evPtr->header.proc = ConsoleEventProc;
- evPtr->infoPtr = infoPtr;
+ evPtr->chanInfoPtr = chanInfoPtr;
Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
}
}
+
+ ReleaseSRWLockShared(&gConsoleLock);
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -494,7 +927,7 @@ ConsoleBlockModeProc(
int mode) /* TCL_MODE_BLOCKING or
* TCL_MODE_NONBLOCKING. */
{
- ConsoleInfo *infoPtr = (ConsoleInfo *)instanceData;
+ ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
/*
* Consoles on Windows can not be switched between blocking and
@@ -505,9 +938,9 @@ ConsoleBlockModeProc(
*/
if (mode == TCL_MODE_NONBLOCKING) {
- infoPtr->flags |= CONSOLE_ASYNC;
+ chanInfoPtr->flags |= CONSOLE_ASYNC;
} else {
- infoPtr->flags &= ~CONSOLE_ASYNC;
+ chanInfoPtr->flags &= ~CONSOLE_ASYNC;
}
return 0;
}
@@ -530,102 +963,102 @@ ConsoleBlockModeProc(
static int
ConsoleCloseProc(
- ClientData instanceData, /* Pointer to ConsoleInfo structure. */
+ ClientData instanceData, /* Pointer to ConsoleChannelInfo structure. */
TCL_UNUSED(Tcl_Interp *),
int flags)
{
- ConsoleInfo *consolePtr = (ConsoleInfo *)instanceData;
+ ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
+ ConsoleHandleInfo *handleInfoPtr;
int errorCode = 0;
- ConsoleInfo *infoPtr, **nextPtrPtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ ConsoleChannelInfo **nextPtrPtr;
+ int closeHandle;
if ((flags & (TCL_CLOSE_READ | TCL_CLOSE_WRITE)) != 0) {
return EINVAL;
}
-
/*
- * Clean up the background thread if necessary. Note that this must be
- * done before we can close the file, since the thread may be blocking
- * trying to read from the console.
+ * Don't close the Win32 handle if the handle is a standard channel
+ * during the thread exit process. Otherwise, one thread may kill the
+ * stdio of another while exiting. Note an explicit close in script will
+ * still close the handle. That's historical behavior on all platforms.
*/
+ if (!TclInThreadExit()
+ || ((GetStdHandle(STD_INPUT_HANDLE) != chanInfoPtr->handle)
+ && (GetStdHandle(STD_OUTPUT_HANDLE) != chanInfoPtr->handle)
+ && (GetStdHandle(STD_ERROR_HANDLE) != chanInfoPtr->handle))) {
+ closeHandle = 1;
+ } else {
+ closeHandle = 0;
+ }
- if (consolePtr->reader.thread) {
- TclPipeThreadStop(&consolePtr->reader.TI, consolePtr->reader.thread);
- CloseHandle(consolePtr->reader.thread);
- CloseHandle(consolePtr->reader.readyEvent);
- consolePtr->reader.thread = NULL;
+ AcquireSRWLockExclusive(&gConsoleLock);
+
+ /* Remove channel from watchers' list */
+ for (nextPtrPtr = &gWatchingChannelList; *nextPtrPtr != NULL;
+ nextPtrPtr = &(*nextPtrPtr)->nextWatchingChannelPtr) {
+ if (*nextPtrPtr == (ConsoleChannelInfo *) chanInfoPtr) {
+ *nextPtrPtr = (*nextPtrPtr)->nextWatchingChannelPtr;
+ break;
+ }
}
- consolePtr->validMask &= ~TCL_READABLE;
- /*
- * Wait for the writer thread to finish the current buffer, then terminate
- * the thread and close the handles. If the channel is nonblocking, there
- * should be no pending write operations.
- */
+ handleInfoPtr = FindConsoleInfo(chanInfoPtr);
+ if (handleInfoPtr) {
+ /*
+ * Console thread may be blocked either waiting for console i/o
+ * or waiting on the condition variable for buffer empty/full
+ */
+ AcquireSRWLockShared(&handleInfoPtr->lock);
+
+ if (closeHandle) {
+ handleInfoPtr->console = INVALID_HANDLE_VALUE;
+ }
- if (consolePtr->writer.thread) {
- if (consolePtr->toWrite) {
+ /* Break the thread out of blocking console i/o */
+ handleInfoPtr->numRefs -= 1; /* Remove reference from this channel */
+ if (handleInfoPtr->numRefs == 1) {
/*
- * We only need to wait if there is something to write. This may
- * prevent infinite wait on exit. [Python Bug 216289]
+ * Abort the i/o if no other threads are listening on it.
+ * Note without this check, an input line will be skipped on
+ * the cancel.
*/
-
- WaitForSingleObject(consolePtr->writer.readyEvent, 5000);
+ CancelSynchronousIo(handleInfoPtr->consoleThread);
}
- TclPipeThreadStop(&consolePtr->writer.TI, consolePtr->writer.thread);
- CloseHandle(consolePtr->writer.thread);
- CloseHandle(consolePtr->writer.readyEvent);
- consolePtr->writer.thread = NULL;
- }
- consolePtr->validMask &= ~TCL_WRITABLE;
-
- /*
- * If the user has been tinkering with the mode, reset it now. We ignore
- * any errors from this; we're quite possibly about to close or exit
- * anyway.
- */
+ /*
+ * Wake up the console handling thread. Note we do not explicitly
+ * tell it handle is closed (below). It will find out on next access
+ */
+ WakeConditionVariable(&handleInfoPtr->consoleThreadCV);
- if ((consolePtr->flags & CONSOLE_READ_OPS) &&
- (consolePtr->flags & CONSOLE_RESET)) {
- SetConsoleMode(consolePtr->handle, consolePtr->initMode);
+ ReleaseSRWLockShared(&handleInfoPtr->lock);
}
- /*
- * Don't close the Win32 handle if the handle is a standard channel during
- * the thread exit process. Otherwise, one thread may kill the stdio of
- * another.
- */
+ ReleaseSRWLockExclusive(&gConsoleLock);
- if (!TclInThreadExit()
- || ((GetStdHandle(STD_INPUT_HANDLE) != consolePtr->handle)
- && (GetStdHandle(STD_OUTPUT_HANDLE) != consolePtr->handle)
- && (GetStdHandle(STD_ERROR_HANDLE) != consolePtr->handle))) {
- if (CloseHandle(consolePtr->handle) == FALSE) {
+ chanInfoPtr->channel = NULL;
+ chanInfoPtr->watchMask = 0;
+ chanInfoPtr->permissions = 0;
+
+ if (closeHandle && chanInfoPtr->handle != INVALID_HANDLE_VALUE) {
+ if (CloseHandle(chanInfoPtr->handle) == FALSE) {
Tcl_WinConvertError(GetLastError());
errorCode = errno;
}
+ chanInfoPtr->handle = INVALID_HANDLE_VALUE;
}
- consolePtr->watchMask &= consolePtr->validMask;
-
/*
- * Remove the file from the list of watched files.
+ * Note, we can check and manipulate numRefs without a lock because
+ * we have removed it from the watch queue so the console thread cannot
+ * get at it.
*/
-
- for (nextPtrPtr = &tsdPtr->firstConsolePtr, infoPtr = *nextPtrPtr;
- infoPtr != NULL;
- nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
- if (infoPtr == (ConsoleInfo *) consolePtr) {
- *nextPtrPtr = infoPtr->nextPtr;
- break;
- }
- }
- if (consolePtr->writeBuf != NULL) {
- ckfree(consolePtr->writeBuf);
- consolePtr->writeBuf = 0;
+ if (chanInfoPtr->numRefs > 1) {
+ /* There may be references already on the event queue */
+ chanInfoPtr->numRefs -= 1;
+ } else {
+ ckfree(chanInfoPtr);
}
- ckfree(consolePtr);
return errorCode;
}
@@ -647,80 +1080,144 @@ ConsoleCloseProc(
*
*----------------------------------------------------------------------
*/
-
static int
ConsoleInputProc(
ClientData instanceData, /* Console state. */
- char *buf, /* Where to store data read. */
+ char *bufPtr, /* Where to store data read. */
int bufSize, /* How much space is available in the
* buffer? */
int *errorCode) /* Where to store error code. */
{
- ConsoleInfo *infoPtr = (ConsoleInfo *)instanceData;
- DWORD count, bytesRead = 0;
- int result;
+ ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
+ ConsoleHandleInfo *handleInfoPtr;
+ RingSizeT numRead;
- *errorCode = 0;
-
- /*
- * Synchronize with the reader thread.
- */
-
- result = WaitForRead(infoPtr, (infoPtr->flags & CONSOLE_ASYNC) ? 0 : 1);
+ if (chanInfoPtr->handle == INVALID_HANDLE_VALUE) {
+ return 0; /* EOF */
+ }
- /*
- * If an error occurred, return immediately.
- */
+ *errorCode = 0;
- if (result == -1) {
- *errorCode = errno;
- return -1;
+ AcquireSRWLockShared(&gConsoleLock);
+ handleInfoPtr = FindConsoleInfo(chanInfoPtr);
+ if (handleInfoPtr == NULL) {
+ /* Really shouldn't happen since channel is holding a reference */
+ ReleaseSRWLockShared(&gConsoleLock);
+ return 0; /* EOF */
}
+ AcquireSRWLockExclusive(&handleInfoPtr->lock);
+ ReleaseSRWLockShared(&gConsoleLock); /* AFTER acquiring handleInfoPtr->lock */
- if (infoPtr->readFlags & CONSOLE_BUFFERED) {
+ while (1) {
+ numRead = RingBufferOut(&handleInfoPtr->buffer, bufPtr, bufSize, 1);
/*
- * Data is stored in the buffer.
+ * Note: even if channel is closed or has an error, as long there is
+ * buffered data, we will pass it up.
*/
+ if (numRead != 0) {
+ /* If console thread was blocked, awaken it */
+ if (chanInfoPtr->flags & CONSOLE_ASYNC) {
+ /* Async channels always want read ahead */
+ handleInfoPtr->flags |= CONSOLE_DATA_AWAITED;
+ WakeConditionVariable(&handleInfoPtr->consoleThreadCV);
+ }
+ break;
+ }
+ /*
+ * No data available.
+ * - If an error was recorded, generate that and reset it.
+ * - If EOF, indicate as much. It is up to the application to close
+ * the channel.
+ * - Otherwise, if non-blocking return EAGAIN or wait for more data.
+ */
+ if (handleInfoPtr->lastError != 0) {
+ if (handleInfoPtr->lastError == ERROR_INVALID_HANDLE) {
+ numRead = 0; /* Treat as EOF */
+ } else {
+ Tcl_WinConvertError(handleInfoPtr->lastError);
+ handleInfoPtr->lastError = 0;
+ *errorCode = Tcl_GetErrno();
+ numRead = -1;
+ }
+ break;
+ }
+ if (handleInfoPtr->console == INVALID_HANDLE_VALUE) {
+ /* EOF - break with numRead == 0 */
+ chanInfoPtr->handle = INVALID_HANDLE_VALUE;
+ break;
+ }
- if (bufSize < (infoPtr->bytesRead - infoPtr->offset)) {
- memcpy(buf, &infoPtr->buffer[infoPtr->offset], bufSize);
- bytesRead = bufSize;
- infoPtr->offset += bufSize;
- } else {
- memcpy(buf, &infoPtr->buffer[infoPtr->offset], bufSize);
- bytesRead = infoPtr->bytesRead - infoPtr->offset;
-
- /*
- * Reset the buffer.
- */
+ /* For async, tell caller we are blocked */
+ if (chanInfoPtr->flags & CONSOLE_ASYNC) {
+ *errorCode = EWOULDBLOCK;
+ numRead = -1;
+ break;
+ }
- infoPtr->readFlags &= ~CONSOLE_BUFFERED;
- infoPtr->offset = 0;
+ /*
+ * Blocking read. Just get data from directly from console. There
+ * is a small complication in that we can only read even number
+ * of bytes (wide-character API) and the destination buffer should be
+ * WCHAR aligned. If either condition is not met, we defer to the
+ * reader thread which handles these case rather than dealing with
+ * them here (which is a little trickier than it might sound.)
+ */
+ if ((1 & (ptrdiff_t)bufPtr) == 0 /* aligned buffer */
+ && bufSize > 1 /* Not single byte read */
+ ) {
+ DWORD lastError;
+ RingSizeT numChars;
+ ReleaseSRWLockExclusive(&handleInfoPtr->lock);
+ lastError = ReadConsoleChars(chanInfoPtr->handle,
+ (WCHAR *)bufPtr,
+ bufSize / sizeof(WCHAR),
+ &numChars);
+ /* NOTE lock released so DON'T break. Return instead */
+ if (lastError != ERROR_SUCCESS) {
+ Tcl_WinConvertError(lastError);
+ *errorCode = Tcl_GetErrno();
+ return -1;
+ } else if (numChars > 0) {
+ /* Successfully read something. */
+ return numChars * sizeof(WCHAR);
+ } else {
+ /*
+ * Ctrl-C/Ctrl-Brk interrupt. Loop around to retry.
+ * We have to reacquire the lock. No worried about handleInfoPtr
+ * having gone away since the channel holds a reference.
+ */
+ AcquireSRWLockExclusive(&handleInfoPtr->lock);
+ continue;
+ }
+ }
+ /*
+ * Deferring blocking read to reader thread.
+ * Release the lock and sleep. Note that because the channel
+ * holds a reference count on handleInfoPtr, it will not
+ * be deallocated while the lock is released.
+ */
+ handleInfoPtr->flags |= CONSOLE_DATA_AWAITED;
+ WakeConditionVariable(&handleInfoPtr->consoleThreadCV);
+ if (!SleepConditionVariableSRW(&handleInfoPtr->interpThreadCV,
+ &handleInfoPtr->lock,
+ INFINITE,
+ 0)) {
+ Tcl_WinConvertError(GetLastError());
+ *errorCode = Tcl_GetErrno();
+ numRead = -1;
+ break;
}
- return bytesRead;
+ /* Lock is reacquired, loop back to try again */
}
- /*
- * Attempt to read bufSize bytes. The read will return immediately if
- * there is any data available. Otherwise it will block until at least one
- * byte is available or an EOF occurs.
- */
-
- if (ReadConsoleBytes(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize,
- &count) == TRUE) {
- /*
- * TODO: This potentially writes beyond the limits specified
- * by the caller. In practice this is harmless, since all writes
- * are into ChannelBuffers, and those have padding, but still
- * ought to remove this, unless some Windows wizard can give
- * a reason not to.
- */
- buf[count] = '\0';
- return count;
+ if (chanInfoPtr->flags & CONSOLE_ASYNC) {
+ handleInfoPtr->flags |= CONSOLE_DATA_AWAITED;
+ WakeConditionVariable(&handleInfoPtr->consoleThreadCV);
}
- return -1;
+ ReleaseSRWLockExclusive(&handleInfoPtr->lock);
+ return numRead;
}
/*
@@ -740,7 +1237,6 @@ ConsoleInputProc(
*
*----------------------------------------------------------------------
*/
-
static int
ConsoleOutputProc(
ClientData instanceData, /* Console state. */
@@ -748,74 +1244,112 @@ ConsoleOutputProc(
int toWrite, /* How many bytes to write? */
int *errorCode) /* Where to store error code. */
{
- ConsoleInfo *infoPtr = (ConsoleInfo *)instanceData;
- ConsoleThreadInfo *threadInfo = &infoPtr->writer;
- DWORD bytesWritten, timeout;
+ ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
+ ConsoleHandleInfo *handleInfoPtr;
+ RingSizeT numWritten;
*errorCode = 0;
- /* avoid blocking if pipe-thread exited */
- timeout = (infoPtr->flags & CONSOLE_ASYNC) || !TclPipeThreadIsAlive(&threadInfo->TI)
- || TclInExit() || TclInThreadExit() ? 0 : INFINITE;
- if (WaitForSingleObject(threadInfo->readyEvent, timeout) == WAIT_TIMEOUT) {
- /*
- * The writer thread is blocked waiting for a write to complete and
- * the channel is in non-blocking mode.
- */
-
- errno = EWOULDBLOCK;
- goto error;
+ if (chanInfoPtr->handle == INVALID_HANDLE_VALUE) {
+ /* Some other thread would have *previously* closed the stdio handle */
+ *errorCode = EPIPE;
+ return -1;
}
- /*
- * Check for a background error on the last write.
- */
-
- if (infoPtr->writeError) {
- Tcl_WinConvertError(infoPtr->writeError);
- infoPtr->writeError = 0;
- goto error;
+ AcquireSRWLockShared(&gConsoleLock);
+ handleInfoPtr = FindConsoleInfo(chanInfoPtr);
+ if (handleInfoPtr == NULL) {
+ /* Really shouldn't happen since channel is holding a reference */
+ *errorCode = EPIPE;
+ ReleaseSRWLockShared(&gConsoleLock);
+ return -1;
}
+ AcquireSRWLockExclusive(&handleInfoPtr->lock);
+ ReleaseSRWLockShared(&gConsoleLock); /* AFTER acquiring handleInfoPtr->lock */
+
+ /* Keep looping until all written. Break out for async and errors */
+ numWritten = 0;
+ while (1) {
+ /* Check for error and closing on every loop. */
+ if (handleInfoPtr->lastError != 0) {
+ Tcl_WinConvertError(handleInfoPtr->lastError);
+ *errorCode = Tcl_GetErrno();
+ numWritten = -1;
+ break;
+ }
+ if (handleInfoPtr->console == INVALID_HANDLE_VALUE) {
+ *errorCode = EPIPE;
+ chanInfoPtr->handle = INVALID_HANDLE_VALUE;
+ numWritten = -1;
+ break;
+ }
- if (infoPtr->flags & CONSOLE_ASYNC) {
/*
- * The console is non-blocking, so copy the data into the output
- * buffer and restart the writer thread.
+ * We can either write directly or through the console thread's
+ * ring buffer. We have to do the latter when
+ * (1) the operation is async since WriteConsoleChars is always blocking
+ * (2) when there is already data in the ring buffer because we don't
+ * want to reorder output from within a thread
+ * (3) when there are an odd number of bytes since WriteConsole
+ * takes whole WCHARs
+ * (4) when the pointer is not aligned on WCHAR
+ * The ring buffer deals with cases (3) and (4). It would be harder
+ * to duplicate that here.
*/
-
- if (toWrite > infoPtr->writeBufLen) {
+ if ((chanInfoPtr->flags & CONSOLE_ASYNC) /* Case (1) */
+ || RingBufferLength(&handleInfoPtr->buffer) != 0 /* Case (2) */
+ || (toWrite & 1) != 0 /* Case (3) */
+ || (PTR2INT(buf) & 1) != 0 /* Case (4) */
+ ) {
+ numWritten += RingBufferIn(&handleInfoPtr->buffer,
+ numWritten + buf,
+ toWrite - numWritten,
+ 1);
+ if (numWritten == toWrite || chanInfoPtr->flags & CONSOLE_ASYNC) {
+ /* All done or async, just accept whatever was written */
+ break;
+ }
/*
- * Reallocate the buffer to be large enough to hold the data.
+ * Release the lock and sleep. Note that because the channel
+ * holds a reference count on handleInfoPtr, it will not
+ * be deallocated while the lock is released.
*/
-
- if (infoPtr->writeBuf) {
- ckfree(infoPtr->writeBuf);
+ WakeConditionVariable(&handleInfoPtr->consoleThreadCV);
+ if (!SleepConditionVariableSRW(&handleInfoPtr->interpThreadCV,
+ &handleInfoPtr->lock,
+ INFINITE,
+ 0)) {
+ /* Report the error */
+ Tcl_WinConvertError(GetLastError());
+ *errorCode = Tcl_GetErrno();
+ numWritten = -1;
+ break;
+ }
+ } else {
+ /* Direct output */
+ DWORD winStatus;
+ HANDLE consoleHandle = handleInfoPtr->console;
+ /* Unlock before blocking in WriteConsole */
+ ReleaseSRWLockExclusive(&handleInfoPtr->lock);
+ /* UNLOCKED so return, DON'T break out of loop as it will unlock again! */
+ winStatus = WriteConsoleChars(consoleHandle,
+ (WCHAR *)buf,
+ toWrite / sizeof(WCHAR),
+ &numWritten);
+ if (winStatus == ERROR_SUCCESS) {
+ return numWritten * sizeof(WCHAR);
+ } else {
+ Tcl_WinConvertError(winStatus);
+ *errorCode = Tcl_GetErrno();
+ return -1;
}
- infoPtr->writeBufLen = toWrite;
- infoPtr->writeBuf = (char *)ckalloc(toWrite);
}
- memcpy(infoPtr->writeBuf, buf, toWrite);
- infoPtr->toWrite = toWrite;
- ResetEvent(threadInfo->readyEvent);
- TclPipeThreadSignal(&threadInfo->TI);
- bytesWritten = toWrite;
- } else {
- /*
- * In the blocking case, just try to write the buffer directly. This
- * avoids an unnecessary copy.
- */
- if (WriteConsoleBytes(infoPtr->handle, buf, (DWORD) toWrite,
- &bytesWritten) == FALSE) {
- Tcl_WinConvertError(GetLastError());
- goto error;
- }
+ /* Lock is reacquired. Continue loop */
}
- return bytesWritten;
-
- error:
- *errorCode = errno;
- return -1;
+ WakeConditionVariable(&handleInfoPtr->consoleThreadCV);
+ ReleaseSRWLockExclusive(&handleInfoPtr->lock);
+ return numWritten;
}
/*
@@ -846,66 +1380,84 @@ ConsoleEventProc(
* such as TCL_FILE_EVENTS. */
{
ConsoleEvent *consoleEvPtr = (ConsoleEvent *) evPtr;
- ConsoleInfo *infoPtr;
- int mask;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ ConsoleChannelInfo *chanInfoPtr;
+ int freeChannel;
+ int mask = 0;
if (!(flags & TCL_FILE_EVENTS)) {
return 0;
}
+ chanInfoPtr = consoleEvPtr->chanInfoPtr;
/*
- * Search through the list of watched consoles for the one whose handle
- * matches the event. We do this rather than simply dereferencing the
- * handle in the event so that consoles can be deleted while the event is
- * in the queue.
+ * We know chanInfoPtr is valid because its reference count would have
+ * been incremented when the event was queued. The corresponding release
+ * happens in this function.
*/
- for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
- infoPtr = infoPtr->nextPtr) {
- if (consoleEvPtr->infoPtr == infoPtr) {
- infoPtr->flags &= ~CONSOLE_PENDING;
- break;
- }
- }
-
/*
- * Remove stale events.
+ * Global lock used for chanInfoPtr. A read (shared) lock suffices
+ * because all access is within the channel owning thread with the
+ * exception of watchers which is a read-only access. See comments
+ * to ConsoleChannelInfo.
*/
-
- if (!infoPtr) {
- return 1;
- }
+ AcquireSRWLockShared(&gConsoleLock);
+ chanInfoPtr->flags &= ~CONSOLE_EVENT_QUEUED;
/*
- * Check to see if the console is readable. Note that we can't tell if a
- * console is writable, so we always report it as being writable unless we
- * have detected EOF.
+ * Only handle the event if the Tcl channel has not gone away AND is
+ * still owned by this thread AND is still watching events.
*/
-
- mask = 0;
- if (infoPtr->watchMask & TCL_WRITABLE) {
- if (WaitForSingleObject(infoPtr->writer.readyEvent,
- 0) != WAIT_TIMEOUT) {
- mask = TCL_WRITABLE;
- }
- }
-
- if (infoPtr->watchMask & TCL_READABLE) {
- if (WaitForRead(infoPtr, 0) >= 0) {
- if (infoPtr->readFlags & CONSOLE_EOF) {
+ if (chanInfoPtr->channel && chanInfoPtr->threadId == Tcl_GetCurrentThread()
+ && (chanInfoPtr->watchMask & (TCL_READABLE|TCL_WRITABLE))) {
+ ConsoleHandleInfo *handleInfoPtr;
+ handleInfoPtr = FindConsoleInfo(chanInfoPtr);
+ if (handleInfoPtr == NULL) {
+ /* Console was closed. EOF->read event only (not write) */
+ if (chanInfoPtr->watchMask & TCL_READABLE) {
mask = TCL_READABLE;
- } else {
- mask |= TCL_READABLE;
}
+ } else {
+ AcquireSRWLockShared(&handleInfoPtr->lock);
+ /* Remember at most one of READABLE, WRITABLE set */
+ if ((chanInfoPtr->watchMask & TCL_READABLE)
+ && RingBufferLength(&handleInfoPtr->buffer)) {
+ mask = TCL_READABLE;
+ } else if ((chanInfoPtr->watchMask & TCL_WRITABLE)
+ && RingBufferHasFreeSpace(&handleInfoPtr->buffer)) {
+ /* Generate write event space available */
+ mask = TCL_WRITABLE;
+ }
+ ReleaseSRWLockShared(&handleInfoPtr->lock);
}
}
/*
- * Inform the channel of the events.
+ * Tcl_NotifyChannel can recurse through the file event callback so need
+ * to release locks first. Our reference still holds so no danger of
+ * chanInfoPtr being deallocated if the callback closes the channel.
*/
+ ReleaseSRWLockShared(&gConsoleLock);
+ if (mask) {
+ Tcl_NotifyChannel(chanInfoPtr->channel, mask);
+ /* Note: chanInfoPtr ref count may have changed */
+ }
+
+ /* No need to lock - see comments earlier */
+
+ /* Remove the reference to the channel from event record */
+ if (chanInfoPtr->numRefs > 1) {
+ chanInfoPtr->numRefs -= 1;
+ freeChannel = 0;
+ } else {
+ assert(chanInfoPtr->channel == NULL);
+ freeChannel = 1;
+ }
+
+ if (freeChannel) {
+ ckfree(chanInfoPtr);
+ }
- Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
return 1;
}
@@ -928,39 +1480,51 @@ ConsoleEventProc(
static void
ConsoleWatchProc(
ClientData instanceData, /* Console state. */
- int mask) /* What events to watch for, OR-ed combination
- * of TCL_READABLE, TCL_WRITABLE and
- * TCL_EXCEPTION. */
+ int newMask) /* What events to watch for, one of
+ * of TCL_READABLE, TCL_WRITABLE
+ */
{
- ConsoleInfo **nextPtrPtr, *ptr;
- ConsoleInfo *infoPtr = (ConsoleInfo *)instanceData;
- int oldMask = infoPtr->watchMask;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ ConsoleChannelInfo **nextPtrPtr, *ptr;
+ ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
+ int oldMask = chanInfoPtr->watchMask;
/*
* Since most of the work is handled by the background threads, we just
* need to update the watchMask and then force the notifier to poll once.
*/
- infoPtr->watchMask = mask & infoPtr->validMask;
- if (infoPtr->watchMask) {
+ chanInfoPtr->watchMask = newMask & chanInfoPtr->permissions;
+ if (chanInfoPtr->watchMask) {
Tcl_Time blockTime = { 0, 0 };
if (!oldMask) {
- infoPtr->nextPtr = tsdPtr->firstConsolePtr;
- tsdPtr->firstConsolePtr = infoPtr;
+ AcquireSRWLockExclusive(&gConsoleLock);
+ /* Add to list of watched channels */
+ chanInfoPtr->nextWatchingChannelPtr = gWatchingChannelList;
+ gWatchingChannelList = chanInfoPtr;
+
+ /*
+ * For read channels, need to tell the console reader thread
+ * that we are looking for data since it will not do reads until
+ * it knows someone is awaiting.
+ */
+ ConsoleHandleInfo *handleInfoPtr;
+ handleInfoPtr = FindConsoleInfo(chanInfoPtr);
+ if (handleInfoPtr) {
+ handleInfoPtr->flags |= CONSOLE_DATA_AWAITED;
+ WakeConditionVariable(&handleInfoPtr->consoleThreadCV);
+ }
+ ReleaseSRWLockExclusive(&gConsoleLock);
}
Tcl_SetMaxBlockTime(&blockTime);
} else if (oldMask) {
- /*
- * Remove the console from the list of watched consoles.
- */
+ /* Remove from list of watched channels */
- for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr;
+ for (nextPtrPtr = &gWatchingChannelList, ptr = *nextPtrPtr;
ptr != NULL;
- nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
- if (infoPtr == ptr) {
- *nextPtrPtr = ptr->nextPtr;
+ nextPtrPtr = &ptr->nextWatchingChannelPtr, ptr = *nextPtrPtr) {
+ if (chanInfoPtr == ptr) {
+ *nextPtrPtr = ptr->nextWatchingChannelPtr;
break;
}
}
@@ -991,116 +1555,59 @@ ConsoleGetHandleProc(
TCL_UNUSED(int) /*direction*/,
ClientData *handlePtr) /* Where to store the handle. */
{
- ConsoleInfo *infoPtr = (ConsoleInfo *)instanceData;
+ ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
- *handlePtr = infoPtr->handle;
- return TCL_OK;
+ if (chanInfoPtr->handle == INVALID_HANDLE_VALUE) {
+ return TCL_ERROR;
+ } else {
+ *handlePtr = chanInfoPtr->handle;
+ return TCL_OK;
+ }
}
/*
- *----------------------------------------------------------------------
+ *------------------------------------------------------------------------
*
- * WaitForRead --
+ * ConsoleDataAvailable --
*
- * Wait until some data is available, the console is at EOF or the reader
- * thread is blocked waiting for data (if the channel is in non-blocking
- * mode).
+ * Checks if there is data in the console input queue.
*
* Results:
- * Returns 1 if console is readable. Returns 0 if there is no data on the
- * console, but there is buffered data. Returns -1 if an error occurred.
- * If an error occurred, the threads may not be synchronized.
+ * Returns 1 if the input queue has data, -1 on error else 0 if empty.
*
* Side effects:
- * Updates the shared state flags. If no error occurred, the reader
- * thread is blocked waiting for a signal from the main thread.
+ * None.
*
- *----------------------------------------------------------------------
+ *------------------------------------------------------------------------
*/
-
-static int
-WaitForRead(
- ConsoleInfo *infoPtr, /* Console state. */
- int blocking) /* Indicates whether call should be blocking
- * or not. */
+ static int
+ ConsoleDataAvailable (HANDLE consoleHandle)
{
- DWORD timeout, count;
- HANDLE *handle = (HANDLE *)infoPtr->handle;
- ConsoleThreadInfo *threadInfo = &infoPtr->reader;
- INPUT_RECORD input;
-
- while (1) {
- /*
- * Synchronize with the reader thread.
- */
-
- /* avoid blocking if pipe-thread exited */
- timeout = (!blocking || !TclPipeThreadIsAlive(&threadInfo->TI)
- || TclInExit() || TclInThreadExit()) ? 0 : INFINITE;
- if (WaitForSingleObject(threadInfo->readyEvent, timeout) == WAIT_TIMEOUT) {
- /*
- * The reader thread is blocked waiting for data and the channel
- * is in non-blocking mode.
- */
-
- errno = EWOULDBLOCK;
- return -1;
- }
-
- /*
- * At this point, the two threads are synchronized, so it is safe to
- * access shared state.
- */
-
- /*
- * If the console has hit EOF, it is always readable.
- */
-
- if (infoPtr->readFlags & CONSOLE_EOF) {
- return 1;
- }
-
- if (PeekConsoleInputW(handle, &input, 1, &count) == FALSE) {
- /*
- * Check to see if the peek failed because of EOF.
- */
-
- Tcl_WinConvertError(GetLastError());
-
- if (errno == EOF) {
- infoPtr->readFlags |= CONSOLE_EOF;
- return 1;
- }
-
- /*
- * Ignore errors if there is data in the buffer.
- */
-
- if (infoPtr->readFlags & CONSOLE_BUFFERED) {
- return 0;
- } else {
- return -1;
- }
- }
+ INPUT_RECORD input[5];
+ DWORD count;
+ DWORD i;
+ /*
+ * Need at least one keyboard event.
+ */
+ if (PeekConsoleInputW(
+ consoleHandle, input, sizeof(input) / sizeof(input[0]), &count)
+ == FALSE) {
+ return -1;
+ }
+ for (i = 0; i < count; ++i) {
/*
- * If there is data in the buffer, the console must be readable (since
- * it is a line-oriented device).
+ * Event must be a keydown because a trailing LF keyup event is always
+ * present for line based input.
*/
-
- if (infoPtr->readFlags & CONSOLE_BUFFERED) {
+ if (input[i].EventType == KEY_EVENT
+ && input[i].Event.KeyEvent.bKeyDown) {
return 1;
}
-
- /*
- * There wasn't any data available, so reset the thread and try again.
- */
-
- ResetEvent(threadInfo->readyEvent);
- TclPipeThreadSignal(&threadInfo->TI);
}
+ return 0;
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -1110,12 +1617,10 @@ WaitForRead(
* available on a console.
*
* Results:
- * None.
+ * Always 0.
*
* Side effects:
- * Signals the main thread when input become available. May cause the
- * main thread to wake up by posting a message. May one line from the
- * console for each wait operation.
+ * Signals the main thread when input become available.
*
*----------------------------------------------------------------------
*/
@@ -1124,76 +1629,178 @@ static DWORD WINAPI
ConsoleReaderThread(
LPVOID arg)
{
- TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg;
- ConsoleInfo *infoPtr = NULL; /* access info only after success init/wait */
- HANDLE *handle = NULL;
- ConsoleThreadInfo *threadInfo = NULL;
- int done = 0;
+ ConsoleHandleInfo *handleInfoPtr = (ConsoleHandleInfo *) arg;
+ ConsoleHandleInfo **iterator;
+ char inputChars[200]; /* Temporary buffer */
+ RingSizeT inputLen = 0;
+ RingSizeT inputOffset = 0;
- while (!done) {
- /*
- * Wait for the main thread to signal before attempting to read.
- */
+ /*
+ * Keep looping until one of the following happens.
+ * - there are no more channels listening on the console
+ * - the console handle has been closed
+ */
+
+ /* This thread is holding a reference so pointer is safe */
+ AcquireSRWLockExclusive(&handleInfoPtr->lock);
+
+ while (1) {
- if (!TclPipeThreadWaitForSignal(&pipeTI)) {
- /* exit */
+ if (handleInfoPtr->numRefs == 1) {
+ /*
+ * Sole reference. That's this thread. Exit since no clients
+ * and no way for a thread to attach to a console after process
+ * start.
+ */
break;
}
- if (!infoPtr) {
- infoPtr = (ConsoleInfo *)pipeTI->clientData;
- handle = (HANDLE *)infoPtr->handle;
- threadInfo = &infoPtr->reader;
- }
-
/*
- * Look for data on the console, but first ignore any events that are
- * not KEY_EVENTs.
+ * Shared buffer has no data. If we have some in our private buffer
+ * copy that. Else check if there has been an error. In both cases
+ * notify the interp threads.
*/
+ if (inputLen > 0 || handleInfoPtr->lastError != 0) {
+ HANDLE consoleHandle;
+ if (inputLen > 0) {
+ /* Private buffer has data. Copy it over. */
+ RingSizeT nStored;
+
+ assert((inputLen - inputOffset) > 0);
+
+ nStored = RingBufferIn(&handleInfoPtr->buffer,
+ inputOffset + inputChars,
+ inputLen - inputOffset,
+ 1);
+ inputOffset += nStored;
+ if (inputOffset == inputLen) {
+ /* Temp buffer now empty */
+ inputOffset = 0;
+ inputLen = 0;
+ }
+ } else {
+ /*
+ * On error, nothing but inform caller and wait
+ * We do not want to exit until there are no client interps.
+ */
+ }
- if (ReadConsoleBytes(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE,
- (LPDWORD) &infoPtr->bytesRead) != FALSE) {
/*
- * Data was stored in the buffer.
+ * Wake up any threads waiting either synchronously or
+ * asynchronously. Since we are providing data, turn off the
+ * AWAITED flag. If the data provided is not sufficient the
+ * clients will request again. Note we have to wake up ALL
+ * awaiting threads, not just one, so they can all reissue
+ * requests if needed. (In a properly designed app, at most one
+ * thread should be reading standard input but...)
*/
+ handleInfoPtr->flags &= ~CONSOLE_DATA_AWAITED;
+ /* Wake synchronous channels */
+ WakeAllConditionVariable(&handleInfoPtr->interpThreadCV);
+ /*
+ * Wake up async channels registered for file events. Note in
+ * order to follow the locking hierarchy, we need to release
+ * handleInfoPtr->lock before calling NudgeWatchers.
+ */
+ consoleHandle = handleInfoPtr->console;
+ ReleaseSRWLockExclusive(&handleInfoPtr->lock);
+ NudgeWatchers(consoleHandle);
+ AcquireSRWLockExclusive(&handleInfoPtr->lock);
- infoPtr->readFlags |= CONSOLE_BUFFERED;
- } else {
- DWORD err = GetLastError();
-
- if (err == (DWORD) EOF) {
- infoPtr->readFlags = CONSOLE_EOF;
- }
- done = 1;
+ /*
+ * Loop back to recheck for exit conditions changes while the
+ * the lock was not held.
+ */
+ continue;
}
/*
- * Signal the main thread by signalling the readable event and then
- * waking up the notifier thread.
+ * Both shared buffer and private buffer are empty. Need to go get
+ * data from console but do not want to read ahead because the
+ * interp thread might change the read mode, e.g. turning off echo
+ * for password input. So only do so if at least one interpreter has
+ * requested data.
*/
-
- SetEvent(threadInfo->readyEvent);
-
- /*
- * Alert the foreground thread. Note that we need to treat this like a
- * critical section so the foreground thread does not terminate this
- * thread while we are holding a mutex in the notifier code.
- */
-
- Tcl_MutexLock(&consoleMutex);
- if (infoPtr->threadId != NULL) {
+ if ((handleInfoPtr->flags & CONSOLE_DATA_AWAITED)
+ && ConsoleDataAvailable(handleInfoPtr->console)) {
+ DWORD error;
+ /* Do not hold the lock while blocked in console */
+ ReleaseSRWLockExclusive(&handleInfoPtr->lock);
/*
- * TIP #218. When in flight ignore the event, no one will receive
- * it anyway.
+ * Note - the temporary buffer serves two purposes. It
*/
+ error = ReadConsoleChars(handleInfoPtr->console,
+ (WCHAR *)inputChars,
+ sizeof(inputChars) / sizeof(WCHAR),
+ &inputLen);
+ AcquireSRWLockExclusive(&handleInfoPtr->lock);
+ if (error == 0) {
+ inputLen *= sizeof(WCHAR);
+ } else {
+ /*
+ * We only store the last error. It is up to channel
+ * handlers whether to close or not in case of errors.
+ */
+ handleInfoPtr->lastError = error;
+ if (handleInfoPtr->lastError == ERROR_INVALID_HANDLE) {
+ handleInfoPtr->console = INVALID_HANDLE_VALUE;
+ }
+ }
+ } else {
+ /*
+ * Either no one was asking for data, or no data was available.
+ * In the former case, wait until someone wakes us asking for
+ * data. In the latter case, there is no alternative but to
+ * poll since ReadConsole does not support async operation.
+ * So sleep for a short while and loop back to retry.
+ */
+ DWORD sleepTime;
+ sleepTime =
+ handleInfoPtr->flags & CONSOLE_DATA_AWAITED ? 50 : INFINITE;
+ SleepConditionVariableSRW(&handleInfoPtr->consoleThreadCV,
+ &handleInfoPtr->lock,
+ sleepTime,
+ 0);
+ }
+
+ /* Loop again to check for exit or wait for readers to wake us */
+ }
- Tcl_ThreadAlert(infoPtr->threadId);
+ /*
+ * Exiting:
+ * - remove the console from global list
+ * - close the handle if still valid
+ * - release the structure
+ * Note there is not need to check for any watchers because we only
+ * exit when there are no channels open to this console.
+ */
+ ReleaseSRWLockExclusive(&handleInfoPtr->lock);
+ AcquireSRWLockExclusive(&gConsoleLock); /* Modifying - exclusive lock */
+ for (iterator = &gConsoleHandleInfoList; *iterator;
+ iterator = &(*iterator)->nextPtr) {
+ if (*iterator == handleInfoPtr) {
+ *iterator = handleInfoPtr->nextPtr;
+ break;
}
- Tcl_MutexUnlock(&consoleMutex);
}
+ ReleaseSRWLockExclusive(&gConsoleLock);
- /* Worker exit, so inform the main thread or free TI-structure (if owned) */
- TclPipeThreadExit(&pipeTI);
+ /* No need for relocking - no other thread should have access to it now */
+ RingBufferClear(&handleInfoPtr->buffer);
+
+ if (handleInfoPtr->console != INVALID_HANDLE_VALUE
+ && handleInfoPtr->lastError != ERROR_INVALID_HANDLE) {
+ SetConsoleMode(handleInfoPtr->console, handleInfoPtr->initMode);
+ /*
+ * NOTE: we do not call CloseHandle(handleInfoPtr->console) here.
+ * As per the GetStdHandle documentation, it need not be closed.
+ * Other components may be directly using it. Note however that
+ * an explicit chan close script command does close the handle
+ * for all threads.
+ */
+ }
+
+ ckfree(handleInfoPtr);
return 0;
}
@@ -1210,89 +1817,257 @@ ConsoleReaderThread(
* Always returns 0.
*
* Side effects:
-
- * Signals the main thread when an output operation is completed. May
- * cause the main thread to wake up by posting a message.
+ * Signals the main thread when an output operation is completed.
*
*----------------------------------------------------------------------
*/
-
static DWORD WINAPI
-ConsoleWriterThread(
- LPVOID arg)
+ConsoleWriterThread(LPVOID arg)
{
- TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg;
- ConsoleInfo *infoPtr = NULL; /* access info only after success init/wait */
- HANDLE *handle = NULL;
- ConsoleThreadInfo *threadInfo = NULL;
- DWORD count, toWrite;
- char *buf;
- int done = 0;
-
- while (!done) {
- /*
- * Wait for the main thread to signal before attempting to write.
- */
- if (!TclPipeThreadWaitForSignal(&pipeTI)) {
- /* exit */
- break;
- }
- if (!infoPtr) {
- infoPtr = (ConsoleInfo *)pipeTI->clientData;
- handle = (HANDLE *)infoPtr->handle;
- threadInfo = &infoPtr->writer;
- }
+ ConsoleHandleInfo *handleInfoPtr = (ConsoleHandleInfo *) arg;
+ ConsoleHandleInfo **iterator;
+ BOOL success;
+ RingSizeT numBytes;
+ /*
+ * This buffer size has no relation really with the size of the shared
+ * buffer. Could be bigger or smaller. Make larger as multiple threads
+ * could potentially be writing to it.
+ */
+ char buffer[2*CONSOLE_BUFFER_SIZE];
- buf = infoPtr->writeBuf;
- toWrite = infoPtr->toWrite;
+ /*
+ * Keep looping until one of the following happens.
+ *
+ * - there are not more channels listening on the console
+ * - the console handle has been closed
+ *
+ * On each iteration,
+ * - if the channel buffer is empty, wait for some channel writer to write
+ * - if there is data in our buffer, write it to the console
+ */
+
+ /* This thread is holding a reference so pointer is safe */
+ AcquireSRWLockExclusive(&handleInfoPtr->lock);
+ while (1) {
+ /* handleInfoPtr->lock must be held on entry to loop */
+
+ int offset;
+ HANDLE consoleHandle;
/*
- * Loop until all of the bytes are written or an error occurs.
+ * Sadly, we need to do another copy because do not want to hold
+ * a lock on handleInfoPtr->buffer while calling WriteConsole as that
+ * might block. Also, we only want to copy an integral number of
+ * WCHAR's, i.e. even number of chars so do some length checks up
+ * front.
*/
-
- while (toWrite > 0) {
- if (WriteConsoleBytes(handle, buf, (DWORD) toWrite,
- &count) == FALSE) {
- infoPtr->writeError = GetLastError();
- done = 1;
+ numBytes = RingBufferLength(&handleInfoPtr->buffer);
+ numBytes &= ~1; /* Copy integral number of WCHARs -> even number of bytes */
+ if (numBytes == 0) {
+ /* No data to write */
+ if (handleInfoPtr->numRefs == 1) {
+ /*
+ * Sole reference. That's this thread. Exit since no clients
+ * and no buffered output.
+ */
break;
}
- toWrite -= count;
- buf += count;
+ /* Wake up any threads waiting synchronously. */
+ WakeConditionVariable(&handleInfoPtr->interpThreadCV);
+ success = SleepConditionVariableSRW(&handleInfoPtr->consoleThreadCV,
+ &handleInfoPtr->lock,
+ INFINITE,
+ 0);
+ /* Note: lock has been acquired again! */
+ if (!success && GetLastError() != ERROR_TIMEOUT) {
+ /* TODO - what can be done? Should not happen */
+ /* For now keep going */
+ }
+ continue;
}
- /*
- * Signal the main thread by signalling the writable event and then
- * waking up the notifier thread.
- */
-
- SetEvent(threadInfo->readyEvent);
+ /* We have data to write */
+ if ((size_t)numBytes > (sizeof(buffer) / sizeof(buffer[0]))) {
+ numBytes = sizeof(buffer);
+ }
+ /* No need to check result, we already checked length bytes available */
+ RingBufferOut(&handleInfoPtr->buffer, buffer, numBytes, 0);
+
+ consoleHandle = handleInfoPtr->console;
+ WakeConditionVariable(&handleInfoPtr->interpThreadCV);
+ ReleaseSRWLockExclusive(&handleInfoPtr->lock);
+ offset = 0;
+ while (numBytes > 0) {
+ RingSizeT numWChars = numBytes / sizeof(WCHAR);
+ DWORD status;
+ status = WriteConsoleChars(handleInfoPtr->console,
+ (WCHAR *)(offset + buffer),
+ numWChars,
+ &numWChars);
+ if (status != 0) {
+ /* Only overwrite if no previous error */
+ if (handleInfoPtr->lastError == 0) {
+ handleInfoPtr->lastError = status;
+ }
+ if (status == ERROR_INVALID_HANDLE) {
+ handleInfoPtr->console = INVALID_HANDLE_VALUE;
+ }
+ /* Assume this write is done but keep looping in case
+ * it is a transient error. Not sure just closing handle
+ * and exiting thread is a good idea until all references
+ * from interp threads are gone.
+ */
+ break;
+ }
+ numBytes -= numWChars * sizeof(WCHAR);
+ offset += numWChars * sizeof(WCHAR);
+ }
+ /* Wake up any threads waiting synchronously. */
+ WakeConditionVariable(&handleInfoPtr->interpThreadCV);
/*
- * Alert the foreground thread. Note that we need to treat this like a
- * critical section so the foreground thread does not terminate this
- * thread while we are holding a mutex in the notifier code.
+ * Wake up all channels registered for file events. Note in
+ * order to follow the locking hierarchy, we cannot hold any locks
+ * when calling NudgeWatchers.
*/
+ NudgeWatchers(consoleHandle);
- Tcl_MutexLock(&consoleMutex);
- if (infoPtr->threadId != NULL) {
- /*
- * TIP #218. When in flight ignore the event, no one will receive
- * it anyway.
- */
+ AcquireSRWLockExclusive(&handleInfoPtr->lock);
+ }
- Tcl_ThreadAlert(infoPtr->threadId);
+ /*
+ * Exiting:
+ * - remove the console from global list
+ * - release the structure
+ * NOTE: we do not call CloseHandle(handleInfoPtr->console) here.
+ * As per the GetStdHandle documentation, it need not be closed.
+ * Other components may be directly using it. Note however that
+ * an explicit chan close script command does close the handle
+ * for all threads.
+ */
+ ReleaseSRWLockExclusive(&handleInfoPtr->lock);
+ AcquireSRWLockExclusive(&gConsoleLock); /* Modifying - exclusive lock */
+ for (iterator = &gConsoleHandleInfoList; *iterator;
+ iterator = &(*iterator)->nextPtr) {
+ if (*iterator == handleInfoPtr) {
+ *iterator = handleInfoPtr->nextPtr;
+ break;
}
- Tcl_MutexUnlock(&consoleMutex);
}
+ ReleaseSRWLockExclusive(&gConsoleLock);
+
+ RingBufferClear(&handleInfoPtr->buffer);
- /* Worker exit, so inform the main thread or free TI-structure (if owned) */
- TclPipeThreadExit(&pipeTI);
+ ckfree(handleInfoPtr);
return 0;
}
/*
+ *------------------------------------------------------------------------
+ *
+ * AllocateConsoleHandleInfo --
+ *
+ * Allocates a ConsoleHandleInfo for the passed console handle. As
+ * a side effect starts a console thread to handle i/o on the handle.
+ *
+ * Important: Caller must be holding an EXCLUSIVE lock on gConsoleLock
+ * when calling this function. The lock continues to be held on return.
+ *
+ * Results:
+ * Pointer to an unlocked ConsoleHandleInfo structure. The reference
+ * count on the structure is 1. This corresponds to the common reference
+ * from the console thread and the gConsoleHandleInfoList. Returns NULL
+ * on error.
+ *
+ * Side effects:
+ * A console reader or writer thread is started. The returned structure
+ * is placed on the active console handler list gConsoleHandleInfoList.
+ *
+ *------------------------------------------------------------------------
+ */
+static ConsoleHandleInfo *
+AllocateConsoleHandleInfo(
+ HANDLE consoleHandle,
+ int permissions) /* TCL_READABLE or TCL_WRITABLE */
+{
+ ConsoleHandleInfo *handleInfoPtr;
+ DWORD consoleMode;
+
+
+ handleInfoPtr = (ConsoleHandleInfo *)ckalloc(sizeof(*handleInfoPtr));
+ handleInfoPtr->console = consoleHandle;
+ InitializeSRWLock(&handleInfoPtr->lock);
+ InitializeConditionVariable(&handleInfoPtr->consoleThreadCV);
+ InitializeConditionVariable(&handleInfoPtr->interpThreadCV);
+ RingBufferInit(&handleInfoPtr->buffer, CONSOLE_BUFFER_SIZE);
+ handleInfoPtr->lastError = 0;
+ handleInfoPtr->permissions = permissions;
+ handleInfoPtr->numRefs = 1; /* See function header */
+ if (permissions == TCL_READABLE) {
+ GetConsoleMode(consoleHandle, &handleInfoPtr->initMode);
+ consoleMode = handleInfoPtr->initMode;
+ consoleMode &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
+ consoleMode |= ENABLE_LINE_INPUT;
+ SetConsoleMode(consoleHandle, consoleMode);
+ }
+ handleInfoPtr->consoleThread = CreateThread(
+ NULL, /* default security descriptor */
+ 2*CONSOLE_BUFFER_SIZE, /* Stack size - gets rounded up to granularity */
+ permissions == TCL_READABLE ? ConsoleReaderThread : ConsoleWriterThread,
+ handleInfoPtr, /* Pass to thread */
+ 0, /* Flags - no special cases */
+ NULL); /* Don't care about thread id */
+ if (handleInfoPtr->consoleThread == NULL) {
+ /* Note - SRWLock and condition variables do not need finalization */
+ RingBufferClear(&handleInfoPtr->buffer);
+ ckfree(handleInfoPtr);
+ return NULL;
+ }
+
+ /* Chain onto global list */
+ handleInfoPtr->nextPtr = gConsoleHandleInfoList;
+ gConsoleHandleInfoList = handleInfoPtr;
+
+ return handleInfoPtr;
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * FindConsoleInfo --
+ *
+ * Finds the ConsoleHandleInfo record for a given ConsoleChannelInfo.
+ * The found record must match the console handle. It is the caller's
+ * responsibility to check the permissions (read/write) in the returned
+ * ConsoleHandleInfo match permissions in chanInfoPtr. This function does
+ * not check that.
+ *
+ * Important: Caller must be holding an shared or exclusive lock on
+ * gConsoleMutex. That ensures the returned pointer stays valid on
+ * return without risk of deallocation by other threads.
+ *
+ * Results:
+ * Pointer to the found ConsoleHandleInfo or NULL if not found
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------
+ */
+static ConsoleHandleInfo *
+FindConsoleInfo(const ConsoleChannelInfo *chanInfoPtr)
+{
+ ConsoleHandleInfo *handleInfoPtr;
+ for (handleInfoPtr = gConsoleHandleInfoList; handleInfoPtr; handleInfoPtr = handleInfoPtr->nextPtr) {
+ if (handleInfoPtr->console == chanInfoPtr->handle) {
+ return handleInfoPtr;
+ }
+ }
+ return NULL;
+}
+
+/*
*----------------------------------------------------------------------
*
* TclWinOpenConsoleChannel --
@@ -1309,33 +2084,30 @@ ConsoleWriterThread(
*
*----------------------------------------------------------------------
*/
-
Tcl_Channel
TclWinOpenConsoleChannel(
HANDLE handle,
char *channelName,
int permissions)
{
- char encoding[4 + TCL_INTEGER_SPACE];
- ConsoleInfo *infoPtr;
- DWORD modes;
+ ConsoleChannelInfo *chanInfoPtr;
+ ConsoleHandleInfo *handleInfoPtr;
- ConsoleInit();
-
- /*
- * See if a channel with this handle already exists.
- */
+ /* A console handle can either be input or output, not both */
+ if (permissions != TCL_READABLE && permissions != TCL_WRITABLE) {
+ return NULL;
+ }
- infoPtr = (ConsoleInfo *)ckalloc(sizeof(ConsoleInfo));
- memset(infoPtr, 0, sizeof(ConsoleInfo));
+ ConsoleInit();
- infoPtr->validMask = permissions;
- infoPtr->handle = handle;
- infoPtr->channel = (Tcl_Channel) NULL;
+ chanInfoPtr = (ConsoleChannelInfo *)ckalloc(sizeof(*chanInfoPtr));
+ memset(chanInfoPtr, 0, sizeof(*chanInfoPtr));
- wsprintfA(encoding, "cp%d", GetConsoleCP());
+ chanInfoPtr->permissions = permissions;
+ chanInfoPtr->handle = handle;
+ chanInfoPtr->channel = (Tcl_Channel) NULL;
- infoPtr->threadId = Tcl_GetCurrentThread();
+ chanInfoPtr->threadId = Tcl_GetCurrentThread();
/*
* Use the pointer for the name of the result channel. This keeps the
@@ -1343,10 +2115,7 @@ TclWinOpenConsoleChannel(
* for instance).
*/
- sprintf(channelName, "file%" TCL_Z_MODIFIER "x", (size_t) infoPtr);
-
- infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName,
- infoPtr, permissions);
+ sprintf(channelName, "file%" TCL_Z_MODIFIER "x", (size_t) chanInfoPtr);
if (permissions & TCL_READABLE) {
/*
@@ -1355,38 +2124,76 @@ TclWinOpenConsoleChannel(
* we only want to catch when complete lines are ready for reading.
*/
- infoPtr->flags |= CONSOLE_READ_OPS;
- GetConsoleMode(infoPtr->handle, &infoPtr->initMode);
- modes = infoPtr->initMode;
- modes &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
- modes |= ENABLE_LINE_INPUT;
- SetConsoleMode(infoPtr->handle, modes);
-
- infoPtr->reader.readyEvent = CreateEventW(NULL, TRUE, TRUE, NULL);
- infoPtr->reader.thread = CreateThread(NULL, 256, ConsoleReaderThread,
- TclPipeThreadCreateTI(&infoPtr->reader.TI, infoPtr,
- infoPtr->reader.readyEvent), 0, NULL);
+ chanInfoPtr->flags |= CONSOLE_READ_OPS;
+ GetConsoleMode(handle, &chanInfoPtr->initMode);
+
+#ifdef OBSOLETE
+ /* Why was priority being set on console input? Code smell */
SetThreadPriority(infoPtr->reader.thread, THREAD_PRIORITY_HIGHEST);
+#endif
+ } else {
+ /* Already checked permissions is WRITABLE if not READABLE */
+ /* TODO - enable ansi escape processing? */
}
- if (permissions & TCL_WRITABLE) {
+ /*
+ * Global lock but that's ok. See comments top of file. Allocations
+ * will happen only a few times in the life of a process and that too
+ * generally at start up where only one thread is active.
+ */
+ AcquireSRWLockExclusive(&gConsoleLock); /*Allocate needs exclusive lock */
- infoPtr->writer.readyEvent = CreateEventW(NULL, TRUE, TRUE, NULL);
- infoPtr->writer.thread = CreateThread(NULL, 256, ConsoleWriterThread,
- TclPipeThreadCreateTI(&infoPtr->writer.TI, infoPtr,
- infoPtr->writer.readyEvent), 0, NULL);
- SetThreadPriority(infoPtr->writer.thread, THREAD_PRIORITY_HIGHEST);
+ handleInfoPtr = FindConsoleInfo(chanInfoPtr);
+ if (handleInfoPtr == NULL) {
+ /* Not found. Allocate one */
+ handleInfoPtr = AllocateConsoleHandleInfo(handle, permissions);
+ } else {
+ /* Found. Its direction (read/write) better be the same */
+ if (handleInfoPtr->permissions != permissions) {
+ handleInfoPtr = NULL;
+ }
+ }
+
+ if (handleInfoPtr == NULL) {
+ ReleaseSRWLockExclusive(&gConsoleLock);
+ if (permissions == TCL_READABLE) {
+ SetConsoleMode(handle, chanInfoPtr->initMode);
+ }
+ ckfree(chanInfoPtr);
+ return NULL;
}
/*
- * Files have default translation of AUTO and ^Z eof char, which means
+ * There is effectively a reference to this structure from the Tcl
+ * channel subsystem. So record that. This reference will be dropped
+ * when the Tcl channel is closed.
+ */
+ chanInfoPtr->numRefs = 1;
+
+ /*
+ * Need to keep track of number of referencing channels for closing.
+ * The pointer is safe since there is a reference held to it from
+ * gConsoleHandleInfoList but still need to lock the structure itself
+ */
+ AcquireSRWLockExclusive(&handleInfoPtr->lock);
+ handleInfoPtr->numRefs += 1;
+ ReleaseSRWLockExclusive(&handleInfoPtr->lock);
+
+ ReleaseSRWLockExclusive(&gConsoleLock);
+
+ /* Note Tcl_CreateChannel never fails other than panic on error */
+ chanInfoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName,
+ chanInfoPtr, permissions);
+
+ /*
+ * Consoles have default translation of auto and ^Z eof char, which means
* that a ^Z will be accepted as EOF when reading.
*/
- Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
- Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
- Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", "utf-16");
- return infoPtr->channel;
+ Tcl_SetChannelOption(NULL, chanInfoPtr->channel, "-translation", "auto");
+ Tcl_SetChannelOption(NULL, chanInfoPtr->channel, "-eofchar", "\032 {}");
+ Tcl_SetChannelOption(NULL, chanInfoPtr->channel, "-encoding", "utf-16");
+ return chanInfoPtr->channel;
}
/*
@@ -1410,33 +2217,15 @@ ConsoleThreadActionProc(
ClientData instanceData,
int action)
{
- ConsoleInfo *infoPtr = (ConsoleInfo *)instanceData;
-
- /*
- * We do not access firstConsolePtr in the thread structures. This is not
- * for all serials managed by the thread, but only those we are watching.
- * Removal of the filevent handlers before transfer thus takes care of
- * this structure.
- */
+ ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
- Tcl_MutexLock(&consoleMutex);
+ /* No need for any locks as no other thread will be writing to it */
if (action == TCL_CHANNEL_THREAD_INSERT) {
- /*
- * We can't copy the thread information from the channel when the
- * channel is created. At this time the channel back pointer has not
- * been set yet. However in that case the threadId has already been
- * set by TclpCreateCommandChannel itself, so the structure is still
- * good.
- */
-
- ConsoleInit();
- if (infoPtr->channel != NULL) {
- infoPtr->threadId = Tcl_GetChannelThread(infoPtr->channel);
- }
+ ConsoleInit(); /* Needed to set up event source handlers for this thread */
+ chanInfoPtr->threadId = Tcl_GetCurrentThread();
} else {
- infoPtr->threadId = NULL;
+ chanInfoPtr->threadId = NULL;
}
- Tcl_MutexUnlock(&consoleMutex);
}
/*
@@ -1456,7 +2245,6 @@ ConsoleThreadActionProc(
*
*----------------------------------------------------------------------
*/
-
static int
ConsoleSetOptionProc(
ClientData instanceData, /* File state. */
@@ -1464,7 +2252,7 @@ ConsoleSetOptionProc(
const char *optionName, /* Which option to set? */
const char *value) /* New value for option. */
{
- ConsoleInfo *infoPtr = (ConsoleInfo *)instanceData;
+ ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
int len = strlen(optionName);
int vlen = strlen(value);
@@ -1472,11 +2260,11 @@ ConsoleSetOptionProc(
* Option -inputmode normal|password|raw
*/
- if ((infoPtr->flags & CONSOLE_READ_OPS) && (len > 1) &&
+ if ((chanInfoPtr->flags & CONSOLE_READ_OPS) && (len > 1) &&
(strncmp(optionName, "-inputmode", len) == 0)) {
DWORD mode;
- if (GetConsoleMode(infoPtr->handle, &mode) == 0) {
+ if (GetConsoleMode(chanInfoPtr->handle, &mode) == 0) {
Tcl_WinConvertError(GetLastError());
if (interp != NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
@@ -1486,18 +2274,18 @@ ConsoleSetOptionProc(
return TCL_ERROR;
}
if (Tcl_UtfNcasecmp(value, "NORMAL", vlen) == 0) {
- mode |= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT;
+ mode |=
+ ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
} else if (Tcl_UtfNcasecmp(value, "PASSWORD", vlen) == 0) {
- mode |= ENABLE_LINE_INPUT;
+ mode |= ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT;
mode &= ~ENABLE_ECHO_INPUT;
} else if (Tcl_UtfNcasecmp(value, "RAW", vlen) == 0) {
- mode &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
+ mode &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
} else if (Tcl_UtfNcasecmp(value, "RESET", vlen) == 0) {
/*
* Reset to the initial mode, whatever that is.
*/
-
- mode = infoPtr->initMode;
+ mode = chanInfoPtr->initMode;
} else {
if (interp) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
@@ -1508,7 +2296,7 @@ ConsoleSetOptionProc(
}
return TCL_ERROR;
}
- if (SetConsoleMode(infoPtr->handle, mode) == 0) {
+ if (SetConsoleMode(chanInfoPtr->handle, mode) == 0) {
Tcl_WinConvertError(GetLastError());
if (interp != NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
@@ -1518,19 +2306,10 @@ ConsoleSetOptionProc(
return TCL_ERROR;
}
- /*
- * If we've changed the mode from default, schedule a reset later.
- */
-
- if (mode == infoPtr->initMode) {
- infoPtr->flags &= ~CONSOLE_RESET;
- } else {
- infoPtr->flags |= CONSOLE_RESET;
- }
return TCL_OK;
}
- if (infoPtr->flags & CONSOLE_READ_OPS) {
+ if (chanInfoPtr->flags & CONSOLE_READ_OPS) {
return Tcl_BadChannelOption(interp, optionName, "inputmode");
} else {
return Tcl_BadChannelOption(interp, optionName, "");
@@ -1562,7 +2341,7 @@ ConsoleGetOptionProc(
const char *optionName, /* Option to get. */
Tcl_DString *dsPtr) /* Where to store value(s). */
{
- ConsoleInfo *infoPtr = (ConsoleInfo *)instanceData;
+ ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
int valid = 0; /* Flag if valid option parsed. */
unsigned int len;
char buf[TCL_INTEGER_SPACE];
@@ -1580,7 +2359,7 @@ ConsoleGetOptionProc(
* represents what almost all scripts really want to know.
*/
- if (infoPtr->flags & CONSOLE_READ_OPS) {
+ if (chanInfoPtr->flags & CONSOLE_READ_OPS) {
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-inputmode");
}
@@ -1588,7 +2367,7 @@ ConsoleGetOptionProc(
DWORD mode;
valid = 1;
- if (GetConsoleMode(infoPtr->handle, &mode) == 0) {
+ if (GetConsoleMode(chanInfoPtr->handle, &mode) == 0) {
Tcl_WinConvertError(GetLastError());
if (interp != NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
@@ -1607,42 +2386,52 @@ ConsoleGetOptionProc(
Tcl_DStringAppendElement(dsPtr, "raw");
}
}
- }
-
- /*
- * Get option -winsize
- * Option is readonly and returned by [fconfigure chan -winsize] but not
- * returned by [fconfigure chan] without explicit option name.
- */
+ } else {
+ /*
+ * Output channel. Get option -winsize
+ * Option is readonly and returned by [fconfigure chan -winsize] but not
+ * returned by [fconfigure chan] without explicit option name.
+ */
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-winsize");
+ }
- if ((len > 1) && (strncmp(optionName, "-winsize", len) == 0)) {
- CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
+ if (len == 0 || (len > 1 && strncmp(optionName, "-winsize", len) == 0)) {
+ CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
- valid = 1;
- if (!GetConsoleScreenBufferInfo(infoPtr->handle, &consoleInfo)) {
- Tcl_WinConvertError(GetLastError());
- if (interp != NULL) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "couldn't read console size: %s",
- Tcl_PosixError(interp)));
+ valid = 1;
+ if (!GetConsoleScreenBufferInfo(chanInfoPtr->handle,
+ &consoleInfo)) {
+ Tcl_WinConvertError(GetLastError());
+ if (interp != NULL) {
+ Tcl_SetObjResult(
+ interp,
+ Tcl_ObjPrintf("couldn't read console size: %s",
+ Tcl_PosixError(interp)));
+ }
+ return TCL_ERROR;
}
- return TCL_ERROR;
+ Tcl_DStringStartSublist(dsPtr);
+ sprintf(buf,
+ "%d",
+ consoleInfo.srWindow.Right - consoleInfo.srWindow.Left + 1);
+ Tcl_DStringAppendElement(dsPtr, buf);
+ sprintf(buf,
+ "%d",
+ consoleInfo.srWindow.Bottom - consoleInfo.srWindow.Top + 1);
+ Tcl_DStringAppendElement(dsPtr, buf);
+ Tcl_DStringEndSublist(dsPtr);
}
- sprintf(buf, "%d",
- consoleInfo.srWindow.Right - consoleInfo.srWindow.Left + 1);
- Tcl_DStringAppendElement(dsPtr, buf);
- sprintf(buf, "%d",
- consoleInfo.srWindow.Bottom - consoleInfo.srWindow.Top + 1);
- Tcl_DStringAppendElement(dsPtr, buf);
}
+
if (valid) {
return TCL_OK;
}
- if (infoPtr->flags & CONSOLE_READ_OPS) {
- return Tcl_BadChannelOption(interp, optionName, "inputmode winsize");
+ if (chanInfoPtr->flags & CONSOLE_READ_OPS) {
+ return Tcl_BadChannelOption(interp, optionName, "inputmode");
} else {
- return Tcl_BadChannelOption(interp, optionName, "");
+ return Tcl_BadChannelOption(interp, optionName, "winsize");
}
}
diff --git a/win/tclWinDde.c b/win/tclWinDde.c
index 2570954..1c10c65 100644
--- a/win/tclWinDde.c
+++ b/win/tclWinDde.c
@@ -1789,9 +1789,9 @@ DdeObjCmd(
}
if (result == TCL_OK) {
- if (objc == 1)
+ if (objc == 1) {
objPtr = objv[0];
- else {
+ } else {
objPtr = Tcl_ConcatObj(objc, objv);
}
if (riPtr->handlerPtr != NULL) {
diff --git a/win/tclWinFCmd.c b/win/tclWinFCmd.c
index 3f6d7f4..2ca041b 100644
--- a/win/tclWinFCmd.c
+++ b/win/tclWinFCmd.c
@@ -330,8 +330,8 @@ DoRenameFile(
Tcl_DStringInit(&srcString);
Tcl_DStringInit(&dstString);
- src = Tcl_WCharToUtfDString(nativeSrcPath, -1, &srcString);
- dst = Tcl_WCharToUtfDString(nativeDstPath, -1, &dstString);
+ src = Tcl_WCharToUtfDString(nativeSrcPath, TCL_INDEX_NONE, &srcString);
+ dst = Tcl_WCharToUtfDString(nativeDstPath, TCL_INDEX_NONE, &dstString);
/*
* Check whether the destination path is actually inside the
@@ -929,7 +929,7 @@ TclpObjCopyDirectory(
} else if (!strcmp(Tcl_DStringValue(&ds), TclGetString(normDestPtr))) {
*errorPtr = destPathPtr;
} else {
- *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
+ *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), TCL_INDEX_NONE);
}
Tcl_DStringFree(&ds);
Tcl_IncrRefCount(*errorPtr);
@@ -1117,7 +1117,7 @@ DoRemoveJustDirectory(
char *p;
Tcl_DStringInit(errorPtr);
- p = Tcl_WCharToUtfDString(nativePath, -1, errorPtr);
+ p = Tcl_WCharToUtfDString(nativePath, TCL_INDEX_NONE, errorPtr);
for (; *p; ++p) {
if (*p == '\\') *p = '/';
}
@@ -1332,7 +1332,7 @@ TraverseWinTree(
Tcl_WinConvertError(GetLastError());
if (errorPtr != NULL) {
Tcl_DStringInit(errorPtr);
- Tcl_WCharToUtfDString(nativeErrfile, -1, errorPtr);
+ Tcl_WCharToUtfDString(nativeErrfile, TCL_INDEX_NONE, errorPtr);
}
result = TCL_ERROR;
}
@@ -1398,7 +1398,7 @@ TraversalCopy(
if (errorPtr != NULL) {
Tcl_DStringInit(errorPtr);
- Tcl_WCharToUtfDString(nativeDst, -1, errorPtr);
+ Tcl_WCharToUtfDString(nativeDst, TCL_INDEX_NONE, errorPtr);
}
return TCL_ERROR;
}
@@ -1454,7 +1454,7 @@ TraversalDelete(
if (errorPtr != NULL) {
Tcl_DStringInit(errorPtr);
- Tcl_WCharToUtfDString(nativeSrc, -1, errorPtr);
+ Tcl_WCharToUtfDString(nativeSrc, TCL_INDEX_NONE, errorPtr);
}
return TCL_ERROR;
}
@@ -1712,7 +1712,7 @@ ConvertFileNameFormat(
*/
Tcl_DStringInit(&dsTemp);
- Tcl_WCharToUtfDString(nativeName, -1, &dsTemp);
+ Tcl_WCharToUtfDString(nativeName, TCL_INDEX_NONE, &dsTemp);
Tcl_DStringFree(&ds);
/*
@@ -1952,14 +1952,14 @@ TclpObjListVolumes(void)
buf[0] = (char) ('a' + i);
if (GetVolumeInformationA(buf, NULL, 0, NULL, NULL, NULL, NULL, 0)
|| (GetLastError() == ERROR_NOT_READY)) {
- elemPtr = Tcl_NewStringObj(buf, -1);
+ elemPtr = Tcl_NewStringObj(buf, TCL_INDEX_NONE);
Tcl_ListObjAppendElement(NULL, resultPtr, elemPtr);
}
}
} else {
for (p = buf; *p != '\0'; p += 4) {
p[2] = '/';
- elemPtr = Tcl_NewStringObj(p, -1);
+ elemPtr = Tcl_NewStringObj(p, TCL_INDEX_NONE);
Tcl_ListObjAppendElement(NULL, resultPtr, elemPtr);
}
}
@@ -2078,7 +2078,7 @@ TclpCreateTemporaryDirectory(
*/
Tcl_DStringInit(&name);
- Tcl_WCharToUtfDString((LPCWSTR) Tcl_DStringValue(&base), -1, &name);
+ Tcl_WCharToUtfDString((LPCWSTR) Tcl_DStringValue(&base), TCL_INDEX_NONE, &name);
Tcl_DStringFree(&base);
return TclDStringToObj(&name);
}
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index 4a07f04..56ef8cb 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -888,7 +888,7 @@ TclpFindExecutable(
GetModuleFileNameW(NULL, wName, sizeof(wName)/sizeof(WCHAR));
WideCharToMultiByte(CP_UTF8, 0, wName, -1, name, sizeof(name), NULL, NULL);
TclWinNoBackslash(name);
- TclSetObjNameOfExecutable(Tcl_NewStringObj(name, -1), NULL);
+ TclSetObjNameOfExecutable(Tcl_NewStringObj(name, TCL_INDEX_NONE), NULL);
}
/*
@@ -1024,7 +1024,7 @@ TclpMatchInDirectory(
* pattern.
*/
- dirName = Tcl_DStringAppend(&dsOrig, pattern, -1);
+ dirName = Tcl_DStringAppend(&dsOrig, pattern, TCL_INDEX_NONE);
} else {
dirName = TclDStringAppendLiteral(&dsOrig, "*.*");
}
@@ -1103,7 +1103,7 @@ TclpMatchInDirectory(
native = data.cFileName;
attr = data.dwFileAttributes;
Tcl_DStringInit(&ds);
- utfname = Tcl_WCharToUtfDString(native, -1, &ds);
+ utfname = Tcl_WCharToUtfDString(native, TCL_INDEX_NONE, &ds);
if (!matchSpecialDots) {
/*
@@ -1989,7 +1989,7 @@ TclpGetCwd(
native += 2;
}
Tcl_DStringInit(bufferPtr);
- Tcl_WCharToUtfDString(native, -1, bufferPtr);
+ Tcl_WCharToUtfDString(native, TCL_INDEX_NONE, bufferPtr);
/*
* Convert to forward slashes for easier use in scripts.
@@ -2198,7 +2198,7 @@ NativeDev(
GetFullPathNameW(nativePath, MAX_PATH, nativeFullPath, &nativePart);
Tcl_DStringInit(&ds);
- fullPath = Tcl_WCharToUtfDString(nativeFullPath, -1, &ds);
+ fullPath = Tcl_WCharToUtfDString(nativeFullPath, TCL_INDEX_NONE, &ds);
if ((fullPath[0] == '\\') && (fullPath[1] == '\\')) {
const char *p;
@@ -2501,7 +2501,7 @@ TclpFilesystemPathType(
Tcl_DString ds;
Tcl_DStringInit(&ds);
- Tcl_WCharToUtfDString(volType, -1, &ds);
+ Tcl_WCharToUtfDString(volType, TCL_INDEX_NONE, &ds);
return TclDStringToObj(&ds);
}
#undef VOL_BUF_SIZE
@@ -2649,7 +2649,7 @@ TclpObjNormalizePath(
*/
nextCheckpoint = 0;
- Tcl_AppendToObj(to, currentPathEndPosition, -1);
+ Tcl_AppendToObj(to, currentPathEndPosition, TCL_INDEX_NONE);
/*
* Convert link to forward slashes.
@@ -2825,7 +2825,7 @@ TclpObjNormalizePath(
tmpPathPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds),
nextCheckpoint);
- Tcl_AppendToObj(tmpPathPtr, lastValidPathEnd, -1);
+ Tcl_AppendToObj(tmpPathPtr, lastValidPathEnd, TCL_INDEX_NONE);
path = TclGetStringFromObj(tmpPathPtr, &len);
Tcl_SetStringObj(pathPtr, path, len);
Tcl_DecrRefCount(tmpPathPtr);
@@ -2898,7 +2898,7 @@ TclWinVolumeRelativeNormalize(
const char *drive = Tcl_GetString(useThisCwd);
absolutePath = Tcl_NewStringObj(drive,2);
- Tcl_AppendToObj(absolutePath, path, -1);
+ Tcl_AppendToObj(absolutePath, path, TCL_INDEX_NONE);
Tcl_IncrRefCount(absolutePath);
/*
@@ -2951,7 +2951,7 @@ TclWinVolumeRelativeNormalize(
Tcl_AppendToObj(absolutePath, "/", 1);
}
Tcl_IncrRefCount(absolutePath);
- Tcl_AppendToObj(absolutePath, path+2, -1);
+ Tcl_AppendToObj(absolutePath, path+2, TCL_INDEX_NONE);
}
*useThisCwdPtr = useThisCwd;
return absolutePath;
@@ -2988,7 +2988,7 @@ TclpNativeToNormalized(
char *copy, *p;
Tcl_DStringInit(&ds);
- Tcl_WCharToUtfDString((const WCHAR *) clientData, -1, &ds);
+ Tcl_WCharToUtfDString((const WCHAR *) clientData, TCL_INDEX_NONE, &ds);
copy = Tcl_DStringValue(&ds);
len = Tcl_DStringLength(&ds);
diff --git a/win/tclWinInit.c b/win/tclWinInit.c
index 647b870..fdeb0aa 100644
--- a/win/tclWinInit.c
+++ b/win/tclWinInit.c
@@ -233,7 +233,7 @@ AppendEnvironment(
WideCharToMultiByte(CP_UTF8, 0, wBuf, -1, buf, MAX_PATH * 3, NULL, NULL);
if (buf[0] != '\0') {
- objPtr = Tcl_NewStringObj(buf, -1);
+ objPtr = Tcl_NewStringObj(buf, TCL_INDEX_NONE);
Tcl_ListObjAppendElement(NULL, pathPtr, objPtr);
TclWinNoBackslash(buf);
@@ -257,7 +257,7 @@ AppendEnvironment(
(void) Tcl_JoinPath(pathc, pathv, &ds);
objPtr = TclDStringToObj(&ds);
} else {
- objPtr = Tcl_NewStringObj(buf, -1);
+ objPtr = Tcl_NewStringObj(buf, TCL_INDEX_NONE);
}
Tcl_ListObjAppendElement(NULL, pathPtr, objPtr);
ckfree(pathv);
@@ -517,11 +517,11 @@ TclpSetVariables(
if (ptr == NULL) {
ptr = Tcl_GetVar2(interp, "env", "HOMEDRIVE", TCL_GLOBAL_ONLY);
if (ptr != NULL) {
- Tcl_DStringAppend(&ds, ptr, -1);
+ Tcl_DStringAppend(&ds, ptr, TCL_INDEX_NONE);
}
ptr = Tcl_GetVar2(interp, "env", "HOMEPATH", TCL_GLOBAL_ONLY);
if (ptr != NULL) {
- Tcl_DStringAppend(&ds, ptr, -1);
+ Tcl_DStringAppend(&ds, ptr, TCL_INDEX_NONE);
}
if (Tcl_DStringLength(&ds) > 0) {
Tcl_SetVar2(interp, "env", "HOME", Tcl_DStringValue(&ds),
@@ -607,7 +607,7 @@ TclpFindVariable(
*/
Tcl_DStringInit(&envString);
- envUpper = Tcl_WCharToUtfDString(env, -1, &envString);
+ envUpper = Tcl_WCharToUtfDString(env, TCL_INDEX_NONE, &envString);
p1 = strchr(envUpper, '=');
if (p1 == NULL) {
continue;
diff --git a/win/tclWinLoad.c b/win/tclWinLoad.c
index e262595..2106343 100644
--- a/win/tclWinLoad.c
+++ b/win/tclWinLoad.c
@@ -114,10 +114,11 @@ TclpDlopen(
* first error for reporting purposes.
*/
if (firstError == ERROR_MOD_NOT_FOUND ||
- firstError == ERROR_DLL_NOT_FOUND)
+ firstError == ERROR_DLL_NOT_FOUND) {
lastError = GetLastError();
- else
+ } else {
lastError = firstError;
+ }
errMsg = Tcl_ObjPrintf("couldn't load library \"%s\": ",
Tcl_GetString(pathPtr));
@@ -219,7 +220,7 @@ FindSymbol(
Tcl_DStringInit(&ds);
TclDStringAppendLiteral(&ds, "_");
- sym2 = Tcl_DStringAppend(&ds, symbol, -1);
+ sym2 = Tcl_DStringAppend(&ds, symbol, TCL_INDEX_NONE);
proc = (void *)GetProcAddress(hInstance, sym2);
Tcl_DStringFree(&ds);
}
diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c
index 29b1c03..4a39e8c 100644
--- a/win/tclWinPipe.c
+++ b/win/tclWinPipe.c
@@ -679,7 +679,7 @@ TclpCreateTempFile(
* Convert the contents from UTF to native encoding
*/
- native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring);
+ native = Tcl_UtfToExternalDString(NULL, contents, TCL_INDEX_NONE, &dstring);
toCopy = Tcl_DStringLength(&dstring);
for (p = native; toCopy > 0; p++, toCopy--) {
@@ -1285,12 +1285,12 @@ ApplicationType(
applType = APPL_NONE;
Tcl_DStringInit(&nameBuf);
- Tcl_DStringAppend(&nameBuf, originalName, -1);
+ Tcl_DStringAppend(&nameBuf, originalName, TCL_INDEX_NONE);
nameLen = Tcl_DStringLength(&nameBuf);
for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
Tcl_DStringSetLength(&nameBuf, nameLen);
- Tcl_DStringAppend(&nameBuf, extensions[i], -1);
+ Tcl_DStringAppend(&nameBuf, extensions[i], TCL_INDEX_NONE);
Tcl_DStringInit(&ds);
nativeName = Tcl_UtfToWCharDString(Tcl_DStringValue(&nameBuf),
Tcl_DStringLength(&nameBuf), &ds);
@@ -1311,7 +1311,7 @@ ApplicationType(
continue;
}
Tcl_DStringInit(&ds);
- strcpy(fullName, Tcl_WCharToUtfDString(nativeFullPath, -1, &ds));
+ strcpy(fullName, Tcl_WCharToUtfDString(nativeFullPath, TCL_INDEX_NONE, &ds));
Tcl_DStringFree(&ds);
ext = strrchr(fullName, '.');
@@ -1403,7 +1403,7 @@ ApplicationType(
GetShortPathNameW(nativeFullPath, nativeFullPath, MAX_PATH);
Tcl_DStringInit(&ds);
- strcpy(fullName, Tcl_WCharToUtfDString(nativeFullPath, -1, &ds));
+ strcpy(fullName, Tcl_WCharToUtfDString(nativeFullPath, TCL_INDEX_NONE, &ds));
Tcl_DStringFree(&ds);
}
return applType;
@@ -1628,7 +1628,7 @@ BuildCommandLine(
* Nothing to escape.
*/
- Tcl_DStringAppend(&ds, arg, -1);
+ Tcl_DStringAppend(&ds, arg, TCL_INDEX_NONE);
} else {
start = arg;
for (special = arg; *special != '\0'; ) {
diff --git a/win/tclWinSerial.c b/win/tclWinSerial.c
index 403c9d5..f087d70 100644
--- a/win/tclWinSerial.c
+++ b/win/tclWinSerial.c
@@ -1678,7 +1678,7 @@ SerialSetOptionProc(
goto getStateFailed;
}
Tcl_DStringInit(&ds);
- native = Tcl_UtfToWCharDString(value, -1, &ds);
+ native = Tcl_UtfToWCharDString(value, TCL_INDEX_NONE, &ds);
result = BuildCommDCBW(native, &dcb);
Tcl_DStringFree(&ds);
@@ -1779,7 +1779,7 @@ SerialSetOptionProc(
if (interp != NULL) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
"bad value for -xchar: should be a list of"
- " two elements with each a single 8-bit character", -1));
+ " two elements with each a single 8-bit character", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TCL", "VALUE", "XCHAR", NULL);
}
ckfree(argv);
@@ -1852,7 +1852,7 @@ SerialSetOptionProc(
(DWORD) (flag ? SETDTR : CLRDTR))) {
if (interp != NULL) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "can't set DTR signal", -1));
+ "can't set DTR signal", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TCL", "OPERATION",
"FCONFIGURE", "TTY_SIGNAL", NULL);
}
@@ -1864,7 +1864,7 @@ SerialSetOptionProc(
(DWORD) (flag ? SETRTS : CLRRTS))) {
if (interp != NULL) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "can't set RTS signal", -1));
+ "can't set RTS signal", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TCL", "OPERATION",
"FCONFIGURE", "TTY_SIGNAL", NULL);
}
@@ -1876,7 +1876,7 @@ SerialSetOptionProc(
(DWORD) (flag ? SETBREAK : CLRBREAK))) {
if (interp != NULL) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "can't set BREAK signal", -1));
+ "can't set BREAK signal", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TCL", "OPERATION",
"FCONFIGURE", "TTY_SIGNAL", NULL);
}
diff --git a/win/tclWinSock.c b/win/tclWinSock.c
index 60575df..e806423 100644
--- a/win/tclWinSock.c
+++ b/win/tclWinSock.c
@@ -377,7 +377,7 @@ InitializeHostName(
* Convert string from native to UTF then change to lowercase.
*/
- Tcl_UtfToLower(Tcl_WCharToUtfDString(wbuf, -1, &ds));
+ Tcl_UtfToLower(Tcl_WCharToUtfDString(wbuf, TCL_INDEX_NONE, &ds));
} else {
if (TclpHasSockets(NULL) == TCL_OK) {
@@ -392,7 +392,7 @@ InitializeHostName(
Tcl_DStringSetLength(&inDs, 256);
if (gethostname(Tcl_DStringValue(&inDs),
Tcl_DStringLength(&inDs)) == 0) {
- Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&inDs), -1,
+ Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&inDs), TCL_INDEX_NONE,
&ds);
}
Tcl_DStringFree(&inDs);
diff --git a/win/tclooConfig.sh b/win/tclooConfig.sh
index 4c2068c..a400b5b 100644
--- a/win/tclooConfig.sh
+++ b/win/tclooConfig.sh
@@ -16,4 +16,4 @@ TCLOO_STUB_LIB_SPEC=""
TCLOO_INCLUDE_SPEC=""
TCLOO_PRIVATE_INCLUDE_SPEC=""
TCLOO_CFLAGS=""
-TCLOO_VERSION=1.2.0
+TCLOO_VERSION=1.3