bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: argmatch: accept perfect matches in documented arglists


From: Akim Demaille
Subject: Re: argmatch: accept perfect matches in documented arglists
Date: Sun, 19 May 2019 17:02:24 +0200

Hey Bruno,

> Le 19 mai 2019 à 14:14, Akim Demaille <address@hidden> a écrit :
> 
>> Le 19 mai 2019 à 14:01, Bruno Haible <address@hidden> a écrit :
> 
>> So I would prefer a macro with one argument, the type of the first field.
> 
> That sounds good.

Well, it is still quite messy.  I had hope to keep the implementation in 
argmatch.c, but there are too many problems with tracking the offset of the 
different members of the structure.  Eventually, I went for something much 
simpler, much safer, and easier to maintain.

What do you think of something like this?

commit 4cca4c589eaf17756a779ab91776bc68315edf4e
Author: Akim Demaille <address@hidden>
Date:   Tue Apr 30 08:01:14 2019 +0200

    WIP: argmatch: provide support for documentation

diff --git a/lib/argmatch.h b/lib/argmatch.h
index 50de57f29..5607def49 100644
--- a/lib/argmatch.h
+++ b/lib/argmatch.h
@@ -23,6 +23,7 @@
 # define ARGMATCH_H_ 1
 
 # include <stddef.h>
+# include <stdio.h>
 
 # include "verify.h"
 
@@ -104,6 +105,89 @@ char const *argmatch_to_argument (void const *value,
   argmatch_to_argument (Value, Arglist,                                 \
                         (void const *) (Vallist), sizeof *(Vallist))
 
+/* A type that groups together all the feature of an argmatch group.  */
+# define ARGMATCH_DEFINE_GROUP(Name, Type)                              \
+  typedef Type argmatch_##Name##_type;                                  \
+                                                                        \
+  typedef enum                                                          \
+  {                                                                     \
+    size = sizeof (argmatch_##Name##_type)                              \
+  } argmatch_##Name##_size;                                             \
+                                                                        \
+  typedef struct                                                        \
+  {                                                                     \
+    /* Value that we document.  */                                      \
+    const argmatch_##Name##_type val;                                   \
+    /* The documentation of each documented value.                      \
+       This dictates the order in which values are documented.          \
+       docs[i] documents doc_vals[i].  */                               \
+    const char const *doc;                                              \
+  } argmatch_##Name##_doc;                                              \
+                                                                        \
+  typedef struct                                                        \
+  {                                                                     \
+    /* Arguments denoting a value.  */                                  \
+    const char const *arg;                                              \
+    /* vals[i] is the value for args[i].  */                            \
+    const argmatch_##Name##_type val;                                   \
+  } argmatch_##Name##_arg;                                              \
+                                                                        \
+  typedef struct                                                        \
+  {                                                                     \
+    /* Printed before the usage message.  */                            \
+    const char *doc_pre;                                                \
+    /* Printed after the usage message.  */                             \
+    const char *doc_post;                                               \
+                                                                        \
+    const argmatch_##Name##_doc* docs;                                  \
+    const argmatch_##Name##_arg* args;                                  \
+  } argmatch_##Name##_group_type;                                       \
+                                                                        \
+  static inline void                                                    \
+  argmatch_usage (const argmatch_##Name##_group_type *g, FILE *out)     \
+  {                                                                     \
+    /* Width of the screen.  Help2man does not seem to support          \
+       arguments on several lines, so in that case pretend a very       \
+       large width. */                                                  \
+    const int screen_width = getenv ("HELP2MAN") ? INT_MAX : 80;        \
+    if (g->doc_pre)                                                     \
+      fprintf (out, "%s\n", _(g->doc_pre));                             \
+    for (int i = 0; g->docs[i].doc; ++i)                                \
+      {                                                                 \
+        int col = 0;                                                    \
+        bool first = true;                                              \
+        for (int j = 0; g->args[j].arg; ++j)                            \
+          if (! memcmp (&g->docs[i].val, &g->args[j].val, size))        \
+            {                                                           \
+              if (!first                                                \
+                  && screen_width < col + 2 + strlen (g->args[j].arg))  \
+                {                                                       \
+                  fprintf (out, ",\n");                                 \
+                  col = 0;                                              \
+                  first = true;                                         \
+                }                                                       \
+              if (first)                                                \
+                {                                                       \
+                  col += fprintf (out, " ");                            \
+                  first = false;                                        \
+                }                                                       \
+              else                                                      \
+                col += fprintf (out, ",");                              \
+              col += fprintf (out,  " %s", g->args[j].arg);             \
+            }                                                           \
+        /* The doc.  Must be on column 20 separated by at least two     \
+           spaces. */                                                   \
+        if (20 < col + 2)                                               \
+          {                                                             \
+            fprintf (out, "\n");                                        \
+            col = 0;                                                    \
+          }                                                             \
+        fprintf (out, "%*s%s\n", 20 - col, "", _(g->docs[i].doc));      \
+      }                                                                 \
+    if (g->doc_post)                                                    \
+      fprintf (out, "%s\n", _(g->doc_post));                            \
+  }
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/tests/test-argmatch.c b/tests/test-argmatch.c
index 9335adf55..de28dd1e2 100644
--- a/tests/test-argmatch.c
+++ b/tests/test-argmatch.c
@@ -21,10 +21,16 @@
 
 #include "argmatch.h"
 
+#include <gettext.h>
+#include <stdbool.h>
 #include <stdlib.h>
+#include <string.h> /* memcmp */
 
 #include "macros.h"
 
+#define _(Msgid)  gettext (Msgid)
+#define N_(Msgid) (Msgid)
+
 /* Some packages define ARGMATCH_DIE and ARGMATCH_DIE_DECL in <config.h>, and
    thus must link with a definition of that function.  Provide it here.  */
 #ifdef ARGMATCH_DIE_DECL
@@ -59,6 +65,45 @@ static const enum backup_type backup_vals[] =
   numbered_backups, numbered_backups, numbered_backups
 };
 
+ARGMATCH_DEFINE_GROUP(backup, enum backup_type);
+
+static const argmatch_backup_doc argmatch_backup_docs[] =
+{
+  { no_backups,                N_("never make backups (even if --backup is 
given)") },
+  { simple_backups,            N_("make numbered backups") },
+  { numbered_existing_backups, N_("numbered if numbered backups exist, simple 
otherwise") },
+  { numbered_backups,          N_("always make simple backups") },
+  { no_backups, NULL }
+};
+
+static const argmatch_backup_arg argmatch_backup_args[] =
+{
+  { "no",                no_backups },
+  { "none",              no_backups },
+  { "off",               no_backups },
+  { "simple",            simple_backups },
+  { "never",             simple_backups },
+  { "single",            simple_backups },
+  { "existing",          numbered_existing_backups },
+  { "nil",               numbered_existing_backups },
+  { "numbered-existing", numbered_existing_backups },
+  { "numbered",          numbered_backups },
+  { "t",                 numbered_backups },
+  { "newstyle",          numbered_backups },
+  { NULL, no_backups }
+};
+
+argmatch_backup_group_type argmatch_backup_group =
+{
+  N_("\
+The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
+The version control method may be selected via the --backup option or 
through\n\
+the VERSION_CONTROL environment variable.  Here are the values:\n"),
+  NULL,
+  argmatch_backup_docs,
+  argmatch_backup_args
+};
+
 int
 main (int argc, char *argv[])
 {
@@ -90,9 +135,11 @@ main (int argc, char *argv[])
   /* Ambiguous abbreviated.  */
   ASSERT (ARGMATCH ("ne", backup_args, backup_vals) == -2);
 
-  /* Ambiguous abbreviated, but same value.  */
+  /* Ambiguous abbreviated, but same value ("single" and "simple").  */
   ASSERT (ARGMATCH ("si", backup_args, backup_vals) == 3);
   ASSERT (ARGMATCH ("s", backup_args, backup_vals) == 3);
 
+  argmatch_usage (&argmatch_backup_group, stdout);
+
   return 0;
 }




When run, it gives:

> $ /tmp/gnutest/gltests/test-argmatch
> The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.
> The version control method may be selected via the --backup option or through
> the VERSION_CONTROL environment variable.  Here are the values:
> 
>   no, none, off     never make backups (even if --backup is given)
>   simple, never, single
>                     make numbered backups
>   existing, nil, numbered-existing
>                     numbered if numbered backups exist, simple otherwise
>   numbered, t, newstyle
>                     always make simple backups

which is what help2man expects.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]