diff options
author | Johannes Winkelmann <jw@smts.ch> | 2005-11-09 21:44:34 +0000 |
---|---|---|
committer | Johannes Winkelmann <jw@smts.ch> | 2005-11-09 21:44:34 +0000 |
commit | 18c22c5a414a8beeac0d958ab39589c883ab06d4 (patch) | |
tree | f535c447c8c53775ad0b501dec83d8e1a33f8b79 /argparser.cpp | |
download | httpup-18c22c5a414a8beeac0d958ab39589c883ab06d4.tar.gz httpup-18c22c5a414a8beeac0d958ab39589c883ab06d4.tar.xz |
import httpup 0.4.0f
Diffstat (limited to 'argparser.cpp')
-rw-r--r-- | argparser.cpp | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/argparser.cpp b/argparser.cpp new file mode 100644 index 0000000..861261c --- /dev/null +++ b/argparser.cpp @@ -0,0 +1,420 @@ +//////////////////////////////////////////////////////////////////////// +// FILE: argparser.cpp +// AUTHOR: Johannes Winkelmann, jw@tks6.net +// COPYRIGHT: (c) 2004 by Johannes Winkelmann +// --------------------------------------------------------------------- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +//////////////////////////////////////////////////////////////////////// + +#include <iostream> +#include <sstream> +#include <cassert> +#include <libgen.h> + +#include "argparser.h" + +using namespace std; + +ArgParser::ArgParser() + : m_cmdIdCounter(0), + m_optIdCounter(0) +{ +} + +ArgParser::~ArgParser() +{ + map<int, Option*>::iterator oit = m_options.begin(); + for (; oit != m_options.end(); ++oit) { + delete oit->second; + } + + map<string, Command*>::iterator cit = m_commands.begin(); + for (; cit != m_commands.end(); ++cit) { + delete cit->second; + } +} + +int ArgParser::addCommand(APCmd& cmd, + const std::string& name, + const std::string& description, + ArgNumberCheck argNumberCheck, + int argNumber, + const std::string& otherArguments) +{ + Command* command = new Command; + + ++m_cmdIdCounter; + cmd.id = m_cmdIdCounter; + + command->apCmd = &cmd; + command->id = m_cmdIdCounter; + command->name = name; + command->argNumber = argNumber; + command->argNumberCheck = argNumberCheck; + + command->description = description; + command->otherArguments = otherArguments; + + m_commands[name] = command; + m_commandIdMap[cmd.id] = command; + + + APCmd apcmd; + apcmd.id = m_cmdIdCounter; + + PREDEFINED_CMD_HELP.init("help", 'h', "Print this help message"); + + // add predefined commands + addOption(cmd, PREDEFINED_CMD_HELP, false); + + return 0; +} + +int ArgParser::addOption(const APCmd& commandKey, + APOpt& key, + bool required) +{ + // TODO: check for null cmd + if (m_commandIdMap.find(commandKey.id) == m_commandIdMap.end()) { + return -1; + } + + Option* o = 0; + if (key.id != -1 && m_options.find(key.id) != m_options.end()) { + o = m_options.find(key.id)->second; + } + + if (!o) { + + assert(key.m_initialized == true); + + o = new Option(); + ++m_optIdCounter; + key.id = m_optIdCounter; + + o->id = key.id; + o->description = key.m_description; + o->requiresValue = key.m_valueRequired; + o->shortName = key.m_shortName; + o->longName = key.m_longName; + o->valueName = key.m_valueName; + + if (key.m_shortName != 0) { + m_optionsByShortName[key.m_shortName] = o; + } + if (key.m_longName != "") { + m_optionsByLongName[key.m_longName] = o; + } + + m_options[key.id] = o; + + } + + + Command* cmd = m_commandIdMap[commandKey.id]; + if (required) { + cmd->mandatoryOptions[key.id] = o; + } else { + cmd->options[key.id] = o; + } + + + return 0; +} + + +void ArgParser::parse(int argc, char** argv) +{ + bool commandFound = false; + string command = ""; + Command* cmd = 0; + int cmdPos = 0; + + m_appName = basename(argv[0]); + + for (int i = 1; i < argc; ++i) { + if (argv[i][0] != '-') { + if (!commandFound) { + if (m_commands.find(argv[i]) == m_commands.end()) { + parseError("Non option / Non command argument '" + + string(argv[i]) + "'"); + } + + cmd = m_commands[argv[i]]; + m_command.id = cmd->apCmd->id; + commandFound = true; + cmdPos = i; + break; + } + } else { + // TODO: add proper handling for global options + + string arg = argv[i]; + if (arg == "-h" || arg == "--help") { + cout << generateUsage() << endl; + exit(0); + } + } + + } + + if (!commandFound) { + parseError("No command used"); + exit(-1); + } + + for (int i = 1; i < argc; ++i) { + + if (i == cmdPos) { + continue; + } + + if (argv[i][0] == '-') { + if (argv[i][1] == '\0') { + parseError("Illegal token: '-'", cmd->name); + } else if (argv[i][1] == '-') { + + char* valPtr = strchr(argv[i]+2, '='); + if (valPtr) { + *valPtr = '\0'; + ++valPtr; + } + + if (m_optionsByLongName.find(argv[i]+2) == + m_optionsByLongName.end()) { + parseError("unknown option:" + string(argv[i]+2), + cmd->name); + } + + Option* o = m_optionsByLongName[argv[i]+2]; + string val = ""; + if (o->requiresValue) { + if (valPtr == NULL || *valPtr == 0) { + parseError("Value required for option '" + + string(argv[i]+2), cmd->name); + } else { + val = valPtr; + } + } + m_setOptions[o->id] = val; + } else { + if (argv[i][2] != '\0') { + parseError("invalid short option '" + + string(argv[i]+1) + "'", cmd->name); + } + + if (m_optionsByShortName.find(argv[i][1]) == + m_optionsByShortName.end()) { + parseError("unknown short option:" + string(argv[i]+1), + cmd->name); + } + + Option* o = m_optionsByShortName[argv[i][1]]; + string val = ""; + if (o->requiresValue) { + if (i+1 == argc) { + parseError("Option required for option '" + + string(argv[i]+1), cmd->name); + } else { + val = argv[i+1]; + ++i; + } + } + m_setOptions[o->id] = val; + } + } else { + m_otherArguments.push_back(string(argv[i])); + } + } + + if (isSet(PREDEFINED_CMD_HELP)) { + cout << generateHelpForCommand(cmd->name) << endl; + exit(0); + } else { + + // make sure all required options of a command are set + + std::map<int, Option*>::iterator it; + it = cmd->mandatoryOptions.begin(); + for (; it != cmd->mandatoryOptions.end(); ++it) { + if (!isSet(it->second->id)) { + parseError("Command '" + cmd->name + + "' requires option " + + string("-") + it->second->shortName + + string(" | ") + + string("--") + it->second->longName + " not found", + cmd->name); + } + } + } + + switch (cmd->argNumberCheck) + { + case EQ: + if (m_otherArguments.size() != cmd->argNumber) { + ostringstream ostr; + ostr << cmd->name + << " takes exactly " + << cmd->argNumber + << (cmd->argNumber == 1 ? " argument." : " arguments."); + + parseError(ostr.str(), cmd->name); + } + break; + case MIN: + if (m_otherArguments.size() < cmd->argNumber) { + ostringstream ostr; + ostr << cmd->name + << " takes at least " + << cmd->argNumber + << (cmd->argNumber == 1 ? " argument." : " arguments."); + + parseError(ostr.str(), cmd->name); + } + break; + case MAX: + if (m_otherArguments.size() > cmd->argNumber) { + ostringstream ostr; + ostr << cmd->name + << " takes at most " + << cmd->argNumber + << (cmd->argNumber == 1 ? " argument." : " arguments."); + + parseError(ostr.str(), cmd->name); + } + break; + case NONE: + default: + break; + } +} + +void ArgParser::parseError(const string& error, const string& cmd) const +{ + cerr << "Parse error: " << error << endl; + if (cmd != "") { + cerr << generateHelpForCommand(cmd) << endl; + } else { + cerr << generateUsage() << endl; + } + exit(-1); +} + +ArgParser::APCmd ArgParser::command() const +{ + return m_command; +} + +bool ArgParser::isSet(const APOpt& key) const +{ + return isSet(key.id); +} + +bool ArgParser::isSet(int key) const +{ + return m_setOptions.find(key) != m_setOptions.end(); +} + + +std::string ArgParser::getOptionValue(const APOpt& key) const +{ + return m_setOptions.find(key.id)->second; +} + +std::string ArgParser::appName() const +{ + return m_appName; +} + +std::string ArgParser::generateHelpForCommand(const std::string& command) const +{ + std::map<std::string, Command*>::const_iterator cit = + m_commands.find(command); + + if (cit == m_commands.end()) { + return ""; + } + + const Command * const cmd = cit->second; + string help = "";; + + help += "command '" + cmd->name + " " + cmd->otherArguments + "'\n"; + help += " " + cmd->description; + help += "\n\n"; + + + std::map<int, Option*>::const_iterator it = + it = cmd->mandatoryOptions.begin(); + if (it != cmd->mandatoryOptions.end()) { + help += " Required: \n"; + for (; it != cmd->mandatoryOptions.end(); ++it) { + help += generateOptionString(it->second); + } + } + + it = cmd->options.begin(); + if (it != cmd->options.end()) { + help += " Optional: \n"; + for (; it != cmd->options.end(); ++it) { + help += generateOptionString(it->second); + } + } + + return help; +} + +string ArgParser::generateOptionString(Option* o) const +{ + string help = " "; + + if (o->shortName) { + help += "-"; + help += o->shortName; + + if (o->requiresValue && o->valueName != "") { + help += " " + o->valueName; + } + + help += " | "; + } + + if (o->longName != "") { + help += "--"; + + help += o->longName; + if (o->requiresValue && o->valueName != "") { + help += "=" + o->valueName; + } + + help += " "; + help += o->description; + help += "\n"; + } + + return help; +} + +std::string ArgParser::generateUsage() const +{ + string usage = getAppIdentification() + + "USAGE: " + m_appName + + " [OPTIONS] command <arguments>\n\n"; + usage += " Where command is one of the following:\n"; + + std::map<std::string, Command*>::const_iterator it; + it = m_commands.begin(); + for (; it != m_commands.end(); ++it) { + usage += " " + it->first + "\t\t" + + it->second->description + "\n"; + } + + return usage; +} + +const std::vector<std::string>& ArgParser::otherArguments() const +{ + return m_otherArguments; +} |