summaryrefslogtreecommitdiffstats
path: root/libsupp/clif.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsupp/clif.c')
-rw-r--r--libsupp/clif.c1259
1 files changed, 1259 insertions, 0 deletions
diff --git a/libsupp/clif.c b/libsupp/clif.c
new file mode 100644
index 0000000..4ef20e4
--- /dev/null
+++ b/libsupp/clif.c
@@ -0,0 +1,1259 @@
+/*
+ Copyright (c) 2000, 2003 Dmitry Butskoy
+ <buc@citadel.stu.neva.ru>
+ License: LGPL v2.1 or any later
+
+ See COPYING.LIB for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "clif.h"
+
+
+#if 1 /* Bad idea, anyway... */
+#define MAX_ARGC_NUMBER 256
+typedef unsigned char _CLIF_index;
+#else
+#define MAX_ARGC_NUMBER (4096 / 5 + 1) /* POSIX ARG_MAX >= 4096 ... */
+typedef unsigned short _CLIF_index;
+#endif
+
+
+/* This is needed for some print info functions.
+ This is ugly for thread-safe (is it really actual on program invoking?),
+ and for several CLIF_parse_cmdline invoking... But foo on this. Yeah...
+*/
+static struct {
+ int argc;
+ char **argv;
+ CLIF_option *option_list;
+ CLIF_argument *argument_list;
+ unsigned int parse_flags;
+} curr = { 0, };
+
+
+static void err_report (const char *format, ...) {
+ va_list ap;
+
+ if (curr.parse_flags & CLIF_SILENT)
+ return;
+
+ va_start (ap, format);
+
+ vfprintf (stderr, format, ap);
+
+ va_end (ap);
+
+ fprintf (stderr, "\n");
+
+ return;
+}
+
+
+/* info generation stuff... */
+
+#define SHORT_PLUS_MINUS "+/-"
+#define LONG_PLUS_MINUS "++/--"
+#define EXCL_DLM " | "
+
+static char *show_short (const CLIF_option *optn) {
+ static char buf[80];
+ char *p = buf;
+ unsigned int flags = optn->flags | curr.parse_flags;
+
+ if (optn->function_plus) {
+ if (!optn->function) *p++ = '+';
+ else {
+ strcpy (p, SHORT_PLUS_MINUS);
+ p += sizeof (SHORT_PLUS_MINUS) - 1;
+ }
+ } else
+ *p++ = '-';
+
+ *p++ = optn->short_opt[0];
+
+ if (optn->arg_name) {
+ char *endp = buf + sizeof (buf) - sizeof (",...]");
+ const char *s;
+
+ if (!(flags & _CLIF_STRICT_JOIN_ARG)) *p++ = ' ';
+ if (flags & CLIF_OPTARG) *p++ = '[';
+
+ s = optn->arg_name;
+ while (*s && p < endp) *p++ = *s++;
+
+ if (flags & CLIF_SEVERAL) {
+ strcpy (p, ",...");
+ p += sizeof (",...") - 1; /* last '\0' ... */
+ }
+
+ if (flags & CLIF_OPTARG) *p++ = ']';
+ }
+
+ *p = '\0';
+
+ return buf;
+}
+
+static char *show_long (const CLIF_option *optn) {
+ static char buf[80];
+ char *p = buf;
+ char *endp;
+ const char *s;
+ unsigned int flags = optn->flags | curr.parse_flags;
+
+
+ if (!(flags & _CLIF_STRICT_KEYWORD)) {
+
+ if (!(flags & _CLIF_STRICT_ONEDASH)) {
+ if (optn->function_plus) {
+ if (!optn->function) { *p++ = '+'; *p++ = '+'; }
+ else {
+ strcpy (p, LONG_PLUS_MINUS);
+ p += sizeof (LONG_PLUS_MINUS) - 1;
+ }
+ } else { *p++ = '-'; *p++ = '-'; }
+
+ } else {
+ if (optn->function_plus) {
+ if (!optn->function) *p++ = '+';
+ else {
+ strcpy (p, SHORT_PLUS_MINUS);
+ p += sizeof (SHORT_PLUS_MINUS) - 1;
+ }
+ } else *p++ = '-';
+ }
+ }
+
+ s = optn->long_opt;
+ endp = buf + sizeof (buf) - sizeof (" [");
+ while (*s && p < endp) *p++ = *s++;
+
+ if (optn->arg_name) {
+
+ if (flags & _CLIF_STRICT_NOEQUAL) {
+ *p++ = ' ';
+ if (flags & CLIF_OPTARG) *p++ = '[';
+ } else {
+ if (flags & CLIF_OPTARG) *p++ = '[';
+ *p++ = '=';
+ }
+
+ s = optn->arg_name;
+ endp = buf + sizeof (buf) - sizeof (",...]");
+ while (*s && p < endp) *p++ = *s++;
+
+ if (flags & CLIF_SEVERAL) {
+ strcpy (p, ",...");
+ p += sizeof (",...") - 1; /* last '\0' ... */
+ }
+
+ if (flags & CLIF_OPTARG) *p++ = ']';
+ }
+
+ *p = '\0';
+
+ return buf;
+}
+
+static char *show_excl (const CLIF_option *option_list, int *cnt_p) {
+ static char buf[256];
+ const CLIF_option *optn;
+ char *p = buf;
+ char *endp = buf + sizeof (buf) - sizeof (EXCL_DLM);
+ int excl_cnt = 0;
+
+ *p = '\0';
+ if (cnt_p) *cnt_p = 0;
+ if (!option_list) return buf;
+
+ for (optn = option_list; optn->short_opt || optn->long_opt; optn++) {
+ char *s;
+
+ if (!(optn->flags & CLIF_EXCL)) continue;
+
+ if (optn->short_opt) s = show_short (optn);
+ else s = show_long (optn);
+
+ if (excl_cnt > 0) { /* i.e., second etc... */
+ strcpy (p, EXCL_DLM);
+ p += sizeof (EXCL_DLM) - 1;
+ }
+
+ while (*s && p < endp) *p++ = *s++;
+
+ excl_cnt++;
+ }
+
+ *p = '\0';
+
+ if (cnt_p) *cnt_p = excl_cnt;
+
+ return buf;
+}
+
+
+static int is_keyword (const CLIF_option *optn) {
+ unsigned int flags = optn->flags | curr.parse_flags;
+
+ return (flags & _CLIF_STRICT_KEYWORD) != 0;
+}
+
+
+static void err_bad_opt (const char *arg, char c, int n) {
+ char sym = (*arg == '+') ? '+' : '-';
+
+ if (c) err_report ("Bad option `%c%c' (argc %d)", sym, c, n);
+ else {
+ char *p = strchr (arg, '=');
+ const char *type = (*arg == sym) ? "option" : "keyword";
+
+ if (p)
+ err_report ("Bad %s `%s' (with arg `%s') (argc %d)",
+ type, arg, p + 1, n);
+ else
+ err_report ("Bad %s `%s' (argc %d)", type, arg, n);
+ }
+}
+
+static void err_bad_arg (const CLIF_option *optn, char c, int n) {
+ CLIF_option tmp = *optn;
+ char ss[80];
+ char *s;
+
+ tmp.arg_name = NULL;
+
+ if (c) {
+ s = show_short (&tmp); /* always without arg... */
+ strncpy (ss, s, sizeof (ss));
+ s = show_short (optn);
+ } else {
+ s = show_long (&tmp); /* always without arg... */
+ strncpy (ss, s, sizeof (ss));
+ s = show_long (optn);
+ }
+
+ err_report ("%s `%s' (argc %d) requires an argument: `%s'",
+ (c || !is_keyword (optn)) ? "Option" : "Keyword", ss, n, s);
+}
+
+static void err_bad_res (const CLIF_option *optn, char c,
+ const char *opt_arg, int n) {
+ CLIF_option tmp = *optn;
+ char *ss;
+ const char *type;
+
+ tmp.arg_name = NULL;
+
+ if (c) {
+ ss = show_short (&tmp);
+ type = "option";
+ } else {
+ ss = show_long (&tmp);
+ type = is_keyword (optn) ? "keyword" : "option";
+ }
+
+ if (optn->arg_name)
+ err_report ("Cannot handle `%s' %s with arg `%s' (argc %d)",
+ ss, type, opt_arg, n);
+ else
+ err_report ("Cannot handle `%s' %s (argc %d)", ss, type, n);
+}
+
+static void err_bad_excl (const CLIF_option *optn, char c, int n) {
+ CLIF_option tmp = *optn;
+ char *ss;
+ char *excl = show_excl (curr.option_list, 0);
+ /* Note: show_(short|long)() nested!!! */
+
+ tmp.arg_name = NULL;
+
+ if (c) ss = show_short (&tmp);
+ else ss = show_long (&tmp);
+
+ err_report ("%s `%s' (argc %d): Only one of:\n %s\n"
+ "may be specified.",
+ (c || !is_keyword (optn)) ? "Option" : "Keyword",
+ ss, n, excl);
+}
+
+
+static CLIF_option *find_long (char *arg, char **arg_p,
+ unsigned int match, unsigned int nomatch) {
+ CLIF_option *optn;
+ CLIF_option *abbrev = NULL;
+ char *abbrev_arg = NULL;
+ int abbrev_found = 0;
+
+
+ for (optn = curr.option_list;
+ optn->short_opt || optn->long_opt;
+ optn++
+ ) {
+ char *a;
+ const char *o;
+ unsigned int flags;
+
+ if (!optn->long_opt) continue;
+
+ flags = curr.parse_flags | optn->flags;
+ if (flags & nomatch) continue;
+ if (match && !(flags & match)) continue; /* XXX: optimize it */
+
+
+ for (a = arg, o = optn->long_opt; *o && *a == *o; a++, o++) ;
+
+ if (*a == '\0' ||
+ (*a == '=' && optn->arg_name && !(flags & _CLIF_STRICT_NOEQUAL))
+ ) { /* looks like end of option... */
+
+ if (!*o) { /* explicit match found */
+ if (*a == '=' && arg_p) *arg_p = a + 1;
+ return optn;
+ }
+
+ if ((flags & CLIF_ABBREV) &&
+ (a - arg >= CLIF_MIN_ABBREV)
+ ) {
+ if (!abbrev_found) {
+ abbrev_found = 1;
+ abbrev = optn;
+ if (*a == '=') abbrev_arg = a + 1;
+ } else /* several possibility case... */
+ abbrev = NULL;
+ }
+ }
+ }
+
+ if (abbrev) { /* implicit match found */
+ if (abbrev_arg && arg_p) *arg_p = abbrev_arg;
+ return abbrev;
+ } else /* no match found */
+ return NULL;
+}
+
+static int check_sym (const CLIF_option *optn, char sym) {
+
+ if (sym == '+') {
+ if (!optn->function_plus) return -1;
+ }
+ else if (sym == '-') {
+ if (!optn->function && optn->function_plus)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int call_function (CLIF_option *optn, char *opt_arg, char sym) {
+ int (*function) (CLIF_option *, char *);
+
+ function = (sym == '+') ? optn->function_plus : optn->function;
+
+ if (!function) return 0;
+
+ if (opt_arg && ((optn->flags | curr.parse_flags) & CLIF_SEVERAL)) {
+ char tmp[80];
+ char *t;
+ char *endt = tmp + sizeof (tmp);
+
+ while (*opt_arg) {
+
+ t = tmp;
+ while (t < endt && *opt_arg &&
+ *opt_arg != ' ' && *opt_arg != '\t' && *opt_arg != ','
+ ) *t++ = *opt_arg++;
+
+ if (t >= endt) return -1;
+
+ *t = '\0';
+
+ if (function (optn, tmp) < 0) return -1;
+
+ while (*opt_arg == ' ' || *opt_arg == '\t' || *opt_arg == ',')
+ opt_arg++;
+ }
+
+ return 0;
+ }
+
+ return function (optn, opt_arg);
+}
+
+
+int CLIF_parse_cmdline (int argc, char *argv[],
+ CLIF_option *option_list,
+ CLIF_argument *argument_list,
+ unsigned int parse_flags) {
+ int i, j;
+ CLIF_option *optn;
+ CLIF_argument *argm;
+ int num_args = 0;
+ int num_argm = 0, strict_beg = 0, strict_end = 0;
+ _CLIF_index arg_n[MAX_ARGC_NUMBER];
+ unsigned int dirty_flags = 0;
+ int dirty_plus = 0;
+ int exclusive_cnt = 0;
+ int posix = getenv ("POSIXLY_CORRECT") != NULL ||
+ (parse_flags & CLIF_POSIX);
+
+ curr.argc = argc;
+ curr.argv = argv;
+ curr.option_list = option_list;
+ curr.argument_list = argument_list;
+ curr.parse_flags = parse_flags;
+
+ if (argc <= 1 && (parse_flags & CLIF_HELP_EMPTY)) {
+ CLIF_current_help ();
+ exit (0);
+ }
+
+ /* Scan argument_list for check and some info. */
+
+ if (argument_list) {
+ enum stages { STRICT_BEG, OPTIONAL, STRICT_END };
+ int stage = STRICT_BEG;
+
+ for (argm = argument_list; argm->name; argm++) {
+
+ if (argm->flags & CLIF_STRICT) {
+
+ if (stage == STRICT_BEG) strict_beg++;
+ else if (stage == OPTIONAL) {
+ stage = STRICT_END;
+ strict_end++;
+ }
+ else if (stage == STRICT_END)
+ strict_end++;
+ } else {
+ if (stage == STRICT_BEG) stage = OPTIONAL;
+ else if (stage == STRICT_END) {
+ err_report ("Incorrect argument list set in program "
+ "source: more than one optional area.");
+ return -1;
+ }
+ }
+
+ num_argm++;
+ }
+ }
+
+ /* Scan option_list for some info. */
+ if (option_list) {
+
+ dirty_flags = parse_flags;
+
+ for (optn = option_list;
+ optn->short_opt || optn->long_opt;
+ optn++
+ ) {
+ dirty_flags |= optn->flags;
+ if (optn->function_plus) dirty_plus = 1;
+ }
+ }
+
+ if (dirty_flags & CLIF_EXCL)
+ exclusive_cnt = 1; /* only one is allowed... */
+
+
+ /* Go ! Store arguments, parse options. */
+
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ char *opt_arg = NULL;
+ char sym = '-';
+
+ if (!option_list)
+ goto handle_arg;
+
+ if (*arg == '+' && dirty_plus)
+ sym = '+';
+
+ if (*arg != sym) { /* argument or keyword */
+
+ if (dirty_flags & CLIF_MAY_KEYWORD) {
+ optn = find_long (arg, &opt_arg, CLIF_MAY_KEYWORD, 0);
+ if (optn) goto long_found;
+ }
+
+ if (num_args == 0 && (parse_flags & CLIF_FIRST_GROUP)) {
+ /* ugly... */
+ parse_flags &= ~CLIF_FIRST_GROUP;
+ dirty_flags &= ~CLIF_FIRST_GROUP; /* to be correct */
+
+ goto handle_short;
+ }
+
+ /* else it is an argument */
+ goto handle_arg;
+
+ }
+ else if (*++arg == sym) { /* `--' - long option */
+ arg++;
+
+ if (*arg == sym || /* `---' - let it be not option... */
+ (parse_flags & (_CLIF_STRICT_KEYWORD|_CLIF_STRICT_ONEDASH))
+ ) {
+ arg -= 2;
+ goto handle_arg; /* not option anyway */
+ }
+
+ optn = find_long (arg, &opt_arg, 0,
+ _CLIF_STRICT_KEYWORD | _CLIF_STRICT_ONEDASH);
+ if (optn) goto long_found;
+
+ /* XXX: May be allow only for `--', not `++' too... */
+ if (!*arg && sym == '-') { /* `--' and no empty longoption */
+ option_list = NULL; /* POSIX way... */
+ continue;
+ }
+
+ /* XXX: or treat as an argument sometimes??? */
+ err_bad_opt (argv[i], 0, i);
+ return -1;
+ }
+ else { /* short option, or several short options... */
+
+ if (dirty_flags & CLIF_MAY_ONEDASH) {
+ optn = find_long (arg, &opt_arg, CLIF_MAY_ONEDASH, 0);
+ if (optn) goto long_found;
+ }
+
+ if (!*arg) { /* POSIX say: only "stdout specification"... */
+ arg--;
+ goto handle_arg;
+ }
+
+ goto handle_short;
+ }
+
+
+ long_found:
+ if (check_sym (optn, sym) < 0) { /* Oops... */
+ err_bad_opt (argv[i], 0, i);
+ return -1;
+ }
+
+ if (optn->flags & CLIF_EXCL) {
+ if (!exclusive_cnt) {
+ err_bad_excl (optn, 0, i);
+ return -1;
+ }
+ exclusive_cnt--;
+ }
+
+ if (optn->arg_name && !opt_arg) {
+ unsigned int flags = optn->flags | parse_flags;
+
+ if (++i >= argc ||
+ !(flags & CLIF_MAY_NOEQUAL)
+ ) { /* missing opt arg */
+ i--;
+
+ if (!(flags & CLIF_OPTARG)) {
+ err_bad_arg (optn, 0, i);
+ return -1;
+ }
+
+ opt_arg = NULL;
+ } else
+ opt_arg = argv[i];
+
+ }
+
+
+ if (call_function (optn, opt_arg, sym) < 0) {
+ err_bad_res (optn, 0, opt_arg, i);
+ return -1;
+ }
+
+ if (optn->flags & CLIF_EXIT)
+ exit (0);
+
+ continue;
+
+
+ handle_arg:
+ if (argument_list) {
+ if (i < MAX_ARGC_NUMBER) /* XXX: ugly, better report */
+ arg_n[num_args++] = i;
+ } else {
+ err_report ("`%s' (argc %d): arguments are not allowed",
+ argv[i], i);
+ return -1;
+ }
+
+ /* POSIX say: No more options after args... */
+ if (posix) option_list = NULL; /* geniously... */
+
+ continue;
+
+
+ handle_short:
+
+ opt_arg = NULL;
+
+ do {
+
+ for (optn = option_list;
+ optn->short_opt || optn->long_opt;
+ optn++
+ ) {
+ if (optn->short_opt && optn->short_opt[0] == *arg)
+ break;
+ }
+ if (!optn->short_opt ||
+ check_sym (optn, sym) < 0
+ ) {
+ err_bad_opt (argv[i], *arg, i);
+ return -1;
+ }
+
+ if (optn->flags & CLIF_EXCL) {
+ if (!exclusive_cnt) {
+ err_bad_excl (optn, *arg, i);
+ return -1;
+ }
+ exclusive_cnt--;
+ }
+
+
+ if (optn->arg_name) {
+ unsigned int flags = parse_flags | optn->flags;
+
+ if (arg[1] == '\0') { /* a last one */
+
+ /* POSIX say: an option with arg cannot be grouped. */
+ if (posix && arg != argv[i] && arg[-1] != sym) {
+ err_bad_arg (optn, *arg, i); /* good way? */
+ return -1;
+ }
+
+ if (++i >= argc ||
+ (flags & _CLIF_STRICT_JOIN_ARG)
+ ) {
+ i--;
+
+ if (!(flags & CLIF_OPTARG)) {
+ err_bad_arg (optn, *arg, i);
+ return -1;
+ }
+
+ opt_arg = NULL;
+ } else
+ opt_arg = argv[i];
+ }
+ else if ((arg == argv[i] || arg[-1] == sym) &&
+ (flags & CLIF_MAY_JOIN_ARG)
+ ) {
+ opt_arg = ++arg;
+ }
+ else { /* inside a group... */
+ if (!(flags & CLIF_OPTARG) ||
+ (flags & CLIF_MAY_JOIN_ARG)
+ ) {
+ err_bad_arg (optn, *arg, i);
+ return -1;
+ }
+
+ opt_arg = NULL;
+ }
+ }
+
+ if (call_function (optn, opt_arg, sym) < 0) {
+ err_bad_res (optn, optn->short_opt[0], opt_arg, i);
+ return -1;
+ }
+
+ if (optn->flags & CLIF_EXIT)
+ exit (0);
+
+ } while (!opt_arg && *++arg);
+
+ } /* for ( ... ) */
+
+
+ if ((parse_flags & CLIF_STRICT_EXCL) && exclusive_cnt != 0) {
+ err_report ("One of these must be specified:\n %s\n",
+ show_excl (option_list, 0));
+ return -1;
+ }
+
+
+ /* Now, after *ALL* options, handle arguments, if any. */
+
+ if (num_args < strict_beg + strict_end) {
+ /* Missing some needed arguments. */
+
+ if (num_args < strict_beg) argm = argument_list + num_args;
+ else
+ argm = argument_list +
+ ((num_args - strict_beg) + (num_argm - strict_end));
+
+ if (num_args == strict_beg + strict_end - 1)
+ err_report ("Specify \"%s\" missing argument.", argm->name);
+ else
+ err_report ("Specify \"%s\" and other missing arguments.",
+ argm->name);
+ return -1;
+ }
+
+ if (num_args > 0) {
+ _CLIF_index argm_index[MAX_ARGC_NUMBER];
+
+ /* assing argm (by index) for each arg... */
+
+ for (i = 0, j = 0; i < strict_beg; i++, j++)
+ argm_index[i] = j;
+ for (i = num_args - strict_end, j = num_argm - strict_end;
+ i < num_args; i++, j++
+ ) argm_index[i] = j;
+ for (i = strict_beg, j = strict_beg;
+ i < num_args - strict_end && j < num_argm - strict_end;
+ i++
+ ) {
+ argm_index[i] = j;
+ if (!(argument_list[j].flags & CLIF_MORE))
+ j++;
+ }
+
+ if (i < num_args - strict_end) { /* there are extra args... */
+ err_report ("Extra arg `%s' (position %d, argc %d)",
+ argv[arg_n[i]], i + 1, arg_n[i]);
+ return -1;
+ }
+
+ if (j < num_argm - strict_end &&
+ !(argument_list[j].flags & CLIF_MORE) &&
+ /* ...i.e, there are some missing optional args... */
+ (argument_list[j].flags & CLIF_ACC_PREV)
+ ) {
+ if (j == 0)
+ err_report ("Incorrect argument list set: first arg "
+ "cannot be `accompanied with previous'.");
+ else
+ err_report ("Arg \"%s\" must be specified because "
+ "\"%s\" `%s' is used.", argument_list[j].name,
+ argument_list[j - 1].name, argv[arg_n[i - 1]]);
+ return -1;
+ }
+
+ if (argm_index[--i] == j &&
+ /* above is true only after OPTIONAL area scan
+ and when `j' is stopped on CLIF_MORE */
+ ++j < num_argm - strict_end
+ /* i.e: there is a *last* one (after CLIF_MORE)
+ in the OPTIONAL area */
+ ) argm_index[i] = j; /* *last* is better than *more* */
+
+
+ /* ...and work now */
+
+ for (i = 0; i < num_args; i++) {
+ argm = argument_list + argm_index[i];
+
+ if (argm->function &&
+ argm->function (argm, argv[arg_n[i]], i) < 0
+ ) {
+ err_report ("Cannot handle \"%s\" cmdline arg `%s' "
+ "on position %d (argc %d)",
+ argm->name, argv[arg_n[i]], i + 1, arg_n[i]);
+ return -1;
+ }
+ }
+
+ /* That`s all. */
+ }
+
+ return 0;
+}
+
+
+static void box_output (int start, int left, int width, const char *str,
+ const char *arg_name) {
+ char *p, *endp, *s;
+ int l;
+ char buf[1024];
+ char spacer[128]; /* assume it is enough */
+
+ if (left > sizeof (spacer) - 2) left = sizeof (spacer) - 2;
+ if (width > sizeof (buf) - 1) width = sizeof (buf) - 1;
+
+ spacer[0] = '\n';
+ memset (spacer + 1, ' ', left);
+ spacer[left + 1] = '\0';
+
+
+ l = left - start;
+ if (l > 0) {
+ memset (buf, ' ', l);
+ buf[l] = '\0';
+ fprintf (stderr, "%s", buf);
+ } else
+ fprintf (stderr, "%s", spacer);
+
+
+ endp = buf + width;
+
+ p = buf;
+
+ while (*str) {
+
+ while (*str && p < endp) {
+
+ if (*str == '%' && arg_name) {
+ if (str[1] == '%') {
+ *p++ = '%';
+ str += 2;
+ continue;
+ }
+ else if (str[1] == 's') {
+ const char *a = arg_name;
+
+ while (*a && p < endp) *p++ = *a++;
+ str += 2;
+ continue;
+ }
+ }
+
+ *p++ = *str++;
+ }
+
+ *p = '\0';
+
+ if (p < endp) break;
+
+
+ while (p > buf && *p != ' ' && *p != '\t') p--;
+ if (p <= buf) return; /* foo on you */
+
+ *p = '\0';
+ fprintf (stderr, "%s", buf);
+ fprintf (stderr, "%s", spacer);
+
+ p++;
+ for (s = buf; *p; *s++ = *p++) ;
+ *s = '\0';
+ p = s;
+ }
+
+
+ fprintf (stderr, "%s", buf);
+
+ return;
+}
+
+
+#define SHORT_LONG_DLM " "
+#define OPT_START_DLM " "
+#define OPT_FIELD_WIDTH 30
+
+#define ARG_MARK_STRICT "+ "
+#define ARG_MARK_GROUP0 " . "
+#define ARG_MARK_GROUP " ' "
+#define ARG_MARK_OPT " "
+#define ARG_FIELD_WIDTH 20
+
+#define SCREEN_WIDTH 80
+
+
+void CLIF_print_options (const char *header,
+ const CLIF_option *option_list) {
+ const CLIF_option *optn;
+ char *excl;
+ int excl_cnt = 0;
+
+ /* Print a header string, if present... */
+ if (header) fprintf (stderr, "%s\n", header);
+
+ if (!option_list) return;
+
+
+ for (optn = option_list; optn->short_opt || optn->long_opt; optn++) {
+ int len;
+
+ /* generate and print an option usage */
+
+ if (optn->short_opt) {
+ if (optn->long_opt)
+ len = fprintf (stderr, OPT_START_DLM "%s"
+ SHORT_LONG_DLM "%s",
+ show_short (optn), show_long (optn));
+ else
+ len = fprintf (stderr, OPT_START_DLM "%s",
+ show_short (optn));
+ } else
+ len = fprintf (stderr, OPT_START_DLM "%s", show_long (optn));
+
+
+ /* print a help string, if present */
+
+ if (optn->help_string)
+ box_output (len, OPT_FIELD_WIDTH,
+ SCREEN_WIDTH - OPT_FIELD_WIDTH,
+ optn->help_string, optn->arg_name);
+
+ fprintf (stderr, "\n"); /* a last one */
+ }
+
+ excl = show_excl (option_list, &excl_cnt);
+ if (excl_cnt > 0) {
+
+ if (excl_cnt == 1) {
+ if ((curr.parse_flags & CLIF_STRICT_EXCL) &&
+ curr.option_list == option_list
+ ) fprintf (stderr, "Anyway `%s' must be specified.\n", excl);
+ else /* simple ordinary option, because excl_cnt == 1 ... */;
+ } else
+ fprintf (stderr, "Only one of these may be specified:\n"
+ " %s\n", excl);
+ }
+
+ return;
+}
+
+
+void CLIF_print_arguments (const char *header,
+ const CLIF_argument *argument_list) {
+ const CLIF_argument *argm;
+
+
+ if (!argument_list) return;
+
+ /* Print a header string, if present... */
+ if (header) fprintf (stderr, "%s\n", header);
+
+
+ for (argm = argument_list; argm->name; argm++) {
+ int len;
+
+ if (argm->flags & CLIF_STRICT)
+ len = fprintf (stderr, ARG_MARK_STRICT "%s", argm->name);
+ else if (argm->flags & CLIF_MORE)
+ len = fprintf (stderr, ARG_MARK_OPT "%s ...", argm->name);
+ else if (argm->flags & CLIF_ACC_PREV)
+ len = fprintf (stderr, ARG_MARK_GROUP "%s", argm->name);
+ else if ((argm + 1)->name && ((argm + 1)->flags & CLIF_ACC_PREV))
+ len = fprintf (stderr, ARG_MARK_GROUP0 "%s", argm->name);
+ else
+ len = fprintf (stderr, ARG_MARK_OPT "%s", argm->name);
+
+ if (argm->help_string)
+ box_output (len, ARG_FIELD_WIDTH,
+ SCREEN_WIDTH - ARG_FIELD_WIDTH,
+ argm->help_string, argm->name);
+
+ fprintf (stderr, "\n");
+ }
+
+ return;
+}
+
+
+void CLIF_print_usage (const char *header, const char *progname,
+ const CLIF_option *option_list,
+ const CLIF_argument *argument_list) {
+
+ if (!progname && curr.argv)
+ progname = curr.argv[0];
+
+ if (!header) {
+ if (progname)
+ fprintf (stderr, "Usage: %s", progname);
+ else
+ fprintf (stderr, "Command line options:");
+ } else {
+ if (progname)
+ fprintf (stderr, "%s\n" OPT_START_DLM "%s", header, progname);
+ else
+ fprintf (stderr, "%s", header);
+ }
+
+
+ if (option_list) {
+ const CLIF_option *optn;
+ char m_buf[256], p_buf[256], mp_buf[256];
+ char *m = m_buf, *p = p_buf, *mp = mp_buf;
+ char *end_m = m_buf + sizeof (m_buf) - 1;
+ char *end_p = p_buf + sizeof (p_buf) - 1;
+ char *end_mp = mp_buf + sizeof (mp_buf) - 1;
+ char *excl;
+ int excl_cnt = 0;
+
+
+ /* first, show exclusive option list, if any... */
+
+ excl = show_excl (option_list, &excl_cnt);
+ if (excl_cnt > 0) {
+ if ((curr.parse_flags & CLIF_STRICT_EXCL) &&
+ curr.option_list == option_list
+ ) {
+ if (excl_cnt == 1)
+ fprintf (stderr, " %s", excl);
+ else
+ fprintf (stderr, " { %s }", excl);
+ } else
+ fprintf (stderr, " [ %s ]", excl);
+ }
+
+
+ /* second, find short options without arguments... */
+
+ for (optn = option_list;
+ optn->short_opt || optn->long_opt;
+ optn++
+ ) {
+ /* We don`t exclude CLIF_EXTRA hear:
+ simple one char don`t eat a lot of space...
+ */
+
+ if (!optn->short_opt ||
+ optn->arg_name ||
+ (optn->flags & CLIF_EXCL)
+ ) continue;
+
+ if (optn->function_plus) {
+ if (optn->function) {
+ if (mp < end_mp) *mp++ = optn->short_opt[0];
+ } else {
+ if (p < end_p) *p++ = optn->short_opt[0];
+ }
+ } else {
+ if (m < end_m) *m++ = optn->short_opt[0];
+ }
+ }
+
+ if (m > (char *) m_buf) {
+ *m = '\0';
+ fprintf (stderr, " [ -%s ]", m_buf);
+ }
+ if (p > (char *) p_buf) {
+ *p = '\0';
+ fprintf (stderr, " [ +%s ]", p_buf);
+ }
+ if (mp > (char *) mp_buf) {
+ *mp = '\0';
+ fprintf (stderr, " [ " SHORT_PLUS_MINUS "%s ]", mp_buf);
+ }
+
+
+ /* third, print all another... */
+
+ for (optn = option_list;
+ optn->short_opt || optn->long_opt;
+ optn++
+ ) {
+ if (optn->flags & CLIF_EXTRA) continue;
+
+ if (optn->flags & CLIF_EXCL)
+ continue; /* already handled */
+
+ if (optn->short_opt) {
+ if (optn->arg_name)
+ fprintf (stderr, " [ %s ]", show_short (optn));
+ else
+ /* already handled */;
+ } else
+ fprintf (stderr, " [ %s ]", show_long (optn));
+ }
+ }
+
+
+ if (argument_list) {
+ const CLIF_argument *argm;
+ int deep = 0;
+
+ for (argm = argument_list; argm->name; argm++) {
+
+ if (argm->flags & CLIF_STRICT) {
+ if (deep > 0) {
+ fputc (' ', stderr);
+ while (deep--) fputc (']', stderr);
+ deep = 0;
+ }
+
+ fprintf (stderr, " %s", argm->name);
+ } else {
+ if (argm->flags & CLIF_MORE)
+ fprintf (stderr, " [ %s ...", argm->name);
+ else if (argm->flags & CLIF_ACC_PREV) {
+ fprintf (stderr, " %s", argm->name);
+ --deep; /* ugly, but easy */
+ } else
+ fprintf (stderr, " [ %s", argm->name);
+
+ deep++;
+ }
+ }
+
+ if (deep > 0) {
+ fputc (' ', stderr);
+ while (deep--) fputc (']', stderr);
+ }
+ }
+
+
+ fprintf (stderr, "\n");
+}
+
+
+int CLIF_current_help (void) {
+
+ if (!curr.argc) return -1; /* i.e., not inited... */
+
+ CLIF_print_usage ("Usage:", curr.argv[0], curr.option_list,
+ curr.argument_list);
+
+ if (curr.option_list)
+ CLIF_print_options ("Options:", curr.option_list);
+
+ if (curr.argument_list)
+ CLIF_print_arguments ("\nArguments:", curr.argument_list);
+
+ return 0;
+}
+
+
+/* Common useful option handlers. */
+
+int CLIF_version_handler (CLIF_option *optn, char *arg) {
+
+ if (!optn->data) return -1;
+
+ fprintf (stderr, "%s\n", ((char *) optn->data));
+
+ return 0; /* be happy */
+}
+
+
+int CLIF_set_flag (CLIF_option *optn, char *arg) {
+
+ if (!optn->data) return -1;
+
+ *((int *) optn->data) = 1;
+
+ return 0;
+}
+
+
+int CLIF_unset_flag (CLIF_option *optn, char *arg) {
+
+ if (!optn->data) return -1;
+
+ *((int *) optn->data) = 0;
+
+ return 0;
+}
+
+
+static int set_string (char **data, char *arg) {
+
+ if (!data) return -1;
+
+ *data = arg;
+
+ return 0;
+}
+
+int CLIF_set_string (CLIF_option *optn, char *arg) {
+
+ return set_string (optn->data, arg);
+}
+
+int CLIF_arg_string (CLIF_argument *argm, char *arg, int index) {
+
+ return set_string (argm->data, arg);
+}
+
+
+static int set_int (int *data, char *arg) {
+ char *q;
+
+ if (!data) return -1;
+
+ *data = (int) strtol (arg, &q, 0);
+
+ return (q == arg || *q) ? -1 : 0;
+}
+
+static int set_uint (unsigned int *data, char *arg) {
+ char *q;
+
+ if (!data) return -1;
+
+ *data = (unsigned int) strtoul (arg, &q, 0);
+
+ return (q == arg || *q) ? -1 : 0;
+}
+
+static int set_double (double *data, char *arg) {
+ char *q;
+
+ if (!data) return -1;
+
+ *data = strtod (arg, &q);
+
+ return (q == arg || *q) ? -1 : 0;
+}
+
+
+int CLIF_set_int (CLIF_option *optn, char *arg) {
+
+ return set_int (optn->data, arg);
+}
+
+int CLIF_set_uint (CLIF_option *optn, char *arg) {
+
+ return set_uint (optn->data, arg);
+}
+
+int CLIF_set_double (CLIF_option *optn, char *arg) {
+
+ return set_double (optn->data, arg);
+}
+
+int CLIF_arg_int (CLIF_argument *argm, char *arg, int index) {
+
+ return set_int (argm->data, arg);
+}
+
+int CLIF_arg_uint (CLIF_argument *argm, char *arg, int index) {
+
+ return set_uint (argm->data, arg);
+}
+
+int CLIF_arg_double (CLIF_argument *argm, char *arg, int index) {
+
+ return set_double (argm->data, arg);
+}
+
+
+int CLIF_call_func (CLIF_option *optn, char *arg) {
+
+ if (!optn->data) return -1;
+
+ if (optn->arg_name) {
+ int (*func) (char *) = optn->data;
+
+ return func (arg);
+ } else {
+ int (*func) (void) = optn->data;
+
+ return func ();
+ }
+}
+
+int CLIF_arg_func (CLIF_argument *argm, char *arg, int index) {
+ int (*func) (char *, int);
+
+ if (!argm->data) return -1;
+
+ func = (int (*) (char *, int)) argm->data;
+
+ return func (arg, index);
+}
+