/*
* 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-1996 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.68 96/10/12 17:05:57
*/
#include "tclInt.h"
#include "tclPort.h"
/*
* Structure used to hold information about variable traces:
*/
typedef struct {
* to be invoked. */
* or NULL. Malloc'ed. */
* 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:
*/
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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
char *dirName;
if (argc != 1) {
return TCL_ERROR;
}
return TCL_ERROR;
}
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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
int noCase = 0;
int indices = 0;
* prevent compiler warning. */
int i;
if (argc < 3) {
" ?switches? exp string ?matchVar? ?subMatchVar ",
"subMatchVar ...?\"", (char *) NULL);
return TCL_ERROR;
}
argc--;
indices = 1;
noCase = 1;
argPtr++;
argc--;
break;
} else {
"\": 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;
for (p = pattern; *p != 0; p++) {
}
}
for (p = string; *p != 0; p++) {
}
}
} else {
}
}
if (noCase) {
}
return TCL_ERROR;
}
if (match < 0) {
return TCL_ERROR;
}
if (!match) {
return TCL_OK;
}
/*
* If additional variable names have been specified, return
* index information in those variables.
*/
argc -= 2;
for (i = 0; i < argc; i++) {
if (indices) {
} else {
}
} else {
if (indices) {
} else {
*last = 0;
}
}
return TCL_ERROR;
}
}
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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
register char *src, c;
if (argc < 5) {
" ?switches? exp string subSpec varName\"", (char *) NULL);
return TCL_ERROR;
}
argc--;
while (argPtr[0][0] == '-') {
noCase = 1;
all = 1;
argPtr++;
argc--;
break;
} else {
"\": 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) {
for (p = pattern; *p != 0; p++) {
}
}
for (p = string; *p != 0; p++) {
}
}
} else {
}
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.
*/
flags = 0;
numMatches = 0;
for (p = string; *p != 0; ) {
if (match < 0) {
goto done;
}
if (!match) {
break;
}
numMatches += 1;
/*
* Copy the portion of the source string before the match to the
* result variable.
*/
c = *src;
*src = 0;
flags);
*src = c;
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.
*/
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;
*src = '\\';
src[1] = c;
goto cantSet;
}
src++;
continue;
} else {
continue;
}
} else {
continue;
}
c = *src;
*src = 0;
*src = c;
goto cantSet;
}
}
*last = 0;
goto cantSet;
}
}
if (*src == '\\') {
src++;
}
}
TCL_APPEND_VALUE) == NULL) {
goto cantSet;
}
}
if (end == p) {
/*
* Always consume at least one character of the input string
* in order to prevent infinite loops.
*/
tmp[1] = 0;
goto cantSet;
}
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)) {
goto cantSet;
}
}
done:
if (noCase) {
}
return code;
}
/*
*----------------------------------------------------------------------
*
* 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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
int new;
if (argc != 3) {
" oldName newName\"", (char *) NULL);
return TCL_ERROR;
}
"\": command doesn't exist", (char *) NULL);
return TCL_ERROR;
}
return TCL_OK;
}
"\": command already exists", (char *) NULL);
return TCL_ERROR;
}
/*
* The code below was added in 11/95 to preserve backwards compatibility
* when "tkerror" was renamed "bgerror": we guarantee that the hash
* table entries for both commands refer to a single shared Command
* structure. This code should eventually become unnecessary.
*/
srcName = "bgerror";
}
dstName = "bgerror";
}
"\": command doesn't exist", (char *) NULL);
return TCL_ERROR;
}
/*
* Prevent formation of alias loops through renaming.
*/
return TCL_ERROR;
}
/*
* The code below provides more backwards compatibility for the
* "tkerror" => "bgerror" renaming. As with the other compatibility
* code above, it should eventually be removed.
*/
/*
* The destination command is "bgerror"; create a "tkerror"
* command that shares the same Command structure.
*/
}
/*
* The source command is "bgerror": delete the hash table
* entry for "tkerror" if it exists.
*/
}
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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
int c, code;
}
}
c = argv[1][0];
code = TCL_RETURN;
code = TCL_CONTINUE;
"continue, or an integer", (char *) NULL);
return TCL_ERROR;
}
} else {
": must be -code, -errorcode, or -errorinfo",
(char *) NULL);
return TCL_ERROR;
}
}
if (argc == 1) {
}
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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
typedef struct {
* field. */
} Field;
* format string. */
* specified. */
* suppressed. */
* all results combined. */
* Malloced; NULL means not allocated
* yet. */
register char *fmt;
/*
* 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"
*/
register char *dst;
if (argc < 3) {
" 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.
*/
if (length < STATIC_SIZE) {
} else {
}
dst++;
if (*fmt != '%') {
continue;
}
fmt++;
if (*fmt == '%') {
dst++;
continue;
}
if (*fmt == '*') {
suppress = 1;
dst++;
fmt++;
} else {
suppress = 0;
}
widthSpecified = 0;
widthSpecified = 1;
dst++;
fmt++;
}
fmt++;
}
dst++;
if (suppress) {
continue;
}
if (numFields == MAX_FIELDS) {
goto done;
}
numFields++;
switch (*fmt) {
case 'd':
case 'i':
case 'o':
case 'x':
break;
case 'u':
break;
case 's':
break;
case 'c':
if (widthSpecified) {
"field width may not be specified in %c conversion";
goto done;
}
break;
case 'e':
case 'f':
case 'g':
dst[0] = 'f';
dst++;
break;
case '[':
do {
fmt++;
if (*fmt == 0) {
goto done;
}
dst++;
} while (*fmt != ']');
break;
default:
*fmt);
goto done;
}
}
*dst = 0;
"different numbers of variable names and field specifiers";
goto done;
}
/*
* Step 2:
*/
}
/*
* 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++) {
}
/*
* Step 3:
*/
/*
* Step 4:
*/
if (numScanned < numFields) {
}
case 'd':
(char *) NULL);
goto done;
}
break;
case 'u':
goto storeError;
}
break;
case 'c':
goto storeError;
}
break;
case 's':
== NULL) {
goto storeError;
}
break;
case 'f':
string);
goto storeError;
}
break;
}
}
done:
}
}
return code;
}
/*
*----------------------------------------------------------------------
*
* Tcl_SourceCmd --
*
* This procedure is invoked to process the "source" 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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
if (argc != 2) {
" fileName\"", (char *) NULL);
return TCL_ERROR;
}
}
/*
*----------------------------------------------------------------------
*
* 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
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) {
} else {
" string ?splitChars?\"", (char *) NULL);
return TCL_ERROR;
}
/*
* Handle the special case of splitting on every character.
*/
if (*splitChars == 0) {
string[1] = 0;
for (p = argv[1]; *p != 0; p++) {
string[0] = *p;
}
return TCL_OK;
}
/*
* Normal case: split on any of a given set of characters.
* Discard instances of the split characters.
*/
char c = *p;
if (*p2 == c) {
*p = 0;
*p = c;
elementStart = p+1;
break;
}
}
}
if (p != argv[1]) {
}
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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
register char *p;
if (argc < 2) {
" option arg ?arg ...?\"", (char *) NULL);
return TCL_ERROR;
}
c = argv[1][0];
if (argc != 4) {
" compare string1 string2\"", (char *) NULL);
return TCL_ERROR;
}
if (match > 0) {
} else if (match < 0) {
} else {
}
return TCL_OK;
if (argc != 4) {
" first string1 string2\"", (char *) NULL);
return TCL_ERROR;
}
first = 1;
match = -1;
c = *argv[2];
for (p = argv[3]; *p != 0; p++) {
if (*p != c) {
continue;
}
if (first) {
break;
}
}
}
return TCL_OK;
int index;
if (argc != 4) {
" index string charIndex\"", (char *) NULL);
return TCL_ERROR;
}
return TCL_ERROR;
}
}
return TCL_OK;
&& (length >= 2)) {
if (argc != 4) {
" last string1 string2\"", (char *) NULL);
return TCL_ERROR;
}
first = 0;
goto firstLast;
&& (length >= 2)) {
if (argc != 3) {
" length string\"", (char *) NULL);
return TCL_ERROR;
}
return TCL_OK;
if (argc != 4) {
" match pattern string\"", (char *) NULL);
return TCL_ERROR;
}
} else {
}
return TCL_OK;
if (argc != 5) {
" range string first last\"", (char *) NULL);
return TCL_ERROR;
}
} else {
"expected integer or \"end\" but got \"",
return TCL_ERROR;
}
}
} else {
"expected integer or \"end\" but got \"",
return TCL_ERROR;
}
}
if (first < 0) {
first = 0;
}
if (last >= stringLength) {
}
char saved, *p;
saved = *p;
*p = 0;
*p = saved;
}
return TCL_OK;
&& (length >= 3)) {
register char *p;
if (argc != 3) {
" tolower string\"", (char *) NULL);
return TCL_ERROR;
}
}
}
return TCL_OK;
&& (length >= 3)) {
register char *p;
if (argc != 3) {
" toupper string\"", (char *) NULL);
return TCL_ERROR;
}
}
}
return TCL_OK;
&& (length == 4)) {
char *trimChars;
register char *p, *checkPtr;
trim:
if (argc == 4) {
} else if (argc == 3) {
trimChars = " \t\n\r";
} else {
return TCL_ERROR;
}
p = argv[2];
if (left) {
for (c = *p; c != 0; p++, c = *p) {
if (*checkPtr == 0) {
goto doneLeft;
}
}
}
}
if (right) {
char *donePtr;
for (c = *p; p != donePtr; p--, c = *p) {
if (*checkPtr == 0) {
goto doneRight;
}
}
}
p[1] = 0;
}
return TCL_OK;
&& (length > 4)) {
left = 1;
goto trim;
&& (length > 4)) {
right = 1;
goto trim;
&& (length > 4)) {
char *string;
if (argc != 4) {
return TCL_ERROR;
}
return TCL_ERROR;
}
if (index < 0) {
index = 0;
}
goto wordendDone;
}
if (!isalnum(c) && (c != '_')) {
break;
}
}
}
return TCL_OK;
&& (length > 4)) {
char *string;
if (argc != 4) {
return TCL_ERROR;
}
return TCL_ERROR;
}
}
if (index <= 0) {
cur = 0;
goto wordstartDone;
}
if (!isalnum(c) && (c != '_')) {
break;
}
}
cur += 1;
}
return TCL_OK;
} else {
"\": should be compare, first, index, last, length, match, ",
"range, tolower, toupper, trim, trimleft, trimright, ",
"wordend, or wordstart", (char *) NULL);
return TCL_ERROR;
}
}
/*
*----------------------------------------------------------------------
*
* 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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
char c;
/*
* Parse command-line options.
*/
p = argv[i];
if (*p != '-') {
break;
}
if (length < 4) {
"\": must be -nobackslashes, -nocommands, ",
"or -novariables", (char *) NULL);
return TCL_ERROR;
}
doBackslashes = 0;
doCmds = 0;
doVars = 0;
} else {
goto badSwitch;
}
}
if (i != (argc-1)) {
" ?-nobackslashes? ?-nocommands? ?-novariables? string\"",
(char *) NULL);
return TCL_ERROR;
}
/*
* Scan through the string one character at a time, performing
* command, variable, and backslash substitutions.
*/
while (*p != 0) {
switch (*p) {
case '\\':
if (doBackslashes) {
if (p != old) {
}
c = Tcl_Backslash(p, &count);
p += count;
old = p;
} else {
p++;
}
break;
case '$':
if (doVars) {
if (p != old) {
}
return TCL_ERROR;
}
old = p;
} else {
p++;
}
break;
case '[':
if (doCmds) {
if (p != old) {
}
return code;
}
} else {
p++;
}
break;
default:
p++;
break;
}
}
if (p != old) {
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* Tcl_SwitchCmd --
*
* This procedure is invoked to process the "switch" 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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
#define EXACT 0
int body;
char *string;
char **switchArgv;
switchArgc--;
switchArgv++;
break;
} else {
"\": should be -exact, -glob, -regexp, or --",
(char *) NULL);
return TCL_ERROR;
}
switchArgc--;
switchArgv++;
}
if (switchArgc < 2) {
argv[0], " ?switches? string pattern body ... ?default body?\"",
(char *) NULL);
return TCL_ERROR;
}
string = *switchArgv;
switchArgc--;
switchArgv++;
/*
* argument, split them out again.
*/
splitArgs = 0;
if (switchArgc == 1) {
return code;
}
splitArgs = 1;
}
for (i = 0; i < switchArgc; i += 2) {
if (i == (switchArgc-1)) {
goto cleanup;
}
/*
* See if the pattern matches the string.
*/
matched = 0;
matched = 1;
} else {
switch (mode) {
case EXACT:
break;
case GLOB:
break;
case REGEXP:
if (matched < 0) {
goto cleanup;
}
break;
}
}
if (!matched) {
continue;
}
/*
* We've got a match. Find a body to execute, skipping bodies
* that are "-".
*/
if (body >= switchArgc) {
goto cleanup;
}
break;
}
}
}
goto cleanup;
}
/*
* Nothing matched: return nothing.
*/
if (splitArgs) {
ckfree((char *) switchArgv);
}
return code;
}
/*
*----------------------------------------------------------------------
*
* Tcl_TimeCmd --
*
* This procedure is invoked to process the "time" 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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
double timePer;
if (argc == 2) {
count = 1;
} else if (argc == 3) {
return TCL_ERROR;
}
} else {
" command ?count?\"", (char *) NULL);
return TCL_ERROR;
}
TclpGetTime(&start);
for (i = count ; i > 0; i--) {
}
return result;
}
}
TclpGetTime(&stop);
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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
int c;
if (argc < 2) {
return TCL_ERROR;
}
&& (length >= 2)) {
char *p;
if (argc != 5) {
return TCL_ERROR;
}
flags = 0;
for (p = argv[3] ; *p != 0; p++) {
if (*p == 'r') {
flags |= TCL_TRACE_READS;
} else if (*p == 'w') {
} else if (*p == 'u') {
} else {
goto badOps;
}
}
if (flags == 0) {
goto badOps;
}
return TCL_ERROR;
}
&& (length >= 2)) == 0) {
char *p;
if (argc != 5) {
return TCL_ERROR;
}
flags = 0;
for (p = argv[3] ; *p != 0; p++) {
if (*p == 'r') {
flags |= TCL_TRACE_READS;
} else if (*p == 'w') {
} else if (*p == 'u') {
} 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.
*/
clientData = 0;
TraceVarProc, clientData)) != 0) {
}
break;
}
}
&& (length >= 2)) {
if (argc != 3) {
return TCL_ERROR;
}
clientData = 0;
TraceVarProc, clientData)) != 0) {
p = ops;
*p = 'r';
p++;
}
*p = 'w';
p++;
}
*p = 'u';
p++;
}
*p = '\0';
prefix = " {";
}
} else {
"\": should be variable, vdelete, or vinfo",
(char *) NULL);
return TCL_ERROR;
}
return TCL_OK;
"\": 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 *
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. */
{
char *result;
int code;
}
/*
* 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.
*/
name2 = "";
}
if (flags & TCL_TRACE_READS) {
} else if (flags & TCL_TRACE_WRITES) {
} else if (flags & TCL_TRACE_UNSETS) {
}
/*
* Execute the command. Be careful to save and restore the
* result from the interpreter used for the command.
*/
} else {
}
}
}
if (flags & TCL_TRACE_DESTROYED) {
}
}
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
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
if (argc != 3) {
return TCL_ERROR;
}
while (1) {
return result;
}
if (!value) {
break;
}
}
break;
}
}
}
}
return result;
}