home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume25 / tcl / part19 < prev    next >
Encoding:
Text File  |  1991-11-15  |  30.5 KB  |  1,068 lines

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