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