Annotation of OpenXM_contrib/gnuplot/amiga.c, Revision 1.1
1.1 ! maekawa 1: /* $Id: amiga.c,v 1.3 1998/03/22 22:31:17 drd Exp $ */
! 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>