add changelog
[platform/upstream/gdbm.git] / src / parseopt.c
1 /* This file is part of GDBM, the GNU data base manager.
2    Copyright (C) 2011, 2013 Free Software Foundation, Inc.
3
4    GDBM is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8
9    GDBM is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with GDBM. If not, see <http://www.gnu.org/licenses/>.   */
16
17 # include "autoconf.h"
18 # include "gdbm.h"
19 # include "gdbmapp.h"
20 # include "gdbmdefs.h"
21 # include <stdio.h>
22 # include <stdarg.h>
23 # include <errno.h>
24 # include <string.h>
25 # include <ctype.h>
26 # ifdef HAVE_GETOPT_H
27 #  include <getopt.h>
28 # endif
29
30 static int argc;
31 static char **argv;
32
33 static struct gdbm_option *option_tab;
34 static size_t option_count;
35 static size_t option_max;
36 static char *short_options;
37 static size_t short_option_count;
38 static size_t short_option_max;
39 #ifdef HAVE_GETOPT_LONG
40 static struct option *long_options;
41 static size_t long_option_count;
42 static size_t long_option_max;
43 #endif
44
45 #define OPT_USAGE -2
46
47 struct gdbm_option parseopt_default_options[] = {
48   { 0, NULL, NULL, "" },
49   { 'h', "help", NULL, N_("give this help list") },
50   { 'V', "version", NULL, N_("print program version") },
51   { OPT_USAGE, "usage", NULL, N_("give a short usage message") },
52   { 0 }
53 };
54
55 #define OPT_END(opt) \
56   ((opt)->opt_short == 0 && (opt)->opt_long == 0 && (opt)->opt_descr == NULL)
57 #define IS_OPTION(opt) \
58   ((opt)->opt_short || (opt)->opt_long)
59 #define IS_GROUP_HEADER(opt)                    \
60   (!IS_OPTION(opt) && (opt)->opt_descr)
61 #define IS_VALID_SHORT_OPTION(opt) \
62   ((opt)->opt_short > 0 && (opt)->opt_short < 127 && \
63    isalnum ((opt)->opt_short))
64 #define IS_VALID_LONG_OPTION(opt) \
65   ((opt)->opt_long != NULL)
66   
67 \f
68 static int
69 optcmp (const void *a, const void *b)
70 {
71   struct gdbm_option const *ap = (struct gdbm_option const *)a;
72   struct gdbm_option const *bp = (struct gdbm_option const *)b;
73
74   while (ap->opt_flags & PARSEOPT_ALIAS)
75     ap--;
76   while (bp->opt_flags & PARSEOPT_ALIAS)
77     bp--;
78   
79   if (IS_VALID_SHORT_OPTION(ap) && IS_VALID_SHORT_OPTION(bp))
80     return ap->opt_short - bp->opt_short;
81   if (IS_VALID_LONG_OPTION(ap) && IS_VALID_LONG_OPTION(bp))
82     return strcmp (ap->opt_long, bp->opt_long);
83   if (IS_VALID_LONG_OPTION(ap))
84     return 1;
85   return -1;
86 }
87
88 static void
89 sort_options (int start, int count)
90 {
91   qsort (option_tab + start, count, sizeof (option_tab[0]), optcmp);
92 }
93
94 static size_t
95 sort_group (size_t start)
96 {
97   size_t i;
98   
99   for (i = start; i < option_count && !IS_GROUP_HEADER (&option_tab[i]); i++)
100     ;
101   sort_options (start, i - start);
102   return i + 1;
103 }
104
105 static void
106 sort_all_options (void)
107 {
108   size_t start;
109
110   /* Ensure sane start of options.  This is necessary because optcmp backs up
111      until it finds an element with cleared PARSEOPT_ALIAS flag bit. */
112   option_tab[0].opt_flags &= PARSEOPT_ALIAS;
113   for (start = 0; start < option_count; )
114     {
115       if (IS_GROUP_HEADER (&option_tab[start]))
116         start = sort_group (start + 1);
117       else 
118         start = sort_group (start);
119     }
120 }
121 \f
122 static void
123 add_options (struct gdbm_option *options)
124 {
125   size_t optcnt = 0;
126   size_t argcnt = 0;
127   size_t count = 0;
128   struct gdbm_option *opt;
129   
130   for (opt = options; !OPT_END(opt); opt++)
131     {
132       count++;
133       if (IS_OPTION(opt))
134         {
135           optcnt++;
136           if (opt->opt_arg)
137             argcnt++;
138         }
139     }
140
141   if (option_count + count + 1 > option_max)
142     {
143       option_max = option_count + count + 1;
144       option_tab = erealloc (option_tab,
145                           sizeof (option_tab[0]) * option_max);
146     }
147   
148 #ifdef HAVE_GETOPT_LONG
149   if (long_option_count + optcnt + 1 > long_option_max)
150     {
151       long_option_max = long_option_count + optcnt + 1;
152       long_options = erealloc (long_options,
153                                sizeof (long_options[0]) * long_option_max);
154     }
155 #endif
156   if (short_option_count + optcnt + argcnt + 1 > short_option_max)
157     {
158       short_option_max = short_option_count + optcnt + argcnt + 1;
159       short_options = erealloc (short_options,
160                                 sizeof (short_options[0]) * short_option_max);
161     }
162
163   for (opt = options; !OPT_END(opt); opt++)
164     {
165       option_tab[option_count++] = *opt;
166       if (!IS_OPTION (opt))
167         continue;
168       if (IS_VALID_SHORT_OPTION (opt))
169         {
170           short_options[short_option_count++] = opt->opt_short;
171           if (opt->opt_arg)
172             short_options[short_option_count++] = ':';
173         }
174 #ifdef HAVE_GETOPT_LONG
175       if (IS_VALID_LONG_OPTION (opt))
176         {
177           long_options[long_option_count].name = opt->opt_long;
178           long_options[long_option_count].has_arg = opt->opt_arg != NULL;
179           long_options[long_option_count].flag = NULL;
180           long_options[long_option_count].val = opt->opt_short;
181           long_option_count++;
182         }
183 #endif
184     }
185   short_options[short_option_count] = 0;
186 #ifdef HAVE_GETOPT_LONG
187   memset (&long_options[long_option_count], 0,
188           sizeof long_options[long_option_count]);
189 #endif
190 }
191
192 int
193 parseopt_first (int pc, char **pv, struct gdbm_option *opts)
194 {
195   free (option_tab);
196   free (short_options);
197   short_option_count = short_option_max = 0;
198 #ifdef HAVE_GETOPT_LONG
199   free (long_options);
200   long_option_count = long_option_max = 0;
201 #endif
202   add_options (opts);
203   add_options (parseopt_default_options);
204   opterr = 0;
205   argc = pc;
206   argv = pv;
207   return parseopt_next ();
208 }
209 \f
210 #define LMARGIN 2
211 #define DESCRCOLUMN 30
212 #define RMARGIN 79
213 #define GROUPCOLUMN 2
214 #define USAGECOLUMN 13
215
216 static void
217 indent (size_t start, size_t col)
218 {
219   for (; start < col; start++)
220     putchar (' ');
221 }
222
223 static void
224 print_option_descr (const char *descr, size_t lmargin, size_t rmargin)
225 {
226   while (*descr)
227     {
228       size_t s = 0;
229       size_t i;
230       size_t width = rmargin - lmargin;
231       
232       for (i = 0; ; i++)
233         {
234           if (descr[i] == 0 || descr[i] == ' ' || descr[i] == '\t')
235             {
236               if (i > width)
237                 break;
238               s = i;
239               if (descr[i] == 0)
240                 break;
241             }
242         }
243       printf ("%*.*s\n", s, s, descr);
244       descr += s;
245       if (*descr)
246         {
247           indent (0, lmargin);
248           descr++;
249         }
250     }
251 }
252 \f
253 char *parseopt_program_name;
254 char *parseopt_program_doc;
255 char *parseopt_program_args;
256 const char *program_bug_address = "<" PACKAGE_BUGREPORT ">";
257 void (*parseopt_help_hook) (FILE *stream);
258 \f
259 static int argsused;
260
261 size_t
262 print_option (size_t num)
263 {
264   struct gdbm_option *opt = option_tab + num;
265   size_t next, i;
266   int delim;
267   int w;
268   
269   if (IS_GROUP_HEADER (opt))
270     {
271       if (num)
272         putchar ('\n');
273       indent (0, GROUPCOLUMN);
274       print_option_descr (gettext (opt->opt_descr),
275                           GROUPCOLUMN, RMARGIN);
276       putchar ('\n');
277       return num + 1;
278     }
279
280   /* count aliases */
281   for (next = num + 1;
282        next < option_count && option_tab[next].opt_flags & PARSEOPT_ALIAS;
283        next++);
284
285   if (opt->opt_flags & PARSEOPT_HIDDEN)
286     return next;
287
288   w = 0;
289   for (i = num; i < next; i++)
290     {
291       if (IS_VALID_SHORT_OPTION (&option_tab[i]))
292         {
293           if (w == 0)
294             {
295               indent (0, LMARGIN);
296               w = LMARGIN;
297             }
298           else
299             w += printf (", ");
300           w += printf ("-%c", option_tab[i].opt_short);
301           delim = ' ';
302         }
303     }
304 #ifdef HAVE_GETOPT_LONG
305   for (i = num; i < next; i++)
306     {
307       if (IS_VALID_LONG_OPTION (&option_tab[i]))
308         {
309           if (w == 0)
310             {
311               indent (0, LMARGIN);
312               w = LMARGIN;
313             }
314           else
315             w += printf (", ");
316           w += printf ("--%s", option_tab[i].opt_long);
317           delim = '=';
318         }
319     }
320 #else
321   if (!w)
322     return next;
323 #endif
324   if (opt->opt_arg)
325     {
326       argsused = 1;
327       w += printf ("%c%s", delim, gettext (opt->opt_arg));
328     }
329   if (w >= DESCRCOLUMN)
330     {
331       putchar ('\n');
332       w = 0;
333     }
334   indent (w, DESCRCOLUMN);
335   print_option_descr (gettext (opt->opt_descr), DESCRCOLUMN, RMARGIN);
336
337   return next;
338 }
339 \f
340 void
341 parseopt_print_help (void)
342 {
343   unsigned i;
344
345   argsused = 0;
346
347   printf ("%s %s [%s]... %s\n", _("Usage:"),
348           parseopt_program_name ? parseopt_program_name : progname,
349           _("OPTION"),
350           gettext (parseopt_program_args));
351   if (parseopt_program_doc)
352     print_option_descr (gettext (parseopt_program_doc), 0, RMARGIN);
353   putchar ('\n');
354
355   sort_all_options ();
356   for (i = 0; i < option_count; )
357     {
358       i = print_option (i);
359     }
360   putchar ('\n');
361 #ifdef HAVE_GETOPT_LONG
362   if (argsused)
363     {
364       print_option_descr (_("Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options."), 0, RMARGIN);
365       putchar ('\n');
366     }
367 #endif
368   if (parseopt_help_hook)
369     parseopt_help_hook (stdout);
370
371  /* TRANSLATORS: The placeholder indicates the bug-reporting address
372     for this package.  Please add _another line_ saying
373     "Report translation bugs to <...>\n" with the address for translation
374     bugs (typically your translation team's web or email address).  */
375   printf (_("Report bugs to %s.\n"), program_bug_address);
376   
377 #ifdef PACKAGE_URL
378   printf (_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
379 #endif
380 }
381 \f
382 static int
383 cmpidx_short (const void *a, const void *b)
384 {
385   unsigned const *ai = (unsigned const *)a;
386   unsigned const *bi = (unsigned const *)b;
387
388   return option_tab[*ai].opt_short - option_tab[*bi].opt_short;
389 }
390   
391 #ifdef HAVE_GETOPT_LONG
392 static int
393 cmpidx_long (const void *a, const void *b)
394 {
395   unsigned const *ai = (unsigned const *)a;
396   unsigned const *bi = (unsigned const *)b;
397   struct gdbm_option const *ap = option_tab + *ai;
398   struct gdbm_option const *bp = option_tab + *bi;
399   return strcmp (ap->opt_long, bp->opt_long);
400 }
401 #endif
402
403 void
404 print_usage (void)
405 {
406   unsigned i;
407   unsigned n;
408   char buf[RMARGIN+1];
409   unsigned *idxbuf;
410   unsigned nidx;
411   
412 #define FLUSH                                                           \
413   do                                                                    \
414     {                                                                   \
415       buf[n] = 0;                                                       \
416       printf ("%s\n", buf);                                             \
417       n = USAGECOLUMN;                                                  \
418       memset (buf, ' ', n);                                             \
419     }                                                                   \
420   while (0)
421 #define ADDC(c)                                                         \
422   do                                                                    \
423     {                                                                   \
424       if (n == RMARGIN) FLUSH;                                          \
425       buf[n++] = c;                                                     \
426     }                                                                   \
427   while (0)
428
429   idxbuf = ecalloc (option_count, sizeof (idxbuf[0]));
430
431   n = snprintf (buf, sizeof buf, "%s %s ", _("Usage:"),
432                 parseopt_program_name ? parseopt_program_name : progname);
433
434   /* Print a list of short options without arguments. */
435   for (i = nidx = 0; i < option_count; i++)
436     if (IS_VALID_SHORT_OPTION (&option_tab[i]) && !option_tab[i].opt_arg)
437       idxbuf[nidx++] = i;
438
439   if (nidx)
440     {
441       qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short);
442
443       ADDC ('[');
444       ADDC ('-');
445       for (i = 0; i < nidx; i++)
446         {
447           ADDC (option_tab[idxbuf[i]].opt_short);
448         }
449       ADDC (']');
450     }
451
452   /* Print a list of short options with arguments. */
453   for (i = nidx = 0; i < option_count; i++)
454     {
455       if (IS_VALID_SHORT_OPTION (&option_tab[i]) && option_tab[i].opt_arg)
456         idxbuf[nidx++] = i;
457     }
458
459   if (nidx)
460     {
461       qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short);
462     
463       for (i = 0; i < nidx; i++)
464         {
465           struct gdbm_option *opt = option_tab + idxbuf[i];
466           const char *arg = gettext (opt->opt_arg);
467           size_t len = 5 + strlen (arg) + 1;
468           
469           if (n + len > RMARGIN) FLUSH;
470           buf[n++] = ' ';
471           buf[n++] = '[';
472           buf[n++] = '-';
473           buf[n++] = opt->opt_short;
474           buf[n++] = ' ';
475           strcpy (&buf[n], arg);
476           n += strlen (arg);
477           buf[n++] = ']';
478         }
479     }
480   
481 #ifdef HAVE_GETOPT_LONG
482   /* Print a list of long options */
483   for (i = nidx = 0; i < option_count; i++)
484     {
485       if (IS_VALID_LONG_OPTION (&option_tab[i]))
486         idxbuf[nidx++] = i;
487     }
488
489   if (nidx)
490     {
491       qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_long);
492         
493       for (i = 0; i < nidx; i++)
494         {
495           struct gdbm_option *opt = option_tab + idxbuf[i];
496           const char *arg = opt->opt_arg ? gettext (opt->opt_arg) : NULL;
497           size_t len = 3 + strlen (opt->opt_long)
498                          + (arg ? 1 + strlen (arg) : 0);
499           if (n + len > RMARGIN) FLUSH;
500           buf[n++] = ' ';
501           buf[n++] = '[';
502           buf[n++] = '-';
503           buf[n++] = '-';
504           strcpy (&buf[n], opt->opt_long);
505           n += strlen (opt->opt_long);
506           if (opt->opt_arg)
507             {
508               buf[n++] = '=';
509               strcpy (&buf[n], arg);
510               n += strlen (arg);
511             }
512           buf[n++] = ']';
513         }
514     }
515 #endif
516   FLUSH;
517   free (idxbuf);
518 }
519 \f
520 const char version_etc_copyright[] =
521   /* Do *not* mark this string for translation.  First %s is a copyright
522      symbol suitable for this locale, and second %s are the copyright
523      years.  */
524   "Copyright %s %s Free Software Foundation, Inc";
525
526 const char license_text[] =
527   "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
528   "This is free software: you are free to change and redistribute it.\n"
529   "There is NO WARRANTY, to the extent permitted by law.";
530
531 void
532 print_version_only (void)
533 {
534   printf ("%s (%s) %s\n",
535            parseopt_program_name ? parseopt_program_name : progname,
536            PACKAGE_NAME,
537            PACKAGE_VERSION);
538   /* TRANSLATORS: Translate "(C)" to the copyright symbol
539      (C-in-a-circle), if this symbol is available in the user's
540      locale.  Otherwise, do not translate "(C)"; leave it as-is.  */
541   printf (version_etc_copyright, _("(C)"), "2011");
542   puts (license_text);
543   putchar ('\n');
544 }
545
546 \f
547 static int
548 handle_option (int c)
549 {
550   switch (c)
551     {
552     case 'h':
553       parseopt_print_help ();
554       exit (0);
555       
556     case 'V':
557       print_version_only ();
558       exit (0);
559       
560     case OPT_USAGE:
561       print_usage ();
562       exit (0);
563       
564     default:
565       break;
566     }
567   return 0;
568 }
569 \f
570 int
571 parseopt_next ()
572 {
573   int rc;
574   
575   do
576     {
577 #ifdef HAVE_GETOPT_LONG
578       rc = getopt_long (argc, argv, short_options, long_options, NULL);
579 #else
580       rc = getopt (argc, argv, short_options);
581 #endif
582     }
583   while (handle_option (rc));
584   return rc;
585 }