home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / x / xvisrc.zoo / unix.c < prev    next >
C/C++ Source or Header  |  1992-07-28  |  21KB  |  982 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)unix.c    2.1 (Chris & John Downey) 7/29/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     unix.c
  14. * module function:
  15.     System interface routines for all versions of UNIX.
  16. * history:
  17.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  18.     Originally by Tim Thompson (twitch!tjt)
  19.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  20.     Heavily modified by Chris & John Downey
  21.  
  22. ***/
  23.  
  24. #include "xvi.h"
  25.  
  26. #ifndef SIGINT
  27. #   include <signal.h>        /* get signals for call_shell() */
  28. #endif
  29.  
  30. #ifdef    BSD
  31. #   include <sys/wait.h>    /* get wait stuff for call_shell() */
  32.     typedef    union wait Wait_t;
  33. #else
  34.     typedef    int Wait_t;
  35. #endif
  36.  
  37. /*
  38.  * CTRL is defined by sgtty.h (or by a file it includes)
  39.  * so we undefine it here to avoid conflicts with the
  40.  * version defined in "xvi.h".
  41.  */
  42. #undef    CTRL
  43.  
  44. #ifdef    sun
  45. #   ifndef TERMIOS
  46. #    define    TERMIOS
  47. #   endif
  48. #endif
  49.  
  50. #ifdef TERMIOS
  51. #   ifndef TERMIO
  52. #    define    TERMIO
  53. #   endif
  54. #endif
  55.  
  56. #ifdef    TERMIO
  57. #   ifdef    TERMIOS
  58. #    include <termios.h>
  59.    typedef struct termios    Termstate;
  60. #    define getstate(p)    ((void) tcgetattr(0, (p)))
  61. #    define setstate(p)    ((void) tcsetattr(0, TCSANOW, (p)))
  62. #    define w_setstate(p)    ((void) tcsetattr(0, TCSADRAIN, (p)))
  63. #   else    /* no TERMIOS */
  64. #    include <termio.h>
  65.    typedef struct termio    Termstate;
  66. #    define getstate(p)    ((void) ioctl(0,TCGETA,(char *)(p)))
  67. #    define setstate(p)    ((void) ioctl(0,TCSETA,(char *)(p)))
  68. #    define w_setstate(p)    ((void) ioctl(0,TCSETAW,(char *)(p)))
  69. #   endif    /* no TERMIOS */
  70.  
  71.     /*
  72.      * Table of line speeds ... exactly 16 long, and the CBAUD mask
  73.      * is 017 (i.e. 15) so we will never access outside the array.
  74.      */
  75.     short    speeds[] = {
  76.     /* B0 */    0,
  77.     /* B50 */    50,
  78.     /* B75 */    75,
  79.     /* B110 */    110,
  80.     /* B134 */    134,
  81.     /* B150 */    150,
  82.     /* B200 */    200,
  83.     /* B300 */    300,
  84.     /* B600 */    600,
  85.     /* B1200 */    1200,
  86.     /* B1800 */    1800,
  87.     /* B2400 */    2400,
  88.     /* B4800 */    4800,
  89.     /* B9600 */    9600,
  90.     /* EXTA */    19200,        /* not defined at V.2 */
  91.     /* EXTB */    38400,        /* not defined at V.2 */
  92.     };
  93.  
  94. #else    /* not TERMIO */
  95.  
  96. #   include <sgtty.h>
  97.     typedef struct sgttyb    Termstate;
  98.  
  99.     static    struct    tchars    ckd_tchars, raw_tchars;
  100.     static    struct    ltchars    ckd_ltchars, raw_ltchars;
  101.  
  102. #   ifdef FD_SET
  103. #    define    fd_set_type    fd_set
  104. #   else        /* FD_SET not defined */
  105.     /*
  106.      * BSD 4.2 doesn't have these macros.
  107.      */
  108.     typedef int fd_set_type;
  109. #    define    FD_ZERO(p)    (*(p) = 0)
  110. #    define    FD_SET(f,p)    (*(p) |= (1 << (f)))
  111. #   endif        /* FD_SET not defined */
  112. #endif    /* not TERMIO */
  113.  
  114. static    Termstate    cooked_state, raw_state;
  115.  
  116. #undef    CTRL
  117.  
  118. #ifdef    SETVBUF_AVAIL
  119.     /*
  120.      * Output buffer to save function calls.
  121.      */
  122.     static    char    outbuffer[128];
  123. #endif
  124.  
  125. #ifdef MEMTEST
  126. #   include <sys/resource.h>
  127. #endif        /* MEMTEST */
  128.  
  129. /*
  130.  * Expected for termcap's benefit.
  131.  */
  132. short        ospeed;            /* tty's baud rate */
  133.  
  134. /*
  135.  * We sometimes use a lot of system calls while trying to read from
  136.  * the keyboard; these are needed to make our automatic buffer
  137.  * preservation and input timeouts work properly. Nevertheless, it
  138.  * is possible that, with this much overhead, a reasonably fast typist
  139.  * could get ahead of us, so we do a small amount of input buffering
  140.  * to reduce the number of system calls.
  141.  *
  142.  * This variable gives the number of characters in the buffer.
  143.  */
  144. static int    kb_nchars;
  145.  
  146. /*
  147.  * Get a single byte from the keyboard.
  148.  *
  149.  * If the keyboard input buffer is empty, & read() fails or times out,
  150.  * return EOF.
  151.  */
  152. static int
  153. kbgetc()
  154. {
  155.     static unsigned char    kbuf[48];
  156.     static unsigned char    *kbp;
  157.  
  158.     if (kb_nchars <= 0) {
  159.     int nread;
  160.  
  161.     if ((nread = read(0, (char *) kbuf, sizeof kbuf)) <= 0) {
  162.         return EOF;
  163.     } else {
  164.         kb_nchars = nread;
  165.         kbp = kbuf;
  166.     }
  167.     }
  168.     --kb_nchars;
  169.     return(*kbp++);
  170. }
  171.  
  172. #ifdef TERMIO
  173.  
  174. /*
  175.  * Set a timeout on standard input. 0 means no timeout.
  176.  *
  177.  * This depends on raw_state having been properly initialized, which
  178.  * should have been done by sys_startv().
  179.  */
  180. static void
  181. input_timeout(msec)
  182. long    msec;
  183. {
  184.     int        vtime;
  185.     static int    lastvtime;
  186.  
  187.     /*
  188.      * If the device state hasn't been changed since last time, we
  189.      * don't need to do anything.
  190.      */
  191.     if ((vtime = (msec + 99) / 100) != lastvtime) {
  192.     lastvtime = vtime;
  193.     raw_state.c_cc[VMIN] = (vtime == 0 ? 1 : 0);
  194.     raw_state.c_cc[VTIME] = vtime;
  195.     setstate(&raw_state);
  196.     }
  197. }
  198.  
  199. #endif    /* TERMIO */
  200.  
  201. /*
  202.  * Get a character from the keyboard.
  203.  *
  204.  * Make sure screen is updated first.
  205.  */
  206. int
  207. inch(timeout)
  208. long    timeout;
  209. {
  210.     int        c;
  211.  
  212.     /*
  213.      * If we had characters left over from last time, return one.
  214.      *
  215.      * Note that if this happens, we don't call flush_output().
  216.      */
  217.     if (kb_nchars > 0) {
  218.     return(kbgetc());
  219.     }
  220.  
  221.     /*
  222.      * Need to get a character. First, flush output to the screen.
  223.      */
  224.     flush_output();
  225.  
  226. #ifdef TERMIO
  227.     if (timeout != 0) {
  228.     input_timeout(timeout);
  229.     c = kbgetc();
  230.     input_timeout(0L);
  231.     return(c);
  232.     }
  233. #else    /* no TERMIO */
  234.     if (timeout != 0) {
  235.     struct timeval    tv;
  236.     fd_set_type    readfds;
  237.  
  238.     tv.tv_sec = (long) (timeout / 1000);
  239.     tv.tv_usec = ((long) timeout * 1000) % (long) 1000000;
  240.  
  241.     FD_ZERO(&readfds);
  242.     FD_SET(0, &readfds);
  243.  
  244.     /*
  245.      * If select does not return 0, some input is available
  246.      * (ignoring the possibility of errors). Otherwise, we
  247.      * timed out, so return EOF.
  248.      */
  249.     if (select(1, &readfds, (fd_set_type *) NULL,
  250.             (fd_set_type *) NULL, &tv) == 0) {
  251.         return(EOF);
  252.     }
  253.     }
  254. #endif    /* no TERMIO */
  255.  
  256.     /*
  257.      * Keep trying until we get at least one character.
  258.      */
  259.     while ((c = kbgetc()) == EOF)
  260.     ;
  261.  
  262.     return(c);
  263. }
  264.  
  265. #if !(defined(__STDC__) || (defined(ultrix) && defined(mips)))
  266. /*
  267.  * If we have ANSI C, we should have strerror() anyway. Also, Ultrix
  268.  * on DECStations provides it.
  269.  */
  270. const char *
  271. strerror(err)
  272. int    err;
  273. {
  274.     extern char    *sys_errlist[];
  275.     extern int    sys_nerr;
  276.  
  277.     return(
  278.     err == 0 ?
  279.         "No error"
  280.         :
  281.         (err > 0 && err < sys_nerr) ?
  282.         sys_errlist[err]
  283.         :
  284.         "Unknown error"
  285.     );
  286. }
  287.  
  288. #endif /* !(defined(__STDC__) || (defined(ultrix) && defined(mips))) */
  289.  
  290. static int
  291. runvp(args)
  292. char    **args;
  293. {
  294.     int        pid;
  295.     Wait_t    status;
  296.  
  297.     pid = fork();
  298.     switch (pid) {
  299.     case -1:        /* fork() failed */
  300.     return(-1);
  301.  
  302.     case 0:            /* this is the child */
  303.     (void) signal(SIGINT, SIG_DFL);
  304.     (void) signal(SIGQUIT, SIG_DFL);
  305.     (void) execvp(args[0], args);
  306.  
  307.     /*
  308.      * Couldn't do it ... use standard output functions here
  309.      * because none of xvi's higher-level functions are usable
  310.      * from this module.
  311.      */
  312.     (void) fputs("\007Can't execute ", stdout);
  313.     (void) fputs(args[0], stdout);
  314.     (void) fputs("\n(", stdout);
  315.     (void) fputs(strerror(errno), stdout);
  316.     (void) fputs(")\nHit return to continue", stdout);
  317.     (void) fflush(stdout);
  318.     (void) getc(stdin);
  319.     exit(1);
  320.  
  321.     default:        /* this is the parent */
  322.     while (wait(&status) != pid)
  323.         ;
  324.     return(0);
  325.     }
  326. }
  327.  
  328. int
  329. call_shell(sh)
  330. char    *sh;
  331. {
  332.     static char    *args[] = { NULL, NULL };
  333.  
  334.     args[0] = sh;
  335.     return(runvp(args));
  336. }
  337.  
  338. int
  339. call_system(command)
  340. char    *command;
  341. {
  342.     static char    *args[] = { NULL, "-c", NULL, NULL };
  343.  
  344.     if (Ps(P_shell) == NULL) {
  345.     (void) puts("\007Can't execute command without SHELL parameter");
  346.     return(-1);
  347.     }
  348.     args[0] = Ps(P_shell);
  349.     args[2] = command;
  350.     return(runvp(args));
  351. }
  352.  
  353. #ifdef ITIMER_REAL
  354.     static int
  355.     nothing()
  356.     {
  357.     return(0);
  358.     }
  359. #endif
  360.  
  361. /*
  362.  * Delay for a short time - preferably less than 1 second.
  363.  * This is for use by showmatch, which wants to hold the
  364.  * cursor over the matching bracket for just long enough
  365.  * that it will be seen.
  366.  */
  367. void
  368. delay()
  369. {
  370. #ifdef ITIMER_REAL
  371.     struct itimerval    timer;
  372.     
  373.     (void) signal(SIGALRM, nothing);
  374.  
  375.     /*
  376.      * We want to pause for 200 msec (1/5th of a second) here,
  377.      * as this seems like a reasonable figure. Note that we can
  378.      * assume that the implementation will have defined tv_usec
  379.      * of a type large enough to hold up to 999999, since that's
  380.      * the largest number of microseconds we can possibly need.
  381.      */
  382.     timer.it_interval.tv_sec = 0;
  383.     timer.it_interval.tv_usec = 0;
  384.     timer.it_value.tv_sec = 0;
  385.     timer.it_value.tv_usec = 200000;
  386.     if (setitimer(ITIMER_REAL, &timer, (struct itimerval *) NULL) == -1)
  387.     return;
  388.     (void) pause();
  389.  
  390.     timer.it_interval.tv_sec = 0;
  391.     timer.it_interval.tv_usec = 0;
  392.     (void) setitimer(ITIMER_REAL, &timer, (struct itimerval *) NULL);
  393. #else /* not ITIMER_REAL */
  394.     sleep(1);
  395. #endif /* not ITIMER_REAL */
  396. }
  397.  
  398. /*
  399.  * Initialise the terminal so that we can do single-character
  400.  * input and output, with no strange mapping, and no echo of
  401.  * input characters.
  402.  *
  403.  * This must be done before any screen-based i/o is performed.
  404.  */
  405. void
  406. sys_init()
  407. {
  408. #ifdef    TIOCGWINSZ
  409.     struct winsize    winsz;    /* for getting window wize */
  410. #endif
  411.  
  412.     /*
  413.      * What the device driver thinks the window's dimensions are.
  414.      */
  415.     unsigned int    rows = 0;
  416.     unsigned int    columns = 0;
  417.  
  418. #ifdef MEMTEST
  419.     {
  420.     static struct rlimit dlimit = { 400 * 1024, 400 * 1024 };
  421.  
  422.     (void) setrlimit(RLIMIT_DATA, &dlimit);
  423.     }
  424. #endif        /* MEMTEST */
  425.  
  426.     /*
  427.      * Set up tty flags in raw and cooked structures.
  428.      * Do this before any termcap-initialisation stuff
  429.      * so that start sequences can be sent.
  430.      */
  431. #ifdef    TERMIO
  432.  
  433.     getstate(&cooked_state);
  434.     raw_state = cooked_state;
  435. #   ifdef   POSIX
  436.     raw_state.c_oflag &= ~(OPOST
  437. #    ifdef    ONLCR
  438.                 | ONLCR
  439. #    endif
  440. #    ifdef    OXTABS
  441.                 | OXTABS
  442. #    endif
  443.                 );
  444.     raw_state.c_iflag &= ~(ICRNL | IGNCR | INLCR);
  445.     raw_state.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL
  446. #    ifdef    ECHOCTL
  447.                 | ECHOCTL
  448. #    endif
  449. #    ifdef    ECHOPRT
  450.                 | ECHOPRT
  451. #    endif
  452. #    ifdef    ECHOKE
  453.                 | ECHOKE
  454. #    endif
  455.                 );
  456. #   else    /* not POSIX */
  457.     /*
  458.      * Assume this is a Sun, but it might not be ...
  459.      */
  460.     raw_state.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONLRET | XTABS);
  461.     raw_state.c_iflag &= ~(ICRNL | IGNCR | INLCR);
  462.     raw_state.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHOK |
  463.            ECHONL | ECHOCTL | ECHOPRT | ECHOKE);
  464. #   endif
  465.     raw_state.c_cc[VMIN] = 1;
  466.     raw_state.c_cc[VTIME] = 0;
  467. #   ifdef    TERMIOS
  468. #    ifdef        VDISCARD
  469.         raw_state.c_cc[VDISCARD] = 0;
  470. #    endif
  471.     raw_state.c_cc[VSUSP] = 0;
  472. #   endif    /* TERMIOS */
  473.  
  474. #else    /* not TERMIO */
  475.  
  476.     /*
  477.      * Probably a BSD system; we must
  478.      *    turn off echo,
  479.      *    set cbreak mode (not raw because we lose
  480.      *        typeahead when switching modes),
  481.      *    turn off tab expansion on output (in case termcap
  482.      *        puts out any tabs in cursor motions etc),
  483.      *    turn off CRMOD so we get \r and \n as they are typed,
  484.      * and
  485.      *    turn off nasty interrupt characters like ^Y so that
  486.      *        we get the control characters we want.
  487.      *
  488.      * All this has to be put back as it was when we go into
  489.      * system mode; a pain, but we have to get it right.
  490.      */
  491.     (void) ioctl(0, TIOCGETP, (char *) &cooked_state);
  492.     raw_state = cooked_state;
  493.     raw_state.sg_flags |= CBREAK;
  494.     raw_state.sg_flags &= ~ (ECHO | XTABS | CRMOD);
  495.  
  496.     (void) ioctl(0, TIOCGETC, (char *) &ckd_tchars);
  497.     raw_tchars = ckd_tchars;
  498.     raw_tchars.t_quitc = -1;
  499.  
  500.     (void) ioctl(0, TIOCGLTC, (char *) &ckd_ltchars);
  501.     raw_ltchars = ckd_ltchars;
  502.     raw_ltchars.t_flushc = -1;
  503.     raw_ltchars.t_lnextc = -1;
  504.     raw_ltchars.t_suspc = -1;
  505.     raw_ltchars.t_dsuspc = -1;
  506.  
  507. #endif    /* not TERMIO */
  508.  
  509. #ifdef    SETVBUF_AVAIL
  510.     /*
  511.      * Set output buffering to avoid calling _flsbuf()
  512.      * for every character sent to the screen.
  513.      */
  514.     setvbuf(stdout, outbuffer, _IOFBF, sizeof(outbuffer));
  515. #endif
  516.  
  517.     /*
  518.      * This is for termcap's benefit.
  519.      */
  520. #ifdef    TERMIO
  521. #   ifdef    POSIX
  522.     ospeed = cooked_state.c_ospeed;
  523. #   else    /* not POSIX */
  524.     ospeed = speeds[cooked_state.c_cflag & CBAUD];
  525. #   endif
  526. #else
  527.     ospeed = cooked_state.sg_ospeed;
  528. #endif
  529.  
  530. #ifdef    TIOCGWINSZ
  531.     /*
  532.      * Find out how big the kernel thinks the window is.
  533.      * These values (if they are non-zero) will override
  534.      * any settings obtained by the terminal interface code.
  535.      */
  536.     (void) ioctl(0, TIOCGWINSZ, (char *) &winsz);
  537.     rows = winsz.ws_row;
  538.     columns = winsz.ws_col;
  539. #else
  540.     rows = columns = 0;
  541. #endif
  542.  
  543.     /*
  544.      * Now set up the terminal interface.
  545.      */
  546.     tty_open(&rows, &columns);
  547.  
  548.     /*
  549.      * Go into raw/cbreak mode, and do any initialisation stuff.
  550.      */
  551.     sys_startv();
  552. }
  553.  
  554. static enum { m_SYS = 0, m_VI = 1 } curmode;
  555.  
  556. /*
  557.  * Set terminal into the mode it was in when we started.
  558.  *
  559.  * sys_endv() can be called when we're already in system mode, so we
  560.  * have to check.
  561.  */
  562. void
  563. sys_endv()
  564. {
  565.     if (curmode == m_SYS)
  566.     return;
  567.     tty_goto((int) Rows - 1, 0);
  568.     set_colour(Pn(P_systemcolour));
  569.     erase_line();
  570.     tty_endv();
  571.  
  572.     (void) fflush(stdout);
  573.  
  574.     /*
  575.      * Restore terminal modes.
  576.      */
  577. #ifdef    TERMIO
  578.     w_setstate(&cooked_state);
  579. #else
  580.     (void) ioctl(0, TIOCSETN, (char *) &cooked_state);
  581.     (void) ioctl(0, TIOCSETC, (char *) &ckd_tchars);
  582.     (void) ioctl(0, TIOCSLTC, (char *) &ckd_ltchars);
  583. #endif
  584.     curmode = m_SYS;
  585. }
  586.  
  587. /*
  588.  * Set terminal to raw/cbreak mode.
  589.  */
  590. void
  591. sys_startv()
  592. {
  593.     if (curmode == m_VI)
  594.     return;
  595. #ifdef    TERMIO
  596.     w_setstate(&raw_state);
  597. #else
  598.     (void) ioctl(0, TIOCSETN, (char *) &raw_state);
  599.     (void) ioctl(0, TIOCSETC, (char *) &raw_tchars);
  600.     (void) ioctl(0, TIOCSLTC, (char *) &raw_ltchars);
  601. #endif
  602.  
  603.     tty_startv();
  604.  
  605.     set_colour(Pn(P_colour));
  606.  
  607.     curmode = m_VI;
  608. }
  609.  
  610. /*
  611.  * "Safe" form of exit - doesn't leave the tty in yillruction mode.
  612.  */
  613. void
  614. sys_exit(code)
  615. int    code;
  616. {
  617.     sys_endv();
  618.     tty_close();
  619.     (void) fclose(stdin);
  620.     (void) fclose(stdout);
  621.     (void) fclose(stderr);
  622.  
  623.     /*
  624.      * This is desperation really.
  625.      * On some BSD systems, calling exit() here produces a core dump.
  626.      */
  627.     _exit(code);
  628. }
  629.  
  630. /*
  631.  * This is a routine that can be passed to tputs() (in the termcap
  632.  * library): it does the same thing as putchar().
  633.  */
  634. void
  635. foutch(c)
  636. int    c;
  637. {
  638.     putchar(c);
  639. }
  640.  
  641. /*
  642.  * Construct unique name for temporary file, to be used as a backup
  643.  * file for the named file.
  644.  */
  645. char *
  646. tempfname(srcname)
  647. char    *srcname;
  648. {
  649.     char    tailname[MAXNAMLEN + 1];
  650.     char    *srctail;
  651.     char    *endp;
  652.     char    *retp;
  653.     unsigned    headlen;
  654.     unsigned    rootlen;
  655.     unsigned    indexnum = 0;
  656.  
  657.     if ((srctail = strrchr(srcname, '/')) == NULL) {
  658.     srctail = srcname;
  659.     } else {
  660.     srctail++;
  661.     }
  662.     headlen = srctail - srcname;
  663.  
  664.     /*
  665.      * Make copy of filename's tail & change it from "wombat" to
  666.      * "#wombat.tmp".
  667.      */
  668.     tailname [0] = '#';
  669.     (void) strncpy(& tailname [1], srctail, sizeof tailname - 1);
  670.     tailname [sizeof tailname - 1] = '\0';
  671.     endp = tailname + strlen(tailname);
  672.  
  673.     /*
  674.      * Don't let name get too long.
  675.      */
  676.     if (endp > & tailname [sizeof tailname - 5]) {
  677.     endp = & tailname [sizeof tailname - 5];
  678.     }
  679.     rootlen = endp - tailname;
  680.  
  681.     /*
  682.      * Now allocate space for new pathname.
  683.      */
  684.     retp = alloc(headlen + rootlen + 5);
  685.     if (retp == NULL) {
  686.     return(NULL);
  687.     }
  688.  
  689.     /*
  690.      * Copy name to new storage, leaving space for ".tmp"
  691.      * (or ".xxx") suffix ...
  692.      */
  693.     if (headlen > 0) {
  694.     (void) memcpy(retp, srcname, (int) headlen);
  695.     }
  696.     (void) memcpy(&retp[headlen], tailname, (int) rootlen);
  697.  
  698.     /*
  699.      * ... & make endp point to the space we're leaving for the suffix.
  700.      */
  701.     endp = &retp[headlen + rootlen];
  702.     strcpy(endp++, ".tmp");
  703.     while (access(retp, 0) == 0) {
  704.     /*
  705.      * Keep trying this until we get a unique file name.
  706.      */
  707.     Flexbuf suffix;
  708.  
  709.     flexnew(&suffix);
  710.     (void) lformat(&suffix, "%03u", ++indexnum);
  711.     (void) strncpy(endp, flexgetstr(&suffix), 3);
  712.     flexdelete(&suffix);
  713.     }
  714.     return retp;
  715. }
  716.  
  717. /*
  718.  * Fork off children thus:
  719.  *
  720.  * [CHILD 1] --- pipe1 ---> [CHILD 2] --- pipe2 ---> [PARENT]
  721.  *    |                        |                        |
  722.  *    V                        V                        V
  723.  * writefunc                exec(cmd)                readfunc
  724.  *
  725.  * connecting the pipes to stdin/stdout as appropriate.
  726.  *
  727.  * We assume that on entry to this function, file descriptors 0, 1 and 2
  728.  * are already open, so we do not have to deal with the possibility of
  729.  * them being allocated as pipe descriptors.
  730.  */
  731. bool_t
  732. sys_pipe(cmd, writefunc, readfunc)
  733. char    *cmd;
  734. int    (*writefunc) P((FILE *));
  735. long    (*readfunc) P((FILE *));
  736. {
  737.     int        pd1[2], pd2[2];        /* descriptors for pipes 1 and 2 */
  738.     int        pid1, pid2;        /* process ids for children 1 and 2 */
  739.     int        died;
  740.     int        retval;
  741.     FILE    *fp;
  742.     Wait_t    status;
  743.     static char    *args[] = { NULL, "-c", NULL, NULL };
  744.  
  745.     if (Ps(P_shell) == NULL) {
  746.     return(FALSE);
  747.     }
  748.     args[0] = Ps(P_shell);
  749.     args[2] = cmd;
  750.  
  751.     /*
  752.      * Initialise these values so we can work out what has happened
  753.      * so far if we have to goto fail for any reason.
  754.      */
  755.     pd1[0] = pd1[1] = pd2[0] = pd2[1] = -1;
  756.     pid1 = pid2 = -1;
  757.  
  758.     if (pipe(pd1) == -1) {
  759.     goto fail;
  760.     }
  761.  
  762.     switch (pid1 = fork()) {
  763.     case -1:                    /* error */
  764.     goto fail;
  765.  
  766.     case 0:                    /* child 1 */
  767.     /*
  768.      * Fdopen pipe1 to get a stream.
  769.      */
  770.     (void) close(pd1[0]);
  771.     fp = fdopen(pd1[1], "w");
  772.     if (fp == NULL) {
  773.         exit(1);
  774.     }
  775.  
  776.     /*
  777.      * Call writefunc.
  778.      */
  779.     (void) (*writefunc)(fp);
  780.     (void) fclose(fp);
  781.  
  782.     /*
  783.      * Our work is done.
  784.      */
  785.     exit(0);
  786.  
  787.     default:                    /* parent */
  788.     (void) close(pd1[1]);
  789.     break;
  790.     }
  791.  
  792.     if (pipe(pd2) == -1) {
  793.     goto fail;
  794.     }
  795.     switch (pid2 = fork()) {
  796.     case -1:                    /* error */
  797.     goto fail;
  798.  
  799.     case 0:                        /* child 2 */
  800.  
  801.     /*
  802.      * Rearrange file descriptors onto stdin/stdout/stderr.
  803.      */
  804.     (void) close(pd1[1]);
  805.     (void) close(pd2[0]);
  806.  
  807.     (void) close(0);
  808.     (void) dup(pd1[0]);        /* dup2(pd1[0], 0) */
  809.     (void) close(pd1[0]);
  810.  
  811.     (void) close(1);
  812.     (void) dup(pd2[1]);        /* dup2(pd2[1], 1) */
  813.     (void) close(pd2[1]);
  814.  
  815.     (void) close(2);
  816.     (void) dup(1);            /* dup2(1, 2) */
  817.     
  818.     /*
  819.      * Exec the command.
  820.      */
  821.     (void) execvp(args[0], args);
  822.     exit(1);
  823.  
  824.     default:                    /* parent */
  825.     (void) close(pd1[0]);
  826.     (void) close(pd2[1]);
  827.     pd1[0] = pd2[1] = -1;
  828.     fp = fdopen(pd2[0], "r");
  829.     if (fp == NULL) {
  830.         goto fail;
  831.     }
  832.     (void) (*readfunc)(fp);
  833.     (void) fclose(fp);
  834.     break;
  835.     }
  836.  
  837.     /*
  838.      * Finally, clean up the children.
  839.      */
  840.     retval = TRUE;
  841.     goto cleanup;
  842.  
  843. fail:
  844.     /*
  845.      * Come here if anything fails (if we are the original process).
  846.      * Close any open pipes and goto cleanup to reap the child processes.
  847.      */
  848.     if (pd1[0] >= 0) {
  849.     (void) close(pd1[0]);
  850.     (void) close(pd1[1]);
  851.     }
  852.     if (pd2[0] >= 0) {
  853.     (void) close(pd2[0]);
  854.     (void) close(pd2[1]);
  855.     }
  856.     retval = FALSE;
  857.  
  858. cleanup:
  859.     /*
  860.      * Come here whether or not we failed, to clean up the children ...
  861.      */
  862. #ifdef    WIFEXITED
  863. #   define    FAILED(s)    (!WIFEXITED(s) || (s).w_retcode != 0)
  864. #else
  865. #   define    FAILED(s)    ((s) != 0)
  866. #endif
  867. #ifndef WTERMSIG
  868. #   ifdef   WIFSIGNALED
  869. #    define    WTERMSIG(s)    (WIFSIGNALED(s) ? (s).w_termsig : 0)
  870. #   else
  871. #    define    WTERMSIG(s)    ((s) & 0177)
  872. #   endif
  873. #endif
  874.     while ((died = wait(&status)) != -1) {
  875.     if (died == pid1 || died == pid2) {
  876.         /*
  877.          * If child 1 was killed with SIGPIPE -
  878.          * because child 2 exited before reading all
  879.          * of its input - it isn't necessarily an
  880.          * error.
  881.          */
  882.         if (
  883.         FAILED(status)
  884.         &&
  885.         (
  886.             died == pid2
  887.             ||
  888.             WTERMSIG(status) != SIGPIPE
  889.         )
  890.         ) {
  891.         retval = FALSE;
  892.         }
  893.     }
  894.     }
  895.  
  896.     return(retval);
  897. }
  898.  
  899. char *
  900. fexpand(name)
  901. char    *name;
  902. {
  903.     static char    meta[] = "*?[]~${}`";
  904.     char    *cp;
  905.     int        pd[2];
  906.     int        has_meta;
  907.  
  908.     has_meta = FALSE;
  909.     for (cp = meta; *cp != '\0'; cp++) {
  910.     if (strchr(name, *cp) != NULL) {
  911.         has_meta = TRUE;
  912.         break;
  913.     }
  914.     }
  915.     if (!has_meta) {
  916.     return(name);
  917.     }
  918.  
  919.     if (Ps(P_shell) == NULL) {
  920.     return(name);
  921.     }
  922.     if (pipe(pd) == -1) {
  923.     return(name);
  924.     }
  925.     fflush(stdout);
  926.     switch (fork()) {
  927.     case -1:                /* error */
  928.     return(name);
  929.  
  930.     case 0:                /* child */
  931.     {
  932.     static char    *args[] = {
  933.         NULL,            /* path of shell */
  934.         "-c",
  935.         NULL,            /* echo %s */
  936.         NULL,
  937.     };
  938.     Flexbuf        cmd;
  939.     int        errout;
  940.  
  941.     args[0] = Ps(P_shell);
  942.     flexnew(&cmd);
  943.     (void) lformat(&cmd, "echo %s", name);
  944.     args[2] = flexgetstr(&cmd);
  945.     (void) fclose(stdout);
  946.     (void) fclose(stderr);
  947.     (void) close(pd[0]);
  948.     while (dup(pd[1]) < 2)        /* redirect both stdout & stderr */
  949.         ;
  950.     (void) close(pd[1]);
  951.     (void) close(0);
  952.     (void) execvp(args[0], args);
  953.     puts(name);
  954.     exit(0);
  955.     }
  956.     default:                /* parent */
  957.     {
  958.     Wait_t        status;
  959.     FILE        *pfp;
  960.     static Flexbuf    newname;
  961.     register int    c;
  962.  
  963.     flexclear(&newname);
  964.     (void) close(pd[1]);
  965.     pfp = fdopen(pd[0], "r");
  966.     if (pfp != NULL) {
  967.         while ((c = getc(pfp)) != EOF && c != '\n') {
  968.         if (!flexaddch(&newname, c)) {
  969.             break;
  970.         }
  971.         }
  972.     }
  973.     (void) fclose(pfp);
  974.     (void) wait(&status);
  975.     if (flexempty(&newname)) {
  976.         return(name);
  977.     }
  978.     return(flexgetstr(&newname));
  979.     }
  980.     }
  981. }
  982.