Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9856afd590 | ||
|
|
a9ef86f152 | ||
|
|
24a04c0819 | ||
|
|
9077b8ce60 | ||
|
|
da169b1600 | ||
|
|
570af63547 | ||
|
|
6e7c474da7 | ||
|
|
fd2d896c8f | ||
|
|
f597744c5f | ||
|
|
261fbdac7e | ||
|
|
64eaafad72 | ||
|
|
23178bb208 | ||
|
|
6c0170972f | ||
|
|
85233a4693 | ||
|
|
189d0a83d2 | ||
|
|
f08ea44564 | ||
|
|
26b5bf3eca | ||
|
|
cd525c7bb1 | ||
|
|
bb1c5fe833 | ||
|
|
3eef9fcea1 | ||
|
|
906cf353b2 | ||
|
|
8a44c35e8c | ||
|
|
92f8ce13df | ||
|
|
e68b491806 |
+22
-10
@@ -2262,7 +2262,7 @@ StringRplcCmd(
|
||||
Tcl_Obj *const objv[]) /* Argument objects. */
|
||||
{
|
||||
Tcl_UniChar *ustring;
|
||||
int first, last, length;
|
||||
int first, last, length, end;
|
||||
|
||||
if (objc < 4 || objc > 5) {
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "string first last ?string?");
|
||||
@@ -2270,20 +2270,33 @@ StringRplcCmd(
|
||||
}
|
||||
|
||||
ustring = Tcl_GetUnicodeFromObj(objv[1], &length);
|
||||
length--;
|
||||
end = length - 1;
|
||||
|
||||
if (TclGetIntForIndexM(interp, objv[2], length, &first) != TCL_OK ||
|
||||
TclGetIntForIndexM(interp, objv[3], length, &last) != TCL_OK){
|
||||
if (TclGetIntForIndexM(interp, objv[2], end, &first) != TCL_OK ||
|
||||
TclGetIntForIndexM(interp, objv[3], end, &last) != TCL_OK){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if ((last < first) || (last < 0) || (first > length)) {
|
||||
/*
|
||||
* The following test screens out most empty substrings as
|
||||
* candidates for replacement. When they are detected, no
|
||||
* replacement is done, and the result is the original string,
|
||||
*/
|
||||
if ((last < 0) || /* Range ends before start of string */
|
||||
(first > end) || /* Range begins after end of string */
|
||||
(last < first)) { /* Range begins after it starts */
|
||||
|
||||
/*
|
||||
* BUT!!! when (end < 0) -- an empty original string -- we can
|
||||
* have (first <= end < 0 <= last) and an empty string is permitted
|
||||
* to be replaced.
|
||||
*/
|
||||
Tcl_SetObjResult(interp, objv[1]);
|
||||
} else {
|
||||
Tcl_Obj *resultPtr;
|
||||
|
||||
ustring = Tcl_GetUnicodeFromObj(objv[1], &length);
|
||||
length--;
|
||||
end = length-1;
|
||||
|
||||
if (first < 0) {
|
||||
first = 0;
|
||||
@@ -2293,9 +2306,9 @@ StringRplcCmd(
|
||||
if (objc == 5) {
|
||||
Tcl_AppendObjToObj(resultPtr, objv[4]);
|
||||
}
|
||||
if (last < length) {
|
||||
if (last < end) {
|
||||
Tcl_AppendUnicodeToObj(resultPtr, ustring + last + 1,
|
||||
length - last);
|
||||
end - last);
|
||||
}
|
||||
Tcl_SetObjResult(interp, resultPtr);
|
||||
}
|
||||
@@ -3127,8 +3140,7 @@ StringTrimCmd(
|
||||
}
|
||||
string1 = TclGetStringFromObj(objv[1], &length1);
|
||||
|
||||
triml = TclTrimLeft(string1, length1, string2, length2);
|
||||
trimr = TclTrimRight(string1 + triml, length1 - triml, string2, length2);
|
||||
triml = TclTrim(string1, length1, string2, length2, &trimr);
|
||||
|
||||
Tcl_SetObjResult(interp,
|
||||
Tcl_NewStringObj(string1 + triml, length1 - triml - trimr));
|
||||
|
||||
+10
-10
@@ -373,14 +373,7 @@ TclFileDeleteCmd(
|
||||
*/
|
||||
|
||||
if (Tcl_FSLstat(objv[i], &statBuf) != 0) {
|
||||
/*
|
||||
* Trying to delete a file that does not exist is not considered
|
||||
* an error, just a no-op
|
||||
*/
|
||||
|
||||
if (errno != ENOENT) {
|
||||
result = TCL_ERROR;
|
||||
}
|
||||
result = TCL_ERROR;
|
||||
} else if (S_ISDIR(statBuf.st_mode)) {
|
||||
/*
|
||||
* We own a reference count on errorBuffer, if it was set as a
|
||||
@@ -416,13 +409,20 @@ TclFileDeleteCmd(
|
||||
}
|
||||
|
||||
if (result != TCL_OK) {
|
||||
result = TCL_ERROR;
|
||||
|
||||
/*
|
||||
* Avoid possible race condition (file/directory deleted after call
|
||||
* of lstat), so bypass ENOENT because not an error, just a no-op
|
||||
*/
|
||||
if (errno == ENOENT) {
|
||||
result = TCL_OK;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* It is important that we break on error, otherwise we might end
|
||||
* up owning reference counts on numerous errorBuffers.
|
||||
*/
|
||||
|
||||
result = TCL_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-9
@@ -626,17 +626,18 @@ Tcl_SetStdChannel(
|
||||
{
|
||||
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
|
||||
|
||||
int init = channel ? 1 : -1;
|
||||
switch (type) {
|
||||
case TCL_STDIN:
|
||||
tsdPtr->stdinInitialized = 1;
|
||||
tsdPtr->stdinInitialized = init;
|
||||
tsdPtr->stdinChannel = channel;
|
||||
break;
|
||||
case TCL_STDOUT:
|
||||
tsdPtr->stdoutInitialized = 1;
|
||||
tsdPtr->stdoutInitialized = init;
|
||||
tsdPtr->stdoutChannel = channel;
|
||||
break;
|
||||
case TCL_STDERR:
|
||||
tsdPtr->stderrInitialized = 1;
|
||||
tsdPtr->stderrInitialized = init;
|
||||
tsdPtr->stderrChannel = channel;
|
||||
break;
|
||||
}
|
||||
@@ -673,8 +674,8 @@ Tcl_GetStdChannel(
|
||||
switch (type) {
|
||||
case TCL_STDIN:
|
||||
if (!tsdPtr->stdinInitialized) {
|
||||
tsdPtr->stdinInitialized = -1;
|
||||
tsdPtr->stdinChannel = TclpGetDefaultStdChannel(TCL_STDIN);
|
||||
tsdPtr->stdinInitialized = 1;
|
||||
|
||||
/*
|
||||
* Artificially bump the refcount to ensure that the channel is
|
||||
@@ -686,6 +687,7 @@ Tcl_GetStdChannel(
|
||||
*/
|
||||
|
||||
if (tsdPtr->stdinChannel != NULL) {
|
||||
tsdPtr->stdinInitialized = 1;
|
||||
Tcl_RegisterChannel(NULL, tsdPtr->stdinChannel);
|
||||
}
|
||||
}
|
||||
@@ -693,9 +695,10 @@ Tcl_GetStdChannel(
|
||||
break;
|
||||
case TCL_STDOUT:
|
||||
if (!tsdPtr->stdoutInitialized) {
|
||||
tsdPtr->stdoutInitialized = -1;
|
||||
tsdPtr->stdoutChannel = TclpGetDefaultStdChannel(TCL_STDOUT);
|
||||
tsdPtr->stdoutInitialized = 1;
|
||||
if (tsdPtr->stdoutChannel != NULL) {
|
||||
tsdPtr->stdoutInitialized = 1;
|
||||
Tcl_RegisterChannel(NULL, tsdPtr->stdoutChannel);
|
||||
}
|
||||
}
|
||||
@@ -703,9 +706,10 @@ Tcl_GetStdChannel(
|
||||
break;
|
||||
case TCL_STDERR:
|
||||
if (!tsdPtr->stderrInitialized) {
|
||||
tsdPtr->stderrInitialized = -1;
|
||||
tsdPtr->stderrChannel = TclpGetDefaultStdChannel(TCL_STDERR);
|
||||
tsdPtr->stderrInitialized = 1;
|
||||
if (tsdPtr->stderrChannel != NULL) {
|
||||
tsdPtr->stderrInitialized = 1;
|
||||
Tcl_RegisterChannel(NULL, tsdPtr->stderrChannel);
|
||||
}
|
||||
}
|
||||
@@ -975,7 +979,7 @@ CheckForStdChannelsBeingClosed(
|
||||
ChannelState *statePtr = ((Channel *) chan)->state;
|
||||
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
|
||||
|
||||
if (tsdPtr->stdinInitialized
|
||||
if (tsdPtr->stdinInitialized == 1
|
||||
&& tsdPtr->stdinChannel != NULL
|
||||
&& statePtr == ((Channel *)tsdPtr->stdinChannel)->state) {
|
||||
if (statePtr->refCount < 2) {
|
||||
@@ -983,7 +987,7 @@ CheckForStdChannelsBeingClosed(
|
||||
tsdPtr->stdinChannel = NULL;
|
||||
return;
|
||||
}
|
||||
} else if (tsdPtr->stdoutInitialized
|
||||
} else if (tsdPtr->stdoutInitialized == 1
|
||||
&& tsdPtr->stdoutChannel != NULL
|
||||
&& statePtr == ((Channel *)tsdPtr->stdoutChannel)->state) {
|
||||
if (statePtr->refCount < 2) {
|
||||
@@ -991,7 +995,7 @@ CheckForStdChannelsBeingClosed(
|
||||
tsdPtr->stdoutChannel = NULL;
|
||||
return;
|
||||
}
|
||||
} else if (tsdPtr->stderrInitialized
|
||||
} else if (tsdPtr->stderrInitialized == 1
|
||||
&& tsdPtr->stderrChannel != NULL
|
||||
&& statePtr == ((Channel *)tsdPtr->stderrChannel)->state) {
|
||||
if (statePtr->refCount < 2) {
|
||||
|
||||
@@ -2747,6 +2747,8 @@ MODULE_SCOPE int TclSubstTokens(Tcl_Interp *interp, Tcl_Token *tokenPtr,
|
||||
int *clNextOuter, CONST char *outerScript);
|
||||
MODULE_SCOPE void TclTransferResult(Tcl_Interp *sourceInterp, int result,
|
||||
Tcl_Interp *targetInterp);
|
||||
MODULE_SCOPE int TclTrim(const char *bytes, int numBytes,
|
||||
const char *trim, int numTrim, int *trimRight);
|
||||
MODULE_SCOPE int TclTrimLeft(const char *bytes, int numBytes,
|
||||
const char *trim, int numTrim);
|
||||
MODULE_SCOPE int TclTrimRight(const char *bytes, int numBytes,
|
||||
|
||||
+109
-90
@@ -6479,14 +6479,32 @@ MakeCachedEnsembleCommand(
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void
|
||||
ClearTable(
|
||||
EnsembleConfig *ensemblePtr)
|
||||
{
|
||||
Tcl_HashTable *hash = &ensemblePtr->subcommandTable;
|
||||
|
||||
if (hash->numEntries != 0) {
|
||||
Tcl_HashSearch search;
|
||||
Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(hash, &search);
|
||||
|
||||
while (hPtr != NULL) {
|
||||
Tcl_Obj *prefixObj = Tcl_GetHashValue(hPtr);
|
||||
Tcl_DecrRefCount(prefixObj);
|
||||
hPtr = Tcl_NextHashEntry(&search);
|
||||
}
|
||||
ckfree((char *) ensemblePtr->subcommandArrayPtr);
|
||||
}
|
||||
Tcl_DeleteHashTable(hash);
|
||||
}
|
||||
|
||||
static void
|
||||
DeleteEnsembleConfig(
|
||||
ClientData clientData)
|
||||
{
|
||||
EnsembleConfig *ensemblePtr = clientData;
|
||||
Namespace *nsPtr = ensemblePtr->nsPtr;
|
||||
Tcl_HashSearch search;
|
||||
Tcl_HashEntry *hEnt;
|
||||
|
||||
/*
|
||||
* Unlink from the ensemble chain if it has not been marked as having been
|
||||
@@ -6519,17 +6537,8 @@ DeleteEnsembleConfig(
|
||||
* Kill the pointer-containing fields.
|
||||
*/
|
||||
|
||||
if (ensemblePtr->subcommandTable.numEntries != 0) {
|
||||
ckfree((char *) ensemblePtr->subcommandArrayPtr);
|
||||
}
|
||||
hEnt = Tcl_FirstHashEntry(&ensemblePtr->subcommandTable, &search);
|
||||
while (hEnt != NULL) {
|
||||
Tcl_Obj *prefixObj = Tcl_GetHashValue(hEnt);
|
||||
ClearTable(ensemblePtr);
|
||||
|
||||
Tcl_DecrRefCount(prefixObj);
|
||||
hEnt = Tcl_NextHashEntry(&search);
|
||||
}
|
||||
Tcl_DeleteHashTable(&ensemblePtr->subcommandTable);
|
||||
if (ensemblePtr->subcmdList != NULL) {
|
||||
Tcl_DecrRefCount(ensemblePtr->subcmdList);
|
||||
}
|
||||
@@ -6582,101 +6591,111 @@ BuildEnsembleConfig(
|
||||
int i, j, isNew;
|
||||
Tcl_HashTable *hash = &ensemblePtr->subcommandTable;
|
||||
Tcl_HashEntry *hPtr;
|
||||
Tcl_Obj *mapDict = ensemblePtr->subcommandDict;
|
||||
Tcl_Obj *subList = ensemblePtr->subcmdList;
|
||||
|
||||
ClearTable(ensemblePtr);
|
||||
Tcl_InitHashTable(hash, TCL_STRING_KEYS);
|
||||
|
||||
if (subList) {
|
||||
int subc;
|
||||
Tcl_Obj **subv, *target, *cmdObj, *cmdPrefixObj;
|
||||
char *name;
|
||||
|
||||
if (hash->numEntries != 0) {
|
||||
/*
|
||||
* Remove pre-existing table.
|
||||
* There is a list of exactly what subcommands go in the table.
|
||||
* Must determine the target for each.
|
||||
*/
|
||||
|
||||
Tcl_HashSearch search;
|
||||
|
||||
ckfree((char *) ensemblePtr->subcommandArrayPtr);
|
||||
hPtr = Tcl_FirstHashEntry(hash, &search);
|
||||
while (hPtr != NULL) {
|
||||
Tcl_Obj *prefixObj = Tcl_GetHashValue(hPtr);
|
||||
Tcl_DecrRefCount(prefixObj);
|
||||
hPtr = Tcl_NextHashEntry(&search);
|
||||
}
|
||||
Tcl_DeleteHashTable(hash);
|
||||
Tcl_InitHashTable(hash, TCL_STRING_KEYS);
|
||||
}
|
||||
|
||||
/*
|
||||
* See if we've got an export list. If so, we will only export exactly
|
||||
* those commands, which may be either implemented by the prefix in the
|
||||
* subcommandDict or mapped directly onto the namespace's commands.
|
||||
*/
|
||||
|
||||
if (ensemblePtr->subcmdList != NULL) {
|
||||
Tcl_Obj **subcmdv, *target, *cmdObj, *cmdPrefixObj;
|
||||
int subcmdc;
|
||||
|
||||
TclListObjGetElements(NULL, ensemblePtr->subcmdList, &subcmdc,
|
||||
&subcmdv);
|
||||
for (i=0 ; i<subcmdc ; i++) {
|
||||
char *name = TclGetString(subcmdv[i]);
|
||||
|
||||
hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
|
||||
|
||||
Tcl_ListObjGetElements(NULL, subList, &subc, &subv);
|
||||
if (subList == mapDict) {
|
||||
/*
|
||||
* Skip non-unique cases.
|
||||
* Strange case where explicit list of subcommands is same value
|
||||
* as the dict mapping to targets.
|
||||
*/
|
||||
|
||||
if (!isNew) {
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < subc; i += 2) {
|
||||
name = TclGetString(subv[i]);
|
||||
hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
|
||||
if (!isNew) {
|
||||
cmdObj = (Tcl_Obj *)Tcl_GetHashValue(hPtr);
|
||||
Tcl_DecrRefCount(cmdObj);
|
||||
}
|
||||
Tcl_SetHashValue(hPtr, subv[i+1]);
|
||||
Tcl_IncrRefCount(subv[i+1]);
|
||||
|
||||
/*
|
||||
* Look in our dictionary (if present) for the command.
|
||||
*/
|
||||
|
||||
if (ensemblePtr->subcommandDict != NULL) {
|
||||
Tcl_DictObjGet(NULL, ensemblePtr->subcommandDict, subcmdv[i],
|
||||
&target);
|
||||
if (target != NULL) {
|
||||
Tcl_SetHashValue(hPtr, target);
|
||||
Tcl_IncrRefCount(target);
|
||||
continue;
|
||||
name = TclGetString(subv[i+1]);
|
||||
hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
|
||||
if (isNew) {
|
||||
cmdObj = Tcl_NewStringObj(ensemblePtr->nsPtr->fullName, -1);
|
||||
if (ensemblePtr->nsPtr->parentPtr != NULL) {
|
||||
Tcl_AppendStringsToObj(cmdObj, "::", name, NULL);
|
||||
} else {
|
||||
Tcl_AppendStringsToObj(cmdObj, name, NULL);
|
||||
}
|
||||
cmdPrefixObj = Tcl_NewListObj(1, &cmdObj);
|
||||
Tcl_SetHashValue(hPtr, cmdPrefixObj);
|
||||
Tcl_IncrRefCount(cmdPrefixObj);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Usual case where we can freely act on the list and dict. */
|
||||
|
||||
/*
|
||||
* Not there, 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).
|
||||
*/
|
||||
for (i = 0; i < subc; i++) {
|
||||
name = TclGetString(subv[i]);
|
||||
hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
|
||||
if (!isNew) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cmdObj = Tcl_NewStringObj(ensemblePtr->nsPtr->fullName, -1);
|
||||
if (ensemblePtr->nsPtr->parentPtr != NULL) {
|
||||
Tcl_AppendStringsToObj(cmdObj, "::", name, NULL);
|
||||
} else {
|
||||
Tcl_AppendStringsToObj(cmdObj, name, NULL);
|
||||
/* Lookup target in the dictionary */
|
||||
if (mapDict) {
|
||||
Tcl_DictObjGet(NULL, mapDict, subv[i], &target);
|
||||
if (target) {
|
||||
Tcl_SetHashValue(hPtr, target);
|
||||
Tcl_IncrRefCount(target);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
cmdObj = Tcl_NewStringObj(ensemblePtr->nsPtr->fullName, -1);
|
||||
if (ensemblePtr->nsPtr->parentPtr != NULL) {
|
||||
Tcl_AppendStringsToObj(cmdObj, "::", name, NULL);
|
||||
} else {
|
||||
Tcl_AppendStringsToObj(cmdObj, name, NULL);
|
||||
}
|
||||
cmdPrefixObj = Tcl_NewListObj(1, &cmdObj);
|
||||
Tcl_SetHashValue(hPtr, cmdPrefixObj);
|
||||
Tcl_IncrRefCount(cmdPrefixObj);
|
||||
}
|
||||
cmdPrefixObj = Tcl_NewListObj(1, &cmdObj);
|
||||
Tcl_SetHashValue(hPtr, cmdPrefixObj);
|
||||
Tcl_IncrRefCount(cmdPrefixObj);
|
||||
}
|
||||
} else if (ensemblePtr->subcommandDict != NULL) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
} 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.
|
||||
*/
|
||||
|
||||
Tcl_DictSearch dictSearch;
|
||||
Tcl_Obj *keyObj, *valueObj;
|
||||
int done;
|
||||
Tcl_DictSearch dictSearch;
|
||||
Tcl_Obj *keyObj, *valueObj;
|
||||
int done;
|
||||
|
||||
Tcl_DictObjFirst(NULL, ensemblePtr->subcommandDict, &dictSearch,
|
||||
&keyObj, &valueObj, &done);
|
||||
while (!done) {
|
||||
char *name = TclGetString(keyObj);
|
||||
Tcl_DictObjFirst(NULL, ensemblePtr->subcommandDict, &dictSearch,
|
||||
&keyObj, &valueObj, &done);
|
||||
while (!done) {
|
||||
char *name = TclGetString(keyObj);
|
||||
|
||||
hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
|
||||
Tcl_SetHashValue(hPtr, valueObj);
|
||||
Tcl_IncrRefCount(valueObj);
|
||||
Tcl_DictObjNext(&dictSearch, &keyObj, &valueObj, &done);
|
||||
}
|
||||
hPtr = Tcl_CreateHashEntry(hash, name, &isNew);
|
||||
Tcl_SetHashValue(hPtr, valueObj);
|
||||
Tcl_IncrRefCount(valueObj);
|
||||
Tcl_DictObjNext(&dictSearch, &keyObj, &valueObj, &done);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Discover what commands are actually exported by the namespace.
|
||||
|
||||
@@ -1869,7 +1869,6 @@ Tcl_FSGetNormalizedPath(
|
||||
*/
|
||||
|
||||
(void) Tcl_GetStringFromObj(dir, &cwdLen);
|
||||
cwdLen += (Tcl_GetString(copy)[cwdLen] == '/');
|
||||
|
||||
/* Normalize the combined string. */
|
||||
|
||||
@@ -1890,13 +1889,13 @@ Tcl_FSGetNormalizedPath(
|
||||
* to a normalized head, we can more efficiently normalize the
|
||||
* combined path by passing over only the unnormalized tail
|
||||
* portion. When this is sufficient, prior developers claim
|
||||
* this should be much faster. We use 'cwdLen-1' so that we are
|
||||
* this should be much faster. We use 'cwdLen' so that we are
|
||||
* already pointing at the dir-separator that we know about.
|
||||
* The normalization code will actually start off directly
|
||||
* after that separator.
|
||||
*/
|
||||
|
||||
TclFSNormalizeToUniquePath(interp, copy, cwdLen-1);
|
||||
TclFSNormalizeToUniquePath(interp, copy, cwdLen);
|
||||
}
|
||||
|
||||
/* Now we need to construct the new path object. */
|
||||
|
||||
@@ -432,6 +432,8 @@ PkgRequireCore(
|
||||
* The version of the package sought is better than the
|
||||
* currently selected version.
|
||||
*/
|
||||
ckfree(bestVersion);
|
||||
bestVersion = NULL;
|
||||
goto newbest;
|
||||
}
|
||||
} else {
|
||||
@@ -460,6 +462,8 @@ PkgRequireCore(
|
||||
* This stable version of the package sought is better
|
||||
* than the currently selected stable version.
|
||||
*/
|
||||
ckfree(bestStableVersion);
|
||||
bestStableVersion = NULL;
|
||||
goto newstable;
|
||||
}
|
||||
} else {
|
||||
|
||||
+181
-56
@@ -1496,12 +1496,48 @@ Tcl_Backslash(
|
||||
return (char) ch;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* UtfWellFormedEnd --
|
||||
* Checks the end of utf string is malformed, if yes - wraps bytes
|
||||
* to the given buffer (as well-formed NTS string). The buffer
|
||||
* argument should be initialized by the caller and ready to use.
|
||||
*
|
||||
* Results:
|
||||
* The bytes with well-formed end of the string.
|
||||
*
|
||||
* Side effects:
|
||||
* Buffer (DString) may be allocated, so must be released.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static inline const char*
|
||||
UtfWellFormedEnd(
|
||||
Tcl_DString *buffer, /* Buffer used to hold well-formed string. */
|
||||
CONST char *bytes, /* Pointer to the beginning of the string. */
|
||||
int length) /* Length of the string. */
|
||||
{
|
||||
CONST char *l = bytes + length;
|
||||
CONST char *p = Tcl_UtfPrev(l, bytes);
|
||||
|
||||
if (Tcl_UtfCharComplete(p, l - p)) {
|
||||
return bytes;
|
||||
}
|
||||
/*
|
||||
* Malformed utf-8 end, be sure we've NTS to safe compare of end-character,
|
||||
* avoid segfault by access violation out of range.
|
||||
*/
|
||||
Tcl_DStringAppend(buffer, bytes, length);
|
||||
return Tcl_DStringValue(buffer);
|
||||
}
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclTrimRight --
|
||||
* Takes two counted strings in the Tcl encoding which must both be
|
||||
* null terminated. Conceptually trims from the right side of the
|
||||
* Takes two counted strings in the Tcl encoding. Conceptually
|
||||
* finds the sub string (offset) to trim from the right side of the
|
||||
* first string all characters found in the second string.
|
||||
*
|
||||
* Results:
|
||||
@@ -1513,8 +1549,8 @@ Tcl_Backslash(
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
TclTrimRight(
|
||||
static inline int
|
||||
TrimRight(
|
||||
const char *bytes, /* String to be trimmed... */
|
||||
int numBytes, /* ...and its length in bytes */
|
||||
const char *trim, /* String of trim characters... */
|
||||
@@ -1523,15 +1559,6 @@ TclTrimRight(
|
||||
const char *p = bytes + numBytes;
|
||||
int pInc;
|
||||
|
||||
if ((bytes[numBytes] != '\0') || (trim[numTrim] != '\0')) {
|
||||
Tcl_Panic("TclTrimRight works only on null-terminated strings");
|
||||
}
|
||||
|
||||
/* Empty strings -> nothing to do */
|
||||
if ((numBytes == 0) || (numTrim == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Outer loop: iterate over string to be trimmed */
|
||||
do {
|
||||
Tcl_UniChar ch1;
|
||||
@@ -1563,13 +1590,44 @@ TclTrimRight(
|
||||
|
||||
return numBytes - (p - bytes);
|
||||
}
|
||||
|
||||
int
|
||||
TclTrimRight(
|
||||
const char *bytes, /* String to be trimmed... */
|
||||
int numBytes, /* ...and its length in bytes */
|
||||
const char *trim, /* String of trim characters... */
|
||||
int numTrim) /* ...and its length in bytes */
|
||||
{
|
||||
int res;
|
||||
Tcl_DString bytesBuf, trimBuf;
|
||||
|
||||
/* Empty strings -> nothing to do */
|
||||
if ((numBytes == 0) || (numTrim == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Tcl_DStringInit(&bytesBuf);
|
||||
Tcl_DStringInit(&trimBuf);
|
||||
bytes = UtfWellFormedEnd(&bytesBuf, bytes, numBytes);
|
||||
trim = UtfWellFormedEnd(&trimBuf, trim, numTrim);
|
||||
|
||||
res = TrimRight(bytes, numBytes, trim, numTrim);
|
||||
if (res > numBytes) {
|
||||
res = numBytes;
|
||||
}
|
||||
|
||||
Tcl_DStringFree(&bytesBuf);
|
||||
Tcl_DStringFree(&trimBuf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclTrimLeft --
|
||||
* Takes two counted strings in the Tcl encoding which must both be
|
||||
* null terminated. Conceptually trims from the left side of the
|
||||
* Takes two counted strings in the Tcl encoding. Conceptually
|
||||
* finds the sub string (offset) to trim from the left side of the
|
||||
* first string all characters found in the second string.
|
||||
*
|
||||
* Results:
|
||||
@@ -1581,8 +1639,8 @@ TclTrimRight(
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
TclTrimLeft(
|
||||
static inline int
|
||||
TrimLeft(
|
||||
const char *bytes, /* String to be trimmed... */
|
||||
int numBytes, /* ...and its length in bytes */
|
||||
const char *trim, /* String of trim characters... */
|
||||
@@ -1590,15 +1648,6 @@ TclTrimLeft(
|
||||
{
|
||||
const char *p = bytes;
|
||||
|
||||
if ((bytes[numBytes] != '\0') || (trim[numTrim] != '\0')) {
|
||||
Tcl_Panic("TclTrimLeft works only on null-terminated strings");
|
||||
}
|
||||
|
||||
/* Empty strings -> nothing to do */
|
||||
if ((numBytes == 0) || (numTrim == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Outer loop: iterate over string to be trimmed */
|
||||
do {
|
||||
Tcl_UniChar ch1;
|
||||
@@ -1626,10 +1675,99 @@ TclTrimLeft(
|
||||
|
||||
p += pInc;
|
||||
numBytes -= pInc;
|
||||
} while (numBytes);
|
||||
} while (numBytes > 0);
|
||||
|
||||
return p - bytes;
|
||||
}
|
||||
|
||||
int
|
||||
TclTrimLeft(
|
||||
const char *bytes, /* String to be trimmed... */
|
||||
int numBytes, /* ...and its length in bytes */
|
||||
const char *trim, /* String of trim characters... */
|
||||
int numTrim) /* ...and its length in bytes */
|
||||
{
|
||||
int res;
|
||||
Tcl_DString bytesBuf, trimBuf;
|
||||
|
||||
/* Empty strings -> nothing to do */
|
||||
if ((numBytes == 0) || (numTrim == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Tcl_DStringInit(&bytesBuf);
|
||||
Tcl_DStringInit(&trimBuf);
|
||||
bytes = UtfWellFormedEnd(&bytesBuf, bytes, numBytes);
|
||||
trim = UtfWellFormedEnd(&trimBuf, trim, numTrim);
|
||||
|
||||
res = TrimLeft(bytes, numBytes, trim, numTrim);
|
||||
if (res > numBytes) {
|
||||
res = numBytes;
|
||||
}
|
||||
|
||||
Tcl_DStringFree(&bytesBuf);
|
||||
Tcl_DStringFree(&trimBuf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclTrim --
|
||||
* Finds the sub string (offset) to trim from both sides of the
|
||||
* first string all characters found in the second string.
|
||||
*
|
||||
* Results:
|
||||
* The number of bytes to be removed from the start of the string
|
||||
*
|
||||
* Side effects:
|
||||
* None.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
TclTrim(
|
||||
const char *bytes, /* String to be trimmed... */
|
||||
int numBytes, /* ...and its length in bytes */
|
||||
const char *trim, /* String of trim characters... */
|
||||
int numTrim, /* ...and its length in bytes */
|
||||
int *trimRight) /* Offset from the end of the string. */
|
||||
{
|
||||
int trimLeft;
|
||||
Tcl_DString bytesBuf, trimBuf;
|
||||
|
||||
*trimRight = 0;
|
||||
/* Empty strings -> nothing to do */
|
||||
if ((numBytes == 0) || (numTrim == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Tcl_DStringInit(&bytesBuf);
|
||||
Tcl_DStringInit(&trimBuf);
|
||||
bytes = UtfWellFormedEnd(&bytesBuf, bytes, numBytes);
|
||||
trim = UtfWellFormedEnd(&trimBuf, trim, numTrim);
|
||||
|
||||
trimLeft = TrimLeft(bytes, numBytes, trim, numTrim);
|
||||
if (trimLeft > numBytes) {
|
||||
trimLeft = numBytes;
|
||||
}
|
||||
numBytes -= trimLeft;
|
||||
/* have to trim yet (first char was already verified within TrimLeft) */
|
||||
if (numBytes > 1) {
|
||||
bytes += trimLeft;
|
||||
*trimRight = TrimRight(bytes, numBytes, trim, numTrim);
|
||||
if (*trimRight > numBytes) {
|
||||
*trimRight = numBytes;
|
||||
}
|
||||
}
|
||||
|
||||
Tcl_DStringFree(&bytesBuf);
|
||||
Tcl_DStringFree(&trimBuf);
|
||||
|
||||
return trimLeft;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
@@ -1687,25 +1825,18 @@ Tcl_Concat(
|
||||
result = (char *) ckalloc((unsigned) (bytesNeeded + argc));
|
||||
|
||||
for (p = result, i = 0; i < argc; i++) {
|
||||
int trim, elemLength;
|
||||
int triml, trimr, elemLength;
|
||||
const char *element;
|
||||
|
||||
element = argv[i];
|
||||
elemLength = strlen(argv[i]);
|
||||
|
||||
/* Trim away the leading whitespace */
|
||||
trim = TclTrimLeft(element, elemLength, CONCAT_WS, CONCAT_WS_SIZE);
|
||||
element += trim;
|
||||
elemLength -= trim;
|
||||
|
||||
/*
|
||||
* Trim away the trailing whitespace. Do not permit trimming
|
||||
* to expose a final backslash character.
|
||||
*/
|
||||
|
||||
trim = TclTrimRight(element, elemLength, CONCAT_WS, CONCAT_WS_SIZE);
|
||||
trim -= trim && (element[elemLength - trim - 1] == '\\');
|
||||
elemLength -= trim;
|
||||
/* Trim away the leading/trailing whitespace. */
|
||||
triml = TclTrim(element, elemLength, CONCAT_WS, CONCAT_WS_SIZE, &trimr);
|
||||
element += triml;
|
||||
elemLength -= triml + trimr;
|
||||
/* Do not permit trimming to expose a final backslash character. */
|
||||
elemLength += trimr && (element[elemLength - 1] == '\\');
|
||||
|
||||
/* If we're left with empty element after trimming, do nothing */
|
||||
if (elemLength == 0) {
|
||||
@@ -1832,23 +1963,16 @@ Tcl_ConcatObj(
|
||||
Tcl_SetObjLength(resPtr, 0);
|
||||
|
||||
for (i = 0; i < objc; i++) {
|
||||
int trim;
|
||||
int triml, trimr;
|
||||
|
||||
element = TclGetStringFromObj(objv[i], &elemLength);
|
||||
|
||||
/* Trim away the leading whitespace */
|
||||
trim = TclTrimLeft(element, elemLength, CONCAT_WS, CONCAT_WS_SIZE);
|
||||
element += trim;
|
||||
elemLength -= trim;
|
||||
|
||||
/*
|
||||
* Trim away the trailing whitespace. Do not permit trimming
|
||||
* to expose a final backslash character.
|
||||
*/
|
||||
|
||||
trim = TclTrimRight(element, elemLength, CONCAT_WS, CONCAT_WS_SIZE);
|
||||
trim -= trim && (element[elemLength - trim - 1] == '\\');
|
||||
elemLength -= trim;
|
||||
/* Trim away the leading/trailing whitespace. */
|
||||
triml = TclTrim(element, elemLength, CONCAT_WS, CONCAT_WS_SIZE, &trimr);
|
||||
element += triml;
|
||||
elemLength -= triml + trimr;
|
||||
/* Do not permit trimming to expose a final backslash character. */
|
||||
elemLength += trimr && (element[elemLength - 1] == '\\');
|
||||
|
||||
/* If we're left with empty element after trimming, do nothing */
|
||||
if (elemLength == 0) {
|
||||
@@ -2340,7 +2464,8 @@ TclStringMatchObj(
|
||||
udata = Tcl_GetUnicodeFromObj(strObj, &length);
|
||||
uptn = Tcl_GetUnicodeFromObj(ptnObj, &plen);
|
||||
match = TclUniCharMatch(udata, length, uptn, plen, flags);
|
||||
} else if (TclIsPureByteArray(strObj) && !flags) {
|
||||
} else if (TclIsPureByteArray(strObj) && TclIsPureByteArray(ptnObj)
|
||||
&& !flags) {
|
||||
unsigned char *data, *ptn;
|
||||
|
||||
data = Tcl_GetByteArrayFromObj(strObj, &length);
|
||||
|
||||
@@ -5732,6 +5732,8 @@ test chan-io-47.6 {file events on shared files, deleting file events} {testfeven
|
||||
chan close $f
|
||||
set x
|
||||
} {{script 1} {}}
|
||||
unset path(foo)
|
||||
removeFile foo
|
||||
|
||||
set path(bar) [makeFile {} bar]
|
||||
|
||||
@@ -5834,6 +5836,9 @@ test chan-io-48.3 {testing readability conditions} {stdio unix nonBlockFiles ope
|
||||
chan close $f
|
||||
list $x $l
|
||||
} {done {0 1 0 1 0 1 0 1 0 1 0 1 0 0}}
|
||||
unset path(bar)
|
||||
removeFile bar
|
||||
|
||||
test chan-io-48.4 {lf write, testing readability, ^Z termination, auto read mode} {fileevent} {
|
||||
file delete $path(test1)
|
||||
set f [open $path(test1) w]
|
||||
|
||||
@@ -287,7 +287,6 @@ test exec-6.3 {redirecting stderr through a pipeline} {exec stdio} {
|
||||
# I/O redirection: combinations.
|
||||
|
||||
set path(gorp.file2) [makeFile {} gorp.file2]
|
||||
file delete $path(gorp.file2)
|
||||
|
||||
test exec-7.1 {multiple I/O redirections} {exec} {
|
||||
exec << "command input" > $path(gorp.file2) [interpreter] $path(cat) < $path(gorp.file)
|
||||
|
||||
+9
-5
@@ -770,6 +770,7 @@ test filename-11.16 {Tcl_GlobCmd} {
|
||||
|
||||
set globname "globTest"
|
||||
set horribleglobname "glob\[\{Test"
|
||||
set tildeglobname "./~test.txt"
|
||||
|
||||
test filename-11.17 {Tcl_GlobCmd} {unix} {
|
||||
list [catch {lsort [glob -directory $globname *]} msg] $msg
|
||||
@@ -940,11 +941,11 @@ test filename-11.21.1 {Tcl_GlobCmd} {
|
||||
|
||||
# Get rid of file/dir if it exists, since it will have
|
||||
# been left behind by a previous failed run.
|
||||
if {[file exists $horribleglobname]} {
|
||||
file delete -force $horribleglobname
|
||||
}
|
||||
file delete -force $horribleglobname
|
||||
file rename globTest $horribleglobname
|
||||
set globname $horribleglobname
|
||||
file delete -force $tildeglobname
|
||||
close [open $tildeglobname w]
|
||||
|
||||
test filename-11.22 {Tcl_GlobCmd} {unix} {
|
||||
list [catch {lsort [glob -dir $globname *]} msg] $msg
|
||||
@@ -1067,7 +1068,9 @@ test filename-11.41 {Tcl_GlobCmd} {
|
||||
test filename-11.42 {Tcl_GlobCmd} {
|
||||
set res [list]
|
||||
foreach f [glob -dir [pwd] *] {
|
||||
lappend res [file tail $f]
|
||||
set f [file tail $f]
|
||||
regsub {^./} $f {} f; # until glob bug [2511011fff] don't fixed (tilde expansion prevention).
|
||||
lappend res $f
|
||||
}
|
||||
expr {$res == [glob *]}
|
||||
} {1}
|
||||
@@ -1109,8 +1112,9 @@ test filename-11.49 {Tcl_GlobCmd} {
|
||||
} {1 {bad argument to "-types": abcde}}
|
||||
|
||||
file rename $horribleglobname globTest
|
||||
file delete -force $tildeglobname
|
||||
set globname globTest
|
||||
unset horribleglobname
|
||||
unset horribleglobname tildeglobname
|
||||
|
||||
test filename-12.1 {simple globbing} {unixOrPc} {
|
||||
list [catch {glob {}} msg] $msg
|
||||
|
||||
@@ -508,6 +508,22 @@ test filesystem-1.52.1 {bug f9f390d0fa: file join where strep is not canonical}
|
||||
file normalize $x
|
||||
file join $x
|
||||
} -result /foo
|
||||
test filesystem-1.53 {[Bug 3559678] - normalize when tail is empty} {
|
||||
string match */ [file normalize [lindex [glob -dir [pwd] {{}}] 0]]
|
||||
} 0
|
||||
test filesystem-1.54 {[Bug ce3a211dcb] - normalize when tail is empty} -setup {
|
||||
set save [pwd]
|
||||
cd [set home [makeDirectory ce3a211dcb]]
|
||||
makeDirectory A $home
|
||||
cd [lindex [glob */] 0]
|
||||
} -body {
|
||||
string match */A [pwd]
|
||||
} -cleanup {
|
||||
cd $home
|
||||
removeDirectory A $home
|
||||
cd $save
|
||||
removeDirectory ce3a211dcb
|
||||
} -result 1
|
||||
|
||||
test filesystem-2.0 {new native path} {unix} {
|
||||
foreach f [lsort [glob -nocomplain /usr/bin/c*]] {
|
||||
|
||||
+1
-1
@@ -1818,7 +1818,7 @@ test info-38.2 {location information for uplevel, dl, direct-literal} -match glo
|
||||
* {type source line 1814 file info.test cmd etrace level 1}
|
||||
* {type source line 1812 file info.test cmd uplevel\\ \\\\ level 1}} -cleanup {interp delete sub}
|
||||
|
||||
test info-39.0 {Bug 4b61afd660} -setup {
|
||||
test info-39.2 {Bug 4b61afd660} -setup {
|
||||
proc probe {} {
|
||||
return [dict get [info frame -1] line]
|
||||
}
|
||||
|
||||
@@ -6130,6 +6130,8 @@ test io-47.6 {file events on shared files, deleting file events} {testfevent fil
|
||||
close $f
|
||||
set x
|
||||
} {{script 1} {}}
|
||||
unset path(foo)
|
||||
removeFile foo
|
||||
|
||||
set path(bar) [makeFile {} bar]
|
||||
|
||||
@@ -6232,6 +6234,9 @@ test io-48.3 {testing readability conditions} {stdio unix nonBlockFiles openpipe
|
||||
close $f
|
||||
list $x $l
|
||||
} {done {0 1 0 1 0 1 0 1 0 1 0 1 0 0}}
|
||||
unset path(bar)
|
||||
removeFile bar
|
||||
|
||||
test io-48.4 {lf write, testing readability, ^Z termination, auto read mode} {fileevent} {
|
||||
file delete $path(test1)
|
||||
set f [open $path(test1) w]
|
||||
|
||||
+1
-4
@@ -366,7 +366,6 @@ test iocmd-10.5 {fblocked command} {
|
||||
set path(test4) [makeFile {} test4]
|
||||
set path(test5) [makeFile {} test5]
|
||||
|
||||
file delete $path(test5)
|
||||
test iocmd-11.1 {I/O to command pipelines} {unixOrPc unixExecs} {
|
||||
set f [open $path(test4) w]
|
||||
close $f
|
||||
@@ -3643,8 +3642,6 @@ foreach file [list test1 test2 test3 test4] {
|
||||
}
|
||||
# delay long enough for background processes to finish
|
||||
after 500
|
||||
foreach file [list test5] {
|
||||
removeFile $file
|
||||
}
|
||||
removeFile test5
|
||||
cleanupTests
|
||||
return
|
||||
|
||||
+1
-1
@@ -141,7 +141,7 @@ test link-2.8 {writing C variables from Tcl} -constraints {testlink} -setup {
|
||||
set uwide "0O"
|
||||
concat [testlink get] | $int $real $bool $string $wide $char $uchar $short $ushort $uint $long $ulong $float $uwide
|
||||
} -result {0 0.0 0 0 0 0 0 0 0 0 0 0 0.0 0 | 0x 0b 0 0 0O 0X 0B 0O 0x 0b 0o 0X 0B 0O}
|
||||
test link-2.8 {writing C variables from Tcl} -constraints {testlink} -setup {
|
||||
test link-2.9 {writing C variables from Tcl} -constraints {testlink} -setup {
|
||||
testlink delete
|
||||
} -body {
|
||||
testlink set 43 1.21 4 - 56785678 64 250 30000 60000 0xbaadbeef 12321 32123 3.25 1231231234
|
||||
|
||||
+32
-1
@@ -1576,7 +1576,10 @@ test namespace-42.7 {ensembles: nested} {
|
||||
namespace delete ns
|
||||
set result
|
||||
} {{1 ::ns::x0::z} 1 2 3}
|
||||
test namespace-42.8 {ensembles: [Bug 1670091]} -setup {
|
||||
test namespace-42.8 {
|
||||
ensembles: [Bug 1670091], panic due to pointer to a deallocated List
|
||||
struct.
|
||||
} -setup {
|
||||
proc demo args {}
|
||||
variable target [list [namespace which demo] x]
|
||||
proc trial args {variable target; string length $target}
|
||||
@@ -1591,6 +1594,34 @@ test namespace-42.8 {ensembles: [Bug 1670091]} -setup {
|
||||
rename foo {}
|
||||
} -result {}
|
||||
|
||||
test namespace-42.9 {
|
||||
ensembles: [Bug 4f6a1ebd64], segmentation fault due to pointer to a
|
||||
deallocated List struct.
|
||||
} -setup {
|
||||
namespace eval n {namespace ensemble create}
|
||||
set lst [dict create one ::two]
|
||||
namespace ensemble configure n -subcommands $lst -map $lst
|
||||
} -body {
|
||||
n one
|
||||
} -cleanup {
|
||||
namespace delete n
|
||||
unset -nocomplain lst
|
||||
} -returnCodes error -match glob -result {invalid command name*}
|
||||
|
||||
test namespace-42.10 {
|
||||
ensembles: [Bug 4f6a1ebd64] segmentation fault due to pointer to a
|
||||
deallocated List struct (this time with duplicate of one in "dict").
|
||||
} -setup {
|
||||
namespace eval n {namespace ensemble create}
|
||||
set lst [list one ::two one ::three]
|
||||
namespace ensemble configure n -subcommands $lst -map $lst
|
||||
} -body {
|
||||
n one
|
||||
} -cleanup {
|
||||
namespace delete n
|
||||
unset -nocomplain lst
|
||||
} -returnCodes error -match glob -result {invalid command name *three*}
|
||||
|
||||
test namespace-43.1 {ensembles: dict-driven} {
|
||||
namespace eval ns {
|
||||
namespace export x*
|
||||
|
||||
+4
-4
@@ -172,17 +172,17 @@ test safe-6.3 {test safe interpreters knowledge of the world} {
|
||||
# aren't leaking infos, but they still do...
|
||||
|
||||
# high level general test
|
||||
test safe-7.1 {tests that everything works at high level} {
|
||||
test safe-7.1 {tests that everything works at high level} -body {
|
||||
set i [safe::interpCreate];
|
||||
# no error shall occur:
|
||||
# (because the default access_path shall include 1st level sub dirs
|
||||
# so package require in a slave works like in the master)
|
||||
set v [interp eval $i {package require http 1}]
|
||||
set v [interp eval $i {package require http 2}]
|
||||
# no error shall occur:
|
||||
interp eval $i {http_config};
|
||||
interp eval $i {http::config}
|
||||
safe::interpDelete $i
|
||||
set v
|
||||
} 1.0
|
||||
} -match glob -result 2.*
|
||||
test safe-7.2 {tests specific path and interpFind/AddToAccessPath} -body {
|
||||
set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]];
|
||||
# should not add anything (p0)
|
||||
|
||||
@@ -1111,6 +1111,9 @@ test string-11.54 {string match, failure} {
|
||||
[string match *a*l*\u0000*cba* $longString] \
|
||||
[string match *===* $longString]
|
||||
} {0 1 1 1 0 0}
|
||||
test string-11.55 {string match, invalid binary optimization} {
|
||||
[format string] match \u0141 [binary format c 65]
|
||||
} 0
|
||||
|
||||
test string-12.1 {string range} {
|
||||
list [catch {string range} msg] $msg
|
||||
@@ -1291,6 +1294,12 @@ test string-14.16 {string replace} {
|
||||
test string-14.17 {string replace} {
|
||||
string replace abcdefghijklmnop end end-1
|
||||
} {abcdefghijklmnop}
|
||||
test string-14.18 {string replace} {
|
||||
string replace abcdefghijklmnop 10 9 XXX
|
||||
} {abcdefghijklmnop}
|
||||
test string-14.19 {string replace} {
|
||||
string replace {} -1 0 A
|
||||
} A
|
||||
|
||||
test string-15.1 {string tolower too few args} {
|
||||
list [catch {string tolower} msg] $msg
|
||||
|
||||
+5
-3
@@ -549,8 +549,9 @@ switch -- $::tcl_platform(platform) {
|
||||
file attributes $notWriteableDir -permissions 00555
|
||||
}
|
||||
default {
|
||||
# note in FAT/NTFS we won't be able to protect directory with read-only attribute...
|
||||
catch {file attributes $notWriteableDir -readonly 1}
|
||||
catch {testchmod 000 $notWriteableDir}
|
||||
catch {testchmod 0 $notWriteableDir}
|
||||
}
|
||||
}
|
||||
test tcltest-8.3 {tcltest a.tcl -tmpdir notReadableDir} {
|
||||
@@ -565,9 +566,10 @@ test tcltest-8.3 {tcltest a.tcl -tmpdir notReadableDir} {
|
||||
# This constraint doesn't go at the top of the file so that it doesn't
|
||||
# interfere with tcltest-5.5
|
||||
testConstraint notFAT [expr {
|
||||
![string match "FAT*" [lindex [file system $notWriteableDir] 1]]
|
||||
![regexp {^(FAT\d*|NTFS)$} [lindex [file system $notWriteableDir] 1]]
|
||||
|| $::tcl_platform(platform) eq "unix" || [llength [info commands testchmod]]
|
||||
}]
|
||||
# FAT permissions are fairly hopeless; ignore this test if that FS is used
|
||||
# FAT/NTFS permissions are fairly hopeless; ignore this test if that FS is used
|
||||
test tcltest-8.4 {tcltest a.tcl -tmpdir notWriteableDir} {
|
||||
-constraints {unixOrPc notRoot notFAT}
|
||||
-body {
|
||||
|
||||
+33
-15
@@ -17,6 +17,8 @@ if {[lsearch [namespace children] ::tcltest] == -1} {
|
||||
|
||||
# Initialise the test constraints
|
||||
|
||||
testConstraint winVista 0
|
||||
testConstraint winXP 0
|
||||
testConstraint testvolumetype [llength [info commands testvolumetype]]
|
||||
testConstraint testfile [llength [info commands testfile]]
|
||||
testConstraint testchmod [llength [info commands testchmod]]
|
||||
@@ -50,20 +52,25 @@ proc cleanup {args} {
|
||||
}
|
||||
}
|
||||
|
||||
if {[testConstraint win]} {
|
||||
set major [string index $tcl_platform(osVersion) 0]
|
||||
if {$major > 5} {
|
||||
testConstraint winVista 1
|
||||
} elseif {$major == 5} {
|
||||
testConstraint winXP 1
|
||||
}
|
||||
}
|
||||
|
||||
# find a CD-ROM so we can test read-only filesystems.
|
||||
|
||||
proc findfile {dir} {
|
||||
foreach p [glob -directory $dir *] {
|
||||
if {[file type $p] == "file"} {
|
||||
return $p
|
||||
}
|
||||
foreach p [glob -nocomplain -type f -directory $dir *] {
|
||||
return $p
|
||||
}
|
||||
foreach p [glob -directory $dir *] {
|
||||
if {[file type $p] == "directory"} {
|
||||
set f [findfile $p]
|
||||
if {$f != ""} {
|
||||
return $f
|
||||
}
|
||||
foreach p [glob -nocomplain -type d -directory $dir *] {
|
||||
set f [findfile $p]
|
||||
if {$f ne ""} {
|
||||
return $f
|
||||
}
|
||||
}
|
||||
return ""
|
||||
@@ -71,7 +78,7 @@ proc findfile {dir} {
|
||||
|
||||
if {[testConstraint testvolumetype]} {
|
||||
foreach p {d e f g h i j k l m n o p q r s t u v w x y z} {
|
||||
if {![catch {testvolumetype ${p}:} result] && $result eq "CDFS"} {
|
||||
if {![catch {testvolumetype ${p}:} result] && $result in {CDFS UDF}} {
|
||||
set cdrom ${p}:
|
||||
set cdfile [findfile $cdrom]
|
||||
testConstraint cdrom 1
|
||||
@@ -893,11 +900,22 @@ test winFCmd-12.4 {ConvertFileNameFormat} {win} {
|
||||
test winFCmd-12.5 {ConvertFileNameFormat: absolute path} {win} {
|
||||
list [file attributes / -longname] [file attributes \\ -longname]
|
||||
} {/ /}
|
||||
test winFCmd-12.6 {ConvertFileNameFormat: absolute path with drive} {win} {
|
||||
test winFCmd-12.6 {ConvertFileNameFormat: absolute path with drive} -setup {
|
||||
catch {file delete -force -- c:/td1}
|
||||
close [open c:/td1 w]
|
||||
list [catch {string tolower [file attributes c:/td1 -longname]} msg] $msg [file delete -force -- c:/td1]
|
||||
} {0 c:/td1 {}}
|
||||
} -constraints {win winXP} -body {
|
||||
createfile c:/td1 {}
|
||||
string tolower [file attributes c:/td1 -longname]
|
||||
} -cleanup {
|
||||
file delete -force -- c:/td1
|
||||
} -result {c:/td1}
|
||||
test winFCmd-12.6.2 {ConvertFileNameFormat: absolute path with drive (in temp folder)} -setup {
|
||||
catch {file delete -force -- $::env(TEMP)/td1}
|
||||
} -constraints {win} -body {
|
||||
createfile $::env(TEMP)/td1 {}
|
||||
string tolower [file attributes $::env(TEMP)/td1 -longname]
|
||||
} -cleanup {
|
||||
file delete -force -- $::env(TEMP)/td1
|
||||
} -result [string tolower [file normalize $::env(TEMP)]/td1]
|
||||
test winFCmd-12.7 {ConvertFileNameFormat} {nonPortable win} {
|
||||
string tolower [file attributes //bisque/tcl/ws -longname]
|
||||
} {//bisque/tcl/ws}
|
||||
|
||||
+5
-4
@@ -21,6 +21,7 @@ set bindir [file join [pwd] [file dirname [info nameofexecutable]]]
|
||||
set cat32 [file join $bindir cat32.exe]
|
||||
|
||||
testConstraint exec [llength [info commands exec]]
|
||||
testConstraint testexcept [llength [info commands testexcept]]
|
||||
testConstraint cat32 [file exists $cat32]
|
||||
testConstraint AllocConsole [catch {puts console1 ""}]
|
||||
testConstraint RealConsole [expr {![testConstraint AllocConsole]}]
|
||||
@@ -193,28 +194,28 @@ test winpipe-4.1 {Tcl_WaitPid} {win nt exec cat32} {
|
||||
vwait x
|
||||
list $result $x [contents $path(stderr)]
|
||||
} "{$big} 1 stderr32"
|
||||
test winpipe-4.2 {Tcl_WaitPid: return of exception codes, SIGFPE} {win exec} {
|
||||
test winpipe-4.2 {Tcl_WaitPid: return of exception codes, SIGFPE} {win exec testexcept} {
|
||||
set f [open "|[list [interpreter]]" w+]
|
||||
set pid [pid $f]
|
||||
puts $f "testexcept float_underflow"
|
||||
set status [catch {close $f}]
|
||||
list $status [expr {$pid == [lindex $::errorCode 1]}] [lindex $::errorCode 2]
|
||||
} {1 1 SIGFPE}
|
||||
test winpipe-4.3 {Tcl_WaitPid: return of exception codes, SIGSEGV} {win exec} {
|
||||
test winpipe-4.3 {Tcl_WaitPid: return of exception codes, SIGSEGV} {win exec testexcept} {
|
||||
set f [open "|[list [interpreter]]" w+]
|
||||
set pid [pid $f]
|
||||
puts $f "testexcept access_violation"
|
||||
set status [catch {close $f}]
|
||||
list $status [expr {$pid == [lindex $::errorCode 1]}] [lindex $::errorCode 2]
|
||||
} {1 1 SIGSEGV}
|
||||
test winpipe-4.4 {Tcl_WaitPid: return of exception codes, SIGILL} {win exec} {
|
||||
test winpipe-4.4 {Tcl_WaitPid: return of exception codes, SIGILL} {win exec testexcept} {
|
||||
set f [open "|[list [interpreter]]" w+]
|
||||
set pid [pid $f]
|
||||
puts $f "testexcept illegal_instruction"
|
||||
set status [catch {close $f}]
|
||||
list $status [expr {$pid == [lindex $::errorCode 1]}] [lindex $::errorCode 2]
|
||||
} {1 1 SIGILL}
|
||||
test winpipe-4.5 {Tcl_WaitPid: return of exception codes, SIGINT} {win exec} {
|
||||
test winpipe-4.5 {Tcl_WaitPid: return of exception codes, SIGINT} {win exec testexcept} {
|
||||
set f [open "|[list [interpreter]]" w+]
|
||||
set pid [pid $f]
|
||||
puts $f "testexcept ctrl+c"
|
||||
|
||||
+40
-19
@@ -1576,11 +1576,12 @@ NativeAccess(
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((mode & W_OK)
|
||||
&& (attr & FILE_ATTRIBUTE_READONLY)
|
||||
&& !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
/*
|
||||
* If it's not a directory (assume file), do several fast checks:
|
||||
*/
|
||||
if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
/*
|
||||
* The attributes say the file is not writable. If the file is a
|
||||
* If the attributes say this is not writable at all. The file is a
|
||||
* regular file (i.e., not a directory), then the file is not
|
||||
* writable, full stop. For directories, the read-only bit is
|
||||
* (mostly) ignored by Windows, so we can't ascertain anything about
|
||||
@@ -1588,21 +1589,38 @@ NativeAccess(
|
||||
* advanced 'getFileSecurityProc', then more robust ACL checks
|
||||
* will be done below.
|
||||
*/
|
||||
|
||||
Tcl_SetErrno(EACCES);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mode & X_OK) {
|
||||
if (!(attr & FILE_ATTRIBUTE_DIRECTORY) && !NativeIsExec(nativePath)) {
|
||||
/*
|
||||
* It's not a directory and doesn't have the correct extension.
|
||||
* Therefore it can't be executable
|
||||
*/
|
||||
|
||||
if ((mode & W_OK) && (attr & FILE_ATTRIBUTE_READONLY)) {
|
||||
Tcl_SetErrno(EACCES);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If doesn't have the correct extension, it can't be executable */
|
||||
if ((mode & X_OK) && !NativeIsExec(nativePath)) {
|
||||
Tcl_SetErrno(EACCES);
|
||||
return -1;
|
||||
}
|
||||
/* Special case for read/write/executable check on file */
|
||||
if ((mode & (R_OK|W_OK|X_OK)) && !(mode & ~(R_OK|W_OK|X_OK))) {
|
||||
DWORD mask = 0;
|
||||
HANDLE hFile;
|
||||
if (mode & R_OK) { mask |= GENERIC_READ; }
|
||||
if (mode & W_OK) { mask |= GENERIC_WRITE; }
|
||||
if (mode & X_OK) { mask |= GENERIC_EXECUTE; }
|
||||
|
||||
hFile = (tclWinProcs->createFileProc)(nativePath, mask,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
|
||||
if (hFile != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hFile);
|
||||
return 0;
|
||||
}
|
||||
/* fast exit if access was denied */
|
||||
if (GetLastError() == ERROR_ACCESS_DENIED) {
|
||||
Tcl_SetErrno(EACCES);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* We cannnot verify the access fast, check it below using security info. */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1811,9 +1829,12 @@ NativeIsExec(
|
||||
* Use wide-char case-insensitive comparison
|
||||
*/
|
||||
|
||||
if ((_wcsicmp(path+len-3, L"exe") == 0)
|
||||
|| (_wcsicmp(path+len-3, L"com") == 0)
|
||||
|| (_wcsicmp(path+len-3, L"bat") == 0)) {
|
||||
path += len-3;
|
||||
if ((_wcsicmp(path, L"exe") == 0)
|
||||
|| (_wcsicmp(path, L"com") == 0)
|
||||
|| (_wcsicmp(path, L"cmd") == 0)
|
||||
|| (_wcsicmp(path, L"ps1") == 0)
|
||||
|| (_wcsicmp(path, L"bat") == 0)) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user