Main Page   Class Hierarchy   Data Structures   File List   Data Fields   Globals  

program.C

Go to the documentation of this file.
00001 // Program.C   -   command line user interface class
00002 //
00003 // History:
00004 //  0.0 16-aug-96  Initial release to get us going.                         PJT
00005 //                 Lot's of missing features,
00006 //                 such as the options mode (only key=val mode works) and
00007 //                 not much system keyword support. 
00008 //                 The code has a lot of memory leaks.
00009 //                 Also still missing a powerful String class. 
00010 //  0.1 10-apr-97  doc fixes, and initial integration into Starlab          PJT
00011 //  0.2  5-jun-97  enabled minimum match
00012 //
00013 //
00014 //
00015 
00016 #include "program.h"
00017 #include <string.h>
00018 #include <stdlib.h>
00019 
00020 #define PROGRAM_VERSION "Program:: Version 0.2 5-jun-97 PJT"
00021 
00022 #define MINMATCH
00023 
00024 
00025 // local functions, currently defined near the end of this file.
00026 //      they are all old NEMO routines and exist because of a lack
00027 //      of a real string class
00028 
00029 static int    xstrlen(void *xspt,int nbyt);
00030 static string parname(string arg);
00031 static string parvalue(string arg);
00032 static string parhelp(string arg);
00033 
00034 Program *Program::self = 0;               // catch those who don't call init
00035 
00036 
00037 Program::Program() {
00038     package = 0;
00039     progname = 0;
00040 }
00041 
00042 Program::Program(string p) {
00043     package = p;
00044     progname = 0;
00045 }
00046 
00047 Program::~Program() {
00048     if (debug_level>9) {
00049         cerr << "[DEBUG " << debug_level << "] dtor " << progname << endl;
00050     }
00051 }
00052 
00053 
00054 void Program::init(int argc, string *argv) {
00055     char *sp = strrchr(argv[0],'/');
00056     if (sp == 0)            // note progname points to 
00057         progname = argv[0];
00058     else
00059         progname = sp+1;
00060 
00061     Program::argc = argc;
00062     Program::argv = argv;
00063     self = this; 
00064 
00065     debug_level = 0;        // should also read from $STARLAB_DEBUG
00066     error_level = 0;        // should also read from $STARLAB_ERROR
00067     help_level = 0;         // should also read from $STARLAB_HELP
00068 
00069     nkeys = xstrlen(keywords, sizeof(string));          // fixed size array!!!!
00070     keys = new keyword[nkeys];
00071     addkey(0,"  argv0=\n Program name",1);
00072     keys[0].val = progname;
00073     for (int i=1; i<nkeys; i++)
00074         addkey(i,keywords[i-1],0);
00075 }
00076 
00077 void Program::append(string skeyvalhelp) {
00078     Warning("Program::append - not implemented yet");
00079 }
00080 
00081 void Program::parse(void) {
00082 #if 0
00083     int posflag = FALSE;            // force key=val, don't allow positional
00084 #else
00085     int posflag = TRUE; 
00086 #endif
00087     string name;
00088 
00089     for (int i=1; argv[i] ; i++) {
00090         if (debug_level > 0)
00091             cout << "Parsing " << argv[i] << endl;
00092         name = parname(argv[i]);
00093         posflag = posflag && (name == 0);
00094         if (posflag) {
00095             if (i >= nkeys) Error("Too many un-named arguments");
00096             // should free old one here too
00097             keys[i].val = strdup(argv[i]);
00098             keys[i].count++;
00099         } else {
00100             if (name == 0) {
00101                 cerr << "Parameter " << name << "must be named" << endl;
00102                 exit(1);
00103             }
00104             int j = findkey(name);
00105             if  (j >= 0) {
00106                 if(keys[j].count)
00107                     Error("Parameter duplicated");
00108                 // free old value here too
00109                 keys[j].val = strdup(parvalue(argv[i]));
00110                 keys[j].count++;
00111             } else {                // not listed - check if system key
00112                 if (strcmp(name,"help")==0)
00113                     cout << "SET_HELP" << endl;
00114                 else if (strcmp(name,"debug")==0)
00115                     set_debug(parvalue(argv[i]));
00116                 else if (strcmp(name,"error")==0)
00117                     set_error(parvalue(argv[i]));
00118                 else
00119                     Error("Parameter %s unknown",name);
00120             } // j>0
00121         } // if(posflag)
00122     } // for (i)
00123 }
00124 
00125 void Program::fini(void) {
00126     int i, n=0;
00127 
00128     for (i=1; i<nkeys; i++)
00129         n += keys[i].upd ? 1 : 0;
00130     if (n && debug_level > 0) {
00131         Warning("The following keywords have never been read:");
00132         for (i=1; i<nkeys; i++)
00133             if (keys[i].upd) cerr << " " << keys[i].key;
00134         cerr << endl;
00135     }
00136 }
00137 
00138 int Program::go(void) {
00139 
00140     if (argc > 1) {
00141         if (strcmp(argv[1],"--help") == 0) {
00142             cerr << PROGRAM_VERSION << endl;
00143             cerr << "Current system options:" << endl;
00144             cerr << " --help         this help" << endl;
00145             cerr << " --keywords     show program keys, values and help" << endl;
00146             cerr << " --version      show program version" << endl;
00147             cerr << " --usage        show program usage line" << endl;
00148             cerr << " --description  show program description" << endl;
00149             cerr << " --examples     show program examples" << endl;
00150             cerr << " --show         show some debugging info" << endl;
00151             if (argc==2) return 0;
00152         }
00153         if (strcmp(argv[1],"--keywords") == 0) {
00154             for (int i=1; i<nkeys; i++) {
00155                 cout << keys[i].key << "=" << keys[i].val << endl;
00156                 cout << "\t" << keys[i].help << endl;
00157             }
00158             if (argc==2) return 0;
00159         }
00160         if (strcmp(argv[1],"--version") == 0) {
00161             cout << progname << " : " << version << endl;
00162             if (argc==2) return 0;
00163             return 0;
00164         }
00165         if (strcmp(argv[1],"--usage") == 0) {
00166             cout <<  usage << endl;
00167             if (argc==2) return 0;
00168         }
00169         if (strcmp(argv[1],"--description") == 0) {
00170             cout << description << endl;
00171             if (argc==2) return 0;
00172         }
00173         if (strcmp(argv[1],"--examples") == 0) {
00174             cout << examples << endl;
00175             if (argc==2) return 0;
00176         }
00177         if (strcmp(argv[1],"--show") == 0) {
00178             show();
00179             if (argc==2) return 0;
00180         }
00181     }
00182 
00183     // check if any  '???' values remain
00184 
00185 
00186     int missing = 0;
00187     for (int i=1; i<nkeys; i++)
00188         if (strcmp(keys[i].val,"???")==0) {
00189             missing++;
00190             break;
00191         }
00192     if (missing) {
00193         cerr << "Insufficient parameters" << endl;
00194         cerr << "Usage: " << progname;
00195         int otherargs = FALSE;
00196         for (int i=1; i<nkeys; i++)
00197             if (strcmp(keys[i].val,"???") == 0)
00198                 printf(" %s=???", keys[i].key);
00199             else
00200                 otherargs = TRUE;
00201         cerr << (otherargs ? " ...\n" : "\n");
00202         Error("The above required keywords have no value");
00203     }
00204     return 1;
00205 }
00206 
00207 void Program::show(void) { 
00208         string *sp;
00209         int i;
00210         cout << "Program: " << progname << endl;
00211         cout << "Usage: " << usage << endl;
00212         cout << "Description: " << endl << description << endl;
00213         cout << "Version: " << version << endl;
00214         cout << "Examples: " << examples << endl;
00215         for (i=0, sp=keywords; *sp; i++, sp++)
00216             cout << "key(" << i+1 << ") = " << *sp << endl;
00217         for (i=1; i<argc; i++)
00218             cout << "arg(" << i << ") = " << argv[i] << endl;
00219         for (i=1; i<nkeys; i++) {
00220             cout << "key(" << i << ") = " << keys[i].key << endl;
00221             cout << "def(" << i << ") = " << keys[i].val << endl;
00222             cout << "help(" << i << ") = " << keys[i].help << endl;
00223         }
00224         cout << "debug_level: " << debug_level << endl;
00225         cout << "error_level: " << error_level << endl;
00226             
00227 }
00228 
00229 Program *Program::main(void) { 
00230     return self; 
00231 }
00232 
00233 int Program::get_argc(void) {
00234     return argc;
00235 }
00236 
00237 char **Program::get_argv(void) {
00238     return argv;
00239 }
00240 
00241 
00242 
00243 
00244 
00245 /* private functions of Program:: */
00246 
00247 void Program::help(void) {
00248 
00249 }
00250 
00251 void Program::addkey(int i, string skeyvalhelp, int system=0)
00252 {
00253     int j;
00254     if (i >= nkeys) Error("addkey internal error");
00255     
00256     keys[i].keyval = skeyvalhelp;
00257     keys[i].option = *skeyvalhelp;
00258     keys[i].key = strdup(parname(skeyvalhelp));
00259     keys[i].val = strdup(parvalue(skeyvalhelp));
00260     keys[i].help = strdup(parhelp(skeyvalhelp));
00261     keys[i].count = 0;
00262     keys[i].upd = 1;
00263     keys[i].system = system;
00264 
00265     // test internal consistencies, duplicate keys etc.
00266 
00267     for (j=0; j<i; j++) {
00268         if (keys[j].option == ' ') continue;
00269         if (keys[j].option == keys[i].option)
00270             Warning("Option %c duplicated (%s,%s)",
00271                 keys[i].option, keys[i].key, keys[j].key);
00272     }
00273     
00274 }
00275 
00276 
00277 string Program::get(string key) {
00278     int i = findkey(key);
00279     if (i<0) Error("%s: Unknown keyword",key);
00280     keys[i].upd = 0;        // mark keyword as read
00281     return keys[i].val;
00282 }
00283 
00284 int Program::count(string key) {
00285     int i = findkey(key);
00286     if (i<0) Error("Illegal key");
00287     return keys[i].count;
00288 }
00289 
00290 int Program::get_debug(void) {
00291     return debug_level;
00292 }
00293 
00294 void Program::set_debug(string debug_value) {
00295     debug_level = atoi(debug_value);
00296     cerr << "DEBUG level set to: " << debug_level << endl;
00297 }
00298 
00299 void Program::set_error(string error_value) {
00300     error_level = atoi(error_value);
00301     if (error_level < 0) Error("%d: Cannot set error < 0",error_level);
00302     cerr << "ERROR level set to: " << error_level << endl;
00303 }
00304 
00305 int Program::dec_error(void) {
00306     if (error_level == 0) return 0;
00307     // tricky: we return error_level, then decrement it (saves a temp)
00308     return error_level--;
00309 }
00310 
00311 
00312 // local helper functions because we don't have a fancy string class (yet)
00313 // these come pretty much straight from NEMO
00314 
00315 static int xstrlen(void *xspt, int nbyt) 
00316 {
00317     int nval, i;
00318     int lpflg;
00319     char *cp = (char *) xspt;
00320 
00321     nval = 0;                                   /* init count of values */
00322     do {                                        /* loop over values */
00323         nval++;                                 /*   count one more */
00324         lpflg = FALSE;                          /*   init loop flag */
00325         for (i = 0; i < nbyt; i++)              /*   loop over bytes */
00326             if (*cp++ != 0)                     /*     a byte of data? */
00327                 lpflg = TRUE;                   /*       set loop flag */
00328     } while (lpflg);                            /* until a NULL value */
00329     return (nval);                              /* return total count */
00330 }
00331 
00332 
00333 static string parname(string arg)
00334 {
00335     static char namebuf[64];
00336     char *ap, *np;
00337 
00338     ap = (char *) arg;
00339 
00340     if (arg[1] == ' ' || arg[1] == ':') {  // old (NEMO) vs. new (STARLAB) way
00341         ap++;           // skip the short option
00342         ap++;           // and the whitespace or colon
00343     }
00344 
00345 #if 0
00346         // should not have to do this, but ok for error / syntax checking
00347     while (*ap == ' ')          /* skip initial whitespace */
00348         ap++;
00349 #endif
00350 
00351     np = &namebuf[0];               /* point to spot where to copy to */
00352     while ((*np = *ap) != 0) {       /* copy until '=' or ' ' */
00353 #if 0
00354         if (*np == '=' || *np == ' ') {
00355 #else
00356         if (*np == '=') {
00357 #endif
00358             *np = 0;
00359             return ((string) namebuf);
00360         }
00361         np++;
00362         ap++;
00363     }
00364 #if 0
00365     namebuf[0] = 0;
00366     return ((string) namebuf);  /* return pointer to null string */
00367 #else
00368     return NULL;                /* return NULL (nothing found) */
00369 #endif    
00370 }
00371 
00372 /*
00373  * PARVALUE: extract value from name=value string, skipping initial whitespace
00374  *           if no value, pointer to 0 (last element) is returned!
00375  *           if no key, pointer to (local copy of ) first element is returned
00376  *  ???      BUG:  when HELPVEC is not defined, helpvec also returned
00377  *           Note: returns unsafe pointer into the input string
00378  *           
00379  */
00380 
00381 static string parvalue(string arg)
00382 {
00383 #if 0
00384     permanent char *valbuf = NULL;      /* dynamic array, with (re)allocate */
00385     permanent int size = 0;
00386 #else
00387     static char valbuf[256];         /* static array: dangerous */
00388 #endif
00389 
00390     char *ap;
00391 
00392     ap = (char *) arg;
00393     while (*ap) {
00394         if (*ap++ == '=') {
00395             while (*ap && *ap == ' ')      /* skip whitespace after '=' */
00396                 ap++;
00397             strncpy(valbuf,ap,255);
00398             valbuf[255] = 0;
00399             ap = valbuf;
00400             while (*ap) {
00401                 if (*ap == '\n') {
00402                     *ap = 0;
00403                     return valbuf;          /* return patched "value\nhelp" */
00404                 }
00405                 ap++;
00406             }
00407             return valbuf;                  /* return unmodified "value" */
00408         }
00409     }
00410 #if 1        
00411     return ((string) ap);           /* return pointer to null string */
00412 #else
00413     return NULL;                    /* return nothing found */
00414 #endif    
00415 }
00416 /* 
00417  * PARHELP: extract help from a defv[] "keyword=value\n help" string
00418  *          If no help part (or \n), returns zero string.
00419  *          Note: returns pointer into original string, assumed
00420  *          to be R/O (might be in text space of code)
00421  */
00422 static string parhelp(string arg)
00423 {
00424     char *cp = (char *) arg;
00425 
00426     while (*cp  && *cp != '\n')      /* scan until EOS or BOH */
00427         cp++;
00428     if(*cp == '\n')
00429         cp++;
00430     while (*cp && (*cp == ' ' || *cp == '\t'))   /* skip white space > BOH */
00431         cp++;
00432     return cp;      // no need to copy to buffer, since this is last 
00433 }
00434 
00435 /*
00436  * FINDKEY:  scan valid keywords and return index (>=0) for a keyname
00437  *           return -1 if none found
00438  *           Optionally match can be done in minimum match mode
00439  */
00440 
00441 int Program::findkey(string name)
00442 {
00443     int i, j, l, count, last;
00444 
00445     //    Warning("Hello world");
00446 
00447     if (nkeys<=0) return -1;                /* quick: no keywords at all */
00448     for (i = 0; i < nkeys; i++)             /* try exact match */
00449         if (strcmp(keys[i].key,name) == 0) return i;
00450 
00451 #if defined(MINMATCH)
00452     l = strlen(name);                       /* try minimum match */
00453     count = 0;                              /* count # matches on */
00454     for (i=1; i<nkeys; i++) {               /* any of the program keys */
00455         if (strncmp(keys[i].key, name,l)==0) {
00456             last = i;
00457             count++;
00458         }
00459     }
00460     if (count==1) {
00461       Warning("Resolving partially matched keyword %s= into %s=",
00462                name, keys[last].key);
00463         return last;
00464     } else if (count > 1) {
00465         Dprintf(0,"### Minimum match failed, found: ");
00466         for (j=0; j<nkeys; j++)
00467             if (strncmp(keys[j].key,name,l)==0)
00468                 Dprintf(0,"%s ",keys[j].key);
00469         Dprintf(0,"\n");
00470         Error("Ambiguous keyword %s=",name);
00471     }
00472 #endif
00473 
00474     return -1;          /* if all else fails: return -1 */
00475 }
00476 

Generated at Sun Feb 24 09:57:12 2002 for STARLAB by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001