- Optification is done by auto builder now
[gnuplot] / src / amiga.c
1 /* $Id: amiga.c,v 1.4 2004/07/01 17:10:03 broeker Exp $ */
2
3 /* GNUPLOT - amiga.c */
4
5 /*[
6  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
7  *
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.
13  *
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,
18  * provided you
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
26  *    software.
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.
30  *
31  * This software is provided "as is" without express or implied warranty
32  * to the extent permitted by applicable law.
33 ]*/
34
35 /*
36  * amiga.c
37  *
38  * Written by Carsten Steger <stegerc@informatik.tu-muenchen.de>
39  *
40  * Popen and pclose have the same semantics as their UNIX counterparts.
41  *
42  * Additionally, they install an exit trap that closes all open pipes,
43  * should the program terminate abnormally.
44  */
45
46 #ifdef HAVE_CONFIG_H
47 # include "config.h"
48 #endif
49
50 #include <stdio.h>
51 #include <ios1.h>
52 #include <error.h>
53 #include <string.h>
54 #include <stdlib.h>
55
56 #include <exec/types.h>
57 #include <dos/dos.h>
58 #include <dos/dosextens.h>
59 #include <dos/dostags.h>
60 #include <proto/exec.h>
61 #include <proto/dos.h>
62
63 #ifdef PIPES  /* dont bother if pipes are not being used elsewhere */
64
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.
67  */
68 #define MAX_OPEN_PIPES 10
69
70 /* We need at least this Dos version to work. */
71 #define DOS_VERSION 37
72
73
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.
77  */
78 struct StartupMessage {
79   struct Message sm_Msg;
80   LONG sm_RetCode;
81   UBYTE *sm_Cmd;
82 };
83
84 /* We keep track of the open pipe through this data structure. */
85 struct PipeFileDescriptor {
86   FILE *pfd_File;
87   struct StartupMessage pfd_Msg;
88 };
89
90
91 /* Needed to check for the required Dos version. */
92 extern struct DosLibrary *DOSBase;
93
94 /* This data structure keeps track of the pipes that are still open. */
95 static struct PipeFileDescriptor OpenPipes[MAX_OPEN_PIPES];
96
97 /* The address of the process that calls popen or pclose. */
98 static struct Process *ThisProcess;
99
100 /* Are we called for the first time? */
101 static LONG FirstCall = TRUE;
102
103
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);
109
110
111 FILE *
112 popen (const char *command, const char *mode)
113 {
114     UBYTE PipeName[16];
115     ULONG ProcAddress;
116     UBYTE HexDigit;
117     UBYTE *NextChar;
118     struct CommandLineInterface *ThisCli;
119     struct PipeFileDescriptor *PipeToUse;
120     LONG PipeNumToUse;
121     LONG ChildPipeMode;
122     BPTR ChildPipe;
123     FILE *ParentPipe;
124     struct Process *ChildProcess;
125     struct TagItem NewProcTags[8] = {
126         {NP_Entry, (Tag) ChildEntry},
127         {NP_Cli, TRUE},
128         {NP_StackSize, 4096},
129         {NP_Input, NULL},
130         {NP_Output, NULL},
131         {NP_CloseInput, FALSE},
132         {NP_CloseOutput, FALSE},
133         {TAG_DONE, 0}
134     };
135
136     /* Test whether we're using the right Dos version. */
137     if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
138         errno = EPIPE;
139         return NULL;
140     }
141
142     /* If we're called for the first time, install exit trap and do some
143      * initialisation stuff.
144      */
145     if (FirstCall) {
146         /* Initialise pipe file descriptor table. */
147         memset (OpenPipes, 0, sizeof (OpenPipes));
148
149         /* Install our exit trap. */
150         if (atexit (CleanUpPipes) != 0) {
151             errno = EPIPE;
152             return NULL;
153         }
154         FirstCall = FALSE;
155     }
156
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);
160
161     /* Get our Cli structure. */
162     ThisCli = Cli ();
163
164     /* Now try to find an empty slot in the pipe file descriptor table.
165      * Return NULL if no slot is available.
166      */
167     for (PipeNumToUse = 0; PipeNumToUse < MAX_OPEN_PIPES; PipeNumToUse++)
168         if (OpenPipes[PipeNumToUse].pfd_File == NULL) break;
169     if (PipeNumToUse >= MAX_OPEN_PIPES) {
170         errno = EMFILE;
171         return NULL;
172     }
173     PipeToUse = &OpenPipes[PipeNumToUse];
174
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;
180     else {
181         errno = EINVAL;
182         return NULL;
183     }
184
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
189      * equivalent to
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.
193      */
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;
201         ProcAddress >>= 4;
202     }
203     /* If MAX_OPEN_PIPES > 10, this will have to be modified. */
204     PipeName[14] = ((UBYTE) PipeNumToUse) + '0';
205
206     /* Create tags for the child process. */
207     if (ThisProcess->pr_CLI)
208         NewProcTags[2].ti_Data = ThisCli->cli_DefaultStack << 2;
209     else
210         NewProcTags[2].ti_Data = ThisProcess->pr_StackSize;
211
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 ().
214      */
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;
222     } else {
223         NewProcTags[3].ti_Data = ChildPipe;
224         NewProcTags[4].ti_Data = Output ();
225         NewProcTags[5].ti_Data = TRUE;
226         NewProcTags[6].ti_Data = FALSE;
227     }
228     if (ChildPipe == NULL || ParentPipe == NULL) {
229         errno = EPIPE;
230         goto cleanup;
231     }
232
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) {
236         errno = ENOMEM;
237         goto cleanup;
238     }
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) {
242         errno = ENOMEM;
243         goto cleanup;
244     }
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;
249
250     /* Now create the child process. */
251     ChildProcess = CreateNewProc (NewProcTags);
252     if (ChildProcess == NULL) {
253         errno = ENOMEM;
254         goto cleanup;
255     }
256
257     /* Pass the startup message to the child process. */
258     PutMsg (&ChildProcess->pr_MsgPort, (struct Message *) &PipeToUse->pfd_Msg);
259
260     /* This is the normal exit point for the function. */
261     return ParentPipe;
262
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).
268      */
269   cleanup:
270     if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL)
271         DeleteMsgPort (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort);
272     if (ParentPipe)
273         fclose (ParentPipe);
274     if (ChildPipe)
275         Close (ChildPipe);
276     return NULL;
277 }
278
279
280 int
281 pclose (FILE *stream)
282 {
283   LONG PipeToClose;
284
285   /* Test whether we're using the right Dos version. */
286   if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
287     errno = EPIPE;
288     return -1;
289   }
290
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.
294    */
295   if (FirstCall) {
296     errno = EBADF;
297     return -1;
298   }
299
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) {
304     errno = EBADF;
305     return -1;
306   }
307   fclose (stream);
308
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;
312
313   /* Free the allocates resources. */
314   DeleteMsgPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
315   free (OpenPipes[PipeToClose].pfd_Msg.sm_Cmd);
316
317   return OpenPipes[PipeToClose].pfd_Msg.sm_RetCode;
318 }
319
320
321 static void
322 CleanUpPipes ()
323 {
324   LONG Count;
325   FILE *Pipe;
326
327   /* Close each open pipe. */
328   for (Count = 0; Count < MAX_OPEN_PIPES; Count++) {
329     Pipe = OpenPipes[Count].pfd_File;
330     if (Pipe != NULL)
331       pclose (Pipe);
332   }
333 }
334
335
336 static int __saveds
337 ChildEntry ()
338 {
339     struct Process *ChildProc;
340     struct StartupMessage *StartUpMessage;
341     LONG ReturnCode;
342     struct DosLibrary *DOSBase;
343     struct TagItem SysTags[3] = {
344         {SYS_Asynch, FALSE},
345         {SYS_UserShell, TRUE},
346         {TAG_DONE, 0}
347     };
348
349     /* We need to open this library, because we don't inherit it from our
350      * parent process.
351      */
352     DOSBase = (struct DosLibrary *) OpenLibrary ("dos.library", DOS_VERSION);
353
354     /* Get the childs process structure. */
355     ChildProc = (struct Process *) FindTask (NULL);
356
357     /* Wait for the startup message from the parent. */
358     WaitPort (&ChildProc->pr_MsgPort);
359     StartUpMessage = (struct StartupMessage *) GetMsg (&ChildProc->pr_MsgPort);
360
361     /* Now run the command and return the result. */
362     if (DOSBase != NULL)
363         ReturnCode = System (StartUpMessage->sm_Cmd, SysTags);
364     else
365         ReturnCode = 10000;
366     StartUpMessage->sm_RetCode = ReturnCode;
367
368     /* Tell the parent that we are done. */
369     ReplyMsg ((struct Message *) StartUpMessage);
370
371     if (DOSBase)
372         CloseLibrary ((struct Library *) DOSBase);
373
374     return 0;
375 }
376
377 #endif /* PIPES */