"Fossies" - the Fresh Open Source Software Archive 
Member "qdiff-0.9.1/tappconfig.cc" (21 Oct 2008, 67053 Bytes) of package /linux/privat/old/qdiff-0.9.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /*GPL*START*
2 *
3 * tappconfig.cc - console application framework
4 *
5 * Copyright (C) 1998 by Johannes Overmann <Johannes.Overmann@gmx.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * *GPL*END*/
21
22 #include <ctype.h>
23 #include <limits.h>
24 #include <float.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <sys/ioctl.h>
28 #include <sys/stat.h>
29 #include <termios.h>
30 #include <unistd.h>
31 #include "tappconfig.h"
32
33 // config:
34
35 // global email of author:
36 const char *TAPPCONFIG_EMAIL = "suntong001@users.sourceforge.net";
37 // gpl message
38 const char *TAPPCONFIG_GPL = "this program is distributed under the terms of the GNU General Public License version 2";
39
40
41 // usage:
42 // ======
43 //
44 // TAppConfig(const char *optionlist[], const char *listname, /* option list (see below) */
45 // int argc, char *av[], /* argc and argv from main or qt */
46 // const char *envstrname, /* environment variable for options (0=none) */
47 // const char *rcname, /* name for config file (0=none) (use "foorc" to get ~/.foorc and /etc/foorc */
48 // const string& version); /* application version */
49 //
50 // the name of the application is taken from argv[0]
51 // the whole magic is in optionlist:
52 // optionlist is a list of c-strings (usually static string constants)
53 // which is terminated by the string "EOL" (end of list, three chars),
54 // each string may contain either an option item or a meta command
55 //
56 // meta commands: (order and position does not matter)
57 // (all meta commands begin with a '#' char)
58 // ---------------------------------------------------
59 // #usage="string" /* set usage, printed on top of --help */
60 // #trailer="string" /* set trailer, printed at the bottom of --help */
61 // #onlycl /* this application does not use config files */
62 // #stopat-- /* stop option parsing after a doubledash (--) */
63 // #remove-- /* remove the first doubledash from parameterlist (--) */
64 // #ignore_negnum /* something like -20 or -.57 is not treated as option */
65 // #commonheadline /* headline on top of --help */
66 //
67 // the usage and the trailer strings may contain the following variable names:
68 // %n is replaced by the application name
69 // %v is replaced by the application version
70 // %e is replaved by the authors email address
71 // %gpl is replaced by a one line version of the GNU General Public License
72 //
73 //
74 // option item:
75 // ------------
76 // an option item string contains a comma separated list of tags. some of
77 // the tags take an argument (commas must be quoted by a backslash or
78 // quotes). some tags are optional, others required.
79 // each such line defines an option item (i.e. a command line options)
80 //
81 // required tags:
82 // --------------
83 // name="name" /* the long option name and also the internal name of that option */
84 // type=type: /* type of the option */
85 // string expected parameter is a character string of arbitrary length, a fallback type for all kinds of parameters
86 // int expected parameter is an integer
87 // double expected parameter is a double precision floating point value
88 // switch no parameter is expected: this is more like a switch (like -l in ls -l)
89 // bool expected parameter is a truth value: 1/t/on/yes/true for 'true' and 0/f/off/no/false for 'false' (case insensitive)
90 // help="help" /* the help message (for --help), the text will be formatted correctly at runtime by TAppConfig */
91 // /* so do not enter newlines unless you really want to end a paragraph */
92 //
93 // optional tags:
94 // --------------
95 // char=c /* the short option name (like -c) */
96 // param=PAR /* what to print after an option that takes an argument in --help (i.e. --width=NUM) */
97 // headline="foo options:" /* headline, printed before this option in --help */
98 // default=val /* default value, if option item is not specified by teh user. this should be of correct type */
99 // /* this default to empty string/0/0.0/false, not possible for type=switch */
100 // upper=val /* upper bound (inclusive) of numeric value (only int/double), default +INF */
101 // lower=val /* lower bound (inclusive) of numeric value (only int/double), default -INF */
102 // alias=aliaslist /* comma separated list (in quotes!) of aliases for this option (use - for single char alias) */
103 // musthave /* this option item is required (so it's not really an option, but a named parameter) */
104 // shouldhave /* this option prints a warning, if it is not specified (it is recommended) */
105 // string-mode-append /* for string type option items, multiple parameters are concatenated (see string-append-separator) */
106 // string-mode-once /* for string type option items, only one parameter is allowed (else an error message is printed) */
107 // string-append-separator=string /* this string separates multiple parameters for string-mode-append */
108 // onlycl /* this option may only be specified on the command line */
109 // save /* this option item and its value is saved in the config file if save() is called */
110 // optional_param /* the parameter for this option is optional, if it is missing the default is used */
111 // hide,hidden /* do not print option in --help, --hhelp will print it but --hhelp itself is hidden */
112 // onlyapp /* for internal use only: application private variable (like application version/name) */
113 // configopt /* for internal use only: application private option item (like --create-rc) */
114 //
115 // example of a small option list:
116 #if 0
117 const char *options[] = {
118 "#usage='Usage: %n [OPTIONS and FILES] -- [FILES]\n\n" // no comma here!
119 "this program does very strange things\n",
120 "#trailer='\n%n version %v *** (C) 1997 by Johannes Overmann\ncomments, bugs and suggestions welcome: %e\n%gpl'",
121 "#onlycl", // only command line options
122 "#stopat--", // stop option scanning after a -- parameter
123 // options
124 "name=recursive , type=switch, char=r, help=recurse directories, headline=file options:",
125 "name=fileext , type=string, char=e, help=process only files with extension in comma separated LIST, string-mode-append, string-append-separator=',', param=LIST",
126
127 "name=ignore-case , type=switch, char=i, help=ignore case, headline=matching options:",
128 "name=lower-case , type=switch, help=lower replacements",
129 "name=upper-case , type=switch, help=upper replacements",
130
131 "name=dummy-mode , type=switch, char=0, help='do *not* write/change anything, just test', headline=common options:",
132 "name=quiet , type=switch, char=q, help=quiet execution\\, do not say anything",
133 "name=progress , type=int, char=P, param=NUM, default=0, lower=0, help='progress indicator, useful with large files, print one dot per event",
134 // --help and --version will appear automatically here
135 "EOL" // end of list
136 };
137 #endif
138 //
139 // notes:
140 // ------
141 // to make it possible for the application to process control characters during runtime every
142 // string is compiled from a c-string format (with \n, \xff and such things) at runtime.
143 // if you enter the option list as string constants, then the c compiler already compiles
144 // those strings. so you have to protect the backslash sequences by backslashes. example: (not worst case, but ugly)
145 // to get a single backslash in the output (i.e. in --help) the string must contain two backslashes (\\), but
146 // you must protect them in your string constant -- so the source looks like this: \\\\ <ugh> :)
147 // (of course, if you want to print this help with printf you have to write \\\\\\\\, tricky eh? :)
148 // to make things more complicated, you have to protect commas in your text by quotes or backslashes which must
149 // also be protected from the compiler to ... oh no.
150 // just take a look at the example above (dummy-mode and quiet), not that worse, isnt it? :)
151
152
153
154 // history:
155 // ========
156 // 1997
157 // 01:45 Jun 11 stop on '--' feature added (929 lines)
158 // 02:00 Jul 3 string once/append feature added (968 lines)
159 // 12:00 Aug 7 application set/get support started (1103 lines)
160 // 13:00 Aug 7 application set/get support finished (untested) (1164 lines)
161 // 13:00 Aug 7 autosave support started
162 // 21:00 Aug 7 autosave canceled: save support instead
163 // 23:30 Aug 7 save support finished (1327 lines)
164 // ??? get default/ranges
165 // 1998
166 // 17:00 Jan 10 improved rc file parsing (string scan tools)
167 // 18:47 Aug 04 fixed %s bug (printf->puts)
168 // 22:48 Aug 05 added %e for authors email
169 // 21:00 Oct 01 documentation (1595)
170 // 21:29 Nov 12 allow %n in help (application name) (1607)
171 // 20:51 Dec 01 ignore_negnum added (1617)
172 // 1999
173 // 13:58 Jan 01 remove-- added (1623)
174 // 15:01 Jan 01 type==STRING/OVERRIDE, rc-file/cmdline not set bug fixed (1625)
175 // 23:00 Feb 03 single char alias added
176 // 01:16 Feb 11 help fit terminal width added (1650)
177 // 15:08 Feb 15 tobject type info removed
178 // 12:29 Apr 02 optional_param added
179 // 22:55 Jun 04 string_append bug fixed
180 // 19:00 Sep 19 environment NULL pointer segfault fixed
181 // 20:58 Sep 20 hide option added
182 // 21:56 Sep 29 --hhelp added (oh how I like this ... :), hidden=hide (203/1677)
183 // 2000:
184 // 01:35 Jul 08 misc bugfixes
185 // 2001:
186 // 22:58 Jun 11 userErrorExitStatus added (273/1946)
187 // 2007:
188 // Jan 28 wasSetByUser() added
189 // Jan 29 print warning for unknown rc-file parameters instead of error (1977 lines)
190
191
192 // global data:
193
194 /// global TAppConfig pointer to the only instance TAppConfig
195 TAppConfig *tApp = 0;
196 // global application name (pseudo invisible to the outside)
197 const char *terrorApplicationName;
198 // standard options
199 static const char *self_conflist[] = {
200 // standard options
201 "type=switch, name=help, char=h, onlycl, help='print this help message, then exit successfully'",
202 "type=switch, name=hhelp, onlycl, hide, help='like help but also show hidden options'",
203 "type=switch, name=version, onlycl, help='print version, then exit successfully'",
204
205 // configuration options
206 "type=switch, name=verbose-config, onlycl, configopt, help='print all options, values and where they were specified, then exit', headline='config file options:'",
207 "type=string, name=rc-file, onlycl, configopt, param=FILE, help='use FILE as rcfile'",
208 "type=string, name=create-rc, onlycl, configopt, param=FILE, help='create a FILE with default values for all options'",
209
210 // pseudovariables only visible to the application, not to the user
211 "type=string, name=application-version, onlyapp",
212 "type=string, name=application-name, onlyapp",
213 "EOL"
214 };
215 /// global exit status on userError()
216 static int userErrorExitStatus = 1;
217
218 // helpers
219
220 template<class T>
221 inline bool tOutOfRange(const T& value, const T& lower, const T& upper) {
222 if((value<lower) || (value>upper)) return true; else return false;}
223
224
225 // file tools
226
227 bool fisdir(const char *fname) {
228 struct stat buf;
229 if(stat(fname, &buf)) return false;
230 if(S_ISDIR(buf.st_mode)) return true;
231 else return false;
232 }
233
234 bool fisregular(const char *fname) {
235 struct stat buf;
236 if(stat(fname, &buf)) return false;
237 if(S_ISREG(buf.st_mode)) return true;
238 else return false;
239 }
240
241 bool fexists(const char *fname) {
242 struct stat buf;
243 if(stat(fname, &buf)) return false;
244 else return true;
245 }
246
247 bool fissymlink(const char *fname) {
248 #ifdef __STRICT_ANSI__
249 fname = fname;
250 return false;
251 #else
252 struct stat buf;
253 if(lstat(fname, &buf)) return false;
254 if(S_ISLNK(buf.st_mode)) return true;
255 else return false;
256 #endif
257 }
258
259 off_t flen(const char *fname) {
260 struct stat buf;
261 if(stat(fname, &buf)) throw TFileOperationErrnoException(fname, "stat");
262 return buf.st_size;
263 }
264
265 off_t flen(int fdes) {
266 struct stat buf;
267 if(fstat(fdes, &buf)) throw TFileOperationErrnoException("<anon>", "fstat");
268 return buf.st_size;
269 }
270
271 off_t flen(FILE *file) {
272 #ifdef __STRICT_ANSI__
273 long c = ftell(file);
274 if(c == -1) throw TFileOperationErrnoException("<anon>", "ftell");
275 if(fseek(file, 0, SEEK_END)) throw TFileOperationErrnoException("<anon>", "fseek");
276 long r = ftell(file);
277 if(r == -1) throw TFileOperationErrnoException("<anon>", "ftell");
278 if(fseek(file, c, SEEK_SET)) throw TFileOperationErrnoException("<anon>", "fseek");
279 return (off_t)r;
280 #else
281 struct stat buf;
282 if(fstat(fileno(file), &buf)) throw TFileOperationErrnoException("<anon>", "fstat");
283 return buf.st_size;
284 #endif
285 }
286
287 // errors
288
289
290 void userWarning(const char *message, ...) {
291 va_list ap;
292
293 va_start(ap, message);
294 if(terrorApplicationName) fprintf(stderr, "\r%s: warning: ", terrorApplicationName);
295 else fprintf(stderr, "\rwarning: ");
296 vfprintf(stderr, message, ap);
297 va_end(ap);
298 }
299
300 // returns old status
301 int setUserErrorExitStatus(int status) {
302 int old = userErrorExitStatus;
303 userErrorExitStatus = status;
304 return old;
305 }
306
307 void userError(const char *message, ...) {
308 va_list ap;
309
310 va_start(ap, message);
311 if(terrorApplicationName) fprintf(stderr, "\r%s: ", terrorApplicationName);
312 else fprintf(stderr, "\rerror: ");
313 vfprintf(stderr, message, ap);
314 va_end(ap);
315 exit(userErrorExitStatus);
316 }
317
318
319 #ifdef CHECKPOINTS
320 #ifdef KEYWORD_CHECKPOINTS
321 #undef for
322 #undef do
323 #undef else
324 #undef switch
325 #undef break
326 #undef return
327 #endif
328 #define MAX_CP 64
329 const char *checkpoint_file[MAX_CP];
330 const char *checkpoint_func[MAX_CP];
331 int checkpoint_line[MAX_CP];
332 static int num_cp = -1;
333
334 int addCheckpoint(const char *file, int line, const char *func) {
335 fprintf(stderr, "%s:%d:%s%s checkpoint\n", file, line, func?func:"", func?":":"");
336 if(num_cp < 0) {
337 // initialize
338 memset(checkpoint_file, 0, sizeof(checkpoint_file));
339 memset(checkpoint_line, 0, sizeof(checkpoint_line));
340 memset(checkpoint_func, 0, sizeof(checkpoint_func));
341 num_cp = 0;
342 } else {
343 checkpoint_file[num_cp] = file;
344 checkpoint_line[num_cp] = line;
345 checkpoint_func[num_cp] = func;
346 num_cp++;
347 if(num_cp >= MAX_CP) num_cp = 0;
348 }
349 return 1;
350 }
351
352 static void printCheckpoints() {
353 if(num_cp < 0) {
354 fprintf(stderr, "\ncheckpoint list not yet initialized\n");
355 return;
356 }
357 int n = num_cp - 1;
358 for(int i = 0; i < MAX_CP; i++, n--) {
359 if(n < 0) n += MAX_CP;
360 if(checkpoint_file[n])
361 fprintf(stderr, "%s:%d:%s%s checkpoint\n", checkpoint_file[n], checkpoint_line[n],
362 checkpoint_func[n]?checkpoint_func[n]:"", checkpoint_func[n]?":":"");
363 }
364 }
365 #endif
366
367 #ifdef __GNUC__
368 #define fatalError(format) fatalError_func(__FILE__,__LINE__,__PRETTY_FUNCTION__,format)
369 #define fatalError1(format,a) fatalError_func(__FILE__,__LINE__,__PRETTY_FUNCTION__,format, a)
370 #define fatalError2(format,a,b) fatalError_func(__FILE__,__LINE__,__PRETTY_FUNCTION__,format, a, b)
371 #define fatalError3(format,a,b,c) fatalError_func(__FILE__,__LINE__,__PRETTY_FUNCTION__,format, a, b, c)
372 #define fatalError4(format,a,b,c,d) fatalError_func(__FILE__,__LINE__,__PRETTY_FUNCTION__,format, a, b, c, d)
373 #else
374 #define fatalError(format) fatalError_func(__FILE__,__LINE__,0,format)
375 #define fatalError1(format,a) fatalError_func(__FILE__,__LINE__,0,format, a)
376 #define fatalError2(format,a,b) fatalError_func(__FILE__,__LINE__,0,format, a, b)
377 #define fatalError3(format,a,b,c) fatalError_func(__FILE__,__LINE__,0,format, a, b, c)
378 #define fatalError4(format,a,b,c,d) fatalError_func(__FILE__,__LINE__,0,format, a, b, c, d)
379 #endif
380 static void fatalError_func(const char *file, int line, const char *function, const char *message, ...)
381 #ifdef __GNUC__
382 __attribute__ ((noreturn,format(printf,4,5)))
383 #endif
384 ;
385 static void fatalError_func(const char *file, int line, const char *function, const char *message, ...)
386 {
387 #ifdef CHECKPOINTS
388 printCheckpoints();
389 #endif
390 if(function) fprintf(stderr, "\n%s:%d: fatal error in function '%s':\n", file, line, function);
391 else fprintf(stderr, "\n%s:%d: fatal error:\n", file, line);
392 va_list ap;
393 va_start(ap, message);
394 vfprintf(stderr, message, ap);
395 va_end(ap);
396 exit(1);
397 }
398
399
400
401
402
403
404
405 // TAppConfigItem implementation
406
407 TAppConfigItem::TAppConfigItem():
408 must_have(false),
409 should_have(false),
410 only_cl(false),
411 configopt(false),
412 only_app(false),
413 save(false),
414 optional_param(false),
415 hide(false),
416 type(TACO_TYPE_NONE),
417 set_in(NEVER),
418 string_mode(OVERRIDE),
419 string_sep(""),
420 double_value(0), double_upper(0), double_lower(0), double_default(0),
421 int_value(0), int_upper(0), int_lower(0), int_default(0),
422 bool_value(false), bool_default(false),
423 printed(false),
424 name(),
425 context(),
426 help(),
427 headline(),
428 char_name(),
429 par(),
430 alias(),
431 type_str(),
432 upper(), lower(), def(),
433 string_value(), string_default()
434 {}
435
436
437 TAppConfigItem::TAppConfigItem(const char *str, const char *line_context,
438 bool privat):
439 must_have(false),
440 should_have(false),
441 only_cl(false),
442 configopt(false),
443 only_app(false),
444 save(false),
445 optional_param(false),
446 hide(false),
447 type(TACO_TYPE_NONE),
448 set_in(NEVER),
449 string_mode(OVERRIDE),
450 string_sep(""),
451 double_value(0), double_upper(0), double_lower(0), double_default(0),
452 int_value(0), int_upper(0), int_lower(0), int_default(0),
453 bool_value(false), bool_default(false),
454 printed(false),
455 name(),
456 context(line_context),
457 help(),
458 headline(),
459 char_name(),
460 par(),
461 alias(),
462 type_str(),
463 upper(), lower(), def(),
464 string_value(), string_default()
465 {
466 tvector<tstring> comp = split(str, ",", true);
467 for(size_t i = 0; i < comp.size(); i++) {
468 setComp(split(comp[i], "=", true, true), privat);
469 }
470 validate(line_context);
471 }
472
473
474 inline static bool isalphaorul(char c) {
475 if(isalpha(c) || c=='_') return true;
476 else return false;
477 }
478
479
480 static tstring Range2Str(int lower, int upper) {
481 if((lower != INT_MIN) || (upper != INT_MAX)) {
482 tstring lstr;
483 tstring ustr;
484 if(lower != INT_MIN) lstr = tstring(lower);
485 if(upper != INT_MAX) ustr = tstring(upper);
486 return "[" + lstr + ".." + ustr + "]";
487 } else {
488 return "";
489 }
490 }
491
492 static tstring Range2Str(double lower, double upper) {
493 if((lower!=-DBL_MAX) || (upper!=DBL_MAX)) {
494 tstring lstr;
495 tstring ustr;
496 if(lower != -DBL_MAX) lstr = tstring(lower);
497 if(upper != DBL_MAX) ustr = tstring(upper);
498 return "[" + lstr + ".." + ustr + "]";
499 } else {
500 return "";
501 }
502 }
503
504 void TAppConfigItem::validate(const char *line_context) {
505 // name
506 if(name.len()<2)
507 fatalError2("%s: name too short! (was %d, must have a least two chars)\n", line_context, int(name.len()));
508 if(!isalphaorul(name[0]))
509 fatalError2("%s: name should begin with alpha char or '_'! (was %c, must have a least two chars)\n", line_context, name[0]);
510 if(char_name.len() > 1)
511 fatalError1("%s: only one character may be specified as char name!\n", line_context);
512
513 // help, alias and flags
514 if((help=="")&&(!only_app)&&(!hide))
515 fatalError1("%s: you must give a help for each non nidden option!\n", line_context);
516 for(size_t i = 0; i < alias.size(); i++)
517 if(alias[i].len() < 1)
518 fatalError1("%s: alias must not be empty!\n", line_context);
519 if(only_app&&only_cl)
520 fatalError1("%s: only one out of [onlyapp,onlycl] may be specified!\n", line_context);
521 if(should_have&&must_have)
522 fatalError1("%s: only one out of [shouldhave,musthave] may be specified!\n", line_context);
523 if((!def.empty()) && must_have && only_cl)
524 fatalError1("%s: default value for required command line option makes no sense!\n", line_context);
525
526 // type
527 if(type_str=="bool") type=BOOL;
528 else if(type_str=="int") type=INT;
529 else if(type_str=="switch") type=SWITCH;
530 else if(type_str=="double") type=DOUBLE;
531 else if(type_str=="string") type=STRING;
532 else fatalError2("%s: illegal/unknown type '%s'!\n", line_context, type_str.c_str());
533
534 // string mode
535 if((string_mode!=OVERRIDE) && (type!=STRING))
536 fatalError1("%s: string-mode-... makes only sense with strings!\n", line_context);
537 if((!string_sep.empty()) && (type!=STRING))
538 fatalError1("%s: string-append-separator makes only sense with strings!\n", line_context);
539 if((!string_sep.empty()) && (string_mode!=APPEND))
540 fatalError1("%s: string-append-separator makes only sense with string-mode-append!\n", line_context);
541
542 // each type
543 switch(type) {
544 case SWITCH:
545 if(must_have||should_have) fatalError1("%s: switch can't be reqired/recommended!\n", line_context);
546 if(!def.empty()) fatalError1("%s: switch can't have a defaut (is always false)!\n", line_context);
547 if(!upper.empty()) fatalError1("%s: switch can't have an upper limit!\n", line_context);
548 if(!lower.empty()) fatalError1("%s: switch can't have a lower limit!\n", line_context);
549 bool_value = false;
550 break;
551 case BOOL:
552 if(!def.empty()) {
553 if(!def.toBool(bool_default))
554 fatalError2("%s: illegal/unknown bool value for default '%s'! (can be [true|yes|on|t|1|false|no|off|f|0])\n", line_context, def.c_str());
555 }
556 else bool_default = false;
557 bool_value = bool_default;
558 if(!upper.empty()) fatalError1("%s: bool can't have an upper limit!\n", line_context);
559 if(!lower.empty()) fatalError1("%s: bool can't have a lower limit!\n", line_context);
560 break;
561 case INT:
562 if(!def.empty()) {
563 if(!def.toInt(int_default, 0))
564 fatalError2("%s: illegal chars for default value in '%s'!\n", line_context, def.c_str());
565 }
566 else int_default = 0;
567 int_value = int_default;
568 if(!upper.empty()) {
569 if(!upper.toInt(int_upper, 0))
570 fatalError2("%s: illegal chars for upper bound in '%s'!\n", line_context, upper.c_str());
571 }
572 else int_upper = INT_MAX;
573 if(!lower.empty()) {
574 if(!lower.toInt(int_lower, 0))
575 fatalError2("%s: illegal chars for lower bound in '%s'!\n", line_context, lower.c_str());
576 }
577 else int_lower = INT_MIN;
578 if(tOutOfRange(int_default, int_lower, int_upper))
579 fatalError3("%s: default value out of range! (%d not in %s)!\n", line_context, int_default, Range2Str(int_lower, int_upper).c_str());
580 break;
581 case DOUBLE:
582 if(!def.empty()) {
583 if(!def.toDouble(double_default))
584 fatalError2("%s: illegal chars for default value in '%s'!\n", line_context, def.c_str());
585 }
586 else double_default = 0.0;
587 double_value = double_default;
588 if(!upper.empty()) {
589 if(!upper.toDouble(double_upper))
590 fatalError2("%s: illegal chars for upper bound in '%s'!\n", line_context, upper.c_str());
591 }
592 else double_upper = DBL_MAX;
593 if(!lower.empty()) {
594 if(!lower.toDouble(double_lower))
595 fatalError2("%s: illegal chars for lower bound in '%s'!\n", line_context, lower.c_str());
596 }
597 else double_lower = -DBL_MAX;
598 if(tOutOfRange(double_default, double_lower, double_upper))
599 fatalError3("%s: default value out of range! (%g not in %s)!\n", line_context, double_default, Range2Str(double_lower, double_upper).c_str());
600 break;
601 case STRING:
602 // all strings are valid: most generic type!
603 string_default = def;
604 string_value = string_default;
605 break;
606 case TACO_TYPE_NONE:
607 fatalError("internal error! (invalid type id)\n");
608 }
609 set_in = DEFAULT;
610 }
611
612 void TAppConfigItem::setComp(const tvector<tstring>& a, bool privat) {
613 if(a.size() > 2)
614 fatalError2("%s: syntax error near component '%s'! (too many '=')\n",
615 context.c_str(), join(a,"=").c_str());
616 if(a.size() == 0)
617 fatalError2("%s: syntax error near component '%s'! (no component)\n",
618 context.c_str(), join(a,"=").c_str());
619 if((a.size()==1) && (a[0].consistsOfSpace())) return;
620 tstring comp = a[0];
621 tstring param;
622 if(a.size()>1) {
623 param=a[1];
624 param.unquote();
625 }
626 if(comp=="name") { name = param;
627 } else if(comp=="help") { help = param;
628 } else if(comp=="headline") { headline = param;
629 } else if(comp=="param") { par = param;
630 } else if(comp=="char") { char_name = param;
631 } else if(comp=="alias") { alias = split(param, "; ,", true, true);
632 } else if(comp=="type") { type_str = param;
633 } else if(comp=="default") { def = param;
634 } else if(comp=="upper") { upper = param;
635 } else if(comp=="lower") { lower = param;
636 } else if(comp=="musthave") { must_have = true;
637 } else if(comp=="shouldhave") { should_have = true;
638 } else if(comp=="string-mode-append") { string_mode = APPEND;
639 } else if(comp=="string-mode-once") { string_mode = ONCE;
640 } else if(comp=="string-append-separator") { string_sep = param;
641 } else if(privat && comp=="configopt") { configopt = true;
642 } else if(comp=="save") { save = true;
643 } else if(comp=="optional_param") { optional_param = true;
644 } else if(comp=="hide") { hide = true;
645 } else if(comp=="hidden") { hide = true;
646 } else if(comp=="onlycl") { only_cl = true;
647 } else if(privat && comp=="onlyapp") { only_app = true;
648 } else fatalError2("%s: unknown component '%s'!\n", context.c_str(), comp.c_str());
649 }
650
651 tstring TAppConfigItem::getWasSetStr(TACO_SET_IN setin, const tstring& env, const tstring& rcfile) const {
652 switch(setin) {
653 case DEFAULT:
654 return "by default";
655 case COMMAND_LINE:
656 return "on command line";
657 case ENVIRONMENT:
658 return "in environment variable '" + env + "'";
659 case RC_FILE:
660 return "in rc file '" + rcfile + "'";
661 default:
662 fatalError1("illegal value %d in setin! (0==NEVER)\n", setin);
663 #ifndef __GNUC__
664 return "";
665 #endif
666 }
667 }
668
669 tstring TAppConfigItem::getWasSetStr(const tstring& env, const tstring& rcfile) const {
670 return getWasSetStr(set_in, env, rcfile);
671 }
672
673 void TAppConfigItem::printValue(const tstring& env, const tstring& rcfile) const {
674 switch(type) {
675 case SWITCH:
676 if(bool_value)
677 printf("switch %s was set %s\n", name.c_str(), getWasSetStr(env, rcfile).c_str());
678 else
679 printf("switch %s was not set\n", name.c_str());
680 break;
681
682 case BOOL:
683 printf("bool %s was set to %s %s\n", name.c_str(), bool_value?"true":"false", getWasSetStr(env, rcfile).c_str());
684 break;
685
686 case INT:
687 printf("int %s was set to %d %s\n", name.c_str(), int_value, getWasSetStr(env, rcfile).c_str());
688 break;
689
690 case DOUBLE:
691 printf("double %s was set to %g %s\n", name.c_str(), double_value, getWasSetStr(env, rcfile).c_str());
692 break;
693
694 case STRING:
695 {
696 tstring s(string_value);
697 s.expandUnprintable('"');
698 printf("string %s was set to \"%s\" %s\n", name.c_str(), s.c_str(), getWasSetStr(env, rcfile).c_str());
699 }
700 break;
701
702 default:
703 fatalError1("printValue(): Illegal value %d in type! (0==TACO_TYPE_NONE)\n", type);
704 }
705 }
706
707 tstring TAppConfigItem::getParamStr() const {
708 if(!par.empty()) return par;
709 switch(type) {
710 case BOOL:
711 return "<bool>";
712 case INT:
713 return "<int>";
714 case DOUBLE:
715 return "<double>";
716 case STRING:
717 return "<string>";
718 case SWITCH:
719 return "";
720 default:
721 fatalError1("getParamStr(): Illegal value %d in type! (0==TACO_TYPE_NONE)\n", type);
722 #ifndef __GNUC__
723 return "";
724 #endif
725 }
726 }
727
728
729 tstring TAppConfigItem::getTypeStr() const {
730 switch(type) {
731 case BOOL:
732 return "bool";
733 case INT:
734 return "int";
735 case DOUBLE:
736 return "double";
737 case STRING:
738 return "string";
739 case SWITCH:
740 return "switch";
741 default:
742 fatalError1("getTypeStr(): Illegal value %d in type! (0==TACO_TYPE_NONE)\n", type);
743 #ifndef __GNUC__
744 return "";
745 #endif
746 }
747 }
748
749 int TAppConfigItem::getOptLen() const {
750 int l = getParamStr().len();
751 if(l) l++;
752 if(l) if(optional_param) l += 2;
753 return name.len() + l;
754 }
755
756 tstring TAppConfigItem::getFlagsStr(const tstring& optprefix,
757 bool globalonlycl) const
758 {
759 tstring h;
760 tstring range;
761 switch(type) {
762 case DOUBLE:
763 range = Range2Str(double_lower, double_upper);
764 break;
765 case INT:
766 range = Range2Str(int_lower, int_upper);
767 break;
768 default:
769 break;
770 }
771 tstring defval;
772 tstring string_mod;
773 switch(type) {
774 case DOUBLE:
775 if(double_default!=0.0) defval = def;
776 break;
777 case INT:
778 if(int_default!=0) defval = def;
779 break;
780 case STRING:
781 {
782 tstring s(string_default);
783 s.expandUnprintable('"');
784 if(!s.empty()) defval = "\"" + s + "\"";
785 }
786 switch(string_mode) {
787 case APPEND: string_mod = "multiple allowed"; break;
788 case ONCE: string_mod = "only once"; break;
789 default: break;
790 }
791 break;
792 case BOOL:
793 if(!def.empty()) defval = def; // take string to allow: yes true on 1 ...
794 break;
795 default:
796 break;
797 }
798 if((only_cl && (!globalonlycl)) || must_have || should_have ||
799 (alias.size()>0) || (!range.empty()) || (!defval.empty()) || (!string_mod.empty()) || optional_param || hide) {
800 h += "(";
801 if(only_cl && (!globalonlycl)) h += "only command line";
802 if(optional_param) {
803 if(h.lastChar()!='(') h+= ", ";
804 h += "parameter is optional";
805 }
806 if(must_have) {
807 if(h.lastChar()!='(') h+= ", ";
808 h += "required";
809 }
810 if(should_have) {
811 if(h.lastChar()!='(') h+= ", ";
812 h += "recommended";
813 }
814 if(hide) {
815 if(h.lastChar()!='(') h+= ", ";
816 h += "hidden";
817 }
818 if(alias.size()==1) {
819 if(h.lastChar()!='(') h+= ", ";
820 if((alias[0].len()==2) && (alias[0][0]=='-')) h += "alias=" + alias[0];
821 else h += "alias=" + optprefix + alias[0];
822 }
823 if(alias.size()>1) {
824 if(h.lastChar()!='(') h+= ", ";
825 h += "aliases={";
826 for(size_t i=0; i < alias.size(); i++) {
827 if((alias[i].len()==2) && (alias[i][0]=='-')) h += alias[i];
828 else h += optprefix + alias[i];
829 if(i < alias.size()-1) h+=", ";
830 }
831 h += "}";
832 }
833 if(!range.empty()) {
834 if(h.lastChar()!='(') h+= ", ";
835 h += "range=" + range;
836 }
837 if(!defval.empty()) {
838 if(h.lastChar()!='(') h+= ", ";
839 h += "default=" + defval;
840 }
841 if(!string_mod.empty()) {
842 if(h.lastChar()!='(') h+= ", ";
843 h += string_mod;
844 }
845 h += ")";
846 }
847 return h;
848 }
849
850 void TAppConfigItem::printItemToFile(FILE *f) const {
851 if(!headline.empty()) { // print headline
852 fprintf(f, "\n# %.76s\n# %.76s\n\n", headline.c_str(), tstring('=', headline.len()).c_str());
853 }
854 tstring h(help + "\n");
855 if(type == SWITCH) {
856 h += "uncomment next line to activate switch";
857 } else {
858 h += "parameter is of type " + getTypeStr();
859 }
860 h += " " + getFlagsStr("", false);
861 while(!h.empty()) {
862 fprintf(f, "# %s\n", h.getFitWords(80 - 2 - 1).c_str());
863 }
864 tstring defval;
865 switch(type) {
866 case DOUBLE:
867 defval = tstring(double_default);
868 break;
869 case INT:
870 defval = tstring(int_default);
871 break;
872 case STRING:
873 {
874 tstring s(string_default);
875 s.expandUnprintable('"');
876 defval = "\"" + s + "\"";
877 }
878 break;
879 case BOOL:
880 if(!def.empty()) defval = def; // take string to allow yes true on 1 ...
881 else defval = bool_default?"true":"false";
882 break;
883 default:
884 break;
885 }
886 if(type == SWITCH) {
887 fprintf(f, "#%s\n", name.c_str());
888 } else {
889 fprintf(f, "%s = %s\n", name.c_str(), defval.c_str());
890 }
891 fprintf(f, "\n");
892 }
893
894
895 void TAppConfigItem::printCurItemToFile(FILE *f, bool simple) const {
896 if(!simple) {
897 if(!headline.empty()) { // print headline
898 fprintf(f, "\n# %.76s\n# %.76s\n\n", headline.c_str(), tstring('=', headline.len()).c_str());
899 }
900 tstring h(help + "\n");
901 if(type==SWITCH) h += "parameter is a switch";
902 else {
903 h += "parameter is of type " + getTypeStr();
904 h += " " + getFlagsStr("", false);
905 }
906 while(!h.empty()) {
907 fprintf(f, "# %s\n", h.getFitWords(80 - 2 - 1).c_str());
908 }
909 }
910 tstring val;
911 switch(type) {
912 case DOUBLE:
913 val = tstring(double_value);
914 break;
915 case INT:
916 val = tstring(int_value);
917 break;
918 case STRING:
919 {
920 tstring s(string_value);
921 s.expandUnprintable('"');
922 val = "\"" + s + "\"";
923 }
924 break;
925 case BOOL:
926 val = bool_value?"true":"false";
927 break;
928 default:
929 break;
930 }
931 if(type == SWITCH) {
932 if(bool_value)
933 fprintf(f, "%s\n", name.c_str());
934 else
935 fprintf(f, "#%s\n", name.c_str());
936 } else {
937 fprintf(f, "%s = %s\n", name.c_str(), val.c_str());
938 }
939 if(!simple) fprintf(f, "\n");
940 }
941
942
943 void TAppConfigItem::printHelp(int max_optlen, bool globalonlycl) const {
944 int hcol = max_optlen + 5 + 2;
945 char buf[256];
946 int width = 80;
947 #ifndef NOWINSIZE
948 struct winsize win;
949 if(ioctl(1, TIOCGWINSZ, &win) == 0) {
950 width = win.ws_col;
951 }
952 #endif
953 if(width < hcol + 8) width = hcol + 8;
954 if(!headline.empty()) {
955 printf("\n%s\n", headline.c_str());
956 }
957 sprintf(buf, "%c%c --%s%s%s%s", (!char_name.empty())?'-':' ', (!char_name.empty())?char_name[0]:' ',
958 name.c_str(), optional_param?"[":"", type==SWITCH?"":("=" + getParamStr()).c_str(), optional_param?"]":"");
959 tstring h(help);
960 h += " " + getFlagsStr("--", globalonlycl);
961 printf("%*s%s\n", -hcol, buf, h.getFitWords(width - hcol - 1).c_str());
962 while(!h.empty()) {
963 printf("%*s%s\n", -hcol, "", h.getFitWords(width - hcol - 1).c_str());
964 }
965 }
966
967
968 void TAppConfigItem::setValue(const tstring& parameter, TACO_SET_IN setin,
969 bool verbose, const tstring& env,
970 const tstring& rcfile, const tstring& line_context) {
971 if(only_app)
972 userError("internal parameter name '%s' is private to the application may not be set by the user!\n", name.c_str());
973 if(only_cl && (!setin==COMMAND_LINE))
974 userError("parameter name '%s' is only available on the command line!\n", name.c_str());
975
976 // remember verbatim value for non string parameters in string_value
977 if(type != STRING) string_value = parameter;
978
979 // prepare option string
980 tstring option;
981 if(setin==COMMAND_LINE) {
982 option = "option -" + char_name + ", --" + name;
983 } else {
984 option = "option '" + name + "'";
985 }
986
987 if(set_in!=DEFAULT) {
988 if(!((type==STRING)&&(string_mode!=OVERRIDE))) {
989 if(verbose)
990 printf("warning: %s (set %s) is overridden %s\n",
991 option.c_str(), getWasSetStr(setin, env, rcfile).c_str(),
992 getWasSetStr(env, rcfile).c_str());
993 return;
994 }
995 }
996
997 switch(type) {
998 case SWITCH:
999 if(!parameter.empty())
1000 userError("%s: %s is a switch and takes no parameter!\n", line_context.c_str(), option.c_str());
1001 bool_value = true;
1002 break;
1003
1004 case BOOL:
1005 if(!parameter.toBool(bool_value))
1006 userError("%s: illegal/unknown bool value '%s' for %s!\n(can be [true|yes|on|t|1|false|no|off|f|0])\n", line_context.c_str(), parameter.c_str(), option.c_str());
1007 break;
1008
1009 case INT:
1010 if(!parameter.toInt(int_value))
1011 userError("%s: illegal int value format for %s in '%s'!\n", line_context.c_str(), option.c_str(), parameter.c_str());
1012 if(tOutOfRange(int_value, int_lower, int_upper))
1013 userError("%s: value out of range for %s! (%d not in %s)!\n", line_context.c_str(), option.c_str(), int_value, Range2Str(int_lower, int_upper).c_str());
1014 break;
1015
1016 case DOUBLE:
1017 if(!parameter.toDouble(double_value))
1018 userError("%s: illegal double value format for %s in '%s'!\n", line_context.c_str(), option.c_str(), parameter.c_str());
1019 if(tOutOfRange(double_value, double_lower, double_upper))
1020 userError("%s: value out of range for %s! (%g not in %s)!\n", line_context.c_str(), option.c_str(), double_value, Range2Str(double_lower, double_upper).c_str());
1021 break;
1022
1023 case STRING:
1024 switch(string_mode) {
1025 case ONCE:
1026 if(set_in != DEFAULT)
1027 userError("%s: %s may only be set once! (was already set to '%s' and was tried to set to '%s')\n", line_context.c_str(), option.c_str(), string_value.c_str(), parameter.c_str());
1028 string_value = parameter;
1029 break;
1030 case APPEND:
1031 if((set_in != DEFAULT)||(!string_value.empty()))
1032 string_value += string_sep;
1033 string_value += parameter;
1034 break;
1035 case OVERRIDE:
1036 string_value = parameter; // overwrite old value, the default
1037 break;
1038 }
1039 break;
1040
1041 default:
1042 fatalError1("setValue(): Illegal value %d in type! (0==TACO_TYPE_NONE)\n", type);
1043 }
1044 set_in = setin;
1045 }
1046
1047
1048 void TAppConfigItem::setValueFromApp(bool parameter) {
1049 if(only_app)
1050 fatalError1("internal parameter name '%s' is private to tapplication may not be set by the user!\n", name.c_str());
1051 if(only_cl)
1052 fatalError1("parameter name '%s' is only available on the command line!\n", name.c_str());
1053 set_in = APPLICATION;
1054 if((type==SWITCH)||(type==BOOL)) {
1055 bool_value=parameter;
1056 } else {
1057 fatalError1("setValueFromApp(bool/switch): type mismatch: type was %s\n", getTypeStr().c_str());
1058 }
1059 }
1060
1061
1062 // returns true if value is valid, else false
1063 bool TAppConfigItem::setValueFromApp(int i) {
1064 if(only_app)
1065 fatalError1("internal parameter name '%s' is private to tapplication may not be set by the user!\n", name.c_str());
1066 if(only_cl)
1067 fatalError1("parameter name '%s' is only available on the command line!\n", name.c_str());
1068 set_in = APPLICATION;
1069 if(type==INT) {
1070 if(i > int_upper) {
1071 int_value = int_upper;
1072 return false;
1073 }
1074 if(i < int_lower) {
1075 int_value = int_lower;
1076 return false;
1077 }
1078 int_value = i;
1079 return true;
1080 } else {
1081 fatalError1("setValueFromApp(int): type mismatch: type was %s\n", getTypeStr().c_str());
1082 #ifndef __GNUC__
1083 return false;
1084 #endif
1085 }
1086 }
1087
1088
1089 // returns true if value is valid, else false
1090 bool TAppConfigItem::setValueFromApp(double d) {
1091 if(only_app)
1092 fatalError1("internal parameter name '%s' is private to tapplication may not be set by the user!\n", name.c_str());
1093 if(only_cl)
1094 fatalError1("parameter name '%s' is only available on the command line!\n", name.c_str());
1095 set_in = APPLICATION;
1096 if(type==DOUBLE) {
1097 if(d > double_upper) {
1098 double_value = double_upper;
1099 return false;
1100 }
1101 if(d < double_lower) {
1102 double_value = double_lower;
1103 return false;
1104 }
1105 double_value = d;
1106 return true;
1107 } else {
1108 fatalError1("setValueFromApp(double): type mismatch: type was %s\n", getTypeStr().c_str());
1109 #ifndef __GNUC__
1110 return false;
1111 #endif
1112 }
1113 }
1114
1115
1116 void TAppConfigItem::setValueFromApp(const tstring& str) {
1117 if(only_app)
1118 fatalError1("internal parameter name '%s' is private to tapplication may not be set by the user!\n", name.c_str());
1119 if(only_cl)
1120 fatalError1("parameter name '%s' is only available on the command line!\n", name.c_str());
1121 set_in = APPLICATION;
1122 if(type==STRING) {
1123 string_value = str;
1124 } else {
1125 fatalError1("setValueFromApp(string): type mismatch: type was %s\n", getTypeStr().c_str());
1126 }
1127 }
1128
1129
1130 // returns true if value is valid, else false
1131 // sets value from string according to any type (switch == bool here)
1132 bool TAppConfigItem::setValueFromAppFromStr(const tstring& parameter) {
1133 if(only_app)
1134 fatalError1("internal parameter name '%s' is private to tapplication may not be set by the user!\n", name.c_str());
1135 if(only_cl)
1136 fatalError1("parameter name '%s' is only available on the command line!\n", name.c_str());
1137 set_in = APPLICATION;
1138 switch(type) {
1139 case SWITCH:
1140 case BOOL:
1141 if(!parameter.toBool(bool_value))
1142 fatalError2("SetValueFromAppFromStr: illegal/unknown bool value '%s' for %s!\n(can be [true|yes|on|t|1|false|no|off|f|0])\n", parameter.c_str(), name.c_str());
1143 return true;
1144
1145 case INT:
1146 if(!parameter.toInt(int_value))
1147 fatalError2("SetValueFromAppFromStr: illegal int value format for %s in '%s'!\n", name.c_str(), parameter.c_str());
1148 return setValueFromApp(int_value);
1149
1150 case DOUBLE:
1151 if(!parameter.toDouble(double_value))
1152 fatalError2("SetValueFromAppFromStr: illegal double value format for %s in '%s'!\n", name.c_str(), parameter.c_str());
1153 return setValueFromApp(double_value);
1154
1155 case STRING:
1156 string_value = parameter;
1157 return true;
1158
1159 default:
1160 fatalError1("SetValueFromStrFromApp(): Illegal value %d in type! (0==TACO_TYPE_NONE)\n", type);
1161 #ifndef __GNUC__
1162 return false;
1163 #endif
1164 }
1165 }
1166
1167
1168
1169 // TAppConfig implementation
1170
1171 TAppConfig::TAppConfig(const char *conflist[], const char *listname,
1172 int argc, char *argv[],
1173 const char *envstrname,
1174 const char *rcname,
1175 const tstring& version):
1176 _params(),
1177 name(),
1178 opt(),
1179 alias(),
1180 envname(),
1181 rc_name(),
1182 rc_str(rcname?rcname:""),
1183 verbose_conf(false),
1184 onlycl(false),
1185 stopatdd(false),
1186 removedd(false),
1187 ignore_negnum(false),
1188 usage(),
1189 trailer(),
1190 commonhead()
1191 {
1192 if(tApp)
1193 fatalError("only exactly one instance of TAppConfig is allowd!\n");
1194 for(int i = 0; i < 256; i++) char2index[i] = -1;
1195 stopatdd = onlycl = verbose_conf = false;
1196 addConfigItems(conflist, listname, false);
1197 addConfigItems(self_conflist, "self_conflist", true);
1198 doCommandLine(argc, argv, version);
1199 for(size_t i=0; i<opt.size(); i++)
1200 opt[i].help.searchReplace("%n", getString("application-name"));
1201 if(getBool("help")) printHelp(false);
1202 if(getBool("hhelp")) printHelp(true);
1203 if(getBool("version")) printf("%s version %s\n", getString("application-name").c_str(),
1204 getString("application-version").c_str());
1205 tstring creatercfile;
1206 if(!onlycl) {
1207 creatercfile = getString("create-rc");
1208 verbose_conf = getBool("verbose-config");
1209 if(!creatercfile.empty()) createRCFile(creatercfile, rcname);
1210 if(envstrname) doEnvironVar(envstrname);
1211 if(rcname) doRCFile(rcname, getString("rc-file"));
1212 if(verbose_conf) printValues();
1213 }
1214 if(verbose_conf || getBool("help") || getBool("hhelp") || getBool("version") || (!creatercfile.empty()))
1215 exit(0);
1216 for(size_t i = 0; i < opt.size(); i++) {
1217 if(opt[i].must_have && (opt[i].set_in==TAppConfigItem::DEFAULT))
1218 userError("error: option '%s' must be specified somewhere!\n",
1219 opt[i].name.c_str());
1220 if(opt[i].should_have && (opt[i].set_in==TAppConfigItem::DEFAULT))
1221 userWarning("warning: option '%s' should be specified somewhere\n",
1222 opt[i].name.c_str());
1223 }
1224 // set global data
1225 tApp = this;
1226 terrorApplicationName = new char[getString("application-name").len()+1];
1227 strcpy((char *)terrorApplicationName, getString("application-name").c_str());
1228 }
1229
1230
1231 TAppConfig::~TAppConfig() {
1232 tApp = 0; // avoid dangling pointer to global application configuration;
1233 delete[] terrorApplicationName; // delete application name in terror
1234 }
1235
1236
1237 // save some items
1238 // return rcname in rc_name_out
1239 bool TAppConfig::save(tstring *rc_name_out) {
1240 if(rc_name_out)
1241 *rc_name_out = "$(HOME)/." + rc_str;
1242 char *home = getenv("HOME");
1243 if(home==0) {
1244 fprintf(stderr, "can't save config: no HOME variable defined!\n");
1245 return false;
1246 }
1247 tstring realname(tstring(home) + "/." + rc_str);
1248 if(rc_name_out)
1249 *rc_name_out = realname;
1250 tstring tmpname(realname + ".tmp");
1251 FILE *tmpf = fopen(tmpname.c_str(), "w");
1252 if(tmpf==0) {
1253 fprintf(stderr, "can't save config: can't open '%s' for writing!\n", tmpname.c_str());
1254 return false;
1255 }
1256
1257 // init
1258 for(size_t i = 0; i < opt.size(); i++) opt[i].printed = false;
1259 int pri = 0;
1260
1261 // update old config
1262 FILE *oldf = fopen(realname.c_str(), "r");
1263 if(oldf) {
1264 if(verbose_conf)
1265 printf("updating items in '%s'\n", realname.c_str());
1266 char buf[1024];
1267 while(fgets(buf, sizeof(buf), oldf)) {
1268 size_t j;
1269 tstring line(buf);
1270 if(strchr(";\\!", buf[0]) || line.consistsOfSpace()) { // copy comment
1271 fputs(buf, tmpf);
1272 } else if(buf[0]=='#') { // check for commented out switches
1273 tstring sw(buf+1);
1274 sw.cropSpace();
1275 if(alias.contains(sw)||name.contains(sw)) {
1276 line = sw;
1277 goto upd;
1278 } else {
1279 fputs(buf, tmpf);
1280 }
1281 } else { // change parameter
1282 j = line.firstOccurence('=');
1283 if(j == tstring::npos) {
1284 line.cropSpace();
1285 for(j = 0; j < line.len() && (!isspace(line[j])); j++);
1286 }
1287 line.truncate(j);
1288 upd:
1289 line.cropSpace();
1290 if(alias.contains(line)) line = opt[alias[line]].name;
1291 if(name.contains(line)) { // valid name found
1292 TAppConfigItem& a(opt[name[line]]);
1293 if((!a.only_app)&&(!a.only_cl)) {
1294 if(a.save) {
1295 a.printCurItemToFile(tmpf, true);
1296 a.printed = true;
1297 } else {
1298 fputs(buf, tmpf);
1299 }
1300 }
1301 } else {
1302 fprintf(stderr, "discarding invalid line from file '%s' :%s", realname.c_str(), buf);
1303 }
1304 }
1305 pri++;
1306 }
1307 fclose(oldf);
1308 }
1309
1310 // write any remaining items?
1311 int rem=0;
1312 for(size_t i = 0; i < opt.size(); i++) {
1313 const TAppConfigItem& a(opt[i]);
1314 if(a.only_app) continue;
1315 if(a.only_cl) continue;
1316 if(a.printed) continue;
1317 if(!a.save) continue;
1318 rem++;
1319 }
1320 if(rem) {
1321 if(verbose_conf)
1322 printf("writing %d remaining items to '%s'\n", rem, realname.c_str());
1323 if(pri) {
1324 fprintf(tmpf, "\n\n\n# the following items have been appended by '%s' V%s\n", getString("application-name").c_str(), getString("application-version").c_str());
1325 } else {
1326 fprintf(tmpf, "# this file was created by '%s' V%s\n", getString("application-name").c_str(), getString("application-version").c_str());
1327 fprintf(tmpf, "# feel free to edit this file\n");
1328 }
1329 fprintf(tmpf, "\n\n");
1330 for(size_t i = 0; i < opt.size(); i++) {
1331 const TAppConfigItem& a(opt[i]);
1332 if(a.only_app) continue;
1333 if(a.only_cl) continue;
1334 if(a.printed) continue;
1335 if(!a.save) continue;
1336 a.printCurItemToFile(tmpf, false);
1337 }
1338 }
1339
1340 // finalize
1341 fclose(tmpf);
1342 if(::rename(tmpname.c_str(), realname.c_str())) {
1343 fprintf(stderr, "can't save config: can't move '%s' to '%s'!\n", tmpname.c_str(), realname.c_str());
1344 return false;
1345 }
1346 return true;
1347 }
1348
1349
1350 void TAppConfig::createRCFile(const tstring& fname, const tstring& rcname) const {
1351 FILE *f;
1352
1353 f = fopen(fname.c_str(), "w");
1354 if(f==NULL)
1355 userError("can't open '%s' for writing!\n", fname.c_str());
1356 fprintf(f, "# this is a machine generated rcfile for %s version %s\n",
1357 getString("application-name").c_str(), getString("application-version").c_str());
1358 fprintf(f, "# delete these lines and edit this file as soon as possible since all options\n");
1359 fprintf(f, "# are set to default values and all switches are disabled (commented out)\n\n\n\n");
1360 for(size_t i = 0; i < opt.size(); i++) {
1361 const TAppConfigItem& a(opt[i]);
1362 if(a.only_app) continue;
1363 if(a.only_cl) continue;
1364 a.printItemToFile(f);
1365 }
1366 fclose(f);
1367 tstring globalname = "/etc/" + rcname;
1368 tstring localname;
1369 char *home = getenv("HOME");
1370 if(home) {
1371 localname = tstring(home) + "/." + rcname;
1372 } else {
1373 localname = "($HOME)/." + rcname;
1374 }
1375 printf("you should now customize the rcfile '%s' and\nsave it as '%s' (or '%s' as root)\n",
1376 fname.c_str(), localname.c_str(), globalname.c_str());
1377 }
1378
1379
1380 tstring TAppConfig::getName(const tstring& str, const tstring& context1,
1381 const tstring& optprefix, bool errorIfNotFound) const {
1382 tstring context(context1);
1383 if(!context.empty()) context += ": ";
1384 if(str.len()==0)
1385 fatalError1("%sempty option name!\n", context.c_str());
1386 if(alias.contains(str)) return opt[alias[str]].name; // alias match
1387 if((str.len()==1)&&(optprefix!="--")) { // char name
1388 if(char2index[(unsigned char)str[0]] < 0)
1389 {
1390 if (errorIfNotFound)
1391 {
1392 userError("%sunknown char option name '%s'! (try --help)\n", context.c_str(), str.c_str());
1393 }
1394 else
1395 {
1396 userWarning("%sunknown char option name '%s'!\n", context.c_str(), str.c_str());
1397 return "";
1398 }
1399 }
1400 return opt[char2index[(unsigned char)str[0]]].name;
1401 }
1402 if(name.contains(str)) return str; // exact match
1403 tvector<tstring> found;
1404 for(size_t i = 0; i < opt.size(); i++) { // search for prefixes
1405 if(opt[i].name.hasPrefix(str) && (!opt[i].only_app))
1406 found += opt[i].name;
1407 }
1408 if(found.size()==0)
1409 {
1410 if (errorIfNotFound)
1411 {
1412 userError("%sunknown option '%s'! (try --help)\n", context.c_str(), (optprefix+str).c_str());
1413 }
1414 else
1415 {
1416 userWarning("%sunknown option '%s'!\n", context.c_str(), (optprefix+str).c_str());
1417 return "";
1418 }
1419 }
1420 if(found.size() != 1) {
1421 tstring option = optprefix + str;
1422 tstring list = optprefix + join(found, (", "+optprefix));
1423 userError("%sambiguous option '%s' matches '%s'!\n", context.c_str(), option.c_str(), list.c_str());
1424 }
1425 return found[0]; // uniq abbrevation
1426 }
1427
1428
1429 bool TAppConfig::getBool(const tstring &n) const {
1430 if(name.contains(n)) {
1431 const TAppConfigItem& a(opt[name[n]]);
1432 if((a.type==TAppConfigItem::BOOL) || (a.type==TAppConfigItem::SWITCH)) return a.bool_value;
1433 else fatalError2("type mismatch in call for %s '%s' as bool!\n", a.getTypeStr().c_str(), n.c_str());
1434 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1435 #ifndef __GNUC__
1436 return false;
1437 #endif
1438 }
1439
1440
1441 void TAppConfig::setValue(const tstring &n, bool b) {
1442 if(name.contains(n)) {
1443 opt[name[n]].setValueFromApp(b);
1444 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1445 }
1446
1447
1448 void TAppConfig::setValue(const tstring &n, const tstring& str) {
1449 if(name.contains(n)) {
1450 opt[name[n]].setValueFromApp(str);
1451 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1452 }
1453
1454
1455 bool TAppConfig::setValue(const tstring &n, int i) {
1456 if(name.contains(n)) {
1457 return opt[name[n]].setValueFromApp(i);
1458 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1459 #ifndef __GNUC__
1460 return false;
1461 #endif
1462 }
1463
1464
1465 bool TAppConfig::setValue(const tstring &n, double d) {
1466 if(name.contains(n)) {
1467 return opt[name[n]].setValueFromApp(d);
1468 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1469 #ifndef __GNUC__
1470 return false;
1471 #endif
1472 }
1473
1474
1475 bool TAppConfig::setValueFromStr(const tstring &n, const tstring& str) {
1476 if(name.contains(n)) {
1477 return opt[name[n]].setValueFromAppFromStr(str);
1478 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1479 #ifndef __GNUC__
1480 return false;
1481 #endif
1482 }
1483
1484
1485 const tstring& TAppConfig::getString(const tstring &n) const {
1486 if(name.contains(n)) {
1487 const TAppConfigItem& a(opt[name[n]]);
1488 return a.string_value;
1489 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1490 #ifndef __GNUC__
1491 static tstring s;
1492 return s;
1493 #endif
1494 }
1495
1496
1497 int TAppConfig::getInt(const tstring &n) const {
1498 if(name.contains(n)) {
1499 const TAppConfigItem& a(opt[name[n]]);
1500 if(a.type==TAppConfigItem::INT) return a.int_value;
1501 else fatalError2("type mismatch in call for %s '%s' as int!\n", a.getTypeStr().c_str(), n.c_str());
1502 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1503 #ifndef __GNUC__
1504 return 0;
1505 #endif
1506 }
1507
1508
1509 int TAppConfig::intUpper(const tstring &n) const {
1510 if(name.contains(n)) {
1511 const TAppConfigItem& a(opt[name[n]]);
1512 if(a.type==TAppConfigItem::INT) return a.int_upper;
1513 else fatalError2("type mismatch in call for %s '%s' as int!\n", a.getTypeStr().c_str(), n.c_str());
1514 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1515 #ifndef __GNUC__
1516 return 0;
1517 #endif
1518 }
1519
1520
1521 int TAppConfig::intLower(const tstring &n) const {
1522 if(name.contains(n)) {
1523 const TAppConfigItem& a(opt[name[n]]);
1524 if(a.type==TAppConfigItem::INT) return a.int_lower;
1525 else fatalError2("type mismatch in call for %s '%s' as int!\n", a.getTypeStr().c_str(), n.c_str());
1526 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1527 #ifndef __GNUC__
1528 return 0;
1529 #endif
1530 }
1531
1532
1533 int TAppConfig::intDefault(const tstring &n) const {
1534 if(name.contains(n)) {
1535 const TAppConfigItem& a(opt[name[n]]);
1536 if(a.type==TAppConfigItem::INT) return a.int_default;
1537 else fatalError2("type mismatch in call for %s '%s' as int!\n", a.getTypeStr().c_str(), n.c_str());
1538 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1539 #ifndef __GNUC__
1540 return 0;
1541 #endif
1542 }
1543
1544
1545 double TAppConfig::getDouble(const tstring &n) const {
1546 if(name.contains(n)) {
1547 const TAppConfigItem& a(opt[name[n]]);
1548 if(a.type==TAppConfigItem::DOUBLE) return a.double_value;
1549 else fatalError2("type mismatch in call for %s '%s' as double!\n", a.getTypeStr().c_str(), n.c_str());
1550 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1551 #ifndef __GNUC__
1552 return 0;
1553 #endif
1554 }
1555
1556
1557 double TAppConfig::doubleUpper(const tstring &n) const {
1558 if(name.contains(n)) {
1559 const TAppConfigItem& a(opt[name[n]]);
1560 if(a.type==TAppConfigItem::DOUBLE) return a.double_upper;
1561 else fatalError2("type mismatch in call for %s '%s' as double!\n", a.getTypeStr().c_str(), n.c_str());
1562 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1563 #ifndef __GNUC__
1564 return 0;
1565 #endif
1566 }
1567
1568
1569 double TAppConfig::doubleLower(const tstring &n) const {
1570 if(name.contains(n)) {
1571 const TAppConfigItem& a(opt[name[n]]);
1572 if(a.type==TAppConfigItem::DOUBLE) return a.double_lower;
1573 else fatalError2("type mismatch in call for %s '%s' as double!\n", a.getTypeStr().c_str(), n.c_str());
1574 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1575 #ifndef __GNUC__
1576 return 0;
1577 #endif
1578 }
1579
1580
1581 double TAppConfig::doubleDefault(const tstring &n) const {
1582 if(name.contains(n)) {
1583 const TAppConfigItem& a(opt[name[n]]);
1584 if(a.type==TAppConfigItem::DOUBLE) return a.double_default;
1585 else fatalError2("type mismatch in call for %s '%s' as double!\n", a.getTypeStr().c_str(), n.c_str());
1586 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1587 #ifndef __GNUC__
1588 return 0;
1589 #endif
1590 }
1591
1592
1593 tstring TAppConfig::stringDefault(const tstring &n) const {
1594 if(name.contains(n)) {
1595 const TAppConfigItem& a(opt[name[n]]);
1596 if(a.type==TAppConfigItem::STRING) return a.string_default;
1597 else fatalError2("type mismatch in call for %s '%s' as string!\n", a.getTypeStr().c_str(), n.c_str());
1598 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1599 #ifndef __GNUC__
1600 static tstring s;
1601 return s;
1602 #endif
1603 }
1604
1605
1606 bool TAppConfig::boolDefault(const tstring &n) const {
1607 if(name.contains(n)) {
1608 const TAppConfigItem& a(opt[name[n]]);
1609 if(a.type==TAppConfigItem::BOOL) return a.bool_default;
1610 else fatalError2("type mismatch in call for %s '%s' as bool!\n", a.getTypeStr().c_str(), n.c_str());
1611 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1612 #ifndef __GNUC__
1613 return false;
1614 #endif
1615 }
1616
1617
1618 bool TAppConfig::getSwitch(const tstring &n) const {
1619 return getBool(n);
1620 }
1621
1622
1623 bool TAppConfig::operator() (const tstring &n) const {
1624 return getBool(n);
1625 }
1626
1627
1628 TAppConfigItem::TACO_SET_IN TAppConfig::wasSetIn(const tstring &n) const {
1629 if(name.contains(n)) {
1630 const TAppConfigItem& a(opt[name[n]]);
1631 return a.set_in;
1632 } else fatalError1("unknown parameter name '%s'\n", n.c_str());
1633 #ifndef __GNUC__
1634 // will never come here, avoid warning
1635 return TAppConfigItem::NEVER;
1636 #endif
1637 }
1638
1639
1640 bool TAppConfig::wasSetByUser(const tstring &n) const
1641 {
1642 TAppConfigItem::TACO_SET_IN set = wasSetIn(n);
1643 return (set == TAppConfigItem::COMMAND_LINE) || (set == TAppConfigItem::RC_FILE);
1644 }
1645
1646
1647 void TAppConfig::setFromStr(const tstring& str, const tstring& context,
1648 TAppConfigItem::TACO_SET_IN setin) {
1649 // prepare name n and parameter par
1650 tstring n;
1651 tstring par;
1652 size_t scanner=0;
1653 str.skipSpace(scanner);
1654 n = str.scanUpTo(scanner, " \t\n=");
1655 str.skipSpace(scanner);
1656 str.perhapsSkipOneChar(scanner, '=');
1657 str.skipSpace(scanner);
1658 par = str.scanRest(scanner);
1659 par.cropSpace();
1660
1661 // set value
1662 if(!n.empty()) {
1663 // get name, ignore unknown parameters in config files with a warning
1664 n = getName(n, context, "", setin != TAppConfigItem::RC_FILE);
1665 if (n.empty())
1666 return; // ignore unknown parameters (warning already printed)
1667 TAppConfigItem& a(opt[name[n]]);
1668 if(a.type == TAppConfigItem::SWITCH) { // need no param
1669 if(str.contains('=')) par = "t"; // force error
1670 } else { // get param
1671 if(par.consistsOfSpace())
1672 userError("%s: missing parameter for option '%s'!\n",
1673 context.c_str(), n.c_str());
1674 }
1675 if(a.type == TAppConfigItem::STRING) {
1676 par.unquote();
1677 par.compileCString();
1678 }
1679 a.setValue(par, setin, verbose_conf, envname, rc_name, context);
1680 } else {
1681 userError("%s: syntax error, missing option name in '%s'!\n",
1682 context.c_str(), str.c_str());
1683 }
1684 }
1685
1686 void TAppConfig::doEnvironVar(const char *env) {
1687 envname = env;
1688 char *p = getenv(env);
1689 if(p) {
1690 if(tstring(p).consistsOfSpace()) {
1691 if(verbose_conf)
1692 printf("environment variable '%s' is empty\n", env);
1693 return;
1694 }
1695 if(verbose_conf)
1696 printf("parsing environment variable '%s'\n", env);
1697 tvector<tstring> a = split(p, ",", true, true);
1698 for(size_t i=0; i<a.size(); i++) {
1699 setFromStr(a[i], "environment '" + tstring(env) + "'", TAppConfigItem::ENVIRONMENT);
1700 }
1701 } else {
1702 if(verbose_conf)
1703 printf("environment variable '%s' not set\n", env);
1704 }
1705 }
1706
1707 void TAppConfig::doRCFile(const tstring& rcfile, const tstring& clrcfile) {
1708 tvector<tstring> a;
1709
1710 // which file ?
1711 if(!clrcfile.empty()) {
1712 if(fexists(clrcfile.c_str())) {
1713 if(verbose_conf)
1714 printf("parsing rc-file '%s'\n", clrcfile.c_str());
1715 rc_name = clrcfile;
1716 } else {
1717 userError("can't load rc-file '%s'!\n", clrcfile.c_str());
1718 }
1719 } else {
1720 tstring globalname = "/etc/" + rcfile;
1721 tstring localname;
1722 char *home = getenv("HOME");
1723 if(home) {
1724 localname = tstring(home) + "/." + rcfile;
1725 }
1726 if(fexists(localname.c_str())) {
1727 if(verbose_conf)
1728 printf("parsing local rc-file '%s'\n", localname.c_str());
1729 rc_name = localname;
1730 } else if(fexists(globalname.c_str())) {
1731 if(verbose_conf)
1732 printf("parsing global rc-file '%s'\n", globalname.c_str());
1733 rc_name = globalname;
1734 } else {
1735 if(verbose_conf)
1736 printf("neither '%s' nor '%s' exist: no rc-file found\n",
1737 localname.c_str(), globalname.c_str());
1738 return;
1739 }
1740 }
1741
1742 // load file
1743 a = loadTextFile(rc_name.c_str());
1744
1745 // parse rc-file
1746 for(size_t i = 0; i < a.size(); i++) {
1747 if(!strchr("#;!/", a[i][0])) { // ignore comment
1748 if(!a[i].consistsOfSpace())
1749 setFromStr(a[i], "rc-file '" + rc_name + "' (line " + tstring(int(i + 1)) + ")", TAppConfigItem::RC_FILE);
1750 }
1751 }
1752 }
1753
1754 void TAppConfig::doCommandLine(int ac, char *av[], const tstring& version) {
1755 // one of these chars follows a signgle dash '-' to make it a numeric arg
1756 // in the sense of 'ignore_negnum'
1757 const char *validnegnumchars="0123456789.";
1758
1759 // set version and name
1760 opt[name["application-name"]].string_value = av[0];
1761 opt[name["application-name"]].string_value.extractFilename();
1762 opt[name["application-version"]].string_value = version;
1763
1764 // parse command line
1765 bool nomoreoptions = false; // -- reached = false
1766 for(int i=1; i<ac; i++) {
1767 if((av[i][0]=='-') &&
1768 (!nomoreoptions) &&
1769 ((!ignore_negnum)||(strchr(validnegnumchars, av[i][1])==0))) {
1770 switch(av[i][1]) {
1771 case 0: // simple parameter '-'
1772 _params += av[i];
1773 break;
1774
1775 case '-': // long name parameter
1776 if(av[i][2]==0) { // simple parameter '--'
1777 if(stopatdd) {
1778 nomoreoptions = true; // suppress option scanning
1779 if(!removedd) _params += av[i];
1780 }
1781 } else {
1782 char *p = strchr(av[i], '=');
1783 tstring n(&av[i][2]);
1784 if(p) n.truncate(p - &av[i][2]);
1785 if(!n.empty()) {
1786 n = getName(n, onlycl?"":"command line", "--");
1787 TAppConfigItem& a(opt[name[n]]);
1788 tstring par;
1789 if(a.type==TAppConfigItem::SWITCH) { // need no param
1790 if(p) par = "t"; // force error
1791 } else { // get param
1792 if(p) par = &av[i][p-av[i]+1];
1793 else {
1794 if(a.optional_param) {
1795 par = a.def;
1796 } else {
1797 i++;
1798 if(i<ac) par = av[i];
1799 else userError("missing parameter for long option '--%s'! (try --help)\n", n.c_str());
1800 }
1801 }
1802 }
1803 par.compileCString();
1804 a.setValue(par, TAppConfigItem::COMMAND_LINE);
1805 } else {
1806 userError("syntax error: missing name in long option '%s'!\n", av[i]);
1807 }
1808 }
1809 break;
1810
1811 default: // short name parameter
1812 for(const char *p = &av[i][1]; *p; p++) {
1813 if(char2index[(unsigned char)*p] >= 0) { // valid short option
1814 TAppConfigItem& a(opt[char2index[(unsigned char)*p]]);
1815 tstring par;
1816 if(a.type != TAppConfigItem::SWITCH) { // get param
1817 if(p[1]) {
1818 par = &p[1];
1819 } else {
1820 i++;
1821 if(i<ac) par = av[i];
1822 else userError("%smissing parameter for option '-%s'! (try --help)\n",
1823 onlycl?"":"command line: ", p);
1824 }
1825 }
1826 a.setValue(par, TAppConfigItem::COMMAND_LINE);
1827 if(a.type != TAppConfigItem::SWITCH) break;
1828 } else {
1829 userError("%sunknown option '-%c'! (try --help)\n",
1830 onlycl?"":"command line: ", *p);
1831 }
1832 }
1833 break;
1834 }
1835 } else {
1836 _params += av[i]; // add simple parameter
1837 }
1838 }
1839 }
1840
1841 int TAppConfig::getMaxOptLen(bool show_hidden) const {
1842 int max = 0;
1843 for(size_t i=0; i<opt.size(); i++) {
1844 if((!opt[i].only_app) && (!(opt[i].configopt && onlycl)) && (show_hidden || (!opt[i].hide))) {
1845 int len = opt[i].getOptLen();
1846 if(len>max) max = len;
1847 }
1848 }
1849 return max;
1850 }
1851
1852
1853 void TAppConfig::printHelp(bool show_hidden) const {
1854 tstring ausage(usage);
1855 tstring atrailer(trailer);
1856 ausage.searchReplace("%n", getString("application-name"));
1857 ausage.searchReplace("%v", getString("application-version"));
1858 ausage.searchReplace("%e", TAPPCONFIG_EMAIL);
1859 ausage.searchReplace("%gpl", TAPPCONFIG_GPL);
1860 atrailer.searchReplace("%n", getString("application-name"));
1861 atrailer.searchReplace("%v", getString("application-version"));
1862 atrailer.searchReplace("%e", TAPPCONFIG_EMAIL);
1863 atrailer.searchReplace("%gpl", TAPPCONFIG_GPL);
1864 puts(ausage.c_str());
1865 int max = getMaxOptLen(show_hidden);
1866 for(size_t i=0; i<opt.size(); i++)
1867 if((!opt[i].only_app) && (!(opt[i].configopt && onlycl)) && (show_hidden || (!opt[i].hide))) {
1868 opt[i].printHelp(max, onlycl);
1869 }
1870 puts(atrailer.c_str());
1871 }
1872
1873
1874 void TAppConfig::printValues() const {
1875 for(size_t i=0; i<opt.size(); i++)
1876 if((!opt[i].only_app) && (!opt[i].only_cl))
1877 opt[i].printValue(envname, rc_name);
1878 }
1879
1880
1881 void TAppConfig::setComp(const tvector<tstring>& a, const tstring& context) {
1882 if((a.size()==1) && (a[0].consistsOfSpace())) return;
1883 tstring comp = a[0];
1884 tstring parameter;
1885 if(a.size()>1) {
1886 parameter=a[1];
1887 parameter.unquote();
1888 }
1889 if(a.size()>2) {
1890 tstring s = join(a, "=");
1891 s.expandUnprintable('\'');
1892 fatalError2("%s: syntax error near component '%s'! (too many '=')\n", context.c_str(), s.c_str());
1893 }
1894 if(comp=="usage") { usage = parameter;
1895 } else if(comp=="commonheadline") { commonhead = parameter;
1896 } else if(comp=="trailer") { trailer = parameter;
1897 } else if(comp=="onlycl") { onlycl = true;
1898 } else if(comp=="stopat--") { stopatdd = true;
1899 } else if(comp=="remove--") { removedd = true;
1900 } else if(comp=="ignore_negnum") { ignore_negnum = true;
1901 } else fatalError2("%s: unknown component '%s'!\n", context.c_str(), comp.c_str());
1902 }
1903
1904
1905 void TAppConfig::doMetaChar(const tstring& str, const tstring& context) {
1906 tvector<tstring> a = split(str, ",", true);
1907 for(size_t i=0; i < a.size(); i++) {
1908 setComp(split(a[i], "=", true, true), context);
1909 }
1910 }
1911
1912 void TAppConfig::addConfigItems(const char **list, const char *listname,
1913 bool privat) {
1914 for(int line=1; ; line++, list++) {
1915 // context string for errors
1916 tstring context("conflist " + tstring(listname) + " (line " + tstring(line) + ")");
1917
1918 // test for end of list (EOL)
1919 if(*list == 0)
1920 fatalError1("%s: list not terminated! (0 pointer, should be \"EOL\")\n", context.c_str());
1921 if(strcmp(*list, "EOL")==0) break;
1922 if(strcmp(*list, "#EOL")==0) break;
1923 if(strcmp(*list, "")==0) break;
1924 if(strlen(*list)<7)
1925 fatalError2("%s: line corrupt (too short) perhaps listitem or list not terminated!\n (was '%s') check listitem (must have at least type and name)\nor terminate list with \"EOL\"\n",
1926 context.c_str(), *list);
1927
1928 // meta char?
1929 if(*list[0] == '#') {
1930 doMetaChar((*list)+1, context);
1931 continue;
1932 }
1933
1934 // get config item
1935 TAppConfigItem a(*list, context.c_str(), privat);
1936
1937 // skip useless options
1938 if(onlycl && a.configopt) continue;
1939
1940 // register name
1941 if(name.contains(a.name))
1942 fatalError3("%s: duplicate name '%s' (previoulsy defined in %s)!\n",
1943 context.c_str(), a.name.c_str(), opt[name[a.name]].context.c_str());
1944 name[a.name] = opt.size();
1945
1946 // register char name
1947 if(!a.char_name.empty()) {
1948 if(char2index[(unsigned char)a.char_name[0]]>=0)
1949 fatalError3("%s: duplicate char name '%c' (previoulsy defined in %s)!\n",
1950 context.c_str(), a.char_name[0], opt[char2index[(unsigned char)a.char_name[0]]].context.c_str());
1951 char2index[(unsigned char)a.char_name[0]] = opt.size();
1952 }
1953
1954 // register aliases
1955 for(size_t i=0; i < a.alias.size(); i++) {
1956 if((a.alias[i].len() == 2) && (a.alias[i][0] == '-')) { // char alias
1957 if(char2index[(unsigned char)a.alias[i][1]]>=0)
1958 fatalError3("%s: duplicate char name for char alias '%c' (previoulsy defined in %s)!\n",
1959 context.c_str(), a.alias[i][1], opt[char2index[(unsigned char)a.alias[i][1]]].context.c_str());
1960 char2index[(unsigned char)a.alias[i][1]] = opt.size();
1961 } else { // name alias
1962 if(alias.contains(a.alias[i]))
1963 fatalError2("%s: duplicate alias '%s'!\n", context.c_str(), a.alias[i].c_str());
1964 alias[a.alias[i]] = opt.size();
1965 }
1966 }
1967
1968 // set headline for help option
1969 if(a.name == "help")
1970 a.headline = commonhead;
1971
1972 // add config item
1973 opt += a;
1974 }
1975 }
1976
1977