diff --git a/CMakeLists.txt b/CMakeLists.txt index 476ff9b..8fe9178 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,7 @@ configure_file( "${PROJECT_SOURCE_DIR}/version.h" ) - - +# set buold type if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." @@ -44,6 +43,9 @@ ELSE() message( FATAL_ERROR "Cannot be compiled on this system" ) endif() +# set common source files +SET( COMMON_SOURCES "tmux-mem-cpu-load.cpp" "graph.cc" "argParse/argParse.cc" ) + # Search for boost # will be needed later #set(Boost_USE_STATIC_LIBS OFF) @@ -57,9 +59,9 @@ SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" ) # add binary tree so we find version.h #include_directories("${PROJECT_BINARY_DIR}" ${Boost_INCLUDE_DIRS}) -include_directories("${PROJECT_BINARY_DIR}" ${Boost_INCLUDE_DIRS}) +include_directories("${PROJECT_BINARY_DIR}" ) -add_executable(tmux-mem-cpu-load tmux-mem-cpu-load.cpp graph.cc ${METER_SOURCES}) +add_executable(tmux-mem-cpu-load ${COMMON_SOURCES} ${METER_SOURCES}) #target_link_libraries(tmux-mem-cpu-load ${Boost_LIBRARIES}) install(TARGETS tmux-mem-cpu-load RUNTIME DESTINATION bin diff --git a/argParse/argParse.cc b/argParse/argParse.cc new file mode 100644 index 0000000..7dbece6 --- /dev/null +++ b/argParse/argParse.cc @@ -0,0 +1,817 @@ +/* + * C++ command line argument parser + * + * Copyright (C) 2005 by + * Michael Hanke michael.hanke@gmail.com + * + * Minor adjustements: 2015 Pawel 'l0ner' Soltys + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include "argParse.h" + +using namespace std; +using namespace ArgvParse; + +ArgvParser::ArgvParser() + : max_key(1), + help_option(0) // must be smaller than max_key initially + +{ + // nothing +} + +ArgvParser::~ArgvParser() +{ + // nothing +} + +void ArgvParser::reset() +{ + max_key = 1; + option2key.clear(); + option2attribute.clear(); + option2descr.clear(); + option2value.clear(); + errorcode2descr.clear(); + argument_container.clear(); + intro_description.clear(); + error_option.clear(); + help_option = 0; +} + +int ArgvParser::optionKey( const string& _name ) const +{ + String2KeyMap::const_iterator it = option2key.find(_name); + + // if not found + if (it == option2key.end()) + return(-1); + + return(it->second); +} + +bool ArgvParser::isDefinedOption( const string& _name ) const +{ + return(option2key.find(_name) != option2key.end()); +} + +bool ArgvParser::foundOption( const string & _name ) const +{ + int key = optionKey(_name); + + // not defined -> cannot by found + if (key == -1) + return(false); + + // return whether the key of the given option name is in the hash of the + // parsed options. + return(option2value.find(key) != option2value.end()); +} + +string ArgvParser::optionValue(const string& _option) const +{ + int key = optionKey(_option); + + // not defined -> cannot by found + if (key == -1) + { + cerr << "ArgvParser::optionValue(): Requested value of an option the" + "parser did not find or does not know." << endl; + return(""); + } + + return(option2value.find(key)->second); +} + +ArgvParser::ParserResults +ArgvParser::parse(int _argc, char ** _argv) +{ + bool finished_options = false; // flag whether an argument was found + // (options are passed) + + // loop over all command line arguments + int i = 1; // argument counter + while( i< _argc ) + { + string argument = _argv[i]; + unsigned int key = 0; + string option; // option name + string value; // option value + + // if argument is an option + if (!isValidOptionString(argument)) + { + // string is a real argument since values are processed elsewhere + finished_options=true; + argument_container.push_back(argument); + } + else // can be a long or multiple short options at this point + { + // check whether we already found an argument + if (finished_options) + { + error_option = argument; + return(OptionAfterArgument); // return error code + } + // check for long options + if (isValidLongOptionString(argument)) + { + // handle long options + + // remove trailing '--' + argument = argument.substr(2); + // check for option value assignment 'option=value' + splitOptionAndValue(argument, option, value); + + if (!isDefinedOption(option)) // is this a known option + { + error_option = option; // store the option that caused the error + return(UnknownOption); // return error code if not + } + + // get the key of this option - now that we know that it is defined + key = option2key.find(option)->second; + if (key == help_option) // if help is requested return error code + return(HelpRequested); + + // do we need to extract a value + // AND a value is not already assigned from the previous step + if ((option2attribute.find(key)->second & RequiresValue) && value.empty()) + { + if (i+1 >= _argc) // are there arguments left? + { + error_option = option; // store the option that caused the error + return(MissingValue); // the was no argument left although we need a value + } + + string temp = _argv[i+1]; // get the next element + ++i; // increase counter now that we moved forward + + if (isValidOptionString(temp)) + { + error_option = option; // store the option that caused the error + return(MissingValue); // missing option value + } + value = temp; // assign value + } + // add option-value map entry + option2value[key] = value; + } + else // handle short options + { + argument = argument.substr(1); // remove trailing '-' + + // check for option value assignment 'option=value' + if (splitOptionAndValue(argument, option, value)) + { + // there was an option <- value assignment + if (option.length() > 1) + { + error_option = option; // store the option that caused the error + return(MalformedMultipleShortOption); // return error code if option has more than one character + } + + if (!isDefinedOption(option)) // is this a known option + { + error_option = option; // store the option that caused the error + return(UnknownOption); // return error code if not + } + key = option2key.find(option)->second; // get the key for the extracted option name + + if (key == help_option) // if help is requested return error code + return(HelpRequested); + + // if value is still empty for some reason: we have an error + if ((option2attribute.find(key)->second & RequiresValue) && value.empty()) + { + error_option = option; // store the option that caused the error + return(MissingValue); // missing option value + } + else + // add option-value map entry + option2value[key] = value; + } + else // no '=' assignment: can be either multiple short options or + // something like '-s 4' + { + // handle short options with value like '-s 4' + option.clear(); + value.clear(); + + if (argument.length() == 1) // if a single short option + { + if (!isDefinedOption(argument)) // is this a known option + { + error_option = argument; // store the option that caused the error + return(UnknownOption); // return error code if not + } + key = option2key.find(argument)->second; // get the key for the extracted option name + + if (key == help_option) // if help is requested return error code + return(HelpRequested); + + // check if option needs a value and next arg is not an option + if ((option2attribute.find(key)->second & RequiresValue)) + { + if (i+1 >= _argc) // are there arguments left? + { + error_option = argument; // store the option that caused the error + return(MissingValue); // the was no argument left although we need a value + } + string temp = _argv[i+1]; // get the next element + ++i; // increase counter now that we moved forward + + if (isValidOptionString(temp)) + { + error_option = argument; // store the option that caused the error + return(MissingValue); // missing option value + } + // add option-value map entry + option2value[key] = temp; + + } + else // no value needed + { + option2value[key] = ""; // assign value + } + } + else // handle multiple short option like '-svxgh' + { + unsigned int short_option_counter = 0; // position in the multiple short option string + while( short_option_counter < argument.length() ) // parse the whole string + { + option = argument[short_option_counter]; // get the option character + + if (!isDefinedOption(option)) // is this a known option + { + error_option = option; // store the option that caused the error + return(UnknownOption); // return error code if not + } + key = option2key.find(option)->second; // get the key for the extracted option name + + if (key == help_option) // if help is requested return error code + return(HelpRequested); + + option2value[key] = value; + + ++short_option_counter; // advance one character forward + } + } + } + } + } + ++i; + } + + map::iterator it; + for( it = option2attribute.begin(); it != option2attribute.end(); it++ ) + { + // if the current option is required look if we got it + if (it->second & Required) + { + // is the object missing + if (option2value.find(it->first) == option2value.end()) + { + // get the list of alternative names for this option + list alternatives = getAllOptionAlternatives(it->first); + + unsigned int count = 0; + for( list::const_iterator alt = alternatives.begin(); + alt != alternatives.end(); + ++alt ) + { + ++count; + // additional '-' for long options + if (alt->length() > 1) + error_option += "-"; + + error_option += "-" + *alt; + + // alternatives to come? + if (count < alternatives.size()) + error_option += ", "; // add separator + } + return(RequiredOptionMissing); + } + } + } + + return(Success); // everthing went fine -> sucess +} + +unsigned int ArgvParser::arguments() const +{ + return(argument_container.size()); +} + +string ArgvParser::argument(unsigned int _id) const +{ + if (_id >= arguments()) + { + cerr << "ArgvParser::argument(): Request for non-existing argument.\n"; + return (""); + } + else + return(argument_container[_id]); +} + +const vector& ArgvParser::allArguments() const +{ + return(argument_container); +} + +string ArgvParser::usageDescription(unsigned int _width) const +{ + string usage; // the usage description text + + if (intro_description.length()) + usage += formatString(intro_description, _width) + "\n\n"; + + if (max_key>1) // if we have some options + usage += formatString("Available options:",_width) + "\n"; + + // loop over all option attribute entries (which equals looping over all + // different options (not option names) + for (Key2AttributeMap::const_iterator it = option2attribute.begin(); + it != option2attribute.end(); + ++it) + { + string os; // temp string for the option + + // get the list of alternative names for this option + list alternatives = getAllOptionAlternatives(it->first); + + unsigned int count = 0; + for( list::const_iterator alt = alternatives.begin(); + alt != alternatives.end(); + ++alt ) + { + ++count; + // additional '-' for long options + if (alt->length() > 1) + os += "-"; + + os += "-" + *alt; + + // note if the option requires a value + if (option2attribute.find(it->first)->second & RequiresValue) + os += " "; + + // alternatives to come? + if (count < alternatives.size()) + os += ", "; // add separator + } + + // note if the option is required + if (option2attribute.find(it->first)->second & Required) + os += " [required]"; + + usage += formatString(os, _width) + "\n"; + + if (option2descr.find(it->first) != option2descr.end()) + usage += formatString(option2descr.find(it->first)->second, _width, 4); + else + usage += formatString("(no description)", _width, 4); + + // finally a little gap + usage += "\n"; + } + usage += "\n"; + + if (!errorcode2descr.size()) // if have no errorcodes + return(usage); + + usage += formatString("Return codes", _width) + "\n"; + + // map::const_iterator eit; + for( std::map::const_iterator alt = errorcode2descr.begin(); + alt != errorcode2descr.end(); + ++alt ) + { + ostringstream code; + code << alt->first; + string label = formatString(code.str(), _width, 4); + string descr = formatString(alt->second, _width, 10); + usage += label + descr.substr(label.length()) + "\n"; + } + + return(usage); +} + +const string& ArgvParser::errorOption( ) const +{ + return(error_option); +} + +std::string ArgvParser::parseErrorDescription( ParserResults _error_code ) const +{ + string descr; + + switch (_error_code) + { + case ArgvParser::Success: + // no error -> nothing to do + break; + case ArgvParser::UnknownOption: + descr = "Unknown option: '" + errorOption() + "'\n"; + break; + case ArgvParser::MissingValue: + descr = "Missing required value for option: '" + errorOption()+ "'\n"; + break; + case ArgvParser::OptionAfterArgument: + descr = "Misplaced option '" + errorOption() + + "' detected. All option have to be BEFORE the first argument\n"; + break; + case ArgvParser::MalformedMultipleShortOption: + descr = "Malformed short-options: '" + errorOption() + "'\n"; + break; + case ArgvParser::ArgvParser::RequiredOptionMissing: + descr = "Required option missing: '" + errorOption() + "'\n"; + break; + case ArgvParser::HelpRequested: // help + descr = usageDescription(); + break; + default: + cerr << "ArgvParser::documentParserErrors(): Unknown error code\n"; + } + + return(descr); +} + +bool ArgvParser::defineOption( const string & _name, + const string& _descr, + OptionAttributes _attrs) +{ + // do nothing if there already is an option of this name + if (isDefinedOption(_name)) + { + cerr << "ArgvParser::defineOption(): The option label equals an already defined option." << endl; + return(false); + } + + // no digits as short options allowed + if (_name.length() == 1 && isDigit(_name[0])) + { + cerr << "ArgvParser::defineOption(): Digits as short option labels are not allowd." << endl; + return(false); + } + + option2key[_name] = max_key; // give the option a unique key + + // store the option attributes + option2attribute[max_key] = _attrs; + + // store the option description if there is one + if (_descr.length()) + option2descr[max_key] = _descr; + + // inc the key counter + ++max_key; + + return(true); +} + +bool ArgvParser::defineOptionAlternative( const string & _original, + const string & _alternative ) +{ + // do nothing if there already is no option of this name + if (!isDefinedOption(_original)) + { + cerr << "ArgvParser::defineOptionAlternative(): Original option label is not a defined option." << endl; + return(false); + } + + // AND no digits as short options allowed + if (_alternative.length() == 1 && isDigit(_alternative[0])) + { + cerr << "ArgvParser::defineOptionAlternative(): Digits as short option labels are not allowd." << endl; + return(false); + } + + // AND do nothing if there already is an option with the alternativ name + if (isDefinedOption(_alternative)) + { + cerr << "ArgvParser::defineOptionAlternative(): The alternative option label equals an already defined option." << endl; + return(false); + } + + option2key[_alternative] = optionKey(_original); + + return(true); +} + +bool ArgvParser::defineOption( const string & _shortname, + const string & _name, + const string& _descr, + OptionAttributes _attrs) +{ + + defineOption( _name, _descr, _attrs); + defineOptionAlternative( _name, _shortname ); + + return(true); + +} + + +bool ArgvParser::setHelpOption(const string& _shortname, + const string& _longname, + const string& _descr) +{ + // do nothing if any name is already in use + if (isDefinedOption(_shortname) || isDefinedOption(_longname)) + { + cerr << "ArgvParser::setHelpOption(): Short or long help option label equals an already defined option." << endl; + return(false); + } + + // define the help option's short name and the alternative + // longname + defineOption(_shortname, _descr, NoAttribute); + defineOptionAlternative(_shortname, _longname); + + help_option = max_key-1; // store the key in a special member + + return(true); +} + +void ArgvParser::addErrorCode(int _code, const string& _descr) +{ + errorcode2descr[_code] = _descr; +} + +void ArgvParser::setIntroduction(const string& _descr) +{ + intro_description = _descr; +} + +list ArgvParser::getAllOptionAlternatives( unsigned int _key ) const +{ + // keys go here + list keys; + // for all container elements + for( map::const_iterator it = option2key.begin(); + it != option2key.end(); + ++it ) + { + if (it->second == _key) + keys.push_back(it->first); + } + return(keys); +} + +bool ArgvParse::isDigit(const char& _char) +{ + if (_char == '0' || _char == '1' || _char == '2' || _char == '3' + || _char == '4' || _char == '5' || _char == '6' || _char == '7' + || _char == '8' || _char == '9') + return(true); + else + return(false); +} + +bool ArgvParse::isValidOptionString(const string& _string) +{ + // minimal short option length is 2 + if (_string.length() < 2) + return(false); + + // is it an option (check for '-' as first character) + if (_string.compare(0, 1, "-")) + return(false); + + // not an option if just '--' + if (_string.length() == 2 && _string == "--") + return(false); + + // it might still be a negative number + // (but not if there is no digit afterwards) + if (isDigit(_string[1])) + return(false); + + // let's consider this an option + return(true); +} + +bool ArgvParse::isValidLongOptionString(const string& _string) +{ + if (_string.length() < 4) // must be at least '--??' + return(false); + + // is it an option (check for '--') + if (_string.compare(0, 2, "--")) + return(false); + else + return(true); +} + +bool ArgvParse::splitOptionAndValue(const string& _string, + string& _option, string& _value) +{ + // string token container + std::vector tokens; + + // split string by '=' delimiter + splitString(tokens, _string, "="); + + // check for option value assignment 'option=value' + if (tokens.size() < 2) + { + _option = _string; // the option is the whole string + return(false); + } + + // separate option and value + _option = tokens[0]; + + // concat all remaining tokens to the value string + for (unsigned int i=1; i & _expanded ) +{ + list tokens; + // split string by delimiter + splitString(tokens, _string, ","); + + // loop over all entries + for(list::const_iterator it = tokens.begin(); it != tokens.end(); it++) + { + const string& entry = *it; // convenience reference + +#ifdef ARGVPARSER_DEBUG + + cout << "TOKEN: " << entry << endl; +#endif + + // if range was given + if (entry.find("-") != string::npos) + { + // split into upper and lower border + list range_borders; + splitString(range_borders, entry, "-"); + + // fail if insane range spec + if (range_borders.size() != 2) + return(false); + + int first = atoi(range_borders.begin()->c_str()); + int second = atoi((++range_borders.begin())->c_str()); + + // write id in increasing order + if (first <= second) + + { + for (int j=first; j<=second; ++j) + { + _expanded.push_back(j); + } + } + else // write id in decreasing order + { + for (int k=first; k>=second; k--) + { + _expanded.push_back(k); + } + } + } + else // single number was given + _expanded.push_back(atoi(entry.c_str())); // store id + } + + return(true); +} + +std::string ArgvParse::formatString(const std::string& _string, + unsigned int _width, + unsigned int _indent) +{ + // if insane parameters do nothing + if (_indent >= _width) + return(_string); + + // list of lines of the formated string + list lines; + + // current position in the string + unsigned int pos = 0; + + // till the end of the string + while (pos < _string.length()) + { + // get the next line of the string + string line = _string.substr(pos, _width - _indent ); + +#ifdef ARGVPARSER_DEBUG + + cout << "EXTRACT: '" << line << "'" << endl; +#endif + + // check for newlines in the line and break line at first occurence (if any) + string::size_type first_newline = line.find_first_of("\n"); + if (first_newline != string::npos) + { + line = line.substr(0, first_newline); + } + + // we need to check for possible breaks within words only if the extracted + // line spans the whole allowed width + bool check_truncation = true; + if (line.length() < _width - _indent) + check_truncation = false; + + // remove unecessary whitespace at front and back + line = trimmedString(line); + +#ifdef ARGVPARSER_DEBUG + + cout << "TRIMMED: '" << line << "'" << endl; +#endif + + // only perform truncation if there was enough data for a full line + if (!check_truncation) + pos += line.length() + 1; + else + { + // look for the last whitespace character + string::size_type last_white_space = line.find_last_of(" \a\b\f\n\r\t\v"); + + if (last_white_space != string::npos) // whitespace found! + { + // truncated the line at the last whitespace + line = string(line, 0, last_white_space); + pos += last_white_space + 1; + } + else // no whitespace found + // rude break! we can leave the line in its current state + pos += _width - _indent; + } + + if (!line.empty()) + { +#ifdef ARGVPARSER_DEBUG + cout << "UNINDEN: '" << line << "'" << endl; +#endif + + if (_indent) + line.insert(0, _indent, ' '); + +#ifdef ARGVPARSER_DEBUG + + cout << "INDENT: '" << line << "'" << endl; +#endif + + lines.push_back(line); + } + } + + // concat the formated string + string formated; + bool first = true; + // for all lines + for (list::iterator it = lines.begin(); it != lines.end(); ++it) + { + // prefix with newline if not first + if (!first) + formated += "\n"; + else + first = false; + + formated += *it; + } + return(formated); +} + diff --git a/argParse/argParse.h b/argParse/argParse.h new file mode 100644 index 0000000..6b8ae27 --- /dev/null +++ b/argParse/argParse.h @@ -0,0 +1,337 @@ +/* + * C++ command line argument parser + * + * Copyright (C) 2005 by + * Michael Hanke michael.hanke@gmail.com + * + * Minor adjustements: 2015 Pawel 'l0ner' Soltys + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __ARGVPARSER_H +#define __ARGVPARSER_H + +#include +#include +#include +#include + +namespace ArgvParse +{ + +/** Provides parsing and storage of POSIX-like command line arguments (argc, argv). +* To use this class for CLI-option parsing, first define a set of valid options using +* the defineOption() method. An option can have several attributes: it can be required +* itself (its missing is considered as an error) and/or it can require a value. +* Options with optional values can be realized by defining the option to not need a +* value and use the syntax '--option=value' or '-o=value' on the command line. +* Every option can have different alternative labels; see defineOptionAlternative(). +* A usage description (see usageDescription()) can be generated from the set of defined options (also see the +* addErrorCode(), setIntroductoryDescription() and setHelpOption() methods). +* \note The implemented parsing algorithm requires that all options have to be given at +* the beginning of the command line. Everything on the commandline after the first +* non-option (or option value) is considered as an argument. +* \attention Short option labels (single letter options) must not be digits +* (the option string itself not a possible value). +* Valid syntaxes are: +* \li program --long-option value -sdfgh -u=5 -i 7 --last=val arg1 arg2 arg3 +* Here is a small code example: +* \code +* #include +* ArgvParser cmd; // the command line parser +* +* // init +* cmd.setIntroductoryDescription("This is foo written by bar."); +* +* //define error codes +* cmd.addErrorCode(0, "Success"); +* cmd.addErrorCode(1, "Error"); +* +* cmd.setHelpOption("h", "help", "Print this help"); +* +* cmd.defineOption("version", ArgvParser::NoOptionAttribute, "Be verbose"); +* cmd.defineOptionAlternative("verbose","v"); +* +* cmd.defineOption("foo", ArgvParser::OptionRequiresValue, "Fooishness. Default value: 0"); +* +* // finally parse and handle return codes (display help etc...) +* int result = cmd.parse(argc, argv); +* +* if (result != ArgvParser::NoParserError) +* cout << cmd.parseErrorDescription(results); +* exit(1); +* \endcode +* +* \author Michael Hanke +*/ +class ArgvParser +{ +public: + typedef int OptionAttributes; + typedef int ParserResults; + typedef std::map String2KeyMap; + typedef std::map Key2AttributeMap; + typedef std::map Key2StringMap; + typedef std::vector ArgumentContainer; + + ArgvParser(); + ~ArgvParser(); + + /** Attributes for options. */ + enum + { + NoAttribute = 0x00, + RequiresValue = 0x01, + Required = 0x02 + }; + /** Return values of the parser. */ + enum + { + Success = 0x00, + UnknownOption = 0x01, + MissingValue = 0x02, + OptionAfterArgument = 0x04, + MalformedMultipleShortOption = 0x08, + RequiredOptionMissing = 0x16, + HelpRequested = 0x32 + }; + + bool defineOption(const std::string& _name, + const std::string& _description = std::string(), + OptionAttributes _attributes = NoAttribute); + + bool defineOption(const std::string& _shortname, + const std::string& _name, + const std::string& _description = std::string(), + OptionAttributes _attributes = NoAttribute); + /** Defines an option with optional attributes (required, ...) and an + * additional (also optional) description. The description becomes part of the + * generated usage help that can be requested by calling the usageDescription() + * method. + * \return Returns FALSE if there already is an option with this name + * OR if a short option string (length == 1) is a digit. In that case no + * action is peformed. + */ + bool defineOptionAlternative(const std::string& _original, + const std::string& _alternative); + /** Define an alternative name for an option that was previously defined by + * defineOption(). + * \return Returns FALSE if there already is an option with the alternative + * name or no option with the original name OR if a short option string + * (length == 1) is a digit. In that case no action is performed. + */ + bool isDefinedOption(const std::string& _name) const; + /** Returns whether _name is a defined option. */ + bool foundOption(const std::string& _name) const; + /** Returns whether _name is an option that was found while parsing + * the command line arguments with the parse() method. In other word: This + * method returns true if the string is an option AND it was given on the + * parsed command line. + */ + bool setHelpOption(const std::string& _longname = "h", + const std::string& _shortname = "help", + const std::string& _descr = ""); + /** Define a help option. If this option is found a special error code is + * returned by the parse method. + * \attention If this method is called twice without an intermediate call + * to the reset() method the previously set help option will remain a valid + * option but is not detected as the special help option and will therefore + * not cause the parse() method to return the special help error code. + * \return Returns FALSE if there already is an option defined that equals + * the short or long name. + */ + unsigned int arguments() const; + /** Returns the number of read arguments. Arguments are efined as beeing + * neither options nor option values and are specified at the end of the + * command line after all options and their values. */ + std::string argument(unsigned int _number) const; + /** Returns the Nth argument. See arguments(). + * \return Argument string or an empty string if there was no argument of + * that id. + */ + const std::vector& allArguments() const; + /** Get the complete argument vector. The order of the arguments in the + * vector is the same as on the commandline. + */ + void addErrorCode(int _code, const std::string& _descr = ""); + /** Add an error code and its description to the command line parser. + * This will do nothing more than adding an entry to the usage description. + */ + void setIntroduction(const std::string& _descr); + /** Set some string as a general description, that will be printed before + * the list of available options. + */ + ParserResults parse(int _argc, char ** _argv); + /** Parse the command line arguments for all known options and arguments. + * \return Error code with parsing result. + * \retval NoParserError Everything went fine. + * \retval ParserUnknownOption Unknown option was found. + * \retval ParserMissingValue A value to a given option is missing. + * \retval ParserOptionAfterArgument Option after an argument detected. All + * options have to given before the first argument. + * \retval ParserMalformedMultipleShortOption Malformed short option string. + * \retval ParserRequiredOptionMissing Required option is missing. + * \retval ParserHelpRequested Help option detected. + */ + std::string optionValue(const std::string& _option) const; + /** Return the value of an option. + * \return Value of a commandline options given by the name of the option or + * an empty string if there was no such option or the option required no + * value. + */ + void reset(); + /** Reset the parser. Call this function if you want to parse another set of + * command line arguments with the same parser object. + */ + const std::string& errorOption() const; + /** Returns the name of the option that was responsible for a parser error. + * An empty string is returned if no error occured at all. + */ + std::string parseErrorDescription(ParserResults _error_code) const; + /** This method can be used to evaluate parser error codes and generate a + * human-readable description. In case of a help request error code the + * usage description as returned by usageDescription() is printed. + */ + std::string usageDescription(unsigned int _width = 80) const; + /** Returns a string with the usage descriptions for all options. The + * description string is formated to fit into a terminal of width _width.*/ + +private: + int optionKey( const std::string& _name ) const; + /** Returns the key of a defined option with name _name or -1 if such option + * is not defined. */ + std::list getAllOptionAlternatives(unsigned int _key) const; + /** Returns a list of option names that are all alternative names associated + * with a single key value. + */ + + /** The current maximum key value for an option. */ + unsigned int max_key; + + /** Map option names to a numeric key. */ + String2KeyMap option2key; + + /** Map option key to option attributes. */ + Key2AttributeMap option2attribute; + + /** Map option key to option description. */ + Key2StringMap option2descr; + + /** Map option key to option value. */ + Key2StringMap option2value; + + /** Map error code to its description. */ + std::map errorcode2descr; + + /** Vector of command line arguments. */ + ArgumentContainer argument_container; + + /** General description to be returned as first part of the generated help page. */ + std::string intro_description; + + /** Holds the key for the help option. */ + unsigned int help_option; + + /** Holds the name of the option that was responsible for a parser error. + */ + std::string error_option; +}; // class ArgvParser + + +// Auxillary functions + +bool isValidOptionString(const std::string& _string); +/** Returns whether the given string is a valid (correct syntax) option string. + * It has to fullfill the following criteria: + * 1. minimum length is 2 characters + * 2. Start with '-' + * 3. if if minimal length -> must not be '--' + * 4. first short option character must not be a digit (to distinguish negative numbers) + */ + +bool isValidLongOptionString(const std::string& _string); +/** Returns whether the given string is a valid (correct syntax) long option string. + * It has to fullfill the following criteria: + * 1. minimum length is 4 characters + * 2. Start with '--' + */ + +bool splitOptionAndValue(const std::string& _string, std::string& _option, + std::string& _value); +/** Splits option and value string if they are given in the form 'option=value'. +* \return Returns TRUE if a value was found. +*/ + +template +void splitString(Container& _container, const std::string& _in, + const char* const _delimiters = " \t\n") +{ +/** String tokenizer using standard C++ functions. Taken from here: + * http://gcc.gnu.org/onlinedocs/libstdc++/21_strings/howto.html#3 + * Splits the string _in by _delimiters and store the tokens in _container. + */ + const std::string::size_type len = _in.length(); + std::string::size_type i = 0; + + while ( i < len ) + { + // eat leading whitespace + i = _in.find_first_not_of (_delimiters, i); + if (i == std::string::npos) + return; // nothing left but white space + + // find the end of the token + std::string::size_type j = _in.find_first_of (_delimiters, i); + + // push token + if (j == std::string::npos) + { + _container.push_back (_in.substr(i)); + return; + } + else + _container.push_back (_in.substr(i, j-i)); + + // set up for next loop + i = j + 1; + } +} + +bool isDigit(const char& _char); +/** Returns true if the character is a digit (what else?). */ + +bool expandRangeStringToUInt(const std::string& _string, + std::vector& _expanded); +/** Build a vector of integers from a string of the form: +* '1,3-5,14,25-20'. This string will be expanded to a list of positive +* integers with the following elements: 1,3,4,5,14,25,24,23,22,21,20. +* All of the expanded elements will be added to the provided list. +* \return Returns FALSE if there was any syntax error in the given string +* In that case the function stops at the point where the error occured. +* Only elements processed up to that point will be added to the expanded +* list. +* \attention This function can only handle unsigned integers! +*/ + +std::string trimmedString(const std::string& _str); +/** Returns a copy of _str with whitespace removed from front and back. */ + +std::string formatString(const std::string& _string, + unsigned int _width, + unsigned int _indent = 0); +/** Formats a string of an arbitrary length to fit a terminal of width +* _width and to be indented by _indent columns. +*/ + +} +; // namespace CommandLineProcessing + +#endif // __CMDLINEPARSER_H diff --git a/tmux-mem-cpu-load.cpp b/tmux-mem-cpu-load.cpp index fb2a816..095a687 100644 --- a/tmux-mem-cpu-load.cpp +++ b/tmux-mem-cpu-load.cpp @@ -20,6 +20,7 @@ #include #include #include // EXIT_SUCCESS +#include "argParse/argParse.h" // Tmux color lookup tables for the different metrics. #include "luts.h" @@ -52,12 +53,13 @@ // OSX: DONE/partial // BSD: TODO + std::string cpu_string(unsigned int cpu_usage_delay, unsigned int graph_lines, - bool use_colors = false) { - + bool use_colors = false) { + float percentage; - //output stuff + //output stuff std::ostringstream oss; oss.precision( 1 ); oss.setf( std::ios::fixed | std::ios::right ); @@ -66,75 +68,80 @@ std::string cpu_string(unsigned int cpu_usage_delay, unsigned int graph_lines, percentage = cpu_percentage( cpu_usage_delay ); if( use_colors ) - oss << cpu_percentage_lut[static_cast( percentage )]; - + oss << cpu_percentage_lut[static_cast( percentage )]; + oss << "["; oss << getGraphByPercentage( unsigned(percentage), graph_lines ); - oss << "]"; + oss << "]"; oss.width( 5 ); oss << percentage; oss << "%"; if( use_colors ) - oss << "#[fg=default,bg=default]"; + oss << "#[fg=default,bg=default]"; return oss.str(); } int main(int argc, char** argv) { - unsigned int cpu_usage_delay = 1000000; - int graph_lines = 10; + using namespace ArgvParse; + + unsigned cpu_usage_delay = 1000000; + unsigned short graph_lines = 10; // max 65535 should be enough bool use_colors = false; - try { - std::istringstream iss; - iss.exceptions ( std::ifstream::failbit | std::ifstream::badbit ); - std::string current_arg; - unsigned int arg_index = 1; - if( argc > arg_index ) - { - if( strcmp( argv[arg_index], "--colors" ) == 0 ) - { - use_colors = true; - ++arg_index; - } - } - if( argc > arg_index ) - { - iss.str( argv[arg_index] ); - int status_interval; - iss >> status_interval; - if( status_interval < 1 ) - { - std::cerr << "Status interval argument must be one or greater." - << std::endl; - return EXIT_FAILURE; - } - cpu_usage_delay = status_interval * 1000000; - ++arg_index; - } - if( argc > arg_index ) - { - iss.str( argv[arg_index] ); - iss.clear(); - iss >> graph_lines; - if( graph_lines < 1 ) - { - std::cerr << "Graph lines argument must be one or greater." - << std::endl; - return EXIT_FAILURE; - } - } - } - catch(const std::exception &e) - { - std::cerr << "Usage: " << argv[0] - << " [--colors] [tmux_status-interval(seconds)] [graph lines]" - << std::endl; - return EXIT_FAILURE; - } + + // Argv parser + ArgvParser arg; + + arg.setIntroduction("tmux-mem-cpu-load \n" + "Usage: tmux-mem-cpu-load [OPTIONS]"); + + arg.setHelpOption("h", "help", "Prints this help message"); + + // define actual options + arg.defineOption("colors", "Use tmux colors in output", + ArgvParser::NoAttribute); + arg.defineOption("i", "interval", "set tmux status refresh interval in " + "seconds. Default: 1 second", ArgvParser::RequiresValue); + arg.defineOption("g", "graph-lines", "Set how many lines should be drawn in " + "a graph. Default: 10", ArgvParser::RequiresValue); + + int result = arg.parse(argc, argv); + + if (result != ArgvParser::Success) { + std::cerr << arg.parseErrorDescription(result); + return EXIT_FAILURE; + } + + // mangle arguments + std::istringstream iss; + iss.exceptions ( std::ifstream::failbit | std::ifstream::badbit ); + + if (arg.foundOption("colors")) + use_colors = true; + + if (arg.foundOption("interval")){ + iss.str(arg.optionValue("interval")); + iss >> cpu_usage_delay; + if (cpu_usage_delay < 1) { + std::cerr << "Status interval argument must be one or greater.\n"; + return EXIT_FAILURE; + } + cpu_usage_delay *= 1000000; + } + + if (arg.foundOption("graph-lines")) { + iss.str( arg.optionValue("graph-lines") ); + iss.clear(); + iss >> graph_lines; + if( graph_lines < 1 ) { + std::cerr << "Graph lines argument must be one or greater.\n"; + return EXIT_FAILURE; + } + } std::cout << mem_string( use_colors ) << ' ' - << cpu_string( cpu_usage_delay, graph_lines, use_colors ) << ' ' - << load_string( use_colors ); + << cpu_string( cpu_usage_delay, graph_lines, use_colors ) << ' ' + << load_string( use_colors ); return EXIT_SUCCESS; }