diff options
| author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2022-08-21 21:43:16 (GMT) | 
|---|---|---|
| committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2022-08-21 21:43:16 (GMT) | 
| commit | c42f34e33320fc95bf80bdca0da2bae7bebbbe0f (patch) | |
| tree | e045a34d312e2e08725507f0d2e43c6d65bc400a | |
| parent | 64a63fa7c5594097d782968787ad37e46f9e4f5e (diff) | |
| parent | 916d72ec1ce61ebd585a78c6a9565f5c49bb8d24 (diff) | |
| download | tcl-c42f34e33320fc95bf80bdca0da2bae7bebbbe0f.zip tcl-c42f34e33320fc95bf80bdca0da2bae7bebbbe0f.tar.gz tcl-c42f34e33320fc95bf80bdca0da2bae7bebbbe0f.tar.bz2 | |
Merge 8.7
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, ¶mc, ¶mv);      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 | 
