]> git.sur5r.net Git - cc65/blobdiff - src/common/cmdline.c
Moved some of the currently existing into a separate module.
[cc65] / src / common / cmdline.c
index 3d4ac8cb17e567f3b178ae7f63de85d4bf8c9300..58be881dbd6de95cbdd3e689ca16e5538bb6331d 100644 (file)
 
 
 #include <stdio.h>
-#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
 
+/* common */
+#include "abend.h"
+#include "chartype.h"
+#include "xmalloc.h"
 #include "cmdline.h"
 
 
 
 
 
-static char** ArgVec     = 0;
-static unsigned ArgCount = 0;
+/* Program name - is set after call to InitCmdLine */
+const char* ProgName;
+
+/* The program argument vector */
+char** ArgVec     = 0;
+unsigned ArgCount = 0;
+
+/* Struct to pass the command line */
+typedef struct {
+    char**             Vec;            /* The argument vector */
+    unsigned           Count;          /* Actual number of arguments */
+    unsigned           Size;           /* Number of argument allocated */
+} CmdLine;
 
 
 
 /*****************************************************************************/
-/*                                          Code                                    */
+/*                            Helper functions                              */
 /*****************************************************************************/
 
 
 
-void InitCmdLine (unsigned aArgCount, char* aArgVec[])
+static void NewCmdLine (CmdLine* L)
+/* Initialize a CmdLine struct */
+{
+    unsigned I;
+
+    /* Initialize the struct */
+    L->Size    = 8;
+    L->Count   = 0;
+    L->Vec     = xmalloc (L->Size * sizeof (L->Vec[0]));
+
+    /* Copy the arguments. We have to allocate them on free store, otherwise
+     * we would have to keep track which one is on free store and which not,
+     * which is a lot more overhead.
+     */
+    for (I = 0; I < L->Count; ++I) {
+       L->Vec[I] = xstrdup (ArgVec[I]);
+    }
+}
+
+
+
+static void AddArg (CmdLine* L, const char* Arg)
+/* Add one argument to the list */
+{
+    if (L->Size <= L->Count) {
+       /* No space left, reallocate */
+       unsigned NewSize = L->Size * 2;
+       char**   NewVec  = xmalloc (NewSize * sizeof (L->Vec[0]));
+       memcpy (NewVec, L->Vec, L->Count * sizeof (L->Vec[0]));
+       xfree (L->Vec);
+       L->Vec  = NewVec;
+       L->Size = NewSize;
+    }
+
+    /* We have space left, add a copy of the argument */
+    L->Vec [L->Count++] = xstrdup (Arg);
+}
+
+
+
+static void ExpandFile (CmdLine* L, const char* Name)
+/* Add the contents of a file to the command line. Each line is a separate
+ * argument with leading and trailing whitespace removed.
+ */
+{
+    char Buf [256];
+
+    /* Try to open the file for reading */
+    FILE* F = fopen (Name, "r");
+    if (F == 0) {
+       AbEnd ("Cannot open \"%s\": %s", Name, strerror (errno));
+    }
+
+    /* File is open, read all lines */
+    while (fgets (Buf, sizeof (Buf), F) != 0) {
+
+       /* Get a pointer to the buffer */
+       const char* B = Buf;
+
+       /* Skip trailing whitespace (this will also kill the newline that is
+        * appended by fgets().
+        */
+       unsigned Len = strlen (Buf);
+       while (Len > 0 && IsSpace (Buf [Len-1])) {
+           --Len;
+       }
+       Buf [Len] = '\0';
+
+       /* Skip leading spaces */
+       while (IsSpace (*B)) {
+           ++B;
+       }
+
+       /* Skip empty lines to work around problems with some editors */
+       if (*B == '\0') {
+           continue;
+       }
+
+       /* Add anything not empty to the command line */
+       AddArg (L, B);
+
+    }
+
+    /* Close the file, ignore errors here since we had the file open for
+     * reading only.
+     */
+    (void) fclose (F);
+}
+
+
+
+/*****************************************************************************/
+/*                                          Code                                    */
+/*****************************************************************************/
+
+
+
+void InitCmdLine (int* aArgCount, char** aArgVec[], const char* aProgName)
 /* Initialize command line parsing. aArgVec is the argument array terminated by
  * a NULL pointer (as usual), ArgCount is the number of valid arguments in the
  * array. Both arguments are remembered in static storage.
  */
 {
-    ArgCount = aArgCount;
-    ArgVec   = aArgVec;
+    CmdLine    L;
+    unsigned   I;
+
+    /* Get the program name from argv[0] but strip a path */
+    if (*(aArgVec)[0] == 0) {
+       /* Use the default name given */
+       ProgName = aProgName;
+    } else {
+       /* Strip a path */
+       const char* FirstArg = (*aArgVec)[0];
+               ProgName = strchr (FirstArg, '\0');
+       while (ProgName > FirstArg) {
+           --ProgName;
+                   if (*ProgName == '/' || *ProgName == '\\') {
+               ++ProgName;
+               break;
+           }
+       }
+       if (ProgName[0] == '\0') {
+           /* Use the default */
+           ProgName = aProgName;
+       }
+    }
+
+    /* Make a CmdLine struct */
+    NewCmdLine (&L);
+
+    /* Walk over the parameters and add them to the CmdLine struct. Add a
+     * special handling for arguments preceeded by the '@' sign - these are
+     * actually files containing arguments.
+     */
+    for (I = 0; I < *aArgCount; ++I) {
+
+       /* Get the next argument */
+       char* Arg = (*aArgVec)[I];
+
+       /* Is this a file argument? */
+       if (Arg && Arg[0] == '@') {
+
+           /* Expand the file */
+           ExpandFile (&L, Arg+1);
+
+       } else {
+
+           /* No file, just add a copy */
+           AddArg (&L, Arg);
+
+       }
+    }
+
+    /* Store the new argument list in a safe place... */
+    ArgCount = L.Count;
+    ArgVec   = L.Vec;
+
+    /* ...and pass back the changed data also */
+    *aArgCount = L.Count;
+    *aArgVec   = L.Vec;
 }
 
 
 
 void UnknownOption (const char* Opt)
-/* Print an error about an unknown option. */
+/* Print an error about an unknown option and die. */
 {
-    fprintf (stderr, "Unknown option: %s\n", Opt);
-    exit (EXIT_FAILURE);
+    AbEnd ("Unknown option: %s\n", Opt);
 }
 
 
@@ -81,17 +248,15 @@ void UnknownOption (const char* Opt)
 void NeedArg (const char* Opt)
 /* Print an error about a missing option argument and exit. */
 {
-    fprintf (stderr, "Option requires an argument: %s\n", Opt);
-    exit (EXIT_FAILURE);
+    AbEnd ("Option requires an argument: %s\n", Opt);
 }
 
 
 
-void InvSym (const char* Def)
-/* Print an error about an invalid symbol definition and die */
+void InvDef (const char* Def)
+/* Print an error about an invalid definition and die */
 {
-    fprintf (stderr, "Invalid symbol definition: `%s'\n", Def);
-    exit (EXIT_FAILURE);
+    AbEnd ("Invalid definition: `%s'\n", Def);
 }
 
 
@@ -130,9 +295,14 @@ void LongOption (int* ArgNum, const LongOpt* OptTab, unsigned OptCount)
        if (strcmp (Opt, OptTab->Option) == 0) {
            /* Found, call the function */
            if (OptTab->ArgCount > 0) {
-               OptTab->Func (Opt, ArgVec[++(*ArgNum)]);
+               /* We need an argument, check if we have one */
+               const char* Arg = ArgVec[++(*ArgNum)];
+               if (Arg == 0) {
+                   NeedArg (Opt);
+               }
+               OptTab->Func (Opt, Arg);
            } else {
-               OptTab->Func (Opt, 0);
+               OptTab->Func (Opt, 0);
            }
            /* Done */
            return;