fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/lib/getopt.c *
7 * Created: 2009-14-21 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2009-2020 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include <ctype.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include <lib/getopt.h>
29
30
31static int atend = 0;
32static int index1 = -1;
33static int index2 = -1;
34static const char *curopt = NULL;
35
36
37static
38int opt_cmp (const pce_option_t *opt1, const pce_option_t *opt2)
39{
40 int c1, c2;
41
42 c1 = (opt1->name1 <= 255) ? tolower (opt1->name1) : opt1->name1;
43 c2 = (opt2->name1 <= 255) ? tolower (opt2->name1) : opt2->name1;
44
45 if (c1 < c2) {
46 return (-1);
47 }
48 else if (c1 > c2) {
49 return (1);
50 }
51 else if (opt1->name1 < opt2->name1) {
52 return (1);
53 }
54 else if (opt1->name1 > opt2->name1) {
55 return (-1);
56 }
57
58 return (0);
59}
60
61static
62unsigned opt_get_width (const pce_option_t *opt)
63{
64 unsigned n;
65
66 if (opt->optdesc == NULL) {
67 return (0);
68 }
69
70 n = 0;
71
72 if (opt->name1 <= 255) {
73 n += 2;
74
75 if (opt->name2 != NULL) {
76 n += 2;
77 }
78 }
79
80 if (opt->name2 != NULL) {
81 n += 2 + strlen (opt->name2);
82 }
83
84 if (opt->argdesc != NULL) {
85 n += 1 + strlen (opt->argdesc);
86 }
87
88 return (n);
89}
90
91static
92unsigned opt_max_width (const pce_option_t *opt)
93{
94 unsigned i, n, w;
95
96 w = 0;
97
98 i = 0;
99 while (opt[i].name1 >= 0) {
100 n = opt_get_width (&opt[i]);
101
102 if (n > w) {
103 w = n;
104 }
105
106 i += 1;
107 }
108
109 return (w);
110}
111
112static
113void sort_options (pce_option_t *opt)
114{
115 unsigned i, j;
116 pce_option_t tmp;
117
118 if (opt[0].name1 < 0) {
119 return;
120 }
121
122 i = 1;
123 while (opt[i].name1 >= 0) {
124 if (opt_cmp (&opt[i], &opt[i - 1]) >= 0) {
125 i += 1;
126 continue;
127 }
128
129 j = i - 1;
130
131 tmp = opt[i];
132 opt[i] = opt[j];
133
134 while ((j > 0) && (opt_cmp (&tmp, &opt[j - 1]) < 0)) {
135 opt[j] = opt[j - 1];
136 j -= 1;
137 }
138
139 opt[j] = tmp;
140
141 i += 1;
142 }
143}
144
145static
146void print_option (const pce_option_t *opt, unsigned w)
147{
148 unsigned n;
149
150 n = 0;
151
152 if (opt->name1 <= 255) {
153 printf (" -%c", opt->name1);
154 n += 2;
155
156 if (opt->name2 != NULL) {
157 printf (", ");
158 n += 2;
159 }
160 }
161 else {
162 printf (" ");
163 }
164
165 if (opt->name2 != NULL) {
166 printf ("--%s", opt->name2);
167 n += 2 + strlen (opt->name2);
168 }
169
170 if (opt->argdesc != NULL) {
171 printf (" %s", opt->argdesc);
172 n += 1 + strlen (opt->argdesc);
173 }
174
175 while (n < w) {
176 fputc (' ', stdout);
177 n += 1;
178 }
179
180 printf ("%s\n", opt->optdesc);
181}
182
183void pce_getopt_help (const char *tag, const char *usage, pce_option_t *opt)
184{
185 unsigned w;
186
187 sort_options (opt);
188
189 w = opt_max_width (opt);
190
191 if (tag != NULL) {
192 printf ("%s\n\n", tag);
193 }
194
195 if (usage != NULL) {
196 printf ("%s\n", usage);
197 }
198
199 while (opt->name1 >= 0) {
200 print_option (opt, w + 2);
201 opt += 1;
202 }
203}
204
205static
206pce_option_t *find_option_name1 (pce_option_t *opt, int name1)
207{
208 while (opt->name1 >= 0) {
209 if (opt->name1 == name1) {
210 return (opt);
211 }
212
213 opt += 1;
214 }
215
216 return (NULL);
217}
218
219static
220pce_option_t *find_option_name2 (pce_option_t *opt, const char *name2)
221{
222 while (opt->name1 >= 0) {
223 if (strcmp (opt->name2, name2) == 0) {
224 return (opt);
225 }
226
227 opt += 1;
228 }
229
230 return (NULL);
231}
232
233int pce_getoptarg (int argc, char **argv, char ***optarg, unsigned cnt)
234{
235 if (index1 < 0) {
236 atend = 0;
237 index1 = 0;
238 index2 = 1;
239 curopt = NULL;
240 }
241
242 if ((index2 + cnt) > argc) {
243 return (GETOPT_MISSING);
244 }
245
246 index1 = index2;
247 index2 += cnt;
248
249 *optarg = argv + index1;
250
251 return (0);
252}
253
254int pce_getopt (int argc, char **argv, char ***optarg, pce_option_t *opt)
255{
256 pce_option_t *ret;
257
258 if ((argc == 0) && (argv == NULL)) {
259 index1 = -1;
260 return (0);
261 }
262
263 if (index1 < 0) {
264 atend = 0;
265 index1 = 0;
266 index2 = 1;
267 curopt = NULL;
268 }
269
270 if (atend || (opt == NULL)) {
271 if (index2 >= argc) {
272 return (GETOPT_DONE);
273 }
274
275 index1 = index2;
276 index2 += 1;
277
278 *optarg = argv + index1;
279
280 return (0);
281 }
282
283 if ((curopt == NULL) || (*curopt == 0)) {
284 if (index2 >= argc) {
285 return (GETOPT_DONE);
286 }
287
288 index1 = index2;
289 index2 += 1;
290
291 curopt = argv[index1];
292
293 if ((curopt[0] != '-') || (curopt[1] == 0)) {
294 *optarg = argv + index1;
295 curopt = NULL;
296 return (0);
297 }
298
299 if (curopt[1] == '-') {
300 if (curopt[2] == 0) {
301 atend = 1;
302
303 if (index2 >= argc) {
304 return (GETOPT_DONE);
305 }
306
307 index1 = index2;
308 index2 += 1;
309
310 *optarg = argv + index1;
311
312 return (0);
313 }
314
315 ret = find_option_name2 (opt, curopt + 2);
316
317 if (ret == NULL) {
318 fprintf (stderr, "%s: unknown option (%s)\n",
319 argv[0], curopt
320 );
321 return (GETOPT_UNKNOWN);
322 }
323
324 if ((index2 + ret->argcnt) > argc) {
325 fprintf (stderr,
326 "%s: missing option argument (%s)\n",
327 argv[0], curopt
328 );
329 return (GETOPT_MISSING);
330 }
331
332 *optarg = argv + index2;
333 index2 += ret->argcnt;
334 curopt = NULL;
335
336 return (ret->name1);
337 }
338
339 curopt += 1;
340 }
341
342 ret = find_option_name1 (opt, *curopt);
343
344 if (ret == NULL) {
345 fprintf (stderr, "%s: unknown option (-%c)\n",
346 argv[0], *curopt
347 );
348 return (GETOPT_UNKNOWN);
349 }
350
351 if ((index2 + ret->argcnt) > argc) {
352 fprintf (stderr,
353 "%s: missing option argument (-%c)\n",
354 argv[0], *curopt
355 );
356 return (GETOPT_MISSING);
357 }
358
359 *optarg = argv + index2;
360 index2 += ret->argcnt;
361 curopt += 1;
362
363 return (ret->name1);
364}