Micropolis
Rev. | b4fe1a1aa49efbd41c500b38f522ee3af3171fd6 |
---|---|
サイズ | 35,676 バイト |
日時 | 2014-12-15 02:24:36 |
作者 | Simon Morgan |
ログメッセージ | first commit
|
/*
* 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 1987-1991 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclCmdMZ.c,v 1.13 92/04/23 11:07:54 ouster Exp $ SPRITE (Berkeley)";
#endif
#include "tclint.h"
/*
* Structure used to hold information about variable traces:
*/
typedef struct {
int flags; /* Operations for which Tcl command is
* to be invoked. */
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_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;
regexp *regexpPtr;
char **argPtr, *string;
int match, i;
if (argc < 3) {
wrongNumArgs:
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?-nocase? 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) {
argPtr++;
argc--;
indices = 1;
} else if (strcmp(argPtr[0], "-nocase") == 0) {
argPtr++;
argc--;
noCase = 1;
} else {
break;
}
}
if (argc < 2) {
goto wrongNumArgs;
}
regexpPtr = TclCompileRegexp(interp, argPtr[0]);
if (regexpPtr == NULL) {
return TCL_ERROR;
}
/*
* Convert the string to lower case, if desired, and perform
* the match.
*/
if (noCase) {
register char *dst, *src;
string = (char *) ckalloc((unsigned) (strlen(argPtr[1]) + 1));
for (src = argPtr[1], dst = string; *src != 0; src++, dst++) {
if (isupper(*src)) {
*dst = tolower(*src);
} else {
*dst = *src;
}
}
*dst = 0;
} else {
string = argPtr[1];
}
tclRegexpError = NULL;
match = regexec(regexpPtr, string);
if (string != argPtr[1]) {
ckfree(string);
}
if (tclRegexpError != NULL) {
Tcl_AppendResult(interp, "error while matching pattern: ",
tclRegexpError, (char *) NULL);
return TCL_ERROR;
}
if (!match) {
interp->result = "0";
return TCL_OK;
}
/*
* If additional variable names have been specified, return
* index information in those variables.
*/
argc -= 2;
if (argc > NSUBEXP) {
interp->result = "too many substring variables";
return TCL_ERROR;
}
for (i = 0; i < argc; i++) {
char *result, info[50];
if (regexpPtr->startp[i] == 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", regexpPtr->startp[i] - string,
regexpPtr->endp[i] - string - 1);
result = Tcl_SetVar(interp, argPtr[i+2], info, 0);
} else {
char savedChar, *first, *last;
first = argPtr[1] + (regexpPtr->startp[i] - string);
last = argPtr[1] + (regexpPtr->endp[i] - string);
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;
}
}
interp->result = "1";
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;
regexp *regexpPtr;
char *string, *p, *firstChar, *newValue, **argPtr;
int match, result, flags;
register char *src, c;
if (argc < 5) {
wrongNumArgs:
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?-nocase? ?-all? exp string subSpec varName\"", (char *) NULL);
return TCL_ERROR;
}
argPtr = argv+1;
argc--;
while (argPtr[0][0] == '-') {
if (strcmp(argPtr[0], "-nocase") == 0) {
argPtr++;
argc--;
noCase = 1;
} else if (strcmp(argPtr[0], "-all") == 0) {
argPtr++;
argc--;
all = 1;
} else {
break;
}
}
if (argc != 4) {
goto wrongNumArgs;
}
regexpPtr = TclCompileRegexp(interp, argPtr[0]);
if (regexpPtr == NULL) {
return TCL_ERROR;
}
/*
* Convert the string to lower case, if desired.
*/
if (noCase) {
register char *dst;
string = (char *) ckalloc((unsigned) (strlen(argPtr[1]) + 1));
for (src = argPtr[1], dst = string; *src != 0; src++, dst++) {
if (isupper(*src)) {
*dst = tolower(*src);
} else {
*dst = *src;
}
}
*dst = 0;
} else {
string = argPtr[1];
}
/*
* 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.
*/
flags = 0;
for (p = string; *p != 0; ) {
tclRegexpError = NULL;
match = regexec(regexpPtr, p);
if (tclRegexpError != NULL) {
Tcl_AppendResult(interp, "error while matching pattern: ",
tclRegexpError, (char *) NULL);
result = TCL_ERROR;
goto done;
}
if (!match) {
break;
}
/*
* Copy the portion of the source string before the match to the
* result variable.
*/
src = argPtr[1] + (regexpPtr->startp[0] - string);
c = *src;
*src = 0;
newValue = Tcl_SetVar(interp, argPtr[3], argPtr[1] + (p - string),
flags);
*src = c;
flags = TCL_APPEND_VALUE;
if (newValue == NULL) {
cantSet:
Tcl_AppendResult(interp, "couldn't set variable \"",
argPtr[3], "\"", (char *) NULL);
result = TCL_ERROR;
goto done;
}
/*
* 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;
newValue = Tcl_SetVar(interp, argPtr[3], firstChar,
TCL_APPEND_VALUE);
*src = '\\';
src[1] = c;
if (newValue == NULL) {
goto cantSet;
}
firstChar = src+2;
src++;
continue;
} else {
continue;
}
} else {
continue;
}
if (firstChar != src) {
c = *src;
*src = 0;
newValue = Tcl_SetVar(interp, argPtr[3], firstChar,
TCL_APPEND_VALUE);
*src = c;
if (newValue == NULL) {
goto cantSet;
}
}
if ((index < NSUBEXP) && (regexpPtr->startp[index] != NULL)
&& (regexpPtr->endp[index] != NULL)) {
char *first, *last, saved;
first = argPtr[1] + (regexpPtr->startp[index] - string);
last = argPtr[1] + (regexpPtr->endp[index] - string);
saved = *last;
*last = 0;
newValue = Tcl_SetVar(interp, argPtr[3], first,
TCL_APPEND_VALUE);
*last = saved;
if (newValue == NULL) {
goto cantSet;
}
}
if (*src == '\\') {
src++;
}
firstChar = src+1;
}
if (firstChar != src) {
if (Tcl_SetVar(interp, argPtr[3], firstChar,
TCL_APPEND_VALUE) == NULL) {
goto cantSet;
}
}
p = regexpPtr->endp[0];
if (!all) {
break;
}
}
/*
* If there were no matches at all, then return a "0" result.
*/
if (p == string) {
interp->result = "0";
result = TCL_OK;
goto done;
}
/*
* Copy the portion of the source string after the last match to the
* result variable.
*/
if (*p != 0) {
if (Tcl_SetVar(interp, argPtr[3], p, TCL_APPEND_VALUE) == NULL) {
goto cantSet;
}
}
interp->result = "1";
result = TCL_OK;
done:
if (string != argPtr[1]) {
ckfree(string);
}
return result;
}
/*
*----------------------------------------------------------------------
*
* Tcl_RenameCmd --
*
* This procedure is invoked to process the "rename" 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_RenameCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
register Command *cmdPtr;
Interp *iPtr = (Interp *) interp;
Tcl_HashEntry *hPtr;
int new;
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" oldName newName\"", (char *) NULL);
return TCL_ERROR;
}
if (argv[2][0] == '\0') {
if (Tcl_DeleteCommand(interp, argv[1]) != 0) {
Tcl_AppendResult(interp, "can't delete \"", argv[1],
"\": command doesn't exist", (char *) NULL);
return TCL_ERROR;
}
return TCL_OK;
}
hPtr = Tcl_FindHashEntry(&iPtr->commandTable, argv[2]);
if (hPtr != NULL) {
Tcl_AppendResult(interp, "can't rename to \"", argv[2],
"\": command already exists", (char *) NULL);
return TCL_ERROR;
}
hPtr = Tcl_FindHashEntry(&iPtr->commandTable, argv[1]);
if (hPtr == NULL) {
Tcl_AppendResult(interp, "can't rename \"", argv[1],
"\": command doesn't exist", (char *) NULL);
return TCL_ERROR;
}
cmdPtr = (Command *) Tcl_GetHashValue(hPtr);
Tcl_DeleteHashEntry(hPtr);
hPtr = Tcl_CreateHashEntry(&iPtr->commandTable, argv[2], &new);
Tcl_SetHashValue(hPtr, cmdPtr);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* Tcl_ReturnCmd --
*
* This procedure is invoked to process the "return" 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_ReturnCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
if (argc > 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?value?\"", (char *) NULL);
return TCL_ERROR;
}
if (argc == 2) {
Tcl_SetResult(interp, argv[1], TCL_VOLATILE);
}
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. */
{
int arg1Length; /* Number of bytes in argument to be
* scanned. This gives an upper limit
* on string field sizes. */
# 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. */
int numScanned; /* sscanf's result. */
register char *fmt;
int i, widthSpecified;
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.
*/
arg1Length = (strlen(argv[1]) + 4) & ~03;
for (fmt = argv[2]; *fmt != 0; fmt++) {
if (*fmt != '%') {
continue;
}
fmt++;
if (*fmt == '*') {
suppress = 1;
fmt++;
} else {
suppress = 0;
}
widthSpecified = 0;
while (isdigit(*fmt)) {
widthSpecified = 1;
fmt++;
}
if (suppress) {
continue;
}
if (numFields == MAX_FIELDS) {
interp->result = "too many fields to scan";
return TCL_ERROR;
}
curField = &fields[numFields];
numFields++;
switch (*fmt) {
case 'D':
case 'O':
case 'X':
case 'd':
case 'o':
case 'x':
curField->fmt = 'd';
curField->size = sizeof(int);
break;
case 's':
curField->fmt = 's';
curField->size = arg1Length;
break;
case 'c':
if (widthSpecified) {
interp->result =
"field width may not be specified in %c conversion";
return TCL_ERROR;
}
curField->fmt = 'c';
curField->size = sizeof(int);
break;
case 'E':
case 'F':
curField->fmt = 'F';
curField->size = sizeof(double);
break;
case 'e':
case 'f':
curField->fmt = 'f';
curField->size = sizeof(float);
break;
case '[':
curField->fmt = 's';
curField->size = arg1Length;
do {
fmt++;
} while (*fmt != ']');
break;
default:
sprintf(interp->result, "bad scan conversion character \"%c\"",
*fmt);
return TCL_ERROR;
}
totalSize += curField->size;
}
if (numFields != (argc-3)) {
interp->result =
"different numbers of variable names and field specifiers";
return TCL_ERROR;
}
/*
* 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;
}
/*
* Step 3:
*/
numScanned = sscanf(argv[1], argv[2],
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[120];
case 'd':
sprintf(string, "%d", *((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);
ckfree((char *) results);
return TCL_ERROR;
}
break;
case 'c':
sprintf(string, "%d", *((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':
sprintf(string, "%g", *((double *) curField->location));
if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
goto storeError;
}
break;
case 'f':
sprintf(string, "%g", *((float *) curField->location));
if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
goto storeError;
}
break;
}
}
ckfree(results);
sprintf(interp->result, "%d", numScanned);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* Tcl_SplitCmd --
*
* 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_SplitCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
char *splitChars;
register char *p, *p2;
char *elementStart;
if (argc == 2) {
splitChars = " \n\t\r";
} else if (argc == 3) {
splitChars = argv[2];
} else {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" string ?splitChars?\"", (char *) NULL);
return TCL_ERROR;
}
/*
* Handle the special case of splitting on every character.
*/
if (*splitChars == 0) {
char string[2];
string[1] = 0;
for (p = argv[1]; *p != 0; p++) {
string[0] = *p;
Tcl_AppendElement(interp, string, 0);
}
return TCL_OK;
}
/*
* Normal case: split on any of a given set of characters.
* Discard instances of the split characters.
*/
for (p = elementStart = argv[1]; *p != 0; p++) {
char c = *p;
for (p2 = splitChars; *p2 != 0; p2++) {
if (*p2 == c) {
*p = 0;
Tcl_AppendElement(interp, elementStart, 0);
*p = c;
elementStart = p+1;
break;
}
}
}
if (p != argv[1]) {
Tcl_AppendElement(interp, elementStart, 0);
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* Tcl_StringCmd --
*
* 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_StringCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
int length;
register char *p, c;
int match;
int first;
int left = 0, right = 0;
if (argc < 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" option arg ?arg ...?\"", (char *) NULL);
return TCL_ERROR;
}
c = argv[1][0];
length = strlen(argv[1]);
if ((c == 'c') && (strncmp(argv[1], "compare", length) == 0)) {
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" compare string1 string2\"", (char *) NULL);
return TCL_ERROR;
}
match = strcmp(argv[2], argv[3]);
if (match > 0) {
interp->result = "1";
} else if (match < 0) {
interp->result = "-1";
} else {
interp->result = "0";
}
return TCL_OK;
} else if ((c == 'f') && (strncmp(argv[1], "first", length) == 0)) {
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" first string1 string2\"", (char *) NULL);
return TCL_ERROR;
}
first = 1;
firstLast:
match = -1;
c = *argv[2];
length = strlen(argv[2]);
for (p = argv[3]; *p != 0; p++) {
if (*p != c) {
continue;
}
if (strncmp(argv[2], p, length) == 0) {
match = p-argv[3];
if (first) {
break;
}
}
}
sprintf(interp->result, "%d", match);
return TCL_OK;
} else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)) {
int index;
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" index string charIndex\"", (char *) NULL);
return TCL_ERROR;
}
if (Tcl_GetInt(interp, argv[3], &index) != TCL_OK) {
return TCL_ERROR;
}
if ((index >= 0) && (index < strlen(argv[2]))) {
interp->result[0] = argv[2][index];
interp->result[1] = 0;
}
return TCL_OK;
} else if ((c == 'l') && (strncmp(argv[1], "last", length) == 0)
&& (length >= 2)) {
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" last string1 string2\"", (char *) NULL);
return TCL_ERROR;
}
first = 0;
goto firstLast;
} else if ((c == 'l') && (strncmp(argv[1], "length", length) == 0)
&& (length >= 2)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" length string\"", (char *) NULL);
return TCL_ERROR;
}
sprintf(interp->result, "%d", strlen(argv[2]));
return TCL_OK;
} else if ((c == 'm') && (strncmp(argv[1], "match", length) == 0)) {
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" match pattern string\"", (char *) NULL);
return TCL_ERROR;
}
if (Tcl_StringMatch(argv[3], argv[2]) != 0) {
interp->result = "1";
} else {
interp->result = "0";
}
return TCL_OK;
} else if ((c == 'r') && (strncmp(argv[1], "range", length) == 0)) {
int first, last, stringLength;
if (argc != 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" range string first last\"", (char *) NULL);
return TCL_ERROR;
}
stringLength = strlen(argv[2]);
if (Tcl_GetInt(interp, argv[3], &first) != TCL_OK) {
return TCL_ERROR;
}
if ((*argv[4] == 'e')
&& (strncmp(argv[4], "end", strlen(argv[4])) == 0)) {
last = stringLength-1;
} else {
if (Tcl_GetInt(interp, argv[4], &last) != TCL_OK) {
Tcl_ResetResult(interp);
Tcl_AppendResult(interp,
"expected integer or \"end\" but got \"",
argv[4], "\"", (char *) NULL);
return TCL_ERROR;
}
}
if (first < 0) {
first = 0;
}
if (last >= stringLength) {
last = stringLength-1;
}
if (last >= first) {
char saved, *p;
p = argv[2] + last + 1;
saved = *p;
*p = 0;
Tcl_SetResult(interp, argv[2] + first, TCL_VOLATILE);
*p = saved;
}
return TCL_OK;
} else if ((c == 't') && (strncmp(argv[1], "tolower", length) == 0)
&& (length >= 3)) {
register char *p;
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" tolower string\"", (char *) NULL);
return TCL_ERROR;
}
Tcl_SetResult(interp, argv[2], TCL_VOLATILE);
for (p = interp->result; *p != 0; p++) {
if (isupper(*p)) {
*p = tolower(*p);
}
}
return TCL_OK;
} else if ((c == 't') && (strncmp(argv[1], "toupper", length) == 0)
&& (length >= 3)) {
register char *p;
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" toupper string\"", (char *) NULL);
return TCL_ERROR;
}
Tcl_SetResult(interp, argv[2], TCL_VOLATILE);
for (p = interp->result; *p != 0; p++) {
if (islower(*p)) {
*p = toupper(*p);
}
}
return TCL_OK;
} else if ((c == 't') && (strncmp(argv[1], "trim", length) == 0)
&& (length == 4)) {
char *trimChars;
register char *p, *checkPtr;
left = right = 1;
trim:
if (argc == 4) {
trimChars = argv[3];
} else if (argc == 3) {
trimChars = " \t\n\r";
} else {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ", argv[1], " string ?chars?\"", (char *) NULL);
return TCL_ERROR;
}
p = argv[2];
if (left) {
for (c = *p; c != 0; p++, c = *p) {
for (checkPtr = trimChars; *checkPtr != c; checkPtr++) {
if (*checkPtr == 0) {
goto doneLeft;
}
}
}
}
doneLeft:
Tcl_SetResult(interp, p, TCL_VOLATILE);
if (right) {
char *donePtr;
p = interp->result + strlen(interp->result) - 1;
donePtr = &interp->result[-1];
for (c = *p; p != donePtr; p--, c = *p) {
for (checkPtr = trimChars; *checkPtr != c; checkPtr++) {
if (*checkPtr == 0) {
goto doneRight;
}
}
}
doneRight:
p[1] = 0;
}
return TCL_OK;
} else if ((c == 't') && (strncmp(argv[1], "trimleft", length) == 0)
&& (length > 4)) {
left = 1;
argv[1] = "trimleft";
goto trim;
} else if ((c == 't') && (strncmp(argv[1], "trimright", length) == 0)
&& (length > 4)) {
right = 1;
argv[1] = "trimright";
goto trim;
} else {
Tcl_AppendResult(interp, "bad option \"", argv[1],
"\": should be compare, first, index, last, length, match, ",
"range, tolower, toupper, trim, trimleft, or trimright",
(char *) NULL);
return TCL_ERROR;
}
}
/*
*----------------------------------------------------------------------
*
* 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. */
{
char c;
int 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->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, length) == 0)) {
Tcl_UntraceVar(interp, argv[2], flags | TCL_TRACE_UNSETS,
TraceVarProc, clientData);
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, 1);
Tcl_AppendElement(interp, tvarPtr->command, 0);
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. */
{
TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
char *result;
int code, cmdLength, flags1, flags2;
Interp dummy;
#define STATIC_SIZE 199
char staticSpace[STATIC_SIZE+1];
char *cmdPtr, *p;
result = 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 = "";
}
cmdLength = tvarPtr->length + Tcl_ScanElement(name1, &flags1) +
Tcl_ScanElement(name2, &flags2) + 5;
if (cmdLength < STATIC_SIZE) {
cmdPtr = staticSpace;
} else {
cmdPtr = (char *) ckalloc((unsigned) cmdLength);
}
p = cmdPtr;
strcpy(p, tvarPtr->command);
p += tvarPtr->length;
*p = ' ';
p++;
p += Tcl_ConvertElement(name1, p, flags1);
*p = ' ';
p++;
p += Tcl_ConvertElement(name2, p, flags2);
*p = ' ';
if (flags & TCL_TRACE_READS) {
p[1] = 'r';
} else if (flags & TCL_TRACE_WRITES) {
p[1] = 'w';
} else if (flags & TCL_TRACE_UNSETS) {
p[1] = 'u';
}
p[2] = '\0';
/*
* Execute the command. Be careful to save and restore the
* result from the interpreter used for the command.
*/
dummy.freeProc = interp->freeProc;
if (interp->freeProc == 0) {
Tcl_SetResult((Tcl_Interp *) &dummy, interp->result, TCL_VOLATILE);
} else {
dummy.result = interp->result;
}
code = Tcl_Eval(interp, cmdPtr, 0, (char **) NULL);
if (cmdPtr != staticSpace) {
ckfree(cmdPtr);
}
if (code != TCL_OK) {
result = "access disallowed by trace command";
Tcl_ResetResult(interp); /* Must clear error state. */
}
Tcl_FreeResult(interp);
interp->result = dummy.result;
interp->freeProc = dummy.freeProc;
}
if (flags & TCL_TRACE_DESTROYED) {
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.
*
* 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], 0, (char **) NULL);
if (result == TCL_CONTINUE) {
result = TCL_OK;
} else if (result != TCL_OK) {
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;
}