1 |
|
|
/* |
2 |
|
|
Teem: Tools to process and visualize scientific data and images . |
3 |
|
|
Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago |
4 |
|
|
Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann |
5 |
|
|
Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah |
6 |
|
|
|
7 |
|
|
This library is free software; you can redistribute it and/or |
8 |
|
|
modify it under the terms of the GNU Lesser General Public License |
9 |
|
|
(LGPL) as published by the Free Software Foundation; either |
10 |
|
|
version 2.1 of the License, or (at your option) any later version. |
11 |
|
|
The terms of redistributing and/or modifying this software also |
12 |
|
|
include exceptions to the LGPL that facilitate static linking. |
13 |
|
|
|
14 |
|
|
This library is distributed in the hope that it will be useful, |
15 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 |
|
|
Lesser General Public License for more details. |
18 |
|
|
|
19 |
|
|
You should have received a copy of the GNU Lesser General Public License |
20 |
|
|
along with this library; if not, write to Free Software Foundation, Inc., |
21 |
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 |
|
|
*/ |
23 |
|
|
|
24 |
|
|
#include "hest.h" |
25 |
|
|
#include "privateHest.h" |
26 |
|
|
#include <limits.h> |
27 |
|
|
|
28 |
|
|
const int |
29 |
|
|
hestPresent = 42; |
30 |
|
|
|
31 |
|
|
hestParm * |
32 |
|
|
hestParmNew() { |
33 |
|
|
hestParm *parm; |
34 |
|
|
|
35 |
|
84 |
parm = AIR_CALLOC(1, hestParm); |
36 |
✓✗ |
42 |
if (parm) { |
37 |
|
42 |
parm->verbosity = hestVerbosity; |
38 |
|
42 |
parm->respFileEnable = hestRespFileEnable; |
39 |
|
42 |
parm->elideSingleEnumType = hestElideSingleEnumType; |
40 |
|
42 |
parm->elideSingleOtherType = hestElideSingleOtherType; |
41 |
|
42 |
parm->elideSingleOtherDefault = hestElideSingleOtherDefault; |
42 |
|
42 |
parm->greedySingleString = hestGreedySingleString; |
43 |
|
42 |
parm->elideSingleNonExistFloatDefault = |
44 |
|
42 |
hestElideSingleNonExistFloatDefault; |
45 |
|
42 |
parm->elideMultipleNonExistFloatDefault = |
46 |
|
42 |
hestElideMultipleNonExistFloatDefault; |
47 |
|
42 |
parm->elideSingleEmptyStringDefault = |
48 |
|
42 |
hestElideSingleEmptyStringDefault; |
49 |
|
42 |
parm->elideMultipleEmptyStringDefault = |
50 |
|
42 |
hestElideMultipleEmptyStringDefault; |
51 |
|
42 |
parm->cleverPluralizeOtherY = hestCleverPluralizeOtherY; |
52 |
|
42 |
parm->columns = hestColumns; |
53 |
|
42 |
parm->respFileFlag = hestRespFileFlag; |
54 |
|
42 |
parm->respFileComment = hestRespFileComment; |
55 |
|
42 |
parm->varParamStopFlag = hestVarParamStopFlag; |
56 |
|
42 |
parm->multiFlagSep = hestMultiFlagSep; |
57 |
|
42 |
} |
58 |
|
42 |
return parm; |
59 |
|
|
} |
60 |
|
|
|
61 |
|
|
hestParm * |
62 |
|
|
hestParmFree(hestParm *parm) { |
63 |
|
|
|
64 |
|
84 |
airFree(parm); |
65 |
|
42 |
return NULL; |
66 |
|
|
} |
67 |
|
|
|
68 |
|
|
void |
69 |
|
|
_hestOptInit(hestOpt *opt) { |
70 |
|
|
|
71 |
|
1742 |
opt->flag = opt->name = NULL; |
72 |
|
871 |
opt->type = opt->min = opt->max = 0; |
73 |
|
871 |
opt->valueP = NULL; |
74 |
|
871 |
opt->dflt = opt->info = NULL; |
75 |
|
871 |
opt->sawP = NULL; |
76 |
|
871 |
opt->enm = NULL; |
77 |
|
871 |
opt->CB = NULL; |
78 |
|
871 |
opt->sawP = NULL; |
79 |
|
871 |
opt->kind = opt->alloc = 0; |
80 |
|
871 |
opt->source = hestSourceUnknown; |
81 |
|
871 |
} |
82 |
|
|
|
83 |
|
|
/* |
84 |
|
|
hestOpt * |
85 |
|
|
hestOptNew(void) { |
86 |
|
|
hestOpt *opt; |
87 |
|
|
|
88 |
|
|
opt = AIR_CALLOC(1, hestOpt); |
89 |
|
|
if (opt) { |
90 |
|
|
_hestOptInit(opt); |
91 |
|
|
opt->min = 1; |
92 |
|
|
} |
93 |
|
|
return opt; |
94 |
|
|
} |
95 |
|
|
*/ |
96 |
|
|
|
97 |
|
|
/* |
98 |
|
|
** as of Sept 2013 this returns information: the index of the |
99 |
|
|
** option just added. Returns UINT_MAX in case of error. |
100 |
|
|
*/ |
101 |
|
|
unsigned int |
102 |
|
|
hestOptAdd(hestOpt **optP, |
103 |
|
|
const char *flag, const char *name, |
104 |
|
|
int type, int min, int max, |
105 |
|
|
void *valueP, const char *dflt, const char *info, ...) { |
106 |
|
|
hestOpt *ret = NULL; |
107 |
|
|
int num; |
108 |
|
1742 |
va_list ap; |
109 |
|
|
unsigned int retIdx; |
110 |
|
|
|
111 |
✗✓ |
871 |
if (!optP) |
112 |
|
|
return UINT_MAX; |
113 |
|
|
|
114 |
✓✓ |
2482 |
num = *optP ? _hestNumOpts(*optP) : 0; |
115 |
✗✓ |
871 |
if (!( ret = AIR_CALLOC(num+2, hestOpt) )) { |
116 |
|
|
return UINT_MAX; |
117 |
|
|
} |
118 |
✓✓ |
871 |
if (num) |
119 |
|
740 |
memcpy(ret, *optP, num*sizeof(hestOpt)); |
120 |
|
|
retIdx = AIR_UINT(num); |
121 |
|
871 |
ret[num].flag = airStrdup(flag); |
122 |
|
871 |
ret[num].name = airStrdup(name); |
123 |
|
871 |
ret[num].type = type; |
124 |
|
871 |
ret[num].min = min; |
125 |
|
871 |
ret[num].max = max; |
126 |
|
871 |
ret[num].valueP = valueP; |
127 |
|
871 |
ret[num].dflt = airStrdup(dflt); |
128 |
|
871 |
ret[num].info = airStrdup(info); |
129 |
|
|
/* initialize the things that may be set below */ |
130 |
|
871 |
ret[num].sawP = NULL; |
131 |
|
871 |
ret[num].enm = NULL; |
132 |
|
871 |
ret[num].CB = NULL; |
133 |
|
|
/* seems to be redundant with above _hestOptInit() */ |
134 |
|
871 |
ret[num].source = hestSourceUnknown; |
135 |
|
|
/* deal with var args */ |
136 |
✓✓ |
871 |
if (5 == _hestKind(&(ret[num]))) { |
137 |
|
58 |
va_start(ap, info); |
138 |
✗✓ |
174 |
ret[num].sawP = va_arg(ap, unsigned int*); |
139 |
|
58 |
va_end(ap); |
140 |
|
58 |
} |
141 |
✓✓ |
871 |
if (airTypeEnum == type) { |
142 |
|
43 |
va_start(ap, info); |
143 |
✗✓ |
129 |
va_arg(ap, unsigned int*); /* skip sawP */ |
144 |
✗✓ |
129 |
ret[num].enm = va_arg(ap, airEnum*); |
145 |
|
43 |
va_end(ap); |
146 |
|
43 |
} |
147 |
✓✓ |
871 |
if (airTypeOther == type) { |
148 |
|
168 |
va_start(ap, info); |
149 |
✗✓ |
504 |
va_arg(ap, unsigned int*); /* skip sawP */ |
150 |
✗✓ |
504 |
va_arg(ap, airEnum*); /* skip enm */ |
151 |
✗✓ |
504 |
ret[num].CB = va_arg(ap, hestCB*); |
152 |
|
168 |
va_end(ap); |
153 |
|
168 |
} |
154 |
|
871 |
_hestOptInit(&(ret[num+1])); |
155 |
|
871 |
ret[num+1].min = 1; |
156 |
✓✓ |
871 |
if (*optP) |
157 |
|
740 |
free(*optP); |
158 |
|
871 |
*optP = ret; |
159 |
|
871 |
return retIdx; |
160 |
|
871 |
} |
161 |
|
|
|
162 |
|
|
void |
163 |
|
|
_hestOptFree(hestOpt *opt) { |
164 |
|
|
|
165 |
|
1742 |
opt->flag = (char *)airFree(opt->flag); |
166 |
|
871 |
opt->name = (char *)airFree(opt->name); |
167 |
|
871 |
opt->dflt = (char *)airFree(opt->dflt); |
168 |
|
871 |
opt->info = (char *)airFree(opt->info); |
169 |
|
871 |
return; |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
hestOpt * |
173 |
|
|
hestOptFree(hestOpt *opt) { |
174 |
|
|
int op, num; |
175 |
|
|
|
176 |
✗✓ |
262 |
if (!opt) |
177 |
|
|
return NULL; |
178 |
|
|
|
179 |
|
131 |
num = _hestNumOpts(opt); |
180 |
✓✗ |
131 |
if (opt[num].min) { |
181 |
|
|
/* we only try to free this if it looks like something we allocated */ |
182 |
✓✓ |
2004 |
for (op=0; op<num; op++) { |
183 |
|
871 |
_hestOptFree(opt+op); |
184 |
|
|
} |
185 |
|
131 |
free(opt); |
186 |
|
131 |
} |
187 |
|
131 |
return NULL; |
188 |
|
131 |
} |
189 |
|
|
|
190 |
|
|
int |
191 |
|
|
hestOptCheck(hestOpt *opt, char **errP) { |
192 |
|
32 |
char *err, me[]="hestOptCheck"; |
193 |
|
|
hestParm *parm; |
194 |
|
|
int big; |
195 |
|
|
|
196 |
|
16 |
big = _hestErrStrlen(opt, 0, NULL); |
197 |
✗✓ |
16 |
if (!( err = AIR_CALLOC(big, char) )) { |
198 |
|
|
fprintf(stderr, "%s PANIC: couldn't allocate error message " |
199 |
|
|
"buffer (size %d)\n", me, big); |
200 |
|
|
if (errP) |
201 |
|
|
*errP = NULL; |
202 |
|
|
return 1; |
203 |
|
|
} |
204 |
|
16 |
parm = hestParmNew(); |
205 |
✗✓✗✓
|
32 |
if (_hestPanic(opt, err, parm)) { |
206 |
|
|
/* problems */ |
207 |
✗✗ |
16 |
if (errP) { |
208 |
|
|
/* they did give a pointer address; they'll free it */ |
209 |
|
|
*errP = err; |
210 |
|
|
} |
211 |
|
|
else { |
212 |
|
|
/* they didn't give a pointer address; their loss */ |
213 |
|
|
free(err); |
214 |
|
|
} |
215 |
|
|
hestParmFree(parm); |
216 |
|
|
return 1; |
217 |
|
|
} |
218 |
|
|
/* else, no problems */ |
219 |
✓✗ |
16 |
if (errP) |
220 |
|
16 |
*errP = NULL; |
221 |
|
16 |
free(err); |
222 |
|
16 |
hestParmFree(parm); |
223 |
|
16 |
return 0; |
224 |
|
16 |
} |
225 |
|
|
|
226 |
|
|
|
227 |
|
|
/* |
228 |
|
|
** _hestIdent() |
229 |
|
|
** |
230 |
|
|
** how to identify an option in error and usage messages |
231 |
|
|
*/ |
232 |
|
|
char * |
233 |
|
|
_hestIdent(char *ident, hestOpt *opt, const hestParm *parm, int brief) { |
234 |
|
332 |
char copy[AIR_STRLEN_HUGE], *sep; |
235 |
|
|
|
236 |
✓✗✗✓
|
332 |
if (opt->flag && (sep = strchr(opt->flag, parm->multiFlagSep))) { |
237 |
|
|
strcpy(copy, opt->flag); |
238 |
|
|
sep = strchr(copy, parm->multiFlagSep); |
239 |
|
|
*sep = '\0'; |
240 |
|
|
if (brief) |
241 |
|
|
sprintf(ident, "-%s%c--%s option", copy, parm->multiFlagSep, sep+1); |
242 |
|
|
else |
243 |
|
|
sprintf(ident, "-%s option", copy); |
244 |
|
|
} |
245 |
|
|
else { |
246 |
✓✗ |
498 |
sprintf(ident, "%s%s%s option", |
247 |
|
|
opt->flag ? "\"-" : "<", |
248 |
|
|
opt->flag ? opt->flag : opt->name, |
249 |
|
|
opt->flag ? "\"" : ">"); |
250 |
|
|
} |
251 |
|
166 |
return ident; |
252 |
|
166 |
} |
253 |
|
|
|
254 |
|
|
int |
255 |
|
|
_hestMax(int max) { |
256 |
|
|
|
257 |
|
19010 |
if (-1 == max) { |
258 |
|
|
max = INT_MAX; |
259 |
|
|
} |
260 |
|
9505 |
return max; |
261 |
|
|
} |
262 |
|
|
|
263 |
|
|
int |
264 |
|
|
_hestKind(const hestOpt *opt) { |
265 |
|
|
int max; |
266 |
|
|
|
267 |
|
5018 |
max = _hestMax(opt->max); |
268 |
✗✓ |
2509 |
if (!( (int)opt->min <= max )) { /* HEY scrutinize casts */ |
269 |
|
|
/* invalid */ |
270 |
|
|
return -1; |
271 |
|
|
} |
272 |
|
|
|
273 |
✓✓ |
2509 |
if (0 == opt->min && 0 == max) { |
274 |
|
|
/* flag */ |
275 |
|
226 |
return 1; |
276 |
|
|
} |
277 |
|
|
|
278 |
✓✓ |
2283 |
if (1 == opt->min && 1 == max) { |
279 |
|
|
/* single fixed parameter */ |
280 |
|
1877 |
return 2; |
281 |
|
|
} |
282 |
|
|
|
283 |
✓✓✓✓
|
653 |
if (2 <= opt->min && 2 <= max && (int)opt->min == max) { /* HEY scrutinize casts */ |
284 |
|
|
/* multiple fixed parameters */ |
285 |
|
229 |
return 3; |
286 |
|
|
} |
287 |
|
|
|
288 |
✓✓ |
177 |
if (0 == opt->min && 1 == max) { |
289 |
|
|
/* single optional parameter */ |
290 |
|
3 |
return 4; |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
/* else multiple variable parameters */ |
294 |
|
174 |
return 5; |
295 |
|
2509 |
} |
296 |
|
|
|
297 |
|
|
void |
298 |
|
|
_hestPrintArgv(int argc, char **argv) { |
299 |
|
|
int a; |
300 |
|
|
|
301 |
|
|
printf("argc=%d : ", argc); |
302 |
|
|
for (a=0; a<argc; a++) { |
303 |
|
|
printf("%s ", argv[a]); |
304 |
|
|
} |
305 |
|
|
printf("\n"); |
306 |
|
|
} |
307 |
|
|
|
308 |
|
|
/* |
309 |
|
|
** _hestWhichFlag() |
310 |
|
|
** |
311 |
|
|
** given a string in "flag" (with the hypen prefix) finds which of |
312 |
|
|
** the flags in the given array of options matches that. Returns |
313 |
|
|
** the index of the matching option, or -1 if there is no match, |
314 |
|
|
** but returns -2 if the flag is the end-of-variable-parameter |
315 |
|
|
** marker (according to parm->varParamStopFlag) |
316 |
|
|
*/ |
317 |
|
|
int |
318 |
|
|
_hestWhichFlag(hestOpt *opt, char *flag, const hestParm *parm) { |
319 |
|
534 |
char buff[AIR_STRLEN_HUGE], copy[AIR_STRLEN_HUGE], *sep; |
320 |
|
|
int op, numOpts; |
321 |
|
|
|
322 |
|
267 |
numOpts = _hestNumOpts(opt); |
323 |
✗✓ |
267 |
if (parm->verbosity) |
324 |
|
|
printf("_hestWhichFlag: flag = %s, numOpts = %d\n", flag, numOpts); |
325 |
✓✓✓✓
|
6672 |
for (op=0; op<numOpts; op++) { |
326 |
✗✓ |
4290 |
if (parm->verbosity) |
327 |
|
|
printf("_hestWhichFlag: op = %d\n", op); |
328 |
✓✗ |
2066 |
if (!opt[op].flag) |
329 |
|
|
continue; |
330 |
✗✓ |
2066 |
if (strchr(opt[op].flag, parm->multiFlagSep) ) { |
331 |
|
|
strcpy(copy, opt[op].flag); |
332 |
|
|
sep = strchr(copy, parm->multiFlagSep); |
333 |
|
|
*sep = '\0'; |
334 |
|
|
/* first try the short version */ |
335 |
|
|
sprintf(buff, "-%s", copy); |
336 |
|
|
if (!strcmp(flag, buff)) |
337 |
|
|
return op; |
338 |
|
|
/* then try the long version */ |
339 |
|
|
sprintf(buff, "--%s", sep+1); |
340 |
|
|
if (!strcmp(flag, buff)) |
341 |
|
|
return op; |
342 |
|
|
} |
343 |
|
|
else { |
344 |
|
|
/* flag has only the short version */ |
345 |
|
2066 |
sprintf(buff, "-%s", opt[op].flag); |
346 |
✓✓ |
2066 |
if (!strcmp(flag, buff)) |
347 |
|
109 |
return op; |
348 |
|
|
} |
349 |
|
|
} |
350 |
✗✓ |
158 |
if (parm->verbosity) |
351 |
|
|
printf("_hestWhichFlag: numOpts = %d\n", numOpts); |
352 |
✓✗ |
158 |
if (parm->varParamStopFlag) { |
353 |
|
158 |
sprintf(buff, "-%c", parm->varParamStopFlag); |
354 |
✗✓ |
158 |
if (parm->verbosity) |
355 |
|
|
printf("_hestWhichFlag: flag = %s, buff = %s\n", flag, buff); |
356 |
✗✓ |
158 |
if (!strcmp(flag, buff)) |
357 |
|
|
return -2; |
358 |
|
|
} |
359 |
✗✓ |
158 |
if (parm->verbosity) |
360 |
|
|
printf("_hestWhichFlag: numOpts = %d\n", numOpts); |
361 |
|
158 |
return -1; |
362 |
|
267 |
} |
363 |
|
|
|
364 |
|
|
|
365 |
|
|
/* |
366 |
|
|
** _hestCase() |
367 |
|
|
** |
368 |
|
|
** helps figure out logic of interpreting parameters and defaults |
369 |
|
|
** for kind 4 and kind 5 options. |
370 |
|
|
*/ |
371 |
|
|
int |
372 |
|
|
_hestCase(hestOpt *opt, int *udflt, unsigned int *nprm, int *appr, int op) { |
373 |
|
|
|
374 |
✓✗✗✓
|
24 |
if (opt[op].flag && !appr[op]) { |
375 |
|
|
return 0; |
376 |
|
|
} |
377 |
✗✓✗✗ ✗✓ |
16 |
else if ( (4 == opt[op].kind && udflt[op]) || |
378 |
✓✗ |
16 |
(5 == opt[op].kind && !nprm[op]) ) { |
379 |
|
|
return 1; |
380 |
|
|
} |
381 |
|
|
else { |
382 |
|
8 |
return 2; |
383 |
|
|
} |
384 |
|
8 |
} |
385 |
|
|
|
386 |
|
|
/* |
387 |
|
|
** _hestExtract() |
388 |
|
|
** |
389 |
|
|
** takes "pnum" parameters, starting at "base", out of the |
390 |
|
|
** given argv, and puts them into a string WHICH THIS FUNCTION |
391 |
|
|
** ALLOCATES, and also adjusts the argc value given as "*argcP". |
392 |
|
|
*/ |
393 |
|
|
char * |
394 |
|
|
_hestExtract(int *argcP, char **argv, unsigned int base, unsigned int pnum) { |
395 |
|
|
unsigned int len, pidx; |
396 |
|
|
char *ret; |
397 |
|
|
|
398 |
✓✓ |
412 |
if (!pnum) |
399 |
|
3 |
return NULL; |
400 |
|
|
|
401 |
|
|
len = 0; |
402 |
✓✓ |
928 |
for (pidx=0; pidx<pnum; pidx++) { |
403 |
✗✓ |
261 |
if (base+pidx==AIR_UINT(*argcP)) { |
404 |
|
|
return NULL; |
405 |
|
|
} |
406 |
|
261 |
len += AIR_UINT(strlen(argv[base+pidx])); |
407 |
✗✓ |
261 |
if (strstr(argv[base+pidx], " ")) { |
408 |
|
|
len += 2; |
409 |
|
|
} |
410 |
|
|
} |
411 |
|
203 |
len += pnum; |
412 |
|
203 |
ret = AIR_CALLOC(len, char); |
413 |
|
203 |
strcpy(ret, ""); |
414 |
✓✓ |
928 |
for (pidx=0; pidx<pnum; pidx++) { |
415 |
|
|
/* if a single element of argv has spaces in it, someone went |
416 |
|
|
to the trouble of putting it in quotes, and we perpetuate |
417 |
|
|
the favor by quoting it when we concatenate all the argv |
418 |
|
|
elements together, so that airParseStrS will recover it as a |
419 |
|
|
single string again */ |
420 |
✗✓ |
261 |
if (strstr(argv[base+pidx], " ")) { |
421 |
|
|
strcat(ret, "\""); |
422 |
|
|
} |
423 |
|
|
/* HEY: if there is a '\"' character in this string, quoted or |
424 |
|
|
not, its going to totally confuse later parsing */ |
425 |
|
261 |
strcat(ret, argv[base+pidx]); |
426 |
✗✓ |
261 |
if (strstr(argv[base+pidx], " ")) { |
427 |
|
|
strcat(ret, "\""); |
428 |
|
|
} |
429 |
✓✓ |
261 |
if (pidx < pnum-1) |
430 |
|
58 |
strcat(ret, " "); |
431 |
|
|
} |
432 |
✓✓ |
3178 |
for (pidx=base+pnum; pidx<=AIR_UINT(*argcP); pidx++) { |
433 |
|
1386 |
argv[pidx-pnum] = argv[pidx]; |
434 |
|
|
} |
435 |
|
203 |
*argcP -= pnum; |
436 |
|
203 |
return ret; |
437 |
|
206 |
} |
438 |
|
|
|
439 |
|
|
int |
440 |
|
|
_hestNumOpts(const hestOpt *opt) { |
441 |
|
|
int num = 0; |
442 |
|
|
|
443 |
✓✓✓✓ ✓✓ |
47245 |
while (opt[num].flag || opt[num].name || opt[num].type) { |
444 |
|
12083 |
num++; |
445 |
|
|
} |
446 |
|
1790 |
return num; |
447 |
|
|
} |
448 |
|
|
|