Mercurial > hg > dmlib
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 } |