Home | History | Annotate | Download | only in flags
      1 /*
      2  * Copyright 2013 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkCommandLineFlags.h"
      9 #include "SkTDArray.h"
     10 
     11 bool SkFlagInfo::CreateStringFlag(const char* name, const char* shortName,
     12                                   SkCommandLineFlags::StringArray* pStrings,
     13                                   const char* defaultValue, const char* helpString) {
     14     SkFlagInfo* info = SkNEW_ARGS(SkFlagInfo, (name, shortName, kString_FlagType, helpString));
     15     info->fDefaultString.set(defaultValue);
     16 
     17     info->fStrings = pStrings;
     18     SetDefaultStrings(pStrings, defaultValue);
     19     return true;
     20 }
     21 
     22 void SkFlagInfo::SetDefaultStrings(SkCommandLineFlags::StringArray* pStrings,
     23                                    const char* defaultValue) {
     24     pStrings->reset();
     25     if (NULL == defaultValue) {
     26         return;
     27     }
     28     // If default is "", leave the array empty.
     29     size_t defaultLength = strlen(defaultValue);
     30     if (defaultLength > 0) {
     31         const char* const defaultEnd = defaultValue + defaultLength;
     32         const char* begin = defaultValue;
     33         while (true) {
     34             while (begin < defaultEnd && ' ' == *begin) {
     35                 begin++;
     36             }
     37             if (begin < defaultEnd) {
     38                 const char* end = begin + 1;
     39                 while (end < defaultEnd && ' ' != *end) {
     40                     end++;
     41                 }
     42                 size_t length = end - begin;
     43                 pStrings->append(begin, length);
     44                 begin = end + 1;
     45             } else {
     46                 break;
     47             }
     48         }
     49     }
     50 }
     51 
     52 static bool string_is_in(const char* target, const char* set[], size_t len) {
     53     for (size_t i = 0; i < len; i++) {
     54         if (0 == strcmp(target, set[i])) {
     55             return true;
     56         }
     57     }
     58     return false;
     59 }
     60 
     61 /**
     62  *  Check to see whether string represents a boolean value.
     63  *  @param string C style string to parse.
     64  *  @param result Pointer to a boolean which will be set to the value in the string, if the
     65  *      string represents a boolean.
     66  *  @param boolean True if the string represents a boolean, false otherwise.
     67  */
     68 static bool parse_bool_arg(const char* string, bool* result) {
     69     static const char* trueValues[] = { "1", "TRUE", "true" };
     70     if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) {
     71         *result = true;
     72         return true;
     73     }
     74     static const char* falseValues[] = { "0", "FALSE", "false" };
     75     if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) {
     76         *result = false;
     77         return true;
     78     }
     79     SkDebugf("Parameter \"%s\" not supported.\n", string);
     80     return false;
     81 }
     82 
     83 bool SkFlagInfo::match(const char* string) {
     84     if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
     85         string++;
     86         const SkString* compareName;
     87         if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
     88             string++;
     89             // There were two dashes. Compare against full name.
     90             compareName = &fName;
     91         } else {
     92             // One dash. Compare against the short name.
     93             compareName = &fShortName;
     94         }
     95         if (kBool_FlagType == fFlagType) {
     96             // In this case, go ahead and set the value.
     97             if (compareName->equals(string)) {
     98                 *fBoolValue = true;
     99                 return true;
    100             }
    101             if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
    102                 string += 2;
    103                 // Only allow "no" to be prepended to the full name.
    104                 if (fName.equals(string)) {
    105                     *fBoolValue = false;
    106                     return true;
    107                 }
    108                 return false;
    109             }
    110             int equalIndex = SkStrFind(string, "=");
    111             if (equalIndex > 0) {
    112                 // The string has an equal sign. Check to see if the string matches.
    113                 SkString flag(string, equalIndex);
    114                 if (flag.equals(*compareName)) {
    115                     // Check to see if the remainder beyond the equal sign is true or false:
    116                     string += equalIndex + 1;
    117                     parse_bool_arg(string, fBoolValue);
    118                     return true;
    119                 } else {
    120                     return false;
    121                 }
    122             }
    123         }
    124         return compareName->equals(string);
    125     } else {
    126         // Has no dash
    127         return false;
    128     }
    129     return false;
    130 }
    131 
    132 SkFlagInfo* SkCommandLineFlags::gHead;
    133 SkString SkCommandLineFlags::gUsage;
    134 
    135 void SkCommandLineFlags::SetUsage(const char* usage) {
    136     gUsage.set(usage);
    137 }
    138 
    139 // Maximum line length for the help message.
    140 #define LINE_LENGTH 80
    141 
    142 static void print_help_for_flag(const SkFlagInfo* flag) {
    143     SkDebugf("\t--%s", flag->name().c_str());
    144     const SkString& shortName = flag->shortName();
    145     if (shortName.size() > 0) {
    146         SkDebugf(" or -%s", shortName.c_str());
    147     }
    148     SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
    149     if (flag->defaultValue().size() > 0) {
    150         SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
    151     }
    152     SkDebugf("\n");
    153     const SkString& help = flag->help();
    154     size_t length = help.size();
    155     const char* currLine = help.c_str();
    156     const char* stop = currLine + length;
    157     while (currLine < stop) {
    158         if (strlen(currLine) < LINE_LENGTH) {
    159             // Only one line length's worth of text left.
    160             SkDebugf("\t\t%s\n", currLine);
    161             break;
    162         }
    163         int lineBreak = SkStrFind(currLine, "\n");
    164         if (lineBreak < 0 || lineBreak > LINE_LENGTH) {
    165             // No line break within line length. Will need to insert one.
    166             // Find a space before the line break.
    167             int spaceIndex = LINE_LENGTH - 1;
    168             while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
    169                 spaceIndex--;
    170             }
    171             int gap;
    172             if (0 == spaceIndex) {
    173                 // No spaces on the entire line. Go ahead and break mid word.
    174                 spaceIndex = LINE_LENGTH;
    175                 gap = 0;
    176             } else {
    177                 // Skip the space on the next line
    178                 gap = 1;
    179             }
    180             SkDebugf("\t\t%.*s\n", spaceIndex, currLine);
    181             currLine += spaceIndex + gap;
    182         } else {
    183             // the line break is within the limit. Break there.
    184             lineBreak++;
    185             SkDebugf("\t\t%.*s", lineBreak, currLine);
    186             currLine += lineBreak;
    187         }
    188     }
    189     SkDebugf("\n");
    190 }
    191 
    192 void SkCommandLineFlags::Parse(int argc, char** argv) {
    193     // Only allow calling this function once.
    194     static bool gOnce;
    195     if (gOnce) {
    196         SkDebugf("Parse should only be called once at the beginning of main!\n");
    197         SkASSERT(false);
    198         return;
    199     }
    200     gOnce = true;
    201 
    202     bool helpPrinted = false;
    203     // Loop over argv, starting with 1, since the first is just the name of the program.
    204     for (int i = 1; i < argc; i++) {
    205         if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
    206             // Print help message.
    207             SkTDArray<const char*> helpFlags;
    208             for (int j = i + 1; j < argc; j++) {
    209                 if (SkStrStartsWith(argv[j], '-')) {
    210                     break;
    211                 }
    212                 helpFlags.append(1, &argv[j]);
    213             }
    214             if (0 == helpFlags.count()) {
    215                 // Only print general help message if help for specific flags is not requested.
    216                 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
    217             }
    218             SkDebugf("Flags:\n");
    219             SkFlagInfo* flag = SkCommandLineFlags::gHead;
    220             while (flag != NULL) {
    221                 // If no flags followed --help, print them all
    222                 bool printFlag = 0 == helpFlags.count();
    223                 if (!printFlag) {
    224                     for (int k = 0; k < helpFlags.count(); k++) {
    225                         if (flag->name().equals(helpFlags[k]) ||
    226                             flag->shortName().equals(helpFlags[k])) {
    227                             printFlag = true;
    228                             helpFlags.remove(k);
    229                             break;
    230                         }
    231                     }
    232                 }
    233                 if (printFlag) {
    234                     print_help_for_flag(flag);
    235                 }
    236                 flag = flag->next();
    237             }
    238             if (helpFlags.count() > 0) {
    239                 SkDebugf("Requested help for unrecognized flags:\n");
    240                 for (int k = 0; k < helpFlags.count(); k++) {
    241                     SkDebugf("\t--%s\n", helpFlags[k]);
    242                 }
    243             }
    244             helpPrinted = true;
    245         }
    246         if (!helpPrinted) {
    247             bool flagMatched = false;
    248             SkFlagInfo* flag = gHead;
    249             while (flag != NULL) {
    250                 if (flag->match(argv[i])) {
    251                     flagMatched = true;
    252                     switch (flag->getFlagType()) {
    253                         case SkFlagInfo::kBool_FlagType:
    254                             // Can be handled by match, above, but can also be set by the next
    255                             // string.
    256                             if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
    257                                 i++;
    258                                 bool value;
    259                                 if (parse_bool_arg(argv[i], &value)) {
    260                                     flag->setBool(value);
    261                                 }
    262                             }
    263                             break;
    264                         case SkFlagInfo::kString_FlagType:
    265                             flag->resetStrings();
    266                             // Add all arguments until another flag is reached.
    267                             while (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
    268                                 i++;
    269                                 flag->append(argv[i]);
    270                             }
    271                             break;
    272                         case SkFlagInfo::kInt_FlagType:
    273                             i++;
    274                             flag->setInt(atoi(argv[i]));
    275                             break;
    276                         case SkFlagInfo::kDouble_FlagType:
    277                             i++;
    278                             flag->setDouble(atof(argv[i]));
    279                             break;
    280                         default:
    281                             SkDEBUGFAIL("Invalid flag type");
    282                     }
    283                     break;
    284                 }
    285                 flag = flag->next();
    286             }
    287             if (!flagMatched) {
    288                 SkDebugf("Got unknown flag \"%s\". Exiting.\n", argv[i]);
    289                 exit(-1);
    290             }
    291         }
    292     }
    293     // Since all of the flags have been set, release the memory used by each
    294     // flag. FLAGS_x can still be used after this.
    295     SkFlagInfo* flag = gHead;
    296     gHead = NULL;
    297     while (flag != NULL) {
    298         SkFlagInfo* next = flag->next();
    299         SkDELETE(flag);
    300         flag = next;
    301     }
    302     if (helpPrinted) {
    303         exit(0);
    304     }
    305 }
    306 
    307 namespace {
    308 
    309 template <typename Strings>
    310 bool ShouldSkipImpl(const Strings& strings, const char* name) {
    311     int count = strings.count();
    312     size_t testLen = strlen(name);
    313     bool anyExclude = count == 0;
    314     for (int i = 0; i < strings.count(); ++i) {
    315         const char* matchName = strings[i];
    316         size_t matchLen = strlen(matchName);
    317         bool matchExclude, matchStart, matchEnd;
    318         if ((matchExclude = matchName[0] == '~')) {
    319             anyExclude = true;
    320             matchName++;
    321             matchLen--;
    322         }
    323         if ((matchStart = matchName[0] == '^')) {
    324             matchName++;
    325             matchLen--;
    326         }
    327         if ((matchEnd = matchName[matchLen - 1] == '$')) {
    328             matchLen--;
    329         }
    330         if (matchStart ? (!matchEnd || matchLen == testLen)
    331                 && strncmp(name, matchName, matchLen) == 0
    332                 : matchEnd ? matchLen <= testLen
    333                 && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
    334                 : strstr(name, matchName) != 0) {
    335             return matchExclude;
    336         }
    337     }
    338     return !anyExclude;
    339 }
    340 
    341 }  // namespace
    342 
    343 bool SkCommandLineFlags::ShouldSkip(const SkTDArray<const char*>& strings, const char* name) {
    344     return ShouldSkipImpl(strings, name);
    345 }
    346 bool SkCommandLineFlags::ShouldSkip(const StringArray& strings, const char* name) {
    347     return ShouldSkipImpl(strings, name);
    348 }
    349