From 193ef44e5be9fc6252f6bdfa934bad74e63ece21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=9Clgen?= Date: Fri, 16 Feb 2024 17:39:59 +0000 Subject: [PATCH] Upgrade RNifti and dependencies --- niftyreg_build_version.txt | 2 +- reg-io/RNifti.h | 2 +- reg-io/RNifti/NiftiImage.h | 114 +++++-- reg-io/RNifti/NiftiImage_impl.h | 76 +++-- reg-io/RNifti/NiftiImage_print.h | 6 +- reg-io/niftilib/nifti1.h | 2 +- reg-io/niftilib/nifti1_io.c | 407 ++++++++++++++++--------- reg-io/niftilib/nifti1_io.h | 8 +- reg-io/niftilib/nifti1_io_version.h | 16 + reg-io/niftilib/nifti2_io.c | 442 +++++++++++++++++----------- reg-io/niftilib/nifti2_io.h | 17 +- reg-io/niftilib/nifti2_io_version.h | 16 + reg-io/znzlib/znzlib.c | 12 +- reg-io/znzlib/znzlib.h | 17 +- 14 files changed, 750 insertions(+), 387 deletions(-) create mode 100644 reg-io/niftilib/nifti1_io_version.h create mode 100644 reg-io/niftilib/nifti2_io_version.h diff --git a/niftyreg_build_version.txt b/niftyreg_build_version.txt index c8f0fcc6..74fa38c9 100644 --- a/niftyreg_build_version.txt +++ b/niftyreg_build_version.txt @@ -1 +1 @@ -406 +407 diff --git a/reg-io/RNifti.h b/reg-io/RNifti.h index 121053e5..16ca0b76 100644 --- a/reg-io/RNifti.h +++ b/reg-io/RNifti.h @@ -7,7 +7,7 @@ // Defined since RNifti v0.10.0, and equal to 100 * (major version) + (minor version). May not // change if the API does not change, and in particular never changes with patch level -#define RNIFTI_VERSION 104 +#define RNIFTI_VERSION 106 // Versions 1 and 2 of the NIfTI reference library are mutually incompatible, but RNifti does some // work to get them to play nicely: diff --git a/reg-io/RNifti/NiftiImage.h b/reg-io/RNifti/NiftiImage.h index b03f5837..26cffe98 100644 --- a/reg-io/RNifti/NiftiImage.h +++ b/reg-io/RNifti/NiftiImage.h @@ -134,7 +134,7 @@ class NiftiImageData double getDouble (void *ptr) const { return static_cast(getNative(ptr).real()); } int getInt (void *ptr) const { return static_cast(getNative(ptr).real()); } void setComplex (void *ptr, const complex128_t value) const { setNative(ptr, std::complex(value)); } - void setDouble (void *ptr, const double value) const { setNative(ptr, std::complex(static_cast(value), 0.0)); } + void setDouble (void *ptr, const double value) const { setNative(ptr, std::complex(value, 0.0)); } void setInt (void *ptr, const int value) const { setNative(ptr, std::complex(static_cast(value), 0.0)); } void minmax (void *ptr, const size_t length, double *min, double *max) const; }; @@ -329,7 +329,9 @@ class NiftiImageData operator Rcomplex() const { const complex128_t value = parent.handler->getComplex(ptr); - Rcomplex rValue = { value.real(), value.imag() }; + Rcomplex rValue; + rValue.r = value.real(); + rValue.i = value.imag(); if (parent.isScaled()) { rValue.r = rValue.r * parent.slope + parent.intercept; @@ -351,7 +353,9 @@ class NiftiImageData class Iterator { private: - const NiftiImageData &parent; + // NB: "parent" cannot be a reference because reference members are immutable. That renders + // the class non-copy-assignable, which is a requirement for iterators (issue #31) + const NiftiImageData *parent; void *ptr; size_t step; @@ -365,16 +369,17 @@ class NiftiImageData /** * Primary constructor - * @param parent A reference to the parent object - * @param ptr An opaque pointer to the memory underpinning the iterator + * @param parent A pointer to the parent object + * @param ptr An opaque pointer to the memory underpinning the iterator. The default, + * \c nullptr, corresponds to the start of the parent object's data blob. * @param step The increment between elements within the blob, in bytes. If zero, the * default, the width associated with the stored datatype will be used. **/ - Iterator (const NiftiImageData &parent, void *ptr = nullptr, const size_t step = 0) + Iterator (const NiftiImageData *parent = nullptr, void *ptr = nullptr, const size_t step = 0) : parent(parent) { - this->ptr = (ptr == nullptr ? parent.dataPtr : ptr); - this->step = (step == 0 ? parent.handler->size() : step); + this->ptr = (ptr == nullptr ? parent->dataPtr : ptr); + this->step = (step == 0 ? parent->handler->size() : step); } /** @@ -387,7 +392,7 @@ class NiftiImageData /** * Reset the iterator to point to the start of the data blob **/ - void reset () { ptr = parent.dataPtr; } + void reset () { ptr = parent->dataPtr; } Iterator & operator++ () { ptr = static_cast(ptr) + step; return *this; } Iterator operator++ (int) { Iterator copy(*this); ptr = static_cast(ptr) + step; return copy; } @@ -415,10 +420,10 @@ class NiftiImageData bool operator> (const Iterator &other) const { return (ptr > other.ptr); } bool operator< (const Iterator &other) const { return (ptr < other.ptr); } - const Element operator* () const { return Element(parent, ptr); } - Element operator* () { return Element(parent, ptr); } - const Element operator[] (const size_t i) const { return Element(parent, static_cast(ptr) + (i * step)); } - Element operator[] (const size_t i) { return Element(parent, static_cast(ptr) + (i * step)); } + const Element operator* () const { return Element(*parent, ptr); } + Element operator* () { return Element(*parent, ptr); } + const Element operator[] (const size_t i) const { return Element(*parent, static_cast(ptr) + (i * step)); } + Element operator[] (const size_t i) { return Element(*parent, static_cast(ptr) + (i * step)); } }; /** @@ -479,8 +484,7 @@ class NiftiImageData else { calibrateFrom(source); - for (size_t i = 0; i < source.length(); ++i) - (*this)[i] = source[i]; + std::copy(source.begin(), source.end(), this->begin()); } } @@ -591,16 +595,16 @@ class NiftiImageData NiftiImageData & disown () { this->owner = false; return *this; } /** Obtain a constant iterator corresponding to the start of the blob */ - const Iterator begin () const { return Iterator(*this); } + const Iterator begin () const { return Iterator(this); } /** Obtain a constant iterator corresponding to the end of the blob */ - const Iterator end () const { return Iterator(*this, static_cast(dataPtr) + totalBytes()); } + const Iterator end () const { return Iterator(this, static_cast(dataPtr) + totalBytes()); } /** Obtain a mutable iterator corresponding to the start of the blob */ - Iterator begin () { return Iterator(*this); } + Iterator begin () { return Iterator(this); } /** Obtain a mutable iterator corresponding to the end of the blob */ - Iterator end () { return Iterator(*this, static_cast(dataPtr) + totalBytes()); } + Iterator end () { return Iterator(this, static_cast(dataPtr) + totalBytes()); } /** * Indexing operator, returning a constant element @@ -1305,6 +1309,16 @@ class NiftiImage **/ void acquire (nifti_image * const image); + /** + * Acquire the same pointer as another \c NiftiImage, incrementing the shared reference count + * @param source A reference to a \c NiftiImage + **/ + void acquire (const NiftiImage &source) + { + refCount = source.refCount; + acquire(source.image); + } + /** * Release the currently wrapped pointer, if it is not \c nullptr, decrementing the reference * count and releasing memory if there are no remaining references to the pointer @@ -1318,6 +1332,12 @@ class NiftiImage **/ void copy (const nifti_image *source, const Copy copy); + /** + * Copy the contents of another \c NiftiImage to create a new image, acquiring a new pointer + * @param source A reference to a \c NiftiImage + **/ + void copy (const NiftiImage &source); + /** * Copy the contents of a \ref Block to create a new image, acquiring a new pointer * @param source A reference to a \ref Block @@ -1408,8 +1428,7 @@ class NiftiImage if (copy != Copy::None) { this->copy(source, copy); } else { - refCount = source.refCount; - acquire(source.image); + acquire(source); } RN_DEBUG("Creating NiftiImage (v%d) with pointer %p (from NiftiImage)", RNIFTI_NIFTILIB_VERSION, this->image); } @@ -1451,6 +1470,34 @@ class NiftiImage RN_DEBUG("Creating NiftiImage (v%d) with pointer %p (from pointer)", RNIFTI_NIFTILIB_VERSION, this->image); } + /** + * Initialise using a NIfTI-1 header + * @param header A reference to a NIfTI-1 header struct + **/ + NiftiImage (const nifti_1_header &header) + : NiftiImage() + { +#if RNIFTI_NIFTILIB_VERSION == 1 + acquire(nifti_convert_nhdr2nim(header, nullptr)); +#elif RNIFTI_NIFTILIB_VERSION == 2 + acquire(nifti_convert_n1hdr2nim(header, nullptr)); +#endif + RN_DEBUG("Creating NiftiImage (v%d) with pointer %p (from header)", RNIFTI_NIFTILIB_VERSION, this->image); + } + +#if RNIFTI_NIFTILIB_VERSION == 2 + /** + * Initialise using a NIfTI-2 header + * @param header A reference to a NIfTI-2 header struct + **/ + NiftiImage (const nifti_2_header &header) + : NiftiImage() + { + acquire(nifti_convert_n2hdr2nim(header, nullptr)); + RN_DEBUG("Creating NiftiImage (v%d) with pointer %p (from header)", RNIFTI_NIFTILIB_VERSION, this->image); + } +#endif + /** * Initialise from basic metadata, allocating and zeroing pixel data * @param dim A vector of image dimensions @@ -2021,11 +2068,12 @@ class NiftiImage * @param dimCount Number of dimensions to consider * @return The number of voxels in the image */ - static size_t calcVoxelNumber(const nifti_image *image, const int dimCount) { + static size_t calcVoxelNumber (const nifti_image *image, const int dimCount) { if (image == nullptr) return 0; size_t voxelNumber = 1; - for (int i = 1; i <= dimCount; i++) { + for (int i = 1; i <= dimCount; i++) + { const size_t dim = static_cast(std::abs(image->dim[i])); voxelNumber *= dim > 0 ? dim : 1; } @@ -2035,7 +2083,7 @@ class NiftiImage /** * Recalculate the number of voxels in the image and update the nvox field */ - void recalcVoxelNumber() { + void recalcVoxelNumber () { if (image != nullptr) image->nvox = calcVoxelNumber(image, image->ndim); } @@ -2061,7 +2109,7 @@ class NiftiImage /** * Return the total size of the image data in bytes */ - size_t totalBytes() const + size_t totalBytes () const { #if RNIFTI_NIFTILIB_VERSION == 1 return nifti_get_volsize(image); @@ -2120,7 +2168,7 @@ class NiftiImage * @param A list of \ref Extension objects * @return Self, with the new extensions attached **/ - NiftiImage & replaceExtensions (const std::list extensions) + NiftiImage & replaceExtensions (const std::list &extensions) { dropExtensions(); for (std::list::const_iterator it=extensions.begin(); it!=extensions.end(); ++it) @@ -2147,7 +2195,7 @@ class NiftiImage * Set the intent name of the image * @param name A string giving the new intent name **/ - void setIntentName(const std::string& name) { + void setIntentName (const std::string &name) { if (image != nullptr) { constexpr size_t intentNameLength = sizeof(image->intent_name) / sizeof(*image->intent_name); @@ -2162,9 +2210,11 @@ class NiftiImage * @param datatype The datatype to use when writing the file * @param filetype The file type to create: a \c NIFTI_FTYPE constant or -1. In the latter case * the file name is used to determine the file type + * @param compression The \c zlib compression level to use, if appropriate. Valid values are + * between 0 and 9 * @return A pair of strings, giving the final header and image paths in that order **/ - std::pair toFile (const std::string fileName, const int datatype = DT_NONE, const int filetype = -1) const; + std::pair toFile (const std::string &fileName, const int datatype = DT_NONE, const int filetype = -1, const int compression = 6) const; /** * Write the image to a NIfTI-1 file @@ -2172,9 +2222,11 @@ class NiftiImage * @param datatype The datatype to use when writing the file, or "auto" * @param filetype The file type to create: a \c NIFTI_FTYPE constant or -1. In the latter case * the file name is used to determine the file type + * @param compression The \c zlib compression level to use, if appropriate. Valid values are + * between 0 and 9 * @return A pair of strings, giving the final header and image paths in that order **/ - std::pair toFile (const std::string fileName, const std::string &datatype, const int filetype = -1) const; + std::pair toFile (const std::string &fileName, const std::string &datatype, const int filetype = -1, const int compression = 6) const; #ifdef USING_R @@ -2189,7 +2241,7 @@ class NiftiImage * @param label A string labelling the image * @return An R character string with additional attributes **/ - Rcpp::RObject toPointer (const std::string label) const; + Rcpp::RObject toPointer (const std::string &label) const; /** * A conditional method that calls either \ref toArray or \ref toPointer @@ -2197,7 +2249,7 @@ class NiftiImage * @param label A string labelling the image * @return An R object **/ - Rcpp::RObject toArrayOrPointer (const bool internal, const std::string label) const; + Rcpp::RObject toArrayOrPointer (const bool internal, const std::string &label) const; #endif diff --git a/reg-io/RNifti/NiftiImage_impl.h b/reg-io/RNifti/NiftiImage_impl.h index 6ae2866c..bf4b359b 100644 --- a/reg-io/RNifti/NiftiImage_impl.h +++ b/reg-io/RNifti/NiftiImage_impl.h @@ -75,7 +75,9 @@ inline int stringToDatatype (const std::string &datatype) datatypeCodes["uint32"] = DT_UINT32; datatypeCodes["int64"] = DT_INT64; datatypeCodes["uint64"] = DT_UINT64; + datatypeCodes["cfloat"] = DT_COMPLEX64; datatypeCodes["complex64"] = DT_COMPLEX64; + datatypeCodes["cdouble"] = DT_COMPLEX128; datatypeCodes["complex128"] = DT_COMPLEX128; datatypeCodes["complex"] = DT_COMPLEX128; datatypeCodes["rgb24"] = DT_RGB24; @@ -91,9 +93,7 @@ inline int stringToDatatype (const std::string &datatype) if (datatypeCodes.count(lowerCaseDatatype) == 0) { - std::ostringstream message; - message << "Datatype \"" << datatype << "\" is not valid"; - Rf_warning(message.str().c_str()); + Rf_warning("Datatype \"%s\" is not valid", datatype.c_str()); return DT_NONE; } else @@ -233,16 +233,10 @@ inline void copyIfPresent (const Rcpp::List &list, const std::set n const Rcpp::RObject object = list[name]; const int length = Rf_length(object); if (length == 0) - { - std::ostringstream message; - message << "Field \"" << name << "\" is empty and will be ignored"; - Rf_warning(message.str().c_str()); - } + Rf_warning("Field \"%s\" is empty and will be ignored", name.c_str()); else if (length > 1) { - std::ostringstream message; - message << "Field \"" << name << "\" has " << length << "elements, but only the first will be used"; - Rf_warning(message.str().c_str()); + Rf_warning("Field \"%s\" has %d elements, but only the first will be used", name.c_str(), length); target = Rcpp::as< std::vector >(object)[0]; } else @@ -624,7 +618,7 @@ inline NiftiImage::Xform::Vector4 NiftiImage::Xform::quaternion () const #elif RNIFTI_NIFTILIB_VERSION == 2 nifti_dmat44_to_quatern(mat, &q[1], &q[2], &q[3], nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); #endif - q[0] = 1 - (q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); + q[0] = 1.0 - (q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); return q; } @@ -788,6 +782,13 @@ inline void NiftiImage::copy (const nifti_image *source, const Copy copy) } } +inline void NiftiImage::copy (const NiftiImage &source) +{ + const nifti_image *sourceStruct = source; + + copy(sourceStruct, Copy::Image); +} + inline void NiftiImage::copy (const Block &source) { const nifti_image *sourceStruct = source.image; @@ -942,7 +943,12 @@ inline void NiftiImage::initFromMriImage (const Rcpp::RObject &object, const boo data = call.eval(); } - const int datatype = (Rf_isNull(data) ? DT_INT32 : sexpTypeToNiftiType(data.sexp_type())); + int datatype = (Rf_isNull(data) ? DT_INT32 : sexpTypeToNiftiType(data.sexp_type())); + if (data.inherits("rgbArray")) + { + const int channels = (data.hasAttribute("channels") ? data.attr("channels") : 3); + datatype = (channels == 4 ? DT_RGBA32 : DT_RGB24); + } dim_t dims[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; const std::vector dimVector = mriImage.field("imageDims"); @@ -975,8 +981,15 @@ inline void NiftiImage::initFromMriImage (const Rcpp::RObject &object, const boo // NB: nifti_get_volsize() will not be right here if there were tags const size_t dataSize = nVoxels * image->nbyper; this->image->data = calloc(1, dataSize); - if (datatype == DT_INT32) + if (datatype == DT_INT32 || datatype == DT_RGBA32) memcpy(this->image->data, INTEGER(data), dataSize); + else if (datatype == DT_RGB24) + { + NiftiImageData newData(image); + std::copy(INTEGER(data), INTEGER(data)+nVoxels, newData.begin()); + } + else if (datatype == DT_COMPLEX128) + memcpy(this->image->data, COMPLEX(data), dataSize); else memcpy(this->image->data, REAL(data), dataSize); } @@ -1273,7 +1286,7 @@ inline NiftiImage::NiftiImage (const std::string &path, const std::vector nifti_brick_list brickList; #if RNIFTI_NIFTILIB_VERSION == 1 - acquire(nifti_image_read_bricks(internal::stringToPath(path), static_cast(volumes.size()), &volumes.front(), &brickList)); + acquire(nifti_image_read_bricks(internal::stringToPath(path), volumes.size(), &volumes.front(), &brickList)); if (image == nullptr) throw std::runtime_error("Failed to read image from path " + path); @@ -1311,7 +1324,7 @@ inline void NiftiImage::updatePixDim (const std::vector &pixDims) for (int i=1; i<8; i++) image->pixdim[i] = 0.0; - const int pixdimLength = static_cast(pixDims.size()); + const int pixdimLength = pixDims.size(); for (int i=0; ipixdim[i+1] = pixDims[i]; @@ -1465,7 +1478,7 @@ inline NiftiImage & NiftiImage::reorient (const int icode, const int jcode, cons for (int j=0; j<3; j++) result(i,j) = nativeMat(i,0) * transform(0,j) + nativeMat(i,1) * transform(1,j) + nativeMat(i,2) * transform(2,j); - result(3,i) = (i == 3 ? 1.f : 0.f); + result(3,i) = (i == 3 ? 1.0 : 0.0); } // Extract the mapping between dimensions and the signs @@ -1497,7 +1510,7 @@ inline NiftiImage & NiftiImage::reorient (const int icode, const int jcode, cons // Flip and/or permute the origin if (signs[j] < 0) - offset[j] = image->dim[locs[j]+1] - origin[locs[j]] - 1; + offset[j] = image->dim[locs[j]+1] - origin[locs[j]] - 1.0; else offset[j] = origin[locs[j]]; } @@ -1565,7 +1578,7 @@ inline NiftiImage & NiftiImage::reorient (const int icode, const int jcode, cons for (size_t i=0; ipixdim[i+1]==0 ? 1 : image->pixdim[i+1]); + matrix(i,i) = (image->pixdim[i+1]==0.0 ? 1.0 : image->pixdim[i+1]); matrix(3,3) = 1.0; return Xform(matrix); } @@ -1874,36 +1887,43 @@ inline NiftiImage & NiftiImage::copyData (const nifti_image *other) return *this; } -inline std::pair NiftiImage::toFile (const std::string fileName, const int datatype, const int filetype) const +inline std::pair NiftiImage::toFile (const std::string &fileName, const int datatype, const int filetype, const int compression) const { const bool changingDatatype = (datatype != DT_NONE && !this->isNull() && datatype != image->datatype); // Copy the source image only if the datatype will be changed - NiftiImage imageToWrite(*this, Copy(changingDatatype)); + NiftiImage imageToWrite(*this, changingDatatype ? Copy::Image : Copy::None); if (changingDatatype) imageToWrite.changeDatatype(datatype, true); if (filetype >= 0 && filetype <= NIFTI_MAX_FTYPE) imageToWrite->nifti_type = filetype; + const char *path = internal::stringToPath(fileName); + + // If we're writing a gzipped file (only), append a compression level to the mode string + std::string mode = "wb"; + if (nifti_is_gzfile(path) && compression >= 0 && compression <= 9) + mode += std::to_string(compression); + #if RNIFTI_NIFTILIB_VERSION == 1 - const int status = nifti_set_filenames(imageToWrite, internal::stringToPath(fileName), false, true); + const int status = nifti_set_filenames(imageToWrite, path, false, true); if (status != 0) throw std::runtime_error("Failed to set filenames for NIfTI object"); - nifti_image_write(imageToWrite); + nifti_image_write_hdr_img(imageToWrite, 1, mode.c_str()); #elif RNIFTI_NIFTILIB_VERSION == 2 - const int status = nifti2_set_filenames(imageToWrite, internal::stringToPath(fileName), false, true); + const int status = nifti2_set_filenames(imageToWrite, path, false, true); if (status != 0) throw std::runtime_error("Failed to set filenames for NIfTI object"); - nifti2_image_write(imageToWrite); + nifti2_image_write_hdr_img(imageToWrite, 1, mode.c_str()); #endif return std::pair(std::string(imageToWrite->fname), std::string(imageToWrite->iname)); } -inline std::pair NiftiImage::toFile (const std::string fileName, const std::string &datatype, const int filetype) const +inline std::pair NiftiImage::toFile (const std::string &fileName, const std::string &datatype, const int filetype, const int compression) const { - return toFile(fileName, internal::stringToDatatype(datatype), filetype); + return toFile(fileName, internal::stringToDatatype(datatype), filetype, compression); } #ifdef USING_R diff --git a/reg-io/RNifti/NiftiImage_print.h b/reg-io/RNifti/NiftiImage_print.h index 2390a2ee..c8370249 100644 --- a/reg-io/RNifti/NiftiImage_print.h +++ b/reg-io/RNifti/NiftiImage_print.h @@ -11,8 +11,8 @@ #define Rc_printf Rprintf #define Rc_fprintf_stdout(...) Rprintf(__VA_ARGS__) #define Rc_fprintf_stderr(...) REprintf(__VA_ARGS__) -#define Rc_fputs_stdout(str) Rprintf(str) -#define Rc_fputs_stderr(str) REprintf(str) +#define Rc_fputs_stdout(str) Rprintf("%s", str) +#define Rc_fputs_stderr(str) REprintf("%s", str) #define Rc_fputc_stdout(ch) Rprintf("%c", ch) #define Rc_fputc_stderr(ch) REprintf("%c", ch) @@ -27,7 +27,7 @@ #define Rc_fputs_stderr(str) fputs(str, stderr) #define Rc_fputc_stdout(ch) fputc(ch, stdout) #define Rc_fputc_stderr(ch) fputc(ch, stderr) -#define Rf_warning(str) fprintf(stderr, "%s\n", str) +#define Rf_warning(...) fprintf(stderr, __VA_ARGS__) #define Rprintf(...) fprintf(stderr, __VA_ARGS__) #endif // USING_R diff --git a/reg-io/niftilib/nifti1.h b/reg-io/niftilib/nifti1.h index 49e7602b..6a7498cf 100644 --- a/reg-io/niftilib/nifti1.h +++ b/reg-io/niftilib/nifti1.h @@ -872,7 +872,7 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; as a displacement field or vector: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_DISPVECT - - dim[5] must be the dimensionality of the displacment + - dim[5] must be the dimensionality of the displacement vector (e.g., 3 for spatial displacement, 2 for in-plane) */ #define NIFTI_INTENT_DISPVECT 1006 /* specifically for displacements */ diff --git a/reg-io/niftilib/nifti1_io.c b/reg-io/niftilib/nifti1_io.c index d8bee4da..5237bb76 100644 --- a/reg-io/niftilib/nifti1_io.c +++ b/reg-io/niftilib/nifti1_io.c @@ -1,6 +1,10 @@ #define NIFTI1_IO_C #include "niftilib/nifti1_io.h" /* typedefs, prototypes, macros, etc. */ +#include "niftilib/nifti1_io_version.h" + +#include +#include /*****===================================================================*****/ /***** Sample functions to deal with NIFTI-1 and ANALYZE files *****/ @@ -41,7 +45,7 @@ static char const * const gni_history[] = " (FMRIB Centre, University of Oxford, UK)\n" " - Mainly adding low-level IO and changing things to allow gzipped\n" " files to be read and written\n" - " - Full backwards compatability should have been maintained\n" + " - Full backwards compatibility should have been maintained\n" "\n", "0.2 16 Nov 2004 [rickr]\n" " (Rick Reynolds of the National Institutes of Health, SSCC/DIRP/NIMH)\n" @@ -264,7 +268,7 @@ static char const * const gni_history[] = "1.12b 25 August 2005 [rickr] - changes by Hans Johnson\n", "1.13 25 August 2005 [rickr]\n", " - finished changes by Hans for Insight\n" - " - added const in all appropraite parameter locations (30-40)\n" + " - added const in all appropriate parameter locations (30-40)\n" " (any pointer referencing data that will not change)\n" " - shortened all string constants below 509 character limit\n" "1.14 28 October 2005 [HJohnson]\n", @@ -340,9 +344,13 @@ static char const * const gni_history[] = "1.45 10 May 2019 [rickr]: added NIFTI_ECODE_QUANTIPHYSE\n", "1.46 26 Sep 2019 [rickr]:\n" " - nifti_read_ascii_image no longer closes fp or free's fname\n", + "2.1.0 18 Jun 2020 [leej3,hmjohnson,rickr]:\n" + " - big version jump - changed to more formal library versioning\n", + "2.1.0.1 - non-release update - 16 Jun 2022 [rickr]:\n" + " - add nifti_image_write_status\n", "----------------------------------------------------------------------\n" }; -static const char gni_version[] = "nifti library version 1.46 (26 Sep, 2019)"; +static const char gni_version[] = NIFTI1_IO_SOURCE_VERSION " (16 Jun, 2022)"; /*! global nifti options structure - init with defaults */ static nifti_global_options g_opts = { @@ -443,12 +451,15 @@ static int unescape_string (char *str); /* string utility functions */ static char *escapize_string (const char *str); /* internal I/O routines */ +static int nifti_image_write_engine(nifti_image *nim, int write_opts, + const char *opts, znzFile *imgfile, const nifti_brick_list *NBL); static znzFile nifti_image_load_prep( nifti_image *nim ); static int has_ascii_header(znzFile fp); /*---------------------------------------------------------------------------*/ /* for calling from some main program */ + /*----------------------------------------------------------------------*/ /*! display the nifti library module history (via stdout) *//*--------------------------------------------------------------------*/ @@ -540,7 +551,7 @@ nifti_image *nifti_image_read_bricks(const char * hname, int nbricks, if( !hname || !NBL ){ Rc_fprintf_stderr("** nifti_image_read_bricks: bad params (%p,%p)\n", - hname, (void *)NBL); + (void *)hname, (void *)NBL); return NULL; } @@ -769,7 +780,7 @@ int nifti_image_load_bricks( nifti_image * nim , int nbricks, if( rv != 0 ){ nifti_free_NBL( NBL ); /* failure! */ - NBL->nbricks = 0; /* repetative, but clear */ + NBL->nbricks = 0; /* repetitive, but clear */ } if( slist ){ free(slist); free(sindex); } @@ -1409,8 +1420,6 @@ char const *nifti_orientation_string( int ii ) \param nbyper pointer to return value: number of bytes per voxel \param swapsize pointer to return value: size of swap blocks - \return appropriate values at nbyper and swapsize - The swapsize is set to 0 if this datatype doesn't ever need swapping. \sa NIFTI1_DATATYPES in nifti1.h @@ -1679,7 +1688,7 @@ mat44 nifti_mat44_inverse( mat44 R ) v1 = R.m[0][3]; v2 = R.m[1][3]; v3 = R.m[2][3]; /* [ 0 0 0 1 ] */ deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 - +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; + +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; /* determinant */ if( deti != 0.0l ) deti = 1.0l / deti ; @@ -1687,19 +1696,19 @@ mat44 nifti_mat44_inverse( mat44 R ) Q.m[0][1] = (float)( deti*(-r12*r33+r32*r13) ) ; Q.m[0][2] = (float)( deti*( r12*r23-r22*r13) ) ; Q.m[0][3] = (float)( deti*(-r12*r23*v3+r12*v2*r33+r22*r13*v3 - -r22*v1*r33-r32*r13*v2+r32*v1*r23) ) ; + -r22*v1*r33-r32*r13*v2+r32*v1*r23) ) ; Q.m[1][0] = (float)( deti*(-r21*r33+r31*r23) ) ; Q.m[1][1] = (float)( deti*( r11*r33-r31*r13) ) ; Q.m[1][2] = (float)( deti*(-r11*r23+r21*r13) ) ; Q.m[1][3] = (float)( deti*( r11*r23*v3-r11*v2*r33-r21*r13*v3 - +r21*v1*r33+r31*r13*v2-r31*v1*r23) ) ; + +r21*v1*r33+r31*r13*v2-r31*v1*r23) ) ; Q.m[2][0] = (float)( deti*( r21*r32-r31*r22) ) ; Q.m[2][1] = (float)( deti*(-r11*r32+r31*r12) ) ; Q.m[2][2] = (float)( deti*( r11*r22-r21*r12) ) ; Q.m[2][3] = (float)( deti*(-r11*r22*v3+r11*r32*v2+r21*r12*v3 - -r21*r32*v1-r31*r12*v2+r31*r22*v1) ) ; + -r21*r32*v1-r31*r12*v2+r31*r22*v1) ) ; Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l ; Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; /* failure flag if deti == 0 */ @@ -1943,7 +1952,7 @@ mat33 nifti_mat33_polar( mat33 A ) } /*---------------------------------------------------------------------------*/ -/*! compute the (closest) orientation from a 4x4 ijk->xyz tranformation matrix +/*! compute the (closest) orientation from a 4x4 ijk->xyz transformation matrix
    Input:  4x4 matrix that transforms (i,j,k) indexes to (x,y,z) coordinates,
@@ -2100,6 +2109,7 @@ void nifti_mat44_to_orientation( mat44 R , int *icod, int *jcod, int *kcod )
      case -2: i = NIFTI_A2P ; break ;
      case  3: i = NIFTI_I2S ; break ;
      case -3: i = NIFTI_S2I ; break ;
+     default: break ;
    }
 
    switch( jbest*qbest ){
@@ -2109,6 +2119,7 @@ void nifti_mat44_to_orientation( mat44 R , int *icod, int *jcod, int *kcod )
      case -2: j = NIFTI_A2P ; break ;
      case  3: j = NIFTI_I2S ; break ;
      case -3: j = NIFTI_S2I ; break ;
+     default: break ;
    }
 
    switch( kbest*rbest ){
@@ -2118,9 +2129,11 @@ void nifti_mat44_to_orientation( mat44 R , int *icod, int *jcod, int *kcod )
      case -2: k = NIFTI_A2P ; break ;
      case  3: k = NIFTI_I2S ; break ;
      case -3: k = NIFTI_S2I ; break ;
+     default: break ;
    }
 
-   *icod = i ; *jcod = j ; *kcod = k ; }
+   *icod = i ; *jcod = j ; *kcod = k ;
+}
 
 /*---------------------------------------------------------------------------*/
 /* Routines to swap byte arrays in various ways:
@@ -2134,8 +2147,8 @@ void nifti_mat44_to_orientation( mat44 R , int *icod, int *jcod, int *kcod )
 /*! swap each byte pair from the given list of n pairs
  *
  *  Due to alignment of structures at some architectures (e.g. on ARM),
- *  stick to char varaibles.
- *  Fixes http://bugs.debian.org/446893   Yaroslav 
+ *  stick to char variables.
+ *  Fixes  Yaroslav 
  *
 *//*--------------------------------------------------------------------*/
 void nifti_swap_2bytes( size_t n , void *ar )    /* 2 bytes at a time */
@@ -2149,7 +2162,7 @@ void nifti_swap_2bytes( size_t n , void *ar )    /* 2 bytes at a time */
        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
        cp1 += 2;
    }
-   }
+}
 
 /*----------------------------------------------------------------------*/
 /*! swap 4 bytes at a time from the given list of n sets of 4 bytes
@@ -2167,7 +2180,7 @@ void nifti_swap_4bytes( size_t n , void *ar )    /* 4 bytes at a time */
        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
        cp0 += 4;
    }
-   }
+}
 
 /*----------------------------------------------------------------------*/
 /*! swap 8 bytes at a time from the given list of n sets of 8 bytes
@@ -2189,7 +2202,7 @@ void nifti_swap_8bytes( size_t n , void *ar )    /* 8 bytes at a time */
        }
        cp0 += 8;
    }
-   }
+}
 
 /*----------------------------------------------------------------------*/
 /*! swap 16 bytes at a time from the given list of n sets of 16 bytes
@@ -2209,7 +2222,7 @@ void nifti_swap_16bytes( size_t n , void *ar )    /* 16 bytes at a time */
        }
        cp0 += 16;
    }
-   }
+}
 
 #if 0  /* not important: save for version update     6 Jul 2010 [rickr] */
 
@@ -2251,7 +2264,7 @@ void nifti_swap_Nbytes( size_t n , int siz , void *ar )  /* subsuming case */
         Rc_fprintf_stderr("** NIfTI: cannot swap in %d byte blocks\n", siz);
         break ;
    }
-   }
+}
 
 
 /*-------------------------------------------------------------------------*/
@@ -2407,7 +2420,7 @@ void old_swap_nifti_header( struct nifti_1_header *h , int is_nifti )
      nifti_swap_4bytes(4,h->srow_y);
      nifti_swap_4bytes(4,h->srow_z);
    }
-   }
+}
 
 #endif /* RNIFTI_NIFTILIB_DEDUPLICATE */
 
@@ -2450,6 +2463,7 @@ int nifti_get_filesize( const char *pathname )
 
 #endif /* USE_STAT */
 
+
 /*----------------------------------------------------------------------*/
 /*! return the total volume size, in bytes
 
@@ -2574,7 +2588,7 @@ int nifti_validfilename(const char* fname)
 
     \return a pointer to the extension substring within the original
             function input parameter name, or NULL if not found.
-    \caution Note that if the input parameter is is immutabale
+    \warning Note that if the input parameter is is immutabale
              (i.e. a const char *) then this function performs an
              implicit casting away of the mutability constraint and
              the return parameter will appear as a mutable
@@ -2652,8 +2666,7 @@ int nifti_is_gzfile(const char* fname)
   if (fname == NULL) { return 0; }
 #ifdef HAVE_ZLIB
   { /* just so len doesn't generate compile warning */
-     int len;
-     len = (int)strlen(fname);
+     size_t len = strlen(fname);
      if (len < 3) return 0;  /* so we don't search before the name */
      if (fileext_compare(fname + strlen(fname) - 3,".gz")==0) { return 1; }
   }
@@ -2779,7 +2792,7 @@ char * nifti_findhdrname(const char* fname)
 
    /* note: efirst is 0 in the case of ".img" */
 
-   /* if the user passed an uppercase entension (.IMG), search for uppercase */
+   /* if the user passed an uppercase extension (.IMG), search for uppercase */
    if( eisupper ) {
       make_uppercase(elist[0]);
       make_uppercase(elist[1]);
@@ -2824,8 +2837,8 @@ char * nifti_findhdrname(const char* fname)
 /*! check current directory for existing image file
 
     \param fname filename to check for
-    \nifti_type  nifti_type for dataset - this determines whether to
-                 first check for ".nii" or ".img" (since both may exist)
+    \param nifti_type  nifti_type for dataset - this determines whether to
+                       first check for ".nii" or ".img" (since both may exist)
 
     \return filename of data/img file on success and NULL if no appropriate
             file could be found
@@ -3070,7 +3083,7 @@ int nifti_set_filenames( nifti_image * nim, const char * prefix, int check,
 
    if( !nim || !prefix ){
       Rc_fprintf_stderr("** nifti_set_filenames, bad params %p, %p\n",
-              (void *)nim,prefix);
+              (void *)nim,(void *)prefix);
       return -1;
    }
 
@@ -3105,11 +3118,11 @@ int nifti_set_filenames( nifti_image * nim, const char * prefix, int check,
     - if type 1, expect .nii (and names must match)
 
     \param nim       given nifti_image
-    \param show_warn if set, print a warning message for any mis-match
+    \param show_warn if set, print a warning message for any mismatch
 
     \return
         -   1 if the values seem to match
-        -   0 if there is a mis-match
+        -   0 if there is a mismatch
         -  -1 if there is not sufficient information to create file(s)
 
     \sa NIFTI_FTYPE_* codes in nifti1_io.h
@@ -3161,7 +3174,7 @@ int nifti_type_and_names_match( nifti_image * nim, int show_warn )
       errs++;
    }
 
-   if( errs ) return 0;   /* do not proceed, but this is just a mis-match */
+   if( errs ) return 0;   /* do not proceed, but this is just a mismatch */
 
    /* general tests */
    if( nim->nifti_type == NIFTI_FTYPE_NIFTI1_1 ){  /* .nii */
@@ -3396,7 +3409,7 @@ int nifti_set_type_from_names( nifti_image * nim )
 
    if( !nim->fname || !nim->iname ){
       Rc_fprintf_stderr("** NSTFN: missing filename(s) fname @ %p, iname @ %p\n",
-              nim->fname, nim->iname);
+              (void *)nim->fname, (void *)nim->iname);
       return -1;
    }
 
@@ -3677,7 +3690,7 @@ nifti_image* nifti_convert_nhdr2nim(struct nifti_1_header nhdr,
      if( nhdr.dim[ii] <= 0 ) nhdr.dim[ii] = 1 ;
 
    /* fix any remaining bad dim[] values, so garbage does not propagate */
-   /* (only values 0 or 1 seem rational, otherwise set to arbirary 1)   */
+   /* (only values 0 or 1 seem rational, otherwise set to arbitrary 1)   */
    for( ii=nhdr.dim[0]+1 ; ii <= 7 ; ii++ )
      if( nhdr.dim[ii] != 1 && nhdr.dim[ii] != 0) nhdr.dim[ii] = 1 ;
 
@@ -4388,7 +4401,7 @@ static int nifti_read_extensions( nifti_image *nim, znzFile fp, int remain )
    nifti1_extender    extdr;      /* defines extension existence  */
    nifti1_extension   extn;       /* single extension to process  */
    nifti1_extension * Elist;      /* list of processed extensions */
-   int                posn, count;
+   int                count;
 
    if( !nim || znz_isnull(fp) ) {
       if( g_opts.debug > 0 )
@@ -4397,16 +4410,16 @@ static int nifti_read_extensions( nifti_image *nim, znzFile fp, int remain )
       return -1;
    }
 
-   posn = znztell(fp);
+   znz_off_t posn = znztell(fp);
 
    if( (posn != sizeof(nifti_1_header)) &&
        (nim->nifti_type != NIFTI_FTYPE_ASCII) )
       Rc_fprintf_stderr("** WARNING: posn not header size (%d, %d)\n",
-              posn, (int)sizeof(nifti_1_header));
+              (int)posn, (int)sizeof(nifti_1_header));
 
    if( g_opts.debug > 2 )
       Rc_fprintf_stderr("-d nre: posn = %d, offset = %d, type = %d, remain = %d\n",
-              posn, nim->iname_offset, nim->nifti_type, remain);
+              (int)posn, nim->iname_offset, nim->nifti_type, remain);
 
    if( remain < 16 ){
       if( g_opts.debug > 2 ){
@@ -4485,7 +4498,7 @@ static int nifti_read_extensions( nifti_image *nim, znzFile fp, int remain )
 
    \param nim    - nifti_image to add extension to
    \param data   - raw extension data
-   \param length - length of raw extension data
+   \param len    - length of raw extension data
    \param ecode  - extension code
 
    \sa extension codes NIFTI_ECODE_* in nifti1_io.h
@@ -4567,7 +4580,7 @@ static int nifti_fill_extension( nifti1_extension *ext, const char * data,
 
    if( !ext || !data || len < 0 ){
       Rc_fprintf_stderr("** fill_ext: bad params (%p,%p,%d)\n",
-              (void *)ext, data, len);
+              (void *)ext, (void *)data, len);
       return -1;
    } else if( ! nifti_is_valid_ecode(ecode) ){
       Rc_fprintf_stderr("** warning: writing unknown ecode %d\n", ecode);
@@ -4815,7 +4828,7 @@ static znzFile nifti_image_load_prep( nifti_image *nim )
       if ( g_opts.debug > 0 ){
          if( !nim ) Rc_fprintf_stderr("** ERROR: N_image_load: no nifti image\n");
          else Rc_fprintf_stderr("** ERROR: N_image_load: bad params (%p,%d,%u)\n",
-                      nim->iname, nim->nbyper, (unsigned)nim->nvox);
+                      (void *)nim->iname, nim->nbyper, (unsigned)nim->nvox);
       }
       return NULL;
    }
@@ -5375,7 +5388,7 @@ nifti_1_header * nifti_make_new_header(const int arg_dims[], int arg_dtype)
 /*! basic creation of a nifti_image struct
 
    Create a nifti_image from the given dimensions and data type.
-   Optinally, allocate zero-filled data.
+   Optionally, allocate zero-filled data.
 
    \param dims      : optional dim[8]   (default {3,1,1,1,0,0,0,0})
    \param datatype  : optional datatype (default DT_FLOAT32)
@@ -5606,7 +5619,7 @@ int nifti_copy_extensions(nifti_image * nim_dest, const nifti_image * nim_src)
     and the bytes used for the data.  Each esize also needs to be a
     multiple of 16, so it may be greater than the sum of its 3 parts.
 *//*--------------------------------------------------------------------*/
-int nifti_extension_size(nifti_image *nim)
+static int nifti_extension_size(nifti_image *nim)
 {
    int c, size = 0;
 
@@ -5683,25 +5696,42 @@ znzFile nifti_image_write_hdr_img( nifti_image *nim , int write_data ,
   return nifti_image_write_hdr_img2(nim,write_data,opts,NULL,NULL);
 }
 
+/*----------------------------------------------------------------------*/
+/*! This writes the header (and optionally the image data) to file.
+ *
+ * This is now just a front-end for nifti_image_write_engine, but the
+ * engine will return a status (for success of write), which is promptly
+ * ignored by this function.
+ *
+ * \sa nifti_image_write_engine
+*//*--------------------------------------------------------------------*/
+znzFile nifti_image_write_hdr_img2(nifti_image *nim, int write_opts,
+               const char * opts, znzFile imgfile, const nifti_brick_list * NBL)
+{
+   znzFile loc_img = imgfile;   /* might be NULL, might point to open struct */
+   (void)nifti_image_write_engine(nim, write_opts, opts, &loc_img, NBL);
+   return loc_img;
+}
 
 #undef  ERREX
-#define ERREX(msg)                                                \
- do{ Rc_fprintf_stderr("** ERROR: nifti_image_write_hdr_img: %s\n",(msg)) ;  \
-     return fp ; } while(0)
+#define ERREX(msg)                                                          \
+ do{ Rc_fprintf_stderr("** ERROR: nifti_image_write_engine: %s\n",(msg)) ;  \
+     if( imgfile ) *imgfile = fp;                                           \
+     return 1 ; } while(0)
 
 
 /* ----------------------------------------------------------------------*/
 /*! This writes the header (and optionally the image data) to file
  *
- * If the image data file is left open it returns a valid znzFile handle.
- * It also uses imgfile as the open image file is not null, and modifies
- * it inside.
+ * If imgfile points to a NULL znzFile, it modifies it to a valid and open
+ * handle.  If it points to an non-NULL znzFile, it uses that as the open
+ * image and simply modifies that structure.  This also depends on write_opts.
  *
  * \param nim        nifti_image to write to disk
  * \param write_opts flags whether to write data and/or close file (see below)
  * \param opts       file-open options, probably "wb" from nifti_image_write()
- * \param imgfile    optional open znzFile struct, for writing image data
-                     (may be NULL)
+ * \param imgfile    pointer to optionally open znzFile, for writing image data
+                     (must not be NULL, contents might be NULL)
  * \param NBL        optional nifti_brick_list, containing the image data
                      (may be NULL)
  *
@@ -5715,19 +5745,19 @@ znzFile nifti_image_write_hdr_img( nifti_image *nim , int write_data ,
  * \sa nifti_image_write, nifti_image_write_hdr_img, nifti_image_free,
  *     nifti_set_filenames
 *//*---------------------------------------------------------------------*/
-znzFile nifti_image_write_hdr_img2(nifti_image *nim, int write_opts,
-               const char * opts, znzFile imgfile, const nifti_brick_list * NBL)
+static int nifti_image_write_engine(nifti_image *nim, int write_opts,
+             const char *opts, znzFile *imgfile, const nifti_brick_list *NBL)
 {
    struct nifti_1_header nhdr ;
    znzFile               fp=NULL;
    size_t                ss ;
    int                   write_data, leave_open;
-   char                  func[] = { "nifti_image_write_hdr_img2" };
+   char                  func[] = { "nifti_image_write_engine" };
 
    write_data = write_opts & 1;  /* just separate the bits now */
    leave_open = write_opts & 2;
 
-   if( ! nim                              ) ERREX("NULL input") ;
+   if( ! nim || ! imgfile                 ) ERREX("NULL input") ;
    if( ! nifti_validfilename(nim->fname)  ) ERREX("bad fname input") ;
    if( write_data && ! nim->data && ! NBL ) ERREX("no image data") ;
 
@@ -5736,6 +5766,7 @@ znzFile nifti_image_write_hdr_img2(nifti_image *nim, int write_opts,
 
    nifti_set_iname_offset(nim);
 
+   /* chit-chat */
    if( g_opts.debug > 1 ){
       Rc_fprintf_stderr("-d writing nifti file '%s'...\n", nim->fname);
       if( g_opts.debug > 2 )
@@ -5743,8 +5774,13 @@ znzFile nifti_image_write_hdr_img2(nifti_image *nim, int write_opts,
                  nim->nifti_type, nim->iname_offset);
    }
 
-   if( nim->nifti_type == NIFTI_FTYPE_ASCII )   /* non-standard case */
-      return nifti_write_ascii_image(nim,NBL,opts,write_data,leave_open);
+   /* get to work */
+
+   /* if non-standard ASCII, just write out and return */
+   if( nim->nifti_type == NIFTI_FTYPE_ASCII ) {
+      *imgfile = nifti_write_ascii_image(nim,NBL,opts,write_data,leave_open);
+      return 0; /* write_ascii has no status */
+   }
 
    nhdr = nifti_convert_nim2nhdr(nim);    /* create the nifti1_header struct */
 
@@ -5755,22 +5791,27 @@ znzFile nifti_image_write_hdr_img2(nifti_image *nim, int write_opts,
        }
        if( nim->iname == NULL ){ /* then make a new one */
          nim->iname = nifti_makeimgname(nim->fname,nim->nifti_type,0,0);
-         if( nim->iname == NULL ) return NULL;
+         if( nim->iname == NULL ) {
+            *imgfile = NULL;
+            return 1;
+         }
        }
    }
 
    /* if we have an imgfile and will write the header there, use it */
-   if( ! znz_isnull(imgfile) && nim->nifti_type == NIFTI_FTYPE_NIFTI1_1 ){
+   if( ! znz_isnull(*imgfile) && nim->nifti_type == NIFTI_FTYPE_NIFTI1_1 ){
       if( g_opts.debug > 2 ) Rc_fprintf_stderr("+d using passed file for hdr\n");
-      fp = imgfile;
+      fp = *imgfile;
    }
    else {
+      /* we will write the header to a new file */
       if( g_opts.debug > 2 )
          Rc_fprintf_stderr("+d opening output file %s [%s]\n",nim->fname,opts);
       fp = znzopen( nim->fname , opts , nifti_is_gzfile(nim->fname) ) ;
       if( znz_isnull(fp) ){
          LNI_FERR(func,"cannot open output file",nim->fname);
-         return fp;
+         *imgfile = fp;
+         return 1;
       }
    }
 
@@ -5779,24 +5820,27 @@ znzFile nifti_image_write_hdr_img2(nifti_image *nim, int write_opts,
    ss = znzwrite(&nhdr , 1 , sizeof(nhdr) , fp); /* write header */
    if( ss < sizeof(nhdr) ){
       LNI_FERR(func,"bad header write to output file",nim->fname);
-      znzclose(fp); return fp;
+      znzclose(fp); *imgfile = fp; return 1;
    }
 
-   /* partial file exists, and errors have been printed, so ignore return */
+   /* write extensions; any errors will be printed */
    if( nim->nifti_type != NIFTI_FTYPE_ANALYZE )
-      (void)nifti_write_extensions(fp,nim);
+      if( nifti_write_extensions(fp,nim) < 0 ) {
+         znzclose(fp); *imgfile = fp; return 1;
+      }
 
    /* if the header is all we want, we are done */
    if( ! write_data && ! leave_open ){
       if( g_opts.debug > 2 ) Rc_fprintf_stderr("-d header is all we want: done\n");
-      znzclose(fp); return(fp);
+      znzclose(fp); *imgfile = fp;  return 0;
    }
 
+   /* if multiple files (hdr/img), close fp and use (any) *imgfile for data */
    if( nim->nifti_type != NIFTI_FTYPE_NIFTI1_1 ){ /* get a new file pointer */
       znzclose(fp);         /* first, close header file */
-      if( ! znz_isnull(imgfile) ){
+      if( ! znz_isnull(*imgfile) ){
          if(g_opts.debug > 2) Rc_fprintf_stderr("+d using passed file for img\n");
-         fp = imgfile;
+         fp = *imgfile;
       }
       else {
          if( g_opts.debug > 2 )
@@ -5811,7 +5855,9 @@ znzFile nifti_image_write_hdr_img2(nifti_image *nim, int write_opts,
    if( write_data ) nifti_write_all_data(fp,nim,NBL);
    if( ! leave_open ) znzclose(fp);
 
-   return fp;
+   *imgfile = fp;
+
+   return 0;
 }
 
 
@@ -5871,28 +5917,74 @@ znzFile nifti_write_ascii_image(nifti_image *nim, const nifti_brick_list * NBL,
 *//*------------------------------------------------------------------------*/
 void nifti_image_write( nifti_image *nim )
 {
-   znzFile fp = nifti_image_write_hdr_img(nim,1,"wb");
+   znzFile fp=NULL;
+   int     rv;
+
+   rv = nifti_image_write_engine(nim, 1, "wb", &fp, NULL);
    if( fp ){
       if( g_opts.debug > 2 ) Rc_fprintf_stderr("-d niw: done with znzFile\n");
       free(fp);
    }
-   if( g_opts.debug > 1 ) Rc_fprintf_stderr("-d nifti_image_write: done\n");
+   if( g_opts.debug > 1 )
+      Rc_fprintf_stderr("-d nifti_image_write: done, status %d\n", rv);
+}
+
+
+/*--------------------------------------------------------------------------*/
+/*! Write a nifti_image to disk, returning 0 on success, else failure.
+
+    This simple write function takes a nifti_image as input and returns
+    the status of the operation.  It is akin to nifti_image_write, but
+    returns the status.  Changing nifti_image_write from void to int
+    would have backward compatibility ramifications.
+
+   \sa nifti_image_write_bricks, nifti_image_free, nifti_set_filenames,
+       nifti_image_write_engine, nifti_image_write
+*//*------------------------------------------------------------------------*/
+int nifti_image_write_status( nifti_image *nim )
+{
+   znzFile fp=NULL;   /* required for _engine, but promptly ignored */
+   int     rv;
+
+   rv = nifti_image_write_engine(nim, 1, "wb", &fp, NULL);
+   if( g_opts.debug > 1 )
+      Rc_fprintf_stderr("-d nifti_image_write_status: done, status %d\n", rv);
+   return rv;
 }
 
 
 /*----------------------------------------------------------------------*/
 /*! similar to nifti_image_write, but data is in NBL struct, not nim->data
 
+   \return 0 on success, 1 on error
+
    \sa nifti_image_write, nifti_image_free, nifti_set_filenames, nifti_free_NBL
 *//*--------------------------------------------------------------------*/
-void nifti_image_write_bricks( nifti_image *nim, const nifti_brick_list * NBL )
+int nifti_image_write_bricks_status( nifti_image *nim,
+                                     const nifti_brick_list * NBL )
 {
-   znzFile fp = nifti_image_write_hdr_img2(nim,1,"wb",NULL,NBL);
+   znzFile fp=NULL;
+   int     rv;
+
+   rv = nifti_image_write_engine(nim, 1, "wb", &fp, NBL);
    if( fp ){
       if( g_opts.debug > 2 ) Rc_fprintf_stderr("-d niwb: done with znzFile\n");
       free(fp);
    }
-   if( g_opts.debug > 1 ) Rc_fprintf_stderr("-d niwb: done writing bricks\n");
+   if( g_opts.debug > 1 )
+      Rc_fprintf_stderr("-d niwb: done writing bricks, status %d\n", rv);
+   return rv;
+}
+
+
+/*----------------------------------------------------------------------*/
+/*! similar to nifti_image_write, but data is in NBL struct, not nim->data
+
+   \sa nifti_image_write, nifti_image_free, nifti_set_filenames, nifti_free_NBL
+*//*--------------------------------------------------------------------*/
+void nifti_image_write_bricks( nifti_image *nim, const nifti_brick_list * NBL )
+{
+   (void)nifti_image_write_bricks_status(nim, NBL);
 }
 
 
@@ -6112,15 +6204,16 @@ char *nifti_image_to_ascii( const nifti_image *nim )
 
    if( nim == NULL ) return NULL ;   /* stupid caller */
 
-   buf = (char *)calloc(1,65534); /* longer than needed, to be safe */
+   const size_t bufLen = 65534; /* longer than needed, to be safe */
+   buf = (char *)calloc(1,bufLen);
    if( !buf ){
       Rc_fprintf_stderr("** NITA: failed to alloc %d bytes\n",65534);
       return NULL;
    }
 
-   sprintf( buf , "nifti_type == NIFTI_FTYPE_NIFTI1_1) ? "NIFTI-1+"
              :(nim->nifti_type == NIFTI_FTYPE_NIFTI1_2) ? "NIFTI-1"
              :(nim->nifti_type == NIFTI_FTYPE_ASCII   ) ? "NIFTI-1A"
@@ -6134,126 +6227,126 @@ char *nifti_image_to_ascii( const nifti_image *nim )
        - The result is that the NIFTI ASCII-format header is XML-compliant. */
 
    ebuf = escapize_string(nim->fname) ;
-   sprintf( buf+strlen(buf) , "  header_filename = %s\n",ebuf); free(ebuf);
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  header_filename = %s\n",ebuf); free(ebuf);
 
    ebuf = escapize_string(nim->iname) ;
-   sprintf( buf+strlen(buf) , "  image_filename = %s\n", ebuf); free(ebuf);
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  image_filename = %s\n", ebuf); free(ebuf);
 
-   sprintf( buf+strlen(buf) , "  image_offset = '%d'\n" , nim->iname_offset );
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  image_offset = '%d'\n" , nim->iname_offset );
 
-   sprintf(buf + strlen(buf), "  ndim = '%d'\n", nim->ndim);
-   sprintf(buf + strlen(buf), "  nx = '%d'\n", nim->nx);
+   snprintf(buf+strlen(buf), bufLen-strlen(buf), "  ndim = '%d'\n", nim->ndim);
+   snprintf(buf+strlen(buf), bufLen-strlen(buf), "  nx = '%d'\n", nim->nx);
    if (nim->ndim > 1)
-     sprintf(buf + strlen(buf), "  ny = '%d'\n", nim->ny);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  ny = '%d'\n", nim->ny);
    if (nim->ndim > 2)
-     sprintf(buf + strlen(buf), "  nz = '%d'\n", nim->nz);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  nz = '%d'\n", nim->nz);
    if (nim->ndim > 3)
-     sprintf(buf + strlen(buf), "  nt = '%d'\n", nim->nt);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  nt = '%d'\n", nim->nt);
    if (nim->ndim > 4)
-     sprintf(buf + strlen(buf), "  nu = '%d'\n", nim->nu);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  nu = '%d'\n", nim->nu);
    if (nim->ndim > 5)
-     sprintf(buf + strlen(buf), "  nv = '%d'\n", nim->nv);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  nv = '%d'\n", nim->nv);
    if (nim->ndim > 6)
-     sprintf(buf + strlen(buf), "  nw = '%d'\n", nim->nw);
-   sprintf(buf + strlen(buf), "  dx = '%g'\n", nim->dx);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  nw = '%d'\n", nim->nw);
+   snprintf(buf+strlen(buf), bufLen-strlen(buf), "  dx = '%g'\n", nim->dx);
    if (nim->ndim > 1)
-     sprintf(buf + strlen(buf), "  dy = '%g'\n", nim->dy);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  dy = '%g'\n", nim->dy);
    if (nim->ndim > 2)
-     sprintf(buf + strlen(buf), "  dz = '%g'\n", nim->dz);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  dz = '%g'\n", nim->dz);
    if (nim->ndim > 3)
-     sprintf(buf + strlen(buf), "  dt = '%g'\n", nim->dt);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  dt = '%g'\n", nim->dt);
    if (nim->ndim > 4)
-     sprintf(buf + strlen(buf), "  du = '%g'\n", nim->du);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  du = '%g'\n", nim->du);
    if (nim->ndim > 5)
-     sprintf(buf + strlen(buf), "  dv = '%g'\n", nim->dv);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  dv = '%g'\n", nim->dv);
    if (nim->ndim > 6)
-     sprintf(buf + strlen(buf), "  dw = '%g'\n", nim->dw);
+     snprintf(buf+strlen(buf), bufLen-strlen(buf), "  dw = '%g'\n", nim->dw);
 
-   sprintf( buf+strlen(buf) , "  datatype = '%d'\n" , nim->datatype ) ;
-   sprintf( buf+strlen(buf) , "  datatype_name = '%s'\n" ,
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  datatype = '%d'\n" , nim->datatype ) ;
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  datatype_name = '%s'\n" ,
                               nifti_datatype_string(nim->datatype) ) ;
 
-   sprintf( buf+strlen(buf) , "  nvox = '%u'\n" , (unsigned)nim->nvox ) ;
-   sprintf( buf+strlen(buf) , "  nbyper = '%d'\n" , nim->nbyper ) ;
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  nvox = '%u'\n" , (unsigned)nim->nvox ) ;
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  nbyper = '%d'\n" , nim->nbyper ) ;
 
-   sprintf( buf+strlen(buf) , "  byteorder = '%s'\n" ,
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  byteorder = '%s'\n" ,
             (nim->byteorder==MSB_FIRST) ? "MSB_FIRST" : "LSB_FIRST" ) ;
 
    if( nim->cal_min < nim->cal_max ){
-     sprintf( buf+strlen(buf) , "  cal_min = '%g'\n", nim->cal_min ) ;
-     sprintf( buf+strlen(buf) , "  cal_max = '%g'\n", nim->cal_max ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  cal_min = '%g'\n", nim->cal_min ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  cal_max = '%g'\n", nim->cal_max ) ;
    }
 
    if( nim->scl_slope != 0.0 ){
-     sprintf( buf+strlen(buf) , "  scl_slope = '%g'\n" , nim->scl_slope ) ;
-     sprintf( buf+strlen(buf) , "  scl_inter = '%g'\n" , nim->scl_inter ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  scl_slope = '%g'\n" , nim->scl_slope ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  scl_inter = '%g'\n" , nim->scl_inter ) ;
    }
 
    if( nim->intent_code > 0 ){
-     sprintf( buf+strlen(buf) , "  intent_code = '%d'\n", nim->intent_code ) ;
-     sprintf( buf+strlen(buf) , "  intent_code_name = '%s'\n" ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_code = '%d'\n", nim->intent_code ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_code_name = '%s'\n" ,
                                 nifti_intent_string(nim->intent_code) ) ;
-     sprintf( buf+strlen(buf) , "  intent_p1 = '%g'\n" , nim->intent_p1 ) ;
-     sprintf( buf+strlen(buf) , "  intent_p2 = '%g'\n" , nim->intent_p2 ) ;
-     sprintf( buf+strlen(buf) , "  intent_p3 = '%g'\n" , nim->intent_p3 ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_p1 = '%g'\n" , nim->intent_p1 ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_p2 = '%g'\n" , nim->intent_p2 ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_p3 = '%g'\n" , nim->intent_p3 ) ;
 
      if( nim->intent_name[0] != '\0' ){
        ebuf = escapize_string(nim->intent_name) ;
-       sprintf( buf+strlen(buf) , "  intent_name = %s\n",ebuf) ;
+       snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_name = %s\n",ebuf) ;
        free(ebuf) ;
      }
    }
 
    if( nim->toffset != 0.0 )
-     sprintf( buf+strlen(buf) , "  toffset = '%g'\n",nim->toffset ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  toffset = '%g'\n",nim->toffset ) ;
 
    if( nim->xyz_units > 0 )
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  xyz_units = '%d'\n"
               "  xyz_units_name = '%s'\n" ,
               nim->xyz_units , nifti_units_string(nim->xyz_units) ) ;
 
    if( nim->time_units > 0 )
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  time_units = '%d'\n"
               "  time_units_name = '%s'\n" ,
               nim->time_units , nifti_units_string(nim->time_units) ) ;
 
    if( nim->freq_dim > 0 )
-     sprintf( buf+strlen(buf) , "  freq_dim = '%d'\n",nim->freq_dim ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  freq_dim = '%d'\n",nim->freq_dim ) ;
    if( nim->phase_dim > 0 )
-     sprintf( buf+strlen(buf) , "  phase_dim = '%d'\n",nim->phase_dim ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  phase_dim = '%d'\n",nim->phase_dim ) ;
    if( nim->slice_dim > 0 )
-     sprintf( buf+strlen(buf) , "  slice_dim = '%d'\n",nim->slice_dim ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  slice_dim = '%d'\n",nim->slice_dim ) ;
    if( nim->slice_code > 0 )
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  slice_code = '%d'\n"
               "  slice_code_name = '%s'\n" ,
               nim->slice_code , nifti_slice_string(nim->slice_code) ) ;
    if( nim->slice_start >= 0 && nim->slice_end > nim->slice_start )
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  slice_start = '%d'\n"
               "  slice_end = '%d'\n"  , nim->slice_start , nim->slice_end ) ;
    if( nim->slice_duration != 0.0 )
-     sprintf( buf+strlen(buf) , "  slice_duration = '%g'\n",
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  slice_duration = '%g'\n",
               nim->slice_duration ) ;
 
    if( nim->descrip[0] != '\0' ){
      ebuf = escapize_string(nim->descrip) ;
-     sprintf( buf+strlen(buf) , "  descrip = %s\n",ebuf) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  descrip = %s\n",ebuf) ;
      free(ebuf) ;
    }
 
    if( nim->aux_file[0] != '\0' ){
      ebuf = escapize_string(nim->aux_file) ;
-     sprintf( buf+strlen(buf) , "  aux_file = %s\n",ebuf) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  aux_file = %s\n",ebuf) ;
      free(ebuf) ;
    }
 
    if( nim->qform_code > 0 ){
      int i,j,k ;
 
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  qform_code = '%d'\n"
               "  qform_code_name = '%s'\n"
      "  qto_xyz_matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" ,
@@ -6267,7 +6360,7 @@ char *nifti_image_to_ascii( const nifti_image *nim )
          nim->qto_xyz.m[3][0] , nim->qto_xyz.m[3][1] ,
          nim->qto_xyz.m[3][2] , nim->qto_xyz.m[3][3]  ) ;
 
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
      "  qto_ijk_matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" ,
          nim->qto_ijk.m[0][0] , nim->qto_ijk.m[0][1] ,
          nim->qto_ijk.m[0][2] , nim->qto_ijk.m[0][3] ,
@@ -6278,7 +6371,7 @@ char *nifti_image_to_ascii( const nifti_image *nim )
          nim->qto_ijk.m[3][0] , nim->qto_ijk.m[3][1] ,
          nim->qto_ijk.m[3][2] , nim->qto_ijk.m[3][3]  ) ;
 
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  quatern_b = '%g'\n"
               "  quatern_c = '%g'\n"
               "  quatern_d = '%g'\n"
@@ -6291,7 +6384,7 @@ char *nifti_image_to_ascii( const nifti_image *nim )
 
      nifti_mat44_to_orientation( nim->qto_xyz , &i,&j,&k ) ;
      if( i > 0 && j > 0 && k > 0 )
-       sprintf( buf+strlen(buf) ,
+       snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
                 "  qform_i_orientation = '%s'\n"
                 "  qform_j_orientation = '%s'\n"
                 "  qform_k_orientation = '%s'\n" ,
@@ -6303,7 +6396,7 @@ char *nifti_image_to_ascii( const nifti_image *nim )
    if( nim->sform_code > 0 ){
      int i,j,k ;
 
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  sform_code = '%d'\n"
               "  sform_code_name = '%s'\n"
      "  sto_xyz_matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" ,
@@ -6317,7 +6410,7 @@ char *nifti_image_to_ascii( const nifti_image *nim )
          nim->sto_xyz.m[3][0] , nim->sto_xyz.m[3][1] ,
          nim->sto_xyz.m[3][2] , nim->sto_xyz.m[3][3]  ) ;
 
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
      "  sto_ijk matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" ,
          nim->sto_ijk.m[0][0] , nim->sto_ijk.m[0][1] ,
          nim->sto_ijk.m[0][2] , nim->sto_ijk.m[0][3] ,
@@ -6330,7 +6423,7 @@ char *nifti_image_to_ascii( const nifti_image *nim )
 
      nifti_mat44_to_orientation( nim->sto_xyz , &i,&j,&k ) ;
      if( i > 0 && j > 0 && k > 0 )
-       sprintf( buf+strlen(buf) ,
+       snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
                 "  sform_i_orientation = '%s'\n"
                 "  sform_j_orientation = '%s'\n"
                 "  sform_k_orientation = '%s'\n" ,
@@ -6339,9 +6432,9 @@ char *nifti_image_to_ascii( const nifti_image *nim )
                 nifti_orientation_string(k)  ) ;
    }
 
-   sprintf( buf+strlen(buf) , "  num_ext = '%d'\n", nim->num_ext ) ;
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  num_ext = '%d'\n", nim->num_ext ) ;
 
-   sprintf( buf+strlen(buf) , "/>\n" ) ;   /* XML-ish closer */
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "/>\n" ) ;   /* XML-ish closer */
 
    nbuf = (int)strlen(buf) ;
    buf  = (char *)realloc((void *)buf, nbuf+1); /* cut back to proper length */
@@ -6379,7 +6472,7 @@ int nifti_short_order(void)   /* determine this CPU's byte order */
 /* macro to check lhs string against "n1"; if it matches,
    interpret rhs string as a number, and put it into nim->"n2" */
 
-#define QQNUM(n1,n2,tt) if( strcmp(lhs,#n1)==0 ) nim->n2=(tt)strtod(rhs,NULL)
+#define QQNUM(n1,n2,tt) if( strcmp(lhs,#n1)==0 ) nim->n2=(tt)(strtod(rhs,NULL))
 
 /* same, but where "n1" == "n2" */
 
@@ -6860,7 +6953,7 @@ compute_strides(int *strides,const int *size,int nbyper)
 /*---------------------------------------------------------------------------*/
 /*! read an arbitrary subregion from a nifti image
 
-    This function may be used to read a single arbitary subregion of any
+    This function may be used to read a single arbitrary subregion of any
     rectangular size from a nifti dataset, such as a small 5x5x5 subregion
     around the center of a 3D image.
 
@@ -6881,7 +6974,7 @@ compute_strides(int *strides,const int *size,int nbyper)
           speed and possibly repeated calls to this function.
     \return
         -  the total number of bytes read, or < 0 on failure
-        -  the read and byte-swapped data, in 'data'            
+ - the read and byte-swapped data, in 'data' \sa nifti_image_read, nifti_image_free, nifti_image_read_bricks nifti_image_load, nifti_read_collapsed_image @@ -6894,7 +6987,7 @@ int nifti_read_subregion_image( nifti_image * nim, znzFile fp; /* file to read */ int i,j,k,l,m,n; /* indices for dims */ long int bytes = 0; /* total # bytes read */ - int total_alloc_size; /* size of buffer allocation */ + size_t total_alloc_size; /* size of buffer allocation */ char *readptr; /* where in *data to read next */ int strides[7]; /* strides between dimensions */ int collapsed_dims[8]; /* for read_collapsed_image */ @@ -6965,6 +7058,13 @@ int nifti_read_subregion_image( nifti_image * nim, /* get the file open */ fp = nifti_image_load_prep( nim ); + if(znz_isnull(fp)) + { + if(g_opts.debug > 0) + Rc_fprintf_stderr("** nifti_read_subregion_image, failed load_prep\n"); + return -1; + } + /* the current offset is just past the nifti header, save * location so that SEEK_SET can be used below */ @@ -6989,9 +7089,10 @@ int nifti_read_subregion_image( nifti_image * nim, { if(g_opts.debug > 1) { - Rc_fprintf_stderr("allocation of %d bytes failed\n",total_alloc_size); - return -1; + Rc_fprintf_stderr("allocation of %d bytes failed\n", (int)total_alloc_size); } + znzclose(fp); + return -1; } /* point to start of data buffer as char * */ @@ -7038,11 +7139,12 @@ int nifti_read_subregion_image( nifti_image * nim, nread = (int)nifti_read_buffer(fp, readptr, read_amount, nim); if(nread != read_amount) { - if(g_opts.debug > 1) + if(g_opts.debug > 0) { Rc_fprintf_stderr("read of %d bytes failed\n",read_amount); - return -1; } + znzclose(fp); + return -1; } bytes += nread; readptr += read_amount; @@ -7053,6 +7155,7 @@ int nifti_read_subregion_image( nifti_image * nim, } } } + znzclose(fp); return bytes; } @@ -7256,7 +7359,7 @@ int * nifti_get_intlist( int nvals , const char * str ) int *subv = NULL ; int *subv_realloc = NULL; int ii , ipos , nout , slen ; - int ibot,itop,istep , nused ; + int ibot,itop,istep ; char *cpt ; /* Meaningless input? */ @@ -7292,7 +7395,13 @@ int * nifti_get_intlist( int nvals , const char * str ) if( str[ipos] == '$' ){ /* special case */ ibot = nvals-1 ; ipos++ ; } else { /* decode an integer */ - ibot = strtol( str+ipos , &cpt , 10 ) ; + errno = 0; + long temp = strtol( str+ipos , &cpt , 10 ) ; + if( (temp == 0 && errno != 0) || temp <= INT_MIN || temp >= INT_MAX){ + Rc_fprintf_stderr("** ERROR: list index does not fit in int\n") ; + free(subv) ; return NULL ; + } + ibot = (int)temp; if( ibot < 0 ){ Rc_fprintf_stderr("** ERROR: list index %d is out of range 0..%d\n", ibot,nvals-1) ; @@ -7303,7 +7412,7 @@ int * nifti_get_intlist( int nvals , const char * str ) ibot,nvals-1) ; free(subv) ; return NULL ; } - nused = (cpt-(str+ipos)) ; + long nused = (cpt-(str+ipos)) ; if( ibot == 0 && nused == 0 ){ Rc_fprintf_stderr("** ERROR: list syntax error '%s'\n",str+ipos) ; free(subv) ; return NULL ; @@ -7349,7 +7458,13 @@ int * nifti_get_intlist( int nvals , const char * str ) if( str[ipos] == '$' ){ /* special case */ itop = nvals-1 ; ipos++ ; } else { /* decode an integer */ - itop = strtol( str+ipos , &cpt , 10 ) ; + errno = 0; + long temp = strtol( str+ipos , &cpt , 10 ) ; + if( (temp == 0 && errno != 0) || temp <= INT_MIN || temp >= INT_MAX){ + Rc_fprintf_stderr("** ERROR: list index does not fit in int\n") ; + free(subv) ; return NULL ; + } + itop = (int)temp; if( itop < 0 ){ Rc_fprintf_stderr("** ERROR: index %d is out of range 0..%d\n", itop,nvals-1) ; @@ -7360,7 +7475,7 @@ int * nifti_get_intlist( int nvals , const char * str ) itop,nvals-1) ; free(subv) ; return NULL ; } - nused = (cpt-(str+ipos)) ; + long nused = (cpt-(str+ipos)) ; if( itop == 0 && nused == 0 ){ Rc_fprintf_stderr("** ERROR: index list syntax error '%s'\n",str+ipos) ; free(subv) ; return NULL ; @@ -7378,12 +7493,18 @@ int * nifti_get_intlist( int nvals , const char * str ) if( str[ipos] == '(' ){ /* decode an integer */ ipos++ ; - istep = strtol( str+ipos , &cpt , 10 ) ; + errno = 0; + long temp = strtol( str+ipos , &cpt , 10 ) ; + if( (temp == 0 && errno != 0) || temp <= INT_MIN || temp >= INT_MAX){ + Rc_fprintf_stderr("** ERROR: list index does not fit in int\n") ; + free(subv) ; return NULL ; + } + istep = (int)temp; if( istep == 0 ){ Rc_fprintf_stderr("** ERROR: index loop step is 0!\n") ; free(subv) ; return NULL ; } - nused = (cpt-(str+ipos)) ; + long nused = (cpt-(str+ipos)) ; ipos += nused ; if( str[ipos] == ')' ) ipos++ ; if( (ibot-itop)*istep > 0 ){ diff --git a/reg-io/niftilib/nifti1_io.h b/reg-io/niftilib/nifti1_io.h index 0e95531c..5c67b585 100644 --- a/reg-io/niftilib/nifti1_io.h +++ b/reg-io/niftilib/nifti1_io.h @@ -49,7 +49,7 @@ extern "C" { Mainly adding low-level IO and changing things to allow gzipped files to be read and written - Full backwards compatability should have been maintained + Full backwards compatibility should have been maintained Modified by: Rick Reynolds (SSCC/DIRP/NIMH, National Institutes of Health) Date: December 2004 @@ -316,8 +316,12 @@ int nifti_read_subregion_image( nifti_image * nim, void ** data ); void nifti_image_write ( nifti_image * nim ) ; +int nifti_image_write_status( nifti_image *nim ); + void nifti_image_write_bricks(nifti_image * nim, const nifti_brick_list * NBL); +int nifti_image_write_bricks_status(nifti_image * nim, + const nifti_brick_list * NBL); void nifti_image_infodump( const nifti_image * nim ) ; void nifti_disp_lib_hist( void ) ; /* to display library history */ @@ -534,7 +538,7 @@ typedef struct { char const * const name; /* text string to match #define */ } nifti_type_ele; -#undef LNI_FERR /* local nifti file error, to be compact and repetative */ +#undef LNI_FERR /* local nifti file error, to be compact and repetitive */ #ifdef USING_R #define LNI_FERR(func,msg,file) \ Rf_warning("%s: %s '%s'\n",func,msg,file) diff --git a/reg-io/niftilib/nifti1_io_version.h b/reg-io/niftilib/nifti1_io_version.h new file mode 100644 index 00000000..ac5e8203 --- /dev/null +++ b/reg-io/niftilib/nifti1_io_version.h @@ -0,0 +1,16 @@ +/* NOTE: When changing version consider the impact on versions in + nifti2_io_version.h nifti1_io_version.h nifticdf_version.h and znzlib.h +*/ +#define NIFTI1_IO_VERSION_MAJOR 2 +#define NIFTI1_IO_VERSION_MINOR 1 +#define NIFTI1_IO_VERSION_PATCH 0 + +/* main string macros: NIFTI1_IO_VERSION and NIFTI1_IO_SOURCE_VERSION */ +#define NIFTI1_IO_VERSION_TO_STRING(x) NIFTI1_IO_VERSION_TO_STRING0(x) +#define NIFTI1_IO_VERSION_TO_STRING0(x) #x +#define NIFTI1_IO_VERSION \ + NIFTI1_IO_VERSION_TO_STRING(NIFTI1_IO_VERSION_MAJOR) \ + "." NIFTI1_IO_VERSION_TO_STRING(NIFTI1_IO_VERSION_MINOR) \ + "." NIFTI1_IO_VERSION_TO_STRING(NIFTI1_IO_VERSION_PATCH) + +#define NIFTI1_IO_SOURCE_VERSION "NIFTI1_IO version " NIFTI1_IO_VERSION diff --git a/reg-io/niftilib/nifti2_io.c b/reg-io/niftilib/nifti2_io.c index a87fa3fd..634bef72 100644 --- a/reg-io/niftilib/nifti2_io.c +++ b/reg-io/niftilib/nifti2_io.c @@ -1,6 +1,7 @@ #define NIFTI2_IO_C #include "niftilib/nifti2_io.h" /* typedefs, prototypes, macros, etc. */ +#include "niftilib/nifti2_io_version.h" /*****===================================================================*****/ /***** Sample functions to deal with NIFTI-1,2 and ANALYZE files *****/ @@ -41,7 +42,7 @@ static char const * const gni1_history[] = " (FMRIB Centre, University of Oxford, UK)\n" " - Mainly adding low-level IO and changing things to allow gzipped\n" " files to be read and written\n" - " - Full backwards compatability should have been maintained\n" + " - Full backwards compatibility should have been maintained\n" "\n", "0.2 16 Nov 2004 [rickr]\n" " (Rick Reynolds of the National Institutes of Health, SSCC/DIRP/NIMH)\n" @@ -192,7 +193,7 @@ static char const * const gni1_history[] = "\n", "1.3 09 Feb 2005 [rickr]\n" " - nifti1.h: added doxygen comments for extension structs\n" - " - nifti1_io.h: put most #defines in #ifdef NIFTI1_IO_C block\n" + " - nifti1_io.h: put most #defines in #ifdef _NIFTI1_IO_C_ block\n" " - added a doxygen-style description to every exported function\n" " - added doxygen-style comments within some functions\n" " - re-exported many znzFile functions that I had made static\n" @@ -264,7 +265,7 @@ static char const * const gni1_history[] = "1.12b 25 August 2005 [rickr] - changes by Hans Johnson\n", "1.13 25 August 2005 [rickr]\n", " - finished changes by Hans for Insight\n" - " - added const in all appropraite parameter locations (30-40)\n" + " - added const in all appropriate parameter locations (30-40)\n" " (any pointer referencing data that will not change)\n" " - shortened all string constants below 509 character limit\n" "1.14 28 October 2005 [HJohnson]\n", @@ -397,11 +398,16 @@ static char const * const gni2_history[] = "2.09 10 May, 2019 [rickr]: added NIFTI_ECODE_QUANTIPHYSE\n" "2.10 26 Sep, 2019 [rickr]: nifti_read_ascii_image no longer closes fp\n", "2.11 3 Oct, 2019 [rickr]: added nifti_[d]mat33_mul\n", + "2.1.0 18 Jun, 2020 [leej3,hmjohnson,rickr]:\n" + " - changed to more formal library versioning\n", + "2.1.0.1 - non-release update - 2 Mar, 2022 [rickr]\n" + " - cast a few more pedantic void*'s\n" + "2.1.0.2 - non-release update - 16 Jun, 2022 [rickr]\n" + " - add nifti_image_write_status\n", "----------------------------------------------------------------------\n" }; -static const char gni_version[] - = "nifti-2 library version 2.11 (3 Oct, 2019)"; +static const char gni_version[] = NIFTI2_IO_SOURCE_VERSION " (16 Jun, 2022)"; /*! global nifti options structure - init with defaults */ /* see 'option accessor functions' */ @@ -489,12 +495,12 @@ static int nifti_NBL_matches_nim(const nifti_image *nim, const nifti_brick_list *NBL); /* for nifti_read_collapsed_image: */ -static int rci_read_data(nifti_image *nim, int *pivots, int64_t *prods, +static int rci_read_data(nifti_image *nim, int64_t *pivots, int64_t *prods, int nprods, const int64_t dims[], char *data, znzFile fp, int64_t base_offset); static int rci_alloc_mem(void **data, const int64_t prods[8], int nprods, int nbyper); static int make_pivot_list(nifti_image * nim, const int64_t dims[], - int pivots[], int64_t prods[], int * nprods ); + int64_t pivots[], int64_t prods[], int * nprods ); /* misc */ static int compare_strlist (const char * str, char ** strlist, int len); @@ -514,6 +520,8 @@ static char *escapize_string (const char *str); static int nifti_ext_type_index(nifti_image * nim, int ecode); /* internal I/O routines */ +static int nifti_image_write_engine(nifti_image *nim, int write_opts, + const char * opts, znzFile * imgfile, const nifti_brick_list * NBL); static znzFile nifti_image_load_prep( nifti_image *nim ); static int has_ascii_header(znzFile fp); /*---------------------------------------------------------------------------*/ @@ -629,7 +637,7 @@ nifti_image *nifti2_image_read_bricks(const char * hname, int64_t nbricks, if( !hname || !NBL ){ Rc_fprintf_stderr("** nifti_image_read_bricks: bad params (%p,%p)\n", - hname, (void *)NBL); + (void *)hname, (void *)NBL); return NULL; } @@ -866,7 +874,7 @@ int nifti2_image_load_bricks( nifti_image * nim , int64_t nbricks, if( rv != 0 ){ nifti_free_NBL( NBL ); /* failure! */ - NBL->nbricks = 0; /* repetative, but clear */ + NBL->nbricks = 0; /* repetitive, but clear */ } if( slist ){ free(slist); free(sindex); } @@ -1524,8 +1532,6 @@ char const *nifti_orientation_string( int ii ) \param nbyper pointer to return value: number of bytes per voxel \param swapsize pointer to return value: size of swap blocks - \return appropriate values at nbyper and swapsize - The swapsize is set to 0 if this datatype doesn't ever need swapping. \sa NIFTI1_DATATYPES in nifti1.h @@ -2613,7 +2619,7 @@ mat33 nifti_mat33_polar( mat33 A ) } /*---------------------------------------------------------------------------*/ -/*! compute the (closest) orientation from a 4x4 ijk->xyz tranformation matrix +/*! compute the (closest) orientation from a 4x4 ijk->xyz transformation matrix
    Input:  4x4 matrix that transforms (i,j,k) indexes to (x,y,z) coordinates,
@@ -2771,7 +2777,7 @@ void nifti_dmat44_to_orientation( nifti_dmat44 R ,
      case -2: i = NIFTI_A2P ; break ;
      case  3: i = NIFTI_I2S ; break ;
      case -3: i = NIFTI_S2I ; break ;
-     default: break;
+     default: break ;
    }
 
    switch( jbest*qbest ){
@@ -2781,7 +2787,7 @@ void nifti_dmat44_to_orientation( nifti_dmat44 R ,
      case -2: j = NIFTI_A2P ; break ;
      case  3: j = NIFTI_I2S ; break ;
      case -3: j = NIFTI_S2I ; break ;
-     default: break;
+     default: break ;
    }
 
    switch( kbest*rbest ){
@@ -2791,13 +2797,13 @@ void nifti_dmat44_to_orientation( nifti_dmat44 R ,
      case -2: k = NIFTI_A2P ; break ;
      case  3: k = NIFTI_I2S ; break ;
      case -3: k = NIFTI_S2I ; break ;
-     default: break;
+     default: break ;
    }
 
    *icod = i ; *jcod = j ; *kcod = k ; }
 
 /*---------------------------------------------------------------------------*/
-/*! compute the (closest) orientation from a 4x4 ijk->xyz tranformation matrix
+/*! compute the (closest) orientation from a 4x4 ijk->xyz transformation matrix
 
    
    Input:  4x4 matrix that transforms (i,j,k) indexes to (x,y,z) coordinates,
@@ -2991,8 +2997,8 @@ void nifti_mat44_to_orientation( mat44 R , int *icod, int *jcod, int *kcod )
 /*! swap each byte pair from the given list of n pairs
  *
  *  Due to alignment of structures at some architectures (e.g. on ARM),
- *  stick to char varaibles.
- *  Fixes http://bugs.debian.org/446893   Yaroslav 
+ *  stick to char variables.
+ *  Fixes  Yaroslav 
  *
 *//*--------------------------------------------------------------------*/
 void nifti_swap_2bytes( int64_t n , void *ar )    /* 2 bytes at a time */
@@ -3494,7 +3500,7 @@ int nifti_validfilename(const char* fname)
 
     \return a pointer to the extension substring within the original
             function input parameter name, or NULL if not found.
-    \caution Note that if the input parameter is is immutabale
+    \warning Note that if the input parameter is is immutabale
              (i.e. a const char *) then this function performs an
              implicit casting away of the mutability constraint and
              the return parameter will appear as a mutable
@@ -3574,8 +3580,7 @@ int nifti_is_gzfile(const char* fname)
   if (fname == NULL) { return 0; }
 #ifdef HAVE_ZLIB
   { /* just so len doesn't generate compile warning */
-     int len;
-     len = (int)strlen(fname);
+     size_t len = strlen(fname);
      if (len < 3) return 0;  /* so we don't search before the name */
      if (fileext_compare(fname + strlen(fname) - 3,".gz")==0) { return 1; }
   }
@@ -3723,7 +3728,7 @@ char * nifti_findhdrname(const char* fname)
 
    /* note: efirst is 0 in the case of ".img" */
 
-   /* if the user passed an uppercase entension (.IMG), search for uppercase */
+   /* if the user passed an uppercase extension (.IMG), search for uppercase */
    if( eisupper ) {
       make_uppercase(elist[0]);
       make_uppercase(elist[1]);
@@ -3768,8 +3773,8 @@ char * nifti_findhdrname(const char* fname)
 /*! check current directory for existing image file
 
     \param fname filename to check for
-    \nifti_type  nifti_type for dataset - this determines whether to
-                 first check for ".nii" or ".img" (since both may exist)
+    \param nifti_type  nifti_type for dataset - this determines whether to
+                       first check for ".nii" or ".img" (since both may exist)
 
     \return filename of data/img file on success and NULL if no appropriate
             file could be found
@@ -4025,7 +4030,7 @@ int nifti2_set_filenames( nifti_image * nim, const char * prefix, int check,
 
    if( !nim || !prefix ){
       Rc_fprintf_stderr("** nifti_set_filenames, bad params %p, %p\n",
-              (void *)nim,prefix);
+              (void *)nim, (void *)prefix);
       return -1;
    }
 
@@ -4060,11 +4065,11 @@ int nifti2_set_filenames( nifti_image * nim, const char * prefix, int check,
     - if type 1, expect .nii (and names must match)
 
     \param nim       given nifti_image
-    \param show_warn if set, print a warning message for any mis-match
+    \param show_warn if set, print a warning message for any mismatch
 
     \return
         -   1 if the values seem to match
-        -   0 if there is a mis-match
+        -   0 if there is a mismatch
         -  -1 if there is not sufficient information to create file(s)
 
     \sa NIFTI_FTYPE_* codes in nifti1_io.h
@@ -4116,7 +4121,7 @@ int nifti2_type_and_names_match( nifti_image * nim, int show_warn )
       errs++;
    }
 
-   if( errs ) return 0;   /* do not proceed, but this is just a mis-match */
+   if( errs ) return 0;   /* do not proceed, but this is just a mismatch */
 
    /* general tests */
    if( (nim->nifti_type == NIFTI_FTYPE_NIFTI1_1) ||
@@ -4353,7 +4358,7 @@ int nifti2_set_type_from_names( nifti_image * nim )
 
    if( !nim->fname || !nim->iname ){
       Rc_fprintf_stderr("** NIFTI_STFN: NULL filename(s) fname @ %p, iname @ %p\n",
-              nim->fname, nim->iname);
+              (void *)nim->fname, (void *)nim->iname);
       return -1;
    }
 
@@ -4376,8 +4381,10 @@ int nifti2_set_type_from_names( nifti_image * nim )
       nim->nifti_type = NIFTI_FTYPE_ASCII;
    } else {
       /* not too picky here, do what must be done, and then verify */
-      if( strcmp(nim->fname, nim->iname) == 0 )          /* one file, type 1 */
-         nim->nifti_type = (nim->nifti_type >= NIFTI_FTYPE_NIFTI2_1) ? NIFTI_FTYPE_NIFTI2_1 : NIFTI_FTYPE_NIFTI1_1;
+      if( strcmp(nim->fname, nim->iname) == 0 ) {        /* one file, type 1 */
+         nim->nifti_type = (nim->nifti_type >= NIFTI_FTYPE_NIFTI2_1) ?
+                              NIFTI_FTYPE_NIFTI2_1 : NIFTI_FTYPE_NIFTI1_1;
+      }
       else if( nim->nifti_type == NIFTI_FTYPE_NIFTI1_1 ) /* cannot be type 1 */
          nim->nifti_type = NIFTI_FTYPE_NIFTI1_2;
       else if( nim->nifti_type == NIFTI_FTYPE_NIFTI2_1 )
@@ -4729,7 +4736,7 @@ nifti_image* nifti_convert_n1hdr2nim(nifti_1_header nhdr, const char * fname)
      if( nhdr.dim[ii] <= 0 ) nhdr.dim[ii] = 1 ;
 
    /* fix any remaining bad dim[] values, so garbage does not propagate */
-   /* (only values 0 or 1 seem rational, otherwise set to arbirary 1)   */
+   /* (only values 0 or 1 seem rational, otherwise set to arbitrary 1)   */
    for( ii=nhdr.dim[0]+1 ; ii <= 7 ; ii++ )
      if( nhdr.dim[ii] != 1 && nhdr.dim[ii] != 0) nhdr.dim[ii] = 1 ;
 
@@ -4960,7 +4967,8 @@ nifti_image* nifti_convert_n1hdr2nim(nifti_1_header nhdr, const char * fname)
 *//*--------------------------------------------------------------------*/
 nifti_image* nifti_convert_n2hdr2nim(nifti_2_header nhdr, const char * fname)
 {
-   int          ii, doswap, ni_ver, is_onefile;
+   int64_t      ii;
+   int          doswap, ni_ver, is_onefile;
    nifti_image *nim;
 
    nim = (nifti_image *)calloc( 1 , sizeof(nifti_image) ) ;
@@ -5008,7 +5016,7 @@ nifti_image* nifti_convert_n2hdr2nim(nifti_2_header nhdr, const char * fname)
      if( nhdr.dim[ii] <= 0 ) nhdr.dim[ii] = 1 ;
 
    /* fix any remaining bad dim[] values, so garbage does not propagate */
-   /* (only values 0 or 1 seem rational, otherwise set to arbirary 1)   */
+   /* (only values 0 or 1 seem rational, otherwise set to arbitrary 1)   */
    for( ii=nhdr.dim[0]+1 ; ii <= 7 ; ii++ )
      if( nhdr.dim[ii] != 1 && nhdr.dim[ii] != 0) nhdr.dim[ii] = 1 ;
 
@@ -5022,9 +5030,9 @@ nifti_image* nifti_convert_n2hdr2nim(nifti_2_header nhdr, const char * fname)
 
    nim->nifti_type = (is_onefile) ? NIFTI_FTYPE_NIFTI2_1 : NIFTI_FTYPE_NIFTI2_2;
 
-   ii = nifti_short_order() ;
-   if( doswap )   nim->byteorder = REVERSE_ORDER(ii) ;
-   else           nim->byteorder = ii ;
+   int byteOrder = nifti_short_order() ;
+   if( doswap )   nim->byteorder = REVERSE_ORDER(byteOrder) ;
+   else           nim->byteorder = byteOrder ;
 
 
   /**- set dimensions of data array */
@@ -6091,7 +6099,7 @@ nifti_image * nifti2_read_ascii_image(znzFile fp, const char *fname, int flen,
                                      int read_data)
 {
    nifti_image * nim;
-   int           slen, txt_size, remain, rv = 0;
+   int           txt_size, remain, rv = 0;
    char        * sbuf, lfunc[25] = { "nifti_read_ascii_image" };
 
    if( nifti_is_gzfile(fname) ){
@@ -6099,11 +6107,11 @@ nifti_image * nifti2_read_ascii_image(znzFile fp, const char *fname, int flen,
               fname);
      return NULL;
    }
-   slen = flen;  /* slen will be our buffer length */
+   int64_t slen = flen;  /* slen will be our buffer length */
    if( slen <= 0 ) slen = nifti_get_filesize(fname);
 
    if( g_opts.debug > 1 )
-      Rc_fprintf_stderr("-d %s: have ASCII NIFTI file of size %d\n",fname,slen);
+      Rc_fprintf_stderr("-d %s: have ASCII NIFTI file of size %d\n",fname,(int)slen);
 
    if( slen > 65530 ) slen = 65530 ;
    sbuf = (char *)calloc(sizeof(char),slen+1) ;
@@ -6259,7 +6267,7 @@ static int nifti_read_extensions( nifti_image *nim, znzFile fp, int64_t remain )
 
    \param nim    - nifti_image to add extension to
    \param data   - raw extension data
-   \param length - length of raw extension data
+   \param len    - length of raw extension data
    \param ecode  - extension code
 
    \sa extension codes NIFTI_ECODE_* in nifti1_io.h
@@ -6517,8 +6525,8 @@ int valid_nifti2_extensions(const nifti_image * nim)
        \return -1 on error, else NIFTI version
  *//*--------------------------------------------------------------------*/
 int nifti_header_version(const char * buf, size_t nbytes){
-   nifti_1_header *n1p = (nifti_1_header *)buf;
-   nifti_2_header *n2p = (nifti_2_header *)buf;
+   const nifti_1_header *n1p = (const nifti_1_header *)buf;
+   const nifti_2_header *n2p = (const nifti_2_header *)buf;
    char            fname[] = { "nifti_header_version" };
    int             sizeof_hdr, sver, nver;
 
@@ -6530,7 +6538,7 @@ int nifti_header_version(const char * buf, size_t nbytes){
 
    if( nbytes < sizeof(nifti_1_header) ) {
       if(g_opts.debug > 0)
-         Rc_fprintf_stderr("** %s: nbytes=%zu, too small for test", fname, nbytes);
+         Rc_fprintf_stderr("** %s: nbytes=%u, too small for test", fname, (unsigned)nbytes);
       return -1;
    }
 
@@ -6659,7 +6667,8 @@ static znzFile nifti_image_load_prep( nifti_image *nim )
       if ( g_opts.debug > 0 ){
          if( !nim ) Rc_fprintf_stderr("** ERROR: N_image_load: no nifti image\n");
          else Rc_fprintf_stderr("** ERROR: nifti_image_load: bad params (%p,%d,"
-                      "%" PRId64 ")\n", nim->iname, nim->nbyper, nim->nvox);
+                      "%" PRId64 ")\n",
+                      (void *)nim->iname, nim->nbyper, nim->nvox);
       }
       return NULL;
    }
@@ -7306,7 +7315,7 @@ nifti_1_header * nifti_make_new_n1_header(const int64_t arg_dims[],
 /*! basic creation of a nifti_image struct
 
    Create a nifti_image from the given dimensions and data type.
-   Optinally, allocate zero-filled data.
+   Optionally, allocate zero-filled data.
 
    \param dims      : optional dim[8]   (default {3,1,1,1,0,0,0,0})
    \param datatype  : optional datatype (default DT_FLOAT32)
@@ -7777,24 +7786,42 @@ znzFile nifti2_image_write_hdr_img( nifti_image *nim , int write_data ,
 }
 
 
+/*----------------------------------------------------------------------*/
+/*! This writes the header (and optionally the image data) to file.
+ *
+ * This is now just a front-end for nifti_image_write_engine, but the
+ * engine will return a status (for success of write), which is promptly
+ * ignored by this function.
+ *
+ * \sa nifti_image_write_engine
+*//*--------------------------------------------------------------------*/
+znzFile nifti_image_write_hdr_img2(nifti_image *nim, int write_opts,
+               const char * opts, znzFile imgfile, const nifti_brick_list * NBL)
+{
+   znzFile loc_img = imgfile;   /* might be NULL, might point to open struct */
+   (void)nifti_image_write_engine(nim, write_opts, opts, &loc_img, NBL);
+   return loc_img;
+}
+
 #undef  ERREX
-#define ERREX(msg)                                                \
- do{ Rc_fprintf_stderr("** ERROR: nifti_image_write_hdr_img: %s\n",(msg)) ;  \
-     return fp ; } while(0)
+#define ERREX(msg)                                                         \
+ do{ Rc_fprintf_stderr("** ERROR: nifti_image_write_engine: %s\n",(msg)) ; \
+     if( imgfile ) *imgfile = fp;                                          \
+     return 1 ; } while(0)
 
 
 /* ----------------------------------------------------------------------*/
 /*! This writes the header (and optionally the image data) to file
  *
- * If the image data file is left open it returns a valid znzFile handle.
- * It also uses imgfile as the open image file is not null, and modifies
- * it inside.
+ * If imgfile points to a NULL znzFile, it modifies it to a valid and open
+ * handle.  If it points to an non-NULL znzFile, it uses that as the open
+ * image and simply modifies that structure.  This also depends on write_opts.
  *
  * \param nim        nifti_image to write to disk
  * \param write_opts flags whether to write data and/or close file (see below)
  * \param opts       file-open options, probably "wb" from nifti_image_write()
- * \param imgfile    optional open znzFile struct, for writing image data
-                     (may be NULL)
+ * \param imgfile    pointer to optionaly open znzFile, for writing image data
+                     (must not be NULL, contents might be NULL)
  * \param NBL        optional nifti_brick_list, containing the image data
                      (may be NULL)
  *
@@ -7808,27 +7835,29 @@ znzFile nifti2_image_write_hdr_img( nifti_image *nim , int write_data ,
  * \sa nifti_image_write, nifti_image_write_hdr_img, nifti_image_free,
  *     nifti_set_filenames
 *//*---------------------------------------------------------------------*/
-znzFile nifti2_image_write_hdr_img2(nifti_image *nim, int write_opts,
-               const char * opts, znzFile imgfile, const nifti_brick_list * NBL)
+static int nifti_image_write_engine(nifti_image *nim, int write_opts,
+        const char * opts, znzFile * imgfile, const nifti_brick_list * NBL)
 {
    nifti_1_header n1hdr ;
    nifti_2_header n2hdr ;
    znzFile        fp=NULL;
    int64_t        ss ;
    int            write_data, leave_open;
-   int            nver=1, hsize=(int)sizeof(nifti_1_header);  /* 5 Aug 2015 */
-   char           func[] = { "nifti_image_write_hdr_img2" };
+   int            nver, hsize;
+   char           func[] = { "nifti_image_write_engine" };
 
    write_data = write_opts & 1;  /* just separate the bits now */
    leave_open = write_opts & 2;
 
-   if( ! nim                              ) ERREX("NULL input") ;
+   /* check for valid input */
+   if( ! nim || ! imgfile                 ) ERREX("NULL input") ;
    if( ! nifti_validfilename(nim->fname)  ) ERREX("bad fname input") ;
    if( write_data && ! nim->data && ! NBL ) ERREX("no image data") ;
 
    if( write_data && NBL && ! nifti_NBL_matches_nim(nim, NBL) )
       ERREX("NBL does not match nim");
 
+   /* chit-chat */
    if( g_opts.debug > 1 ){
       Rc_fprintf_stderr("-d writing nifti file '%s'...\n", nim->fname);
       if( g_opts.debug > 2 )
@@ -7836,42 +7865,63 @@ znzFile nifti2_image_write_hdr_img2(nifti_image *nim, int write_opts,
                  nim->nifti_type, nim->iname_offset);
    }
 
-   if( nim->nifti_type == NIFTI_FTYPE_ASCII )   /* non-standard case */
-      return nifti_write_ascii_image(nim,NBL,opts,write_data,leave_open);
-   else if( nim->nifti_type == NIFTI_FTYPE_NIFTI2_1 || nim->nifti_type == NIFTI_FTYPE_NIFTI2_2 ) {
+   /* get to work */
+
+   /* if non-standard ASCII, just write output and return */
+   if( nim->nifti_type == NIFTI_FTYPE_ASCII ) {
+      *imgfile = nifti_write_ascii_image(nim,NBL,opts,write_data,leave_open);
+      return 0; /* write_ascii has no status, either */
+   }
+
+   /* create a header structure to write out */
+   if( nim->nifti_type == NIFTI_FTYPE_NIFTI2_1 ||
+            nim->nifti_type == NIFTI_FTYPE_NIFTI2_2 ) {
       nifti_set_iname_offset(nim, 2);
-      if( nifti_convert_nim2n2hdr(nim, &n2hdr) ) return NULL;
-      nver = 2;
+      if( nifti_convert_nim2n2hdr(nim, &n2hdr) ) {
+         *imgfile = NULL;
+         return 1;
+      }
+      nver = 2; /* we will write NIFTI-2 */
       hsize = (int)sizeof(nifti_2_header);
-   }
-   else {
+   } else {
       nifti_set_iname_offset(nim, 1);
-      if( nifti_convert_nim2n1hdr(nim, &n1hdr) ) return NULL;
+      if( nifti_convert_nim2n1hdr(nim, &n1hdr) ) {
+         *imgfile = NULL;
+         return 1;
+      }
+      nver = 1;
+      hsize = (int)sizeof(nifti_1_header);  /* 5 Aug 2015 */
    }
 
    /* if writing to 2 files, make sure iname is set and different from fname */
-   if( (nim->nifti_type != NIFTI_FTYPE_NIFTI1_1) && (nim->nifti_type != NIFTI_FTYPE_NIFTI2_1) ){
+   if( (nim->nifti_type != NIFTI_FTYPE_NIFTI1_1) &&
+       (nim->nifti_type != NIFTI_FTYPE_NIFTI2_1) ){
        if( nim->iname && strcmp(nim->iname,nim->fname) == 0 ){
          free(nim->iname) ; nim->iname = NULL ;
        }
        if( nim->iname == NULL ){ /* then make a new one */
          nim->iname = nifti_makeimgname(nim->fname,nim->nifti_type,0,0);
-         if( nim->iname == NULL ) return NULL;
+         if( nim->iname == NULL ) {
+            *imgfile = NULL;
+            return 1;
+         }
        }
    }
 
-   /* if we have an imgfile and will write the header there, use it */
-   if( ! znz_isnull(imgfile) && (nim->nifti_type == NIFTI_FTYPE_NIFTI1_1 || nim->nifti_type == NIFTI_FTYPE_NIFTI2_1) ){
+   /* if we have an imgfile and will also write the header there, use it */
+   if( ! znz_isnull(*imgfile) && (nim->nifti_type == NIFTI_FTYPE_NIFTI1_1 ||
+                                  nim->nifti_type == NIFTI_FTYPE_NIFTI2_1) ){
       if( g_opts.debug > 2 ) Rc_fprintf_stderr("+d using passed file for hdr\n");
-      fp = imgfile;
-   }
-   else {
+      fp = *imgfile;
+   } else {
+      /* we will write the header to a new file */
       if( g_opts.debug > 2 )
          Rc_fprintf_stderr("+d opening output file %s [%s]\n",nim->fname,opts);
       fp = znzopen( nim->fname , opts , nifti_is_gzfile(nim->fname) ) ;
       if( znz_isnull(fp) ){
          LNI_FERR(func,"cannot open output file",nim->fname);
-         return fp;
+         *imgfile = fp;
+         return 1;
       }
    }
 
@@ -7882,26 +7932,31 @@ znzFile nifti2_image_write_hdr_img2(nifti_image *nim, int write_opts,
 
    if( ss < hsize ){
       LNI_FERR(func,"bad header write to output file",nim->fname);
-      znzclose(fp); return fp;
+      znzclose(fp); *imgfile = fp; return 1;
    }
 
-   /* partial file exists, and errors have been printed, so ignore return */
+   /* write extensions; any errors will be printed */
    if( nim->nifti_type != NIFTI_FTYPE_ANALYZE )
-      (void)nifti_write_extensions(fp,nim);
+      if( nifti_write_extensions(fp,nim) < 0 ) {
+         znzclose(fp); *imgfile = fp; return 1;
+      }
 
    /* if the header is all we want, we are done */
    if( ! write_data && ! leave_open ){
       if( g_opts.debug > 2 ) Rc_fprintf_stderr("-d header is all we want: done\n");
-      znzclose(fp); return(fp);
+      znzclose(fp); *imgfile = fp; return 0;
    }
 
-   if( (nim->nifti_type != NIFTI_FTYPE_NIFTI1_1) && (nim->nifti_type != NIFTI_FTYPE_NIFTI2_1) ){ /* get a new file pointer */
+   /* if multiple files (hdr/img), close fp and use (any) *imgfile for data */
+   if( (nim->nifti_type != NIFTI_FTYPE_NIFTI1_1) &&
+       (nim->nifti_type != NIFTI_FTYPE_NIFTI2_1) ){ /* get a new file pointer */
       znzclose(fp);         /* first, close header file */
-      if( ! znz_isnull(imgfile) ){
+      /* use any valid *imgfile for img */
+      if( ! znz_isnull(*imgfile) ){
          if(g_opts.debug > 2) Rc_fprintf_stderr("+d using passed file for img\n");
-         fp = imgfile;
-      }
-      else {
+         fp = *imgfile;
+      } else {
+         /* else we need a new img file pointer */
          if( g_opts.debug > 2 )
             Rc_fprintf_stderr("+d opening img file '%s'\n", nim->iname);
          fp = znzopen( nim->iname , opts , nifti_is_gzfile(nim->iname) ) ;
@@ -7909,12 +7964,16 @@ znzFile nifti2_image_write_hdr_img2(nifti_image *nim, int write_opts,
       }
    }
 
+   /* have image pointer, ready to write */
+
    znzseek(fp, nim->iname_offset, SEEK_SET);  /* in any case, seek to offset */
 
    if( write_data ) nifti_write_all_data(fp,nim,NBL);
    if( ! leave_open ) znzclose(fp);
 
-   return fp;
+   *imgfile = fp;
+
+   return 0;
 }
 
 
@@ -7975,28 +8034,72 @@ znzFile nifti2_write_ascii_image(nifti_image *nim, const nifti_brick_list * NBL,
 *//*------------------------------------------------------------------------*/
 void nifti2_image_write( nifti_image *nim )
 {
-   znzFile fp = nifti_image_write_hdr_img(nim,1,"wb");
-   if( fp ){
+   (void)nifti_image_write_status(nim);
+}
+
+
+/*--------------------------------------------------------------------------*/
+/*! Write a nifti_image to disk, returning 0 on success, else failure.
+
+    This simple write function takes a nifti_image as input and returns
+    the status of the operation.  It is akin to nifti_image_write, but
+    returns the status.  Changing nifti_image_write from void to int
+    would have backward compatibility ramifications.
+
+   \sa nifti_image_write_bricks, nifti_image_free, nifti_set_filenames,
+       nifti_image_write_engine, nifti_image_write
+*//*------------------------------------------------------------------------*/
+int nifti2_image_write_status( nifti_image *nim )
+{
+   znzFile fp=NULL;   /* required for _engine, but promptly ignored */
+   int     rv;
+
+   rv = nifti_image_write_engine(nim, 1, "wb", &fp, NULL);
+
+   if( fp ){ /* this should not happen, as we requested file closure */
       if( g_opts.debug > 2 ) Rc_fprintf_stderr("-d niw: done with znzFile\n");
       free(fp);
    }
-   if( g_opts.debug > 1 ) Rc_fprintf_stderr("-d nifti_image_write: done\n");
+   if( g_opts.debug > 1 )
+      Rc_fprintf_stderr("-d nifti_image_write_status: done, status %d\n", rv);
+
+   return rv;
 }
 
 
 /*----------------------------------------------------------------------*/
-/*! similar to nifti_image_write, but data is in NBL struct, not nim->data
+/*! similar to nifti_image_write_status, but data is in NBL struct,
+    not nim->data
+
+   \return 0 on success, 1 on error
 
    \sa nifti_image_write, nifti_image_free, nifti_set_filenames, nifti_free_NBL
 *//*--------------------------------------------------------------------*/
-void nifti2_image_write_bricks( nifti_image *nim, const nifti_brick_list * NBL )
+int nifti2_image_write_bricks_status( nifti_image *nim,
+                                     const nifti_brick_list * NBL )
 {
-   znzFile fp = nifti_image_write_hdr_img2(nim,1,"wb",NULL,NBL);
+   znzFile fp=NULL;
+   int     rv;
+
+   rv = nifti_image_write_engine(nim, 1, "wb", &fp, NBL);
    if( fp ){
-      if( g_opts.debug > 2 ) Rc_fprintf_stderr("-d niwb: done with znzFile\n");
+      if( g_opts.debug > 2 ) Rc_fprintf_stderr("-d niw: done with znzFile\n");
       free(fp);
    }
-   if( g_opts.debug > 1 ) Rc_fprintf_stderr("-d niwb: done writing bricks\n");
+   if( g_opts.debug > 1 )
+      Rc_fprintf_stderr("-d niwb: done writing bricks, status %d\n", rv);
+   return rv;
+}
+
+
+/*----------------------------------------------------------------------*/
+/*! similar to nifti_image_write, but data is in NBL struct, not nim->data
+
+   \sa nifti_image_write, nifti_image_free, nifti_set_filenames, nifti_free_NBL
+*//*--------------------------------------------------------------------*/
+void nifti2_image_write_bricks( nifti_image *nim, const nifti_brick_list * NBL )
+{
+   (void)nifti_image_write_bricks_status(nim, NBL);
 }
 
 
@@ -8220,15 +8323,16 @@ char *nifti2_image_to_ascii( const nifti_image *nim )
    if( g_opts.debug > 2 )
       Rc_fprintf_stderr("+d converting %s to ASCII\n",nim->fname);
 
-   buf = (char *)calloc(1,65534); /* longer than needed, to be safe */
+   const size_t bufLen = 65534; /* longer than needed, to be safe */
+   buf = (char *)calloc(1,bufLen);
    if( !buf ){
       Rc_fprintf_stderr("** NIFTI NITA: failed to alloc %d bytes\n",65534);
       return NULL;
    }
 
-   sprintf( buf , "nifti_type == NIFTI_FTYPE_NIFTI1_1) ? "NIFTI-1+"
              :(nim->nifti_type == NIFTI_FTYPE_NIFTI1_2) ? "NIFTI-1"
              :(nim->nifti_type == NIFTI_FTYPE_ASCII   ) ? "NIFTI-1A"
@@ -8244,123 +8348,123 @@ char *nifti2_image_to_ascii( const nifti_image *nim )
        - The result is that the NIFTI ASCII-format header is XML-compliant. */
 
    ebuf = escapize_string(nim->fname) ;
-   sprintf( buf+strlen(buf) , "  header_filename = %s\n",ebuf); free(ebuf);
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  header_filename = %s\n",ebuf); free(ebuf);
 
    ebuf = escapize_string(nim->iname) ;
-   sprintf( buf+strlen(buf) , "  image_filename = %s\n", ebuf); free(ebuf);
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  image_filename = %s\n", ebuf); free(ebuf);
 
-   sprintf( buf+strlen(buf) , "  image_offset = '%" PRId64 "'\n" ,
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  image_offset = '%" PRId64 "'\n" ,
             nim->iname_offset );
 
-   sprintf( buf+strlen(buf), "  ndim = '%" PRId64 "'\n",nim->ndim);
-   sprintf( buf+strlen(buf), "  nx = '%" PRId64 "'\n",  nim->nx  );
+   snprintf( buf+strlen(buf), bufLen-strlen(buf), "  ndim = '%" PRId64 "'\n",nim->ndim);
+   snprintf( buf+strlen(buf), bufLen-strlen(buf), "  nx = '%" PRId64 "'\n",  nim->nx  );
    if( nim->ndim > 1 )
-      sprintf( buf+strlen(buf), "  ny = '%" PRId64 "'\n",  nim->ny  );
+      snprintf( buf+strlen(buf), bufLen-strlen(buf), "  ny = '%" PRId64 "'\n",  nim->ny  );
    if( nim->ndim > 2 )
-      sprintf( buf+strlen(buf), "  nz = '%" PRId64 "'\n",  nim->nz  );
+      snprintf( buf+strlen(buf), bufLen-strlen(buf), "  nz = '%" PRId64 "'\n",  nim->nz  );
    if( nim->ndim > 3 )
-      sprintf( buf+strlen(buf), "  nt = '%" PRId64 "'\n",  nim->nt  );
+      snprintf( buf+strlen(buf), bufLen-strlen(buf), "  nt = '%" PRId64 "'\n",  nim->nt  );
    if( nim->ndim > 4 )
-      sprintf( buf+strlen(buf), "  nu = '%" PRId64 "'\n",  nim->nu  );
+      snprintf( buf+strlen(buf), bufLen-strlen(buf), "  nu = '%" PRId64 "'\n",  nim->nu  );
    if( nim->ndim > 5 )
-      sprintf( buf+strlen(buf), "  nv = '%" PRId64 "'\n",  nim->nv  );
+      snprintf( buf+strlen(buf), bufLen-strlen(buf), "  nv = '%" PRId64 "'\n",  nim->nv  );
    if( nim->ndim > 6 )
-      sprintf( buf+strlen(buf), "  nw = '%" PRId64 "'\n",  nim->nw  );
-
-                       sprintf( buf+strlen(buf), "  dx = '%g'\n",   nim->dx  );
-   if( nim->ndim > 1 ) sprintf( buf+strlen(buf), "  dy = '%g'\n",   nim->dy  );
-   if( nim->ndim > 2 ) sprintf( buf+strlen(buf), "  dz = '%g'\n",   nim->dz  );
-   if( nim->ndim > 3 ) sprintf( buf+strlen(buf), "  dt = '%g'\n",   nim->dt  );
-   if( nim->ndim > 4 ) sprintf( buf+strlen(buf), "  du = '%g'\n",   nim->du  );
-   if( nim->ndim > 5 ) sprintf( buf+strlen(buf), "  dv = '%g'\n",   nim->dv  );
-   if( nim->ndim > 6 ) sprintf( buf+strlen(buf), "  dw = '%g'\n",   nim->dw  );
-
-   sprintf( buf+strlen(buf) , "  datatype = '%d'\n" , nim->datatype ) ;
-   sprintf( buf+strlen(buf) , "  datatype_name = '%s'\n" ,
+      snprintf( buf+strlen(buf), bufLen-strlen(buf), "  nw = '%" PRId64 "'\n",  nim->nw  );
+
+                       snprintf( buf+strlen(buf), bufLen-strlen(buf), "  dx = '%g'\n",   nim->dx  );
+   if( nim->ndim > 1 ) snprintf( buf+strlen(buf), bufLen-strlen(buf), "  dy = '%g'\n",   nim->dy  );
+   if( nim->ndim > 2 ) snprintf( buf+strlen(buf), bufLen-strlen(buf), "  dz = '%g'\n",   nim->dz  );
+   if( nim->ndim > 3 ) snprintf( buf+strlen(buf), bufLen-strlen(buf), "  dt = '%g'\n",   nim->dt  );
+   if( nim->ndim > 4 ) snprintf( buf+strlen(buf), bufLen-strlen(buf), "  du = '%g'\n",   nim->du  );
+   if( nim->ndim > 5 ) snprintf( buf+strlen(buf), bufLen-strlen(buf), "  dv = '%g'\n",   nim->dv  );
+   if( nim->ndim > 6 ) snprintf( buf+strlen(buf), bufLen-strlen(buf), "  dw = '%g'\n",   nim->dw  );
+
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  datatype = '%d'\n" , nim->datatype ) ;
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  datatype_name = '%s'\n" ,
                               nifti_datatype_string(nim->datatype) ) ;
 
-   sprintf( buf+strlen(buf) , "  nvox = '%" PRId64 "'\n" ,  nim->nvox ) ;
-   sprintf( buf+strlen(buf) , "  nbyper = '%d'\n" , nim->nbyper ) ;
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  nvox = '%" PRId64 "'\n" ,  nim->nvox ) ;
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  nbyper = '%d'\n" , nim->nbyper ) ;
 
-   sprintf( buf+strlen(buf) , "  byteorder = '%s'\n" ,
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  byteorder = '%s'\n" ,
             (nim->byteorder==MSB_FIRST) ? "MSB_FIRST" : "LSB_FIRST" ) ;
 
    if( nim->cal_min < nim->cal_max ){
-     sprintf( buf+strlen(buf) , "  cal_min = '%g'\n", nim->cal_min ) ;
-     sprintf( buf+strlen(buf) , "  cal_max = '%g'\n", nim->cal_max ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  cal_min = '%g'\n", nim->cal_min ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  cal_max = '%g'\n", nim->cal_max ) ;
    }
 
    if( nim->scl_slope != 0.0 ){
-     sprintf( buf+strlen(buf) , "  scl_slope = '%g'\n" , nim->scl_slope ) ;
-     sprintf( buf+strlen(buf) , "  scl_inter = '%g'\n" , nim->scl_inter ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  scl_slope = '%g'\n" , nim->scl_slope ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  scl_inter = '%g'\n" , nim->scl_inter ) ;
    }
 
    if( nim->intent_code > 0 ){
-     sprintf( buf+strlen(buf) , "  intent_code = '%d'\n", nim->intent_code ) ;
-     sprintf( buf+strlen(buf) , "  intent_code_name = '%s'\n" ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_code = '%d'\n", nim->intent_code ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_code_name = '%s'\n" ,
                                 nifti_intent_string(nim->intent_code) ) ;
-     sprintf( buf+strlen(buf) , "  intent_p1 = '%g'\n" , nim->intent_p1 ) ;
-     sprintf( buf+strlen(buf) , "  intent_p2 = '%g'\n" , nim->intent_p2 ) ;
-     sprintf( buf+strlen(buf) , "  intent_p3 = '%g'\n" , nim->intent_p3 ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_p1 = '%g'\n" , nim->intent_p1 ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_p2 = '%g'\n" , nim->intent_p2 ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_p3 = '%g'\n" , nim->intent_p3 ) ;
 
      if( nim->intent_name[0] != '\0' ){
        ebuf = escapize_string(nim->intent_name) ;
-       sprintf( buf+strlen(buf) , "  intent_name = %s\n",ebuf) ;
+       snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  intent_name = %s\n",ebuf) ;
        free(ebuf) ;
      }
    }
 
    if( nim->toffset != 0.0 )
-     sprintf( buf+strlen(buf) , "  toffset = '%g'\n",nim->toffset ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  toffset = '%g'\n",nim->toffset ) ;
 
    if( nim->xyz_units > 0 )
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  xyz_units = '%d'\n"
               "  xyz_units_name = '%s'\n" ,
               nim->xyz_units , nifti_units_string(nim->xyz_units) ) ;
 
    if( nim->time_units > 0 )
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  time_units = '%d'\n"
               "  time_units_name = '%s'\n" ,
               nim->time_units , nifti_units_string(nim->time_units) ) ;
 
    if( nim->freq_dim > 0 )
-     sprintf( buf+strlen(buf) , "  freq_dim = '%d'\n",nim->freq_dim ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  freq_dim = '%d'\n",nim->freq_dim ) ;
    if( nim->phase_dim > 0 )
-     sprintf( buf+strlen(buf) , "  phase_dim = '%d'\n",nim->phase_dim ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  phase_dim = '%d'\n",nim->phase_dim ) ;
    if( nim->slice_dim > 0 )
-     sprintf( buf+strlen(buf) , "  slice_dim = '%d'\n",nim->slice_dim ) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  slice_dim = '%d'\n",nim->slice_dim ) ;
    if( nim->slice_code > 0 )
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  slice_code = '%d'\n"
               "  slice_code_name = '%s'\n" ,
               nim->slice_code , nifti_slice_string(nim->slice_code) ) ;
    if( nim->slice_start >= 0 && nim->slice_end > nim->slice_start )
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  slice_start = '%" PRId64 "'\n"
               "  slice_end = '%" PRId64 "'\n",
               nim->slice_start , nim->slice_end ) ;
    if( nim->slice_duration != 0.0 )
-     sprintf( buf+strlen(buf) , "  slice_duration = '%g'\n",
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  slice_duration = '%g'\n",
               nim->slice_duration ) ;
 
    if( nim->descrip[0] != '\0' ){
      ebuf = escapize_string(nim->descrip) ;
-     sprintf( buf+strlen(buf) , "  descrip = %s\n",ebuf) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  descrip = %s\n",ebuf) ;
      free(ebuf) ;
    }
 
    if( nim->aux_file[0] != '\0' ){
      ebuf = escapize_string(nim->aux_file) ;
-     sprintf( buf+strlen(buf) , "  aux_file = %s\n",ebuf) ;
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  aux_file = %s\n",ebuf) ;
      free(ebuf) ;
    }
 
    if( nim->qform_code > 0 ){
      int i,j,k ;
 
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  qform_code = '%d'\n"
               "  qform_code_name = '%s'\n"
      "  qto_xyz_matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" ,
@@ -8374,7 +8478,7 @@ char *nifti2_image_to_ascii( const nifti_image *nim )
          nim->qto_xyz.m[3][0] , nim->qto_xyz.m[3][1] ,
          nim->qto_xyz.m[3][2] , nim->qto_xyz.m[3][3]  ) ;
 
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
      "  qto_ijk_matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" ,
          nim->qto_ijk.m[0][0] , nim->qto_ijk.m[0][1] ,
          nim->qto_ijk.m[0][2] , nim->qto_ijk.m[0][3] ,
@@ -8385,7 +8489,7 @@ char *nifti2_image_to_ascii( const nifti_image *nim )
          nim->qto_ijk.m[3][0] , nim->qto_ijk.m[3][1] ,
          nim->qto_ijk.m[3][2] , nim->qto_ijk.m[3][3]  ) ;
 
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  quatern_b = '%g'\n"
               "  quatern_c = '%g'\n"
               "  quatern_d = '%g'\n"
@@ -8398,7 +8502,7 @@ char *nifti2_image_to_ascii( const nifti_image *nim )
 
      nifti_dmat44_to_orientation( nim->qto_xyz , &i,&j,&k ) ;
      if( i > 0 && j > 0 && k > 0 )
-       sprintf( buf+strlen(buf) ,
+       snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
                 "  qform_i_orientation = '%s'\n"
                 "  qform_j_orientation = '%s'\n"
                 "  qform_k_orientation = '%s'\n" ,
@@ -8410,7 +8514,7 @@ char *nifti2_image_to_ascii( const nifti_image *nim )
    if( nim->sform_code > 0 ){
      int i,j,k ;
 
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
               "  sform_code = '%d'\n"
               "  sform_code_name = '%s'\n"
      "  sto_xyz_matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" ,
@@ -8424,7 +8528,7 @@ char *nifti2_image_to_ascii( const nifti_image *nim )
          nim->sto_xyz.m[3][0] , nim->sto_xyz.m[3][1] ,
          nim->sto_xyz.m[3][2] , nim->sto_xyz.m[3][3]  ) ;
 
-     sprintf( buf+strlen(buf) ,
+     snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
      "  sto_ijk matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" ,
          nim->sto_ijk.m[0][0] , nim->sto_ijk.m[0][1] ,
          nim->sto_ijk.m[0][2] , nim->sto_ijk.m[0][3] ,
@@ -8437,7 +8541,7 @@ char *nifti2_image_to_ascii( const nifti_image *nim )
 
      nifti_dmat44_to_orientation( nim->sto_xyz , &i,&j,&k ) ;
      if( i > 0 && j > 0 && k > 0 )
-       sprintf( buf+strlen(buf) ,
+       snprintf( buf+strlen(buf) , bufLen-strlen(buf) ,
                 "  sform_i_orientation = '%s'\n"
                 "  sform_j_orientation = '%s'\n"
                 "  sform_k_orientation = '%s'\n" ,
@@ -8446,9 +8550,9 @@ char *nifti2_image_to_ascii( const nifti_image *nim )
                 nifti_orientation_string(k)  ) ;
    }
 
-   sprintf( buf+strlen(buf) , "  num_ext = '%d'\n", nim->num_ext ) ;
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "  num_ext = '%d'\n", nim->num_ext ) ;
 
-   sprintf( buf+strlen(buf) , "/>\n" ) ;   /* XML-ish closer */
+   snprintf( buf+strlen(buf) , bufLen-strlen(buf) , "/>\n" ) ;   /* XML-ish closer */
 
    nbuf = (int)strlen(buf) ;
    buf  = (char *)realloc((void *)buf, nbuf+1); /* cut back to proper length */
@@ -8485,7 +8589,7 @@ int nifti_short_order(void)   /* determine this CPU's byte order */
 /* macro to check lhs string against "n1"; if it matches,
    interpret rhs string as a number, and put it into nim->"n2" */
 
-#define QQNUM(n1,n2,tt) if( strcmp(lhs,#n1)==0 ) nim->n2=(tt)strtod(rhs,NULL)
+#define QQNUM(n1,n2,tt) if( strcmp(lhs,#n1)==0 ) nim->n2=(tt)(strtod(rhs,NULL))
 
 /* same, but where "n1" == "n2" */
 
@@ -8903,7 +9007,8 @@ int64_t nifti2_read_collapsed_image( nifti_image * nim, const int64_t dims [8],
 {
    znzFile fp;
    int64_t prods[8];          /* sizes are bounded by dims[], so 8 */
-   int     pivots[8], nprods; /* sizes are bounded by dims[], so 8 */
+   int64_t pivots[8];         /* sizes are bounded by dims[], so 8 */
+   int     nprods;
    int64_t c, bytes;
 
    /** - check pointers for sanity */
@@ -8979,7 +9084,7 @@ compute_strides(int64_t *strides,const int64_t *size,int nbyper)
 /*---------------------------------------------------------------------------*/
 /*! read an arbitrary subregion from a nifti image
 
-    This function may be used to read a single arbitary subregion of any
+    This function may be used to read a single arbitrary subregion of any
     rectangular size from a nifti dataset, such as a small 5x5x5 subregion
     around the center of a 3D image.
 
@@ -9000,7 +9105,7 @@ compute_strides(int64_t *strides,const int64_t *size,int nbyper)
           speed and possibly repeated calls to this function.
     \return
         -  the total number of bytes read, or < 0 on failure
-        -  the read and byte-swapped data, in 'data'            
+ - the read and byte-swapped data, in 'data' \sa nifti_image_read, nifti_image_free, nifti_image_read_bricks nifti_image_load, nifti_read_collapsed_image @@ -9062,6 +9167,12 @@ int64_t nifti2_read_subregion_image( nifti_image * nim, /* get the file open */ fp = nifti_image_load_prep( nim ); + if(znz_isnull(fp)) { + if(g_opts.debug > 0) + Rc_fprintf_stderr("** nifti_read_subregion_image, failed load_prep\n"); + return -1; + } + /* the current offset is just past the nifti header, save * location so that SEEK_SET can be used below */ @@ -9081,6 +9192,7 @@ int64_t nifti2_read_subregion_image( nifti_image * nim, if(g_opts.debug > 1) Rc_fprintf_stderr("allocation of %" PRId64 " bytes failed\n", total_alloc_size); + znzclose(fp); return -1; } @@ -9120,11 +9232,11 @@ int64_t nifti2_read_subregion_image( nifti_image * nim, read_amount = rs[0] * nim->nbyper; /* read a row of subregion */ nread = nifti_read_buffer(fp, readptr, read_amount, nim); if(nread != read_amount) { - if(g_opts.debug > 1) { + if(g_opts.debug > 0) Rc_fprintf_stderr("read of %" PRId64 " bytes failed\n", read_amount); - return -1; - } + znzclose(fp); + return -1; } bytes += nread; readptr += read_amount; @@ -9147,7 +9259,7 @@ int64_t nifti2_read_subregion_image( nifti_image * nim, return 0 on success, < 0 on failure */ -static int rci_read_data(nifti_image * nim, int * pivots, int64_t * prods, +static int rci_read_data(nifti_image * nim, int64_t * pivots, int64_t * prods, int nprods, const int64_t dims[], char * data, znzFile fp, int64_t base_offset) { @@ -9166,7 +9278,7 @@ static int rci_read_data(nifti_image * nim, int * pivots, int64_t * prods, /* make sure things look good here */ if( *pivots != 0 ){ - Rc_fprintf_stderr("** NIFTI rciRD: final pivot == %d!\n", *pivots); + Rc_fprintf_stderr("** NIFTI rciRD: final pivot == %d!\n", (int)*pivots); return -1; } @@ -9269,13 +9381,11 @@ static int rci_alloc_mem(void **data, const int64_t prods[8], int nprods, int nb wants to collapse a dimension. The last pivot should always be zero (note that we have space for that in the lists). */ -static int make_pivot_list(nifti_image *nim, const int64_t dims[], int pivots[], +static int make_pivot_list(nifti_image *nim, const int64_t dims[], int64_t pivots[], int64_t prods[], int * nprods ) { - int len, dind; - - len = 0; - dind = nim->dim[0]; + int len = 0; + int64_t dind = nim->dim[0]; while( dind > 0 ){ prods[len] = 1; while( dind > 0 && (nim->dim[dind] == 1 || dims[dind] == -1) ){ @@ -9299,7 +9409,7 @@ static int make_pivot_list(nifti_image *nim, const int64_t dims[], int pivots[], if( g_opts.debug > 2 ){ Rc_fprintf_stderr("+d pivot list created, pivots :"); for(dind = 0; dind < len; dind++) - Rc_fprintf_stderr(" %d", pivots[dind]); + Rc_fprintf_stderr(" %lld", (long long)pivots[dind]); Rc_fprintf_stderr(", prods :"); for(dind = 0; dind < len; dind++) Rc_fprintf_stderr(" %" PRId64 "", prods[dind]); diff --git a/reg-io/niftilib/nifti2_io.h b/reg-io/niftilib/nifti2_io.h index 946e6d4e..c8829dad 100644 --- a/reg-io/niftilib/nifti2_io.h +++ b/reg-io/niftilib/nifti2_io.h @@ -54,7 +54,7 @@ extern "C" { Mainly adding low-level IO and changing things to allow gzipped files to be read and written - Full backwards compatability should have been maintained + Full backwards compatibility should have been maintained ...................................................................... Modified by: Rick Reynolds (SSCC/DIRP/NIMH, National Institutes of Health) @@ -69,7 +69,7 @@ extern "C" { Converted to be based on nifti_2_header. - ** NOT BACKWARD COMPATABLE ** + ** NOT BACKWARD COMPATIBLE ** These routines will read/write both NIFTI-1 and NIFTI-2 image files, but modification to the _calling_ routies is necessary, since: @@ -79,6 +79,11 @@ extern "C" { c. some routines have been changed to apply to multiple NIFTI types */ +/********************** file identification magic ****************************/ + +extern char nifti1_magic[4]; +extern char nifti2_magic[8]; + /********************** Some sample data structures **************************/ #if RNIFTI_NIFTILIB_VERSION == 2 @@ -462,8 +467,12 @@ int64_t nifti2_read_subregion_image(nifti_image *nim, const int64_t *start_ const int64_t *region_size, void ** data); void nifti2_image_write ( nifti_image * nim ) ; +int nifti2_image_write_status( nifti_image *nim ) ; /* 7 Jun 2022 */ + void nifti2_image_write_bricks(nifti_image * nim, const nifti_brick_list * NBL); +int nifti2_image_write_bricks_status(nifti_image * nim, + const nifti_brick_list * NBL); void nifti2_image_infodump( const nifti_image * nim ) ; void nifti2_disp_lib_hist( int ver ) ; /* to display library history */ @@ -635,7 +644,9 @@ int nifti_valid_header_size(int ni_ver, int whine); #define nifti_read_subregion_image nifti2_read_subregion_image #define nifti_image_write nifti2_image_write +#define nifti_image_write_status nifti2_image_write_status #define nifti_image_write_bricks nifti2_image_write_bricks +#define nifti_image_write_bricks_status nifti2_image_write_bricks_status #define nifti_image_infodump nifti2_image_infodump #define nifti_disp_lib_hist nifti2_disp_lib_hist @@ -774,7 +785,7 @@ typedef struct { char const * const name; /* text string to match #define */ } nifti_type_ele; -#undef LNI_FERR /* local nifti file error, to be compact and repetative */ +#undef LNI_FERR /* local nifti file error, to be compact and repetitive */ #ifdef USING_R #define LNI_FERR(func,msg,file) \ Rf_warning("%s: %s '%s'\n",func,msg,file) diff --git a/reg-io/niftilib/nifti2_io_version.h b/reg-io/niftilib/nifti2_io_version.h new file mode 100644 index 00000000..8d0f3966 --- /dev/null +++ b/reg-io/niftilib/nifti2_io_version.h @@ -0,0 +1,16 @@ +/* NOTE: When changing version consider the impact on versions in + nifti2_io_version.h nifti1_io_version.h nifticdf_version.h and znzlib.h +*/ +#define NIFTI2_IO_VERSION_MAJOR 2 +#define NIFTI2_IO_VERSION_MINOR 1 +#define NIFTI2_IO_VERSION_PATCH 0 + +/* main string macros: NIFTI2_IO_VERSION and NIFTI2_IO_SOURCE_VERSION */ +#define NIFTI2_IO_VERSION_TO_STRING(x) NIFTI2_IO_VERSION_TO_STRING0(x) +#define NIFTI2_IO_VERSION_TO_STRING0(x) #x +#define NIFTI2_IO_VERSION \ + NIFTI2_IO_VERSION_TO_STRING(NIFTI2_IO_VERSION_MAJOR) \ + "." NIFTI2_IO_VERSION_TO_STRING(NIFTI2_IO_VERSION_MINOR) \ + "." NIFTI2_IO_VERSION_TO_STRING(NIFTI2_IO_VERSION_PATCH) + +#define NIFTI2_IO_SOURCE_VERSION "NIFTI2_IO version " NIFTI2_IO_VERSION diff --git a/reg-io/znzlib/znzlib.c b/reg-io/znzlib/znzlib.c index 170a6065..d8beaa2d 100644 --- a/reg-io/znzlib/znzlib.c +++ b/reg-io/znzlib/znzlib.c @@ -143,7 +143,7 @@ size_t znzread(void* buf, size_t size, size_t nmemb, znzFile file) /* gzread/write take unsigned int length, so maybe read in int pieces (noted by M Hanke, example given by M Adler) 6 July 2010 [rickr] */ while( remain > 0 ) { - n2read = (remain < ZNZ_MAX_BLOCK_SIZE) ? remain : ZNZ_MAX_BLOCK_SIZE; + n2read = (remain < ZNZ_MAX_BLOCK_SIZE) ? (unsigned)remain : ZNZ_MAX_BLOCK_SIZE; nread = gzread(file->zfptr, (void *)cbuf, n2read); if( nread < 0 ) return nread; /* returns -1 on error */ @@ -175,7 +175,7 @@ size_t znzwrite(const void* buf, size_t size, size_t nmemb, znzFile file) #ifdef HAVE_ZLIB if (file->zfptr!=NULL) { while( remain > 0 ) { - n2write = (remain < ZNZ_MAX_BLOCK_SIZE) ? remain : ZNZ_MAX_BLOCK_SIZE; + n2write = (remain < ZNZ_MAX_BLOCK_SIZE) ? (unsigned)remain : ZNZ_MAX_BLOCK_SIZE; nwritten = gzwrite(file->zfptr, (const void *)cbuf, n2write); /* gzread returns 0 on error, but in case that ever changes... */ @@ -198,11 +198,11 @@ size_t znzwrite(const void* buf, size_t size, size_t nmemb, znzFile file) return fwrite(buf,size,nmemb,file->nzfptr); } -long znzseek(znzFile file, long offset, int whence) +znz_off_t znzseek(znzFile file, znz_off_t offset, int whence) { if (file==NULL) { return 0; } #ifdef HAVE_ZLIB - if (file->zfptr!=NULL) return (long) gzseek(file->zfptr,offset,whence); + if (file->zfptr!=NULL) return (znz_off_t) gzseek(file->zfptr,offset,whence); #endif return fseek(file->nzfptr,offset,whence); } @@ -223,11 +223,11 @@ int znzrewind(znzFile stream) return 0; } -long znztell(znzFile file) +znz_off_t znztell(znzFile file) { if (file==NULL) { return 0; } #ifdef HAVE_ZLIB - if (file->zfptr!=NULL) return (long) gztell(file->zfptr); + if (file->zfptr!=NULL) return (znz_off_t) gztell(file->zfptr); #endif return ftell(file->nzfptr); } diff --git a/reg-io/znzlib/znzlib.h b/reg-io/znzlib/znzlib.h index d0e95aa1..78049a9a 100644 --- a/reg-io/znzlib/znzlib.h +++ b/reg-io/znzlib/znzlib.h @@ -46,6 +46,7 @@ extern "C" { #include #include + /* include optional check for HAVE_FDOPEN here, from deleted config.h: uncomment the following line if fdopen() exists for your compiler and @@ -53,6 +54,18 @@ extern "C" { */ /* #define HAVE_FDOPEN */ +#if defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) || defined(_MSVC) || defined(_MSC_VER) +#include +#define fseek _fseeki64 +#define ftell _ftelli64 +#define znz_off_t long long +#elif defined(__APPLE__) || defined(__FreeBSD__) +#define znz_off_t off_t +#else +#include +#include +#define znz_off_t off_t +#endif #ifdef HAVE_ZLIB #if defined(ITKZLIB) && !defined(ITK_USE_SYSTEM_ZLIB) @@ -96,11 +109,11 @@ size_t znzread(void* buf, size_t size, size_t nmemb, znzFile file); size_t znzwrite(const void* buf, size_t size, size_t nmemb, znzFile file); -long znzseek(znzFile file, long offset, int whence); +znz_off_t znzseek(znzFile file, znz_off_t offset, int whence); int znzrewind(znzFile stream); -long znztell(znzFile file); +znz_off_t znztell(znzFile file); int znzputs(const char *str, znzFile file);