GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/air/enum.c Lines: 125 211 59.2 %
Date: 2017-05-26 Branches: 93 184 50.5 %

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 "air.h"
25
26
/*
27
** Until Teem has its own printf implementation, this will have to do;
28
** it is imperfect because these are not functionally identical.
29
*/
30
#if defined(WIN32) || defined(_WIN32)
31
#  define snprintf _snprintf
32
#endif
33
34
/*
35
******** airEnumUnknown
36
**
37
** return the value representing "unknown" in an enum
38
*/
39
int
40
airEnumUnknown(const airEnum *enm) {
41
42

6
  if (enm && enm->val) {
43
    return enm->val[0];
44
  } else {
45
2
    return 0;
46
  }
47
2
}
48
49
/*
50
** _airEnumIndex()
51
**
52
** given an enum "enm" and value "val", return the index into enm->str[]
53
** and enm->desc[] which correspond to that value.  To be safe, when
54
** given an invalid enum value, we return zero.
55
*/
56
static unsigned int
57
_airEnumIndex(const airEnum *enm, int val) {
58
  unsigned int ii, ret;
59
60
  ret = 0;
61
119774
  if (enm->val) {
62
200188
    for (ii=1; ii<=enm->M; ii++) {
63
100094
      if (val == enm->val[ii]) {
64
        ret = ii;
65
7169
        break;
66
      }
67
    }
68
  } else {
69
    unsigned int uval;
70
    uval = AIR_UINT(val);
71
158153
    ret = (0 <= val && uval <= enm->M) ? uval : 0;
72
  }
73
59887
  return ret;
74
}
75
76
/*
77
** returns non-zero if there is an error: given "val" is *not*
78
** a valid value of the airEnum "enm"
79
*/
80
int
81
airEnumValCheck(const airEnum *enm, int val) {
82
83
117146
  return (0 == _airEnumIndex(enm, val));
84
}
85
86
const char *
87
airEnumStr(const airEnum *enm, int val) {
88
  unsigned int idx;
89
90
2628
  idx = _airEnumIndex(enm, val);
91
1314
  return enm->str[idx];
92
}
93
94
const char *
95
airEnumDesc(const airEnum *enm, int val) {
96
  unsigned int idx;
97
98
  idx = _airEnumIndex(enm, val);
99
  return enm->desc[idx];
100
}
101
102
int
103
airEnumVal(const airEnum *enm, const char *str) {
104
2110
  char *strCpy, test[AIR_STRLEN_SMALL];
105
  unsigned int ii;
106
107
1055
  if (!str) {
108
    return airEnumUnknown(enm);
109
  }
110
111
1055
  strCpy = airStrdup(str);
112
1055
  if (!enm->sense) {
113
1055
    airToLower(strCpy);
114
1055
  }
115
116
1055
  if (enm->strEqv) {
117
    /* want strlen and not airStrlen here because the strEqv array
118
       should be terminated by a non-null empty string */
119
113932
    for (ii=0; strlen(enm->strEqv[ii]); ii++) {
120
56966
      airStrcpy(test, AIR_STRLEN_SMALL, enm->strEqv[ii]);
121
56966
      if (!enm->sense) {
122
56966
        airToLower(test);
123
56966
      }
124
56966
      if (!strcmp(test, strCpy)) {
125
972
        free(strCpy);
126
972
        return enm->valEqv[ii];
127
      }
128
    }
129
  } else {
130
    /* enm->strEqv NULL */
131
294
    for (ii=1; ii<=enm->M; ii++) {
132
145
      airStrcpy(test, AIR_STRLEN_SMALL, enm->str[ii]);
133
145
      if (!enm->sense) {
134
145
        airToLower(test);
135
145
      }
136
145
      if (!strcmp(test, strCpy)) {
137
81
        free(strCpy);
138
185
        return enm->val ? enm->val[ii] : (int)ii; /* HEY scrutinize cast */
139
      }
140
    }
141
  }
142
143
  /* else we never matched a string */
144
2
  free(strCpy);
145
2
  return airEnumUnknown(enm);
146
1055
}
147
148
/*
149
******** airEnumFmtDesc()
150
**
151
** Formats a description line for one element "val" of airEnum "enm",
152
** and puts the result in a NEWLY ALLOCATED string which is the return
153
** of this function.  The formatting is done via sprintf(), as governed
154
** by "fmt", which should contain to "%s" conversion sequences, the
155
** first for the string version "val", and the second for the
156
** description If "canon", then the canonical string representation
157
** will be used (the one in enm->str[]), otherwise the shortest string
158
** representation will be used (which differs from the canonical one
159
** when there is a strEqv[]/valEqv[] pair defining a shorter string)
160
*/
161
char *
162
airEnumFmtDesc(const airEnum *enm, int val, int canon, const char *fmt) {
163
  const char *desc;
164
  char *buff, ident[AIR_STRLEN_SMALL];
165
  const char *_ident;
166
  int i;
167
  size_t len;
168
169
  if (!(enm && enm->desc && fmt)) {
170
    return airStrdup("(airEnumDesc: invalid args)");
171
  }
172
  if (airEnumValCheck(enm, val)) {
173
    val = airEnumUnknown(enm);
174
  }
175
  _ident = airEnumStr(enm, val);
176
  if (!canon && enm->strEqv) {
177
    len = airStrlen(_ident);
178
    for (i=0; airStrlen(enm->strEqv[i]); i++) {
179
      if (val != enm->valEqv[i]) {
180
        /* this isn't a string representing the value we care about */
181
        continue;
182
      }
183
      if (airStrlen(enm->strEqv[i]) < len) {
184
        /* this one is shorter */
185
        len = airStrlen(enm->strEqv[i]);
186
        _ident = enm->strEqv[i];
187
      }
188
    }
189
  }
190
  airStrcpy(ident, AIR_STRLEN_SMALL, _ident);
191
  if (!enm->sense) {
192
    airToLower(ident);
193
  }
194
  desc = enm->desc[_airEnumIndex(enm, val)];
195
  buff = AIR_CALLOC(airStrlen(fmt) + airStrlen(ident) +
196
                    airStrlen(desc) + 1, char);
197
  if (buff) {
198
    sprintf(buff, fmt, ident, desc);
199
  }
200
  return buff;
201
}
202
203
static void
204
_enumPrintVal(FILE *file, const airEnum *enm, int ii) {
205
206
1702
  if (enm->desc) {
207
647
    fprintf(file, "desc: %s\n", enm->desc[ii]);
208
647
  }
209
851
  if (enm->strEqv) {
210
    unsigned int jj;
211
660
    fprintf(file, "eqv:"); fflush(file);
212
    jj = 0;
213
206874
    while (airStrlen(enm->strEqv[jj])) {
214

296989
      if (enm->valEqv[jj] == (enm->val
215
91435
                              ? enm->val[ii]
216
                              : ii)) {
217
1388
        fprintf(file, " \"%s\"", enm->strEqv[jj]);
218
1388
      }
219
102777
      jj++;
220
    }
221
660
    fprintf(file, "\n");
222
660
  }
223
851
}
224
225
void
226
airEnumPrint(FILE *file, const airEnum *enm) {
227
  int ii; /* this should arguable be unsigned int, but
228
             airEnum values were kept as "int", even after
229
             the great unsigned conversion */
230
231
124
  if (!(file && enm)) {
232
    return;
233
  }
234
235
62
  if (airStrlen(enm->name)) {
236
62
    fprintf(file, "airEnum \"%s\":\n", enm->name);
237
62
  } else {
238
    fprintf(file, "airEnum (NO NAME!):\n");
239
  }
240
62
  fprintf(file, "(%s case sensitive)\n", (enm->sense ? "yes, is" : "is not"));
241

124
  if (enm->val) {
242
73
    fprintf(file, "Values (%u valid) given explicitly\n", enm->M);
243
11
    fprintf(file, "--- (0) %d: \"%s\"\n", enm->val[0], enm->str[0]);
244
772
    for (ii=1; ii<=AIR_CAST(int, enm->M); ii++) {
245
375
      fprintf(file, "--- (%d) %d: \"%s\" == \"%s\"\n", ii,
246
375
              enm->val[ii], enm->str[ii],
247
375
              airEnumStr(enm, enm->val[ii]));
248
375
      _enumPrintVal(file, enm, ii);
249
    }
250
  } else {
251
    /* enm->val NULL */
252
51
    fprintf(file, "Values implicit; [1,%u] valid\n", enm->M);
253
51
    fprintf(file, "--- 0: \"%s\"\n", enm->str[0]);
254
1054
    for (ii=1; ii<=AIR_CAST(int, enm->M); ii++) {
255
952
      fprintf(file, "--- %d: %s == %s\n", ii, enm->str[ii],
256
476
              airEnumStr(enm, ii));
257
476
      _enumPrintVal(file, enm, ii);
258
    }
259
  }
260
62
  return;
261
62
}
262
263
/* ---- BEGIN non-NrrdIO */
264
/*
265
******** airEnumCheck
266
**
267
** tries various things to check on the construction and internal
268
** consistency of an airEnum; returns 1 if there is a problem, and 0
269
** if all is well.  we're in air, so there's no biff, but we sprintf a
270
** description of the error into "err", if given
271
**
272
** The requirement that the strings have strlen <= AIR_STRLEN_SMALL-1
273
** is a reflection of the cheap implementation of the airEnum
274
** functions in this file, rather than an actual restriction on what an
275
** airEnum could be.
276
*/
277
int
278
airEnumCheck(char err[AIR_STRLEN_LARGE], const airEnum *enm) {
279
  static const char me[]="airEnumCheck";
280
  unsigned int ii, jj;
281
  size_t slen, ASL;
282
283
  ASL = AIR_STRLEN_LARGE;
284
124
  if (!enm) {
285
    if (err) {
286
      snprintf(err, ASL, "%s: got NULL enm", me);
287
    }
288
    return 1;
289
  }
290
62
  if (!enm->name) {
291
    if (err) {
292
      snprintf(err, ASL, "%s: enm->name NULL", me);
293
    }
294
    return 1;
295
  }
296
62
  if (0 == enm->M) {
297
    if (err) {
298
      snprintf(err, ASL, "%s(%s): enm->M zero; no values in enum",
299
               me, enm->name);
300
    }
301
    return 1;
302
  }
303
1950
  for (ii=0; ii<=enm->M; ii++) {
304
913
    if (!enm->str[ii]) {
305
      if (err) {
306
        snprintf(err, ASL, "%s(%s): enm->str[%u] NULL", me, enm->name, ii);
307
      }
308
      return 1;
309
    }
310
913
    slen = airStrlen(enm->str[ii]);
311
913
    if (!( slen >= 1 && slen <= AIR_STRLEN_SMALL-1 )) {
312
      if (err) {
313
        char stmp[AIR_STRLEN_SMALL];
314
        snprintf(err, ASL, "%s(%s): strlen(enm->str[%u] \"%s\") "
315
                 "%s not in range [1,%u]", me,
316
                 enm->name, ii, enm->str[ii],
317
                 airSprintSize_t(stmp, slen), AIR_STRLEN_SMALL-1);
318
      }
319
      return 1;
320
    }
321
    /* make sure there are no duplicates among the strings,
322
       including remapping the case in case of case insensitivity */
323
58144
    for (jj=ii+1; jj<=enm->M; jj++) {
324
28159
      if (!enm->str[jj]) {
325
        if (err) {
326
          snprintf(err, ASL, "%s(%s): enm->str[%u] NULL", me, enm->name, jj);
327
        }
328
        return 1;
329
      }
330
28159
      if (!strcmp(enm->str[ii], enm->str[jj])) {
331
        if (err) {
332
          snprintf(err, ASL, "%s(%s): str[%d] and [%u] both \"%s\"",
333
                   me, enm->name, ii, jj, enm->str[jj]);
334
        }
335
        return 1;
336
      }
337
28159
      if (!enm->sense) {
338
28159
        char bb1[AIR_STRLEN_SMALL], bb2[AIR_STRLEN_SMALL];
339
28159
        strcpy(bb1, enm->str[ii]);
340
28159
        airToLower(bb1);
341
28159
        strcpy(bb2, enm->str[jj]);
342
28159
        airToLower(bb2);
343
28159
        if (!strcmp(bb1, bb2)) {
344
          if (err) {
345
            snprintf(err, ASL, "%s(%s): after case-lowering, "
346
                     "str[%d] and [%u] both \"%s\"",
347
                     me, enm->name, ii, jj, bb1);
348
          }
349
          return 1;
350
        }
351
56318
      }
352
    }
353
  }
354
62
  if (enm->val) {
355
772
    for (ii=1; ii<=enm->M; ii++) {
356
375
      if (enm->val[0] == enm->val[ii]) {
357
        if (err) {
358
          snprintf(err, ASL, "%s(%s): val[%u] %u same as "
359
                   "unknown/invalid val[0] %u",
360
                   me, enm->name, ii, enm->val[ii], enm->val[0]);
361
        }
362
        return 1;
363
      }
364
47780
      for (jj=ii+1; jj<=enm->M; jj++) {
365
23515
        if (enm->val[jj] == enm->val[ii]) {
366
          if (err) {
367
            snprintf(err, ASL, "%s(%s): val[%u] %u same as val[%u] %u", me,
368
                     enm->name, ii, enm->val[ii], jj, enm->val[jj]);
369
          }
370
          return 1;
371
        }
372
      }
373
    }
374
  }
375
62
  if (enm->desc) {
376
1470
    for (ii=0; ii<=enm->M; ii++) {
377
691
      if (!enm->desc[ii]) {
378
        if (err) {
379
          snprintf(err, ASL, "%s(%s): enm->desc[%u] NULL", me, enm->name, ii);
380
        }
381
        return 1;
382
      }
383
      /* we don't really care about slen, but learning it will
384
         hopefully produce some memory error if the array is not valid */
385
691
      slen = airStrlen(enm->desc[ii]);
386
691
      if (!( slen > 0 )) {
387
        if (err) {
388
          snprintf(err, ASL, "%s(%s): enm->desc[%u] empty", me, enm->name, ii);
389
        }
390
        return 1;
391
      }
392
    }
393
  }
394
62
  if (enm->strEqv) {
395
    /* the strEqv array is one of the easiest ways to mess up an
396
       airEnum definition; it deserves these tests and maybe more */
397
38
    if (!enm->valEqv) {
398
      if (err) {
399
        snprintf(err, ASL, "%s(%s): non-NULL strEqv but NULL valEqv",
400
                 me, enm->name);
401
      }
402
      return 1;
403
    }
404
38
    if (!( airStrlen(enm->strEqv[0]) )) {
405
      if (err) {
406
        snprintf(err, ASL, "%s(%s): strEqv[0] is NULL or empty",
407
                 me, enm->name);
408
      }
409
      return 1;
410
    }
411
    /* check length and see if any string maps to an invalid value */
412
2852
    for (ii=0; (slen = strlen(enm->strEqv[ii])); ii++) {
413
1388
      if (!( slen <= AIR_STRLEN_SMALL-1 )) {
414
        if (err) {
415
          char stmp[AIR_STRLEN_SMALL];
416
          snprintf(err, ASL, "%s(%s): strlen(enm->strEqv[%u] \"%s\") "
417
                   "%s not <= %u", me, enm->name, ii, enm->strEqv[ii],
418
                   airSprintSize_t(stmp, slen), AIR_STRLEN_SMALL-1);
419
        }
420
        return 1;
421
      }
422
      /* see if a string maps to an invalid value */
423
1388
      if (airEnumValCheck(enm, enm->valEqv[ii])) {
424
        if (err) {
425
          snprintf(err, ASL, "%s(%s): valEqv[%u] %u (with strEqv[%u] \"%s\")"
426
                   " not valid",
427
                   me, enm->name, ii, enm->valEqv[ii], ii, enm->strEqv[ii]);
428
        }
429
        return 1;
430
      }
431
    }
432
    /* make sure eqv strings contain the canonical string */
433
1396
    for (ii=1; ii<=enm->M; ii++) {
434
      int eval, rval;
435
1658
      eval = (enm->val ? enm->val[ii] : AIR_CAST(int, ii));
436
660
      rval = airEnumVal(enm, enm->str[ii]);
437
660
      if (eval != rval) {
438
        if (err) {
439
          snprintf(err, ASL, "%s(%s): ii=%u->eval=%d->str=\"%s\"->%d != %d "
440
                   "(i.e. canonical string didn't map to its own value)",
441
                   me, enm->name, ii, eval, enm->str[ii], rval, eval);
442
        }
443
        return 1;
444
      }
445
660
    }
446
    /* make sure there are no duplicates among the strEqv,
447
       including remapping the case in case of case insensitivity */
448
2852
    for (ii=0; strlen(enm->strEqv[ii]); ii++) {
449
210706
      for (jj=ii+1; strlen(enm->strEqv[jj]); jj++) {
450
103965
        if (!strcmp(enm->strEqv[ii], enm->strEqv[jj])) {
451
          if (err) {
452
            snprintf(err, ASL, "%s(%s): strEqv[%d] and [%u] both \"%s\"",
453
                     me, enm->name, ii, jj, enm->strEqv[jj]);
454
          }
455
          return 1;
456
        }
457
103965
        if (!enm->sense) {
458
103965
          char bb1[AIR_STRLEN_SMALL], bb2[AIR_STRLEN_SMALL];
459
103965
          strcpy(bb1, enm->strEqv[ii]);
460
103965
          airToLower(bb1);
461
103965
          strcpy(bb2, enm->strEqv[jj]);
462
103965
          airToLower(bb2);
463
103965
          if (!strcmp(bb1, bb2)) {
464
            if (err) {
465
              snprintf(err, ASL, "%s(%s): after case-lowering, "
466
                       "strEqv[%d] and [%u] both \"%s\"",
467
                       me, enm->name, ii, jj, bb1);
468
            }
469
            return 1;
470
          }
471
207930
        }
472
      }
473
    }
474
  }
475
62
  return 0;
476
62
}
477
/* ---- END non-NrrdIO */
478
/* this is the end */