[BACK]Return to amiga.c CVS log [TXT][DIR] Up to [local] / OpenXM_contrib / gnuplot

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>