Annotation of micropolis/src/tcl/tclunxut.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.