|
|
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.