"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.
For more information about "tappconfig.cc" see the
Fossies "Dox" file reference documentation.
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