"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