GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/write.c Lines: 256 524 48.9 %
Date: 2017-05-26 Branches: 170 341 49.9 %

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
/*
28
  #include <sys/types.h>
29
  #include <unistd.h>
30
*/
31
32
int
33
nrrdIoStateSet(NrrdIoState *nio, int parm, int value) {
34
  static const char me[]="nrrdIoStateSet";
35
36
  if (!nio) {
37
    biffAddf(NRRD, "%s: got NULL pointer", me);
38
    return 1;
39
  }
40
  if (!( AIR_IN_OP(nrrdIoStateUnknown, parm, nrrdIoStateLast) )) {
41
    biffAddf(NRRD, "%s: identifier %d not in valid range [%d,%d]", me,
42
             parm, nrrdIoStateUnknown+1, nrrdIoStateLast-1);
43
    return 1;
44
  }
45
  switch (parm) {
46
  case nrrdIoStateDetachedHeader:
47
    nio->detachedHeader = !!value;
48
    break;
49
  case nrrdIoStateBareText:
50
    nio->bareText = !!value;
51
    break;
52
  case nrrdIoStateCharsPerLine:
53
    if (value < 40) {
54
      biffAddf(NRRD, "%s: %d charsPerLine is awfully small", me, value);
55
      return 1;
56
    }
57
    /* cast won't lose info because "value" must be positive */
58
    nio->charsPerLine = AIR_CAST(unsigned int, value);
59
    break;
60
  case nrrdIoStateValsPerLine:
61
    if (value < 4) {
62
      biffAddf(NRRD, "%s: %d valsPerLine is awfully small", me, value);
63
      return 1;
64
    }
65
    /* cast won't lose info because "value" must be positive */
66
    nio->valsPerLine = AIR_CAST(unsigned int, value);
67
    break;
68
  case nrrdIoStateSkipData:
69
    nio->skipData = !!value;
70
    break;
71
  case nrrdIoStateKeepNrrdDataFileOpen:
72
    nio->keepNrrdDataFileOpen = !!value;
73
    break;
74
  case nrrdIoStateZlibLevel:
75
    if (!( AIR_IN_CL(-1, value, 9) )) {
76
      biffAddf(NRRD, "%s: zlibLevel %d invalid", me, value);
77
      return 1;
78
    }
79
    nio->zlibLevel = value;
80
    break;
81
  case nrrdIoStateZlibStrategy:
82
    if (!( AIR_IN_OP(nrrdZlibStrategyUnknown, value, nrrdZlibStrategyLast) )) {
83
      biffAddf(NRRD, "%s: zlibStrategy %d invalid", me, value);
84
      return 1;
85
    }
86
    nio->zlibStrategy = value;
87
    break;
88
  case nrrdIoStateBzip2BlockSize:
89
    if (!( AIR_IN_CL(-1, value, 9) )) {
90
      biffAddf(NRRD, "%s: bzip2BlockSize %d invalid", me, value);
91
      return 1;
92
    }
93
    nio->bzip2BlockSize = value;
94
    break;
95
  default:
96
    fprintf(stderr, "!%s: PANIC: didn't recognize parm %d\n", me, parm);
97
    return 1;
98
  }
99
  return 0;
100
}
101
102
int
103
nrrdIoStateEncodingSet(NrrdIoState *nio, const NrrdEncoding *encoding) {
104
  static const char me[]="nrrdIoStateEncodingSet";
105
106
  if (!( nio && encoding )) {
107
    if (nio) {
108
      nio->encoding = nrrdEncodingUnknown;
109
    }
110
    biffAddf(NRRD, "%s: got NULL pointer", me);
111
    return 1;
112
  }
113
  if (!encoding->available()) {
114
    nio->encoding = nrrdEncodingUnknown;
115
    biffAddf(NRRD, "%s: %s encoding isn't actually available", me,
116
             encoding->name);
117
    return 1;
118
  }
119
  nio->encoding = encoding;
120
  return 0;
121
}
122
123
int
124
nrrdIoStateFormatSet(NrrdIoState *nio, const NrrdFormat *format) {
125
  static const char me[]="nrrdIoStateFormatSet";
126
127
  if (!( nio && format )) {
128
    if (nio) {
129
      nio->format = nrrdFormatUnknown;
130
    }
131
    biffAddf(NRRD, "%s: got NULL pointer", me);
132
    return 1;
133
  }
134
  if (!format->available()) {
135
    nio->format = nrrdFormatUnknown;
136
    biffAddf(NRRD, "%s: %s format isn't actually available", me,
137
             format->name);
138
    return 1;
139
  }
140
  nio->format = format;
141
  return 0;
142
}
143
144
/*
145
** no biff
146
*/
147
int
148
nrrdIoStateGet(NrrdIoState *nio, int parm) {
149
  static const char me[]="nrrdIoStateGet";
150
  int value;
151
152
  if (!nio) {
153
    /* got NULL pointer */
154
    return -1;
155
  }
156
  if (!( AIR_IN_OP(nrrdIoStateUnknown, parm, nrrdIoStateLast) )) {
157
    /* got bogus parameter identifier */
158
    return -1;
159
  }
160
  switch (parm) {
161
  case nrrdIoStateDetachedHeader:
162
    value = !!nio->detachedHeader;
163
    break;
164
  case nrrdIoStateBareText:
165
    value = !!nio->bareText;
166
    break;
167
  case nrrdIoStateCharsPerLine:
168
    /* HEY: this cast is a bad because nio->charsPerLine is unsigned */
169
    value = AIR_CAST(int, nio->charsPerLine);
170
    break;
171
  case nrrdIoStateValsPerLine:
172
    /* HEY: this cast is a bad because nio->valsPerLine is unsigned */
173
    value = AIR_CAST(int, nio->valsPerLine);
174
    break;
175
  case nrrdIoStateSkipData:
176
    value = !!nio->skipData;
177
    break;
178
  case nrrdIoStateKeepNrrdDataFileOpen:
179
    value = !!nio->keepNrrdDataFileOpen;
180
    break;
181
  case nrrdIoStateZlibLevel:
182
    value = nio->zlibLevel;
183
    break;
184
  case nrrdIoStateZlibStrategy:
185
    value = nio->zlibStrategy;
186
    break;
187
  case nrrdIoStateBzip2BlockSize:
188
    value = nio->bzip2BlockSize;
189
    break;
190
  default:
191
    fprintf(stderr, "!%s: PANIC: didn't recognize parm %d\n", me, parm);
192
    return -1;
193
  }
194
  return value;
195
}
196
197
/*
198
** no biff
199
*/
200
const NrrdEncoding *
201
nrrdIoStateEncodingGet(NrrdIoState *nio) {
202
203
  return nio ? nio->encoding : nrrdEncodingUnknown;
204
}
205
206
/*
207
** no biff
208
*/
209
const NrrdFormat *
210
nrrdIoStateFormatGet(NrrdIoState *nio) {
211
212
  return nio ? nio->format : nrrdFormatUnknown;
213
}
214
215
void
216
_nrrdStrcatSpaceVector(char *str, unsigned int spaceDim,
217
                       const double val[NRRD_SPACE_DIM_MAX]) {
218
136
  char buff[AIR_STRLEN_MED];  /* bad Gordon */
219
  unsigned int dd;
220
221

136
  if (AIR_EXISTS(val[0])) {
222
128
    strcat(str, "(");
223
480
    for (dd=0; dd<spaceDim; dd++) {
224
180
      strcpy(buff, "");
225
180
      airSinglePrintf(NULL, buff, "%.17g", val[dd]);
226
180
      strcat(str, buff);
227
180
      sprintf(buff, "%s", dd+1 < spaceDim ? "," : ")");
228
180
      strcat(str, buff);
229
    }
230
  } else {
231
8
    strcat(str, _nrrdNoSpaceVector);
232
  }
233
  return;
234
68
}
235
236
int
237
_nrrdFieldInteresting(const Nrrd *nrrd, NrrdIoState *nio, int field) {
238
  int ret;
239
  unsigned int ai;
240
241
792
  if (!( nrrd
242

1188
         && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX)
243
396
         && nio
244
396
         && nio->encoding
245
396
         && AIR_IN_OP(nrrdField_unknown, field, nrrdField_last) )) {
246
    return 0;
247
  }
248
249
  ret = 0;
250







757
  switch (field) {
251
  case nrrdField_comment:
252
    /* comments and key/value pairs are always handled differently (by
253
       being printed explicity), so they are never "interesting" */
254
    break;
255
  case nrrdField_content:
256
12
    ret = !!(airStrlen(nrrd->content));
257
12
    break;
258
  case nrrdField_number:
259
    /* "number" is entirely redundant with "sizes", which is a
260
       required field.  Absolutely nothing is lost in eliding "number"
261
       from the header, so "number" is NEVER interesting.  Should this
262
       judgement later be found in error, this is the one place where
263
       the policy change can be implemented */
264
    break;
265
  case nrrdField_type:
266
    /* this is vital */
267
    ret = 1;
268
11
    break;
269
  case nrrdField_block_size:
270
11
    ret = (nrrdTypeBlock == nrrd->type);
271
11
    break;
272
  case nrrdField_dimension:
273
    /* this is vital */
274
    ret = 1;
275
11
    break;
276
  case nrrdField_space:
277
    /* its interesting if its known */
278
15
    ret = (nrrdSpaceUnknown != nrrd->space);
279
15
    break;
280
  case nrrdField_space_dimension:
281
    /* its interesting if its non-zero and if space is not known */
282
40
    ret = (nrrd->spaceDim > 0 && nrrdSpaceUnknown == nrrd->space);
283
15
    break;
284
  case nrrdField_sizes:
285
    /* this is vital */
286
    ret = 1;
287
11
    break;
288
  case nrrdField_spacings:
289
102
    for (ai=0; ai<nrrd->dim; ai++) {
290
39
      ret |= AIR_EXISTS(nrrd->axis[ai].spacing);
291
    }
292
    break;
293
  case nrrdField_thicknesses:
294
118
    for (ai=0; ai<nrrd->dim; ai++) {
295
44
      ret |= AIR_EXISTS(nrrd->axis[ai].thickness);
296
    }
297
    break;
298
  case nrrdField_axis_mins:
299
102
    for (ai=0; ai<nrrd->dim; ai++) {
300
39
      ret |= AIR_EXISTS(nrrd->axis[ai].min);
301
    }
302
    break;
303
  case nrrdField_axis_maxs:
304
102
    for (ai=0; ai<nrrd->dim; ai++) {
305
39
      ret |= AIR_EXISTS(nrrd->axis[ai].max);
306
    }
307
    break;
308
  case nrrdField_space_directions:
309
12
    ret = nrrd->spaceDim > 0;
310
12
    break;
311
  case nrrdField_centers:
312
102
    for (ai=0; ai<nrrd->dim; ai++) {
313
39
      ret |= (nrrdCenterUnknown != nrrd->axis[ai].center);
314
    }
315
    break;
316
  case nrrdField_kinds:
317
110
    for (ai=0; ai<nrrd->dim; ai++) {
318
41
      ret |= (nrrdKindUnknown != nrrd->axis[ai].kind);
319
    }
320
    break;
321
  case nrrdField_labels:
322
102
    for (ai=0; ai<nrrd->dim; ai++) {
323
39
      ret |= !!(airStrlen(nrrd->axis[ai].label));
324
    }
325
    break;
326
  case nrrdField_units:
327
102
    for (ai=0; ai<nrrd->dim; ai++) {
328
39
      ret |= !!(airStrlen(nrrd->axis[ai].units));
329
    }
330
    break;
331
  case nrrdField_min:
332
  case nrrdField_max:
333
    /* these no longer exist in the Nrrd struct; we never write them */
334
    ret = AIR_FALSE;
335
22
    break;
336
  case nrrdField_old_min:
337
12
    ret = AIR_EXISTS(nrrd->oldMin);
338
12
    break;
339
  case nrrdField_old_max:
340
12
    ret = AIR_EXISTS(nrrd->oldMax);
341
12
    break;
342
  case nrrdField_endian:
343
33
    ret = nio->encoding->endianMatters && 1 < nrrdElementSize(nrrd);
344
11
    break;
345
  case nrrdField_encoding:
346
    /* this is vital */
347
    ret = 1;
348
11
    break;
349
  case nrrdField_line_skip:
350
11
    ret = nio->lineSkip > 0;
351
11
    break;
352
  case nrrdField_byte_skip:
353
11
    ret = nio->byteSkip != 0;
354
11
    break;
355
  case nrrdField_keyvalue:
356
    /* comments and key/value pairs are always handled differently (by
357
       being printed explicity), so they are never "interesting" */
358
    break;
359
  case nrrdField_sample_units:
360
14
    ret = !!airStrlen(nrrd->sampleUnits);
361
14
    break;
362
  case nrrdField_space_units:
363
78
    for (ai=0; ai<nrrd->spaceDim; ai++) {
364
27
      ret |= !!(airStrlen(nrrd->spaceUnits[ai]));
365
    }
366
    break;
367
  case nrrdField_space_origin:
368
    /* we're trusting other validity checks to ensure that
369
       all the coeffs exist or not, together */
370
12
    ret = (nrrd->spaceDim > 0
371
33
           && AIR_EXISTS(nrrd->spaceOrigin[0]));
372
12
    break;
373
  case nrrdField_measurement_frame:
374
    /* we're trusting other validity checks to ensure that
375
       all the coeffs exist or not, together */
376
23
    ret = (nrrd->spaceDim > 0
377
64
           && AIR_EXISTS(nrrd->measurementFrame[0][0]));
378
23
    break;
379
  case nrrdField_data_file:
380
    /* detached header was either requested or is required */
381
11
    ret = (nio->detachedHeader
382
22
           || nio->dataFNFormat
383
33
           || nio->dataFNArr->len > 1);
384
11
    break;
385
  }
386
387
396
  return ret;
388
396
}
389
390
/*
391
** _nrrdSprintFieldInfo
392
**
393
** this prints "<prefix><field>: <info>" into *strP (after allocating it for
394
** big enough, usually with a stupidly big margin of error), in a form
395
** suitable to be written to NRRD or other image headers.  This will always
396
** print something (for valid inputs), even stupid <info>s like
397
** "(unknown endian)".  It is up to the caller to decide which fields
398
** are worth writing, via _nrrdFieldInteresting().
399
**
400
** NOTE: some of these fields make sense in non-NRRD files (e.g. all
401
** the per-axis information), but many only make sense in NRRD files.
402
** This is just one example of NRRD-format-specific stuff that is not
403
** in formatNRRD.c
404
*/
405
void
406
_nrrdSprintFieldInfo(char **strP, const char *prefix,
407
                     const Nrrd *nrrd, NrrdIoState *nio, int field,
408
                     int dropAxis0) {
409
  static const char me[]="_nrrdSprintFieldInfo";
410
242
  char buff[AIR_STRLEN_MED], *fnb, stmp[AIR_STRLEN_SMALL],
411
    *strtmp=NULL;
412
121
  double colvec[NRRD_SPACE_DIM_MAX];
413
  const char *fs;
414
  unsigned int ii, dd,
415
    uintStrlen = 11,
416
    size_tStrlen = 33,
417
    doubleStrlen = 513;
418
  size_t fslen, fdlen, maxl;
419
  int endi;
420
421

242
  if (!( strP && prefix
422
121
         && nrrd
423
242
         && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX)
424
121
         && AIR_IN_OP(nrrdField_unknown, field, nrrdField_last) )) {
425
    return;
426
  }
427
  /* As of Sun Dec  2 01:57:48 CST 2012 (revision 5832) the only
428
     places where this function is called is when it has been guarded
429
     by "if (_nrrdFieldInteresting())" (except for in formatText.c when
430
     its called on the dimension field, which is always interesting).
431
     So, the following:
432
433
     if (!_nrrdFieldInteresting(nrrd, nio, field)) {
434
       *strP = airStrdup("");
435
     }
436
437
     was redundant and confusingly created the appearance of a memory
438
     leak waiting to happen.  We now let the default switch statement
439
     set *strP to NULL (all the other cases set it), to smoke out
440
     errors in how this function is called */
441
442
121
  fs = airEnumStr(nrrdField, field);
443
121
  fslen = strlen(prefix) + strlen(fs) + strlen(": ") + 1;
444








121
  switch (field) {
445
  case nrrdField_comment:
446
  case nrrdField_keyvalue:
447
    fprintf(stderr, "%s: CONFUSION: why are you calling me on \"%s\"?\n", me,
448
            airEnumStr(nrrdField, nrrdField_comment));
449
    *strP = airStrdup("");
450
    break;
451
  case nrrdField_content:
452
2
    strtmp = airOneLinify(airStrdup(nrrd->content));
453
2
    *strP = AIR_CALLOC(fslen + strlen(strtmp), char);
454
2
    sprintf(*strP, "%s%s: %s", prefix, fs, strtmp);
455
2
    airFree(strtmp); strtmp = NULL;
456
2
    break;
457
  case nrrdField_number:
458
    *strP = AIR_CALLOC(fslen + size_tStrlen, char);
459
    sprintf(*strP, "%s%s: %s", prefix, fs,
460
            airSprintSize_t(stmp, nrrdElementNumber(nrrd)));
461
    break;
462
  case nrrdField_type:
463
11
    *strP = AIR_CALLOC(fslen + strlen(airEnumStr(nrrdType, nrrd->type)), char);
464
11
    sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdType, nrrd->type));
465
11
    break;
466
  case nrrdField_block_size:
467
    *strP = AIR_CALLOC(fslen + size_tStrlen, char);
468
    sprintf(*strP, "%s%s: %s", prefix, fs,
469
            airSprintSize_t(stmp, nrrd->blockSize));
470
    break;
471
  case nrrdField_dimension:
472
11
    *strP = AIR_CALLOC(fslen + uintStrlen, char);
473
11
    sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->dim);
474
11
    break;
475
  case nrrdField_space:
476
8
    *strP = AIR_CALLOC(fslen
477
                       + strlen(airEnumStr(nrrdSpace, nrrd->space)), char);
478
8
    sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdSpace, nrrd->space));
479
8
    break;
480
  case nrrdField_space_dimension:
481
1
    *strP = AIR_CALLOC(fslen + uintStrlen, char);
482
1
    sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->spaceDim);
483
1
    break;
484
    /* ---- begin per-axis fields ---- */
485
  case nrrdField_sizes:
486
11
    *strP = AIR_CALLOC(fslen + nrrd->dim*(size_tStrlen + 1), char);
487
11
    sprintf(*strP, "%s%s:", prefix, fs);
488
96
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
489
37
      sprintf(buff, " %s", airSprintSize_t(stmp, nrrd->axis[ii].size));
490
37
      strcat(*strP, buff);
491
    }
492
    break;
493
  case nrrdField_spacings:
494
    *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char);
495
    sprintf(*strP, "%s%s:", prefix, fs);
496
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
497
      airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].spacing);
498
      strcat(*strP, buff);
499
    }
500
    break;
501
  case nrrdField_thicknesses:
502
    *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char);
503
    sprintf(*strP, "%s%s:", prefix, fs);
504
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
505
      airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].thickness);
506
      strcat(*strP, buff);
507
    }
508
    break;
509
  case nrrdField_axis_mins:
510
2
    *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char);
511
2
    sprintf(*strP, "%s%s:", prefix, fs);
512
10
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
513
3
      airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].min);
514
3
      strcat(*strP, buff);
515
    }
516
    break;
517
  case nrrdField_axis_maxs:
518
2
    *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char);
519
2
    sprintf(*strP, "%s%s:", prefix, fs);
520
10
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
521
3
      airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].max);
522
3
      strcat(*strP, buff);
523
    }
524
    break;
525
  case nrrdField_space_directions:
526
9
    *strP = AIR_CALLOC(fslen
527
                       + nrrd->dim*nrrd->spaceDim*(doubleStrlen
528
                                                   + strlen("(,) ")), char);
529
9
    sprintf(*strP, "%s%s: ", prefix, fs);
530
88
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
531
70
      _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim,
532
35
                             nrrd->axis[ii].spaceDirection);
533
35
      if (ii < nrrd->dim-1) {
534
26
        strcat(*strP, " ");
535
26
      }
536
    }
537
    break;
538
  case nrrdField_centers:
539
    fdlen = 0;
540
98
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
541
106
      fdlen += 1 + airStrlen(nrrd->axis[ii].center
542
30
                             ? airEnumStr(nrrdCenter, nrrd->axis[ii].center)
543
                             : NRRD_UNKNOWN);
544
    }
545
11
    *strP = AIR_CALLOC(fslen + fdlen, char);
546
11
    sprintf(*strP, "%s%s:", prefix, fs);
547
98
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
548
106
      sprintf(buff, " %s",
549
              (nrrd->axis[ii].center
550
               ? airEnumStr(nrrdCenter, nrrd->axis[ii].center)
551
               : NRRD_UNKNOWN));
552
38
      strcat(*strP, buff);
553
    }
554
    break;
555
  case nrrdField_kinds:
556
    fdlen = 0;
557
92
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
558
108
      fdlen += 1 + airStrlen(nrrd->axis[ii].kind
559
36
                             ? airEnumStr(nrrdKind, nrrd->axis[ii].kind)
560
                             : NRRD_UNKNOWN);
561
    }
562
10
    *strP = AIR_CALLOC(fslen + fdlen, char);
563
10
    sprintf(*strP, "%s%s:", prefix, fs);
564
92
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
565
108
      sprintf(buff, " %s",
566
              (nrrd->axis[ii].kind
567
               ? airEnumStr(nrrdKind, nrrd->axis[ii].kind)
568
               : NRRD_UNKNOWN));
569
36
      strcat(*strP, buff);
570
    }
571
    break;
572
  case nrrdField_labels:
573
  case nrrdField_units:
574
#define LABEL_OR_UNITS (nrrdField_labels == field \
575
                        ? nrrd->axis[ii].label \
576
                        : nrrd->axis[ii].units)
577
    fdlen = 0;
578
18
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
579
      /* The "2*" is because at worst every character needs escaping.
580
         The "+ 3" for the |" "| between each part */
581
18
      fdlen += 2*airStrlen(LABEL_OR_UNITS) + 3;
582
    }
583
3
    fdlen += 1; /* for '\0' */
584
3
    *strP = AIR_CALLOC(fslen + fdlen, char);
585
3
    sprintf(*strP, "%s%s:", prefix, fs);
586
18
    for (ii=!!dropAxis0; ii<nrrd->dim; ii++) {
587
6
      strcat(*strP, " \"");
588


18
      if (nrrdField_labels == field
589
12
          ? airStrlen(nrrd->axis[ii].label)
590
          : airStrlen(nrrd->axis[ii].units)) {
591
15
        _nrrdWriteEscaped(NULL, *strP, LABEL_OR_UNITS,
592
                          "\"", _NRRD_WHITESPACE_NOTAB);
593
5
      }
594
6
      strcat(*strP, "\"");
595
    }
596
#undef LABEL_OR_UNITS
597
    break;
598
    /* ---- end per-axis fields ---- */
599
  case nrrdField_min:
600
  case nrrdField_max:
601
    /* we're basically a no-op, now that these fields became meaningless */
602
    *strP = AIR_CALLOC(fslen + doubleStrlen, char);
603
    sprintf(*strP, "%s%s: 0.0", prefix, fs);
604
    strcat(*strP, buff);
605
    break;
606
  case nrrdField_old_min:
607
    *strP = AIR_CALLOC(fslen + doubleStrlen, char);
608
    sprintf(*strP, "%s%s: ", prefix, fs);
609
    airSinglePrintf(NULL, buff, "%.17g", nrrd->oldMin);
610
    strcat(*strP, buff);
611
    break;
612
  case nrrdField_old_max:
613
    *strP = AIR_CALLOC(fslen + doubleStrlen, char);
614
    sprintf(*strP, "%s%s: ", prefix, fs);
615
    airSinglePrintf(NULL, buff, "%.17g", nrrd->oldMax);
616
    strcat(*strP, buff);
617
    break;
618
  case nrrdField_endian:
619
11
    if (airEndianUnknown != nio->endian) {
620
      /* we know a specific endianness because either it was recorded as
621
         part of "unu make -h", or it was set (and data was possibly
622
         altered) as part of "unu save" */
623
      endi = nio->endian;
624
    } else {
625
      /* we record our current architecture's endian because we're
626
         going to writing out data */
627
11
      endi = airMyEndian();
628
    }
629
11
    *strP = AIR_CALLOC(fslen + strlen(airEnumStr(airEndian, endi)), char);
630
11
    sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(airEndian, endi));
631
11
    break;
632
  case nrrdField_encoding:
633
11
    *strP = AIR_CALLOC(fslen + strlen(nio->encoding->name), char);
634
11
    sprintf(*strP, "%s%s: %s", prefix, fs, nio->encoding->name);
635
11
    break;
636
  case nrrdField_line_skip:
637
    *strP = AIR_CALLOC(fslen + uintStrlen, char);
638
    sprintf(*strP, "%s%s: %d", prefix, fs, nio->lineSkip);
639
    break;
640
  case nrrdField_byte_skip:
641
    *strP = AIR_CALLOC(fslen + uintStrlen, char);
642
    sprintf(*strP, "%s%s: %ld", prefix, fs, nio->byteSkip);
643
    break;
644
  case nrrdField_sample_units:
645
    strtmp = airOneLinify(airStrdup(nrrd->sampleUnits));
646
    *strP = AIR_CALLOC(fslen + strlen(strtmp), char);
647
    sprintf(*strP, "%s%s: \"%s\"", prefix, fs, strtmp);
648
    airFree(strtmp); strtmp = NULL;
649
    break;
650
  case nrrdField_space_units:
651
    fdlen = 0;
652
8
    for (ii=0; ii<nrrd->spaceDim; ii++) {
653
      /* The "2*" is because at worst every character needs escaping.
654
         See note in formatNRRD.c about how even though its not part
655
         of the format, we have worst-case scenario of having to
656
         escape a space units which is nothing but ". The "+ 3" for
657
         the |" "| between each part */
658
3
      fdlen += 2*airStrlen(nrrd->spaceUnits[ii]) + 3;
659
    }
660
1
    fdlen += 1; /* for '\0' */
661
1
    *strP = AIR_CALLOC(fslen + fdlen, char);
662
1
    sprintf(*strP, "%s%s:", prefix, fs);
663
8
    for (ii=0; ii<nrrd->spaceDim; ii++) {
664
3
      strcat(*strP, " \"");
665
3
      if (airStrlen(nrrd->spaceUnits[ii])) {
666
3
        _nrrdWriteEscaped(NULL, *strP, nrrd->spaceUnits[ii],
667
                          "\"", _NRRD_WHITESPACE_NOTAB);
668
3
      }
669
3
      strcat(*strP, "\"");
670
    }
671
    break;
672
  case nrrdField_space_origin:
673
9
    *strP = AIR_CALLOC(fslen + nrrd->spaceDim*(doubleStrlen
674
                                               + strlen("(,) ")), char);
675
9
    sprintf(*strP, "%s%s: ", prefix, fs);
676
9
    _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->spaceOrigin);
677
9
    break;
678
  case nrrdField_measurement_frame:
679
8
    *strP = AIR_CALLOC(fslen + (nrrd->spaceDim*
680
                                nrrd->spaceDim*(doubleStrlen
681
                                                + strlen("(,) "))), char);
682
8
    sprintf(*strP, "%s%s: ", prefix, fs);
683
64
    for (dd=0; dd<nrrd->spaceDim; dd++) {
684
192
      for (ii=0; ii<nrrd->spaceDim; ii++) {
685
72
        colvec[ii] = nrrd->measurementFrame[dd][ii];
686
      }
687
24
      _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, colvec);
688
24
      if (dd < nrrd->spaceDim-1) {
689
16
        strcat(*strP, " ");
690
16
      }
691
    }
692
    break;
693
  case nrrdField_data_file:
694
    /* NOTE: this comes last (nrrdField_data_file is the highest-valued
695
       member of the nrrdField* enum) because the "LIST" form of the
696
       data file specification requires that the following lines be
697
       the filenames */
698
    /* error checking elsewhere: assumes there is data file info */
699
    if (nio->dataFNFormat) {
700
      *strP = AIR_CALLOC(fslen + strlen(nio->dataFNFormat) + 4*uintStrlen,
701
                         char);
702
      if (nio->dataFileDim == nrrd->dim-1) {
703
        sprintf(*strP, "%s%s: %s %d %d %d", prefix, fs, nio->dataFNFormat,
704
                nio->dataFNMin, nio->dataFNMax, nio->dataFNStep);
705
      } else {
706
        sprintf(*strP, "%s%s: %s %d %d %d %u", prefix, fs, nio->dataFNFormat,
707
                nio->dataFNMin, nio->dataFNMax, nio->dataFNStep,
708
                nio->dataFileDim);
709
      }
710
    } else if (nio->dataFNArr->len > 1) {
711
      maxl = 0;
712
      for (ii=0; ii<nio->dataFNArr->len; ii++) {
713
        maxl = AIR_MAX(maxl, strlen(nio->dataFN[ii]));
714
      }
715
      *strP = AIR_CALLOC(fslen + strlen(NRRD_LIST_FLAG)
716
                         + uintStrlen + nio->dataFNArr->len * (maxl + 1),
717
                         char);
718
      fnb = AIR_CALLOC(fslen + strlen(NRRD_LIST_FLAG)
719
                       + uintStrlen + maxl + 1, char);
720
      if (nio->dataFileDim == nrrd->dim-1) {
721
        sprintf(*strP, "%s%s: LIST\n", prefix, fs);
722
      } else {
723
        sprintf(*strP, "%s%s: LIST %u\n", prefix, fs, nio->dataFileDim);
724
      }
725
      for (ii=0; ii<nio->dataFNArr->len; ii++) {
726
        sprintf(fnb, "%s%s", nio->dataFN[ii],
727
                ii<nio->dataFNArr->len-1 ? "\n" : "");
728
        strcat(*strP, fnb);
729
      }
730
      free(fnb);
731
    } else {
732
      /* there is some ambiguity between a "LIST" of length one,
733
         and a single explicit data filename, but that's harmless */
734
      *strP = AIR_CALLOC(fslen + strlen("./")
735
                         + strlen(nio->dataFN[0]) + 1, char);
736
      sprintf(*strP, "%s%s: %s%s", prefix, fs,
737
              /* this is a favor to older readers that can deal with
738
                 this NRRD file because its being saved in a NRRD0003
739
                 (or below) version, so we don't want to confuse them
740
                 by not having the old explicit header-relative flag */
741
              (_nrrdFormatNRRD_whichVersion(nrrd, nio) < 4 ? "./" : ""),
742
              nio->dataFN[0]);
743
    }
744
    break;
745
  default:
746
    fprintf(stderr, "%s: CONFUSION: field %d unrecognized\n", me, field);
747
    *strP = NULL;
748
    break;
749
  }
750
751
121
  return;
752
121
}
753
754
/*
755
** _nrrdFprintFieldInfo
756
**
757
** convenience wrapper around _nrrdSprintFieldInfo, for writing into
758
** a file.  Same caveats here: use _nrrdFieldInteresting
759
*/
760
void
761
_nrrdFprintFieldInfo(FILE *file, const char *prefix,
762
                     const Nrrd *nrrd, NrrdIoState *nio, int field,
763
                     int dropAxis0) {
764
242
  char *line=NULL;
765
766
121
  _nrrdSprintFieldInfo(&line, prefix, nrrd, nio, field, dropAxis0);
767
121
  if (line) {
768
121
    fprintf(file, "%s\n", line);
769
121
    free(line);
770
121
  }
771
  return;
772
121
}
773
774
int
775
_nrrdEncodingMaybeSet(NrrdIoState *nio) {
776
  static const char me[]="_nrrdEncodingMaybeSet";
777
778
48
  if (!nio) {
779
    biffAddf(NRRD, "%s: got NULL pointer", me);
780
    return 1;
781
  }
782
24
  if (!nio->encoding) {
783
    biffAddf(NRRD, "%s: invalid (NULL) encoding", me);
784
    return 1;
785
  }
786
24
  if (nrrdEncodingUnknown == nio->encoding) {
787
12
    nio->encoding = nrrdEncodingArray[nrrdDefaultWriteEncodingType];
788
12
  }
789
24
  if (!nio->encoding->available()) {
790
    biffAddf(NRRD, "%s: %s encoding not available in this Teem build",
791
             me, nio->encoding->name);
792
    return 1;
793
  }
794
24
  return 0;
795
24
}
796
797
/*
798
** we can assume (via action of caller nrrdSave) that nio->encoding
799
** has been set
800
**
801
** we must set nio->format to something useful/non-trivial
802
*/
803
int
804
_nrrdFormatMaybeGuess(const Nrrd *nrrd, NrrdIoState *nio,
805
                      const char *filename) {
806
  static const char me[]="_nrrdFormatMaybeGuess";
807
24
  char mesg[AIR_STRLEN_MED];
808
  int fi, guessed, available, fits;
809
810
12
  if (!nio->format) {
811
    biffAddf(NRRD, "%s: got invalid (NULL) format", me);
812
    return 1;
813
  }
814
12
  if (nrrdFormatUnknown == nio->format) {
815
26
    for (fi = nrrdFormatTypeUnknown+1;
816
13
         fi < nrrdFormatTypeLast;
817
1
         fi++) {
818
13
      if (nrrdFormatArray[fi]->nameLooksLike(filename)) {
819
12
        nio->format = nrrdFormatArray[fi];
820
12
        break;
821
      }
822
    }
823
12
    if (nrrdFormatUnknown == nio->format) {
824
      /* no nameLooksLike() returned non-zero, punt */
825
      nio->format = nrrdFormatNRRD;
826
    }
827
    guessed = AIR_TRUE;
828
12
  } else {
829
    guessed = AIR_FALSE;
830
  }
831
12
  available = nio->format->available();
832
12
  fits = nio->format->fitsInto(nrrd, nio->encoding, AIR_FALSE);
833
  /* !available ==> !fits, by the nature of fitsInto() */
834
12
  if (!( available && fits )) {
835
    sprintf(mesg, "can not use %s format: %s", nio->format->name,
836
            (!available
837
             ? "not available in this Teem build"
838
             : "array doesn\'t fit"));
839
    if (guessed) {
840
      if (1 <= nrrdStateVerboseIO) {
841
        fprintf(stderr, "(%s: %s --> saving to NRRD format)\n", me, mesg);
842
      }
843
      nio->format = nrrdFormatNRRD;
844
    } else {
845
      /* problem: this was the format someone explicitly requested */
846
      biffAddf(NRRD, "%s: %s", me, mesg);
847
      return 1;
848
    }
849
  }
850
851
12
  return 0;
852
12
}
853
854
int
855
_nrrdFormatMaybeSet(NrrdIoState *nio) {
856
  static const char me[]="_nrrdFormatMaybeSet";
857
858
24
  if (!nio->format) {
859
    biffAddf(NRRD, "%s: invalid (NULL) format", me);
860
    return 1;
861
  }
862
12
  if (nrrdFormatUnknown == nio->format) {
863
    nio->format = nrrdFormatNRRD;
864
  }
865
12
  if (!nio->format->available()) {
866
    biffAddf(NRRD, "%s: %s format not available in this Teem build",
867
             me, nio->format->name);
868
    return 1;
869
  }
870
12
  return 0;
871
12
}
872
873
/*
874
** _nrrdWrite
875
**
876
** Write a nrrd to given file or string (allocated by nrrd), using the
877
** format and and encoding indicated in nio.  Cleverness should be
878
** isolated and collected here: by the time nio->format->write() is
879
** called, all writing parameters must be given explicitly, and their
880
** appropriateness is explicitly tested
881
*/
882
int
883
_nrrdWrite(FILE *file, char **stringP, const Nrrd *nrrd, NrrdIoState *_nio) {
884
  static const char me[]="_nrrdWrite";
885
  NrrdIoState *nio;
886
  airArray *mop;
887
888
24
  if (!((file || stringP) && nrrd)) {
889
    biffAddf(NRRD, "%s: got NULL pointer", me);
890
    return 1;
891
  }
892
12
  if (file && stringP) {
893
    biffAddf(NRRD, "%s: can't write to both file and string", me);
894
    return 1;
895
  }
896
12
  if (nrrdCheck(nrrd)) {
897
    biffAddf(NRRD, "%s:", me);
898
    return 1;
899
  }
900
12
  mop = airMopNew();
901
12
  if (_nio) {
902
    nio = _nio;
903
12
  } else {
904
    nio = nrrdIoStateNew();
905
    if (!nio) {
906
      biffAddf(NRRD, "%s: couldn't alloc local NrrdIoState", me);
907
      airMopError(mop); return 1;
908
    }
909
    airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways);
910
  }
911
24
  if (_nrrdEncodingMaybeSet(nio)
912
24
      || _nrrdFormatMaybeSet(nio)) {
913
    biffAddf(NRRD, "%s: ", me);
914
    airMopError(mop); return 1;
915
  }
916

24
  if (nio->byteSkip || nio->lineSkip) {
917
    /* NOTE: unu make bypasses this by calling nrrdFormatNRRD->write()
918
       directly */
919
    biffAddf(NRRD, "%s: can't generate line or byte skips on data write", me);
920
    airMopError(mop); return 1;
921
  }
922
923
12
  if (stringP) {
924
    if (nrrdFormatNRRD != nio->format) {
925
      biffAddf(NRRD, "%s: sorry, can only write %s files to strings (not %s)",
926
               me, nrrdFormatNRRD->name, nio->format->name);
927
      airMopError(mop); return 1;
928
    }
929
    /* we do this in two passes; first see how much room is needed
930
       for the header, then allocate, then write the header */
931
    nio->learningHeaderStrlen = AIR_TRUE;
932
    if (nio->format->write(NULL, nrrd, nio)) {
933
      biffAddf(NRRD, "%s:", me);
934
      airMopError(mop); return 1;
935
    }
936
    *stringP = AIR_MALLOC(nio->headerStrlen + 1, char);
937
    if (!*stringP) {
938
      biffAddf(NRRD, "%s: couldn't allocate header string (%u len )",
939
               me, nio->headerStrlen);
940
      airMopError(mop); return 1;
941
    }
942
    nio->learningHeaderStrlen = AIR_FALSE;
943
    nio->headerStringWrite = *stringP;
944
    if (nio->format->write(NULL, nrrd, nio)) {
945
      biffAddf(NRRD, "%s:", me);
946
      airMopError(mop); return 1;
947
    }
948
  } else {
949
    /* call the writer appropriate for the format */
950
12
    if (nio->format->write(file, nrrd, nio)) {
951
      biffAddf(NRRD, "%s:", me);
952
      airMopError(mop); return 1;
953
    }
954
  }
955
956
12
  airMopOkay(mop);
957
12
  return 0;
958
12
}
959
960
/*
961
******** nrrdWrite
962
**
963
** wrapper around _nrrdWrite; writes to a FILE*
964
*/
965
int
966
nrrdWrite(FILE *file, const Nrrd *nrrd, NrrdIoState *_nio) {
967
  static const char me[]="nrrdWrite";
968
969
24
  if (_nrrdWrite(file, NULL, nrrd, _nio)) {
970
    biffAddf(NRRD, "%s: trouble", me);
971
    return 1;
972
  }
973
12
  return 0;
974
12
}
975
976
/*
977
******** nrrdStringWrite
978
**
979
** wrapper around _nrrdWrite; *allocates* and writes to a string
980
*/
981
int
982
nrrdStringWrite(char **stringP, const Nrrd *nrrd, NrrdIoState *_nio) {
983
  static const char me[]="nrrdStringWrite";
984
985
  if (_nrrdWrite(NULL, stringP, nrrd, _nio)) {
986
    biffAddf(NRRD, "%s: trouble", me);
987
    return 1;
988
  }
989
  return 0;
990
}
991
992
/*
993
******** nrrdSave
994
**
995
** save a given nrrd to a given filename, with cleverness to guess
996
** format if not specified by the caller
997
**
998
** currently, for NRRD format files, we play the detached header game
999
** whenever the filename ends in NRRD_EXT_NHDR, and when we play this
1000
** game, the data file is ALWAYS header relative.
1001
*/
1002
int
1003
nrrdSave(const char *filename, const Nrrd *nrrd, NrrdIoState *nio) {
1004
  static const char me[]="nrrdSave";
1005
  FILE *file;
1006
  airArray *mop;
1007
1008
24
  if (!(nrrd && filename)) {
1009
    biffAddf(NRRD, "%s: got NULL pointer", me);
1010
    return 1;
1011
  }
1012
12
  mop = airMopNew();
1013
12
  if (!nio) {
1014
12
    nio = nrrdIoStateNew();
1015
12
    if (!nio) {
1016
      biffAddf(NRRD, "%s: couldn't alloc local NrrdIoState", me);
1017
      return 1;
1018
    }
1019
12
    airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways);
1020
12
  }
1021
24
  if (_nrrdEncodingMaybeSet(nio)
1022
24
      || _nrrdFormatMaybeGuess(nrrd, nio, filename)) {
1023
    biffAddf(NRRD, "%s: ", me);
1024
    airMopError(mop); return 1;
1025
  }
1026
1027
23
  if (nrrdFormatNRRD == nio->format
1028
23
      && airEndsWith(filename, NRRD_EXT_NHDR)) {
1029
    nio->detachedHeader = AIR_TRUE;
1030
    _nrrdSplitName(&(nio->path), &(nio->base), filename);
1031
    /* nix the ".nhdr" suffix */
1032
    nio->base[strlen(nio->base) - strlen(NRRD_EXT_NHDR)] = 0;
1033
    /* nrrdFormatNRRD->write will do the rest */
1034
  } else {
1035
12
    nio->detachedHeader = AIR_FALSE;
1036
  }
1037
1038
12
  if (!( file = airFopen(filename, stdout, "wb") )) {
1039
    biffAddf(NRRD, "%s: couldn't fopen(\"%s\",\"wb\"): %s",
1040
             me, filename, strerror(errno));
1041
    airMopError(mop); return 1;
1042
  }
1043
12
  airMopAdd(mop, file, (airMopper)airFclose, airMopAlways);
1044
1045
12
  if (nrrdWrite(file, nrrd, nio)) {
1046
    biffAddf(NRRD, "%s:", me);
1047
    airMopError(mop); return 1;
1048
  }
1049
1050
12
  airMopOkay(mop);
1051
12
  return 0;
1052
12
}
1053
1054
int
1055
nrrdSaveMulti(const char *fnameFormat, const Nrrd *const *nin,
1056
              unsigned int ninLen, unsigned int numStart, NrrdIoState *nio) {
1057
  static const char me[]="nrrdSaveMulti";
1058
  char *fname;
1059
  airArray *mop;
1060
  unsigned int nii;
1061
1062
  if (!( fnameFormat && nin )) {
1063
    biffAddf(NRRD, "%s: got NULL pointer", me);
1064
    return 1;
1065
  }
1066
  if (!( _nrrdContainsPercentThisAndMore(fnameFormat, 'u') )) {
1067
    biffAddf(NRRD, "%s: given format \"%s\" doesn't seem to "
1068
             "have the \"%%u\" conversion specification to sprintf "
1069
             "an unsigned int\n", me, fnameFormat);
1070
    return 1;
1071
  }
1072
1073
  mop = airMopNew();
1074
  /* should be big enough for the number replacing the format sequence */
1075
  fname = AIR_CALLOC(strlen(fnameFormat) + 128, char);
1076
  if (!(fname)) {
1077
    biffAddf(NRRD, "%s: couldn't allocate local fname buffer", me);
1078
    airMopError(mop); return 1;
1079
  }
1080
  airMopAdd(mop, fname, airFree, airMopAlways);
1081
1082
  for (nii=0; nii<ninLen; nii++) {
1083
    unsigned int num;
1084
    num = numStart + nii;
1085
    sprintf(fname, fnameFormat, num);
1086
    if (nrrdSave(fname, nin[nii], nio)) {
1087
      biffAddf(NRRD, "%s: trouble saving nin[%u] to %s", me, nii, fname);
1088
      airMopError(mop); return 1;
1089
    }
1090
    /* HEY: GLK hopes that the nio doesn't have any state that needs
1091
       resetting, but we can't call nrrdIoStateInit() because that
1092
       would negate the purpose of sending in the nio for all but the
1093
       first saved nrrd */
1094
  }
1095
1096
  airMopOkay(mop);
1097
  return 0;
1098
}