tclCmdAH.c

Go to the documentation of this file.
00001 /*
00002  * tclCmdAH.c --
00003  *
00004  *      This file contains the top-level command routines for most of the Tcl
00005  *      built-in commands whose names begin with the letters A to H.
00006  *
00007  * Copyright (c) 1987-1993 The Regents of the University of California.
00008  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
00009  *
00010  * See the file "license.terms" for information on usage and redistribution of
00011  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00012  *
00013  * RCS: @(#) $Id: tclCmdAH.c,v 1.91 2007/12/13 15:23:15 dgp Exp $
00014  */
00015 
00016 #include "tclInt.h"
00017 #include <locale.h>
00018 
00019 /*
00020  * Prototypes for local procedures defined in this file:
00021  */
00022 
00023 static int              CheckAccess(Tcl_Interp *interp, Tcl_Obj *pathPtr,
00024                             int mode);
00025 static int              EncodingDirsObjCmd(ClientData dummy,
00026                             Tcl_Interp *interp, int objc,
00027                             Tcl_Obj *CONST objv[]);
00028 static int              GetStatBuf(Tcl_Interp *interp, Tcl_Obj *pathPtr,
00029                             Tcl_FSStatProc *statProc, Tcl_StatBuf *statPtr);
00030 static char *           GetTypeFromMode(int mode);
00031 static int              StoreStatData(Tcl_Interp *interp, Tcl_Obj *varName,
00032                             Tcl_StatBuf *statPtr);
00033 
00034 /*
00035  *----------------------------------------------------------------------
00036  *
00037  * Tcl_BreakObjCmd --
00038  *
00039  *      This procedure is invoked to process the "break" Tcl command. See the
00040  *      user documentation for details on what it does.
00041  *
00042  *      With the bytecode compiler, this procedure is only called when a
00043  *      command name is computed at runtime, and is "break" or the name to
00044  *      which "break" was renamed: e.g., "set z break; $z"
00045  *
00046  * Results:
00047  *      A standard Tcl result.
00048  *
00049  * Side effects:
00050  *      See the user documentation.
00051  *
00052  *----------------------------------------------------------------------
00053  */
00054 
00055         /* ARGSUSED */
00056 int
00057 Tcl_BreakObjCmd(
00058     ClientData dummy,           /* Not used. */
00059     Tcl_Interp *interp,         /* Current interpreter. */
00060     int objc,                   /* Number of arguments. */
00061     Tcl_Obj *CONST objv[])      /* Argument objects. */
00062 {
00063     if (objc != 1) {
00064         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00065         return TCL_ERROR;
00066     }
00067     return TCL_BREAK;
00068 }
00069 
00070 /*
00071  *----------------------------------------------------------------------
00072  *
00073  * Tcl_CaseObjCmd --
00074  *
00075  *      This procedure is invoked to process the "case" Tcl command. See the
00076  *      user documentation for details on what it does. THIS COMMAND IS
00077  *      OBSOLETE AND DEPRECATED. SLATED FOR REMOVAL IN TCL 9.0.
00078  *
00079  * Results:
00080  *      A standard Tcl object result.
00081  *
00082  * Side effects:
00083  *      See the user documentation.
00084  *
00085  *----------------------------------------------------------------------
00086  */
00087 
00088         /* ARGSUSED */
00089 int
00090 Tcl_CaseObjCmd(
00091     ClientData dummy,           /* Not used. */
00092     Tcl_Interp *interp,         /* Current interpreter. */
00093     int objc,                   /* Number of arguments. */
00094     Tcl_Obj *CONST objv[])      /* Argument objects. */
00095 {
00096     register int i;
00097     int body, result, caseObjc;
00098     char *stringPtr, *arg;
00099     Tcl_Obj *CONST *caseObjv;
00100     Tcl_Obj *armPtr;
00101 
00102     if (objc < 3) {
00103         Tcl_WrongNumArgs(interp, 1, objv,
00104                 "string ?in? patList body ... ?default body?");
00105         return TCL_ERROR;
00106     }
00107 
00108     stringPtr = TclGetString(objv[1]);
00109     body = -1;
00110 
00111     arg = TclGetString(objv[2]);
00112     if (strcmp(arg, "in") == 0) {
00113         i = 3;
00114     } else {
00115         i = 2;
00116     }
00117     caseObjc = objc - i;
00118     caseObjv = objv + i;
00119 
00120     /*
00121      * If all of the pattern/command pairs are lumped into a single argument,
00122      * split them out again.
00123      */
00124 
00125     if (caseObjc == 1) {
00126         Tcl_Obj **newObjv;
00127 
00128         TclListObjGetElements(interp, caseObjv[0], &caseObjc, &newObjv);
00129         caseObjv = newObjv;
00130     }
00131 
00132     for (i = 0;  i < caseObjc;  i += 2) {
00133         int patObjc, j;
00134         CONST char **patObjv;
00135         char *pat;
00136         unsigned char *p;
00137 
00138         if (i == (caseObjc - 1)) {
00139             Tcl_ResetResult(interp);
00140             Tcl_AppendResult(interp, "extra case pattern with no body", NULL);
00141             return TCL_ERROR;
00142         }
00143 
00144         /*
00145          * Check for special case of single pattern (no list) with no
00146          * backslash sequences.
00147          */
00148 
00149         pat = TclGetString(caseObjv[i]);
00150         for (p = (unsigned char *) pat; *p != '\0'; p++) {
00151             if (isspace(*p) || (*p == '\\')) {  /* INTL: ISO space, UCHAR */
00152                 break;
00153             }
00154         }
00155         if (*p == '\0') {
00156             if ((*pat == 'd') && (strcmp(pat, "default") == 0)) {
00157                 body = i + 1;
00158             }
00159             if (Tcl_StringMatch(stringPtr, pat)) {
00160                 body = i + 1;
00161                 goto match;
00162             }
00163             continue;
00164         }
00165 
00166         /*
00167          * Break up pattern lists, then check each of the patterns in the
00168          * list.
00169          */
00170 
00171         result = Tcl_SplitList(interp, pat, &patObjc, &patObjv);
00172         if (result != TCL_OK) {
00173             return result;
00174         }
00175         for (j = 0; j < patObjc; j++) {
00176             if (Tcl_StringMatch(stringPtr, patObjv[j])) {
00177                 body = i + 1;
00178                 break;
00179             }
00180         }
00181         ckfree((char *) patObjv);
00182         if (j < patObjc) {
00183             break;
00184         }
00185     }
00186 
00187   match:
00188     if (body != -1) {
00189         armPtr = caseObjv[body - 1];
00190         result = Tcl_EvalObjEx(interp, caseObjv[body], 0);
00191         if (result == TCL_ERROR) {
00192             Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
00193                     "\n    (\"%.50s\" arm line %d)",
00194                     TclGetString(armPtr), interp->errorLine));
00195         }
00196         return result;
00197     }
00198 
00199     /*
00200      * Nothing matched: return nothing.
00201      */
00202 
00203     return TCL_OK;
00204 }
00205 
00206 /*
00207  *----------------------------------------------------------------------
00208  *
00209  * Tcl_CatchObjCmd --
00210  *
00211  *      This object-based procedure is invoked to process the "catch" Tcl
00212  *      command. See the user documentation for details on what it does.
00213  *
00214  * Results:
00215  *      A standard Tcl object result.
00216  *
00217  * Side effects:
00218  *      See the user documentation.
00219  *
00220  *----------------------------------------------------------------------
00221  */
00222 
00223         /* ARGSUSED */
00224 int
00225 Tcl_CatchObjCmd(
00226     ClientData dummy,           /* Not used. */
00227     Tcl_Interp *interp,         /* Current interpreter. */
00228     int objc,                   /* Number of arguments. */
00229     Tcl_Obj *CONST objv[])      /* Argument objects. */
00230 {
00231     Tcl_Obj *varNamePtr = NULL;
00232     Tcl_Obj *optionVarNamePtr = NULL;
00233     int result;
00234     Interp *iPtr = (Interp *) interp;
00235 
00236     if ((objc < 2) || (objc > 4)) {
00237         Tcl_WrongNumArgs(interp, 1, objv,
00238                 "script ?resultVarName? ?optionVarName?");
00239         return TCL_ERROR;
00240     }
00241 
00242     if (objc >= 3) {
00243         varNamePtr = objv[2];
00244     }
00245     if (objc == 4) {
00246         optionVarNamePtr = objv[3];
00247     }
00248 
00249     /*
00250      * TIP #280. Make invoking context available to caught script.
00251      */
00252 
00253     result = TclEvalObjEx(interp, objv[1], 0, iPtr->cmdFramePtr, 1);
00254 
00255     /*
00256      * We disable catch in interpreters where the limit has been exceeded.
00257      */
00258 
00259     if (Tcl_LimitExceeded(interp)) {
00260         Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
00261                 "\n    (\"catch\" body line %d)", interp->errorLine));
00262         return TCL_ERROR;
00263     }
00264 
00265     if (objc >= 3) {
00266         if (NULL == Tcl_ObjSetVar2(interp, varNamePtr, NULL,
00267                 Tcl_GetObjResult(interp), 0)) {
00268             Tcl_ResetResult(interp);
00269             Tcl_AppendResult(interp,
00270                     "couldn't save command result in variable", NULL);
00271             return TCL_ERROR;
00272         }
00273     }
00274     if (objc == 4) {
00275         Tcl_Obj *options = Tcl_GetReturnOptions(interp, result);
00276         if (NULL == Tcl_ObjSetVar2(interp, optionVarNamePtr, NULL,
00277                 options, 0)) {
00278             Tcl_ResetResult(interp);
00279             Tcl_AppendResult(interp,
00280                     "couldn't save return options in variable", NULL);
00281             return TCL_ERROR;
00282         }
00283     }
00284 
00285     Tcl_ResetResult(interp);
00286     Tcl_SetObjResult(interp, Tcl_NewIntObj(result));
00287     return TCL_OK;
00288 }
00289 
00290 /*
00291  *----------------------------------------------------------------------
00292  *
00293  * Tcl_CdObjCmd --
00294  *
00295  *      This procedure is invoked to process the "cd" Tcl command. See the
00296  *      user documentation for details on what it does.
00297  *
00298  * Results:
00299  *      A standard Tcl result.
00300  *
00301  * Side effects:
00302  *      See the user documentation.
00303  *
00304  *----------------------------------------------------------------------
00305  */
00306 
00307         /* ARGSUSED */
00308 int
00309 Tcl_CdObjCmd(
00310     ClientData dummy,           /* Not used. */
00311     Tcl_Interp *interp,         /* Current interpreter. */
00312     int objc,                   /* Number of arguments. */
00313     Tcl_Obj *CONST objv[])      /* Argument objects. */
00314 {
00315     Tcl_Obj *dir;
00316     int result;
00317 
00318     if (objc > 2) {
00319         Tcl_WrongNumArgs(interp, 1, objv, "?dirName?");
00320         return TCL_ERROR;
00321     }
00322 
00323     if (objc == 2) {
00324         dir = objv[1];
00325     } else {
00326         TclNewLiteralStringObj(dir, "~");
00327         Tcl_IncrRefCount(dir);
00328     }
00329     if (Tcl_FSConvertToPathType(interp, dir) != TCL_OK) {
00330         result = TCL_ERROR;
00331     } else {
00332         result = Tcl_FSChdir(dir);
00333         if (result != TCL_OK) {
00334             Tcl_AppendResult(interp, "couldn't change working directory to \"",
00335                     TclGetString(dir), "\": ", Tcl_PosixError(interp), NULL);
00336             result = TCL_ERROR;
00337         }
00338     }
00339     if (objc != 2) {
00340         Tcl_DecrRefCount(dir);
00341     }
00342     return result;
00343 }
00344 
00345 /*
00346  *----------------------------------------------------------------------
00347  *
00348  * Tcl_ConcatObjCmd --
00349  *
00350  *      This object-based procedure is invoked to process the "concat" Tcl
00351  *      command. See the user documentation for details on what it does.
00352  *
00353  * Results:
00354  *      A standard Tcl object result.
00355  *
00356  * Side effects:
00357  *      See the user documentation.
00358  *
00359  *----------------------------------------------------------------------
00360  */
00361 
00362         /* ARGSUSED */
00363 int
00364 Tcl_ConcatObjCmd(
00365     ClientData dummy,           /* Not used. */
00366     Tcl_Interp *interp,         /* Current interpreter. */
00367     int objc,                   /* Number of arguments. */
00368     Tcl_Obj *CONST objv[])      /* Argument objects. */
00369 {
00370     if (objc >= 2) {
00371         Tcl_SetObjResult(interp, Tcl_ConcatObj(objc-1, objv+1));
00372     }
00373     return TCL_OK;
00374 }
00375 
00376 /*
00377  *----------------------------------------------------------------------
00378  *
00379  * Tcl_ContinueObjCmd --
00380  *
00381  *      This procedure is invoked to process the "continue" Tcl command. See
00382  *      the user documentation for details on what it does.
00383  *
00384  *      With the bytecode compiler, this procedure is only called when a
00385  *      command name is computed at runtime, and is "continue" or the name to
00386  *      which "continue" was renamed: e.g., "set z continue; $z"
00387  *
00388  * Results:
00389  *      A standard Tcl result.
00390  *
00391  * Side effects:
00392  *      See the user documentation.
00393  *
00394  *----------------------------------------------------------------------
00395  */
00396 
00397         /* ARGSUSED */
00398 int
00399 Tcl_ContinueObjCmd(
00400     ClientData dummy,           /* Not used. */
00401     Tcl_Interp *interp,         /* Current interpreter. */
00402     int objc,                   /* Number of arguments. */
00403     Tcl_Obj *CONST objv[])      /* Argument objects. */
00404 {
00405     if (objc != 1) {
00406         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00407         return TCL_ERROR;
00408     }
00409     return TCL_CONTINUE;
00410 }
00411 
00412 /*
00413  *----------------------------------------------------------------------
00414  *
00415  * Tcl_EncodingObjCmd --
00416  *
00417  *      This command manipulates encodings.
00418  *
00419  * Results:
00420  *      A standard Tcl result.
00421  *
00422  * Side effects:
00423  *      See the user documentation.
00424  *
00425  *----------------------------------------------------------------------
00426  */
00427 
00428 int
00429 Tcl_EncodingObjCmd(
00430     ClientData dummy,           /* Not used. */
00431     Tcl_Interp *interp,         /* Current interpreter. */
00432     int objc,                   /* Number of arguments. */
00433     Tcl_Obj *CONST objv[])      /* Argument objects. */
00434 {
00435     int index;
00436 
00437     static CONST char *optionStrings[] = {
00438         "convertfrom", "convertto", "dirs", "names", "system",
00439         NULL
00440     };
00441     enum options {
00442         ENC_CONVERTFROM, ENC_CONVERTTO, ENC_DIRS, ENC_NAMES, ENC_SYSTEM
00443     };
00444 
00445     if (objc < 2) {
00446         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
00447         return TCL_ERROR;
00448     }
00449     if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
00450             &index) != TCL_OK) {
00451         return TCL_ERROR;
00452     }
00453 
00454     switch ((enum options) index) {
00455     case ENC_CONVERTTO:
00456     case ENC_CONVERTFROM: {
00457         Tcl_Obj *data;
00458         Tcl_DString ds;
00459         Tcl_Encoding encoding;
00460         int length;
00461         char *stringPtr;
00462 
00463         if (objc == 3) {
00464             encoding = Tcl_GetEncoding(interp, NULL);
00465             data = objv[2];
00466         } else if (objc == 4) {
00467             if (Tcl_GetEncodingFromObj(interp, objv[2], &encoding) != TCL_OK) {
00468                 return TCL_ERROR;
00469             }
00470             data = objv[3];
00471         } else {
00472             Tcl_WrongNumArgs(interp, 2, objv, "?encoding? data");
00473             return TCL_ERROR;
00474         }
00475 
00476         if ((enum options) index == ENC_CONVERTFROM) {
00477             /*
00478              * Treat the string as binary data.
00479              */
00480 
00481             stringPtr = (char *) Tcl_GetByteArrayFromObj(data, &length);
00482             Tcl_ExternalToUtfDString(encoding, stringPtr, length, &ds);
00483 
00484             /*
00485              * Note that we cannot use Tcl_DStringResult here because it will
00486              * truncate the string at the first null byte.
00487              */
00488 
00489             Tcl_SetObjResult(interp, Tcl_NewStringObj(
00490                     Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)));
00491             Tcl_DStringFree(&ds);
00492         } else {
00493             /*
00494              * Store the result as binary data.
00495              */
00496 
00497             stringPtr = TclGetStringFromObj(data, &length);
00498             Tcl_UtfToExternalDString(encoding, stringPtr, length, &ds);
00499             Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(
00500                     (unsigned char *) Tcl_DStringValue(&ds),
00501                     Tcl_DStringLength(&ds)));
00502             Tcl_DStringFree(&ds);
00503         }
00504 
00505         Tcl_FreeEncoding(encoding);
00506         break;
00507     }
00508     case ENC_DIRS:
00509         return EncodingDirsObjCmd(dummy, interp, objc-1, objv+1);
00510     case ENC_NAMES:
00511         if (objc > 2) {
00512             Tcl_WrongNumArgs(interp, 2, objv, NULL);
00513             return TCL_ERROR;
00514         }
00515         Tcl_GetEncodingNames(interp);
00516         break;
00517     case ENC_SYSTEM:
00518         if (objc > 3) {
00519             Tcl_WrongNumArgs(interp, 2, objv, "?encoding?");
00520             return TCL_ERROR;
00521         }
00522         if (objc == 2) {
00523             Tcl_SetObjResult(interp, Tcl_NewStringObj(
00524                     Tcl_GetEncodingName(NULL), -1));
00525         } else {
00526             return Tcl_SetSystemEncoding(interp, TclGetString(objv[2]));
00527         }
00528         break;
00529     }
00530     return TCL_OK;
00531 }
00532 
00533 /*
00534  *----------------------------------------------------------------------
00535  *
00536  * EncodingDirsObjCmd --
00537  *
00538  *      This command manipulates the encoding search path.
00539  *
00540  * Results:
00541  *      A standard Tcl result.
00542  *
00543  * Side effects:
00544  *      Can set the encoding search path.
00545  *
00546  *----------------------------------------------------------------------
00547  */
00548 
00549 int
00550 EncodingDirsObjCmd(
00551     ClientData dummy,           /* Not used. */
00552     Tcl_Interp *interp,         /* Current interpreter. */
00553     int objc,                   /* Number of arguments. */
00554     Tcl_Obj *CONST objv[])      /* Argument objects. */
00555 {
00556     if (objc > 2) {
00557         Tcl_WrongNumArgs(interp, 1, objv, "?dirList?");
00558         return TCL_ERROR;
00559     }
00560     if (objc == 1) {
00561         Tcl_SetObjResult(interp, Tcl_GetEncodingSearchPath());
00562         return TCL_OK;
00563     }
00564     if (Tcl_SetEncodingSearchPath(objv[1]) == TCL_ERROR) {
00565         Tcl_AppendResult(interp, "expected directory list but got \"",
00566                 TclGetString(objv[1]), "\"", NULL);
00567         return TCL_ERROR;
00568     }
00569     Tcl_SetObjResult(interp, objv[1]);
00570     return TCL_OK;
00571 }
00572 
00573 /*
00574  *----------------------------------------------------------------------
00575  *
00576  * Tcl_ErrorObjCmd --
00577  *
00578  *      This procedure is invoked to process the "error" Tcl command. See the
00579  *      user documentation for details on what it does.
00580  *
00581  * Results:
00582  *      A standard Tcl object result.
00583  *
00584  * Side effects:
00585  *      See the user documentation.
00586  *
00587  *----------------------------------------------------------------------
00588  */
00589 
00590         /* ARGSUSED */
00591 int
00592 Tcl_ErrorObjCmd(
00593     ClientData dummy,           /* Not used. */
00594     Tcl_Interp *interp,         /* Current interpreter. */
00595     int objc,                   /* Number of arguments. */
00596     Tcl_Obj *CONST objv[])      /* Argument objects. */
00597 {
00598     Tcl_Obj *options, *optName;
00599 
00600     if ((objc < 2) || (objc > 4)) {
00601         Tcl_WrongNumArgs(interp, 1, objv, "message ?errorInfo? ?errorCode?");
00602         return TCL_ERROR;
00603     }
00604 
00605     TclNewLiteralStringObj(options, "-code error -level 0");
00606 
00607     if (objc >= 3) {            /* Process the optional info argument */
00608         TclNewLiteralStringObj(optName, "-errorinfo");
00609         Tcl_ListObjAppendElement(NULL, options, optName);
00610         Tcl_ListObjAppendElement(NULL, options, objv[2]);
00611     }
00612 
00613     if (objc >= 4) {            /* Process the optional code argument */
00614         TclNewLiteralStringObj(optName, "-errorcode");
00615         Tcl_ListObjAppendElement(NULL, options, optName);
00616         Tcl_ListObjAppendElement(NULL, options, objv[3]);
00617     }
00618 
00619     Tcl_SetObjResult(interp, objv[1]);
00620     return Tcl_SetReturnOptions(interp, options);
00621 }
00622 
00623 /*
00624  *----------------------------------------------------------------------
00625  *
00626  * Tcl_EvalObjCmd --
00627  *
00628  *      This object-based procedure is invoked to process the "eval" Tcl
00629  *      command. See the user documentation for details on what it does.
00630  *
00631  * Results:
00632  *      A standard Tcl object result.
00633  *
00634  * Side effects:
00635  *      See the user documentation.
00636  *
00637  *----------------------------------------------------------------------
00638  */
00639 
00640         /* ARGSUSED */
00641 int
00642 Tcl_EvalObjCmd(
00643     ClientData dummy,           /* Not used. */
00644     Tcl_Interp *interp,         /* Current interpreter. */
00645     int objc,                   /* Number of arguments. */
00646     Tcl_Obj *CONST objv[])      /* Argument objects. */
00647 {
00648     int result;
00649     register Tcl_Obj *objPtr;
00650     Interp *iPtr = (Interp *) interp;
00651 
00652     if (objc < 2) {
00653         Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?");
00654         return TCL_ERROR;
00655     }
00656 
00657     if (objc == 2) {
00658         /*
00659          * TIP #280. Make invoking context available to eval'd script.
00660          */
00661 
00662         result = TclEvalObjEx(interp, objv[1], TCL_EVAL_DIRECT,
00663                 iPtr->cmdFramePtr, 1);
00664     } else {
00665         /*
00666          * More than one argument: concatenate them together with spaces
00667          * between, then evaluate the result. Tcl_EvalObjEx will delete the
00668          * object when it decrements its refcount after eval'ing it.
00669          */
00670 
00671         objPtr = Tcl_ConcatObj(objc-1, objv+1);
00672 
00673         /*
00674          * TIP #280. Make invoking context available to eval'd script.
00675          */
00676 
00677         result = TclEvalObjEx(interp, objPtr, TCL_EVAL_DIRECT, NULL, 0);
00678     }
00679     if (result == TCL_ERROR) {
00680         Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
00681                 "\n    (\"eval\" body line %d)", interp->errorLine));
00682     }
00683     return result;
00684 }
00685 
00686 /*
00687  *----------------------------------------------------------------------
00688  *
00689  * Tcl_ExitObjCmd --
00690  *
00691  *      This procedure is invoked to process the "exit" Tcl command. See the
00692  *      user documentation for details on what it does.
00693  *
00694  * Results:
00695  *      A standard Tcl object result.
00696  *
00697  * Side effects:
00698  *      See the user documentation.
00699  *
00700  *----------------------------------------------------------------------
00701  */
00702 
00703         /* ARGSUSED */
00704 int
00705 Tcl_ExitObjCmd(
00706     ClientData dummy,           /* Not used. */
00707     Tcl_Interp *interp,         /* Current interpreter. */
00708     int objc,                   /* Number of arguments. */
00709     Tcl_Obj *CONST objv[])      /* Argument objects. */
00710 {
00711     int value;
00712 
00713     if ((objc != 1) && (objc != 2)) {
00714         Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?");
00715         return TCL_ERROR;
00716     }
00717 
00718     if (objc == 1) {
00719         value = 0;
00720     } else if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) {
00721         return TCL_ERROR;
00722     }
00723     Tcl_Exit(value);
00724     /*NOTREACHED*/
00725     return TCL_OK;              /* Better not ever reach this! */
00726 }
00727 
00728 /*
00729  *----------------------------------------------------------------------
00730  *
00731  * Tcl_ExprObjCmd --
00732  *
00733  *      This object-based procedure is invoked to process the "expr" Tcl
00734  *      command. See the user documentation for details on what it does.
00735  *
00736  *      With the bytecode compiler, this procedure is called in two
00737  *      circumstances: 1) to execute expr commands that are too complicated or
00738  *      too unsafe to try compiling directly into an inline sequence of
00739  *      instructions, and 2) to execute commands where the command name is
00740  *      computed at runtime and is "expr" or the name to which "expr" was
00741  *      renamed (e.g., "set z expr; $z 2+3")
00742  *
00743  * Results:
00744  *      A standard Tcl object result.
00745  *
00746  * Side effects:
00747  *      See the user documentation.
00748  *
00749  *----------------------------------------------------------------------
00750  */
00751 
00752         /* ARGSUSED */
00753 int
00754 Tcl_ExprObjCmd(
00755     ClientData dummy,           /* Not used. */
00756     Tcl_Interp *interp,         /* Current interpreter. */
00757     int objc,                   /* Number of arguments. */
00758     Tcl_Obj *CONST objv[])      /* Argument objects. */
00759 {
00760     register Tcl_Obj *objPtr;
00761     Tcl_Obj *resultPtr;
00762     int result;
00763 
00764     if (objc < 2) {
00765         Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?");
00766         return TCL_ERROR;
00767     }
00768 
00769     objPtr = Tcl_ConcatObj(objc-1, objv+1);
00770     Tcl_IncrRefCount(objPtr);
00771     result = Tcl_ExprObj(interp, objPtr, &resultPtr);
00772     Tcl_DecrRefCount(objPtr);
00773 
00774     if (result == TCL_OK) {
00775         Tcl_SetObjResult(interp, resultPtr);
00776         Tcl_DecrRefCount(resultPtr);    /* Done with the result object */
00777     }
00778 
00779     return result;
00780 }
00781 
00782 /*
00783  *----------------------------------------------------------------------
00784  *
00785  * Tcl_FileObjCmd --
00786  *
00787  *      This procedure is invoked to process the "file" Tcl command. See the
00788  *      user documentation for details on what it does. PLEASE NOTE THAT THIS
00789  *      FAILS WITH FILENAMES AND PATHS WITH EMBEDDED NULLS. With the
00790  *      object-based Tcl_FS APIs, the above NOTE may no longer be true. In any
00791  *      case this assertion should be tested.
00792  *
00793  * Results:
00794  *      A standard Tcl result.
00795  *
00796  * Side effects:
00797  *      See the user documentation.
00798  *
00799  *----------------------------------------------------------------------
00800  */
00801 
00802         /* ARGSUSED */
00803 int
00804 Tcl_FileObjCmd(
00805     ClientData dummy,           /* Not used. */
00806     Tcl_Interp *interp,         /* Current interpreter. */
00807     int objc,                   /* Number of arguments. */
00808     Tcl_Obj *CONST objv[])      /* Argument objects. */
00809 {
00810     int index, value;
00811     Tcl_StatBuf buf;
00812     struct utimbuf tval;
00813 
00814     /*
00815      * This list of constants should match the fileOption string array below.
00816      */
00817 
00818     static CONST char *fileOptions[] = {
00819         "atime",        "attributes",   "channels",     "copy",
00820         "delete",
00821         "dirname",      "executable",   "exists",       "extension",
00822         "isdirectory",  "isfile",       "join",         "link",
00823         "lstat",        "mtime",        "mkdir",        "nativename",
00824         "normalize",    "owned",
00825         "pathtype",     "readable",     "readlink",     "rename",
00826         "rootname",     "separator",    "size",         "split",
00827         "stat",         "system",
00828         "tail",         "type",         "volumes",      "writable",
00829         NULL
00830     };
00831     enum options {
00832         FCMD_ATIME,     FCMD_ATTRIBUTES, FCMD_CHANNELS, FCMD_COPY,
00833         FCMD_DELETE,
00834         FCMD_DIRNAME,   FCMD_EXECUTABLE, FCMD_EXISTS,   FCMD_EXTENSION,
00835         FCMD_ISDIRECTORY, FCMD_ISFILE,  FCMD_JOIN,      FCMD_LINK,
00836         FCMD_LSTAT,     FCMD_MTIME,     FCMD_MKDIR,     FCMD_NATIVENAME,
00837         FCMD_NORMALIZE, FCMD_OWNED,
00838         FCMD_PATHTYPE,  FCMD_READABLE,  FCMD_READLINK,  FCMD_RENAME,
00839         FCMD_ROOTNAME,  FCMD_SEPARATOR, FCMD_SIZE,      FCMD_SPLIT,
00840         FCMD_STAT,      FCMD_SYSTEM,
00841         FCMD_TAIL,      FCMD_TYPE,      FCMD_VOLUMES,   FCMD_WRITABLE
00842     };
00843 
00844     if (objc < 2) {
00845         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
00846         return TCL_ERROR;
00847     }
00848     if (Tcl_GetIndexFromObj(interp, objv[1], fileOptions, "option", 0,
00849             &index) != TCL_OK) {
00850         return TCL_ERROR;
00851     }
00852 
00853     switch ((enum options) index) {
00854 
00855     case FCMD_ATIME:
00856     case FCMD_MTIME:
00857         if ((objc < 3) || (objc > 4)) {
00858             Tcl_WrongNumArgs(interp, 2, objv, "name ?time?");
00859             return TCL_ERROR;
00860         }
00861         if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
00862             return TCL_ERROR;
00863         }
00864         if (objc == 4) {
00865             /*
00866              * Need separate variable for reading longs from an object on
00867              * 64-bit platforms. [Bug #698146]
00868              */
00869 
00870             long newTime;
00871 
00872             if (TclGetLongFromObj(interp, objv[3], &newTime) != TCL_OK) {
00873                 return TCL_ERROR;
00874             }
00875 
00876             if (index == FCMD_ATIME) {
00877                 tval.actime = newTime;
00878                 tval.modtime = buf.st_mtime;
00879             } else {    /* index == FCMD_MTIME */
00880                 tval.actime = buf.st_atime;
00881                 tval.modtime = newTime;
00882             }
00883 
00884             if (Tcl_FSUtime(objv[2], &tval) != 0) {
00885                 Tcl_AppendResult(interp, "could not set ",
00886                         (index == FCMD_ATIME ? "access" : "modification"),
00887                         " time for file \"", TclGetString(objv[2]), "\": ",
00888                         Tcl_PosixError(interp), NULL);
00889                 return TCL_ERROR;
00890             }
00891 
00892             /*
00893              * Do another stat to ensure that the we return the new recognized
00894              * atime - hopefully the same as the one we sent in. However, fs's
00895              * like FAT don't even know what atime is.
00896              */
00897 
00898             if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
00899                 return TCL_ERROR;
00900             }
00901         }
00902 
00903         Tcl_SetObjResult(interp, Tcl_NewLongObj((long)
00904                 (index == FCMD_ATIME ? buf.st_atime : buf.st_mtime)));
00905         return TCL_OK;
00906     case FCMD_ATTRIBUTES:
00907         return TclFileAttrsCmd(interp, objc, objv);
00908     case FCMD_CHANNELS:
00909         if ((objc < 2) || (objc > 3)) {
00910             Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
00911             return TCL_ERROR;
00912         }
00913         return Tcl_GetChannelNamesEx(interp,
00914                 ((objc == 2) ? NULL : TclGetString(objv[2])));
00915     case FCMD_COPY:
00916         return TclFileCopyCmd(interp, objc, objv);
00917     case FCMD_DELETE:
00918         return TclFileDeleteCmd(interp, objc, objv);
00919     case FCMD_DIRNAME: {
00920         Tcl_Obj *dirPtr;
00921 
00922         if (objc != 3) {
00923             goto only3Args;
00924         }
00925         dirPtr = TclPathPart(interp, objv[2], TCL_PATH_DIRNAME);
00926         if (dirPtr == NULL) {
00927             return TCL_ERROR;
00928         } else {
00929             Tcl_SetObjResult(interp, dirPtr);
00930             Tcl_DecrRefCount(dirPtr);
00931             return TCL_OK;
00932         }
00933     }
00934     case FCMD_EXECUTABLE:
00935         if (objc != 3) {
00936             goto only3Args;
00937         }
00938         return CheckAccess(interp, objv[2], X_OK);
00939     case FCMD_EXISTS:
00940         if (objc != 3) {
00941             goto only3Args;
00942         }
00943         return CheckAccess(interp, objv[2], F_OK);
00944     case FCMD_EXTENSION: {
00945         Tcl_Obj *ext;
00946 
00947         if (objc != 3) {
00948             goto only3Args;
00949         }
00950         ext = TclPathPart(interp, objv[2], TCL_PATH_EXTENSION);
00951         if (ext != NULL) {
00952             Tcl_SetObjResult(interp, ext);
00953             Tcl_DecrRefCount(ext);
00954             return TCL_OK;
00955         } else {
00956             return TCL_ERROR;
00957         }
00958     }
00959     case FCMD_ISDIRECTORY:
00960         if (objc != 3) {
00961             goto only3Args;
00962         }
00963         value = 0;
00964         if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
00965             value = S_ISDIR(buf.st_mode);
00966         }
00967         Tcl_SetObjResult(interp, Tcl_NewBooleanObj(value));
00968         return TCL_OK;
00969     case FCMD_ISFILE:
00970         if (objc != 3) {
00971             goto only3Args;
00972         }
00973         value = 0;
00974         if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
00975             value = S_ISREG(buf.st_mode);
00976         }
00977         Tcl_SetObjResult(interp, Tcl_NewBooleanObj(value));
00978         return TCL_OK;
00979     case FCMD_OWNED:
00980         if (objc != 3) {
00981             goto only3Args;
00982         }
00983         value = 0;
00984         if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
00985             /*
00986              * For Windows, there are no user ids associated with a file, so
00987              * we always return 1.
00988              */
00989 
00990 #if defined(__WIN32__)
00991             value = 1;
00992 #else
00993             value = (geteuid() == buf.st_uid);
00994 #endif
00995         }
00996         Tcl_SetObjResult(interp, Tcl_NewBooleanObj(value));
00997         return TCL_OK;
00998     case FCMD_JOIN: {
00999         Tcl_Obj *resObj;
01000 
01001         if (objc < 3) {
01002             Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?");
01003             return TCL_ERROR;
01004         }
01005         resObj = Tcl_FSJoinToPath(NULL, objc - 2, objv + 2);
01006         Tcl_SetObjResult(interp, resObj);
01007         return TCL_OK;
01008     }
01009     case FCMD_LINK: {
01010         Tcl_Obj *contents;
01011         int index;
01012 
01013         if (objc < 3 || objc > 5) {
01014             Tcl_WrongNumArgs(interp, 2, objv, "?-linktype? linkname ?target?");
01015             return TCL_ERROR;
01016         }
01017 
01018         /*
01019          * Index of the 'source' argument.
01020          */
01021 
01022         if (objc == 5) {
01023             index = 3;
01024         } else {
01025             index = 2;
01026         }
01027 
01028         if (objc > 3) {
01029             int linkAction;
01030             if (objc == 5) {
01031                 /*
01032                  * We have a '-linktype' argument.
01033                  */
01034 
01035                 static CONST char *linkTypes[] = {
01036                     "-symbolic", "-hard", NULL
01037                 };
01038                 if (Tcl_GetIndexFromObj(interp, objv[2], linkTypes, "switch",
01039                         0, &linkAction) != TCL_OK) {
01040                     return TCL_ERROR;
01041                 }
01042                 if (linkAction == 0) {
01043                     linkAction = TCL_CREATE_SYMBOLIC_LINK;
01044                 } else {
01045                     linkAction = TCL_CREATE_HARD_LINK;
01046                 }
01047             } else {
01048                 linkAction = TCL_CREATE_SYMBOLIC_LINK|TCL_CREATE_HARD_LINK;
01049             }
01050             if (Tcl_FSConvertToPathType(interp, objv[index]) != TCL_OK) {
01051                 return TCL_ERROR;
01052             }
01053 
01054             /*
01055              * Create link from source to target.
01056              */
01057 
01058             contents = Tcl_FSLink(objv[index], objv[index+1], linkAction);
01059             if (contents == NULL) {
01060                 /*
01061                  * We handle three common error cases specially, and for all
01062                  * other errors, we use the standard posix error message.
01063                  */
01064 
01065                 if (errno == EEXIST) {
01066                     Tcl_AppendResult(interp, "could not create new link \"",
01067                             TclGetString(objv[index]),
01068                             "\": that path already exists", NULL);
01069                 } else if (errno == ENOENT) {
01070                     /*
01071                      * There are two cases here: either the target doesn't
01072                      * exist, or the directory of the src doesn't exist.
01073                      */
01074 
01075                     int access;
01076                     Tcl_Obj *dirPtr = TclPathPart(interp, objv[index],
01077                             TCL_PATH_DIRNAME);
01078 
01079                     if (dirPtr == NULL) {
01080                         return TCL_ERROR;
01081                     }
01082                     access = Tcl_FSAccess(dirPtr, F_OK);
01083                     Tcl_DecrRefCount(dirPtr);
01084                     if (access != 0) {
01085                         Tcl_AppendResult(interp,
01086                                 "could not create new link \"",
01087                                 TclGetString(objv[index]),
01088                                 "\": no such file or directory", NULL);
01089                     } else {
01090                         Tcl_AppendResult(interp,
01091                                 "could not create new link \"",
01092                                 TclGetString(objv[index]), "\": target \"",
01093                                 TclGetString(objv[index+1]),
01094                                 "\" doesn't exist", NULL);
01095                     }
01096                 } else {
01097                     Tcl_AppendResult(interp,
01098                             "could not create new link \"",
01099                             TclGetString(objv[index]), "\" pointing to \"",
01100                             TclGetString(objv[index+1]), "\": ",
01101                             Tcl_PosixError(interp), NULL);
01102                 }
01103                 return TCL_ERROR;
01104             }
01105         } else {
01106             if (Tcl_FSConvertToPathType(interp, objv[index]) != TCL_OK) {
01107                 return TCL_ERROR;
01108             }
01109 
01110             /*
01111              * Read link
01112              */
01113 
01114             contents = Tcl_FSLink(objv[index], NULL, 0);
01115             if (contents == NULL) {
01116                 Tcl_AppendResult(interp, "could not read link \"",
01117                         TclGetString(objv[index]), "\": ",
01118                         Tcl_PosixError(interp), NULL);
01119                 return TCL_ERROR;
01120             }
01121         }
01122         Tcl_SetObjResult(interp, contents);
01123         if (objc == 3) {
01124             /*
01125              * If we are reading a link, we need to free this result refCount.
01126              * If we are creating a link, this will just be objv[index+1], and
01127              * so we don't own it.
01128              */
01129 
01130             Tcl_DecrRefCount(contents);
01131         }
01132         return TCL_OK;
01133     }
01134     case FCMD_LSTAT:
01135         if (objc != 4) {
01136             Tcl_WrongNumArgs(interp, 2, objv, "name varName");
01137             return TCL_ERROR;
01138         }
01139         if (GetStatBuf(interp, objv[2], Tcl_FSLstat, &buf) != TCL_OK) {
01140             return TCL_ERROR;
01141         }
01142         return StoreStatData(interp, objv[3], &buf);
01143     case FCMD_STAT:
01144         if (objc != 4) {
01145             Tcl_WrongNumArgs(interp, 2, objv, "name varName");
01146             return TCL_ERROR;
01147         }
01148         if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
01149             return TCL_ERROR;
01150         }
01151         return StoreStatData(interp, objv[3], &buf);
01152     case FCMD_SIZE:
01153         if (objc != 3) {
01154             goto only3Args;
01155         }
01156         if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
01157             return TCL_ERROR;
01158         }
01159         Tcl_SetObjResult(interp,
01160                 Tcl_NewWideIntObj((Tcl_WideInt) buf.st_size));
01161         return TCL_OK;
01162     case FCMD_TYPE:
01163         if (objc != 3) {
01164             goto only3Args;
01165         }
01166         if (GetStatBuf(interp, objv[2], Tcl_FSLstat, &buf) != TCL_OK) {
01167             return TCL_ERROR;
01168         }
01169         Tcl_SetObjResult(interp, Tcl_NewStringObj(
01170                 GetTypeFromMode((unsigned short) buf.st_mode), -1));
01171         return TCL_OK;
01172     case FCMD_MKDIR:
01173         if (objc < 3) {
01174             Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?");
01175             return TCL_ERROR;
01176         }
01177         return TclFileMakeDirsCmd(interp, objc, objv);
01178     case FCMD_NATIVENAME: {
01179         CONST char *fileName;
01180         Tcl_DString ds;
01181 
01182         if (objc != 3) {
01183             goto only3Args;
01184         }
01185         fileName = TclGetString(objv[2]);
01186         fileName = Tcl_TranslateFileName(interp, fileName, &ds);
01187         if (fileName == NULL) {
01188             return TCL_ERROR;
01189         }
01190         Tcl_SetObjResult(interp, Tcl_NewStringObj(fileName,
01191                 Tcl_DStringLength(&ds)));
01192         Tcl_DStringFree(&ds);
01193         return TCL_OK;
01194     }
01195     case FCMD_NORMALIZE: {
01196         Tcl_Obj *fileName;
01197 
01198         if (objc != 3) {
01199             Tcl_WrongNumArgs(interp, 2, objv, "filename");
01200             return TCL_ERROR;
01201         }
01202 
01203         fileName = Tcl_FSGetNormalizedPath(interp, objv[2]);
01204         if (fileName == NULL) {
01205             return TCL_ERROR;
01206         }
01207         Tcl_SetObjResult(interp, fileName);
01208         return TCL_OK;
01209     }
01210     case FCMD_PATHTYPE: {
01211         Tcl_Obj *typeName;
01212 
01213         if (objc != 3) {
01214             goto only3Args;
01215         }
01216 
01217         switch (Tcl_FSGetPathType(objv[2])) {
01218         case TCL_PATH_ABSOLUTE:
01219             TclNewLiteralStringObj(typeName, "absolute");
01220             break;
01221         case TCL_PATH_RELATIVE:
01222             TclNewLiteralStringObj(typeName, "relative");
01223             break;
01224         case TCL_PATH_VOLUME_RELATIVE:
01225             TclNewLiteralStringObj(typeName, "volumerelative");
01226             break;
01227         default:
01228             return TCL_OK;
01229         }
01230         Tcl_SetObjResult(interp, typeName);
01231         return TCL_OK;
01232     }
01233     case FCMD_READABLE:
01234         if (objc != 3) {
01235             goto only3Args;
01236         }
01237         return CheckAccess(interp, objv[2], R_OK);
01238     case FCMD_READLINK: {
01239         Tcl_Obj *contents;
01240 
01241         if (objc != 3) {
01242             goto only3Args;
01243         }
01244 
01245         if (Tcl_FSConvertToPathType(interp, objv[2]) != TCL_OK) {
01246             return TCL_ERROR;
01247         }
01248 
01249         contents = Tcl_FSLink(objv[2], NULL, 0);
01250 
01251         if (contents == NULL) {
01252             Tcl_AppendResult(interp, "could not readlink \"",
01253                     TclGetString(objv[2]), "\": ", Tcl_PosixError(interp),
01254                     NULL);
01255             return TCL_ERROR;
01256         }
01257         Tcl_SetObjResult(interp, contents);
01258         Tcl_DecrRefCount(contents);
01259         return TCL_OK;
01260     }
01261     case FCMD_RENAME:
01262         return TclFileRenameCmd(interp, objc, objv);
01263     case FCMD_ROOTNAME: {
01264         Tcl_Obj *root;
01265 
01266         if (objc != 3) {
01267             goto only3Args;
01268         }
01269         root = TclPathPart(interp, objv[2], TCL_PATH_ROOT);
01270         if (root != NULL) {
01271             Tcl_SetObjResult(interp, root);
01272             Tcl_DecrRefCount(root);
01273             return TCL_OK;
01274         } else {
01275             return TCL_ERROR;
01276         }
01277     }
01278     case FCMD_SEPARATOR:
01279         if ((objc < 2) || (objc > 3)) {
01280             Tcl_WrongNumArgs(interp, 2, objv, "?name?");
01281             return TCL_ERROR;
01282         }
01283         if (objc == 2) {
01284             char *separator = NULL; /* lint */
01285 
01286             switch (tclPlatform) {
01287             case TCL_PLATFORM_UNIX:
01288                 separator = "/";
01289                 break;
01290             case TCL_PLATFORM_WINDOWS:
01291                 separator = "\\";
01292                 break;
01293             }
01294             Tcl_SetObjResult(interp, Tcl_NewStringObj(separator, 1));
01295         } else {
01296             Tcl_Obj *separatorObj = Tcl_FSPathSeparator(objv[2]);
01297 
01298             if (separatorObj == NULL) {
01299                 Tcl_SetResult(interp, "Unrecognised path", TCL_STATIC);
01300                 return TCL_ERROR;
01301             }
01302             Tcl_SetObjResult(interp, separatorObj);
01303         }
01304         return TCL_OK;
01305     case FCMD_SPLIT: {
01306         Tcl_Obj *res;
01307 
01308         if (objc != 3) {
01309             goto only3Args;
01310         }
01311         res = Tcl_FSSplitPath(objv[2], NULL);
01312         if (res == NULL) {
01313             /* How can the interp be NULL here?! DKF */
01314             if (interp != NULL) {
01315                 Tcl_AppendResult(interp, "could not read \"",
01316                         TclGetString(objv[2]),
01317                         "\": no such file or directory", NULL);
01318             }
01319             return TCL_ERROR;
01320         }
01321         Tcl_SetObjResult(interp, res);
01322         return TCL_OK;
01323     }
01324     case FCMD_SYSTEM: {
01325         Tcl_Obj *fsInfo;
01326 
01327         if (objc != 3) {
01328             goto only3Args;
01329         }
01330         fsInfo = Tcl_FSFileSystemInfo(objv[2]);
01331         if (fsInfo == NULL) {
01332             Tcl_SetResult(interp, "Unrecognised path", TCL_STATIC);
01333             return TCL_ERROR;
01334         }
01335         Tcl_SetObjResult(interp, fsInfo);
01336         return TCL_OK;
01337     }
01338     case FCMD_TAIL: {
01339         Tcl_Obj *dirPtr;
01340 
01341         if (objc != 3) {
01342             goto only3Args;
01343         }
01344         dirPtr = TclPathPart(interp, objv[2], TCL_PATH_TAIL);
01345         if (dirPtr == NULL) {
01346             return TCL_ERROR;
01347         }
01348         Tcl_SetObjResult(interp, dirPtr);
01349         Tcl_DecrRefCount(dirPtr);
01350         return TCL_OK;
01351     }
01352     case FCMD_VOLUMES:
01353         if (objc != 2) {
01354             Tcl_WrongNumArgs(interp, 2, objv, NULL);
01355             return TCL_ERROR;
01356         }
01357         Tcl_SetObjResult(interp, Tcl_FSListVolumes());
01358         return TCL_OK;
01359     case FCMD_WRITABLE:
01360         if (objc != 3) {
01361             goto only3Args;
01362         }
01363         return CheckAccess(interp, objv[2], W_OK);
01364     }
01365 
01366   only3Args:
01367     Tcl_WrongNumArgs(interp, 2, objv, "name");
01368     return TCL_ERROR;
01369 }
01370 
01371 /*
01372  *---------------------------------------------------------------------------
01373  *
01374  * CheckAccess --
01375  *
01376  *      Utility procedure used by Tcl_FileObjCmd() to query file attributes
01377  *      available through the access() system call.
01378  *
01379  * Results:
01380  *      Always returns TCL_OK. Sets interp's result to boolean true or false
01381  *      depending on whether the file has the specified attribute.
01382  *
01383  * Side effects:
01384  *      None.
01385  *
01386  *---------------------------------------------------------------------------
01387  */
01388 
01389 static int
01390 CheckAccess(
01391     Tcl_Interp *interp,         /* Interp for status return. Must not be
01392                                  * NULL. */
01393     Tcl_Obj *pathPtr,           /* Name of file to check. */
01394     int mode)                   /* Attribute to check; passed as argument to
01395                                  * access(). */
01396 {
01397     int value;
01398 
01399     if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
01400         value = 0;
01401     } else {
01402         value = (Tcl_FSAccess(pathPtr, mode) == 0);
01403     }
01404     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(value));
01405 
01406     return TCL_OK;
01407 }
01408 
01409 /*
01410  *---------------------------------------------------------------------------
01411  *
01412  * GetStatBuf --
01413  *
01414  *      Utility procedure used by Tcl_FileObjCmd() to query file attributes
01415  *      available through the stat() or lstat() system call.
01416  *
01417  * Results:
01418  *      The return value is TCL_OK if the specified file exists and can be
01419  *      stat'ed, TCL_ERROR otherwise. If TCL_ERROR is returned, an error
01420  *      message is left in interp's result. If TCL_OK is returned, *statPtr is
01421  *      filled with information about the specified file.
01422  *
01423  * Side effects:
01424  *      None.
01425  *
01426  *---------------------------------------------------------------------------
01427  */
01428 
01429 static int
01430 GetStatBuf(
01431     Tcl_Interp *interp,         /* Interp for error return. May be NULL. */
01432     Tcl_Obj *pathPtr,           /* Path name to examine. */
01433     Tcl_FSStatProc *statProc,   /* Either stat() or lstat() depending on
01434                                  * desired behavior. */
01435     Tcl_StatBuf *statPtr)       /* Filled with info about file obtained by
01436                                  * calling (*statProc)(). */
01437 {
01438     int status;
01439 
01440     if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
01441         return TCL_ERROR;
01442     }
01443 
01444     status = (*statProc)(pathPtr, statPtr);
01445 
01446     if (status < 0) {
01447         if (interp != NULL) {
01448             Tcl_AppendResult(interp, "could not read \"",
01449                     TclGetString(pathPtr), "\": ",
01450                     Tcl_PosixError(interp), NULL);
01451         }
01452         return TCL_ERROR;
01453     }
01454     return TCL_OK;
01455 }
01456 
01457 /*
01458  *----------------------------------------------------------------------
01459  *
01460  * StoreStatData --
01461  *
01462  *      This is a utility procedure that breaks out the fields of a "stat"
01463  *      structure and stores them in textual form into the elements of an
01464  *      associative array.
01465  *
01466  * Results:
01467  *      Returns a standard Tcl return value. If an error occurs then a message
01468  *      is left in interp's result.
01469  *
01470  * Side effects:
01471  *      Elements of the associative array given by "varName" are modified.
01472  *
01473  *----------------------------------------------------------------------
01474  */
01475 
01476 static int
01477 StoreStatData(
01478     Tcl_Interp *interp,         /* Interpreter for error reports. */
01479     Tcl_Obj *varName,           /* Name of associative array variable in which
01480                                  * to store stat results. */
01481     Tcl_StatBuf *statPtr)       /* Pointer to buffer containing stat data to
01482                                  * store in varName. */
01483 {
01484     Tcl_Obj *field, *value;
01485     register unsigned short mode;
01486 
01487     /*
01488      * Assume Tcl_ObjSetVar2() does not keep a copy of the field name!
01489      *
01490      * Might be a better idea to call Tcl_SetVar2Ex() instead, except we want
01491      * to have an object (i.e. possibly cached) array variable name but a
01492      * string element name, so no API exists. Messy.
01493      */
01494 
01495 #define STORE_ARY(fieldName, object) \
01496     TclNewLiteralStringObj(field, fieldName); \
01497     Tcl_IncrRefCount(field); \
01498     value = (object); \
01499     if (Tcl_ObjSetVar2(interp,varName,field,value,TCL_LEAVE_ERR_MSG)==NULL) { \
01500         TclDecrRefCount(field); \
01501         return TCL_ERROR; \
01502     } \
01503     TclDecrRefCount(field);
01504 
01505     /*
01506      * Watch out porters; the inode is meant to be an *unsigned* value, so the
01507      * cast might fail when there isn't a real arithmentic 'long long' type...
01508      */
01509 
01510     STORE_ARY("dev",    Tcl_NewLongObj((long)statPtr->st_dev));
01511     STORE_ARY("ino",    Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_ino));
01512     STORE_ARY("nlink",  Tcl_NewLongObj((long)statPtr->st_nlink));
01513     STORE_ARY("uid",    Tcl_NewLongObj((long)statPtr->st_uid));
01514     STORE_ARY("gid",    Tcl_NewLongObj((long)statPtr->st_gid));
01515     STORE_ARY("size",   Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_size));
01516 #ifdef HAVE_ST_BLOCKS
01517     STORE_ARY("blocks", Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_blocks));
01518 #endif
01519     STORE_ARY("atime",  Tcl_NewLongObj((long)statPtr->st_atime));
01520     STORE_ARY("mtime",  Tcl_NewLongObj((long)statPtr->st_mtime));
01521     STORE_ARY("ctime",  Tcl_NewLongObj((long)statPtr->st_ctime));
01522     mode = (unsigned short) statPtr->st_mode;
01523     STORE_ARY("mode",   Tcl_NewIntObj(mode));
01524     STORE_ARY("type",   Tcl_NewStringObj(GetTypeFromMode(mode), -1));
01525 #undef STORE_ARY
01526 
01527     return TCL_OK;
01528 }
01529 
01530 /*
01531  *----------------------------------------------------------------------
01532  *
01533  * GetTypeFromMode --
01534  *
01535  *      Given a mode word, returns a string identifying the type of a file.
01536  *
01537  * Results:
01538  *      A static text string giving the file type from mode.
01539  *
01540  * Side effects:
01541  *      None.
01542  *
01543  *----------------------------------------------------------------------
01544  */
01545 
01546 static char *
01547 GetTypeFromMode(
01548     int mode)
01549 {
01550     if (S_ISREG(mode)) {
01551         return "file";
01552     } else if (S_ISDIR(mode)) {
01553         return "directory";
01554     } else if (S_ISCHR(mode)) {
01555         return "characterSpecial";
01556     } else if (S_ISBLK(mode)) {
01557         return "blockSpecial";
01558     } else if (S_ISFIFO(mode)) {
01559         return "fifo";
01560 #ifdef S_ISLNK
01561     } else if (S_ISLNK(mode)) {
01562         return "link";
01563 #endif
01564 #ifdef S_ISSOCK
01565     } else if (S_ISSOCK(mode)) {
01566         return "socket";
01567 #endif
01568     }
01569     return "unknown";
01570 }
01571 
01572 /*
01573  *----------------------------------------------------------------------
01574  *
01575  * Tcl_ForObjCmd --
01576  *
01577  *      This procedure is invoked to process the "for" Tcl command. See the
01578  *      user documentation for details on what it does.
01579  *
01580  *      With the bytecode compiler, this procedure is only called when a
01581  *      command name is computed at runtime, and is "for" or the name to which
01582  *      "for" was renamed: e.g.,
01583  *      "set z for; $z {set i 0} {$i<100} {incr i} {puts $i}"
01584  *
01585  * Results:
01586  *      A standard Tcl result.
01587  *
01588  * Side effects:
01589  *      See the user documentation.
01590  *
01591  *----------------------------------------------------------------------
01592  */
01593 
01594         /* ARGSUSED */
01595 int
01596 Tcl_ForObjCmd(
01597     ClientData dummy,           /* Not used. */
01598     Tcl_Interp *interp,         /* Current interpreter. */
01599     int objc,                   /* Number of arguments. */
01600     Tcl_Obj *CONST objv[])      /* Argument objects. */
01601 {
01602     int result, value;
01603     Interp *iPtr = (Interp *) interp;
01604 
01605     if (objc != 5) {
01606         Tcl_WrongNumArgs(interp, 1, objv, "start test next command");
01607         return TCL_ERROR;
01608     }
01609 
01610     /*
01611      * TIP #280. Make invoking context available to initial script.
01612      */
01613 
01614     result = TclEvalObjEx(interp, objv[1], 0, iPtr->cmdFramePtr, 1);
01615     if (result != TCL_OK) {
01616         if (result == TCL_ERROR) {
01617             Tcl_AddErrorInfo(interp, "\n    (\"for\" initial command)");
01618         }
01619         return result;
01620     }
01621     while (1) {
01622         /*
01623          * We need to reset the result before passing it off to
01624          * Tcl_ExprBooleanObj. Otherwise, any error message will be appended
01625          * to the result of the last evaluation.
01626          */
01627 
01628         Tcl_ResetResult(interp);
01629         result = Tcl_ExprBooleanObj(interp, objv[2], &value);
01630         if (result != TCL_OK) {
01631             return result;
01632         }
01633         if (!value) {
01634             break;
01635         }
01636 
01637         /*
01638          * TIP #280. Make invoking context available to loop body.
01639          */
01640 
01641         result = TclEvalObjEx(interp, objv[4], 0, iPtr->cmdFramePtr, 4);
01642         if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
01643             if (result == TCL_ERROR) {
01644                 Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
01645                         "\n    (\"for\" body line %d)", interp->errorLine));
01646             }
01647             break;
01648         }
01649 
01650         /*
01651          * TIP #280. Make invoking context available to next script.
01652          */
01653 
01654         result = TclEvalObjEx(interp, objv[3], 0, iPtr->cmdFramePtr, 3);
01655         if (result == TCL_BREAK) {
01656             break;
01657         } else if (result != TCL_OK) {
01658             if (result == TCL_ERROR) {
01659                 Tcl_AddErrorInfo(interp, "\n    (\"for\" loop-end command)");
01660             }
01661             return result;
01662         }
01663     }
01664     if (result == TCL_BREAK) {
01665         result = TCL_OK;
01666     }
01667     if (result == TCL_OK) {
01668         Tcl_ResetResult(interp);
01669     }
01670     return result;
01671 }
01672 
01673 /*
01674  *----------------------------------------------------------------------
01675  *
01676  * Tcl_ForeachObjCmd --
01677  *
01678  *      This object-based procedure is invoked to process the "foreach" Tcl
01679  *      command. See the user documentation for details on what it does.
01680  *
01681  * Results:
01682  *      A standard Tcl object result.
01683  *
01684  * Side effects:
01685  *      See the user documentation.
01686  *
01687  *----------------------------------------------------------------------
01688  */
01689 
01690         /* ARGSUSED */
01691 int
01692 Tcl_ForeachObjCmd(
01693     ClientData dummy,           /* Not used. */
01694     Tcl_Interp *interp,         /* Current interpreter. */
01695     int objc,                   /* Number of arguments. */
01696     Tcl_Obj *CONST objv[])      /* Argument objects. */
01697 {
01698     int result = TCL_OK;
01699     int i;                      /* i selects a value list */
01700     int j, maxj;                /* Number of loop iterations */
01701     int v;                      /* v selects a loop variable */
01702     int numLists = (objc-2)/2;  /* Count of value lists */
01703     Tcl_Obj *bodyPtr;
01704     Interp *iPtr = (Interp *) interp;
01705 
01706     int *index;                 /* Array of value list indices */
01707     int *varcList;              /* # loop variables per list */
01708     Tcl_Obj ***varvList;        /* Array of var name lists */
01709     Tcl_Obj **vCopyList;        /* Copies of var name list arguments */
01710     int *argcList;              /* Array of value list sizes */
01711     Tcl_Obj ***argvList;        /* Array of value lists */
01712     Tcl_Obj **aCopyList;        /* Copies of value list arguments */
01713 
01714     if (objc < 4 || (objc%2 != 0)) {
01715         Tcl_WrongNumArgs(interp, 1, objv,
01716                 "varList list ?varList list ...? command");
01717         return TCL_ERROR;
01718     }
01719 
01720     /*
01721      * Manage numList parallel value lists.
01722      * argvList[i] is a value list counted by argcList[i]l;
01723      * varvList[i] is the list of variables associated with the value list;
01724      * varcList[i] is the number of variables associated with the value list;
01725      * index[i] is the current pointer into the value list argvList[i].
01726      */
01727 
01728     index = (int *) TclStackAlloc(interp, 3 * numLists * sizeof(int));
01729     varcList = index + numLists;
01730     argcList = varcList + numLists;
01731     memset(index, 0, 3 * numLists * sizeof(int));
01732 
01733     varvList = (Tcl_Obj ***)
01734             TclStackAlloc(interp, 2 * numLists * sizeof(Tcl_Obj **));
01735     argvList = varvList + numLists;
01736     memset(varvList, 0, 2 * numLists * sizeof(Tcl_Obj **));
01737 
01738     vCopyList = (Tcl_Obj **)
01739             TclStackAlloc(interp, 2 * numLists * sizeof(Tcl_Obj *));
01740     aCopyList = vCopyList + numLists;
01741     memset(vCopyList, 0, 2 * numLists * sizeof(Tcl_Obj *));
01742 
01743     /*
01744      * Break up the value lists and variable lists into elements.
01745      */
01746 
01747     maxj = 0;
01748     for (i=0 ; i<numLists ; i++) {
01749         
01750         vCopyList[i] = TclListObjCopy(interp, objv[1+i*2]);
01751         if (vCopyList[i] == NULL) {
01752             result = TCL_ERROR;
01753             goto done;
01754         }
01755         TclListObjGetElements(NULL, vCopyList[i], &varcList[i], &varvList[i]);
01756         if (varcList[i] < 1) {
01757             Tcl_AppendResult(interp, "foreach varlist is empty", NULL);
01758             result = TCL_ERROR;
01759             goto done;
01760         }
01761 
01762         aCopyList[i] = TclListObjCopy(interp, objv[2+i*2]);
01763         if (aCopyList[i] == NULL) {
01764             result = TCL_ERROR;
01765             goto done;
01766         }
01767         TclListObjGetElements(NULL, aCopyList[i], &argcList[i], &argvList[i]);
01768 
01769         j = argcList[i] / varcList[i];
01770         if ((argcList[i] % varcList[i]) != 0) {
01771             j++;
01772         }
01773         if (j > maxj) {
01774             maxj = j;
01775         }
01776     }
01777 
01778     /*
01779      * Iterate maxj times through the lists in parallel. If some value lists
01780      * run out of values, set loop vars to ""
01781      */
01782 
01783     bodyPtr = objv[objc-1];
01784     for (j=0 ; j<maxj ; j++) {
01785         for (i=0 ; i<numLists ; i++) {
01786             for (v=0 ; v<varcList[i] ; v++) {
01787                 int k = index[i]++;
01788                 Tcl_Obj *valuePtr, *varValuePtr;
01789 
01790                 if (k < argcList[i]) {
01791                     valuePtr = argvList[i][k];
01792                 } else {
01793                     valuePtr = Tcl_NewObj(); /* Empty string */
01794                 }
01795                 varValuePtr = Tcl_ObjSetVar2(interp, varvList[i][v], NULL,
01796                         valuePtr, TCL_LEAVE_ERR_MSG);
01797                 if (varValuePtr == NULL) {
01798                     Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
01799                             "\n    (setting foreach loop variable \"%s\"",
01800                             TclGetString(varvList[i][v])));
01801                     result = TCL_ERROR;
01802                     goto done;
01803                 }
01804             }
01805         }
01806 
01807         /*
01808          * TIP #280. Make invoking context available to loop body.
01809          */
01810 
01811         result = TclEvalObjEx(interp, bodyPtr, 0, iPtr->cmdFramePtr, objc-1);
01812         if (result != TCL_OK) {
01813             if (result == TCL_CONTINUE) {
01814                 result = TCL_OK;
01815             } else if (result == TCL_BREAK) {
01816                 result = TCL_OK;
01817                 break;
01818             } else if (result == TCL_ERROR) {
01819                 Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
01820                         "\n    (\"foreach\" body line %d)",
01821                         interp->errorLine));
01822                 break;
01823             } else {
01824                 break;
01825             }
01826         }
01827     }
01828     if (result == TCL_OK) {
01829         Tcl_ResetResult(interp);
01830     }
01831 
01832   done:
01833     for (i=0 ; i<numLists ; i++) {
01834         if (vCopyList[i]) {
01835             Tcl_DecrRefCount(vCopyList[i]);
01836         }
01837         if (aCopyList[i]) {
01838             Tcl_DecrRefCount(aCopyList[i]);
01839         }
01840     }
01841     TclStackFree(interp, vCopyList);    /* Tcl_Obj * arrays */
01842     TclStackFree(interp, varvList);     /* Tcl_Obj ** arrays */
01843     TclStackFree(interp, index);        /* int arrays */
01844     return result;
01845 }
01846 
01847 /*
01848  *----------------------------------------------------------------------
01849  *
01850  * Tcl_FormatObjCmd --
01851  *
01852  *      This procedure is invoked to process the "format" Tcl command. See
01853  *      the user documentation for details on what it does.
01854  *
01855  * Results:
01856  *      A standard Tcl result.
01857  *
01858  * Side effects:
01859  *      See the user documentation.
01860  *
01861  *----------------------------------------------------------------------
01862  */
01863 
01864         /* ARGSUSED */
01865 int
01866 Tcl_FormatObjCmd(
01867     ClientData dummy,           /* Not used. */
01868     Tcl_Interp *interp,         /* Current interpreter. */
01869     int objc,                   /* Number of arguments. */
01870     Tcl_Obj *CONST objv[])      /* Argument objects. */
01871 {
01872     Tcl_Obj *resultPtr;         /* Where result is stored finally. */
01873 
01874     if (objc < 2) {
01875         Tcl_WrongNumArgs(interp, 1, objv, "formatString ?arg arg ...?");
01876         return TCL_ERROR;
01877     }
01878 
01879     resultPtr = Tcl_Format(interp, TclGetString(objv[1]), objc-2, objv+2);
01880     if (resultPtr == NULL) {
01881         return TCL_ERROR;
01882     }
01883     Tcl_SetObjResult(interp, resultPtr);
01884     return TCL_OK;
01885 }
01886 
01887 /*
01888  * Local Variables:
01889  * mode: c
01890  * c-basic-offset: 4
01891  * fill-column: 78
01892  * End:
01893  */



Generated on Wed Mar 12 12:18:12 2008 by  doxygen 1.5.1