[cig-commits] r12948 - cs/cigma/trunk/src
luis at geodynamics.org
luis at geodynamics.org
Wed Sep 24 04:10:36 PDT 2008
Author: luis
Date: 2008-09-24 04:10:34 -0700 (Wed, 24 Sep 2008)
New Revision: 12948
Added:
cs/cigma/trunk/src/cli_application.cpp
cs/cigma/trunk/src/cli_application.h
Log:
Common code for multiple CLI commands
Added: cs/cigma/trunk/src/cli_application.cpp
===================================================================
--- cs/cigma/trunk/src/cli_application.cpp (rev 0)
+++ cs/cigma/trunk/src/cli_application.cpp 2008-09-24 11:10:34 UTC (rev 12948)
@@ -0,0 +1,265 @@
+/* C++ includes */
+#include <iostream>
+#include <iomanip>
+#include <cstdlib>
+#include <cassert>
+#include <stdexcept>
+
+/* cigma includes */
+#include "config.h"
+#include "cli_application.h"
+
+/* basic commands */
+#include "cli_list_cmd.h"
+//#include "cli_compare_cmd.h"
+//#include "cli_extract_cmd.h"
+//#include "cli_eval_cmd.h"
+//#include "cli_info_cmd.h"
+
+using namespace std;
+using namespace cigma;
+
+
+// ----------------------------------------------------------------------------
+
+Application::Application()
+{
+ /* Initialize the basic set of commands */
+ addCommand(new ListCmd());
+ //addCommand(new ExtractCmd());
+ //addCommand(new EvalCmd());
+ //addCommand(new CompareCmd());
+ //addCommand(new InfoCmd());
+}
+
+
+// ----------------------------------------------------------------------------
+
+Application::~Application()
+{
+ for (CmdMap::iterator i = commands.begin(); i != commands.end(); ++i)
+ {
+ Command *cmd = i->second;
+ if (cmd != 0) delete cmd;
+ }
+}
+
+void Application::addCommand(Command *cmd)
+{
+ if (cmd != 0)
+ {
+ names.push_back(cmd->name);
+ commands[cmd->name] = cmd;
+ }
+}
+
+Command *Application::getCommand(const std::string &name)
+{
+ Command *cmd = 0;
+ CmdMap::iterator it;
+
+ it = commands.find(name);
+ if (it != commands.end())
+ {
+ cmd = it->second;
+ }
+
+ return cmd;
+}
+
+static void printquickhelp()
+{
+ cout << "Type 'cigma help' for usage." << endl;
+}
+
+static bool helpflag(const char *s)
+{
+ return (strcmp(s, "help") == 0) ||
+ (strcmp(s, "--help") == 0) ||
+ (strcmp(s, "-h") == 0);
+}
+
+static bool versionflag(const char *s)
+{
+ return (strcmp(s, "version") == 0) ||
+ (strcmp(s, "--version") == 0);
+}
+
+void Application::showHelp(int argc, char *argv[])
+{
+ /*
+ * As a precondition, we assume that a help request
+ * (i.e. one of argv[] elements satisfies the statement
+ * assert(helpflag(argv[i]) == true).
+ *
+ * We still need to determine what kind of help request
+ * was made. If we want help on a subcommand, defer to
+ * its own cmd->printUsage() method, otherwise we call
+ * the method this->showHelp() instead. If there
+ * are any errors, we prefer to call printquickhelp().
+ */
+ if (argc > 3)
+ {
+ //
+ // argv[0] argv[1] argv[2] ...
+ //
+ cout << "Too many arguments" << endl;
+ printquickhelp();
+ }
+ else if (argc == 3)
+ {
+ //
+ // argv[0] argv[1] argv[2]
+ // cigma help subcommand
+ //
+ assert(helpflag(argv[1]));
+ Command *cmd = this->getCommand(argv[2]);
+ if (cmd != 0)
+ {
+ cmd->printUsage();
+ }
+ else
+ {
+ /* Complain, since argv[2] was not a valid subcommand */
+ cout << "Unknown "
+ << ((argv[2][0] == '-') ? "option" : "command")
+ << ": '" << argv[2] << "'"
+ << endl;
+ printquickhelp();
+ }
+ }
+ else if (argc == 2)
+ {
+ //
+ // argv[0] argv[1]
+ // cigma help
+ //
+ if (helpflag(argv[1]))
+ {
+ this->showHelp();
+ }
+ else
+ {
+ // this should be unreachable
+ throw std::invalid_argument("Bad help request.");
+ }
+ }
+ else
+ {
+ //
+ // argv[0]
+ //
+ printquickhelp();
+ }
+}
+
+void Application::showHelp()
+{
+ /*
+ * Use the same help display from the subversion command, which is also
+ * an application with multiple subcommands.
+ */
+ cout << "Usage: cigma <subcommand> [options] [args]" << endl;
+ cout << "Type 'cigma help <subcommand>' for help on a specific subcommand." << endl;
+ cout << "Type 'cigma --version' to see the program version" << endl << endl;
+ cout << "Available subcommands:" << endl;
+
+ /*
+ * We already know the lengths of strings corresponding to
+ * each name and its brief description, so we can calculate the
+ * optimal amount of white space to align the following two columns.
+ *
+ * Alternatively, we can just assume a maximum length for the
+ * first column, by using std::setw(9) for example. This is probably
+ * more elegant, since we only ever add new commands at compile time.
+ */
+ string prefix = " ";
+ for (CmdNames::iterator i = names.begin(); i != names.end(); ++i)
+ {
+ Command *cmd = getCommand(*i);
+ if (cmd != 0)
+ {
+ cout << prefix
+ << std::setw(9) << std::left
+ << cmd->name
+ << cmd->brief
+ << endl;
+ }
+ }
+
+ cout << endl;
+ cout << "Cigma is a tool for querying & analyzing numerical models." << endl;
+ cout << "For additional information, visit http://geodynamics.org/" << endl;
+}
+
+void Application::showVersion()
+{
+ cout << PACKAGE_STRING << endl;
+}
+
+int Application::main(int argc, char *argv[])
+{
+ try
+ {
+ Command *cmd = 0;
+
+ /*
+ * Check argv[1] for a command, but make sure to intercept
+ * any help or version requests.
+ */
+ if (argc > 1)
+ {
+ if (helpflag(argv[1]))
+ {
+ this->showHelp(argc, argv);
+ return 0;
+ }
+ else if (versionflag(argv[1]))
+ {
+ this->showVersion();
+ return 0;
+ }
+ else
+ {
+ cmd = this->getCommand(argv[1]);
+ }
+ }
+
+ if (cmd != 0)
+ {
+ /* Let the command handle the rest. */
+ int status = cmd->main(argc-1, argv+1);
+ return status;
+ }
+ else
+ {
+ if (argc > 1)
+ {
+ /* Complain, since argv[1] was not a valid command */
+ cout << "Unknown "
+ << ((argv[1][0] == '-') ? "option" : "command")
+ << ": '" << argv[1] << "'"
+ << endl;
+ }
+ printquickhelp();
+ return -1;
+ }
+ }
+ catch (bad_cast &e)
+ {
+ cout << "bad cast: " << e.what() << endl;
+ return -1;
+ }
+ catch (exception &e)
+ {
+ cout << "cigma: " << e.what() << endl;
+ return -1;
+ }
+ catch (...)
+ {
+ cout << "Unrecognized exception!" << endl;
+ }
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
Added: cs/cigma/trunk/src/cli_application.h
===================================================================
--- cs/cigma/trunk/src/cli_application.h (rev 0)
+++ cs/cigma/trunk/src/cli_application.h 2008-09-24 11:10:34 UTC (rev 12948)
@@ -0,0 +1,79 @@
+#ifndef __CIGMA_APPLICATION_H__
+#define __CIGMA_APPLICATION_H__
+
+#include <map>
+#include <vector>
+#include <string>
+
+#include "cli_command.h"
+#include <boost/noncopyable.hpp>
+
+namespace cigma
+{
+ class Application;
+}
+
+/**
+ * @brief Basic command-line application with subcommands.
+ *
+ */
+class cigma::Application : private boost::noncopyable
+{
+public:
+ /**
+ * Constructor for our application, where we initialize a fixed
+ * set of commands. Therefore, any new commands must be added
+ * explicitly in the implementation of this method.
+ */
+ Application();
+
+ /**
+ * Destructor for our application. It deallocates the command
+ * objects that were created in the constructor.
+ */
+ ~Application();
+
+protected:
+ /**
+ * Internal method for adding a new command to the collection.
+ */
+ void addCommand(Command *cmd);
+
+ /**
+ * Internal method for retrieving an appropriate command given its name.
+ */
+ Command *getCommand(const std::string &name);
+
+ /**
+ * Internal method for displaying the appropriate help string
+ * for a subcommand.
+ */
+ void showHelp(int argc, char *argv[]);
+
+ /*
+ * Internal method for displaying basic help information.
+ */
+ void showHelp();
+
+ /**
+ * Internal method for displaying the current version string.
+ */
+ void showVersion();
+
+public:
+ /**
+ * Main method for dispatching to the appropriate command,
+ * which is determined based on the value of argv[1].
+ */
+ int main(int argc, char *argv[]);
+
+private:
+
+ typedef std::map<std::string,Command*> CmdMap;
+ typedef std::vector<std::string> CmdNames;
+
+ CmdNames names; /// Order in which commands are displayed
+ CmdMap commands; /// Maps names to their corresponding command pointers.
+};
+
+#endif
More information about the cig-commits
mailing list