source: git/src/cmdline.c @ 8676839

stereo-2025
Last change on this file since 8676839 was 5a2d346, checked in by Olly Betts <olly@…>, 7 months ago

Support custom placeholders in --help output

So far this only improves the extend --help output in a very minor
way.

  • Property mode set to 100644
File size: 7.2 KB
RevLine 
[6974a34]1/* cmdline.c
[aadc86e]2 * Wrapper for GNU getopt which deals with standard options
[ab8bd76]3 * Copyright (C) 1998-2001,2003,2004,2011,2012,2014,2024 Olly Betts
[846746e]4 *
[89231c4]5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
[846746e]9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
[89231c4]12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
[846746e]14 *
[89231c4]15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
[ecbc6c18]17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
[aadc86e]18 */
19
20#include <config.h>
21
22#include <ctype.h>
[3b9ec3f]23#include <errno.h>
[dd6f74e]24#include <float.h>
[aadc86e]25#include <stdio.h>
[e05bdb8]26#include <string.h>
[f2122f0]27#include <limits.h>
[aadc86e]28
[be97baf]29#include "getopt.h"
30
[aadc86e]31#include "cmdline.h"
[45af761]32#include "debug.h"
[e05bdb8]33#include "filename.h"
[aadc86e]34
[d227ee5]35#include "message.h"
36
[aadc86e]37/* It might be useful to be able to disable all long options on small
[175cac6]38 * platforms like older PDAs.
[aadc86e]39 */
40#if 0
41# define getopt_long(ARGC, ARGV, STR, OPTS, PTR) getopt(ARGC, ARGV, STR)
42#endif
43
44/*
45 * bad command line give:
46 * <problem>
[cb3d1e2]47 *
[e05bdb8]48 * <short syntax>
[cb3d1e2]49 *
[aadc86e]50 * --help gives:
51 * <version>
[cb3d1e2]52 *
[aadc86e]53 * <short syntax>
54 *
55 * <table>
[cb3d1e2]56 *
[aadc86e]57 * <blurb>
58 *
59 * --version gives:
60 * <version>
61 */
62
63/*
64 * want to cope with optional/required parameters on long options
65 * and also parameters on short options
[3b9ec3f]66 */
[aadc86e]67
68static const char newline_tabs[] = "\n\t\t\t\t";
69
[3b9ec3f]70static int argc;
[647407d]71static char * const *argv;
[3b9ec3f]72static const char *shortopts;
73static const struct option *longopts;
74static int *longind;
75static const struct help_msg *help;
76static int min_args, max_args;
[d8dbdff]77static int msg_args, msg_extra;
78static const char * msg_extra_arg;
[3b9ec3f]79
80void
81cmdline_help(void)
[aadc86e]82{
[9e513bd3]83   while (help && help->opt) {
[aadc86e]84      const char *longopt = 0;
85      int opt = help->opt;
86      const struct option *o = 0;
87
88      if (HLP_ISLONG(opt)) {
[3b9ec3f]89         o = longopts + HLP_DECODELONG(opt);
[aadc86e]90         longopt = o->name;
91         opt = o->val;
92      }
93
[0580c6a]94      if (isalnum((unsigned char)opt))
[5fbfd8d]95         printf("  -%c%c", opt, longopt ? ',' : ' ');
[aadc86e]96      else
97         fputs("     ", stdout);
98
99      if (longopt) {
100         int len = strlen(longopt);
101         printf(" --%s", longopt);
102         if (o && o->has_arg) {
103            const char *p;
104            len += len + 1;
105
106            if (o->has_arg == optional_argument) {
107               putchar('[');
108               len += 2;
109            }
110
111            putchar('=');
112
[5a2d346]113            if (help->placeholder) {
114                fputs(help->placeholder, stdout);
115            } else {
116                for (p = longopt; *p ; p++) {
117                    unsigned char ch = *p;
118                    putchar((ch == '-') ? '_' : toupper(ch));
119                }
[ab8bd76]120            }
[aadc86e]121
122            if (o->has_arg == optional_argument) putchar(']');
123         }
124         len = (len >> 3) + 2;
125         if (len > 4) len = 0;
126         fputs(newline_tabs + len, stdout);
127      } else {
128         fputs(newline_tabs + 1, stdout);
129      }
130
[45af761]131      if (help->arg) {
132          SVX_ASSERT(strstr(msg(help->msg_no), "%s") != NULL);
133          printf(msg(help->msg_no), help->arg);
134          putnl();
135      } else {
136          SVX_ASSERT(strstr(msg(help->msg_no), "%s") == NULL);
137          puts(msg(help->msg_no));
138      }
[aadc86e]139      help++;
140   }
[45af761]141   fputs("      --help\t\t\t", stdout);
[736f7df]142   /* TRANSLATORS: description of --help option */
[45af761]143   puts(msg(/*display this help and exit*/150));
144   fputs("      --version\t\t\t", stdout);
[c5d45ba]145   /* TRANSLATORS: description of --version option */
[45af761]146   puts(msg(/*output version information and exit*/151));
[acc20b1]147
[d8dbdff]148   if (msg_extra) {
[acc20b1]149      putnl();
[d8dbdff]150      if (msg_extra_arg) {
151          SVX_ASSERT(strstr(msg(msg_extra), "%s") != NULL);
152          printf(msg(msg_extra), msg_extra_arg);
153          putnl();
154      } else {
155          SVX_ASSERT(strstr(msg(msg_extra), "%s") == NULL);
156          puts(msg(msg_extra));
157      }
[acc20b1]158   }
[647407d]159
[aadc86e]160   exit(0);
161}
162
[3b9ec3f]163void
164cmdline_version(void)
[aadc86e]165{
[82afbc4]166   printf("%s - "PRETTYPACKAGE" "VERSION"\n", msg_appname());
[aadc86e]167}
168
[3b9ec3f]169void
170cmdline_syntax(void)
[cb3d1e2]171{
[736f7df]172   /* TRANSLATORS: as in: Usage: cavern … */
[ee7511a]173   printf("\n%s: %s", msg(/*Usage*/49), msg_appname());
[736f7df]174   /* TRANSLATORS: in command line usage messages e.g. Usage: cavern [OPTION]… */
[9e513bd3]175   if (help && help->opt) printf(" [%s]...", msg(/*OPTION*/153));
[d8dbdff]176   if (msg_args) {
[acc20b1]177      putchar(' ');
[d8dbdff]178      puts(msg(msg_args));
[acc20b1]179      return;
180   }
[3b9ec3f]181   if (min_args) {
182      int i = min_args;
[a580dc1]183      while (i--) printf(" %s", msg(/*FILE*/124));
[3b9ec3f]184   }
[647407d]185   if (max_args == -1) {
[a580dc1]186      if (!min_args) printf(" [%s]", msg(/*FILE*/124));
[647407d]187      fputs("...", stdout);
188   } else if (max_args > min_args) {
189      int i = max_args - min_args;
[a580dc1]190      while (i--) printf(" [%s]", msg(/*FILE*/124));
[647407d]191   }
[3b9ec3f]192   putnl();
193}
194
[2668614]195static void
196syntax_and_help_pointer(void)
197{
198   cmdline_syntax();
[ee7511a]199   fprintf(stderr, msg(/*Try “%s --help” for more information.\n*/157),
[2668614]200           msg_appname());
201   exit(1);
202}
203
204static void
205moan_and_die(int msgno)
206{
207   fprintf(stderr, "%s: ", msg_appname());
208   fprintf(stderr, msg(msgno), optarg);
209   fputnl(stderr);
210   cmdline_syntax();
211   exit(1);
212}
213
214void
215cmdline_too_few_args(void)
216{
217   fprintf(stderr, "%s: %s\n", msg_appname(), msg(/*too few arguments*/122));
218   syntax_and_help_pointer();
219}
220
221void
222cmdline_too_many_args(void)
223{
224   fprintf(stderr, "%s: %s\n", msg_appname(), msg(/*too many arguments*/123));
225   syntax_and_help_pointer();
226}
227
[acc20b1]228void
[d8dbdff]229cmdline_set_syntax_message(int msg_args_, int msg_extra_, const char * arg)
[acc20b1]230{
[d8dbdff]231   msg_args = msg_args_;
232   msg_extra = msg_extra_;
233   msg_extra_arg = arg;
[acc20b1]234}
235
[3b9ec3f]236int
237cmdline_int_arg(void)
[aadc86e]238{
[f2122f0]239   long result;
[3b9ec3f]240   char *endptr;
[cb3d1e2]241
[3b9ec3f]242   errno = 0;
243
244   result = strtol(optarg, &endptr, 10);
245
[f2122f0]246   if (errno == ERANGE || result > INT_MAX || result < INT_MIN) {
[0804fbe]247      moan_and_die(/*numeric argument “%s” out of range*/185);
[3b9ec3f]248   } else if (*optarg == '\0' || *endptr != '\0') {
[0804fbe]249      moan_and_die(/*argument “%s” not an integer*/186);
[3b9ec3f]250   }
[cb3d1e2]251
[f2122f0]252   return (int)result;
[aadc86e]253}
254
[acc20b1]255double
256cmdline_double_arg(void)
[aadc86e]257{
[acc20b1]258   double result;
[3b9ec3f]259   char *endptr;
[cb3d1e2]260
[3b9ec3f]261   errno = 0;
262
263   result = strtod(optarg, &endptr);
264
265   if (errno == ERANGE) {
[0804fbe]266      moan_and_die(/*numeric argument “%s” out of range*/185);
[3b9ec3f]267   } else if (*optarg == '\0' || *endptr != '\0') {
[0804fbe]268      moan_and_die(/*argument “%s” not a number*/187);
[3b9ec3f]269   }
[cb3d1e2]270
[3b9ec3f]271   return result;
272}
273
274void
275cmdline_init(int argc_, char *const *argv_, const char *shortopts_,
276             const struct option *longopts_, int *longind_,
277             const struct help_msg *help_,
278             int min_args_, int max_args_)
279{
280   argc = argc_;
281   argv = argv_;
282   shortopts = shortopts_;
283   longopts = longopts_;
284   longind = longind_;
285   help = help_;
286   min_args = min_args_;
287   max_args = max_args_;
288}
289
290int
291cmdline_getopt(void)
292{
293   int opt = getopt_long(argc, argv, shortopts, longopts, longind);
[aadc86e]294
[2668614]295   switch (opt) {
296    case EOF:
[a580dc1]297      /* check valid # of args given - if not give syntax message */
[3b9ec3f]298      if (argc - optind < min_args) {
[2668614]299         cmdline_too_few_args();
[3b9ec3f]300      } else if (max_args >= 0 && argc - optind > max_args) {
[2668614]301         cmdline_too_many_args();
[3b9ec3f]302      }
[2668614]303      break;
[aadc86e]304    case ':': /* parameter missing */
305    case '?': /* unknown opt, ambiguous match, or extraneous param */
[a580dc1]306      /* getopt displays a message for us */
[2668614]307      syntax_and_help_pointer();
308      break;
[aadc86e]309    case HLP_VERSION: /* --version */
[3b9ec3f]310      cmdline_version();
[aadc86e]311      exit(0);
312    case HLP_HELP: /* --help */
[3b9ec3f]313      cmdline_version();
314      cmdline_syntax();
[aadc86e]315      putchar('\n');
[3b9ec3f]316      cmdline_help();
[aadc86e]317      exit(0);
318   }
319   return opt;
320}
Note: See TracBrowser for help on using the repository browser.