Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "utils/arguments_parser.h"
     18 
     19 #include <unordered_set>
     20 
     21 namespace latinime {
     22 namespace dicttoolkit {
     23 
     24 const size_t ArgumentSpec::UNLIMITED_COUNT = S_INT_MAX;
     25 
     26 bool ArgumentsParser::validateSpecs() const {
     27     std::unordered_set<std::string> argumentNameSet;
     28     for (size_t i = 0; i < mArgumentSpecs.size() ; ++i) {
     29         if (mArgumentSpecs[i].getMinCount() == 0 && mArgumentSpecs[i].getMaxCount() == 0) {
     30             AKLOGE("minCount = maxCount = 0 for %s.", mArgumentSpecs[i].getName().c_str());
     31             return false;
     32         }
     33         if (mArgumentSpecs[i].getMinCount() != mArgumentSpecs[i].getMaxCount()
     34                 && i != mArgumentSpecs.size() - 1) {
     35             AKLOGE("Variable length argument must be at the end.",
     36                     mArgumentSpecs[i].getName().c_str()v  );
     37             return false;
     38         }
     39         if (argumentNameSet.count(mArgumentSpecs[i].getName()) > 0) {
     40             AKLOGE("Multiple arguments have the same name \"%s\".",
     41                     mArgumentSpecs[i].getName().c_str());
     42             return false;
     43         }
     44         argumentNameSet.insert(mArgumentSpecs[i].getName());
     45     }
     46     return true;
     47 }
     48 
     49 void ArgumentsParser::printUsage(const std::string &commandName,
     50         const std::string &description) const {
     51     printf("Usage: %s", commandName.c_str());
     52     for (const auto &option : mOptionSpecs) {
     53         const std::string &optionName = option.first;
     54         const OptionSpec &spec = option.second;
     55         printf(" [-%s", optionName.c_str());
     56         if (spec.needsValue()) {
     57             printf(" <%s>", spec.getValueName().c_str());
     58         }
     59         printf("]");
     60     }
     61     for (const auto &argSpec : mArgumentSpecs) {
     62         if (argSpec.getMinCount() == 0 && argSpec.getMaxCount() == 1) {
     63             printf(" [<%s>]", argSpec.getName().c_str());
     64         } else if (argSpec.getMinCount() == 1 && argSpec.getMaxCount() == 1) {
     65             printf(" <%s>", argSpec.getName().c_str());
     66         } else if (argSpec.getMinCount() == 0) {
     67             printf(" [<%s>...]", argSpec.getName().c_str());
     68         } else if (argSpec.getMinCount() == 1) {
     69             printf(" <%s>...", argSpec.getName().c_str());
     70         }
     71     }
     72     printf("\n%s\n\n", description.c_str());
     73     for (const auto &option : mOptionSpecs) {
     74         const std::string &optionName = option.first;
     75         const OptionSpec &spec = option.second;
     76         printf(" -%s", optionName.c_str());
     77         if (spec.needsValue()) {
     78             printf(" <%s>", spec.getValueName().c_str());
     79         }
     80         printf("\t\t\t%s", spec.getDescription().c_str());
     81         if (spec.needsValue() && !spec.getDefaultValue().empty()) {
     82             printf("\tdefault: %s", spec.getDefaultValue().c_str());
     83         }
     84         printf("\n");
     85     }
     86     for (const auto &argSpec : mArgumentSpecs) {
     87         printf(" <%s>\t\t\t%s\n", argSpec.getName().c_str(), argSpec.getDescription().c_str());
     88     }
     89     printf("\n\n");
     90 }
     91 
     92 const ArgumentsAndOptions ArgumentsParser::parseArguments(const int argc, char **argv,
     93         const bool printErrorMessage) const {
     94     if (argc <= 0) {
     95         AKLOGE("Invalid argc (%d).", argc);
     96         ASSERT(false);
     97         return ArgumentsAndOptions();
     98     }
     99     std::unordered_map<std::string, std::string> options;
    100     for (const auto &entry : mOptionSpecs) {
    101         const std::string &optionName = entry.first;
    102         const OptionSpec &optionSpec = entry.second;
    103         if (optionSpec.needsValue() && !optionSpec.getDefaultValue().empty()) {
    104             // Set default value.
    105             options[optionName] = optionSpec.getDefaultValue();
    106         }
    107     }
    108     std::unordered_map<std::string, std::vector<std::string>> arguments;
    109     auto argumentSpecIt = mArgumentSpecs.cbegin();
    110     for (int i = 1; i < argc; ++i) {
    111         const std::string arg = argv[i];
    112         if (arg.length() > 1 && arg[0] == '-') {
    113             // option
    114             const std::string optionName = arg.substr(1);
    115             const auto it = mOptionSpecs.find(optionName);
    116             if (it == mOptionSpecs.end()) {
    117                 if (printErrorMessage) {
    118                     fprintf(stderr, "Unknown option: '%s'\n", optionName.c_str());
    119                 }
    120                 return ArgumentsAndOptions();
    121             }
    122             std::string optionValue;
    123             if (it->second.needsValue()) {
    124                 ++i;
    125                 if (i >= argc) {
    126                     if (printErrorMessage) {
    127                         fprintf(stderr, "Missing argument for option '%s'\n", optionName.c_str());
    128                     }
    129                     return ArgumentsAndOptions();
    130                 }
    131                 optionValue = argv[i];
    132             }
    133             options[optionName] = optionValue;
    134         } else {
    135             // argument
    136             if (argumentSpecIt == mArgumentSpecs.end()) {
    137                 if (printErrorMessage) {
    138                     fprintf(stderr, "Too many arguments.\n");
    139                 }
    140                 return ArgumentsAndOptions();
    141             }
    142             arguments[argumentSpecIt->getName()].push_back(arg);
    143             if (arguments[argumentSpecIt->getName()].size() >= argumentSpecIt->getMaxCount()) {
    144                 ++argumentSpecIt;
    145             }
    146         }
    147     }
    148 
    149     if (argumentSpecIt != mArgumentSpecs.end()) {
    150         const auto &it = arguments.find(argumentSpecIt->getName());
    151         const size_t minCount = argumentSpecIt->getMinCount();
    152         const size_t actualcount = it == arguments.end() ? 0 : it->second.size();
    153         if (minCount > actualcount) {
    154             if (printErrorMessage) {
    155                 fprintf(stderr, "Not enough arguments. %zd argumant(s) required for <%s>\n",
    156                         minCount, argumentSpecIt->getName().c_str());
    157             }
    158             return ArgumentsAndOptions();
    159         }
    160     }
    161     return ArgumentsAndOptions(std::move(options), std::move(arguments));
    162 }
    163 
    164 } // namespace dicttoolkit
    165 } // namespace latinime
    166