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