GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/read.c Lines: 148 294 50.3 %
Date: 2017-05-26 Branches: 82 172 47.7 %

Line Branch Exec Source
1
/*
2
  Teem: Tools to process and visualize scientific data and images             .
3
  Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
4
  Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5
  Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6
7
  This library is free software; you can redistribute it and/or
8
  modify it under the terms of the GNU Lesser General Public License
9
  (LGPL) as published by the Free Software Foundation; either
10
  version 2.1 of the License, or (at your option) any later version.
11
  The terms of redistributing and/or modifying this software also
12
  include exceptions to the LGPL that facilitate static linking.
13
14
  This library is distributed in the hope that it will be useful,
15
  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
  Lesser General Public License for more details.
18
19
  You should have received a copy of the GNU Lesser General Public License
20
  along with this library; if not, write to Free Software Foundation, Inc.,
21
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
*/
23
24
#include "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
}