Initial release of Maemo 5 port of gnuplot
[gnuplot] / src / amiga.c
diff --git a/src/amiga.c b/src/amiga.c
new file mode 100644 (file)
index 0000000..e8ad5e9
--- /dev/null
@@ -0,0 +1,377 @@
+/* $Id: amiga.c,v 1.4 2004/07/01 17:10:03 broeker Exp $ */
+
+/* GNUPLOT - amiga.c */
+
+/*[
+ * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
+ *
+ * Permission to use, copy, and distribute this software and its
+ * documentation for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation.
+ *
+ * Permission to modify the software is granted, but not the right to
+ * distribute the complete modified source code.  Modifications are to
+ * be distributed as patches to the released version.  Permission to
+ * distribute binaries produced by compiling modified sources is granted,
+ * provided you
+ *   1. distribute the corresponding source modifications from the
+ *    released version in the form of a patch file along with the binaries,
+ *   2. add special version identification to distinguish your version
+ *    in addition to the base release version number,
+ *   3. provide your name and address as the primary contact for the
+ *    support of your modified version, and
+ *   4. retain our contact information in regard to use of the base
+ *    software.
+ * Permission to distribute the released version of the source code along
+ * with corresponding source modifications in the form of a patch file is
+ * granted with same provisions 2 through 4 for binary distributions.
+ *
+ * This software is provided "as is" without express or implied warranty
+ * to the extent permitted by applicable law.
+]*/
+
+/*
+ * amiga.c
+ *
+ * Written by Carsten Steger <stegerc@informatik.tu-muenchen.de>
+ *
+ * Popen and pclose have the same semantics as their UNIX counterparts.
+ *
+ * Additionally, they install an exit trap that closes all open pipes,
+ * should the program terminate abnormally.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <ios1.h>
+#include <error.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <exec/types.h>
+#include <dos/dos.h>
+#include <dos/dosextens.h>
+#include <dos/dostags.h>
+#include <proto/exec.h>
+#include <proto/dos.h>
+
+#ifdef PIPES  /* dont bother if pipes are not being used elsewhere */
+
+/* Maximum number of open pipes. If this is set to a number > 10, the code
+ * that constructs the pipe names in popen () will have to be modified.
+ */
+#define MAX_OPEN_PIPES 10
+
+/* We need at least this Dos version to work. */
+#define DOS_VERSION 37
+
+
+/* This data structure is sent to the child process with sm_Cmd set to the
+ * command to be executed. When the child is done it sets sm_RetCode to
+ * the return code of the executed command.
+ */
+struct StartupMessage {
+  struct Message sm_Msg;
+  LONG sm_RetCode;
+  UBYTE *sm_Cmd;
+};
+
+/* We keep track of the open pipe through this data structure. */
+struct PipeFileDescriptor {
+  FILE *pfd_File;
+  struct StartupMessage pfd_Msg;
+};
+
+
+/* Needed to check for the required Dos version. */
+extern struct DosLibrary *DOSBase;
+
+/* This data structure keeps track of the pipes that are still open. */
+static struct PipeFileDescriptor OpenPipes[MAX_OPEN_PIPES];
+
+/* The address of the process that calls popen or pclose. */
+static struct Process *ThisProcess;
+
+/* Are we called for the first time? */
+static LONG FirstCall = TRUE;
+
+
+/* Prototypes for the functions below. */
+FILE *popen (const char *command, const char *mode);
+int pclose (FILE *stream);
+static void CleanUpPipes (void);
+static int __saveds ChildEntry (void);
+
+
+FILE *
+popen (const char *command, const char *mode)
+{
+    UBYTE PipeName[16];
+    ULONG ProcAddress;
+    UBYTE HexDigit;
+    UBYTE *NextChar;
+    struct CommandLineInterface *ThisCli;
+    struct PipeFileDescriptor *PipeToUse;
+    LONG PipeNumToUse;
+    LONG ChildPipeMode;
+    BPTR ChildPipe;
+    FILE *ParentPipe;
+    struct Process *ChildProcess;
+    struct TagItem NewProcTags[8] = {
+       {NP_Entry, (Tag) ChildEntry},
+       {NP_Cli, TRUE},
+       {NP_StackSize, 4096},
+       {NP_Input, NULL},
+       {NP_Output, NULL},
+       {NP_CloseInput, FALSE},
+       {NP_CloseOutput, FALSE},
+       {TAG_DONE, 0}
+    };
+
+    /* Test whether we're using the right Dos version. */
+    if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
+       errno = EPIPE;
+       return NULL;
+    }
+
+    /* If we're called for the first time, install exit trap and do some
+     * initialisation stuff.
+     */
+    if (FirstCall) {
+       /* Initialise pipe file descriptor table. */
+       memset (OpenPipes, 0, sizeof (OpenPipes));
+
+       /* Install our exit trap. */
+       if (atexit (CleanUpPipes) != 0) {
+           errno = EPIPE;
+           return NULL;
+       }
+       FirstCall = FALSE;
+    }
+
+    /* If we don't know our process' address yet, we should get it now. */
+    if (ThisProcess == NULL)
+       ThisProcess = (struct Process *) FindTask (NULL);
+
+    /* Get our Cli structure. */
+    ThisCli = Cli ();
+
+    /* Now try to find an empty slot in the pipe file descriptor table.
+     * Return NULL if no slot is available.
+     */
+    for (PipeNumToUse = 0; PipeNumToUse < MAX_OPEN_PIPES; PipeNumToUse++)
+       if (OpenPipes[PipeNumToUse].pfd_File == NULL) break;
+    if (PipeNumToUse >= MAX_OPEN_PIPES) {
+       errno = EMFILE;
+       return NULL;
+    }
+    PipeToUse = &OpenPipes[PipeNumToUse];
+
+    /* Check if the specified mode is valid. */
+    if (strcmp (mode, "r") == 0)
+       ChildPipeMode = MODE_NEWFILE;
+    else if (strcmp (mode, "w") == 0)
+       ChildPipeMode = MODE_OLDFILE;
+    else {
+       errno = EINVAL;
+       return NULL;
+    }
+
+    /* Make a unique file name for the pipe that we are about to open. The
+     * file name has the following format: "PIPE:XXXXXXXX_Y", where
+     * XXXXXXXX is the address of our process in hex, Y is the number of the
+     * slot in the pipe descriptor table that we will use. The code is
+     * equivalent to
+     * sprintf (PipeNameWriter, "PIPE:%08lX_%1d", ThisProcess, PipeNumToUse);
+     * but it doesn't need sprintf and therefore makes programs that don't
+     * use printf a lot shorter.
+     */
+    strcpy (PipeName, "PIPE:00000000_0");
+    NextChar = PipeName + 12;
+    ProcAddress = (ULONG) ThisProcess;
+    while (ProcAddress != 0) {
+       HexDigit = (UBYTE) ProcAddress & 0xf;
+       HexDigit = HexDigit < 10 ? HexDigit + '0' : HexDigit - 10 + 'A';
+       *NextChar-- = HexDigit;
+       ProcAddress >>= 4;
+    }
+    /* If MAX_OPEN_PIPES > 10, this will have to be modified. */
+    PipeName[14] = ((UBYTE) PipeNumToUse) + '0';
+
+    /* Create tags for the child process. */
+    if (ThisProcess->pr_CLI)
+       NewProcTags[2].ti_Data = ThisCli->cli_DefaultStack << 2;
+    else
+       NewProcTags[2].ti_Data = ThisProcess->pr_StackSize;
+
+    /* Open both ends of the pipe. The child's side is opened with Open (),
+     * while the parent's side is opened with fopen ().
+     */
+    ChildPipe = Open (PipeName, ChildPipeMode);
+    ParentPipe = fopen (PipeName, mode);
+    if (ChildPipeMode == MODE_NEWFILE) {
+       NewProcTags[3].ti_Data = Input ();
+       NewProcTags[4].ti_Data = ChildPipe;
+       NewProcTags[5].ti_Data = FALSE;
+       NewProcTags[6].ti_Data = TRUE;
+    } else {
+       NewProcTags[3].ti_Data = ChildPipe;
+       NewProcTags[4].ti_Data = Output ();
+       NewProcTags[5].ti_Data = TRUE;
+       NewProcTags[6].ti_Data = FALSE;
+    }
+    if (ChildPipe == NULL || ParentPipe == NULL) {
+       errno = EPIPE;
+       goto cleanup;
+    }
+
+    /* Now generate a entry in the pipe file descriptor table. */
+    PipeToUse->pfd_Msg.sm_Cmd = malloc (strlen (command) + 1);
+    if (PipeToUse->pfd_Msg.sm_Cmd == NULL) {
+       errno = ENOMEM;
+       goto cleanup;
+    }
+    strcpy (PipeToUse->pfd_Msg.sm_Cmd, command);
+    PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort = CreateMsgPort ();
+    if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL) {
+       errno = ENOMEM;
+       goto cleanup;
+    }
+    PipeToUse->pfd_Msg.sm_Msg.mn_Node.ln_Type = NT_MESSAGE;
+    PipeToUse->pfd_Msg.sm_Msg.mn_Node.ln_Pri = 0;
+    PipeToUse->pfd_Msg.sm_Msg.mn_Length = sizeof (struct StartupMessage);
+    PipeToUse->pfd_File = ParentPipe;
+
+    /* Now create the child process. */
+    ChildProcess = CreateNewProc (NewProcTags);
+    if (ChildProcess == NULL) {
+       errno = ENOMEM;
+       goto cleanup;
+    }
+
+    /* Pass the startup message to the child process. */
+    PutMsg (&ChildProcess->pr_MsgPort, (struct Message *) &PipeToUse->pfd_Msg);
+
+    /* This is the normal exit point for the function. */
+    return ParentPipe;
+
+    /* This code is only executed if there was an error. In this case the
+     * allocated resources must be freed. The code is actually clearer (at
+     * least in my opinion) and more concise by using goto than by using a
+     * function (global variables or function parameters needed) or a lot
+     * of if-constructions (code gets blown up unnecessarily).
+     */
+  cleanup:
+    if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL)
+       DeleteMsgPort (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort);
+    if (ParentPipe)
+       fclose (ParentPipe);
+    if (ChildPipe)
+       Close (ChildPipe);
+    return NULL;
+}
+
+
+int
+pclose (FILE *stream)
+{
+  LONG PipeToClose;
+
+  /* Test whether we're using the right Dos version. */
+  if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
+    errno = EPIPE;
+    return -1;
+  }
+
+  /* Test whether this is the first call to this module or not. If so,
+   * pclose has been called before popen and we return with an error
+   * because the initialisation has yet to be done.
+   */
+  if (FirstCall) {
+    errno = EBADF;
+    return -1;
+  }
+
+  /* Search for the correct table entry and close the associated file. */
+  for (PipeToClose = 0; PipeToClose < MAX_OPEN_PIPES; PipeToClose++)
+    if (OpenPipes[PipeToClose].pfd_File == stream) break;
+  if (PipeToClose >= MAX_OPEN_PIPES) {
+    errno = EBADF;
+    return -1;
+  }
+  fclose (stream);
+
+  /* Now wait for the child to terminate and get its exit status. */
+  WaitPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
+  OpenPipes[PipeToClose].pfd_File = NULL;
+
+  /* Free the allocates resources. */
+  DeleteMsgPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
+  free (OpenPipes[PipeToClose].pfd_Msg.sm_Cmd);
+
+  return OpenPipes[PipeToClose].pfd_Msg.sm_RetCode;
+}
+
+
+static void
+CleanUpPipes ()
+{
+  LONG Count;
+  FILE *Pipe;
+
+  /* Close each open pipe. */
+  for (Count = 0; Count < MAX_OPEN_PIPES; Count++) {
+    Pipe = OpenPipes[Count].pfd_File;
+    if (Pipe != NULL)
+      pclose (Pipe);
+  }
+}
+
+
+static int __saveds
+ChildEntry ()
+{
+    struct Process *ChildProc;
+    struct StartupMessage *StartUpMessage;
+    LONG ReturnCode;
+    struct DosLibrary *DOSBase;
+    struct TagItem SysTags[3] = {
+       {SYS_Asynch, FALSE},
+       {SYS_UserShell, TRUE},
+       {TAG_DONE, 0}
+    };
+
+    /* We need to open this library, because we don't inherit it from our
+     * parent process.
+     */
+    DOSBase = (struct DosLibrary *) OpenLibrary ("dos.library", DOS_VERSION);
+
+    /* Get the childs process structure. */
+    ChildProc = (struct Process *) FindTask (NULL);
+
+    /* Wait for the startup message from the parent. */
+    WaitPort (&ChildProc->pr_MsgPort);
+    StartUpMessage = (struct StartupMessage *) GetMsg (&ChildProc->pr_MsgPort);
+
+    /* Now run the command and return the result. */
+    if (DOSBase != NULL)
+       ReturnCode = System (StartUpMessage->sm_Cmd, SysTags);
+    else
+       ReturnCode = 10000;
+    StartUpMessage->sm_RetCode = ReturnCode;
+
+    /* Tell the parent that we are done. */
+    ReplyMsg ((struct Message *) StartUpMessage);
+
+    if (DOSBase)
+       CloseLibrary ((struct Library *) DOSBase);
+
+    return 0;
+}
+
+#endif /* PIPES */