]> git.sur5r.net Git - cc65/blob - src/common/cmdline.c
New condes type interruptor
[cc65] / src / common / cmdline.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 cmdline.c                                 */
4 /*                                                                           */
5 /*                 Helper functions for command line parsing                 */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2000     Ullrich von Bassewitz                                        */
10 /*              Wacholderweg 14                                              */
11 /*              D-70597 Stuttgart                                            */
12 /* EMail:       uz@musoftware.de                                             */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39
40 /* common */
41 #include "abend.h"
42 #include "chartype.h"
43 #include "fname.h"
44 #include "xmalloc.h"
45 #include "cmdline.h"
46
47
48
49 /*****************************************************************************/
50 /*                                   Data                                    */
51 /*****************************************************************************/
52
53
54
55 /* Program name - is set after call to InitCmdLine */
56 const char* ProgName;
57
58 /* The program argument vector */
59 char** ArgVec     = 0;
60 unsigned ArgCount = 0;
61
62 /* Struct to pass the command line */
63 typedef struct {
64     char**      Vec;            /* The argument vector */
65     unsigned    Count;          /* Actual number of arguments */
66     unsigned    Size;           /* Number of argument allocated */
67 } CmdLine;
68
69
70
71 /*****************************************************************************/
72 /*                             Helper functions                              */
73 /*****************************************************************************/
74
75
76
77 static void NewCmdLine (CmdLine* L)
78 /* Initialize a CmdLine struct */
79 {
80     /* Initialize the struct */
81     L->Size    = 8;
82     L->Count   = 0;
83     L->Vec     = xmalloc (L->Size * sizeof (L->Vec[0]));
84 }
85
86
87
88 static void AddArg (CmdLine* L, char* Arg)
89 /* Add one argument to the list */
90 {
91     if (L->Size <= L->Count) {
92         /* No space left, reallocate */
93         unsigned NewSize = L->Size * 2;
94         char**   NewVec  = xmalloc (NewSize * sizeof (L->Vec[0]));
95         memcpy (NewVec, L->Vec, L->Count * sizeof (L->Vec[0]));
96         xfree (L->Vec);
97         L->Vec  = NewVec;
98         L->Size = NewSize;
99     }
100
101     /* We have space left, add a copy of the argument */
102     L->Vec[L->Count++] = Arg;
103 }
104
105     
106
107 static void ExpandFile (CmdLine* L, const char* Name)
108 /* Add the contents of a file to the command line. Each line is a separate
109  * argument with leading and trailing whitespace removed.
110  */
111 {
112     char Buf [256];
113
114     /* Try to open the file for reading */
115     FILE* F = fopen (Name, "r");
116     if (F == 0) {
117         AbEnd ("Cannot open \"%s\": %s", Name, strerror (errno));
118     }
119
120     /* File is open, read all lines */
121     while (fgets (Buf, sizeof (Buf), F) != 0) {
122
123         /* Get a pointer to the buffer */
124         const char* B = Buf;
125
126         /* Skip trailing whitespace (this will also kill the newline that is
127          * appended by fgets().
128          */
129         unsigned Len = strlen (Buf);
130         while (Len > 0 && IsSpace (Buf [Len-1])) {
131             --Len;
132         }
133         Buf [Len] = '\0';
134
135         /* Skip leading spaces */
136         while (IsSpace (*B)) {
137             ++B;
138         }
139
140         /* Skip empty lines to work around problems with some editors */
141         if (*B == '\0') {
142             continue;
143         }
144
145         /* Add anything not empty to the command line */
146         AddArg (L, xstrdup (B));
147
148     }
149
150     /* Close the file, ignore errors here since we had the file open for
151      * reading only.
152      */
153     (void) fclose (F);
154 }
155
156
157
158 /*****************************************************************************/
159 /*                                   Code                                    */
160 /*****************************************************************************/
161
162
163
164 void InitCmdLine (int* aArgCount, char** aArgVec[], const char* aProgName)
165 /* Initialize command line parsing. aArgVec is the argument array terminated by
166  * a NULL pointer (as usual), ArgCount is the number of valid arguments in the
167  * array. Both arguments are remembered in static storage.
168  */
169 {
170     CmdLine     L;
171     int         I;
172
173     /* Get the program name from argv[0] but strip a path */
174     if (*(aArgVec)[0] == 0) {
175         /* Use the default name given */
176         ProgName = aProgName;
177     } else {
178         /* Strip a path */
179         ProgName = FindName ((*aArgVec)[0]);
180         if (ProgName[0] == '\0') {
181             /* Use the default */
182             ProgName = aProgName;
183         }
184     }
185
186     /* Make a CmdLine struct */
187     NewCmdLine (&L);
188
189     /* Walk over the parameters and add them to the CmdLine struct. Add a
190      * special handling for arguments preceeded by the '@' sign - these are
191      * actually files containing arguments.
192      */
193     for (I = 0; I < *aArgCount; ++I) {
194
195         /* Get the next argument */
196         char* Arg = (*aArgVec)[I];
197
198         /* Is this a file argument? */
199         if (Arg && Arg[0] == '@') {
200
201             /* Expand the file */
202             ExpandFile (&L, Arg+1);
203
204         } else {
205
206             /* No file, just add a copy */
207             AddArg (&L, Arg);
208
209         }
210     }
211
212     /* Store the new argument list in a safe place... */
213     ArgCount = L.Count;
214     ArgVec   = L.Vec;
215
216     /* ...and pass back the changed data also */
217     *aArgCount = L.Count;
218     *aArgVec   = L.Vec;
219 }
220
221
222
223 void UnknownOption (const char* Opt)
224 /* Print an error about an unknown option and die. */
225 {
226     AbEnd ("Unknown option: %s", Opt);
227 }
228
229
230
231 void NeedArg (const char* Opt)
232 /* Print an error about a missing option argument and exit. */
233 {
234     AbEnd ("Option requires an argument: %s", Opt);
235 }
236
237
238
239 void InvDef (const char* Def)
240 /* Print an error about an invalid definition and die */
241 {
242     AbEnd ("Invalid definition: `%s'", Def);
243 }
244
245
246
247 const char* GetArg (unsigned* ArgNum, unsigned Len)
248 /* Get an argument for a short option. The argument may be appended to the
249  * option itself or may be separate. Len is the length of the option string.
250  */
251 {
252     const char* Arg = ArgVec[*ArgNum];
253     if (Arg[Len] != '\0') {
254         /* Argument appended */
255         return Arg + Len;
256     } else {
257         /* Separate argument */
258         Arg = ArgVec[*ArgNum + 1];
259         if (Arg == 0) {
260             /* End of arguments */
261             NeedArg (ArgVec[*ArgNum]);
262         }
263         ++(*ArgNum);
264         return Arg;
265     }
266 }
267
268
269
270 void LongOption (unsigned* ArgNum, const LongOpt* OptTab, unsigned OptCount)
271 /* Handle a long command line option */
272 {
273     /* Get the option and the argument (which may be zero) */
274     const char* Opt = ArgVec[*ArgNum];
275
276     /* Search the table for a match */
277     while (OptCount) {
278         if (strcmp (Opt, OptTab->Option) == 0) {
279             /* Found, call the function */
280             if (OptTab->ArgCount > 0) {
281                 /* We need an argument, check if we have one */
282                 const char* Arg = ArgVec[++(*ArgNum)];
283                 if (Arg == 0) {
284                     NeedArg (Opt);
285                 }
286                 OptTab->Func (Opt, Arg);
287             } else {
288                 OptTab->Func (Opt, 0);
289             }
290             /* Done */
291             return;
292         }
293
294         /* Next table entry */
295         --OptCount;
296         ++OptTab;
297     }
298
299     /* Invalid option */
300     UnknownOption (Opt);
301 }
302
303
304
305