parse-options: be able to generate usages automatically
authorPierre Habouzit <madcoder@debian.org>
Sun, 14 Oct 2007 23:38:30 +0000 (01:38 +0200)
committerJunio C Hamano <gitster@pobox.com>
Tue, 30 Oct 2007 04:03:30 +0000 (21:03 -0700)
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
parse-options.c
parse-options.h

index 7bdffdbe513f56ec995519e2992c551b4f6e22de..57a2a1126613ad31ecb2d55c9ff37f58b42d1618 100644 (file)
@@ -123,7 +123,7 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,
 }
 
 int parse_options(int argc, const char **argv, const struct option *options,
-                  const char *usagestr, int flags)
+                  const char * const usagestr[], int flags)
 {
        struct optparse_t args = { argv + 1, argc - 1, NULL };
        int j = 0;
@@ -140,9 +140,9 @@ int parse_options(int argc, const char **argv, const struct option *options,
                        args.opt = arg + 1;
                        do {
                                if (*args.opt == 'h')
-                                       usage(usagestr);
+                                       usage_with_options(usagestr, options);
                                if (parse_short_opt(&args, options) < 0)
-                                       usage(usagestr);
+                                       usage_with_options(usagestr, options);
                        } while (args.opt);
                        continue;
                }
@@ -156,12 +156,75 @@ int parse_options(int argc, const char **argv, const struct option *options,
                }
 
                if (!strcmp(arg + 2, "help"))
-                       usage(usagestr);
+                       usage_with_options(usagestr, options);
                if (parse_long_opt(&args, arg + 2, options))
-                       usage(usagestr);
+                       usage_with_options(usagestr, options);
        }
 
        memmove(argv + j, args.argv, args.argc * sizeof(*argv));
        argv[j + args.argc] = NULL;
        return j + args.argc;
 }
+
+#define USAGE_OPTS_WIDTH 24
+#define USAGE_GAP         2
+
+void usage_with_options(const char * const *usagestr,
+                        const struct option *opts)
+{
+       struct strbuf sb;
+
+       strbuf_init(&sb, 4096);
+       strbuf_addstr(&sb, *usagestr);
+       strbuf_addch(&sb, '\n');
+       while (*++usagestr)
+               strbuf_addf(&sb, "    %s\n", *usagestr);
+
+       if (opts->type != OPTION_GROUP)
+               strbuf_addch(&sb, '\n');
+
+       for (; opts->type != OPTION_END; opts++) {
+               size_t pos;
+               int pad;
+
+               if (opts->type == OPTION_GROUP) {
+                       strbuf_addch(&sb, '\n');
+                       if (*opts->help)
+                               strbuf_addf(&sb, "%s\n", opts->help);
+                       continue;
+               }
+
+               pos = sb.len;
+               strbuf_addstr(&sb, "    ");
+               if (opts->short_name)
+                       strbuf_addf(&sb, "-%c", opts->short_name);
+               if (opts->long_name && opts->short_name)
+                       strbuf_addstr(&sb, ", ");
+               if (opts->long_name)
+                       strbuf_addf(&sb, "--%s", opts->long_name);
+
+               switch (opts->type) {
+               case OPTION_INTEGER:
+                       strbuf_addstr(&sb, " <n>");
+                       break;
+               case OPTION_STRING:
+                       if (opts->argh)
+                               strbuf_addf(&sb, " <%s>", opts->argh);
+                       else
+                               strbuf_addstr(&sb, " ...");
+                       break;
+               default:
+                       break;
+               }
+
+               pad = sb.len - pos;
+               if (pad <= USAGE_OPTS_WIDTH)
+                       pad = USAGE_OPTS_WIDTH - pad;
+               else {
+                       strbuf_addch(&sb, '\n');
+                       pad = USAGE_OPTS_WIDTH;
+               }
+               strbuf_addf(&sb, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
+       }
+       usage(sb.buf);
+}
index 76d73b299f8fdfb2dcdcf881f4e813cc14ad4db5..3006a769cdd561d8a66c3eaeab3307d2e8273ca3 100644 (file)
@@ -3,6 +3,7 @@
 
 enum parse_opt_type {
        OPTION_END,
+       OPTION_GROUP,
        OPTION_BOOLEAN,
        OPTION_STRING,
        OPTION_INTEGER,
@@ -17,12 +18,15 @@ struct option {
        int short_name;
        const char *long_name;
        void *value;
+       const char *argh;
+       const char *help;
 };
 
 #define OPT_END()                   { OPTION_END }
-#define OPT_BOOLEAN(s, l, v, h)     { OPTION_BOOLEAN, (s), (l), (v) }
-#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v) }
-#define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v) }
+#define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
+#define OPT_BOOLEAN(s, l, v, h)     { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) }
+#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), NULL, (h) }
+#define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
 
 /* parse_options() will filter out the processed options and leave the
  * non-option argments in argv[].
@@ -30,6 +34,9 @@ struct option {
  */
 extern int parse_options(int argc, const char **argv,
                          const struct option *options,
-                         const char *usagestr, int flags);
+                         const char * const usagestr[], int flags);
+
+extern NORETURN void usage_with_options(const char * const *usagestr,
+                                        const struct option *options);
 
 #endif