-
Notifications
You must be signed in to change notification settings - Fork 17
/
tccmdutl.cpp
363 lines (311 loc) · 11.8 KB
/
tccmdutl.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/*
* Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
tccmdutl.cpp - TADS 3 Compiler command-line parsing utilities
Function
Defines some utility functions for command-line parsing.
Notes
Modified
04/03/02 MJRoberts - Creation
*/
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "os.h"
#include "t3std.h"
#include "tccmdutl.h"
/* ------------------------------------------------------------------------ */
/*
* Get an option argument. We take a argument vector, the index of the
* vector entry containing the option whose argument we're to fetch, and
* the length of the option string. We'll return a pointer to the option
* string, or null if no argument is present.
*
* We'll look for the argument's option first in the same vector entry
* containing the option, appended directly to the option string; if
* there's nothing there, we'll look for the argument in the next vector
* entry. This lets us find option arguments that use syntax like
* "-Itest" or "-I test".
*/
char *CTcCommandUtil::get_opt_arg(int argc, char **argv,
int *curarg, int optlen)
{
/*
* if it's jammed up against the option letter, get it from the
* current array entry; otherwise, get it from the next array entry
*/
if (argv[*curarg][optlen + 1] != '\0')
{
/* it's attached to the same option entry */
return argv[*curarg] + optlen + 1;
}
else if (*curarg + 1 < argc)
{
/* it's by itself as the next option entry */
++(*curarg);
return argv[*curarg];
}
else
{
/* it's not present at all */
return 0;
}
}
/* ------------------------------------------------------------------------ */
/*
* Simple option helper implementation for counting arguments. Some
* callers will want to let us scan an option file to get an argument count
* before they allocate space for the arguments. On these count-only
* passes, the caller doesn't usually care about the contents of the file,
* so they don't want to bother performing the full set of operations that
* the helper normally defines. For these cases, callers can pass us a
* null helper, in which case we'll use this simple helper by default.
*/
class CTcOptFileHelperDefault: public CTcOptFileHelper
{
public:
/* allocate an option string */
virtual char *alloc_opt_file_str(size_t len)
{
return (char *)t3malloc(len + 1);
}
/* free a string allocated with alloc_opt_file_str() */
virtual void free_opt_file_str(char *str)
{
t3free(str);
}
/* process a comment line */
virtual void process_comment_line(const char *) { }
/* process a non-comment line */
virtual void process_non_comment_line(const char *) { }
/* process a configuration line */
virtual void process_config_line(const char *, const char *, int) { }
};
/* ------------------------------------------------------------------------ */
/*
* Parse an options file. If argv is null, we'll simply count the
* arguments and return the count. If argv is non-null, it must be
* allocated with the proper number of slots for the number of arguments
* in the file (as obtained from a call to this function with argv = null).
*
* Each string returned in argv[] is separately allocated with a call to
* helper->alloc_opt_file_str().
*/
int CTcCommandUtil::parse_opt_file(osfildef *fp, char **argv,
CTcOptFileHelper *helper)
{
char *buf;
char config_id[128];
size_t buflen;
int argc;
CTcOptFileHelperDefault default_helper;
/* if they didn't give us a helper object, use our default */
if (helper == 0)
helper = &default_helper;
/* allocate our initial buffer */
buflen = 512;
buf = helper->alloc_opt_file_str(buflen);
/* we're not in a configuration section yet */
config_id[0] = '\0';
/* keep going until we run out of text in the file */
for (argc = 0 ; ; )
{
size_t len;
char *p;
/* read the next line - stop if we're done */
if (osfgets(buf, buflen, fp) == 0)
break;
/* check for proper termination */
for (;;)
{
/* get the buffer length */
len = strlen(buf);
/* check for a newline of some kind at the end */
if (len != 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
{
/*
* remove consecutive newline/return characters - in case
* we are using a file moved from another system with
* incompatible newline conventions, simply remove all of
* the newlines we find
*/
while (len != 0
&& (buf[len-1] == '\n' || buf[len - 1] == '\r'))
--len;
/* null-terminate the line */
buf[len] = '\0';
/* we now have our line */
break;
}
else if (len == buflen - 1)
{
char *new_buf;
size_t new_buf_len;
/*
* The buffer was completely filled, and wasn't
* null-terminated - the line must be too long for the
* buffer. Expand the buffer and go back for more.
*/
new_buf_len = buflen + 512;
new_buf = helper->alloc_opt_file_str(new_buf_len);
/* copy the old buffer into the new buffer */
memcpy(new_buf, buf, buflen);
/* discard the old buffer */
helper->free_opt_file_str(buf);
/* switch to the new buffer */
buf = new_buf;
buflen = new_buf_len;
/* append more data into the line buffer and try again */
if (osfgets(buf + len, buflen - len, fp) == 0)
break;
}
else
{
/*
* The buffer wasn't completely full, and we didn't find a
* newline. This must mean that we've reached the end of
* the file, so we've read as much as we can for this
* line.
*/
break;
}
}
/* skip leading spaces */
for (p = buf ; isspace(*p) ; ++p) ;
/*
* Check to see if we're entering a new configuration section. If
* the line matches the pattern "[Config:xxx]", then we're starting
* a new configuration section with identifier "xxx".
*/
if (strlen(p) > 8 && memicmp(p, "[config:", 8) == 0)
{
char *start;
size_t copy_len;
/*
* note the part after the colon and up to the closing bracket
* - it's the ID of this configuration section
*/
for (p += 8, start = p ; *p != ']' && *p != '\0' ; ++p) ;
/* limit the ID size to our buffer maximum */
copy_len = p - start;
if (copy_len > sizeof(config_id) - 1)
copy_len = sizeof(config_id) - 1;
/* copy and null-terminate the ID string */
memcpy(config_id, start, copy_len);
config_id[copy_len] = '\0';
/* process the configuration ID line itself as a config line */
helper->process_config_line(config_id, buf, TRUE);
/* we're done processing this line */
continue;
}
/*
* if we're in a configuration section, simply process the line
* through the helper as a configuration line - this doesn't
* contain any options we can parse, since configuration sections
* are private to their respective definers
*/
if (config_id[0] != '\0')
{
/* process the configuration line through the helper */
helper->process_config_line(config_id, buf, FALSE);
/* we're done with this line */
continue;
}
/*
* If we've reached the end of the line or a command character
* ('#'), we're done with this line. Note that we check for
* comments AFTER checking to see if we're in a config section,
* because we want to send any comment lines within a config
* section to the helper for processing as part of the config -
* even comments are opaque to us within a config section.
*/
if (*p == '\0' || *p == '#')
{
/* tell the helper about the comment */
helper->process_comment_line(buf);
/* we're done with the line - go back for the next one */
continue;
}
/* tell the helper about the non-comment line */
helper->process_non_comment_line(buf);
/* parse the options on this line */
for (p = buf ; *p != '\0' ; )
{
char *start;
size_t arg_len;
/* skip leading spaces */
for ( ; isspace(*p) ; ++p) ;
/* check to see if we reached the end of the line */
if (*p == '\0')
break;
/*
* if the argument is quoted, find the matching quote;
* otherwise, look for the next space
*/
if (*p == '"')
{
char *dst;
/* find the matching quote */
for (++p, start = dst = p ; *p != '\0' ; ++p)
{
/*
* if this is a quote, check for stuttering - if it's
* stuttered, treat it as a single quote (and convert
* it to same), otherwise we've reached the end of the
* argument
*/
if (*p == '"')
{
/* check for stuttering */
if (*(p+1) == '"')
{
/*
* it's stuttered - skip the extra quote, so
* that we copy only a single quote
*/
++p;
}
else
{
/* this is our closing quote - skip it */
++p;
/* we're done with the argument */
break;
}
}
/* copy this character to the output */
*dst++ = *p;
}
/* note the length of the argument */
arg_len = dst - start;
}
else
{
/* find the next space */
for (start = p ; *p != '\0' && !isspace(*p) ; ++p) ;
/* note the length of the argument */
arg_len = p - start;
}
/* store the argument if we have an output vector */
if (argv != 0)
{
/* allocate the argument */
argv[argc] = helper->alloc_opt_file_str(arg_len + 1);
/* copy it into the result */
memcpy(argv[argc], start, arg_len);
argv[argc][arg_len] = '\0';
}
/* count the argument */
++argc;
}
}
/* done with our line buffer - delete it */
helper->free_opt_file_str(buf);
/* return the argument count */
return argc;
}