X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcommon%2Fcmdline.c;h=58be881dbd6de95cbdd3e689ca16e5538bb6331d;hb=c9cb564b9bf5e91cef70cc7941f8f3465f0abc70;hp=3d4ac8cb17e567f3b178ae7f63de85d4bf8c9300;hpb=b9ce81c7e3da01500e1ee69bb5aaea2344b3b930;p=cc65 diff --git a/src/common/cmdline.c b/src/common/cmdline.c index 3d4ac8cb1..58be881db 100644 --- a/src/common/cmdline.c +++ b/src/common/cmdline.c @@ -34,8 +34,13 @@ #include -#include +#include +#include +/* common */ +#include "abend.h" +#include "chartype.h" +#include "xmalloc.h" #include "cmdline.h" @@ -46,34 +51,196 @@ -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;