--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/cli++/main.cc Sun Jan 20 00:22:09 2008 +0100
@@ -0,0 +1,342 @@
+// Copyright (C) 1999,2000 Bruce Guenter <bruceg@em.ca>
+//
+// 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#include <config.h>
+#include "ac/time.h"
+#include "fdbuf/fdbuf.h"
+#include <stdlib.h>
+#include <string.h>
+#include "cli++.h"
+
+#ifndef HAVE_SRANDOM
+void srandom(unsigned int seed);
+#endif
+
+static bool do_show_usage = false;
+const char* argv0;
+const char* argv0base;
+const char* argv0dir;
+
+static cli_option help_option = { 'h', "help", cli_option::flag,
+ true, &do_show_usage,
+ "Display this help and exit", 0 };
+
+static cli_option** options;
+static unsigned optionc;
+
+static void build_options()
+{
+ for(optionc = 0;
+ cli_options[optionc].ch || cli_options[optionc].name;
+ optionc++) ;
+ optionc++;
+ options = new cli_option*[optionc];
+ for(unsigned i = 0; i < optionc-1; i++)
+ options[i] = &cli_options[i];
+ options[optionc-1] = &help_option;
+}
+
+static inline unsigned max(unsigned a, unsigned b)
+{
+ return (a>b) ? a : b;
+}
+
+static const char* fill(unsigned i)
+{
+ static unsigned lastlen = 0;
+ static char* buf = 0;
+ if(i > lastlen) {
+ delete[] buf;
+ buf = new char[i+1];
+ lastlen = i;
+ }
+ memset(buf, ' ', i);
+ buf[i] = 0;
+ return buf;
+}
+
+static void show_usage()
+{
+ fout << "usage: " << cli_program << " [flags] " << cli_args_usage << endl;
+}
+
+static unsigned calc_max_width()
+{
+ // maxwidth is the maximum width of the long argument
+ unsigned maxwidth = 0;
+ for(unsigned i = 0; i < optionc; i++) {
+ unsigned width = 0;
+ cli_option* o = options[i];
+ if(o->name) {
+ width += strlen(o->name);
+ switch(o->type) {
+ case cli_option::string: width += 6; break;
+ case cli_option::integer: width += 4; break;
+ case cli_option::uinteger: width += 4; break;
+ case cli_option::stringlist: width += 5; break;
+ case cli_option::flag: break;
+ case cli_option::counter: break;
+ }
+ }
+ if(width > maxwidth)
+ maxwidth = width;
+ }
+ return maxwidth;
+}
+
+static void show_option(cli_option* o, unsigned maxwidth)
+{
+ if(o == &help_option)
+ fout << '\n';
+ if(o->ch)
+ fout << " -" << o->ch;
+ else
+ fout << " ";
+ fout << (o->ch && o->name ? ", " : " ");
+ if(o->name) {
+ const char* extra = "";
+ switch(o->type) {
+ case cli_option::string: extra = "=VALUE"; break;
+ case cli_option::integer: extra = "=INT"; break;
+ case cli_option::uinteger: extra = "=UNS"; break;
+ case cli_option::stringlist: extra = "=ITEM"; break;
+ case cli_option::flag: break;
+ case cli_option::counter: break;
+ }
+ fout << "--" << o->name << extra
+ << fill(maxwidth - strlen(o->name) - strlen(extra) + 2);
+ }
+ else
+ fout << fill(maxwidth+4);
+ fout << o->helpstr << '\n';
+ if(o->defaultstr)
+ fout << fill(maxwidth+10) << "(Defaults to " << o->defaultstr << ")\n";
+}
+
+static void show_help()
+{
+ if(cli_help_prefix)
+ fout << cli_help_prefix;
+ unsigned maxwidth = calc_max_width();
+ for(unsigned i = 0; i < optionc; i++)
+ show_option(options[i], maxwidth);
+ if(cli_help_suffix)
+ fout << cli_help_suffix;
+}
+
+void usage(int exit_value, const char* errorstr)
+{
+ if(errorstr)
+ ferr << cli_program << ": " << errorstr << endl;
+ show_usage();
+ show_help();
+ exit(exit_value);
+}
+
+cli_stringlist* stringlist_append(cli_stringlist* node, const char* newstr)
+{
+ cli_stringlist* newnode = new cli_stringlist(newstr);
+ if(node) {
+ cli_stringlist* head = node;
+ while(node->next)
+ node = node->next;
+ node->next = newnode;
+ return head;
+ }
+ else
+ return newnode;
+}
+
+int cli_option::set(const char* arg)
+{
+ char* endptr;
+ switch(type) {
+ case flag:
+ *(int*)dataptr = flag_value;
+ return 0;
+ case counter:
+ *(int*)dataptr += flag_value;
+ return 0;
+ case integer:
+ *(int*)dataptr = strtol(arg, &endptr, 10);
+ if(*endptr) {
+ ferr << argv0 << ": invalid integer: " << arg << endl;
+ return -1;
+ }
+ return 1;
+ case uinteger:
+ *(unsigned*)dataptr = strtoul(arg, &endptr, 10);
+ if(*endptr) {
+ ferr << argv0 << ": invalid unsigned integer: " << arg << endl;
+ return -1;
+ }
+ return 1;
+ case stringlist:
+ *(cli_stringlist**)dataptr =
+ stringlist_append(*(cli_stringlist**)dataptr, arg);
+ return 1;
+ default: // string
+ *(const char**)dataptr = arg;
+ return 1;
+ }
+}
+
+static int parse_short(int argc, char* argv[])
+{
+ int end = strlen(argv[0]) - 1;
+ for(int i = 1; i <= end; i++) {
+ int ch = argv[0][i];
+ unsigned j;
+ for(j = 0; j < optionc; j++) {
+ cli_option* o = options[j];
+ if(o->ch == ch) {
+ if(o->type != cli_option::flag &&
+ o->type != cli_option::counter) {
+ if(i < end) {
+ if(o->set(argv[0]+i+1) != -1)
+ return 0;
+ }
+ else if(argc <= 1) {
+ ferr << argv0 << ": option -" << o->ch
+ << " requires a value." << endl;
+ }
+ else
+ if(o->set(argv[1]) != -1)
+ return 1;
+ }
+ else if(o->set(0) != -1)
+ break;
+ return -1;
+ }
+ }
+ if(j >= optionc) {
+ ferr << argv0 << ": unknown option letter -" << argv[0][i] << endl;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int cli_option::parse_long_eq(const char* arg)
+{
+ if(type == flag || type == counter) {
+ ferr << argv0 << ": option --" << name
+ << " does not take a value." << endl;
+ return -1;
+ }
+ else
+ return set(arg)-1;
+}
+
+int cli_option::parse_long_noeq(const char* arg)
+{
+ if(type == flag || type == counter)
+ return set(0);
+ else if(arg)
+ return set(arg);
+ else {
+ ferr << argv0 << ": option --" << name
+ << " requires a value." << endl;
+ return -1;
+ }
+}
+
+static int parse_long(int, char* argv[])
+{
+ const char* arg = argv[0]+2;
+ for(unsigned j = 0; j < optionc; j++) {
+ cli_option* o = options[j];
+ if(o->name) {
+ size_t len = strlen(o->name);
+ if(!memcmp(arg, o->name, len)) {
+ if(arg[len] == '\0')
+ return o->parse_long_noeq(argv[1]);
+ else if(arg[len] == '=')
+ return o->parse_long_eq(arg+len+1);
+ }
+ }
+ }
+ ferr << argv0 << ": unknown option string: '--" << arg << "'" << endl;
+ return -1;
+}
+
+static int parse_args(int argc, char* argv[])
+{
+ build_options();
+ int i;
+ for(i = 1; i < argc; i++) {
+ const char* arg = argv[i];
+ // Stop at the first non-option argument
+ if(arg[0] != '-')
+ break;
+ // Stop after the first "-" or "--"
+ if(arg[1] == '\0' ||
+ (arg[1] == '-' && arg[2] == '\0')) {
+ i++;
+ break;
+ }
+ int j = (arg[1] != '-') ?
+ parse_short(argc-i, argv+i) :
+ parse_long(argc-i, argv+i);
+ if(j < 0)
+ usage(1);
+ else
+ i += j;
+ }
+ return i;
+}
+
+static void set_argv0(const char* p)
+{
+ argv0 = p;
+ static const char* empty = "";
+ const char* s = strrchr(p, '/');
+ if(s) {
+ ++s;
+ argv0base = s;
+ size_t length = s-p;
+ char* tmp = new char[length+1];
+ memcpy(tmp, p, length);
+ tmp[length] = 0;
+ argv0dir = tmp;
+ }
+ else {
+ argv0base = p;
+ argv0dir = empty;
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ srandom(tv.tv_usec ^ tv.tv_sec);
+
+ set_argv0(argv[0]);
+ int lastarg = parse_args(argc, argv);
+
+ if(do_show_usage)
+ usage(0);
+
+ argc -= lastarg;
+ argv += lastarg;
+ if(argc < cli_args_min)
+ usage(1, "Too few command-line arguments");
+ if(cli_args_max >= cli_args_min && argc > cli_args_max)
+ usage(1, "Too many command-line arguments");
+
+ return cli_main(argc, argv);
+}