1/* 2 * Copyright (C) 2015 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 "Flags.h" 18#include "util/StringPiece.h" 19#include "util/Util.h" 20 21#include <iomanip> 22#include <iostream> 23#include <string> 24#include <vector> 25 26namespace aapt { 27 28Flags& Flags::requiredFlag(const StringPiece& name, const StringPiece& description, 29 std::string* value) { 30 auto func = [value](const StringPiece& arg) -> bool { 31 *value = arg.toString(); 32 return true; 33 }; 34 35 mFlags.push_back(Flag{ name.toString(), description.toString(), func, true, 1, false}); 36 return *this; 37} 38 39Flags& Flags::requiredFlagList(const StringPiece& name, const StringPiece& description, 40 std::vector<std::string>* value) { 41 auto func = [value](const StringPiece& arg) -> bool { 42 value->push_back(arg.toString()); 43 return true; 44 }; 45 46 mFlags.push_back(Flag{ name.toString(), description.toString(), func, true, 1, false }); 47 return *this; 48} 49 50Flags& Flags::optionalFlag(const StringPiece& name, const StringPiece& description, 51 Maybe<std::string>* value) { 52 auto func = [value](const StringPiece& arg) -> bool { 53 *value = arg.toString(); 54 return true; 55 }; 56 57 mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 1, false }); 58 return *this; 59} 60 61Flags& Flags::optionalFlagList(const StringPiece& name, const StringPiece& description, 62 std::vector<std::string>* value) { 63 auto func = [value](const StringPiece& arg) -> bool { 64 value->push_back(arg.toString()); 65 return true; 66 }; 67 68 mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 1, false }); 69 return *this; 70} 71 72Flags& Flags::optionalSwitch(const StringPiece& name, const StringPiece& description, 73 bool* value) { 74 auto func = [value](const StringPiece& arg) -> bool { 75 *value = true; 76 return true; 77 }; 78 79 mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 0, false }); 80 return *this; 81} 82 83void Flags::usage(const StringPiece& command, std::ostream* out) { 84 constexpr size_t kWidth = 50; 85 86 *out << command << " [options]"; 87 for (const Flag& flag : mFlags) { 88 if (flag.required) { 89 *out << " " << flag.name << " arg"; 90 } 91 } 92 93 *out << " files...\n\nOptions:\n"; 94 95 for (const Flag& flag : mFlags) { 96 std::string argLine = flag.name; 97 if (flag.numArgs > 0) { 98 argLine += " arg"; 99 } 100 101 // Split the description by newlines and write out the argument (which is empty after 102 // the first line) followed by the description line. This will make sure that multiline 103 // descriptions are still right justified and aligned. 104 for (StringPiece line : util::tokenize<char>(flag.description, '\n')) { 105 *out << " " << std::setw(kWidth) << std::left << argLine << line << "\n"; 106 argLine = " "; 107 } 108 } 109 *out << " " << std::setw(kWidth) << std::left << "-h" << "Displays this help menu\n"; 110 out->flush(); 111} 112 113bool Flags::parse(const StringPiece& command, const std::vector<StringPiece>& args, 114 std::ostream* outError) { 115 for (size_t i = 0; i < args.size(); i++) { 116 StringPiece arg = args[i]; 117 if (*(arg.data()) != '-') { 118 mArgs.push_back(arg.toString()); 119 continue; 120 } 121 122 if (arg == "-h" || arg == "--help") { 123 usage(command, outError); 124 return false; 125 } 126 127 bool match = false; 128 for (Flag& flag : mFlags) { 129 if (arg == flag.name) { 130 if (flag.numArgs > 0) { 131 i++; 132 if (i >= args.size()) { 133 *outError << flag.name << " missing argument.\n\n"; 134 usage(command, outError); 135 return false; 136 } 137 flag.action(args[i]); 138 } else { 139 flag.action({}); 140 } 141 flag.parsed = true; 142 match = true; 143 break; 144 } 145 } 146 147 if (!match) { 148 *outError << "unknown option '" << arg << "'.\n\n"; 149 usage(command, outError); 150 return false; 151 } 152 } 153 154 for (const Flag& flag : mFlags) { 155 if (flag.required && !flag.parsed) { 156 *outError << "missing required flag " << flag.name << "\n\n"; 157 usage(command, outError); 158 return false; 159 } 160 } 161 return true; 162} 163 164const std::vector<std::string>& Flags::getArgs() { 165 return mArgs; 166} 167 168} // namespace aapt 169