|
|
1.1 ! root 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 1987 Regents of the University of California ! 9: * Permission to use, copy, modify, and distribute this ! 10: * software and its documentation for any purpose and without ! 11: * fee is hereby granted, provided that the above copyright ! 12: * notice appear in all copies. The University of California ! 13: * makes no representations about the suitability of this ! 14: * software for any purpose. It is provided "as is" without ! 15: * express or implied warranty. ! 16: */ ! 17: ! 18: #ifndef lint ! 19: static char rcsid[] = "$Header: /sprite/src/lib/tcl/RCS/tclCmdAH.c,v 1.45 90/04/18 17:09:19 ouster Exp $ SPRITE (Berkeley)"; ! 20: #pragma ref rcsid ! 21: #endif not lint ! 22: ! 23: #define _POSIX_SOURCE ! 24: ! 25: #include <ctype.h> ! 26: #include <errno.h> ! 27: #include <signal.h> ! 28: #include <stdio.h> ! 29: #include <stdlib.h> ! 30: #include <unistd.h> ! 31: #include <string.h> ! 32: #include <sys/types.h> ! 33: #include <fcntl.h> ! 34: #include <sys/stat.h> ! 35: #include <sys/wait.h> ! 36: #include "tclInt.h" ! 37: ! 38: extern long lseek(); ! 39: extern char *mktemp(); ! 40: ! 41: /* ! 42: *---------------------------------------------------------------------- ! 43: * ! 44: * Tcl_BreakCmd -- ! 45: * ! 46: * This procedure is invoked to process the "break" Tcl command. ! 47: * See the user documentation for details on what it does. ! 48: * ! 49: * Results: ! 50: * A standard Tcl result. ! 51: * ! 52: * Side effects: ! 53: * See the user documentation. ! 54: * ! 55: *---------------------------------------------------------------------- ! 56: */ ! 57: ! 58: /* ARGSUSED */ ! 59: int ! 60: Tcl_BreakCmd(dummy, interp, argc, argv) ! 61: ClientData dummy; /* Not used. */ ! 62: Tcl_Interp *interp; /* Current interpreter. */ ! 63: int argc; /* Number of arguments. */ ! 64: char **argv; /* Argument strings. */ ! 65: { ! 66: #pragma ref dummy ! 67: if (argc != 1) { ! 68: sprintf(interp->result, "too many args: should be \"%.50s\"", argv[0]); ! 69: return TCL_ERROR; ! 70: } ! 71: return TCL_BREAK; ! 72: } ! 73: ! 74: /* ! 75: *---------------------------------------------------------------------- ! 76: * ! 77: * Tcl_CaseCmd -- ! 78: * ! 79: * This procedure is invoked to process the "case" Tcl command. ! 80: * See the user documentation for details on what it does. ! 81: * ! 82: * Results: ! 83: * A standard Tcl result. ! 84: * ! 85: * Side effects: ! 86: * See the user documentation. ! 87: * ! 88: *---------------------------------------------------------------------- ! 89: */ ! 90: ! 91: /* ARGSUSED */ ! 92: int ! 93: Tcl_CaseCmd(dummy, interp, argc, argv) ! 94: ClientData dummy; /* Not used. */ ! 95: Tcl_Interp *interp; /* Current interpreter. */ ! 96: int argc; /* Number of arguments. */ ! 97: char **argv; /* Argument strings. */ ! 98: { ! 99: #pragma ref dummy ! 100: int i, result; ! 101: int body; ! 102: char *string; ! 103: ! 104: if (argc < 4) { ! 105: sprintf(interp->result, ! 106: "%s \"%.50s string [in] patList body ... [default body]\"", ! 107: "not enough args: should be", argv[0]); ! 108: return TCL_ERROR; ! 109: } ! 110: string = argv[1]; ! 111: body = NULL; ! 112: if (strcmp(argv[2], "in") == 0) { ! 113: i = 3; ! 114: } else { ! 115: i = 2; ! 116: } ! 117: for (; i < argc; i += 2) { ! 118: int patArgc, j; ! 119: char **patArgv; ! 120: register char *p; ! 121: ! 122: if (i == (argc-1)) { ! 123: sprintf(interp->result, "extra pattern with no body in \"%.50s\"", ! 124: argv[0]); ! 125: return TCL_ERROR; ! 126: } ! 127: ! 128: /* ! 129: * Check for special case of single pattern (no list) with ! 130: * no backslash sequences. ! 131: */ ! 132: ! 133: for (p = argv[i]; *p != 0; p++) { ! 134: if (isspace(*p) || (*p == '\\')) { ! 135: break; ! 136: } ! 137: } ! 138: if (*p == 0) { ! 139: if ((*argv[i] == 'd') && (strcmp(argv[i], "default") == 0)) { ! 140: body = i+1; ! 141: } ! 142: if (Tcl_StringMatch(string, argv[i])) { ! 143: body = i+1; ! 144: goto match; ! 145: } ! 146: continue; ! 147: } ! 148: ! 149: /* ! 150: * Break up pattern lists, then check each of the patterns ! 151: * in the list. ! 152: */ ! 153: ! 154: result = Tcl_SplitList(interp, argv[i], &patArgc, &patArgv); ! 155: if (result != TCL_OK) { ! 156: return result; ! 157: } ! 158: for (j = 0; j < patArgc; j++) { ! 159: if (Tcl_StringMatch(string, patArgv[j])) { ! 160: body = i+1; ! 161: break; ! 162: } ! 163: } ! 164: free((char *) patArgv); ! 165: if (j < patArgc) { ! 166: break; ! 167: } ! 168: } ! 169: ! 170: match: ! 171: if (body != NULL) { ! 172: result = Tcl_Eval(interp, argv[body], 0, (char **) NULL); ! 173: if (result == TCL_ERROR) { ! 174: char msg[100]; ! 175: sprintf(msg, " (\"%.50s\" arm line %d)", argv[i], ! 176: interp->errorLine); ! 177: Tcl_AddErrorInfo(interp, msg); ! 178: } ! 179: return result; ! 180: } ! 181: ! 182: /* ! 183: * Nothing matched: return nothing. ! 184: */ ! 185: return TCL_OK; ! 186: } ! 187: ! 188: /* ! 189: *---------------------------------------------------------------------- ! 190: * ! 191: * Tcl_CatchCmd -- ! 192: * ! 193: * This procedure is invoked to process the "catch" Tcl command. ! 194: * See the user documentation for details on what it does. ! 195: * ! 196: * Results: ! 197: * A standard Tcl result. ! 198: * ! 199: * Side effects: ! 200: * See the user documentation. ! 201: * ! 202: *---------------------------------------------------------------------- ! 203: */ ! 204: ! 205: /* ARGSUSED */ ! 206: int ! 207: Tcl_CatchCmd(dummy, interp, argc, argv) ! 208: ClientData dummy; /* Not used. */ ! 209: Tcl_Interp *interp; /* Current interpreter. */ ! 210: int argc; /* Number of arguments. */ ! 211: char **argv; /* Argument strings. */ ! 212: { ! 213: #pragma ref dummy ! 214: int result; ! 215: ! 216: if ((argc != 2) && (argc != 3)) { ! 217: sprintf(interp->result, ! 218: "wrong # args: should be \"%.50s command [varName]\"", ! 219: argv[0]); ! 220: return TCL_ERROR; ! 221: } ! 222: result = Tcl_Eval(interp, argv[1], 0, (char **) NULL); ! 223: if (argc == 3) { ! 224: Tcl_SetVar(interp, argv[2], interp->result, 0); ! 225: } ! 226: Tcl_Return(interp, (char *) NULL, TCL_STATIC); ! 227: sprintf(interp->result, "%d", result); ! 228: return TCL_OK; ! 229: } ! 230: ! 231: /* ! 232: *---------------------------------------------------------------------- ! 233: * ! 234: * Tcl_ConcatCmd -- ! 235: * ! 236: * This procedure is invoked to process the "concat" Tcl command. ! 237: * See the user documentation for details on what it does. ! 238: * ! 239: * Results: ! 240: * A standard Tcl result. ! 241: * ! 242: * Side effects: ! 243: * See the user documentation. ! 244: * ! 245: *---------------------------------------------------------------------- ! 246: */ ! 247: ! 248: /* ARGSUSED */ ! 249: int ! 250: Tcl_ConcatCmd(dummy, interp, argc, argv) ! 251: ClientData dummy; /* Not used. */ ! 252: Tcl_Interp *interp; /* Current interpreter. */ ! 253: int argc; /* Number of arguments. */ ! 254: char **argv; /* Argument strings. */ ! 255: { ! 256: #pragma ref dummy ! 257: if (argc == 1) { ! 258: sprintf(interp->result, ! 259: "not enough args: should be \"%.50s arg [arg ...]\"", ! 260: argv[0]); ! 261: return TCL_ERROR; ! 262: } ! 263: ! 264: interp->result = Tcl_Concat(argc-1, argv+1); ! 265: interp->dynamic = 1; ! 266: return TCL_OK; ! 267: } ! 268: ! 269: /* ! 270: *---------------------------------------------------------------------- ! 271: * ! 272: * Tcl_ContinueCmd -- ! 273: * ! 274: * This procedure is invoked to process the "continue" Tcl command. ! 275: * See the user documentation for details on what it does. ! 276: * ! 277: * Results: ! 278: * A standard Tcl result. ! 279: * ! 280: * Side effects: ! 281: * See the user documentation. ! 282: * ! 283: *---------------------------------------------------------------------- ! 284: */ ! 285: ! 286: /* ARGSUSED */ ! 287: int ! 288: Tcl_ContinueCmd(dummy, interp, argc, argv) ! 289: ClientData dummy; /* Not used. */ ! 290: Tcl_Interp *interp; /* Current interpreter. */ ! 291: int argc; /* Number of arguments. */ ! 292: char **argv; /* Argument strings. */ ! 293: { ! 294: #pragma ref dummy ! 295: if (argc != 1) { ! 296: sprintf(interp->result, "too many args: should be \"%.50s\"", argv[0]); ! 297: return TCL_ERROR; ! 298: } ! 299: return TCL_CONTINUE; ! 300: } ! 301: ! 302: /* ! 303: *---------------------------------------------------------------------- ! 304: * ! 305: * Tcl_ErrorCmd -- ! 306: * ! 307: * This procedure is invoked to process the "error" Tcl command. ! 308: * See the user documentation for details on what it does. ! 309: * ! 310: * Results: ! 311: * A standard Tcl result. ! 312: * ! 313: * Side effects: ! 314: * See the user documentation. ! 315: * ! 316: *---------------------------------------------------------------------- ! 317: */ ! 318: ! 319: /* ARGSUSED */ ! 320: int ! 321: Tcl_ErrorCmd(dummy, interp, argc, argv) ! 322: ClientData dummy; /* Not used. */ ! 323: Tcl_Interp *interp; /* Current interpreter. */ ! 324: int argc; /* Number of arguments. */ ! 325: char **argv; /* Argument strings. */ ! 326: { ! 327: #pragma ref dummy ! 328: Interp *iPtr = (Interp *) interp; ! 329: ! 330: if ((argc != 2) && (argc != 3)) { ! 331: sprintf(interp->result, "wrong # args: should be \"%.50s message [errorInfo]\"", ! 332: argv[0]); ! 333: return TCL_ERROR; ! 334: } ! 335: if (argc == 3) { ! 336: Tcl_AddErrorInfo(interp, argv[2]); ! 337: iPtr->flags |= ERR_ALREADY_LOGGED; ! 338: } ! 339: Tcl_Return(interp, argv[1], TCL_VOLATILE); ! 340: return TCL_ERROR; ! 341: } ! 342: ! 343: /* ! 344: *---------------------------------------------------------------------- ! 345: * ! 346: * Tcl_EvalCmd -- ! 347: * ! 348: * This procedure is invoked to process the "eval" Tcl command. ! 349: * See the user documentation for details on what it does. ! 350: * ! 351: * Results: ! 352: * A standard Tcl result. ! 353: * ! 354: * Side effects: ! 355: * See the user documentation. ! 356: * ! 357: *---------------------------------------------------------------------- ! 358: */ ! 359: ! 360: /* ARGSUSED */ ! 361: int ! 362: Tcl_EvalCmd(dummy, interp, argc, argv) ! 363: ClientData dummy; /* Not used. */ ! 364: Tcl_Interp *interp; /* Current interpreter. */ ! 365: int argc; /* Number of arguments. */ ! 366: char **argv; /* Argument strings. */ ! 367: { ! 368: #pragma ref dummy ! 369: int result; ! 370: char *cmd; ! 371: ! 372: if (argc < 2) { ! 373: sprintf(interp->result, ! 374: "not enough args: should be \"%.50s arg [arg ...]\"", ! 375: argv[0]); ! 376: return TCL_ERROR; ! 377: } ! 378: if (argc == 2) { ! 379: result = Tcl_Eval(interp, argv[1], 0, (char **) NULL); ! 380: } else { ! 381: ! 382: /* ! 383: * More than one argument: concatenate them together with spaces ! 384: * between, then evaluate the result. ! 385: */ ! 386: ! 387: cmd = Tcl_Concat(argc-1, argv+1); ! 388: result = Tcl_Eval(interp, cmd, 0, (char **) NULL); ! 389: free(cmd); ! 390: } ! 391: if (result == TCL_ERROR) { ! 392: char msg[60]; ! 393: sprintf(msg, " (\"eval\" body line %d)", interp->errorLine); ! 394: Tcl_AddErrorInfo(interp, msg); ! 395: } ! 396: return result; ! 397: } ! 398: ! 399: /* ! 400: *---------------------------------------------------------------------- ! 401: * ! 402: * Tcl_ExecCmd -- ! 403: * ! 404: * This procedure is invoked to process the "exec" Tcl command. ! 405: * See the user documentation for details on what it does. ! 406: * ! 407: * Results: ! 408: * A standard Tcl result. ! 409: * ! 410: * Side effects: ! 411: * See the user documentation. ! 412: * ! 413: *---------------------------------------------------------------------- ! 414: */ ! 415: ! 416: /* ARGSUSED */ ! 417: int ! 418: Tcl_ExecCmd(dummy, interp, argc, argv) ! 419: ClientData dummy; /* Not used. */ ! 420: Tcl_Interp *interp; /* Current interpreter. */ ! 421: int argc; /* Number of arguments. */ ! 422: char **argv; /* Argument strings. */ ! 423: { ! 424: #pragma ref dummy ! 425: char *input = ""; /* Points to the input remaining to ! 426: * send to the child process. */ ! 427: int inputSize; /* # of bytes of input. */ ! 428: #define MAX_PIPE_INPUT 4095 ! 429: #define TMP_FILE_NAME "/tmp/tcl.XXXXXX" ! 430: char *output = NULL; /* Output received from child. */ ! 431: int outputSize; /* Number of valid bytes at output. */ ! 432: int outputSpace; /* Total space available at output. */ ! 433: int stdIn[2], stdOut[2], count, result, i; ! 434: int pid = -1; /* -1 means child process doesn't ! 435: * exist (yet). Non-zero gives its ! 436: * id (0 only in child). */ ! 437: int status; ! 438: char *cmdName, *execName; ! 439: ! 440: /* ! 441: * Look through the arguments for a standard input specification ! 442: * ("< value" in two arguments). If found, collapse it out. ! 443: * Shuffle all the arguments back over the "exec" argument, so that ! 444: * there's room for a NULL argument at the end. ! 445: */ ! 446: ! 447: cmdName = argv[0]; ! 448: for (i = 1; i < argc; i++) { ! 449: argv[i-1] = argv[i]; ! 450: if ((argv[i][0] != '<') || (argv[i][1] != 0)) { ! 451: continue; ! 452: } ! 453: i++; ! 454: if (i >= argc) { ! 455: sprintf(interp->result, ! 456: "specified \"<\" but no input in \"%.50s\" command", ! 457: cmdName); ! 458: return TCL_ERROR; ! 459: } ! 460: input = argv[i]; ! 461: for (i++; i < argc; i++) { ! 462: argv[i-3] = argv[i]; ! 463: } ! 464: argc -= 2; ! 465: } ! 466: ! 467: argc -= 1; /* Drop "exec" argument. */ ! 468: argv[argc] = NULL; ! 469: if (argc < 1) { ! 470: sprintf(interp->result, "not enough arguments to \"%.50s\" command", ! 471: cmdName); ! 472: return TCL_ERROR; ! 473: } ! 474: execName = Tcl_TildeSubst(interp, argv[0]); ! 475: if (execName == NULL) { ! 476: return TCL_ERROR; ! 477: } ! 478: ! 479: /* ! 480: * Set up the input stream for child. Use a pipe if the amount of ! 481: * input data is small enough for us to write it to the pipe without ! 482: * overflowing the pipe and blocking. If there's too much input data, ! 483: * then write it to a temporary file. ! 484: */ ! 485: ! 486: stdIn[0] = stdIn[1] = stdOut[0] = stdOut[1] = -1; ! 487: inputSize = strlen(input); ! 488: if (inputSize <= MAX_PIPE_INPUT) { ! 489: if (pipe(stdIn) < 0) { ! 490: sprintf(interp->result, ! 491: "couldn't create input pipe for \"%.50s\" command: %.50s", ! 492: cmdName, strerror(errno)); ! 493: result = TCL_ERROR; ! 494: goto cleanup; ! 495: } ! 496: if (write(stdIn[1], input, inputSize) != inputSize) { ! 497: sprintf(interp->result, ! 498: "couldn't write pipe input for command: %.50s", ! 499: strerror(errno)); ! 500: result = TCL_ERROR; ! 501: goto cleanup; ! 502: } ! 503: close(stdIn[1]); ! 504: stdIn[1] = -1; ! 505: } else { ! 506: char tmp[L_tmpnam]; ! 507: tmpnam(tmp); ! 508: stdIn[0] = open(tmp, O_RDWR|O_CREAT, 0); ! 509: if (stdIn[0] < 0) { ! 510: sprintf(interp->result, ! 511: "couldn't create input file for \"%.50s\" command: %.50s", ! 512: cmdName, strerror(errno)); ! 513: result = TCL_ERROR; ! 514: goto cleanup; ! 515: } ! 516: if (write(stdIn[0], input, inputSize) != inputSize) { ! 517: sprintf(interp->result, ! 518: "couldn't write file input for command: %.50s", ! 519: strerror(errno)); ! 520: result = TCL_ERROR; ! 521: goto cleanup; ! 522: } ! 523: if ((lseek(stdIn[0], 0L, 0) == -1) || (unlink(tmp) == -1)) { ! 524: sprintf(interp->result, ! 525: "couldn't reset or close input file for command: %.50s", ! 526: strerror(errno)); ! 527: result = TCL_ERROR; ! 528: goto cleanup; ! 529: } ! 530: } ! 531: ! 532: /* ! 533: * Set up an output pipe from the child's stdout/stderr back to ! 534: * us, then fork the child. ! 535: */ ! 536: ! 537: if (pipe(stdOut) < 0) { ! 538: sprintf(interp->result, ! 539: "couldn't create output pipe for \"%.50s\" command", ! 540: cmdName); ! 541: result = TCL_ERROR; ! 542: goto cleanup; ! 543: } ! 544: pid = fork(); ! 545: if (pid == -1) { ! 546: sprintf(interp->result, ! 547: "couldn't fork child for \"%.50s\" command: %.50s", ! 548: cmdName, strerror(errno)); ! 549: result = TCL_ERROR; ! 550: goto cleanup; ! 551: } ! 552: if (pid == 0) { ! 553: char errSpace[100]; ! 554: ! 555: if ((dup2(stdIn[0], 0) == -1) || (dup2(stdOut[1], 1) == -1) ! 556: || (dup2(stdOut[1], 2) == -1)) { ! 557: char *err; ! 558: err = "forked process couldn't set up input/output"; ! 559: write(stdOut[1], err, strlen(err)); ! 560: _exit(1); ! 561: } ! 562: close(stdIn[0]); ! 563: close(stdOut[0]); ! 564: close(stdOut[1]); ! 565: execvp(execName, argv); ! 566: sprintf(errSpace, "couldn't find a \"%.50s\" to execute", argv[0]); ! 567: write(1, errSpace, strlen(errSpace)); ! 568: _exit(1); ! 569: } ! 570: ! 571: /* ! 572: * In the parent, read output from the child until end of file ! 573: * (this should mean that the child has completed and died). ! 574: */ ! 575: ! 576: close(stdIn[0]); ! 577: stdIn[0] = -1; ! 578: close(stdOut[1]); ! 579: stdOut[1] = -1; ! 580: outputSize = 0; ! 581: outputSpace = 0; ! 582: result = -1; ! 583: while (1) { ! 584: if ((outputSpace - outputSize) < 100) { ! 585: char *newOutput; ! 586: ! 587: if (outputSpace == 0) { ! 588: outputSpace = 200; ! 589: } else { ! 590: outputSpace = 2*outputSpace; ! 591: } ! 592: newOutput = (char *) malloc((unsigned) outputSpace); ! 593: if (output != 0) { ! 594: bcopy(output, newOutput, outputSize); ! 595: free(output); ! 596: } ! 597: output = newOutput; ! 598: } ! 599: count = read(stdOut[0], output+outputSize, ! 600: outputSpace-outputSize-1); ! 601: ! 602: if (count == 0) { ! 603: break; ! 604: } ! 605: if (count < 0) { ! 606: sprintf(interp->result, ! 607: "error reading stdout during \"%.50s\": %.50s", ! 608: cmdName, strerror(errno)); ! 609: result = TCL_ERROR; ! 610: goto cleanup; ! 611: } ! 612: outputSize += count; ! 613: } ! 614: ! 615: /* ! 616: * The command is supposedly done now. Terminate the result ! 617: * string and wait for the process really to complete. ! 618: */ ! 619: ! 620: output[outputSize] = 0; ! 621: interp->result = output; ! 622: interp->dynamic = 1; ! 623: ! 624: cleanup: ! 625: if (pid != -1) { ! 626: while (1) { ! 627: int child; ! 628: ! 629: child = wait(&status); ! 630: if (child == -1) { ! 631: sprintf(interp->result, ! 632: "child process disappeared mysteriously"); ! 633: result = TCL_ERROR; ! 634: break; ! 635: } ! 636: if (child == pid) { ! 637: break; ! 638: } ! 639: } ! 640: if (!WIFEXITED(status)) { ! 641: sprintf(interp->result, "command terminated abnormally"); ! 642: result = TCL_ERROR; ! 643: } ! 644: result = status; ! 645: } ! 646: if (stdIn[0] != -1) { ! 647: close(stdIn[0]); ! 648: } ! 649: if (stdIn[1] != -1) { ! 650: close(stdIn[1]); ! 651: } ! 652: if (stdOut[0] != -1) { ! 653: close(stdOut[0]); ! 654: } ! 655: if (stdOut[1] != -1) { ! 656: close(stdOut[1]); ! 657: } ! 658: return result; ! 659: } ! 660: ! 661: /* ! 662: *---------------------------------------------------------------------- ! 663: * ! 664: * Tcl_ExprCmd -- ! 665: * ! 666: * This procedure is invoked to process the "expr" Tcl command. ! 667: * See the user documentation for details on what it does. ! 668: * ! 669: * Results: ! 670: * A standard Tcl result. ! 671: * ! 672: * Side effects: ! 673: * See the user documentation. ! 674: * ! 675: *---------------------------------------------------------------------- ! 676: */ ! 677: ! 678: /* ARGSUSED */ ! 679: int ! 680: Tcl_ExprCmd(dummy, interp, argc, argv) ! 681: ClientData dummy; /* Not used. */ ! 682: Tcl_Interp *interp; /* Current interpreter. */ ! 683: int argc; /* Number of arguments. */ ! 684: char **argv; /* Argument strings. */ ! 685: { ! 686: #pragma ref dummy ! 687: int result, value; ! 688: ! 689: if (argc != 2) { ! 690: sprintf(interp->result, ! 691: "wrong # args: should be \"%.50s expression\"", argv[0]); ! 692: return TCL_ERROR; ! 693: } ! 694: ! 695: result = Tcl_Expr(interp, argv[1], &value); ! 696: if (result != TCL_OK) { ! 697: return result; ! 698: } ! 699: ! 700: /* ! 701: * Turn the integer result back into a string. ! 702: */ ! 703: ! 704: sprintf(interp->result, "%d", value); ! 705: return TCL_OK; ! 706: } ! 707: ! 708: /* ! 709: *---------------------------------------------------------------------- ! 710: * ! 711: * Tcl_FileCmd -- ! 712: * ! 713: * This procedure is invoked to process the "file" Tcl command. ! 714: * See the user documentation for details on what it does. ! 715: * ! 716: * Results: ! 717: * A standard Tcl result. ! 718: * ! 719: * Side effects: ! 720: * See the user documentation. ! 721: * ! 722: *---------------------------------------------------------------------- ! 723: */ ! 724: ! 725: /* ARGSUSED */ ! 726: int ! 727: Tcl_FileCmd(dummy, interp, argc, argv) ! 728: ClientData dummy; /* Not used. */ ! 729: Tcl_Interp *interp; /* Current interpreter. */ ! 730: int argc; /* Number of arguments. */ ! 731: char **argv; /* Argument strings. */ ! 732: { ! 733: #pragma ref dummy ! 734: char *p; ! 735: int length, mode, statOp; ! 736: struct stat statBuf; ! 737: char *fileName; ! 738: ! 739: if (argc != 3) { ! 740: sprintf(interp->result, ! 741: "wrong # args: should be \"%.50s name option\"", argv[0]); ! 742: return TCL_ERROR; ! 743: } ! 744: length = strlen(argv[2]); ! 745: ! 746: /* ! 747: * First handle operations on the file name. ! 748: */ ! 749: ! 750: fileName = Tcl_TildeSubst(interp, argv[1]); ! 751: if ((argv[2][0] == 'd') && (strncmp(argv[2], "dirname", length) == 0)) { ! 752: p = strrchr(fileName, '/'); ! 753: if (p == NULL) { ! 754: interp->result = "."; ! 755: } else if (p == fileName) { ! 756: interp->result = "/"; ! 757: } else { ! 758: *p = 0; ! 759: Tcl_Return(interp, fileName, TCL_VOLATILE); ! 760: *p = '/'; ! 761: } ! 762: return TCL_OK; ! 763: } else if ((argv[2][0] == 'r') && (length >= 2) ! 764: && (strncmp(argv[2], "rootname", length) == 0)) { ! 765: p = strrchr(fileName, '.'); ! 766: if (p == NULL) { ! 767: Tcl_Return(interp, fileName, TCL_VOLATILE); ! 768: } else { ! 769: *p = 0; ! 770: Tcl_Return(interp, fileName, TCL_VOLATILE); ! 771: *p = '.'; ! 772: } ! 773: return TCL_OK; ! 774: } else if ((argv[2][0] == 'e') && (length >= 3) ! 775: && (strncmp(argv[2], "extension", length) == 0)) { ! 776: char *lastSlash; ! 777: ! 778: p = strrchr(fileName, '.'); ! 779: lastSlash = strrchr(fileName, '/'); ! 780: if ((p != NULL) && ((lastSlash == NULL) || (lastSlash < p))) { ! 781: Tcl_Return(interp, p, TCL_VOLATILE); ! 782: } ! 783: return TCL_OK; ! 784: } else if ((argv[2][0] == 't') && (strncmp(argv[2], "tail", length) == 0)) { ! 785: p = strrchr(fileName, '/'); ! 786: if (p != NULL) { ! 787: Tcl_Return(interp, p+1, TCL_VOLATILE); ! 788: } else { ! 789: Tcl_Return(interp, fileName, TCL_VOLATILE); ! 790: } ! 791: return TCL_OK; ! 792: } ! 793: ! 794: /* ! 795: * Next, handle operations that can be satisfied with the "access" ! 796: * kernel call. ! 797: */ ! 798: ! 799: if (fileName == NULL) { ! 800: return TCL_ERROR; ! 801: } ! 802: if ((argv[2][0] == 'r') && (length >= 2) ! 803: && (strncmp(argv[2], "readable", length) == 0)) { ! 804: mode = R_OK; ! 805: checkAccess: ! 806: if (access(fileName, mode) == -1) { ! 807: interp->result = "0"; ! 808: } else { ! 809: interp->result = "1"; ! 810: } ! 811: return TCL_OK; ! 812: } else if ((argv[2][0] == 'w') ! 813: && (strncmp(argv[2], "writable", length) == 0)) { ! 814: mode = W_OK; ! 815: goto checkAccess; ! 816: } else if ((argv[2][0] == 'e') && (length >= 3) ! 817: && (strncmp(argv[2], "executable", length) == 0)) { ! 818: mode = X_OK; ! 819: goto checkAccess; ! 820: } else if ((argv[2][0] == 'e') && (length >= 3) ! 821: && (strncmp(argv[2], "exists", length) == 0)) { ! 822: mode = F_OK; ! 823: goto checkAccess; ! 824: } ! 825: ! 826: /* ! 827: * Lastly, check stuff that requires the file to be stat-ed. ! 828: */ ! 829: ! 830: if ((argv[2][0] == 'o') && (strncmp(argv[2], "owned", length) == 0)) { ! 831: statOp = 0; ! 832: } else if ((argv[2][0] == 'i') && (length >= 3) ! 833: && (strncmp(argv[2], "isfile", length) == 0)) { ! 834: statOp = 1; ! 835: } else if ((argv[2][0] == 'i') && (length >= 3) ! 836: && (strncmp(argv[2], "isdirectory", length) == 0)) { ! 837: statOp = 2; ! 838: } else { ! 839: sprintf(interp->result, "bad \"%.30s\" option \"%.30s\": must be dirname, executable, exists, extension, isdirectory, isfile, owned, readable, root, tail, or writable", ! 840: argv[0], argv[2]); ! 841: return TCL_ERROR; ! 842: } ! 843: if (stat(fileName, &statBuf) == -1) { ! 844: interp->result = "0"; ! 845: return TCL_OK; ! 846: } ! 847: switch (statOp) { ! 848: case 0: ! 849: mode = (geteuid() == statBuf.st_uid); ! 850: break; ! 851: case 1: ! 852: mode = S_ISREG(statBuf.st_mode); ! 853: break; ! 854: case 2: ! 855: mode = S_ISDIR(statBuf.st_mode); ! 856: break; ! 857: } ! 858: if (mode) { ! 859: interp->result = "1"; ! 860: } else { ! 861: interp->result = "0"; ! 862: } ! 863: return TCL_OK; ! 864: } ! 865: ! 866: /* ! 867: *---------------------------------------------------------------------- ! 868: * ! 869: * Tcl_ForCmd -- ! 870: * ! 871: * This procedure is invoked to process the "for" Tcl command. ! 872: * See the user documentation for details on what it does. ! 873: * ! 874: * Results: ! 875: * A standard Tcl result. ! 876: * ! 877: * Side effects: ! 878: * See the user documentation. ! 879: * ! 880: *---------------------------------------------------------------------- ! 881: */ ! 882: ! 883: /* ARGSUSED */ ! 884: int ! 885: Tcl_ForCmd(dummy, interp, argc, argv) ! 886: ClientData dummy; /* Not used. */ ! 887: Tcl_Interp *interp; /* Current interpreter. */ ! 888: int argc; /* Number of arguments. */ ! 889: char **argv; /* Argument strings. */ ! 890: { ! 891: #pragma ref dummy ! 892: int result, value; ! 893: ! 894: if (argc != 5) { ! 895: sprintf(interp->result, ! 896: "wrong # args: should be \"%.50s start test next command\"", ! 897: argv[0]); ! 898: return TCL_ERROR; ! 899: } ! 900: ! 901: result = Tcl_Eval(interp, argv[1], 0, (char **) NULL); ! 902: if (result != TCL_OK) { ! 903: if (result == TCL_ERROR) { ! 904: Tcl_AddErrorInfo(interp, " (\"for\" initial command)"); ! 905: } ! 906: return result; ! 907: } ! 908: while (1) { ! 909: result = Tcl_Expr(interp, argv[2], &value); ! 910: if (result != TCL_OK) { ! 911: return result; ! 912: } ! 913: if (!value) { ! 914: break; ! 915: } ! 916: result = Tcl_Eval(interp, argv[4], 0, (char **) NULL); ! 917: if (result == TCL_CONTINUE) { ! 918: result = TCL_OK; ! 919: } else if (result != TCL_OK) { ! 920: if (result == TCL_ERROR) { ! 921: char msg[60]; ! 922: sprintf(msg, " (\"for\" body line %d)", interp->errorLine); ! 923: Tcl_AddErrorInfo(interp, msg); ! 924: } ! 925: break; ! 926: } ! 927: result = Tcl_Eval(interp, argv[3], 0, (char **) NULL); ! 928: if (result == TCL_BREAK) { ! 929: break; ! 930: } else if (result != TCL_OK) { ! 931: if (result == TCL_ERROR) { ! 932: Tcl_AddErrorInfo(interp, " (\"for\" loop-end command)"); ! 933: } ! 934: return result; ! 935: } ! 936: } ! 937: if (result == TCL_BREAK) { ! 938: result = TCL_OK; ! 939: } ! 940: if (result == TCL_OK) { ! 941: Tcl_Return(interp, (char *) NULL, TCL_STATIC); ! 942: } ! 943: return result; ! 944: } ! 945: ! 946: /* ! 947: *---------------------------------------------------------------------- ! 948: * ! 949: * Tcl_ForeachCmd -- ! 950: * ! 951: * This procedure is invoked to process the "foreach" Tcl command. ! 952: * See the user documentation for details on what it does. ! 953: * ! 954: * Results: ! 955: * A standard Tcl result. ! 956: * ! 957: * Side effects: ! 958: * See the user documentation. ! 959: * ! 960: *---------------------------------------------------------------------- ! 961: */ ! 962: ! 963: /* ARGSUSED */ ! 964: int ! 965: Tcl_ForeachCmd(dummy, interp, argc, argv) ! 966: ClientData dummy; /* Not used. */ ! 967: Tcl_Interp *interp; /* Current interpreter. */ ! 968: int argc; /* Number of arguments. */ ! 969: char **argv; /* Argument strings. */ ! 970: { ! 971: #pragma ref dummy ! 972: int listArgc, i, result; ! 973: char **listArgv; ! 974: ! 975: if (argc != 4) { ! 976: sprintf(interp->result, ! 977: "wrong # args: should be \"%.50s varName list command\"", ! 978: argv[0]); ! 979: return TCL_ERROR; ! 980: } ! 981: ! 982: /* ! 983: * Break the list up into elements, and execute the command once ! 984: * for each value of the element. ! 985: */ ! 986: ! 987: result = Tcl_SplitList(interp, argv[2], &listArgc, &listArgv); ! 988: if (result != TCL_OK) { ! 989: return result; ! 990: } ! 991: for (i = 0; i < listArgc; i++) { ! 992: Tcl_SetVar(interp, argv[1], listArgv[i], 0); ! 993: ! 994: result = Tcl_Eval(interp, argv[3], 0, (char **) NULL); ! 995: if (result != TCL_OK) { ! 996: if (result == TCL_CONTINUE) { ! 997: result = TCL_OK; ! 998: } else if (result == TCL_BREAK) { ! 999: result = TCL_OK; ! 1000: break; ! 1001: } else if (result == TCL_ERROR) { ! 1002: char msg[100]; ! 1003: sprintf(msg, " (\"foreach\" body line %d)", interp->errorLine); ! 1004: Tcl_AddErrorInfo(interp, msg); ! 1005: break; ! 1006: } else { ! 1007: break; ! 1008: } ! 1009: } ! 1010: } ! 1011: free((char *) listArgv); ! 1012: if (result == TCL_OK) { ! 1013: Tcl_Return(interp, (char *) NULL, TCL_STATIC); ! 1014: } ! 1015: return result; ! 1016: } ! 1017: ! 1018: /* ! 1019: *---------------------------------------------------------------------- ! 1020: * ! 1021: * Tcl_FormatCmd -- ! 1022: * ! 1023: * This procedure is invoked to process the "format" Tcl command. ! 1024: * See the user documentation for details on what it does. ! 1025: * ! 1026: * Results: ! 1027: * A standard Tcl result. ! 1028: * ! 1029: * Side effects: ! 1030: * See the user documentation. ! 1031: * ! 1032: *---------------------------------------------------------------------- ! 1033: */ ! 1034: ! 1035: /* ARGSUSED */ ! 1036: int ! 1037: Tcl_FormatCmd(dummy, interp, argc, argv) ! 1038: ClientData dummy; /* Not used. */ ! 1039: Tcl_Interp *interp; /* Current interpreter. */ ! 1040: int argc; /* Number of arguments. */ ! 1041: char **argv; /* Argument strings. */ ! 1042: { ! 1043: #pragma ref dummy ! 1044: register char *format; /* Used to read characters from the format ! 1045: * string. */ ! 1046: char newFormat[40]; /* A new format specifier is generated here. */ ! 1047: int width; /* Field width from field specifier, or 0 if ! 1048: * no width given. */ ! 1049: int precision; /* Field precision from field specifier, or 0 ! 1050: * if no precision given. */ ! 1051: int size; /* Number of bytes needed for result of ! 1052: * conversion, based on type of conversion ! 1053: * ("e", "s", etc.) and width from above. */ ! 1054: char *oneWordValue; /* Used to hold value to pass to sprintf, if ! 1055: * it's a one-word value. */ ! 1056: double twoWordValue; /* Used to hold value to pass to sprintf if ! 1057: * it's a two-word value. */ ! 1058: int useTwoWords; /* 0 means use oneWordValue, 1 means use ! 1059: * twoWordValue. */ ! 1060: char *dst = interp->result; /* Where result is stored. Starts off at ! 1061: * interp->resultSpace, but may get dynamically ! 1062: * re-allocated if this isn't enough. */ ! 1063: int dstSize = 0; /* Number of non-null characters currently ! 1064: * stored at dst. */ ! 1065: int dstSpace = TCL_RESULT_SIZE; ! 1066: /* Total amount of storage space available ! 1067: * in dst (not including null terminator. */ ! 1068: int noPercent; /* Special case for speed: indicates there's ! 1069: * no field specifier, just a string to copy. */ ! 1070: char **curArg; /* Remainder of argv array. */ ! 1071: ! 1072: /* ! 1073: * This procedure is a bit nasty. The goal is to use sprintf to ! 1074: * do most of the dirty work. There are several problems: ! 1075: * 1. this procedure can't trust its arguments. ! 1076: * 2. we must be able to provide a large enough result area to hold ! 1077: * whatever's generated. This is hard to estimate. ! 1078: * 2. there's no way to move the arguments from argv to the call ! 1079: * to sprintf in a reasonable way. This is particularly nasty ! 1080: * because some of the arguments may be two-word values (doubles). ! 1081: * So, what happens here is to scan the format string one % group ! 1082: * at a time, making many individual calls to sprintf. ! 1083: */ ! 1084: ! 1085: if (argc < 2) { ! 1086: sprintf(interp->result, ! 1087: "too few args: should be \"%.50s formatString [arg arg ...]\"", ! 1088: argv[0]); ! 1089: return TCL_ERROR; ! 1090: } ! 1091: curArg = argv+2; ! 1092: argc -= 2; ! 1093: for (format = argv[1]; *format != 0; ) { ! 1094: register char *newPtr = newFormat; ! 1095: ! 1096: width = precision = useTwoWords = noPercent = 0; ! 1097: ! 1098: /* ! 1099: * Get rid of any characters before the next field specifier. ! 1100: * Collapse backslash sequences found along the way. ! 1101: */ ! 1102: ! 1103: if (*format != '%') { ! 1104: register char *p; ! 1105: int bsSize; ! 1106: ! 1107: oneWordValue = format; ! 1108: for (p = format; (*format != '%') && (*format != 0); p++) { ! 1109: if (*format == '\\') { ! 1110: *p = Tcl_Backslash(format, &bsSize); ! 1111: format += bsSize; ! 1112: } else { ! 1113: *p = *format; ! 1114: format++; ! 1115: } ! 1116: } ! 1117: size = p - oneWordValue; ! 1118: noPercent = 1; ! 1119: goto doField; ! 1120: } ! 1121: ! 1122: if (format[1] == '%') { ! 1123: oneWordValue = format; ! 1124: size = 1; ! 1125: noPercent = 1; ! 1126: format += 2; ! 1127: goto doField; ! 1128: } ! 1129: ! 1130: /* ! 1131: * Parse off a field specifier, compute how many characters ! 1132: * will be needed to store the result, and substitute for ! 1133: * "*" size specifiers. ! 1134: */ ! 1135: ! 1136: *newPtr = '%'; ! 1137: newPtr++; ! 1138: format++; ! 1139: if (*format == '-') { ! 1140: *newPtr = '-'; ! 1141: newPtr++; ! 1142: format++; ! 1143: } ! 1144: if (*format == '0') { ! 1145: *newPtr = '0'; ! 1146: newPtr++; ! 1147: format++; ! 1148: } ! 1149: if (isdigit(*format)) { ! 1150: width = atoi(format); ! 1151: do { ! 1152: format++; ! 1153: } while (isdigit(*format)); ! 1154: } else if (*format == '*') { ! 1155: if (argc <= 0) { ! 1156: goto notEnoughArgs; ! 1157: } ! 1158: width = atoi(*curArg); ! 1159: argc--; ! 1160: curArg++; ! 1161: format++; ! 1162: } ! 1163: if (width != 0) { ! 1164: sprintf(newPtr, "%d", width); ! 1165: while (*newPtr != 0) { ! 1166: newPtr++; ! 1167: } ! 1168: } ! 1169: if (*format == '.') { ! 1170: *newPtr = '.'; ! 1171: newPtr++; ! 1172: format++; ! 1173: } ! 1174: if (isdigit(*format)) { ! 1175: precision = atoi(format); ! 1176: do { ! 1177: format++; ! 1178: } while (isdigit(*format)); ! 1179: } else if (*format == '*') { ! 1180: if (argc <= 0) { ! 1181: goto notEnoughArgs; ! 1182: } ! 1183: precision = atoi(*curArg); ! 1184: argc--; ! 1185: curArg++; ! 1186: format++; ! 1187: } ! 1188: if (precision != 0) { ! 1189: sprintf(newPtr, "%d", precision); ! 1190: while (*newPtr != 0) { ! 1191: newPtr++; ! 1192: } ! 1193: } ! 1194: if (*format == '#') { ! 1195: *newPtr = '#'; ! 1196: newPtr++; ! 1197: format++; ! 1198: } ! 1199: if (*format == 'l') { ! 1200: format++; ! 1201: } ! 1202: *newPtr = *format; ! 1203: newPtr++; ! 1204: *newPtr = 0; ! 1205: if (argc <= 0) { ! 1206: goto notEnoughArgs; ! 1207: } ! 1208: switch (*format) { ! 1209: case 'D': ! 1210: case 'd': ! 1211: case 'O': ! 1212: case 'o': ! 1213: case 'X': ! 1214: case 'x': ! 1215: case 'U': ! 1216: case 'u': { ! 1217: char *end; ! 1218: ! 1219: oneWordValue = (char *) strtol(*curArg, &end, 0); ! 1220: if ((*curArg == 0) || (*end != 0)) { ! 1221: sprintf(interp->result, ! 1222: "expected integer but got \"%.50s\" instead", ! 1223: *curArg); ! 1224: goto fmtError; ! 1225: } ! 1226: size = 40; ! 1227: break; ! 1228: } ! 1229: case 's': ! 1230: oneWordValue = *curArg; ! 1231: size = strlen(*curArg); ! 1232: break; ! 1233: case 'c': { ! 1234: char *end; ! 1235: ! 1236: oneWordValue = (char *) strtol(*curArg, &end, 0); ! 1237: if ((*curArg == 0) || (*end != 0)) { ! 1238: sprintf(interp->result, ! 1239: "expected integer but got \"%.50s\" instead", ! 1240: *curArg); ! 1241: goto fmtError; ! 1242: } ! 1243: size = 1; ! 1244: break; ! 1245: } ! 1246: case 'F': ! 1247: case 'f': ! 1248: case 'E': ! 1249: case 'e': ! 1250: case 'G': ! 1251: case 'g': ! 1252: if (sscanf(*curArg, "%F", &twoWordValue) != 1) { ! 1253: sprintf(interp->result, ! 1254: "expected floating-point number but got \"%.50s\" instead", ! 1255: *curArg); ! 1256: goto fmtError; ! 1257: } ! 1258: useTwoWords = 1; ! 1259: size = 320; ! 1260: if (precision > 10) { ! 1261: size += precision; ! 1262: } ! 1263: break; ! 1264: case 0: ! 1265: interp->result = "format string ended in middle of field specifier"; ! 1266: goto fmtError; ! 1267: default: ! 1268: sprintf(interp->result, "bad field specifier \"%c\"", *format); ! 1269: goto fmtError; ! 1270: } ! 1271: argc--; ! 1272: curArg++; ! 1273: format++; ! 1274: ! 1275: /* ! 1276: * Make sure that there's enough space to hold the formatted ! 1277: * result, then format it. ! 1278: */ ! 1279: ! 1280: doField: ! 1281: if (width > size) { ! 1282: size = width; ! 1283: } ! 1284: if ((dstSize + size) > dstSpace) { ! 1285: char *newDst; ! 1286: int newSpace; ! 1287: ! 1288: newSpace = 2*(dstSize + size); ! 1289: newDst = (char *) malloc((unsigned) newSpace+1); ! 1290: if (dstSize != 0) { ! 1291: bcopy(dst, newDst, dstSize); ! 1292: } ! 1293: if (dstSpace != TCL_RESULT_SIZE) { ! 1294: free(dst); ! 1295: } ! 1296: dst = newDst; ! 1297: dstSpace = newSpace; ! 1298: } ! 1299: if (noPercent) { ! 1300: bcopy(oneWordValue, dst+dstSize, size); ! 1301: dstSize += size; ! 1302: dst[dstSize] = 0; ! 1303: } else { ! 1304: if (useTwoWords) { ! 1305: sprintf(dst+dstSize, newFormat, twoWordValue); ! 1306: } else { ! 1307: sprintf(dst+dstSize, newFormat, oneWordValue); ! 1308: } ! 1309: dstSize += strlen(dst+dstSize); ! 1310: } ! 1311: } ! 1312: ! 1313: interp->result = dst; ! 1314: interp->dynamic = !(dstSpace == TCL_RESULT_SIZE); ! 1315: return TCL_OK; ! 1316: ! 1317: notEnoughArgs: ! 1318: sprintf(interp->result, ! 1319: "invoked \"%.50s\" without enough arguments", argv[0]); ! 1320: fmtError: ! 1321: if (dstSpace != TCL_RESULT_SIZE) { ! 1322: free(dst); ! 1323: } ! 1324: return TCL_ERROR; ! 1325: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.