tclCmdAH.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:63k
源码类别:

通讯编程

开发平台:

Visual C++

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