[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