options.cxx

Go to the documentation of this file.
00001 //
00002 // Copyright 2002, Lowell Boggs Jr.
00003 //
00004 // This file or directory, containing source code for a computer program,
00005 // is Copyrighted by Lowell Boggs, Jr.  987 Regency Drive, Lewisville
00006 // TX (USA), 75067.  You may use, copy, modify, and distribute this
00007 // source file without charge or obligation so long as you agree to
00008 // the following:
00009 //
00010 //  1.  You must indemnify Lowell Boggs against any and all financial
00011 //      obligations caused by its use, misuse, function, or malfunction.
00012 //      Further, you acknowledge that there is no warranty of any kind,
00013 //      whatsoever.
00014 //
00015 //  2.  You agree not to attempt to patent any portion of this original
00016 //      work -- though you may attempt to patent your own extensions to
00017 //      it if you so choose.
00018 //
00019 //  3.  You keep this copyright notice with the file and all copies
00020 //      of the file and do not change it anyway except language translation.
00021 //
00022 // You are responsible for enforcing your own compliance with these
00023 // conditions and may not use this source file if you cannot agree to the
00024 // above terms and conditions.
00025 
00029 
00030 #include <cxxtls/options.h>
00031 #include <stdlib.h>
00032 #include <portable_io.h>
00033 #include <algorithm>
00034 #include <ctype.h>
00035 #include <string.h>
00036 
00037 using namespace std;
00038 
00039 namespace cxxtls
00040 {
00041 
00042 
00043 ProgramOptions*
00044 ProgramOptions::
00045 global_options_=0;  // global variable holding _the_ program options.  If null
00046                     // then no options are processed
00047 
00048 void
00049 ProgramOptions::
00050 constructor(const ProgramOptions::Descriptors& desc,
00051             int                                argc,
00052             const char*const*                  args,
00053             const char*const*                  environ,
00054             bool                               exit_on_error
00055            )
00056   //
00057   // This is the real constructor that parses the parms, environment, etc.
00058   //
00059 {
00060    // create the environment first because its easy
00061 
00062    if(environ)
00063    {
00064      while(*environ)
00065      {
00066        char const *p = *environ++;
00067 
00068        char const *equal = (char const*)(memchr(p, '=', strlen(p)));
00069 
00070        if(!equal)
00071          continue;  // badly formed environment????
00072 
00073        string name(p, equal);
00074 
00075        string value(equal+1, equal+strlen(equal));
00076 
00077        setenv(name, value);
00078 
00079      }
00080    }
00081 
00082    if(argc && args)
00083    {
00084       argv.push_back(*args);  // save argument 0 in the argv array and don't
00085                               // process it like it was an option
00086                         
00087       --argc;
00088       ++args;
00089 
00090       while(argc)
00091       {
00092         // parse the options into the options map and the arguments into argv
00093         
00094         if(args[0][0] == '-')
00095         {
00096           // handle -options
00097         
00098            std::vector<Descriptor>::const_iterator f = desc.options_.begin();
00099            std::vector<Descriptor>::const_iterator l = desc.options_.end();
00100         
00101            // look for an exact match of the current argument value
00102            // within the option descriptors
00103         
00104            while( f != l)
00105            {
00106               if( f->name_ == *args )
00107               {
00108                 break;
00109               }
00110         
00111               ++f;
00112         
00113            }
00114         
00115            bool single_char_parm=false;
00116         
00117            if(f == l)
00118            {
00119              // didn't find it, try some other strategies:
00120              //
00121              //  1:  see if there is a single character option which
00122              //      has the same value as the first character of the
00123              //      parm.  If it does and if it accepts parameters
00124              //      then treat the rest of the parm as its parameter.
00125              //
00126         
00127              string tmp(args[0], args[0]+2);
00128         
00129              for(f = desc.options_.begin(); f != l; ++f)
00130                if(tmp == f->name_ && f->parms_)
00131                  {
00132                    single_char_parm=true;
00133                    break;
00134                  }
00135            }
00136         
00137            if(f == l)
00138            {
00139         
00141              // did not find a match of any form
00143         
00144              char buffer[2048];
00145         
00146              sprintf(buffer, "program option '%s' is not recognized", *args);
00147         
00148              if(exit_on_error_)
00149              {
00150                fprintf(stderr, "%s\n", buffer);
00151                exit(1);
00152              }
00153         
00154              error_ = true;
00155         
00156              errmsg_ = buffer;
00157         
00158              return;
00159         
00160            }
00161         
00162            // at this point, we know that f points to a valid descriptor for this
00163            // argument
00164         
00165            if(single_char_parm && strlen(*args) > 2)
00166            {
00167              // this is a single character parm whose data is in the same
00168              // string:  -Ifilename
00169              //
00170         
00171              string name(*args, *args + 2);
00172 
00173              Option *opt = get_option(name); // see if option already set
00174 
00175              if(opt)
00176              {
00177                opt->append_value(string(*args+2, *args + strlen(*args)));
00178              }
00179              else
00180              {
00181                Option &newbie = add_option(name);
00182         
00183                newbie.append_value(string(*args+2, *args + strlen(*args)));
00184              }
00185 
00186            }
00187            else
00188            {
00189         
00190              Option *newbie = get_option(*args);  // see if already set
00191         
00192              if(!newbie)
00193                newbie = &add_option(*args);    // nope, add it
00194              else
00195              {
00196                // option was already set -- so this is a second
00197                // invocation.  If the option doesn't take parameters
00198                // then this means to unset the option
00199         
00200                if(f->parms_ == no_values)
00201                {
00202                  // unset it
00203                 
00204                  remove_option(*args);
00205                 
00206                  --argc;
00207                  ++args;
00208                 
00209                  continue;  // the outer loop to parse options
00210                 
00211                }
00212                else
00213                if(f->parms_ == one_value)
00214                {
00215                  // whoa, this is an error, you can't
00216                  // set more than one value for this guy.
00217                 
00218                  char buffer[2048];
00219 
00220                  sprintf(buffer, "Multiple settings of option '%s' not allowed", *args);
00221                 
00222                  if(exit_on_error_)
00223                  {
00224                    fprintf(stderr, "%s\n", buffer);
00225                    exit(1);
00226                  }
00227                 
00228                  error_ = true;
00229                 
00230                  errmsg_ = buffer;
00231                 
00232                  return;
00233                }
00234         
00235              }
00236         
00237              if(f->parms_ && argc)
00238              {
00239                --argc; // skip to the data part
00240                ++args;
00241         
00242                newbie->append_value(*args);
00243              }
00244            }
00245         }
00246         else
00247         {
00248           argv.push_back(*args);
00249         }
00250         
00251         --argc;
00252         ++args;
00253       }
00254    }
00255 }
00256 
00257 ProgramOptions::Option&
00258 ProgramOptions::
00259 add_option(string const &name)
00260 {
00261   Option* p = new Option(name);
00262 
00263   options_.insert( std::pair<string, Option*>(name, p) );
00264 
00265   return *p;
00266 }
00267 
00268 
00269 void
00270 ProgramOptions::
00271 Descriptors::
00272 add_option(string const &name, int parms)
00273 {
00274   options_.push_back(Descriptor(name, parms));
00275 }
00276 
00277 void
00278 ProgramOptions::
00279 setenv(string const &name, string const &value)
00280 {
00281    environ_.insert( std::pair<string,string> (name, value) );
00282 }
00283 
00284 ProgramOptions::EnvVar
00285 ProgramOptions::
00286 getenv(string const &name) const
00287 {
00288   std::map<string,string>::const_iterator e;
00289 
00290   e = environ_.find(name);
00291 
00292   if(e == environ_.end())
00293     return EnvVar(name);
00294 
00295   return EnvVar(name, e->second);
00296 
00297 }
00298 
00299 ProgramOptions::
00300 ~ProgramOptions()
00301 {
00302   // free up any memory not automatically cleaned up
00303   // by containers.
00304 
00305   while(options_.begin() != options_.end())
00306   {
00307     Options::iterator i = options_.begin();
00308 
00309     Option* tmp = i->second;
00310 
00311     options_.erase(i);  // remove 'i' from the container
00312 
00313     delete tmp;
00314 
00315   }
00316 
00317 }
00318 
00319 void
00320 ProgramOptions::Option::
00321 append_value(string const &value)
00322 {
00323   values_.push_back(value);
00324 
00325 }
00326 
00327 
00328 ProgramOptions::Option::
00329 Option(string const &name)
00330 : name_(name),
00331   set_(true)
00332 {
00333 }
00334 
00335 
00336 ProgramOptions::Option::
00337 Option()
00338 : name_("not set"),
00339   set_(false)
00340 {
00341 }
00342 
00343 string
00344 ProgramOptions::Option::
00345 value() const
00346 {
00347    string rv;
00348 
00349    if(values_.size() != 0)
00350    {
00351       vector<string>::const_iterator f = values_.begin();
00352       vector<string>::const_iterator l = values_.end();
00353 
00354       static string comma(",");
00355 
00356       do
00357       {
00358         rv += *f;
00359         
00360         ++f;
00361         
00362         if(f != l)
00363         {
00364           rv += comma;
00365         }
00366         
00367       }
00368       while(f != l);
00369 
00370 
00371    }
00372 
00373    return rv;
00374 }
00375 
00376 ProgramOptions::Option*
00377 ProgramOptions::
00378 get_option(string const &name)
00379 {
00380   Options::const_iterator f = options_.find(name);
00381 
00382   if(f == options_.end())
00383     return 0;
00384 
00385   return f->second;
00386 }
00387 
00388 void
00389 ProgramOptions::
00390 remove_option(string const &name)
00391 {
00392   Options::iterator f = options_.find(name);
00393 
00394   if(f != options_.end())
00395     options_.erase(f);
00396 }
00397 
00398 ProgramOptions::Option
00399 ProgramOptions::
00400 null_option_;
00401 
00402 
00403 ProgramOptions::Option const&
00404 ProgramOptions::
00405 option(string const &name) const
00406 {
00407   ProgramOptions* me = (ProgramOptions*)(this);
00408 
00409   Option* rv = me->get_option(name);
00410 
00411   if(!rv)
00412     return null_option_;
00413 
00414   return *rv;
00415 
00416 }
00417 
00418 
00419 ProgramOptions::
00420 ProgramOptions(string             text_desc,
00421                int                argc,
00422                const char*const*  args,
00423                const char*const*  environ,
00424                bool               exit_on_error
00425               )
00426 : exit_on_error_(exit_on_error),
00427   error_(false)
00428 {
00429   // construct the options given a string instead of a vector of
00430   // descriptors for the options.  The vector of options is created
00431   // from the string.
00432 
00433   string::iterator nc = text_desc.begin();
00434   string::iterator lc = text_desc.end();
00435 
00436   ProgramOptions::Descriptors desc;
00437 
00438   string option_name;
00439 
00440   while(nc != lc)
00441   {
00442      char c = *nc++;
00443 
00444      int values=-1;
00445 
00446      if(c == ',')
00447        values=no_values;
00448      else
00449      if(c == ':')
00450        values=one_value;
00451      else
00452      if(c == ';')
00453        values=many_values;
00454      else
00455        option_name += c;
00456 
00457      if(values >= 0)
00458      {
00459        // end of current option;
00460 
00461        if(option_name.size())
00462          desc.add_option(option_name, values);
00463         
00464        option_name.resize(0);
00465 
00466      }
00467 
00468   }
00469 
00470   if(option_name.size() && option_name[0] == '-')
00471   {
00472     // must have forgotten the trailing separator -- assume ','
00473 
00474     desc.add_option(option_name, no_values);
00475 
00476   }
00477 
00478   constructor(desc, argc, args, environ, exit_on_error);
00479 
00480 }
00481 
00482 string
00483 ProgramOptions::
00484 expand(string const &in) const
00485 {
00486   string rv;
00487 
00488   size_t offset=0;
00489 
00490   size_t in_size = in.size();
00491 
00492   while(offset < in_size)
00493   {
00494      string::const_iterator start  = in.begin() + offset;
00495      string::const_iterator end    = in.end();
00496      string::const_iterator dollar = find(start, end, '$');
00497 
00498      if(dollar == end)
00499      {
00500        // whoops, out of text
00501 
00502        rv.append(start, end);
00503        return rv;
00504 
00505      }
00506      else
00507      {
00508        // perform a substitituion
00509 
00510        rv.append(start, dollar);
00511 
00512        ++dollar;
00513 
00514        if(dollar == end)  // last char in string was dollar, thats baaaaad
00515          return rv;
00516         
00517        char c = *dollar;
00518 
00519        if(c >= '0' && c < '9')
00520        {
00521          // parm substitution
00522         
00523          ++dollar;
00524         
00525          c -= '0';
00526         
00527          // c is now a parm index
00528         
00529          if( size_t(c) < argv.size())
00530          {
00531            rv += argv[c];
00532          }
00533         
00534        }
00535        else
00536        {
00537          // environment variable substitution
00538         
00539          string varname;
00540         
00541          while(dollar < end &&
00542                ( isalnum(*dollar) || *dollar == '_')
00543               )
00544          {
00545 
00546              varname += *dollar++;
00547 
00548          }
00549         
00550          string value = getenv(varname);
00551         
00552          rv += value;
00553         
00554          char const *varname_ptr = varname.c_str();
00555          char const *value_ptr   = value.c_str();
00556         
00557          if(value_ptr == varname_ptr)
00558            varname_ptr = value_ptr;
00559         
00560        }
00561 
00562        offset = dollar - in.begin();
00563 
00564      }
00565   }
00566 
00567   return rv;
00568 
00569 }
00570 
00571 } // namespace cxxtls
Generated on Wed Feb 29 22:50:05 2012 for CXXUtilities by  doxygen 1.6.3