home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / ex / ex_filter.c < prev    next >
Text File  |  1997-06-24  |  10KB  |  349 lines

  1. /*-
  2.  * Copyright (c) 1991, 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 1991, 1993, 1994, 1995, 1996
  5.  *    Keith Bostic.  All rights reserved.
  6.  *
  7.  * See the LICENSE file for redistribution information.
  8.  */
  9.  
  10. #include "config.h"
  11.  
  12. #ifndef lint
  13. static const char sccsid[] = "@(#)ex_filter.c    10.34 (Berkeley) 10/23/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18.  
  19. #include <bitstring.h>
  20. #include <errno.h>
  21. #include <fcntl.h>
  22. #include <limits.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <unistd.h>
  27.  
  28. #include "../common/common.h"
  29.  
  30. static int filter_ldisplay __P((SCR *, FILE *));
  31.  
  32. /*
  33.  * ex_filter --
  34.  *    Run a range of lines through a filter utility and optionally
  35.  *    replace the original text with the stdout/stderr output of
  36.  *    the utility.
  37.  *
  38.  * PUBLIC: int ex_filter __P((SCR *, 
  39.  * PUBLIC:    EXCMD *, MARK *, MARK *, MARK *, char *, enum filtertype));
  40.  */
  41. int
  42. ex_filter(sp, cmdp, fm, tm, rp, cmd, ftype)
  43.     SCR *sp;
  44.     EXCMD *cmdp;
  45.     MARK *fm, *tm, *rp;
  46.     char *cmd;
  47.     enum filtertype ftype;
  48. {
  49.     FILE *ifp, *ofp;
  50.     pid_t parent_writer_pid, utility_pid;
  51.     recno_t nread;
  52.     int input[2], output[2], rval;
  53.     char *name;
  54. #if VI_DOSISH
  55.     char *comspec;
  56. #endif
  57.  
  58.     rval = 0;
  59.  
  60.     /* Set return cursor position, which is never less than line 1. */
  61.     *rp = *fm;
  62.     if (rp->lno == 0)
  63.         rp->lno = 1;
  64.  
  65.     /* We're going to need a shell. */
  66. #if VI_DOSISH
  67.     if (!(comspec = getenv("COMSPEC")))
  68.         return (1);
  69. #else
  70.     if (opts_empty(sp, O_SHELL, 0))
  71.         return (1);
  72. #endif
  73.  
  74.     /*
  75.      * There are three different processes running through this code.
  76.      * They are the utility, the parent-writer and the parent-reader.
  77.      * The parent-writer is the process that writes from the file to
  78.      * the utility, the parent reader is the process that reads from
  79.      * the utility.
  80.      *
  81.      * Input and output are named from the utility's point of view.
  82.      * The utility reads from input[0] and the parent(s) write to
  83.      * input[1].  The parent(s) read from output[0] and the utility
  84.      * writes to output[1].
  85.      *
  86.      * !!!
  87.      * Historically, in the FILTER_READ case, the utility reads from
  88.      * the terminal (e.g. :r! cat works).  Otherwise open up utility
  89.      * input pipe.
  90.      */
  91.     ofp = NULL;
  92.     input[0] = input[1] = output[0] = output[1] = -1;
  93.     if (ftype != FILTER_READ && pipe(input) < 0) {
  94.         msgq(sp, M_SYSERR, "pipe");
  95.         goto err;
  96.     }
  97.  
  98.     /* Open up utility output pipe. */
  99.     if (pipe(output) < 0) {
  100.         msgq(sp, M_SYSERR, "pipe");
  101.         goto err;
  102.     }
  103.     if ((ofp = fdopen(output[0], "r")) == NULL) {
  104.         msgq(sp, M_SYSERR, "fdopen");
  105.         goto err;
  106.     }
  107.  
  108.     /* Fork off the utility process. */
  109.     switch (utility_pid = vfork()) {
  110.     case -1:            /* Error. */
  111.         msgq(sp, M_SYSERR, "vfork");
  112. err:        if (input[0] != -1)
  113.             (void)close(input[0]);
  114.         if (input[1] != -1)
  115.             (void)close(input[1]);
  116.         if (ofp != NULL)
  117.             (void)fclose(ofp);
  118.         else if (output[0] != -1)
  119.             (void)close(output[0]);
  120.         if (output[1] != -1)
  121.             (void)close(output[1]);
  122.         return (1);
  123.     case 0:                /* Utility. */
  124.         /*
  125.          * Redirect stdin from the read end of the input pipe, and
  126.          * redirect stdout/stderr to the write end of the output pipe.
  127.          *
  128.          * !!!
  129.          * Historically, ex only directed stdout into the input pipe,
  130.          * letting stderr come out on the terminal as usual.  Vi did
  131.          * not, directing both stdout and stderr into the input pipe.
  132.          * We match that practice in both ex and vi for consistency.
  133.          */
  134.         if (input[0] != -1)
  135.             (void)dup2(input[0], STDIN_FILENO);
  136.         (void)dup2(output[1], STDOUT_FILENO);
  137.         (void)dup2(output[1], STDERR_FILENO);
  138.  
  139.         /* Close the utility's file descriptors. */
  140.         if (input[0] != -1)
  141.             (void)close(input[0]);
  142.         if (input[1] != -1)
  143.             (void)close(input[1]);
  144.         (void)close(output[0]);
  145.         (void)close(output[1]);
  146.  
  147. #if VI_DOSISH
  148.         /*
  149.          * This is expensive:  we have vi, %COMSPEC%, and the
  150.          * user pipeline hanging around.  Fie on whoever decided
  151.          * P_OVERLAY aka exec() wasn't needed (the "whoever" is
  152.          * Microsoft, naturally; P_OVERLAY has been defined but not
  153.          * implemented since at least MS-DOS 3).
  154.          *
  155.          * On the other hand, IBM should have had the intelligence
  156.          * to implement it....
  157.          *
  158.          * With current versions of EMX, we only get here on OS/2; the
  159.          * pipe() calls fail elsewhere.  I presume 95/NT can simulate
  160.          * real operating systems, but EMX doesn't so presume.
  161.          */
  162.         if ((name = strrchr(comspec, '/')) != NULL)
  163.             ++name;
  164.         else if ((name = strrchr(comspec, '\\')) != NULL)
  165.             ++name;
  166.         else
  167.             name = comspec;
  168.         execl(comspec, name, "/C", cmd, NULL);
  169. #else
  170.         if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
  171.             name = O_STR(sp, O_SHELL);
  172.         else
  173.             ++name;
  174.  
  175.         execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL);
  176. #endif
  177.         msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
  178.         _exit (127);
  179.         /* NOTREACHED */
  180.     default:            /* Parent-reader, parent-writer. */
  181.         /* Close the pipe ends neither parent will use. */
  182.         if (input[0] != -1)
  183.             (void)close(input[0]);
  184.         (void)close(output[1]);
  185.         break;
  186.     }
  187.  
  188.     /*
  189.      * FILTER_RBANG, FILTER_READ:
  190.      *
  191.      * Reading is the simple case -- we don't need a parent writer,
  192.      * so the parent reads the output from the read end of the output
  193.      * pipe until it finishes, then waits for the child.  Ex_readfp
  194.      * appends to the MARK, and closes ofp.
  195.      *
  196.      * For FILTER_RBANG, there is nothing to write to the utility.
  197.      * Make sure it doesn't wait forever by closing its standard
  198.      * input.
  199.      *
  200.      * !!!
  201.      * Set the return cursor to the last line read in for FILTER_READ.
  202.      * Historically, this behaves differently from ":r file" command,
  203.      * which leaves the cursor at the first line read in.  Check to
  204.      * make sure that it's not past EOF because we were reading into an
  205.      * empty file.
  206.      */
  207.     if (ftype == FILTER_RBANG || ftype == FILTER_READ) {
  208.         if (ftype == FILTER_RBANG)
  209.             (void)close(input[1]);
  210.  
  211.         if (ex_readfp(sp, "filter", ofp, fm, &nread, 1))
  212.             rval = 1;
  213.         sp->rptlines[L_ADDED] += nread;
  214.         if (ftype == FILTER_READ)
  215.             if (fm->lno == 0)
  216.                 rp->lno = nread;
  217.             else
  218.                 rp->lno += nread;
  219.         goto uwait;
  220.     }
  221.  
  222.     /*
  223.      * FILTER_BANG, FILTER_WRITE
  224.      *
  225.      * Here we need both a reader and a writer.  Temporary files are
  226.      * expensive and we'd like to avoid disk I/O.  Using pipes has the
  227.      * obvious starvation conditions.  It's done as follows:
  228.      *
  229.      *    fork
  230.      *    child
  231.      *        write lines out
  232.      *        exit
  233.      *    parent
  234.      *        FILTER_BANG:
  235.      *            read lines into the file
  236.      *            delete old lines
  237.      *        FILTER_WRITE
  238.      *            read and display lines
  239.      *        wait for child
  240.      *
  241.      * XXX
  242.      * We get away without locking the underlying database because we know
  243.      * that none of the records that we're reading will be modified until
  244.      * after we've read them.  This depends on the fact that the current
  245.      * B+tree implementation doesn't balance pages or similar things when
  246.      * it inserts new records.  When the DB code has locking, we should
  247.      * treat vi as if it were multiple applications sharing a database, and
  248.      * do the required locking.  If necessary a work-around would be to do
  249.      * explicit locking in the line.c:db_get() code, based on the flag set
  250.      * here.
  251.      */
  252.     F_SET(sp->ep, F_MULTILOCK);
  253.     switch (parent_writer_pid = fork()) {
  254.     case -1:            /* Error. */
  255.         msgq(sp, M_SYSERR, "fork");
  256.         (void)close(input[1]);
  257.         (void)close(output[0]);
  258.         rval = 1;
  259.         break;
  260.     case 0:                /* Parent-writer. */
  261.         /*
  262.          * Write the selected lines to the write end of the input
  263.          * pipe.  This instance of ifp is closed by ex_writefp.
  264.          */
  265.         (void)close(output[0]);
  266.         if ((ifp = fdopen(input[1], "w")) == NULL)
  267.             _exit (1);
  268.         _exit(ex_writefp(sp, "filter", ifp, fm, tm, NULL, NULL, 1));
  269.  
  270.         /* NOTREACHED */
  271.     default:            /* Parent-reader. */
  272.         (void)close(input[1]);
  273.         if (ftype == FILTER_WRITE) {
  274.             /*
  275.              * Read the output from the read end of the output
  276.              * pipe and display it.  Filter_ldisplay closes ofp.
  277.              */
  278.             if (filter_ldisplay(sp, ofp))
  279.                 rval = 1;
  280.         } else {
  281.             /*
  282.              * Read the output from the read end of the output
  283.              * pipe.  Ex_readfp appends to the MARK and closes
  284.              * ofp.
  285.              */
  286.             if (ex_readfp(sp, "filter", ofp, tm, &nread, 1))
  287.                 rval = 1;
  288.             sp->rptlines[L_ADDED] += nread;
  289.         }
  290.  
  291.         /* Wait for the parent-writer. */
  292.         if (proc_wait(sp,
  293.             (long)parent_writer_pid, "parent-writer", 0, 1))
  294.             rval = 1;
  295.  
  296.         /* Delete any lines written to the utility. */
  297.         if (rval == 0 && ftype == FILTER_BANG &&
  298.             (cut(sp, NULL, fm, tm, CUT_LINEMODE) ||
  299.             del(sp, fm, tm, 1))) {
  300.             rval = 1;
  301.             break;
  302.         }
  303.  
  304.         /*
  305.          * If the filter had no output, we may have just deleted
  306.          * the cursor.  Don't do any real error correction, we'll
  307.          * try and recover later.
  308.          */
  309.          if (rp->lno > 1 && !db_exist(sp, rp->lno))
  310.             --rp->lno;
  311.         break;
  312.     }
  313.     F_CLR(sp->ep, F_MULTILOCK);
  314.  
  315.     /*
  316.      * !!!
  317.      * Ignore errors on vi file reads, to make reads prettier.  It's
  318.      * completely inconsistent, and historic practice.
  319.      */
  320. uwait:    return (proc_wait(sp, (long)utility_pid, cmd,
  321.         ftype == FILTER_READ && F_ISSET(sp, SC_VI) ? 1 : 0, 0) || rval);
  322. }
  323.  
  324. /*
  325.  * filter_ldisplay --
  326.  *    Display output from a utility.
  327.  *
  328.  * !!!
  329.  * Historically, the characters were passed unmodified to the terminal.
  330.  * We use the ex print routines to make sure they're printable.
  331.  */
  332. static int
  333. filter_ldisplay(sp, fp)
  334.     SCR *sp;
  335.     FILE *fp;
  336. {
  337.     size_t len;
  338.  
  339.     EX_PRIVATE *exp;
  340.  
  341.     for (exp = EXP(sp); !ex_getline(sp, fp, &len) && !INTERRUPTED(sp);)
  342.         if (ex_ldisplay(sp, exp->ibp, len, 0, 0))
  343.             break;
  344.     if (ferror(fp))
  345.         msgq(sp, M_SYSERR, "filter read");
  346.     (void)fclose(fp);
  347.     return (0);
  348. }
  349.