3 * popen.c - Unix comaptible popen() and pclose()
5 * Author: Rick Schaeffer <ricks@isc-br.isc-br.com>
6 * Pavel Fedin <sonic_amiga@rambler.ru>
9 /****** net.lib/popen *******************************************************
11 popen, pclose - initiate I/O to/from a process
16 FILE *popen(command, type)
23 The arguments to popen are pointers to null-terminated
24 strings containing respectively a command line and an
25 I/O mode, either "r" for reading or "w" for writing. It
26 creates a pipe between the calling process and the command
27 to be executed. The value returned is a stream pointer that
28 can be used (as appropriate) to write to the standard input
29 of the command or read from its standard output.
31 A stream opened by popen **MUST** be closed by pclose, which
32 waits for the associated process to terminate and returns
33 the exit status of the command.
35 Because stdio files are shared, a type "r" command may be
36 used as an input filter, and a type "w" as an output filter.
39 Popen returns a null pointer if files or processes cannot be
42 Pclose returns -1 if stream is not associated with a
46 Original version by Rick Schaeffer <ricks@isc-br.isc-br.com>
54 #include <exec/types.h>
55 #include <exec/memory.h>
57 #include <dos/dosextens.h>
58 #include <dos/record.h>
59 #include <dos/dostags.h>
61 #include <proto/exec.h>
62 #include <proto/dos.h>
63 #include <clib/alib_protos.h>
69 extern char *mktemp(char *);
75 struct Library
*DOSBase
;
81 struct POmsg childmsg
;
85 static struct pstruct poarray
[MAXPIPES
];
87 static int childprocess(void);
89 FILE *popen(const char *cmd
, const char *mode
)
91 static char tempname
[] = "pipe:pXXX.XXX";
92 char *pname
,redir
[20];
95 struct pstruct
*poptr
;
96 struct Process
*child
;
97 struct CommandLineInterface
*cli
;
99 struct Process
*thistask
;
102 /* First, get pointers to our process and cli structs */
103 thistask
= (struct Process
*) FindTask(NULL
);
107 /* now find an open pipe (we currently only allow 6 simultaneously
109 for (i
=0; i
<MAXPIPES
; i
++) {
110 if (poarray
[i
].fptr
== NULL
) {
116 fprintf(stderr
,"popen: Unable to find an open pipe\n");
120 if (strcmp(mode
,"r") == 0)
121 pmode
= MODE_NEWFILE
;
122 else if (strcmp(mode
,"w") == 0)
123 pmode
= MODE_OLDFILE
;
125 fprintf(stderr
,"popen: Mode must be 'r' or 'w'\n");
130 /* Try to make a guaranteed unique file name for the pipe */
131 strcpy(redir
,tempname
);
134 pname
= mktemp(redir
); /* set up a pipe: file name */
136 /* Now get the child's stack and priority set up */
138 stacksize
= cli
->cli_DefaultStack
<< 2;
140 stacksize
= thistask
->pr_StackSize
;
142 /* Now open our side of the pipe */
143 fptr
= fopen(pname
,mode
);
145 fprintf(stderr
,"popen: Unable to open pipe file %s\n",pname
);
149 /* create the command. since the "System" function runs through
150 the default shell, we need to tell it not to fail so that we
151 ALWAYS get back the exit status. This wouldn't be necessary
152 if the CLI created by the System function inherited the parent's
154 The pipe file is passed as redirection to shell, which the
155 SystemTags() will parse. For some reason the default "more"
156 could not get it's input properly if redirection was not used.
158 poptr
->childmsg
.cmd
= malloc(strlen(cmd
) + 15 + 20);
159 sprintf(poptr
->childmsg
.cmd
, "failat 9999\n%s %c%s\n",
160 cmd
, (pmode
== MODE_NEWFILE
) ? '>' : '<', pname
);
162 /* Create a port that we can get the child's exit status through */
163 poptr
->childmsg
.POm
.mn_ReplyPort
= CreateMsgPort();
164 poptr
->childmsg
.POm
.mn_Node
.ln_Type
= NT_MESSAGE
;
165 poptr
->childmsg
.POm
.mn_Node
.ln_Pri
= 0;
166 if (poptr
->childmsg
.POm
.mn_ReplyPort
== 0) {
168 fprintf(stderr
,"popen: Couldn't create message port\n");
173 /* Now we can start the new process. NOTE: this is actually going
174 to create a process consisting ONLY of the function "childprocess"
175 which can be seen below. childprocess() then runs the command
176 passed in the startup message.
178 child
= CreateNewProcTags(
179 NP_Entry
, (IPTR
) childprocess
,
182 NP_CloseInput
, FALSE
,
183 NP_CloseOutput
, FALSE
,
184 NP_StackSize
, stacksize
,
187 NP_CodeType
, CODETYPE_PPC
,
192 poptr
->childmsg
.DOSBase
= (struct Library
*)DOSBase
;
194 /* now pass the child the startup message */
195 PutMsg(&child
->pr_MsgPort
,(struct Message
*) &poptr
->childmsg
);
197 return(poptr
->fptr
= fptr
);
200 FILE *popenl(const char *arg0
, ...)
203 char argbuf
[512], *mode
;
205 strcpy(argbuf
, arg0
);
209 char *s
= va_arg(ap
, char *);
213 strcat(argbuf
, "\n");
221 strcat(argbuf
, "\"");
223 strcat(argbuf
, "\"");
230 mode
= va_arg(ap
, char *);
233 return(popen(argbuf
, mode
));
237 int pclose(FILE *fptr
)
241 /* Figure out which pipe we used for this file */
242 for (i
=0; i
<MAXPIPES
; i
++)
243 if (poarray
[i
].fptr
== fptr
)
246 fprintf(stderr
,"popen: DISASTER...couldn't find file pointer in pclose\n");
253 /* now wait for the exit status */
254 WaitPort(poarray
[i
].childmsg
.POm
.mn_ReplyPort
);
255 poarray
[i
].fptr
= NULL
;
257 /* clean things up */
258 DeletePort(poarray
[i
].childmsg
.POm
.mn_ReplyPort
);
259 free(poarray
[i
].childmsg
.cmd
);
260 return(poarray
[i
].childmsg
.rc
);
263 /* SAS/C autoinitialization for cleanup! */
264 void STDARGS DESTRUCTOR
_STD_4000_popen(void)
268 /* Close all the open pipes! */
269 for(i
=0; i
<MAXPIPES
; i
++)
273 pclose(poarray
[i
].fptr
);
277 } /* _STD_4000_popen */
279 char *mktemp(char * template)
282 register unsigned long val
;
287 for (val
= (unsigned long) FindTask(0L) ; ; )
291 } else if (*cp
!= '.')
296 while (lock
= Lock(template, ACCESS_READ
))
306 if (lock
= Lock(template, ACCESS_READ
)) {
314 /* WATCH OUT! This only works without __saveds because of the special
315 SAS/C 6.1 tricks I use! Check the output with omd! */
316 static int INTERRUPT
childprocess(void)
318 struct ExecBase
*SysBase
= *((struct ExecBase
**)4);
319 struct Library
*DOSBase
;
321 struct POmsg
*startupmsg
;
324 /* find our process structure */
325 me
= (struct Process
*) FindTask(NULL
);
327 /* Wait for the parent to kick us off */
328 WaitPort(&me
->pr_MsgPort
);
330 /* Get the command to execute */
331 startupmsg
= (struct POmsg
*) GetMsg(&me
->pr_MsgPort
);
333 DOSBase
= startupmsg
->DOSBase
;
337 /* Now run the command. stdin and stdout are already set up */
338 i
= SystemTags(startupmsg
->cmd
,
345 /* UNIX compatibility ... */
350 /* pass the exit code back to the parent */
351 ReplyMsg((struct Message
*) startupmsg
);