1 /*
2 * wmslib/src/wms/clp.c, part of wmslib (Library functions)
3 * Copyright (C) 1995-1997 William Shubert.
4 * See "configure.h.in" for more copyright information.
5 *
6 * Command Line Parse.
7 */
8
9 #include <wms.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/stat.h> /* To get umask()...I hope this is portable! */
13 #include <wms/str.h>
14
15 #if STDC_HEADERS
16 #include <stdlib.h>
17 #endif /* STDC_HEADERS */
18
19 #ifdef _WMS_CLP_H_
20 #error Levelization Error.
21 #endif
22 #include "clp.h"
23
24
25 /**********************************************************************
26 * Globals
27 **********************************************************************/
28 const char *wms_progname = "UNKNOWN";
29
30 static bool showErrs = TRUE;
31
32
33 /**********************************************************************
34 * Forward Declarations
35 **********************************************************************/
36 static ClpEntry *clp_iLookup(Clp *clp, const char *key, bool *bval);
37 static void clpEntry_free(ClpEntry *ci);
38 static void strip(char *argv[]);
39 static void clp_help(Clp *clp);
40 static void setStrList(ClpEntry *entry, const char *vals);
41 static void moreStrs(ClpEntry *ce, int newNumStrs);
42
43
44 /**********************************************************************
45 * Functions
46 **********************************************************************/
clp_create(const ClpSetup vars[])47 Clp *clp_create(const ClpSetup vars[]) {
48 int numVars, i;
49 Clp *newClp;
50
51 newClp = wms_malloc(sizeof(Clp));
52 MAGIC_SET(newClp);
53 for (numVars = 0; !(vars[numVars].flags & CLPSETUP_ENDFLAG); ++numVars);
54 newClp->numInfos = numVars + 2;
55 newClp->infos = wms_malloc((numVars + 2) * sizeof(ClpEntry));
56 for (i = 0; i < numVars; ++i) {
57 MAGIC_SET(&newClp->infos[i]);
58 newClp->infos[i].name = vars[i].name;
59 newClp->infos[i].desc = vars[i].desc;
60 newClp->infos[i].flags = vars[i].flags;
61 newClp->infos[i].test = vars[i].test;
62 if (vars[i].name == NULL)
63 newClp->infos[i].flags |= CLPSETUP_NOSAVE;
64 newClp->infos[i].where = clpWhere_unset;
65 newClp->infos[i].type = clpDtype_string;
66 newClp->infos[i].numStrs = 0;
67 newClp->infos[i].maxStrs = 0;
68 newClp->infos[i].storage.strList = NULL;
69 if (vars[i].defVal) {
70 setStrList(&newClp->infos[i], vars[i].defVal);
71 } else {
72 setStrList(&newClp->infos[i], "");
73 }
74 assert(newClp->infos[i].numStrs > 0);
75 }
76 MAGIC_SET(&newClp->infos[i]);
77 newClp->infos[i].name = NULL;
78 newClp->infos[i].desc = "";
79 newClp->infos[i].flags = CLPSETUP_BOOL|CLPSETUP_NOSAVE;
80 newClp->infos[i].where = clpWhere_unset;
81 newClp->infos[i].type = clpDtype_bool;
82 newClp->infos[i].test = NULL;
83
84 ++i;
85 MAGIC_SET(&newClp->infos[i]);
86 newClp->infos[i].name = "help,-help";
87 newClp->infos[i].desc = "Show this message";
88 newClp->infos[i].flags = CLPSETUP_BOOL|CLPSETUP_NOSAVE|CLPSETUP_HELP;
89 newClp->infos[i].where = clpWhere_unset;
90 newClp->infos[i].type = clpDtype_bool;
91 newClp->infos[i].test = NULL;
92 return(newClp);
93 }
94
95
clp_destroy(Clp * clp)96 void clp_destroy(Clp *clp) {
97 int i;
98
99 assert(MAGIC(clp));
100 for (i = 0; i < clp->numInfos; ++i)
101 clpEntry_free(&clp->infos[i]);
102 wms_free(clp->infos);
103 wms_free(clp);
104 }
105
106
clp_rCmdline(Clp * clp,char * argv[])107 int clp_rCmdline(Clp *clp, char *argv[]) {
108 int numArgs;
109 ClpEntry *ce;
110 bool bval;
111
112 assert(MAGIC(clp));
113 wms_progname = argv[0];
114 strip(argv);
115 for (numArgs = 0; argv[numArgs];) {
116 if (argv[numArgs][0] == '-') {
117 /* It's a switch. */
118 ce = clp_iLookup(clp, argv[numArgs]+1, &bval);
119 ce->where = clpWhere_cmdline;
120 if (ce->flags & CLPSETUP_BOOL) {
121 if (bval && (ce->flags & CLPSETUP_HELP)) {
122 clp_help(clp);
123 exit(1);
124 }
125 clpEntry_setBool(ce, bval);
126 } else {
127 if (argv[numArgs+1])
128 clpEntry_setStr(ce, argv[numArgs+1]);
129 else {
130 fprintf(stderr, "%s: Switch \"%s\" requires an argument.\n",
131 wms_progname, argv[numArgs]);
132 exit(1);
133 }
134 strip(argv + numArgs);
135 }
136 strip(argv + numArgs);
137 } else
138 ++numArgs;
139 }
140 return(numArgs);
141 }
142
143
144 /*
145 * clp_rFile returns FALSE if it can't find the file at all.
146 */
clp_rFile(Clp * clp,const char * fname)147 bool clp_rFile(Clp *clp, const char *fname) {
148 FILE *ifile;
149 char line_in[1024], *arg;
150 Str full_fn;
151 char *home, *key_start;
152 int i, ch_in;
153 bool retval = FALSE;
154 ClpEntry *ci;
155
156 assert(MAGIC(clp));
157 str_initChars(&full_fn, fname);
158 showErrs = FALSE;
159 if (fname[0] == '~') {
160 /* Prepend "$HOME" */
161 home = getenv("HOME");
162 if (home == NULL) {
163 fprintf(stderr,
164 "%s: Error: Could not find environment variable \"HOME\".\n",
165 wms_progname);
166 showErrs = TRUE;
167 str_deinit(&full_fn);
168 return(FALSE);
169 }
170 str_print(&full_fn, "%s/%s", home, fname+1);
171 }
172 ifile = fopen(str_chars(&full_fn), "r");
173 if (ifile != NULL) {
174 retval = TRUE;
175 do {
176 ch_in = getc(ifile);
177 for (i = 0; (i < 1023) && (ch_in != '\n') && (ch_in != EOF); ++i) {
178 line_in[i] = ch_in;
179 ch_in = getc(ifile);
180 }
181 while ((ch_in != '\n') && (ch_in != EOF))
182 ch_in = getc(ifile);
183 line_in[i] = '\0';
184 if ((line_in[0] == '#') || (line_in[0] == '\0'))
185 continue;
186 key_start = strchr(line_in, '.') + 1;
187 if (key_start != NULL) {
188 arg = strchr(line_in, ':');
189 *arg = '\0';
190 arg += 2;
191 ci = clp_iLookup(clp, key_start, NULL);
192 if (ci != NULL) {
193 if ((ci->where == clpWhere_unset) ||
194 (ci->where == clpWhere_xdefs)) {
195 ci->where = clpWhere_rcfile;
196 setStrList(ci, arg);
197 }
198 }
199 }
200 } while (ch_in != EOF);
201 fclose(ifile);
202 }
203 str_deinit(&full_fn);
204 showErrs = TRUE;
205 return(retval);
206 }
207
208
clp_wFile(Clp * clp,const char * fname,const char * pname)209 void clp_wFile(Clp *clp, const char *fname, const char *pname) {
210 FILE *ofile;
211 Str full_fn;
212 char *home;
213 const char *strOut;
214 int i, prevMask, strNum, charNum;
215 ClpEntry *ci;
216
217 str_initChars(&full_fn, fname);
218 if (fname[0] == '~') {
219 /* Prepend "$HOME" */
220 home = getenv("HOME");
221 if (home == NULL) {
222 fprintf(stderr,
223 "%s: Error: Could not find environment variable \"HOME\".\n",
224 wms_progname);
225 str_deinit(&full_fn);
226 return;
227 }
228 str_print(&full_fn, "%s/%s", home, fname+1);
229 }
230 prevMask = umask(0177);
231 ofile = fopen(str_chars(&full_fn), "w");
232 umask(prevMask);
233 if (ofile == NULL) {
234 fprintf(stderr, "%s: Error: Could not open file \"%s\" for writing.\n",
235 wms_progname, fname);
236 perror(wms_progname);
237 exit(1);
238 }
239 fprintf(ofile,
240 "# NOTICE: Please do not edit this file.\n"
241 "# It was automatically generated by \"%s\". If you want to\n"
242 "# change one of these values, please use command line switches,\n"
243 "# X defaults, or a setup window.\n"
244 "# As a last resort you may simply delete this file.\n\n",
245 wms_progname);
246 for (i = 0; i < clp->numInfos; ++i) {
247 ci = &clp->infos[i];
248 if (!(ci->flags & CLPSETUP_NOSAVE)) {
249 switch(ci->type) {
250 case clpDtype_int:
251 fprintf(ofile, "%s.%s: %d\n", pname, ci->name, ci->storage.ival);
252 break;
253 case clpDtype_double:
254 fprintf(ofile, "%s.%s: %f\n", pname, ci->name, ci->storage.dval);
255 break;
256 case clpDtype_bool:
257 if (ci->storage.bval)
258 fprintf(ofile, "%s.%s: y\n", pname, ci->name);
259 else
260 fprintf(ofile, "%s.%s: n\n", pname, ci->name);
261 break;
262 case clpDtype_string:
263 fprintf(ofile, "%s.%s: ", pname, ci->name);
264 for (strNum = 0; strNum < ci->numStrs; ++strNum) {
265 if (strNum != 0)
266 putc('|', ofile);
267 strOut = str_chars(&ci->storage.strList[strNum]);
268 for (charNum = 0; strOut[charNum]; ++charNum) {
269 if ((strOut[charNum] == '|') ||
270 (strOut[charNum] == '\\'))
271 putc('\\', ofile);
272 putc(strOut[charNum], ofile);
273 }
274 }
275 putc('\n', ofile);
276 break;
277 default:
278 /* Should never reach here. */
279 assert(0);
280 break;
281 }
282 }
283 }
284 fclose(ofile);
285 str_deinit(&full_fn);
286 }
287
288
clpEntry_iGetInt(ClpEntry * ce,bool * err)289 int clpEntry_iGetInt(ClpEntry *ce, bool *err) {
290 switch(ce->type) {
291 case clpDtype_int:
292 if (err)
293 *err = FALSE;
294 return(ce->storage.ival);
295 break;
296 case clpDtype_string:
297 return(wms_atoi(str_chars(&ce->storage.strList[0]), err));
298 break;
299 default:
300 if (err)
301 *err = TRUE;
302 return(0);
303 }
304 }
305
306
clpEntry_iGetDouble(ClpEntry * ce,bool * err)307 double clpEntry_iGetDouble(ClpEntry *ce, bool *err) {
308 switch(ce->type) {
309 case clpDtype_double:
310 if (err)
311 *err = FALSE;
312 return(ce->storage.dval);
313 break;
314 case clpDtype_string:
315 return(wms_atof(str_chars(&ce->storage.strList[0]), err));
316 break;
317 default:
318 if (err)
319 *err = TRUE;
320 return(0.0);
321 }
322 }
323
324
clpEntry_iGetStrNum(ClpEntry * ce,int num,bool * err)325 const char *clpEntry_iGetStrNum(ClpEntry *ce, int num, bool *err) {
326 if (ce->type == clpDtype_string) {
327 if (err)
328 *err = FALSE;
329 assert(num < ce->numStrs);
330 return(str_chars(&ce->storage.strList[num]));
331 } else {
332 if (err)
333 *err = TRUE;
334 return(NULL);
335 }
336 }
337
338
clpEntry_iGetBool(ClpEntry * ce,bool * err)339 bool clpEntry_iGetBool(ClpEntry *ce, bool *err) {
340 const char *str;
341
342 switch(ce->type) {
343 case clpDtype_bool:
344 if (err)
345 *err = FALSE;
346 return(ce->storage.bval);
347 break;
348 case clpDtype_string:
349 str = str_chars(&ce->storage.strList[0]);
350 if ((!strcmp(str, "1")) ||
351 (!strcmp(str, "t")) ||
352 (!strcmp(str, "T")) ||
353 (!strcmp(str, "y")) ||
354 (!strcmp(str, "Y")) ||
355 (!strcmp(str, "true")) ||
356 (!strcmp(str, "True")) ||
357 (!strcmp(str, "TRUE")) ||
358 (!strcmp(str, "yes")) ||
359 (!strcmp(str, "Yes")) ||
360 (!strcmp(str, "YES")))
361 return(TRUE);
362 if ((!strcmp(str, "0")) ||
363 (!strcmp(str, "f")) ||
364 (!strcmp(str, "F")) ||
365 (!strcmp(str, "n")) ||
366 (!strcmp(str, "N")) ||
367 (!strcmp(str, "false")) ||
368 (!strcmp(str, "False")) ||
369 (!strcmp(str, "FALSE")) ||
370 (!strcmp(str, "no")) ||
371 (!strcmp(str, "No")) ||
372 (!strcmp(str, "NO")))
373 return(FALSE);
374 if (err)
375 *err = TRUE;
376 return(FALSE);
377 break;
378 default:
379 if (err)
380 *err = TRUE;
381 return(FALSE);
382 }
383 }
384
385
clpEntry_setInt(ClpEntry * ce,int val)386 bool clpEntry_setInt(ClpEntry *ce, int val) {
387 ClpEntry newCe;
388
389 MAGIC_SET(&newCe);
390 if (ce->test) {
391 newCe.type = clpDtype_int;
392 newCe.storage.ival = val;
393 if (!ce->test(&newCe))
394 return(FALSE);
395 }
396 clpEntry_free(ce);
397 ce->type = clpDtype_int;
398 ce->storage.ival = val;
399 MAGIC_UNSET(&newCe);
400 return(TRUE);
401 }
402
403
clpEntry_setDouble(ClpEntry * ce,double val)404 bool clpEntry_setDouble(ClpEntry *ce, double val) {
405 ClpEntry newCe;
406
407 MAGIC_SET(&newCe);
408 if (ce->test) {
409 newCe.type = clpDtype_double;
410 newCe.storage.dval = val;
411 if (!ce->test(&newCe))
412 return(FALSE);
413 }
414 clpEntry_free(ce);
415 ce->type = clpDtype_double;
416 ce->storage.dval = val;
417 MAGIC_UNSET(&newCe);
418 return(TRUE);
419 }
420
421
clpEntry_setStrNum(ClpEntry * ce,const char * val,int num)422 bool clpEntry_setStrNum(ClpEntry *ce, const char *val, int num) {
423 ClpEntry newCe;
424 bool testResult;
425
426 if (ce->test) {
427 assert(num == 0);
428 MAGIC_SET(&newCe);
429 newCe.type = clpDtype_string;
430 newCe.storage.strList = str_createChars(val);
431 newCe.numStrs = 1;
432 testResult = ce->test(&newCe);
433 str_destroy(newCe.storage.strList);
434 MAGIC_UNSET(&newCe);
435 if (!testResult)
436 return(FALSE);
437 }
438 if (ce->type == clpDtype_string) {
439 if (num >= ce->numStrs) {
440 moreStrs(ce, num+1);
441 }
442 str_copyChars(&ce->storage.strList[num], val);
443 } else {
444 assert(num == 0);
445 clpEntry_free(ce);
446 ce->type = clpDtype_string;
447 ce->numStrs = 1;
448 ce->maxStrs = 1;
449 ce->storage.strList = str_createChars(val);
450 }
451 return(TRUE);
452 }
453
454
moreStrs(ClpEntry * ce,int newNumStrs)455 static void moreStrs(ClpEntry *ce, int newNumStrs) {
456 Str *newStrs;
457 int i;
458
459 assert(newNumStrs > ce->numStrs);
460 newStrs = wms_malloc(newNumStrs * sizeof(Str));
461 for (i = 0; i < ce->numStrs; ++i) {
462 newStrs[i] = ce->storage.strList[i];
463 }
464 for (; i < newNumStrs; ++i) {
465 str_init(&newStrs[i]);
466 }
467 wms_free(ce->storage.strList);
468 ce->storage.strList = newStrs;
469 }
470
471
clpEntry_setBool(ClpEntry * ce,bool val)472 bool clpEntry_setBool(ClpEntry *ce, bool val) {
473 ClpEntry newCe;
474
475 MAGIC_SET(&newCe);
476 if (ce->test) {
477 newCe.type = clpDtype_bool;
478 newCe.storage.bval = val;
479 if (!ce->test(&newCe))
480 return(FALSE);
481 }
482 clpEntry_free(ce);
483 ce->type = clpDtype_bool;
484 ce->storage.bval = val;
485 MAGIC_UNSET(&newCe);
486 return(TRUE);
487 }
488
489
clp_lookup(Clp * clp,const char * key)490 ClpEntry *clp_lookup(Clp *clp, const char *key) {
491 assert(MAGIC(clp));
492 return(clp_iLookup(clp, key, NULL));
493 }
494
495
clp_iLookup(Clp * clp,const char * key,bool * bval)496 static ClpEntry *clp_iLookup(Clp *clp, const char *key, bool *bval) {
497 ClpEntry *ci;
498 int i, j, keyLen;
499 bool reversed;
500
501 assert(MAGIC(clp));
502 keyLen = strlen(key);
503 reversed = !strncmp(key, "no", 2);
504 if (bval)
505 *bval = TRUE;
506 for (i = 0; i < clp->numInfos; ++i) {
507 ci = &clp->infos[i];
508 if (ci->name) {
509 for (j = 0; ci->name[j];) {
510 if (!strncmp(ci->name+j, key, keyLen)) {
511 if ((ci->name[j + keyLen] == '\0') ||
512 (ci->name[j + keyLen] == ','))
513 return(ci);
514 }
515 if (reversed && (ci->flags & CLPSETUP_BOOL) &&
516 !strncmp(ci->name, key+2, keyLen - 2)) {
517 if (bval)
518 *bval = FALSE;
519 return(ci);
520 }
521 while (ci->name[j] && (ci->name[j] != ','))
522 ++j;
523 if (ci->name[j])
524 ++j;
525 }
526 }
527 }
528 if (showErrs) {
529 fprintf(stderr, "%s: \"-%s\" is not a valid flag.\n"
530 " Use \"%s -help\" for a list of valid flags.\n",
531 wms_progname, key, wms_progname);
532 exit(1);
533 } else
534 return(NULL);
535 }
536
537
strip(char * argv[])538 static void strip(char *argv[]) {
539 do {
540 argv[0] = argv[1];
541 ++argv;
542 } while (argv[0] != NULL);
543 }
544
545
clpEntry_free(ClpEntry * ci)546 static void clpEntry_free(ClpEntry *ci) {
547 int i;
548
549 assert(MAGIC(ci));
550 if (ci->type == clpDtype_string) {
551 for (i = 0; i < ci->maxStrs; ++i)
552 str_deinit(&ci->storage.strList[i]);
553 wms_free(ci->storage.strList);
554 }
555 }
556
557
clp_help(Clp * clp)558 static void clp_help(Clp *clp) {
559 int i, j;
560 char name[40];
561 const char *prestr;
562
563 assert(MAGIC(clp));
564 for (i = 0; i < clp->numInfos; ++i) {
565 if (clp->infos[i].desc) {
566 if (clp->infos[i].name) {
567 name[0] = '\0';
568 if (clp->infos[i].flags & CLPSETUP_SHOWBOOL)
569 prestr = "-[no]";
570 else
571 prestr = "-";
572 strcpy(name, prestr);
573 for (j = 0; clp->infos[i].name[j]; ++j) {
574 strncat(name, clp->infos[i].name+j, 1);
575 if (clp->infos[i].name[j] == ',')
576 strcat(name, prestr);
577 }
578 fprintf(stderr, " %20s %s\n", name, clp->infos[i].desc);
579 } else
580 fprintf(stderr, " %s\n", clp->infos[i].desc);
581 }
582 }
583 }
584
585
setStrList(ClpEntry * entry,const char * vals)586 static void setStrList(ClpEntry *entry, const char *vals) {
587 int numVals = 1;
588 int i;
589 Str *newStrs;
590 bool escaped;
591
592 for (i = 0, escaped = FALSE; vals[i]; ++i) {
593 if (!escaped && (vals[i] == '|'))
594 ++numVals;
595 escaped = (!escaped && (vals[i] == '\\'));
596 }
597 if (numVals > entry->maxStrs) {
598 newStrs = wms_malloc(numVals * sizeof(Str));
599 for (i = 0; i < numVals; ++i) {
600 str_init(&newStrs[i]);
601 }
602 if (entry->storage.strList != NULL)
603 wms_free(entry->storage.strList);
604 entry->maxStrs = numVals;
605 entry->storage.strList = newStrs;
606 } else
607 newStrs = entry->storage.strList;
608 entry->numStrs = numVals;
609 numVals = 0;
610 str_clip(&newStrs[0], 0);
611 for (i = 0, escaped = FALSE; vals[i]; ++i) {
612 if (escaped) {
613 str_catChar(&newStrs[numVals], vals[i]);
614 escaped = FALSE;
615 } else {
616 if (vals[i] == '\\')
617 escaped = TRUE;
618 else if (vals[i] == '|') {
619 ++numVals;
620 assert(numVals < entry->numStrs);
621 str_clip(&newStrs[numVals], 0);
622 } else {
623 str_catChar(&newStrs[numVals], vals[i]);
624 }
625 }
626 }
627 }
628
629