File: | src/nrrd/read.c |
Location: | line 159, column 19 |
Description: | Dereference of undefined pointer value |
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_BZIP21 | |||
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,((unsigned int)(strcspn(nio->headerStringRead + nio->headerStrpos , _nrrdLineSep))) | |||
46 | strcspn(nio->headerStringRead + nio->headerStrpos, _nrrdLineSep))((unsigned int)(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)__builtin___strncpy_chk (nio->line, nio->headerStringRead + nio->headerStrpos, len1, __builtin_object_size (nio-> line, 2 > 1 ? 1 : 0)); | |||
58 | nio->line[len1] = '\0'; | |||
59 | nio->headerStrpos += len1; | |||
60 | len2 = AIR_CAST(unsigned int,((unsigned int)(strspn(nio->headerStringRead + nio->headerStrpos , _nrrdLineSep))) | |||
61 | strspn(nio->headerStringRead + nio->headerStrpos, _nrrdLineSep))((unsigned int)(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 | char **line; | |||
83 | airArray *mop, *lineArr; | |||
84 | airPtrPtrUnion appu; | |||
85 | unsigned int lineIdx, len, needLen; | |||
86 | ||||
87 | if (!( lenP && nio && (file || nio->headerStringRead))) { | |||
88 | biffAddf(NRRDnrrdBiffKey, "%s: got NULL pointer (%p, %p, %p/%p)", me, | |||
89 | AIR_CAST(void*, lenP)((void*)(lenP)), AIR_CAST(void*, nio)((void*)(nio)), | |||
90 | AIR_CAST(void*, file)((void*)(file)), nio->headerStringRead); | |||
91 | return 1; | |||
92 | } | |||
93 | if (0 == nio->lineLen) { | |||
94 | /* nio->line hasn't been allocated for anything */ | |||
95 | nio->lineLen = 3; | |||
96 | nio->line = (char*)malloc(nio->lineLen); | |||
97 | if (!nio->line) { | |||
98 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't alloc %d-char line\n", me, nio->lineLen); | |||
99 | *lenP = 0; return 1; | |||
100 | } | |||
101 | } | |||
102 | if (file) { | |||
103 | len = airOneLine(file, nio->line, nio->lineLen); | |||
104 | } 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(NRRDnrrdBiffKey, "%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 | 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 | *lenP = len; | |||
124 | } 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 | appu.cp = &line; | |||
129 | lineArr = airArrayNew(appu.v, NULL((void*)0), sizeof(char *), 1); | |||
130 | if (!lineArr) { | |||
131 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't allocate airArray", me); | |||
132 | *lenP = 0; return 1; | |||
133 | } | |||
134 | airArrayPointerCB(lineArr, airNull, airFree); | |||
135 | mop = airMopNew(); | |||
136 | airMopAdd(mop, lineArr, (airMopper)airArrayNuke, airMopAlways); | |||
137 | while (len == nio->lineLen+1) { | |||
138 | lineIdx = airArrayLenIncr(lineArr, 1); | |||
139 | if (!lineArr->data) { | |||
140 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't increment line buffer array", me); | |||
141 | *lenP = 0; airMopError(mop); return 1; | |||
142 | } | |||
143 | line[lineIdx] = nio->line; | |||
144 | nio->lineLen *= 2; | |||
145 | nio->line = (char*)malloc(nio->lineLen); | |||
146 | if (!nio->line) { | |||
147 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't alloc %d-char line\n", | |||
148 | me, nio->lineLen); | |||
149 | *lenP = 0; airMopError(mop); return 1; | |||
150 | } | |||
151 | 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 | if (!lineArr->data) { | |||
156 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't increment line buffer array", me); | |||
157 | *lenP = 0; airMopError(mop); return 1; | |||
158 | } | |||
159 | line[lineIdx] = nio->line; | |||
| ||||
160 | nio->lineLen *= 3; /* for good measure */ | |||
161 | nio->line = (char*)malloc(nio->lineLen); | |||
162 | if (!nio->line) { | |||
163 | biffAddf(NRRDnrrdBiffKey, "%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 | strcpy(nio->line, "")__builtin___strcpy_chk (nio->line, "", __builtin_object_size (nio->line, 2 > 1 ? 1 : 0)); | |||
168 | for (lineIdx=0; lineIdx<lineArr->len; lineIdx++) { | |||
169 | strcat(nio->line, line[lineIdx])__builtin___strcat_chk (nio->line, line[lineIdx], __builtin_object_size (nio->line, 2 > 1 ? 1 : 0)); | |||
170 | } | |||
171 | /* HEY: API is bad: *lenP should be a size_t pointer! */ | |||
172 | *lenP = AIR_UINT(strlen(nio->line))((unsigned int)(strlen(nio->line))) + 1; | |||
173 | airMopError(mop); | |||
174 | } | |||
175 | return 0; | |||
176 | } | |||
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 | needDataSize = nrrdElementNumber(nrrd)*nrrdElementSize(nrrd); | |||
200 | if (nio->oldData && needDataSize == nio->oldDataSize) { | |||
201 | /* re-use old data */ | |||
202 | 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 | } else { | |||
206 | nrrd->data = airFree(nrrd->data); | |||
207 | fd = file ? fileno(file) : -1; | |||
208 | if (nrrdEncodingRaw == nio->encoding | |||
209 | && -1 != fd | |||
210 | && airNoDio_okay == airDioTest(fd, NULL((void*)0), needDataSize)) { | |||
211 | nrrd->data = airDioMalloc(needDataSize, fd); | |||
212 | } | |||
213 | if (!nrrd->data) { | |||
214 | /* directIO-compatible allocation wasn't tried, or it failed */ | |||
215 | nrrd->data = malloc(needDataSize); | |||
216 | } | |||
217 | if (!nrrd->data) { | |||
218 | char stmp1[AIR_STRLEN_SMALL(128+1)], stmp2[AIR_STRLEN_SMALL(128+1)]; | |||
219 | biffAddf(NRRDnrrdBiffKey, "%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 | memset(nrrd->data, 0, needDataSize)__builtin___memset_chk (nrrd->data, 0, needDataSize, __builtin_object_size (nrrd->data, 0)); | |||
227 | return 0; | |||
228 | } | |||
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 | 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 | if (!( dataFile && nio )) { | |||
| ||||
247 | biffAddf(NRRDnrrdBiffKey, "%s: got NULL pointer", me); | |||
248 | return 1; | |||
249 | } | |||
250 | ||||
251 | for (lsi=0; lsi<nio->lineSkip; lsi++) { | |||
252 | if (_nrrdOneLine(&skipRet, nio, dataFile)) { | |||
253 | biffAddf(NRRDnrrdBiffKey, "%s: error skipping line %u of %u", | |||
254 | me, lsi+1, nio->lineSkip); | |||
255 | return 1; | |||
256 | } | |||
257 | if (!skipRet) { | |||
258 | biffAddf(NRRDnrrdBiffKey, "%s: hit EOF skipping line %u of %u", | |||
259 | me, lsi+1, nio->lineSkip); | |||
260 | return 1; | |||
261 | } | |||
262 | } | |||
263 | return 0; | |||
264 | } | |||
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 | if (!( dataFile && nrrd && nio )) { | |||
273 | biffAddf(NRRDnrrdBiffKey, "%s: got NULL pointer", me); | |||
274 | return 1; | |||
275 | } | |||
276 | if (nio->encoding->isCompression) { | |||
277 | biffAddf(NRRDnrrdBiffKey, "%s: this function can't work with compressed " | |||
278 | "encoding %s", me, nio->encoding->name); | |||
279 | return 1; | |||
280 | } | |||
281 | if (byteSkip < 0) { | |||
282 | long backwards; | |||
283 | if (nrrdEncodingRaw != nio->encoding) { | |||
284 | biffAddf(NRRDnrrdBiffKey, "%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 | if (stdin__stdinp == dataFile) { | |||
290 | biffAddf(NRRDnrrdBiffKey, "%s: can't fseek on stdin", me); | |||
291 | return 1; | |||
292 | } | |||
293 | bsize = nrrdElementNumber(nrrd)/_nrrdDataFNNumber(nio); | |||
294 | bsize *= nrrdElementSize(nrrd); | |||
295 | /* backwards is (positive) number of bytes AFTER data that we ignore */ | |||
296 | backwards = -byteSkip - 1; | |||
297 | /* HEY what if bsize fits in size_t but not in (signed) long? */ | |||
298 | if (fseek(dataFile, -AIR_CAST(long, bsize)((long)(bsize)) - backwards, SEEK_END2)) { | |||
299 | char stmp[AIR_STRLEN_SMALL(128+1)]; | |||
300 | biffAddf(NRRDnrrdBiffKey, "%s: failed to fseek(dataFile, %s, SEEK_END)", me, | |||
301 | airSprintSize_t(stmp, bsize)); | |||
302 | return 1; | |||
303 | } | |||
304 | if (nrrdStateVerboseIO >= 2) { | |||
305 | fprintf(stderr__stderrp, "(%s: actually skipped %d bytes)\n", | |||
306 | me, (int)ftell(dataFile)); | |||
307 | } | |||
308 | } else { | |||
309 | if ((stdin__stdinp == dataFile) || (-1==fseek(dataFile, byteSkip, SEEK_CUR1))) { | |||
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(-1) == skipRet) { | |||
316 | biffAddf(NRRDnrrdBiffKey, "%s: hit EOF skipping byte %ld of %ld", | |||
317 | me, skipi, byteSkip); | |||
318 | return 1; | |||
319 | } | |||
320 | } | |||
321 | } | |||
322 | } | |||
323 | return 0; | |||
324 | } | |||
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 | if (!( dataFile && nrrd && nio )) { | |||
338 | biffAddf(NRRDnrrdBiffKey, "%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 | if (_nrrdByteSkipSkip(dataFile, nrrd, nio, nio->byteSkip)) { | |||
344 | biffAddf(NRRDnrrdBiffKey, "%s: trouble", me); | |||
345 | return 1; | |||
346 | } | |||
347 | ||||
348 | return 0; | |||
349 | } | |||
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 | unsigned int llen; | |||
369 | NrrdIoState *nio; | |||
370 | int nfi; | |||
371 | airArray *mop; | |||
372 | ||||
373 | /* sanity check, for good measure */ | |||
374 | if (!nrrdSanity()) { | |||
375 | biffAddf(NRRDnrrdBiffKey, "%s: sanity check FAILED: have to fix and re-compile", | |||
376 | me); | |||
377 | return 1; | |||
378 | } | |||
379 | ||||
380 | if (!((file || string) && nrrd)) { | |||
381 | biffAddf(NRRDnrrdBiffKey, "%s: got NULL pointer", me); | |||
382 | return 1; | |||
383 | } | |||
384 | if (file && string) { | |||
385 | biffAddf(NRRDnrrdBiffKey, "%s: can't read from both file and string", me); | |||
386 | return 1; | |||
387 | } | |||
388 | mop = airMopNew(); | |||
389 | if (_nio) { | |||
390 | nio = _nio; | |||
391 | } else { | |||
392 | nio = nrrdIoStateNew(); | |||
393 | if (!nio) { | |||
394 | biffAddf(NRRDnrrdBiffKey, "%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 | nio->oldData = nrrd->data; | |||
403 | nio->oldDataSize = (nio->oldData | |||
404 | ? 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 | nrrd->data = NULL((void*)0); | |||
411 | ||||
412 | /* initialize given nrrd (but we have thwarted freeing existing memory) */ | |||
413 | nrrdInit(nrrd); | |||
414 | ||||
415 | /* tell the nio where to find the string to read from */ | |||
416 | nio->headerStringRead = string; | |||
417 | ||||
418 | if (_nrrdOneLine(&llen, nio, file)) { | |||
419 | biffAddf(NRRDnrrdBiffKey, "%s: error getting first line (containing \"magic\")", | |||
420 | me); | |||
421 | airMopError(mop); return 1; | |||
422 | } | |||
423 | if (!llen) { | |||
424 | biffAddf(NRRDnrrdBiffKey, "%s: immediately hit EOF", me); | |||
425 | airMopError(mop); return 1; | |||
426 | } | |||
427 | ||||
428 | nio->format = nrrdFormatUnknown; | |||
429 | for (nfi = nrrdFormatTypeUnknown+1; | |||
430 | nfi < nrrdFormatTypeLast; | |||
431 | nfi++) { | |||
432 | if (nrrdFormatArray[nfi]->contentStartsLike(nio)) { | |||
433 | nio->format = nrrdFormatArray[nfi]; | |||
434 | break; | |||
435 | } | |||
436 | } | |||
437 | if (nrrdFormatUnknown == nio->format) { | |||
438 | char linestart[AIR_STRLEN_SMALL(128+1)], stmp[AIR_STRLEN_SMALL(128+1)]; | |||
439 | airStrcpy(linestart, AIR_STRLEN_SMALL(128+1), nio->line); | |||
440 | if (strlen(linestart) != strlen(nio->line)) { | |||
441 | biffAddf(NRRDnrrdBiffKey, "%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(NRRDnrrdBiffKey, "%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 | if (string && nrrdFormatNRRD != nio->format) { | |||
451 | biffAddf(NRRDnrrdBiffKey, "%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 | if (nio->format->read(file, nrrd, nio)) { | |||
458 | biffAddf(NRRDnrrdBiffKey, "%s: trouble reading %s file", me, nio->format->name); | |||
459 | airMopError(mop); return 1; | |||
460 | } | |||
461 | ||||
462 | /* reshape up grayscale images, if desired */ | |||
463 | if (nio->format->isImage && 2 == nrrd->dim && nrrdStateGrayscaleImage3D) { | |||
464 | if (nrrdAxesInsert(nrrd, nrrd, 0)) { | |||
465 | biffAddf(NRRDnrrdBiffKey, "%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 | if (nio->oldData != nrrd->data) { | |||
473 | nio->oldData = airFree(nio->oldData); | |||
474 | nio->oldDataSize = 0; | |||
475 | } | |||
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 | if (_nrrdCheck(nrrd, AIR_FALSE0, AIR_TRUE1)) { | |||
481 | biffAddf(NRRDnrrdBiffKey, "%s: problem with nrrd after reading", me); | |||
482 | return 1; | |||
483 | } | |||
484 | ||||
485 | airMopOkay(mop); | |||
486 | return 0; | |||
487 | } | |||
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 | if (_nrrdRead(nrrd, file, NULL((void*)0), _nio)) { | |||
499 | biffAddf(NRRDnrrdBiffKey, "%s: trouble", me); | |||
500 | return 1; | |||
501 | } | |||
502 | return 0; | |||
503 | } | |||
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((void*)0), string, _nio)) { | |||
519 | biffAddf(NRRDnrrdBiffKey, "%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 | if (dirP) { | |||
543 | *dirP = (char *)airFree(*dirP); | |||
544 | } | |||
545 | if (baseP) { | |||
546 | *baseP = (char *)airFree(*baseP); | |||
547 | } | |||
548 | 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 | if (where && airStrlen(where) > 1) { | |||
558 | if (dirP) { | |||
559 | *dirP = airStrdup(name); | |||
560 | (*dirP)[where - name] = 0; | |||
561 | } | |||
562 | 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 | if (dirP) { | |||
570 | *dirP = airStrdup("."); | |||
571 | } | |||
572 | if (baseP) { | |||
573 | *baseP = airStrdup(name); | |||
574 | } | |||
575 | } | |||
576 | return; | |||
577 | } | |||
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 | if (!(nrrd && filename)) { | |||
617 | biffAddf(NRRDnrrdBiffKey, "%s: got NULL pointer", me); | |||
618 | return 1; | |||
619 | } | |||
620 | mop = airMopNew(); | |||
621 | if (!nio) { | |||
622 | nio = nrrdIoStateNew(); | |||
623 | if (!nio) { | |||
624 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't alloc I/O struct", me); | |||
625 | return 1; | |||
626 | } | |||
627 | airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); | |||
628 | } | |||
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 | _nrrdSplitName(&(nio->path), NULL((void*)0), filename); | |||
634 | /* printf("!%s: |%s|%s|\n", me, nio->dir, nio->base); */ | |||
635 | ||||
636 | if (!( file = airFopen(filename, stdin__stdinp, "rb") )) { | |||
637 | biffAddf(NRRDnrrdBiffKey, "%s: fopen(\"%s\",\"rb\") failed: %s", | |||
638 | me, filename, strerror(errno(*__error()))); | |||
639 | airMopError(mop); return 2; | |||
640 | } | |||
641 | airMopAdd(mop, file, (airMopper)airFclose, airMopOnError); | |||
642 | /* non-error exiting is handled below */ | |||
643 | ||||
644 | if (nrrdRead(nrrd, file, nio)) { | |||
645 | biffAddf(NRRDnrrdBiffKey, "%s: trouble reading \"%s\"", me, filename); | |||
646 | airMopError(mop); return 1; | |||
647 | } | |||
648 | ||||
649 | if (nrrdFormatNRRD == nio->format | |||
650 | && nio->keepNrrdDataFileOpen | |||
651 | && 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 | airFclose(file); | |||
658 | } | |||
659 | ||||
660 | airMopOkay(mop); | |||
661 | return 0; | |||
662 | } | |||
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(NRRDnrrdBiffKey, "%s: got NULL pointer", me); | |||
675 | return 1; | |||
676 | } | |||
677 | if (!( _nrrdContainsPercentThisAndMore(fnameFormat, 'u') )) { | |||
678 | biffAddf(NRRDnrrdBiffKey, "%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))((char *)(malloc(strlen(fnameFormat) + 128))); | |||
687 | if (!(fname)) { | |||
688 | biffAddf(NRRDnrrdBiffKey, "%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)__builtin___sprintf_chk (fname, 0, __builtin_object_size (fname , 2 > 1 ? 1 : 0), fnameFormat, num); | |||
697 | if (nrrdLoad(nin[nii], fname, nio)) { | |||
698 | biffAddf(NRRDnrrdBiffKey, "%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 | } |