#include "tclInt.h"
#include "tclPort.h"
#include "tclCompile.h"
#include "tclRegexp.h"
#define SCAN_NOSKIP 0x1
#define SCAN_SUPPRESS 0x2
#define SCAN_UNSIGNED 0x4
#define SCAN_WIDTH 0x8
#define SCAN_SIGNOK 0x10
#define SCAN_NODIGITS 0x20
#define SCAN_NOZERO 0x40
#define SCAN_XOK 0x80
#define SCAN_PTOK 0x100
#define SCAN_EXPOK 0x200
typedef struct {
int flags;
char *errMsg;
size_t length;
char command[4];
} TraceVarInfo;
static char * TraceVarProc _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, char *name1, char *name2,
int flags));
int
Tcl_PwdObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
Tcl_DString ds;
if (objc != 1) {
Tcl_WrongNumArgs(interp, 1, objv, NULL);
return TCL_ERROR;
}
if (Tcl_GetCwd(interp, &ds) == NULL) {
return TCL_ERROR;
}
Tcl_DStringResult(interp, &ds);
return TCL_OK;
}
int
Tcl_RegexpObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
int i, indices, match, about, offset, all, doinline, numMatchesSaved;
int cflags, eflags, stringLength;
Tcl_RegExp regExpr;
Tcl_Obj *objPtr, *resultPtr;
Tcl_RegExpInfo info;
static char *options[] = {
"-all", "-about", "-indices", "-inline",
"-expanded", "-line", "-linestop", "-lineanchor",
"-nocase", "-start", "--", (char *) NULL
};
enum options {
REGEXP_ALL, REGEXP_ABOUT, REGEXP_INDICES, REGEXP_INLINE,
REGEXP_EXPANDED,REGEXP_LINE, REGEXP_LINESTOP,REGEXP_LINEANCHOR,
REGEXP_NOCASE, REGEXP_START, REGEXP_LAST
};
indices = 0;
about = 0;
cflags = TCL_REG_ADVANCED;
eflags = 0;
offset = 0;
all = 0;
doinline = 0;
for (i = 1; i < objc; i++) {
char *name;
int index;
name = Tcl_GetString(objv[i]);
if (name[0] != '-') {
break;
}
if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,
&index) != TCL_OK) {
return TCL_ERROR;
}
switch ((enum options) index) {
case REGEXP_ALL: {
all = 1;
break;
}
case REGEXP_INDICES: {
indices = 1;
break;
}
case REGEXP_INLINE: {
doinline = 1;
break;
}
case REGEXP_NOCASE: {
cflags |= TCL_REG_NOCASE;
break;
}
case REGEXP_ABOUT: {
about = 1;
break;
}
case REGEXP_EXPANDED: {
cflags |= TCL_REG_EXPANDED;
break;
}
case REGEXP_LINE: {
cflags |= TCL_REG_NEWLINE;
break;
}
case REGEXP_LINESTOP: {
cflags |= TCL_REG_NLSTOP;
break;
}
case REGEXP_LINEANCHOR: {
cflags |= TCL_REG_NLANCH;
break;
}
case REGEXP_START: {
if (++i >= objc) {
goto endOfForLoop;
}
if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {
return TCL_ERROR;
}
if (offset < 0) {
offset = 0;
}
break;
}
case REGEXP_LAST: {
i++;
goto endOfForLoop;
}
}
}
endOfForLoop:
if ((objc - i) < (2 - about)) {
Tcl_WrongNumArgs(interp, 1, objv, "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
return TCL_ERROR;
}
objc -= i;
objv += i;
if (doinline && ((objc - 2) != 0)) {
Tcl_AppendResult(interp, "regexp match variables not allowed",
" when using -inline", (char *) NULL);
return TCL_ERROR;
}
regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
if (regExpr == NULL) {
return TCL_ERROR;
}
objPtr = objv[1];
if (about) {
if (TclRegAbout(interp, regExpr) < 0) {
return TCL_ERROR;
}
return TCL_OK;
}
if (offset > 0) {
eflags |= TCL_REG_NOTBOL;
}
objc -= 2;
objv += 2;
resultPtr = Tcl_GetObjResult(interp);
if (doinline) {
numMatchesSaved = -1;
} else {
numMatchesSaved = (objc == 0) ? all : objc;
}
stringLength = Tcl_GetCharLength(objPtr);
while (1) {
match = Tcl_RegExpExecObj(interp, regExpr, objPtr,
offset , numMatchesSaved, eflags);
if (match < 0) {
return TCL_ERROR;
}
if (match == 0) {
if (all <= 1) {
if (doinline) {
Tcl_SetListObj(resultPtr, 0, NULL);
} else {
Tcl_SetIntObj(resultPtr, 0);
}
return TCL_OK;
}
break;
}
Tcl_RegExpGetInfo(regExpr, &info);
if (doinline) {
objc = info.nsubs + 1;
}
for (i = 0; i < objc; i++) {
Tcl_Obj *newPtr;
if (indices) {
int start, end;
Tcl_Obj *objs[2];
if (i <= info.nsubs) {
start = offset + info.matches[i].start;
end = offset + info.matches[i].end;
if (end >= offset) {
end--;
}
} else {
start = -1;
end = -1;
}
objs[0] = Tcl_NewLongObj(start);
objs[1] = Tcl_NewLongObj(end);
newPtr = Tcl_NewListObj(2, objs);
} else {
if (i <= info.nsubs) {
newPtr = Tcl_GetRange(objPtr,
offset + info.matches[i].start,
offset + info.matches[i].end - 1);
} else {
newPtr = Tcl_NewObj();
}
}
if (doinline) {
if (Tcl_ListObjAppendElement(interp, resultPtr, newPtr)
!= TCL_OK) {
Tcl_DecrRefCount(newPtr);
return TCL_ERROR;
}
} else {
Tcl_Obj *valuePtr;
valuePtr = Tcl_ObjSetVar2(interp, objv[i], NULL, newPtr, 0);
if (valuePtr == NULL) {
Tcl_DecrRefCount(newPtr);
Tcl_AppendResult(interp, "couldn't set variable \"",
Tcl_GetString(objv[i]), "\"", (char *) NULL);
return TCL_ERROR;
}
}
}
if (all == 0) {
break;
}
if (info.matches[0].end == 0) {
offset++;
}
offset += info.matches[0].end;
all++;
if (offset >= stringLength) {
break;
}
}
if (!doinline) {
Tcl_SetIntObj(resultPtr, (all ? all-1 : 1));
}
return TCL_OK;
}
int
Tcl_RegsubObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
int i, result, cflags, all, wlen, numMatches, offset;
Tcl_RegExp regExpr;
Tcl_Obj *resultPtr, *varPtr, *objPtr;
Tcl_UniChar *wstring;
char *subspec;
static char *options[] = {
"-all", "-nocase", "-expanded",
"-line", "-linestop", "-lineanchor", "-start",
"--", NULL
};
enum options {
REGSUB_ALL, REGSUB_NOCASE, REGSUB_EXPANDED,
REGSUB_LINE, REGSUB_LINESTOP, REGSUB_LINEANCHOR, REGSUB_START,
REGSUB_LAST
};
cflags = TCL_REG_ADVANCED;
all = 0;
offset = 0;
for (i = 1; i < objc; i++) {
char *name;
int index;
name = Tcl_GetString(objv[i]);
if (name[0] != '-') {
break;
}
if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,
&index) != TCL_OK) {
return TCL_ERROR;
}
switch ((enum options) index) {
case REGSUB_ALL: {
all = 1;
break;
}
case REGSUB_NOCASE: {
cflags |= TCL_REG_NOCASE;
break;
}
case REGSUB_EXPANDED: {
cflags |= TCL_REG_EXPANDED;
break;
}
case REGSUB_LINE: {
cflags |= TCL_REG_NEWLINE;
break;
}
case REGSUB_LINESTOP: {
cflags |= TCL_REG_NLSTOP;
break;
}
case REGSUB_LINEANCHOR: {
cflags |= TCL_REG_NLANCH;
break;
}
case REGSUB_START: {
if (++i >= objc) {
goto endOfForLoop;
}
if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {
return TCL_ERROR;
}
if (offset < 0) {
offset = 0;
}
break;
}
case REGSUB_LAST: {
i++;
goto endOfForLoop;
}
}
}
endOfForLoop:
if (objc - i != 4) {
Tcl_WrongNumArgs(interp, 1, objv,
"?switches? exp string subSpec varName");
return TCL_ERROR;
}
objv += i;
regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
if (regExpr == NULL) {
return TCL_ERROR;
}
result = TCL_OK;
resultPtr = Tcl_NewObj();
Tcl_IncrRefCount(resultPtr);
objPtr = objv[1];
wlen = Tcl_GetCharLength(objPtr);
wstring = Tcl_GetUnicode(objPtr);
subspec = Tcl_GetString(objv[2]);
varPtr = objv[3];
numMatches = 0;
for ( ; offset < wlen; ) {
int start, end, subStart, subEnd, match;
char *src, *firstChar;
char c;
Tcl_RegExpInfo info;
match = Tcl_RegExpExecObj(interp, regExpr, objPtr, offset,
10 , ((offset > 0) ? TCL_REG_NOTBOL : 0));
if (match < 0) {
result = TCL_ERROR;
goto done;
}
if (match == 0) {
break;
}
if ((numMatches == 0) && (offset > 0)) {
Tcl_AppendUnicodeToObj(resultPtr, wstring, offset);
}
numMatches++;
Tcl_RegExpGetInfo(regExpr, &info);
start = info.matches[0].start;
end = info.matches[0].end;
Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, start);
src = subspec;
firstChar = subspec;
for (c = *src; c != '\0'; src++, c = *src) {
int index;
if (c == '&') {
index = 0;
} else if (c == '\\') {
c = src[1];
if ((c >= '0') && (c <= '9')) {
index = c - '0';
} else if ((c == '\\') || (c == '&')) {
Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);
Tcl_AppendToObj(resultPtr, &c, 1);
firstChar = src + 2;
src++;
continue;
} else {
continue;
}
} else {
continue;
}
if (firstChar != src) {
Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);
}
if (index <= info.nsubs) {
subStart = info.matches[index].start;
subEnd = info.matches[index].end;
if ((subStart >= 0) && (subEnd >= 0)) {
Tcl_AppendUnicodeToObj(resultPtr,
wstring + offset + subStart, subEnd - subStart);
}
}
if (*src == '\\') {
src++;
}
firstChar = src + 1;
}
if (firstChar != src) {
Tcl_AppendToObj(resultPtr, firstChar, src - firstChar);
}
if (end == 0) {
Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
offset++;
}
offset += end;
if (!all) {
break;
}
}
if (numMatches == 0) {
Tcl_AppendUnicodeToObj(resultPtr, wstring, wlen);
} else if (offset < wlen) {
Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset);
}
if (Tcl_ObjSetVar2(interp, varPtr, NULL, resultPtr, 0) == NULL) {
Tcl_AppendResult(interp, "couldn't set variable \"",
Tcl_GetString(varPtr), "\"", (char *) NULL);
result = TCL_ERROR;
} else {
Tcl_SetIntObj(Tcl_GetObjResult(interp), numMatches);
}
done:
Tcl_DecrRefCount(resultPtr);
return result;
}
int
Tcl_RenameObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
char *oldName, *newName;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "oldName newName");
return TCL_ERROR;
}
oldName = Tcl_GetString(objv[1]);
newName = Tcl_GetString(objv[2]);
return TclRenameCommand(interp, oldName, newName);
}
int
Tcl_ReturnObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
Interp *iPtr = (Interp *) interp;
int optionLen, argLen, code, result;
if (iPtr->errorInfo != NULL) {
ckfree(iPtr->errorInfo);
iPtr->errorInfo = NULL;
}
if (iPtr->errorCode != NULL) {
ckfree(iPtr->errorCode);
iPtr->errorCode = NULL;
}
code = TCL_OK;
for (objv++, objc--; objc > 1; objv += 2, objc -= 2) {
char *option = Tcl_GetStringFromObj(objv[0], &optionLen);
char *arg = Tcl_GetStringFromObj(objv[1], &argLen);
if (strcmp(option, "-code") == 0) {
register int c = arg[0];
if ((c == 'o') && (strcmp(arg, "ok") == 0)) {
code = TCL_OK;
} else if ((c == 'e') && (strcmp(arg, "error") == 0)) {
code = TCL_ERROR;
} else if ((c == 'r') && (strcmp(arg, "return") == 0)) {
code = TCL_RETURN;
} else if ((c == 'b') && (strcmp(arg, "break") == 0)) {
code = TCL_BREAK;
} else if ((c == 'c') && (strcmp(arg, "continue") == 0)) {
code = TCL_CONTINUE;
} else {
result = Tcl_GetIntFromObj((Tcl_Interp *) NULL, objv[1],
&code);
if (result != TCL_OK) {
Tcl_ResetResult(interp);
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"bad completion code \"",
Tcl_GetString(objv[1]),
"\": must be ok, error, return, break, ",
"continue, or an integer", (char *) NULL);
return result;
}
}
} else if (strcmp(option, "-errorinfo") == 0) {
iPtr->errorInfo =
(char *) ckalloc((unsigned) (strlen(arg) + 1));
strcpy(iPtr->errorInfo, arg);
} else if (strcmp(option, "-errorcode") == 0) {
iPtr->errorCode =
(char *) ckalloc((unsigned) (strlen(arg) + 1));
strcpy(iPtr->errorCode, arg);
} else {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"bad option \"", option,
"\": must be -code, -errorcode, or -errorinfo",
(char *) NULL);
return TCL_ERROR;
}
}
if (objc == 1) {
Tcl_SetObjResult(interp, objv[0]);
}
iPtr->returnCode = code;
return TCL_RETURN;
}
int
Tcl_SourceObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
char *bytes;
int result;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "fileName");
return TCL_ERROR;
}
bytes = Tcl_GetString(objv[1]);
result = Tcl_EvalFile(interp, bytes);
return result;
}
int
Tcl_SplitObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
Tcl_UniChar ch;
int len;
char *splitChars, *string, *end;
int splitCharLen, stringLen;
Tcl_Obj *listPtr, *objPtr;
if (objc == 2) {
splitChars = " \n\t\r";
splitCharLen = 4;
} else if (objc == 3) {
splitChars = Tcl_GetStringFromObj(objv[2], &splitCharLen);
} else {
Tcl_WrongNumArgs(interp, 1, objv, "string ?splitChars?");
return TCL_ERROR;
}
string = Tcl_GetStringFromObj(objv[1], &stringLen);
end = string + stringLen;
listPtr = Tcl_GetObjResult(interp);
if (stringLen == 0) {
} else if (splitCharLen == 0) {
for ( ; string < end; string += len) {
len = Tcl_UtfToUniChar(string, &ch);
objPtr = Tcl_NewStringObj(string, len);
Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
}
} else {
char *element, *p, *splitEnd;
int splitLen;
Tcl_UniChar splitChar;
splitEnd = splitChars + splitCharLen;
for (element = string; string < end; string += len) {
len = Tcl_UtfToUniChar(string, &ch);
for (p = splitChars; p < splitEnd; p += splitLen) {
splitLen = Tcl_UtfToUniChar(p, &splitChar);
if (ch == splitChar) {
objPtr = Tcl_NewStringObj(element, string - element);
Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
element = string + len;
break;
}
}
}
objPtr = Tcl_NewStringObj(element, string - element);
Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
}
return TCL_OK;
}
int
Tcl_StringObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
int index, left, right;
Tcl_Obj *resultPtr;
char *string1, *string2;
int length1, length2;
static char *options[] = {
"bytelength", "compare", "equal", "first",
"index", "is", "last", "length",
"map", "match", "range", "repeat",
"replace", "tolower", "toupper", "totitle",
"trim", "trimleft", "trimright",
"wordend", "wordstart", (char *) NULL
};
enum options {
STR_BYTELENGTH, STR_COMPARE, STR_EQUAL, STR_FIRST,
STR_INDEX, STR_IS, STR_LAST, STR_LENGTH,
STR_MAP, STR_MATCH, STR_RANGE, STR_REPEAT,
STR_REPLACE, STR_TOLOWER, STR_TOUPPER, STR_TOTITLE,
STR_TRIM, STR_TRIMLEFT, STR_TRIMRIGHT,
STR_WORDEND, STR_WORDSTART
};
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0,
&index) != TCL_OK) {
return TCL_ERROR;
}
resultPtr = Tcl_GetObjResult(interp);
switch ((enum options) index) {
case STR_EQUAL:
case STR_COMPARE: {
int i, match, length, nocase = 0, reqlength = -1;
if (objc < 4 || objc > 7) {
str_cmp_args:
Tcl_WrongNumArgs(interp, 2, objv,
"?-nocase? ?-length int? string1 string2");
return TCL_ERROR;
}
for (i = 2; i < objc-2; i++) {
string2 = Tcl_GetStringFromObj(objv[i], &length2);
if ((length2 > 1)
&& strncmp(string2, "-nocase", (size_t) length2) == 0) {
nocase = 1;
} else if ((length2 > 1)
&& strncmp(string2, "-length", (size_t) length2) == 0) {
if (i+1 >= objc-2) {
goto str_cmp_args;
}
if (Tcl_GetIntFromObj(interp, objv[++i],
&reqlength) != TCL_OK) {
return TCL_ERROR;
}
} else {
Tcl_AppendStringsToObj(resultPtr, "bad option \"",
string2, "\": must be -nocase or -length",
(char *) NULL);
return TCL_ERROR;
}
}
string1 = Tcl_GetStringFromObj(objv[objc-2], &length1);
string2 = Tcl_GetStringFromObj(objv[objc-1], &length2);
length = (length1 < length2) ? length1 : length2;
if (reqlength == 0) {
match = 0;
} else if (nocase || ((reqlength > 0) && (reqlength <= length))) {
length1 = Tcl_NumUtfChars(string1, length1);
length2 = Tcl_NumUtfChars(string2, length2);
length = (length1 < length2) ? length1 : length2;
if ((reqlength > 0) && (reqlength < length)) {
length = reqlength;
} else if (reqlength < 0) {
reqlength = (length1 > length2) ? length1 : length2;
}
if (nocase) {
match = Tcl_UtfNcasecmp(string1, string2,
(unsigned) length);
} else {
match = Tcl_UtfNcmp(string1, string2, (unsigned) length);
}
if ((match == 0) && (reqlength > length)) {
match = length1 - length2;
}
} else {
match = memcmp(string1, string2, (unsigned) length);
if (match == 0) {
match = length1 - length2;
}
}
if ((enum options) index == STR_EQUAL) {
Tcl_SetBooleanObj(resultPtr, (match) ? 0 : 1);
} else {
Tcl_SetIntObj(resultPtr, ((match > 0) ? 1 :
(match < 0) ? -1 : 0));
}
break;
}
case STR_FIRST: {
register char *p, *end;
int match, utflen, start;
if (objc < 4 || objc > 5) {
Tcl_WrongNumArgs(interp, 2, objv,
"string1 string2 ?startIndex?");
return TCL_ERROR;
}
match = -1;
start = 0;
utflen = -1;
string1 = Tcl_GetStringFromObj(objv[2], &length1);
string2 = Tcl_GetStringFromObj(objv[3], &length2);
if (objc == 5) {
utflen = Tcl_NumUtfChars(string2, length2);
if (TclGetIntForIndex(interp, objv[4], utflen-1,
&start) != TCL_OK) {
return TCL_ERROR;
}
if (start >= utflen) {
goto str_first_done;
} else if (start > 0) {
if (length2 == utflen) {
string2 += start;
length2 -= start;
} else {
char *s = Tcl_UtfAtIndex(string2, start);
length2 -= s - string2;
string2 = s;
}
}
}
if (length1 > 0) {
end = string2 + length2 - length1 + 1;
for (p = string2; p < end; p++) {
p = memchr(p, *string1, (unsigned) (end - p));
if (p == NULL) {
break;
}
if (memcmp(string1, p, (unsigned) length1) == 0) {
match = p - string2;
break;
}
}
}
str_first_done:
if (match != -1) {
if (objc == 4) {
match = Tcl_NumUtfChars(string2, match);
} else if (length2 == utflen) {
match += start;
} else {
match = start + Tcl_NumUtfChars(string2, match);
}
}
Tcl_SetIntObj(resultPtr, match);
break;
}
case STR_INDEX: {
char buf[TCL_UTF_MAX];
Tcl_UniChar unichar;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");
return TCL_ERROR;
}
if (objv[2]->typePtr == &tclByteArrayType) {
string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);
if (TclGetIntForIndex(interp, objv[3], length1 - 1,
&index) != TCL_OK) {
return TCL_ERROR;
}
Tcl_SetByteArrayObj(resultPtr,
(unsigned char *)(&string1[index]), 1);
} else {
string1 = Tcl_GetStringFromObj(objv[2], &length1);
length2 = Tcl_GetCharLength(objv[2]);
if (TclGetIntForIndex(interp, objv[3], length2 - 1,
&index) != TCL_OK) {
return TCL_ERROR;
}
if ((index >= 0) && (index < length2)) {
unichar = Tcl_GetUniChar(objv[2], index);
length2 = Tcl_UniCharToUtf((int)unichar, buf);
Tcl_SetStringObj(resultPtr, buf, length2);
}
}
break;
}
case STR_IS: {
char *end;
Tcl_UniChar ch;
int (*chcomp)_ANSI_ARGS_((int)) = NULL;
int i, failat = 0, result = 1, strict = 0;
Tcl_Obj *objPtr, *failVarObj = NULL;
static char *isOptions[] = {
"alnum", "alpha", "ascii", "control",
"boolean", "digit", "double", "false",
"graph", "integer", "lower", "print",
"punct", "space", "true", "upper",
"wordchar", "xdigit", (char *) NULL
};
enum isOptions {
STR_IS_ALNUM, STR_IS_ALPHA, STR_IS_ASCII, STR_IS_CONTROL,
STR_IS_BOOL, STR_IS_DIGIT, STR_IS_DOUBLE, STR_IS_FALSE,
STR_IS_GRAPH, STR_IS_INT, STR_IS_LOWER, STR_IS_PRINT,
STR_IS_PUNCT, STR_IS_SPACE, STR_IS_TRUE, STR_IS_UPPER,
STR_IS_WORD, STR_IS_XDIGIT
};
if (objc < 4 || objc > 7) {
Tcl_WrongNumArgs(interp, 2, objv,
"class ?-strict? ?-failindex var? str");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[2], isOptions, "class", 0,
&index) != TCL_OK) {
return TCL_ERROR;
}
if (objc != 4) {
for (i = 3; i < objc-1; i++) {
string2 = Tcl_GetStringFromObj(objv[i], &length2);
if ((length2 > 1) &&
strncmp(string2, "-strict", (size_t) length2) == 0) {
strict = 1;
} else if ((length2 > 1) &&
strncmp(string2, "-failindex", (size_t) length2) == 0) {
if (i+1 >= objc-1) {
Tcl_WrongNumArgs(interp, 3, objv,
"?-strict? ?-failindex var? str");
return TCL_ERROR;
}
failVarObj = objv[++i];
} else {
Tcl_AppendStringsToObj(resultPtr, "bad option \"",
string2, "\": must be -strict or -failindex",
(char *) NULL);
return TCL_ERROR;
}
}
}
objPtr = objv[objc-1];
string1 = Tcl_GetStringFromObj(objPtr, &length1);
if (length1 == 0) {
if (strict) {
result = 0;
}
goto str_is_done;
}
end = string1 + length1;
switch ((enum isOptions) index) {
case STR_IS_ALNUM:
chcomp = Tcl_UniCharIsAlnum;
break;
case STR_IS_ALPHA:
chcomp = Tcl_UniCharIsAlpha;
break;
case STR_IS_ASCII:
for (; string1 < end; string1++, failat++) {
if (*((unsigned char *)string1) >= 0x80) {
result = 0;
break;
}
}
break;
case STR_IS_BOOL:
case STR_IS_TRUE:
case STR_IS_FALSE:
if (objPtr->typePtr == &tclBooleanType) {
if ((((enum isOptions) index == STR_IS_TRUE) &&
objPtr->internalRep.longValue == 0) ||
(((enum isOptions) index == STR_IS_FALSE) &&
objPtr->internalRep.longValue != 0)) {
result = 0;
}
} else if ((Tcl_GetBoolean(NULL, string1, &i)
== TCL_ERROR) ||
(((enum isOptions) index == STR_IS_TRUE) &&
i == 0) ||
(((enum isOptions) index == STR_IS_FALSE) &&
i != 0)) {
result = 0;
}
break;
case STR_IS_CONTROL:
chcomp = Tcl_UniCharIsControl;
break;
case STR_IS_DIGIT:
chcomp = Tcl_UniCharIsDigit;
break;
case STR_IS_DOUBLE: {
char *stop;
if ((objPtr->typePtr == &tclDoubleType) ||
(objPtr->typePtr == &tclIntType)) {
break;
}
if (TclLooksLikeInt(string1, length1)) {
errno = 0;
strtoul(string1, &stop, 0);
if (stop == end) {
if (errno == ERANGE) {
result = 0;
failat = -1;
}
break;
}
}
errno = 0;
strtod(string1, &stop);
if (errno == ERANGE) {
result = 0;
failat = -1;
} else if (stop == string1) {
result = 0;
failat = 0;
} else {
failat = stop - string1;
string1 = stop;
chcomp = Tcl_UniCharIsSpace;
}
break;
}
case STR_IS_GRAPH:
chcomp = Tcl_UniCharIsGraph;
break;
case STR_IS_INT: {
char *stop;
if ((objPtr->typePtr == &tclIntType) ||
(Tcl_GetInt(NULL, string1, &i) == TCL_OK)) {
break;
}
result = 0;
errno = 0;
strtoul(string1, &stop, 0);
if (errno == ERANGE) {
failat = -1;
} else if (stop == string1) {
failat = 0;
} else {
failat = stop - string1;
string1 = stop;
chcomp = Tcl_UniCharIsSpace;
}
break;
}
case STR_IS_LOWER:
chcomp = Tcl_UniCharIsLower;
break;
case STR_IS_PRINT:
chcomp = Tcl_UniCharIsPrint;
break;
case STR_IS_PUNCT:
chcomp = Tcl_UniCharIsPunct;
break;
case STR_IS_SPACE:
chcomp = Tcl_UniCharIsSpace;
break;
case STR_IS_UPPER:
chcomp = Tcl_UniCharIsUpper;
break;
case STR_IS_WORD:
chcomp = Tcl_UniCharIsWordChar;
break;
case STR_IS_XDIGIT: {
for (; string1 < end; string1++, failat++) {
if ((*((unsigned char *)string1) >= 0xC0) ||
!isxdigit(*(unsigned char *)string1)) {
result = 0;
break;
}
}
break;
}
}
if (chcomp != NULL) {
for (; string1 < end; string1 += length2, failat++) {
length2 = Tcl_UtfToUniChar(string1, &ch);
if (!chcomp(ch)) {
result = 0;
break;
}
}
}
str_is_done:
if ((result == 0) && (failVarObj != NULL) &&
Tcl_ObjSetVar2(interp, failVarObj, NULL, Tcl_NewIntObj(failat),
TCL_LEAVE_ERR_MSG) == NULL) {
return TCL_ERROR;
}
Tcl_SetBooleanObj(resultPtr, result);
break;
}
case STR_LAST: {
register char *p;
int match, utflen, start;
if (objc < 4 || objc > 5) {
Tcl_WrongNumArgs(interp, 2, objv,
"string1 string2 ?startIndex?");
return TCL_ERROR;
}
match = -1;
start = 0;
utflen = -1;
string1 = Tcl_GetStringFromObj(objv[2], &length1);
string2 = Tcl_GetStringFromObj(objv[3], &length2);
if (objc == 5) {
utflen = Tcl_NumUtfChars(string2, length2);
if (TclGetIntForIndex(interp, objv[4], utflen-1,
&start) != TCL_OK) {
return TCL_ERROR;
}
if (start < 0) {
goto str_last_done;
} else if (start < utflen) {
if (length2 == utflen) {
p = string2 + start + 1 - length1;
} else {
p = Tcl_UtfAtIndex(string2, start+1) - length1;
}
} else {
p = string2 + length2 - length1;
}
} else {
p = string2 + length2 - length1;
}
if (length1 > 0) {
for (; p >= string2; p--) {
while ((p != string2) && (*p != *string1)) {
p--;
}
if (memcmp(string1, p, (unsigned) length1) == 0) {
match = p - string2;
break;
}
}
}
str_last_done:
if (match != -1) {
if ((objc == 4) || (length2 != utflen)) {
match = Tcl_NumUtfChars(string2, match);
}
}
Tcl_SetIntObj(resultPtr, match);
break;
}
case STR_BYTELENGTH:
case STR_LENGTH: {
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "string");
return TCL_ERROR;
}
if ((enum options) index == STR_BYTELENGTH) {
(void) Tcl_GetStringFromObj(objv[2], &length1);
Tcl_SetIntObj(resultPtr, length1);
} else {
if (objv[2]->typePtr == &tclByteArrayType) {
(void) Tcl_GetByteArrayFromObj(objv[2], &length1);
Tcl_SetIntObj(resultPtr, length1);
} else {
Tcl_SetIntObj(resultPtr,
Tcl_GetCharLength(objv[2]));
}
}
break;
}
case STR_MAP: {
int uselen, mapElemc, len, nocase = 0;
Tcl_Obj **mapElemv;
char *end;
Tcl_UniChar ch;
int (*str_comp_fn)();
if (objc < 4 || objc > 5) {
Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? charMap string");
return TCL_ERROR;
}
if (objc == 5) {
string2 = Tcl_GetStringFromObj(objv[2], &length2);
if ((length2 > 1) &&
strncmp(string2, "-nocase", (size_t) length2) == 0) {
nocase = 1;
} else {
Tcl_AppendStringsToObj(resultPtr, "bad option \"",
string2, "\": must be -nocase",
(char *) NULL);
return TCL_ERROR;
}
}
if (Tcl_ListObjGetElements(interp, objv[objc-2], &mapElemc,
&mapElemv) != TCL_OK) {
return TCL_ERROR;
}
if (mapElemc == 0) {
Tcl_SetObjResult(interp, objv[objc-1]);
} else if (mapElemc & 1) {
Tcl_SetStringObj(resultPtr, "char map list unbalanced", -1);
return TCL_ERROR;
}
string1 = Tcl_GetStringFromObj(objv[objc-1], &length1);
if (length1 == 0) {
break;
}
end = string1 + length1;
if (nocase) {
length1 = Tcl_NumUtfChars(string1, length1);
str_comp_fn = Tcl_UtfNcasecmp;
} else {
str_comp_fn = memcmp;
}
for ( ; string1 < end; string1 += len) {
len = Tcl_UtfToUniChar(string1, &ch);
for (index = 0; index < mapElemc; index +=2) {
string2 = Tcl_GetStringFromObj(mapElemv[index],
&length2);
if (nocase) {
uselen = Tcl_NumUtfChars(string2, length2);
} else {
uselen = length2;
}
if ((uselen > 0) && (uselen <= length1) &&
(str_comp_fn(string2, string1, uselen) == 0)) {
len = length2;
string2 = Tcl_GetStringFromObj(mapElemv[index+1],
&length2);
Tcl_AppendToObj(resultPtr, string2, length2);
break;
}
}
if (index == mapElemc) {
Tcl_AppendToObj(resultPtr, string1, len);
}
if (nocase) {
length1--;
} else {
length1 -= len;
}
}
break;
}
case STR_MATCH: {
int nocase = 0;
if (objc < 4 || objc > 5) {
Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? pattern string");
return TCL_ERROR;
}
if (objc == 5) {
string2 = Tcl_GetStringFromObj(objv[2], &length2);
if ((length2 > 1) &&
strncmp(string2, "-nocase", (size_t) length2) == 0) {
nocase = 1;
} else {
Tcl_AppendStringsToObj(resultPtr, "bad option \"",
string2, "\": must be -nocase",
(char *) NULL);
return TCL_ERROR;
}
}
Tcl_SetBooleanObj(resultPtr,
Tcl_StringCaseMatch(Tcl_GetString(objv[objc-1]),
Tcl_GetString(objv[objc-2]),
nocase));
break;
}
case STR_RANGE: {
int first, last;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "string first last");
return TCL_ERROR;
}
if (objv[2]->typePtr == &tclByteArrayType) {
string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);
if (TclGetIntForIndex(interp, objv[3], length1 - 1,
&first) != TCL_OK) {
return TCL_ERROR;
}
if (TclGetIntForIndex(interp, objv[4], length1 - 1,
&last) != TCL_OK) {
return TCL_ERROR;
}
if (first < 0) {
first = 0;
}
if (last >= length1 - 1) {
last = length1 - 1;
}
if (last >= first) {
int numBytes = last - first + 1;
resultPtr = Tcl_NewByteArrayObj(
(unsigned char *) &string1[first], numBytes);
Tcl_SetObjResult(interp, resultPtr);
}
} else {
string1 = Tcl_GetStringFromObj(objv[2], &length1);
length2 = Tcl_GetCharLength(objv[2]) - 1;
if (TclGetIntForIndex(interp, objv[3], length2,
&first) != TCL_OK) {
return TCL_ERROR;
}
if (TclGetIntForIndex(interp, objv[4], length2,
&last) != TCL_OK) {
return TCL_ERROR;
}
if (first < 0) {
first = 0;
}
if (last >= length2) {
last = length2;
}
if (last >= first) {
resultPtr = Tcl_GetRange(objv[2], first, last);
Tcl_SetObjResult(interp, resultPtr);
}
}
break;
}
case STR_REPEAT: {
int count;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "string count");
return TCL_ERROR;
}
if (Tcl_GetIntFromObj(interp, objv[3], &count) != TCL_OK) {
return TCL_ERROR;
}
string1 = Tcl_GetStringFromObj(objv[2], &length1);
if (length1 > 0) {
for (index = 0; index < count; index++) {
Tcl_AppendToObj(resultPtr, string1, length1);
}
}
break;
}
case STR_REPLACE: {
int first, last;
if (objc < 5 || objc > 6) {
Tcl_WrongNumArgs(interp, 2, objv,
"string first last ?string?");
return TCL_ERROR;
}
string1 = Tcl_GetStringFromObj(objv[2], &length1);
length1 = Tcl_NumUtfChars(string1, length1) - 1;
if (TclGetIntForIndex(interp, objv[3], length1,
&first) != TCL_OK) {
return TCL_ERROR;
}
if (TclGetIntForIndex(interp, objv[4], length1,
&last) != TCL_OK) {
return TCL_ERROR;
}
if ((last < first) || (first > length1) || (last < 0)) {
Tcl_SetObjResult(interp, objv[2]);
} else {
char *start, *end;
if (first < 0) {
first = 0;
}
start = Tcl_UtfAtIndex(string1, first);
end = Tcl_UtfAtIndex(start, ((last > length1) ? length1 : last)
- first + 1);
Tcl_SetStringObj(resultPtr, string1, start - string1);
if (objc == 6) {
Tcl_AppendObjToObj(resultPtr, objv[5]);
}
if (last < length1) {
Tcl_AppendToObj(resultPtr, end, -1);
}
}
break;
}
case STR_TOLOWER:
case STR_TOUPPER:
case STR_TOTITLE:
if (objc < 3 || objc > 5) {
Tcl_WrongNumArgs(interp, 2, objv, "string ?first? ?last?");
return TCL_ERROR;
}
string1 = Tcl_GetStringFromObj(objv[2], &length1);
if (objc == 3) {
Tcl_SetStringObj(resultPtr, string1, length1);
if ((enum options) index == STR_TOLOWER) {
length1 = Tcl_UtfToLower(Tcl_GetString(resultPtr));
} else if ((enum options) index == STR_TOUPPER) {
length1 = Tcl_UtfToUpper(Tcl_GetString(resultPtr));
} else {
length1 = Tcl_UtfToTitle(Tcl_GetString(resultPtr));
}
Tcl_SetObjLength(resultPtr, length1);
} else {
int first, last;
char *start, *end;
length1 = Tcl_NumUtfChars(string1, length1) - 1;
if (TclGetIntForIndex(interp, objv[3], length1,
&first) != TCL_OK) {
return TCL_ERROR;
}
if (first < 0) {
first = 0;
}
last = first;
if ((objc == 5) && (TclGetIntForIndex(interp, objv[4], length1,
&last) != TCL_OK)) {
return TCL_ERROR;
}
if (last >= length1) {
last = length1;
}
if (last < first) {
Tcl_SetObjResult(interp, objv[2]);
break;
}
start = Tcl_UtfAtIndex(string1, first);
end = Tcl_UtfAtIndex(start, last - first + 1);
length2 = end-start;
string2 = ckalloc((size_t) length2+1);
memcpy(string2, start, (size_t) length2);
string2[length2] = '\0';
if ((enum options) index == STR_TOLOWER) {
length2 = Tcl_UtfToLower(string2);
} else if ((enum options) index == STR_TOUPPER) {
length2 = Tcl_UtfToUpper(string2);
} else {
length2 = Tcl_UtfToTitle(string2);
}
Tcl_SetStringObj(resultPtr, string1, start - string1);
Tcl_AppendToObj(resultPtr, string2, length2);
Tcl_AppendToObj(resultPtr, end, -1);
ckfree(string2);
}
break;
case STR_TRIM: {
Tcl_UniChar ch, trim;
register char *p, *end;
char *check, *checkEnd;
int offset;
left = 1;
right = 1;
dotrim:
if (objc == 4) {
string2 = Tcl_GetStringFromObj(objv[3], &length2);
} else if (objc == 3) {
string2 = " \t\n\r";
length2 = strlen(string2);
} else {
Tcl_WrongNumArgs(interp, 2, objv, "string ?chars?");
return TCL_ERROR;
}
string1 = Tcl_GetStringFromObj(objv[2], &length1);
checkEnd = string2 + length2;
if (left) {
end = string1 + length1;
for (p = string1; p < end; p += offset) {
offset = Tcl_UtfToUniChar(p, &ch);
for (check = string2; ; ) {
if (check >= checkEnd) {
p = end;
break;
}
check += Tcl_UtfToUniChar(check, &trim);
if (ch == trim) {
length1 -= offset;
string1 += offset;
break;
}
}
}
}
if (right) {
end = string1;
for (p = string1 + length1; p > end; ) {
p = Tcl_UtfPrev(p, string1);
offset = Tcl_UtfToUniChar(p, &ch);
for (check = string2; ; ) {
if (check >= checkEnd) {
p = end;
break;
}
check += Tcl_UtfToUniChar(check, &trim);
if (ch == trim) {
length1 -= offset;
break;
}
}
}
}
Tcl_SetStringObj(resultPtr, string1, length1);
break;
}
case STR_TRIMLEFT: {
left = 1;
right = 0;
goto dotrim;
}
case STR_TRIMRIGHT: {
left = 0;
right = 1;
goto dotrim;
}
case STR_WORDEND: {
int cur;
Tcl_UniChar ch;
char *p, *end;
int numChars;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "string index");
return TCL_ERROR;
}
string1 = Tcl_GetStringFromObj(objv[2], &length1);
numChars = Tcl_NumUtfChars(string1, length1);
if (TclGetIntForIndex(interp, objv[3], numChars-1,
&index) != TCL_OK) {
return TCL_ERROR;
}
if (index < 0) {
index = 0;
}
if (index < numChars) {
p = Tcl_UtfAtIndex(string1, index);
end = string1+length1;
for (cur = index; p < end; cur++) {
p += Tcl_UtfToUniChar(p, &ch);
if (!Tcl_UniCharIsWordChar(ch)) {
break;
}
}
if (cur == index) {
cur++;
}
} else {
cur = numChars;
}
Tcl_SetIntObj(resultPtr, cur);
break;
}
case STR_WORDSTART: {
int cur;
Tcl_UniChar ch;
char *p;
int numChars;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "string index");
return TCL_ERROR;
}
string1 = Tcl_GetStringFromObj(objv[2], &length1);
numChars = Tcl_NumUtfChars(string1, length1);
if (TclGetIntForIndex(interp, objv[3], numChars-1,
&index) != TCL_OK) {
return TCL_ERROR;
}
if (index >= numChars) {
index = numChars - 1;
}
cur = 0;
if (index > 0) {
p = Tcl_UtfAtIndex(string1, index);
for (cur = index; cur >= 0; cur--) {
Tcl_UtfToUniChar(p, &ch);
if (!Tcl_UniCharIsWordChar(ch)) {
break;
}
p = Tcl_UtfPrev(p, string1);
}
if (cur != index) {
cur += 1;
}
}
Tcl_SetIntObj(resultPtr, cur);
break;
}
}
return TCL_OK;
}
int
Tcl_SubstObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
static char *substOptions[] = {
"-nobackslashes", "-nocommands", "-novariables", (char *) NULL
};
enum substOptions {
SUBST_NOBACKSLASHES, SUBST_NOCOMMANDS, SUBST_NOVARS
};
Interp *iPtr = (Interp *) interp;
Tcl_DString result;
char *p, *old, *value;
int optionIndex, code, count, doVars, doCmds, doBackslashes, i;
doVars = doCmds = doBackslashes = 1;
for (i = 1; i < (objc-1); i++) {
p = Tcl_GetString(objv[i]);
if (*p != '-') {
break;
}
if (Tcl_GetIndexFromObj(interp, objv[i], substOptions,
"switch", 0, &optionIndex) != TCL_OK) {
return TCL_ERROR;
}
switch (optionIndex) {
case SUBST_NOBACKSLASHES: {
doBackslashes = 0;
break;
}
case SUBST_NOCOMMANDS: {
doCmds = 0;
break;
}
case SUBST_NOVARS: {
doVars = 0;
break;
}
default: {
panic("Tcl_SubstObjCmd: bad option index to SubstOptions");
}
}
}
if (i != (objc-1)) {
Tcl_WrongNumArgs(interp, 1, objv,
"?-nobackslashes? ?-nocommands? ?-novariables? string");
return TCL_ERROR;
}
Tcl_DStringInit(&result);
old = p = Tcl_GetString(objv[i]);
while (*p != 0) {
switch (*p) {
case '\\':
if (doBackslashes) {
char buf[TCL_UTF_MAX];
if (p != old) {
Tcl_DStringAppend(&result, old, p-old);
}
Tcl_DStringAppend(&result, buf,
Tcl_UtfBackslash(p, &count, buf));
p += count;
old = p;
} else {
p++;
}
break;
case '$':
if (doVars) {
if (p != old) {
Tcl_DStringAppend(&result, old, p-old);
}
value = Tcl_ParseVar(interp, p, &p);
if (value == NULL) {
Tcl_DStringFree(&result);
return TCL_ERROR;
}
Tcl_DStringAppend(&result, value, -1);
old = p;
} else {
p++;
}
break;
case '[':
if (doCmds) {
if (p != old) {
Tcl_DStringAppend(&result, old, p-old);
}
iPtr->evalFlags = TCL_BRACKET_TERM;
code = Tcl_Eval(interp, p+1);
if (code == TCL_ERROR) {
Tcl_DStringFree(&result);
return code;
}
old = p = (p+1 + iPtr->termOffset+1);
Tcl_DStringAppend(&result, iPtr->result, -1);
Tcl_ResetResult(interp);
} else {
p++;
}
break;
default:
p++;
break;
}
}
if (p != old) {
Tcl_DStringAppend(&result, old, p-old);
}
Tcl_DStringResult(interp, &result);
return TCL_OK;
}
int
Tcl_SwitchObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
int i, j, index, mode, matched, result, splitObjs, seenComment;
char *string, *pattern;
Tcl_Obj *stringObj;
static char *options[] = {
"-exact", "-glob", "-regexp", "--",
NULL
};
enum options {
OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_LAST
};
mode = OPT_EXACT;
for (i = 1; i < objc; i++) {
string = Tcl_GetString(objv[i]);
if (string[0] != '-') {
break;
}
if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0,
&index) != TCL_OK) {
return TCL_ERROR;
}
if (index == OPT_LAST) {
i++;
break;
}
mode = index;
}
if (objc - i < 2) {
Tcl_WrongNumArgs(interp, 1, objv,
"?switches? string pattern body ... ?default body?");
return TCL_ERROR;
}
stringObj = objv[i];
objc -= i + 1;
objv += i + 1;
splitObjs = 0;
if (objc == 1) {
Tcl_Obj **listv;
if (Tcl_ListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK) {
return TCL_ERROR;
}
objv = listv;
splitObjs = 1;
}
seenComment = 0;
for (i = 0; i < objc; i += 2) {
if (i == objc - 1) {
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp),
"extra switch pattern with no body", -1);
if (splitObjs && seenComment) {
Tcl_AppendToObj(Tcl_GetObjResult(interp),
", this may be due to a comment incorrectly placed outside of a switch body - see the \"switch\" documentation", -1);
}
return TCL_ERROR;
}
pattern = Tcl_GetString(objv[i]);
if (splitObjs && *pattern == '#') {
seenComment = 1;
}
matched = 0;
if ((i == objc - 2)
&& (*pattern == 'd')
&& (strcmp(pattern, "default") == 0)) {
matched = 1;
} else {
switch (mode) {
case OPT_EXACT:
matched = (strcmp(Tcl_GetString(stringObj), pattern) == 0);
break;
case OPT_GLOB:
matched = Tcl_StringMatch(Tcl_GetString(stringObj),
pattern);
break;
case OPT_REGEXP:
matched = Tcl_RegExpMatchObj(interp, stringObj, objv[i]);
if (matched < 0) {
return TCL_ERROR;
}
break;
}
}
if (matched == 0) {
continue;
}
for (j = i + 1; ; j += 2) {
if (j >= objc) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"no body specified for pattern \"", pattern,
"\"", (char *) NULL);
return TCL_ERROR;
}
if (strcmp(Tcl_GetString(objv[j]), "-") != 0) {
break;
}
}
result = Tcl_EvalObjEx(interp, objv[j], 0);
if (result == TCL_ERROR) {
char msg[100 + TCL_INTEGER_SPACE];
sprintf(msg, "\n (\"%.50s\" arm line %d)", pattern,
interp->errorLine);
Tcl_AddObjErrorInfo(interp, msg, -1);
}
return result;
}
return TCL_OK;
}
int
Tcl_TimeObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
register Tcl_Obj *objPtr;
register int i, result;
int count;
double totalMicroSec;
Tcl_Time start, stop;
char buf[100];
if (objc == 2) {
count = 1;
} else if (objc == 3) {
result = Tcl_GetIntFromObj(interp, objv[2], &count);
if (result != TCL_OK) {
return result;
}
} else {
Tcl_WrongNumArgs(interp, 1, objv, "command ?count?");
return TCL_ERROR;
}
objPtr = objv[1];
i = count;
TclpGetTime(&start);
while (i-- > 0) {
result = Tcl_EvalObjEx(interp, objPtr, 0);
if (result != TCL_OK) {
return result;
}
}
TclpGetTime(&stop);
totalMicroSec =
(stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
sprintf(buf, "%.0f microseconds per iteration",
((count <= 0) ? 0 : totalMicroSec/count));
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp), buf, -1);
return TCL_OK;
}
int
Tcl_TraceObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
int optionIndex, commandLength;
char *name, *rwuOps, *command, *p;
size_t length;
static char *traceOptions[] = {
"variable", "vdelete", "vinfo", (char *) NULL
};
enum traceOptions {
TRACE_VARIABLE, TRACE_VDELETE, TRACE_VINFO
};
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option [arg arg ...]");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], traceOptions,
"option", 0, &optionIndex) != TCL_OK) {
return TCL_ERROR;
}
switch ((enum traceOptions) optionIndex) {
case TRACE_VARIABLE: {
int flags;
TraceVarInfo *tvarPtr;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
return TCL_ERROR;
}
flags = 0;
rwuOps = Tcl_GetString(objv[3]);
for (p = rwuOps; *p != 0; p++) {
if (*p == 'r') {
flags |= TCL_TRACE_READS;
} else if (*p == 'w') {
flags |= TCL_TRACE_WRITES;
} else if (*p == 'u') {
flags |= TCL_TRACE_UNSETS;
} else {
goto badOps;
}
}
if (flags == 0) {
goto badOps;
}
command = Tcl_GetStringFromObj(objv[4], &commandLength);
length = (size_t) commandLength;
tvarPtr = (TraceVarInfo *) ckalloc((unsigned)
(sizeof(TraceVarInfo) - sizeof(tvarPtr->command)
+ length + 1));
tvarPtr->flags = flags;
tvarPtr->errMsg = NULL;
tvarPtr->length = length;
flags |= TCL_TRACE_UNSETS;
strcpy(tvarPtr->command, command);
name = Tcl_GetString(objv[2]);
if (Tcl_TraceVar(interp, name, flags, TraceVarProc,
(ClientData) tvarPtr) != TCL_OK) {
ckfree((char *) tvarPtr);
return TCL_ERROR;
}
break;
}
case TRACE_VDELETE: {
int flags;
TraceVarInfo *tvarPtr;
ClientData clientData;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
return TCL_ERROR;
}
flags = 0;
rwuOps = Tcl_GetString(objv[3]);
for (p = rwuOps; *p != 0; p++) {
if (*p == 'r') {
flags |= TCL_TRACE_READS;
} else if (*p == 'w') {
flags |= TCL_TRACE_WRITES;
} else if (*p == 'u') {
flags |= TCL_TRACE_UNSETS;
} else {
goto badOps;
}
}
if (flags == 0) {
goto badOps;
}
command = Tcl_GetStringFromObj(objv[4], &commandLength);
length = (size_t) commandLength;
clientData = 0;
name = Tcl_GetString(objv[2]);
while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
TraceVarProc, clientData)) != 0) {
tvarPtr = (TraceVarInfo *) clientData;
if ((tvarPtr->length == length) && (tvarPtr->flags == flags)
&& (strncmp(command, tvarPtr->command,
(size_t) length) == 0)) {
Tcl_UntraceVar(interp, name, flags | TCL_TRACE_UNSETS,
TraceVarProc, clientData);
if (tvarPtr->errMsg != NULL) {
ckfree(tvarPtr->errMsg);
}
ckfree((char *) tvarPtr);
break;
}
}
break;
}
case TRACE_VINFO: {
ClientData clientData;
char ops[4];
Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "name");
return TCL_ERROR;
}
resultListPtr = Tcl_GetObjResult(interp);
clientData = 0;
name = Tcl_GetString(objv[2]);
while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
TraceVarProc, clientData)) != 0) {
TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
pairObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
p = ops;
if (tvarPtr->flags & TCL_TRACE_READS) {
*p = 'r';
p++;
}
if (tvarPtr->flags & TCL_TRACE_WRITES) {
*p = 'w';
p++;
}
if (tvarPtr->flags & TCL_TRACE_UNSETS) {
*p = 'u';
p++;
}
*p = '\0';
elemObjPtr = Tcl_NewStringObj(ops, -1);
Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
}
Tcl_SetObjResult(interp, resultListPtr);
break;
}
default: {
panic("Tcl_TraceObjCmd: bad option index to TraceOptions");
}
}
return TCL_OK;
badOps:
Tcl_AppendResult(interp, "bad operations \"", rwuOps,
"\": should be one or more of rwu", (char *) NULL);
return TCL_ERROR;
}
static char *
TraceVarProc(clientData, interp, name1, name2, flags)
ClientData clientData;
Tcl_Interp *interp;
char *name1;
char *name2;
int flags;
{
Tcl_SavedResult state;
TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
char *result;
int code;
Tcl_DString cmd;
result = NULL;
if (tvarPtr->errMsg != NULL) {
ckfree(tvarPtr->errMsg);
tvarPtr->errMsg = NULL;
}
if ((tvarPtr->flags & flags) && !(flags & TCL_INTERP_DESTROYED)) {
if (name2 == NULL) {
name2 = "";
}
Tcl_DStringInit(&cmd);
Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
Tcl_DStringAppendElement(&cmd, name1);
Tcl_DStringAppendElement(&cmd, name2);
if (flags & TCL_TRACE_READS) {
Tcl_DStringAppend(&cmd, " r", 2);
} else if (flags & TCL_TRACE_WRITES) {
Tcl_DStringAppend(&cmd, " w", 2);
} else if (flags & TCL_TRACE_UNSETS) {
Tcl_DStringAppend(&cmd, " u", 2);
}
Tcl_SaveResult(interp, &state);
code = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
if (code != TCL_OK) {
char *string;
int length;
string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length);
tvarPtr->errMsg = (char *) ckalloc((unsigned) (length + 1));
memcpy(tvarPtr->errMsg, string, (size_t) (length + 1));
result = tvarPtr->errMsg;
}
Tcl_RestoreResult(interp, &state);
Tcl_DStringFree(&cmd);
}
if (flags & TCL_TRACE_DESTROYED) {
result = NULL;
if (tvarPtr->errMsg != NULL) {
ckfree(tvarPtr->errMsg);
}
ckfree((char *) tvarPtr);
}
return result;
}
int
Tcl_WhileObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
int result, value;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "test command");
return TCL_ERROR;
}
while (1) {
result = Tcl_ExprBooleanObj(interp, objv[1], &value);
if (result != TCL_OK) {
return result;
}
if (!value) {
break;
}
result = Tcl_EvalObjEx(interp, objv[2], 0);
if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
if (result == TCL_ERROR) {
char msg[32 + TCL_INTEGER_SPACE];
sprintf(msg, "\n (\"while\" body line %d)",
interp->errorLine);
Tcl_AddErrorInfo(interp, msg);
}
break;
}
}
if (result == TCL_BREAK) {
result = TCL_OK;
}
if (result == TCL_OK) {
Tcl_ResetResult(interp);
}
return result;
}