GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/hest/usage.c Lines: 201 223 90.1 %
Date: 2017-05-26 Branches: 167 204 81.9 %

Line Branch Exec Source
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
27
/*
28
** don't ask
29
*/
30
void
31
_hestSetBuff(char *B, const hestOpt *O, const hestParm *P,
32
             int showshort, int showlong) {
33
4894
  char copy[AIR_STRLEN_HUGE], *sep;
34
  int max; unsigned int len;
35
36
2447
  max = _hestMax(O->max);
37
2447
  if (O->flag) {
38
2351
    strcpy(copy, O->flag);
39
2351
    if ((sep = strchr(copy, P->multiFlagSep))) {
40
1328
      *sep = 0;
41
1328
      if (showshort) {
42
996
        strcat(B, "-");
43
996
        strcat(B, copy);
44
996
      }
45
1328
      if (showlong) {
46
664
        if (showshort) {
47
332
          len = AIR_UINT(strlen(B));
48
332
          B[len] = P->multiFlagSep;
49
332
          B[len+1] = '\0';
50
332
        }
51
664
        strcat(B, "--");
52
664
        strcat(B, sep+1);
53
664
      }
54
    } else {
55
1023
      strcat(B, "-");
56
1023
      strcat(B, O->flag);
57
    }
58
2351
    if (O->min || max) {
59
2146
      strcat(B, "\t");
60
2146
    }
61
  }
62
2447
  if (!O->min && max) {
63
28
    strcat(B, "[");
64
28
  }
65
2447
  if (O->min || max) {
66
2242
    strcat(B, "<");
67
2242
    strcat(B, O->name);
68
2242
    if ((int)(O->min) < max && max > 1) { /* HEY scrutinize casts */
69
188
      strcat(B, "\t...");
70
188
    }
71
2242
    strcat(B, ">");
72
2242
  }
73
2447
  if (!O->min && max) {
74
28
    strcat(B, "]");
75
28
  }
76
2447
}
77
78
/* early version of _hestSetBuff() function */
79
#define SETBUFF(B, O) \
80
  strcat(B, O.flag ? "-" : ""), \
81
  strcat(B, O.flag ? O.flag : ""), \
82
  strcat(B, O.flag && (O.min || _hestMax(O.max)) ? "\t" : ""), \
83
  strcat(B, !O.min && _hestMax(O.max) ? "[" : ""), \
84
  strcat(B, O.min || _hestMax(O.max) ? "<" : ""), \
85
  strcat(B, O.min || _hestMax(O.max) ? O.name : ""), \
86
  strcat(B, (O.min < _hestMax(O.max) && (_hestMax(O.max) > 1)) ? " ...": ""), \
87
  strcat(B, O.min || _hestMax(O.max) ? ">" : ""), \
88
  strcat(B, !O.min && _hestMax(O.max) ? "]" : "");
89
90
/*
91
** _hestPrintStr()
92
**
93
** not a useful function.  Do not use.
94
*/
95
void
96
_hestPrintStr(FILE *f, unsigned int indent, unsigned int already,
97
              unsigned int width, const char *_str, int bslash) {
98
2288
  char *str, *ws, *last;
99
  int newed=AIR_FALSE; unsigned int wrd, nwrd, ii, pos;
100
101
1144
  str = airStrdup(_str);
102
1144
  nwrd = airStrntok(str, " ");
103
  pos = already;
104
47534
  for (wrd=0; wrd<nwrd; wrd++) {
105
    /* we used airStrtok() to delimit words on spaces ... */
106
22623
    ws = airStrtok(!wrd ? str : NULL, " ", &last);
107
    /* ... but then convert tabs to spaces */
108
22623
    airStrtrans(ws, '\t', ' ');
109
22623
    if (pos + 1 + AIR_UINT(strlen(ws)) <= width - !!bslash) {
110
      /* if this word would still fit on the current line */
111
40654
      if (wrd && !newed) fprintf(f, " ");
112
21059
      fprintf(f, "%s", ws);
113
21059
      pos += 1 + AIR_UINT(strlen(ws));
114
      newed = AIR_FALSE;
115
21059
    } else {
116
      /* else we start a new line and print the indent */
117
1564
      if (bslash) {
118
152
        fprintf(f, " \\");
119
152
      }
120
1564
      fprintf(f, "\n");
121
42976
      for (ii=0; ii<indent; ii++) {
122
19924
        fprintf(f, " ");
123
      }
124
1564
      fprintf(f, "%s", ws);
125
1564
      pos = indent + AIR_UINT(strlen(ws));
126
    }
127
    /* if the last character of the word was a newline, then indent */
128
22623
    if ('\n' == ws[strlen(ws)-1]) {
129
8316
      for (ii=0; ii<indent; ii++) {
130
3828
        fprintf(f, " ");
131
      }
132
      pos = indent;
133
      newed = AIR_TRUE;
134
330
    } else {
135
      newed = AIR_FALSE;
136
    }
137
  }
138
1144
  fprintf(f, "\n");
139
1144
  free(str);
140
1144
}
141
142
/*
143
******** hestMinNumArgs
144
**
145
** The idea is that this helps quickly determine if the options given
146
** on the command line are insufficient, in order to produce general
147
** usage information instead of some specific parse error.
148
**
149
** Because hest is strictly agnostic with respect to how many command-line
150
** arguments actually constitute the command itself ("rmdir": one argument,
151
** "cvs checkout": two arguments), it only concerns itself with the
152
** command-line arguments following the command.
153
**
154
** Thus, hestMinMinArgs() returns the minimum number of command-line
155
** arguments (following the command) that could be valid.  If your
156
** command is only one argument (like "rmdir"), then you might use
157
** the true argc passed by the OS to main() as such:
158
**
159
**   if (argc-1 < hestMinNumArgs(opt)) {
160
**     ... usage ...
161
**   }
162
**
163
** But if your command is two arguments (like "cvs checkout"):
164
**
165
**   if (argc-2 < hestMinNumArgs(opt)) {
166
**     ... usage ...
167
**   }
168
**
169
** HOWEVER! don't forget the response files can complicate all this:
170
** in one argument a response file can provide information for any
171
** number of arguments, and the argc itself is kind of meaningless.
172
** The code examples above only really apply when
173
** hparm->respFileEnable is false.  For example, in unrrdu (private.h)
174
** we find:
175
**
176
**   if ( (hparm->respFileEnable && !argc) ||
177
**        (!hparm->respFileEnable && argc < hestMinNumArgs(opt)) ) {
178
**     ... usage ...
179
**   }
180
**
181
*/
182
int
183
hestMinNumArgs(hestOpt *opt) {
184
  hestParm *parm;
185
  int i, count, numOpts;
186
187
  parm = hestParmNew();
188
  if (_hestPanic(opt, NULL, parm)) {
189
    hestParmFree(parm);
190
    return _hestMax(-1);
191
  }
192
  count = 0;
193
  numOpts = _hestNumOpts(opt);
194
  for (i=0; i<numOpts; i++) {
195
    if (!opt[i].dflt) {
196
      count += opt[i].min;
197
      if (!(0 == opt[i].min && 0 == opt[i].max)) {
198
        count += !!opt[i].flag;
199
      }
200
    }
201
  }
202
  hestParmFree(parm);
203
  return count;
204
}
205
206
void
207
hestInfo(FILE *file, const char *argv0, const char *info,
208
         const hestParm *_parm) {
209
  hestParm *parm;
210
211
324
  parm = _parm ? NULL : hestParmNew();
212
  /* how to const-correctly use parm or _parm in an expression */
213
#define PARM (_parm ? _parm : parm)
214
215
108
  if (info) {
216
108
    if (argv0) {
217
108
      fprintf(file, "\n%s: ", argv0);
218
216
      _hestPrintStr(file, 0, AIR_UINT(strlen(argv0)) + 2,
219
108
                    PARM->columns, info, AIR_FALSE);
220
108
    } else {
221
      fprintf(file, "ERROR: hestInfo got NULL argv0\n");
222
    }
223
  }
224
108
  if (parm) {
225
    hestParmFree(parm);
226
  }
227
108
}
228
229
void
230
hestUsage(FILE *f, hestOpt *opt, const char *argv0,
231
          const hestParm *_parm) {
232
  int i, numOpts;
233
214
  char buff[2*AIR_STRLEN_HUGE], tmpS[AIR_STRLEN_HUGE];
234
  hestParm *parm;
235
236
214
  parm = _parm ? NULL : hestParmNew();
237
238
107
  if (_hestPanic(opt, NULL, PARM)) {
239
    /* we can't continue; the opt array is botched */
240
    if (parm) {
241
      hestParmFree(parm);
242
    }
243
    return;
244
  }
245
246
107
  numOpts = _hestNumOpts(opt);
247
107
  fprintf(f, "\n");
248
107
  strcpy(buff, "Usage: ");
249
107
  strcat(buff, argv0 ? argv0 : "");
250
107
  if (PARM->respFileEnable) {
251
93
    sprintf(tmpS, " [%cfile\t...]", PARM->respFileFlag);
252
93
    strcat(buff, tmpS);
253
93
  }
254
1624
  for (i=0; i<numOpts; i++) {
255
705
    strcat(buff, " ");
256

1955
    if (1 == opt[i].kind || (opt[i].flag && opt[i].dflt))
257
563
      strcat(buff, "[");
258
705
    _hestSetBuff(buff, opt + i, PARM, AIR_TRUE, AIR_TRUE);
259

1955
    if (1 == opt[i].kind || (opt[i].flag && opt[i].dflt))
260
563
      strcat(buff, "]");
261
  }
262
263
107
  _hestPrintStr(f, AIR_UINT(strlen("Usage: ")), 0,
264
107
                PARM->columns, buff, AIR_TRUE);
265
107
  if (parm) {
266
    hestParmFree(parm);
267
  }
268
107
  return;
269
107
}
270
271
void
272
hestGlossary(FILE *f, hestOpt *opt, const hestParm *_parm) {
273
  int i, j, maxlen, numOpts; unsigned int len;
274
214
  char buff[2*AIR_STRLEN_HUGE], tmpS[AIR_STRLEN_HUGE];
275
  hestParm *parm;
276
277
214
  parm = _parm ? NULL : hestParmNew();
278
279
107
  if (_hestPanic(opt, NULL, PARM)) {
280
    /* we can't continue; the opt array is botched */
281
    if (parm) {
282
      hestParmFree(parm);
283
    }
284
    return;
285
  }
286
287
107
  numOpts = _hestNumOpts(opt);
288
289
  maxlen = 0;
290
107
  if (numOpts) {
291
107
    fprintf(f, "\n");
292
107
  }
293
1624
  for (i=0; i<numOpts; i++) {
294
705
    strcpy(buff, "");
295
705
    _hestSetBuff(buff, opt + i, PARM, AIR_TRUE, AIR_FALSE);
296
2115
    maxlen = AIR_MAX((int)strlen(buff), maxlen);
297
  }
298
107
  if (PARM->respFileEnable) {
299
93
    sprintf(buff, "%cfile ...", PARM->respFileFlag);
300
93
    len = AIR_UINT(strlen(buff));
301
968
    for (j=len; j<maxlen; j++) {
302
391
      fprintf(f, " ");
303
    }
304
93
    fprintf(f, "%s = ", buff);
305
93
    strcpy(buff, "response file(s) containing command-line arguments");
306
93
    _hestPrintStr(f, maxlen + 3, maxlen + 3, PARM->columns, buff, AIR_FALSE);
307
93
  }
308
1624
  for (i=0; i<numOpts; i++) {
309
705
    strcpy(buff, "");
310
705
    _hestSetBuff(buff, opt + i, PARM, AIR_TRUE, AIR_FALSE);
311
705
    airOneLinify(buff);
312
705
    len = AIR_UINT(strlen(buff));
313
7964
    for (j=len; j<maxlen; j++) {
314
3277
      fprintf(f, " ");
315
    }
316
705
    fprintf(f, "%s", buff);
317
705
    strcpy(buff, "");
318
#if 1
319

1378
    if (opt[i].flag && strchr(opt[i].flag, PARM->multiFlagSep)) {
320
      /* there is a long-form flag as well as short */
321
332
      _hestSetBuff(buff, opt + i, PARM, AIR_FALSE, AIR_TRUE);
322
332
      strcat(buff, " = ");
323
332
      fprintf(f, " , ");
324
332
    } else {
325
      /* there is only a short-form flag */
326
373
      fprintf(f, " = ");
327
    }
328
#else
329
    fprintf(f, " = ");
330
#endif
331
705
    if (opt[i].info) {
332
705
      strcat(buff, opt[i].info);
333
705
    }
334

925
    if ((opt[i].min || _hestMax(opt[i].max))
335
712
        && (!( 2 == opt[i].kind
336
1188
               && airTypeEnum == opt[i].type
337
588
               && PARM->elideSingleEnumType ))
338
682
        && (!( 2 == opt[i].kind
339
1188
               && airTypeOther == opt[i].type
340
696
               && PARM->elideSingleOtherType ))
341
        ) {
342
      /* if there are newlines in the info, then we want to clarify the
343
         type by printing it on its own line */
344

1282
      if (opt[i].info && strchr(opt[i].info, '\n')) {
345
32
        strcat(buff, "\n ");
346
32
      }
347
      else {
348
609
        strcat(buff, " ");
349
      }
350
641
      strcat(buff, "(");
351

648
      if (opt[i].min == 0 && _hestMax(opt[i].max) == 1) {
352
1
        strcat(buff, "optional\t");
353
1
      }
354
      else {
355

1230
        if ((int)opt[i].min == _hestMax(opt[i].max) && _hestMax(opt[i].max) > 1) { /* HEY scrutinize casts */
356
43
          sprintf(tmpS, "%d\t", _hestMax(opt[i].max));
357
43
          strcat(buff, tmpS);
358
43
        }
359
597
        else if ((int)opt[i].min < _hestMax(opt[i].max)) { /* HEY scrutinize casts */
360

100
          if (-1 == opt[i].max) {
361
98
            sprintf(tmpS, "%d\tor\tmore\t", opt[i].min);
362
48
          }
363
          else {
364
2
            sprintf(tmpS, "%d..%d\t", opt[i].min, _hestMax(opt[i].max));
365
          }
366
50
          strcat(buff, tmpS);
367
50
        }
368
      }
369


3365
      sprintf(tmpS, "%s%s",
370
              (airTypeEnum == opt[i].type
371
               ? opt[i].enm->name
372
               : (airTypeOther == opt[i].type
373
                  ? opt[i].CB->type
374
                  : airTypeStr[opt[i].type])),
375
              (_hestMax(opt[i].max) > 1
376
               ? (airTypeOther == opt[i].type
377
                  && 'y' == opt[i].CB->type[airStrlen(opt[i].CB->type)-1]
378
                  && PARM->cleverPluralizeOtherY
379
                  ? "\bies"
380
                  : "s")
381
               : ""));
382
641
      strcat(buff, tmpS);
383
641
      strcat(buff, ")");
384
641
    }
385
    /*
386
    fprintf(stderr, "!%s: PARM->elideSingleOtherDefault = %d\n",
387
            "hestGlossary", PARM->elideSingleOtherDefault);
388
    */
389
705
    if (opt[i].dflt
390

1211
        && (opt[i].min || _hestMax(opt[i].max))
391
506
        && (!( 2 == opt[i].kind
392

1350
               && (airTypeFloat == opt[i].type || airTypeDouble == opt[i].type)
393
504
               && !AIR_EXISTS(airAtod(opt[i].dflt))
394
124
               && PARM->elideSingleNonExistFloatDefault ))
395

992
        && (!( (3 == opt[i].kind || 5 == opt[i].kind)
396

563
               && (airTypeFloat == opt[i].type || airTypeDouble == opt[i].type)
397
73
               && !AIR_EXISTS(airAtod(opt[i].dflt))
398
39
               && PARM->elideMultipleNonExistFloatDefault ))
399
508
        && (!( 2 == opt[i].kind
400
946
               && airTypeOther == opt[i].type
401
565
               && PARM->elideSingleOtherDefault ))
402
575
        && (!( 2 == opt[i].kind
403
862
               && airTypeString == opt[i].type
404
535
               && PARM->elideSingleEmptyStringDefault
405
154
               && 0 == airStrlen(opt[i].dflt) ))
406

904
        && (!( (3 == opt[i].kind || 5 == opt[i].kind)
407
476
               && airTypeString == opt[i].type
408
61
               && PARM->elideMultipleEmptyStringDefault
409
10
               && 0 == airStrlen(opt[i].dflt) ))
410
        ) {
411
      /* if there are newlines in the info, then we want to clarify the
412
         default by printing it on its own line */
413

910
      if (opt[i].info && strchr(opt[i].info, '\n')) {
414
10
        strcat(buff, "\n ");
415
10
      }
416
      else {
417
445
        strcat(buff, "; ");
418
      }
419
455
      strcat(buff, "default:\t");
420
455
      strcpy(tmpS, opt[i].dflt);
421
455
      airStrtrans(tmpS, ' ', '\t');
422
455
      strcat(buff, "\"");
423
455
      strcat(buff, tmpS);
424
455
      strcat(buff, "\"");
425
455
    }
426
705
    _hestPrintStr(f, maxlen + 3, maxlen + 3, PARM->columns, buff, AIR_FALSE);
427
  }
428
107
  if (parm) {
429
    hestParmFree(parm);
430
  }
431
432
107
  return;
433
107
}
434
435
#undef PARM
436