/src/ntp-dev/sntp/libopts/save.c
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | /* |
3 | | * \file save.c |
4 | | * |
5 | | * This module's routines will take the currently set options and |
6 | | * store them into an ".rc" file for re-interpretation the next |
7 | | * time the invoking program is run. |
8 | | * |
9 | | * @addtogroup autoopts |
10 | | * @{ |
11 | | */ |
12 | | /* |
13 | | * This file is part of AutoOpts, a companion to AutoGen. |
14 | | * AutoOpts is free software. |
15 | | * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved |
16 | | * |
17 | | * AutoOpts is available under any one of two licenses. The license |
18 | | * in use must be one of these two and the choice is under the control |
19 | | * of the user of the license. |
20 | | * |
21 | | * The GNU Lesser General Public License, version 3 or later |
22 | | * See the files "COPYING.lgplv3" and "COPYING.gplv3" |
23 | | * |
24 | | * The Modified Berkeley Software Distribution License |
25 | | * See the file "COPYING.mbsd" |
26 | | * |
27 | | * These files have the following sha256 sums: |
28 | | * |
29 | | * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 |
30 | | * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 |
31 | | * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd |
32 | | */ |
33 | | |
34 | | /* = = = START-STATIC-FORWARD = = = */ |
35 | | static char const * |
36 | | find_dir_name(tOptions * opts, int * p_free); |
37 | | |
38 | | static char const * |
39 | | find_file_name(tOptions * opts, int * p_free_name); |
40 | | |
41 | | static void |
42 | | prt_entry(FILE * fp, tOptDesc * od, char const * l_arg); |
43 | | |
44 | | static void |
45 | | prt_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp); |
46 | | |
47 | | static void |
48 | | prt_string(FILE * fp, char const * name, char const * pz); |
49 | | |
50 | | static void |
51 | | prt_val_list(FILE * fp, char const * name, tArgList * al); |
52 | | |
53 | | static void |
54 | | prt_nested(FILE * fp, tOptDesc * p); |
55 | | |
56 | | static FILE * |
57 | | open_sv_file(tOptions * opts); |
58 | | |
59 | | static void |
60 | | prt_no_arg_opt(FILE * fp, tOptDesc * p, tOptDesc * pOD); |
61 | | |
62 | | static void |
63 | | prt_str_arg(FILE * fp, tOptDesc * pOD); |
64 | | |
65 | | static void |
66 | | prt_enum_arg(FILE * fp, tOptDesc * od); |
67 | | |
68 | | static void |
69 | | prt_set_arg(FILE * fp, tOptDesc * od); |
70 | | |
71 | | static void |
72 | | prt_file_arg(FILE * fp, tOptDesc * od, tOptions * opts); |
73 | | /* = = = END-STATIC-FORWARD = = = */ |
74 | | |
75 | | /** |
76 | | */ |
77 | | static char const * |
78 | | find_dir_name(tOptions * opts, int * p_free) |
79 | 0 | { |
80 | 0 | char const * pzDir; |
81 | |
|
82 | 0 | if ( (opts->specOptIdx.save_opts == NO_EQUIVALENT) |
83 | 0 | || (opts->specOptIdx.save_opts == 0)) |
84 | 0 | return NULL; |
85 | | |
86 | 0 | pzDir = opts->pOptDesc[ opts->specOptIdx.save_opts ].optArg.argString; |
87 | 0 | if ((pzDir != NULL) && (*pzDir != NUL)) |
88 | 0 | return pzDir; |
89 | | |
90 | | /* |
91 | | * This function only works if there is a directory where |
92 | | * we can stash the RC (INI) file. |
93 | | */ |
94 | 0 | { |
95 | 0 | char const * const * papz = opts->papzHomeList; |
96 | 0 | if (papz == NULL) |
97 | 0 | return NULL; |
98 | | |
99 | 0 | while (papz[1] != NULL) papz++; |
100 | 0 | pzDir = *papz; |
101 | 0 | } |
102 | | |
103 | | /* |
104 | | * IF it does not require deciphering an env value, then just copy it |
105 | | */ |
106 | 0 | if (*pzDir != '$') |
107 | 0 | return pzDir; |
108 | | |
109 | 0 | { |
110 | 0 | char const * pzEndDir = strchr(++pzDir, DIRCH); |
111 | 0 | char * pzFileName; |
112 | 0 | char * pzEnv; |
113 | |
|
114 | 0 | if (pzEndDir != NULL) { |
115 | 0 | char z[ AO_NAME_SIZE ]; |
116 | 0 | if ((pzEndDir - pzDir) > AO_NAME_LIMIT ) |
117 | 0 | return NULL; |
118 | 0 | memcpy(z, pzDir, (size_t)(pzEndDir - pzDir)); |
119 | 0 | z[pzEndDir - pzDir] = NUL; |
120 | 0 | pzEnv = getenv(z); |
121 | 0 | } else { |
122 | | |
123 | | /* |
124 | | * Make sure we can get the env value (after stripping off |
125 | | * any trailing directory or file names) |
126 | | */ |
127 | 0 | pzEnv = getenv(pzDir); |
128 | 0 | } |
129 | | |
130 | 0 | if (pzEnv == NULL) { |
131 | 0 | fprintf(stderr, zsave_warn, opts->pzProgName); |
132 | 0 | fprintf(stderr, zNotDef, pzDir); |
133 | 0 | return NULL; |
134 | 0 | } |
135 | | |
136 | 0 | if (pzEndDir == NULL) |
137 | 0 | return pzEnv; |
138 | | |
139 | 0 | { |
140 | 0 | size_t sz = strlen(pzEnv) + strlen(pzEndDir) + 2; |
141 | 0 | pzFileName = (char *)AGALOC(sz, "dir name"); |
142 | 0 | } |
143 | |
|
144 | 0 | if (pzFileName == NULL) |
145 | 0 | return NULL; |
146 | | |
147 | 0 | *p_free = 1; |
148 | | /* |
149 | | * Glue together the full name into the allocated memory. |
150 | | * FIXME: We lose track of this memory. |
151 | | */ |
152 | 0 | sprintf(pzFileName, "%s/%s", pzEnv, pzEndDir); |
153 | 0 | return pzFileName; |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | /** |
158 | | */ |
159 | | static char const * |
160 | | find_file_name(tOptions * opts, int * p_free_name) |
161 | 0 | { |
162 | 0 | struct stat stBuf; |
163 | 0 | int free_dir_name = 0; |
164 | |
|
165 | 0 | char const * pzDir = find_dir_name(opts, &free_dir_name); |
166 | 0 | if (pzDir == NULL) |
167 | 0 | return NULL; |
168 | | |
169 | | /* |
170 | | * See if we can find the specified directory. We use a once-only loop |
171 | | * structure so we can bail out early. |
172 | | */ |
173 | 0 | if (stat(pzDir, &stBuf) != 0) do { |
174 | 0 | char z[AG_PATH_MAX]; |
175 | 0 | char * dirchp; |
176 | | |
177 | | /* |
178 | | * IF we could not, check to see if we got a full |
179 | | * path to a file name that has not been created yet. |
180 | | */ |
181 | 0 | if (errno != ENOENT) { |
182 | 0 | bogus_name: |
183 | 0 | fprintf(stderr, zsave_warn, opts->pzProgName); |
184 | 0 | fprintf(stderr, zNoStat, errno, strerror(errno), pzDir); |
185 | 0 | if (free_dir_name) |
186 | 0 | AGFREE(pzDir); |
187 | 0 | return NULL; |
188 | 0 | } |
189 | | |
190 | | /* |
191 | | * Strip off the last component, stat the remaining string and |
192 | | * that string must name a directory |
193 | | */ |
194 | 0 | dirchp = strrchr(pzDir, DIRCH); |
195 | 0 | if (dirchp == NULL) { |
196 | 0 | stBuf.st_mode = S_IFREG; |
197 | 0 | break; /* found directory -- viz., "." */ |
198 | 0 | } |
199 | | |
200 | 0 | if ((size_t)(dirchp - pzDir) >= sizeof(z)) |
201 | 0 | goto bogus_name; |
202 | | |
203 | 0 | memcpy(z, pzDir, (size_t)(dirchp - pzDir)); |
204 | 0 | z[dirchp - pzDir] = NUL; |
205 | |
|
206 | 0 | if ((stat(z, &stBuf) != 0) || ! S_ISDIR(stBuf.st_mode)) |
207 | 0 | goto bogus_name; |
208 | 0 | stBuf.st_mode = S_IFREG; /* file within this directory */ |
209 | 0 | } while (false); |
210 | | |
211 | | /* |
212 | | * IF what we found was a directory, |
213 | | * THEN tack on the config file name |
214 | | */ |
215 | 0 | if (S_ISDIR(stBuf.st_mode)) { |
216 | 0 | size_t sz = strlen(pzDir) + strlen(opts->pzRcName) + 2; |
217 | |
|
218 | 0 | { |
219 | 0 | char * pzPath = (char *)AGALOC(sz, "file name"); |
220 | 0 | #ifdef HAVE_SNPRINTF |
221 | 0 | snprintf(pzPath, sz, "%s/%s", pzDir, opts->pzRcName); |
222 | | #else |
223 | | sprintf(pzPath, "%s/%s", pzDir, opts->pzRcName); |
224 | | #endif |
225 | 0 | if (free_dir_name) |
226 | 0 | AGFREE(pzDir); |
227 | 0 | pzDir = pzPath; |
228 | 0 | free_dir_name = 1; |
229 | 0 | } |
230 | | |
231 | | /* |
232 | | * IF we cannot stat the object for any reason other than |
233 | | * it does not exist, then we bail out |
234 | | */ |
235 | 0 | if (stat(pzDir, &stBuf) != 0) { |
236 | 0 | if (errno != ENOENT) { |
237 | 0 | fprintf(stderr, zsave_warn, opts->pzProgName); |
238 | 0 | fprintf(stderr, zNoStat, errno, strerror(errno), |
239 | 0 | pzDir); |
240 | 0 | AGFREE(pzDir); |
241 | 0 | return NULL; |
242 | 0 | } |
243 | | |
244 | | /* |
245 | | * It does not exist yet, but it will be a regular file |
246 | | */ |
247 | 0 | stBuf.st_mode = S_IFREG; |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | | /* |
252 | | * Make sure that whatever we ultimately found, that it either is |
253 | | * or will soon be a file. |
254 | | */ |
255 | 0 | if (! S_ISREG(stBuf.st_mode)) { |
256 | 0 | fprintf(stderr, zsave_warn, opts->pzProgName, pzDir); |
257 | 0 | if (free_dir_name) |
258 | 0 | AGFREE(pzDir); |
259 | 0 | return NULL; |
260 | 0 | } |
261 | | |
262 | | /* |
263 | | * Get rid of the old file |
264 | | */ |
265 | 0 | unlink(pzDir); |
266 | 0 | *p_free_name = free_dir_name; |
267 | 0 | return pzDir; |
268 | 0 | } |
269 | | |
270 | | /** |
271 | | * print one option entry to the save file. |
272 | | * |
273 | | * @param[in] fp the file pointer for the save file |
274 | | * @param[in] od the option descriptor to print |
275 | | * @param[in] l_arg the last argument for the option |
276 | | */ |
277 | | static void |
278 | | prt_entry(FILE * fp, tOptDesc * od, char const * l_arg) |
279 | 0 | { |
280 | 0 | int space_ct; |
281 | | |
282 | | /* |
283 | | * There is an argument. Pad the name so values line up. |
284 | | * Not disabled *OR* this got equivalenced to another opt, |
285 | | * then use current option name. |
286 | | * Otherwise, there must be a disablement name. |
287 | | */ |
288 | 0 | { |
289 | 0 | char const * pz = |
290 | 0 | (! DISABLED_OPT(od) || (od->optEquivIndex != NO_EQUIVALENT)) |
291 | 0 | ? od->pz_Name |
292 | 0 | : od->pz_DisableName; |
293 | 0 | space_ct = 17 - strlen(pz); |
294 | 0 | fputs(pz, fp); |
295 | 0 | } |
296 | |
|
297 | 0 | if ( (l_arg == NULL) |
298 | 0 | && (OPTST_GET_ARGTYPE(od->fOptState) != OPARG_TYPE_NUMERIC)) |
299 | 0 | goto end_entry; |
300 | | |
301 | 0 | fputs(" = ", fp); |
302 | 0 | while (space_ct-- > 0) fputc(' ', fp); |
303 | | |
304 | | /* |
305 | | * IF the option is numeric only, |
306 | | * THEN the char pointer is really the number |
307 | | */ |
308 | 0 | if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NUMERIC) |
309 | 0 | fprintf(fp, "%d", (int)(intptr_t)l_arg); |
310 | | |
311 | 0 | else { |
312 | 0 | for (;;) { |
313 | 0 | char const * eol = strchr(l_arg, NL); |
314 | | |
315 | | /* |
316 | | * IF this is the last line |
317 | | * THEN bail and print it |
318 | | */ |
319 | 0 | if (eol == NULL) |
320 | 0 | break; |
321 | | |
322 | | /* |
323 | | * Print the continuation and the text from the current line |
324 | | */ |
325 | 0 | (void)fwrite(l_arg, (size_t)(eol - l_arg), (size_t)1, fp); |
326 | 0 | l_arg = eol+1; /* advance the Last Arg pointer */ |
327 | 0 | fputs("\\\n", fp); |
328 | 0 | } |
329 | | |
330 | | /* |
331 | | * Terminate the entry |
332 | | */ |
333 | 0 | fputs(l_arg, fp); |
334 | 0 | } |
335 | |
|
336 | 0 | end_entry: |
337 | 0 | fputc(NL, fp); |
338 | 0 | } |
339 | | |
340 | | /** |
341 | | */ |
342 | | static void |
343 | | prt_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp) |
344 | 0 | { |
345 | 0 | while (--depth >= 0) |
346 | 0 | putc(' ', fp), putc(' ', fp); |
347 | |
|
348 | 0 | switch (ovp->valType) { |
349 | 0 | default: |
350 | 0 | case OPARG_TYPE_NONE: |
351 | 0 | fprintf(fp, NULL_ATR_FMT, ovp->pzName); |
352 | 0 | break; |
353 | | |
354 | 0 | case OPARG_TYPE_STRING: |
355 | 0 | prt_string(fp, ovp->pzName, ovp->v.strVal); |
356 | 0 | break; |
357 | | |
358 | 0 | case OPARG_TYPE_ENUMERATION: |
359 | 0 | case OPARG_TYPE_MEMBERSHIP: |
360 | 0 | if (pOD != NULL) { |
361 | 0 | uint32_t opt_state = pOD->fOptState; |
362 | 0 | uintptr_t val = pOD->optArg.argEnum; |
363 | 0 | char const * typ = (ovp->valType == OPARG_TYPE_ENUMERATION) |
364 | 0 | ? "keyword" : "set-membership"; |
365 | |
|
366 | 0 | fprintf(fp, TYPE_ATR_FMT, ovp->pzName, typ); |
367 | | |
368 | | /* |
369 | | * This is a magic incantation that will convert the |
370 | | * bit flag values back into a string suitable for printing. |
371 | | */ |
372 | 0 | (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD ); |
373 | 0 | if (pOD->optArg.argString != NULL) { |
374 | 0 | fputs(pOD->optArg.argString, fp); |
375 | |
|
376 | 0 | if (ovp->valType != OPARG_TYPE_ENUMERATION) { |
377 | | /* |
378 | | * set membership strings get allocated |
379 | | */ |
380 | 0 | AGFREE(pOD->optArg.argString); |
381 | 0 | } |
382 | 0 | } |
383 | |
|
384 | 0 | pOD->optArg.argEnum = val; |
385 | 0 | pOD->fOptState = opt_state; |
386 | 0 | fprintf(fp, END_XML_FMT, ovp->pzName); |
387 | 0 | break; |
388 | 0 | } |
389 | | /* FALLTHROUGH */ |
390 | | |
391 | 0 | case OPARG_TYPE_NUMERIC: |
392 | 0 | fprintf(fp, NUMB_ATR_FMT, ovp->pzName, ovp->v.longVal); |
393 | 0 | break; |
394 | | |
395 | 0 | case OPARG_TYPE_BOOLEAN: |
396 | 0 | fprintf(fp, BOOL_ATR_FMT, ovp->pzName, |
397 | 0 | ovp->v.boolVal ? "true" : "false"); |
398 | 0 | break; |
399 | | |
400 | 0 | case OPARG_TYPE_HIERARCHY: |
401 | 0 | prt_val_list(fp, ovp->pzName, ovp->v.nestVal); |
402 | 0 | break; |
403 | 0 | } |
404 | 0 | } |
405 | | |
406 | | /** |
407 | | */ |
408 | | static void |
409 | | prt_string(FILE * fp, char const * name, char const * pz) |
410 | 0 | { |
411 | 0 | fprintf(fp, OPEN_XML_FMT, name); |
412 | 0 | for (;;) { |
413 | 0 | int ch = ((int)*(pz++)) & 0xFF; |
414 | |
|
415 | 0 | switch (ch) { |
416 | 0 | case NUL: goto string_done; |
417 | | |
418 | 0 | case '&': |
419 | 0 | case '<': |
420 | 0 | case '>': |
421 | 0 | #if __GNUC__ >= 4 |
422 | 0 | case 1 ... (' ' - 1): |
423 | 0 | case ('~' + 1) ... 0xFF: |
424 | 0 | #endif |
425 | 0 | emit_special_char(fp, ch); |
426 | 0 | break; |
427 | | |
428 | 0 | default: |
429 | | #if __GNUC__ < 4 |
430 | | if ( ((ch >= 1) && (ch <= (' ' - 1))) |
431 | | || ((ch >= ('~' + 1)) && (ch <= 0xFF)) ) { |
432 | | emit_special_char(fp, ch); |
433 | | break; |
434 | | } |
435 | | #endif |
436 | 0 | putc(ch, fp); |
437 | 0 | } |
438 | 0 | } string_done:; |
439 | 0 | fprintf(fp, END_XML_FMT, name); |
440 | 0 | } |
441 | | |
442 | | /** |
443 | | */ |
444 | | static void |
445 | | prt_val_list(FILE * fp, char const * name, tArgList * al) |
446 | 0 | { |
447 | 0 | static int depth = 1; |
448 | |
|
449 | 0 | int sp_ct; |
450 | 0 | int opt_ct; |
451 | 0 | void ** opt_list; |
452 | |
|
453 | 0 | if (al == NULL) |
454 | 0 | return; |
455 | 0 | opt_ct = al->useCt; |
456 | 0 | opt_list = (void **)al->apzArgs; |
457 | |
|
458 | 0 | if (opt_ct <= 0) { |
459 | 0 | fprintf(fp, OPEN_CLOSE_FMT, name); |
460 | 0 | return; |
461 | 0 | } |
462 | | |
463 | 0 | fprintf(fp, NESTED_OPT_FMT, name); |
464 | |
|
465 | 0 | depth++; |
466 | 0 | while (--opt_ct >= 0) { |
467 | 0 | tOptionValue const * ovp = *(opt_list++); |
468 | |
|
469 | 0 | prt_value(fp, depth, NULL, ovp); |
470 | 0 | } |
471 | 0 | depth--; |
472 | |
|
473 | 0 | for (sp_ct = depth; --sp_ct >= 0;) |
474 | 0 | putc(' ', fp), putc(' ', fp); |
475 | 0 | fprintf(fp, "</%s>\n", name); |
476 | 0 | } |
477 | | |
478 | | /** |
479 | | */ |
480 | | static void |
481 | | prt_nested(FILE * fp, tOptDesc * p) |
482 | 0 | { |
483 | 0 | int opt_ct; |
484 | 0 | tArgList * al = p->optCookie; |
485 | 0 | void ** opt_list; |
486 | |
|
487 | 0 | if (al == NULL) |
488 | 0 | return; |
489 | | |
490 | 0 | opt_ct = al->useCt; |
491 | 0 | opt_list = (void **)al->apzArgs; |
492 | |
|
493 | 0 | if (opt_ct <= 0) |
494 | 0 | return; |
495 | | |
496 | 0 | do { |
497 | 0 | tOptionValue const * base = *(opt_list++); |
498 | 0 | tOptionValue const * ovp = optionGetValue(base, NULL); |
499 | |
|
500 | 0 | if (ovp == NULL) |
501 | 0 | continue; |
502 | | |
503 | 0 | fprintf(fp, NESTED_OPT_FMT, p->pz_Name); |
504 | |
|
505 | 0 | do { |
506 | 0 | prt_value(fp, 1, p, ovp); |
507 | |
|
508 | 0 | } while (ovp = optionNextValue(base, ovp), |
509 | 0 | ovp != NULL); |
510 | |
|
511 | 0 | fprintf(fp, "</%s>\n", p->pz_Name); |
512 | 0 | } while (--opt_ct > 0); |
513 | 0 | } |
514 | | |
515 | | /** |
516 | | * open the file for saving option state. |
517 | | * |
518 | | * @param[in] opts the program options structure |
519 | | * @returns the open file pointer. It may be NULL. |
520 | | */ |
521 | | static FILE * |
522 | | open_sv_file(tOptions * opts) |
523 | 0 | { |
524 | 0 | FILE * fp; |
525 | |
|
526 | 0 | { |
527 | 0 | int free_name = 0; |
528 | 0 | char const * pzFName = find_file_name(opts, &free_name); |
529 | 0 | if (pzFName == NULL) |
530 | 0 | return NULL; |
531 | | |
532 | 0 | fp = fopen(pzFName, "w" FOPEN_BINARY_FLAG); |
533 | 0 | if (fp == NULL) { |
534 | 0 | fprintf(stderr, zsave_warn, opts->pzProgName); |
535 | 0 | fprintf(stderr, zNoCreat, errno, strerror(errno), pzFName); |
536 | 0 | if (free_name) |
537 | 0 | AGFREE(pzFName); |
538 | 0 | return fp; |
539 | 0 | } |
540 | | |
541 | 0 | if (free_name) |
542 | 0 | AGFREE(pzFName); |
543 | 0 | } |
544 | | |
545 | 0 | fputs("# ", fp); |
546 | 0 | { |
547 | 0 | char const * e = strchr(opts->pzUsageTitle, NL); |
548 | 0 | if (e++ != NULL) |
549 | 0 | fwrite(opts->pzUsageTitle, 1, e - opts->pzUsageTitle, fp); |
550 | 0 | } |
551 | |
|
552 | 0 | { |
553 | 0 | time_t cur_time = time(NULL); |
554 | 0 | char * time_str = ctime(&cur_time); |
555 | |
|
556 | 0 | fprintf(fp, zPresetFile, time_str); |
557 | | #ifdef HAVE_ALLOCATED_CTIME |
558 | | /* |
559 | | * The return values for ctime(), localtime(), and gmtime() |
560 | | * normally point to static data that is overwritten by each call. |
561 | | * The test to detect allocated ctime, so we leak the memory. |
562 | | */ |
563 | | AGFREE(time_str); |
564 | | #endif |
565 | 0 | } |
566 | |
|
567 | 0 | return fp; |
568 | 0 | } |
569 | | |
570 | | /** |
571 | | */ |
572 | | static void |
573 | | prt_no_arg_opt(FILE * fp, tOptDesc * p, tOptDesc * pOD) |
574 | 0 | { |
575 | | /* |
576 | | * The aliased to argument indicates whether or not the option |
577 | | * is "disabled". However, the original option has the name |
578 | | * string, so we get that there, not with "p". |
579 | | */ |
580 | 0 | char const * pznm = |
581 | 0 | (DISABLED_OPT(p)) ? pOD->pz_DisableName : pOD->pz_Name; |
582 | | /* |
583 | | * If the option was disabled and the disablement name is NULL, |
584 | | * then the disablement was caused by aliasing. |
585 | | * Use the name as the string to emit. |
586 | | */ |
587 | 0 | if (pznm == NULL) |
588 | 0 | pznm = pOD->pz_Name; |
589 | |
|
590 | 0 | fprintf(fp, "%s\n", pznm); |
591 | 0 | } |
592 | | |
593 | | /** |
594 | | */ |
595 | | static void |
596 | | prt_str_arg(FILE * fp, tOptDesc * pOD) |
597 | 0 | { |
598 | 0 | if (pOD->fOptState & OPTST_STACKED) { |
599 | 0 | tArgList * pAL = (tArgList *)pOD->optCookie; |
600 | 0 | int uct = pAL->useCt; |
601 | 0 | char const ** ppz = pAL->apzArgs; |
602 | | |
603 | | /* |
604 | | * un-disable multiple copies of disabled options. |
605 | | */ |
606 | 0 | if (uct > 1) |
607 | 0 | pOD->fOptState &= ~OPTST_DISABLED; |
608 | |
|
609 | 0 | while (uct-- > 0) |
610 | 0 | prt_entry(fp, pOD, *(ppz++)); |
611 | 0 | } else { |
612 | 0 | prt_entry(fp, pOD, pOD->optArg.argString); |
613 | 0 | } |
614 | 0 | } |
615 | | |
616 | | /** |
617 | | * print the string value of an enumeration. |
618 | | * |
619 | | * @param[in] fp the file pointer to write to |
620 | | * @param[in] od the option descriptor with the enumerated value |
621 | | */ |
622 | | static void |
623 | | prt_enum_arg(FILE * fp, tOptDesc * od) |
624 | 0 | { |
625 | 0 | uintptr_t val = od->optArg.argEnum; |
626 | | |
627 | | /* |
628 | | * This is a magic incantation that will convert the |
629 | | * bit flag values back into a string suitable for printing. |
630 | | */ |
631 | 0 | (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od); |
632 | 0 | prt_entry(fp, od, VOIDP(od->optArg.argString)); |
633 | |
|
634 | 0 | od->optArg.argEnum = val; |
635 | 0 | } |
636 | | |
637 | | /** |
638 | | * Print the bits set in a bit mask option. |
639 | | * We call the option handling function with a magic value for |
640 | | * the options pointer and it allocates and fills in the string. |
641 | | * We print that with a call to prt_entry(). |
642 | | * |
643 | | * @param[in] fp the file pointer to write to |
644 | | * @param[in] od the option descriptor with a bit mask value type |
645 | | */ |
646 | | static void |
647 | | prt_set_arg(FILE * fp, tOptDesc * od) |
648 | 0 | { |
649 | 0 | char * list = optionMemberList(od); |
650 | 0 | size_t len = strlen(list); |
651 | 0 | char * buf = (char *)AGALOC(len + 3, "dir name"); |
652 | 0 | *buf= '='; |
653 | 0 | memcpy(buf+1, list, len + 1); |
654 | 0 | prt_entry(fp, od, buf); |
655 | 0 | AGFREE(buf); |
656 | 0 | AGFREE(list); |
657 | 0 | } |
658 | | |
659 | | /** |
660 | | * figure out what the option file name argument is. |
661 | | * If one can be found, call prt_entry() to emit it. |
662 | | * |
663 | | * @param[in] fp the file pointer to write to. |
664 | | * @param[in] od the option descriptor with a bit mask value type |
665 | | * @param[in] opts the program options descriptor |
666 | | */ |
667 | | static void |
668 | | prt_file_arg(FILE * fp, tOptDesc * od, tOptions * opts) |
669 | 0 | { |
670 | | /* |
671 | | * If the cookie is not NULL, then it has the file name, period. |
672 | | * Otherwise, if we have a non-NULL string argument, then.... |
673 | | */ |
674 | 0 | if (od->optCookie != NULL) |
675 | 0 | prt_entry(fp, od, od->optCookie); |
676 | | |
677 | 0 | else if (HAS_originalOptArgArray(opts)) { |
678 | 0 | char const * orig = |
679 | 0 | opts->originalOptArgArray[od->optIndex].argString; |
680 | |
|
681 | 0 | if (od->optArg.argString == orig) |
682 | 0 | return; |
683 | | |
684 | 0 | prt_entry(fp, od, od->optArg.argString); |
685 | 0 | } |
686 | 0 | } |
687 | | |
688 | | /*=export_func optionSaveFile |
689 | | * |
690 | | * what: saves the option state to a file |
691 | | * |
692 | | * arg: tOptions *, opts, program options descriptor |
693 | | * |
694 | | * doc: |
695 | | * |
696 | | * This routine will save the state of option processing to a file. The name |
697 | | * of that file can be specified with the argument to the @code{--save-opts} |
698 | | * option, or by appending the @code{rcfile} attribute to the last |
699 | | * @code{homerc} attribute. If no @code{rcfile} attribute was specified, it |
700 | | * will default to @code{.@i{programname}rc}. If you wish to specify another |
701 | | * file, you should invoke the @code{SET_OPT_SAVE_OPTS(@i{filename})} macro. |
702 | | * |
703 | | * The recommend usage is as follows: |
704 | | * @example |
705 | | * optionProcess(&progOptions, argc, argv); |
706 | | * if (i_want_a_non_standard_place_for_this) |
707 | | * SET_OPT_SAVE_OPTS("myfilename"); |
708 | | * optionSaveFile(&progOptions); |
709 | | * @end example |
710 | | * |
711 | | * err: |
712 | | * |
713 | | * If no @code{homerc} file was specified, this routine will silently return |
714 | | * and do nothing. If the output file cannot be created or updated, a message |
715 | | * will be printed to @code{stderr} and the routine will return. |
716 | | =*/ |
717 | | void |
718 | | optionSaveFile(tOptions * opts) |
719 | 0 | { |
720 | 0 | tOptDesc * od; |
721 | 0 | int ct; |
722 | 0 | FILE * fp = open_sv_file(opts); |
723 | |
|
724 | 0 | if (fp == NULL) |
725 | 0 | return; |
726 | | |
727 | | /* |
728 | | * FOR each of the defined options, ... |
729 | | */ |
730 | 0 | ct = opts->presetOptCt; |
731 | 0 | od = opts->pOptDesc; |
732 | 0 | do { |
733 | 0 | tOptDesc * p; |
734 | | |
735 | | /* |
736 | | * IF the option has not been defined |
737 | | * OR it does not take an initialization value |
738 | | * OR it is equivalenced to another option |
739 | | * THEN continue (ignore it) |
740 | | * |
741 | | * Equivalenced options get picked up when the equivalenced-to |
742 | | * option is processed. |
743 | | */ |
744 | 0 | if (UNUSED_OPT(od)) |
745 | 0 | continue; |
746 | | |
747 | 0 | if ((od->fOptState & OPTST_DO_NOT_SAVE_MASK) != 0) |
748 | 0 | continue; |
749 | | |
750 | 0 | if ( (od->optEquivIndex != NO_EQUIVALENT) |
751 | 0 | && (od->optEquivIndex != od->optIndex)) |
752 | 0 | continue; |
753 | | |
754 | | /* |
755 | | * The option argument data are found at the equivalenced-to option, |
756 | | * but the actual option argument type comes from the original |
757 | | * option descriptor. Be careful! |
758 | | */ |
759 | 0 | p = ((od->fOptState & OPTST_EQUIVALENCE) != 0) |
760 | 0 | ? (opts->pOptDesc + od->optActualIndex) : od; |
761 | |
|
762 | 0 | switch (OPTST_GET_ARGTYPE(od->fOptState)) { |
763 | 0 | case OPARG_TYPE_NONE: |
764 | 0 | prt_no_arg_opt(fp, p, od); |
765 | 0 | break; |
766 | | |
767 | 0 | case OPARG_TYPE_NUMERIC: |
768 | 0 | prt_entry(fp, p, VOIDP(p->optArg.argInt)); |
769 | 0 | break; |
770 | | |
771 | 0 | case OPARG_TYPE_STRING: |
772 | 0 | prt_str_arg(fp, p); |
773 | 0 | break; |
774 | | |
775 | 0 | case OPARG_TYPE_ENUMERATION: |
776 | 0 | prt_enum_arg(fp, p); |
777 | 0 | break; |
778 | | |
779 | 0 | case OPARG_TYPE_MEMBERSHIP: |
780 | 0 | prt_set_arg(fp, p); |
781 | 0 | break; |
782 | | |
783 | 0 | case OPARG_TYPE_BOOLEAN: |
784 | 0 | prt_entry(fp, p, p->optArg.argBool ? "true" : "false"); |
785 | 0 | break; |
786 | | |
787 | 0 | case OPARG_TYPE_HIERARCHY: |
788 | 0 | prt_nested(fp, p); |
789 | 0 | break; |
790 | | |
791 | 0 | case OPARG_TYPE_FILE: |
792 | 0 | prt_file_arg(fp, p, opts); |
793 | 0 | break; |
794 | | |
795 | 0 | default: |
796 | 0 | break; /* cannot handle - skip it */ |
797 | 0 | } |
798 | 0 | } while (od++, (--ct > 0)); |
799 | | |
800 | 0 | fclose(fp); |
801 | 0 | } |
802 | | /** @} |
803 | | * |
804 | | * Local Variables: |
805 | | * mode: C |
806 | | * c-file-style: "stroustrup" |
807 | | * indent-tabs-mode: nil |
808 | | * End: |
809 | | * end of autoopts/save.c */ |