"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