Home | History | Annotate | Download | only in base
      1 /*
      2  * libjingle
      3  * Copyright 2006, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 
     32 
     33 #ifdef WIN32
     34 #include "talk/base/win32.h"
     35 #include <shellapi.h>
     36 #endif
     37 
     38 #include "talk/base/flags.h"
     39 
     40 
     41 // -----------------------------------------------------------------------------
     42 // Implementation of Flag
     43 
     44 Flag::Flag(const char* file, const char* name, const char* comment,
     45            Type type, void* variable, FlagValue default__)
     46     : file_(file),
     47       name_(name),
     48       comment_(comment),
     49       type_(type),
     50       variable_(reinterpret_cast<FlagValue*>(variable)),
     51       default_(default__) {
     52   FlagList::Register(this);
     53 }
     54 
     55 
     56 void Flag::SetToDefault() {
     57   // Note that we cannot simply do '*variable_ = default_;' since
     58   // flag variables are not really of type FlagValue and thus may
     59   // be smaller! The FlagValue union is simply 'overlayed' on top
     60   // of a flag variable for convenient access. Since union members
     61   // are guarantee to be aligned at the beginning, this works.
     62   switch (type_) {
     63     case Flag::BOOL:
     64       variable_->b = default_.b;
     65       return;
     66     case Flag::INT:
     67       variable_->i = default_.i;
     68       return;
     69     case Flag::FLOAT:
     70       variable_->f = default_.f;
     71       return;
     72     case Flag::STRING:
     73       variable_->s = default_.s;
     74       return;
     75   }
     76   UNREACHABLE();
     77 }
     78 
     79 
     80 static const char* Type2String(Flag::Type type) {
     81   switch (type) {
     82     case Flag::BOOL: return "bool";
     83     case Flag::INT: return "int";
     84     case Flag::FLOAT: return "float";
     85     case Flag::STRING: return "string";
     86   }
     87   UNREACHABLE();
     88   return NULL;
     89 }
     90 
     91 
     92 static void PrintFlagValue(Flag::Type type, FlagValue* p) {
     93   switch (type) {
     94     case Flag::BOOL:
     95       printf("%s", (p->b ? "true" : "false"));
     96       return;
     97     case Flag::INT:
     98       printf("%d", p->i);
     99       return;
    100     case Flag::FLOAT:
    101       printf("%f", p->f);
    102       return;
    103     case Flag::STRING:
    104       printf("%s", p->s);
    105       return;
    106   }
    107   UNREACHABLE();
    108 }
    109 
    110 
    111 void Flag::Print(bool print_current_value) {
    112   printf("  --%s (%s)  type: %s  default: ", name_, comment_,
    113           Type2String(type_));
    114   PrintFlagValue(type_, &default_);
    115   if (print_current_value) {
    116     printf("  current value: ");
    117     PrintFlagValue(type_, variable_);
    118   }
    119   printf("\n");
    120 }
    121 
    122 
    123 // -----------------------------------------------------------------------------
    124 // Implementation of FlagList
    125 
    126 Flag* FlagList::list_ = NULL;
    127 
    128 
    129 FlagList::FlagList() {
    130   list_ = NULL;
    131 }
    132 
    133 void FlagList::Print(const char* file, bool print_current_value) {
    134   // Since flag registration is likely by file (= C++ file),
    135   // we don't need to sort by file and still get grouped output.
    136   const char* current = NULL;
    137   for (Flag* f = list_; f != NULL; f = f->next()) {
    138     if (file == NULL || file == f->file()) {
    139       if (current != f->file()) {
    140         printf("Flags from %s:\n", f->file());
    141         current = f->file();
    142       }
    143       f->Print(print_current_value);
    144     }
    145   }
    146 }
    147 
    148 
    149 Flag* FlagList::Lookup(const char* name) {
    150   Flag* f = list_;
    151   while (f != NULL && strcmp(name, f->name()) != 0)
    152     f = f->next();
    153   return f;
    154 }
    155 
    156 
    157 void FlagList::SplitArgument(const char* arg,
    158                              char* buffer, int buffer_size,
    159                              const char** name, const char** value,
    160                              bool* is_bool) {
    161   *name = NULL;
    162   *value = NULL;
    163   *is_bool = false;
    164 
    165   if (*arg == '-') {
    166     // find the begin of the flag name
    167     arg++;  // remove 1st '-'
    168     if (*arg == '-')
    169       arg++;  // remove 2nd '-'
    170     if (arg[0] == 'n' && arg[1] == 'o') {
    171       arg += 2;  // remove "no"
    172       *is_bool = true;
    173     }
    174     *name = arg;
    175 
    176     // find the end of the flag name
    177     while (*arg != '\0' && *arg != '=')
    178       arg++;
    179 
    180     // get the value if any
    181     if (*arg == '=') {
    182       // make a copy so we can NUL-terminate flag name
    183       int n = static_cast<int>(arg - *name);
    184       if (n >= buffer_size)
    185         Fatal(__FILE__, __LINE__, "CHECK(%s) failed", "n < buffer_size");
    186       memcpy(buffer, *name, n * sizeof(char));
    187       buffer[n] = '\0';
    188       *name = buffer;
    189       // get the value
    190       *value = arg + 1;
    191     }
    192   }
    193 }
    194 
    195 
    196 int FlagList::SetFlagsFromCommandLine(int* argc, const char** argv,
    197                                       bool remove_flags) {
    198   // parse arguments
    199   for (int i = 1; i < *argc; /* see below */) {
    200     int j = i;  // j > 0
    201     const char* arg = argv[i++];
    202 
    203     // split arg into flag components
    204     char buffer[1024];
    205     const char* name;
    206     const char* value;
    207     bool is_bool;
    208     SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);
    209 
    210     if (name != NULL) {
    211       // lookup the flag
    212       Flag* flag = Lookup(name);
    213       if (flag == NULL) {
    214         fprintf(stderr, "Error: unrecognized flag %s\n", arg);
    215         return j;
    216       }
    217 
    218       // if we still need a flag value, use the next argument if available
    219       if (flag->type() != Flag::BOOL && value == NULL) {
    220         if (i < *argc) {
    221           value = argv[i++];
    222         } else {
    223           fprintf(stderr, "Error: missing value for flag %s of type %s\n",
    224             arg, Type2String(flag->type()));
    225           return j;
    226         }
    227       }
    228 
    229       // set the flag
    230       char empty[] = { '\0' };
    231       char* endp = empty;
    232       switch (flag->type()) {
    233         case Flag::BOOL:
    234           *flag->bool_variable() = !is_bool;
    235           break;
    236         case Flag::INT:
    237           *flag->int_variable() = strtol(value, &endp, 10);
    238           break;
    239         case Flag::FLOAT:
    240           *flag->float_variable() = strtod(value, &endp);
    241           break;
    242         case Flag::STRING:
    243           *flag->string_variable() = value;
    244           break;
    245       }
    246 
    247       // handle errors
    248       if ((flag->type() == Flag::BOOL && value != NULL) ||
    249           (flag->type() != Flag::BOOL && is_bool) ||
    250           *endp != '\0') {
    251         fprintf(stderr, "Error: illegal value for flag %s of type %s\n",
    252           arg, Type2String(flag->type()));
    253         return j;
    254       }
    255 
    256       // remove the flag & value from the command
    257       if (remove_flags)
    258         while (j < i)
    259           argv[j++] = NULL;
    260     }
    261   }
    262 
    263   // shrink the argument list
    264   if (remove_flags) {
    265     int j = 1;
    266     for (int i = 1; i < *argc; i++) {
    267       if (argv[i] != NULL)
    268         argv[j++] = argv[i];
    269     }
    270     *argc = j;
    271   }
    272 
    273   // parsed all flags successfully
    274   return 0;
    275 }
    276 
    277 void FlagList::Register(Flag* flag) {
    278   assert(flag != NULL && strlen(flag->name()) > 0);
    279   if (Lookup(flag->name()) != NULL)
    280     Fatal(flag->file(), 0, "flag %s declared twice", flag->name());
    281   flag->next_ = list_;
    282   list_ = flag;
    283 }
    284 
    285 #ifdef WIN32
    286 WindowsCommandLineArguments::WindowsCommandLineArguments() {
    287   // start by getting the command line.
    288   LPTSTR command_line = ::GetCommandLine();
    289    // now, convert it to a list of wide char strings.
    290   LPWSTR *wide_argv = ::CommandLineToArgvW(command_line, &argc_);
    291   // now allocate an array big enough to hold that many string pointers.
    292   argv_ = new char*[argc_];
    293 
    294   // iterate over the returned wide strings;
    295   for(int i = 0; i < argc_; ++i) {
    296     std::string s = talk_base::ToUtf8(wide_argv[i], wcslen(wide_argv[i]));
    297     char *buffer = new char[s.length() + 1];
    298     talk_base::strcpyn(buffer, s.length() + 1, s.c_str());
    299 
    300     // make sure the argv array has the right string at this point.
    301     argv_[i] = buffer;
    302   }
    303   LocalFree(wide_argv);
    304 }
    305 
    306 WindowsCommandLineArguments::~WindowsCommandLineArguments() {
    307   // need to free each string in the array, and then the array.
    308   for(int i = 0; i < argc_; i++) {
    309     delete[] argv_[i];
    310   }
    311 
    312   delete[] argv_;
    313 }
    314 #endif  // WIN32
    315 
    316