]>
Commit | Line | Data |
---|---|---|
997358a6 MW |
1 | /* |
2 | * pick up more options from a file, in the middle of an option scan | |
3 | * Copyright (C) 1998, 1999 Henry Spencer. | |
4 | * | |
5 | * This library is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU Library General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or (at your | |
8 | * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>. | |
9 | * | |
10 | * This library is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | |
13 | * License for more details. | |
14 | * | |
15 | * RCSID $Id: optionsfrom.c,v 1.1 2004/03/15 20:35:26 as Exp $ | |
16 | */ | |
17 | #include "internal.h" | |
18 | #include "freeswan.h" | |
19 | ||
20 | #include <stdio.h> | |
21 | ||
22 | #define MAX 100 /* loop-detection limit */ | |
23 | ||
24 | /* internal work area */ | |
25 | struct work { | |
26 | # define LOTS 1024 | |
27 | char buf[LOTS]; | |
28 | char *line; | |
29 | char *pending; | |
30 | }; | |
31 | ||
32 | static const char *dowork(const char *, int *, char ***, int); | |
33 | static const char *getanarg(FILE *, struct work *, char **); | |
34 | static char *getline(FILE *, char *, size_t); | |
35 | ||
36 | /* | |
37 | - optionsfrom - add some options, taken from a file, to argc/argv | |
38 | * If errsto is non-NULL, does not return in event of error. | |
39 | */ | |
40 | const char * /* NULL for success, else string literal */ | |
41 | optionsfrom(filename, argcp, argvp, optind, errsto) | |
42 | const char *filename; | |
43 | int *argcp; /* pointer to argc */ | |
44 | char ***argvp; /* pointer to argv */ | |
45 | int optind; /* current optind, number of next argument */ | |
46 | FILE *errsto; /* where to report errors (NULL means return) */ | |
47 | { | |
48 | const char *e; | |
49 | static int nuses = 0; | |
50 | ||
51 | if (errsto != NULL) { | |
52 | nuses++; | |
53 | if (nuses >= MAX) { | |
54 | fprintf(errsto, | |
55 | "%s: optionsfrom called %d times, looping?\n", | |
56 | (*argvp)[0], nuses); | |
57 | exit(2); | |
58 | } | |
59 | } else | |
60 | nuses = 0; | |
61 | ||
62 | e = dowork(filename, argcp, argvp, optind); | |
63 | if (e != NULL && errsto != NULL) { | |
64 | fprintf(errsto, "%s: optionsfrom failed: %s\n", (*argvp)[0], e); | |
65 | exit(2); | |
66 | } | |
67 | return e; | |
68 | } | |
69 | ||
70 | /* | |
71 | - dowork - do all the real work of optionsfrom | |
72 | * Does not alter the existing arguments, but does relocate and alter | |
73 | * the argv pointer vector. | |
74 | */ | |
75 | static const char * /* NULL for success, else string literal */ | |
76 | dowork(filename, argcp, argvp, optind) | |
77 | const char *filename; | |
78 | int *argcp; /* pointer to argc */ | |
79 | char ***argvp; /* pointer to argv */ | |
80 | int optind; /* current optind, number of next argument */ | |
81 | { | |
82 | char **newargv; | |
83 | char **tmp; | |
84 | int newargc; | |
85 | int next; /* place for next argument */ | |
86 | int room; /* how many more new arguments we can hold */ | |
87 | # define SOME 10 /* first guess at how many we'll need */ | |
88 | FILE *f; | |
89 | int i; | |
90 | const char *p; | |
91 | struct work wa; /* for getanarg() */ | |
92 | ||
93 | f = fopen(filename, "r"); | |
94 | if (f == NULL) | |
95 | return "unable to open file"; | |
96 | ||
97 | newargc = *argcp + SOME; | |
98 | newargv = malloc((newargc+1) * sizeof(char *)); | |
99 | if (newargv == NULL) | |
100 | return "unable to allocate memory"; | |
101 | memcpy(newargv, *argvp, optind * sizeof(char *)); | |
102 | room = SOME; | |
103 | next = optind; | |
104 | ||
105 | newargv[next] = NULL; | |
106 | wa.pending = NULL; | |
107 | while ((p = getanarg(f, &wa, &newargv[next])) == NULL) { | |
108 | if (room == 0) { | |
109 | newargc += SOME; | |
110 | tmp = realloc(newargv, (newargc+1) * sizeof(char *)); | |
111 | if (tmp == NULL) { | |
112 | p = "out of space for new argv"; | |
113 | break; /* NOTE BREAK OUT */ | |
114 | } | |
115 | newargv = tmp; | |
116 | room += SOME; | |
117 | } | |
118 | next++; | |
119 | room--; | |
120 | } | |
121 | if (p != NULL && !feof(f)) { /* error of some kind */ | |
122 | for (i = optind+1; i <= next; i++) | |
123 | if (newargv[i] != NULL) | |
124 | free(newargv[i]); | |
125 | free(newargv); | |
126 | fclose(f); | |
127 | return p; | |
128 | } | |
129 | ||
130 | fclose(f); | |
131 | memcpy(newargv + next, *argvp + optind, | |
132 | (*argcp+1-optind) * sizeof(char *)); | |
133 | *argcp += next - optind; | |
134 | *argvp = newargv; | |
135 | return NULL; | |
136 | } | |
137 | ||
138 | /* | |
139 | - getanarg - get a malloced argument from the file | |
140 | */ | |
141 | static const char * /* NULL for success, else string literal */ | |
142 | getanarg(f, w, linep) | |
143 | FILE *f; | |
144 | struct work *w; | |
145 | char **linep; /* where to store pointer if successful */ | |
146 | { | |
147 | size_t len; | |
148 | char *p; | |
149 | char *endp; | |
150 | ||
151 | while (w->pending == NULL) { /* no pending line */ | |
152 | if ((w->line = getline(f, w->buf, sizeof(w->buf))) == NULL) | |
153 | return "error in line read"; /* caller checks EOF */ | |
154 | if (w->line[0] != '#' && | |
155 | *(w->line + strspn(w->line, " \t")) != '\0') | |
156 | w->pending = w->line; | |
157 | } | |
158 | ||
159 | if (w->pending == w->line && w->line[0] != '-') { | |
160 | /* fresh plain line */ | |
161 | w->pending = NULL; | |
162 | p = w->line; | |
163 | endp = p + strlen(p); | |
164 | if (*p == '"' && endp > p+1 && *(endp-1) == '"') { | |
165 | p++; | |
166 | endp--; | |
167 | *endp = '\0'; | |
168 | } | |
169 | if (w->line == w->buf) { | |
170 | *linep = malloc(endp - p + 1); | |
171 | if (*linep == NULL) | |
172 | return "out of memory for new line"; | |
173 | strcpy(*linep, p); | |
174 | } else /* getline already malloced it */ | |
175 | *linep = p; | |
176 | return NULL; | |
177 | } | |
178 | ||
179 | /* chip off a piece of a pending line */ | |
180 | p = w->pending; | |
181 | p += strspn(p, " \t"); | |
182 | endp = p + strcspn(p, " \t"); | |
183 | len = endp - p; | |
184 | if (*endp != '\0') { | |
185 | *endp++ = '\0'; | |
186 | endp += strspn(endp, " \t"); | |
187 | } | |
188 | /* endp now points to next real character, or to line-end NUL */ | |
189 | *linep = malloc(len + 1); | |
190 | if (*linep == NULL) { | |
191 | if (w->line != w->buf) | |
192 | free(w->line); | |
193 | return "out of memory for new argument"; | |
194 | } | |
195 | strcpy(*linep, p); | |
196 | if (*endp == '\0') { | |
197 | w->pending = NULL; | |
198 | if (w->line != w->buf) | |
199 | free(w->line); | |
200 | } else | |
201 | w->pending = endp; | |
202 | return NULL; | |
203 | } | |
204 | ||
205 | /* | |
206 | - getline - read a line from the file, trim newline off | |
207 | */ | |
208 | static char * /* pointer to line, NULL for eof/error */ | |
209 | getline(f, buf, bufsize) | |
210 | FILE *f; | |
211 | char *buf; /* buffer to use, if convenient */ | |
212 | size_t bufsize; /* size of buf */ | |
213 | { | |
214 | size_t len; | |
215 | ||
216 | if (fgets(buf, bufsize, f) == NULL) | |
217 | return NULL; | |
218 | len = strlen(buf); | |
219 | ||
220 | if (len < bufsize-1 || buf[bufsize-1] == '\n') { | |
221 | /* it fit */ | |
222 | buf[len-1] = '\0'; | |
223 | return buf; | |
224 | } | |
225 | ||
226 | /* oh crud, buffer overflow */ | |
227 | /* for now, to hell with it */ | |
228 | return NULL; | |
229 | } | |
230 | ||
231 | ||
232 | ||
233 | #ifdef TEST | |
234 | ||
235 | #include <getopt.h> | |
236 | ||
237 | char usage[] = "Usage: tester [--foo] [--bar] [--optionsfrom file] arg ..."; | |
238 | struct option opts[] = { | |
239 | "foo", 0, NULL, 'f', | |
240 | "bar", 0, NULL, 'b', | |
241 | "builtin", 0, NULL, 'B', | |
242 | "optionsfrom", 1, NULL, '+', | |
243 | "help", 0, NULL, 'h', | |
244 | "version", 0, NULL, 'v', | |
245 | 0, 0, NULL, 0, | |
246 | }; | |
247 | ||
248 | int | |
249 | main(argc, argv) | |
250 | int argc; | |
251 | char *argv[]; | |
252 | { | |
253 | int opt; | |
254 | extern char *optarg; | |
255 | extern int optind; | |
256 | int errflg = 0; | |
257 | const char *p; | |
258 | int i; | |
259 | FILE *errs = NULL; | |
260 | ||
261 | while ((opt = getopt_long(argc, argv, "", opts, NULL)) != EOF) | |
262 | switch (opt) { | |
263 | case 'f': | |
264 | case 'b': | |
265 | break; | |
266 | case 'B': | |
267 | errs = stderr; | |
268 | break; | |
269 | case '+': /* optionsfrom */ | |
270 | p = optionsfrom(optarg, &argc, &argv, optind, errs); | |
271 | if (p != NULL) { | |
272 | fprintf(stderr, "%s: optionsfrom error: %s\n", | |
273 | argv[0], p); | |
274 | exit(1); | |
275 | } | |
276 | break; | |
277 | case 'h': /* help */ | |
278 | printf("%s\n", usage); | |
279 | exit(0); | |
280 | break; | |
281 | case 'v': /* version */ | |
282 | printf("1\n"); | |
283 | exit(0); | |
284 | break; | |
285 | case '?': | |
286 | default: | |
287 | errflg = 1; | |
288 | break; | |
289 | } | |
290 | if (errflg) { | |
291 | fprintf(stderr, "%s\n", usage); | |
292 | exit(2); | |
293 | } | |
294 | ||
295 | for (i = 1; i < argc; i++) | |
296 | printf("%d: `%s'\n", i, argv[i]); | |
297 | exit(0); | |
298 | } | |
299 | ||
300 | ||
301 | #endif /* TEST */ |