mab@druwy.ATT.COM (Alan Bland) (01/17/89)
Does anyone have a UNIX-like implementation of popen and pclose that uses the 1.3 pipe handler? I'm porting some UNIX code and would like to take advantage of pipes. I could roll my own, but I haven't yet learned all the secrets of file handles with Execute(), and figure there must be somebody out there who has already done popen and pclose and taken care of most or all of the gotchas. Thanx. -- // Alan Bland // AT&T Bell Laboratories, Denver CO // (303)-538-3510 - att!druwy!mab
nordmark@nada.kth.se (Arne Nordmark) (01/19/89)
In article <3768@druwy.ATT.COM> mab@druwy.ATT.COM (Alan Bland) writes: > >Does anyone have a UNIX-like implementation of popen and pclose that >uses the 1.3 pipe handler? I'm porting some UNIX code and would like >to take advantage of pipes. I could roll my own, but I haven't yet >learned all the secrets of file handles with Execute(), and figure >there must be somebody out there who has already done popen and pclose >and taken care of most or all of the gotchas. Thanx. Yeah, there is a *lot* of gotchas. This took me at least a week of headaches to put together last spring, but it emulates the *IX popen() very well. (It does not run the popened command through sh though :-) I've used it in lots of *IX stuff that I've ported. (including UUCP). 1.3 didn't exist when I wrote this, so I used the ConMan PIP: device instead. Later I have tried to use the 1.3 pipe device, but to no avail - PIPE: doesn't seem to work like a *IX pipe, but PIP: certainly does. Also I have used ARP's ASyncRun() instead of Execute(). The latter is a truly miserable function that doesn't at all live up to the requirements for writing a popen() function. (I have tried...) This isn't a nice peice of code, but it works! (it has been extensively tested). Feel free to modify and/or clean up the code, and of course use it! Have fun! -- Gunnar /**************************************************************************** * * popen.c V1.0 (c) CopyRight 1988, Gunnar Nordmark. * * Gunnar Nordmark gno@stacken.kth.se * Nora strand 5 gno@SESTAK.BITNET * S-182 34 DANDERYD {mcvax,munnari,ukc,unido}!enea!sics!epsilon!gno * SWEDEN * * You may freely distribute this source as long as * the Copyright notice is left intact. * * Notes: * Works for Lattice 4.01 (don't forget to compile with -y) * lc -v -y popen * or Manx 3.6a * cc popen * * Needs arp.library V34 or greater. DON'T FORGET TO OPEN IT! * Also needs PIP: device included in ConMan 1.1 (must be in your MountList) * * Important: * If you haven't got the bug-fix for arpbase.h 34.00 you must change the * structure definition for ProcessControlBlock to the following: * struct ProcessControlBlock { ULONG pcb_StackSize; BYTE pcb_Pri; BYTE pcb_Control; APTR pcb_TrapCode; ULONG pcb_Input,p_Output; union { ULONG pcb_SplatFile; BYTE *pcb_ConName; } pcb_Console; CPTR pcb_LoadedCode; struct ZombieMsg *pcb_LastGasp; struct MsgPort *pcb_WBProcess; }; * **************************************************************************** NAME popen, pclose - initiate I/O to/from a process SYNOPSIS #include <stdio.h> 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. BUGS Buffered reading before opening an input filter may leave the standard input of that filter mispositioned. Similar problems with an output filter may be forestalled by careful buffer flushing, for instance, with fflush. ***************************************************************************/ #include <libraries/dosextens.h> /* fix for arpbase.h, do this *before* including arpbase.h */ #define p_Output pcb_Output #include <libraries/arpbase.h> #include <arpfunctions.h> #include <exec/memory.h> #include <stdio.h> #include <fcntl.h> #ifdef AZTEC_C #define READONLY O_RDONLY #define WRITEONLY O_WRONLY extern char *AllocMem(); extern struct Task *FindTask(); extern struct Message *GetMsg(); extern struct MsgPort *CreatePort(); extern BPTR Open(); extern LONG Read(); extern LONG Write(); extern FILE *fdopen(); extern char *calloc(); #else #include <ios1.h> #include <proto/exec.h> #include <proto/dos.h> #define READONLY UFB_RA | O_RAW #define WRITEONLY UFB_RA | O_RAW #define MAXSTREAM NUFBS #define _devtab _ufbs #define fd ufbfh #define mode ufbflg #define geta4(a) extern struct UFB _ufbs[]; #endif #define PIPE "PIP:2000" #define ACTION_DOUBLE 2000L #define ACTION_END 1007L #define STKSIZ 1000 #define LINSIZ 256 #define YES 1 #define NO 0 #define iswspace(c) ((c)==' ' || (c)=='\t' || (c)=='\n') struct closeinfo { struct MsgPort *replyport; BPTR pipe2; }; struct MsgPort *replyports[MAXSTREAM]; /* * openpipe() * Opens a pipe and returns two filehandles to it. * The address of the second handle must be passed * as an argument. Both results are NULL if the * opens fails. */ BPTR openpipe(pipe2) BPTR *pipe2; { BPTR pipe1; struct FileHandle *handle1, *handle2; *pipe2=NULL; if (!(handle2=(struct FileHandle *) AllocMem((LONG)sizeof(struct FileHandle), MEMF_PUBLIC))) return NULL; if (!(pipe1=Open(PIPE, ACTION_DOUBLE))) { FreeMem((BYTE *)handle2, (LONG)sizeof(struct FileHandle)); return NULL; } handle1=(struct FileHandle *)BADDR(pipe1); *handle2=*handle1; *pipe2=(long)handle2>>2; return pipe1; } /* * closepipe() * Closes the 'second' filehandle returned by openpipe() * The 'first' one you should Close() normally; */ void closepipe(pipe2) BPTR pipe2; { struct FileHandle *handle2; handle2=(struct FileHandle *)BADDR(pipe2); SendPacket(ACTION_END, (LONG *)&handle2->fh_Arg1, handle2->fh_Type); FreeMem((BYTE *)handle2, (LONG)sizeof(struct FileHandle)); } void closeproc() { struct Process *proc; struct ZombieMsg *zmsg; struct closeinfo *ci; geta4(); proc=(struct Process *)FindTask(NULL); WaitPort(&proc->pr_MsgPort); zmsg=(struct ZombieMsg *)GetMsg(&proc->pr_MsgPort); ci=(struct closeinfo *)zmsg->zm_UserInfo; closepipe(ci->pipe2); PutMsg(ci->replyport, (struct Message *)zmsg); } FILE *popen(commandline, type) char *commandline, *type; { char commandbuf[LINSIZ], *command=commandbuf, *arguments; struct ProcessControlBlock pcb; int fh; FILE *stream=NULL; BPTR input, output, errput; BPTR pipe1=NULL; BPTR pipe2=NULL; struct MsgPort *replyport=NULL; struct MsgPort *port=NULL; struct ZombieMsg *zmsg=NULL; struct closeinfo *ci=NULL; struct Process *proc; if (strlen(commandline)>=LINSIZ || strcmp(type, "r") && strcmp(type, "w")) goto popenerror; strcpy(command, commandline); while (iswspace(*command)) command++; arguments=command; while (*arguments && !iswspace(*arguments)) arguments++; if (*arguments) *arguments++='\0'; else *++arguments='\0'; strcat(arguments, "\n"); input=_devtab[fileno(stdin)].fd; output=_devtab[fileno(stdout)].fd; errput=_devtab[fileno(stderr)].fd; if (!(pipe1=openpipe(&pipe2))) goto popenerror; for (fh=3; fh<=MAXSTREAM; fh++) if (_devtab[fh].fd==NULL) break; if (fh>MAXSTREAM) goto popenerror; _devtab[fh].fd=pipe1; _devtab[fh].mode=*type=='r' ? READONLY : WRITEONLY; if (!(stream=fdopen(fh, type))) goto popenerror; if (!(ci=(struct closeinfo *)calloc(1, sizeof(struct closeinfo)))) goto popenerror; if (!(zmsg=(struct ZombieMsg *)calloc(1, sizeof(struct ZombieMsg)))) goto popenerror; if ((replyport=CreatePort(NULL, 0L))==NULL) goto popenerror; setmem(&pcb, sizeof(struct ProcessControlBlock), 0); pcb.pcb_StackSize=STKSIZ; pcb.pcb_Control=PRF_SAVEIO | PRF_NOCLI | PRF_CODE; pcb.pcb_Input=input; pcb.pcb_Output=output; pcb.pcb_LoadedCode=(CPTR)closeproc; if (ASyncRun("closeproc", NULL, &pcb)!=0L) goto popenerror; port=pcb.pcb_WBProcess; ci->replyport=replyport; ci->pipe2=pipe2; zmsg->zm_ExecMessage.mn_Node.ln_Type=NT_MESSAGE; zmsg->zm_ExecMessage.mn_ReplyPort=port; zmsg->zm_UserInfo=(ULONG)ci; proc=(struct Process *)FindTask(NULL); setmem(&pcb, sizeof(struct ProcessControlBlock), 0); pcb.pcb_StackSize=proc->pr_CLI ? ((struct CommandLineInterface *)BADDR(proc->pr_CLI))->cli_DefaultStack<<2 : proc->pr_StackSize; pcb.pcb_Pri=proc->pr_Task.tc_Node.ln_Pri; pcb.pcb_Control=PRF_SAVEIO; pcb.pcb_Input=*type=='r' ? input : pipe2; pcb.pcb_Output=*type=='w' ? output : pipe2; pcb.pcb_Console.pcb_SplatFile=errput; pcb.pcb_LastGasp=zmsg; if (ASyncRun(command, arguments, &pcb)>0L) { replyports[fh]=replyport; return stream; } PutMsg(port, (struct Message *)zmsg); WaitPort(replyport); GetMsg(replyport); popenerror: if (replyport) DeletePort(replyport); if (zmsg) free(zmsg); if (ci) free(ci); if (stream) fclose(stream); else if (pipe1) { Close(pipe1); _devtab[fh].fd=NULL; _devtab[fh].mode=0; } if (pipe2 && !port) closepipe(pipe2); return NULL; } int pclose(stream) FILE *stream; { int fh, returncode; struct ZombieMsg *zmsg; struct MsgPort *replyport; struct closeinfo *ci; fh=fileno(stream); if (!(replyport=replyports[fh])) return -1; fclose(stream); WaitPort(replyport); zmsg=(struct ZombieMsg *)GetMsg(replyport); returncode=zmsg->zm_ReturnCode; ci=(struct closeinfo *)zmsg->zm_UserInfo; free(ci); free(zmsg); DeletePort(replyport); replyports[fh]=NULL; return returncode; }
ecphssrw@solaria.csun.edu (Stephen Walton) (01/19/89)
In article <3768@druwy.ATT.COM> mab@druwy.ATT.COM (Alan Bland) writes: > >Does anyone have a UNIX-like implementation of popen and pclose that >uses the 1.3 pipe handler? I'm porting some UNIX code and would like >to take advantage of pipes. I could roll my own, but I haven't yet >learned all the secrets of file handles with Execute()... Pipes on the Amiga are really much simpler than you think (I think :-) ). Just pretend they are files. Then, instead of something like: pipe_handle = popen("command", "w"); you would do pipe_handle = fopen("pipe:myfile", "w"); Execute("command <pipe:myfile", 0L, 0L); and voila. You probably want to use mktemp() to generate your pipe file name, just to be safe. -- Stephen Walton, Dept. of Physics & Astronomy, Cal State Univ. Northridge RCKG01M@CALSTATE.BITNET ecphssrw@afws.csun.edu swalton@solar.stanford.edu ...!csun!afws.csun.edu!ecphssrw
vkr@osupyr.mast.ohio-state.edu (Vidhyanath K. Rao) (01/22/89)
In article <442@solaria.csun.edu> ecphssrw@solaria.csun.edu (Stephen R. Walton) writes: >Pipes on the Amiga are really much simpler than you think (I think :-) ). >Just pretend they are files. Then, instead of something like: > pipe_handle = popen("command", "w"); >you would do > pipe_handle = fopen("pipe:myfile", "w"); > Execute("command <pipe:myfile", 0L, 0L); >and voila. You probably want to use mktemp() to generate your pipe >file name, just to be safe. There is one feature that you should be aware of (I don't know if this also occurs in *N*X pipes): gets() is implemented usually as `Read(Stdin(), sptr, 256L)' CON: and, I suspect the ConMan pip: will return when a `newline' is encountered. PIPE: *will not*. This irrelevent for stored files. But if you use the 1.3 PIPE:, and expect the spawned process to get its input as you shove it down the line, you have to pad it at the end. -- It is the man not the method that Nath solves the problem. vkr@osupyr.mast.ohio-state.edu -Poincare. (614)-366-9341
vkr@osupyr.mast.ohio-state.edu (Vidhyanath K. Rao) (01/22/89)
In article <1200@osupyr.mast.ohio-state.edu> I wrote > gets() is implemented usually as `Read(Stdin(), sptr, 256L)' It should say: gets() is usually implemented as `Read(Stdin(), bfrptr, 256L); {other stuff} -- It is the man not the method that Nath solves the problem. vkr@osupyr.mast.ohio-state.edu -Poincare. (614)-366-9341
peter@sugar.uu.net (Peter da Silva) (01/22/89)
In article <1200@osupyr.mast.ohio-state.edu>, vkr@osupyr.mast.ohio-state.edu (Vidhyanath K. Rao) writes: > gets() is implemented usually as `Read(Stdin(), sptr, 256L)' I certainly hope this isn't true. Any implementation of the standard I/O library that does this is badly broken. Gets should be freely intermixable with getchar(), fread(), fgets(), and so on... it should behave as if it's: gets(s) char *s; { int c; char *ptr; while((c = getchar()) != EOF) { if(c=='\n') break; *ptr++ = c; } *ptr == 0; return (c==EOF)?0:s; } -- Peter "Have you hugged your wolf today" da Silva `-_-' Hackercorp. ...texbell!sugar!peter, or peter@sugar.uu.net 'U`