Article View: comp.sys.amiga.tech
Article #11036Re: popen/pclose for 1.3
From: Arne Nordmark
Date: Wed, 18 Jan 1989 22:19
Date: Wed, 18 Jan 1989 22:19
399 lines
10780 bytes
10780 bytes
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;
}
Technical Details
Message-ID:
<756@draken.nada.kth.se>Path:
arc1-ca.pugleaf.net!utzoo!attcan!uunet!mcvax!enea!kth!draken!nordmarkReferences:
<3768@druwy.ATT.COM>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!attcan!uunet!mcvax!enea!kth!draken!nordmark From: nordmark@nada.kth.se (Arne Nordmark) Newsgroups: comp.sys.amiga.tech Subject: Re: popen/pclose for 1.3 Summary: Here is a popen() that uses the ConMan PIP: device Keywords: pipes Message-ID: <756@draken.nada.kth.se> Date: 18 Jan 89 22:19:15 GMT References: <3768@druwy.ATT.COM> Reply-To: stacken.kth.se!gno@uunet.uu.net (Gunnar Nordmark) Organization: Royal Institute of Technology, Stockholm, Sweden Lines: 399