|
|
1.1 ! root 1: /* ! 2: * tclUnixUtil.c -- ! 3: * ! 4: * This file contains a collection of utility procedures that ! 5: * are present in the Tcl's UNIX core but not in the generic ! 6: * core. For example, they do file manipulation and process ! 7: * manipulation. ! 8: * ! 9: * The Tcl_Fork and Tcl_WaitPids procedures are based on code ! 10: * contributed by Karl Lehenbauer, Mark Diekhans and Peter ! 11: * da Silva. ! 12: * ! 13: * Copyright 1991 Regents of the University of California ! 14: * Permission to use, copy, modify, and distribute this ! 15: * software and its documentation for any purpose and without ! 16: * fee is hereby granted, provided that this copyright ! 17: * notice appears in all copies. The University of California ! 18: * makes no representations about the suitability of this ! 19: * software for any purpose. It is provided "as is" without ! 20: * express or implied warranty. ! 21: */ ! 22: ! 23: #ifndef lint ! 24: static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclUnixUtil.c,v 1.18 91/11/21 14:53:46 ouster Exp $ SPRITE (Berkeley)"; ! 25: #endif /* not lint */ ! 26: ! 27: #include "tclint.h" ! 28: #include "tclunix.h" ! 29: ! 30: /* ! 31: * Data structures of the following type are used by Tcl_Fork and ! 32: * Tcl_WaitPids to keep track of child processes. ! 33: */ ! 34: ! 35: typedef struct { ! 36: int pid; /* Process id of child. */ ! 37: WAIT_STATUS_TYPE status; /* Status returned when child exited or ! 38: * suspended. */ ! 39: int flags; /* Various flag bits; see below for ! 40: * definitions. */ ! 41: } WaitInfo; ! 42: ! 43: /* ! 44: * Flag bits in WaitInfo structures: ! 45: * ! 46: * WI_READY - Non-zero means process has exited or ! 47: * suspended since it was forked or last ! 48: * returned by Tcl_WaitPids. ! 49: * WI_DETACHED - Non-zero means no-one cares about the ! 50: * process anymore. Ignore it until it ! 51: * exits, then forget about it. ! 52: */ ! 53: ! 54: #define WI_READY 1 ! 55: #define WI_DETACHED 2 ! 56: ! 57: static WaitInfo *waitTable = NULL; ! 58: static int waitTableSize = 0; /* Total number of entries available in ! 59: * waitTable. */ ! 60: static int waitTableUsed = 0; /* Number of entries in waitTable that ! 61: * are actually in use right now. Active ! 62: * entries are always at the beginning ! 63: * of the table. */ ! 64: #define WAIT_TABLE_GROW_BY 4 ! 65: ! 66: /* ! 67: *---------------------------------------------------------------------- ! 68: * ! 69: * Tcl_EvalFile -- ! 70: * ! 71: * Read in a file and process the entire file as one gigantic ! 72: * Tcl command. ! 73: * ! 74: * Results: ! 75: * A standard Tcl result, which is either the result of executing ! 76: * the file or an error indicating why the file couldn't be read. ! 77: * ! 78: * Side effects: ! 79: * Depends on the commands in the file. ! 80: * ! 81: *---------------------------------------------------------------------- ! 82: */ ! 83: ! 84: int ! 85: Tcl_EvalFile(interp, fileName) ! 86: Tcl_Interp *interp; /* Interpreter in which to process file. */ ! 87: char *fileName; /* Name of file to process. Tilde-substitution ! 88: * will be performed on this name. */ ! 89: { ! 90: int fileId, result; ! 91: struct stat statBuf; ! 92: char *cmdBuffer, *end, *oldScriptFile; ! 93: Interp *iPtr = (Interp *) interp; ! 94: ! 95: oldScriptFile = iPtr->scriptFile; ! 96: iPtr->scriptFile = fileName; ! 97: fileName = Tcl_TildeSubst(interp, fileName); ! 98: if (fileName == NULL) { ! 99: goto error; ! 100: } ! 101: #ifdef MSDOS ! 102: filename2DOS(fileName); ! 103: #endif ! 104: fileId = open(fileName, O_RDONLY, 0); ! 105: ! 106: if (fileId < 0) { ! 107: Tcl_AppendResult(interp, "couldn't read file \"", fileName, ! 108: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 109: goto error; ! 110: } ! 111: if (fstat(fileId, &statBuf) == -1) { ! 112: Tcl_AppendResult(interp, "couldn't stat file \"", fileName, ! 113: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 114: close(fileId); ! 115: goto error; ! 116: } ! 117: cmdBuffer = (char *) ckalloc((unsigned) statBuf.st_size+1); ! 118: #ifdef MSDOS ! 119: if (read(fileId, cmdBuffer, (int) statBuf.st_size) < 0) { ! 120: #else ! 121: if (read(fileId, cmdBuffer, (int) statBuf.st_size) != statBuf.st_size) { ! 122: #endif ! 123: Tcl_AppendResult(interp, "error in reading file \"", fileName, ! 124: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 125: close(fileId); ! 126: goto error; ! 127: } ! 128: if (close(fileId) != 0) { ! 129: Tcl_AppendResult(interp, "error closing file \"", fileName, ! 130: "\": ", Tcl_UnixError(interp), (char *) NULL); ! 131: goto error; ! 132: } ! 133: cmdBuffer[statBuf.st_size] = 0; ! 134: result = Tcl_Eval(interp, cmdBuffer, 0, &end); ! 135: if (result == TCL_RETURN) { ! 136: result = TCL_OK; ! 137: } ! 138: if (result == TCL_ERROR) { ! 139: char msg[200]; ! 140: ! 141: /* ! 142: * Record information telling where the error occurred. ! 143: */ ! 144: ! 145: sprintf(msg, "\n (file \"%.150s\" line %d)", fileName, ! 146: interp->errorLine); ! 147: Tcl_AddErrorInfo(interp, msg); ! 148: } ! 149: ckfree(cmdBuffer); ! 150: iPtr->scriptFile = oldScriptFile; ! 151: return result; ! 152: ! 153: error: ! 154: iPtr->scriptFile = oldScriptFile; ! 155: return TCL_ERROR; ! 156: } ! 157: ! 158: /* ! 159: *---------------------------------------------------------------------- ! 160: * ! 161: * Tcl_Fork -- ! 162: * ! 163: * Create a new process using the vfork system call, and keep ! 164: * track of it for "safe" waiting with Tcl_WaitPids. ! 165: * ! 166: * Results: ! 167: * The return value is the value returned by the vfork system ! 168: * call (0 means child, > 0 means parent (value is child id), ! 169: * < 0 means error). ! 170: * ! 171: * Side effects: ! 172: * A new process is created, and an entry is added to an internal ! 173: * table of child processes if the process is created successfully. ! 174: * ! 175: *---------------------------------------------------------------------- ! 176: */ ! 177: ! 178: int ! 179: Tcl_Fork() ! 180: { ! 181: WaitInfo *waitPtr; ! 182: pid_t pid; ! 183: ! 184: /* ! 185: * Disable SIGPIPE signals: if they were allowed, this process ! 186: * might go away unexpectedly if children misbehave. This code ! 187: * can potentially interfere with other application code that ! 188: * expects to handle SIGPIPEs; what's really needed is an ! 189: * arbiter for signals to allow them to be "shared". ! 190: */ ! 191: ! 192: if (waitTable == NULL) { ! 193: (void) signal(SIGPIPE, SIG_IGN); ! 194: } ! 195: ! 196: /* ! 197: * Enlarge the wait table if there isn't enough space for a new ! 198: * entry. ! 199: */ ! 200: ! 201: if (waitTableUsed == waitTableSize) { ! 202: int newSize; ! 203: WaitInfo *newWaitTable; ! 204: ! 205: newSize = waitTableSize + WAIT_TABLE_GROW_BY; ! 206: newWaitTable = (WaitInfo *) ckalloc((unsigned) ! 207: (newSize * sizeof(WaitInfo))); ! 208: memcpy((VOID *) newWaitTable, (VOID *) waitTable, ! 209: (waitTableSize * sizeof(WaitInfo))); ! 210: if (waitTable != NULL) { ! 211: ckfree((char *) waitTable); ! 212: } ! 213: waitTable = newWaitTable; ! 214: waitTableSize = newSize; ! 215: } ! 216: ! 217: /* ! 218: * Make a new process and enter it into the table if the fork ! 219: * is successful. ! 220: */ ! 221: ! 222: waitPtr = &waitTable[waitTableUsed]; ! 223: pid = fork(); ! 224: if (pid > 0) { ! 225: waitPtr->pid = pid; ! 226: waitPtr->flags = 0; ! 227: waitTableUsed++; ! 228: } ! 229: return pid; ! 230: } ! 231: ! 232: /* ! 233: *---------------------------------------------------------------------- ! 234: * ! 235: * Tcl_WaitPids -- ! 236: * ! 237: * This procedure is used to wait for one or more processes created ! 238: * by Tcl_Fork to exit or suspend. It records information about ! 239: * all processes that exit or suspend, even those not waited for, ! 240: * so that later waits for them will be able to get the status ! 241: * information. ! 242: * ! 243: * Results: ! 244: * -1 is returned if there is an error in the wait kernel call. ! 245: * Otherwise the pid of an exited/suspended process from *pidPtr ! 246: * is returned and *statusPtr is set to the status value returned ! 247: * by the wait kernel call. ! 248: * ! 249: * Side effects: ! 250: * Doesn't return until one of the pids at *pidPtr exits or suspends. ! 251: * ! 252: *---------------------------------------------------------------------- ! 253: */ ! 254: ! 255: int ! 256: Tcl_WaitPids(numPids, pidPtr, statusPtr) ! 257: int numPids; /* Number of pids to wait on: gives size ! 258: * of array pointed to by pidPtr. */ ! 259: int *pidPtr; /* Pids to wait on: return when one of ! 260: * these processes exits or suspends. */ ! 261: int *statusPtr; /* Wait status is returned here. */ ! 262: { ! 263: int i, count, pid; ! 264: register WaitInfo *waitPtr; ! 265: int anyProcesses; ! 266: WAIT_STATUS_TYPE status; ! 267: ! 268: while (1) { ! 269: /* ! 270: * Scan the table of child processes to see if one of the ! 271: * specified children has already exited or suspended. If so, ! 272: * remove it from the table and return its status. ! 273: */ ! 274: ! 275: anyProcesses = 0; ! 276: for (waitPtr = waitTable, count = waitTableUsed; ! 277: count > 0; waitPtr++, count--) { ! 278: for (i = 0; i < numPids; i++) { ! 279: if (pidPtr[i] != waitPtr->pid) { ! 280: continue; ! 281: } ! 282: anyProcesses = 1; ! 283: if (waitPtr->flags & WI_READY) { ! 284: *statusPtr = *((int *) &waitPtr->status); ! 285: pid = waitPtr->pid; ! 286: if (WIFEXITED(waitPtr->status) ! 287: || WIFSIGNALED(waitPtr->status)) { ! 288: *waitPtr = waitTable[waitTableUsed-1]; ! 289: waitTableUsed--; ! 290: } else { ! 291: waitPtr->flags &= ~WI_READY; ! 292: } ! 293: return pid; ! 294: } ! 295: } ! 296: } ! 297: ! 298: /* ! 299: * Make sure that the caller at least specified one valid ! 300: * process to wait for. ! 301: */ ! 302: ! 303: if (!anyProcesses) { ! 304: errno = ECHILD; ! 305: return -1; ! 306: } ! 307: ! 308: /* ! 309: * Wait for a process to exit or suspend, then update its ! 310: * entry in the table and go back to the beginning of the ! 311: * loop to see if it's one of the desired processes. ! 312: */ ! 313: ! 314: pid = wait(&status); ! 315: if (pid < 0) { ! 316: return pid; ! 317: } ! 318: for (waitPtr = waitTable, count = waitTableUsed; ; ! 319: waitPtr++, count--) { ! 320: if (count == 0) { ! 321: break; /* Ignore unknown processes. */ ! 322: } ! 323: if (pid != waitPtr->pid) { ! 324: continue; ! 325: } ! 326: ! 327: /* ! 328: * If the process has been detached, then ignore anything ! 329: * other than an exit, and drop the entry on exit. ! 330: */ ! 331: ! 332: if (waitPtr->flags & WI_DETACHED) { ! 333: if (WIFEXITED(status) || WIFSIGNALED(status)) { ! 334: *waitPtr = waitTable[waitTableUsed-1]; ! 335: waitTableUsed--; ! 336: } ! 337: } else { ! 338: waitPtr->status = status; ! 339: waitPtr->flags |= WI_READY; ! 340: } ! 341: break; ! 342: } ! 343: } ! 344: } ! 345: ! 346: /* ! 347: *---------------------------------------------------------------------- ! 348: * ! 349: * Tcl_DetachPids -- ! 350: * ! 351: * This procedure is called to indicate that one or more child ! 352: * processes have been placed in background and are no longer ! 353: * cared about. They should be ignored in future calls to ! 354: * Tcl_WaitPids. ! 355: * ! 356: * Results: ! 357: * None. ! 358: * ! 359: * Side effects: ! 360: * None. ! 361: * ! 362: *---------------------------------------------------------------------- ! 363: */ ! 364: ! 365: void ! 366: Tcl_DetachPids(numPids, pidPtr) ! 367: int numPids; /* Number of pids to detach: gives size ! 368: * of array pointed to by pidPtr. */ ! 369: int *pidPtr; /* Array of pids to detach: must have ! 370: * been created by Tcl_Fork. */ ! 371: { ! 372: register WaitInfo *waitPtr; ! 373: int i, count, pid; ! 374: ! 375: for (i = 0; i < numPids; i++) { ! 376: pid = pidPtr[i]; ! 377: for (waitPtr = waitTable, count = waitTableUsed; ! 378: count > 0; waitPtr++, count--) { ! 379: if (pid != waitPtr->pid) { ! 380: continue; ! 381: } ! 382: ! 383: /* ! 384: * If the process has already exited then destroy its ! 385: * table entry now. ! 386: */ ! 387: ! 388: if ((waitPtr->flags & WI_READY) && (WIFEXITED(waitPtr->status) ! 389: || WIFSIGNALED(waitPtr->status))) { ! 390: *waitPtr = waitTable[waitTableUsed-1]; ! 391: waitTableUsed--; ! 392: } else { ! 393: waitPtr->flags |= WI_DETACHED; ! 394: } ! 395: goto nextPid; ! 396: } ! 397: panic("Tcl_Detach couldn't find process"); ! 398: ! 399: nextPid: ! 400: continue; ! 401: } ! 402: } ! 403: ! 404: /* ! 405: *---------------------------------------------------------------------- ! 406: * ! 407: * Tcl_CreatePipeline -- ! 408: * ! 409: * Given an argc/argv array, instantiate a pipeline of processes ! 410: * as described by the argv. ! 411: * ! 412: * Results: ! 413: * The return value is a count of the number of new processes ! 414: * created, or -1 if an error occurred while creating the pipeline. ! 415: * *pidArrayPtr is filled in with the address of a dynamically ! 416: * allocated array giving the ids of all of the processes. It ! 417: * is up to the caller to free this array when it isn't needed ! 418: * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in ! 419: * with the file id for the input pipe for the pipeline (if any): ! 420: * the caller must eventually close this file. If outPipePtr ! 421: * isn't NULL, then *outPipePtr is filled in with the file id ! 422: * for the output pipe from the pipeline: the caller must close ! 423: * this file. If errFilePtr isn't NULL, then *errFilePtr is filled ! 424: * with a file id that may be used to read error output after the ! 425: * pipeline completes. ! 426: * ! 427: * Side effects: ! 428: * Processes and pipes are created. ! 429: * ! 430: *---------------------------------------------------------------------- ! 431: */ ! 432: ! 433: int ! 434: Tcl_CreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr, ! 435: outPipePtr, errFilePtr) ! 436: Tcl_Interp *interp; /* Interpreter to use for error reporting. */ ! 437: int argc; /* Number of entries in argv. */ ! 438: char **argv; /* Array of strings describing commands in ! 439: * pipeline plus I/O redirection with <, ! 440: * <<, and >. Argv[argc] must be NULL. */ ! 441: int **pidArrayPtr; /* Word at *pidArrayPtr gets filled in with ! 442: * address of array of pids for processes ! 443: * in pipeline (first pid is first process ! 444: * in pipeline). */ ! 445: int *inPipePtr; /* If non-NULL, input to the pipeline comes ! 446: * from a pipe (unless overridden by ! 447: * redirection in the command). The file ! 448: * id with which to write to this pipe is ! 449: * stored at *inPipePtr. -1 means command ! 450: * specified its own input source. */ ! 451: int *outPipePtr; /* If non-NULL, output to the pipeline goes ! 452: * to a pipe, unless overriden by redirection ! 453: * in the command. The file id with which to ! 454: * read frome this pipe is stored at ! 455: * *outPipePtr. -1 means command specified ! 456: * its own output sink. */ ! 457: int *errFilePtr; /* If non-NULL, all stderr output from the ! 458: * pipeline will go to a temporary file ! 459: * created here, and a descriptor to read ! 460: * the file will be left at *errFilePtr. ! 461: * The file will be removed already, so ! 462: * closing this descriptor will be the end ! 463: * of the file. If this is NULL, then ! 464: * all stderr output goes to our stderr. */ ! 465: { ! 466: int *pidPtr = NULL; /* Points to malloc-ed array holding all ! 467: * the pids of child processes. */ ! 468: int numPids = 0; /* Actual number of processes that exist ! 469: * at *pidPtr right now. */ ! 470: int cmdCount; /* Count of number of distinct commands ! 471: * found in argc/argv. */ ! 472: char *input = NULL; /* Describes input for pipeline, depending ! 473: * on "inputFile". NULL means take input ! 474: * from stdin/pipe. */ ! 475: int inputFile = 0; /* Non-zero means input is name of input ! 476: * file. Zero means input holds actual ! 477: * text to be input to command. */ ! 478: char *output = NULL; /* Holds name of output file to pipe to, ! 479: * or NULL if output goes to stdout/pipe. */ ! 480: int inputId = -1; /* Readable file id input to current command in ! 481: * pipeline (could be file or pipe). -1 ! 482: * means use stdin. */ ! 483: int outputId = -1; /* Writable file id for output from current ! 484: * command in pipeline (could be file or pipe). ! 485: * -1 means use stdout. */ ! 486: int errorId = -1; /* Writable file id for all standard error ! 487: * output from all commands in pipeline. -1 ! 488: * means use stderr. */ ! 489: int lastOutputId = -1; /* Write file id for output from last command ! 490: * in pipeline (could be file or pipe). ! 491: * -1 means use stdout. */ ! 492: int pipeIds[2]; /* File ids for pipe that's being created. */ ! 493: int firstArg, lastArg; /* Indexes of first and last arguments in ! 494: * current command. */ ! 495: int lastBar; ! 496: char *execName; ! 497: int i, j, pid; ! 498: ! 499: if (inPipePtr != NULL) { ! 500: *inPipePtr = -1; ! 501: } ! 502: if (outPipePtr != NULL) { ! 503: *outPipePtr = -1; ! 504: } ! 505: if (errFilePtr != NULL) { ! 506: *errFilePtr = -1; ! 507: } ! 508: pipeIds[0] = pipeIds[1] = -1; ! 509: ! 510: /* ! 511: * First, scan through all the arguments to figure out the structure ! 512: * of the pipeline. Count the number of distinct processes (it's the ! 513: * number of "|" arguments). If there are "<", "<<", or ">" arguments ! 514: * then make note of input and output redirection and remove these ! 515: * arguments and the arguments that follow them. ! 516: */ ! 517: ! 518: cmdCount = 1; ! 519: lastBar = -1; ! 520: for (i = 0; i < argc; i++) { ! 521: if ((argv[i][0] == '|') && ((argv[i][1] == 0))) { ! 522: if ((i == (lastBar+1)) || (i == (argc-1))) { ! 523: interp->result = "illegal use of | in command"; ! 524: return -1; ! 525: } ! 526: lastBar = i; ! 527: cmdCount++; ! 528: continue; ! 529: } else if (argv[i][0] == '<') { ! 530: if (argv[i][1] == 0) { ! 531: input = argv[i+1]; ! 532: inputFile = 1; ! 533: } else if ((argv[i][1] == '<') && (argv[i][2] == 0)) { ! 534: input = argv[i+1]; ! 535: inputFile = 0; ! 536: } else { ! 537: continue; ! 538: } ! 539: } else if ((argv[i][0] == '>') && (argv[i][1] == 0)) { ! 540: output = argv[i+1]; ! 541: } else { ! 542: continue; ! 543: } ! 544: if (i >= (argc-1)) { ! 545: Tcl_AppendResult(interp, "can't specify \"", argv[i], ! 546: "\" as last word in command", (char *) NULL); ! 547: return -1; ! 548: } ! 549: for (j = i+2; j < argc; j++) { ! 550: argv[j-2] = argv[j]; ! 551: } ! 552: argc -= 2; ! 553: i--; /* Process new arg from same position. */ ! 554: } ! 555: if (argc == 0) { ! 556: interp->result = "didn't specify command to execute"; ! 557: return -1; ! 558: } ! 559: ! 560: /* ! 561: * Set up the redirected input source for the pipeline, if ! 562: * so requested. ! 563: */ ! 564: ! 565: if (input != NULL) { ! 566: if (!inputFile) { ! 567: /* ! 568: * Immediate data in command. Create temporary file and ! 569: * put data into file. ! 570: */ ! 571: ! 572: #ifdef MSDOS ! 573: # define TMP_STDIN_NAME "tcl.in" ! 574: #else ! 575: # define TMP_STDIN_NAME "/tmp/tcl.in.XXXXXX" ! 576: #endif ! 577: char inName[sizeof(TMP_STDIN_NAME) + 1]; ! 578: int length; ! 579: ! 580: strcpy(inName, TMP_STDIN_NAME); ! 581: mkstemp(inName); ! 582: inputId = open(inName, O_RDWR|O_CREAT|O_TRUNC, 0600); ! 583: if (inputId < 0) { ! 584: Tcl_AppendResult(interp, ! 585: "couldn't create input file for command: ", ! 586: Tcl_UnixError(interp), (char *) NULL); ! 587: goto error; ! 588: } ! 589: length = strlen(input); ! 590: #ifdef MSDOS ! 591: if (write(inputId, input, length) < 0) { ! 592: #else ! 593: if (write(inputId, input, length) != length) { ! 594: #endif ! 595: Tcl_AppendResult(interp, ! 596: "couldn't write file input for command: ", ! 597: Tcl_UnixError(interp), (char *) NULL); ! 598: goto error; ! 599: } ! 600: if ((lseek(inputId, 0L, 0) == -1) || (unlink(inName) == -1)) { ! 601: Tcl_AppendResult(interp, ! 602: "couldn't reset or remove input file for command: ", ! 603: Tcl_UnixError(interp), (char *) NULL); ! 604: goto error; ! 605: } ! 606: } else { ! 607: /* ! 608: * File redirection. Just open the file. ! 609: */ ! 610: ! 611: inputId = open(input, O_RDONLY, 0); ! 612: if (inputId < 0) { ! 613: Tcl_AppendResult(interp, ! 614: "couldn't read file \"", input, "\": ", ! 615: Tcl_UnixError(interp), (char *) NULL); ! 616: goto error; ! 617: } ! 618: } ! 619: } else if (inPipePtr != NULL) { ! 620: if (pipe(pipeIds) != 0) { ! 621: Tcl_AppendResult(interp, ! 622: "couldn't create input pipe for command: ", ! 623: Tcl_UnixError(interp), (char *) NULL); ! 624: goto error; ! 625: } ! 626: inputId = pipeIds[0]; ! 627: *inPipePtr = pipeIds[1]; ! 628: pipeIds[0] = pipeIds[1] = -1; ! 629: } ! 630: ! 631: /* ! 632: * Set up the redirected output sink for the pipeline from one ! 633: * of two places, if requested. ! 634: */ ! 635: ! 636: if (output != NULL) { ! 637: /* ! 638: * Output is to go to a file. ! 639: */ ! 640: ! 641: lastOutputId = open(output, O_WRONLY|O_CREAT|O_TRUNC, 0666); ! 642: if (lastOutputId < 0) { ! 643: Tcl_AppendResult(interp, ! 644: "couldn't write file \"", output, "\": ", ! 645: Tcl_UnixError(interp), (char *) NULL); ! 646: goto error; ! 647: } ! 648: } else if (outPipePtr != NULL) { ! 649: /* ! 650: * Output is to go to a pipe. ! 651: */ ! 652: ! 653: if (pipe(pipeIds) != 0) { ! 654: Tcl_AppendResult(interp, ! 655: "couldn't create output pipe: ", ! 656: Tcl_UnixError(interp), (char *) NULL); ! 657: goto error; ! 658: } ! 659: lastOutputId = pipeIds[1]; ! 660: *outPipePtr = pipeIds[0]; ! 661: pipeIds[0] = pipeIds[1] = -1; ! 662: } ! 663: ! 664: /* ! 665: * Set up the standard error output sink for the pipeline, if ! 666: * requested. Use a temporary file which is opened, then deleted. ! 667: * Could potentially just use pipe, but if it filled up it could ! 668: * cause the pipeline to deadlock: we'd be waiting for processes ! 669: * to complete before reading stderr, and processes couldn't complete ! 670: * because stderr was backed up. ! 671: */ ! 672: ! 673: if (errFilePtr != NULL) { ! 674: # define TMP_STDERR_NAME "/tmp/tcl.err.XXXXXX" ! 675: char errName[sizeof(TMP_STDERR_NAME) + 1]; ! 676: ! 677: strcpy(errName, TMP_STDERR_NAME); ! 678: mkstemp(errName); ! 679: errorId = open(errName, O_WRONLY|O_CREAT|O_TRUNC, 0600); ! 680: if (errorId < 0) { ! 681: errFileError: ! 682: Tcl_AppendResult(interp, ! 683: "couldn't create error file for command: ", ! 684: Tcl_UnixError(interp), (char *) NULL); ! 685: goto error; ! 686: } ! 687: *errFilePtr = open(errName, O_RDONLY, 0); ! 688: if (*errFilePtr < 0) { ! 689: goto errFileError; ! 690: } ! 691: if (unlink(errName) == -1) { ! 692: Tcl_AppendResult(interp, ! 693: "couldn't remove error file for command: ", ! 694: Tcl_UnixError(interp), (char *) NULL); ! 695: goto error; ! 696: } ! 697: } ! 698: ! 699: /* ! 700: * Scan through the argc array, forking off a process for each ! 701: * group of arguments between "|" arguments. ! 702: */ ! 703: ! 704: pidPtr = (int *) ckalloc((unsigned) (cmdCount * sizeof(int))); ! 705: for (i = 0; i < numPids; i++) { ! 706: pidPtr[i] = -1; ! 707: } ! 708: for (firstArg = 0; firstArg < argc; numPids++, firstArg = lastArg+1) { ! 709: for (lastArg = firstArg; lastArg < argc; lastArg++) { ! 710: if ((argv[lastArg][0] == '|') && (argv[lastArg][1] == 0)) { ! 711: break; ! 712: } ! 713: } ! 714: argv[lastArg] = NULL; ! 715: if (lastArg == argc) { ! 716: outputId = lastOutputId; ! 717: } else { ! 718: if (pipe(pipeIds) != 0) { ! 719: Tcl_AppendResult(interp, "couldn't create pipe: ", ! 720: Tcl_UnixError(interp), (char *) NULL); ! 721: goto error; ! 722: } ! 723: outputId = pipeIds[1]; ! 724: } ! 725: execName = Tcl_TildeSubst(interp, argv[firstArg]); ! 726: pid = Tcl_Fork(); ! 727: if (pid == -1) { ! 728: Tcl_AppendResult(interp, "couldn't fork child process: ", ! 729: Tcl_UnixError(interp), (char *) NULL); ! 730: goto error; ! 731: } ! 732: if (pid == 0) { ! 733: char errSpace[200]; ! 734: ! 735: if (((inputId != -1) && (dup2(inputId, 0) == -1)) ! 736: || ((outputId != -1) && (dup2(outputId, 1) == -1)) ! 737: || ((errorId != -1) && (dup2(errorId, 2) == -1))) { ! 738: char *err; ! 739: err = "forked process couldn't set up input/output\n"; ! 740: write(errorId < 0 ? 2 : errorId, err, strlen(err)); ! 741: _exit(1); ! 742: } ! 743: for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); ! 744: i++) { ! 745: close(i); ! 746: } ! 747: execvp(execName, &argv[firstArg]); ! 748: sprintf(errSpace, "couldn't find \"%.150s\" to execute\n", ! 749: argv[firstArg]); ! 750: write(2, errSpace, strlen(errSpace)); ! 751: _exit(1); ! 752: } else { ! 753: pidPtr[numPids] = pid; ! 754: } ! 755: ! 756: /* ! 757: * Close off our copies of file descriptors that were set up for ! 758: * this child, then set up the input for the next child. ! 759: */ ! 760: ! 761: if (inputId != -1) { ! 762: close(inputId); ! 763: } ! 764: if (outputId != -1) { ! 765: close(outputId); ! 766: } ! 767: inputId = pipeIds[0]; ! 768: pipeIds[0] = pipeIds[1] = -1; ! 769: } ! 770: *pidArrayPtr = pidPtr; ! 771: ! 772: /* ! 773: * All done. Cleanup open files lying around and then return. ! 774: */ ! 775: ! 776: cleanup: ! 777: if (inputId != -1) { ! 778: close(inputId); ! 779: } ! 780: if (lastOutputId != -1) { ! 781: close(lastOutputId); ! 782: } ! 783: if (errorId != -1) { ! 784: close(errorId); ! 785: } ! 786: return numPids; ! 787: ! 788: /* ! 789: * An error occurred. There could have been extra files open, such ! 790: * as pipes between children. Clean them all up. Detach any child ! 791: * processes that have been created. ! 792: */ ! 793: ! 794: error: ! 795: if ((inPipePtr != NULL) && (*inPipePtr != -1)) { ! 796: close(*inPipePtr); ! 797: *inPipePtr = -1; ! 798: } ! 799: if ((outPipePtr != NULL) && (*outPipePtr != -1)) { ! 800: close(*outPipePtr); ! 801: *outPipePtr = -1; ! 802: } ! 803: if ((errFilePtr != NULL) && (*errFilePtr != -1)) { ! 804: close(*errFilePtr); ! 805: *errFilePtr = -1; ! 806: } ! 807: if (pipeIds[0] != -1) { ! 808: close(pipeIds[0]); ! 809: } ! 810: if (pipeIds[1] != -1) { ! 811: close(pipeIds[1]); ! 812: } ! 813: if (pidPtr != NULL) { ! 814: for (i = 0; i < numPids; i++) { ! 815: if (pidPtr[i] != -1) { ! 816: Tcl_DetachPids(1, &pidPtr[i]); ! 817: } ! 818: } ! 819: ckfree((char *) pidPtr); ! 820: } ! 821: numPids = -1; ! 822: goto cleanup; ! 823: } ! 824: ! 825: /* ! 826: *---------------------------------------------------------------------- ! 827: * ! 828: * Tcl_UnixError -- ! 829: * ! 830: * This procedure is typically called after UNIX kernel calls ! 831: * return errors. It stores machine-readable information about ! 832: * the error in $errorCode returns an information string for ! 833: * the caller's use. ! 834: * ! 835: * Results: ! 836: * The return value is a human-readable string describing the ! 837: * error, as returned by strerror. ! 838: * ! 839: * Side effects: ! 840: * The global variable $errorCode is reset. ! 841: * ! 842: *---------------------------------------------------------------------- ! 843: */ ! 844: ! 845: char * ! 846: Tcl_UnixError(interp) ! 847: Tcl_Interp *interp; /* Interpreter whose $errorCode variable ! 848: * is to be changed. */ ! 849: { ! 850: char *id, *msg; ! 851: ! 852: id = Tcl_ErrnoId(); ! 853: msg = strerror(errno); ! 854: Tcl_SetErrorCode(interp, "UNIX", id, msg, (char *) NULL); ! 855: return msg; ! 856: } ! 857: ! 858: /* ! 859: *---------------------------------------------------------------------- ! 860: * ! 861: * TclMakeFileTable -- ! 862: * ! 863: * Create or enlarge the file table for the interpreter, so that ! 864: * there is room for a given index. ! 865: * ! 866: * Results: ! 867: * None. ! 868: * ! 869: * Side effects: ! 870: * The file table for iPtr will be created if it doesn't exist ! 871: * (and entries will be added for stdin, stdout, and stderr). ! 872: * If it already exists, then it will be grown if necessary. ! 873: * ! 874: *---------------------------------------------------------------------- ! 875: */ ! 876: ! 877: void ! 878: TclMakeFileTable(iPtr, index) ! 879: Interp *iPtr; /* Interpreter whose table of files is ! 880: * to be manipulated. */ ! 881: int index; /* Make sure table is large enough to ! 882: * hold at least this index. */ ! 883: { ! 884: /* ! 885: * If the table doesn't even exist, then create it and initialize ! 886: * entries for standard files. ! 887: */ ! 888: ! 889: if (iPtr->numFiles == 0) { ! 890: OpenFile *filePtr; ! 891: int i; ! 892: ! 893: if (index < 2) { ! 894: iPtr->numFiles = 3; ! 895: } else { ! 896: iPtr->numFiles = index+1; ! 897: } ! 898: iPtr->filePtrArray = (OpenFile **) ckalloc((unsigned) ! 899: ((iPtr->numFiles)*sizeof(OpenFile *))); ! 900: for (i = iPtr->numFiles-1; i >= 0; i--) { ! 901: iPtr->filePtrArray[i] = NULL; ! 902: } ! 903: ! 904: filePtr = (OpenFile *) ckalloc(sizeof(OpenFile)); ! 905: filePtr->f = stdin; ! 906: filePtr->f2 = NULL; ! 907: filePtr->readable = 1; ! 908: filePtr->writable = 0; ! 909: filePtr->numPids = 0; ! 910: filePtr->pidPtr = NULL; ! 911: filePtr->errorId = -1; ! 912: iPtr->filePtrArray[0] = filePtr; ! 913: ! 914: filePtr = (OpenFile *) ckalloc(sizeof(OpenFile)); ! 915: filePtr->f = stdout; ! 916: filePtr->f2 = NULL; ! 917: filePtr->readable = 0; ! 918: filePtr->writable = 1; ! 919: filePtr->numPids = 0; ! 920: filePtr->pidPtr = NULL; ! 921: filePtr->errorId = -1; ! 922: iPtr->filePtrArray[1] = filePtr; ! 923: ! 924: filePtr = (OpenFile *) ckalloc(sizeof(OpenFile)); ! 925: filePtr->f = stderr; ! 926: filePtr->f2 = NULL; ! 927: filePtr->readable = 0; ! 928: filePtr->writable = 1; ! 929: filePtr->numPids = 0; ! 930: filePtr->pidPtr = NULL; ! 931: filePtr->errorId = -1; ! 932: iPtr->filePtrArray[2] = filePtr; ! 933: } else if (index >= iPtr->numFiles) { ! 934: int newSize; ! 935: OpenFile **newPtrArray; ! 936: int i; ! 937: ! 938: newSize = index+1; ! 939: newPtrArray = (OpenFile **) ckalloc((unsigned) ! 940: ((newSize)*sizeof(OpenFile *))); ! 941: memcpy((VOID *) newPtrArray, (VOID *) iPtr->filePtrArray, ! 942: iPtr->numFiles*sizeof(OpenFile *)); ! 943: for (i = iPtr->numFiles; i < newSize; i++) { ! 944: newPtrArray[i] = NULL; ! 945: } ! 946: ckfree((char *) iPtr->filePtrArray); ! 947: iPtr->numFiles = newSize; ! 948: iPtr->filePtrArray = newPtrArray; ! 949: } ! 950: } ! 951: ! 952: /* ! 953: *---------------------------------------------------------------------- ! 954: * ! 955: * TclGetOpenFile -- ! 956: * ! 957: * Given a string identifier for an open file, find the corresponding ! 958: * open file structure, if there is one. ! 959: * ! 960: * Results: ! 961: * A standard Tcl return value. If the open file is successfully ! 962: * located, *filePtrPtr is modified to point to its structure. ! 963: * If TCL_ERROR is returned then interp->result contains an error ! 964: * message. ! 965: * ! 966: * Side effects: ! 967: * None. ! 968: * ! 969: *---------------------------------------------------------------------- ! 970: */ ! 971: ! 972: int ! 973: TclGetOpenFile(interp, string, filePtrPtr) ! 974: Tcl_Interp *interp; /* Interpreter in which to find file. */ ! 975: char *string; /* String that identifies file. */ ! 976: OpenFile **filePtrPtr; /* Address of word in which to store pointer ! 977: * to structure about open file. */ ! 978: { ! 979: int fd = 0; /* Initial value needed only to stop compiler ! 980: * warnings. */ ! 981: Interp *iPtr = (Interp *) interp; ! 982: ! 983: if ((string[0] == 'f') && (string[1] == 'i') && (string[2] == 'l') ! 984: & (string[3] == 'e')) { ! 985: char *end; ! 986: ! 987: fd = strtoul(string+4, &end, 10); ! 988: if ((end == string+4) || (*end != 0)) { ! 989: goto badId; ! 990: } ! 991: } else if ((string[0] == 's') && (string[1] == 't') ! 992: && (string[2] == 'd')) { ! 993: if (strcmp(string+3, "in") == 0) { ! 994: fd = 0; ! 995: } else if (strcmp(string+3, "out") == 0) { ! 996: fd = 1; ! 997: } else if (strcmp(string+3, "err") == 0) { ! 998: fd = 2; ! 999: } else { ! 1000: goto badId; ! 1001: } ! 1002: } else { ! 1003: badId: ! 1004: Tcl_AppendResult(interp, "bad file identifier \"", string, ! 1005: "\"", (char *) NULL); ! 1006: return TCL_ERROR; ! 1007: } ! 1008: ! 1009: if (fd >= iPtr->numFiles) { ! 1010: if ((iPtr->numFiles == 0) && (fd <= 2)) { ! 1011: TclMakeFileTable(iPtr, fd); ! 1012: } else { ! 1013: notOpen: ! 1014: Tcl_AppendResult(interp, "file \"", string, "\" isn't open", ! 1015: (char *) NULL); ! 1016: return TCL_ERROR; ! 1017: } ! 1018: } ! 1019: if (iPtr->filePtrArray[fd] == NULL) { ! 1020: goto notOpen; ! 1021: } ! 1022: *filePtrPtr = iPtr->filePtrArray[fd]; ! 1023: return TCL_OK; ! 1024: } ! 1025: ! 1026: #ifdef MSDOS ! 1027: int ! 1028: filename2DOS(name) ! 1029: char *name; ! 1030: { ! 1031: for ( ; *name; name++) if (*name == '/') *name = '\\'; ! 1032: } ! 1033: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.