tclCmdAH.cGo 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 1.5.1 |