Gitweb links:

...log 
http://git.netsurf-browser.org/libnsgif.git/shortlog/d4e69fcebd776233c42ef044230ce3b5b83e61de
...commit 
http://git.netsurf-browser.org/libnsgif.git/commit/d4e69fcebd776233c42ef044230ce3b5b83e61de
...tree 
http://git.netsurf-browser.org/libnsgif.git/tree/d4e69fcebd776233c42ef044230ce3b5b83e61de

The branch, master has been updated
       via  d4e69fcebd776233c42ef044230ce3b5b83e61de (commit)
       via  68350cd5c6d73ad210e1ae9d1dd102f4295639c1 (commit)
       via  26dd4eae0c35b25d1f8cf9ce3896317cf04961ce (commit)
       via  ffec523aa0a938beab7def1893d41685e28d2b34 (commit)
       via  fde3d979ace97bd109fce443b49cbbfa5a9307a2 (commit)
       via  d63e9e479c63f8f0a1579ea58205862047297f1a (commit)
      from  ca8b9c3784f1f0f074cff1e5474d4a16b80885c6 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commitdiff 
http://git.netsurf-browser.org/libnsgif.git/commit/?id=d4e69fcebd776233c42ef044230ce3b5b83e61de
commit d4e69fcebd776233c42ef044230ce3b5b83e61de
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    test: Add --help option to nsgif utility

diff --git a/test/nsgif.c b/test/nsgif.c
index 53c6a68..f873bab 100644
--- a/test/nsgif.c
+++ b/test/nsgif.c
@@ -29,15 +29,17 @@ static struct nsgif_options {
        uint64_t loops;
        bool palette;
        bool info;
+       bool help;
 } nsgif_options;
 
 static const struct cli_table_entry cli_entries[] = {
        {
-               .s = 'm',
-               .l = "ppm",
-               .t = CLI_STRING,
-               .v.s = &nsgif_options.ppm,
-               .d = "Convert frames to PPM image at given path."
+               .s = 'h',
+               .l = "help",
+               .t = CLI_BOOL,
+               .no_pos = true,
+               .v.b = &nsgif_options.help,
+               .d = "Print this text.",
        },
        {
                .s = 'i',
@@ -55,6 +57,13 @@ static const struct cli_table_entry cli_entries[] = {
                     "The default is 1."
        },
        {
+               .s = 'm',
+               .l = "ppm",
+               .t = CLI_STRING,
+               .v.s = &nsgif_options.ppm,
+               .d = "Convert frames to PPM image at given path."
+       },
+       {
                .s = 'p',
                .l = "palette",
                .t = CLI_BOOL,
@@ -358,6 +367,11 @@ int main(int argc, char *argv[])
                return EXIT_FAILURE;
        }
 
+       if (nsgif_options.help) {
+               cli_help(&cli, argv[0]);
+               return EXIT_SUCCESS;
+       }
+
        if (nsgif_options.ppm != NULL) {
                ppm = fopen(nsgif_options.ppm, "w+");
                if (ppm == NULL) {


commitdiff 
http://git.netsurf-browser.org/libnsgif.git/commit/?id=68350cd5c6d73ad210e1ae9d1dd102f4295639c1
commit 68350cd5c6d73ad210e1ae9d1dd102f4295639c1
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    test: Add appliction description to help output

diff --git a/test/nsgif.c b/test/nsgif.c
index 7d61504..53c6a68 100644
--- a/test/nsgif.c
+++ b/test/nsgif.c
@@ -74,6 +74,7 @@ const struct cli_table cli = {
        .entries = cli_entries,
        .count = (sizeof(cli_entries))/(sizeof(*cli_entries)),
        .min_positional = 1,
+       .d = "NSGIF - A utility for inspecting and decoding GIFs with libnsgif",
 };
 
 static void *bitmap_create(int width, int height)


commitdiff 
http://git.netsurf-browser.org/libnsgif.git/commit/?id=26dd4eae0c35b25d1f8cf9ce3896317cf04961ce
commit 26dd4eae0c35b25d1f8cf9ce3896317cf04961ce
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    cli: Print useful error message for unknown enum value

diff --git a/test/cli.c b/test/cli.c
index b1b776f..851750c 100644
--- a/test/cli.c
+++ b/test/cli.c
@@ -22,6 +22,7 @@
  */
 struct cli_ctx {
        const struct cli_table *cli; /**< Client CLI spec. */
+       size_t pos_count; /**< The number of positional arguments found. */
        bool no_pos; /**< Have an argument that negates min_positional. */
 };
 
@@ -120,6 +121,8 @@ static bool cli__parse_value_enum(
                }
        }
 
+       fprintf(stderr, "ERROR: Unknown enum value '%s'.\n", str);
+
        return false;
 }
 
@@ -294,6 +297,73 @@ static bool cli__handle_arg_value(const struct 
cli_table_entry *entry,
        return true;
 }
 
+static inline bool cli__is_negative(const char *arg)
+{
+       int64_t i;
+       size_t pos = 0;
+
+       return cli__parse_value_int(arg, &i, &pos)
+                       && pos == strlen(arg)
+                       && i < 0;
+}
+
+/**
+ * Parse a positional argument according to the given CLI spec entry.
+ *
+ * \param[in] ctx    Command line interface parsing context.
+ * \param[in] entry  Client command line interface argument specification.
+ * \param[in] arg    Argument to parse.
+ * \return true on success, or false otherwise.
+ */
+static bool cli__parse_positional_entry(struct cli_ctx *ctx,
+               const struct cli_table_entry *entry,
+               const char *arg)
+{
+       size_t pos = 0;
+       bool ret;
+
+       ret = cli__parse_value(entry, arg, &pos);
+       if (ret != true) {
+               return ret;
+       } else if (arg[pos] != '\0') {
+               fprintf(stderr, "Failed to parse value '%s' for arg '%s'\n",
+                               arg, entry->l);
+               return false;
+       }
+
+       ctx->pos_count++;
+       return true;
+}
+
+/**
+ * Parse a positional argument.
+ *
+ * \param[in] ctx    Command line interface parsing context.
+ * \param[in] arg    Argument to parse.
+ * \param[in] count  Number of positional arguments parsed already.
+ * \return true on success, or false otherwise.
+ */
+static bool cli__parse_positional(struct cli_ctx *ctx,
+               const char *arg)
+{
+       const struct cli_table *cli = ctx->cli;
+       size_t positional = 0;
+
+       for (size_t i = 0; i < cli->count; i++) {
+               if (cli__entry_is_positional(&cli->entries[i])) {
+                       if (positional == ctx->pos_count) {
+                               return cli__parse_positional_entry(ctx,
+                                               &cli->entries[i], arg);
+                       }
+
+                       positional++;
+               }
+       }
+
+       fprintf(stderr, "Unexpected positional argument: '%s'\n", arg);
+       return false;
+}
+
 /**
  * Parse a flags argument.
  *
@@ -318,6 +388,9 @@ static bool cli__parse_short(struct cli_ctx *ctx,
 
                entry = cli__lookup_short(ctx->cli, arg[pos]);
                if (entry == NULL) {
+                       if (cli__is_negative(argv[pos])) {
+                               return cli__parse_positional(ctx, argv[pos]);
+                       }
                        return false;
                }
 
@@ -424,60 +497,6 @@ static bool cli__parse_long(struct cli_ctx *ctx,
 }
 
 /**
- * Parse a positional argument according to the given CLI spec entry.
- *
- * \param[in] entry  Client command line interface argument specification.
- * \param[in] arg    Argument to parse.
- * \return true on success, or false otherwise.
- */
-static bool cli__parse_positional_entry(
-               const struct cli_table_entry *entry,
-               const char *arg)
-{
-       size_t pos = 0;
-       bool ret;
-
-       ret = cli__parse_value(entry, arg, &pos);
-       if (ret != true) {
-               return ret;
-       } else if (arg[pos] != '\0') {
-               fprintf(stderr, "Failed to parse value '%s' for arg '%s'\n",
-                               arg, entry->l);
-               return false;
-       }
-
-       return true;
-}
-
-/**
- * Parse a positional argument.
- *
- * \param[in] cli    Client command line interface specification.
- * \param[in] arg    Argument to parse.
- * \param[in] count  Number of positional arguments parsed already.
- * \return true on success, or false otherwise.
- */
-static bool cli__parse_positional(const struct cli_table *cli,
-               const char *arg, size_t count)
-{
-       size_t positional = 0;
-
-       for (size_t i = 0; i < cli->count; i++) {
-               if (cli__entry_is_positional(&cli->entries[i])) {
-                       if (positional == count) {
-                               return cli__parse_positional_entry(
-                                               &cli->entries[i], arg);
-                       }
-
-                       positional++;
-               }
-       }
-
-       fprintf(stderr, "Unexpected positional argument: '%s'\n", arg);
-       return false;
-}
-
-/**
  * Get the string to indicate type of value expected for an argument.
  *
  * \param[in] type  The argument type.
@@ -569,20 +588,9 @@ static void cli__count(const struct cli_table *cli,
        }
 }
 
-static inline bool cli__is_negative(const char *arg)
-{
-       int64_t i;
-       size_t pos = 0;
-
-       return cli__parse_value_int(arg, &i, &pos)
-                       && pos == strlen(arg)
-                       && i < 0;
-}
-
 /* Documented in cli.h */
 bool cli_parse(const struct cli_table *cli, int argc, const char **argv)
 {
-       size_t pos_count = 0;
        struct cli_ctx ctx = {
                .cli = cli,
        };
@@ -593,7 +601,6 @@ bool cli_parse(const struct cli_table *cli, int argc, const 
char **argv)
 
        for (int i = ARG_FIRST; i < argc; i++) {
                const char *arg = argv[i];
-               size_t pos_inc = 0;
                bool ret;
 
                if (arg[0] == '-') {
@@ -601,28 +608,17 @@ bool cli_parse(const struct cli_table *cli, int argc, 
const char **argv)
                                ret = cli__parse_long(&ctx, argc, argv, &i);
                        } else {
                                ret = cli__parse_short(&ctx, argc, argv, &i);
-                               if (ret != true) {
-                                       if (cli__is_negative(argv[i])) {
-                                               pos_inc = 1;
-                                               ret = cli__parse_positional(
-                                                               cli, argv[i],
-                                                               pos_count);
-                                       }
-                               }
                        }
                } else {
-                       pos_inc = 1;
-                       ret = cli__parse_positional(cli, argv[i], pos_count);
+                       ret = cli__parse_positional(&ctx, argv[i]);
                }
 
                if (ret != true) {
                        return ret;
                }
-
-               pos_count += pos_inc;
        }
 
-       if (ctx.no_pos == false && pos_count < cli->min_positional) {
+       if (ctx.no_pos == false && ctx.pos_count < cli->min_positional) {
                fprintf(stderr, "Insufficient positional arguments found.\n");
                return false;
        }


commitdiff 
http://git.netsurf-browser.org/libnsgif.git/commit/?id=ffec523aa0a938beab7def1893d41685e28d2b34
commit ffec523aa0a938beab7def1893d41685e28d2b34
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    cli: Allow optional application description

diff --git a/test/cli.c b/test/cli.c
index 8f32228..b1b776f 100644
--- a/test/cli.c
+++ b/test/cli.c
@@ -742,6 +742,12 @@ void cli_help(const struct cli_table *cli, const char 
*prog_name)
 
        cli__count(cli, &count, &pcount, &max_len, &pmax_len, &phas_desc);
 
+       if (cli->d != NULL) {
+               fprintf(stderr, "\n");
+               cli__print_wrapping_string(cli->d, 0);
+               fprintf(stderr, "\n");
+       }
+
        fprintf(stderr, "\nUsage: %s", prog_name);
 
        if (pcount > 0) {
diff --git a/test/cli.h b/test/cli.h
index d7493e4..ffcd272 100644
--- a/test/cli.h
+++ b/test/cli.h
@@ -73,6 +73,7 @@ struct cli_table {
        const struct cli_table_entry *entries;
        size_t count;
        size_t min_positional;
+       const char *d; /**< Description of this application for help output. */
 };
 
 /**


commitdiff 
http://git.netsurf-browser.org/libnsgif.git/commit/?id=fde3d979ace97bd109fce443b49cbbfa5a9307a2
commit fde3d979ace97bd109fce443b49cbbfa5a9307a2
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    cli: Improve help output for enum arguments

diff --git a/test/cli.c b/test/cli.c
index e1d1a8b..8f32228 100644
--- a/test/cli.c
+++ b/test/cli.c
@@ -641,46 +641,90 @@ static size_t cli__terminal_width(void)
 }
 
 /**
- * Print an entry's description, with a given indent.
+ * Print a wrapped string, with a given indent.
  *
  * The indent is assumed to already be applied for the first line of the
  * output by the caller.
- * 
- * \param[in] entry   The entry to print the description for.
+ *
+ * \param[in] str     The string to print.
  * \param[in] indent  The number of spaces to pad the left margin with.
  */
-static void cli__print_description(const struct cli_table_entry *entry,
-               size_t indent)
+static void cli__print_wrapping_string(const char *str, size_t indent)
 {
        size_t terminal_width = cli__terminal_width();
        size_t avail = (indent > terminal_width) ? 0 : terminal_width - indent;
        size_t space = avail;
-       const char *desc = entry->d;
-
-       if (desc != NULL) {
-               while (*desc != '\0') {
-                       size_t word_len = strcspn(desc, " \n\t");
-                       if (word_len <= space || space == avail) {
-                               fprintf(stderr, "%*.*s",
-                                               (int)word_len,
-                                               (int)word_len, desc);
-                               desc += word_len;
-                               if (word_len <= space) {
-                                       space -= word_len;
-                               }
-                               if (space > 0) {
-                                       fprintf(stderr, " ");
-                                       space--;
-                               }
-                       } else {
-                               fprintf(stderr, "\n%*s", (int)indent, "");
-                               space = avail;
+
+       while (*str != '\0') {
+               size_t word_len = strcspn(str, " \n\t");
+               if (word_len <= space || space == avail) {
+                       fprintf(stderr, "%*.*s",
+                                       (int)word_len,
+                                       (int)word_len, str);
+                       str += word_len;
+                       if (word_len <= space) {
+                               space -= word_len;
+                       }
+                       if (space > 0) {
+                               fprintf(stderr, " ");
+                               space--;
                        }
-                       desc += strspn(desc, " \n\t");
+               } else {
+                       fprintf(stderr, "\n%*s", (int)indent, "");
+                       space = avail;
                }
+               str += strspn(str, " \n\t");
+       }
+}
+
+/**
+ * Print an entry's description, with a given indent.
+ *
+ * The indent is assumed to already be applied for the first line of the
+ * output by the caller.
+ *
+ * \param[in] entry   The entry to print the description for.
+ * \param[in] indent  The number of spaces to pad the left margin with.
+ */
+static void cli__print_description(const struct cli_table_entry *entry,
+               size_t indent)
+{
+       if (entry->d != NULL) {
+               cli__print_wrapping_string(entry->d, indent);
        }
 
        fprintf(stderr, "\n");
+
+       if (entry->t == CLI_ENUM) {
+               size_t max_len = 0;
+
+               for (const struct cli_str_val *e = entry->v.e.desc;
+                               e->str != NULL; e++) {
+                       size_t len = strlen(e->str);
+                       if (max_len < len) {
+                               max_len = len;
+                       }
+               }
+
+               fprintf(stderr, "\n");
+
+               for (const struct cli_str_val *e = entry->v.e.desc;
+                               e->str != NULL; e++) {
+                       fprintf(stderr, "        ");
+
+                       if (e->d == NULL || e->d[0] == '\0') {
+                               fprintf(stderr, "%s\n",
+                                               e->str);
+                       } else {
+                               fprintf(stderr, "%-*s - ",
+                                               (int)(max_len),
+                                               e->str);
+                               cli__print_wrapping_string(e->d,
+                                               8 + max_len + 3);
+                               fprintf(stderr, "\n");
+                       }
+               }
+       }
 }
 
 /* Documented in cli.h */
diff --git a/test/cli.h b/test/cli.h
index ec14d1e..d7493e4 100644
--- a/test/cli.h
+++ b/test/cli.h
@@ -34,13 +34,16 @@ enum cli_arg_type {
        CLI_STRING, /**< Has string value. */
 };
 
+/** Enum value descriptor. */
 struct cli_str_val {
-       const char *str;
-       int64_t val;
+       const char *str; /**< String for the enum value name. */
+       int64_t val;     /**< The value for this string. */
+       const char *d;   /**< Description of this value for help output. */
 };
 
+/** Enum data. */
 struct cli_enum {
-       const struct cli_str_val *desc;
+       const struct cli_str_val *desc; /**< Array describing enum values. */
        int64_t *e; /**< Location to store \ref CLI_ENUM value. */
 };
 
@@ -58,9 +61,9 @@ struct cli_table_entry {
                int64_t *i;     /**< Location to store \ref CLI_INT value. */
                uint64_t *u;    /**< Location to store \ref CLI_UINT value. */
                const char **s; /**< Location to store \ref CLI_STRING value. */
-               struct cli_enum e;
+               struct cli_enum e; /**< \ref CLI_ENUM value details. */
        } v; /**< Where to store type-specific values. */
-       const char *d; /**< Description. */
+       const char *d; /**< Description of this argument for help output. */
 };
 
 /**


commitdiff 
http://git.netsurf-browser.org/libnsgif.git/commit/?id=d63e9e479c63f8f0a1579ea58205862047297f1a
commit d63e9e479c63f8f0a1579ea58205862047297f1a
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    cli: Allow arguments that disable positional argument requirement

diff --git a/test/cli.c b/test/cli.c
index 031cd97..e1d1a8b 100644
--- a/test/cli.c
+++ b/test/cli.c
@@ -1,7 +1,7 @@
 /*
  * SPDX-License-Identifier: ISC
  *
- * Copyright (C) 2021 Michael Drake <[email protected]>
+ * Copyright (C) 2021-2022 Michael Drake <[email protected]>
  */
 
 /**
@@ -18,6 +18,14 @@
 #include "cli.h"
 
 /**
+ * CLI parsing context.
+ */
+struct cli_ctx {
+       const struct cli_table *cli; /**< Client CLI spec. */
+       bool no_pos; /**< Have an argument that negates min_positional. */
+};
+
+/**
  * Check whether a CLI argument type should have a numerical value.
  *
  * \param[in]  type  An argument type.
@@ -289,13 +297,13 @@ static bool cli__handle_arg_value(const struct 
cli_table_entry *entry,
 /**
  * Parse a flags argument.
  *
- * \param[in]  cli      Client command line interface specification.
+ * \param[in]  ctx      Command line interface parsing context.
  * \param[in]  argc     Number of command line arguments.
  * \param[in]  argv     String vector containing command line arguments.
  * \param[out] arg_pos  Current position in argv, updated on exit.
  * \return true on success, or false otherwise.
  */
-static bool cli__parse_short(const struct cli_table *cli,
+static bool cli__parse_short(struct cli_ctx *ctx,
                int argc, const char **argv, int *arg_pos)
 {
        const char *arg = argv[*arg_pos];
@@ -308,11 +316,15 @@ static bool cli__parse_short(const struct cli_table *cli,
        while (arg[pos] != '\0') {
                const struct cli_table_entry *entry;
 
-               entry = cli__lookup_short(cli, arg[pos]);
+               entry = cli__lookup_short(ctx->cli, arg[pos]);
                if (entry == NULL) {
                        return false;
                }
 
+               if (entry->no_pos) {
+                       ctx->no_pos = true;
+               }
+
                if (entry->t == CLI_BOOL) {
                        *entry->v.b = true;
                } else {
@@ -364,13 +376,13 @@ static const struct cli_table_entry *cli__lookup_long(
 /**
  * Parse a long argument.
  *
- * \param[in]  cli      Client command line interface specification.
+ * \param[in]  ctx      Command line interface parsing context.
  * \param[in]  argc     Number of command line arguments.
  * \param[in]  argv     String vector containing command line arguments.
  * \param[out] arg_pos  Current position in argv, updated on exit.
  * \return true on success, or false otherwise.
  */
-static bool cli__parse_long(const struct cli_table *cli,
+static bool cli__parse_long(struct cli_ctx *ctx,
                int argc, const char **argv, int *arg_pos)
 {
        const struct cli_table_entry *entry;
@@ -382,11 +394,15 @@ static bool cli__parse_long(const struct cli_table *cli,
                return false;
        }
 
-       entry = cli__lookup_long(cli, arg, &pos);
+       entry = cli__lookup_long(ctx->cli, arg, &pos);
        if (entry == NULL) {
                return false;
        }
 
+       if (entry->no_pos) {
+               ctx->no_pos = true;
+       }
+
        if (entry->t == CLI_BOOL) {
                if (arg[pos] != '\0') {
                        fprintf(stderr, "Unexpected value for argument '%s'\n",
@@ -567,6 +583,9 @@ static inline bool cli__is_negative(const char *arg)
 bool cli_parse(const struct cli_table *cli, int argc, const char **argv)
 {
        size_t pos_count = 0;
+       struct cli_ctx ctx = {
+               .cli = cli,
+       };
        enum {
                ARG_PROG_NAME,
                ARG_FIRST,
@@ -579,9 +598,9 @@ bool cli_parse(const struct cli_table *cli, int argc, const 
char **argv)
 
                if (arg[0] == '-') {
                        if (arg[1] == '-') {
-                               ret = cli__parse_long(cli, argc, argv, &i);
+                               ret = cli__parse_long(&ctx, argc, argv, &i);
                        } else {
-                               ret = cli__parse_short(cli, argc, argv, &i);
+                               ret = cli__parse_short(&ctx, argc, argv, &i);
                                if (ret != true) {
                                        if (cli__is_negative(argv[i])) {
                                                pos_inc = 1;
@@ -603,7 +622,7 @@ bool cli_parse(const struct cli_table *cli, int argc, const 
char **argv)
                pos_count += pos_inc;
        }
 
-       if (pos_count < cli->min_positional) {
+       if (ctx.no_pos == false && pos_count < cli->min_positional) {
                fprintf(stderr, "Insufficient positional arguments found.\n");
                return false;
        }
diff --git a/test/cli.h b/test/cli.h
index 91db086..ec14d1e 100644
--- a/test/cli.h
+++ b/test/cli.h
@@ -1,7 +1,7 @@
 /*
  * SPDX-License-Identifier: ISC
  *
- * Copyright (C) 2021 Michael Drake <[email protected]>
+ * Copyright (C) 2021-2022 Michael Drake <[email protected]>
  */
 
 /**
@@ -51,6 +51,7 @@ struct cli_table_entry {
        const char *l; /**< Long argument name. */
        const char  s; /**< Short flag name. (Non-positional arguments.) */
        bool p; /**< Whether the argument is a positional argument. */
+       bool no_pos; /**< When present, no positional arguments are required. */
        enum cli_arg_type t; /**< Argument type. */
        union {
                bool *b;        /**< Location to store \ref CLI_BOOL value. */


-----------------------------------------------------------------------

Summary of changes:
 test/cli.c   |  293 +++++++++++++++++++++++++++++++++++-----------------------
 test/cli.h   |   17 ++--
 test/nsgif.c |   25 ++++-
 3 files changed, 210 insertions(+), 125 deletions(-)

diff --git a/test/cli.c b/test/cli.c
index 031cd97..851750c 100644
--- a/test/cli.c
+++ b/test/cli.c
@@ -1,7 +1,7 @@
 /*
  * SPDX-License-Identifier: ISC
  *
- * Copyright (C) 2021 Michael Drake <[email protected]>
+ * Copyright (C) 2021-2022 Michael Drake <[email protected]>
  */
 
 /**
@@ -18,6 +18,15 @@
 #include "cli.h"
 
 /**
+ * CLI parsing context.
+ */
+struct cli_ctx {
+       const struct cli_table *cli; /**< Client CLI spec. */
+       size_t pos_count; /**< The number of positional arguments found. */
+       bool no_pos; /**< Have an argument that negates min_positional. */
+};
+
+/**
  * Check whether a CLI argument type should have a numerical value.
  *
  * \param[in]  type  An argument type.
@@ -112,6 +121,8 @@ static bool cli__parse_value_enum(
                }
        }
 
+       fprintf(stderr, "ERROR: Unknown enum value '%s'.\n", str);
+
        return false;
 }
 
@@ -286,16 +297,83 @@ static bool cli__handle_arg_value(const struct 
cli_table_entry *entry,
        return true;
 }
 
+static inline bool cli__is_negative(const char *arg)
+{
+       int64_t i;
+       size_t pos = 0;
+
+       return cli__parse_value_int(arg, &i, &pos)
+                       && pos == strlen(arg)
+                       && i < 0;
+}
+
+/**
+ * Parse a positional argument according to the given CLI spec entry.
+ *
+ * \param[in] ctx    Command line interface parsing context.
+ * \param[in] entry  Client command line interface argument specification.
+ * \param[in] arg    Argument to parse.
+ * \return true on success, or false otherwise.
+ */
+static bool cli__parse_positional_entry(struct cli_ctx *ctx,
+               const struct cli_table_entry *entry,
+               const char *arg)
+{
+       size_t pos = 0;
+       bool ret;
+
+       ret = cli__parse_value(entry, arg, &pos);
+       if (ret != true) {
+               return ret;
+       } else if (arg[pos] != '\0') {
+               fprintf(stderr, "Failed to parse value '%s' for arg '%s'\n",
+                               arg, entry->l);
+               return false;
+       }
+
+       ctx->pos_count++;
+       return true;
+}
+
+/**
+ * Parse a positional argument.
+ *
+ * \param[in] ctx    Command line interface parsing context.
+ * \param[in] arg    Argument to parse.
+ * \param[in] count  Number of positional arguments parsed already.
+ * \return true on success, or false otherwise.
+ */
+static bool cli__parse_positional(struct cli_ctx *ctx,
+               const char *arg)
+{
+       const struct cli_table *cli = ctx->cli;
+       size_t positional = 0;
+
+       for (size_t i = 0; i < cli->count; i++) {
+               if (cli__entry_is_positional(&cli->entries[i])) {
+                       if (positional == ctx->pos_count) {
+                               return cli__parse_positional_entry(ctx,
+                                               &cli->entries[i], arg);
+                       }
+
+                       positional++;
+               }
+       }
+
+       fprintf(stderr, "Unexpected positional argument: '%s'\n", arg);
+       return false;
+}
+
 /**
  * Parse a flags argument.
  *
- * \param[in]  cli      Client command line interface specification.
+ * \param[in]  ctx      Command line interface parsing context.
  * \param[in]  argc     Number of command line arguments.
  * \param[in]  argv     String vector containing command line arguments.
  * \param[out] arg_pos  Current position in argv, updated on exit.
  * \return true on success, or false otherwise.
  */
-static bool cli__parse_short(const struct cli_table *cli,
+static bool cli__parse_short(struct cli_ctx *ctx,
                int argc, const char **argv, int *arg_pos)
 {
        const char *arg = argv[*arg_pos];
@@ -308,11 +386,18 @@ static bool cli__parse_short(const struct cli_table *cli,
        while (arg[pos] != '\0') {
                const struct cli_table_entry *entry;
 
-               entry = cli__lookup_short(cli, arg[pos]);
+               entry = cli__lookup_short(ctx->cli, arg[pos]);
                if (entry == NULL) {
+                       if (cli__is_negative(argv[pos])) {
+                               return cli__parse_positional(ctx, argv[pos]);
+                       }
                        return false;
                }
 
+               if (entry->no_pos) {
+                       ctx->no_pos = true;
+               }
+
                if (entry->t == CLI_BOOL) {
                        *entry->v.b = true;
                } else {
@@ -364,13 +449,13 @@ static const struct cli_table_entry *cli__lookup_long(
 /**
  * Parse a long argument.
  *
- * \param[in]  cli      Client command line interface specification.
+ * \param[in]  ctx      Command line interface parsing context.
  * \param[in]  argc     Number of command line arguments.
  * \param[in]  argv     String vector containing command line arguments.
  * \param[out] arg_pos  Current position in argv, updated on exit.
  * \return true on success, or false otherwise.
  */
-static bool cli__parse_long(const struct cli_table *cli,
+static bool cli__parse_long(struct cli_ctx *ctx,
                int argc, const char **argv, int *arg_pos)
 {
        const struct cli_table_entry *entry;
@@ -382,11 +467,15 @@ static bool cli__parse_long(const struct cli_table *cli,
                return false;
        }
 
-       entry = cli__lookup_long(cli, arg, &pos);
+       entry = cli__lookup_long(ctx->cli, arg, &pos);
        if (entry == NULL) {
                return false;
        }
 
+       if (entry->no_pos) {
+               ctx->no_pos = true;
+       }
+
        if (entry->t == CLI_BOOL) {
                if (arg[pos] != '\0') {
                        fprintf(stderr, "Unexpected value for argument '%s'\n",
@@ -408,60 +497,6 @@ static bool cli__parse_long(const struct cli_table *cli,
 }
 
 /**
- * Parse a positional argument according to the given CLI spec entry.
- *
- * \param[in] entry  Client command line interface argument specification.
- * \param[in] arg    Argument to parse.
- * \return true on success, or false otherwise.
- */
-static bool cli__parse_positional_entry(
-               const struct cli_table_entry *entry,
-               const char *arg)
-{
-       size_t pos = 0;
-       bool ret;
-
-       ret = cli__parse_value(entry, arg, &pos);
-       if (ret != true) {
-               return ret;
-       } else if (arg[pos] != '\0') {
-               fprintf(stderr, "Failed to parse value '%s' for arg '%s'\n",
-                               arg, entry->l);
-               return false;
-       }
-
-       return true;
-}
-
-/**
- * Parse a positional argument.
- *
- * \param[in] cli    Client command line interface specification.
- * \param[in] arg    Argument to parse.
- * \param[in] count  Number of positional arguments parsed already.
- * \return true on success, or false otherwise.
- */
-static bool cli__parse_positional(const struct cli_table *cli,
-               const char *arg, size_t count)
-{
-       size_t positional = 0;
-
-       for (size_t i = 0; i < cli->count; i++) {
-               if (cli__entry_is_positional(&cli->entries[i])) {
-                       if (positional == count) {
-                               return cli__parse_positional_entry(
-                                               &cli->entries[i], arg);
-                       }
-
-                       positional++;
-               }
-       }
-
-       fprintf(stderr, "Unexpected positional argument: '%s'\n", arg);
-       return false;
-}
-
-/**
  * Get the string to indicate type of value expected for an argument.
  *
  * \param[in] type  The argument type.
@@ -553,20 +588,12 @@ static void cli__count(const struct cli_table *cli,
        }
 }
 
-static inline bool cli__is_negative(const char *arg)
-{
-       int64_t i;
-       size_t pos = 0;
-
-       return cli__parse_value_int(arg, &i, &pos)
-                       && pos == strlen(arg)
-                       && i < 0;
-}
-
 /* Documented in cli.h */
 bool cli_parse(const struct cli_table *cli, int argc, const char **argv)
 {
-       size_t pos_count = 0;
+       struct cli_ctx ctx = {
+               .cli = cli,
+       };
        enum {
                ARG_PROG_NAME,
                ARG_FIRST,
@@ -574,36 +601,24 @@ bool cli_parse(const struct cli_table *cli, int argc, 
const char **argv)
 
        for (int i = ARG_FIRST; i < argc; i++) {
                const char *arg = argv[i];
-               size_t pos_inc = 0;
                bool ret;
 
                if (arg[0] == '-') {
                        if (arg[1] == '-') {
-                               ret = cli__parse_long(cli, argc, argv, &i);
+                               ret = cli__parse_long(&ctx, argc, argv, &i);
                        } else {
-                               ret = cli__parse_short(cli, argc, argv, &i);
-                               if (ret != true) {
-                                       if (cli__is_negative(argv[i])) {
-                                               pos_inc = 1;
-                                               ret = cli__parse_positional(
-                                                               cli, argv[i],
-                                                               pos_count);
-                                       }
-                               }
+                               ret = cli__parse_short(&ctx, argc, argv, &i);
                        }
                } else {
-                       pos_inc = 1;
-                       ret = cli__parse_positional(cli, argv[i], pos_count);
+                       ret = cli__parse_positional(&ctx, argv[i]);
                }
 
                if (ret != true) {
                        return ret;
                }
-
-               pos_count += pos_inc;
        }
 
-       if (pos_count < cli->min_positional) {
+       if (ctx.no_pos == false && ctx.pos_count < cli->min_positional) {
                fprintf(stderr, "Insufficient positional arguments found.\n");
                return false;
        }
@@ -622,46 +637,90 @@ static size_t cli__terminal_width(void)
 }
 
 /**
- * Print an entry's description, with a given indent.
+ * Print a wrapped string, with a given indent.
  *
  * The indent is assumed to already be applied for the first line of the
  * output by the caller.
- * 
- * \param[in] entry   The entry to print the description for.
+ *
+ * \param[in] str     The string to print.
  * \param[in] indent  The number of spaces to pad the left margin with.
  */
-static void cli__print_description(const struct cli_table_entry *entry,
-               size_t indent)
+static void cli__print_wrapping_string(const char *str, size_t indent)
 {
        size_t terminal_width = cli__terminal_width();
        size_t avail = (indent > terminal_width) ? 0 : terminal_width - indent;
        size_t space = avail;
-       const char *desc = entry->d;
-
-       if (desc != NULL) {
-               while (*desc != '\0') {
-                       size_t word_len = strcspn(desc, " \n\t");
-                       if (word_len <= space || space == avail) {
-                               fprintf(stderr, "%*.*s",
-                                               (int)word_len,
-                                               (int)word_len, desc);
-                               desc += word_len;
-                               if (word_len <= space) {
-                                       space -= word_len;
-                               }
-                               if (space > 0) {
-                                       fprintf(stderr, " ");
-                                       space--;
-                               }
-                       } else {
-                               fprintf(stderr, "\n%*s", (int)indent, "");
-                               space = avail;
+
+       while (*str != '\0') {
+               size_t word_len = strcspn(str, " \n\t");
+               if (word_len <= space || space == avail) {
+                       fprintf(stderr, "%*.*s",
+                                       (int)word_len,
+                                       (int)word_len, str);
+                       str += word_len;
+                       if (word_len <= space) {
+                               space -= word_len;
                        }
-                       desc += strspn(desc, " \n\t");
+                       if (space > 0) {
+                               fprintf(stderr, " ");
+                               space--;
+                       }
+               } else {
+                       fprintf(stderr, "\n%*s", (int)indent, "");
+                       space = avail;
                }
+               str += strspn(str, " \n\t");
+       }
+}
+
+/**
+ * Print an entry's description, with a given indent.
+ *
+ * The indent is assumed to already be applied for the first line of the
+ * output by the caller.
+ *
+ * \param[in] entry   The entry to print the description for.
+ * \param[in] indent  The number of spaces to pad the left margin with.
+ */
+static void cli__print_description(const struct cli_table_entry *entry,
+               size_t indent)
+{
+       if (entry->d != NULL) {
+               cli__print_wrapping_string(entry->d, indent);
        }
 
        fprintf(stderr, "\n");
+
+       if (entry->t == CLI_ENUM) {
+               size_t max_len = 0;
+
+               for (const struct cli_str_val *e = entry->v.e.desc;
+                               e->str != NULL; e++) {
+                       size_t len = strlen(e->str);
+                       if (max_len < len) {
+                               max_len = len;
+                       }
+               }
+
+               fprintf(stderr, "\n");
+
+               for (const struct cli_str_val *e = entry->v.e.desc;
+                               e->str != NULL; e++) {
+                       fprintf(stderr, "        ");
+
+                       if (e->d == NULL || e->d[0] == '\0') {
+                               fprintf(stderr, "%s\n",
+                                               e->str);
+                       } else {
+                               fprintf(stderr, "%-*s - ",
+                                               (int)(max_len),
+                                               e->str);
+                               cli__print_wrapping_string(e->d,
+                                               8 + max_len + 3);
+                               fprintf(stderr, "\n");
+                       }
+               }
+       }
 }
 
 /* Documented in cli.h */
@@ -679,6 +738,12 @@ void cli_help(const struct cli_table *cli, const char 
*prog_name)
 
        cli__count(cli, &count, &pcount, &max_len, &pmax_len, &phas_desc);
 
+       if (cli->d != NULL) {
+               fprintf(stderr, "\n");
+               cli__print_wrapping_string(cli->d, 0);
+               fprintf(stderr, "\n");
+       }
+
        fprintf(stderr, "\nUsage: %s", prog_name);
 
        if (pcount > 0) {
diff --git a/test/cli.h b/test/cli.h
index 91db086..ffcd272 100644
--- a/test/cli.h
+++ b/test/cli.h
@@ -1,7 +1,7 @@
 /*
  * SPDX-License-Identifier: ISC
  *
- * Copyright (C) 2021 Michael Drake <[email protected]>
+ * Copyright (C) 2021-2022 Michael Drake <[email protected]>
  */
 
 /**
@@ -34,13 +34,16 @@ enum cli_arg_type {
        CLI_STRING, /**< Has string value. */
 };
 
+/** Enum value descriptor. */
 struct cli_str_val {
-       const char *str;
-       int64_t val;
+       const char *str; /**< String for the enum value name. */
+       int64_t val;     /**< The value for this string. */
+       const char *d;   /**< Description of this value for help output. */
 };
 
+/** Enum data. */
 struct cli_enum {
-       const struct cli_str_val *desc;
+       const struct cli_str_val *desc; /**< Array describing enum values. */
        int64_t *e; /**< Location to store \ref CLI_ENUM value. */
 };
 
@@ -51,15 +54,16 @@ struct cli_table_entry {
        const char *l; /**< Long argument name. */
        const char  s; /**< Short flag name. (Non-positional arguments.) */
        bool p; /**< Whether the argument is a positional argument. */
+       bool no_pos; /**< When present, no positional arguments are required. */
        enum cli_arg_type t; /**< Argument type. */
        union {
                bool *b;        /**< Location to store \ref CLI_BOOL value. */
                int64_t *i;     /**< Location to store \ref CLI_INT value. */
                uint64_t *u;    /**< Location to store \ref CLI_UINT value. */
                const char **s; /**< Location to store \ref CLI_STRING value. */
-               struct cli_enum e;
+               struct cli_enum e; /**< \ref CLI_ENUM value details. */
        } v; /**< Where to store type-specific values. */
-       const char *d; /**< Description. */
+       const char *d; /**< Description of this argument for help output. */
 };
 
 /**
@@ -69,6 +73,7 @@ struct cli_table {
        const struct cli_table_entry *entries;
        size_t count;
        size_t min_positional;
+       const char *d; /**< Description of this application for help output. */
 };
 
 /**
diff --git a/test/nsgif.c b/test/nsgif.c
index 7d61504..f873bab 100644
--- a/test/nsgif.c
+++ b/test/nsgif.c
@@ -29,15 +29,17 @@ static struct nsgif_options {
        uint64_t loops;
        bool palette;
        bool info;
+       bool help;
 } nsgif_options;
 
 static const struct cli_table_entry cli_entries[] = {
        {
-               .s = 'm',
-               .l = "ppm",
-               .t = CLI_STRING,
-               .v.s = &nsgif_options.ppm,
-               .d = "Convert frames to PPM image at given path."
+               .s = 'h',
+               .l = "help",
+               .t = CLI_BOOL,
+               .no_pos = true,
+               .v.b = &nsgif_options.help,
+               .d = "Print this text.",
        },
        {
                .s = 'i',
@@ -55,6 +57,13 @@ static const struct cli_table_entry cli_entries[] = {
                     "The default is 1."
        },
        {
+               .s = 'm',
+               .l = "ppm",
+               .t = CLI_STRING,
+               .v.s = &nsgif_options.ppm,
+               .d = "Convert frames to PPM image at given path."
+       },
+       {
                .s = 'p',
                .l = "palette",
                .t = CLI_BOOL,
@@ -74,6 +83,7 @@ const struct cli_table cli = {
        .entries = cli_entries,
        .count = (sizeof(cli_entries))/(sizeof(*cli_entries)),
        .min_positional = 1,
+       .d = "NSGIF - A utility for inspecting and decoding GIFs with libnsgif",
 };
 
 static void *bitmap_create(int width, int height)
@@ -357,6 +367,11 @@ int main(int argc, char *argv[])
                return EXIT_FAILURE;
        }
 
+       if (nsgif_options.help) {
+               cli_help(&cli, argv[0]);
+               return EXIT_SUCCESS;
+       }
+
        if (nsgif_options.ppm != NULL) {
                ppm = fopen(nsgif_options.ppm, "w+");
                if (ppm == NULL) {


-- 
NetSurf GIF Decoder
_______________________________________________
netsurf-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to