Parent Directory | Revision Log | Patch
revision 64 by dashley, Sun Oct 30 04:21:11 2016 UTC | revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC | |
---|---|---|
# | Line 1 | Line 1 |
/* $Header$ */ | ||
/* | ||
* tclCmdAH.c -- | ||
* | ||
* This file contains the top-level command routines for most of | ||
* the Tcl built-in commands whose names begin with the letters | ||
* A to H. | ||
* | ||
* 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. | ||
* | ||
* RCS: @(#) $Id: tclcmdah.c,v 1.1.1.1 2001/06/13 04:34:24 dtashley Exp $ | ||
*/ | ||
#include "tclInt.h" | ||
#include "tclPort.h" | ||
#include <locale.h> | ||
typedef int (StatProc)_ANSI_ARGS_((CONST char *path, struct stat *buf)); | ||
/* | ||
* Prototypes for local procedures defined in this file: | ||
*/ | ||
static int CheckAccess _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tcl_Obj *objPtr, int mode)); | ||
static int GetStatBuf _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tcl_Obj *objPtr, StatProc *statProc, | ||
struct stat *statPtr)); | ||
static char * GetTypeFromMode _ANSI_ARGS_((int mode)); | ||
static int SplitPath _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tcl_Obj *objPtr, int *argcPtr, char ***argvPtr)); | ||
static int StoreStatData _ANSI_ARGS_((Tcl_Interp *interp, | ||
char *varName, struct stat *statPtr)); | ||
static char ** StringifyObjects _ANSI_ARGS_((int objc, | ||
Tcl_Obj *CONST objv[])); | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_BreakObjCmd -- | ||
* | ||
* This procedure is invoked to process the "break" 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 "break" or the name | ||
* to which "break" was renamed: e.g., "set z break; $z" | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* See the user documentation. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
/* ARGSUSED */ | ||
int | ||
Tcl_BreakObjCmd(dummy, interp, objc, objv) | ||
ClientData dummy; /* Not used. */ | ||
Tcl_Interp *interp; /* Current interpreter. */ | ||
int objc; /* Number of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument objects. */ | ||
{ | ||
if (objc != 1) { | ||
Tcl_WrongNumArgs(interp, 1, objv, NULL); | ||
return TCL_ERROR; | ||
} | ||
return TCL_BREAK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_CaseObjCmd -- | ||
* | ||
* This procedure is invoked to process the "case" 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_CaseObjCmd(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 int i; | ||
int body, result; | ||
char *string, *arg; | ||
int caseObjc; | ||
Tcl_Obj *CONST *caseObjv; | ||
Tcl_Obj *armPtr; | ||
if (objc < 3) { | ||
Tcl_WrongNumArgs(interp, 1, objv, | ||
"string ?in? patList body ... ?default body?"); | ||
return TCL_ERROR; | ||
} | ||
string = Tcl_GetString(objv[1]); | ||
body = -1; | ||
arg = Tcl_GetString(objv[2]); | ||
if (strcmp(arg, "in") == 0) { | ||
i = 3; | ||
} else { | ||
i = 2; | ||
} | ||
caseObjc = objc - i; | ||
caseObjv = objv + i; | ||
/* | ||
* If all of the pattern/command pairs are lumped into a single | ||
* argument, split them out again. | ||
*/ | ||
if (caseObjc == 1) { | ||
Tcl_Obj **newObjv; | ||
Tcl_ListObjGetElements(interp, caseObjv[0], &caseObjc, &newObjv); | ||
caseObjv = newObjv; | ||
} | ||
for (i = 0; i < caseObjc; i += 2) { | ||
int patObjc, j; | ||
char **patObjv; | ||
char *pat; | ||
unsigned char *p; | ||
if (i == (caseObjc - 1)) { | ||
Tcl_ResetResult(interp); | ||
Tcl_AppendToObj(Tcl_GetObjResult(interp), | ||
"extra case pattern with no body", -1); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Check for special case of single pattern (no list) with | ||
* no backslash sequences. | ||
*/ | ||
pat = Tcl_GetString(caseObjv[i]); | ||
for (p = (unsigned char *) pat; *p != '\0'; p++) { | ||
if (isspace(*p) || (*p == '\\')) { /* INTL: ISO space, UCHAR */ | ||
break; | ||
} | ||
} | ||
if (*p == '\0') { | ||
if ((*pat == 'd') && (strcmp(pat, "default") == 0)) { | ||
body = i + 1; | ||
} | ||
if (Tcl_StringMatch(string, pat)) { | ||
body = i + 1; | ||
goto match; | ||
} | ||
continue; | ||
} | ||
/* | ||
* Break up pattern lists, then check each of the patterns | ||
* in the list. | ||
*/ | ||
result = Tcl_SplitList(interp, pat, &patObjc, &patObjv); | ||
if (result != TCL_OK) { | ||
return result; | ||
} | ||
for (j = 0; j < patObjc; j++) { | ||
if (Tcl_StringMatch(string, patObjv[j])) { | ||
body = i + 1; | ||
break; | ||
} | ||
} | ||
ckfree((char *) patObjv); | ||
if (j < patObjc) { | ||
break; | ||
} | ||
} | ||
match: | ||
if (body != -1) { | ||
armPtr = caseObjv[body - 1]; | ||
result = Tcl_EvalObjEx(interp, caseObjv[body], 0); | ||
if (result == TCL_ERROR) { | ||
char msg[100 + TCL_INTEGER_SPACE]; | ||
arg = Tcl_GetString(armPtr); | ||
sprintf(msg, | ||
"\n (\"%.50s\" arm line %d)", arg, | ||
interp->errorLine); | ||
Tcl_AddObjErrorInfo(interp, msg, -1); | ||
} | ||
return result; | ||
} | ||
/* | ||
* Nothing matched: return nothing. | ||
*/ | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_CatchObjCmd -- | ||
* | ||
* This object-based procedure is invoked to process the "catch" 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_CatchObjCmd(dummy, interp, objc, objv) | ||
ClientData dummy; /* Not used. */ | ||
Tcl_Interp *interp; /* Current interpreter. */ | ||
int objc; /* Number of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument objects. */ | ||
{ | ||
Tcl_Obj *varNamePtr = NULL; | ||
int result; | ||
if ((objc != 2) && (objc != 3)) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "command ?varName?"); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Save a pointer to the variable name object, if any, in case the | ||
* Tcl_EvalObj reallocates the bytecode interpreter's evaluation | ||
* stack rendering objv invalid. | ||
*/ | ||
if (objc == 3) { | ||
varNamePtr = objv[2]; | ||
} | ||
result = Tcl_EvalObjEx(interp, objv[1], 0); | ||
if (objc == 3) { | ||
if (Tcl_ObjSetVar2(interp, varNamePtr, NULL, | ||
Tcl_GetObjResult(interp), 0) == NULL) { | ||
Tcl_ResetResult(interp); | ||
Tcl_AppendToObj(Tcl_GetObjResult(interp), | ||
"couldn't save command result in variable", -1); | ||
return TCL_ERROR; | ||
} | ||
} | ||
/* | ||
* Set the interpreter's object result to an integer object holding the | ||
* integer Tcl_EvalObj result. Note that we don't bother generating a | ||
* string representation. We reset the interpreter's object result | ||
* to an unshared empty object and then set it to be an integer object. | ||
*/ | ||
Tcl_ResetResult(interp); | ||
Tcl_SetIntObj(Tcl_GetObjResult(interp), result); | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_CdObjCmd -- | ||
* | ||
* This procedure is invoked to process the "cd" 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_CdObjCmd(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 *dirName; | ||
Tcl_DString ds; | ||
int result; | ||
if (objc > 2) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "?dirName?"); | ||
return TCL_ERROR; | ||
} | ||
if (objc == 2) { | ||
dirName = Tcl_GetString(objv[1]); | ||
} else { | ||
dirName = "~"; | ||
} | ||
if (Tcl_TranslateFileName(interp, dirName, &ds) == NULL) { | ||
return TCL_ERROR; | ||
} | ||
result = Tcl_Chdir(Tcl_DStringValue(&ds)); | ||
Tcl_DStringFree(&ds); | ||
if (result != 0) { | ||
Tcl_AppendResult(interp, "couldn't change working directory to \"", | ||
dirName, "\": ", Tcl_PosixError(interp), (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_ConcatObjCmd -- | ||
* | ||
* This object-based procedure is invoked to process the "concat" 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_ConcatObjCmd(dummy, interp, objc, objv) | ||
ClientData dummy; /* Not used. */ | ||
Tcl_Interp *interp; /* Current interpreter. */ | ||
int objc; /* Number of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument objects. */ | ||
{ | ||
if (objc >= 2) { | ||
Tcl_SetObjResult(interp, Tcl_ConcatObj(objc-1, objv+1)); | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_ContinueObjCmd - | ||
* | ||
* This procedure is invoked to process the "continue" 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 "continue" or the name | ||
* to which "continue" was renamed: e.g., "set z continue; $z" | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* See the user documentation. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
/* ARGSUSED */ | ||
int | ||
Tcl_ContinueObjCmd(dummy, interp, objc, objv) | ||
ClientData dummy; /* Not used. */ | ||
Tcl_Interp *interp; /* Current interpreter. */ | ||
int objc; /* Number of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument objects. */ | ||
{ | ||
if (objc != 1) { | ||
Tcl_WrongNumArgs(interp, 1, objv, NULL); | ||
return TCL_ERROR; | ||
} | ||
return TCL_CONTINUE; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_EncodingObjCmd -- | ||
* | ||
* This command manipulates encodings. | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* See the user documentation. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_EncodingObjCmd(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, length; | ||
Tcl_Encoding encoding; | ||
char *string; | ||
Tcl_DString ds; | ||
Tcl_Obj *resultPtr; | ||
static char *optionStrings[] = { | ||
"convertfrom", "convertto", "names", "system", | ||
NULL | ||
}; | ||
enum options { | ||
ENC_CONVERTFROM, ENC_CONVERTTO, ENC_NAMES, ENC_SYSTEM | ||
}; | ||
if (objc < 2) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); | ||
return TCL_ERROR; | ||
} | ||
if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0, | ||
&index) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
switch ((enum options) index) { | ||
case ENC_CONVERTTO: | ||
case ENC_CONVERTFROM: { | ||
char *name; | ||
Tcl_Obj *data; | ||
if (objc == 3) { | ||
name = NULL; | ||
data = objv[2]; | ||
} else if (objc == 4) { | ||
name = Tcl_GetString(objv[2]); | ||
data = objv[3]; | ||
} else { | ||
Tcl_WrongNumArgs(interp, 2, objv, "?encoding? data"); | ||
return TCL_ERROR; | ||
} | ||
encoding = Tcl_GetEncoding(interp, name); | ||
if (!encoding) { | ||
return TCL_ERROR; | ||
} | ||
if ((enum options) index == ENC_CONVERTFROM) { | ||
/* | ||
* Treat the string as binary data. | ||
*/ | ||
string = (char *) Tcl_GetByteArrayFromObj(data, &length); | ||
Tcl_ExternalToUtfDString(encoding, string, length, &ds); | ||
/* | ||
* Note that we cannot use Tcl_DStringResult here because | ||
* it will truncate the string at the first null byte. | ||
*/ | ||
Tcl_SetStringObj(Tcl_GetObjResult(interp), | ||
Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); | ||
Tcl_DStringFree(&ds); | ||
} else { | ||
/* | ||
* Store the result as binary data. | ||
*/ | ||
string = Tcl_GetStringFromObj(data, &length); | ||
Tcl_UtfToExternalDString(encoding, string, length, &ds); | ||
resultPtr = Tcl_GetObjResult(interp); | ||
Tcl_SetByteArrayObj(resultPtr, | ||
(unsigned char *) Tcl_DStringValue(&ds), | ||
Tcl_DStringLength(&ds)); | ||
Tcl_DStringFree(&ds); | ||
} | ||
Tcl_FreeEncoding(encoding); | ||
break; | ||
} | ||
case ENC_NAMES: { | ||
if (objc > 2) { | ||
Tcl_WrongNumArgs(interp, 2, objv, NULL); | ||
return TCL_ERROR; | ||
} | ||
Tcl_GetEncodingNames(interp); | ||
break; | ||
} | ||
case ENC_SYSTEM: { | ||
if (objc > 3) { | ||
Tcl_WrongNumArgs(interp, 2, objv, "?encoding?"); | ||
return TCL_ERROR; | ||
} | ||
if (objc == 2) { | ||
Tcl_SetResult(interp, Tcl_GetEncodingName(NULL), TCL_STATIC); | ||
} else { | ||
return Tcl_SetSystemEncoding(interp, | ||
Tcl_GetStringFromObj(objv[2], NULL)); | ||
} | ||
break; | ||
} | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_ErrorObjCmd -- | ||
* | ||
* This procedure is invoked to process the "error" 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_ErrorObjCmd(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; | ||
char *info; | ||
int infoLen; | ||
if ((objc < 2) || (objc > 4)) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "message ?errorInfo? ?errorCode?"); | ||
return TCL_ERROR; | ||
} | ||
if (objc >= 3) { /* process the optional info argument */ | ||
info = Tcl_GetStringFromObj(objv[2], &infoLen); | ||
if (*info != 0) { | ||
Tcl_AddObjErrorInfo(interp, info, infoLen); | ||
iPtr->flags |= ERR_ALREADY_LOGGED; | ||
} | ||
} | ||
if (objc == 4) { | ||
Tcl_SetVar2Ex(interp, "errorCode", NULL, objv[3], TCL_GLOBAL_ONLY); | ||
iPtr->flags |= ERROR_CODE_SET; | ||
} | ||
Tcl_SetObjResult(interp, objv[1]); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_EvalObjCmd -- | ||
* | ||
* This object-based procedure is invoked to process the "eval" 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_EvalObjCmd(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 result; | ||
register Tcl_Obj *objPtr; | ||
if (objc < 2) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?"); | ||
return TCL_ERROR; | ||
} | ||
if (objc == 2) { | ||
result = Tcl_EvalObjEx(interp, objv[1], TCL_EVAL_DIRECT); | ||
} else { | ||
/* | ||
* More than one argument: concatenate them together with spaces | ||
* between, then evaluate the result. Tcl_EvalObjEx will delete | ||
* the object when it decrements its refcount after eval'ing it. | ||
*/ | ||
objPtr = Tcl_ConcatObj(objc-1, objv+1); | ||
result = Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_DIRECT); | ||
} | ||
if (result == TCL_ERROR) { | ||
char msg[32 + TCL_INTEGER_SPACE]; | ||
sprintf(msg, "\n (\"eval\" body line %d)", interp->errorLine); | ||
Tcl_AddObjErrorInfo(interp, msg, -1); | ||
} | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_ExitObjCmd -- | ||
* | ||
* This procedure is invoked to process the "exit" 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_ExitObjCmd(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 value; | ||
if ((objc != 1) && (objc != 2)) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?"); | ||
return TCL_ERROR; | ||
} | ||
if (objc == 1) { | ||
value = 0; | ||
} else if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
Tcl_Exit(value); | ||
/*NOTREACHED*/ | ||
return TCL_OK; /* Better not ever reach this! */ | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_ExprObjCmd -- | ||
* | ||
* This object-based procedure is invoked to process the "expr" Tcl | ||
* command. See the user documentation for details on what it does. | ||
* | ||
* With the bytecode compiler, this procedure is called in two | ||
* circumstances: 1) to execute expr commands that are too complicated | ||
* or too unsafe to try compiling directly into an inline sequence of | ||
* instructions, and 2) to execute commands where the command name is | ||
* computed at runtime and is "expr" or the name to which "expr" was | ||
* renamed (e.g., "set z expr; $z 2+3") | ||
* | ||
* Results: | ||
* A standard Tcl object result. | ||
* | ||
* Side effects: | ||
* See the user documentation. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
/* ARGSUSED */ | ||
int | ||
Tcl_ExprObjCmd(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; | ||
Tcl_Obj *resultPtr; | ||
register char *bytes; | ||
int length, i, result; | ||
if (objc < 2) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?"); | ||
return TCL_ERROR; | ||
} | ||
if (objc == 2) { | ||
result = Tcl_ExprObj(interp, objv[1], &resultPtr); | ||
if (result == TCL_OK) { | ||
Tcl_SetObjResult(interp, resultPtr); | ||
Tcl_DecrRefCount(resultPtr); /* done with the result object */ | ||
} | ||
return result; | ||
} | ||
/* | ||
* Create a new object holding the concatenated argument strings. | ||
*/ | ||
bytes = Tcl_GetStringFromObj(objv[1], &length); | ||
objPtr = Tcl_NewStringObj(bytes, length); | ||
Tcl_IncrRefCount(objPtr); | ||
for (i = 2; i < objc; i++) { | ||
Tcl_AppendToObj(objPtr, " ", 1); | ||
bytes = Tcl_GetStringFromObj(objv[i], &length); | ||
Tcl_AppendToObj(objPtr, bytes, length); | ||
} | ||
/* | ||
* Evaluate the concatenated string object. | ||
*/ | ||
result = Tcl_ExprObj(interp, objPtr, &resultPtr); | ||
if (result == TCL_OK) { | ||
Tcl_SetObjResult(interp, resultPtr); | ||
Tcl_DecrRefCount(resultPtr); /* done with the result object */ | ||
} | ||
/* | ||
* Free allocated resources. | ||
*/ | ||
Tcl_DecrRefCount(objPtr); | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_FileObjCmd -- | ||
* | ||
* This procedure is invoked to process the "file" Tcl command. | ||
* See the user documentation for details on what it does. | ||
* PLEASE NOTE THAT THIS FAILS WITH FILENAMES AND PATHS WITH | ||
* EMBEDDED NULLS, WHICH COULD THEORETICALLY HAPPEN ON A MAC. | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* See the user documentation. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
/* ARGSUSED */ | ||
int | ||
Tcl_FileObjCmd(dummy, interp, objc, objv) | ||
ClientData dummy; /* Not used. */ | ||
Tcl_Interp *interp; /* Current interpreter. */ | ||
int objc; /* Number of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument objects. */ | ||
{ | ||
Tcl_Obj *resultPtr; | ||
int index; | ||
/* | ||
* This list of constants should match the fileOption string array below. | ||
*/ | ||
static char *fileOptions[] = { | ||
"atime", "attributes", "channels", "copy", | ||
"delete", | ||
"dirname", "executable", "exists", "extension", | ||
"isdirectory", "isfile", "join", "lstat", | ||
"mtime", "mkdir", "nativename", "owned", | ||
"pathtype", "readable", "readlink", "rename", | ||
"rootname", "size", "split", "stat", | ||
"tail", "type", "volumes", "writable", | ||
(char *) NULL | ||
}; | ||
enum options { | ||
FILE_ATIME, FILE_ATTRIBUTES, FILE_CHANNELS, FILE_COPY, | ||
FILE_DELETE, | ||
FILE_DIRNAME, FILE_EXECUTABLE, FILE_EXISTS, FILE_EXTENSION, | ||
FILE_ISDIRECTORY, FILE_ISFILE, FILE_JOIN, FILE_LSTAT, | ||
FILE_MTIME, FILE_MKDIR, FILE_NATIVENAME, FILE_OWNED, | ||
FILE_PATHTYPE, FILE_READABLE, FILE_READLINK, FILE_RENAME, | ||
FILE_ROOTNAME, FILE_SIZE, FILE_SPLIT, FILE_STAT, | ||
FILE_TAIL, FILE_TYPE, FILE_VOLUMES, FILE_WRITABLE | ||
}; | ||
if (objc < 2) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); | ||
return TCL_ERROR; | ||
} | ||
if (Tcl_GetIndexFromObj(interp, objv[1], fileOptions, "option", 0, | ||
&index) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
resultPtr = Tcl_GetObjResult(interp); | ||
switch ((enum options) index) { | ||
case FILE_ATIME: { | ||
struct stat buf; | ||
char *fileName; | ||
struct utimbuf tval; | ||
if ((objc < 3) || (objc > 4)) { | ||
Tcl_WrongNumArgs(interp, 2, objv, "name ?time?"); | ||
return TCL_ERROR; | ||
} | ||
if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
if (objc == 4) { | ||
if (Tcl_GetLongFromObj(interp, objv[3], | ||
(long*)(&buf.st_atime)) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
tval.actime = buf.st_atime; | ||
tval.modtime = buf.st_mtime; | ||
fileName = Tcl_GetString(objv[2]); | ||
if (utime(fileName, &tval) != 0) { | ||
Tcl_AppendStringsToObj(resultPtr, | ||
"could not set access time for file \"", | ||
fileName, "\": ", | ||
Tcl_PosixError(interp), (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Do another stat to ensure that the we return the | ||
* new recognized atime - hopefully the same as the | ||
* one we sent in. However, fs's like FAT don't | ||
* even know what atime is. | ||
*/ | ||
if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} | ||
Tcl_SetLongObj(resultPtr, (long) buf.st_atime); | ||
return TCL_OK; | ||
} | ||
case FILE_ATTRIBUTES: { | ||
return TclFileAttrsCmd(interp, objc, objv); | ||
} | ||
case FILE_CHANNELS: { | ||
if ((objc < 2) || (objc > 3)) { | ||
Tcl_WrongNumArgs(interp, 2, objv, "?pattern?"); | ||
return TCL_ERROR; | ||
} | ||
return Tcl_GetChannelNamesEx(interp, | ||
((objc == 2) ? NULL : Tcl_GetString(objv[2]))); | ||
} | ||
case FILE_COPY: { | ||
int result; | ||
char **argv; | ||
argv = StringifyObjects(objc, objv); | ||
result = TclFileCopyCmd(interp, objc, argv); | ||
ckfree((char *) argv); | ||
return result; | ||
} | ||
case FILE_DELETE: { | ||
int result; | ||
char **argv; | ||
argv = StringifyObjects(objc, objv); | ||
result = TclFileDeleteCmd(interp, objc, argv); | ||
ckfree((char *) argv); | ||
return result; | ||
} | ||
case FILE_DIRNAME: { | ||
int argc; | ||
char **argv; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
if (SplitPath(interp, objv[2], &argc, &argv) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Return all but the last component. If there is only one | ||
* component, return it if the path was non-relative, otherwise | ||
* return the current directory. | ||
*/ | ||
if (argc > 1) { | ||
Tcl_DString ds; | ||
Tcl_DStringInit(&ds); | ||
Tcl_JoinPath(argc - 1, argv, &ds); | ||
Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&ds), | ||
Tcl_DStringLength(&ds)); | ||
Tcl_DStringFree(&ds); | ||
} else if ((argc == 0) | ||
|| (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) { | ||
Tcl_SetStringObj(resultPtr, | ||
((tclPlatform == TCL_PLATFORM_MAC) ? ":" : "."), 1); | ||
} else { | ||
Tcl_SetStringObj(resultPtr, argv[0], -1); | ||
} | ||
ckfree((char *) argv); | ||
return TCL_OK; | ||
} | ||
case FILE_EXECUTABLE: { | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
return CheckAccess(interp, objv[2], X_OK); | ||
} | ||
case FILE_EXISTS: { | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
return CheckAccess(interp, objv[2], F_OK); | ||
} | ||
case FILE_EXTENSION: { | ||
char *fileName, *extension; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
fileName = Tcl_GetString(objv[2]); | ||
extension = TclGetExtension(fileName); | ||
if (extension != NULL) { | ||
Tcl_SetStringObj(resultPtr, extension, -1); | ||
} | ||
return TCL_OK; | ||
} | ||
case FILE_ISDIRECTORY: { | ||
int value; | ||
struct stat buf; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
value = 0; | ||
if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) { | ||
value = S_ISDIR(buf.st_mode); | ||
} | ||
Tcl_SetBooleanObj(resultPtr, value); | ||
return TCL_OK; | ||
} | ||
case FILE_ISFILE: { | ||
int value; | ||
struct stat buf; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
value = 0; | ||
if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) { | ||
value = S_ISREG(buf.st_mode); | ||
} | ||
Tcl_SetBooleanObj(resultPtr, value); | ||
return TCL_OK; | ||
} | ||
case FILE_JOIN: { | ||
char **argv; | ||
Tcl_DString ds; | ||
if (objc < 3) { | ||
Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?"); | ||
return TCL_ERROR; | ||
} | ||
argv = StringifyObjects(objc - 2, objv + 2); | ||
Tcl_DStringInit(&ds); | ||
Tcl_JoinPath(objc - 2, argv, &ds); | ||
Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&ds), | ||
Tcl_DStringLength(&ds)); | ||
Tcl_DStringFree(&ds); | ||
ckfree((char *) argv); | ||
return TCL_OK; | ||
} | ||
case FILE_LSTAT: { | ||
char *varName; | ||
struct stat buf; | ||
if (objc != 4) { | ||
Tcl_WrongNumArgs(interp, 2, objv, "name varName"); | ||
return TCL_ERROR; | ||
} | ||
if (GetStatBuf(interp, objv[2], TclpLstat, &buf) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
varName = Tcl_GetString(objv[3]); | ||
return StoreStatData(interp, varName, &buf); | ||
} | ||
case FILE_MTIME: { | ||
struct stat buf; | ||
char *fileName; | ||
struct utimbuf tval; | ||
if ((objc < 3) || (objc > 4)) { | ||
Tcl_WrongNumArgs(interp, 2, objv, "name ?time?"); | ||
return TCL_ERROR; | ||
} | ||
if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
if (objc == 4) { | ||
if (Tcl_GetLongFromObj(interp, objv[3], | ||
(long*)(&buf.st_mtime)) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
tval.actime = buf.st_atime; | ||
tval.modtime = buf.st_mtime; | ||
fileName = Tcl_GetString(objv[2]); | ||
if (utime(fileName, &tval) != 0) { | ||
Tcl_AppendStringsToObj(resultPtr, | ||
"could not set modification time for file \"", | ||
fileName, "\": ", | ||
Tcl_PosixError(interp), (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Do another stat to ensure that the we return the | ||
* new recognized atime - hopefully the same as the | ||
* one we sent in. However, fs's like FAT don't | ||
* even know what atime is. | ||
*/ | ||
if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} | ||
Tcl_SetLongObj(resultPtr, (long) buf.st_mtime); | ||
return TCL_OK; | ||
} | ||
case FILE_MKDIR: { | ||
char **argv; | ||
int result; | ||
if (objc < 3) { | ||
Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?"); | ||
return TCL_ERROR; | ||
} | ||
argv = StringifyObjects(objc, objv); | ||
result = TclFileMakeDirsCmd(interp, objc, argv); | ||
ckfree((char *) argv); | ||
return result; | ||
} | ||
case FILE_NATIVENAME: { | ||
char *fileName; | ||
Tcl_DString ds; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
fileName = Tcl_GetString(objv[2]); | ||
fileName = Tcl_TranslateFileName(interp, fileName, &ds); | ||
if (fileName == NULL) { | ||
return TCL_ERROR; | ||
} | ||
Tcl_SetStringObj(resultPtr, fileName, Tcl_DStringLength(&ds)); | ||
Tcl_DStringFree(&ds); | ||
return TCL_OK; | ||
} | ||
case FILE_OWNED: { | ||
int value; | ||
struct stat buf; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
value = 0; | ||
if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) { | ||
/* | ||
* For Windows and Macintosh, there are no user ids | ||
* associated with a file, so we always return 1. | ||
*/ | ||
#if (defined(__WIN32__) || defined(MAC_TCL)) | ||
value = 1; | ||
#else | ||
value = (geteuid() == buf.st_uid); | ||
#endif | ||
} | ||
Tcl_SetBooleanObj(resultPtr, value); | ||
return TCL_OK; | ||
} | ||
case FILE_PATHTYPE: { | ||
char *fileName; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
fileName = Tcl_GetString(objv[2]); | ||
switch (Tcl_GetPathType(fileName)) { | ||
case TCL_PATH_ABSOLUTE: | ||
Tcl_SetStringObj(resultPtr, "absolute", -1); | ||
break; | ||
case TCL_PATH_RELATIVE: | ||
Tcl_SetStringObj(resultPtr, "relative", -1); | ||
break; | ||
case TCL_PATH_VOLUME_RELATIVE: | ||
Tcl_SetStringObj(resultPtr, "volumerelative", -1); | ||
break; | ||
} | ||
return TCL_OK; | ||
} | ||
case FILE_READABLE: { | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
return CheckAccess(interp, objv[2], R_OK); | ||
} | ||
case FILE_READLINK: { | ||
char *fileName, *contents; | ||
Tcl_DString name, link; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
fileName = Tcl_GetString(objv[2]); | ||
fileName = Tcl_TranslateFileName(interp, fileName, &name); | ||
if (fileName == NULL) { | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* If S_IFLNK isn't defined it means that the machine doesn't | ||
* support symbolic links, so the file can't possibly be a | ||
* symbolic link. Generate an EINVAL error, which is what | ||
* happens on machines that do support symbolic links when | ||
* you invoke readlink on a file that isn't a symbolic link. | ||
*/ | ||
#ifndef S_IFLNK | ||
contents = NULL; | ||
errno = EINVAL; | ||
#else | ||
contents = TclpReadlink(fileName, &link); | ||
#endif /* S_IFLNK */ | ||
Tcl_DStringFree(&name); | ||
if (contents == NULL) { | ||
Tcl_AppendResult(interp, "could not readlink \"", | ||
Tcl_GetString(objv[2]), "\": ", | ||
Tcl_PosixError(interp), (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
Tcl_DStringResult(interp, &link); | ||
return TCL_OK; | ||
} | ||
case FILE_RENAME: { | ||
int result; | ||
char **argv; | ||
argv = StringifyObjects(objc, objv); | ||
result = TclFileRenameCmd(interp, objc, argv); | ||
ckfree((char *) argv); | ||
return result; | ||
} | ||
case FILE_ROOTNAME: { | ||
int length; | ||
char *fileName, *extension; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
fileName = Tcl_GetStringFromObj(objv[2], &length); | ||
extension = TclGetExtension(fileName); | ||
if (extension == NULL) { | ||
Tcl_SetObjResult(interp, objv[2]); | ||
} else { | ||
Tcl_SetStringObj(resultPtr, fileName, | ||
(int) (length - strlen(extension))); | ||
} | ||
return TCL_OK; | ||
} | ||
case FILE_SIZE: { | ||
struct stat buf; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
Tcl_SetLongObj(resultPtr, (long) buf.st_size); | ||
return TCL_OK; | ||
} | ||
case FILE_SPLIT: { | ||
int i, argc; | ||
char **argv; | ||
char *fileName; | ||
Tcl_Obj *objPtr; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
fileName = Tcl_GetString(objv[2]); | ||
Tcl_SplitPath(fileName, &argc, &argv); | ||
for (i = 0; i < argc; i++) { | ||
objPtr = Tcl_NewStringObj(argv[i], -1); | ||
Tcl_ListObjAppendElement(NULL, resultPtr, objPtr); | ||
} | ||
ckfree((char *) argv); | ||
return TCL_OK; | ||
} | ||
case FILE_STAT: { | ||
char *varName; | ||
struct stat buf; | ||
if (objc != 4) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "stat name varName"); | ||
return TCL_ERROR; | ||
} | ||
if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
varName = Tcl_GetString(objv[3]); | ||
return StoreStatData(interp, varName, &buf); | ||
} | ||
case FILE_TAIL: { | ||
int argc; | ||
char **argv; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
if (SplitPath(interp, objv[2], &argc, &argv) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Return the last component, unless it is the only component, | ||
* and it is the root of an absolute path. | ||
*/ | ||
if (argc > 0) { | ||
if ((argc > 1) | ||
|| (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) { | ||
Tcl_SetStringObj(resultPtr, argv[argc - 1], -1); | ||
} | ||
} | ||
ckfree((char *) argv); | ||
return TCL_OK; | ||
} | ||
case FILE_TYPE: { | ||
struct stat buf; | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
if (GetStatBuf(interp, objv[2], TclpLstat, &buf) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
Tcl_SetStringObj(resultPtr, | ||
GetTypeFromMode((unsigned short) buf.st_mode), -1); | ||
return TCL_OK; | ||
} | ||
case FILE_VOLUMES: { | ||
if (objc != 2) { | ||
Tcl_WrongNumArgs(interp, 2, objv, NULL); | ||
return TCL_ERROR; | ||
} | ||
return TclpListVolumes(interp); | ||
} | ||
case FILE_WRITABLE: { | ||
if (objc != 3) { | ||
goto only3Args; | ||
} | ||
return CheckAccess(interp, objv[2], W_OK); | ||
} | ||
} | ||
only3Args: | ||
Tcl_WrongNumArgs(interp, 2, objv, "name"); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
*--------------------------------------------------------------------------- | ||
* | ||
* SplitPath -- | ||
* | ||
* Utility procedure used by Tcl_FileObjCmd() to split a path. | ||
* Differs from standard Tcl_SplitPath in its handling of home | ||
* directories; Tcl_SplitPath preserves the "~" while this | ||
* procedure computes the actual full path name. | ||
* | ||
* Results: | ||
* The return value is TCL_OK if the path could be split, TCL_ERROR | ||
* otherwise. If TCL_ERROR was returned, an error message is left | ||
* in interp. If TCL_OK was returned, *argvPtr is set to a newly | ||
* allocated array of strings that represent the individual | ||
* directories in the specified path, and *argcPtr is filled with | ||
* the length of that array. | ||
* | ||
* Side effects: | ||
* Memory allocated. The caller must eventually free this memory | ||
* by calling ckfree() on *argvPtr. | ||
* | ||
*--------------------------------------------------------------------------- | ||
*/ | ||
static int | ||
SplitPath(interp, objPtr, argcPtr, argvPtr) | ||
Tcl_Interp *interp; /* Interp for error return. May be NULL. */ | ||
Tcl_Obj *objPtr; /* Path to be split. */ | ||
int *argcPtr; /* Filled with length of following array. */ | ||
char ***argvPtr; /* Filled with array of strings representing | ||
* the elements of the specified path. */ | ||
{ | ||
char *fileName; | ||
fileName = Tcl_GetString(objPtr); | ||
/* | ||
* If there is only one element, and it starts with a tilde, | ||
* perform tilde substitution and resplit the path. | ||
*/ | ||
Tcl_SplitPath(fileName, argcPtr, argvPtr); | ||
if ((*argcPtr == 1) && (fileName[0] == '~')) { | ||
Tcl_DString ds; | ||
ckfree((char *) *argvPtr); | ||
fileName = Tcl_TranslateFileName(interp, fileName, &ds); | ||
if (fileName == NULL) { | ||
return TCL_ERROR; | ||
} | ||
Tcl_SplitPath(fileName, argcPtr, argvPtr); | ||
Tcl_DStringFree(&ds); | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*--------------------------------------------------------------------------- | ||
* | ||
* CheckAccess -- | ||
* | ||
* Utility procedure used by Tcl_FileObjCmd() to query file | ||
* attributes available through the access() system call. | ||
* | ||
* Results: | ||
* Always returns TCL_OK. Sets interp's result to boolean true or | ||
* false depending on whether the file has the specified attribute. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*--------------------------------------------------------------------------- | ||
*/ | ||
static int | ||
CheckAccess(interp, objPtr, mode) | ||
Tcl_Interp *interp; /* Interp for status return. Must not be | ||
* NULL. */ | ||
Tcl_Obj *objPtr; /* Name of file to check. */ | ||
int mode; /* Attribute to check; passed as argument to | ||
* access(). */ | ||
{ | ||
int value; | ||
char *fileName; | ||
Tcl_DString ds; | ||
fileName = Tcl_GetString(objPtr); | ||
fileName = Tcl_TranslateFileName(interp, fileName, &ds); | ||
if (fileName == NULL) { | ||
value = 0; | ||
} else { | ||
value = (TclAccess(fileName, mode) == 0); | ||
Tcl_DStringFree(&ds); | ||
} | ||
Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value); | ||
return TCL_OK; | ||
} | ||
/* | ||
*--------------------------------------------------------------------------- | ||
* | ||
* GetStatBuf -- | ||
* | ||
* Utility procedure used by Tcl_FileObjCmd() to query file | ||
* attributes available through the stat() or lstat() system call. | ||
* | ||
* Results: | ||
* The return value is TCL_OK if the specified file exists and can | ||
* be stat'ed, TCL_ERROR otherwise. If TCL_ERROR is returned, an | ||
* error message is left in interp's result. If TCL_OK is returned, | ||
* *statPtr is filled with information about the specified file. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*--------------------------------------------------------------------------- | ||
*/ | ||
static int | ||
GetStatBuf(interp, objPtr, statProc, statPtr) | ||
Tcl_Interp *interp; /* Interp for error return. May be NULL. */ | ||
Tcl_Obj *objPtr; /* Path name to examine. */ | ||
StatProc *statProc; /* Either stat() or lstat() depending on | ||
* desired behavior. */ | ||
struct stat *statPtr; /* Filled with info about file obtained by | ||
* calling (*statProc)(). */ | ||
{ | ||
char *fileName; | ||
Tcl_DString ds; | ||
int status; | ||
fileName = Tcl_GetString(objPtr); | ||
fileName = Tcl_TranslateFileName(interp, fileName, &ds); | ||
if (fileName == NULL) { | ||
return TCL_ERROR; | ||
} | ||
status = (*statProc)(Tcl_DStringValue(&ds), statPtr); | ||
Tcl_DStringFree(&ds); | ||
if (status < 0) { | ||
if (interp != NULL) { | ||
Tcl_AppendResult(interp, "could not read \"", | ||
Tcl_GetString(objPtr), "\": ", | ||
Tcl_PosixError(interp), (char *) NULL); | ||
} | ||
return TCL_ERROR; | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* StoreStatData -- | ||
* | ||
* This is a utility procedure that breaks out the fields of a | ||
* "stat" structure and stores them in textual form into the | ||
* elements of an associative array. | ||
* | ||
* Results: | ||
* Returns a standard Tcl return value. If an error occurs then | ||
* a message is left in interp's result. | ||
* | ||
* Side effects: | ||
* Elements of the associative array given by "varName" are modified. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static int | ||
StoreStatData(interp, varName, statPtr) | ||
Tcl_Interp *interp; /* Interpreter for error reports. */ | ||
char *varName; /* Name of associative array variable | ||
* in which to store stat results. */ | ||
struct stat *statPtr; /* Pointer to buffer containing | ||
* stat data to store in varName. */ | ||
{ | ||
char string[TCL_INTEGER_SPACE]; | ||
TclFormatInt(string, (long) statPtr->st_dev); | ||
if (Tcl_SetVar2(interp, varName, "dev", string, TCL_LEAVE_ERR_MSG) | ||
== NULL) { | ||
return TCL_ERROR; | ||
} | ||
TclFormatInt(string, (long) statPtr->st_ino); | ||
if (Tcl_SetVar2(interp, varName, "ino", string, TCL_LEAVE_ERR_MSG) | ||
== NULL) { | ||
return TCL_ERROR; | ||
} | ||
TclFormatInt(string, (unsigned short) statPtr->st_mode); | ||
if (Tcl_SetVar2(interp, varName, "mode", string, TCL_LEAVE_ERR_MSG) | ||
== NULL) { | ||
return TCL_ERROR; | ||
} | ||
TclFormatInt(string, (long) statPtr->st_nlink); | ||
if (Tcl_SetVar2(interp, varName, "nlink", string, TCL_LEAVE_ERR_MSG) | ||
== NULL) { | ||
return TCL_ERROR; | ||
} | ||
TclFormatInt(string, (long) statPtr->st_uid); | ||
if (Tcl_SetVar2(interp, varName, "uid", string, TCL_LEAVE_ERR_MSG) | ||
== NULL) { | ||
return TCL_ERROR; | ||
} | ||
TclFormatInt(string, (long) statPtr->st_gid); | ||
if (Tcl_SetVar2(interp, varName, "gid", string, TCL_LEAVE_ERR_MSG) | ||
== NULL) { | ||
return TCL_ERROR; | ||
} | ||
sprintf(string, "%lu", (unsigned long) statPtr->st_size); | ||
if (Tcl_SetVar2(interp, varName, "size", string, TCL_LEAVE_ERR_MSG) | ||
== NULL) { | ||
return TCL_ERROR; | ||
} | ||
TclFormatInt(string, (long) statPtr->st_atime); | ||
if (Tcl_SetVar2(interp, varName, "atime", string, TCL_LEAVE_ERR_MSG) | ||
== NULL) { | ||
return TCL_ERROR; | ||
} | ||
TclFormatInt(string, (long) statPtr->st_mtime); | ||
if (Tcl_SetVar2(interp, varName, "mtime", string, TCL_LEAVE_ERR_MSG) | ||
== NULL) { | ||
return TCL_ERROR; | ||
} | ||
TclFormatInt(string, (long) statPtr->st_ctime); | ||
if (Tcl_SetVar2(interp, varName, "ctime", string, TCL_LEAVE_ERR_MSG) | ||
== NULL) { | ||
return TCL_ERROR; | ||
} | ||
if (Tcl_SetVar2(interp, varName, "type", | ||
GetTypeFromMode((unsigned short) statPtr->st_mode), | ||
TCL_LEAVE_ERR_MSG) == NULL) { | ||
return TCL_ERROR; | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* GetTypeFromMode -- | ||
* | ||
* Given a mode word, returns a string identifying the type of a | ||
* file. | ||
* | ||
* Results: | ||
* A static text string giving the file type from mode. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static char * | ||
GetTypeFromMode(mode) | ||
int mode; | ||
{ | ||
if (S_ISREG(mode)) { | ||
return "file"; | ||
} else if (S_ISDIR(mode)) { | ||
return "directory"; | ||
} else if (S_ISCHR(mode)) { | ||
return "characterSpecial"; | ||
} else if (S_ISBLK(mode)) { | ||
return "blockSpecial"; | ||
} else if (S_ISFIFO(mode)) { | ||
return "fifo"; | ||
#ifdef S_ISLNK | ||
} else if (S_ISLNK(mode)) { | ||
return "link"; | ||
#endif | ||
#ifdef S_ISSOCK | ||
} else if (S_ISSOCK(mode)) { | ||
return "socket"; | ||
#endif | ||
} | ||
return "unknown"; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_ForObjCmd -- | ||
* | ||
* This procedure is invoked to process the "for" 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 "for" or the name | ||
* to which "for" was renamed: e.g., | ||
* "set z for; $z {set i 0} {$i<100} {incr i} {puts $i}" | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* See the user documentation. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
/* ARGSUSED */ | ||
int | ||
Tcl_ForObjCmd(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 result, value; | ||
if (objc != 5) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "start test next command"); | ||
return TCL_ERROR; | ||
} | ||
result = Tcl_EvalObjEx(interp, objv[1], 0); | ||
if (result != TCL_OK) { | ||
if (result == TCL_ERROR) { | ||
Tcl_AddErrorInfo(interp, "\n (\"for\" initial command)"); | ||
} | ||
return result; | ||
} | ||
while (1) { | ||
/* | ||
* We need to reset the result before passing it off to | ||
* Tcl_ExprBooleanObj. Otherwise, any error message will be appended | ||
* to the result of the last evaluation. | ||
*/ | ||
Tcl_ResetResult(interp); | ||
result = Tcl_ExprBooleanObj(interp, objv[2], &value); | ||
if (result != TCL_OK) { | ||
return result; | ||
} | ||
if (!value) { | ||
break; | ||
} | ||
result = Tcl_EvalObjEx(interp, objv[4], 0); | ||
if ((result != TCL_OK) && (result != TCL_CONTINUE)) { | ||
if (result == TCL_ERROR) { | ||
char msg[32 + TCL_INTEGER_SPACE]; | ||
sprintf(msg, "\n (\"for\" body line %d)",interp->errorLine); | ||
Tcl_AddErrorInfo(interp, msg); | ||
} | ||
break; | ||
} | ||
result = Tcl_EvalObjEx(interp, objv[3], 0); | ||
if (result == TCL_BREAK) { | ||
break; | ||
} else if (result != TCL_OK) { | ||
if (result == TCL_ERROR) { | ||
Tcl_AddErrorInfo(interp, "\n (\"for\" loop-end command)"); | ||
} | ||
return result; | ||
} | ||
} | ||
if (result == TCL_BREAK) { | ||
result = TCL_OK; | ||
} | ||
if (result == TCL_OK) { | ||
Tcl_ResetResult(interp); | ||
} | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_ForeachObjCmd -- | ||
* | ||
* This object-based procedure is invoked to process the "foreach" 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_ForeachObjCmd(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 result = TCL_OK; | ||
int i; /* i selects a value list */ | ||
int j, maxj; /* Number of loop iterations */ | ||
int v; /* v selects a loop variable */ | ||
int numLists; /* Count of value lists */ | ||
Tcl_Obj *bodyPtr; | ||
/* | ||
* We copy the argument object pointers into a local array to avoid | ||
* the problem that "objv" might become invalid. It is a pointer into | ||
* the evaluation stack and that stack might be grown and reallocated | ||
* if the loop body requires a large amount of stack space. | ||
*/ | ||
#define NUM_ARGS 9 | ||
Tcl_Obj *(argObjStorage[NUM_ARGS]); | ||
Tcl_Obj **argObjv = argObjStorage; | ||
#define STATIC_LIST_SIZE 4 | ||
int indexArray[STATIC_LIST_SIZE]; /* Array of value list indices */ | ||
int varcListArray[STATIC_LIST_SIZE]; /* # loop variables per list */ | ||
Tcl_Obj **varvListArray[STATIC_LIST_SIZE]; /* Array of var name lists */ | ||
int argcListArray[STATIC_LIST_SIZE]; /* Array of value list sizes */ | ||
Tcl_Obj **argvListArray[STATIC_LIST_SIZE]; /* Array of value lists */ | ||
int *index = indexArray; | ||
int *varcList = varcListArray; | ||
Tcl_Obj ***varvList = varvListArray; | ||
int *argcList = argcListArray; | ||
Tcl_Obj ***argvList = argvListArray; | ||
if (objc < 4 || (objc%2 != 0)) { | ||
Tcl_WrongNumArgs(interp, 1, objv, | ||
"varList list ?varList list ...? command"); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Create the object argument array "argObjv". Make sure argObjv is | ||
* large enough to hold the objc arguments. | ||
*/ | ||
if (objc > NUM_ARGS) { | ||
argObjv = (Tcl_Obj **) ckalloc(objc * sizeof(Tcl_Obj *)); | ||
} | ||
for (i = 0; i < objc; i++) { | ||
argObjv[i] = objv[i]; | ||
} | ||
/* | ||
* Manage numList parallel value lists. | ||
* argvList[i] is a value list counted by argcList[i] | ||
* varvList[i] is the list of variables associated with the value list | ||
* varcList[i] is the number of variables associated with the value list | ||
* index[i] is the current pointer into the value list argvList[i] | ||
*/ | ||
numLists = (objc-2)/2; | ||
if (numLists > STATIC_LIST_SIZE) { | ||
index = (int *) ckalloc(numLists * sizeof(int)); | ||
varcList = (int *) ckalloc(numLists * sizeof(int)); | ||
varvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **)); | ||
argcList = (int *) ckalloc(numLists * sizeof(int)); | ||
argvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **)); | ||
} | ||
for (i = 0; i < numLists; i++) { | ||
index[i] = 0; | ||
varcList[i] = 0; | ||
varvList[i] = (Tcl_Obj **) NULL; | ||
argcList[i] = 0; | ||
argvList[i] = (Tcl_Obj **) NULL; | ||
} | ||
/* | ||
* Break up the value lists and variable lists into elements | ||
*/ | ||
maxj = 0; | ||
for (i = 0; i < numLists; i++) { | ||
result = Tcl_ListObjGetElements(interp, argObjv[1+i*2], | ||
&varcList[i], &varvList[i]); | ||
if (result != TCL_OK) { | ||
goto done; | ||
} | ||
if (varcList[i] < 1) { | ||
Tcl_AppendToObj(Tcl_GetObjResult(interp), | ||
"foreach varlist is empty", -1); | ||
result = TCL_ERROR; | ||
goto done; | ||
} | ||
result = Tcl_ListObjGetElements(interp, argObjv[2+i*2], | ||
&argcList[i], &argvList[i]); | ||
if (result != TCL_OK) { | ||
goto done; | ||
} | ||
j = argcList[i] / varcList[i]; | ||
if ((argcList[i] % varcList[i]) != 0) { | ||
j++; | ||
} | ||
if (j > maxj) { | ||
maxj = j; | ||
} | ||
} | ||
/* | ||
* Iterate maxj times through the lists in parallel | ||
* If some value lists run out of values, set loop vars to "" | ||
*/ | ||
bodyPtr = argObjv[objc-1]; | ||
for (j = 0; j < maxj; j++) { | ||
for (i = 0; i < numLists; i++) { | ||
/* | ||
* If a variable or value list object has been converted to | ||
* another kind of Tcl object, convert it back to a list object | ||
* and refetch the pointer to its element array. | ||
*/ | ||
if (argObjv[1+i*2]->typePtr != &tclListType) { | ||
result = Tcl_ListObjGetElements(interp, argObjv[1+i*2], | ||
&varcList[i], &varvList[i]); | ||
if (result != TCL_OK) { | ||
panic("Tcl_ForeachObjCmd: could not reconvert variable list %d to a list object\n", i); | ||
} | ||
} | ||
if (argObjv[2+i*2]->typePtr != &tclListType) { | ||
result = Tcl_ListObjGetElements(interp, argObjv[2+i*2], | ||
&argcList[i], &argvList[i]); | ||
if (result != TCL_OK) { | ||
panic("Tcl_ForeachObjCmd: could not reconvert value list %d to a list object\n", i); | ||
} | ||
} | ||
for (v = 0; v < varcList[i]; v++) { | ||
int k = index[i]++; | ||
Tcl_Obj *valuePtr, *varValuePtr; | ||
int isEmptyObj = 0; | ||
if (k < argcList[i]) { | ||
valuePtr = argvList[i][k]; | ||
} else { | ||
valuePtr = Tcl_NewObj(); /* empty string */ | ||
isEmptyObj = 1; | ||
} | ||
varValuePtr = Tcl_ObjSetVar2(interp, varvList[i][v], | ||
NULL, valuePtr, 0); | ||
if (varValuePtr == NULL) { | ||
if (isEmptyObj) { | ||
Tcl_DecrRefCount(valuePtr); | ||
} | ||
Tcl_ResetResult(interp); | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"couldn't set loop variable: \"", | ||
Tcl_GetString(varvList[i][v]), "\"", (char *) NULL); | ||
result = TCL_ERROR; | ||
goto done; | ||
} | ||
} | ||
} | ||
result = Tcl_EvalObjEx(interp, bodyPtr, 0); | ||
if (result != TCL_OK) { | ||
if (result == TCL_CONTINUE) { | ||
result = TCL_OK; | ||
} else if (result == TCL_BREAK) { | ||
result = TCL_OK; | ||
break; | ||
} else if (result == TCL_ERROR) { | ||
char msg[32 + TCL_INTEGER_SPACE]; | ||
sprintf(msg, "\n (\"foreach\" body line %d)", | ||
interp->errorLine); | ||
Tcl_AddObjErrorInfo(interp, msg, -1); | ||
break; | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
if (result == TCL_OK) { | ||
Tcl_ResetResult(interp); | ||
} | ||
done: | ||
if (numLists > STATIC_LIST_SIZE) { | ||
ckfree((char *) index); | ||
ckfree((char *) varcList); | ||
ckfree((char *) argcList); | ||
ckfree((char *) varvList); | ||
ckfree((char *) argvList); | ||
} | ||
if (argObjv != argObjStorage) { | ||
ckfree((char *) argObjv); | ||
} | ||
return result; | ||
#undef STATIC_LIST_SIZE | ||
#undef NUM_ARGS | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_FormatObjCmd -- | ||
* | ||
* This procedure is invoked to process the "format" 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_FormatObjCmd(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 *format; /* Used to read characters from the format | ||
* string. */ | ||
int formatLen; /* The length of the format string */ | ||
char *endPtr; /* Points to the last char in format array */ | ||
char newFormat[40]; /* A new format specifier is generated here. */ | ||
int width; /* Field width from field specifier, or 0 if | ||
* no width given. */ | ||
int precision; /* Field precision from field specifier, or 0 | ||
* if no precision given. */ | ||
int size; /* Number of bytes needed for result of | ||
* conversion, based on type of conversion | ||
* ("e", "s", etc.), width, and precision. */ | ||
int intValue; /* Used to hold value to pass to sprintf, if | ||
* it's a one-word integer or char value */ | ||
char *ptrValue = NULL; /* Used to hold value to pass to sprintf, if | ||
* it's a one-word value. */ | ||
double doubleValue; /* Used to hold value to pass to sprintf if | ||
* it's a double value. */ | ||
int whichValue; /* Indicates which of intValue, ptrValue, | ||
* or doubleValue has the value to pass to | ||
* sprintf, according to the following | ||
* definitions: */ | ||
# define INT_VALUE 0 | ||
# define CHAR_VALUE 1 | ||
# define PTR_VALUE 2 | ||
# define DOUBLE_VALUE 3 | ||
# define STRING_VALUE 4 | ||
# define MAX_FLOAT_SIZE 320 | ||
Tcl_Obj *resultPtr; /* Where result is stored finally. */ | ||
char staticBuf[MAX_FLOAT_SIZE + 1]; | ||
/* A static buffer to copy the format results | ||
* into */ | ||
char *dst = staticBuf; /* The buffer that sprintf writes into each | ||
* time the format processes a specifier */ | ||
int dstSize = MAX_FLOAT_SIZE; | ||
/* The size of the dst buffer */ | ||
int noPercent; /* Special case for speed: indicates there's | ||
* no field specifier, just a string to copy.*/ | ||
int objIndex; /* Index of argument to substitute next. */ | ||
int gotXpg = 0; /* Non-zero means that an XPG3 %n$-style | ||
* specifier has been seen. */ | ||
int gotSequential = 0; /* Non-zero means that a regular sequential | ||
* (non-XPG3) conversion specifier has been | ||
* seen. */ | ||
int useShort; /* Value to be printed is short (half word). */ | ||
char *end; /* Used to locate end of numerical fields. */ | ||
int stringLen = 0; /* Length of string in characters rather | ||
* than bytes. Used for %s substitution. */ | ||
int gotMinus; /* Non-zero indicates that a minus flag has | ||
* been seen in the current field. */ | ||
int gotPrecision; /* Non-zero indicates that a precision has | ||
* been set for the current field. */ | ||
int gotZero; /* Non-zero indicates that a zero flag has | ||
* been seen in the current field. */ | ||
/* | ||
* This procedure is a bit nasty. The goal is to use sprintf to | ||
* do most of the dirty work. There are several problems: | ||
* 1. this procedure can't trust its arguments. | ||
* 2. we must be able to provide a large enough result area to hold | ||
* whatever's generated. This is hard to estimate. | ||
* 3. there's no way to move the arguments from objv to the call | ||
* to sprintf in a reasonable way. This is particularly nasty | ||
* because some of the arguments may be two-word values (doubles). | ||
* So, what happens here is to scan the format string one % group | ||
* at a time, making many individual calls to sprintf. | ||
*/ | ||
if (objc < 2) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "formatString ?arg arg ...?"); | ||
return TCL_ERROR; | ||
} | ||
format = (char *) Tcl_GetStringFromObj(objv[1], &formatLen); | ||
endPtr = format + formatLen; | ||
resultPtr = Tcl_NewObj(); | ||
objIndex = 2; | ||
while (format < endPtr) { | ||
register char *newPtr = newFormat; | ||
width = precision = noPercent = useShort = 0; | ||
gotZero = gotMinus = gotPrecision = 0; | ||
whichValue = PTR_VALUE; | ||
/* | ||
* Get rid of any characters before the next field specifier. | ||
*/ | ||
if (*format != '%') { | ||
ptrValue = format; | ||
while ((*format != '%') && (format < endPtr)) { | ||
format++; | ||
} | ||
size = format - ptrValue; | ||
noPercent = 1; | ||
goto doField; | ||
} | ||
if (format[1] == '%') { | ||
ptrValue = format; | ||
size = 1; | ||
noPercent = 1; | ||
format += 2; | ||
goto doField; | ||
} | ||
/* | ||
* Parse off a field specifier, compute how many characters | ||
* will be needed to store the result, and substitute for | ||
* "*" size specifiers. | ||
*/ | ||
*newPtr = '%'; | ||
newPtr++; | ||
format++; | ||
if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */ | ||
int tmp; | ||
/* | ||
* Check for an XPG3-style %n$ specification. Note: there | ||
* must not be a mixture of XPG3 specs and non-XPG3 specs | ||
* in the same format string. | ||
*/ | ||
tmp = strtoul(format, &end, 10); /* INTL: "C" locale. */ | ||
if (*end != '$') { | ||
goto notXpg; | ||
} | ||
format = end+1; | ||
gotXpg = 1; | ||
if (gotSequential) { | ||
goto mixedXPG; | ||
} | ||
objIndex = tmp+1; | ||
if ((objIndex < 2) || (objIndex >= objc)) { | ||
goto badIndex; | ||
} | ||
goto xpgCheckDone; | ||
} | ||
notXpg: | ||
gotSequential = 1; | ||
if (gotXpg) { | ||
goto mixedXPG; | ||
} | ||
xpgCheckDone: | ||
while ((*format == '-') || (*format == '#') || (*format == '0') | ||
|| (*format == ' ') || (*format == '+')) { | ||
if (*format == '-') { | ||
gotMinus = 1; | ||
} | ||
if (*format == '0') { | ||
/* | ||
* This will be handled by sprintf for numbers, but we | ||
* need to do the char/string ones ourselves | ||
*/ | ||
gotZero = 1; | ||
} | ||
*newPtr = *format; | ||
newPtr++; | ||
format++; | ||
} | ||
if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */ | ||
width = strtoul(format, &end, 10); /* INTL: Tcl source. */ | ||
format = end; | ||
} else if (*format == '*') { | ||
if (objIndex >= objc) { | ||
goto badIndex; | ||
} | ||
if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */ | ||
objv[objIndex], &width) != TCL_OK) { | ||
goto fmtError; | ||
} | ||
if (width < 0) { | ||
width = -width; | ||
*newPtr = '-'; | ||
gotMinus = 1; | ||
newPtr++; | ||
} | ||
objIndex++; | ||
format++; | ||
} | ||
if (width > 100000) { | ||
/* | ||
* Don't allow arbitrarily large widths: could cause core | ||
* dump when we try to allocate a zillion bytes of memory | ||
* below. | ||
*/ | ||
width = 100000; | ||
} else if (width < 0) { | ||
width = 0; | ||
} | ||
if (width != 0) { | ||
TclFormatInt(newPtr, width); /* INTL: printf format. */ | ||
while (*newPtr != 0) { | ||
newPtr++; | ||
} | ||
} | ||
if (*format == '.') { | ||
*newPtr = '.'; | ||
newPtr++; | ||
format++; | ||
gotPrecision = 1; | ||
} | ||
if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */ | ||
precision = strtoul(format, &end, 10); /* INTL: "C" locale. */ | ||
format = end; | ||
} else if (*format == '*') { | ||
if (objIndex >= objc) { | ||
goto badIndex; | ||
} | ||
if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */ | ||
objv[objIndex], &precision) != TCL_OK) { | ||
goto fmtError; | ||
} | ||
objIndex++; | ||
format++; | ||
} | ||
if (gotPrecision) { | ||
TclFormatInt(newPtr, precision); /* INTL: printf format. */ | ||
while (*newPtr != 0) { | ||
newPtr++; | ||
} | ||
} | ||
if (*format == 'l') { | ||
format++; | ||
} else if (*format == 'h') { | ||
useShort = 1; | ||
*newPtr = 'h'; | ||
newPtr++; | ||
format++; | ||
} | ||
*newPtr = *format; | ||
newPtr++; | ||
*newPtr = 0; | ||
if (objIndex >= objc) { | ||
goto badIndex; | ||
} | ||
switch (*format) { | ||
case 'i': | ||
newPtr[-1] = 'd'; | ||
case 'd': | ||
case 'o': | ||
case 'u': | ||
case 'x': | ||
case 'X': | ||
if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */ | ||
objv[objIndex], &intValue) != TCL_OK) { | ||
goto fmtError; | ||
} | ||
whichValue = INT_VALUE; | ||
size = 40 + precision; | ||
break; | ||
case 's': | ||
/* | ||
* Compute the length of the string in characters and add | ||
* any additional space required by the field width. All of | ||
* the extra characters will be spaces, so one byte per | ||
* character is adequate. | ||
*/ | ||
whichValue = STRING_VALUE; | ||
ptrValue = Tcl_GetStringFromObj(objv[objIndex], &size); | ||
stringLen = Tcl_NumUtfChars(ptrValue, size); | ||
if (gotPrecision && (precision < stringLen)) { | ||
stringLen = precision; | ||
} | ||
size = Tcl_UtfAtIndex(ptrValue, stringLen) - ptrValue; | ||
if (width > stringLen) { | ||
size += (width - stringLen); | ||
} | ||
break; | ||
case 'c': | ||
if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */ | ||
objv[objIndex], &intValue) != TCL_OK) { | ||
goto fmtError; | ||
} | ||
whichValue = CHAR_VALUE; | ||
size = width + TCL_UTF_MAX; | ||
break; | ||
case 'e': | ||
case 'E': | ||
case 'f': | ||
case 'g': | ||
case 'G': | ||
if (Tcl_GetDoubleFromObj(interp, /* INTL: Tcl source. */ | ||
objv[objIndex], &doubleValue) != TCL_OK) { | ||
goto fmtError; | ||
} | ||
whichValue = DOUBLE_VALUE; | ||
size = MAX_FLOAT_SIZE; | ||
if (precision > 10) { | ||
size += precision; | ||
} | ||
break; | ||
case 0: | ||
Tcl_SetResult(interp, | ||
"format string ended in middle of field specifier", | ||
TCL_STATIC); | ||
goto fmtError; | ||
default: { | ||
char buf[40]; | ||
sprintf(buf, "bad field specifier \"%c\"", *format); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
goto fmtError; | ||
} | ||
} | ||
objIndex++; | ||
format++; | ||
/* | ||
* Make sure that there's enough space to hold the formatted | ||
* result, then format it. | ||
*/ | ||
doField: | ||
if (width > size) { | ||
size = width; | ||
} | ||
if (noPercent) { | ||
Tcl_AppendToObj(resultPtr, ptrValue, size); | ||
} else { | ||
if (size > dstSize) { | ||
if (dst != staticBuf) { | ||
ckfree(dst); | ||
} | ||
dst = (char *) ckalloc((unsigned) (size + 1)); | ||
dstSize = size; | ||
} | ||
switch (whichValue) { | ||
case DOUBLE_VALUE: { | ||
sprintf(dst, newFormat, doubleValue); /* INTL: user locale. */ | ||
break; | ||
} | ||
case INT_VALUE: { | ||
if (useShort) { | ||
sprintf(dst, newFormat, (short) intValue); | ||
} else { | ||
sprintf(dst, newFormat, intValue); | ||
} | ||
break; | ||
} | ||
case CHAR_VALUE: { | ||
char *ptr; | ||
char padChar = (gotZero ? '0' : ' '); | ||
ptr = dst; | ||
if (!gotMinus) { | ||
for ( ; --width > 0; ptr++) { | ||
*ptr = padChar; | ||
} | ||
} | ||
ptr += Tcl_UniCharToUtf(intValue, ptr); | ||
for ( ; --width > 0; ptr++) { | ||
*ptr = padChar; | ||
} | ||
*ptr = '\0'; | ||
break; | ||
} | ||
case STRING_VALUE: { | ||
char *ptr; | ||
char padChar = (gotZero ? '0' : ' '); | ||
int pad; | ||
ptr = dst; | ||
if (width > stringLen) { | ||
pad = width - stringLen; | ||
} else { | ||
pad = 0; | ||
} | ||
if (!gotMinus) { | ||
while (pad > 0) { | ||
*ptr++ = padChar; | ||
pad--; | ||
} | ||
} | ||
size = Tcl_UtfAtIndex(ptrValue, stringLen) - ptrValue; | ||
if (size) { | ||
memcpy(ptr, ptrValue, (size_t) size); | ||
ptr += size; | ||
} | ||
while (pad > 0) { | ||
*ptr++ = padChar; | ||
pad--; | ||
} | ||
*ptr = '\0'; | ||
break; | ||
} | ||
default: { | ||
sprintf(dst, newFormat, ptrValue); | ||
break; | ||
} | ||
} | ||
Tcl_AppendToObj(resultPtr, dst, -1); | ||
} | ||
} | ||
Tcl_SetObjResult(interp, resultPtr); | ||
if(dst != staticBuf) { | ||
ckfree(dst); | ||
} | ||
return TCL_OK; | ||
mixedXPG: | ||
Tcl_SetResult(interp, | ||
"cannot mix \"%\" and \"%n$\" conversion specifiers", TCL_STATIC); | ||
goto fmtError; | ||
badIndex: | ||
if (gotXpg) { | ||
Tcl_SetResult(interp, | ||
"\"%n$\" argument index out of range", TCL_STATIC); | ||
} else { | ||
Tcl_SetResult(interp, | ||
"not enough arguments for all format specifiers", TCL_STATIC); | ||
} | ||
fmtError: | ||
if(dst != staticBuf) { | ||
ckfree(dst); | ||
} | ||
Tcl_DecrRefCount(resultPtr); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
*--------------------------------------------------------------------------- | ||
* | ||
* StringifyObjects -- | ||
* | ||
* Helper function to bridge the gap between an object-based procedure | ||
* and an older string-based procedure. | ||
* | ||
* Given an array of objects, allocate an array that consists of the | ||
* string representations of those objects. | ||
* | ||
* Results: | ||
* The return value is a pointer to the newly allocated array of | ||
* strings. Elements 0 to (objc-1) of the string array point to the | ||
* string representation of the corresponding element in the source | ||
* object array; element objc of the string array is NULL. | ||
* | ||
* Side effects: | ||
* Memory allocated. The caller must eventually free this memory | ||
* by calling ckfree() on the return value. | ||
* | ||
*--------------------------------------------------------------------------- | ||
*/ | ||
static char ** | ||
StringifyObjects(objc, objv) | ||
int objc; /* Number of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument objects. */ | ||
{ | ||
int i; | ||
char **argv; | ||
argv = (char **) ckalloc((objc + 1) * sizeof(char *)); | ||
for (i = 0; i < objc; i++) { | ||
argv[i] = Tcl_GetString(objv[i]); | ||
} | ||
argv[i] = NULL; | ||
return argv; | ||
} | ||
/* $History: tclcmdah.c $ | ||
* | ||
* ***************** Version 1 ***************** | ||
* User: Dtashley Date: 1/02/01 Time: 1:28a | ||
* Created in $/IjuScripter, IjuConsole/Source/Tcl Base | ||
* Initial check-in. | ||
*/ | ||
/* End of TCLCMDAH.C */ | ||
1 | /* $Header$ */ | |
2 | /* | |
3 | * tclCmdAH.c -- | |
4 | * | |
5 | * This file contains the top-level command routines for most of | |
6 | * the Tcl built-in commands whose names begin with the letters | |
7 | * A to H. | |
8 | * | |
9 | * Copyright (c) 1987-1993 The Regents of the University of California. | |
10 | * Copyright (c) 1994-1997 Sun Microsystems, Inc. | |
11 | * | |
12 | * See the file "license.terms" for information on usage and redistribution | |
13 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. | |
14 | * | |
15 | * RCS: @(#) $Id: tclcmdah.c,v 1.1.1.1 2001/06/13 04:34:24 dtashley Exp $ | |
16 | */ | |
17 | ||
18 | #include "tclInt.h" | |
19 | #include "tclPort.h" | |
20 | #include <locale.h> | |
21 | ||
22 | typedef int (StatProc)_ANSI_ARGS_((CONST char *path, struct stat *buf)); | |
23 | ||
24 | /* | |
25 | * Prototypes for local procedures defined in this file: | |
26 | */ | |
27 | ||
28 | static int CheckAccess _ANSI_ARGS_((Tcl_Interp *interp, | |
29 | Tcl_Obj *objPtr, int mode)); | |
30 | static int GetStatBuf _ANSI_ARGS_((Tcl_Interp *interp, | |
31 | Tcl_Obj *objPtr, StatProc *statProc, | |
32 | struct stat *statPtr)); | |
33 | static char * GetTypeFromMode _ANSI_ARGS_((int mode)); | |
34 | static int SplitPath _ANSI_ARGS_((Tcl_Interp *interp, | |
35 | Tcl_Obj *objPtr, int *argcPtr, char ***argvPtr)); | |
36 | static int StoreStatData _ANSI_ARGS_((Tcl_Interp *interp, | |
37 | char *varName, struct stat *statPtr)); | |
38 | static char ** StringifyObjects _ANSI_ARGS_((int objc, | |
39 | Tcl_Obj *CONST objv[])); | |
40 | ||
41 | /* | |
42 | *---------------------------------------------------------------------- | |
43 | * | |
44 | * Tcl_BreakObjCmd -- | |
45 | * | |
46 | * This procedure is invoked to process the "break" Tcl command. | |
47 | * See the user documentation for details on what it does. | |
48 | * | |
49 | * With the bytecode compiler, this procedure is only called when | |
50 | * a command name is computed at runtime, and is "break" or the name | |
51 | * to which "break" was renamed: e.g., "set z break; $z" | |
52 | * | |
53 | * Results: | |
54 | * A standard Tcl result. | |
55 | * | |
56 | * Side effects: | |
57 | * See the user documentation. | |
58 | * | |
59 | *---------------------------------------------------------------------- | |
60 | */ | |
61 | ||
62 | /* ARGSUSED */ | |
63 | int | |
64 | Tcl_BreakObjCmd(dummy, interp, objc, objv) | |
65 | ClientData dummy; /* Not used. */ | |
66 | Tcl_Interp *interp; /* Current interpreter. */ | |
67 | int objc; /* Number of arguments. */ | |
68 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
69 | { | |
70 | if (objc != 1) { | |
71 | Tcl_WrongNumArgs(interp, 1, objv, NULL); | |
72 | return TCL_ERROR; | |
73 | } | |
74 | return TCL_BREAK; | |
75 | } | |
76 | ||
77 | /* | |
78 | *---------------------------------------------------------------------- | |
79 | * | |
80 | * Tcl_CaseObjCmd -- | |
81 | * | |
82 | * This procedure is invoked to process the "case" Tcl command. | |
83 | * See the user documentation for details on what it does. | |
84 | * | |
85 | * Results: | |
86 | * A standard Tcl object result. | |
87 | * | |
88 | * Side effects: | |
89 | * See the user documentation. | |
90 | * | |
91 | *---------------------------------------------------------------------- | |
92 | */ | |
93 | ||
94 | /* ARGSUSED */ | |
95 | int | |
96 | Tcl_CaseObjCmd(dummy, interp, objc, objv) | |
97 | ClientData dummy; /* Not used. */ | |
98 | Tcl_Interp *interp; /* Current interpreter. */ | |
99 | int objc; /* Number of arguments. */ | |
100 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
101 | { | |
102 | register int i; | |
103 | int body, result; | |
104 | char *string, *arg; | |
105 | int caseObjc; | |
106 | Tcl_Obj *CONST *caseObjv; | |
107 | Tcl_Obj *armPtr; | |
108 | ||
109 | if (objc < 3) { | |
110 | Tcl_WrongNumArgs(interp, 1, objv, | |
111 | "string ?in? patList body ... ?default body?"); | |
112 | return TCL_ERROR; | |
113 | } | |
114 | ||
115 | string = Tcl_GetString(objv[1]); | |
116 | body = -1; | |
117 | ||
118 | arg = Tcl_GetString(objv[2]); | |
119 | if (strcmp(arg, "in") == 0) { | |
120 | i = 3; | |
121 | } else { | |
122 | i = 2; | |
123 | } | |
124 | caseObjc = objc - i; | |
125 | caseObjv = objv + i; | |
126 | ||
127 | /* | |
128 | * If all of the pattern/command pairs are lumped into a single | |
129 | * argument, split them out again. | |
130 | */ | |
131 | ||
132 | if (caseObjc == 1) { | |
133 | Tcl_Obj **newObjv; | |
134 | ||
135 | Tcl_ListObjGetElements(interp, caseObjv[0], &caseObjc, &newObjv); | |
136 | caseObjv = newObjv; | |
137 | } | |
138 | ||
139 | for (i = 0; i < caseObjc; i += 2) { | |
140 | int patObjc, j; | |
141 | char **patObjv; | |
142 | char *pat; | |
143 | unsigned char *p; | |
144 | ||
145 | if (i == (caseObjc - 1)) { | |
146 | Tcl_ResetResult(interp); | |
147 | Tcl_AppendToObj(Tcl_GetObjResult(interp), | |
148 | "extra case pattern with no body", -1); | |
149 | return TCL_ERROR; | |
150 | } | |
151 | ||
152 | /* | |
153 | * Check for special case of single pattern (no list) with | |
154 | * no backslash sequences. | |
155 | */ | |
156 | ||
157 | pat = Tcl_GetString(caseObjv[i]); | |
158 | for (p = (unsigned char *) pat; *p != '\0'; p++) { | |
159 | if (isspace(*p) || (*p == '\\')) { /* INTL: ISO space, UCHAR */ | |
160 | break; | |
161 | } | |
162 | } | |
163 | if (*p == '\0') { | |
164 | if ((*pat == 'd') && (strcmp(pat, "default") == 0)) { | |
165 | body = i + 1; | |
166 | } | |
167 | if (Tcl_StringMatch(string, pat)) { | |
168 | body = i + 1; | |
169 | goto match; | |
170 | } | |
171 | continue; | |
172 | } | |
173 | ||
174 | ||
175 | /* | |
176 | * Break up pattern lists, then check each of the patterns | |
177 | * in the list. | |
178 | */ | |
179 | ||
180 | result = Tcl_SplitList(interp, pat, &patObjc, &patObjv); | |
181 | if (result != TCL_OK) { | |
182 | return result; | |
183 | } | |
184 | for (j = 0; j < patObjc; j++) { | |
185 | if (Tcl_StringMatch(string, patObjv[j])) { | |
186 | body = i + 1; | |
187 | break; | |
188 | } | |
189 | } | |
190 | ckfree((char *) patObjv); | |
191 | if (j < patObjc) { | |
192 | break; | |
193 | } | |
194 | } | |
195 | ||
196 | match: | |
197 | if (body != -1) { | |
198 | armPtr = caseObjv[body - 1]; | |
199 | result = Tcl_EvalObjEx(interp, caseObjv[body], 0); | |
200 | if (result == TCL_ERROR) { | |
201 | char msg[100 + TCL_INTEGER_SPACE]; | |
202 | ||
203 | arg = Tcl_GetString(armPtr); | |
204 | sprintf(msg, | |
205 | "\n (\"%.50s\" arm line %d)", arg, | |
206 | interp->errorLine); | |
207 | Tcl_AddObjErrorInfo(interp, msg, -1); | |
208 | } | |
209 | return result; | |
210 | } | |
211 | ||
212 | /* | |
213 | * Nothing matched: return nothing. | |
214 | */ | |
215 | ||
216 | return TCL_OK; | |
217 | } | |
218 | ||
219 | /* | |
220 | *---------------------------------------------------------------------- | |
221 | * | |
222 | * Tcl_CatchObjCmd -- | |
223 | * | |
224 | * This object-based procedure is invoked to process the "catch" Tcl | |
225 | * command. See the user documentation for details on what it does. | |
226 | * | |
227 | * Results: | |
228 | * A standard Tcl object result. | |
229 | * | |
230 | * Side effects: | |
231 | * See the user documentation. | |
232 | * | |
233 | *---------------------------------------------------------------------- | |
234 | */ | |
235 | ||
236 | /* ARGSUSED */ | |
237 | int | |
238 | Tcl_CatchObjCmd(dummy, interp, objc, objv) | |
239 | ClientData dummy; /* Not used. */ | |
240 | Tcl_Interp *interp; /* Current interpreter. */ | |
241 | int objc; /* Number of arguments. */ | |
242 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
243 | { | |
244 | Tcl_Obj *varNamePtr = NULL; | |
245 | int result; | |
246 | ||
247 | if ((objc != 2) && (objc != 3)) { | |
248 | Tcl_WrongNumArgs(interp, 1, objv, "command ?varName?"); | |
249 | return TCL_ERROR; | |
250 | } | |
251 | ||
252 | /* | |
253 | * Save a pointer to the variable name object, if any, in case the | |
254 | * Tcl_EvalObj reallocates the bytecode interpreter's evaluation | |
255 | * stack rendering objv invalid. | |
256 | */ | |
257 | ||
258 | if (objc == 3) { | |
259 | varNamePtr = objv[2]; | |
260 | } | |
261 | ||
262 | result = Tcl_EvalObjEx(interp, objv[1], 0); | |
263 | ||
264 | if (objc == 3) { | |
265 | if (Tcl_ObjSetVar2(interp, varNamePtr, NULL, | |
266 | Tcl_GetObjResult(interp), 0) == NULL) { | |
267 | Tcl_ResetResult(interp); | |
268 | Tcl_AppendToObj(Tcl_GetObjResult(interp), | |
269 | "couldn't save command result in variable", -1); | |
270 | return TCL_ERROR; | |
271 | } | |
272 | } | |
273 | ||
274 | /* | |
275 | * Set the interpreter's object result to an integer object holding the | |
276 | * integer Tcl_EvalObj result. Note that we don't bother generating a | |
277 | * string representation. We reset the interpreter's object result | |
278 | * to an unshared empty object and then set it to be an integer object. | |
279 | */ | |
280 | ||
281 | Tcl_ResetResult(interp); | |
282 | Tcl_SetIntObj(Tcl_GetObjResult(interp), result); | |
283 | return TCL_OK; | |
284 | } | |
285 | ||
286 | /* | |
287 | *---------------------------------------------------------------------- | |
288 | * | |
289 | * Tcl_CdObjCmd -- | |
290 | * | |
291 | * This procedure is invoked to process the "cd" Tcl command. | |
292 | * See the user documentation for details on what it does. | |
293 | * | |
294 | * Results: | |
295 | * A standard Tcl result. | |
296 | * | |
297 | * Side effects: | |
298 | * See the user documentation. | |
299 | * | |
300 | *---------------------------------------------------------------------- | |
301 | */ | |
302 | ||
303 | /* ARGSUSED */ | |
304 | int | |
305 | Tcl_CdObjCmd(dummy, interp, objc, objv) | |
306 | ClientData dummy; /* Not used. */ | |
307 | Tcl_Interp *interp; /* Current interpreter. */ | |
308 | int objc; /* Number of arguments. */ | |
309 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
310 | { | |
311 | char *dirName; | |
312 | Tcl_DString ds; | |
313 | int result; | |
314 | ||
315 | if (objc > 2) { | |
316 | Tcl_WrongNumArgs(interp, 1, objv, "?dirName?"); | |
317 | return TCL_ERROR; | |
318 | } | |
319 | ||
320 | if (objc == 2) { | |
321 | dirName = Tcl_GetString(objv[1]); | |
322 | } else { | |
323 | dirName = "~"; | |
324 | } | |
325 | if (Tcl_TranslateFileName(interp, dirName, &ds) == NULL) { | |
326 | return TCL_ERROR; | |
327 | } | |
328 | ||
329 | result = Tcl_Chdir(Tcl_DStringValue(&ds)); | |
330 | Tcl_DStringFree(&ds); | |
331 | ||
332 | if (result != 0) { | |
333 | Tcl_AppendResult(interp, "couldn't change working directory to \"", | |
334 | dirName, "\": ", Tcl_PosixError(interp), (char *) NULL); | |
335 | return TCL_ERROR; | |
336 | } | |
337 | return TCL_OK; | |
338 | } | |
339 | ||
340 | /* | |
341 | *---------------------------------------------------------------------- | |
342 | * | |
343 | * Tcl_ConcatObjCmd -- | |
344 | * | |
345 | * This object-based procedure is invoked to process the "concat" Tcl | |
346 | * command. See the user documentation for details on what it does. | |
347 | * | |
348 | * Results: | |
349 | * A standard Tcl object result. | |
350 | * | |
351 | * Side effects: | |
352 | * See the user documentation. | |
353 | * | |
354 | *---------------------------------------------------------------------- | |
355 | */ | |
356 | ||
357 | /* ARGSUSED */ | |
358 | int | |
359 | Tcl_ConcatObjCmd(dummy, interp, objc, objv) | |
360 | ClientData dummy; /* Not used. */ | |
361 | Tcl_Interp *interp; /* Current interpreter. */ | |
362 | int objc; /* Number of arguments. */ | |
363 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
364 | { | |
365 | if (objc >= 2) { | |
366 | Tcl_SetObjResult(interp, Tcl_ConcatObj(objc-1, objv+1)); | |
367 | } | |
368 | return TCL_OK; | |
369 | } | |
370 | ||
371 | /* | |
372 | *---------------------------------------------------------------------- | |
373 | * | |
374 | * Tcl_ContinueObjCmd - | |
375 | * | |
376 | * This procedure is invoked to process the "continue" Tcl command. | |
377 | * See the user documentation for details on what it does. | |
378 | * | |
379 | * With the bytecode compiler, this procedure is only called when | |
380 | * a command name is computed at runtime, and is "continue" or the name | |
381 | * to which "continue" was renamed: e.g., "set z continue; $z" | |
382 | * | |
383 | * Results: | |
384 | * A standard Tcl result. | |
385 | * | |
386 | * Side effects: | |
387 | * See the user documentation. | |
388 | * | |
389 | *---------------------------------------------------------------------- | |
390 | */ | |
391 | ||
392 | /* ARGSUSED */ | |
393 | int | |
394 | Tcl_ContinueObjCmd(dummy, interp, objc, objv) | |
395 | ClientData dummy; /* Not used. */ | |
396 | Tcl_Interp *interp; /* Current interpreter. */ | |
397 | int objc; /* Number of arguments. */ | |
398 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
399 | { | |
400 | if (objc != 1) { | |
401 | Tcl_WrongNumArgs(interp, 1, objv, NULL); | |
402 | return TCL_ERROR; | |
403 | } | |
404 | return TCL_CONTINUE; | |
405 | } | |
406 | ||
407 | /* | |
408 | *---------------------------------------------------------------------- | |
409 | * | |
410 | * Tcl_EncodingObjCmd -- | |
411 | * | |
412 | * This command manipulates encodings. | |
413 | * | |
414 | * Results: | |
415 | * A standard Tcl result. | |
416 | * | |
417 | * Side effects: | |
418 | * See the user documentation. | |
419 | * | |
420 | *---------------------------------------------------------------------- | |
421 | */ | |
422 | ||
423 | int | |
424 | Tcl_EncodingObjCmd(dummy, interp, objc, objv) | |
425 | ClientData dummy; /* Not used. */ | |
426 | Tcl_Interp *interp; /* Current interpreter. */ | |
427 | int objc; /* Number of arguments. */ | |
428 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
429 | { | |
430 | int index, length; | |
431 | Tcl_Encoding encoding; | |
432 | char *string; | |
433 | Tcl_DString ds; | |
434 | Tcl_Obj *resultPtr; | |
435 | ||
436 | static char *optionStrings[] = { | |
437 | "convertfrom", "convertto", "names", "system", | |
438 | NULL | |
439 | }; | |
440 | enum options { | |
441 | ENC_CONVERTFROM, ENC_CONVERTTO, ENC_NAMES, ENC_SYSTEM | |
442 | }; | |
443 | ||
444 | if (objc < 2) { | |
445 | Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); | |
446 | return TCL_ERROR; | |
447 | } | |
448 | if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0, | |
449 | &index) != TCL_OK) { | |
450 | return TCL_ERROR; | |
451 | } | |
452 | ||
453 | switch ((enum options) index) { | |
454 | case ENC_CONVERTTO: | |
455 | case ENC_CONVERTFROM: { | |
456 | char *name; | |
457 | Tcl_Obj *data; | |
458 | if (objc == 3) { | |
459 | name = NULL; | |
460 | data = objv[2]; | |
461 | } else if (objc == 4) { | |
462 | name = Tcl_GetString(objv[2]); | |
463 | data = objv[3]; | |
464 | } else { | |
465 | Tcl_WrongNumArgs(interp, 2, objv, "?encoding? data"); | |
466 | return TCL_ERROR; | |
467 | } | |
468 | ||
469 | encoding = Tcl_GetEncoding(interp, name); | |
470 | if (!encoding) { | |
471 | return TCL_ERROR; | |
472 | } | |
473 | ||
474 | if ((enum options) index == ENC_CONVERTFROM) { | |
475 | /* | |
476 | * Treat the string as binary data. | |
477 | */ | |
478 | ||
479 | string = (char *) Tcl_GetByteArrayFromObj(data, &length); | |
480 | Tcl_ExternalToUtfDString(encoding, string, length, &ds); | |
481 | ||
482 | /* | |
483 | * Note that we cannot use Tcl_DStringResult here because | |
484 | * it will truncate the string at the first null byte. | |
485 | */ | |
486 | ||
487 | Tcl_SetStringObj(Tcl_GetObjResult(interp), | |
488 | Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); | |
489 | Tcl_DStringFree(&ds); | |
490 | } else { | |
491 | /* | |
492 | * Store the result as binary data. | |
493 | */ | |
494 | ||
495 | string = Tcl_GetStringFromObj(data, &length); | |
496 | Tcl_UtfToExternalDString(encoding, string, length, &ds); | |
497 | resultPtr = Tcl_GetObjResult(interp); | |
498 | Tcl_SetByteArrayObj(resultPtr, | |
499 | (unsigned char *) Tcl_DStringValue(&ds), | |
500 | Tcl_DStringLength(&ds)); | |
501 | Tcl_DStringFree(&ds); | |
502 | } | |
503 | ||
504 | Tcl_FreeEncoding(encoding); | |
505 | break; | |
506 | } | |
507 | case ENC_NAMES: { | |
508 | if (objc > 2) { | |
509 | Tcl_WrongNumArgs(interp, 2, objv, NULL); | |
510 | return TCL_ERROR; | |
511 | } | |
512 | Tcl_GetEncodingNames(interp); | |
513 | break; | |
514 | } | |
515 | case ENC_SYSTEM: { | |
516 | if (objc > 3) { | |
517 | Tcl_WrongNumArgs(interp, 2, objv, "?encoding?"); | |
518 | return TCL_ERROR; | |
519 | } | |
520 | if (objc == 2) { | |
521 | Tcl_SetResult(interp, Tcl_GetEncodingName(NULL), TCL_STATIC); | |
522 | } else { | |
523 | return Tcl_SetSystemEncoding(interp, | |
524 | Tcl_GetStringFromObj(objv[2], NULL)); | |
525 | } | |
526 | break; | |
527 | } | |
528 | } | |
529 | return TCL_OK; | |
530 | } | |
531 | ||
532 | /* | |
533 | *---------------------------------------------------------------------- | |
534 | * | |
535 | * Tcl_ErrorObjCmd -- | |
536 | * | |
537 | * This procedure is invoked to process the "error" Tcl command. | |
538 | * See the user documentation for details on what it does. | |
539 | * | |
540 | * Results: | |
541 | * A standard Tcl object result. | |
542 | * | |
543 | * Side effects: | |
544 | * See the user documentation. | |
545 | * | |
546 | *---------------------------------------------------------------------- | |
547 | */ | |
548 | ||
549 | /* ARGSUSED */ | |
550 | int | |
551 | Tcl_ErrorObjCmd(dummy, interp, objc, objv) | |
552 | ClientData dummy; /* Not used. */ | |
553 | Tcl_Interp *interp; /* Current interpreter. */ | |
554 | int objc; /* Number of arguments. */ | |
555 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
556 | { | |
557 | Interp *iPtr = (Interp *) interp; | |
558 | char *info; | |
559 | int infoLen; | |
560 | ||
561 | if ((objc < 2) || (objc > 4)) { | |
562 | Tcl_WrongNumArgs(interp, 1, objv, "message ?errorInfo? ?errorCode?"); | |
563 | return TCL_ERROR; | |
564 | } | |
565 | ||
566 | if (objc >= 3) { /* process the optional info argument */ | |
567 | info = Tcl_GetStringFromObj(objv[2], &infoLen); | |
568 | if (*info != 0) { | |
569 | Tcl_AddObjErrorInfo(interp, info, infoLen); | |
570 | iPtr->flags |= ERR_ALREADY_LOGGED; | |
571 | } | |
572 | } | |
573 | ||
574 | if (objc == 4) { | |
575 | Tcl_SetVar2Ex(interp, "errorCode", NULL, objv[3], TCL_GLOBAL_ONLY); | |
576 | iPtr->flags |= ERROR_CODE_SET; | |
577 | } | |
578 | ||
579 | Tcl_SetObjResult(interp, objv[1]); | |
580 | return TCL_ERROR; | |
581 | } | |
582 | ||
583 | /* | |
584 | *---------------------------------------------------------------------- | |
585 | * | |
586 | * Tcl_EvalObjCmd -- | |
587 | * | |
588 | * This object-based procedure is invoked to process the "eval" Tcl | |
589 | * command. See the user documentation for details on what it does. | |
590 | * | |
591 | * Results: | |
592 | * A standard Tcl object result. | |
593 | * | |
594 | * Side effects: | |
595 | * See the user documentation. | |
596 | * | |
597 | *---------------------------------------------------------------------- | |
598 | */ | |
599 | ||
600 | /* ARGSUSED */ | |
601 | int | |
602 | Tcl_EvalObjCmd(dummy, interp, objc, objv) | |
603 | ClientData dummy; /* Not used. */ | |
604 | Tcl_Interp *interp; /* Current interpreter. */ | |
605 | int objc; /* Number of arguments. */ | |
606 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
607 | { | |
608 | int result; | |
609 | register Tcl_Obj *objPtr; | |
610 | ||
611 | if (objc < 2) { | |
612 | Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?"); | |
613 | return TCL_ERROR; | |
614 | } | |
615 | ||
616 | if (objc == 2) { | |
617 | result = Tcl_EvalObjEx(interp, objv[1], TCL_EVAL_DIRECT); | |
618 | } else { | |
619 | /* | |
620 | * More than one argument: concatenate them together with spaces | |
621 | * between, then evaluate the result. Tcl_EvalObjEx will delete | |
622 | * the object when it decrements its refcount after eval'ing it. | |
623 | */ | |
624 | objPtr = Tcl_ConcatObj(objc-1, objv+1); | |
625 | result = Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_DIRECT); | |
626 | } | |
627 | if (result == TCL_ERROR) { | |
628 | char msg[32 + TCL_INTEGER_SPACE]; | |
629 | ||
630 | sprintf(msg, "\n (\"eval\" body line %d)", interp->errorLine); | |
631 | Tcl_AddObjErrorInfo(interp, msg, -1); | |
632 | } | |
633 | return result; | |
634 | } | |
635 | ||
636 | /* | |
637 | *---------------------------------------------------------------------- | |
638 | * | |
639 | * Tcl_ExitObjCmd -- | |
640 | * | |
641 | * This procedure is invoked to process the "exit" Tcl command. | |
642 | * See the user documentation for details on what it does. | |
643 | * | |
644 | * Results: | |
645 | * A standard Tcl object result. | |
646 | * | |
647 | * Side effects: | |
648 | * See the user documentation. | |
649 | * | |
650 | *---------------------------------------------------------------------- | |
651 | */ | |
652 | ||
653 | /* ARGSUSED */ | |
654 | int | |
655 | Tcl_ExitObjCmd(dummy, interp, objc, objv) | |
656 | ClientData dummy; /* Not used. */ | |
657 | Tcl_Interp *interp; /* Current interpreter. */ | |
658 | int objc; /* Number of arguments. */ | |
659 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
660 | { | |
661 | int value; | |
662 | ||
663 | if ((objc != 1) && (objc != 2)) { | |
664 | Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?"); | |
665 | return TCL_ERROR; | |
666 | } | |
667 | ||
668 | if (objc == 1) { | |
669 | value = 0; | |
670 | } else if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) { | |
671 | return TCL_ERROR; | |
672 | } | |
673 | Tcl_Exit(value); | |
674 | /*NOTREACHED*/ | |
675 | return TCL_OK; /* Better not ever reach this! */ | |
676 | } | |
677 | ||
678 | /* | |
679 | *---------------------------------------------------------------------- | |
680 | * | |
681 | * Tcl_ExprObjCmd -- | |
682 | * | |
683 | * This object-based procedure is invoked to process the "expr" Tcl | |
684 | * command. See the user documentation for details on what it does. | |
685 | * | |
686 | * With the bytecode compiler, this procedure is called in two | |
687 | * circumstances: 1) to execute expr commands that are too complicated | |
688 | * or too unsafe to try compiling directly into an inline sequence of | |
689 | * instructions, and 2) to execute commands where the command name is | |
690 | * computed at runtime and is "expr" or the name to which "expr" was | |
691 | * renamed (e.g., "set z expr; $z 2+3") | |
692 | * | |
693 | * Results: | |
694 | * A standard Tcl object result. | |
695 | * | |
696 | * Side effects: | |
697 | * See the user documentation. | |
698 | * | |
699 | *---------------------------------------------------------------------- | |
700 | */ | |
701 | ||
702 | /* ARGSUSED */ | |
703 | int | |
704 | Tcl_ExprObjCmd(dummy, interp, objc, objv) | |
705 | ClientData dummy; /* Not used. */ | |
706 | Tcl_Interp *interp; /* Current interpreter. */ | |
707 | int objc; /* Number of arguments. */ | |
708 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
709 | { | |
710 | register Tcl_Obj *objPtr; | |
711 | Tcl_Obj *resultPtr; | |
712 | register char *bytes; | |
713 | int length, i, result; | |
714 | ||
715 | if (objc < 2) { | |
716 | Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?"); | |
717 | return TCL_ERROR; | |
718 | } | |
719 | ||
720 | if (objc == 2) { | |
721 | result = Tcl_ExprObj(interp, objv[1], &resultPtr); | |
722 | if (result == TCL_OK) { | |
723 | Tcl_SetObjResult(interp, resultPtr); | |
724 | Tcl_DecrRefCount(resultPtr); /* done with the result object */ | |
725 | } | |
726 | return result; | |
727 | } | |
728 | ||
729 | /* | |
730 | * Create a new object holding the concatenated argument strings. | |
731 | */ | |
732 | ||
733 | bytes = Tcl_GetStringFromObj(objv[1], &length); | |
734 | objPtr = Tcl_NewStringObj(bytes, length); | |
735 | Tcl_IncrRefCount(objPtr); | |
736 | for (i = 2; i < objc; i++) { | |
737 | Tcl_AppendToObj(objPtr, " ", 1); | |
738 | bytes = Tcl_GetStringFromObj(objv[i], &length); | |
739 | Tcl_AppendToObj(objPtr, bytes, length); | |
740 | } | |
741 | ||
742 | /* | |
743 | * Evaluate the concatenated string object. | |
744 | */ | |
745 | ||
746 | result = Tcl_ExprObj(interp, objPtr, &resultPtr); | |
747 | if (result == TCL_OK) { | |
748 | Tcl_SetObjResult(interp, resultPtr); | |
749 | Tcl_DecrRefCount(resultPtr); /* done with the result object */ | |
750 | } | |
751 | ||
752 | /* | |
753 | * Free allocated resources. | |
754 | */ | |
755 | ||
756 | Tcl_DecrRefCount(objPtr); | |
757 | return result; | |
758 | } | |
759 | ||
760 | /* | |
761 | *---------------------------------------------------------------------- | |
762 | * | |
763 | * Tcl_FileObjCmd -- | |
764 | * | |
765 | * This procedure is invoked to process the "file" Tcl command. | |
766 | * See the user documentation for details on what it does. | |
767 | * PLEASE NOTE THAT THIS FAILS WITH FILENAMES AND PATHS WITH | |
768 | * EMBEDDED NULLS, WHICH COULD THEORETICALLY HAPPEN ON A MAC. | |
769 | * | |
770 | * Results: | |
771 | * A standard Tcl result. | |
772 | * | |
773 | * Side effects: | |
774 | * See the user documentation. | |
775 | * | |
776 | *---------------------------------------------------------------------- | |
777 | */ | |
778 | ||
779 | /* ARGSUSED */ | |
780 | int | |
781 | Tcl_FileObjCmd(dummy, interp, objc, objv) | |
782 | ClientData dummy; /* Not used. */ | |
783 | Tcl_Interp *interp; /* Current interpreter. */ | |
784 | int objc; /* Number of arguments. */ | |
785 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
786 | { | |
787 | Tcl_Obj *resultPtr; | |
788 | int index; | |
789 | ||
790 | /* | |
791 | * This list of constants should match the fileOption string array below. | |
792 | */ | |
793 | ||
794 | static char *fileOptions[] = { | |
795 | "atime", "attributes", "channels", "copy", | |
796 | "delete", | |
797 | "dirname", "executable", "exists", "extension", | |
798 | "isdirectory", "isfile", "join", "lstat", | |
799 | "mtime", "mkdir", "nativename", "owned", | |
800 | "pathtype", "readable", "readlink", "rename", | |
801 | "rootname", "size", "split", "stat", | |
802 | "tail", "type", "volumes", "writable", | |
803 | (char *) NULL | |
804 | }; | |
805 | enum options { | |
806 | FILE_ATIME, FILE_ATTRIBUTES, FILE_CHANNELS, FILE_COPY, | |
807 | FILE_DELETE, | |
808 | FILE_DIRNAME, FILE_EXECUTABLE, FILE_EXISTS, FILE_EXTENSION, | |
809 | FILE_ISDIRECTORY, FILE_ISFILE, FILE_JOIN, FILE_LSTAT, | |
810 | FILE_MTIME, FILE_MKDIR, FILE_NATIVENAME, FILE_OWNED, | |
811 | FILE_PATHTYPE, FILE_READABLE, FILE_READLINK, FILE_RENAME, | |
812 | FILE_ROOTNAME, FILE_SIZE, FILE_SPLIT, FILE_STAT, | |
813 | FILE_TAIL, FILE_TYPE, FILE_VOLUMES, FILE_WRITABLE | |
814 | }; | |
815 | ||
816 | if (objc < 2) { | |
817 | Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); | |
818 | return TCL_ERROR; | |
819 | } | |
820 | if (Tcl_GetIndexFromObj(interp, objv[1], fileOptions, "option", 0, | |
821 | &index) != TCL_OK) { | |
822 | return TCL_ERROR; | |
823 | } | |
824 | ||
825 | resultPtr = Tcl_GetObjResult(interp); | |
826 | switch ((enum options) index) { | |
827 | case FILE_ATIME: { | |
828 | struct stat buf; | |
829 | char *fileName; | |
830 | struct utimbuf tval; | |
831 | ||
832 | if ((objc < 3) || (objc > 4)) { | |
833 | Tcl_WrongNumArgs(interp, 2, objv, "name ?time?"); | |
834 | return TCL_ERROR; | |
835 | } | |
836 | if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | |
837 | return TCL_ERROR; | |
838 | } | |
839 | if (objc == 4) { | |
840 | if (Tcl_GetLongFromObj(interp, objv[3], | |
841 | (long*)(&buf.st_atime)) != TCL_OK) { | |
842 | return TCL_ERROR; | |
843 | } | |
844 | tval.actime = buf.st_atime; | |
845 | tval.modtime = buf.st_mtime; | |
846 | fileName = Tcl_GetString(objv[2]); | |
847 | if (utime(fileName, &tval) != 0) { | |
848 | Tcl_AppendStringsToObj(resultPtr, | |
849 | "could not set access time for file \"", | |
850 | fileName, "\": ", | |
851 | Tcl_PosixError(interp), (char *) NULL); | |
852 | return TCL_ERROR; | |
853 | } | |
854 | /* | |
855 | * Do another stat to ensure that the we return the | |
856 | * new recognized atime - hopefully the same as the | |
857 | * one we sent in. However, fs's like FAT don't | |
858 | * even know what atime is. | |
859 | */ | |
860 | if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | |
861 | return TCL_ERROR; | |
862 | } | |
863 | } | |
864 | Tcl_SetLongObj(resultPtr, (long) buf.st_atime); | |
865 | return TCL_OK; | |
866 | } | |
867 | case FILE_ATTRIBUTES: { | |
868 | return TclFileAttrsCmd(interp, objc, objv); | |
869 | } | |
870 | case FILE_CHANNELS: { | |
871 | if ((objc < 2) || (objc > 3)) { | |
872 | Tcl_WrongNumArgs(interp, 2, objv, "?pattern?"); | |
873 | return TCL_ERROR; | |
874 | } | |
875 | return Tcl_GetChannelNamesEx(interp, | |
876 | ((objc == 2) ? NULL : Tcl_GetString(objv[2]))); | |
877 | } | |
878 | case FILE_COPY: { | |
879 | int result; | |
880 | char **argv; | |
881 | ||
882 | argv = StringifyObjects(objc, objv); | |
883 | result = TclFileCopyCmd(interp, objc, argv); | |
884 | ckfree((char *) argv); | |
885 | return result; | |
886 | } | |
887 | case FILE_DELETE: { | |
888 | int result; | |
889 | char **argv; | |
890 | ||
891 | argv = StringifyObjects(objc, objv); | |
892 | result = TclFileDeleteCmd(interp, objc, argv); | |
893 | ckfree((char *) argv); | |
894 | return result; | |
895 | } | |
896 | case FILE_DIRNAME: { | |
897 | int argc; | |
898 | char **argv; | |
899 | ||
900 | if (objc != 3) { | |
901 | goto only3Args; | |
902 | } | |
903 | if (SplitPath(interp, objv[2], &argc, &argv) != TCL_OK) { | |
904 | return TCL_ERROR; | |
905 | } | |
906 | ||
907 | /* | |
908 | * Return all but the last component. If there is only one | |
909 | * component, return it if the path was non-relative, otherwise | |
910 | * return the current directory. | |
911 | */ | |
912 | ||
913 | if (argc > 1) { | |
914 | Tcl_DString ds; | |
915 | ||
916 | Tcl_DStringInit(&ds); | |
917 | Tcl_JoinPath(argc - 1, argv, &ds); | |
918 | Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&ds), | |
919 | Tcl_DStringLength(&ds)); | |
920 | Tcl_DStringFree(&ds); | |
921 | } else if ((argc == 0) | |
922 | || (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) { | |
923 | Tcl_SetStringObj(resultPtr, | |
924 | ((tclPlatform == TCL_PLATFORM_MAC) ? ":" : "."), 1); | |
925 | } else { | |
926 | Tcl_SetStringObj(resultPtr, argv[0], -1); | |
927 | } | |
928 | ckfree((char *) argv); | |
929 | return TCL_OK; | |
930 | } | |
931 | case FILE_EXECUTABLE: { | |
932 | if (objc != 3) { | |
933 | goto only3Args; | |
934 | } | |
935 | return CheckAccess(interp, objv[2], X_OK); | |
936 | } | |
937 | case FILE_EXISTS: { | |
938 | if (objc != 3) { | |
939 | goto only3Args; | |
940 | } | |
941 | return CheckAccess(interp, objv[2], F_OK); | |
942 | } | |
943 | case FILE_EXTENSION: { | |
944 | char *fileName, *extension; | |
945 | if (objc != 3) { | |
946 | goto only3Args; | |
947 | } | |
948 | fileName = Tcl_GetString(objv[2]); | |
949 | extension = TclGetExtension(fileName); | |
950 | if (extension != NULL) { | |
951 | Tcl_SetStringObj(resultPtr, extension, -1); | |
952 | } | |
953 | return TCL_OK; | |
954 | } | |
955 | case FILE_ISDIRECTORY: { | |
956 | int value; | |
957 | struct stat buf; | |
958 | ||
959 | if (objc != 3) { | |
960 | goto only3Args; | |
961 | } | |
962 | value = 0; | |
963 | if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) { | |
964 | value = S_ISDIR(buf.st_mode); | |
965 | } | |
966 | Tcl_SetBooleanObj(resultPtr, value); | |
967 | return TCL_OK; | |
968 | } | |
969 | case FILE_ISFILE: { | |
970 | int value; | |
971 | struct stat buf; | |
972 | ||
973 | if (objc != 3) { | |
974 | goto only3Args; | |
975 | } | |
976 | value = 0; | |
977 | if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) { | |
978 | value = S_ISREG(buf.st_mode); | |
979 | } | |
980 | Tcl_SetBooleanObj(resultPtr, value); | |
981 | return TCL_OK; | |
982 | } | |
983 | case FILE_JOIN: { | |
984 | char **argv; | |
985 | Tcl_DString ds; | |
986 | ||
987 | if (objc < 3) { | |
988 | Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?"); | |
989 | return TCL_ERROR; | |
990 | } | |
991 | argv = StringifyObjects(objc - 2, objv + 2); | |
992 | Tcl_DStringInit(&ds); | |
993 | Tcl_JoinPath(objc - 2, argv, &ds); | |
994 | Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&ds), | |
995 | Tcl_DStringLength(&ds)); | |
996 | Tcl_DStringFree(&ds); | |
997 | ckfree((char *) argv); | |
998 | return TCL_OK; | |
999 | } | |
1000 | case FILE_LSTAT: { | |
1001 | char *varName; | |
1002 | struct stat buf; | |
1003 | ||
1004 | if (objc != 4) { | |
1005 | Tcl_WrongNumArgs(interp, 2, objv, "name varName"); | |
1006 | return TCL_ERROR; | |
1007 | } | |
1008 | if (GetStatBuf(interp, objv[2], TclpLstat, &buf) != TCL_OK) { | |
1009 | return TCL_ERROR; | |
1010 | } | |
1011 | varName = Tcl_GetString(objv[3]); | |
1012 | return StoreStatData(interp, varName, &buf); | |
1013 | } | |
1014 | case FILE_MTIME: { | |
1015 | struct stat buf; | |
1016 | char *fileName; | |
1017 | struct utimbuf tval; | |
1018 | ||
1019 | if ((objc < 3) || (objc > 4)) { | |
1020 | Tcl_WrongNumArgs(interp, 2, objv, "name ?time?"); | |
1021 | return TCL_ERROR; | |
1022 | } | |
1023 | if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | |
1024 | return TCL_ERROR; | |
1025 | } | |
1026 | if (objc == 4) { | |
1027 | if (Tcl_GetLongFromObj(interp, objv[3], | |
1028 | (long*)(&buf.st_mtime)) != TCL_OK) { | |
1029 | return TCL_ERROR; | |
1030 | } | |
1031 | tval.actime = buf.st_atime; | |
1032 | tval.modtime = buf.st_mtime; | |
1033 | fileName = Tcl_GetString(objv[2]); | |
1034 | if (utime(fileName, &tval) != 0) { | |
1035 | Tcl_AppendStringsToObj(resultPtr, | |
1036 | "could not set modification time for file \"", | |
1037 | fileName, "\": ", | |
1038 | Tcl_PosixError(interp), (char *) NULL); | |
1039 | return TCL_ERROR; | |
1040 | } | |
1041 | /* | |
1042 | * Do another stat to ensure that the we return the | |
1043 | * new recognized atime - hopefully the same as the | |
1044 | * one we sent in. However, fs's like FAT don't | |
1045 | * even know what atime is. | |
1046 | */ | |
1047 | if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | |
1048 | return TCL_ERROR; | |
1049 | } | |
1050 | } | |
1051 | Tcl_SetLongObj(resultPtr, (long) buf.st_mtime); | |
1052 | return TCL_OK; | |
1053 | } | |
1054 | case FILE_MKDIR: { | |
1055 | char **argv; | |
1056 | int result; | |
1057 | ||
1058 | if (objc < 3) { | |
1059 | Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?"); | |
1060 | return TCL_ERROR; | |
1061 | } | |
1062 | argv = StringifyObjects(objc, objv); | |
1063 | result = TclFileMakeDirsCmd(interp, objc, argv); | |
1064 | ckfree((char *) argv); | |
1065 | return result; | |
1066 | } | |
1067 | case FILE_NATIVENAME: { | |
1068 | char *fileName; | |
1069 | Tcl_DString ds; | |
1070 | ||
1071 | if (objc != 3) { | |
1072 | goto only3Args; | |
1073 | } | |
1074 | fileName = Tcl_GetString(objv[2]); | |
1075 | fileName = Tcl_TranslateFileName(interp, fileName, &ds); | |
1076 | if (fileName == NULL) { | |
1077 | return TCL_ERROR; | |
1078 | } | |
1079 | Tcl_SetStringObj(resultPtr, fileName, Tcl_DStringLength(&ds)); | |
1080 | Tcl_DStringFree(&ds); | |
1081 | return TCL_OK; | |
1082 | } | |
1083 | case FILE_OWNED: { | |
1084 | int value; | |
1085 | struct stat buf; | |
1086 | ||
1087 | if (objc != 3) { | |
1088 | goto only3Args; | |
1089 | } | |
1090 | value = 0; | |
1091 | if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) { | |
1092 | /* | |
1093 | * For Windows and Macintosh, there are no user ids | |
1094 | * associated with a file, so we always return 1. | |
1095 | */ | |
1096 | ||
1097 | #if (defined(__WIN32__) || defined(MAC_TCL)) | |
1098 | value = 1; | |
1099 | #else | |
1100 | value = (geteuid() == buf.st_uid); | |
1101 | #endif | |
1102 | } | |
1103 | Tcl_SetBooleanObj(resultPtr, value); | |
1104 | return TCL_OK; | |
1105 | } | |
1106 | case FILE_PATHTYPE: { | |
1107 | char *fileName; | |
1108 | ||
1109 | if (objc != 3) { | |
1110 | goto only3Args; | |
1111 | } | |
1112 | fileName = Tcl_GetString(objv[2]); | |
1113 | switch (Tcl_GetPathType(fileName)) { | |
1114 | case TCL_PATH_ABSOLUTE: | |
1115 | Tcl_SetStringObj(resultPtr, "absolute", -1); | |
1116 | break; | |
1117 | case TCL_PATH_RELATIVE: | |
1118 | Tcl_SetStringObj(resultPtr, "relative", -1); | |
1119 | break; | |
1120 | case TCL_PATH_VOLUME_RELATIVE: | |
1121 | Tcl_SetStringObj(resultPtr, "volumerelative", -1); | |
1122 | break; | |
1123 | } | |
1124 | return TCL_OK; | |
1125 | } | |
1126 | case FILE_READABLE: { | |
1127 | if (objc != 3) { | |
1128 | goto only3Args; | |
1129 | } | |
1130 | return CheckAccess(interp, objv[2], R_OK); | |
1131 | } | |
1132 | case FILE_READLINK: { | |
1133 | char *fileName, *contents; | |
1134 | Tcl_DString name, link; | |
1135 | ||
1136 | if (objc != 3) { | |
1137 | goto only3Args; | |
1138 | } | |
1139 | ||
1140 | fileName = Tcl_GetString(objv[2]); | |
1141 | fileName = Tcl_TranslateFileName(interp, fileName, &name); | |
1142 | if (fileName == NULL) { | |
1143 | return TCL_ERROR; | |
1144 | } | |
1145 | ||
1146 | /* | |
1147 | * If S_IFLNK isn't defined it means that the machine doesn't | |
1148 | * support symbolic links, so the file can't possibly be a | |
1149 | * symbolic link. Generate an EINVAL error, which is what | |
1150 | * happens on machines that do support symbolic links when | |
1151 | * you invoke readlink on a file that isn't a symbolic link. | |
1152 | */ | |
1153 | ||
1154 | #ifndef S_IFLNK | |
1155 | contents = NULL; | |
1156 | errno = EINVAL; | |
1157 | #else | |
1158 | contents = TclpReadlink(fileName, &link); | |
1159 | #endif /* S_IFLNK */ | |
1160 | ||
1161 | Tcl_DStringFree(&name); | |
1162 | if (contents == NULL) { | |
1163 | Tcl_AppendResult(interp, "could not readlink \"", | |
1164 | Tcl_GetString(objv[2]), "\": ", | |
1165 | Tcl_PosixError(interp), (char *) NULL); | |
1166 | return TCL_ERROR; | |
1167 | } | |
1168 | Tcl_DStringResult(interp, &link); | |
1169 | return TCL_OK; | |
1170 | } | |
1171 | case FILE_RENAME: { | |
1172 | int result; | |
1173 | char **argv; | |
1174 | ||
1175 | argv = StringifyObjects(objc, objv); | |
1176 | result = TclFileRenameCmd(interp, objc, argv); | |
1177 | ckfree((char *) argv); | |
1178 | return result; | |
1179 | } | |
1180 | case FILE_ROOTNAME: { | |
1181 | int length; | |
1182 | char *fileName, *extension; | |
1183 | ||
1184 | if (objc != 3) { | |
1185 | goto only3Args; | |
1186 | } | |
1187 | fileName = Tcl_GetStringFromObj(objv[2], &length); | |
1188 | extension = TclGetExtension(fileName); | |
1189 | if (extension == NULL) { | |
1190 | Tcl_SetObjResult(interp, objv[2]); | |
1191 | } else { | |
1192 | Tcl_SetStringObj(resultPtr, fileName, | |
1193 | (int) (length - strlen(extension))); | |
1194 | } | |
1195 | return TCL_OK; | |
1196 | } | |
1197 | case FILE_SIZE: { | |
1198 | struct stat buf; | |
1199 | ||
1200 | if (objc != 3) { | |
1201 | goto only3Args; | |
1202 | } | |
1203 | if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | |
1204 | return TCL_ERROR; | |
1205 | } | |
1206 | Tcl_SetLongObj(resultPtr, (long) buf.st_size); | |
1207 | return TCL_OK; | |
1208 | } | |
1209 | case FILE_SPLIT: { | |
1210 | int i, argc; | |
1211 | char **argv; | |
1212 | char *fileName; | |
1213 | Tcl_Obj *objPtr; | |
1214 | ||
1215 | if (objc != 3) { | |
1216 | goto only3Args; | |
1217 | } | |
1218 | fileName = Tcl_GetString(objv[2]); | |
1219 | Tcl_SplitPath(fileName, &argc, &argv); | |
1220 | for (i = 0; i < argc; i++) { | |
1221 | objPtr = Tcl_NewStringObj(argv[i], -1); | |
1222 | Tcl_ListObjAppendElement(NULL, resultPtr, objPtr); | |
1223 | } | |
1224 | ckfree((char *) argv); | |
1225 | return TCL_OK; | |
1226 | } | |
1227 | case FILE_STAT: { | |
1228 | char *varName; | |
1229 | struct stat buf; | |
1230 | ||
1231 | if (objc != 4) { | |
1232 | Tcl_WrongNumArgs(interp, 1, objv, "stat name varName"); | |
1233 | return TCL_ERROR; | |
1234 | } | |
1235 | if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) { | |
1236 | return TCL_ERROR; | |
1237 | } | |
1238 | varName = Tcl_GetString(objv[3]); | |
1239 | return StoreStatData(interp, varName, &buf); | |
1240 | } | |
1241 | case FILE_TAIL: { | |
1242 | int argc; | |
1243 | char **argv; | |
1244 | ||
1245 | if (objc != 3) { | |
1246 | goto only3Args; | |
1247 | } | |
1248 | if (SplitPath(interp, objv[2], &argc, &argv) != TCL_OK) { | |
1249 | return TCL_ERROR; | |
1250 | } | |
1251 | ||
1252 | /* | |
1253 | * Return the last component, unless it is the only component, | |
1254 | * and it is the root of an absolute path. | |
1255 | */ | |
1256 | ||
1257 | if (argc > 0) { | |
1258 | if ((argc > 1) | |
1259 | || (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) { | |
1260 | Tcl_SetStringObj(resultPtr, argv[argc - 1], -1); | |
1261 | } | |
1262 | } | |
1263 | ckfree((char *) argv); | |
1264 | return TCL_OK; | |
1265 | } | |
1266 | case FILE_TYPE: { | |
1267 | struct stat buf; | |
1268 | ||
1269 | if (objc != 3) { | |
1270 | goto only3Args; | |
1271 | } | |
1272 | if (GetStatBuf(interp, objv[2], TclpLstat, &buf) != TCL_OK) { | |
1273 | return TCL_ERROR; | |
1274 | } | |
1275 | Tcl_SetStringObj(resultPtr, | |
1276 | GetTypeFromMode((unsigned short) buf.st_mode), -1); | |
1277 | return TCL_OK; | |
1278 | } | |
1279 | case FILE_VOLUMES: { | |
1280 | if (objc != 2) { | |
1281 | Tcl_WrongNumArgs(interp, 2, objv, NULL); | |
1282 | return TCL_ERROR; | |
1283 | } | |
1284 | return TclpListVolumes(interp); | |
1285 | } | |
1286 | case FILE_WRITABLE: { | |
1287 | if (objc != 3) { | |
1288 | goto only3Args; | |
1289 | } | |
1290 | return CheckAccess(interp, objv[2], W_OK); | |
1291 | } | |
1292 | } | |
1293 | ||
1294 | only3Args: | |
1295 | Tcl_WrongNumArgs(interp, 2, objv, "name"); | |
1296 | return TCL_ERROR; | |
1297 | } | |
1298 | ||
1299 | /* | |
1300 | *--------------------------------------------------------------------------- | |
1301 | * | |
1302 | * SplitPath -- | |
1303 | * | |
1304 | * Utility procedure used by Tcl_FileObjCmd() to split a path. | |
1305 | * Differs from standard Tcl_SplitPath in its handling of home | |
1306 | * directories; Tcl_SplitPath preserves the "~" while this | |
1307 | * procedure computes the actual full path name. | |
1308 | * | |
1309 | * Results: | |
1310 | * The return value is TCL_OK if the path could be split, TCL_ERROR | |
1311 | * otherwise. If TCL_ERROR was returned, an error message is left | |
1312 | * in interp. If TCL_OK was returned, *argvPtr is set to a newly | |
1313 | * allocated array of strings that represent the individual | |
1314 | * directories in the specified path, and *argcPtr is filled with | |
1315 | * the length of that array. | |
1316 | * | |
1317 | * Side effects: | |
1318 | * Memory allocated. The caller must eventually free this memory | |
1319 | * by calling ckfree() on *argvPtr. | |
1320 | * | |
1321 | *--------------------------------------------------------------------------- | |
1322 | */ | |
1323 | ||
1324 | static int | |
1325 | SplitPath(interp, objPtr, argcPtr, argvPtr) | |
1326 | Tcl_Interp *interp; /* Interp for error return. May be NULL. */ | |
1327 | Tcl_Obj *objPtr; /* Path to be split. */ | |
1328 | int *argcPtr; /* Filled with length of following array. */ | |
1329 | char ***argvPtr; /* Filled with array of strings representing | |
1330 | * the elements of the specified path. */ | |
1331 | { | |
1332 | char *fileName; | |
1333 | ||
1334 | fileName = Tcl_GetString(objPtr); | |
1335 | ||
1336 | /* | |
1337 | * If there is only one element, and it starts with a tilde, | |
1338 | * perform tilde substitution and resplit the path. | |
1339 | */ | |
1340 | ||
1341 | Tcl_SplitPath(fileName, argcPtr, argvPtr); | |
1342 | if ((*argcPtr == 1) && (fileName[0] == '~')) { | |
1343 | Tcl_DString ds; | |
1344 | ||
1345 | ckfree((char *) *argvPtr); | |
1346 | fileName = Tcl_TranslateFileName(interp, fileName, &ds); | |
1347 | if (fileName == NULL) { | |
1348 | return TCL_ERROR; | |
1349 | } | |
1350 | Tcl_SplitPath(fileName, argcPtr, argvPtr); | |
1351 | Tcl_DStringFree(&ds); | |
1352 | } | |
1353 | return TCL_OK; | |
1354 | } | |
1355 | ||
1356 | /* | |
1357 | *--------------------------------------------------------------------------- | |
1358 | * | |
1359 | * CheckAccess -- | |
1360 | * | |
1361 | * Utility procedure used by Tcl_FileObjCmd() to query file | |
1362 | * attributes available through the access() system call. | |
1363 | * | |
1364 | * Results: | |
1365 | * Always returns TCL_OK. Sets interp's result to boolean true or | |
1366 | * false depending on whether the file has the specified attribute. | |
1367 | * | |
1368 | * Side effects: | |
1369 | * None. | |
1370 | * | |
1371 | *--------------------------------------------------------------------------- | |
1372 | */ | |
1373 | ||
1374 | static int | |
1375 | CheckAccess(interp, objPtr, mode) | |
1376 | Tcl_Interp *interp; /* Interp for status return. Must not be | |
1377 | * NULL. */ | |
1378 | Tcl_Obj *objPtr; /* Name of file to check. */ | |
1379 | int mode; /* Attribute to check; passed as argument to | |
1380 | * access(). */ | |
1381 | { | |
1382 | int value; | |
1383 | char *fileName; | |
1384 | Tcl_DString ds; | |
1385 | ||
1386 | fileName = Tcl_GetString(objPtr); | |
1387 | fileName = Tcl_TranslateFileName(interp, fileName, &ds); | |
1388 | if (fileName == NULL) { | |
1389 | value = 0; | |
1390 | } else { | |
1391 | value = (TclAccess(fileName, mode) == 0); | |
1392 | Tcl_DStringFree(&ds); | |
1393 | } | |
1394 | Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value); | |
1395 | ||
1396 | return TCL_OK; | |
1397 | } | |
1398 | ||
1399 | /* | |
1400 | *--------------------------------------------------------------------------- | |
1401 | * | |
1402 | * GetStatBuf -- | |
1403 | * | |
1404 | * Utility procedure used by Tcl_FileObjCmd() to query file | |
1405 | * attributes available through the stat() or lstat() system call. | |
1406 | * | |
1407 | * Results: | |
1408 | * The return value is TCL_OK if the specified file exists and can | |
1409 | * be stat'ed, TCL_ERROR otherwise. If TCL_ERROR is returned, an | |
1410 | * error message is left in interp's result. If TCL_OK is returned, | |
1411 | * *statPtr is filled with information about the specified file. | |
1412 | * | |
1413 | * Side effects: | |
1414 | * None. | |
1415 | * | |
1416 | *--------------------------------------------------------------------------- | |
1417 | */ | |
1418 | ||
1419 | static int | |
1420 | GetStatBuf(interp, objPtr, statProc, statPtr) | |
1421 | Tcl_Interp *interp; /* Interp for error return. May be NULL. */ | |
1422 | Tcl_Obj *objPtr; /* Path name to examine. */ | |
1423 | StatProc *statProc; /* Either stat() or lstat() depending on | |
1424 | * desired behavior. */ | |
1425 | struct stat *statPtr; /* Filled with info about file obtained by | |
1426 | * calling (*statProc)(). */ | |
1427 | { | |
1428 | char *fileName; | |
1429 | Tcl_DString ds; | |
1430 | int status; | |
1431 | ||
1432 | fileName = Tcl_GetString(objPtr); | |
1433 | fileName = Tcl_TranslateFileName(interp, fileName, &ds); | |
1434 | if (fileName == NULL) { | |
1435 | return TCL_ERROR; | |
1436 | } | |
1437 | ||
1438 | status = (*statProc)(Tcl_DStringValue(&ds), statPtr); | |
1439 | Tcl_DStringFree(&ds); | |
1440 | ||
1441 | if (status < 0) { | |
1442 | if (interp != NULL) { | |
1443 | Tcl_AppendResult(interp, "could not read \"", | |
1444 | Tcl_GetString(objPtr), "\": ", | |
1445 | Tcl_PosixError(interp), (char *) NULL); | |
1446 | } | |
1447 | return TCL_ERROR; | |
1448 | } | |
1449 | return TCL_OK; | |
1450 | } | |
1451 | ||
1452 | /* | |
1453 | *---------------------------------------------------------------------- | |
1454 | * | |
1455 | * StoreStatData -- | |
1456 | * | |
1457 | * This is a utility procedure that breaks out the fields of a | |
1458 | * "stat" structure and stores them in textual form into the | |
1459 | * elements of an associative array. | |
1460 | * | |
1461 | * Results: | |
1462 | * Returns a standard Tcl return value. If an error occurs then | |
1463 | * a message is left in interp's result. | |
1464 | * | |
1465 | * Side effects: | |
1466 | * Elements of the associative array given by "varName" are modified. | |
1467 | * | |
1468 | *---------------------------------------------------------------------- | |
1469 | */ | |
1470 | ||
1471 | static int | |
1472 | StoreStatData(interp, varName, statPtr) | |
1473 | Tcl_Interp *interp; /* Interpreter for error reports. */ | |
1474 | char *varName; /* Name of associative array variable | |
1475 | * in which to store stat results. */ | |
1476 | struct stat *statPtr; /* Pointer to buffer containing | |
1477 | * stat data to store in varName. */ | |
1478 | { | |
1479 | char string[TCL_INTEGER_SPACE]; | |
1480 | ||
1481 | TclFormatInt(string, (long) statPtr->st_dev); | |
1482 | if (Tcl_SetVar2(interp, varName, "dev", string, TCL_LEAVE_ERR_MSG) | |
1483 | == NULL) { | |
1484 | return TCL_ERROR; | |
1485 | } | |
1486 | TclFormatInt(string, (long) statPtr->st_ino); | |
1487 | if (Tcl_SetVar2(interp, varName, "ino", string, TCL_LEAVE_ERR_MSG) | |
1488 | == NULL) { | |
1489 | return TCL_ERROR; | |
1490 | } | |
1491 | TclFormatInt(string, (unsigned short) statPtr->st_mode); | |
1492 | if (Tcl_SetVar2(interp, varName, "mode", string, TCL_LEAVE_ERR_MSG) | |
1493 | == NULL) { | |
1494 | return TCL_ERROR; | |
1495 | } | |
1496 | TclFormatInt(string, (long) statPtr->st_nlink); | |
1497 | if (Tcl_SetVar2(interp, varName, "nlink", string, TCL_LEAVE_ERR_MSG) | |
1498 | == NULL) { | |
1499 | return TCL_ERROR; | |
1500 | } | |
1501 | TclFormatInt(string, (long) statPtr->st_uid); | |
1502 | if (Tcl_SetVar2(interp, varName, "uid", string, TCL_LEAVE_ERR_MSG) | |
1503 | == NULL) { | |
1504 | return TCL_ERROR; | |
1505 | } | |
1506 | TclFormatInt(string, (long) statPtr->st_gid); | |
1507 | if (Tcl_SetVar2(interp, varName, "gid", string, TCL_LEAVE_ERR_MSG) | |
1508 | == NULL) { | |
1509 | return TCL_ERROR; | |
1510 | } | |
1511 | sprintf(string, "%lu", (unsigned long) statPtr->st_size); | |
1512 | if (Tcl_SetVar2(interp, varName, "size", string, TCL_LEAVE_ERR_MSG) | |
1513 | == NULL) { | |
1514 | return TCL_ERROR; | |
1515 | } | |
1516 | TclFormatInt(string, (long) statPtr->st_atime); | |
1517 | if (Tcl_SetVar2(interp, varName, "atime", string, TCL_LEAVE_ERR_MSG) | |
1518 | == NULL) { | |
1519 | return TCL_ERROR; | |
1520 | } | |
1521 | TclFormatInt(string, (long) statPtr->st_mtime); | |
1522 | if (Tcl_SetVar2(interp, varName, "mtime", string, TCL_LEAVE_ERR_MSG) | |
1523 | == NULL) { | |
1524 | return TCL_ERROR; | |
1525 | } | |
1526 | TclFormatInt(string, (long) statPtr->st_ctime); | |
1527 | if (Tcl_SetVar2(interp, varName, "ctime", string, TCL_LEAVE_ERR_MSG) | |
1528 | == NULL) { | |
1529 | return TCL_ERROR; | |
1530 | } | |
1531 | if (Tcl_SetVar2(interp, varName, "type", | |
1532 | GetTypeFromMode((unsigned short) statPtr->st_mode), | |
1533 | TCL_LEAVE_ERR_MSG) == NULL) { | |
1534 | return TCL_ERROR; | |
1535 | } | |
1536 | return TCL_OK; | |
1537 | } | |
1538 | ||
1539 | /* | |
1540 | *---------------------------------------------------------------------- | |
1541 | * | |
1542 | * GetTypeFromMode -- | |
1543 | * | |
1544 | * Given a mode word, returns a string identifying the type of a | |
1545 | * file. | |
1546 | * | |
1547 | * Results: | |
1548 | * A static text string giving the file type from mode. | |
1549 | * | |
1550 | * Side effects: | |
1551 | * None. | |
1552 | * | |
1553 | *---------------------------------------------------------------------- | |
1554 | */ | |
1555 | ||
1556 | static char * | |
1557 | GetTypeFromMode(mode) | |
1558 | int mode; | |
1559 | { | |
1560 | if (S_ISREG(mode)) { | |
1561 | return "file"; | |
1562 | } else if (S_ISDIR(mode)) { | |
1563 | return "directory"; | |
1564 | } else if (S_ISCHR(mode)) { | |
1565 | return "characterSpecial"; | |
1566 | } else if (S_ISBLK(mode)) { | |
1567 | return "blockSpecial"; | |
1568 | } else if (S_ISFIFO(mode)) { | |
1569 | return "fifo"; | |
1570 | #ifdef S_ISLNK | |
1571 | } else if (S_ISLNK(mode)) { | |
1572 | return "link"; | |
1573 | #endif | |
1574 | #ifdef S_ISSOCK | |
1575 | } else if (S_ISSOCK(mode)) { | |
1576 | return "socket"; | |
1577 | #endif | |
1578 | } | |
1579 | return "unknown"; | |
1580 | } | |
1581 | ||
1582 | /* | |
1583 | *---------------------------------------------------------------------- | |
1584 | * | |
1585 | * Tcl_ForObjCmd -- | |
1586 | * | |
1587 | * This procedure is invoked to process the "for" Tcl command. | |
1588 | * See the user documentation for details on what it does. | |
1589 | * | |
1590 | * With the bytecode compiler, this procedure is only called when | |
1591 | * a command name is computed at runtime, and is "for" or the name | |
1592 | * to which "for" was renamed: e.g., | |
1593 | * "set z for; $z {set i 0} {$i<100} {incr i} {puts $i}" | |
1594 | * | |
1595 | * Results: | |
1596 | * A standard Tcl result. | |
1597 | * | |
1598 | * Side effects: | |
1599 | * See the user documentation. | |
1600 | * | |
1601 | *---------------------------------------------------------------------- | |
1602 | */ | |
1603 | ||
1604 | /* ARGSUSED */ | |
1605 | int | |
1606 | Tcl_ForObjCmd(dummy, interp, objc, objv) | |
1607 | ClientData dummy; /* Not used. */ | |
1608 | Tcl_Interp *interp; /* Current interpreter. */ | |
1609 | int objc; /* Number of arguments. */ | |
1610 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
1611 | { | |
1612 | int result, value; | |
1613 | ||
1614 | if (objc != 5) { | |
1615 | Tcl_WrongNumArgs(interp, 1, objv, "start test next command"); | |
1616 | return TCL_ERROR; | |
1617 | } | |
1618 | ||
1619 | result = Tcl_EvalObjEx(interp, objv[1], 0); | |
1620 | if (result != TCL_OK) { | |
1621 | if (result == TCL_ERROR) { | |
1622 | Tcl_AddErrorInfo(interp, "\n (\"for\" initial command)"); | |
1623 | } | |
1624 | return result; | |
1625 | } | |
1626 | while (1) { | |
1627 | /* | |
1628 | * We need to reset the result before passing it off to | |
1629 | * Tcl_ExprBooleanObj. Otherwise, any error message will be appended | |
1630 | * to the result of the last evaluation. | |
1631 | */ | |
1632 | ||
1633 | Tcl_ResetResult(interp); | |
1634 | result = Tcl_ExprBooleanObj(interp, objv[2], &value); | |
1635 | if (result != TCL_OK) { | |
1636 | return result; | |
1637 | } | |
1638 | if (!value) { | |
1639 | break; | |
1640 | } | |
1641 | result = Tcl_EvalObjEx(interp, objv[4], 0); | |
1642 | if ((result != TCL_OK) && (result != TCL_CONTINUE)) { | |
1643 | if (result == TCL_ERROR) { | |
1644 | char msg[32 + TCL_INTEGER_SPACE]; | |
1645 | ||
1646 | sprintf(msg, "\n (\"for\" body line %d)",interp->errorLine); | |
1647 | Tcl_AddErrorInfo(interp, msg); | |
1648 | } | |
1649 | break; | |
1650 | } | |
1651 | result = Tcl_EvalObjEx(interp, objv[3], 0); | |
1652 | if (result == TCL_BREAK) { | |
1653 | break; | |
1654 | } else if (result != TCL_OK) { | |
1655 | if (result == TCL_ERROR) { | |
1656 | Tcl_AddErrorInfo(interp, "\n (\"for\" loop-end command)"); | |
1657 | } | |
1658 | return result; | |
1659 | } | |
1660 | } | |
1661 | if (result == TCL_BREAK) { | |
1662 | result = TCL_OK; | |
1663 | } | |
1664 | if (result == TCL_OK) { | |
1665 | Tcl_ResetResult(interp); | |
1666 | } | |
1667 | return result; | |
1668 | } | |
1669 | ||
1670 | /* | |
1671 | *---------------------------------------------------------------------- | |
1672 | * | |
1673 | * Tcl_ForeachObjCmd -- | |
1674 | * | |
1675 | * This object-based procedure is invoked to process the "foreach" Tcl | |
1676 | * command. See the user documentation for details on what it does. | |
1677 | * | |
1678 | * Results: | |
1679 | * A standard Tcl object result. | |
1680 | * | |
1681 | * Side effects: | |
1682 | * See the user documentation. | |
1683 | * | |
1684 | *---------------------------------------------------------------------- | |
1685 | */ | |
1686 | ||
1687 | /* ARGSUSED */ | |
1688 | int | |
1689 | Tcl_ForeachObjCmd(dummy, interp, objc, objv) | |
1690 | ClientData dummy; /* Not used. */ | |
1691 | Tcl_Interp *interp; /* Current interpreter. */ | |
1692 | int objc; /* Number of arguments. */ | |
1693 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
1694 | { | |
1695 | int result = TCL_OK; | |
1696 | int i; /* i selects a value list */ | |
1697 | int j, maxj; /* Number of loop iterations */ | |
1698 | int v; /* v selects a loop variable */ | |
1699 | int numLists; /* Count of value lists */ | |
1700 | Tcl_Obj *bodyPtr; | |
1701 | ||
1702 | /* | |
1703 | * We copy the argument object pointers into a local array to avoid | |
1704 | * the problem that "objv" might become invalid. It is a pointer into | |
1705 | * the evaluation stack and that stack might be grown and reallocated | |
1706 | * if the loop body requires a large amount of stack space. | |
1707 | */ | |
1708 | ||
1709 | #define NUM_ARGS 9 | |
1710 | Tcl_Obj *(argObjStorage[NUM_ARGS]); | |
1711 | Tcl_Obj **argObjv = argObjStorage; | |
1712 | ||
1713 | #define STATIC_LIST_SIZE 4 | |
1714 | int indexArray[STATIC_LIST_SIZE]; /* Array of value list indices */ | |
1715 | int varcListArray[STATIC_LIST_SIZE]; /* # loop variables per list */ | |
1716 | Tcl_Obj **varvListArray[STATIC_LIST_SIZE]; /* Array of var name lists */ | |
1717 | int argcListArray[STATIC_LIST_SIZE]; /* Array of value list sizes */ | |
1718 | Tcl_Obj **argvListArray[STATIC_LIST_SIZE]; /* Array of value lists */ | |
1719 | ||
1720 | int *index = indexArray; | |
1721 | int *varcList = varcListArray; | |
1722 | Tcl_Obj ***varvList = varvListArray; | |
1723 | int *argcList = argcListArray; | |
1724 | Tcl_Obj ***argvList = argvListArray; | |
1725 | ||
1726 | if (objc < 4 || (objc%2 != 0)) { | |
1727 | Tcl_WrongNumArgs(interp, 1, objv, | |
1728 | "varList list ?varList list ...? command"); | |
1729 | return TCL_ERROR; | |
1730 | } | |
1731 | ||
1732 | /* | |
1733 | * Create the object argument array "argObjv". Make sure argObjv is | |
1734 | * large enough to hold the objc arguments. | |
1735 | */ | |
1736 | ||
1737 | if (objc > NUM_ARGS) { | |
1738 | argObjv = (Tcl_Obj **) ckalloc(objc * sizeof(Tcl_Obj *)); | |
1739 | } | |
1740 | for (i = 0; i < objc; i++) { | |
1741 | argObjv[i] = objv[i]; | |
1742 | } | |
1743 | ||
1744 | /* | |
1745 | * Manage numList parallel value lists. | |
1746 | * argvList[i] is a value list counted by argcList[i] | |
1747 | * varvList[i] is the list of variables associated with the value list | |
1748 | * varcList[i] is the number of variables associated with the value list | |
1749 | * index[i] is the current pointer into the value list argvList[i] | |
1750 | */ | |
1751 | ||
1752 | numLists = (objc-2)/2; | |
1753 | if (numLists > STATIC_LIST_SIZE) { | |
1754 | index = (int *) ckalloc(numLists * sizeof(int)); | |
1755 | varcList = (int *) ckalloc(numLists * sizeof(int)); | |
1756 | varvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **)); | |
1757 | argcList = (int *) ckalloc(numLists * sizeof(int)); | |
1758 | argvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **)); | |
1759 | } | |
1760 | for (i = 0; i < numLists; i++) { | |
1761 | index[i] = 0; | |
1762 | varcList[i] = 0; | |
1763 | varvList[i] = (Tcl_Obj **) NULL; | |
1764 | argcList[i] = 0; | |
1765 | argvList[i] = (Tcl_Obj **) NULL; | |
1766 | } | |
1767 | ||
1768 | /* | |
1769 | * Break up the value lists and variable lists into elements | |
1770 | */ | |
1771 | ||
1772 | maxj = 0; | |
1773 | for (i = 0; i < numLists; i++) { | |
1774 | result = Tcl_ListObjGetElements(interp, argObjv[1+i*2], | |
1775 | &varcList[i], &varvList[i]); | |
1776 | if (result != TCL_OK) { | |
1777 | goto done; | |
1778 | } | |
1779 | if (varcList[i] < 1) { | |
1780 | Tcl_AppendToObj(Tcl_GetObjResult(interp), | |
1781 | "foreach varlist is empty", -1); | |
1782 | result = TCL_ERROR; | |
1783 | goto done; | |
1784 | } | |
1785 | ||
1786 | result = Tcl_ListObjGetElements(interp, argObjv[2+i*2], | |
1787 | &argcList[i], &argvList[i]); | |
1788 | if (result != TCL_OK) { | |
1789 | goto done; | |
1790 | } | |
1791 | ||
1792 | j = argcList[i] / varcList[i]; | |
1793 | if ((argcList[i] % varcList[i]) != 0) { | |
1794 | j++; | |
1795 | } | |
1796 | if (j > maxj) { | |
1797 | maxj = j; | |
1798 | } | |
1799 | } | |
1800 | ||
1801 | /* | |
1802 | * Iterate maxj times through the lists in parallel | |
1803 | * If some value lists run out of values, set loop vars to "" | |
1804 | */ | |
1805 | ||
1806 | bodyPtr = argObjv[objc-1]; | |
1807 | for (j = 0; j < maxj; j++) { | |
1808 | for (i = 0; i < numLists; i++) { | |
1809 | /* | |
1810 | * If a variable or value list object has been converted to | |
1811 | * another kind of Tcl object, convert it back to a list object | |
1812 | * and refetch the pointer to its element array. | |
1813 | */ | |
1814 | ||
1815 | if (argObjv[1+i*2]->typePtr != &tclListType) { | |
1816 | result = Tcl_ListObjGetElements(interp, argObjv[1+i*2], | |
1817 | &varcList[i], &varvList[i]); | |
1818 | if (result != TCL_OK) { | |
1819 | panic("Tcl_ForeachObjCmd: could not reconvert variable list %d to a list object\n", i); | |
1820 | } | |
1821 | } | |
1822 | if (argObjv[2+i*2]->typePtr != &tclListType) { | |
1823 | result = Tcl_ListObjGetElements(interp, argObjv[2+i*2], | |
1824 | &argcList[i], &argvList[i]); | |
1825 | if (result != TCL_OK) { | |
1826 | panic("Tcl_ForeachObjCmd: could not reconvert value list %d to a list object\n", i); | |
1827 | } | |
1828 | } | |
1829 | ||
1830 | for (v = 0; v < varcList[i]; v++) { | |
1831 | int k = index[i]++; | |
1832 | Tcl_Obj *valuePtr, *varValuePtr; | |
1833 | int isEmptyObj = 0; | |
1834 | ||
1835 | if (k < argcList[i]) { | |
1836 | valuePtr = argvList[i][k]; | |
1837 | } else { | |
1838 | valuePtr = Tcl_NewObj(); /* empty string */ | |
1839 | isEmptyObj = 1; | |
1840 | } | |
1841 | varValuePtr = Tcl_ObjSetVar2(interp, varvList[i][v], | |
1842 | NULL, valuePtr, 0); | |
1843 | if (varValuePtr == NULL) { | |
1844 | if (isEmptyObj) { | |
1845 | Tcl_DecrRefCount(valuePtr); | |
1846 | } | |
1847 | Tcl_ResetResult(interp); | |
1848 | Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | |
1849 | "couldn't set loop variable: \"", | |
1850 | Tcl_GetString(varvList[i][v]), "\"", (char *) NULL); | |
1851 | result = TCL_ERROR; | |
1852 | goto done; | |
1853 | } | |
1854 | ||
1855 | } | |
1856 | } | |
1857 | ||
1858 | result = Tcl_EvalObjEx(interp, bodyPtr, 0); | |
1859 | if (result != TCL_OK) { | |
1860 | if (result == TCL_CONTINUE) { | |
1861 | result = TCL_OK; | |
1862 | } else if (result == TCL_BREAK) { | |
1863 | result = TCL_OK; | |
1864 | break; | |
1865 | } else if (result == TCL_ERROR) { | |
1866 | char msg[32 + TCL_INTEGER_SPACE]; | |
1867 | ||
1868 | sprintf(msg, "\n (\"foreach\" body line %d)", | |
1869 | interp->errorLine); | |
1870 | Tcl_AddObjErrorInfo(interp, msg, -1); | |
1871 | break; | |
1872 | } else { | |
1873 | break; | |
1874 | } | |
1875 | } | |
1876 | } | |
1877 | if (result == TCL_OK) { | |
1878 | Tcl_ResetResult(interp); | |
1879 | } | |
1880 | ||
1881 | done: | |
1882 | if (numLists > STATIC_LIST_SIZE) { | |
1883 | ckfree((char *) index); | |
1884 | ckfree((char *) varcList); | |
1885 | ckfree((char *) argcList); | |
1886 | ckfree((char *) varvList); | |
1887 | ckfree((char *) argvList); | |
1888 | } | |
1889 | if (argObjv != argObjStorage) { | |
1890 | ckfree((char *) argObjv); | |
1891 | } | |
1892 | return result; | |
1893 | #undef STATIC_LIST_SIZE | |
1894 | #undef NUM_ARGS | |
1895 | } | |
1896 | ||
1897 | /* | |
1898 | *---------------------------------------------------------------------- | |
1899 | * | |
1900 | * Tcl_FormatObjCmd -- | |
1901 | * | |
1902 | * This procedure is invoked to process the "format" Tcl command. | |
1903 | * See the user documentation for details on what it does. | |
1904 | * | |
1905 | * Results: | |
1906 | * A standard Tcl result. | |
1907 | * | |
1908 | * Side effects: | |
1909 | * See the user documentation. | |
1910 | * | |
1911 | *---------------------------------------------------------------------- | |
1912 | */ | |
1913 | ||
1914 | /* ARGSUSED */ | |
1915 | int | |
1916 | Tcl_FormatObjCmd(dummy, interp, objc, objv) | |
1917 | ClientData dummy; /* Not used. */ | |
1918 | Tcl_Interp *interp; /* Current interpreter. */ | |
1919 | int objc; /* Number of arguments. */ | |
1920 | Tcl_Obj *CONST objv[]; /* Argument objects. */ | |
1921 | { | |
1922 | char *format; /* Used to read characters from the format | |
1923 | * string. */ | |
1924 | int formatLen; /* The length of the format string */ | |
1925 | char *endPtr; /* Points to the last char in format array */ | |
1926 | char newFormat[40]; /* A new format specifier is generated here. */ | |
1927 | int width; /* Field width from field specifier, or 0 if | |
1928 | * no width given. */ | |
1929 | int precision; /* Field precision from field specifier, or 0 | |
1930 | * if no precision given. */ | |
1931 | int size; /* Number of bytes needed for result of | |
1932 | * conversion, based on type of conversion | |
1933 | * ("e", "s", etc.), width, and precision. */ | |
1934 | int intValue; /* Used to hold value to pass to sprintf, if | |
1935 | * it's a one-word integer or char value */ | |
1936 | char *ptrValue = NULL; /* Used to hold value to pass to sprintf, if | |
1937 | * it's a one-word value. */ | |
1938 | double doubleValue; /* Used to hold value to pass to sprintf if | |
1939 | * it's a double value. */ | |
1940 | int whichValue; /* Indicates which of intValue, ptrValue, | |
1941 | * or doubleValue has the value to pass to | |
1942 | * sprintf, according to the following | |
1943 | * definitions: */ | |
1944 | # define INT_VALUE 0 | |
1945 | # define CHAR_VALUE 1 | |
1946 | # define PTR_VALUE 2 | |
1947 | # define DOUBLE_VALUE 3 | |
1948 | # define STRING_VALUE 4 | |
1949 | # define MAX_FLOAT_SIZE 320 | |
1950 | ||
1951 | Tcl_Obj *resultPtr; /* Where result is stored finally. */ | |
1952 | char staticBuf[MAX_FLOAT_SIZE + 1]; | |
1953 | /* A static buffer to copy the format results | |
1954 | * into */ | |
1955 | char *dst = staticBuf; /* The buffer that sprintf writes into each | |
1956 | * time the format processes a specifier */ | |
1957 | int dstSize = MAX_FLOAT_SIZE; | |
1958 | /* The size of the dst buffer */ | |
1959 | int noPercent; /* Special case for speed: indicates there's | |
1960 | * no field specifier, just a string to copy.*/ | |
1961 | int objIndex; /* Index of argument to substitute next. */ | |
1962 |