Index: trunk/psLib/src/fits/psFits.c
===================================================================
--- trunk/psLib/src/fits/psFits.c	(revision 15126)
+++ trunk/psLib/src/fits/psFits.c	(revision 15179)
@@ -7,6 +7,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.71 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2007-09-18 03:01:17 $
+ *  @version $Revision: 1.72 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-10-03 21:27:21 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -20,4 +20,5 @@
 
 #include "psFits.h"
+#include "psFitsHeader.h"
 #include "string.h"
 #include "psError.h"
@@ -65,5 +66,5 @@
             return false;
         }
-	fits->fd = NULL;
+        fits->fd = NULL;
     }
     return true;
@@ -74,5 +75,5 @@
     if (!fits) return;
     if (fits->fd) {
-	fitsClose(fits);
+        fitsClose(fits);
     }
     psFree (fits->extword);
@@ -81,9 +82,5 @@
 bool psFitsClose(psFits* fits)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     bool status = fitsClose(fits);
@@ -95,12 +92,8 @@
 psFits* psFitsOpen(const char* name, const char* mode)
 {
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
     int status = 0;
     fitsfile *fptr = NULL;      /* Pointer to the FITS file */
-
-    if (name == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("Specified filename can not be NULL."));
-        return NULL;
-    }
 
     /* check the mode to determine how to open/create file */
@@ -131,6 +124,5 @@
         if (access(name, F_OK) == 0) {
             // file exists, delete old one first
-            remove
-                (name);
+            remove(name);
         }
 
@@ -140,10 +132,7 @@
         (void)fits_create_file
         #endif
-        (&fptr,
-         name,
-         &status);
+        (&fptr, name, &status);
         if (fptr == NULL || status != 0) {
-            char fitsErr[MAX_STRING_LENGTH]
-            ;
+            char fitsErr[MAX_STRING_LENGTH];
             fits_get_errstatus(status, fitsErr);
             psError(PS_ERR_IO, true,
@@ -158,11 +147,7 @@
         (void)fits_open_file
         #endif
-        (&fptr,
-         name,
-         iomode,
-         &status);
+        (&fptr, name, iomode, &status);
         if (fptr == NULL || status != 0) {
-            char fitsErr[MAX_STRING_LENGTH]
-            ;
+            char fitsErr[MAX_STRING_LENGTH];
             fits_get_errstatus(status, fitsErr);
             psError(PS_ERR_IO, true,
@@ -177,4 +162,5 @@
     fits->writable = (iomode == READWRITE);
     fits->extword = NULL;
+    fits->conventions.compression = true;
     psMemSetDeallocator(fits,(psFreeFunc)fitsFree);
 
@@ -182,10 +168,34 @@
 }
 
-static void psFitsOptionsFree(psFitsOptions *opt)
-{
-    psFree(opt->tilesize);
-}
-
-psFitsOptions* psFitsOptionsAlloc(
+psErrorCode p_psFitsError(const char* filename, unsigned int lineno, const char* func, int status,
+                          bool new, const char *errorMsg, ...)
+{
+    if (status == 0) {
+        return PS_ERR_NONE;
+    }
+
+    va_list ap;                         // Variable arguments
+    va_start(ap, errorMsg);
+    psString msg = NULL;                // Message to pass to psError
+    psStringAppendV(&msg, errorMsg, ap);
+    va_end(ap);
+
+    char cfitsioMsg[MAX_STRING_LENGTH];   // Error message from cfitsio
+    (void)fits_get_errstatus(status, cfitsioMsg);
+
+    psStringAppend(&msg, "[CFITSIO error: %s]", cfitsioMsg);
+
+    psErrorCode code = p_psError(filename, lineno, func, PS_ERR_IO, new, msg); // Error code
+    psFree(msg);
+    return code;
+}
+
+static void psFitsCompressionFree(psFitsCompression *comp)
+{
+    PS_ASSERT_PTR_NON_NULL(comp,);
+    psFree(comp->tilesize);
+}
+
+psFitsCompression* psFitsCompressionAlloc(
     psFitsCompressionType type,         ///< type of compression
     psVector *tilesize,                 ///< vector defining compression tile size
@@ -195,15 +205,14 @@
 )
 {
-    psFitsOptions *opt = psAlloc(sizeof(psFitsOptions));
-
-    opt->type = type;
-    opt->tilesize = psVectorCopy(NULL, tilesize, PS_DATA_S64);
-    opt->noisebits = noisebits;
-    opt->scale = scale;
-    opt->smooth = smooth;
-
-    psMemSetDeallocator(opt, (psFreeFunc) psFitsOptionsFree);
-
-    return opt;
+    psFitsCompression *comp = psAlloc(sizeof(psFitsCompression));
+    psMemSetDeallocator(comp, (psFreeFunc) psFitsCompressionFree);
+
+    comp->type = type;
+    comp->tilesize = psVectorCopy(NULL, tilesize, PS_DATA_S64);
+    comp->noisebits = noisebits;
+    comp->scale = scale;
+    comp->smooth = smooth;
+
+    return comp;
 }
 
@@ -224,84 +233,70 @@
 }
 
-// move to the first HDU where extword == extname.  this is equivalent to fits_movnam_hdu() for
-// a user-defined word in place of EXTNAME
-bool p_psFitsMoveExtName_UserKey(const psFits *fits,
-				 const char *extname,
-				 const char *extword)
-{
-    PS_ASSERT_PTR_NON_NULL(fits,    false);
-    PS_ASSERT_PTR_NON_NULL(extname, false);
-    PS_ASSERT_PTR_NON_NULL(extword, false);
-
-    int hdutype = 0;
-    char name[MAX_STRING_LENGTH];
-    char extstring[MAX_STRING_LENGTH];
-
-    sprintf (extstring, "'%s'", extname);
-
-    // NOTE: fits_* return 0 for success
-    for (int i = 1; true; i++) {
-	// are we able to read the next HDU?
-
-	int status = 0;
-	if (fits_movabs_hdu(fits->fd, i, &hdutype, &status)) {
-	    char fitsErr[MAX_STRING_LENGTH];
-	    fits_get_errstatus(status, fitsErr);
-	    psError(PS_ERR_LOCATION_INVALID, true,
-		    _("Could not find HDU with %s = '%s'. CFITSIO Error: %s"),
-		    extword, extname, fitsErr);
-	    return false;
-	}
-	// is there a keyword called 'extword'? (read as string regardless of type)
-	status = 0;
-	if (fits_read_keyword(fits->fd, (char *)extword, name, NULL, &status)) {
-	    continue;
-	}
-	// if this was read as a string, we will have leading and trailing single-quotes
-	// try both for comparison
-	
-	// do we have the right hdu (names match)?
-	if (!strcmp (name, extname) || !strcmp (name, extstring)) {
-	    return true;
-	}
-    }
-    psAbort("we should not reach here");
-}
-
-// XXX I will need to define a low-level function p_psFitsMoveExtName_UserKey () which
-// uses fits_movabs_hdu() to replicate the functionality of fits_movnam_hdu using an
-// alternate name for EXTNAME
+// Files compressed with cfitsio's "imcopy" program may have multiple EXTNAME keywords, with the first set to
+// COMPRESSED_IMAGE.  However, fits_movnam_hdu won't find the second (proper) value of EXTNAME, and so can
+// fail to find a perfectly legitimate extension, simply because imcopy does something silly.  However, we
+// really want to be able to read these files (MegaCam data are shipped as imcopy-compressed images).
+// Therefore, we implement our own version of moving to an extension specified by name.  The pure cfitsio
+// version is used if "conventions.compression" handling is turned off in the psFits structure.
 bool psFitsMoveExtName(const psFits* fits,
                        const char* extname)
 {
-    int status = 0;
-
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
-
-    if (extname == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("Specified extension name can not be NULL."));
-        return false;
-    }
-
-    if (fits->extword != NULL) {
-	bool result = p_psFitsMoveExtName_UserKey(fits, extname, fits->extword);
-	return (result);
-    }
-
-    if (fits_movnam_hdu(fits->fd, ANY_HDU, (char*)extname, 0, &status) != 0) {
-        char fitsErr[MAX_STRING_LENGTH];
-        fits_get_errstatus(status, fitsErr);
-        psError(PS_ERR_LOCATION_INVALID, true,
-                _("Could not find HDU '%s'. CFITSIO Error: %s"),
-                extname, fitsErr);
-        return false;
-    }
-
-    return true;
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_STRING_NON_EMPTY(extname, false);
+
+    int status = 0;
+
+    if (!fits->conventions.compression && !fits->extword) {
+        // User wants to use cfitsio.  Good luck to them!
+        if (fits_movnam_hdu(fits->fd, ANY_HDU, (char*)extname, 0, &status) != 0) {
+            psFitsError(status, true, _("Could not find HDU '%s'"), extname);
+            return false;
+        }
+        return true;
+    }
+
+    bool ignoreCI = (fits->conventions.compression &&
+                     (strcmp(extname, "COMPRESSED_IMAGE") != 0)); // Ignore COMPRESSED_IMAGE extension name?
+    char *extword = (fits->extword ? fits->extword : "EXTNAME"); // Word to use as extension name
+
+#if 0
+    // XXX Future optimisation: loop through from the current HDU to the end, then from the start to the
+    // current position.  This will save seeking through the file multiple times.
+    int currentExt = psFitsGetExtNum(fits); // Current extension number
+    int numExt = psFitsGetSize(fits);   // Total number of extensions
+#endif
+
+    for (int i = 1; true; i++) {
+        int hdutype = 0;
+        if (fits_movabs_hdu(fits->fd, i, &hdutype, &status)) {
+            // We've run off the end
+            psFitsError(status, true, _("Could not find HDU with %s = '%s'"), extword, extname);
+            return false;
+        }
+        // Is there a keyword called 'extword'? (read as string regardless of type)
+        char name[MAX_STRING_LENGTH];  // Name of extension
+        if (fits_read_keyword(fits->fd, extword, name, NULL, &status)) {
+            // It doesn't exist in the header.
+            // This isn't the extension you're looking for.  Move along.
+            status = 0;
+            continue;
+        }
+        char *fixed = p_psFitsHeaderParseString(name); // Parsed version (removing quotes and spaces)
+
+        if (ignoreCI && strcmp(fixed, "COMPRESSED_IMAGE") == 0) {
+            // Read it again, Sam
+            if (fits_read_keyword(fits->fd, extword, name, NULL, &status)) {
+                status = 0;
+                continue;
+            }
+            fixed = p_psFitsHeaderParseString(name);
+        }
+
+        if (strcmp(fixed, extname) == 0) {
+            // We've arrived
+            return true;
+        }
+    }
+    psAbort("Should never reach here.");
 }
 
@@ -310,9 +305,5 @@
                       bool relative)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     int status = 0;
@@ -346,9 +337,6 @@
 bool psFitsMoveLast(psFits* fits)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
     int size = psFitsGetSize(fits);
     if (size == 0) { // empty file -- no action needed
@@ -361,13 +349,6 @@
 int psFitsGetExtNum(const psFits* fits)
 {
+    PS_ASSERT_FITS_NON_NULL(fits, false);
     int hdunum;
-
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return PS_FITS_TYPE_NONE;
-    }
-
-
     return fits_get_hdu_num(fits->fd,&hdunum) - 1;
 }
@@ -375,9 +356,5 @@
 psString psFitsGetExtName(const psFits* fits)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return NULL;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
 
     int status = 0;
@@ -389,5 +366,5 @@
         psError(PS_ERR_BAD_PARAMETER_NULL, true,
                 _("Header keyword %s is not found"), extword);
-	return NULL;
+        return NULL;
     }
     return psStringCopy(name);
@@ -396,15 +373,6 @@
 bool psFitsSetExtName(psFits* fits, const char* name)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
-
-    if (name == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("Specified extension name can not be NULL."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
 
     int status = 0;
@@ -428,9 +396,5 @@
                         bool relative)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     if (! fits->writable) {
@@ -466,9 +430,6 @@
                          const char* extname)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_STRING_NON_EMPTY(extname, false);
 
     if (! fits->writable) {
@@ -504,9 +465,5 @@
 int psFitsGetSize(const psFits* fits)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return 0;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
 
     int num = 0;
@@ -527,9 +484,5 @@
 psFitsType psFitsGetExtType(const psFits* fits)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return PS_FITS_TYPE_NONE;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, PS_FITS_TYPE_NONE);
 
     int status = 0;
@@ -556,9 +509,5 @@
 bool psFitsTruncate(psFits* fits)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return PS_FITS_TYPE_NONE;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
 
     if (! fits->writable) {
@@ -597,4 +546,6 @@
 )
 {
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
     // convert psFitsCompressionType to cfitsio compression types
     int comptype;
@@ -640,4 +591,5 @@
         psAbort("can't map (long) type to a psLib type");
     }
+    psFree(dim);
     // status check belongs to fits_set_tile_dim() call
     if (status) {
@@ -681,17 +633,103 @@
 }
 
-
-bool psFitsSetOptions(
+psFitsCompression *psFitsCompressionGet(psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    int status = 0;                     // cfitsio status
+
+    psFitsCompressionType type = psFitsCompressionGetType(fits);
+    if (type < 0) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to get compression type.");
+        return NULL;
+    }
+
+    psElemType tileType;                // Type corresponding to "long"
+    if (sizeof(long) == sizeof(psS64)) {
+        tileType = PS_TYPE_S64;
+    } else if (sizeof(long) == sizeof(psS32)) {
+        tileType = PS_TYPE_S32;
+    } else {
+        psAbort("can't map (long) type to a psLib type");
+    }
+
+    psVector *tiles = psVectorAlloc(3, tileType); // Tile sizes
+    if (fits_get_tile_dim(fits->fd, 3, (long*)tiles->data.U8, &status)) {
+        psFitsError(status, true, "Unable to get compression tile sizes.");
+        psFree(tiles);
+        return NULL;
+    }
+
+    int noisebits;                      // Noise bits for compression
+    if (fits_get_noise_bits(fits->fd, &noisebits, &status)) {
+        psFitsError(status, true, "Unable to get compression noise bits.");
+        psFree(tiles);
+        return NULL;
+    }
+
+    int hscale = 0, hsmooth = 0;        // Scaling and smoothing for HCOMPRESS
+
+#if FITS_HCOMP
+    if (fits_get_hcomp_scale(fits->fd, &hscale, &status)) {
+        psFitsError(status, true, "Unable to get HCOMPRESS scaling.");
+        psFree(tiles);
+        return NULL;
+    }
+    if (fits_get_hcomp_smooth(fits->fd, &hsmooth, &status)) {
+        psFitsError(status, true, "Unable to get HCOMPRESS smoothing.");
+        psFree(tiles);
+        return NULL;
+    }
+#endif // FITS_HCOMP
+
+    psFitsCompression *compress = psFitsCompressionAlloc(type, tiles, noisebits, hscale, hsmooth);
+    psFree(tiles);                      // Drop reference
+
+    return compress;
+}
+
+psFitsCompressionType psFitsCompressionGetType(psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, -1);
+
+    int status = 0;                     // cfitsio status
+    int comptype = 0;                   // cfitsio compression type
+    if (fits_get_compression_type(fits->fd, &comptype, &status)) {
+        psFitsError(status, true, "Unable to get compression type.");
+        return -1;
+    }
+
+    psFitsCompressionType type;
+    switch (comptype) {
+      case 0:
+        type = PS_FITS_COMPRESS_NONE;
+        break;
+      case GZIP_1:
+        type = PS_FITS_COMPRESS_GZIP;
+        break;
+      case RICE_1:
+        type = PS_FITS_COMPRESS_RICE;
+        break;
+      case HCOMPRESS_1:
+        type = PS_FITS_COMPRESS_HCOMPRESS;
+        break;
+      case PLIO_1:
+        type = PS_FITS_COMPRESS_PLIO;
+        break;
+      default:
+        psError(PS_ERR_UNKNOWN, true, "cfitsio reports unknown compression type.");
+        return -1;
+    }
+
+    return type;
+}
+
+
+bool psFitsCompressionApply(
     psFits* fits,                       ///< psFits object to close
-    psFitsOptions *opt                  ///< options object
+    psFitsCompression *comp             ///< options object
 )
 {
-    return psFitsSetCompression(
-            fits,
-            opt->type,
-            opt->tilesize,
-            opt->noisebits,
-            opt->scale,
-            opt->smooth);
+    return psFitsSetCompression(fits, comp->type, comp->tilesize, comp->noisebits, comp->scale, comp->smooth);
 }
 
@@ -826,2 +864,17 @@
 
 
+psFitsCompressionType psFitsCompressionTypeFromString(const char *string)
+{
+    if (!string || strlen(string) == 0) {
+        psWarning("Unable to identify compression type --- none set.");
+        return PS_FITS_COMPRESS_NONE;
+    }
+
+    if (strcmp(string, "GZIP") == 0) return PS_FITS_COMPRESS_GZIP;
+    if (strcmp(string, "RICE") == 0) return PS_FITS_COMPRESS_RICE;
+    if (strcmp(string, "HCOMPRESS") == 0) return PS_FITS_COMPRESS_HCOMPRESS;
+    if (strcmp(string, "PLIO") == 0) return PS_FITS_COMPRESS_PLIO;
+
+    psWarning("Unable to identify compression type (%s) --- none set.", string);
+    return PS_FITS_COMPRESS_NONE;
+}
Index: trunk/psLib/src/fits/psFits.h
===================================================================
--- trunk/psLib/src/fits/psFits.h	(revision 15126)
+++ trunk/psLib/src/fits/psFits.h	(revision 15179)
@@ -4,6 +4,6 @@
  * @author Robert DeSonia, MHPCC
  *
- * @version $Revision: 1.31 $ $Name: not supported by cvs2svn $
- * @date $Date: 2007-09-18 02:56:36 $
+ * @version $Revision: 1.32 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-10-03 21:27:21 $
  * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
  */
@@ -21,4 +21,6 @@
 #include "psMetadata.h"
 #include "psImage.h"
+
+#include "psErrorCodes.h"
 
 /** FITS HDU type.
@@ -49,15 +51,15 @@
  *
  */
-typedef struct
-{
-    fitsfile* fd;			///< the CFITSIO fits files handle.
-    bool writable;			///< Is the file writable?
-    char *extword;			///< user-specified word to name extensions (NULL implies EXTNAME)
-}
-psFits;
-
-/** FITS options object. */
-typedef struct
-{
+typedef struct {
+    fitsfile* fd;                       ///< the CFITSIO fits files handle.
+    bool writable;                      ///< Is the file writable?
+    char *extword;                      ///< user-specified word to name extensions (NULL implies EXTNAME)
+    struct {
+        bool compression;               ///< Honour compression convention, handling compressed images
+    } conventions;                      ///< Conventions to honour
+} psFits;
+
+/** FITS compression settings. */
+typedef struct {
     psFitsCompressionType type;         ///< type of compression
     psVector *tilesize;                 ///< vector defining compression tile size
@@ -65,6 +67,5 @@
     int scale;                          ///< hcompress scale
     int smooth;                         ///< hcompress smothing
-}
-psFitsOptions;
+} psFitsCompression;
 
 /** Opens a FITS file and allocates the associated psFits object.
@@ -84,9 +85,35 @@
 );
 
-/** Creates a new FITS options struct 
+
+/// Generate an error including the cfitsio error string
+#ifdef DOXYGEN
+psErrorCode psFitsError(
+    int status,                         ///< cfitsio status value
+    bool new,                           ///< new error?
+    const char *errorMsg,               ///< printf-style format of header line
+    ...                                 ///< any parameters required in format
+    );
+#else // ifdef DOXYGEN
+psErrorCode p_psFitsError(
+    const char* filename,               ///< file name
+    unsigned int lineno,                ///< line number in file
+    const char* func,                   ///< function name
+    int status,                         ///< cfitsio status value
+    bool new,                           ///< new error?
+    const char *errorMsg,               ///< printf-style format of header line
+    ...                                 ///< any parameters required in format
+    ) PS_ATTR_FORMAT(printf, 6, 7);
+#ifndef SWIG
+#define psFitsError(status,new,...) \
+      p_psFitsError(__FILE__,__LINE__,__func__,status,new,__VA_ARGS__)
+#endif // ifndef SWIG
+#endif // ifdef DOXYGEN
+
+
+/** Creates a new FITS options struct
  *
  *  @return psFitsOptions or NULL on failure
  */
-psFitsOptions* psFitsOptionsAlloc(
+psFitsCompression* psFitsCompressionAlloc(
     psFitsCompressionType type,         ///< type of compression
     psVector *tilesize,                 ///< vector defining compression tile size
@@ -96,4 +123,9 @@
 );
 
+/// Return the FITS compression type specified by a string
+psFitsCompressionType psFitsCompressionTypeFromString(
+    const char *string                  ///< String with compression type
+    );
+
 /** Closes a FITS file.
  *
@@ -111,5 +143,5 @@
  */
 bool psFitsSetCompression(
-    psFits* fits,                       ///< psFits object to close
+    psFits* fits,                       ///< psFits object for which to set compression
     psFitsCompressionType type,         ///< type of compression
     psVector *tilesize,                 ///< vector defining compression tile size
@@ -119,11 +151,21 @@
 );
 
+/// Get the compression options for a file handle
+psFitsCompression *psFitsCompressionGet(
+    psFits* fits                        ///< psFits object for which to get compression
+);
+
+/// Get the compression type for a file handle
+psFitsCompressionType psFitsCompressionGetType(
+    psFits* fits                        ///< psFits object for which to get compression type
+    );
+
 /** Sets FITS write options
  *
  *  @return bool      TRUE if successfully configured, otherwise FALSE
  */
-bool psFitsSetOptions(
-    psFits* fits,                       ///< psFits object to close
-    psFitsOptions *opt                  ///< options object
+bool psFitsCompressionApply(
+    psFits* fits,                       ///< psFits object for which to set compression
+    psFitsCompression *compress         ///< options object
 );
 
@@ -146,6 +188,6 @@
 // a user-defined word in place of EXTNAME
 bool p_psFitsMoveExtName_UserKey(const psFits *fits,
-				 const char *extname,
-				 const char *extword);
+                                 const char *extname,
+                                 const char *extword);
 
 /** Moves the FITS HDU to the specified extension name.
@@ -261,4 +303,10 @@
                           );
 
+#define PS_ASSERT_FITS_NON_NULL(NAME, RVAL) \
+if (!(NAME) || !(NAME)->fd) { \
+    psError(PS_ERR_UNEXPECTED_NULL, true, "Error: FITS file pointer %s is NULL", #NAME); \
+    return RVAL; \
+}
+
 /// @}
 #endif // #ifndef PS_FITS_H
Index: trunk/psLib/src/fits/psFitsHeader.c
===================================================================
--- trunk/psLib/src/fits/psFitsHeader.c	(revision 15126)
+++ trunk/psLib/src/fits/psFitsHeader.c	(revision 15179)
@@ -7,6 +7,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.34 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2007-08-10 02:23:42 $
+ *  @version $Revision: 1.35 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-10-03 21:27:21 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -19,4 +19,5 @@
 #include <unistd.h>
 
+#include "psAssert.h"
 #include "psFits.h"
 #include "string.h"
@@ -32,31 +33,143 @@
 
 #define MAX_STRING_LENGTH 256  // maximum length string for FITS routines
+#define NUM_EMPTY_KEYS 8                // Number of keywords before header is considered practically empty
 
 // list of FITS header keys to ignore; NULL-terminated
-static char* ignoreFitsKeys[] = {"", NULL};
+static const char* ignoreFitsKeys[] = { "", NULL};
 
 // List of FITS header keys that may be duplicated; NULL-terminated
-static char *duplicateFitsKeys[] = {"COMMENT", "HIERARCH", "HISTORY", NULL};
+static const char *duplicateFitsKeys[] = { "COMMENT", "HIERARCH", "HISTORY", NULL};
 
 // List of FITS header keys not to write (handled by cfitsio); NULL-terminated
-static char *noWriteFitsKeys[] = {"SIMPLE", "XTENSION", "BITPIX", "NAXIS", "EXTNAME", "BSCALE", "BZERO",
-                                  "TFIELDS", NULL};
+static const char *noWriteFitsKeys[] = { "SIMPLE", "XTENSION", "BITPIX", "NAXIS", "EXTNAME", "BSCALE",
+                                         "BZERO", "TFIELDS", "PCOUNT", "GCOUNT", "ZIMAGE", "ZBITPIX",
+                                         "ZCMPTYPE", NULL};
+
 // List of the start of FITS header keys not to write (handled by cfitsio); NULL-terminated
-static char *noWriteFitsKeyStarts[] = {"NAXIS", "TTYPE", "TFORM", NULL};
-
-// List of FITS header keys to be written with fits_write_comment
-
-psMetadata* psFitsReadHeader(psMetadata* out,
-                             const psFits* fits)
-{
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return NULL;
-    }
-
-    if (out == NULL) {
-        out = psMetadataAlloc();
-    }
+static const char *noWriteFitsKeyStarts[] = { "NAXIS", "TTYPE", "TFORM", "ZNAXIS", "ZTILE", "ZNAME", "ZVAL",
+                                              NULL};
+
+// List of FITS header keys that may be present if the header is considered "empty"; NULL-terminated
+static const char *emptyKeys[] = { "SIMPLE", "BITPIX", "NAXIS", "EXTEND", "COMMENT", "CHECKSUM", "DATASUM",
+                                   NULL };
+
+// Compare a keyword with a list of keywords; return true if it's in the list
+static bool keywordInList(const char *keyword, // Keyword to check
+                          const char **list // List of keywords
+    )
+{
+    for (const char **check = list; *check; ++check) {
+        if (strcmp(keyword, *check) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+
+bool psFitsCheckSingleCompressedImagePHU(const psFits *fits, psMetadata *header)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    if (!fits->conventions.compression) {
+        // User has turned off compression conventions; doesn't want any nasty surprises
+        return false;
+    }
+
+    if (psFitsGetExtNum(fits) != 0) {
+        // It's not the PHU, so it can't be the PHU for a single compressed image!
+        return false;
+    }
+
+    if (psFitsGetSize(fits) != 2) {
+        // No second extension, or multiple extensions
+        return false;
+    }
+
+    int numKeys;                        // Number of keywords in the header
+    int status = 0;                     // CFITSIO status
+    fits_get_hdrspace(fits->fd, &numKeys, 0, &status);
+    if (numKeys > NUM_EMPTY_KEYS) {
+        return false;
+    }
+
+    int bitpix, naxis;                  // Bits per pixel and number of axes
+    long naxes[MAX_COMPRESS_DIM];       // Dimensions
+    fits_get_img_param(fits->fd, MAX_COMPRESS_DIM, &bitpix, &naxis, naxes, &status);
+    if (naxis != 0) {
+        return false;
+    }
+
+    if (!header) {
+        for (int i = 1; i <= numKeys; i++) {
+            // Just want to read the keyword names, without parsing the values and stuffing into a metadata
+            char keyName[MAX_STRING_LENGTH];// Keyword name
+            char keyValue[MAX_STRING_LENGTH]; // Corresponding value
+            char keyComment[MAX_STRING_LENGTH]; // Corresponding comment
+            fits_read_keyn(fits->fd, i, keyName, keyValue, keyComment, &status);
+            if (!keywordInList(keyName, emptyKeys)) {
+                return false;
+            }
+        }
+    } else {
+        psMetadataIterator *iter = psMetadataIteratorAlloc(header, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *item;           // Item from iteration
+        while ((item = psMetadataGetAndIncrement(iter))) {
+            if (!keywordInList(item->name, emptyKeys)) {
+                psFree(iter);
+                return false;
+            }
+        }
+        psFree(iter);
+    }
+
+    if (!psFitsMoveExtNum(fits, 1, false)) {
+        psWarning("Unable to examine first extension as suspect compressed image.");
+        return false;
+    }
+
+    if (fits_is_compressed_image(fits->fd, &status)) {
+        return true;
+    }
+
+    // It's not a single compressed image PHU --- move back to the PHU for the user
+    if (!psFitsMoveExtNum(fits, 0, false)) {
+        psWarning("Unable to examine first extension as suspect compressed image.");
+        return false;
+    }
+
+    return false;
+}
+
+char *p_psFitsHeaderParseString(char *string)
+{
+    if (!string || strlen(string) == 0) {
+        return string;
+    }
+
+    char *fixed = string;       // Fixed version of the string
+    // remove the single-quotes at front/end
+    if (fixed[0] == '\'' && fixed[strlen(string)-1] == '\'') {
+        string[strlen(string)-1] = '\0'; // Remove the trailing quote
+        fixed += 1; // Advance past the leading quote
+    }
+    // Remove trailing spaces, which are not significant, according to the FITS standard
+    // http://archive.stsci.edu/fits/fits_standard/node31.html
+    char *lastSpace = NULL; // The last space in the string
+    while (strlen(fixed) > 1 && (lastSpace = strrchr(fixed, ' ')) && lastSpace[1] == '\0') {
+        // This is a trailing space, not a leading space.
+        lastSpace[0] = '\0'; // Truncate the string here
+    }
+
+    return fixed;
+}
+
+// Read the header
+static psMetadata *readHeader(const psFits *fits // FITS file from which to read header
+                              )
+{
+    assert(fits);
+
+    psMetadata *header = psMetadataAlloc(); // Header, to return
 
     // Get number of key names
@@ -74,30 +187,13 @@
 
         // Check to see if the keyword should be ignored
-        bool ignoreKey = false;         // Ignore this keyword?
-        for (int i = 0; ignoreFitsKeys[i] && !ignoreKey; i++) {
-            if (strcmp(keyName, ignoreFitsKeys[i]) == 0) {
-                ignoreKey = true;
-            }
-        }
-        if (ignoreKey) {
+        if (keywordInList(keyName, ignoreFitsKeys)) {
             // We're done here; skip to the next key
             continue;
         }
 
-#if 0
-        // This doesn't seem to be necessary
-        if (strncmp(keyName, "HIERARCH ", 9) == 0) {
-            char temp[MAX_STRING_LENGTH];
-            strcpy(temp, &keyName[9]);
-            strcpy(keyName, temp);
-        }
-#endif
-
         // Check to see if the keyword should be duplicated
         int dupFlag = 0;                // Duplicate flag
-        for (int i = 0; duplicateFitsKeys[i] && !dupFlag; i++) {
-            if (strcmp(keyName, duplicateFitsKeys[i]) == 0) {
-                dupFlag = PS_META_DUPLICATE_OK;
-            }
+        if (keywordInList(keyName, duplicateFitsKeys)) {
+            dupFlag = PS_META_DUPLICATE_OK;
         }
 
@@ -116,5 +212,5 @@
         case 'X': // bit
         case 'B': // byte
-            success = psMetadataAddS8(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, atoi(keyValue));
+            success = psMetadataAddS8(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, atoi(keyValue));
             break;
         case 'I': // short int.
@@ -122,67 +218,59 @@
             // Trap NAN, INF and -INF, which cfitsio doesn't handle.
             if (strncasecmp(keyValue, "NAN", 3) == 0) {
-                success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, NAN);
+                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, NAN);
             } else if (strncasecmp(keyValue, "INF", 3) == 0) {
-                success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, INFINITY);
+                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, INFINITY);
             } else if (strncasecmp(keyValue, "-INF", 4) == 0) {
-                success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, -INFINITY);
+                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, -INFINITY);
             } else {
-                success = psMetadataAddS32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, atoi(keyValue));
+                success = psMetadataAddS32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment,
+                                           atoi(keyValue));
             }
             break;
         case 'J': // int.
-            success = psMetadataAddS32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, atoi(keyValue));
+            success = psMetadataAddS32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, atoi(keyValue));
             break;
         case 'U': // unsigned int.
-            success = psMetadataAddU32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, atol(keyValue));
+            success = psMetadataAddU32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, atol(keyValue));
             break;
 
         case 'K': // long int. can't all fit in a psS32, put in psF64
         case 'F':
-            success = psMetadataAddF64(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, atof(keyValue));
+            success = psMetadataAddF64(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, atof(keyValue));
             break;
         case 'C': {
-                char *keyValueFixed = keyValue; // Fixed version of the string
-                // remove the single-quotes at front/end
-                if (keyValueFixed[0] == '\'' && keyValueFixed[strlen(keyValue)-1] == '\'') {
-                    keyValue[strlen(keyValue)-1] = '\0'; // Remove the trailing quote
-                    keyValueFixed += 1; // Advance past the leading quote
-                }
-                // Remove trailing spaces, which are not significant, according to the FITS standard
-                // http://archive.stsci.edu/fits/fits_standard/node31.html
-                char *lastSpace = NULL; // The last space in the string
-                while (strlen(keyValueFixed) > 1 && (lastSpace = strrchr(keyValueFixed, ' ')) &&
-                        lastSpace[1] == '\0') {
-                    // This is a trailing space, not a leading space.
-                    lastSpace[0] = '\0'; // Truncate the string here
-                }
-
-                // Need to trap NAN, INF and -INF written by psFitsWriteHeader.
-                // cfitsio won't write these, so we write them as strings, and then have to trap them on read.
-                if (strcasecmp(keyValueFixed, "NAN") == 0) {
-                    success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, NAN);
-                } else if (strcasecmp(keyValueFixed, "INF") == 0) {
-                    success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, INFINITY);
-                } else if (strcasecmp(keyValueFixed, "-INF") == 0) {
-                    success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, -INFINITY);
-                } else {
-                    success = psMetadataAddStr(out, PS_LIST_TAIL, keyName, dupFlag, keyComment,
-                                               keyValueFixed);
-                }
-                break;
+            char *keyValueFixed = p_psFitsHeaderParseString(keyValue); // Fixed version of the string
+
+            // Need to trap NAN, INF and -INF written by psFitsWriteHeader.
+            // cfitsio won't write these, so we write them as strings, and then have to trap them on read.
+            if (strcasecmp(keyValueFixed, "NAN") == 0) {
+                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, NAN);
+            } else if (strcasecmp(keyValueFixed, "INF") == 0) {
+                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, INFINITY);
+            } else if (strcasecmp(keyValueFixed, "-INF") == 0) {
+                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, -INFINITY);
+            } else if (!fits->conventions.compression ||
+                       (strcmp(keyName, "EXTNAME") != 0 || strcmp(keyValueFixed, "COMPRESSED_IMAGE") != 0)) {
+                // Ignore EXTNAME=COMPRESSED_IMAGE if compression convention is to be respected
+                success = psMetadataAddStr(header, PS_LIST_TAIL, keyName, dupFlag, keyComment,
+                                           keyValueFixed);
             }
+            break;
+        }
         case 'L': {
-                bool temp = (keyValue[0] == 'T') ? 1 : 0;
-                success = psMetadataAddBool(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, temp);
-                break;
-            }
+            bool temp = (keyValue[0] == 'T') ? 1 : 0;
+            success = psMetadataAddBool(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, temp);
+            break;
+        }
         default:
             psError(PS_ERR_IO, true, _("Specified FITS metadata type, %c, is not supported."), keyType);
-            return out;
+            psFree(header);
+            return NULL;
         }
 
         if (!success) {
             psError(PS_ERR_UNKNOWN, false, _("Failed to add metadata item, %s."), keyName);
-            return out;
+            psFree(header);
+            return NULL;
         }
 
@@ -193,7 +281,60 @@
         (void)fits_get_errstatus(status, fitsErr);
         psError(PS_ERR_IO, true, _("Failed to add metadata item, %s."), fitsErr);
-        return false;
-    }
-
+        psFree(header);
+        return false;
+    }
+
+    return header;
+}
+
+
+psMetadata* psFitsReadHeader(psMetadata* out,
+                             const psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    psMetadata *header = readHeader(fits); // Header
+    if (!header) {
+        return NULL;
+    }
+
+    // Explore the potential case that this is an empty PHU, and the first extension contains the sole image,
+    // which is compressed.
+    if (psFitsCheckSingleCompressedImagePHU(fits, header)) {
+        // This is really what we want, not the empty PHU
+        psTrace("psLib.fits", 1,
+                "This PHU should really be a compressed image --- getting that header instead.");
+        psFree(header);
+        header = readHeader(fits);
+        if (!header) {
+            return NULL;
+        }
+    }
+
+    if (!out) {
+        return header;
+    }
+
+    // Need to move header onto the nominated metadata
+    psMetadataIterator *iter = psMetadataIteratorAlloc(header, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;           // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        // Need to look for MULTI, which won't be picked up using the iterator.
+        psMetadataItem *multiCheckItem = psMetadataLookup(header, item->name);
+        assert(multiCheckItem);
+        unsigned int flag = 0;      // Flag to indicate MULTI; otherwise default action
+        if (multiCheckItem->type == PS_DATA_METADATA_MULTI) {
+            flag = PS_META_DUPLICATE_OK;
+        }
+        if (!psMetadataAddItem(out, item, PS_LIST_TAIL, flag)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to add header item %s to extant metadata.",
+                    item->name);
+            psFree(iter);
+            psFree(header);
+            return NULL;
+        }
+    }
+    psFree(iter);
+    psFree(header);
     return out;
 }
@@ -201,11 +342,7 @@
 psMetadata* psFitsReadHeaderSet(psMetadata* out, const psFits* fits)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psFits object can not NULL."));
-        psFree(out);
-        return NULL;
-    }
-
-    if (out == NULL) {
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    if (!out) {
         out = psMetadataAlloc();
     }
@@ -279,11 +416,5 @@
             // image, the NAXISn haven't been changed; or after converting to F32, the BITPIX hasn't been
             // changed) so we'll take care of that for them.
-            bool writeKey = true;           // Should we write this keyword?
-            for (int i = 0; noWriteFitsKeys[i] && writeKey; i++) {
-                if (strcmp(item->name, noWriteFitsKeys[i]) == 0) {
-                    writeKey = false;
-                }
-            }
-            if (!writeKey) {
+            if (keywordInList(item->name, noWriteFitsKeys)) {
                 // Don't write it; skip to the next key
                 continue;
@@ -294,4 +425,5 @@
                 // that go in are correct.  However, when we're writing a "blank" HDU (header only), we want
                 // to preserve NAXISn etc for reference, so we don't do this.
+                bool writeKey = true;   // Write this keyword?
                 for (int i = 0; noWriteFitsKeyStarts[i] && writeKey; i++) {
                     if (strncmp(item->name, noWriteFitsKeyStarts[i], strlen(noWriteFitsKeyStarts[i])) == 0) {
@@ -349,5 +481,6 @@
                         }
                     } else {
-                        fits_update_key(fits->fd, TFLOAT, item->name, &item->data.F32, item->comment, &status);
+                        fits_update_key(fits->fd, TFLOAT, item->name, &item->data.F32, item->comment,
+                                        &status);
                     }
                     break;
@@ -364,5 +497,6 @@
                         }
                     } else {
-                        fits_update_key(fits->fd, TDOUBLE, item->name, &item->data.F64, item->comment, &status);
+                        fits_update_key(fits->fd, TDOUBLE, item->name, &item->data.F64, item->comment,
+                                        &status);
                     }
                     break;
@@ -395,13 +529,6 @@
                       )
 {
-    if (!fits) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psFits object can not NULL."));
-        return false;
-    }
-
-    if (!output) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psMetadata was NULL.  Need a non-NULL psMetadata for operation to be performed."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_METADATA_NON_NULL(output, false);
 
     return fitsWriteHeader(fits, output, true);
@@ -413,8 +540,5 @@
                      )
 {
-    if (!fits) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psFits object can not NULL."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     // We allow output == NULL in order to write a minimal header.
@@ -458,8 +582,5 @@
 bool psFitsHeaderValidate(psMetadata *header)
 {
-    if (header == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psMetadata was NULL.  Need a non-NULL psMetadata for operation to be performed."));
-        return false;
-    }
+    PS_ASSERT_METADATA_NON_NULL(header, false);
 
     // Traverse the metadata list and inspect at each key
Index: trunk/psLib/src/fits/psFitsHeader.h
===================================================================
--- trunk/psLib/src/fits/psFitsHeader.h	(revision 15126)
+++ trunk/psLib/src/fits/psFitsHeader.h	(revision 15179)
@@ -4,6 +4,6 @@
  * @author Robert DeSonia, MHPCC
  *
- * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
- * @date $Date: 2007-01-23 22:47:23 $
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-10-03 21:27:21 $
  *
  * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -18,4 +18,22 @@
 #include "psFits.h"
 #include "psMetadata.h"
+
+
+/// Determine whether the current HDU is an empty PHU with a single compressed image following.
+///
+/// In that case, what should be treated as an image PHU is technically an empty PHU with a binary table
+/// extension.  We test the current position, number of extensions, the FITS headers and presence of a
+/// following compressed image to determine if this is the case.  If so, the FITS file pointer is left
+/// pointing at the compressed image.
+bool psFitsCheckSingleCompressedImagePHU(const psFits *fits, ///< FITS file pointer
+                                         const psMetadata *header ///< Header, or NULL
+    );
+
+/// Parse a string read from the FITS header.
+
+/// Removes the quotes, and any trailing spaces.  NOTE: the returned string is NOT on the psLib memory system
+/// --- it's simply a hacked version of the input string.  Note also that the input string is MODIFIED.
+char *p_psFitsHeaderParseString(char *string ///< String to parse
+    );
 
 /** Reads the header of the current HDU.
Index: trunk/psLib/src/fits/psFitsImage.c
===================================================================
--- trunk/psLib/src/fits/psFitsImage.c	(revision 15126)
+++ trunk/psLib/src/fits/psFitsImage.c	(revision 15179)
@@ -7,6 +7,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2007-01-09 22:38:52 $
+ *  @version $Revision: 1.18 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-10-03 21:27:21 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -38,6 +38,5 @@
 
 // Information required to read a FITS file
-typedef struct
-{
+typedef struct {
     int nAxis;                          // Number of axes
     int bitPix;                         // Bits per pixel
@@ -48,6 +47,5 @@
     int fitsDatatype;                   // cfitsio data type
     int psDatatype;                     // psLib data type
-}
-p_psFitsReadInfo;
+} p_psFitsReadInfo;
 
 static p_psFitsReadInfo *p_psFitsReadInfoAlloc(const psFits *fits, // The FITS file handle
@@ -56,5 +54,5 @@
                                               )
 {
-    PS_ASSERT_PTR_NON_NULL(fits, NULL);
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
     PS_ASSERT_INT_NONNEGATIVE(z, NULL);
 
@@ -221,4 +219,13 @@
                         )
 {
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(z, NULL);
+
+    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+        // This is really what we want, not the empty PHU
+        psTrace("psLib.fits", 1,
+                "This PHU should really be a compressed image --- reading that image instead.");
+    }
+
     p_psFitsReadInfo *info = p_psFitsReadInfoAlloc(fits, region, z);
 
@@ -243,7 +250,16 @@
                               )
 {
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(z, NULL);
+
     if (output && output->parent) {
         psError(PS_ERR_IO, true, "Unable to read into a buffer for a child image.\n");
         return NULL;
+    }
+
+    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+        // This is really what we want, not the empty PHU
+        psTrace("psLib.fits", 1,
+                "This PHU should really be a compressed image --- reading that image instead.");
     }
 
@@ -270,4 +286,6 @@
                       const char* extname)
 {
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_IMAGE_NON_NULL(input, false);
     // this is equivalent to insert after the last HDU
 
@@ -283,14 +301,6 @@
                        bool after)
 {
-
-    if (!fits) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psFits object can not NULL."));
-        return false;
-    }
-
-    if (!input) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psImage was NULL.  Need a non-NULL psImage for operation to be performed."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_IMAGE_NON_NULL(input, false);
 
     int numCols = input->numCols;       // Number of columns for image
@@ -391,17 +401,8 @@
                        int z)
 {
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_IMAGE_NON_NULL(input, false);
+
     int status = 0;
-
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
-
-    if (input == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psImage was NULL.  Need a non-NULL psImage for operation to be performed."));
-        return false;
-    }
 
     // check to see if we are positioned on an image HDU
@@ -499,4 +500,6 @@
 psArray *psFitsReadImageCube(const psFits *fits, psRegion region)
 {
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
     int nAxis = 0;                      // Number of axes
     long nAxes[3];                      // Number of pixels on each axis
@@ -504,12 +507,12 @@
     char fitsErr[80] = "";              // CFITSIO error message string
 
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return NULL;
-    }
-
     // Some of this replicates what is in psFitsReadImage, so it's a little inefficient.  But it saves
     // code replication, and should be sufficient for our needs.
+
+    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+        // This is really what we want, not the empty PHU
+        psTrace("psLib.fits", 1,
+                "This PHU should really be a compressed image --- reading that image instead.");
+    }
 
     if (fits_get_img_dim(fits->fd, &nAxis, &status) != 0) {
@@ -544,5 +547,7 @@
 
     // Bad dimensionality
-    psError(PS_ERR_IO, true, _("Image number of dimensions, %d, is not valid.  Only two or three dimensions supported for FITS I/O."), nAxis);
+    psError(PS_ERR_IO, true,
+            _("Image number of dimensions, %d, is not valid."
+              " Only two or three dimensions supported for FITS I/O."), nAxis);
     return NULL;
 }
@@ -550,15 +555,6 @@
 bool psFitsWriteImageCube(psFits *fits, psMetadata *header, const psArray *input, const char *extname)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
-
-    if (input == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psImage was NULL.  Need a non-NULL psImage for operation to be performed."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_ARRAY_NON_NULL(input, false);
 
     if (input->n == 0) {
@@ -592,8 +588,8 @@
     }
     bool update = psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS", PS_META_REPLACE, "Dimensionality", 3) &&
-                  psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS1", PS_META_REPLACE, "Number of columns", numCols) &&
-                  psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS2", PS_META_REPLACE, "Number of rows", numRows) &&
-                  psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS3", PS_META_REPLACE, "Number of image planes",
-                                   input->n);
+        psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS1", PS_META_REPLACE, "Number of columns", numCols) &&
+        psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS2", PS_META_REPLACE, "Number of rows", numRows) &&
+        psMetadataAddS32(headerCopy, PS_LIST_HEAD, "NAXIS3", PS_META_REPLACE, "Number of image planes",
+                         input->n);
     if (! update) {
         psError(PS_ERR_UNKNOWN, false, _("Failed to add metadata item, %s."),
@@ -625,15 +621,6 @@
 bool psFitsUpdateImageCube(psFits *fits, const psArray *input, int x0, int y0)
 {
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
-
-    if (input == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psImage was NULL.  Need a non-NULL psImage for operation to be performed."));
-        return false;
-    }
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_ARRAY_NON_NULL(input, false);
 
     if (input->n == 0) {
Index: trunk/psLib/src/fits/psFitsTable.c
===================================================================
--- trunk/psLib/src/fits/psFitsTable.c	(revision 15126)
+++ trunk/psLib/src/fits/psFitsTable.c	(revision 15179)
@@ -7,6 +7,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.27 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2007-03-22 21:40:47 $
+ *  @version $Revision: 1.28 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-10-03 21:27:21 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -38,13 +38,10 @@
                                int row)
 {
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_INT_NONNEGATIVE(row, NULL);
+
     long numRows;
     int numCols;
     int status = 0;
-
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return NULL;
-    }
 
     // check to see if we even are positioned on a table HDU
@@ -185,12 +182,9 @@
                                const char* colname)
 {
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(colname, NULL);
+
     int colnum = 0;
     int status = 0;
-
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return NULL;
-    }
 
     // check to see if we even are positioned on a table HDU
@@ -266,12 +260,9 @@
                                    const char* colname)
 {
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(colname, NULL);
+
     int status = 0;
     int colnum = 0;
-
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return NULL;
-    }
 
     // check to see if we even are positioned on a table HDU
@@ -348,11 +339,7 @@
 psArray* psFitsReadTable(const psFits* fits)
 {
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
     int status = 0;
-
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return NULL;
-    }
 
     // check to see if we even are positioned on a table HDU
@@ -400,5 +387,9 @@
                       const char *extname)
 {
-    psFitsMoveLast(fits);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    if (!psFitsMoveLast(fits)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to move to last extension to write table");
+        return false;
+    }
     return psFitsInsertTable(fits, header, table, extname, true);
 }
@@ -460,11 +451,9 @@
 // Column specification
 // Included here, because there's no need for the user to have access to it
-typedef struct
-{
+typedef struct {
     psDataType type;                    // psLib type (e.g., PS_DATA_STRING or PS_TYPE_F32)
     size_t size;                        // Size (number of repeats)
     psElemType vectorType;              // psLib type of vectors
-}
-colSpec;
+} colSpec;
 
 
@@ -475,8 +464,8 @@
                        bool after)
 {
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_ARRAY_NON_NULL(table, false);
+
     int status = 0;
-
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_ARRAY_NON_NULL(table, false);
 
     long numRows = table->n;
@@ -614,5 +603,6 @@
         char fitsErr[MAX_STRING_LENGTH];
         fits_get_errstatus(status, fitsErr);
-        psError(PS_ERR_LOCATION_INVALID, true, "Unable to create FITS table with %ld columns and %ld rows: %s",
+        psError(PS_ERR_LOCATION_INVALID, true,
+                "Unable to create FITS table with %ld columns and %ld rows: %s",
                 numColumns, table->n, fitsErr);
         psFree(colSpecsIter);
@@ -632,9 +622,9 @@
     if (extname && strlen(extname) > 0) {
         if (!psFitsSetExtName(fits, extname)) {
-	    psError(PS_ERR_IO, false, "Unable to write FITS header extension name.\n");
-	    psFree(colSpecsIter);
-	    psFree(colSpecs);
-	    return false;
-	}
+            psError(PS_ERR_IO, false, "Unable to write FITS header extension name.\n");
+            psFree(colSpecsIter);
+            psFree(colSpecs);
+            return false;
+        }
     }
 
@@ -728,17 +718,9 @@
                        int row)
 {
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_METADATA_NON_NULL(data, false);
+    PS_ASSERT_INT_NONNEGATIVE(row, false);
+
     int status = 0;
-
-    if (fits == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psFits object can not NULL."));
-        return false;
-    }
-
-    if (data == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                _("The input psImage was NULL.  Need a non-NULL psImage for operation to be performed."));
-        return false;
-    }
 
     // check to see if we even are positioned on a table HDU
