GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/air/string.c Lines: 126 156 80.8 %
Date: 2017-05-26 Branches: 77 114 67.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
/* this has to default to false in order for airStrtok to be a
27
   functional substitute for strtok() */
28
int airStrtokQuoting = AIR_FALSE;
29
30
/*
31
******** airStrdup()
32
**
33
** because they didn't put strdup() in ANSI.
34
** This will return NULL if given NULL.
35
*/
36
char *
37
airStrdup(const char *s) {
38
  char *ret;
39
40
27438
  if (!s) {
41
    ret = NULL;
42
1659
  }
43
  else {
44
12060
    ret = (char *)malloc(strlen(s)+1);
45
12060
    if (ret) {
46
12060
      strcpy(ret, s);
47
12060
    }
48
  }
49
13719
  return ret;
50
}
51
52
/*
53
******** airStrlen()
54
**
55
** just like strlen, but safe to call on NULL (for which return is 0)
56
*/
57
size_t
58
airStrlen(const char *s) {
59
  size_t ret;
60
61
646118
  if (!s) {
62
    ret = 0;
63
105583
  }
64
  else {
65
217476
    ret = strlen(s);
66
  }
67
323059
  return ret;
68
}
69
70
/* ---- BEGIN non-NrrdIO */
71
/*
72
******** airStrcmp
73
**
74
** like strcmp, but:
75
** safe to call with NULL strings,
76
** and, a NULL string is treated same as an empty string
77
*/
78
int
79
airStrcmp(const char *_s1, const char *_s2) {
80
  static const char empty[]="";
81
  const char *s1, *s2;
82
  int ret;
83
84
24230
  s1 = _s1 ? _s1 : empty;
85
12115
  s2 = _s2 ? _s2 : empty;
86
  /*
87
  fprintf(stderr, "airStrcmp: _s1=|%s|->s1=|%s| _s2=|%s|->s2=|%s|\n",
88
          _s1 ? _s1 : "(NULL)", s1,
89
          _s2 ? _s2 : "(NULL)", s2);
90
  */
91
12115
  ret = strcmp(s1, s2);
92
12115
  return ret;
93
}
94
95
/* ---- END non-NrrdIO */
96
/*
97
******** airStrtok()
98
**
99
** thread-safe strtok() replacement.  Use just like strtok(), but on
100
** each call to parse a given string, pass as the last argument the
101
** address of a char*, to be used for saving state while the string is
102
** traversed.  Like strtok(), this will alter the "s" array passed to
103
** it on the first call, and like strtok(), this returns pointers into
104
** this string (rather than allocating new strings for each token).
105
*/
106
char *
107
airStrtok(char *s, const char *ct, char **last) {
108
  char *h, *e, *q;
109
110
99776
  if (!(ct && last)) {
111
    /* can't do any work, bail */
112
    return NULL;
113
  }
114
149664
  h = s ? s : *last;
115
49888
  if (!airStrlen(h))
116
1557
    return NULL;
117
48331
  h += strspn(h, ct);
118
48331
  if ('\"' == *h && airStrtokQuoting) {
119
    /* something is trying to be quoted, and, we'll respect that */
120
    /* have to find the next un-escaped '\"' */
121
    h++;
122
    q = h;
123
    while (*q && !('\"' == *q && '\\' != q[-1])) {
124
      q++;
125
    }
126
    if (*q) {
127
      /* we found an unescaped '\"' */
128
      e = q;
129
    } else {
130
      /* give up; pretend we never tried to do this quoting stuff */
131
      e = h + strcspn(h, ct);
132
    }
133
  } else {
134
48331
    e = h + strcspn(h, ct);
135
  }
136
48331
  if ('\0' == *e) {
137
3174
    *last = e;
138
3174
  }
139
  else {
140
45157
    *e = '\0';
141
45157
    *last = e + 1;
142
  }
143
48331
  return h;
144
49888
}
145
146
/*
147
******** airStrntok()
148
**
149
** returns the number of tokens parsable by airStrtok(), but does
150
** NOT alter the given string
151
*/
152
unsigned int
153
airStrntok(const char *_s, const char *ct) {
154
2674
  char *s, *t, *l=NULL;
155
  unsigned int n = 0;
156
157
1337
  if (_s && ct) {
158
1337
    s = airStrdup(_s);
159
1337
    t = airStrtok(s, ct, &l);
160
49164
    while (t) {
161
23245
      n++;
162
23245
      t = airStrtok(NULL, ct, &l);
163
    }
164
1337
    airFree(s);  /* no NULL assignment to s, else compile warnings */
165
1337
  }
166
1337
  return n;
167
1337
}
168
169
char *
170
airStrtrans(char *s, char from, char to) {
171
  size_t i, l;
172
173
46156
  if (s) {
174
23078
    l = strlen(s);
175
313856
    for (i=0; i<l; i++) {
176
133850
      if (s[i] == from) {
177
1910
        s[i] = to;
178
1910
      }
179
    }
180
  }
181
23078
  return s;
182
}
183
184
/*
185
******** airStrcpy
186
**
187
** Like strncpy but logic is different (and perhaps more useful), being:
188
** "dst is allocated for dstSize chars. Copy as much of src as can
189
** "fit in dst, and always 0-terminate the resulting dst.",
190
** instead of strncpy's "Copy at most n characters, blah blah blah,
191
** and you still have to 0-terminate the rest yourself".
192
**
193
** E.g. with declaration buff[AIR_STRLEN_SMALL], you call
194
** airStrcpy(buff, AIR_STRLEN_SMALL, src), and know that then
195
** strlen(buff) <= AIR_STRLEN_SMALL-1. (see note in air.h about
196
** the meaning of the STRLEN #defines).
197
**
198
** Returns NULL if there was a problem (NULL dst or dstSize zero),
199
** otherwise returns dst
200
*/
201
char *
202
airStrcpy(char *dst, size_t dstSize, const char *src) {
203
  size_t ii, copyLen, srcLen;
204
205
115772
  if (!(dst && dstSize > 0)) {
206
    return NULL;
207
  }
208
57886
  srcLen = airStrlen(src);
209
57886
  if (1 == dstSize || !srcLen) {
210
2
    dst[0] = '\0';
211
2
    return dst;
212
  }
213
  /* else dstSize > 1  AND src is a non-empy string */
214
57884
  copyLen = AIR_MIN(dstSize-1, srcLen);
215
1106670
  for (ii=0; ii<copyLen; ii++) {
216
495451
    dst[ii] = src[ii];
217
  }
218
57884
  dst[copyLen] = '\0';
219
57884
  return dst;
220
57886
}
221
222
/*
223
******** airEndsWith
224
**
225
** if "s" ends with "suff", then returns 1, 0 otherwise
226
*/
227
int
228
airEndsWith(const char *s, const char *suff) {
229
230
50
  if (!(s && suff))
231
    return 0;
232
25
  if (!(strlen(s) >= strlen(suff)))
233
    return 0;
234
25
  if (!strncmp(s + strlen(s) - strlen(suff), suff, strlen(suff)))
235
12
    return 1;
236
  else
237
13
    return 0;
238
25
}
239
240
/*
241
******** airUnescape()
242
**
243
** unescapes \\ and \n in place in a given string.
244
** Always returns the same pointer as given
245
*/
246
char *
247
airUnescape(char *s) {
248
  size_t i, j, len;
249
  int found=0;
250
251
12
  len = airStrlen(s);
252
6
  if (!len)
253
    return s;
254
255
86184
  for (i=1, j=0; i<len; i++, j++) {
256

45926
    if (s[i-1] == '\\' && s[i] == '\\') {
257
1429
      s[j] = '\\'; i++; found = 1;
258

44497
    } else if (s[i-1] == '\\' && s[i] == 'n') {
259
1411
      s[j] = '\n'; i++; found = 1;
260
1411
    } else {
261
40246
      s[j] = s[i-1]; found = 0;
262
    }
263
  }
264
12
  if (i == len || !found) s[j++] = s[len-1];
265
6
  s[j] = 0;
266
267
6
  return s;
268
6
}
269
270
/*
271
******** airOneLinify()
272
**
273
** converts all contiguous white space (as determined by isspace()) to
274
** a single ' ', entirely removes non-printable (as determined by
275
** isprint()) characters, and entirely removes white space contiguous
276
** with the end of the string, even if that means shrinking the string
277
** to "".
278
**
279
** Useful for cleaning up lines of text to be saved as strings in
280
** fields of other structs.
281
**
282
** Whatever happens, this returns the pointer passed to it
283
*/
284
char *
285
airOneLinify(char *s) {
286
  size_t i, j, len;
287
288
1674
  len = airStrlen(s);
289
837
  if (!len)
290
    return s;
291
292
  /* convert white space to space (' '), and delete unprintables */
293

677268
  for (i=0; i<len && s[i]; i++) {
294
225198
    if (isspace(AIR_CAST(int, s[i]))) {
295
15053
      s[i] = ' ';
296
15053
      continue;
297
    }
298
210145
    if (!isprint(AIR_CAST(int, s[i]))) {
299
      for (j=i; j<len; j++) {
300
        /* this will copy the '\0' at the end */
301
        s[j] = s[j+1];
302
      }
303
      i--;
304
      continue;
305
    }
306
  }
307
308
  /* compress all contiguous spaces into one */
309
452070
  for (i=0; i<len; i++) {
310

240612
    while (' ' == s[i] && ' ' == s[i+1]) {
311
16362972
      for (j=i+1; j<len; j++) {
312
8181125
        s[j] = s[j+1];
313
      }
314
    }
315
  }
316
317
  /* lose trailing white space */
318
837
  i = airStrlen(s);
319
837
  if (' ' == s[i-1]) {
320
5
    s[i-1] = '\0';
321
5
  }
322
323
837
  return s;
324
837
}
325
326
/*
327
******** airToLower()
328
**
329
** calls tolower() on all characters in a string, and returns the same
330
** pointer that it was given
331
*/
332
char *
333
airToLower(char *str) {
334
  char *c;
335
336
648866
  if (str) {
337
    c = str;
338
7092424
    while (*c) {
339
3221779
      *c = AIR_CAST(char, tolower(AIR_INT(*c)));
340
3221779
      c++;
341
    }
342
  }
343
324433
  return str;
344
}
345
346
/*
347
******** airToUpper()
348
**
349
** calls toupper() on all characters in a string, and returns the same
350
** pointer that it was given
351
*/
352
char *
353
airToUpper(char *str) {
354
  char *c;
355
356
  if (str) {
357
    c = str;
358
    while (*c) {
359
      *c = AIR_CAST(char, toupper(AIR_INT(*c)));
360
      c++;
361
    }
362
  }
363
  return str;
364
}
365
366
/*
367
******** airOneLine()
368
**
369
** gets one line from "file", putting it into an array of given size.
370
** "size" must be the size of line buffer "line": the size which
371
** "line" was allocated for, not the number of non-null characters it
372
** was meant to hold.  "size" must be at least 3.  Always
373
** null-terminates the contents of the array (except if the arguments
374
** are invalid).  The idea is that the null-termination replaces the
375
** line termination.
376
**
377
** 0: if saw EOF before seeing a newline, or arguments are invalid
378
** 1: if line was a single newline
379
** n; n <= size: if line was n-1 characters followed by newline
380
** size+1: if didn't see a newline within size-1 characters
381
**
382
** So except for returns of -1 and size+1, the return is the number of
383
** characters comprising the line, including the newline character.
384
**
385
** For all you DOS\Windows\Cygwin users, this will quietly pretend that
386
** a "\r\n" pair is really just "\n", including the way that characters
387
** comprising the line are counted.  However, there is no pretension
388
** that on those platforms, "\n" by itself does not actually count as
389
** a newline.
390
**
391
** Finally, for those trafficking in legacy Mac text files (for which
392
** the line termination is only "\r", the same applies- these are also
393
** effectively treated the same as a newline.
394
*/
395
unsigned int
396
airOneLine(FILE *file, char *line, unsigned int size) {
397
  int cc=0;
398
  unsigned int ii;
399
400
774
  if (!(size >= 3  /* need room for a character and a Windows newline */
401
387
        && line && file)) {
402
    return 0;
403
  }
404
  /* c is always set at least once, but not so for any char in line[]  */
405
371122
  for (ii=0;
406
185561
       (ii <= size-2              /* room for line[ii] and \0 after that */
407
371044
        && EOF != (cc=getc(file)) /* didn't hit EOF trying to read char */
408
185483
        && cc != '\n'             /* char isn't newline */
409
185174
        && cc != '\r');           /* char isn't carriage return */
410
185174
       ++ii) {
411
185174
    line[ii] = AIR_CAST(char, cc);
412
  }
413
414
387
  if (EOF == cc) {
415
    /* for-loop terminated because we hit EOF */
416
6
    line[0] = '\0';
417
6
    return 0;
418
381
  } else if ('\r' == cc || '\n' == cc) {
419
    /* for-loop terminated because we hit '\n' or '\r' */
420
    /* if it was '\r', see if next character is '\n' */
421
303
    if ('\r' == cc) {
422
      cc = getc(file);
423
      if (EOF != cc && '\n' != cc) {
424
        /* oops, we got something, and it was not a '\n'; put it back */
425
        ungetc(cc, file);
426
      }
427
    }
428
303
    line[ii] = '\0';
429
303
    return ii+1;
430
  } else {
431
    /* for-loop terminated because we got to end of buffer (ii == size-1) */
432
78
    cc = getc(file);
433
    /* but see if we were about to get '\r', "\r\n", or '\n' */
434
78
    if ('\r' == cc) {
435
      int dd;
436
      dd = getc(file);
437
      if (EOF != dd && '\n' != dd) {
438
        /* oops, put it back */
439
        ungetc(dd, file);
440
      }
441
      line[ii] = '\0';
442
      return ii+1;
443
78
    } else if ('\n' == cc) {
444
2
      line[ii] = '\0';
445
2
      return ii+1;
446
    } else {
447
      /* weren't about to get a line termination,
448
         we really did run out of buffer */
449
76
      if (EOF != cc) {
450
76
        ungetc(cc, file);  /* we're allowed one ungetc on ANY stream */
451
76
      }
452
76
      line[size-1] = '\0';
453
76
      return size+1;
454
    }
455
  }
456
387
}
457