1 /*****************************************************************************
2  Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 *****************************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 #include <stdarg.h>
19 
20 /* utility */
21 #include "fciconv.h"
22 #include "fcintl.h"
23 #include "mem.h"
24 #include "shared.h"
25 #include "support.h"
26 
27 #include "fc_cmdhelp.h"
28 
29 struct cmdarg {
30   char shortarg;
31   char *longarg;
32   char *helpstr;
33 };
34 
35 /* 'struct cmdarg_list' and related functions. */
36 #define SPECLIST_TAG cmdarg
37 #define SPECLIST_TYPE struct cmdarg
38 #include "speclist.h"
39 #define cmdarg_list_iterate(cmdarg_list, pcmdarg)                            \
40   TYPED_LIST_ITERATE(struct cmdarg, cmdarg_list, pcmdarg)
41 #define cmdarg_list_iterate_end LIST_ITERATE_END
42 
43 struct cmdhelp {
44   char *cmdname;
45   struct cmdarg_list *cmdarglist;
46 };
47 
48 static struct cmdarg *cmdarg_new(const char *shortarg, const char *longarg,
49                                  const char *helpstr);
50 static void cmdarg_destroy(struct cmdarg *pcmdarg);
51 static int cmdarg_compare(const struct cmdarg *const *pcmdarg0,
52                           const struct cmdarg *const *pcmdarg1);
53 
54 /*****************************************************************************
55   Create a new command help struct.
56 *****************************************************************************/
cmdhelp_new(const char * cmdname)57 struct cmdhelp *cmdhelp_new(const char *cmdname)
58 {
59   struct cmdhelp *pcmdhelp = fc_calloc(1, sizeof(*pcmdhelp));
60 
61   pcmdhelp->cmdname = fc_strdup(fc_basename(cmdname));
62   pcmdhelp->cmdarglist = cmdarg_list_new();
63 
64   return pcmdhelp;
65 }
66 
67 /*****************************************************************************
68   Destroy a command help struct.
69 *****************************************************************************/
cmdhelp_destroy(struct cmdhelp * pcmdhelp)70 void cmdhelp_destroy(struct cmdhelp *pcmdhelp)
71 {
72   if (pcmdhelp) {
73     if (pcmdhelp->cmdname) {
74       free(pcmdhelp->cmdname);
75     }
76     cmdarg_list_iterate(pcmdhelp->cmdarglist, pcmdarg) {
77       cmdarg_destroy(pcmdarg);
78     } cmdarg_list_iterate_end;
79   }
80   free(pcmdhelp);
81 }
82 
83 /*****************************************************************************
84   Add a command help moption.
85 *****************************************************************************/
cmdhelp_add(struct cmdhelp * pcmdhelp,const char * shortarg,const char * longarg,const char * helpstr,...)86 void cmdhelp_add(struct cmdhelp *pcmdhelp, const char *shortarg,
87                  const char *longarg, const char *helpstr, ...)
88 {
89   va_list args;
90   char buf[512];
91   struct cmdarg *pcmdarg;
92 
93   va_start(args, helpstr);
94   fc_vsnprintf(buf, sizeof(buf), helpstr, args);
95   va_end(args);
96 
97   pcmdarg = cmdarg_new(shortarg, longarg, buf);
98   cmdarg_list_append(pcmdhelp->cmdarglist, pcmdarg);
99 }
100 
101 /*****************************************************************************
102   Display the help for the command.
103 *****************************************************************************/
cmdhelp_display(struct cmdhelp * pcmdhelp,bool sort,bool gui_options,bool report_bugs)104 void cmdhelp_display(struct cmdhelp *pcmdhelp, bool sort, bool gui_options,
105                      bool report_bugs)
106 {
107   fc_fprintf(stderr, _("Usage: %s [option ...]\nValid option are:\n"),
108              pcmdhelp->cmdname);
109 
110   cmdarg_list_sort(pcmdhelp->cmdarglist, cmdarg_compare);
111   cmdarg_list_iterate(pcmdhelp->cmdarglist, pcmdarg) {
112     if (pcmdarg->shortarg != '\0') {
113       fc_fprintf(stderr, "  -%c, --%-15s %s\n", pcmdarg->shortarg,
114                  pcmdarg->longarg, pcmdarg->helpstr);
115     } else {
116       fc_fprintf(stderr, "      --%-15s %s\n", pcmdarg->longarg,
117                  pcmdarg->helpstr);
118     }
119   } cmdarg_list_iterate_end;
120 
121   if (gui_options) {
122     char buf[128];
123 
124     fc_snprintf(buf, sizeof(buf), _("Try \"%s -- --help\" for more."),
125                 pcmdhelp->cmdname);
126 
127     /* The nearly empty strings in the two functions below have to be adapted
128      * if the format of the command argument list above is changed.*/
129     fc_fprintf(stderr, "      --                %s\n",
130                _("Pass any following options to the UI."));
131     fc_fprintf(stderr, "                        %s\n", buf);
132   }
133 
134   if (report_bugs) {
135     /* TRANS: No full stop after the URL, could cause confusion. */
136     fc_fprintf(stderr, _("Report bugs at %s\n"), BUG_URL);
137   }
138 }
139 
140 /*****************************************************************************
141   Create a new command argument struct.
142 *****************************************************************************/
cmdarg_new(const char * shortarg,const char * longarg,const char * helpstr)143 static struct cmdarg *cmdarg_new(const char *shortarg, const char *longarg,
144                                  const char *helpstr)
145 {
146   struct cmdarg *pcmdarg = fc_calloc(1, sizeof(*pcmdarg));
147 
148   if (shortarg && strlen(shortarg) == 1) {
149     pcmdarg->shortarg = shortarg[0];
150   } else {
151     /* '\0' means no short argument for this option. */
152     pcmdarg->shortarg = '\0';
153   }
154   pcmdarg->longarg = fc_strdup(longarg);
155   pcmdarg->helpstr = fc_strdup(helpstr);
156 
157   return pcmdarg;
158 }
159 
160 /*****************************************************************************
161   Destroy a command argument struct.
162 *****************************************************************************/
cmdarg_destroy(struct cmdarg * pcmdarg)163 static void cmdarg_destroy(struct cmdarg *pcmdarg)
164 {
165   if (pcmdarg) {
166     if (pcmdarg->longarg) {
167       free(pcmdarg->longarg);
168     }
169     if (pcmdarg->helpstr) {
170       free(pcmdarg->helpstr);
171     }
172   }
173   free(pcmdarg);
174 }
175 
176 /*****************************************************************************
177   Compare two command argument definitions.
178 *****************************************************************************/
cmdarg_compare(const struct cmdarg * const * pp0,const struct cmdarg * const * pp1)179 static int cmdarg_compare(const struct cmdarg *const *pp0,
180                           const struct cmdarg *const *pp1)
181 {
182   const struct cmdarg *pcmdarg0 = *pp0;
183   const struct cmdarg *pcmdarg1 = *pp1;
184   int c0, c1;
185 
186   if (pcmdarg0 == NULL) {
187     return -1;
188   }
189   if (pcmdarg1 == NULL) {
190     return 1;
191   }
192 
193   /* Arguments without a short option are listed at the end sorted by the
194    * long option. */
195   if (pcmdarg0->shortarg == '\0') {
196     if (pcmdarg1->shortarg == '\0') {
197       return fc_strcasecmp(pcmdarg0->longarg, pcmdarg1->longarg);
198     } else {
199       return 1;
200     }
201   }
202   if (pcmdarg1->shortarg == '\0') {
203     return -1;
204   }
205 
206   /* All other are sorted alphabetically by the shortarg in the following
207    * order: AaBbCcDd... */
208   c0 = (int) (unsigned char) fc_tolower(pcmdarg0->shortarg);
209   c1 = (int) (unsigned char) fc_tolower(pcmdarg1->shortarg);
210   if (c0 == c1) {
211     return (int) (unsigned char)pcmdarg0->shortarg
212            - (int) (unsigned char)pcmdarg1->shortarg;
213   } else {
214     return c0 - c1;
215   }
216 }
217