Annotation of OpenXM_contrib/gnuplot/amiga.c, Revision 1.1.1.2
1.1.1.2 ! maekawa 1: /* $Id: amiga.c,v 1.1.1.2 1998/04/15 19:21:59 lhecking Exp $ */
1.1 maekawa 2:
3: /* GNUPLOT - amiga.c */
4:
5: /*[
6: * Copyright 1986 - 1993, 1998 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:
47: #include <stdio.h>
48: #include <ios1.h>
49: #include <error.h>
50: #include <string.h>
51: #include <stdlib.h>
52:
53: #include <exec/types.h>
54: #include <dos/dos.h>
55: #include <dos/dosextens.h>
56: #include <dos/dostags.h>
57: #include <proto/exec.h>
58: #include <proto/dos.h>
59:
60: #ifdef PIPES /* dont bother if pipes are not being used elsewhere */
61:
62: /* Maximum number of open pipes. If this is set to a number > 10, the code
63: * that constructs the pipe names in popen () will have to be modified.
64: */
65: #define MAX_OPEN_PIPES 10
66:
67: /* We need at least this Dos version to work. */
68: #define DOS_VERSION 37
69:
70:
71: /* This data structure is sent to the child process with sm_Cmd set to the
72: * command to be executed. When the child is done it sets sm_RetCode to
73: * the return code of the executed command.
74: */
75: struct StartupMessage {
76: struct Message sm_Msg;
77: LONG sm_RetCode;
78: UBYTE *sm_Cmd;
79: };
80:
81: /* We keep track of the open pipe through this data structure. */
82: struct PipeFileDescriptor {
83: FILE *pfd_File;
84: struct StartupMessage pfd_Msg;
85: };
86:
87:
88: /* Needed to check for the required Dos version. */
89: extern struct DosLibrary *DOSBase;
90:
91: /* This data structure keeps track of the pipes that are still open. */
92: static struct PipeFileDescriptor OpenPipes[MAX_OPEN_PIPES];
93:
94: /* The address of the process that calls popen or pclose. */
95: static struct Process *ThisProcess;
96:
97: /* Are we called for the first time? */
98: static LONG FirstCall = TRUE;
99:
100:
101: /* Prototypes for the functions below. */
102: FILE *popen (const char *command, const char *mode);
103: int pclose (FILE *stream);
104: static void CleanUpPipes (void);
105: static int __saveds ChildEntry (void);
106:
107:
108: FILE *popen (command, mode)
109: const char *command;
110: const char *mode;
111: {
112: UBYTE PipeName[16];
113: ULONG ProcAddress;
114: UBYTE HexDigit;
115: UBYTE *NextChar;
116: struct CommandLineInterface *ThisCli;
117: struct PipeFileDescriptor *PipeToUse;
118: LONG PipeNumToUse;
119: LONG ChildPipeMode;
120: BPTR ChildPipe;
121: FILE *ParentPipe;
122: struct Process *ChildProcess;
123: struct TagItem NewProcTags[8] = {
124: {NP_Entry, (Tag) ChildEntry},
125: {NP_Cli, TRUE},
126: {NP_StackSize, 4096},
127: {NP_Input, NULL},
128: {NP_Output, NULL},
129: {NP_CloseInput, FALSE},
130: {NP_CloseOutput, FALSE},
131: {TAG_DONE, 0}
132: };
133:
134: /* Test whether we're using the right Dos version. */
135: if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
136: errno = EPIPE;
137: return NULL;
138: }
139:
140: /* If we're called for the first time, install exit trap and do some
141: * initialisation stuff.
142: */
143: if (FirstCall) {
144: /* Initialise pipe file descriptor table. */
145: memset (OpenPipes, 0, sizeof (OpenPipes));
146:
147: /* Install our exit trap. */
148: if (atexit (CleanUpPipes) != 0) {
149: errno = EPIPE;
150: return NULL;
151: }
152: FirstCall = FALSE;
153: }
154:
155: /* If we don't know our process' address yet, we should get it now. */
156: if (ThisProcess == NULL)
157: ThisProcess = (struct Process *) FindTask (NULL);
158:
159: /* Get our Cli structure. */
160: ThisCli = Cli ();
161:
162: /* Now try to find an empty slot in the pipe file descriptor table.
163: * Return NULL if no slot is available.
164: */
165: for (PipeNumToUse = 0; PipeNumToUse < MAX_OPEN_PIPES; PipeNumToUse++)
166: if (OpenPipes[PipeNumToUse].pfd_File == NULL) break;
167: if (PipeNumToUse >= MAX_OPEN_PIPES) {
168: errno = EMFILE;
169: return NULL;
170: }
171: PipeToUse = &OpenPipes[PipeNumToUse];
172:
173: /* Check if the specified mode is valid. */
174: if (strcmp (mode, "r") == 0)
175: ChildPipeMode = MODE_NEWFILE;
176: else if (strcmp (mode, "w") == 0)
177: ChildPipeMode = MODE_OLDFILE;
178: else {
179: errno = EINVAL;
180: return NULL;
181: }
182:
183: /* Make a unique file name for the pipe that we are about to open. The
184: * file name has the following format: "PIPE:XXXXXXXX_Y", where
185: * XXXXXXXX is the address of our process in hex, Y is the number of the
186: * slot in the pipe descriptor table that we will use. The code is
187: * equivalent to
188: * sprintf (PipeNameWriter, "PIPE:%08lX_%1d", ThisProcess, PipeNumToUse);
189: * but it doesn't need sprintf and therefore makes programs that don't
190: * use printf a lot shorter.
191: */
192: strcpy (PipeName, "PIPE:00000000_0");
193: NextChar = PipeName + 12;
194: ProcAddress = (ULONG) ThisProcess;
195: while (ProcAddress != 0) {
196: HexDigit = (UBYTE) ProcAddress & 0xf;
197: HexDigit = HexDigit < 10 ? HexDigit + '0' : HexDigit - 10 + 'A';
198: *NextChar-- = HexDigit;
199: ProcAddress >>= 4;
200: }
201: /* If MAX_OPEN_PIPES > 10, this will have to be modified. */
202: PipeName[14] = ((UBYTE) PipeNumToUse) + '0';
203:
204: /* Create tags for the child process. */
205: if (ThisProcess->pr_CLI)
206: NewProcTags[2].ti_Data = ThisCli->cli_DefaultStack << 2;
207: else
208: NewProcTags[2].ti_Data = ThisProcess->pr_StackSize;
209:
210: /* Open both ends of the pipe. The child's side is opened with Open (),
211: * while the parent's side is opened with fopen ().
212: */
213: ChildPipe = Open (PipeName, ChildPipeMode);
214: ParentPipe = fopen (PipeName, mode);
215: if (ChildPipeMode == MODE_NEWFILE) {
216: NewProcTags[3].ti_Data = Input ();
217: NewProcTags[4].ti_Data = ChildPipe;
218: NewProcTags[5].ti_Data = FALSE;
219: NewProcTags[6].ti_Data = TRUE;
220: } else {
221: NewProcTags[3].ti_Data = ChildPipe;
222: NewProcTags[4].ti_Data = Output ();
223: NewProcTags[5].ti_Data = TRUE;
224: NewProcTags[6].ti_Data = FALSE;
225: }
226: if (ChildPipe == NULL || ParentPipe == NULL) {
227: errno = EPIPE;
228: goto cleanup;
229: }
230:
231: /* Now generate a entry in the pipe file descriptor table. */
232: PipeToUse->pfd_Msg.sm_Cmd = malloc (strlen (command) + 1);
233: if (PipeToUse->pfd_Msg.sm_Cmd == NULL) {
234: errno = ENOMEM;
235: goto cleanup;
236: }
237: strcpy (PipeToUse->pfd_Msg.sm_Cmd, command);
238: PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort = CreateMsgPort ();
239: if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL) {
240: errno = ENOMEM;
241: goto cleanup;
242: }
243: PipeToUse->pfd_Msg.sm_Msg.mn_Node.ln_Type = NT_MESSAGE;
244: PipeToUse->pfd_Msg.sm_Msg.mn_Node.ln_Pri = 0;
245: PipeToUse->pfd_Msg.sm_Msg.mn_Length = sizeof (struct StartupMessage);
246: PipeToUse->pfd_File = ParentPipe;
247:
248: /* Now create the child process. */
249: ChildProcess = CreateNewProc (NewProcTags);
250: if (ChildProcess == NULL) {
251: errno = ENOMEM;
252: goto cleanup;
253: }
254:
255: /* Pass the startup message to the child process. */
256: PutMsg (&ChildProcess->pr_MsgPort, (struct Message *) &PipeToUse->pfd_Msg);
257:
258: /* This is the normal exit point for the function. */
259: return ParentPipe;
260:
261: /* This code is only executed if there was an error. In this case the
262: * allocated resources must be freed. The code is actually clearer (at
263: * least in my opinion) and more concise by using goto than by using a
264: * function (global variables or function parameters needed) or a lot
265: * of if-constructions (code gets blown up unnecessarily).
266: */
267: cleanup:
268: if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL)
269: DeleteMsgPort (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort);
270: if (ParentPipe)
271: fclose (ParentPipe);
272: if (ChildPipe)
273: Close (ChildPipe);
274: return NULL;
275: }
276:
277:
278: int pclose (stream)
279: FILE *stream;
280: {
281: LONG PipeToClose;
282:
283: /* Test whether we're using the right Dos version. */
284: if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
285: errno = EPIPE;
286: return -1;
287: }
288:
289: /* Test whether this is the first call to this module or not. If so,
290: * pclose has been called before popen and we return with an error
291: * because the initialisation has yet to be done.
292: */
293: if (FirstCall) {
294: errno = EBADF;
295: return -1;
296: }
297:
298: /* Search for the correct table entry and close the associated file. */
299: for (PipeToClose = 0; PipeToClose < MAX_OPEN_PIPES; PipeToClose++)
300: if (OpenPipes[PipeToClose].pfd_File == stream) break;
301: if (PipeToClose >= MAX_OPEN_PIPES) {
302: errno = EBADF;
303: return -1;
304: }
305: fclose (stream);
306:
307: /* Now wait for the child to terminate and get its exit status. */
308: WaitPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
309: OpenPipes[PipeToClose].pfd_File = NULL;
310:
311: /* Free the allocates resources. */
312: DeleteMsgPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
313: free (OpenPipes[PipeToClose].pfd_Msg.sm_Cmd);
314:
315: return OpenPipes[PipeToClose].pfd_Msg.sm_RetCode;
316: }
317:
318:
319: static void CleanUpPipes ()
320: {
321: LONG Count;
322: FILE *Pipe;
323:
324: /* Close each open pipe. */
325: for (Count = 0; Count < MAX_OPEN_PIPES; Count++) {
326: Pipe = OpenPipes[Count].pfd_File;
327: if (Pipe != NULL)
328: pclose (Pipe);
329: }
330: }
331:
332:
333: static int __saveds ChildEntry ()
334: {
335: struct Process *ChildProc;
336: struct StartupMessage *StartUpMessage;
337: LONG ReturnCode;
338: struct DosLibrary *DOSBase;
339: struct TagItem SysTags[3] = {
340: {SYS_Asynch, FALSE},
341: {SYS_UserShell, TRUE},
342: {TAG_DONE, 0}
343: };
344:
345: /* We need to open this library, because we don't inherit it from our
346: * parent process.
347: */
348: DOSBase = (struct DosLibrary *) OpenLibrary ("dos.library", DOS_VERSION);
349:
350: /* Get the childs process structure. */
351: ChildProc = (struct Process *) FindTask (NULL);
352:
353: /* Wait for the startup message from the parent. */
354: WaitPort (&ChildProc->pr_MsgPort);
355: StartUpMessage = (struct StartupMessage *) GetMsg (&ChildProc->pr_MsgPort);
356:
357: /* Now run the command and return the result. */
358: if (DOSBase != NULL)
359: ReturnCode = System (StartUpMessage->sm_Cmd, SysTags);
360: else
361: ReturnCode = 10000;
362: StartUpMessage->sm_RetCode = ReturnCode;
363:
364: /* Tell the parent that we are done. */
365: ReplyMsg ((struct Message *) StartUpMessage);
366:
367: if (DOSBase)
368: CloseLibrary ((struct Library *) DOSBase);
369:
370: return 0;
371: }
372:
373: #endif /* PIPES */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>