]> git.sur5r.net Git - cc65/blob - src/common/cmdline.c
Fixed a signedness problem
[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 "xmalloc.h"
44 #include "cmdline.h"
45
46
47
48 /*****************************************************************************/
49 /*                                   Data                                    */
50 /*****************************************************************************/
51
52
53
54 /* Program name - is set after call to InitCmdLine */
55 const char* ProgName;
56
57 /* The program argument vector */
58 char** ArgVec     = 0;
59 unsigned ArgCount = 0;
60
61 /* Struct to pass the command line */
62 typedef struct {
63     char**      Vec;            /* The argument vector */
64     unsigned    Count;          /* Actual number of arguments */
65     unsigned    Size;           /* Number of argument allocated */
66 } CmdLine;
67
68
69
70 /*****************************************************************************/
71 /*                             Helper functions                              */
72 /*****************************************************************************/
73
74
75
76 static void NewCmdLine (CmdLine* L)
77 /* Initialize a CmdLine struct */
78 {
79     unsigned I;
80
81     /* Initialize the struct */
82     L->Size    = 8;
83     L->Count   = 0;
84     L->Vec     = xmalloc (L->Size * sizeof (L->Vec[0]));
85
86     /* Copy the arguments. We have to allocate them on free store, otherwise
87      * we would have to keep track which one is on free store and which not,
88      * which is a lot more overhead.
89      */
90     for (I = 0; I < L->Count; ++I) {
91         L->Vec[I] = xstrdup (ArgVec[I]);
92     }
93 }
94
95
96
97 static void AddArg (CmdLine* L, const char* Arg)
98 /* Add one argument to the list */
99 {
100     if (L->Size <= L->Count) {
101         /* No space left, reallocate */
102         unsigned NewSize = L->Size * 2;
103         char**   NewVec  = xmalloc (NewSize * sizeof (L->Vec[0]));
104         memcpy (NewVec, L->Vec, L->Count * sizeof (L->Vec[0]));
105         xfree (L->Vec);
106         L->Vec  = NewVec;
107         L->Size = NewSize;
108     }
109
110     /* We have space left, add a copy of the argument */
111     L->Vec [L->Count++] = xstrdup (Arg);
112 }
113
114
115
116 static void ExpandFile (CmdLine* L, const char* Name)
117 /* Add the contents of a file to the command line. Each line is a separate
118  * argument with leading and trailing whitespace removed.
119  */
120 {
121     char Buf [256];
122
123     /* Try to open the file for reading */
124     FILE* F = fopen (Name, "r");
125     if (F == 0) {
126         AbEnd ("Cannot open \"%s\": %s", Name, strerror (errno));
127     }
128
129     /* File is open, read all lines */
130     while (fgets (Buf, sizeof (Buf), F) != 0) {
131
132         /* Get a pointer to the buffer */
133         const char* B = Buf;
134
135         /* Skip trailing whitespace (this will also kill the newline that is
136          * appended by fgets().
137          */
138         unsigned Len = strlen (Buf);
139         while (Len > 0 && IsSpace (Buf [Len-1])) {
140             --Len;
141         }
142         Buf [Len] = '\0';
143
144         /* Skip leading spaces */
145         while (IsSpace (*B)) {
146             ++B;
147         }
148
149         /* Skip empty lines to work around problems with some editors */
150         if (*B == '\0') {
151             continue;
152         }
153
154         /* Add anything not empty to the command line */
155         AddArg (L, B);
156
157     }
158
159     /* Close the file, ignore errors here since we had the file open for
160      * reading only.
161      */
162     (void) fclose (F);
163 }
164
165
166
167 /*****************************************************************************/
168 /*                                   Code                                    */
169 /*****************************************************************************/
170
171
172
173 void InitCmdLine (unsigned* aArgCount, char** aArgVec[], const char* aProgName)
174 /* Initialize command line parsing. aArgVec is the argument array terminated by
175  * a NULL pointer (as usual), ArgCount is the number of valid arguments in the
176  * array. Both arguments are remembered in static storage.
177  */
178 {
179     CmdLine     L;
180     unsigned    I;
181
182     /* Get the program name from argv[0] but strip a path */
183     if (*(aArgVec)[0] == 0) {
184         /* Use the default name given */
185         ProgName = aProgName;
186     } else {
187         /* Strip a path */
188         const char* FirstArg = (*aArgVec)[0];
189         ProgName = strchr (FirstArg, '\0');
190         while (ProgName > FirstArg) {
191             --ProgName;
192             if (*ProgName == '/' || *ProgName == '\\') {
193                 ++ProgName;
194                 break;
195             }
196         }
197         if (ProgName[0] == '\0') {
198             /* Use the default */
199             ProgName = aProgName;
200         }
201     }
202
203     /* Make a CmdLine struct */
204     NewCmdLine (&L);
205
206     /* Walk over the parameters and add them to the CmdLine struct. Add a
207      * special handling for arguments preceeded by the '@' sign - these are
208      * actually files containing arguments.
209      */
210     for (I = 0; I < *aArgCount; ++I) {
211
212         /* Get the next argument */
213         char* Arg = (*aArgVec)[I];
214
215         /* Is this a file argument? */
216         if (Arg && Arg[0] == '@') {
217
218             /* Expand the file */
219             ExpandFile (&L, Arg+1);
220
221         } else {
222
223             /* No file, just add a copy */
224             AddArg (&L, Arg);
225
226         }
227     }
228
229     /* Store the new argument list in a safe place... */
230     ArgCount = L.Count;
231     ArgVec   = L.Vec;
232
233     /* ...and pass back the changed data also */
234     *aArgCount = L.Count;
235     *aArgVec   = L.Vec;
236 }
237
238
239
240 void UnknownOption (const char* Opt)
241 /* Print an error about an unknown option and die. */
242 {
243     AbEnd ("Unknown option: %s\n", Opt);
244 }
245
246
247
248 void NeedArg (const char* Opt)
249 /* Print an error about a missing option argument and exit. */
250 {
251     AbEnd ("Option requires an argument: %s\n", Opt);
252 }
253
254
255
256 void InvDef (const char* Def)
257 /* Print an error about an invalid definition and die */
258 {
259     AbEnd ("Invalid definition: `%s'\n", Def);
260 }
261
262
263
264 const char* GetArg (int* ArgNum, unsigned Len)
265 /* Get an argument for a short option. The argument may be appended to the
266  * option itself or may be separate. Len is the length of the option string.
267  */
268 {
269     const char* Arg = ArgVec[*ArgNum];
270     if (Arg[Len] != '\0') {
271         /* Argument appended */
272         return Arg + Len;
273     } else {
274         /* Separate argument */
275         Arg = ArgVec[*ArgNum + 1];
276         if (Arg == 0) {
277             /* End of arguments */
278             NeedArg (ArgVec[*ArgNum]);
279         }
280         ++(*ArgNum);
281         return Arg;
282     }
283 }
284
285
286
287 void LongOption (int* ArgNum, const LongOpt* OptTab, unsigned OptCount)
288 /* Handle a long command line option */
289 {
290     /* Get the option and the argument (which may be zero) */
291     const char* Opt = ArgVec[*ArgNum];
292
293     /* Search the table for a match */
294     while (OptCount) {
295         if (strcmp (Opt, OptTab->Option) == 0) {
296             /* Found, call the function */
297             if (OptTab->ArgCount > 0) {
298                 /* We need an argument, check if we have one */
299                 const char* Arg = ArgVec[++(*ArgNum)];
300                 if (Arg == 0) {
301                     NeedArg (Opt);
302                 }
303                 OptTab->Func (Opt, Arg);
304             } else {
305                 OptTab->Func (Opt, 0);
306             }
307             /* Done */
308             return;
309         }
310
311         /* Next table entry */
312         --OptCount;
313         ++OptTab;
314     }
315
316     /* Invalid option */
317     UnknownOption (Opt);
318 }
319
320
321