|
|
1.1 ! root 1: /* ! 2: * tclUnixAZ.c -- ! 3: * ! 4: * This file contains the top-level command procedures for ! 5: * commands in the Tcl core that require UNIX facilities ! 6: * such as files and process execution. Much of the code ! 7: * in this file is based on earlier versions contributed ! 8: * by Karl Lehenbauer, Mark Diekhans and Peter da Silva. ! 9: * ! 10: * Copyright 1991 Regents of the University of California ! 11: * Permission to use, copy, modify, and distribute this ! 12: * software and its documentation for any purpose and without ! 13: * fee is hereby granted, provided that this copyright ! 14: * notice appears in all copies. The University of California ! 15: * makes no representations about the suitability of this ! 16: * software for any purpose. It is provided "as is" without ! 17: * express or implied warranty. ! 18: */ ! 19: ! 20: #ifndef lint ! 21: static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclUnixAZ.c,v 1.36 92/04/16 13:32:02 ouster Exp $ sprite (Berkeley)"; ! 22: #endif /* not lint */ ! 23: ! 24: #include "tclint.h" ! 25: #include "tclunix.h" ! 26: ! 27: /* ! 28: * The variable below caches the name of the current working directory ! 29: * in order to avoid repeated calls to getwd. The string is malloc-ed. ! 30: * NULL means the cache needs to be refreshed. ! 31: */ ! 32: ! 33: static char *currentDir = NULL; ! 34: ! 35: /* ! 36: * Prototypes for local procedures defined in this file: ! 37: */ ! 38: ! 39: static int CleanupChildren _ANSI_ARGS_((Tcl_Interp *interp, ! 40: int numPids, int *pidPtr, int errorId)); ! 41: static char * GetFileType _ANSI_ARGS_((int mode)); ! 42: static int StoreStatData _ANSI_ARGS_((Tcl_Interp *interp, ! 43: char *varName, struct stat *statPtr)); ! 44: ! 45: /* ! 46: *---------------------------------------------------------------------- ! 47: * ! 48: * Tcl_CdCmd -- ! 49: * ! 50: * This procedure is invoked to process the "cd" Tcl command. ! 51: * See the user documentation for details on what it does. ! 52: * ! 53: * Results: ! 54: * A standard Tcl result. ! 55: * ! 56: * Side effects: ! 57: * See the user documentation. ! 58: * ! 59: *---------------------------------------------------------------------- ! 60: */ ! 61: ! 62: /* ARGSUSED */ ! 63: int ! 64: Tcl_CdCmd(dummy, interp, argc, argv) ! 65: ClientData dummy; /* Not used. */ ! 66: Tcl_Interp *interp; /* Current interpreter. */ ! 67: int argc; /* Number of arguments. */ ! 68: char **argv; /* Argument strings. */ ! 69: { ! 70: char *dirName; ! 71: ! 72: if (argc > 2) { ! 73: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 74: " dirName\"", (char *) NULL); ! 75: return TCL_ERROR; ! 76: } ! 77: ! 78: if (argc == 2) { ! 79: dirName = argv[1]; ! 80: } else { ! 81: dirName = "~"; ! 82: } ! 83: dirName = Tcl_TildeSubst(interp, dirName); ! 84: if (dirName == NULL) { ! 85: return TCL_ERROR; ! 86: } ! 87: if (currentDir != NULL) { ! 88: ckfree(currentDir); ! 89: currentDir = NULL; ! 90: } ! 91: if (chdir(dirName) != 0) { ! 92: Tcl_AppendResult(interp, "couldn't change working directory to \"", ! 93: dirName, "\": ", Tcl_UnixError(interp), (char *) NULL); ! 94: return TCL_ERROR; ! 95: } ! 96: return TCL_OK; ! 97: } ! 98: ! 99: /* ! 100: *---------------------------------------------------------------------- ! 101: * ! 102: * Tcl_CloseCmd -- ! 103: * ! 104: * This procedure is invoked to process the "close" Tcl command. ! 105: * See the user documentation for details on what it does. ! 106: * ! 107: * Results: ! 108: * A standard Tcl result. ! 109: * ! 110: * Side effects: ! 111: * See the user documentation. ! 112: * ! 113: *---------------------------------------------------------------------- ! 114: */ ! 115: ! 116: /* ARGSUSED */ ! 117: int ! 118: Tcl_CloseCmd(dummy, interp, argc, argv) ! 119: ClientData dummy; /* Not used. */ ! 120: Tcl_Interp *interp; /* Current interpreter. */ ! 121: int argc; /* Number of arguments. */ ! 122: char **argv; /* Argument strings. */ ! 123: { ! 124: OpenFile *filePtr; ! 125: int result = TCL_OK; ! 126: ! 127: if (argc != 2) { ! 128: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 129: " fileId\"", (char *) NULL); ! 130: return TCL_ERROR; ! 131: } ! 132: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) { ! 133: return TCL_ERROR; ! 134: } ! 135: ((Interp *) interp)->filePtrArray[fileno(filePtr->f)] = NULL; ! 136: ! 137: /* ! 138: * First close the file (in the case of a process pipeline, there may ! 139: * be two files, one for the pipe at each end of the pipeline). ! 140: */ ! 141: ! 142: if (filePtr->f2 != NULL) { ! 143: if (fclose(filePtr->f2) == EOF) { ! 144: Tcl_AppendResult(interp, "error closing \"", argv[1], ! 145: "\": ", Tcl_UnixError(interp), "\n", (char *) NULL); ! 146: result = TCL_ERROR; ! 147: } ! 148: } ! 149: if (fclose(filePtr->f) == EOF) { ! 150: Tcl_AppendResult(interp, "error closing \"", argv[1], ! 151: "\": ", Tcl_UnixError(interp), "\n", (char *) NULL); ! 152: result = TCL_ERROR; ! 153: } ! 154: ! 155: /* ! 156: * If the file was a connection to a pipeline, clean up everything ! 157: * associated with the child processes. ! 158: */ ! 159: ! 160: if (filePtr->numPids > 0) { ! 161: if (CleanupChildren(interp, filePtr->numPids, filePtr->pidPtr, ! 162: filePtr->errorId) != TCL_OK) { ! 163: result = TCL_ERROR; ! 164: } ! 165: } ! 166: ! 167: ckfree((char *) filePtr); ! 168: return result; ! 169: } ! 170: ! 171: /* ! 172: *---------------------------------------------------------------------- ! 173: * ! 174: * Tcl_EofCmd -- ! 175: * ! 176: * This procedure is invoked to process the "eof" Tcl command. ! 177: * See the user documentation for details on what it does. ! 178: * ! 179: * Results: ! 180: * A standard Tcl result. ! 181: * ! 182: * Side effects: ! 183: * See the user documentation. ! 184: * ! 185: *---------------------------------------------------------------------- ! 186: */ ! 187: ! 188: /* ARGSUSED */ ! 189: int ! 190: Tcl_EofCmd(notUsed, interp, argc, argv) ! 191: ClientData notUsed; /* Not used. */ ! 192: Tcl_Interp *interp; /* Current interpreter. */ ! 193: int argc; /* Number of arguments. */ ! 194: char **argv; /* Argument strings. */ ! 195: { ! 196: OpenFile *filePtr; ! 197: ! 198: if (argc != 2) { ! 199: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 200: " fileId\"", (char *) NULL); ! 201: return TCL_ERROR; ! 202: } ! 203: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) { ! 204: return TCL_ERROR; ! 205: } ! 206: if (feof(filePtr->f)) { ! 207: interp->result = "1"; ! 208: } else { ! 209: interp->result = "0"; ! 210: } ! 211: return TCL_OK; ! 212: } ! 213: ! 214: /* ! 215: *---------------------------------------------------------------------- ! 216: * ! 217: * Tcl_ExecCmd -- ! 218: * ! 219: * This procedure is invoked to process the "exec" Tcl command. ! 220: * See the user documentation for details on what it does. ! 221: * ! 222: * Results: ! 223: * A standard Tcl result. ! 224: * ! 225: * Side effects: ! 226: * See the user documentation. ! 227: * ! 228: *---------------------------------------------------------------------- ! 229: */ ! 230: ! 231: /* ARGSUSED */ ! 232: int ! 233: Tcl_ExecCmd(dummy, interp, argc, argv) ! 234: ClientData dummy; /* Not used. */ ! 235: Tcl_Interp *interp; /* Current interpreter. */ ! 236: int argc; /* Number of arguments. */ ! 237: char **argv; /* Argument strings. */ ! 238: { ! 239: int outputId; /* File id for output pipe. -1 ! 240: * means command overrode. */ ! 241: int errorId; /* File id for temporary file ! 242: * containing error output. */ ! 243: int *pidPtr; ! 244: int numPids, result; ! 245: ! 246: /* ! 247: * See if the command is to be run in background; if so, create ! 248: * the command, detach it, and return. ! 249: */ ! 250: ! 251: if ((argv[argc-1][0] == '&') && (argv[argc-1][1] == 0)) { ! 252: argc--; ! 253: argv[argc] = NULL; ! 254: numPids = Tcl_CreatePipeline(interp, argc-1, argv+1, &pidPtr, ! 255: (int *) NULL, (int *) NULL, (int *) NULL); ! 256: if (numPids < 0) { ! 257: return TCL_ERROR; ! 258: } ! 259: Tcl_DetachPids(numPids, pidPtr); ! 260: ckfree((char *) pidPtr); ! 261: return TCL_OK; ! 262: } ! 263: ! 264: /* ! 265: * Create the command's pipeline. ! 266: */ ! 267: ! 268: numPids = Tcl_CreatePipeline(interp, argc-1, argv+1, &pidPtr, ! 269: (int *) NULL, &outputId, &errorId); ! 270: if (numPids < 0) { ! 271: return TCL_ERROR; ! 272: } ! 273: ! 274: /* ! 275: * Read the child's output (if any) and put it into the result. ! 276: */ ! 277: ! 278: result = TCL_OK; ! 279: if (outputId != -1) { ! 280: while (1) { ! 281: # define BUFFER_SIZE 1000 ! 282: char buffer[BUFFER_SIZE+1]; ! 283: int count; ! 284: ! 285: count = read(outputId, buffer, BUFFER_SIZE); ! 286: ! 287: if (count == 0) { ! 288: break; ! 289: } ! 290: if (count < 0) { ! 291: Tcl_ResetResult(interp); ! 292: Tcl_AppendResult(interp, ! 293: "error reading from output pipe: ", ! 294: Tcl_UnixError(interp), (char *) NULL); ! 295: result = TCL_ERROR; ! 296: break; ! 297: } ! 298: buffer[count] = 0; ! 299: Tcl_AppendResult(interp, buffer, (char *) NULL); ! 300: } ! 301: close(outputId); ! 302: } ! 303: ! 304: if (CleanupChildren(interp, numPids, pidPtr, errorId) != TCL_OK) { ! 305: result = TCL_ERROR; ! 306: } ! 307: return result; ! 308: } ! 309: ! 310: /* ! 311: *---------------------------------------------------------------------- ! 312: * ! 313: * Tcl_ExitCmd -- ! 314: * ! 315: * This procedure is invoked to process the "exit" Tcl command. ! 316: * See the user documentation for details on what it does. ! 317: * ! 318: * Results: ! 319: * A standard Tcl result. ! 320: * ! 321: * Side effects: ! 322: * See the user documentation. ! 323: * ! 324: *---------------------------------------------------------------------- ! 325: */ ! 326: ! 327: /* ARGSUSED */ ! 328: int ! 329: Tcl_ExitCmd(dummy, interp, argc, argv) ! 330: ClientData dummy; /* Not used. */ ! 331: Tcl_Interp *interp; /* Current interpreter. */ ! 332: int argc; /* Number of arguments. */ ! 333: char **argv; /* Argument strings. */ ! 334: { ! 335: int value; ! 336: ! 337: if ((argc != 1) && (argc != 2)) { ! 338: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 339: " ?returnCode?\"", (char *) NULL); ! 340: return TCL_ERROR; ! 341: } ! 342: if (argc == 1) { ! 343: exit(0); ! 344: } ! 345: if (Tcl_GetInt(interp, argv[1], &value) != TCL_OK) { ! 346: return TCL_ERROR; ! 347: } ! 348: exit(value); ! 349: #if 0 ! 350: return TCL_OK; /* Better not ever reach this! */ ! 351: #endif ! 352: } ! 353: ! 354: /* ! 355: *---------------------------------------------------------------------- ! 356: * ! 357: * Tcl_FileCmd -- ! 358: * ! 359: * This procedure is invoked to process the "file" Tcl command. ! 360: * See the user documentation for details on what it does. ! 361: * ! 362: * Results: ! 363: * A standard Tcl result. ! 364: * ! 365: * Side effects: ! 366: * See the user documentation. ! 367: * ! 368: *---------------------------------------------------------------------- ! 369: */ ! 370: ! 371: /* ARGSUSED */ ! 372: int ! 373: Tcl_FileCmd(dummy, interp, argc, argv) ! 374: ClientData dummy; /* Not used. */ ! 375: Tcl_Interp *interp; /* Current interpreter. */ ! 376: int argc; /* Number of arguments. */ ! 377: char **argv; /* Argument strings. */ ! 378: { ! 379: char *p; ! 380: int length, statOp; ! 381: int mode = 0; /* Initialized only to prevent ! 382: * compiler warning message. */ ! 383: struct stat statBuf; ! 384: char *fileName, c; ! 385: ! 386: if (argc < 3) { ! 387: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 388: " option name ?arg ...?\"", (char *) NULL); ! 389: return TCL_ERROR; ! 390: } ! 391: c = argv[1][0]; ! 392: length = strlen(argv[1]); ! 393: ! 394: /* ! 395: * First handle operations on the file name. ! 396: */ ! 397: ! 398: fileName = Tcl_TildeSubst(interp, argv[2]); ! 399: if (fileName == NULL) { ! 400: return TCL_ERROR; ! 401: } ! 402: if ((c == 'd') && (strncmp(argv[1], "dirname", length) == 0)) { ! 403: if (argc != 3) { ! 404: argv[1] = "dirname"; ! 405: not3Args: ! 406: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 407: " ", argv[1], " name\"", (char *) NULL); ! 408: return TCL_ERROR; ! 409: } ! 410: #ifdef MSDOS ! 411: p = strrchr(fileName, '\\'); ! 412: #else ! 413: p = strrchr(fileName, '/'); ! 414: #endif ! 415: if (p == NULL) { ! 416: interp->result = "."; ! 417: } else if (p == fileName) { ! 418: #ifdef MSDOS ! 419: interp->result = "\\"; ! 420: #else ! 421: interp->result = "/"; ! 422: #endif ! 423: } else { ! 424: *p = 0; ! 425: Tcl_SetResult(interp, fileName, TCL_VOLATILE); ! 426: *p = '/'; ! 427: } ! 428: return TCL_OK; ! 429: } else if ((c == 'r') && (strncmp(argv[1], "rootname", length) == 0) ! 430: && (length >= 2)) { ! 431: char *lastSlash; ! 432: ! 433: if (argc != 3) { ! 434: argv[1] = "rootname"; ! 435: goto not3Args; ! 436: } ! 437: p = strrchr(fileName, '.'); ! 438: #ifdef MSDOS ! 439: lastSlash = strrchr(fileName, '\\'); ! 440: #else ! 441: lastSlash = strrchr(fileName, '/'); ! 442: #endif ! 443: if ((p == NULL) || ((lastSlash != NULL) && (lastSlash > p))) { ! 444: Tcl_SetResult(interp, fileName, TCL_VOLATILE); ! 445: } else { ! 446: *p = 0; ! 447: Tcl_SetResult(interp, fileName, TCL_VOLATILE); ! 448: *p = '.'; ! 449: } ! 450: return TCL_OK; ! 451: } else if ((c == 'e') && (strncmp(argv[1], "extension", length) == 0) ! 452: && (length >= 3)) { ! 453: char *lastSlash; ! 454: ! 455: if (argc != 3) { ! 456: argv[1] = "extension"; ! 457: goto not3Args; ! 458: } ! 459: p = strrchr(fileName, '.'); ! 460: #ifdef MSDOS ! 461: lastSlash = strrchr(fileName, '\\'); ! 462: #else ! 463: lastSlash = strrchr(fileName, '/'); ! 464: #endif ! 465: if ((p != NULL) && ((lastSlash == NULL) || (lastSlash < p))) { ! 466: Tcl_SetResult(interp, p, TCL_VOLATILE); ! 467: } ! 468: return TCL_OK; ! 469: } else if ((c == 't') && (strncmp(argv[1], "tail", length) == 0) ! 470: && (length >= 2)) { ! 471: if (argc != 3) { ! 472: argv[1] = "tail"; ! 473: goto not3Args; ! 474: } ! 475: #ifdef MSDOS ! 476: p = strrchr(fileName, '\\'); ! 477: #else ! 478: p = strrchr(fileName, '/'); ! 479: #endif ! 480: if (p != NULL) { ! 481: Tcl_SetResult(interp, p+1, TCL_VOLATILE); ! 482: } else { ! 483: Tcl_SetResult(interp, fileName, TCL_VOLATILE); ! 484: } ! 485: return TCL_OK; ! 486: } ! 487: ! 488: /* ! 489: * Next, handle operations that can be satisfied with the "access" ! 490: * kernel call. ! 491: */ ! 492: ! 493: if (fileName == NULL) { ! 494: return TCL_ERROR; ! 495: } ! 496: if ((c == 'r') && (strncmp(argv[1], "readable", length) == 0) ! 497: && (length >= 5)) { ! 498: if (argc != 3) { ! 499: argv[1] = "readable"; ! 500: goto not3Args; ! 501: } ! 502: mode = R_OK; ! 503: checkAccess: ! 504: if (access(fileName, mode) == -1) { ! 505: interp->result = "0"; ! 506: } else { ! 507: interp->result = "1"; ! 508: } ! 509: return TCL_OK; ! 510: } else if ((c == 'w') && (strncmp(argv[1], "writable", length) == 0)) { ! 511: if (argc != 3) { ! 512: argv[1] = "writable"; ! 513: goto not3Args; ! 514: } ! 515: mode = W_OK; ! 516: goto checkAccess; ! 517: } else if ((c == 'e') && (strncmp(argv[1], "executable", length) == 0) ! 518: && (length >= 3)) { ! 519: if (argc != 3) { ! 520: argv[1] = "executable"; ! 521: goto not3Args; ! 522: } ! 523: mode = X_OK; ! 524: goto checkAccess; ! 525: } else if ((c == 'e') && (strncmp(argv[1], "exists", length) == 0) ! 526: && (length >= 3)) { ! 527: if (argc != 3) { ! 528: argv[1] = "exists"; ! 529: goto not3Args; ! 530: } ! 531: mode = F_OK; ! 532: goto checkAccess; ! 533: } ! 534: ! 535: /* ! 536: * Lastly, check stuff that requires the file to be stat-ed. ! 537: */ ! 538: ! 539: if ((c == 'a') && (strncmp(argv[1], "atime", length) == 0)) { ! 540: if (argc != 3) { ! 541: argv[1] = "atime"; ! 542: goto not3Args; ! 543: } ! 544: if (stat(fileName, &statBuf) == -1) { ! 545: goto badStat; ! 546: } ! 547: sprintf(interp->result, "%ld", statBuf.st_atime); ! 548: return TCL_OK; ! 549: } else if ((c == 'i') && (strncmp(argv[1], "isdirectory", length) == 0) ! 550: && (length >= 3)) { ! 551: if (argc != 3) { ! 552: argv[1] = "isdirectory"; ! 553: goto not3Args; ! 554: } ! 555: statOp = 2; ! 556: } else if ((c == 'i') && (strncmp(argv[1], "isfile", length) == 0) ! 557: && (length >= 3)) { ! 558: if (argc != 3) { ! 559: argv[1] = "isfile"; ! 560: goto not3Args; ! 561: } ! 562: statOp = 1; ! 563: } else if ((c == 'l') && (strncmp(argv[1], "lstat", length) == 0)) { ! 564: if (argc != 4) { ! 565: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 566: " lstat name varName\"", (char *) NULL); ! 567: return TCL_ERROR; ! 568: } ! 569: ! 570: if (lstat(fileName, &statBuf) == -1) { ! 571: Tcl_AppendResult(interp, "couldn't lstat \"", argv[2], ! 572: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 573: return TCL_ERROR; ! 574: } ! 575: return StoreStatData(interp, argv[3], &statBuf); ! 576: } else if ((c == 'm') && (strncmp(argv[1], "mtime", length) == 0)) { ! 577: if (argc != 3) { ! 578: argv[1] = "mtime"; ! 579: goto not3Args; ! 580: } ! 581: if (stat(fileName, &statBuf) == -1) { ! 582: goto badStat; ! 583: } ! 584: sprintf(interp->result, "%ld", statBuf.st_mtime); ! 585: return TCL_OK; ! 586: } else if ((c == 'o') && (strncmp(argv[1], "owned", length) == 0)) { ! 587: if (argc != 3) { ! 588: argv[1] = "owned"; ! 589: goto not3Args; ! 590: } ! 591: statOp = 0; ! 592: #ifdef S_IFLNK ! 593: /* ! 594: * This option is only included if symbolic links exist on this system ! 595: * (in which case S_IFLNK should be defined). ! 596: */ ! 597: } else if ((c == 'r') && (strncmp(argv[1], "readlink", length) == 0) ! 598: && (length >= 5)) { ! 599: char linkValue[MAXPATHLEN+1]; ! 600: int linkLength; ! 601: ! 602: if (argc != 3) { ! 603: argv[1] = "readlink"; ! 604: goto not3Args; ! 605: } ! 606: linkLength = readlink(fileName, linkValue, sizeof(linkValue) - 1); ! 607: if (linkLength == -1) { ! 608: Tcl_AppendResult(interp, "couldn't readlink \"", argv[2], ! 609: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 610: return TCL_ERROR; ! 611: } ! 612: linkValue[linkLength] = 0; ! 613: Tcl_SetResult(interp, linkValue, TCL_VOLATILE); ! 614: return TCL_OK; ! 615: #endif ! 616: } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0) ! 617: && (length >= 2)) { ! 618: if (argc != 3) { ! 619: argv[1] = "size"; ! 620: goto not3Args; ! 621: } ! 622: if (stat(fileName, &statBuf) == -1) { ! 623: goto badStat; ! 624: } ! 625: sprintf(interp->result, "%ld", statBuf.st_size); ! 626: return TCL_OK; ! 627: } else if ((c == 's') && (strncmp(argv[1], "stat", length) == 0) ! 628: && (length >= 2)) { ! 629: if (argc != 4) { ! 630: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 631: " stat name varName\"", (char *) NULL); ! 632: return TCL_ERROR; ! 633: } ! 634: ! 635: if (stat(fileName, &statBuf) == -1) { ! 636: badStat: ! 637: Tcl_AppendResult(interp, "couldn't stat \"", argv[2], ! 638: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 639: return TCL_ERROR; ! 640: } ! 641: return StoreStatData(interp, argv[3], &statBuf); ! 642: } else if ((c == 't') && (strncmp(argv[1], "type", length) == 0) ! 643: && (length >= 2)) { ! 644: if (argc != 3) { ! 645: argv[1] = "type"; ! 646: goto not3Args; ! 647: } ! 648: if (lstat(fileName, &statBuf) == -1) { ! 649: goto badStat; ! 650: } ! 651: interp->result = GetFileType((int) statBuf.st_mode); ! 652: return TCL_OK; ! 653: } else { ! 654: Tcl_AppendResult(interp, "bad option \"", argv[1], ! 655: "\": should be atime, dirname, executable, exists, ", ! 656: "extension, isdirectory, isfile, lstat, mtime, owned, ", ! 657: "readable, ", ! 658: #ifdef S_IFLNK ! 659: "readlink, ", ! 660: #endif ! 661: "root, size, stat, tail, type, ", ! 662: "or writable", ! 663: (char *) NULL); ! 664: return TCL_ERROR; ! 665: } ! 666: if (stat(fileName, &statBuf) == -1) { ! 667: interp->result = "0"; ! 668: return TCL_OK; ! 669: } ! 670: switch (statOp) { ! 671: case 0: ! 672: mode = (geteuid() == statBuf.st_uid); ! 673: break; ! 674: case 1: ! 675: mode = S_ISREG(statBuf.st_mode); ! 676: break; ! 677: case 2: ! 678: mode = S_ISDIR(statBuf.st_mode); ! 679: break; ! 680: } ! 681: if (mode) { ! 682: interp->result = "1"; ! 683: } else { ! 684: interp->result = "0"; ! 685: } ! 686: return TCL_OK; ! 687: } ! 688: ! 689: /* ! 690: *---------------------------------------------------------------------- ! 691: * ! 692: * StoreStatData -- ! 693: * ! 694: * This is a utility procedure that breaks out the fields of a ! 695: * "stat" structure and stores them in textual form into the ! 696: * elements of an associative array. ! 697: * ! 698: * Results: ! 699: * Returns a standard Tcl return value. If an error occurs then ! 700: * a message is left in interp->result. ! 701: * ! 702: * Side effects: ! 703: * Elements of the associative array given by "varName" are modified. ! 704: * ! 705: *---------------------------------------------------------------------- ! 706: */ ! 707: ! 708: static int ! 709: StoreStatData(interp, varName, statPtr) ! 710: Tcl_Interp *interp; /* Interpreter for error reports. */ ! 711: char *varName; /* Name of associative array variable ! 712: * in which to store stat results. */ ! 713: struct stat *statPtr; /* Pointer to buffer containing ! 714: * stat data to store in varName. */ ! 715: { ! 716: char string[30]; ! 717: ! 718: sprintf(string, "%d", statPtr->st_dev); ! 719: if (Tcl_SetVar2(interp, varName, "dev", string, TCL_LEAVE_ERR_MSG) ! 720: == NULL) { ! 721: return TCL_ERROR; ! 722: } ! 723: sprintf(string, "%d", statPtr->st_ino); ! 724: if (Tcl_SetVar2(interp, varName, "ino", string, TCL_LEAVE_ERR_MSG) ! 725: == NULL) { ! 726: return TCL_ERROR; ! 727: } ! 728: sprintf(string, "%d", statPtr->st_mode); ! 729: if (Tcl_SetVar2(interp, varName, "mode", string, TCL_LEAVE_ERR_MSG) ! 730: == NULL) { ! 731: return TCL_ERROR; ! 732: } ! 733: sprintf(string, "%d", statPtr->st_nlink); ! 734: if (Tcl_SetVar2(interp, varName, "nlink", string, TCL_LEAVE_ERR_MSG) ! 735: == NULL) { ! 736: return TCL_ERROR; ! 737: } ! 738: sprintf(string, "%d", statPtr->st_uid); ! 739: if (Tcl_SetVar2(interp, varName, "uid", string, TCL_LEAVE_ERR_MSG) ! 740: == NULL) { ! 741: return TCL_ERROR; ! 742: } ! 743: sprintf(string, "%d", statPtr->st_gid); ! 744: if (Tcl_SetVar2(interp, varName, "gid", string, TCL_LEAVE_ERR_MSG) ! 745: == NULL) { ! 746: return TCL_ERROR; ! 747: } ! 748: sprintf(string, "%ld", statPtr->st_size); ! 749: if (Tcl_SetVar2(interp, varName, "size", string, TCL_LEAVE_ERR_MSG) ! 750: == NULL) { ! 751: return TCL_ERROR; ! 752: } ! 753: sprintf(string, "%ld", statPtr->st_atime); ! 754: if (Tcl_SetVar2(interp, varName, "atime", string, TCL_LEAVE_ERR_MSG) ! 755: == NULL) { ! 756: return TCL_ERROR; ! 757: } ! 758: sprintf(string, "%ld", statPtr->st_mtime); ! 759: if (Tcl_SetVar2(interp, varName, "mtime", string, TCL_LEAVE_ERR_MSG) ! 760: == NULL) { ! 761: return TCL_ERROR; ! 762: } ! 763: sprintf(string, "%ld", statPtr->st_ctime); ! 764: if (Tcl_SetVar2(interp, varName, "ctime", string, TCL_LEAVE_ERR_MSG) ! 765: == NULL) { ! 766: return TCL_ERROR; ! 767: } ! 768: if (Tcl_SetVar2(interp, varName, "type", ! 769: GetFileType((int) statPtr->st_mode), TCL_LEAVE_ERR_MSG) == NULL) { ! 770: return TCL_ERROR; ! 771: } ! 772: return TCL_OK; ! 773: } ! 774: ! 775: /* ! 776: *---------------------------------------------------------------------- ! 777: * ! 778: * GetFileType -- ! 779: * ! 780: * Given a mode word, returns a string identifying the type of a ! 781: * file. ! 782: * ! 783: * Results: ! 784: * A static text string giving the file type from mode. ! 785: * ! 786: * Side effects: ! 787: * None. ! 788: * ! 789: *---------------------------------------------------------------------- ! 790: */ ! 791: ! 792: static char * ! 793: GetFileType(mode) ! 794: int mode; ! 795: { ! 796: if (S_ISREG(mode)) { ! 797: return "file"; ! 798: } else if (S_ISDIR(mode)) { ! 799: return "directory"; ! 800: } else if (S_ISCHR(mode)) { ! 801: return "characterSpecial"; ! 802: } else if (S_ISBLK(mode)) { ! 803: return "blockSpecial"; ! 804: } else if (S_ISFIFO(mode)) { ! 805: return "fifo"; ! 806: } else if (S_ISLNK(mode)) { ! 807: return "link"; ! 808: } else if (S_ISSOCK(mode)) { ! 809: return "socket"; ! 810: } ! 811: return "unknown"; ! 812: } ! 813: ! 814: /* ! 815: *---------------------------------------------------------------------- ! 816: * ! 817: * Tcl_FlushCmd -- ! 818: * ! 819: * This procedure is invoked to process the "flush" Tcl command. ! 820: * See the user documentation for details on what it does. ! 821: * ! 822: * Results: ! 823: * A standard Tcl result. ! 824: * ! 825: * Side effects: ! 826: * See the user documentation. ! 827: * ! 828: *---------------------------------------------------------------------- ! 829: */ ! 830: ! 831: /* ARGSUSED */ ! 832: int ! 833: Tcl_FlushCmd(notUsed, interp, argc, argv) ! 834: ClientData notUsed; /* Not used. */ ! 835: Tcl_Interp *interp; /* Current interpreter. */ ! 836: int argc; /* Number of arguments. */ ! 837: char **argv; /* Argument strings. */ ! 838: { ! 839: OpenFile *filePtr; ! 840: FILE *f; ! 841: ! 842: if (argc != 2) { ! 843: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 844: " fileId\"", (char *) NULL); ! 845: return TCL_ERROR; ! 846: } ! 847: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) { ! 848: return TCL_ERROR; ! 849: } ! 850: if (!filePtr->writable) { ! 851: Tcl_AppendResult(interp, "\"", argv[1], ! 852: "\" wasn't opened for writing", (char *) NULL); ! 853: return TCL_ERROR; ! 854: } ! 855: f = filePtr->f2; ! 856: if (f == NULL) { ! 857: f = filePtr->f; ! 858: } ! 859: if (fflush(f) == EOF) { ! 860: Tcl_AppendResult(interp, "error flushing \"", argv[1], ! 861: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 862: clearerr(f); ! 863: return TCL_ERROR; ! 864: } ! 865: return TCL_OK; ! 866: } ! 867: ! 868: /* ! 869: *---------------------------------------------------------------------- ! 870: * ! 871: * Tcl_GetsCmd -- ! 872: * ! 873: * This procedure is invoked to process the "gets" Tcl command. ! 874: * See the user documentation for details on what it does. ! 875: * ! 876: * Results: ! 877: * A standard Tcl result. ! 878: * ! 879: * Side effects: ! 880: * See the user documentation. ! 881: * ! 882: *---------------------------------------------------------------------- ! 883: */ ! 884: ! 885: /* ARGSUSED */ ! 886: int ! 887: Tcl_GetsCmd(notUsed, interp, argc, argv) ! 888: ClientData notUsed; /* Not used. */ ! 889: Tcl_Interp *interp; /* Current interpreter. */ ! 890: int argc; /* Number of arguments. */ ! 891: char **argv; /* Argument strings. */ ! 892: { ! 893: # define BUF_SIZE 200 ! 894: char buffer[BUF_SIZE+1]; ! 895: int totalCount, done, flags; ! 896: OpenFile *filePtr; ! 897: register FILE *f; ! 898: ! 899: if ((argc != 2) && (argc != 3)) { ! 900: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 901: " fileId ?varName?\"", (char *) NULL); ! 902: return TCL_ERROR; ! 903: } ! 904: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) { ! 905: return TCL_ERROR; ! 906: } ! 907: if (!filePtr->readable) { ! 908: Tcl_AppendResult(interp, "\"", argv[1], ! 909: "\" wasn't opened for reading", (char *) NULL); ! 910: return TCL_ERROR; ! 911: } ! 912: ! 913: /* ! 914: * We can't predict how large a line will be, so read it in ! 915: * pieces, appending to the current result or to a variable. ! 916: */ ! 917: ! 918: totalCount = 0; ! 919: done = 0; ! 920: flags = 0; ! 921: f = filePtr->f; ! 922: while (!done) { ! 923: register int c, count; ! 924: register char *p; ! 925: ! 926: for (p = buffer, count = 0; count < BUF_SIZE-1; count++, p++) { ! 927: c = getc(f); ! 928: if (c == EOF) { ! 929: if (ferror(filePtr->f)) { ! 930: Tcl_ResetResult(interp); ! 931: Tcl_AppendResult(interp, "error reading \"", argv[1], ! 932: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 933: clearerr(filePtr->f); ! 934: return TCL_ERROR; ! 935: } else if (feof(filePtr->f)) { ! 936: if ((totalCount == 0) && (count == 0)) { ! 937: totalCount = -1; ! 938: } ! 939: done = 1; ! 940: break; ! 941: } ! 942: } ! 943: if (c == '\n') { ! 944: done = 1; ! 945: break; ! 946: } ! 947: *p = c; ! 948: } ! 949: *p = 0; ! 950: if (argc == 2) { ! 951: Tcl_AppendResult(interp, buffer, (char *) NULL); ! 952: } else { ! 953: if (Tcl_SetVar(interp, argv[2], buffer, flags|TCL_LEAVE_ERR_MSG) ! 954: == NULL) { ! 955: return TCL_ERROR; ! 956: } ! 957: flags = TCL_APPEND_VALUE; ! 958: } ! 959: totalCount += count; ! 960: } ! 961: ! 962: if (argc == 3) { ! 963: sprintf(interp->result, "%d", totalCount); ! 964: } ! 965: return TCL_OK; ! 966: } ! 967: ! 968: /* ! 969: *---------------------------------------------------------------------- ! 970: * ! 971: * Tcl_OpenCmd -- ! 972: * ! 973: * This procedure is invoked to process the "open" Tcl command. ! 974: * See the user documentation for details on what it does. ! 975: * ! 976: * Results: ! 977: * A standard Tcl result. ! 978: * ! 979: * Side effects: ! 980: * See the user documentation. ! 981: * ! 982: *---------------------------------------------------------------------- ! 983: */ ! 984: ! 985: /* ARGSUSED */ ! 986: int ! 987: Tcl_OpenCmd(notUsed, interp, argc, argv) ! 988: ClientData notUsed; /* Not used. */ ! 989: Tcl_Interp *interp; /* Current interpreter. */ ! 990: int argc; /* Number of arguments. */ ! 991: char **argv; /* Argument strings. */ ! 992: { ! 993: Interp *iPtr = (Interp *) interp; ! 994: int pipeline, fd; ! 995: char *access; ! 996: register OpenFile *filePtr; ! 997: ! 998: if (argc == 2) { ! 999: access = "r"; ! 1000: } else if (argc == 3) { ! 1001: access = argv[2]; ! 1002: } else { ! 1003: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 1004: " filename ?access?\"", (char *) NULL); ! 1005: return TCL_ERROR; ! 1006: } ! 1007: ! 1008: filePtr = (OpenFile *) ckalloc(sizeof(OpenFile)); ! 1009: filePtr->f = NULL; ! 1010: filePtr->f2 = NULL; ! 1011: filePtr->readable = 0; ! 1012: filePtr->writable = 0; ! 1013: filePtr->numPids = 0; ! 1014: filePtr->pidPtr = NULL; ! 1015: filePtr->errorId = -1; ! 1016: ! 1017: /* ! 1018: * Verify the requested form of access. ! 1019: */ ! 1020: ! 1021: pipeline = 0; ! 1022: if (argv[1][0] == '|') { ! 1023: pipeline = 1; ! 1024: } ! 1025: switch (access[0]) { ! 1026: case 'r': ! 1027: filePtr->readable = 1; ! 1028: break; ! 1029: case 'w': ! 1030: filePtr->writable = 1; ! 1031: break; ! 1032: case 'a': ! 1033: filePtr->writable = 1; ! 1034: break; ! 1035: default: ! 1036: badAccess: ! 1037: Tcl_AppendResult(interp, "illegal access mode \"", access, ! 1038: "\"", (char *) NULL); ! 1039: goto error; ! 1040: } ! 1041: if (access[1] == '+') { ! 1042: filePtr->readable = filePtr->writable = 1; ! 1043: if (access[2] != 0) { ! 1044: goto badAccess; ! 1045: } ! 1046: } else if (access[1] != 0) { ! 1047: goto badAccess; ! 1048: } ! 1049: ! 1050: /* ! 1051: * Open the file or create a process pipeline. ! 1052: */ ! 1053: ! 1054: if (!pipeline) { ! 1055: char *fileName = argv[1]; ! 1056: ! 1057: if (fileName[0] == '~') { ! 1058: fileName = Tcl_TildeSubst(interp, fileName); ! 1059: if (fileName == NULL) { ! 1060: goto error; ! 1061: } ! 1062: } ! 1063: filePtr->f = fopen(fileName, access); ! 1064: if (filePtr->f == NULL) { ! 1065: Tcl_AppendResult(interp, "couldn't open \"", argv[1], ! 1066: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 1067: goto error; ! 1068: } ! 1069: } else { ! 1070: int *inPipePtr, *outPipePtr; ! 1071: int cmdArgc, inPipe, outPipe; ! 1072: char **cmdArgv; ! 1073: ! 1074: if (Tcl_SplitList(interp, argv[1]+1, &cmdArgc, &cmdArgv) != TCL_OK) { ! 1075: goto error; ! 1076: } ! 1077: inPipePtr = (filePtr->writable) ? &inPipe : NULL; ! 1078: outPipePtr = (filePtr->readable) ? &outPipe : NULL; ! 1079: inPipe = outPipe = -1; ! 1080: filePtr->numPids = Tcl_CreatePipeline(interp, cmdArgc, cmdArgv, ! 1081: &filePtr->pidPtr, inPipePtr, outPipePtr, &filePtr->errorId); ! 1082: ckfree((char *) cmdArgv); ! 1083: if (filePtr->numPids < 0) { ! 1084: goto error; ! 1085: } ! 1086: if (filePtr->readable) { ! 1087: if (outPipe == -1) { ! 1088: if (inPipe != -1) { ! 1089: close(inPipe); ! 1090: } ! 1091: Tcl_AppendResult(interp, "can't read output from command:", ! 1092: " standard output was redirected", (char *) NULL); ! 1093: goto error; ! 1094: } ! 1095: filePtr->f = fdopen(outPipe, "r"); ! 1096: } ! 1097: if (filePtr->writable) { ! 1098: if (inPipe == -1) { ! 1099: Tcl_AppendResult(interp, "can't write input to command:", ! 1100: " standard input was redirected", (char *) NULL); ! 1101: goto error; ! 1102: } ! 1103: if (filePtr->f != NULL) { ! 1104: filePtr->f2 = fdopen(inPipe, "w"); ! 1105: } else { ! 1106: filePtr->f = fdopen(inPipe, "w"); ! 1107: } ! 1108: } ! 1109: } ! 1110: ! 1111: /* ! 1112: * Enter this new OpenFile structure in the table for the ! 1113: * interpreter. May have to expand the table to do this. ! 1114: */ ! 1115: ! 1116: fd = fileno(filePtr->f); ! 1117: TclMakeFileTable(iPtr, fd); ! 1118: if (iPtr->filePtrArray[fd] != NULL) { ! 1119: panic("Tcl_OpenCmd found file already open"); ! 1120: } ! 1121: iPtr->filePtrArray[fd] = filePtr; ! 1122: sprintf(interp->result, "file%d", fd); ! 1123: return TCL_OK; ! 1124: ! 1125: error: ! 1126: if (filePtr->f != NULL) { ! 1127: fclose(filePtr->f); ! 1128: } ! 1129: if (filePtr->f2 != NULL) { ! 1130: fclose(filePtr->f2); ! 1131: } ! 1132: if (filePtr->numPids > 0) { ! 1133: Tcl_DetachPids(filePtr->numPids, filePtr->pidPtr); ! 1134: ckfree((char *) filePtr->pidPtr); ! 1135: } ! 1136: if (filePtr->errorId != -1) { ! 1137: close(filePtr->errorId); ! 1138: } ! 1139: ckfree((char *) filePtr); ! 1140: return TCL_ERROR; ! 1141: } ! 1142: ! 1143: /* ! 1144: *---------------------------------------------------------------------- ! 1145: * ! 1146: * Tcl_PwdCmd -- ! 1147: * ! 1148: * This procedure is invoked to process the "pwd" Tcl command. ! 1149: * See the user documentation for details on what it does. ! 1150: * ! 1151: * Results: ! 1152: * A standard Tcl result. ! 1153: * ! 1154: * Side effects: ! 1155: * See the user documentation. ! 1156: * ! 1157: *---------------------------------------------------------------------- ! 1158: */ ! 1159: ! 1160: /* ARGSUSED */ ! 1161: int ! 1162: Tcl_PwdCmd(dummy, interp, argc, argv) ! 1163: ClientData dummy; /* Not used. */ ! 1164: Tcl_Interp *interp; /* Current interpreter. */ ! 1165: int argc; /* Number of arguments. */ ! 1166: char **argv; /* Argument strings. */ ! 1167: { ! 1168: char buffer[MAXPATHLEN+1]; ! 1169: ! 1170: if (argc != 1) { ! 1171: Tcl_AppendResult(interp, "wrong # args: should be \"", ! 1172: argv[0], "\"", (char *) NULL); ! 1173: return TCL_ERROR; ! 1174: } ! 1175: if (currentDir == NULL) { ! 1176: #if TCL_GETWD ! 1177: if (getwd(buffer) == NULL) { ! 1178: Tcl_AppendResult(interp, "error getting working directory name: ", ! 1179: buffer, (char *) NULL); ! 1180: return TCL_ERROR; ! 1181: } ! 1182: #else ! 1183: if (getcwd(buffer, MAXPATHLEN) == 0) { ! 1184: if (errno == ERANGE) { ! 1185: interp->result = "working directory name is too long"; ! 1186: } else { ! 1187: Tcl_AppendResult(interp, ! 1188: "error getting working directory name: ", ! 1189: Tcl_UnixError(interp), (char *) NULL); ! 1190: } ! 1191: return TCL_ERROR; ! 1192: } ! 1193: #endif ! 1194: currentDir = (char *) ckalloc((unsigned) (strlen(buffer) + 1)); ! 1195: strcpy(currentDir, buffer); ! 1196: } ! 1197: interp->result = currentDir; ! 1198: return TCL_OK; ! 1199: } ! 1200: ! 1201: /* ! 1202: *---------------------------------------------------------------------- ! 1203: * ! 1204: * Tcl_PutsCmd -- ! 1205: * ! 1206: * This procedure is invoked to process the "puts" Tcl command. ! 1207: * See the user documentation for details on what it does. ! 1208: * ! 1209: * Results: ! 1210: * A standard Tcl result. ! 1211: * ! 1212: * Side effects: ! 1213: * See the user documentation. ! 1214: * ! 1215: *---------------------------------------------------------------------- ! 1216: */ ! 1217: ! 1218: /* ARGSUSED */ ! 1219: int ! 1220: Tcl_PutsCmd(dummy, interp, argc, argv) ! 1221: ClientData dummy; /* Not used. */ ! 1222: Tcl_Interp *interp; /* Current interpreter. */ ! 1223: int argc; /* Number of arguments. */ ! 1224: char **argv; /* Argument strings. */ ! 1225: { ! 1226: OpenFile *filePtr; ! 1227: FILE *f; ! 1228: ! 1229: if (argc == 4) { ! 1230: if (strncmp(argv[3], "nonewline", strlen(argv[3])) != 0) { ! 1231: Tcl_AppendResult(interp, "bad argument \"", argv[3], ! 1232: "\": should be \"nonewline\"", (char *) NULL); ! 1233: return TCL_ERROR; ! 1234: } ! 1235: } else if (argc != 3) { ! 1236: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 1237: " fileId string ?nonewline?\"", (char *) NULL); ! 1238: return TCL_ERROR; ! 1239: } ! 1240: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) { ! 1241: return TCL_ERROR; ! 1242: } ! 1243: if (!filePtr->writable) { ! 1244: Tcl_AppendResult(interp, "\"", argv[1], ! 1245: "\" wasn't opened for writing", (char *) NULL); ! 1246: return TCL_ERROR; ! 1247: } ! 1248: ! 1249: f = filePtr->f2; ! 1250: if (f == NULL) { ! 1251: f = filePtr->f; ! 1252: } ! 1253: fputs(argv[2], f); ! 1254: if (argc == 3) { ! 1255: fputc('\n', f); ! 1256: } ! 1257: if (ferror(f)) { ! 1258: Tcl_AppendResult(interp, "error writing \"", argv[1], ! 1259: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 1260: clearerr(f); ! 1261: return TCL_ERROR; ! 1262: } ! 1263: return TCL_OK; ! 1264: } ! 1265: ! 1266: /* ! 1267: *---------------------------------------------------------------------- ! 1268: * ! 1269: * Tcl_ReadCmd -- ! 1270: * ! 1271: * This procedure is invoked to process the "read" Tcl command. ! 1272: * See the user documentation for details on what it does. ! 1273: * ! 1274: * Results: ! 1275: * A standard Tcl result. ! 1276: * ! 1277: * Side effects: ! 1278: * See the user documentation. ! 1279: * ! 1280: *---------------------------------------------------------------------- ! 1281: */ ! 1282: ! 1283: /* ARGSUSED */ ! 1284: int ! 1285: Tcl_ReadCmd(dummy, interp, argc, argv) ! 1286: ClientData dummy; /* Not used. */ ! 1287: Tcl_Interp *interp; /* Current interpreter. */ ! 1288: int argc; /* Number of arguments. */ ! 1289: char **argv; /* Argument strings. */ ! 1290: { ! 1291: OpenFile *filePtr; ! 1292: int bytesLeft, bytesRead, count; ! 1293: #define READ_BUF_SIZE 4096 ! 1294: char buffer[READ_BUF_SIZE+1]; ! 1295: int newline; ! 1296: ! 1297: if ((argc != 2) && (argc != 3)) { ! 1298: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 1299: " fileId ?numBytes|nonewline?\"", (char *) NULL); ! 1300: return TCL_ERROR; ! 1301: } ! 1302: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) { ! 1303: return TCL_ERROR; ! 1304: } ! 1305: if (!filePtr->readable) { ! 1306: Tcl_AppendResult(interp, "\"", argv[1], ! 1307: "\" wasn't opened for reading", (char *) NULL); ! 1308: return TCL_ERROR; ! 1309: } ! 1310: ! 1311: /* ! 1312: * Compute how many bytes to read, and see whether the final ! 1313: * newline should be dropped. ! 1314: */ ! 1315: ! 1316: newline = 1; ! 1317: if ((argc > 2) && isdigit(argv[2][0])) { ! 1318: if (Tcl_GetInt(interp, argv[2], &bytesLeft) != TCL_OK) { ! 1319: return TCL_ERROR; ! 1320: } ! 1321: } else { ! 1322: bytesLeft = 1<<30; ! 1323: if (argc > 2) { ! 1324: if (strncmp(argv[2], "nonewline", strlen(argv[2])) == 0) { ! 1325: newline = 0; ! 1326: } else { ! 1327: Tcl_AppendResult(interp, "bad argument \"", argv[2], ! 1328: "\": should be \"nonewline\"", (char *) NULL); ! 1329: return TCL_ERROR; ! 1330: } ! 1331: } ! 1332: } ! 1333: ! 1334: /* ! 1335: * Read the file in one or more chunks. ! 1336: */ ! 1337: ! 1338: bytesRead = 0; ! 1339: while (bytesLeft > 0) { ! 1340: count = READ_BUF_SIZE; ! 1341: if (bytesLeft < READ_BUF_SIZE) { ! 1342: count = bytesLeft; ! 1343: } ! 1344: count = fread(buffer, 1, count, filePtr->f); ! 1345: if (ferror(filePtr->f)) { ! 1346: Tcl_ResetResult(interp); ! 1347: Tcl_AppendResult(interp, "error reading \"", argv[1], ! 1348: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 1349: clearerr(filePtr->f); ! 1350: return TCL_ERROR; ! 1351: } ! 1352: if (count == 0) { ! 1353: break; ! 1354: } ! 1355: buffer[count] = 0; ! 1356: Tcl_AppendResult(interp, buffer, (char *) NULL); ! 1357: bytesLeft -= count; ! 1358: bytesRead += count; ! 1359: } ! 1360: if ((newline == 0) && (interp->result[bytesRead-1] == '\n')) { ! 1361: interp->result[bytesRead-1] = 0; ! 1362: } ! 1363: return TCL_OK; ! 1364: } ! 1365: ! 1366: /* ! 1367: *---------------------------------------------------------------------- ! 1368: * ! 1369: * Tcl_SeekCmd -- ! 1370: * ! 1371: * This procedure is invoked to process the "seek" Tcl command. ! 1372: * See the user documentation for details on what it does. ! 1373: * ! 1374: * Results: ! 1375: * A standard Tcl result. ! 1376: * ! 1377: * Side effects: ! 1378: * See the user documentation. ! 1379: * ! 1380: *---------------------------------------------------------------------- ! 1381: */ ! 1382: ! 1383: /* ARGSUSED */ ! 1384: int ! 1385: Tcl_SeekCmd(notUsed, interp, argc, argv) ! 1386: ClientData notUsed; /* Not used. */ ! 1387: Tcl_Interp *interp; /* Current interpreter. */ ! 1388: int argc; /* Number of arguments. */ ! 1389: char **argv; /* Argument strings. */ ! 1390: { ! 1391: OpenFile *filePtr; ! 1392: int offset, mode; ! 1393: ! 1394: if ((argc != 3) && (argc != 4)) { ! 1395: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 1396: " fileId offset ?origin?\"", (char *) NULL); ! 1397: return TCL_ERROR; ! 1398: } ! 1399: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) { ! 1400: return TCL_ERROR; ! 1401: } ! 1402: if (Tcl_GetInt(interp, argv[2], &offset) != TCL_OK) { ! 1403: return TCL_ERROR; ! 1404: } ! 1405: mode = SEEK_SET; ! 1406: if (argc == 4) { ! 1407: int length; ! 1408: char c; ! 1409: ! 1410: length = strlen(argv[3]); ! 1411: c = argv[3][0]; ! 1412: if ((c == 's') && (strncmp(argv[3], "start", length) == 0)) { ! 1413: mode = SEEK_SET; ! 1414: } else if ((c == 'c') && (strncmp(argv[3], "current", length) == 0)) { ! 1415: mode = SEEK_CUR; ! 1416: } else if ((c == 'e') && (strncmp(argv[3], "end", length) == 0)) { ! 1417: mode = SEEK_END; ! 1418: } else { ! 1419: Tcl_AppendResult(interp, "bad origin \"", argv[3], ! 1420: "\": should be start, current, or end", (char *) NULL); ! 1421: return TCL_ERROR; ! 1422: } ! 1423: } ! 1424: if (fseek(filePtr->f, offset, mode) == -1) { ! 1425: Tcl_AppendResult(interp, "error during seek: ", ! 1426: Tcl_UnixError(interp), (char *) NULL); ! 1427: clearerr(filePtr->f); ! 1428: return TCL_ERROR; ! 1429: } ! 1430: ! 1431: return TCL_OK; ! 1432: } ! 1433: ! 1434: /* ! 1435: *---------------------------------------------------------------------- ! 1436: * ! 1437: * Tcl_SourceCmd -- ! 1438: * ! 1439: * This procedure is invoked to process the "source" Tcl command. ! 1440: * See the user documentation for details on what it does. ! 1441: * ! 1442: * Results: ! 1443: * A standard Tcl result. ! 1444: * ! 1445: * Side effects: ! 1446: * See the user documentation. ! 1447: * ! 1448: *---------------------------------------------------------------------- ! 1449: */ ! 1450: ! 1451: /* ARGSUSED */ ! 1452: int ! 1453: Tcl_SourceCmd(dummy, interp, argc, argv) ! 1454: ClientData dummy; /* Not used. */ ! 1455: Tcl_Interp *interp; /* Current interpreter. */ ! 1456: int argc; /* Number of arguments. */ ! 1457: char **argv; /* Argument strings. */ ! 1458: { ! 1459: if (argc != 2) { ! 1460: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 1461: " fileName\"", (char *) NULL); ! 1462: return TCL_ERROR; ! 1463: } ! 1464: return Tcl_EvalFile(interp, argv[1]); ! 1465: } ! 1466: ! 1467: /* ! 1468: *---------------------------------------------------------------------- ! 1469: * ! 1470: * Tcl_TellCmd -- ! 1471: * ! 1472: * This procedure is invoked to process the "tell" Tcl command. ! 1473: * See the user documentation for details on what it does. ! 1474: * ! 1475: * Results: ! 1476: * A standard Tcl result. ! 1477: * ! 1478: * Side effects: ! 1479: * See the user documentation. ! 1480: * ! 1481: *---------------------------------------------------------------------- ! 1482: */ ! 1483: ! 1484: /* ARGSUSED */ ! 1485: int ! 1486: Tcl_TellCmd(notUsed, interp, argc, argv) ! 1487: ClientData notUsed; /* Not used. */ ! 1488: Tcl_Interp *interp; /* Current interpreter. */ ! 1489: int argc; /* Number of arguments. */ ! 1490: char **argv; /* Argument strings. */ ! 1491: { ! 1492: OpenFile *filePtr; ! 1493: ! 1494: if (argc != 2) { ! 1495: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 1496: " fileId\"", (char *) NULL); ! 1497: return TCL_ERROR; ! 1498: } ! 1499: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) { ! 1500: return TCL_ERROR; ! 1501: } ! 1502: sprintf(interp->result, "%d", ftell(filePtr->f)); ! 1503: return TCL_OK; ! 1504: } ! 1505: ! 1506: /* ! 1507: *---------------------------------------------------------------------- ! 1508: * ! 1509: * Tcl_TimeCmd -- ! 1510: * ! 1511: * This procedure is invoked to process the "time" Tcl command. ! 1512: * See the user documentation for details on what it does. ! 1513: * ! 1514: * Results: ! 1515: * A standard Tcl result. ! 1516: * ! 1517: * Side effects: ! 1518: * See the user documentation. ! 1519: * ! 1520: *---------------------------------------------------------------------- ! 1521: */ ! 1522: ! 1523: /* ARGSUSED */ ! 1524: int ! 1525: Tcl_TimeCmd(dummy, interp, argc, argv) ! 1526: ClientData dummy; /* Not used. */ ! 1527: Tcl_Interp *interp; /* Current interpreter. */ ! 1528: int argc; /* Number of arguments. */ ! 1529: char **argv; /* Argument strings. */ ! 1530: { ! 1531: int count, i, result; ! 1532: double timePer; ! 1533: #if TCL_GETTOD ! 1534: struct timeval start, stop; ! 1535: struct timezone tz; ! 1536: int micros; ! 1537: #else ! 1538: struct tms dummy2; ! 1539: long start, stop; ! 1540: #endif ! 1541: ! 1542: if (argc == 2) { ! 1543: count = 1; ! 1544: } else if (argc == 3) { ! 1545: if (Tcl_GetInt(interp, argv[2], &count) != TCL_OK) { ! 1546: return TCL_ERROR; ! 1547: } ! 1548: } else { ! 1549: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 1550: " command ?count?\"", (char *) NULL); ! 1551: return TCL_ERROR; ! 1552: } ! 1553: #if TCL_GETTOD ! 1554: gettimeofday(&start, &tz); ! 1555: #else ! 1556: start = times(&dummy2); ! 1557: #endif ! 1558: for (i = count ; i > 0; i--) { ! 1559: result = Tcl_Eval(interp, argv[1], 0, (char **) NULL); ! 1560: if (result != TCL_OK) { ! 1561: if (result == TCL_ERROR) { ! 1562: char msg[60]; ! 1563: sprintf(msg, "\n (\"time\" body line %d)", ! 1564: interp->errorLine); ! 1565: Tcl_AddErrorInfo(interp, msg); ! 1566: } ! 1567: return result; ! 1568: } ! 1569: } ! 1570: #if TCL_GETTOD ! 1571: gettimeofday(&stop, &tz); ! 1572: micros = (stop.tv_sec - start.tv_sec)*1000000 ! 1573: + (stop.tv_usec - start.tv_usec); ! 1574: timePer = micros; ! 1575: #else ! 1576: stop = times(&dummy2); ! 1577: timePer = (((double) (stop - start))*1000000.0)/CLK_TCK; ! 1578: #endif ! 1579: Tcl_ResetResult(interp); ! 1580: sprintf(interp->result, "%.0f microseconds per iteration", timePer/count); ! 1581: return TCL_OK; ! 1582: } ! 1583: ! 1584: /* ! 1585: *---------------------------------------------------------------------- ! 1586: * ! 1587: * CleanupChildren -- ! 1588: * ! 1589: * This is a utility procedure used to wait for child processes ! 1590: * to exit, record information about abnormal exits, and then ! 1591: * collect any stderr output generated by them. ! 1592: * ! 1593: * Results: ! 1594: * The return value is a standard Tcl result. If anything at ! 1595: * weird happened with the child processes, TCL_ERROR is returned ! 1596: * and a message is left in interp->result. ! 1597: * ! 1598: * Side effects: ! 1599: * If the last character of interp->result is a newline, then it ! 1600: * is removed. File errorId gets closed, and pidPtr is freed ! 1601: * back to the storage allocator. ! 1602: * ! 1603: *---------------------------------------------------------------------- ! 1604: */ ! 1605: ! 1606: static int ! 1607: CleanupChildren(interp, numPids, pidPtr, errorId) ! 1608: Tcl_Interp *interp; /* Used for error messages. */ ! 1609: int numPids; /* Number of entries in pidPtr array. */ ! 1610: int *pidPtr; /* Array of process ids of children. */ ! 1611: int errorId; /* File descriptor index for file containing ! 1612: * stderr output from pipeline. -1 means ! 1613: * there isn't any stderr output. */ ! 1614: { ! 1615: int result = TCL_OK; ! 1616: int i, pid, length; ! 1617: WAIT_STATUS_TYPE waitStatus; ! 1618: ! 1619: for (i = 0; i < numPids; i++) { ! 1620: pid = Tcl_WaitPids(1, &pidPtr[i], (int *) &waitStatus); ! 1621: if (pid == -1) { ! 1622: Tcl_AppendResult(interp, "error waiting for process to exit: ", ! 1623: Tcl_UnixError(interp), (char *) NULL); ! 1624: continue; ! 1625: } ! 1626: ! 1627: /* ! 1628: * Create error messages for unusual process exits. An ! 1629: * extra newline gets appended to each error message, but ! 1630: * it gets removed below (in the same fashion that an ! 1631: * extra newline in the command's output is removed). ! 1632: */ ! 1633: ! 1634: if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) { ! 1635: char msg1[20], msg2[20]; ! 1636: ! 1637: result = TCL_ERROR; ! 1638: sprintf(msg1, "%d", pid); ! 1639: if (WIFEXITED(waitStatus)) { ! 1640: sprintf(msg2, "%d", WEXITSTATUS(waitStatus)); ! 1641: Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2, ! 1642: (char *) NULL); ! 1643: } else if (WIFSIGNALED(waitStatus)) { ! 1644: char *p; ! 1645: ! 1646: p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus))); ! 1647: Tcl_SetErrorCode(interp, "CHILDKILLED", msg1, ! 1648: Tcl_SignalId((int) (WTERMSIG(waitStatus))), p, ! 1649: (char *) NULL); ! 1650: Tcl_AppendResult(interp, "child killed: ", p, "\n", ! 1651: (char *) NULL); ! 1652: } else if (WIFSTOPPED(waitStatus)) { ! 1653: char *p; ! 1654: ! 1655: p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus))); ! 1656: Tcl_SetErrorCode(interp, "CHILDSUSP", msg1, ! 1657: Tcl_SignalId((int) (WSTOPSIG(waitStatus))), p, (char *) NULL); ! 1658: Tcl_AppendResult(interp, "child suspended: ", p, "\n", ! 1659: (char *) NULL); ! 1660: } else { ! 1661: Tcl_AppendResult(interp, ! 1662: "child wait status didn't make sense\n", ! 1663: (char *) NULL); ! 1664: } ! 1665: } ! 1666: } ! 1667: ckfree((char *) pidPtr); ! 1668: ! 1669: /* ! 1670: * Read the standard error file. If there's anything there, ! 1671: * then return an error and add the file's contents to the result ! 1672: * string. ! 1673: */ ! 1674: ! 1675: if (errorId >= 0) { ! 1676: while (1) { ! 1677: # define BUFFER_SIZE 1000 ! 1678: char buffer[BUFFER_SIZE+1]; ! 1679: int count; ! 1680: ! 1681: count = read(errorId, buffer, BUFFER_SIZE); ! 1682: ! 1683: if (count == 0) { ! 1684: break; ! 1685: } ! 1686: if (count < 0) { ! 1687: Tcl_AppendResult(interp, ! 1688: "error reading stderr output file: ", ! 1689: Tcl_UnixError(interp), (char *) NULL); ! 1690: break; ! 1691: } ! 1692: buffer[count] = 0; ! 1693: Tcl_AppendResult(interp, buffer, (char *) NULL); ! 1694: } ! 1695: close(errorId); ! 1696: } ! 1697: ! 1698: /* ! 1699: * If the last character of interp->result is a newline, then remove ! 1700: * the newline character (the newline would just confuse things). ! 1701: */ ! 1702: ! 1703: length = strlen(interp->result); ! 1704: if ((length > 0) && (interp->result[length-1] == '\n')) { ! 1705: interp->result[length-1] = '\0'; ! 1706: } ! 1707: ! 1708: return result; ! 1709: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.