comparison src/dmargs.c @ 1742:ddec147d1f90

Bring in changes from the th-libs version of commandline argument handling.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 09 Jun 2018 17:56:07 +0300
parents 985225a93aeb
children bcdea45a14cb
comparison
equal deleted inserted replaced
1741:6f1313c761aa 1742:ddec147d1f90
1 /*
2 * Simple commandline argument processing
3 * Programmed and designed by Matti 'ccr' Hamalainen
4 * (C) Copyright 2002-2018 Tecnic Software productions (TNSP)
5 *
6 * Please read file 'COPYING' for information on license and distribution.
7 */
8 /// @file
9 /// @brief Simple commandline argument processing functions
1 #include "dmargs.h" 10 #include "dmargs.h"
2 #define TH_EXTERNAL 1 11
3 #define th_optarg_t DMOptArg 12
4 #define th_args_process dmArgsProcess 13 /**
5 #define th_args_help dmArgsPrintHelp 14 * Parse and optionally handle the given long or short option argument.
6 15 * @param currArg current argument string
7 #define THERR dmErrorMsg 16 * @param argIndex pointer to index of current argument in argv[]
8 #define th_calloc dmCalloc 17 * @param argc number of arguments
9 #define th_malloc dmMalloc 18 * @param argv argument string array
10 #define th_free dmFree 19 * @param opts options list array
11 20 * @param nopts number of elements in options list array
12 #include "dmargs_int.c" 21 * @param handle_option function pointer to callback that handles option arguments
22 * @param doProcess if TRUE, actually handle the argument, aka call the handle_option() function. if FALSE, only validity of options are checked.
23 * @param isLong TRUE if the option is a --long-format one
24 */
25 static BOOL dmArgsProcessOpt(
26 char *currArg, int *argIndex,
27 int argc, char *argv[],
28 const DMOptArg opts[], int nopts,
29 BOOL (*handle_option)(int id, char *, char *),
30 BOOL doProcess, BOOL isLong)
31 {
32 const DMOptArg *opt = NULL;
33 char *optArg = NULL;
34 int optIndex;
35
36 for (optIndex = 0; optIndex < nopts; optIndex++)
37 {
38 const DMOptArg *node = &opts[optIndex];
39 if (isLong && node->o_long != NULL)
40 {
41 if (strcmp(currArg, node->o_long) == 0)
42 {
43 opt = node;
44 optArg = NULL;
45 break;
46 }
47
48 size_t len = strlen(node->o_long);
49 if (strncmp(currArg, node->o_long, len) == 0 &&
50 currArg[len] == '=')
51 {
52 opt = node;
53 optArg = (&currArg[len+1] != 0) ? &currArg[len+1] : NULL;
54 break;
55 }
56 }
57 else
58 if (!isLong && node->o_short != 0)
59 {
60 if (*currArg == node->o_short)
61 {
62 opt = node;
63 optArg = (currArg[1] != 0) ? &currArg[1] : NULL;
64 }
65 }
66 }
67
68 if (opt != NULL)
69 {
70 // Check for the possible option argument
71 if ((opt->flags & OPT_ARGMASK) == OPT_ARGREQ && optArg == NULL)
72 {
73 if (*argIndex < argc)
74 {
75 (*argIndex)++;
76 optArg = argv[*argIndex];
77 }
78
79 if (optArg == NULL)
80 {
81 dmErrorMsg("Option '%s%s' requires an argument.\n",
82 isLong ? "--" : "-",
83 currArg);
84 return FALSE;
85 }
86 }
87
88 // Option was given succesfully, try to process it
89 if (doProcess && !handle_option(opt->id, optArg, currArg))
90 return FALSE;
91 }
92 else
93 {
94 dmErrorMsg("Unknown %s option '%s%s'\n",
95 isLong ? "long" : "short",
96 isLong ? "--" : "-",
97 currArg);
98
99 return FALSE;
100 }
101
102 return TRUE;
103 }
104
105
106 /**
107 * Process given array of commandline arguments, handling short
108 * and long options by calling the respective callback functions.
109 *
110 * @param argc number of arguments
111 * @param argv argument list
112 * @param opts supported option list array
113 * @param nopts number of elements in the option list array
114 * @param handle_option callback function
115 * @param handle_other callback function
116 * @param flags processing flags
117 * @return return TRUE if all is well
118 */
119 BOOL dmArgsProcess(int argc, char *argv[],
120 const DMOptArg *opts, const int nopts,
121 BOOL(*handle_option)(int id, char *, char *),
122 BOOL(*handle_other)(char *), const int flags)
123 {
124 int argIndex, handleFlags = flags & OPTH_ONLY_MASK;
125 BOOL optionsOK = TRUE, endOfOptions = FALSE;
126
127 for (argIndex = 1; argIndex < argc; argIndex++)
128 {
129 char *str = argv[argIndex];
130 if (*str == '-' && !endOfOptions)
131 {
132 // Should we process options?
133 BOOL doProcess = (handleFlags & OPTH_ONLY_OPTS) || handleFlags == 0;
134 BOOL isLong;
135
136 str++;
137 if (*str == '-')
138 {
139 // Check for "--", which ends the options-list
140 str++;
141 if (*str == 0)
142 {
143 endOfOptions = TRUE;
144 continue;
145 }
146
147 // We have a long option
148 isLong = TRUE;
149 }
150 else
151 isLong = FALSE;
152
153 if (!dmArgsProcessOpt(str, &argIndex, argc, argv,
154 opts, nopts, handle_option, doProcess, isLong))
155 optionsOK = FALSE;
156 }
157 else
158 if (handleFlags == OPTH_ONLY_OTHER || handleFlags == 0)
159 {
160 // Was not option argument
161 if (handle_other == NULL ||
162 (handle_other != NULL && !handle_other(str)))
163 {
164 dmErrorMsg("Invalid argument '%s'\n", str);
165 optionsOK = FALSE;
166 }
167 }
168
169 // Check if we bail out on invalid argument
170 if (!optionsOK && (flags & OPTH_BAILOUT))
171 return FALSE;
172 }
173
174 return optionsOK;
175 }
176
177
178 static void dmPad(FILE *outFile, int count)
179 {
180 while (count--)
181 fputc(' ', outFile);
182 }
183
184
185 static void dmPrintWrap(FILE *fh, const char *str, int spad, int rpad, int width)
186 {
187 size_t pos = 0;
188 BOOL first = TRUE;
189
190 while (str[pos])
191 {
192 // Pre-pad line
193 int linelen = first ? spad : rpad;
194 dmPad(fh, first ? 0 : rpad);
195 first = FALSE;
196
197 // Skip whitespace at line start
198 while (isspace(str[pos]) || str[pos] == '\n') pos++;
199
200 // Handle each word
201 while (str[pos] && str[pos] != '\n')
202 {
203 size_t next;
204 int wlen;
205 for (wlen = 0, next = pos; str[next] && !isspace(str[next]) && str[next] != '\n'; next++, wlen++);
206 // fprintf(stdout, "X '%c', %d .. linelen=%d/%d, wlen=%d\n", str[pos], pos, linelen, width, wlen);
207 if (linelen + wlen < width)
208 {
209 for (;pos < next; pos++, linelen++)
210 fputc(str[pos], fh);
211
212 if (str[next] == '\n' || str[next] == 0)
213 {
214 fprintf(fh, "\n");
215 break;
216 }
217 else
218 {
219 fputc(str[pos], fh);
220 pos++;
221 linelen++;
222 }
223 }
224 else
225 {
226 fprintf(fh, "\n");
227 break;
228 }
229 }
230 }
231 }
232
233
234 /**
235 * Print help for commandline arguments/options
236 * @param fh stdio file handle to output to
237 * @param opts options list array
238 * @param nopts number of elements in options list array
239 * @param flags flags (currently unused)
240 */
241 void dmArgsPrintHelp(FILE *fh,
242 const DMOptArg *opts, const int nopts,
243 const int flags)
244 {
245 int index;
246 (void) flags;
247
248 // Print out option list
249 for (index = 0; index < nopts; index++)
250 {
251 const DMOptArg *opt = &opts[index];
252 char tmpStr[128];
253
254 // Print short option
255 if (opt->o_short != 0)
256 {
257 snprintf(tmpStr, sizeof(tmpStr),
258 "-%c,", opt->o_short);
259 }
260 else
261 tmpStr[0] = 0;
262
263 fprintf(fh, " %-5s", tmpStr);
264
265 // Print long option
266 if (opt->o_long != NULL)
267 {
268 snprintf(tmpStr, sizeof(tmpStr), "--%s%s",
269 opt->o_long,
270 (opt->flags & OPT_ARGREQ) ? "=ARG" : "");
271 }
272 else
273 tmpStr[0] = 0;
274
275 fprintf(fh, "%-20s", tmpStr);
276
277 dmPrintWrap(fh, opt->desc, 26, 26, 79);
278 }
279 }