home *** CD-ROM | disk | FTP | other *** search
/ back2roots/padua / padua.7z / padua / uucp / ListSERV2_3.lha / ListSERV / source / popen2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-10  |  11.2 KB  |  375 lines

  1. /*
  2. ** popen.c
  3. ** Written by Rick Schaeffer (ricks@isc-br.isc-br.com)
  4. **
  5. ** apoted for sendmail by wusel@hactar.hanse.de, Thu, 20 Feb 1992 07:26:26 +0100
  6. **
  7. ** FIXME: change the errno's ...
  8.  
  9. NAME
  10.      popen, pclose - initiate I/O to/from a process
  11.  
  12. SYNOPSIS
  13.      #include <stdio.h>
  14.  
  15.      FILE *popen(command, type)
  16.      char *command, *type;
  17.  
  18.      pclose(stream)
  19.      FILE *stream;
  20.  
  21. DESCRIPTION
  22.      The arguments to popen are pointers to null-terminated
  23.      strings containing respectively a command line and an
  24.      I/O mode, either "r" for reading or "w" for writing.  It
  25.      creates a pipe between the calling process and the command
  26.      to be executed.  The value returned is a stream pointer that
  27.      can be used (as appropriate) to write to the standard input
  28.      of the command or read from its standard output.
  29.  
  30.      A stream opened by popen **MUST** be closed by pclose, which
  31.      waits for the associated process to terminate and returns
  32.      the exit status of the command.
  33.  
  34.      Because stdio files are shared, a type "r" command may be
  35.      used as an input filter, and a type "w" as an output filter.
  36.  
  37. DIAGNOSTICS
  38.      Popen returns a null pointer if files or processes cannot be
  39.      created.
  40.  
  41.      Pclose returns -1 if stream is not associated with a
  42.      `popened' command.
  43.  
  44. */
  45.  
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <string.h>
  49. #include <errno.h>
  50. #include <exec/types.h>
  51. #include <exec/memory.h>
  52. #include <exec/libraries.h>
  53. #include <dos/dos.h>
  54. #include <dos/dosextens.h>
  55. #include <dos/record.h>
  56. #include <dos/dostags.h>
  57. #include <proto/exec.h>
  58. #include <proto/dos.h>
  59.  
  60. static const char RCSId[] = "$Id: popen2.c,v 1.1 1993/12/25 23:38:29 simons Rel simons $";
  61.  
  62. struct POmsg {
  63.         struct Message POm;
  64.         char *cmd;
  65.         BPTR pipe;
  66.         int rc;
  67.         char rw;
  68. };
  69.  
  70. struct pstruct {
  71.         FILE *fptr;
  72.         struct POmsg childmsg;
  73. };
  74.  
  75. struct pstruct poarray[6];
  76.  
  77. FILE *popen(char *cmd, char *mode)
  78. {
  79.         char *pname, redir[60];
  80.         short i;
  81.         int pmode;
  82.         int childprocess();
  83.         struct TagItem nptags[] =
  84.         {
  85.                 {NP_Entry, (Tag) childprocess},
  86.                 {NP_Input, 0},
  87.                 {NP_Output, 0},
  88.                 {NP_CloseInput, 0},
  89.                 {NP_CloseOutput, 0},
  90.                 {NP_StackSize, 50000},
  91.                 {NP_Cli, 1},
  92. #ifdef DEBUG
  93.                 {NP_CommandName, (Tag) "childtask()"},
  94. #endif
  95.                 {TAG_DONE, 0}
  96.         };
  97.         struct pstruct *poptr;
  98.         BPTR pfd;
  99.         struct Process *child;
  100.         struct CommandLineInterface *cli;
  101.         struct Process *thistask;
  102.  
  103.         /* First, get pointers to our process and cli structs */
  104.         thistask = (struct Process *) FindTask(NULL);
  105.         cli = Cli();
  106.         poptr = NULL;
  107.  
  108.         /*
  109.          * now find an open pipe (we currently only allow 6
  110.          * simultaneously * open pipes)
  111.          */
  112.         for (i = 0; i < 6; i++) {
  113.                 if (poarray[i].fptr == NULL) {
  114.                         poptr = &poarray[i];
  115.                         break;
  116.                 }
  117.         }
  118.         if (poptr == NULL) {
  119.                 fprintf(stderr, "popen: Unable to find an open pipe.\n");
  120.                 return (errno = EPIPE, NULL);
  121.         }
  122.         if (!strcmp(mode, "r"))
  123.                 pmode = MODE_NEWFILE;
  124.         else if (!strcmp(mode, "w"))
  125.                 pmode = MODE_OLDFILE;
  126.         else {
  127.                 fprintf(stderr, "popen: Mode must be 'r' or 'w'.\n");
  128.                 return (errno = EINVAL, NULL);
  129.         }
  130.  
  131.         /* Try to make a guaranteed unique file name for the pipe */
  132.         sprintf(redir, "PIPE:popen.%08lx.%d", thistask, i);
  133.         pname = redir;          /* set up a PIPE: file name */
  134.  
  135. #ifdef DEBUG
  136.         printf("popen: Geh' in den Keller und rei▀ Dich zusammen, %s!\n", pname);
  137.         nptags[7].ti_Data = (Tag) pname;
  138. #endif
  139.  
  140.         /* Now get the child's stack and priority set up */
  141.         if (thistask->pr_CLI)
  142.                 nptags[5].ti_Data = cli->cli_DefaultStack << 2;
  143.         else
  144.                 nptags[5].ti_Data = thistask->pr_StackSize;
  145.  
  146.         /* Open the side of the pipe for the child */
  147.         pfd = Open(pname, pmode);
  148.         if (pfd == 0) {
  149.                 fprintf(stderr, "popen: Unable to open pipe file (#%d, %s: %ld).\n", i, pname, IoErr());
  150.                 return (errno = ENOENT, NULL);
  151.         }
  152.  
  153.         /* set up the tags for the new process */
  154.         if (pmode == MODE_NEWFILE) {    /* "r" */
  155.                 nptags[1].ti_Data = (Tag) Input();
  156.                 nptags[2].ti_Data = (Tag) pfd;
  157.                 nptags[3].ti_Data = FALSE;
  158.                 nptags[4].ti_Data = FALSE /* TRUE */ ;
  159. /*      poptr->childmsg.pipe=NULL;*/
  160.         }
  161.         else {                  /* "w" */
  162.                 nptags[1].ti_Data = (Tag) pfd;
  163.                 nptags[2].ti_Data = (Tag) Output();
  164.                 nptags[3].ti_Data = FALSE /* TRUE */ ;
  165.                 nptags[4].ti_Data = FALSE;
  166. /*      poptr->childmsg.pipe=pfd;*/
  167.         }
  168.         poptr->childmsg.pipe = pfd;
  169.         poptr->childmsg.rw = *mode;
  170.  
  171.         /*
  172.          * create the command.  since the "System" function runs through
  173.          * the default shell, we need to tell it not to fail so that we
  174.          * ALWAYS get back the exit status.  This wouldn't be necessary
  175.          * if the CLI created by the System function inherited the
  176.          * parent's FAILAT level
  177.          */
  178.         poptr->childmsg.cmd = malloc(strlen(cmd) + 15);
  179.         if (!poptr->childmsg.cmd) {
  180.                 fprintf(stderr, "popen: Out of memory.\n");
  181.                 Close(pfd);
  182.                 return (errno = ENOMEM, NULL);
  183.         }
  184.         strcpy(poptr->childmsg.cmd, "failat 9999\n");
  185.         strcat(poptr->childmsg.cmd, cmd);
  186.  
  187.         /* Create a port that we can get the child's exit status through */
  188.         poptr->childmsg.POm.mn_ReplyPort = CreatePort(NULL, 0);
  189.         poptr->childmsg.POm.mn_Node.ln_Type = NT_MESSAGE;
  190.         poptr->childmsg.POm.mn_Node.ln_Pri = 0;
  191.         poptr->childmsg.POm.mn_Length = sizeof(struct POmsg);
  192.  
  193.         if (!poptr->childmsg.POm.mn_ReplyPort) {
  194.                 fprintf(stderr, "popen: Couldn't create message port.\n");
  195.                 free(poptr->childmsg.cmd);
  196.                 Close(pfd);
  197.                 return (errno = EFAULT, NULL);
  198.         }
  199.  
  200.         /*
  201.          * Now we can start the new process.  NOTE: this is actually
  202.          * going to create a process consisting ONLY of the function
  203.          * "childprocess" which can be seen below.  childprocess() then
  204.          * runs the command passed in the startup message.
  205.          */
  206.  
  207. #ifdef DEBUG
  208.         PutStr("popen: Creating child process\n");
  209.         Flush(Output());
  210. #endif
  211.         child = CreateNewProc(nptags);
  212. #ifdef DEBUG
  213.         printf("popen: created new process @ $%06lx.\n", child);
  214.         fflush(stdout);
  215. #endif
  216.         if (!child) {
  217.                 fprintf(stderr, "popen: CreateNewProc() failed.\n");
  218.                 free(poptr->childmsg.cmd);
  219.                 Close(pfd);
  220.                 return (errno = EFAULT, NULL);
  221.         }
  222.         /* now pass the child the startup message */
  223.         PutMsg(&child->pr_MsgPort, (struct Message *) &poptr->childmsg);
  224.  
  225. #ifdef DEBUG
  226.         PutStr("popen: Sent startupmsg, now opening our side of the pipe\n");
  227.         Flush(Output());
  228. #endif
  229.         /* Now open our side of the pipe */
  230.         poptr->fptr = fopen(pname, mode);
  231. #ifdef DEBUG
  232.         printf("fopen(%s, %s)=$%06lx\n", pname, mode, poptr->fptr);
  233. #endif
  234.  
  235.         if (!poptr->fptr) {
  236. #ifdef DEBUG
  237.                 PutStr("popen: fopen() failed! Let's clean up and return NULL ...\n");
  238.                 Flush(Output());
  239. #endif
  240.                 fprintf(stderr, "popen: Unable to open pipe file %s.\n", pname);
  241.                 DeletePort(poptr->childmsg.POm.mn_ReplyPort);
  242.                 return (errno = ENOENT, NULL);
  243.         }
  244.  
  245. #ifdef DEBUG
  246.         PutStr("popen: Returning FILE * to caller\n");
  247.         Flush(Output());
  248. #endif
  249.         return (poptr->fptr);
  250. }
  251.  
  252. pclose(FILE * fptr)
  253. {
  254.         short i;
  255.  
  256.         /* Figure out which pipe we used for this file */
  257.         for (i = 0; i < 6; i++)
  258.                 if (poarray[i].fptr == fptr)
  259.                         break;
  260.  
  261.         if (i > 5) {
  262.                 fprintf(stderr, "popen: DISASTER - couldn't find file pointer in pclose().\n");
  263.                 return (-1);
  264. /*      exit(1); */
  265.         }
  266.  
  267. #ifdef DEBUG
  268.         PutStr("pclose: Closing pipe\n");
  269.         Flush(Output());
  270. #endif
  271.         /* Close the file */
  272.         fclose(fptr);
  273.  
  274. #ifdef DEBUG
  275.         PutStr("pclose: Waiting for child to terminate\n");
  276.         Flush(Output());
  277. #endif
  278.         /* Now wait for the exit status */
  279.         WaitPort(poarray[i].childmsg.POm.mn_ReplyPort);
  280.  
  281.         /* Clean things up */
  282.         DeletePort(poarray[i].childmsg.POm.mn_ReplyPort);
  283.         free(poarray[i].childmsg.cmd);
  284. #ifdef DEBUG
  285.         PutStr("pclose: Returning rc\n");
  286.         Flush(Output());
  287. #endif
  288.         /* Finally, make this pipe available again ... */
  289.         poarray[i].fptr = NULL;
  290.         return (poarray[i].childmsg.rc);
  291. }
  292.  
  293. int __saveds childprocess(void)
  294. {
  295.         struct TagItem systags[] =
  296.         {
  297.                 {SYS_UserShell, 0},
  298.                 {TAG_DONE, 0}
  299.         };
  300.         struct Process *me;
  301.         struct POmsg *startupmsg;
  302.         int i;
  303.  
  304.         /* Find our process structure */
  305.         me = (struct Process *) FindTask(NULL);
  306.  
  307.         /* Wait for the parent to kick us off */
  308.         WaitPort(&me->pr_MsgPort);
  309.  
  310.         /* Get the command to execute */
  311.         startupmsg = (struct POmsg *) GetMsg(&me->pr_MsgPort);
  312.  
  313.         /* Now run the command.  stdin and stdout are already set up */
  314.         i = SystemTagList(startupmsg->cmd, systags);
  315.  
  316.         /* Store the rc for pclose() */
  317.         startupmsg->rc = i;
  318.  
  319.         /*
  320.          * ╖╖ Always ╖╖ try to dry the pipe; read until EOF, as
  321.          * otherwise the calling process will be blocked - forever =:-(
  322.          * this may cause some delay  (if there are many bytes to junk),
  323.          * but at least,  it works.                     wusel, Sat, 16
  324.          * May 1992 03:11:10 +0200
  325.          */
  326.         if (startupmsg->rw == 'w') {
  327.                 while (FGetC(startupmsg->pipe) != -1L) ;
  328.         }
  329.         Close(startupmsg->pipe);
  330.         startupmsg->pipe = NULL;
  331.  
  332.         /* Pass the exit code back to the parent */
  333.         ReplyMsg((struct Message *) startupmsg);
  334.         return (0);
  335. }
  336.  
  337. int oldchildprocess(void)
  338. {
  339.         struct TagItem systags[] =
  340.         {
  341.                 {SYS_UserShell, 0},
  342.                 {TAG_DONE, 0}
  343.         };
  344.         struct Process *me;
  345.         struct POmsg *startupmsg;
  346.         int i;
  347.  
  348.         /* find our process structure */
  349.         me = (struct Process *) FindTask(NULL);
  350.  
  351.         /* Wait for the parent to kick us off */
  352.         WaitPort(&me->pr_MsgPort);
  353.  
  354.         /* Get the command to execute */
  355.         startupmsg = (struct POmsg *) GetMsg(&me->pr_MsgPort);
  356.  
  357.         /* Now run the command.  stdin and stdout are already set up */
  358.         i = SystemTagList(startupmsg->cmd, systags);
  359.         startupmsg->rc = i;
  360.  
  361.         /* pass the exit code back to the parent */
  362.         ReplyMsg((struct Message *) startupmsg);
  363.         return (0);
  364. }
  365.  
  366. int pexit(void)
  367. {                               /* close all open pipes ... */
  368.         short i;
  369.  
  370.         for (i = 0; i < 6; i++)
  371.                 if (poarray[i].fptr)
  372.                         (void) pclose(poarray[i].fptr);
  373.         return (0);
  374. }
  375.