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 21namespace latinime { 22namespace dicttoolkit { 23 24const size_t ArgumentSpec::UNLIMITED_COUNT = S_INT_MAX; 25 26bool 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 49void 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 92const 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