]> git.draconx.ca Git - dxcommon.git/blob - src/help.c
Add common option formatting routines.
[dxcommon.git] / src / help.c
1 /*
2  * Copyright © 2021 Nick Bowler
3  *
4  * Helper functions for formatting --help program output.
5  *
6  * In order to support localized output, this depends on the Gnulib gettext-h
7  * and mbswidth modules.  However, if ENABLE_NLS is not defined (or defined to
8  * 0) then these modules are not required.
9  *
10  * License WTFPL2: Do What The Fuck You Want To Public License, version 2.
11  * This is free software: you are free to do what the fuck you want to.
12  * There is NO WARRANTY, to the extent permitted by law.
13  */
14
15 #if HAVE_CONFIG_H
16 #       include <config.h>
17 #endif
18 #include <stdio.h>
19 #include <string.h>
20 #include <assert.h>
21 #include <limits.h>
22 #include <getopt.h>
23
24 #include "help.h"
25
26 #ifndef ENABLE_NLS
27 #       define ENABLE_NLS 0
28 #endif
29
30 #if ENABLE_NLS
31 #       include <gettext.h>
32 #       include <mbswidth.h>
33 #else
34 #       define gettext(s) (s)
35 #       define pgettext_expr(c, s) (s)
36 #       define mbsnwidth(a, b, c) (assert(0), 0)
37 #endif
38
39 #define _(s) gettext(s)
40
41 /* Returns a single numeric value depending on the type of option:
42  *
43  *   6 - if the option has a short option character and an optional argument
44  *   5 - if the option has a short option character and a mandatory argument
45  *   4 - if the option has a short option charater and no argument
46  *   3 - N/A
47  *   2 - if the option has no short option character and an optional argument
48  *   1 - if the option has no short option character and a mandatory argument
49  *   0 - if the option has no short option character and no argument
50  */
51 static int option_type(const struct option *opt)
52 {
53         return ((opt->val <= CHAR_MAX) << 2) | (opt->has_arg & 3);
54 }
55
56 enum {
57         OPT_SHORT_WITH_OPTIONAL_ARG  = 6,
58         OPT_SHORT_WITH_MANDATORY_ARG = 5,
59         OPT_SHORT_WITHOUT_ARG        = 4,
60         OPT_LONG_WITH_OPTIONAL_ARG   = 2,
61         OPT_LONG_WITH_MANDATORY_ARG  = 1,
62         OPT_LONG_WITHOUT_ARG         = 0,
63 };
64
65 int help_print_optstring(const struct option *opt, const char *argname, int l)
66 {
67         char optstring[100];
68         int w;
69
70         if (!ENABLE_NLS)
71                 goto no_translate;
72
73         switch (option_type(opt)) {
74         case OPT_SHORT_WITH_OPTIONAL_ARG:
75                 w = snprintf(optstring, sizeof optstring,
76                              _("  -%c, --%s[=%s]"), opt->val, opt->name,
77                              pgettext_expr(opt->name, argname));
78                 break;
79         case OPT_LONG_WITH_OPTIONAL_ARG:
80                 w = snprintf(optstring, sizeof optstring,
81                              _("  --%s[=%s]"), opt->name,
82                              pgettext_expr(opt->name, argname));
83                 break;
84         case OPT_SHORT_WITH_MANDATORY_ARG:
85                 w = snprintf(optstring, sizeof optstring,
86                              _("  -%c, --%s=%s"), opt->val, opt->name,
87                              pgettext_expr(opt->name, argname));
88                 break;
89         case OPT_LONG_WITH_MANDATORY_ARG:
90                 w = snprintf(optstring, sizeof optstring,
91                              _("  --%s=%s"), opt->name,
92                              pgettext_expr(opt->name, argname));
93                 break;
94         case OPT_SHORT_WITHOUT_ARG:
95                 w = snprintf(optstring, sizeof optstring,
96                              _("  -%c, --%s"), opt->val, opt->name);
97                 break;
98         case OPT_LONG_WITHOUT_ARG:
99                 w = snprintf(optstring, sizeof optstring,
100                              _("  --%s"), opt->name);
101                 break;
102         default:
103                 assert(0);
104         }
105
106         if (w < 0)
107                 goto no_translate;
108
109         w = mbsnwidth(optstring, w, 0);
110         printf("%s", optstring);
111         goto out;
112
113 no_translate:
114         switch (option_type(opt)) {
115         case OPT_SHORT_WITH_OPTIONAL_ARG:
116                 w = printf("  -%c, --%s[=%s]", opt->val, opt->name, argname);
117                 break;
118         case OPT_LONG_WITH_OPTIONAL_ARG:
119                 w = printf("  --%s[=%s]", opt->name, argname);
120                 break;
121         case OPT_SHORT_WITH_MANDATORY_ARG:
122                 w = printf("  -%c, --%s=%s", opt->val, opt->name, argname);
123                 break;
124         case OPT_LONG_WITH_MANDATORY_ARG:
125                 w = printf("  --%s=%s", opt->name, argname);
126                 break;
127         case OPT_SHORT_WITHOUT_ARG:
128                 w = printf("  -%c, --%s", opt->val, opt->name);
129                 break;
130         case OPT_LONG_WITHOUT_ARG:
131                 w = printf("  --%s", opt->name);
132                 break;
133         default:
134                 assert(0);
135         }
136 out:
137         if (w < 0 || w > l) {
138                 putchar('\n');
139                 return 0;
140         }
141
142         return w;
143 }
144
145 void help_print_desc(const struct option *opt, const char *s, int i, int w)
146 {
147         for (s = pgettext_expr(opt->name, s); *s; w = 0) {
148                 const char *nl = strchr(s, '\n');
149                 int n = (nl ? nl-s : -1);
150
151                 printf("%*s%.*s\n", i-w, "", n, s);
152                 if (!nl)
153                         break;
154
155                 s = nl+1;
156         }
157 }