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