home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 2 / crawlyvol2.bin / apps / text_ed / elv16b2 / st / system.c < prev    next >
C/C++ Source or Header  |  1992-05-13  |  11KB  |  519 lines

  1. /* system.c  -- UNIX version */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains a new version of the system() function and related stuff.
  12.  *
  13.  * Entry points are:
  14.  *    system(cmd)        - run a single shell command
  15.  *    wildcard(names)        - expand wildcard characters in filanames
  16.  *    filter(m,n,cmd,back)    - run text lines through a filter program
  17.  *
  18.  * This is probably the single least portable file in the program.  The code
  19.  * shown here should work correctly if it links at all; it will work on UNIX
  20.  * and any O.S./Compiler combination which adheres to UNIX forking conventions.
  21.  */
  22.  
  23. #include "config.h"
  24. #include "vi.h"
  25. #if MINT
  26. #include "ctype.h"
  27. extern int __mint;
  28. #endif
  29.  
  30. extern char    **environ;
  31.  
  32. #if ANY_UNIX || MINT
  33.  
  34. #if !MINT
  35. /* This is a new version of the system() function.  The only difference
  36.  * between this one and the library one is: this one uses the o_shell option.
  37.  */
  38. int system(cmd)
  39. #ifdef    __STDC__
  40.     const
  41. #endif
  42.     char    *cmd;    /* a command to run */
  43. {
  44.     int    pid;    /* process ID of child */
  45.     int    died;
  46.     int    status;    /* exit status of the command */
  47.  
  48.  
  49.     signal(SIGINT, SIG_IGN);
  50.     pid = fork();
  51.     switch (pid)
  52.     {
  53.       case -1:                        /* error */
  54.         msg("fork() failed");
  55.         status = -1;
  56.         break;
  57.  
  58.       case 0:                        /* child */
  59.         /* for the child, close all files except stdin/out/err */
  60.         for (status = 3; status < 60 && (close(status), errno != EINVAL); status++)
  61.         {
  62.         }
  63.  
  64.         signal(SIGINT, SIG_DFL);
  65.         if (cmd == o_shell)
  66.         {
  67.             execle(o_shell, o_shell, (char *)0, environ);
  68.         }
  69.         else
  70.         {
  71.             execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
  72.         }
  73.         msg("execle(\"%s\", ...) failed", o_shell);
  74.         exit(1); /* if we get here, the exec failed */
  75.  
  76.       default:                        /* parent */
  77.         do
  78.         {
  79.             died = wait(&status);
  80.         } while (died >= 0 && died != pid);
  81.         if (died < 0)
  82.         {
  83.             status = -1;
  84.         }
  85. #if __GNUC__
  86.         signal(SIGINT, (void (*)()) trapint);
  87. #else
  88.         signal(SIGINT, trapint);
  89. #endif
  90.     }
  91.  
  92.     return status;
  93. }
  94. #endif
  95.  
  96. /* This private function opens a pipe from a filter.  It is similar to the
  97.  * system() function above, and to popen(cmd, "r").
  98.  */
  99. int rpipe(cmd, in)
  100.     char    *cmd;    /* the filter command to use */
  101.     int    in;    /* the fd to use for stdin */
  102. {
  103.     int    r0w1[2];/* the pipe fd's */
  104.  
  105. #if MINT
  106. /* maximum commandline length that we try to parse ourselves */
  107. #define MAXCMDLEN 0x400
  108.  
  109.     static char *extensions[] = { "ttp", "prg", "tos", (char *) NULL};
  110.     extern char *findfile (char *, char *, char **);
  111.     extern char *getenv();
  112.  
  113.     static char *argv[MAXCMDLEN/2+2];
  114.     static char cmdcopy[MAXCMDLEN+2];
  115.     static char cmdpath[FILENAME_MAX];
  116.     char **argp, *path, *p;
  117.     size_t clen;
  118.  
  119.     /* if MiNT is not running, we have to use a tempfile */
  120.     if (!__mint)
  121.         return trpipe(cmd, in);
  122. #endif
  123.  
  124.     /* make the pipe */
  125.     if (pipe(r0w1) < 0)
  126.     {
  127.         return -1;    /* pipe failed */
  128.     }
  129.  
  130.     /* The parent process (elvis) ignores signals while the filter runs.
  131.      * The child (the filter program) will reset this, so that it can
  132.      * catch the signal.
  133.      */
  134.     signal(SIGINT, SIG_IGN);
  135.  
  136. #if MINT
  137.     /* MiNT <= 0.8 had a problem with broken pipes...  */
  138.     if (__mint <= 8) {
  139.         signal(SIGTTIN, SIG_IGN);
  140.         signal(SIGTTOU, SIG_IGN);
  141.     }
  142.  
  143.     /* see if cmd is simple enough so that we don't need a shell */
  144.     if ((path = getenv("PATH")) && (clen = strlen(cmd)) &&
  145.         clen < MAXCMDLEN && !strpbrk(cmd, "'\"`><;$~[*?&|(){}") &&
  146.         (p = findfile(strtok(strcpy(cmdcopy, cmd), " \t"), path, extensions))) {
  147.         argp = argv;
  148.         *argp++ = strcpy(cmdpath, p);
  149.         while (*argp++ = strtok((char *) NULL, " \t"))
  150.             ;
  151.     } else
  152.         p = 0;
  153. #endif
  154.  
  155.     switch (fork())
  156.     {
  157.       case -1:                        /* error */
  158.         return -1;
  159.  
  160.       case 0:                        /* child */
  161.         /* close the "read" end of the pipe */
  162.         close(r0w1[0]);
  163.  
  164.         /* redirect stdout to go to the "write" end of the pipe */
  165. #if MINT
  166.         /* GEMDOS redirection bugs department...  */
  167.         dup2(r0w1[1], 1);
  168.         dup2(r0w1[1], 2);
  169.         close(r0w1[1]);
  170.  
  171.         /* redirect stdin */
  172.         if (in != 0)
  173.         {
  174.             dup2(in, 0);
  175.             close(in);
  176.         }
  177. #else
  178.         close(1);
  179.         dup(r0w1[1]);
  180.         close(2);
  181.         dup(r0w1[1]);
  182.         close(r0w1[1]);
  183.  
  184.         /* redirect stdin */
  185.         if (in != 0)
  186.         {
  187.             close(0);
  188.             dup(in);
  189.             close(in);
  190.         }
  191. #endif
  192.  
  193.         /* the filter should accept SIGINT signals */
  194.         signal(SIGINT, SIG_DFL);
  195.  
  196.         /* exec the shell to run the command */
  197. #if MINT
  198.         errno = 0;
  199.         if (!p || (execv(p, argv) == -1 && errno == ENOEXEC))
  200.             execlp(o_shell, o_shell, "-c", cmd, (char *)0);
  201. #else
  202.         execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
  203. #endif
  204.         exit(1); /* if we get here, exec failed */
  205.  
  206.       default:                        /* parent */
  207.         /* close the "write" end of the pipe */    
  208.         close(r0w1[1]);
  209.  
  210.         return r0w1[0];
  211.     }
  212. }
  213.  
  214. #endif /* non-DOS */
  215.  
  216. #if OSK
  217.  
  218. /* This private function opens a pipe from a filter.  It is similar to the
  219.  * system() function above, and to popen(cmd, "r").
  220.  */
  221. int rpipe(cmd, in)
  222.     char    *cmd;    /* the filter command to use */
  223.     int    in;    /* the fd to use for stdin */
  224. {
  225.     return osk_popen(cmd, "r", in, 0);
  226. }    
  227. #endif
  228.  
  229. #if ANY_UNIX || OSK || MINT
  230.  
  231. /* This function closes the pipe opened by rpipe(), and returns 0 for success */
  232. int rpclose(fd)
  233.     int    fd;
  234. {
  235.     int    status;
  236.  
  237. #if MINT
  238.     if (!__mint)
  239.         return trpclose(fd);
  240. #endif
  241.     close(fd);
  242.     wait(&status);
  243. #if __GNUC__
  244.     signal(SIGINT, (void (*)()) trapint);
  245. #else
  246.     signal(SIGINT, trapint);
  247. #endif
  248.     return status;
  249. }
  250.  
  251. #endif /* non-DOS */
  252.  
  253. /* This function expands wildcards in a filename or filenames.  It does this
  254.  * by running the "echo" command on the filenames via the shell; it is assumed
  255.  * that the shell will expand the names for you.  If for any reason it can't
  256.  * run echo, then it returns the names unmodified.
  257.  */
  258.  
  259. #if MSDOS || TOS
  260. #define    PROG    "wildcard "
  261. #define    PROGLEN    9
  262. #include <string.h>
  263. #else
  264. #define    PROG    "echo "
  265. #define    PROGLEN    5
  266. #endif
  267.  
  268. #if !AMIGA
  269. char *wildcard(names)
  270.     char    *names;
  271. {
  272.  
  273. # if VMS
  274. /* 
  275.    We could use expand() [vmswild.c], but what's the point on VMS? 
  276.    Anyway, echo is the wrong thing to do, it takes too long to build
  277.    a subprocess on VMS and any "echo" program would have to be supplied
  278.    by elvis.  More importantly, many VMS utilities expand names 
  279.    themselves (the shell doesn't do any expansion) so the concept is
  280.    non-native.  jdc
  281. */
  282.     return names;
  283. # else
  284.  
  285.     int    i, j, fd;
  286.     REG char *s, *d;
  287.  
  288. #if 1
  289.     /* before we do anything, see if there are wildcards at all  -nox */
  290.     if (!strpbrk(names, "*?[`{$~"))
  291.         return names;
  292. #endif
  293.  
  294.     /* build the echo command */
  295.     if (names != tmpblk.c)
  296.     {
  297.         /* the names aren't in tmpblk.c, so we can do it the easy way */
  298.         strcpy(tmpblk.c, PROG);
  299.         strcat(tmpblk.c, names);
  300.     }
  301.     else
  302.     {
  303.         /* the names are already in tmpblk.c, so shift them to make
  304.          * room for the word "echo "
  305.          */
  306.         for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
  307.         {
  308.             *--d = *--s;
  309.         }
  310.         strncpy(names, PROG, PROGLEN);
  311.     }
  312.  
  313.     /* run the command & read the resulting names */
  314.     fd = rpipe(tmpblk.c, 0);
  315.     if (fd < 0) return names;
  316.     i = 0;
  317.     do
  318.     {
  319.         j = tread(fd, tmpblk.c + i, BLKSIZE - i);
  320.         i += j;
  321.     } while (j > 0);
  322.  
  323.     /* successful? */
  324.     if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
  325.     {
  326. #if MINT
  327.         /* strange shells doing strange things... */
  328.         while (isspace(tmpblk.c[--i]))
  329.             ;
  330.         tmpblk.c[++i] = '\0';
  331. #else
  332.         tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
  333. #endif
  334.         return tmpblk.c;
  335.     }
  336.     else
  337.     {
  338.         return names;
  339.     }
  340. # endif
  341. }
  342. #endif
  343.  
  344. /* This function runs a range of lines through a filter program, and replaces
  345.  * the original text with the filtered version.  As a special case, if "to"
  346.  * is MARK_UNSET, then it runs the filter program with stdin coming from
  347.  * /dev/null, and inserts any output lines.
  348.  */
  349. int filter(from, to, cmd, back)
  350.     MARK    from, to;    /* the range of lines to filter */
  351.     char    *cmd;        /* the filter command */
  352.     int    back;        /* boolean: will we read lines back? */
  353. {
  354.     int    scratch;    /* fd of the scratch file */
  355.     int    fd;        /* fd of the pipe from the filter */
  356.     char    scrout[50];    /* name of the scratch out file */
  357.     MARK    new;        /* place where new text should go */
  358.     long    sent, rcvd;    /* number of lines sent/received */
  359.     int    i, j;
  360.  
  361.     /* write the lines (if specified) to a temp file */
  362.     if (to)
  363.     {
  364.         /* we have lines */
  365. #if MSDOS || TOS || MINT
  366.         strcpy(scrout, o_directory);
  367.         if ((i=strlen(scrout)) && !strchr("\\/:", scrout[i-1]))
  368.             scrout[i++]=SLASH;
  369.         strcpy(scrout+i, SCRATCHOUT+3);
  370. #else
  371.         sprintf(scrout, SCRATCHOUT, o_directory);
  372. #endif
  373.         mktemp(scrout);
  374.         cmd_write(from, to, CMD_BANG, FALSE, scrout);
  375.         sent = markline(to) - markline(from) + 1L;
  376.  
  377.         /* use those lines as stdin */
  378.         scratch = open(scrout, O_RDONLY);
  379.         if (scratch < 0)
  380.         {
  381.             unlink(scrout);
  382.             return -1;
  383.         }
  384.     }
  385.     else
  386.     {
  387.         scratch = 0;
  388.         sent = 0L;
  389.     }
  390.  
  391.     /* start the filter program */
  392. #if VMS
  393.     /* 
  394.        VMS doesn't know a thing about file descriptor 0.  The rpipe
  395.        concept is non-portable.  Hence we need a file name argument.
  396.     */
  397.     fd = rpipe(cmd, scratch, scrout);
  398. #else
  399.     fd = rpipe(cmd, scratch);
  400. #endif
  401.     if (fd < 0)
  402.     {
  403.         if (to)
  404.         {
  405.             close(scratch);
  406.             unlink(scrout);
  407.         }
  408.         return -1;
  409.     }
  410.  
  411.     if (back)
  412.     {
  413.         ChangeText
  414.         {
  415.             /* adjust MARKs for whole lines, and set "new" */
  416.             from &= ~(BLKSIZE - 1);
  417.             if (to)
  418.             {
  419.                 to &= ~(BLKSIZE - 1);
  420.                 to += BLKSIZE;
  421.                 new = to;
  422.             }
  423.             else
  424.             {
  425.                 new = from + BLKSIZE;
  426.             }
  427.  
  428. #if VMS
  429. /* Reading from a VMS mailbox (pipe) is record oriented... */
  430. # define tread vms_pread
  431. #endif
  432.  
  433.             /* repeatedly read in new text and add it */
  434.             rcvd = 0L;
  435.             while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
  436.             {
  437.                 tmpblk.c[i] = '\0';
  438.                 add(new, tmpblk.c);
  439. #if VMS
  440.                 /* What!  An advantage to record oriented reads? */
  441.                 new += (i - 1);
  442.                 new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
  443.                 rcvd++;
  444. #else
  445.                 for (i = 0; tmpblk.c[i]; i++)
  446.                 {
  447.                     if (tmpblk.c[i] == '\n')
  448.                     {
  449.                         new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
  450.                         rcvd++;
  451.                     }
  452.                     else
  453.                     {
  454.                         new++;
  455.                     }
  456.                 }
  457. #endif
  458.             }
  459. #if 1
  460.             /* doesn't the delete() belong into here?  -nox */
  461.  
  462.             /* delete old text, if any */
  463.             if (to)
  464.             {
  465.                 cut(from, to);
  466.                 delete(from, to);
  467.             }
  468. #else
  469.         }
  470.  
  471.         /* delete old text, if any */
  472.         if (to)
  473.         {
  474.             cut(from, to);
  475.             delete(from, to);
  476. #endif
  477.         }
  478.     }
  479.     else
  480.     {
  481.         /* read the command's output, and copy it to the screen */
  482.         while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
  483.         {
  484.             for (j = 0; j < i; j++)
  485.             {
  486.                 addch(tmpblk.c[j]);
  487.             }
  488.         }
  489.         rcvd = 0;
  490.     }
  491.  
  492.     /* Reporting... */
  493.     if (sent >= *o_report || rcvd >= *o_report)
  494.     {
  495.         if (sent > 0L && rcvd > 0L)
  496.         {
  497.             msg("%ld lines out, %ld lines back", sent, rcvd);
  498.         }
  499.         else if (sent > 0)
  500.         {
  501.             msg("%ld lines written to filter", sent);
  502.         }
  503.         else
  504.         {
  505.             msg("%ld lines read from filter", rcvd);
  506.         }
  507.     }
  508.     rptlines = 0L;
  509.  
  510.     /* cleanup */
  511.     rpclose(fd);
  512.     if (to)
  513.     {
  514.         close(scratch);
  515.         unlink(scrout);
  516.     }
  517.     return 0;
  518. }
  519.