Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!rutgers!cs.utexas.edu!uunet!isc-br!eagle!ricks From: ricks@eagle.iscs.com (Rick Schaeffer 99) Newsgroups: comp.sys.amiga.tech Subject: Re: Using PIP: Keywords: dupping closing pipes popen Message-ID: <2676@isc-br.ISCS.COM> Date: 17 Dec 89 18:35:11 GMT References: <4689@sugar.hackercorp.com> <1610.AA1610@julie> <1718.AA1718@julie> <4778@sugar.hackercorp.com> Sender: news@isc-br.ISCS.COM Reply-To: ricks@eagle.UUCP (Rick Schaeffer 99) Organization: ISC Systems Corporation, Spokane WA Lines: 216 I've been following the discussion on using pipes with interest. Recently I hacked this small routine which uses the ARP 1.3 AsyncRun() function and AmigaDos 1.3 pipes to provide a Unix-like popen() function. It seems to work fine on anything I've tried (including standard AmigaDos programs like type and list that were written in BCPL). It probably has some bugs but, since it's short and *does* seem to work I thought I'd post it. Comments, improvements, and whatnot welcome. It was compiled with Lattice 5.04 but shouldn't be too tough to make with Aztec. /* ** popen.c ** Written by Rick Schaeffer (ricks@isc-br.isc-br.com) NAME popen, pclose - initiate I/O to/from a process SYNOPSIS #include FILE *popen(command, type) char *command, *type; pclose(stream) FILE *stream; DESCRIPTION The arguments to popen are pointers to null-terminated strings containing respectively a command line and an I/O mode, either "r" for reading or "w" for writing. It creates a pipe between the calling process and the command to be executed. The value returned is a stream pointer that can be used (as appropriate) to write to the standard input of the command or read from its standard output. A stream opened by popen **MUST** be closed by pclose, which waits for the associated process to terminate and returns the exit status of the command. Because stdio files are shared, a type "r" command may be used as an input filter, and a type "w" as an output filter. DIAGNOSTICS Popen returns a null pointer if files or processes cannot be created. Pclose returns -1 if stream is not associated with a `popened' command. */ #include #include #include #include #include #include struct ArpBase *ArpBase; static struct Process *thistask; static struct ProcessControlBlock pcb; struct pstruct { FILE *fptr; struct ZombieMsg exitmsg; }; struct pstruct poarray[6]; FILE *popen(cmd,mode) char *cmd; char *mode; { char *parms; static char tempname[] = "pipe:pXXX.XXX"; char *pname,redir[20],*mktemp(); short i; int pmode; struct pstruct *poptr; BPTR pfd; if (thistask == NULL) thistask = (struct Process *) FindTask(NULL); if (ArpBase == NULL) ArpBase = (struct ArpBase *) OpenLibrary(ArpName,ArpVersion); if (ArpBase == NULL) { fprintf(stderr,"Arp Open Failed\n"); return(NULL); } poptr = NULL; for (i=0; i<6; i++) { if (poarray[i].fptr == NULL) { poptr = &poarray[i]; break; } } if (poptr == NULL) { fprintf(stderr,"popen: Unable to find an open pipe\n"); return(NULL); } if (strcmp(mode,"r") == 0) pmode = MODE_NEWFILE; else if (strcmp(mode,"w") == 0) pmode = MODE_OLDFILE; else { fprintf(stderr,"popen: Mode must be 'r' or 'w'\n"); return(NULL); } tempname[5] = 'a' + i; strcpy(redir,tempname); pname = mktemp(redir); /* set up a pipe: file name */ setmem(&pcb, sizeof(struct ProcessControlBlock), 0); /* Now get the child's stack and priority set up */ if (thistask->pr_CLI) { struct CommandLineInterface *cli; cli = (struct CommandLineInterface *) BADDR(thistask->pr_CLI); pcb.pcb_StackSize = cli->cli_DefaultStack << 2; } else pcb.pcb_StackSize = thistask->pr_StackSize; pcb.pcb_Pri = thistask->pr_Task.tc_Node.ln_Pri; /* Open the side of the pipe for the child */ pfd = Open(pname,pmode); if (pfd == 0) { fprintf(stderr,"popen: Unable to open pipe file\n"); return(NULL); } if (pmode == MODE_NEWFILE) pcb.pcb_Output = pfd; else pcb.pcb_Input = pfd; /* Locate the break between command and parameters */ parms = strpbrk(cmd," \t"); if (parms) { *parms++ = 0; parms = stpblk(parms); if (parms && *parms == 0) parms = NULL; } /* Create a port for the child's exit message */ poptr->exitmsg.zm_ExecMessage.mn_ReplyPort = CreatePort(NULL,0); if (poptr->exitmsg.zm_ExecMessage.mn_ReplyPort == 0) { fprintf(stderr,"popen: Couldn't create message port\n"); return(NULL); } pcb.pcb_LastGasp = &poptr->exitmsg; if (ASyncRun(cmd,parms,&pcb) < 0) { fprintf(stderr,"popen: AsyncRun failed\n"); DeletePort(poptr->exitmsg.zm_ExecMessage.mn_ReplyPort); return(NULL); } /* Now open our side of the pipe */ poptr->fptr = fopen(pname,mode); if (poptr->fptr == NULL) { fprintf(stderr,"popen: Unable to open pipe file %s\n",pname); DeletePort(poptr->exitmsg.zm_ExecMessage.mn_ReplyPort); return(NULL); } return(poptr->fptr); } pclose(fptr) FILE *fptr; { short i; for (i=0; i<6; i++) if (poarray[i].fptr == fptr) break; if (i > 5) { fprintf(stderr,"popen: DISASTER...couldn't find file pointer in pclose\n"); exit(1); } fclose(fptr); WaitPort(poarray[i].exitmsg.zm_ExecMessage.mn_ReplyPort); poarray[i].fptr = NULL; DeletePort(poarray[i].exitmsg.zm_ExecMessage.mn_ReplyPort); return(poarray[i].exitmsg.zm_ReturnCode); } char *mktemp(template) char *template; { register char *cp; register unsigned long val; cp = template; cp += strlen(cp); for (val = (unsigned long) FindTask(0L) ; ; ) if (*--cp == 'X') { *cp = val%10 + '0'; val /= 10; } else if (*cp != '.') break; if (*++cp != 0) { *cp = 'A'; while (access(template, 0) == 0) { if (*cp == 'Z') { *template = 0; break; } ++*cp; } } else { if (access(template, 0) == 0) *template = 0; } return template; } -- Rick Schaeffer UUCP: uunet!isc-br.isc-br.com!ricks ISC-Bunker Ramo ricks@isc-br.isc-br.com Box TAF-C8 Phone: (509)927-5114 Spokane, WA 99220