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 "nrrd.h" |
25 |
|
|
#include "privateNrrd.h" |
26 |
|
|
|
27 |
|
|
#if TEEM_BZIP2 |
28 |
|
|
#include <bzlib.h> |
29 |
|
|
#endif |
30 |
|
|
|
31 |
|
|
/* The "/ *Teem:" (without space) comments in here are an experiment */ |
32 |
|
|
|
33 |
|
|
char _nrrdRelativePathFlag[] = "./"; |
34 |
|
|
char _nrrdFieldSep[] = " \t"; |
35 |
|
|
char _nrrdLineSep[] = "\r\n"; |
36 |
|
|
char _nrrdNoSpaceVector[] = "none"; |
37 |
|
|
char _nrrdTextSep[] = " ,\t"; |
38 |
|
|
|
39 |
|
|
/* |
40 |
|
|
** return length of next "line" in nio->headerStringRead |
41 |
|
|
*/ |
42 |
|
|
unsigned int |
43 |
|
|
_nrrdHeaderStringOneLineStrlen(NrrdIoState *nio) { |
44 |
|
|
|
45 |
|
|
return AIR_CAST(unsigned int, |
46 |
|
|
strcspn(nio->headerStringRead + nio->headerStrpos, _nrrdLineSep)); |
47 |
|
|
} |
48 |
|
|
|
49 |
|
|
/* |
50 |
|
|
** read next "line" in nio->headerStringRead |
51 |
|
|
*/ |
52 |
|
|
unsigned int |
53 |
|
|
_nrrdHeaderStringOneLine(NrrdIoState *nio) { |
54 |
|
|
unsigned int len1, len2; |
55 |
|
|
|
56 |
|
|
len1 = _nrrdHeaderStringOneLineStrlen(nio); |
57 |
|
|
strncpy(nio->line, nio->headerStringRead + nio->headerStrpos, len1); |
58 |
|
|
nio->line[len1] = '\0'; |
59 |
|
|
nio->headerStrpos += len1; |
60 |
|
|
len2 = AIR_CAST(unsigned int, |
61 |
|
|
strspn(nio->headerStringRead + nio->headerStrpos, _nrrdLineSep)); |
62 |
|
|
nio->headerStrpos += len2; |
63 |
|
|
return len1; |
64 |
|
|
} |
65 |
|
|
|
66 |
|
|
/* |
67 |
|
|
** _nrrdOneLine |
68 |
|
|
** |
69 |
|
|
** wrapper around airOneLine; does re-allocation of line buffer |
70 |
|
|
** ("line") in the NrrdIoState if needed. The return value semantics |
71 |
|
|
** are similar, except that what airOneLine would return, we put |
72 |
|
|
** in *lenP. If there is an error (airOneLine returned 0, |
73 |
|
|
** something couldn't be allocated), *lenP is set to 0, and |
74 |
|
|
** we return 1. HITTING EOF IS NOT ACTUALLY AN ERROR, see code |
75 |
|
|
** below. Otherwise we return 0. |
76 |
|
|
** |
77 |
|
|
** Does use biff |
78 |
|
|
*/ |
79 |
|
|
int |
80 |
|
|
_nrrdOneLine(unsigned int *lenP, NrrdIoState *nio, FILE *file) { |
81 |
|
|
static const char me[]="_nrrdOneLine"; |
82 |
|
622 |
char **line; |
83 |
|
|
airArray *mop, *lineArr; |
84 |
|
|
airPtrPtrUnion appu; |
85 |
|
|
unsigned int lineIdx, len, needLen; |
86 |
|
|
|
87 |
✓✗✗✓ ✗✗ |
622 |
if (!( lenP && nio && (file || nio->headerStringRead))) { |
88 |
|
|
biffAddf(NRRD, "%s: got NULL pointer (%p, %p, %p/%p)", me, |
89 |
|
|
AIR_CAST(void*, lenP), AIR_CAST(void*, nio), |
90 |
|
|
AIR_CAST(void*, file), nio->headerStringRead); |
91 |
|
|
return 1; |
92 |
|
|
} |
93 |
✓✓ |
311 |
if (0 == nio->lineLen) { |
94 |
|
|
/* nio->line hasn't been allocated for anything */ |
95 |
|
25 |
nio->lineLen = 3; |
96 |
|
25 |
nio->line = (char*)malloc(nio->lineLen); |
97 |
✗✓ |
25 |
if (!nio->line) { |
98 |
|
|
biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); |
99 |
|
|
*lenP = 0; return 1; |
100 |
|
|
} |
101 |
|
|
} |
102 |
✓✗ |
311 |
if (file) { |
103 |
|
311 |
len = airOneLine(file, nio->line, nio->lineLen); |
104 |
|
311 |
} else { |
105 |
|
|
/* NOTE: NULL-ity error check above makes this safe */ |
106 |
|
|
needLen = _nrrdHeaderStringOneLineStrlen(nio); |
107 |
|
|
if (needLen+1 > nio->lineLen) { |
108 |
|
|
nio->lineLen = needLen+1; |
109 |
|
|
airFree(nio->line); /* lose previous allocated line */ |
110 |
|
|
nio->line = (char*)malloc(nio->lineLen); |
111 |
|
|
if (!nio->line) { |
112 |
|
|
biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", |
113 |
|
|
me, nio->lineLen); |
114 |
|
|
*lenP = 0; return 1; |
115 |
|
|
} |
116 |
|
|
} |
117 |
|
|
len = _nrrdHeaderStringOneLine(nio); |
118 |
|
|
} |
119 |
✓✓ |
311 |
if (len <= nio->lineLen) { |
120 |
|
|
/* otherwise we hit EOF (or end of nio->headerStringRead) before a |
121 |
|
|
newline, or the line (possibly empty) fit within the nio->line, |
122 |
|
|
neither of which is an error here */ |
123 |
|
266 |
*lenP = len; |
124 |
|
266 |
} else { |
125 |
|
|
/* line didn't fit in buffer, so we have to increase line |
126 |
|
|
buffer size and put the line together in pieces */ |
127 |
|
|
/* NOTE: this will never happen when reading from nio->headerStringRead */ |
128 |
|
45 |
appu.cp = &line; |
129 |
|
45 |
lineArr = airArrayNew(appu.v, NULL, sizeof(char *), 1); |
130 |
✗✓ |
45 |
if (!lineArr) { |
131 |
|
|
biffAddf(NRRD, "%s: couldn't allocate airArray", me); |
132 |
|
|
*lenP = 0; return 1; |
133 |
|
|
} |
134 |
|
45 |
airArrayPointerCB(lineArr, airNull, airFree); |
135 |
|
45 |
mop = airMopNew(); |
136 |
|
45 |
airMopAdd(mop, lineArr, (airMopper)airArrayNuke, airMopAlways); |
137 |
✓✓✓✓
|
363 |
while (len == nio->lineLen+1) { |
138 |
|
121 |
lineIdx = airArrayLenIncr(lineArr, 1); |
139 |
✗✓ |
197 |
if (!lineArr->data) { |
140 |
|
|
biffAddf(NRRD, "%s: couldn't increment line buffer array", me); |
141 |
|
|
*lenP = 0; airMopError(mop); return 1; |
142 |
|
|
} |
143 |
|
76 |
line[lineIdx] = nio->line; |
144 |
|
76 |
nio->lineLen *= 2; |
145 |
|
76 |
nio->line = (char*)malloc(nio->lineLen); |
146 |
✗✓ |
76 |
if (!nio->line) { |
147 |
|
|
biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", |
148 |
|
|
me, nio->lineLen); |
149 |
|
|
*lenP = 0; airMopError(mop); return 1; |
150 |
|
|
} |
151 |
|
76 |
len = airOneLine(file, nio->line, nio->lineLen); |
152 |
|
|
} |
153 |
|
|
/* last part did fit in nio->line buffer, also save this into line[] */ |
154 |
|
|
lineIdx = airArrayLenIncr(lineArr, 1); |
155 |
✗✓ |
45 |
if (!lineArr->data) { |
156 |
|
|
biffAddf(NRRD, "%s: couldn't increment line buffer array", me); |
157 |
|
|
*lenP = 0; airMopError(mop); return 1; |
158 |
|
|
} |
159 |
|
45 |
line[lineIdx] = nio->line; |
160 |
|
45 |
nio->lineLen *= 3; /* for good measure */ |
161 |
|
45 |
nio->line = (char*)malloc(nio->lineLen); |
162 |
✗✓ |
45 |
if (!nio->line) { |
163 |
|
|
biffAddf(NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); |
164 |
|
|
*lenP = 0; airMopError(mop); return 1; |
165 |
|
|
} |
166 |
|
|
/* now concatenate everything into a new nio->line */ |
167 |
|
45 |
strcpy(nio->line, ""); |
168 |
✓✓✓✓
|
498 |
for (lineIdx=0; lineIdx<lineArr->len; lineIdx++) { |
169 |
|
287 |
strcat(nio->line, line[lineIdx]); |
170 |
|
|
} |
171 |
|
|
/* HEY: API is bad: *lenP should be a size_t pointer! */ |
172 |
|
45 |
*lenP = AIR_UINT(strlen(nio->line)) + 1; |
173 |
|
45 |
airMopError(mop); |
174 |
|
|
} |
175 |
|
311 |
return 0; |
176 |
|
311 |
} |
177 |
|
|
|
178 |
|
|
/* |
179 |
|
|
** _nrrdCalloc() |
180 |
|
|
** |
181 |
|
|
** allocates the data for the array, but only if necessary (as informed by |
182 |
|
|
** nio->oldData and nio->oldDataSize). |
183 |
|
|
** |
184 |
|
|
** as a recent feature, this will handle the extra work of allocating |
185 |
|
|
** memory in the special way required for direct IO, if possible. For |
186 |
|
|
** this to work, though, the FILE *file has to be passed. Since file |
187 |
|
|
** is not otherwise needed, it can be passed as NULL for non-direct-IO |
188 |
|
|
** situations. In any case, if the directIO-compatible allocation fails |
189 |
|
|
** its not error, and we revert to regular allocation. |
190 |
|
|
** |
191 |
|
|
** NOTE: this assumes the checking that is done by _nrrdHeaderCheck |
192 |
|
|
*/ |
193 |
|
|
int |
194 |
|
|
_nrrdCalloc(Nrrd *nrrd, NrrdIoState *nio, FILE *file) { |
195 |
|
|
static const char me[]="_nrrdCalloc"; |
196 |
|
|
size_t needDataSize; |
197 |
|
|
int fd; |
198 |
|
|
|
199 |
|
50 |
needDataSize = nrrdElementNumber(nrrd)*nrrdElementSize(nrrd); |
200 |
✓✓✓✓
|
30 |
if (nio->oldData && needDataSize == nio->oldDataSize) { |
201 |
|
|
/* re-use old data */ |
202 |
|
1 |
nrrd->data = nio->oldData; |
203 |
|
|
/* its not an error to have a directIO-incompatible pointer, so |
204 |
|
|
there's no other error checking to do here */ |
205 |
|
1 |
} else { |
206 |
|
24 |
nrrd->data = airFree(nrrd->data); |
207 |
✓✗ |
72 |
fd = file ? fileno(file) : -1; |
208 |
✗✓ |
48 |
if (nrrdEncodingRaw == nio->encoding |
209 |
✓✗ |
24 |
&& -1 != fd |
210 |
|
24 |
&& airNoDio_okay == airDioTest(fd, NULL, needDataSize)) { |
211 |
|
|
nrrd->data = airDioMalloc(needDataSize, fd); |
212 |
|
|
} |
213 |
✓✗ |
24 |
if (!nrrd->data) { |
214 |
|
|
/* directIO-compatible allocation wasn't tried, or it failed */ |
215 |
|
24 |
nrrd->data = malloc(needDataSize); |
216 |
|
24 |
} |
217 |
✗✓ |
24 |
if (!nrrd->data) { |
218 |
|
|
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; |
219 |
|
|
biffAddf(NRRD, "%s: couldn't allocate %s things of size %s", me, |
220 |
|
|
airSprintSize_t(stmp1, nrrdElementNumber(nrrd)), |
221 |
|
|
airSprintSize_t(stmp2, nrrdElementSize(nrrd))); |
222 |
|
|
return 1; |
223 |
|
|
} |
224 |
|
|
} |
225 |
|
|
/* make it look like it came from calloc(), as used by nrrdNew() */ |
226 |
|
25 |
memset(nrrd->data, 0, needDataSize); |
227 |
|
25 |
return 0; |
228 |
|
25 |
} |
229 |
|
|
|
230 |
|
|
/* |
231 |
|
|
******** nrrdLineSkip |
232 |
|
|
** |
233 |
|
|
** public for the sake of things like "unu make" |
234 |
|
|
** uses the NrrdIoState for its line buffer (used by _nrrdOneLine) |
235 |
|
|
*/ |
236 |
|
|
int |
237 |
|
|
nrrdLineSkip(FILE *dataFile, NrrdIoState *nio) { |
238 |
|
|
static const char me[]="nrrdLineSkip"; |
239 |
|
46 |
unsigned int lsi, skipRet; |
240 |
|
|
|
241 |
|
|
/* For compressed data: If you don't actually have ascii headers on |
242 |
|
|
top of your gzipped data then you will potentially huge lines |
243 |
|
|
while _nrrdOneLine looks for line terminations. Quoting Gordon: |
244 |
|
|
"Garbage in, Garbage out." */ |
245 |
|
|
|
246 |
✗✓ |
23 |
if (!( dataFile && nio )) { |
247 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
248 |
|
|
return 1; |
249 |
|
|
} |
250 |
|
|
|
251 |
✗✓ |
46 |
for (lsi=0; lsi<nio->lineSkip; lsi++) { |
252 |
|
|
if (_nrrdOneLine(&skipRet, nio, dataFile)) { |
253 |
|
|
biffAddf(NRRD, "%s: error skipping line %u of %u", |
254 |
|
|
me, lsi+1, nio->lineSkip); |
255 |
|
|
return 1; |
256 |
|
|
} |
257 |
|
|
if (!skipRet) { |
258 |
|
|
biffAddf(NRRD, "%s: hit EOF skipping line %u of %u", |
259 |
|
|
me, lsi+1, nio->lineSkip); |
260 |
|
|
return 1; |
261 |
|
|
} |
262 |
|
|
} |
263 |
|
23 |
return 0; |
264 |
|
23 |
} |
265 |
|
|
|
266 |
|
|
int |
267 |
|
|
_nrrdByteSkipSkip(FILE *dataFile, Nrrd *nrrd, NrrdIoState *nio, long int byteSkip) { |
268 |
|
|
static const char me[]="nrrdByteSkipSkip"; |
269 |
|
|
int skipRet; |
270 |
|
|
size_t bsize; |
271 |
|
|
|
272 |
✗✓ |
46 |
if (!( dataFile && nrrd && nio )) { |
273 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
274 |
|
|
return 1; |
275 |
|
|
} |
276 |
✗✓ |
23 |
if (nio->encoding->isCompression) { |
277 |
|
|
biffAddf(NRRD, "%s: this function can't work with compressed " |
278 |
|
|
"encoding %s", me, nio->encoding->name); |
279 |
|
|
return 1; |
280 |
|
|
} |
281 |
✓✓ |
23 |
if (byteSkip < 0) { |
282 |
|
|
long backwards; |
283 |
✗✓ |
3 |
if (nrrdEncodingRaw != nio->encoding) { |
284 |
|
|
biffAddf(NRRD, "%s: this function can do backwards byte skip only " |
285 |
|
|
"in %s encoding, not %s", me, |
286 |
|
|
nrrdEncodingRaw->name, nio->encoding->name); |
287 |
|
|
return 1; |
288 |
|
|
} |
289 |
✗✓ |
3 |
if (stdin == dataFile) { |
290 |
|
|
biffAddf(NRRD, "%s: can't fseek on stdin", me); |
291 |
|
|
return 1; |
292 |
|
|
} |
293 |
|
3 |
bsize = nrrdElementNumber(nrrd)/_nrrdDataFNNumber(nio); |
294 |
|
3 |
bsize *= nrrdElementSize(nrrd); |
295 |
|
|
/* backwards is (positive) number of bytes AFTER data that we ignore */ |
296 |
|
3 |
backwards = -byteSkip - 1; |
297 |
|
|
/* HEY what if bsize fits in size_t but not in (signed) long? */ |
298 |
✗✓ |
3 |
if (fseek(dataFile, -AIR_CAST(long, bsize) - backwards, SEEK_END)) { |
299 |
|
|
char stmp[AIR_STRLEN_SMALL]; |
300 |
|
|
biffAddf(NRRD, "%s: failed to fseek(dataFile, %s, SEEK_END)", me, |
301 |
|
|
airSprintSize_t(stmp, bsize)); |
302 |
|
|
return 1; |
303 |
|
|
} |
304 |
✗✓ |
3 |
if (nrrdStateVerboseIO >= 2) { |
305 |
|
|
fprintf(stderr, "(%s: actually skipped %d bytes)\n", |
306 |
|
|
me, (int)ftell(dataFile)); |
307 |
|
|
} |
308 |
|
3 |
} else { |
309 |
✓✗✗✓
|
40 |
if ((stdin == dataFile) || (-1==fseek(dataFile, byteSkip, SEEK_CUR))) { |
310 |
|
|
long skipi; |
311 |
|
|
/* fseek failed, perhaps because we're reading stdin, so |
312 |
|
|
we revert to consuming the input one byte at a time */ |
313 |
|
|
for (skipi=0; skipi<byteSkip; skipi++) { |
314 |
|
|
skipRet = fgetc(dataFile); |
315 |
|
|
if (EOF == skipRet) { |
316 |
|
|
biffAddf(NRRD, "%s: hit EOF skipping byte %ld of %ld", |
317 |
|
|
me, skipi, byteSkip); |
318 |
|
|
return 1; |
319 |
|
|
} |
320 |
|
|
} |
321 |
|
|
} |
322 |
|
|
} |
323 |
|
23 |
return 0; |
324 |
|
23 |
} |
325 |
|
|
|
326 |
|
|
/* |
327 |
|
|
******** nrrdByteSkip |
328 |
|
|
** |
329 |
|
|
** public for the sake of things like "unu make" |
330 |
|
|
** uses nio for information about how much data should actually be skipped |
331 |
|
|
** with negative byteSkip |
332 |
|
|
*/ |
333 |
|
|
int |
334 |
|
|
nrrdByteSkip(FILE *dataFile, Nrrd *nrrd, NrrdIoState *nio) { |
335 |
|
|
static const char me[]="nrrdByteSkip"; |
336 |
|
|
|
337 |
✗✓ |
46 |
if (!( dataFile && nrrd && nio )) { |
338 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
339 |
|
|
return 1; |
340 |
|
|
} |
341 |
|
|
/* HEY: with the advent of NRRD0006 per-file skips, maybe this is |
342 |
|
|
the function that should be public */ |
343 |
✗✓ |
23 |
if (_nrrdByteSkipSkip(dataFile, nrrd, nio, nio->byteSkip)) { |
344 |
|
|
biffAddf(NRRD, "%s: trouble", me); |
345 |
|
|
return 1; |
346 |
|
|
} |
347 |
|
|
|
348 |
|
23 |
return 0; |
349 |
|
23 |
} |
350 |
|
|
|
351 |
|
|
/* |
352 |
|
|
** _nrrdRead() |
353 |
|
|
** |
354 |
|
|
** read in nrrd from a given file *OR* given string. The main job of |
355 |
|
|
** this function is to start reading the file/string, to determine the |
356 |
|
|
** format, and then call the appropriate format's reader. This means |
357 |
|
|
** that the various encoding (data) readers can assume that |
358 |
|
|
** nio->format is usefully set. |
359 |
|
|
** |
360 |
|
|
** If (file), the only input information that nio is used for is |
361 |
|
|
** nio->path, so that detached header-relative data files can be |
362 |
|
|
** found. If (string), the headerStr-related fields in the _nio will |
363 |
|
|
** be set/used |
364 |
|
|
*/ |
365 |
|
|
int |
366 |
|
|
_nrrdRead(Nrrd *nrrd, FILE *file, const char *string, NrrdIoState *_nio) { |
367 |
|
|
static const char me[]="_nrrdRead"; |
368 |
|
50 |
unsigned int llen; |
369 |
|
|
NrrdIoState *nio; |
370 |
|
|
int nfi; |
371 |
|
|
airArray *mop; |
372 |
|
|
|
373 |
|
|
/* sanity check, for good measure */ |
374 |
✗✓ |
25 |
if (!nrrdSanity()) { |
375 |
|
|
biffAddf(NRRD, "%s: sanity check FAILED: have to fix and re-compile", |
376 |
|
|
me); |
377 |
|
|
return 1; |
378 |
|
|
} |
379 |
|
|
|
380 |
✗✓ |
25 |
if (!((file || string) && nrrd)) { |
381 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
382 |
|
|
return 1; |
383 |
|
|
} |
384 |
✗✓ |
25 |
if (file && string) { |
385 |
|
|
biffAddf(NRRD, "%s: can't read from both file and string", me); |
386 |
|
|
return 1; |
387 |
|
|
} |
388 |
|
25 |
mop = airMopNew(); |
389 |
✓✗ |
25 |
if (_nio) { |
390 |
|
|
nio = _nio; |
391 |
|
25 |
} else { |
392 |
|
|
nio = nrrdIoStateNew(); |
393 |
|
|
if (!nio) { |
394 |
|
|
biffAddf(NRRD, "%s: couldn't alloc I/O struct", me); |
395 |
|
|
return 1; |
396 |
|
|
} |
397 |
|
|
airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); |
398 |
|
|
} |
399 |
|
|
|
400 |
|
|
/* remember old data pointer and allocated size. Whether or not to |
401 |
|
|
free() this memory will be decided later */ |
402 |
|
25 |
nio->oldData = nrrd->data; |
403 |
✓✓ |
55 |
nio->oldDataSize = (nio->oldData |
404 |
|
5 |
? nrrdElementNumber(nrrd)*nrrdElementSize(nrrd) |
405 |
|
|
: 0); |
406 |
|
|
/* |
407 |
|
|
fprintf(stderr, "!%s: nio->oldData = %p, oldDataSize = %d\n", me, |
408 |
|
|
nio->oldData, (int)(nio->oldDataSize)); |
409 |
|
|
*/ |
410 |
|
25 |
nrrd->data = NULL; |
411 |
|
|
|
412 |
|
|
/* initialize given nrrd (but we have thwarted freeing existing memory) */ |
413 |
|
25 |
nrrdInit(nrrd); |
414 |
|
|
|
415 |
|
|
/* tell the nio where to find the string to read from */ |
416 |
|
25 |
nio->headerStringRead = string; |
417 |
|
|
|
418 |
✗✓ |
25 |
if (_nrrdOneLine(&llen, nio, file)) { |
419 |
|
|
biffAddf(NRRD, "%s: error getting first line (containing \"magic\")", |
420 |
|
|
me); |
421 |
|
|
airMopError(mop); return 1; |
422 |
|
|
} |
423 |
✗✓ |
25 |
if (!llen) { |
424 |
|
|
biffAddf(NRRD, "%s: immediately hit EOF", me); |
425 |
|
|
airMopError(mop); return 1; |
426 |
|
|
} |
427 |
|
|
|
428 |
|
25 |
nio->format = nrrdFormatUnknown; |
429 |
✓✗ |
54 |
for (nfi = nrrdFormatTypeUnknown+1; |
430 |
|
27 |
nfi < nrrdFormatTypeLast; |
431 |
|
2 |
nfi++) { |
432 |
✓✓ |
27 |
if (nrrdFormatArray[nfi]->contentStartsLike(nio)) { |
433 |
|
25 |
nio->format = nrrdFormatArray[nfi]; |
434 |
|
25 |
break; |
435 |
|
|
} |
436 |
|
|
} |
437 |
✗✓ |
25 |
if (nrrdFormatUnknown == nio->format) { |
438 |
|
|
char linestart[AIR_STRLEN_SMALL], stmp[AIR_STRLEN_SMALL]; |
439 |
|
|
airStrcpy(linestart, AIR_STRLEN_SMALL, nio->line); |
440 |
|
|
if (strlen(linestart) != strlen(nio->line)) { |
441 |
|
|
biffAddf(NRRD, "%s: couldn't parse (length %s) line starting " |
442 |
|
|
"with \"%s\" as magic or beginning of any recognized format", |
443 |
|
|
me, airSprintSize_t(stmp, strlen(nio->line)), linestart); |
444 |
|
|
} else { |
445 |
|
|
biffAddf(NRRD, "%s: couldn't parse \"%s\" as magic or beginning " |
446 |
|
|
"of any recognized format", me, nio->line); |
447 |
|
|
} |
448 |
|
|
airMopError(mop); return 1; |
449 |
|
|
} |
450 |
✗✓✗✗
|
25 |
if (string && nrrdFormatNRRD != nio->format) { |
451 |
|
|
biffAddf(NRRD, "%s: sorry, can only read %s files from strings (not %s)", |
452 |
|
|
me, nrrdFormatNRRD->name, nio->format->name); |
453 |
|
|
airMopError(mop); return 1; |
454 |
|
|
} |
455 |
|
|
|
456 |
|
|
/* try to read the file */ |
457 |
✗✓ |
25 |
if (nio->format->read(file, nrrd, nio)) { |
458 |
|
|
biffAddf(NRRD, "%s: trouble reading %s file", me, nio->format->name); |
459 |
|
|
airMopError(mop); return 1; |
460 |
|
|
} |
461 |
|
|
|
462 |
|
|
/* reshape up grayscale images, if desired */ |
463 |
✓✓✗✓
|
27 |
if (nio->format->isImage && 2 == nrrd->dim && nrrdStateGrayscaleImage3D) { |
464 |
|
|
if (nrrdAxesInsert(nrrd, nrrd, 0)) { |
465 |
|
|
biffAddf(NRRD, "%s:", me); |
466 |
|
|
return 1; |
467 |
|
|
} |
468 |
|
|
} |
469 |
|
|
|
470 |
|
|
/* free prior memory if we didn't end up using it */ |
471 |
|
|
/* HEY: could actually do a check on the nio to refine this */ |
472 |
✓✓ |
25 |
if (nio->oldData != nrrd->data) { |
473 |
|
24 |
nio->oldData = airFree(nio->oldData); |
474 |
|
24 |
nio->oldDataSize = 0; |
475 |
|
24 |
} |
476 |
|
|
|
477 |
|
|
/* finally, make sure that what we're returning isn't malformed somehow, |
478 |
|
|
except that we (probably stupidly) allow nrrd->data to be NULL, given |
479 |
|
|
the possibility of using nio->skipData */ |
480 |
✗✓ |
25 |
if (_nrrdCheck(nrrd, AIR_FALSE, AIR_TRUE)) { |
481 |
|
|
biffAddf(NRRD, "%s: problem with nrrd after reading", me); |
482 |
|
|
return 1; |
483 |
|
|
} |
484 |
|
|
|
485 |
|
25 |
airMopOkay(mop); |
486 |
|
25 |
return 0; |
487 |
|
25 |
} |
488 |
|
|
|
489 |
|
|
/* |
490 |
|
|
******** nrrdRead() |
491 |
|
|
** |
492 |
|
|
** now just a wrapper around _nrrdRead(); reads a NRRD from a FILE * |
493 |
|
|
*/ |
494 |
|
|
int |
495 |
|
|
nrrdRead(Nrrd *nrrd, FILE *file, NrrdIoState *_nio) { |
496 |
|
|
static const char me[]="nrrdRead"; |
497 |
|
|
|
498 |
✗✓ |
50 |
if (_nrrdRead(nrrd, file, NULL, _nio)) { |
499 |
|
|
biffAddf(NRRD, "%s: trouble", me); |
500 |
|
|
return 1; |
501 |
|
|
} |
502 |
|
25 |
return 0; |
503 |
|
25 |
} |
504 |
|
|
|
505 |
|
|
/* |
506 |
|
|
******** nrrdStringRead() |
507 |
|
|
** |
508 |
|
|
** also a wrapper around _nrrdRead(); reads a NRRD from a char *. |
509 |
|
|
** |
510 |
|
|
** Because the same underlying _nrrdRead() is used, the same semantics |
511 |
|
|
** about using existing nrrd->data when possible applies, as does the |
512 |
|
|
** action of nrrdStateGrayscaleImage3D |
513 |
|
|
*/ |
514 |
|
|
int |
515 |
|
|
nrrdStringRead(Nrrd *nrrd, const char *string, NrrdIoState *_nio) { |
516 |
|
|
static const char me[]="nrrdRead"; |
517 |
|
|
|
518 |
|
|
if (_nrrdRead(nrrd, NULL, string, _nio)) { |
519 |
|
|
biffAddf(NRRD, "%s: trouble", me); |
520 |
|
|
return 1; |
521 |
|
|
} |
522 |
|
|
return 0; |
523 |
|
|
} |
524 |
|
|
|
525 |
|
|
/* |
526 |
|
|
** _nrrdSplitName() |
527 |
|
|
** |
528 |
|
|
** splits a file name into a path and a base filename. The path |
529 |
|
|
** separator is '/', but there is a hack (thanks Torsten Rohlfing) |
530 |
|
|
** which allows '\' to work on Windows. The division between the path |
531 |
|
|
** and the base is the last path separator in the file name. The path |
532 |
|
|
** is everything prior to this, and base is everything after (so the |
533 |
|
|
** base does NOT start with the path separator). If there is not a |
534 |
|
|
** '/' in the name, or if a path separator appears as the last |
535 |
|
|
** character, then the path is set to ".", and the name is copied into |
536 |
|
|
** base. |
537 |
|
|
*/ |
538 |
|
|
void |
539 |
|
|
_nrrdSplitName(char **dirP, char **baseP, const char *name) { |
540 |
|
|
char *where; |
541 |
|
|
|
542 |
✓✗ |
50 |
if (dirP) { |
543 |
|
25 |
*dirP = (char *)airFree(*dirP); |
544 |
|
25 |
} |
545 |
✗✓ |
25 |
if (baseP) { |
546 |
|
|
*baseP = (char *)airFree(*baseP); |
547 |
|
|
} |
548 |
|
25 |
where = strrchr(name, '/'); |
549 |
|
|
#ifdef _WIN32 |
550 |
|
|
/* Deal with Windows "\" path separators; thanks to Torsten Rohlfing */ |
551 |
|
|
if ( !where || (strrchr(name, '\\') > where) ) { |
552 |
|
|
where = strrchr(name, '\\'); |
553 |
|
|
} |
554 |
|
|
#endif |
555 |
|
|
/* we found a valid break if the last directory character |
556 |
|
|
is somewhere in the string except the last character */ |
557 |
✓✓✓✗
|
32 |
if (where && airStrlen(where) > 1) { |
558 |
✓✗ |
7 |
if (dirP) { |
559 |
|
7 |
*dirP = airStrdup(name); |
560 |
|
7 |
(*dirP)[where - name] = 0; |
561 |
|
7 |
} |
562 |
✗✓ |
7 |
if (baseP) { |
563 |
|
|
*baseP = airStrdup(where + 1); |
564 |
|
|
} |
565 |
|
|
} else { |
566 |
|
|
/* if the name had no slash, its in the current directory, which |
567 |
|
|
means that we need to explicitly store "." as the header |
568 |
|
|
directory in case we have header-relative data. */ |
569 |
✓✗ |
18 |
if (dirP) { |
570 |
|
18 |
*dirP = airStrdup("."); |
571 |
|
18 |
} |
572 |
✗✓ |
18 |
if (baseP) { |
573 |
|
|
*baseP = airStrdup(name); |
574 |
|
|
} |
575 |
|
|
} |
576 |
|
|
return; |
577 |
|
25 |
} |
578 |
|
|
|
579 |
|
|
/* |
580 |
|
|
******** nrrdLoad() |
581 |
|
|
** |
582 |
|
|
** |
583 |
|
|
** |
584 |
|
|
** call tree for this, to help figure out what's going on |
585 |
|
|
** |
586 |
|
|
** read.c/nrrdLoad |
587 |
|
|
** | read.c/_nrrdSplitName |
588 |
|
|
** | read.c/nrrdRead |
589 |
|
|
** | nio->format->read |
590 |
|
|
** = formatNRRD.c/_nrrdFormatNRRD_read: |
591 |
|
|
** | read.c/_nrrdOneLine |
592 |
|
|
** | parseNrrd.c/_nrrdReadNrrdParseField |
593 |
|
|
** | parseNrrd.c/nrrdFieldInfoParse[] |
594 |
|
|
** = parseNrrd.c/_nrrdReadNrrdParse_data_file |
595 |
|
|
** | fopen(dataName) |
596 |
|
|
** | formatNRRD.c/_nrrdHeaderCheck |
597 |
|
|
** | read.c/nrrdLineSkip |
598 |
|
|
** | read.c/nrrdByteSkip |
599 |
|
|
** | nio->encoding->read |
600 |
|
|
** = encodingRaw.c/_nrrdEncodingRaw_read |
601 |
|
|
** | read.c/_nrrdCalloc |
602 |
|
|
** | formatNRRD.c/nrrdSwapEndian |
603 |
|
|
** | miscAir.c/airFclose |
604 |
|
|
** |
605 |
|
|
** (more documentation here) |
606 |
|
|
** |
607 |
|
|
** sneakiness: returns 2 if the reason for problem was a failed fopen(). |
608 |
|
|
** |
609 |
|
|
*/ |
610 |
|
|
int /*Teem: biff if (ret) */ |
611 |
|
|
nrrdLoad(Nrrd *nrrd, const char *filename, NrrdIoState *nio) { |
612 |
|
|
static const char me[]="nrrdLoad"; |
613 |
|
|
FILE *file; |
614 |
|
|
airArray *mop; |
615 |
|
|
|
616 |
✗✓ |
50 |
if (!(nrrd && filename)) { |
617 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
618 |
|
|
return 1; |
619 |
|
|
} |
620 |
|
25 |
mop = airMopNew(); |
621 |
✓✗ |
25 |
if (!nio) { |
622 |
|
25 |
nio = nrrdIoStateNew(); |
623 |
✗✓ |
25 |
if (!nio) { |
624 |
|
|
biffAddf(NRRD, "%s: couldn't alloc I/O struct", me); |
625 |
|
|
return 1; |
626 |
|
|
} |
627 |
|
25 |
airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); |
628 |
|
25 |
} |
629 |
|
|
|
630 |
|
|
/* we save the directory of the filename given to us so that if it turns |
631 |
|
|
out that this is a detached header with a header-relative data file, |
632 |
|
|
then we will know how to find the data file */ |
633 |
|
25 |
_nrrdSplitName(&(nio->path), NULL, filename); |
634 |
|
|
/* printf("!%s: |%s|%s|\n", me, nio->dir, nio->base); */ |
635 |
|
|
|
636 |
✗✓ |
25 |
if (!( file = airFopen(filename, stdin, "rb") )) { |
637 |
|
|
biffAddf(NRRD, "%s: fopen(\"%s\",\"rb\") failed: %s", |
638 |
|
|
me, filename, strerror(errno)); |
639 |
|
|
airMopError(mop); return 2; |
640 |
|
|
} |
641 |
|
25 |
airMopAdd(mop, file, (airMopper)airFclose, airMopOnError); |
642 |
|
|
/* non-error exiting is handled below */ |
643 |
|
|
|
644 |
✗✓ |
25 |
if (nrrdRead(nrrd, file, nio)) { |
645 |
|
|
biffAddf(NRRD, "%s: trouble reading \"%s\"", me, filename); |
646 |
|
|
airMopError(mop); return 1; |
647 |
|
|
} |
648 |
|
|
|
649 |
✗✗ |
25 |
if (nrrdFormatNRRD == nio->format |
650 |
✓✓ |
48 |
&& nio->keepNrrdDataFileOpen |
651 |
✗✓ |
23 |
&& file == nio->dataFile ) { |
652 |
|
|
/* we have to keep the datafile open. If was attached, we can't |
653 |
|
|
close file, because that is the datafile. If was detached, |
654 |
|
|
file != nio->dataFile, so we can close file. */ |
655 |
|
|
} else { |
656 |
|
|
/* always close non-NRRD files */ |
657 |
|
25 |
airFclose(file); |
658 |
|
|
} |
659 |
|
|
|
660 |
|
25 |
airMopOkay(mop); |
661 |
|
25 |
return 0; |
662 |
|
25 |
} |
663 |
|
|
|
664 |
|
|
int |
665 |
|
|
nrrdLoadMulti(Nrrd *const *nin, unsigned int ninLen, |
666 |
|
|
const char *fnameFormat, |
667 |
|
|
unsigned int numStart, NrrdIoState *nio) { |
668 |
|
|
static const char me[]="nrrdLoadMulti"; |
669 |
|
|
char *fname; |
670 |
|
|
airArray *mop; |
671 |
|
|
unsigned int nii; |
672 |
|
|
|
673 |
|
|
if (!(nin && fnameFormat)) { |
674 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
675 |
|
|
return 1; |
676 |
|
|
} |
677 |
|
|
if (!( _nrrdContainsPercentThisAndMore(fnameFormat, 'u') )) { |
678 |
|
|
biffAddf(NRRD, "%s: given format \"%s\" doesn't seem to " |
679 |
|
|
"have the \"%%u\" conversion specification to sprintf " |
680 |
|
|
"an unsigned int\n", me, fnameFormat); |
681 |
|
|
return 1; |
682 |
|
|
} |
683 |
|
|
|
684 |
|
|
mop = airMopNew(); |
685 |
|
|
/* should be big enough for the number replacing the format sequence */ |
686 |
|
|
fname = AIR_CAST(char *, malloc(strlen(fnameFormat) + 128)); |
687 |
|
|
if (!(fname)) { |
688 |
|
|
biffAddf(NRRD, "%s: couldn't allocate local fname buffer", me); |
689 |
|
|
airMopError(mop); return 1; |
690 |
|
|
} |
691 |
|
|
airMopAdd(mop, fname, airFree, airMopAlways); |
692 |
|
|
|
693 |
|
|
for (nii=0; nii<ninLen; nii++) { |
694 |
|
|
unsigned int num; |
695 |
|
|
num = numStart + nii; |
696 |
|
|
sprintf(fname, fnameFormat, num); |
697 |
|
|
if (nrrdLoad(nin[nii], fname, nio)) { |
698 |
|
|
biffAddf(NRRD, "%s: trouble loading nin[%u] from %s", me, nii, fname); |
699 |
|
|
airMopError(mop); return 1; |
700 |
|
|
} |
701 |
|
|
/* HEY: GLK hopes that the nio doesn't have any state that needs |
702 |
|
|
resetting, but we can't call nrrdIoStateInit() because that |
703 |
|
|
would negate the purpose of sending in the nio for all but the |
704 |
|
|
first saved nrrd */ |
705 |
|
|
} |
706 |
|
|
|
707 |
|
|
airMopOkay(mop); |
708 |
|
|
return 0; |
709 |
|
|
} |