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