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 |
|
|
|