1 /* $Id: amiga.c,v 1.4 2004/07/01 17:10:03 broeker Exp $ */
3 /* GNUPLOT - amiga.c */
6 * Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
8 * Permission to use, copy, and distribute this software and its
9 * documentation for any purpose with or without fee is hereby granted,
10 * provided that the above copyright notice appear in all copies and
11 * that both that copyright notice and this permission notice appear
12 * in supporting documentation.
14 * Permission to modify the software is granted, but not the right to
15 * distribute the complete modified source code. Modifications are to
16 * be distributed as patches to the released version. Permission to
17 * distribute binaries produced by compiling modified sources is granted,
19 * 1. distribute the corresponding source modifications from the
20 * released version in the form of a patch file along with the binaries,
21 * 2. add special version identification to distinguish your version
22 * in addition to the base release version number,
23 * 3. provide your name and address as the primary contact for the
24 * support of your modified version, and
25 * 4. retain our contact information in regard to use of the base
27 * Permission to distribute the released version of the source code along
28 * with corresponding source modifications in the form of a patch file is
29 * granted with same provisions 2 through 4 for binary distributions.
31 * This software is provided "as is" without express or implied warranty
32 * to the extent permitted by applicable law.
38 * Written by Carsten Steger <stegerc@informatik.tu-muenchen.de>
40 * Popen and pclose have the same semantics as their UNIX counterparts.
42 * Additionally, they install an exit trap that closes all open pipes,
43 * should the program terminate abnormally.
56 #include <exec/types.h>
58 #include <dos/dosextens.h>
59 #include <dos/dostags.h>
60 #include <proto/exec.h>
61 #include <proto/dos.h>
63 #ifdef PIPES /* dont bother if pipes are not being used elsewhere */
65 /* Maximum number of open pipes. If this is set to a number > 10, the code
66 * that constructs the pipe names in popen () will have to be modified.
68 #define MAX_OPEN_PIPES 10
70 /* We need at least this Dos version to work. */
71 #define DOS_VERSION 37
74 /* This data structure is sent to the child process with sm_Cmd set to the
75 * command to be executed. When the child is done it sets sm_RetCode to
76 * the return code of the executed command.
78 struct StartupMessage {
79 struct Message sm_Msg;
84 /* We keep track of the open pipe through this data structure. */
85 struct PipeFileDescriptor {
87 struct StartupMessage pfd_Msg;
91 /* Needed to check for the required Dos version. */
92 extern struct DosLibrary *DOSBase;
94 /* This data structure keeps track of the pipes that are still open. */
95 static struct PipeFileDescriptor OpenPipes[MAX_OPEN_PIPES];
97 /* The address of the process that calls popen or pclose. */
98 static struct Process *ThisProcess;
100 /* Are we called for the first time? */
101 static LONG FirstCall = TRUE;
104 /* Prototypes for the functions below. */
105 FILE *popen (const char *command, const char *mode);
106 int pclose (FILE *stream);
107 static void CleanUpPipes (void);
108 static int __saveds ChildEntry (void);
112 popen (const char *command, const char *mode)
118 struct CommandLineInterface *ThisCli;
119 struct PipeFileDescriptor *PipeToUse;
124 struct Process *ChildProcess;
125 struct TagItem NewProcTags[8] = {
126 {NP_Entry, (Tag) ChildEntry},
128 {NP_StackSize, 4096},
131 {NP_CloseInput, FALSE},
132 {NP_CloseOutput, FALSE},
136 /* Test whether we're using the right Dos version. */
137 if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
142 /* If we're called for the first time, install exit trap and do some
143 * initialisation stuff.
146 /* Initialise pipe file descriptor table. */
147 memset (OpenPipes, 0, sizeof (OpenPipes));
149 /* Install our exit trap. */
150 if (atexit (CleanUpPipes) != 0) {
157 /* If we don't know our process' address yet, we should get it now. */
158 if (ThisProcess == NULL)
159 ThisProcess = (struct Process *) FindTask (NULL);
161 /* Get our Cli structure. */
164 /* Now try to find an empty slot in the pipe file descriptor table.
165 * Return NULL if no slot is available.
167 for (PipeNumToUse = 0; PipeNumToUse < MAX_OPEN_PIPES; PipeNumToUse++)
168 if (OpenPipes[PipeNumToUse].pfd_File == NULL) break;
169 if (PipeNumToUse >= MAX_OPEN_PIPES) {
173 PipeToUse = &OpenPipes[PipeNumToUse];
175 /* Check if the specified mode is valid. */
176 if (strcmp (mode, "r") == 0)
177 ChildPipeMode = MODE_NEWFILE;
178 else if (strcmp (mode, "w") == 0)
179 ChildPipeMode = MODE_OLDFILE;
185 /* Make a unique file name for the pipe that we are about to open. The
186 * file name has the following format: "PIPE:XXXXXXXX_Y", where
187 * XXXXXXXX is the address of our process in hex, Y is the number of the
188 * slot in the pipe descriptor table that we will use. The code is
190 * sprintf (PipeNameWriter, "PIPE:%08lX_%1d", ThisProcess, PipeNumToUse);
191 * but it doesn't need sprintf and therefore makes programs that don't
192 * use printf a lot shorter.
194 strcpy (PipeName, "PIPE:00000000_0");
195 NextChar = PipeName + 12;
196 ProcAddress = (ULONG) ThisProcess;
197 while (ProcAddress != 0) {
198 HexDigit = (UBYTE) ProcAddress & 0xf;
199 HexDigit = HexDigit < 10 ? HexDigit + '0' : HexDigit - 10 + 'A';
200 *NextChar-- = HexDigit;
203 /* If MAX_OPEN_PIPES > 10, this will have to be modified. */
204 PipeName[14] = ((UBYTE) PipeNumToUse) + '0';
206 /* Create tags for the child process. */
207 if (ThisProcess->pr_CLI)
208 NewProcTags[2].ti_Data = ThisCli->cli_DefaultStack << 2;
210 NewProcTags[2].ti_Data = ThisProcess->pr_StackSize;
212 /* Open both ends of the pipe. The child's side is opened with Open (),
213 * while the parent's side is opened with fopen ().
215 ChildPipe = Open (PipeName, ChildPipeMode);
216 ParentPipe = fopen (PipeName, mode);
217 if (ChildPipeMode == MODE_NEWFILE) {
218 NewProcTags[3].ti_Data = Input ();
219 NewProcTags[4].ti_Data = ChildPipe;
220 NewProcTags[5].ti_Data = FALSE;
221 NewProcTags[6].ti_Data = TRUE;
223 NewProcTags[3].ti_Data = ChildPipe;
224 NewProcTags[4].ti_Data = Output ();
225 NewProcTags[5].ti_Data = TRUE;
226 NewProcTags[6].ti_Data = FALSE;
228 if (ChildPipe == NULL || ParentPipe == NULL) {
233 /* Now generate a entry in the pipe file descriptor table. */
234 PipeToUse->pfd_Msg.sm_Cmd = malloc (strlen (command) + 1);
235 if (PipeToUse->pfd_Msg.sm_Cmd == NULL) {
239 strcpy (PipeToUse->pfd_Msg.sm_Cmd, command);
240 PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort = CreateMsgPort ();
241 if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL) {
245 PipeToUse->pfd_Msg.sm_Msg.mn_Node.ln_Type = NT_MESSAGE;
246 PipeToUse->pfd_Msg.sm_Msg.mn_Node.ln_Pri = 0;
247 PipeToUse->pfd_Msg.sm_Msg.mn_Length = sizeof (struct StartupMessage);
248 PipeToUse->pfd_File = ParentPipe;
250 /* Now create the child process. */
251 ChildProcess = CreateNewProc (NewProcTags);
252 if (ChildProcess == NULL) {
257 /* Pass the startup message to the child process. */
258 PutMsg (&ChildProcess->pr_MsgPort, (struct Message *) &PipeToUse->pfd_Msg);
260 /* This is the normal exit point for the function. */
263 /* This code is only executed if there was an error. In this case the
264 * allocated resources must be freed. The code is actually clearer (at
265 * least in my opinion) and more concise by using goto than by using a
266 * function (global variables or function parameters needed) or a lot
267 * of if-constructions (code gets blown up unnecessarily).
270 if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL)
271 DeleteMsgPort (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort);
281 pclose (FILE *stream)
285 /* Test whether we're using the right Dos version. */
286 if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
291 /* Test whether this is the first call to this module or not. If so,
292 * pclose has been called before popen and we return with an error
293 * because the initialisation has yet to be done.
300 /* Search for the correct table entry and close the associated file. */
301 for (PipeToClose = 0; PipeToClose < MAX_OPEN_PIPES; PipeToClose++)
302 if (OpenPipes[PipeToClose].pfd_File == stream) break;
303 if (PipeToClose >= MAX_OPEN_PIPES) {
309 /* Now wait for the child to terminate and get its exit status. */
310 WaitPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
311 OpenPipes[PipeToClose].pfd_File = NULL;
313 /* Free the allocates resources. */
314 DeleteMsgPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
315 free (OpenPipes[PipeToClose].pfd_Msg.sm_Cmd);
317 return OpenPipes[PipeToClose].pfd_Msg.sm_RetCode;
327 /* Close each open pipe. */
328 for (Count = 0; Count < MAX_OPEN_PIPES; Count++) {
329 Pipe = OpenPipes[Count].pfd_File;
339 struct Process *ChildProc;
340 struct StartupMessage *StartUpMessage;
342 struct DosLibrary *DOSBase;
343 struct TagItem SysTags[3] = {
345 {SYS_UserShell, TRUE},
349 /* We need to open this library, because we don't inherit it from our
352 DOSBase = (struct DosLibrary *) OpenLibrary ("dos.library", DOS_VERSION);
354 /* Get the childs process structure. */
355 ChildProc = (struct Process *) FindTask (NULL);
357 /* Wait for the startup message from the parent. */
358 WaitPort (&ChildProc->pr_MsgPort);
359 StartUpMessage = (struct StartupMessage *) GetMsg (&ChildProc->pr_MsgPort);
361 /* Now run the command and return the result. */
363 ReturnCode = System (StartUpMessage->sm_Cmd, SysTags);
366 StartUpMessage->sm_RetCode = ReturnCode;
368 /* Tell the parent that we are done. */
369 ReplyMsg ((struct Message *) StartUpMessage);
372 CloseLibrary ((struct Library *) DOSBase);