1 /*
2 * Copyright (c) 1999-2002, Darren Hiebert
3 *
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
6 *
7 * This module contains functions for reading command line arguments.
8 */
9
10 /*
11 * INCLUDE FILES
12 */
13 #include "general.h" /* must always come first */
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 #include "args_p.h"
20 #include "debug.h"
21 #include "routines.h"
22 #include "vstring.h"
23
24 /*
25 * FUNCTION DEFINITIONS
26 */
27
nextStringArg(const char ** const next)28 static char *nextStringArg (const char** const next)
29 {
30 char* result = NULL;
31 const char* start;
32
33 Assert (*next != NULL);
34 for (start = *next ; isspace ((int) *start) ; ++start)
35 ;
36 if (*start == '\0')
37 *next = start;
38 else
39 {
40 size_t length;
41 const char* end;
42
43 for (end = start ; *end != '\0' && ! isspace ((int) *end) ; ++end)
44 ;
45 length = end - start;
46 Assert (length > 0);
47 result = xMalloc (length + 1, char);
48 strncpy (result, start, length);
49 result [length] = '\0';
50 *next = end;
51 }
52 return result;
53 }
54
nextStringLine(const char ** const next)55 static char* nextStringLine (const char** const next)
56 {
57 char* result = NULL;
58 size_t length;
59 const char* end;
60
61 Assert (*next != NULL);
62 for (end = *next ; *end != '\n' && *end != '\0' ; ++end)
63 ;
64 length = end - *next;
65 if (length > 0)
66 {
67 result = xMalloc (length + 1, char);
68 strncpy (result, *next, length);
69 result [length] = '\0';
70 }
71 if (*end == '\n')
72 ++end;
73 else if (*end == '\r')
74 {
75 ++end;
76 if (*end == '\n')
77 ++end;
78 }
79 *next = end;
80 return result;
81 }
82
nextString(const Arguments * const current,const char ** const next)83 static char* nextString (const Arguments* const current, const char** const next)
84 {
85 char* result;
86 if (current->lineMode)
87 result = nextStringLine (next);
88 else
89 result = nextStringArg (next);
90 return result;
91 }
92
nextFileArg(FILE * const fp)93 static char* nextFileArg (FILE* const fp)
94 {
95 char* result = NULL;
96 Assert (fp != NULL);
97 if (! feof (fp))
98 {
99 vString* vs = vStringNew ();
100 int c;
101 do
102 c = fgetc (fp);
103 while (isspace (c));
104
105 if (c != EOF)
106 {
107 do
108 {
109 vStringPut (vs, c);
110 c = fgetc (fp);
111 } while (c != EOF && ! isspace (c));
112 Assert (vStringLength (vs) > 0);
113 result = xMalloc (vStringLength (vs) + 1, char);
114 strcpy (result, vStringValue (vs));
115 }
116 vStringDelete (vs);
117 }
118 return result;
119 }
120
nextFileLine(FILE * const fp)121 static char* nextFileLine (FILE* const fp)
122 {
123 char* result = NULL;
124 Assert (fp != NULL);
125 if (! feof (fp))
126 {
127 vString* vs = vStringNew ();
128 int c;
129
130 c = fgetc (fp);
131 while (c != EOF)
132 {
133 if (c != '\n' && c != '\r')
134 vStringPut (vs, c);
135 else if (vStringLength (vs) > 0)
136 break;
137 c = fgetc (fp);
138 }
139 if (c != EOF || vStringLength (vs) > 0)
140 {
141 if (c == '\r')
142 {
143 c = fgetc (fp);
144 if (c != '\n')
145 c = ungetc (c, fp);
146 }
147 vStringStripTrailing (vs);
148 vStringStripLeading (vs);
149 result = xMalloc (vStringLength (vs) + 1, char);
150 strcpy (result, vStringValue (vs));
151 }
152 vStringDelete (vs);
153 }
154 return result;
155 }
156
isCommentLine(char * line)157 static bool isCommentLine (char* line)
158 {
159 while (isspace(*line))
160 ++line;
161 return (*line == '#');
162 }
163
isOptscriptLine(char * line)164 static bool isOptscriptLine (char *line)
165 {
166 size_t len = strlen (line);
167 if (len < 2)
168 return false;
169 if (line [len - 1] == '{' && line [len - 2] == '{')
170 return true;
171 return false;
172 }
173
nextOptscriptLines(FILE * const fp,char * line)174 static char* nextOptscriptLines (FILE* const fp, char *line)
175 {
176 vString *vstr = vStringNewInit (line);
177 vStringPut (vstr, '\n');
178 eFree (line);
179
180 /* \n}}, \n=>1, }=>2, }=>3 */
181 int endMarkers = 0;
182 int c;
183 while (true)
184 {
185 c = fgetc (fp);
186 if (c == EOF)
187 break;
188
189 if (c == '\r' || c == '\n')
190 {
191 if (c == '\r')
192 {
193 c = fgetc (fp);
194 if (c != '\n')
195 {
196 ungetc(c, fp);
197 c = '\n';
198 }
199 }
200 if (c == '\n')
201 {
202 vStringPut (vstr, c);
203 if (endMarkers != 1)
204 endMarkers = 1;
205 }
206 }
207 else if (c == '}')
208 {
209 vStringPut (vstr, c);
210 if (endMarkers == 1 || endMarkers == 2)
211 endMarkers++;
212 if (endMarkers == 3)
213 break;
214 }
215 else
216 {
217 endMarkers = 0;
218 vStringPut (vstr, c);
219 }
220 }
221
222 if (c == EOF)
223 {
224 switch (endMarkers)
225 {
226 case 0:
227 vStringPut (vstr, '\n');
228 /* Fall through */
229 case 1:
230 vStringPut (vstr, '}');
231 /* Fall through */
232 case 2:
233 vStringPut (vstr, '}');
234 default:
235 break;
236 }
237 }
238
239 c = fgetc (fp);
240 while (c != EOF)
241 {
242 if (c == '\n')
243 break;
244 if (c == '\r')
245 {
246 c = fgetc (fp);
247 if (c == '\n')
248 break;
249 ungetc (c, fp);
250 }
251 c = fgetc (fp);
252 }
253 return vStringDeleteUnwrap (vstr);
254 }
255
nextFileLineSkippingComments(FILE * const fp)256 static char* nextFileLineSkippingComments (FILE* const fp)
257 {
258 char* result;
259 bool comment;
260 bool optscript;
261
262 do
263 {
264 result = nextFileLine (fp);
265 comment = false;
266 optscript = false;
267 if (result)
268 {
269 comment = isCommentLine (result);
270 optscript = isOptscriptLine (result);
271 }
272 if (comment)
273 eFree (result);
274 else if (optscript)
275 result = nextOptscriptLines (fp, result);
276 } while (comment);
277
278 return result;
279 }
280
nextFileString(const Arguments * const current,FILE * const fp)281 static char* nextFileString (const Arguments* const current, FILE* const fp)
282 {
283 char* result;
284 if (current->lineMode)
285 result = nextFileLineSkippingComments (fp);
286 else
287 result = nextFileArg (fp);
288 return result;
289 }
290
argNewFromString(const char * const string)291 extern Arguments* argNewFromString (const char* const string)
292 {
293 Arguments* result = xMalloc (1, Arguments);
294 memset (result, 0, sizeof (Arguments));
295 result->type = ARG_STRING;
296 result->u.stringArgs.next = string;
297 result->item = nextString (result, &result->u.stringArgs.next);
298 return result;
299 }
300
argNewFromArgv(char * const * const argv)301 extern Arguments* argNewFromArgv (char* const* const argv)
302 {
303 Arguments* result = xMalloc (1, Arguments);
304 memset (result, 0, sizeof (Arguments));
305 result->type = ARG_ARGV;
306 result->u.argvArgs.argv = argv;
307 result->u.argvArgs.item = result->u.argvArgs.argv;
308 result->item = *result->u.argvArgs.item;
309 return result;
310 }
311
argNewFromFile(FILE * const fp)312 extern Arguments* argNewFromFile (FILE* const fp)
313 {
314 Arguments* result = xMalloc (1, Arguments);
315 memset (result, 0, sizeof (Arguments));
316 result->type = ARG_FILE;
317 result->u.fileArgs.fp = fp;
318 result->item = nextFileString (result, result->u.fileArgs.fp);
319 return result;
320 }
321
argNewFromLineFile(FILE * const fp)322 extern Arguments* argNewFromLineFile (FILE* const fp)
323 {
324 Arguments* result = xMalloc (1, Arguments);
325 memset (result, 0, sizeof (Arguments));
326 result->type = ARG_FILE;
327 result->lineMode = true;
328 result->u.fileArgs.fp = fp;
329 result->item = nextFileString (result, result->u.fileArgs.fp);
330 return result;
331 }
332
argItem(const Arguments * const current)333 extern char *argItem (const Arguments* const current)
334 {
335 Assert (current != NULL);
336 Assert (! argOff (current));
337 return current->item;
338 }
339
argOff(const Arguments * const current)340 extern bool argOff (const Arguments* const current)
341 {
342 Assert (current != NULL);
343 return (bool) (current->item == NULL);
344 }
345
argSetWordMode(Arguments * const current)346 extern void argSetWordMode (Arguments* const current)
347 {
348 Assert (current != NULL);
349 current->lineMode = false;
350 }
351
argSetLineMode(Arguments * const current)352 extern void argSetLineMode (Arguments* const current)
353 {
354 Assert (current != NULL);
355 current->lineMode = true;
356 }
357
argForth(Arguments * const current)358 extern void argForth (Arguments* const current)
359 {
360 Assert (current != NULL);
361 Assert (! argOff (current));
362 switch (current->type)
363 {
364 case ARG_STRING:
365 if (current->item != NULL)
366 eFree (current->item);
367 current->item = nextString (current, ¤t->u.stringArgs.next);
368 break;
369 case ARG_ARGV:
370 ++current->u.argvArgs.item;
371 current->item = *current->u.argvArgs.item;
372 break;
373 case ARG_FILE:
374 if (current->item != NULL)
375 eFree (current->item);
376 current->item = nextFileString (current, current->u.fileArgs.fp);
377 break;
378 default:
379 Assert ("Invalid argument type" == NULL);
380 break;
381 }
382 }
383
argDelete(Arguments * const current)384 extern void argDelete (Arguments* const current)
385 {
386 Assert (current != NULL);
387 if ((current->type == ARG_STRING
388 || current->type == ARG_FILE) && current->item != NULL)
389 eFree (current->item);
390 memset (current, 0, sizeof (Arguments));
391 eFree (current);
392 }
393