2187 lines
53 KiB
C
2187 lines
53 KiB
C
/*
|
||
* tclCmdMZ.c --
|
||
*
|
||
* This file contains the top-level command routines for most of
|
||
* the Tcl built-in commands whose names begin with the letters
|
||
* M to Z. It contains only commands in the generic core (i.e.
|
||
* those that don't depend much upon UNIX facilities).
|
||
*
|
||
* Copyright (c) 1987-1993 The Regents of the University of California.
|
||
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
|
||
*
|
||
* See the file "license.terms" for information on usage and redistribution
|
||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
*
|
||
* SCCS: @(#) tclCmdMZ.c 1.104 97/10/31 13:06:19
|
||
*/
|
||
|
||
#include "tclInt.h"
|
||
#include "tclPort.h"
|
||
#include "tclCompile.h"
|
||
|
||
/*
|
||
* Structure used to hold information about variable traces:
|
||
*/
|
||
|
||
typedef struct {
|
||
int flags; /* Operations for which Tcl command is
|
||
* to be invoked. */
|
||
char *errMsg; /* Error message returned from Tcl command,
|
||
* or NULL. Malloc'ed. */
|
||
int length; /* Number of non-NULL chars. in command. */
|
||
char command[4]; /* Space for Tcl command to invoke. Actual
|
||
* size will be as large as necessary to
|
||
* hold command. This field must be the
|
||
* last in the structure, so that it can
|
||
* be larger than 4 bytes. */
|
||
} TraceVarInfo;
|
||
|
||
/*
|
||
* Forward declarations for procedures defined in this file:
|
||
*/
|
||
|
||
static char * TraceVarProc _ANSI_ARGS_((ClientData clientData,
|
||
Tcl_Interp *interp, char *name1, char *name2,
|
||
int flags));
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_PwdCmd --
|
||
*
|
||
* This procedure is invoked to process the "pwd" Tcl command.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_PwdCmd(dummy, interp, argc, argv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int argc; /* Number of arguments. */
|
||
char **argv; /* Argument strings. */
|
||
{
|
||
char *dirName;
|
||
|
||
if (argc != 1) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], "\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
dirName = TclGetCwd(interp);
|
||
if (dirName == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
Tcl_SetResult(interp, dirName, TCL_VOLATILE);
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_RegexpCmd --
|
||
*
|
||
* This procedure is invoked to process the "regexp" Tcl command.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_RegexpCmd(dummy, interp, argc, argv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int argc; /* Number of arguments. */
|
||
char **argv; /* Argument strings. */
|
||
{
|
||
int noCase = 0;
|
||
int indices = 0;
|
||
Tcl_RegExp regExpr;
|
||
char **argPtr, *string, *pattern, *start, *end;
|
||
int match = 0; /* Initialization needed only to
|
||
* prevent compiler warning. */
|
||
int i;
|
||
Tcl_DString stringDString, patternDString;
|
||
|
||
if (argc < 3) {
|
||
wrongNumArgs:
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||
" ?switches? exp string ?matchVar? ?subMatchVar ",
|
||
"subMatchVar ...?\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
argPtr = argv+1;
|
||
argc--;
|
||
while ((argc > 0) && (argPtr[0][0] == '-')) {
|
||
if (strcmp(argPtr[0], "-indices") == 0) {
|
||
indices = 1;
|
||
} else if (strcmp(argPtr[0], "-nocase") == 0) {
|
||
noCase = 1;
|
||
} else if (strcmp(argPtr[0], "--") == 0) {
|
||
argPtr++;
|
||
argc--;
|
||
break;
|
||
} else {
|
||
Tcl_AppendResult(interp, "bad switch \"", argPtr[0],
|
||
"\": must be -indices, -nocase, or --", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
argPtr++;
|
||
argc--;
|
||
}
|
||
if (argc < 2) {
|
||
goto wrongNumArgs;
|
||
}
|
||
|
||
/*
|
||
* Convert the string and pattern to lower case, if desired, and
|
||
* perform the matching operation.
|
||
*/
|
||
|
||
if (noCase) {
|
||
register char *p;
|
||
|
||
Tcl_DStringInit(&patternDString);
|
||
Tcl_DStringAppend(&patternDString, argPtr[0], -1);
|
||
pattern = Tcl_DStringValue(&patternDString);
|
||
for (p = pattern; *p != 0; p++) {
|
||
if (isupper(UCHAR(*p))) {
|
||
*p = (char)tolower(UCHAR(*p));
|
||
}
|
||
}
|
||
Tcl_DStringInit(&stringDString);
|
||
Tcl_DStringAppend(&stringDString, argPtr[1], -1);
|
||
string = Tcl_DStringValue(&stringDString);
|
||
for (p = string; *p != 0; p++) {
|
||
if (isupper(UCHAR(*p))) {
|
||
*p = (char)tolower(UCHAR(*p));
|
||
}
|
||
}
|
||
} else {
|
||
pattern = argPtr[0];
|
||
string = argPtr[1];
|
||
}
|
||
regExpr = Tcl_RegExpCompile(interp, pattern);
|
||
if (regExpr != NULL) {
|
||
match = Tcl_RegExpExec(interp, regExpr, string, string);
|
||
}
|
||
if (noCase) {
|
||
Tcl_DStringFree(&stringDString);
|
||
Tcl_DStringFree(&patternDString);
|
||
}
|
||
if (regExpr == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
if (match < 0) {
|
||
return TCL_ERROR;
|
||
}
|
||
if (!match) {
|
||
Tcl_SetResult(interp, "0", TCL_STATIC);
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
* If additional variable names have been specified, return
|
||
* index information in those variables.
|
||
*/
|
||
|
||
argc -= 2;
|
||
for (i = 0; i < argc; i++) {
|
||
char *result, info[50];
|
||
|
||
Tcl_RegExpRange(regExpr, i, &start, &end);
|
||
if (start == NULL) {
|
||
if (indices) {
|
||
result = Tcl_SetVar(interp, argPtr[i+2], "-1 -1", 0);
|
||
} else {
|
||
result = Tcl_SetVar(interp, argPtr[i+2], "", 0);
|
||
}
|
||
} else {
|
||
if (indices) {
|
||
sprintf(info, "%d %d", (int)(start - string),
|
||
(int)(end - string - 1));
|
||
result = Tcl_SetVar(interp, argPtr[i+2], info, 0);
|
||
} else {
|
||
char savedChar, *first, *last;
|
||
|
||
first = argPtr[1] + (start - string);
|
||
last = argPtr[1] + (end - string);
|
||
if (first == last) { /* don't modify argument */
|
||
result = Tcl_SetVar(interp, argPtr[i+2], "", 0);
|
||
} else {
|
||
savedChar = *last;
|
||
*last = 0;
|
||
result = Tcl_SetVar(interp, argPtr[i+2], first, 0);
|
||
*last = savedChar;
|
||
}
|
||
}
|
||
}
|
||
if (result == NULL) {
|
||
Tcl_AppendResult(interp, "couldn't set variable \"",
|
||
argPtr[i+2], "\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
}
|
||
Tcl_SetResult(interp, "1", TCL_STATIC);
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_RegsubCmd --
|
||
*
|
||
* This procedure is invoked to process the "regsub" Tcl command.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_RegsubCmd(dummy, interp, argc, argv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int argc; /* Number of arguments. */
|
||
char **argv; /* Argument strings. */
|
||
{
|
||
int noCase = 0, all = 0;
|
||
Tcl_RegExp regExpr;
|
||
char *string, *pattern, *p, *firstChar, **argPtr;
|
||
int match, code, numMatches;
|
||
char *start, *end, *subStart, *subEnd;
|
||
register char *src, c;
|
||
Tcl_DString stringDString, patternDString, resultDString;
|
||
|
||
if (argc < 5) {
|
||
wrongNumArgs:
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||
" ?switches? exp string subSpec varName\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
argPtr = argv+1;
|
||
argc--;
|
||
while (argPtr[0][0] == '-') {
|
||
if (strcmp(argPtr[0], "-nocase") == 0) {
|
||
noCase = 1;
|
||
} else if (strcmp(argPtr[0], "-all") == 0) {
|
||
all = 1;
|
||
} else if (strcmp(argPtr[0], "--") == 0) {
|
||
argPtr++;
|
||
argc--;
|
||
break;
|
||
} else {
|
||
Tcl_AppendResult(interp, "bad switch \"", argPtr[0],
|
||
"\": must be -all, -nocase, or --", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
argPtr++;
|
||
argc--;
|
||
}
|
||
if (argc != 4) {
|
||
goto wrongNumArgs;
|
||
}
|
||
|
||
/*
|
||
* Convert the string and pattern to lower case, if desired.
|
||
*/
|
||
|
||
if (noCase) {
|
||
Tcl_DStringInit(&patternDString);
|
||
Tcl_DStringAppend(&patternDString, argPtr[0], -1);
|
||
pattern = Tcl_DStringValue(&patternDString);
|
||
for (p = pattern; *p != 0; p++) {
|
||
if (isupper(UCHAR(*p))) {
|
||
*p = (char)tolower(UCHAR(*p));
|
||
}
|
||
}
|
||
Tcl_DStringInit(&stringDString);
|
||
Tcl_DStringAppend(&stringDString, argPtr[1], -1);
|
||
string = Tcl_DStringValue(&stringDString);
|
||
for (p = string; *p != 0; p++) {
|
||
if (isupper(UCHAR(*p))) {
|
||
*p = (char)tolower(UCHAR(*p));
|
||
}
|
||
}
|
||
} else {
|
||
pattern = argPtr[0];
|
||
string = argPtr[1];
|
||
}
|
||
Tcl_DStringInit(&resultDString);
|
||
regExpr = Tcl_RegExpCompile(interp, pattern);
|
||
if (regExpr == NULL) {
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
|
||
/*
|
||
* The following loop is to handle multiple matches within the
|
||
* same source string; each iteration handles one match and its
|
||
* corresponding substitution. If "-all" hasn't been specified
|
||
* then the loop body only gets executed once.
|
||
*/
|
||
|
||
numMatches = 0;
|
||
for (p = string; *p != 0; ) {
|
||
match = Tcl_RegExpExec(interp, regExpr, p, string);
|
||
if (match < 0) {
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
if (!match) {
|
||
break;
|
||
}
|
||
numMatches += 1;
|
||
|
||
/*
|
||
* Copy the portion of the source string before the match to the
|
||
* result variable.
|
||
*/
|
||
|
||
Tcl_RegExpRange(regExpr, 0, &start, &end);
|
||
Tcl_DStringAppend(&resultDString, argPtr[1] + (p - string), start - p);
|
||
|
||
/*
|
||
* Append the subSpec argument to the variable, making appropriate
|
||
* substitutions. This code is a bit hairy because of the backslash
|
||
* conventions and because the code saves up ranges of characters in
|
||
* subSpec to reduce the number of calls to Tcl_SetVar.
|
||
*/
|
||
|
||
for (src = firstChar = argPtr[2], 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 == '&')) {
|
||
*src = c;
|
||
src[1] = 0;
|
||
Tcl_DStringAppend(&resultDString, firstChar, -1);
|
||
*src = '\\';
|
||
src[1] = c;
|
||
firstChar = src+2;
|
||
src++;
|
||
continue;
|
||
} else {
|
||
continue;
|
||
}
|
||
} else {
|
||
continue;
|
||
}
|
||
if (firstChar != src) {
|
||
c = *src;
|
||
*src = 0;
|
||
Tcl_DStringAppend(&resultDString, firstChar, -1);
|
||
*src = c;
|
||
}
|
||
Tcl_RegExpRange(regExpr, index, &subStart, &subEnd);
|
||
if ((subStart != NULL) && (subEnd != NULL)) {
|
||
char *first, *last, saved;
|
||
|
||
first = argPtr[1] + (subStart - string);
|
||
last = argPtr[1] + (subEnd - string);
|
||
saved = *last;
|
||
*last = 0;
|
||
Tcl_DStringAppend(&resultDString, first, -1);
|
||
*last = saved;
|
||
}
|
||
if (*src == '\\') {
|
||
src++;
|
||
}
|
||
firstChar = src+1;
|
||
}
|
||
if (firstChar != src) {
|
||
Tcl_DStringAppend(&resultDString, firstChar, -1);
|
||
}
|
||
if (end == p) {
|
||
|
||
/*
|
||
* Always consume at least one character of the input string
|
||
* in order to prevent infinite loops.
|
||
*/
|
||
|
||
Tcl_DStringAppend(&resultDString, argPtr[1] + (p - string), 1);
|
||
p = end + 1;
|
||
} else {
|
||
p = end;
|
||
}
|
||
if (!all) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Copy the portion of the source string after the last match to the
|
||
* result variable.
|
||
*/
|
||
|
||
if ((*p != 0) || (numMatches == 0)) {
|
||
Tcl_DStringAppend(&resultDString, argPtr[1] + (p - string), -1);
|
||
}
|
||
if (Tcl_SetVar(interp, argPtr[3], Tcl_DStringValue(&resultDString), 0)
|
||
== NULL) {
|
||
Tcl_AppendResult(interp,
|
||
"couldn't set variable \"", argPtr[3], "\"",
|
||
(char *) NULL);
|
||
code = TCL_ERROR;
|
||
} else {
|
||
char buf[40];
|
||
|
||
TclFormatInt(buf, numMatches);
|
||
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
||
code = TCL_OK;
|
||
}
|
||
|
||
done:
|
||
if (noCase) {
|
||
Tcl_DStringFree(&stringDString);
|
||
Tcl_DStringFree(&patternDString);
|
||
}
|
||
Tcl_DStringFree(&resultDString);
|
||
return code;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_RenameObjCmd --
|
||
*
|
||
* This procedure is invoked to process the "rename" Tcl command.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl object result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_RenameObjCmd(dummy, interp, objc, objv)
|
||
ClientData dummy; /* Arbitrary value passed to the command. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int objc; /* Number of arguments. */
|
||
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
||
{
|
||
char *oldName, *newName;
|
||
|
||
if (objc != 3) {
|
||
Tcl_WrongNumArgs(interp, 1, objv, "oldName newName");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
oldName = Tcl_GetStringFromObj(objv[1], (int *) NULL);
|
||
newName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
|
||
return TclRenameCommand(interp, oldName, newName);
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_ReturnObjCmd --
|
||
*
|
||
* This object-based procedure is invoked to process the "return" Tcl
|
||
* command. See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl object result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_ReturnObjCmd(dummy, interp, objc, objv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int objc; /* Number of arguments. */
|
||
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
||
{
|
||
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;
|
||
|
||
/*
|
||
* THIS FAILS IF AN OBJECT CONTAINS AN EMBEDDED NULL.
|
||
*/
|
||
|
||
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_GetStringFromObj(objv[1], (int *) NULL),
|
||
"\": 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) {
|
||
/*
|
||
* Set the interpreter's object result. An inline version of
|
||
* Tcl_SetObjResult.
|
||
*/
|
||
|
||
Tcl_SetObjResult(interp, objv[0]);
|
||
}
|
||
iPtr->returnCode = code;
|
||
return TCL_RETURN;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_ScanCmd --
|
||
*
|
||
* This procedure is invoked to process the "scan" Tcl command.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_ScanCmd(dummy, interp, argc, argv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int argc; /* Number of arguments. */
|
||
char **argv; /* Argument strings. */
|
||
{
|
||
# define MAX_FIELDS 20
|
||
typedef struct {
|
||
char fmt; /* Format for field. */
|
||
int size; /* How many bytes to allow for
|
||
* field. */
|
||
char *location; /* Where field will be stored. */
|
||
} Field;
|
||
Field fields[MAX_FIELDS]; /* Info about all the fields in the
|
||
* format string. */
|
||
register Field *curField;
|
||
int numFields = 0; /* Number of fields actually
|
||
* specified. */
|
||
int suppress; /* Current field is assignment-
|
||
* suppressed. */
|
||
int totalSize = 0; /* Number of bytes needed to store
|
||
* all results combined. */
|
||
char *results; /* Where scanned output goes.
|
||
* Malloced; NULL means not allocated
|
||
* yet. */
|
||
int numScanned; /* sscanf's result. */
|
||
register char *fmt;
|
||
int i, widthSpecified, length, code;
|
||
char buf[40];
|
||
|
||
/*
|
||
* The variables below are used to hold a copy of the format
|
||
* string, so that we can replace format specifiers like "%f"
|
||
* and "%F" with specifiers like "%lf"
|
||
*/
|
||
|
||
# define STATIC_SIZE 5
|
||
char copyBuf[STATIC_SIZE], *fmtCopy;
|
||
register char *dst;
|
||
|
||
if (argc < 3) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||
" string format ?varName varName ...?\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
* This procedure operates in four stages:
|
||
* 1. Scan the format string, collecting information about each field.
|
||
* 2. Allocate an array to hold all of the scanned fields.
|
||
* 3. Call sscanf to do all the dirty work, and have it store the
|
||
* parsed fields in the array.
|
||
* 4. Pick off the fields from the array and assign them to variables.
|
||
*/
|
||
|
||
code = TCL_OK;
|
||
results = NULL;
|
||
length = strlen(argv[2]) * 2 + 1;
|
||
if (length < STATIC_SIZE) {
|
||
fmtCopy = copyBuf;
|
||
} else {
|
||
fmtCopy = (char *) ckalloc((unsigned) length);
|
||
}
|
||
dst = fmtCopy;
|
||
for (fmt = argv[2]; *fmt != 0; fmt++) {
|
||
*dst = *fmt;
|
||
dst++;
|
||
if (*fmt != '%') {
|
||
continue;
|
||
}
|
||
fmt++;
|
||
if (*fmt == '%') {
|
||
*dst = *fmt;
|
||
dst++;
|
||
continue;
|
||
}
|
||
if (*fmt == '*') {
|
||
suppress = 1;
|
||
*dst = *fmt;
|
||
dst++;
|
||
fmt++;
|
||
} else {
|
||
suppress = 0;
|
||
}
|
||
widthSpecified = 0;
|
||
while (isdigit(UCHAR(*fmt))) {
|
||
widthSpecified = 1;
|
||
*dst = *fmt;
|
||
dst++;
|
||
fmt++;
|
||
}
|
||
if ((*fmt == 'l') || (*fmt == 'h') || (*fmt == 'L')) {
|
||
fmt++;
|
||
}
|
||
*dst = *fmt;
|
||
dst++;
|
||
if (suppress) {
|
||
continue;
|
||
}
|
||
if (numFields == MAX_FIELDS) {
|
||
Tcl_SetResult(interp, "too many fields to scan", TCL_STATIC);
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
curField = &fields[numFields];
|
||
numFields++;
|
||
switch (*fmt) {
|
||
case 'd':
|
||
case 'i':
|
||
case 'o':
|
||
case 'x':
|
||
curField->fmt = 'd';
|
||
curField->size = sizeof(int);
|
||
break;
|
||
|
||
case 'u':
|
||
curField->fmt = 'u';
|
||
curField->size = sizeof(int);
|
||
break;
|
||
|
||
case 's':
|
||
curField->fmt = 's';
|
||
curField->size = strlen(argv[1]) + 1;
|
||
break;
|
||
|
||
case 'c':
|
||
if (widthSpecified) {
|
||
Tcl_SetResult(interp,
|
||
"field width may not be specified in %c conversion",
|
||
TCL_STATIC);
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
curField->fmt = 'c';
|
||
curField->size = sizeof(int);
|
||
break;
|
||
|
||
case 'e':
|
||
case 'f':
|
||
case 'g':
|
||
dst[-1] = 'l';
|
||
dst[0] = 'f';
|
||
dst++;
|
||
curField->fmt = 'f';
|
||
curField->size = sizeof(double);
|
||
break;
|
||
|
||
case '[':
|
||
curField->fmt = 's';
|
||
curField->size = strlen(argv[1]) + 1;
|
||
do {
|
||
fmt++;
|
||
if (*fmt == 0) {
|
||
Tcl_SetResult(interp,
|
||
"unmatched [ in format string", TCL_STATIC);
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
*dst = *fmt;
|
||
dst++;
|
||
} while (*fmt != ']');
|
||
break;
|
||
|
||
default:
|
||
{
|
||
char buf[50];
|
||
|
||
sprintf(buf, "bad scan conversion character \"%c\"", *fmt);
|
||
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
}
|
||
curField->size = TCL_ALIGN(curField->size);
|
||
totalSize += curField->size;
|
||
}
|
||
*dst = 0;
|
||
|
||
if (numFields != (argc-3)) {
|
||
Tcl_SetResult(interp,
|
||
"different numbers of variable names and field specifiers",
|
||
TCL_STATIC);
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
|
||
/*
|
||
* Step 2:
|
||
*/
|
||
|
||
results = (char *) ckalloc((unsigned) totalSize);
|
||
for (i = 0, totalSize = 0, curField = fields;
|
||
i < numFields; i++, curField++) {
|
||
curField->location = results + totalSize;
|
||
totalSize += curField->size;
|
||
}
|
||
|
||
/*
|
||
* Fill in the remaining fields with NULL; the only purpose of
|
||
* this is to keep some memory analyzers, like Purify, from
|
||
* complaining.
|
||
*/
|
||
|
||
for ( ; i < MAX_FIELDS; i++, curField++) {
|
||
curField->location = NULL;
|
||
}
|
||
|
||
/*
|
||
* Step 3:
|
||
*/
|
||
|
||
numScanned = sscanf(argv[1], fmtCopy,
|
||
fields[0].location, fields[1].location, fields[2].location,
|
||
fields[3].location, fields[4].location, fields[5].location,
|
||
fields[6].location, fields[7].location, fields[8].location,
|
||
fields[9].location, fields[10].location, fields[11].location,
|
||
fields[12].location, fields[13].location, fields[14].location,
|
||
fields[15].location, fields[16].location, fields[17].location,
|
||
fields[18].location, fields[19].location);
|
||
|
||
/*
|
||
* Step 4:
|
||
*/
|
||
|
||
if (numScanned < numFields) {
|
||
numFields = numScanned;
|
||
}
|
||
for (i = 0, curField = fields; i < numFields; i++, curField++) {
|
||
switch (curField->fmt) {
|
||
char string[TCL_DOUBLE_SPACE];
|
||
|
||
case 'd':
|
||
TclFormatInt(string, *((int *) curField->location));
|
||
if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
|
||
storeError:
|
||
Tcl_AppendResult(interp,
|
||
"couldn't set variable \"", argv[i+3], "\"",
|
||
(char *) NULL);
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
break;
|
||
|
||
case 'u':
|
||
sprintf(string, "%u", *((int *) curField->location));
|
||
if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
|
||
goto storeError;
|
||
}
|
||
break;
|
||
|
||
case 'c':
|
||
TclFormatInt(string, *((char *) curField->location) & 0xff);
|
||
if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
|
||
goto storeError;
|
||
}
|
||
break;
|
||
|
||
case 's':
|
||
if (Tcl_SetVar(interp, argv[i+3], curField->location, 0)
|
||
== NULL) {
|
||
goto storeError;
|
||
}
|
||
break;
|
||
|
||
case 'f':
|
||
Tcl_PrintDouble((Tcl_Interp *) NULL,
|
||
*((double *) curField->location), string);
|
||
if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
|
||
goto storeError;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
TclFormatInt(buf, numScanned);
|
||
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
||
done:
|
||
if (results != NULL) {
|
||
ckfree(results);
|
||
}
|
||
if (fmtCopy != copyBuf) {
|
||
ckfree(fmtCopy);
|
||
}
|
||
return code;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_SourceObjCmd --
|
||
*
|
||
* This procedure is invoked to process the "source" Tcl command.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl object result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_SourceObjCmd(dummy, interp, objc, objv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int objc; /* Number of arguments. */
|
||
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
||
{
|
||
char *bytes;
|
||
int result;
|
||
|
||
if (objc != 2) {
|
||
Tcl_WrongNumArgs(interp, 1, objv, "fileName");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
* THIS FAILS IF THE OBJECT'S STRING REP CONTAINS A NULL.
|
||
*/
|
||
|
||
bytes = Tcl_GetStringFromObj(objv[1], (int *) NULL);
|
||
result = Tcl_EvalFile(interp, bytes);
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_SplitObjCmd --
|
||
*
|
||
* This procedure is invoked to process the "split" Tcl command.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_SplitObjCmd(dummy, interp, objc, objv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int objc; /* Number of arguments. */
|
||
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
||
{
|
||
register char *p, *p2;
|
||
char *splitChars, *string, *elementStart;
|
||
int splitCharLen, stringLen, i, j;
|
||
Tcl_Obj *listPtr;
|
||
|
||
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);
|
||
listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
|
||
|
||
/*
|
||
* Handle the special case of splitting on every character.
|
||
*/
|
||
|
||
if (splitCharLen == 0) {
|
||
for (i = 0, p = string; i < stringLen; i++, p++) {
|
||
Tcl_ListObjAppendElement(interp, listPtr,
|
||
Tcl_NewStringObj(p, 1));
|
||
}
|
||
} else {
|
||
/*
|
||
* Normal case: split on any of a given set of characters.
|
||
* Discard instances of the split characters.
|
||
*/
|
||
|
||
for (i = 0, p = elementStart = string; i < stringLen; i++, p++) {
|
||
for (j = 0, p2 = splitChars; j < splitCharLen; j++, p2++) {
|
||
if (*p2 == *p) {
|
||
Tcl_ListObjAppendElement(interp, listPtr,
|
||
Tcl_NewStringObj(elementStart, (p-elementStart)));
|
||
elementStart = p+1;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (p != string) {
|
||
int remainingChars = stringLen - (elementStart-string);
|
||
Tcl_ListObjAppendElement(interp, listPtr,
|
||
Tcl_NewStringObj(elementStart, remainingChars));
|
||
}
|
||
}
|
||
|
||
Tcl_SetObjResult(interp, listPtr);
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_StringObjCmd --
|
||
*
|
||
* This procedure is invoked to process the "string" Tcl command.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_StringObjCmd(dummy, interp, objc, objv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int objc; /* Number of arguments. */
|
||
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
||
{
|
||
int index, left, right;
|
||
Tcl_Obj *resultPtr;
|
||
char *string1, *string2;
|
||
int length1, length2;
|
||
static char *options[] = {
|
||
"compare", "first", "index", "last",
|
||
"length", "match", "range", "tolower",
|
||
"toupper", "trim", "trimleft", "trimright",
|
||
"wordend", "wordstart", NULL
|
||
};
|
||
enum options {
|
||
STR_COMPARE, STR_FIRST, STR_INDEX, STR_LAST,
|
||
STR_LENGTH, STR_MATCH, STR_RANGE, STR_TOLOWER,
|
||
STR_TOUPPER, 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_COMPARE: {
|
||
int match, length;
|
||
|
||
if (objc != 4) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "string1 string2");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
string1 = Tcl_GetStringFromObj(objv[2], &length1);
|
||
string2 = Tcl_GetStringFromObj(objv[3], &length2);
|
||
|
||
length = (length1 < length2) ? length1 : length2;
|
||
match = memcmp(string1, string2, (unsigned) length);
|
||
if (match == 0) {
|
||
match = length1 - length2;
|
||
}
|
||
Tcl_SetIntObj(resultPtr, (match > 0) ? 1 : (match < 0) ? -1 : 0);
|
||
break;
|
||
}
|
||
case STR_FIRST: {
|
||
register char *p, *end;
|
||
int match;
|
||
|
||
if (objc != 4) {
|
||
badFirstLastArgs:
|
||
Tcl_WrongNumArgs(interp, 2, objv, "string1 string2");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
match = -1;
|
||
string1 = Tcl_GetStringFromObj(objv[2], &length1);
|
||
string2 = Tcl_GetStringFromObj(objv[3], &length2);
|
||
if (length1 > 0) {
|
||
end = string2 + length2 - length1 + 1;
|
||
for (p = string2; p < end; p++) {
|
||
/*
|
||
* Scan forward to find the first character.
|
||
*/
|
||
|
||
p = memchr(p, *string1, (unsigned) (end - p));
|
||
if (p == NULL) {
|
||
break;
|
||
}
|
||
if (memcmp(string1, p, (unsigned) length1) == 0) {
|
||
match = p - string2;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
Tcl_SetIntObj(resultPtr, match);
|
||
break;
|
||
}
|
||
case STR_INDEX: {
|
||
int index;
|
||
|
||
if (objc != 4) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
string1 = Tcl_GetStringFromObj(objv[2], &length1);
|
||
if (Tcl_GetIntFromObj(interp, objv[3], &index) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
if ((index >= 0) && (index < length1)) {
|
||
Tcl_SetStringObj(resultPtr, string1 + index, 1);
|
||
}
|
||
break;
|
||
}
|
||
case STR_LAST: {
|
||
register char *p;
|
||
int match;
|
||
|
||
if (objc != 4) {
|
||
goto badFirstLastArgs;
|
||
}
|
||
|
||
match = -1;
|
||
string1 = Tcl_GetStringFromObj(objv[2], &length1);
|
||
string2 = Tcl_GetStringFromObj(objv[3], &length2);
|
||
if (length1 > 0) {
|
||
for (p = string2 + length2 - length1; p >= string2; p--) {
|
||
/*
|
||
* Scan backwards to find the first character.
|
||
*/
|
||
|
||
while ((p != string2) && (*p != *string1)) {
|
||
p--;
|
||
}
|
||
if (memcmp(string1, p, (unsigned) length1) == 0) {
|
||
match = p - string2;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
Tcl_SetIntObj(resultPtr, match);
|
||
break;
|
||
}
|
||
case STR_LENGTH: {
|
||
if (objc != 3) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "string");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
(void) Tcl_GetStringFromObj(objv[2], &length1);
|
||
Tcl_SetIntObj(resultPtr, length1);
|
||
break;
|
||
}
|
||
case STR_MATCH: {
|
||
if (objc != 4) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "pattern string");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
string1 = Tcl_GetStringFromObj(objv[2], &length1);
|
||
string2 = Tcl_GetStringFromObj(objv[3], &length2);
|
||
Tcl_SetBooleanObj(resultPtr, Tcl_StringMatch(string2, string1));
|
||
break;
|
||
}
|
||
case STR_RANGE: {
|
||
int first, last;
|
||
|
||
if (objc != 5) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "string first last");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
string1 = Tcl_GetStringFromObj(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) {
|
||
Tcl_SetStringObj(resultPtr, string1 + first, last - first + 1);
|
||
}
|
||
break;
|
||
}
|
||
case STR_TOLOWER: {
|
||
register char *p, *end;
|
||
|
||
if (objc != 3) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "string");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
string1 = Tcl_GetStringFromObj(objv[2], &length1);
|
||
|
||
/*
|
||
* Since I know resultPtr is not a shared object, I can reach
|
||
* in and diddle the bytes in its string rep to convert them in
|
||
* place to lower case.
|
||
*/
|
||
|
||
Tcl_SetStringObj(resultPtr, string1, length1);
|
||
string1 = Tcl_GetStringFromObj(resultPtr, &length1);
|
||
end = string1 + length1;
|
||
for (p = string1; p < end; p++) {
|
||
if (isupper(UCHAR(*p))) {
|
||
*p = (char) tolower(UCHAR(*p));
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case STR_TOUPPER: {
|
||
register char *p, *end;
|
||
|
||
if (objc != 3) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "string");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
string1 = Tcl_GetStringFromObj(objv[2], &length1);
|
||
|
||
/*
|
||
* Since I know resultPtr is not a shared object, I can reach
|
||
* in and diddle the bytes in its string rep to convert them in
|
||
* place to upper case.
|
||
*/
|
||
|
||
Tcl_SetStringObj(resultPtr, string1, length1);
|
||
string1 = Tcl_GetStringFromObj(resultPtr, &length1);
|
||
end = string1 + length1;
|
||
for (p = string1; p < end; p++) {
|
||
if (islower(UCHAR(*p))) {
|
||
*p = (char) toupper(UCHAR(*p));
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case STR_TRIM: {
|
||
char ch;
|
||
register char *p, *end;
|
||
char *check, *checkEnd;
|
||
|
||
left = 1;
|
||
right = 1;
|
||
|
||
trim:
|
||
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++) {
|
||
ch = *p;
|
||
for (check = string2; ; check++) {
|
||
if (check >= checkEnd) {
|
||
p = end;
|
||
break;
|
||
}
|
||
if (ch == *check) {
|
||
length1--;
|
||
string1++;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (right) {
|
||
end = string1;
|
||
for (p = string1 + length1; p > end; ) {
|
||
p--;
|
||
ch = *p;
|
||
for (check = string2; ; check++) {
|
||
if (check >= checkEnd) {
|
||
p = end;
|
||
break;
|
||
}
|
||
if (ch == *check) {
|
||
length1--;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Tcl_SetStringObj(resultPtr, string1, length1);
|
||
break;
|
||
}
|
||
case STR_TRIMLEFT: {
|
||
left = 1;
|
||
right = 0;
|
||
goto trim;
|
||
}
|
||
case STR_TRIMRIGHT: {
|
||
left = 0;
|
||
right = 1;
|
||
goto trim;
|
||
}
|
||
case STR_WORDEND: {
|
||
int cur, c;
|
||
|
||
if (objc != 4) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "string index");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
string1 = Tcl_GetStringFromObj(objv[2], &length1);
|
||
if (Tcl_GetIntFromObj(interp, objv[3], &index) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
if (index < 0) {
|
||
index = 0;
|
||
}
|
||
cur = length1;
|
||
if (index < length1) {
|
||
for (cur = index; cur < length1; cur++) {
|
||
c = UCHAR(string1[cur]);
|
||
if (!isalnum(c) && (c != '_')) {
|
||
break;
|
||
}
|
||
}
|
||
if (cur == index) {
|
||
cur = index + 1;
|
||
}
|
||
}
|
||
Tcl_SetIntObj(resultPtr, cur);
|
||
break;
|
||
}
|
||
case STR_WORDSTART: {
|
||
int cur, c;
|
||
|
||
if (objc != 4) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "string index");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
string1 = Tcl_GetStringFromObj(objv[2], &length1);
|
||
if (Tcl_GetIntFromObj(interp, objv[3], &index) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
if (index >= length1) {
|
||
index = length1 - 1;
|
||
}
|
||
cur = 0;
|
||
if (index > 0) {
|
||
for (cur = index; cur >= 0; cur--) {
|
||
c = UCHAR(string1[cur]);
|
||
if (!isalnum(c) && (c != '_')) {
|
||
break;
|
||
}
|
||
}
|
||
if (cur != index) {
|
||
cur += 1;
|
||
}
|
||
}
|
||
Tcl_SetIntObj(resultPtr, cur);
|
||
break;
|
||
}
|
||
}
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_SubstCmd --
|
||
*
|
||
* This procedure is invoked to process the "subst" Tcl command.
|
||
* See the user documentation for details on what it does. This
|
||
* command is an almost direct copy of an implementation by
|
||
* Andrew Payne.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_SubstCmd(dummy, interp, argc, argv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int argc; /* Number of arguments. */
|
||
char **argv; /* Argument strings. */
|
||
{
|
||
Interp *iPtr = (Interp *) interp;
|
||
Tcl_DString result;
|
||
char *p, *old, *value;
|
||
int code, count, doVars, doCmds, doBackslashes, i;
|
||
size_t length;
|
||
char c;
|
||
|
||
/*
|
||
* Parse command-line options.
|
||
*/
|
||
|
||
doVars = doCmds = doBackslashes = 1;
|
||
for (i = 1; i < (argc-1); i++) {
|
||
p = argv[i];
|
||
if (*p != '-') {
|
||
break;
|
||
}
|
||
length = strlen(p);
|
||
if (length < 4) {
|
||
badSwitch:
|
||
Tcl_AppendResult(interp, "bad switch \"", p,
|
||
"\": must be -nobackslashes, -nocommands, ",
|
||
"or -novariables", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
if ((p[3] == 'b') && (strncmp(p, "-nobackslashes", length) == 0)) {
|
||
doBackslashes = 0;
|
||
} else if ((p[3] == 'c') && (strncmp(p, "-nocommands", length) == 0)) {
|
||
doCmds = 0;
|
||
} else if ((p[3] == 'v') && (strncmp(p, "-novariables", length) == 0)) {
|
||
doVars = 0;
|
||
} else {
|
||
goto badSwitch;
|
||
}
|
||
}
|
||
if (i != (argc-1)) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||
" ?-nobackslashes? ?-nocommands? ?-novariables? string\"",
|
||
(char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
* Scan through the string one character at a time, performing
|
||
* command, variable, and backslash substitutions.
|
||
*/
|
||
|
||
Tcl_DStringInit(&result);
|
||
old = p = argv[i];
|
||
while (*p != 0) {
|
||
switch (*p) {
|
||
case '\\':
|
||
if (doBackslashes) {
|
||
if (p != old) {
|
||
Tcl_DStringAppend(&result, old, p-old);
|
||
}
|
||
c = Tcl_Backslash(p, &count);
|
||
Tcl_DStringAppend(&result, &c, 1);
|
||
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;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_SwitchObjCmd --
|
||
*
|
||
* This object-based procedure is invoked to process the "switch" Tcl
|
||
* command. See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl object result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_SwitchObjCmd(dummy, interp, objc, objv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int objc; /* Number of arguments. */
|
||
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
||
{
|
||
#define EXACT 0
|
||
#define GLOB 1
|
||
#define REGEXP 2
|
||
int switchObjc, index;
|
||
Tcl_Obj *CONST *switchObjv;
|
||
Tcl_Obj *patternObj, *bodyObj;
|
||
char *string, *pattern, *body;
|
||
int splitObjs, length, patternLen, i, code, mode, matched, bodyIdx;
|
||
static char *switches[] =
|
||
{"-exact", "-glob", "-regexp", "--", (char *) NULL};
|
||
|
||
switchObjc = objc-1;
|
||
switchObjv = objv+1;
|
||
mode = EXACT;
|
||
|
||
while (switchObjc > 0) {
|
||
string = Tcl_GetStringFromObj(switchObjv[0], &length);
|
||
if (*string != '-') {
|
||
break;
|
||
}
|
||
if (Tcl_GetIndexFromObj(interp, switchObjv[0], switches,
|
||
"option", 0, &index) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
switch (index) {
|
||
case 0: /* -exact */
|
||
mode = EXACT;
|
||
break;
|
||
case 1: /* -glob */
|
||
mode = GLOB;
|
||
break;
|
||
case 2: /* -regexp */
|
||
mode = REGEXP;
|
||
break;
|
||
case 3: /* -- */
|
||
switchObjc--;
|
||
switchObjv++;
|
||
goto doneWithSwitches;
|
||
}
|
||
switchObjc--;
|
||
switchObjv++;
|
||
}
|
||
|
||
doneWithSwitches:
|
||
if (switchObjc < 2) {
|
||
Tcl_WrongNumArgs(interp, 1, objv,
|
||
"?switches? string pattern body ... ?default body?");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
string = Tcl_GetStringFromObj(switchObjv[0], &length);
|
||
switchObjc--;
|
||
switchObjv++;
|
||
|
||
/*
|
||
* If all of the pattern/command pairs are lumped into a single
|
||
* argument, split them out again.
|
||
*/
|
||
|
||
splitObjs = 0;
|
||
if (switchObjc == 1) {
|
||
code = Tcl_ListObjLength(interp, switchObjv[0], &switchObjc);
|
||
if (code != TCL_OK) {
|
||
return code;
|
||
}
|
||
splitObjs = 1;
|
||
}
|
||
|
||
for (i = 0; i < switchObjc; i += 2) {
|
||
if (i == (switchObjc-1)) {
|
||
Tcl_ResetResult(interp);
|
||
Tcl_AppendToObj(Tcl_GetObjResult(interp),
|
||
"extra switch pattern with no body", -1);
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
|
||
/*
|
||
* See if the pattern matches the string.
|
||
*/
|
||
|
||
if (splitObjs) {
|
||
code = Tcl_ListObjIndex(interp, switchObjv[0], i, &patternObj);
|
||
if (code != TCL_OK) {
|
||
return code;
|
||
}
|
||
pattern = Tcl_GetStringFromObj(patternObj, &patternLen);
|
||
} else {
|
||
pattern = Tcl_GetStringFromObj(switchObjv[i], &patternLen);
|
||
}
|
||
|
||
matched = 0;
|
||
if ((*pattern == 'd') && (i == switchObjc-2)
|
||
&& (strcmp(pattern, "default") == 0)) {
|
||
matched = 1;
|
||
} else {
|
||
/*
|
||
* THIS FAILS IF AN OBJECT'S STRING REP HAS A NULL.
|
||
*/
|
||
switch (mode) {
|
||
case EXACT:
|
||
matched = (strcmp(string, pattern) == 0);
|
||
break;
|
||
case GLOB:
|
||
matched = Tcl_StringMatch(string, pattern);
|
||
break;
|
||
case REGEXP:
|
||
matched = Tcl_RegExpMatch(interp, string, pattern);
|
||
if (matched < 0) {
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if (!matched) {
|
||
continue;
|
||
}
|
||
|
||
/*
|
||
* We've got a match. Find a body to execute, skipping bodies
|
||
* that are "-".
|
||
*/
|
||
|
||
for (bodyIdx = i+1; ; bodyIdx += 2) {
|
||
if (bodyIdx >= switchObjc) {
|
||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
|
||
"no body specified for pattern \"", pattern,
|
||
"\"", (char *) NULL);
|
||
code = TCL_ERROR;
|
||
goto done;
|
||
}
|
||
|
||
if (splitObjs) {
|
||
code = Tcl_ListObjIndex(interp, switchObjv[0], bodyIdx,
|
||
&bodyObj);
|
||
if (code != TCL_OK) {
|
||
return code;
|
||
}
|
||
} else {
|
||
bodyObj = switchObjv[bodyIdx];
|
||
}
|
||
/*
|
||
* THIS FAILS IF AN OBJECT'S STRING REP HAS A NULL.
|
||
*/
|
||
body = Tcl_GetStringFromObj(bodyObj, &length);
|
||
if ((length != 1) || (body[0] != '-')) {
|
||
break;
|
||
}
|
||
}
|
||
code = Tcl_EvalObj(interp, bodyObj);
|
||
if (code == TCL_ERROR) {
|
||
char msg[100];
|
||
sprintf(msg, "\n (\"%.50s\" arm line %d)", pattern,
|
||
interp->errorLine);
|
||
Tcl_AddObjErrorInfo(interp, msg, -1);
|
||
}
|
||
goto done;
|
||
}
|
||
|
||
/*
|
||
* Nothing matched: return nothing.
|
||
*/
|
||
|
||
code = TCL_OK;
|
||
|
||
done:
|
||
return code;
|
||
#undef EXACT
|
||
#undef GLOB
|
||
#undef REGEXP
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_TimeObjCmd --
|
||
*
|
||
* This object-based procedure is invoked to process the "time" Tcl
|
||
* command. See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl object result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_TimeObjCmd(dummy, interp, objc, objv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int objc; /* Number of arguments. */
|
||
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
||
{
|
||
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_EvalObj(interp, objPtr);
|
||
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;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_TraceCmd --
|
||
*
|
||
* This procedure is invoked to process the "trace" Tcl command.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_TraceCmd(dummy, interp, argc, argv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int argc; /* Number of arguments. */
|
||
char **argv; /* Argument strings. */
|
||
{
|
||
int c;
|
||
size_t length;
|
||
|
||
if (argc < 2) {
|
||
Tcl_AppendResult(interp, "too few args: should be \"",
|
||
argv[0], " option [arg arg ...]\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
c = argv[1][1];
|
||
length = strlen(argv[1]);
|
||
if ((c == 'a') && (strncmp(argv[1], "variable", length) == 0)
|
||
&& (length >= 2)) {
|
||
char *p;
|
||
int flags, length;
|
||
TraceVarInfo *tvarPtr;
|
||
|
||
if (argc != 5) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " variable name ops command\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
flags = 0;
|
||
for (p = argv[3] ; *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;
|
||
}
|
||
|
||
length = strlen(argv[4]);
|
||
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, argv[4]);
|
||
if (Tcl_TraceVar(interp, argv[2], flags, TraceVarProc,
|
||
(ClientData) tvarPtr) != TCL_OK) {
|
||
ckfree((char *) tvarPtr);
|
||
return TCL_ERROR;
|
||
}
|
||
} else if ((c == 'd') && (strncmp(argv[1], "vdelete", length)
|
||
&& (length >= 2)) == 0) {
|
||
char *p;
|
||
int flags, length;
|
||
TraceVarInfo *tvarPtr;
|
||
ClientData clientData;
|
||
|
||
if (argc != 5) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " vdelete name ops command\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
flags = 0;
|
||
for (p = argv[3] ; *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;
|
||
}
|
||
|
||
/*
|
||
* Search through all of our traces on this variable to
|
||
* see if there's one with the given command. If so, then
|
||
* delete the first one that matches.
|
||
*/
|
||
|
||
length = strlen(argv[4]);
|
||
clientData = 0;
|
||
while ((clientData = Tcl_VarTraceInfo(interp, argv[2], 0,
|
||
TraceVarProc, clientData)) != 0) {
|
||
tvarPtr = (TraceVarInfo *) clientData;
|
||
if ((tvarPtr->length == length) && (tvarPtr->flags == flags)
|
||
&& (strncmp(argv[4], tvarPtr->command,
|
||
(size_t) length) == 0)) {
|
||
Tcl_UntraceVar(interp, argv[2], flags | TCL_TRACE_UNSETS,
|
||
TraceVarProc, clientData);
|
||
if (tvarPtr->errMsg != NULL) {
|
||
ckfree(tvarPtr->errMsg);
|
||
}
|
||
ckfree((char *) tvarPtr);
|
||
break;
|
||
}
|
||
}
|
||
} else if ((c == 'i') && (strncmp(argv[1], "vinfo", length) == 0)
|
||
&& (length >= 2)) {
|
||
ClientData clientData;
|
||
char ops[4], *p;
|
||
char *prefix = "{";
|
||
|
||
if (argc != 3) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " vinfo name\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
clientData = 0;
|
||
while ((clientData = Tcl_VarTraceInfo(interp, argv[2], 0,
|
||
TraceVarProc, clientData)) != 0) {
|
||
TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
|
||
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';
|
||
Tcl_AppendResult(interp, prefix, (char *) NULL);
|
||
Tcl_AppendElement(interp, ops);
|
||
Tcl_AppendElement(interp, tvarPtr->command);
|
||
Tcl_AppendResult(interp, "}", (char *) NULL);
|
||
prefix = " {";
|
||
}
|
||
} else {
|
||
Tcl_AppendResult(interp, "bad option \"", argv[1],
|
||
"\": should be variable, vdelete, or vinfo",
|
||
(char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
return TCL_OK;
|
||
|
||
badOps:
|
||
Tcl_AppendResult(interp, "bad operations \"", argv[3],
|
||
"\": should be one or more of rwu", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TraceVarProc --
|
||
*
|
||
* This procedure is called to handle variable accesses that have
|
||
* been traced using the "trace" command.
|
||
*
|
||
* Results:
|
||
* Normally returns NULL. If the trace command returns an error,
|
||
* then this procedure returns an error string.
|
||
*
|
||
* Side effects:
|
||
* Depends on the command associated with the trace.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
static char *
|
||
TraceVarProc(clientData, interp, name1, name2, flags)
|
||
ClientData clientData; /* Information about the variable trace. */
|
||
Tcl_Interp *interp; /* Interpreter containing variable. */
|
||
char *name1; /* Name of variable or array. */
|
||
char *name2; /* Name of element within array; NULL means
|
||
* scalar variable is being referenced. */
|
||
int flags; /* OR-ed bits giving operation and other
|
||
* information. */
|
||
{
|
||
Interp *iPtr = (Interp *) interp;
|
||
TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
|
||
char *result;
|
||
int code;
|
||
Interp dummy;
|
||
Tcl_DString cmd;
|
||
Tcl_Obj *saveObjPtr, *oldObjResultPtr;
|
||
|
||
result = NULL;
|
||
if (tvarPtr->errMsg != NULL) {
|
||
ckfree(tvarPtr->errMsg);
|
||
tvarPtr->errMsg = NULL;
|
||
}
|
||
if ((tvarPtr->flags & flags) && !(flags & TCL_INTERP_DESTROYED)) {
|
||
|
||
/*
|
||
* Generate a command to execute by appending list elements
|
||
* for the two variable names and the operation. The five
|
||
* extra characters are for three space, the opcode character,
|
||
* and the terminating null.
|
||
*/
|
||
|
||
if (name2 == NULL) {
|
||
name2 = "";
|
||
}
|
||
Tcl_DStringInit(&cmd);
|
||
Tcl_DStringAppend(&cmd, tvarPtr->command, 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);
|
||
}
|
||
|
||
/*
|
||
* Execute the command. Be careful to save and restore both the
|
||
* string and object results from the interpreter used for
|
||
* the command. We discard any object result the command returns.
|
||
*/
|
||
|
||
dummy.objResultPtr = Tcl_NewObj();
|
||
Tcl_IncrRefCount(dummy.objResultPtr);
|
||
if (interp->freeProc == 0) {
|
||
dummy.freeProc = (Tcl_FreeProc *) 0;
|
||
dummy.result = "";
|
||
Tcl_SetResult((Tcl_Interp *) &dummy, interp->result,
|
||
TCL_VOLATILE);
|
||
} else {
|
||
dummy.freeProc = interp->freeProc;
|
||
dummy.result = interp->result;
|
||
interp->freeProc = (Tcl_FreeProc *) 0;
|
||
}
|
||
|
||
saveObjPtr = Tcl_GetObjResult(interp);
|
||
Tcl_IncrRefCount(saveObjPtr);
|
||
|
||
code = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
|
||
if (code != TCL_OK) { /* copy error msg to result */
|
||
tvarPtr->errMsg = (char *)
|
||
ckalloc((unsigned) (strlen(interp->result) + 1));
|
||
strcpy(tvarPtr->errMsg, interp->result);
|
||
result = tvarPtr->errMsg;
|
||
Tcl_ResetResult(interp); /* must clear error state. */
|
||
}
|
||
|
||
/*
|
||
* Restore the interpreter's string result.
|
||
*/
|
||
|
||
Tcl_SetResult(interp, dummy.result,
|
||
(dummy.freeProc == 0) ? TCL_VOLATILE : dummy.freeProc);
|
||
|
||
/*
|
||
* Restore the interpreter's object result from saveObjPtr.
|
||
*/
|
||
|
||
oldObjResultPtr = iPtr->objResultPtr;
|
||
iPtr->objResultPtr = saveObjPtr; /* was incremented above */
|
||
Tcl_DecrRefCount(oldObjResultPtr);
|
||
|
||
Tcl_DecrRefCount(dummy.objResultPtr);
|
||
dummy.objResultPtr = NULL;
|
||
Tcl_DStringFree(&cmd);
|
||
}
|
||
if (flags & TCL_TRACE_DESTROYED) {
|
||
result = NULL;
|
||
if (tvarPtr->errMsg != NULL) {
|
||
ckfree(tvarPtr->errMsg);
|
||
}
|
||
ckfree((char *) tvarPtr);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tcl_WhileCmd --
|
||
*
|
||
* This procedure is invoked to process the "while" Tcl command.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* With the bytecode compiler, this procedure is only called when
|
||
* a command name is computed at runtime, and is "while" or the name
|
||
* to which "while" was renamed: e.g., "set z while; $z {$i<100} {}"
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
int
|
||
Tcl_WhileCmd(dummy, interp, argc, argv)
|
||
ClientData dummy; /* Not used. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int argc; /* Number of arguments. */
|
||
char **argv; /* Argument strings. */
|
||
{
|
||
int result, value;
|
||
|
||
if (argc != 3) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " test command\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
while (1) {
|
||
result = Tcl_ExprBoolean(interp, argv[1], &value);
|
||
if (result != TCL_OK) {
|
||
return result;
|
||
}
|
||
if (!value) {
|
||
break;
|
||
}
|
||
result = Tcl_Eval(interp, argv[2]);
|
||
if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
|
||
if (result == TCL_ERROR) {
|
||
char msg[60];
|
||
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;
|
||
}
|
||
|