1335 lines
38 KiB
C
1335 lines
38 KiB
C
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@file fits_h.c
|
|
@author N. Devillard
|
|
@date Mar 2000
|
|
@version $Revision: 1.27 $
|
|
@brief FITS header handling
|
|
|
|
This file contains definition and related methods for the FITS header
|
|
structure. This structure is meant to remain opaque to the user, who
|
|
only accesses it through the dedicated functions.
|
|
|
|
The 'keytuple' type is strictly internal to this module.
|
|
It describes FITS cards as tuples (key,value,comment,line), where key
|
|
is always a non-NULL character string, value and comment are
|
|
allowed to be NULL. 'line' is a string containing the line as it
|
|
has been read from the input FITS file (raw). It is set to NULL if
|
|
the card is modified later. This allows in output two options:
|
|
either reconstruct the FITS lines by printing key = value / comment
|
|
in a FITS-compliant way, or output the lines as they were found in
|
|
input, except for the modified ones.
|
|
|
|
The following functions are associated methods
|
|
to this data structure:
|
|
|
|
- keytuple_new() constructor
|
|
- keytuple_del() destructor
|
|
- keytuple_dmp() dumps a keytuple to stdout
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
/*
|
|
$Id: fits_h.c,v 1.27 2005/06/06 12:14:15 yjung Exp $
|
|
$Author: yjung $
|
|
$Date: 2005/06/06 12:14:15 $
|
|
$Revision: 1.27 $
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Includes
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
#include "fits_p.h"
|
|
#include "fits_h.h"
|
|
#include "simple.h"
|
|
#include "xmemory.h"
|
|
#include "expkey.h"
|
|
#include "qerror.h"
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief keytuple object (internal)
|
|
|
|
This structure represents a FITS card (key, val, comment) in memory.
|
|
A FITS header is a list of such structs. Although the struct is here
|
|
made public for convenience, it is not supposed to be directly used.
|
|
High-level FITS routines should do the job just fine, without need
|
|
to know about this struct.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
typedef struct _keytuple_ {
|
|
|
|
char * key ; /** Key: unique string in a list */
|
|
char * val ; /** Value, always as a string */
|
|
char * com ; /** Comment associated to key */
|
|
char * lin ; /** Initial line in FITS header if applicable */
|
|
int typ ; /** Key type */
|
|
|
|
/** Implemented as a doubly-linked list */
|
|
struct _keytuple_ * next ;
|
|
struct _keytuple_ * prev ;
|
|
} keytuple ;
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@enum keytype
|
|
@brief Possible key types
|
|
|
|
This enum stores all possible types for a FITS keyword. These determine
|
|
the order of appearance in a header, they are a crucial point for
|
|
DICB (ESO) compatibility. This classification is internal to this
|
|
module.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
typedef enum _keytype_ {
|
|
keytype_undef =0,
|
|
|
|
keytype_top =1,
|
|
|
|
/* Mandatory keywords */
|
|
/* All FITS files */
|
|
keytype_bitpix =2,
|
|
keytype_naxis =3,
|
|
|
|
keytype_naxis1 =11,
|
|
keytype_naxis2 =12,
|
|
keytype_naxis3 =13,
|
|
keytype_naxis4 =14,
|
|
keytype_naxisi =20,
|
|
/* Random groups only */
|
|
keytype_group =30,
|
|
/* Extensions */
|
|
keytype_pcount =31,
|
|
keytype_gcount =32,
|
|
/* Main header */
|
|
keytype_extend =33,
|
|
/* Images */
|
|
keytype_bscale =34,
|
|
keytype_bzero =35,
|
|
/* Tables */
|
|
keytype_tfields =36,
|
|
keytype_tbcoli =40,
|
|
keytype_tformi =41,
|
|
|
|
/* Other primary keywords */
|
|
keytype_primary =100,
|
|
|
|
/* HIERARCH ESO keywords ordered according to DICB */
|
|
keytype_hierarch_dpr =200,
|
|
keytype_hierarch_obs =201,
|
|
keytype_hierarch_tpl =202,
|
|
keytype_hierarch_gen =203,
|
|
keytype_hierarch_tel =204,
|
|
keytype_hierarch_ins =205,
|
|
keytype_hierarch_det =206,
|
|
keytype_hierarch_log =207,
|
|
keytype_hierarch_pro =208,
|
|
/* Other HIERARCH keywords */
|
|
keytype_hierarch =300,
|
|
|
|
/* HISTORY and COMMENT */
|
|
keytype_history =400,
|
|
keytype_comment =500,
|
|
/* END */
|
|
keytype_end =1000
|
|
} keytype ;
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Private to this module
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
static keytuple * keytuple_new(const char *, const char *, const char
|
|
*, const char *);
|
|
static void keytuple_del(keytuple * k);
|
|
static void keytuple_dmp(keytuple * k);
|
|
static keytype keytuple_type(const char * key);
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Private functions
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief keytuple constructor
|
|
@param key Key associated to key tuple (cannot be NULL).
|
|
@param val Value associated to key tuple.
|
|
@param com Comment associated to key tuple.
|
|
@param lin Initial line read from FITS header (if applicable).
|
|
@return 1 pointer to newly allocated keytuple.
|
|
|
|
This function is a keytuple creator. NULL values and zero-length strings
|
|
are valid parameters for all but the key field. The returned object must
|
|
be deallocated using keytuple_del().
|
|
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
static keytuple * keytuple_new(
|
|
const char * key,
|
|
const char * val,
|
|
const char * com,
|
|
const char * lin)
|
|
{
|
|
keytuple * k ;
|
|
|
|
if (key==NULL) return NULL ;
|
|
|
|
/* Allocate space for new structure */
|
|
k = malloc(sizeof(keytuple));
|
|
/* Hook a copy of the new key */
|
|
k->key = strdup(qfits_expand_keyword(key)) ;
|
|
/* Hook a copy of the value if defined */
|
|
k->val = NULL ;
|
|
if (val!=NULL) {
|
|
if (strlen(val)>0) k->val = strdup(val);
|
|
}
|
|
/* Hook a copy of the comment if defined */
|
|
k->com = NULL ;
|
|
if (com!=NULL) {
|
|
if (strlen(com)>0) k->com = strdup(com) ;
|
|
}
|
|
/* Hook a copy of the initial line if defined */
|
|
k->lin = NULL ;
|
|
if (lin!=NULL) {
|
|
if (strlen(lin)>0) k->lin = strdup(lin);
|
|
}
|
|
k->next = NULL ;
|
|
k->prev = NULL ;
|
|
k->typ = keytuple_type(key);
|
|
|
|
return k;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief keytuple type identification routine
|
|
@param key String representing a FITS keyword.
|
|
@return A key type (see keytype enum)
|
|
|
|
This function identifies the type of a FITS keyword when given the
|
|
keyword as a string. Keywords are expected literally as they are
|
|
found in a FITS header on disk.
|
|
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
static keytype keytuple_type(const char * key)
|
|
{
|
|
keytype kt ;
|
|
|
|
kt = keytype_undef ;
|
|
/* Assign type to key tuple */
|
|
if (!strcmp(key, "SIMPLE") || !strcmp(key, "XTENSION")) kt = keytype_top ;
|
|
else if (!strcmp(key, "END")) kt = keytype_end ;
|
|
else if (!strcmp(key, "BITPIX")) kt = keytype_bitpix ;
|
|
else if (!strcmp(key, "NAXIS")) kt = keytype_naxis ;
|
|
else if (!strcmp(key, "NAXIS1")) kt = keytype_naxis1 ;
|
|
else if (!strcmp(key, "NAXIS2")) kt = keytype_naxis2 ;
|
|
else if (!strcmp(key, "NAXIS3")) kt = keytype_naxis3 ;
|
|
else if (!strcmp(key, "NAXIS4")) kt = keytype_naxis4 ;
|
|
else if (!strncmp(key, "NAXIS", 5)) kt = keytype_naxisi ;
|
|
else if (!strcmp(key, "GROUP")) kt = keytype_group ;
|
|
else if (!strcmp(key, "PCOUNT")) kt = keytype_pcount ;
|
|
else if (!strcmp(key, "GCOUNT")) kt = keytype_gcount ;
|
|
else if (!strcmp(key, "EXTEND")) kt = keytype_extend ;
|
|
else if (!strcmp(key, "BSCALE")) kt = keytype_bscale ;
|
|
else if (!strcmp(key, "BZERO")) kt = keytype_bzero ;
|
|
else if (!strcmp(key, "TFIELDS")) kt = keytype_tfields ;
|
|
else if (!strncmp(key, "TBCOL", 5)) kt = keytype_tbcoli ;
|
|
else if (!strncmp(key, "TFORM", 5)) kt = keytype_tformi ;
|
|
else if (!strncmp(key, "HIERARCH ESO DPR", 16)) kt = keytype_hierarch_dpr ;
|
|
else if (!strncmp(key, "HIERARCH ESO OBS", 16)) kt = keytype_hierarch_obs ;
|
|
else if (!strncmp(key, "HIERARCH ESO TPL", 16)) kt = keytype_hierarch_tpl ;
|
|
else if (!strncmp(key, "HIERARCH ESO GEN", 16)) kt = keytype_hierarch_gen ;
|
|
else if (!strncmp(key, "HIERARCH ESO TEL", 16)) kt = keytype_hierarch_tel ;
|
|
else if (!strncmp(key, "HIERARCH ESO INS", 16)) kt = keytype_hierarch_ins ;
|
|
else if (!strncmp(key, "HIERARCH ESO LOG", 16)) kt = keytype_hierarch_log ;
|
|
else if (!strncmp(key, "HIERARCH ESO PRO", 16)) kt = keytype_hierarch_pro ;
|
|
else if (!strncmp(key, "HIERARCH", 8)) kt = keytype_hierarch ;
|
|
else if (!strcmp(key, "HISTORY")) kt = keytype_history ;
|
|
else if (!strcmp(key, "COMMENT")) kt = keytype_comment ;
|
|
else if ((int)strlen(key)<9) kt = keytype_primary ;
|
|
return kt ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Keytuple destructor.
|
|
@param k Keytuple to deallocate.
|
|
@return void
|
|
Keytuple destructor.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
static void keytuple_del(keytuple * k)
|
|
{
|
|
if (k==NULL) return ;
|
|
if (k->key) free(k->key);
|
|
if (k->val) free(k->val);
|
|
if (k->com) free(k->com);
|
|
if (k->lin) free(k->lin);
|
|
free(k);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Keytuple dumper.
|
|
@param k Keytuple to dump
|
|
@return void
|
|
|
|
This function dumps a key tuple to stdout. It is meant for debugging
|
|
purposes only.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
static void keytuple_dmp(keytuple * k)
|
|
{
|
|
if (!k) return ;
|
|
printf("[%s]=[", k->key);
|
|
if (k->val) printf("%s", k->val);
|
|
printf("]");
|
|
if (k->com) printf("/[%s]", k->com);
|
|
printf("\n");
|
|
return ;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Public functions
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief FITS header constructor
|
|
@return 1 newly allocated (empty) qfits_header object.
|
|
|
|
This is the main constructor for a qfits_header object. It returns
|
|
an allocated linked-list handler with an empty card list.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
qfits_header * qfits_header_new(void)
|
|
{
|
|
qfits_header * h ;
|
|
h = malloc(sizeof(qfits_header));
|
|
h->first = NULL ;
|
|
h->last = NULL ;
|
|
h->n = 0 ;
|
|
return h;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief FITS header default constructor.
|
|
@return 1 newly allocated qfits_header object.
|
|
|
|
This is a secondary constructor for a qfits_header object. It returns
|
|
an allocated linked-list handler containing two cards: the first one
|
|
(SIMPLE=T) and the last one (END).
|
|
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
qfits_header * qfits_header_default(void)
|
|
{
|
|
qfits_header * h ;
|
|
h = qfits_header_new() ;
|
|
qfits_header_append(h, "SIMPLE", "T", "Fits format", NULL);
|
|
qfits_header_append(h, "END", NULL, NULL, NULL);
|
|
return h;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Add a new card to a FITS header
|
|
@param hdr qfits_header object to modify
|
|
@param key FITS key
|
|
@param val FITS value
|
|
@param com FITS comment
|
|
@param lin FITS original line if exists
|
|
@return void
|
|
|
|
This function adds a new card into a header, at the one-before-last
|
|
position, i.e. the entry just before the END entry if it is there.
|
|
The key must always be a non-NULL string, all other input parameters
|
|
are allowed to get NULL values.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
void qfits_header_add(
|
|
qfits_header * hdr,
|
|
char * key,
|
|
char * val,
|
|
char * com,
|
|
char * lin)
|
|
{
|
|
keytuple * k ;
|
|
keytuple * kbf ;
|
|
keytuple * first ;
|
|
keytuple * last ;
|
|
|
|
if (hdr==NULL || key==NULL) return ;
|
|
if (hdr->n<2) return ;
|
|
|
|
first = (keytuple*)hdr->first ;
|
|
last = (keytuple*)hdr->last ;
|
|
|
|
if (((keytype)first->typ != keytype_top) ||
|
|
((keytype)last->typ != keytype_end)) return ;
|
|
|
|
/* Create new key tuple */
|
|
k = keytuple_new(key, val, com, lin);
|
|
|
|
/* Find the last keytuple with same key type */
|
|
kbf = first ;
|
|
while (kbf!=NULL) {
|
|
if ((k->typ>=kbf->typ) && (k->typ<kbf->next->typ)) break ;
|
|
kbf = kbf->next ;
|
|
}
|
|
if (kbf==NULL) kbf = last->prev ;
|
|
|
|
/* Hook it into list */
|
|
k->next = kbf->next ;
|
|
(kbf->next)->prev = k ;
|
|
kbf->next = k ;
|
|
k->prev = kbf ;
|
|
|
|
hdr->n ++ ;
|
|
return ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief add a new card to a FITS header
|
|
@param hdr qfits_header object to modify
|
|
@param after Key to specify insertion place
|
|
@param key FITS key
|
|
@param val FITS value
|
|
@param com FITS comment
|
|
@param lin FITS original line if exists
|
|
@return void
|
|
|
|
Adds a new card to a FITS header, after the specified key. Nothing
|
|
happens if the specified key is not found in the header. All fields
|
|
can be NULL, except after and key.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
void qfits_header_add_after(
|
|
qfits_header * hdr,
|
|
char * after,
|
|
char * key,
|
|
char * val,
|
|
char * com,
|
|
char * lin)
|
|
{
|
|
keytuple * kreq;
|
|
keytuple * k;
|
|
char * exp_after ;
|
|
|
|
if (hdr==NULL || after==NULL || key==NULL) return ;
|
|
|
|
exp_after = qfits_expand_keyword(after);
|
|
/* Locate where the entry is requested */
|
|
kreq = (keytuple*)(hdr->first) ;
|
|
while (kreq!=NULL) {
|
|
if (!strcmp(kreq->key, exp_after)) break ;
|
|
kreq = kreq->next ;
|
|
}
|
|
if (kreq==NULL) return ;
|
|
k = keytuple_new(key, val, com, lin);
|
|
|
|
k->next = kreq->next ;
|
|
kreq->next->prev = k ;
|
|
kreq->next = k ;
|
|
k->prev = kreq ;
|
|
hdr->n ++ ;
|
|
return ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Append a new card to a FITS header.
|
|
@param hdr qfits_header object to modify
|
|
@param key FITS key
|
|
@param val FITS value
|
|
@param com FITS comment
|
|
@param lin FITS original line if exists
|
|
@return void
|
|
|
|
Adds a new card in a FITS header as the last one. All fields can be
|
|
NULL except key.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
void qfits_header_append(
|
|
qfits_header * hdr,
|
|
const char * key,
|
|
const char * val,
|
|
const char * com,
|
|
const char * lin)
|
|
{
|
|
keytuple * k;
|
|
keytuple * last ;
|
|
|
|
if (hdr==NULL || key==NULL) return ;
|
|
|
|
k = keytuple_new(key, val, com, lin);
|
|
if (hdr->n==0) {
|
|
hdr->first = hdr->last = k ;
|
|
hdr->n = 1 ;
|
|
return ;
|
|
}
|
|
last = (keytuple*)hdr->last ;
|
|
last->next = k ;
|
|
k->prev = last ;
|
|
hdr->last = k ;
|
|
hdr->n++ ;
|
|
return ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Delete a card in a FITS header.
|
|
@param hdr qfits_header to modify
|
|
@param key specifies which card to remove
|
|
@return void
|
|
|
|
Removes a card from a FITS header. The first found card that matches
|
|
the key is removed.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
void qfits_header_del(qfits_header * hdr, char * key)
|
|
{
|
|
keytuple * k ;
|
|
char * xkey ;
|
|
|
|
if (hdr==NULL || key==NULL) return ;
|
|
|
|
xkey = qfits_expand_keyword(key);
|
|
k = (keytuple*)hdr->first ;
|
|
while (k!=NULL) {
|
|
if (!strcmp(k->key, xkey)) break ;
|
|
k = k->next ;
|
|
}
|
|
if (k==NULL)
|
|
return ;
|
|
if(k == hdr->first) {
|
|
hdr->first = k->next ;
|
|
} else {
|
|
k->prev->next = k->next ;
|
|
k->next->prev = k->prev ;
|
|
}
|
|
keytuple_del(k);
|
|
return ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Modifies a FITS card.
|
|
@param hdr qfits_header to modify
|
|
@param key FITS key
|
|
@param val FITS value
|
|
@param com FITS comment
|
|
@return void
|
|
|
|
Finds the first card in the header matching 'key', and replaces its
|
|
value and comment fields by the provided values. The initial FITS
|
|
line is set to NULL in the card.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
void qfits_header_mod(
|
|
qfits_header * hdr,
|
|
char * key,
|
|
char * val,
|
|
char * com)
|
|
{
|
|
keytuple * k ;
|
|
char * xkey ;
|
|
|
|
if (hdr==NULL || key==NULL) return ;
|
|
|
|
xkey = qfits_expand_keyword(key);
|
|
k = (keytuple*)hdr->first ;
|
|
while (k!=NULL) {
|
|
if (!strcmp(k->key, xkey)) break ;
|
|
k=k->next ;
|
|
}
|
|
if (k==NULL) return ;
|
|
|
|
if (k->val) free(k->val);
|
|
if (k->com) free(k->com);
|
|
if (k->lin) free(k->lin);
|
|
k->val = NULL ;
|
|
k->com = NULL ;
|
|
k->lin = NULL ;
|
|
if (val) {
|
|
if (strlen(val)>0) k->val = strdup(val);
|
|
}
|
|
if (com) {
|
|
if (strlen(com)>0) k->com = strdup(com);
|
|
}
|
|
return ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Sort a FITS header
|
|
@param hdr Header to sort (modified)
|
|
@return -1 in error case, 0 otherwise
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
int qfits_header_sort(qfits_header ** hdr)
|
|
{
|
|
qfits_header * sorted ;
|
|
keytuple * k ;
|
|
keytuple * kbf ;
|
|
keytuple * next ;
|
|
keytuple * last ;
|
|
|
|
/* Test entries */
|
|
if (hdr == NULL) return -1 ;
|
|
if (*hdr == NULL) return -1 ;
|
|
if ((*hdr)->n < 2) return 0 ;
|
|
|
|
/* Create the new FITS header */
|
|
sorted = qfits_header_new() ;
|
|
|
|
/* Move the first keytuple to the sorted empty header */
|
|
k = (keytuple*)(*hdr)->first ;
|
|
next = k->next ;
|
|
sorted->first = sorted->last = k ;
|
|
k->next = k->prev = NULL ;
|
|
sorted->n = 1 ;
|
|
|
|
/* Loop over the other tuples */
|
|
while (next != NULL) {
|
|
k = next ;
|
|
next = k->next ;
|
|
|
|
/* Find k's place in sorted */
|
|
kbf = (keytuple*)sorted->first ;
|
|
while (kbf!=NULL) {
|
|
if (k->typ < kbf->typ) break ;
|
|
kbf = kbf->next ;
|
|
}
|
|
|
|
/* Hook k into sorted list */
|
|
if (kbf == NULL) {
|
|
/* k is last in sorted */
|
|
last = sorted->last ;
|
|
sorted->last = k ;
|
|
k->next = NULL ;
|
|
k->prev = last ;
|
|
last->next = k ;
|
|
} else {
|
|
/* k goes just before kbf */
|
|
k->next = kbf ;
|
|
k->prev = kbf->prev ;
|
|
if (kbf->prev != NULL) (kbf->prev)->next = k ;
|
|
else sorted->first = k ;
|
|
kbf->prev = k ;
|
|
}
|
|
(sorted->n) ++ ;
|
|
}
|
|
|
|
/* Replace the input header by the sorted one */
|
|
(*hdr)->first = (*hdr)->last = NULL ;
|
|
qfits_header_destroy(*hdr) ;
|
|
*hdr = sorted ;
|
|
|
|
return 0 ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Copy a FITS header
|
|
@param src Header to replicate
|
|
@return Pointer to newly allocated qfits_header object.
|
|
|
|
Makes a strict copy of all information contained in the source
|
|
header. The returned header must be freed using qfits_header_destroy.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
qfits_header * qfits_header_copy(qfits_header * src)
|
|
{
|
|
qfits_header * fh_copy ;
|
|
keytuple * k ;
|
|
|
|
if (src==NULL) return NULL ;
|
|
|
|
fh_copy = qfits_header_new();
|
|
k = (keytuple*)src->first ;
|
|
while (k!=NULL) {
|
|
qfits_header_append(fh_copy, k->key, k->val, k->com, k->lin) ;
|
|
k = k->next ;
|
|
}
|
|
return fh_copy ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Touch all cards in a FITS header
|
|
@param hdr qfits_header to modify
|
|
@return void
|
|
|
|
Touches all cards in a FITS header, i.e. all original FITS lines are
|
|
freed and set to NULL. Useful when a header needs to be reformatted.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
void qfits_header_touchall(qfits_header * hdr)
|
|
{
|
|
keytuple * k ;
|
|
|
|
if (hdr==NULL) return ;
|
|
k = (keytuple*)hdr->first ;
|
|
while (k!=NULL) {
|
|
if (k->lin!=NULL) {
|
|
free(k->lin);
|
|
k->lin=NULL ;
|
|
}
|
|
k=k->next ;
|
|
}
|
|
return ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Dump a FITS header to stdout
|
|
@param hdr qfits_header to dump
|
|
@return void
|
|
Dump a FITS header to stdout. Mostly for debugging purposes.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
void qfits_header_consoledump(qfits_header * hdr)
|
|
{
|
|
keytuple * k ;
|
|
|
|
if (hdr==NULL) return ;
|
|
k = (keytuple*)hdr->first ;
|
|
printf("------------------------------------\n");
|
|
while (k!=NULL) {
|
|
keytuple_dmp(k) ;
|
|
k=k->next ;
|
|
}
|
|
return ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief qfits_header destructor
|
|
@param hdr qfits_header to deallocate
|
|
@return void
|
|
|
|
Frees all memory associated to a given qfits_header object.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
void qfits_header_destroy(qfits_header * hdr)
|
|
{
|
|
keytuple * k ;
|
|
keytuple * kn ;
|
|
|
|
if (hdr==NULL) return ;
|
|
|
|
k = (keytuple*)hdr->first ;
|
|
while (k!=NULL) {
|
|
kn = k->next ;
|
|
keytuple_del(k);
|
|
k = kn ;
|
|
}
|
|
free(hdr);
|
|
return ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Return the value associated to a key, as a string
|
|
@param hdr qfits_header to parse
|
|
@param key key to find
|
|
@return pointer to statically allocated string
|
|
|
|
Finds the value associated to the given key and return it as a
|
|
string. The returned pointer is statically allocated, so do not
|
|
modify its contents or try to free it.
|
|
|
|
Returns NULL if no matching key is found or no value is attached.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
char * qfits_header_getstr(qfits_header * hdr, const char * key)
|
|
{
|
|
keytuple * k ;
|
|
char * xkey ;
|
|
|
|
if (hdr==NULL || key==NULL) return NULL ;
|
|
|
|
xkey = qfits_expand_keyword(key);
|
|
k = (keytuple*)hdr->first ;
|
|
while (k!=NULL) {
|
|
if (!strcmp(k->key, xkey)) break ;
|
|
k=k->next ;
|
|
}
|
|
if (k==NULL) return NULL ;
|
|
return k->val ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Find a matching key in a header.
|
|
@param hdr qfits_header to parse
|
|
@param key Key prefix to match
|
|
@return pointer to statically allocated string.
|
|
|
|
This function finds the first keyword in the given header for
|
|
which the given 'key' is a prefix, and returns the full name
|
|
of the matching key (NOT ITS VALUE). This is useful to locate
|
|
any keyword starting with a given prefix. Careful with HIERARCH
|
|
keywords, the shortFITS notation is not likely to be accepted here.
|
|
|
|
Examples:
|
|
|
|
@verbatim
|
|
s = qfits_header_findmatch(hdr, "SIMP") returns "SIMPLE"
|
|
s = qfits_header_findmatch(hdr, "HIERARCH ESO DET") returns
|
|
the first detector keyword among the HIERACH keys.
|
|
@endverbatim
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
char * qfits_header_findmatch(qfits_header * hdr, char * key)
|
|
{
|
|
keytuple * k ;
|
|
|
|
if (hdr==NULL || key==NULL) return NULL ;
|
|
|
|
k = (keytuple*)hdr->first ;
|
|
while (k!=NULL) {
|
|
if (!strncmp(k->key, key, (int)strlen(key))) break ;
|
|
k=k->next ;
|
|
}
|
|
if (k==NULL) return NULL ;
|
|
return k->key ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Return the i-th key/val/com/line tuple in a header.
|
|
@param hdr Header to consider
|
|
@param idx Index of the requested card
|
|
@param key Output key
|
|
@param val Output value
|
|
@param com Output comment
|
|
@param lin Output initial line
|
|
@return int 0 if Ok, -1 if error occurred.
|
|
|
|
This function is useful to browse a FITS header object card by card.
|
|
By iterating on the number of cards (available in the 'n' field of
|
|
the qfits_header struct), you can retrieve the FITS lines and their
|
|
components one by one. Indexes run from 0 to n-1. You can pass NULL
|
|
values for key, val, com or lin if you are not interested in a
|
|
given field.
|
|
|
|
@code
|
|
int i ;
|
|
char key[FITS_LINESZ+1] ;
|
|
char val[FITS_LINESZ+1] ;
|
|
char com[FITS_LINESZ+1] ;
|
|
char lin[FITS_LINESZ+1] ;
|
|
|
|
for (i=0 ; i<hdr->n ; i++) {
|
|
qfits_header_getitem(hdr, i, key, val, com, lin);
|
|
printf("card[%d] key[%s] val[%s] com[%s]\n", i, key, val, com);
|
|
}
|
|
@endcode
|
|
|
|
This function has primarily been written to interface a qfits_header
|
|
object to other languages (C++/Python). If you are working within a
|
|
C program, you should use the other header manipulation routines
|
|
available in this module.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
int qfits_header_getitem(
|
|
qfits_header * hdr,
|
|
int idx,
|
|
char * key,
|
|
char * val,
|
|
char * com,
|
|
char * lin)
|
|
{
|
|
keytuple * k ;
|
|
int count ;
|
|
|
|
if (hdr==NULL) return -1 ;
|
|
if (key==NULL && val==NULL && com==NULL && lin==NULL) return 0 ;
|
|
if (idx<0 || idx>hdr->n) return -1 ;
|
|
|
|
count=0 ;
|
|
k = (keytuple*)hdr->first ;
|
|
while (count<idx) {
|
|
k = k->next ;
|
|
count++ ;
|
|
}
|
|
if (key!=NULL) strcpy(key, k->key);
|
|
if (val!=NULL) {
|
|
if (k->val!=NULL) strcpy(val, k->val);
|
|
else val[0]=0 ;
|
|
}
|
|
if (com!=NULL) {
|
|
if (k->com!=NULL) strcpy(com, k->com);
|
|
else com[0]=0 ;
|
|
}
|
|
if (lin!=NULL) {
|
|
if (k->lin!=NULL) strcpy(lin, k->lin);
|
|
else lin[0]=0 ;
|
|
}
|
|
return 0 ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Return the FITS line associated to a key, as a string
|
|
@param hdr qfits_header to parse
|
|
@param key key to find
|
|
@return pointer to statically allocated string
|
|
|
|
Finds the FITS line associated to the given key and return it as a
|
|
string. The returned pointer is statically allocated, so do not
|
|
modify its contents or try to free it.
|
|
|
|
Returns NULL if no matching key is found or no line is attached.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
char * qfits_header_getline(qfits_header * hdr, char * key)
|
|
{
|
|
keytuple * k ;
|
|
char * xkey ;
|
|
|
|
if (hdr==NULL || key==NULL) return NULL ;
|
|
|
|
xkey = qfits_expand_keyword(key);
|
|
k = (keytuple*)hdr->first ;
|
|
while (k!=NULL) {
|
|
if (!strcmp(k->key, xkey)) break ;
|
|
k=k->next ;
|
|
}
|
|
if (k==NULL) return NULL ;
|
|
return k->lin ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Return the comment associated to a key, as a string
|
|
@param hdr qfits_header to parse
|
|
@param key key to find
|
|
@return pointer to statically allocated string
|
|
@doc
|
|
|
|
Finds the comment associated to the given key and return it as a
|
|
string. The returned pointer is statically allocated, so do not
|
|
modify its contents or try to free it.
|
|
|
|
Returns NULL if no matching key is found or no comment is attached.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
char * qfits_header_getcom(qfits_header * hdr, char * key)
|
|
{
|
|
keytuple * k ;
|
|
char * xkey ;
|
|
|
|
if (hdr==NULL || key==NULL) return NULL ;
|
|
|
|
xkey = qfits_expand_keyword(key);
|
|
k = (keytuple*)hdr->first ;
|
|
while (k!=NULL) {
|
|
if (!strcmp(k->key, xkey)) break ;
|
|
k=k->next ;
|
|
}
|
|
if (k==NULL) return NULL ;
|
|
return k->com ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Return the value associated to a key, as an int
|
|
@param hdr qfits_header to parse
|
|
@param key key to find
|
|
@param errval default value to return if nothing is found
|
|
@return int
|
|
|
|
Finds the value associated to the given key and return it as an
|
|
int. Returns errval if no matching key is found or no value is
|
|
attached.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
int qfits_header_getint(qfits_header * hdr, const char * key, int errval)
|
|
{
|
|
char * c ;
|
|
int d ;
|
|
|
|
if (hdr==NULL || key==NULL) return errval ;
|
|
|
|
c = qfits_header_getstr(hdr, key);
|
|
if (c==NULL) return errval ;
|
|
if (sscanf(c, "%d", &d)!=1) return errval ;
|
|
return d ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Return the value associated to a key, as a double
|
|
@param hdr qfits_header to parse
|
|
@param key key to find
|
|
@param errval default value to return if nothing is found
|
|
@return double
|
|
|
|
Finds the value associated to the given key and return it as a
|
|
double. Returns errval if no matching key is found or no value is
|
|
attached.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
double qfits_header_getdouble(qfits_header * hdr, const char * key, double errval)
|
|
{
|
|
char * c ;
|
|
|
|
if (hdr==NULL || key==NULL) return errval ;
|
|
|
|
c = qfits_header_getstr(hdr, key);
|
|
if (c==NULL) return errval ;
|
|
return atof(c);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Return the value associated to a key, as a boolean (int).
|
|
@param hdr qfits_header to parse
|
|
@param key key to find
|
|
@param errval default value to return if nothing is found
|
|
@return int
|
|
|
|
Finds the value associated to the given key and return it as a
|
|
boolean. Returns errval if no matching key is found or no value is
|
|
attached. A boolean is here understood as an int taking the value 0
|
|
or 1. errval can be set to any other integer value to reflect that
|
|
nothing was found.
|
|
|
|
errval is returned if no matching key is found or no value is
|
|
attached.
|
|
|
|
A true value is any character string beginning with a 'y' (yes), a
|
|
't' (true) or the digit '1'. A false value is any character string
|
|
beginning with a 'n' (no), a 'f' (false) or the digit '0'.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
int qfits_header_getboolean(qfits_header * hdr, const char * key, int errval)
|
|
{
|
|
char * c ;
|
|
int ret ;
|
|
|
|
if (hdr==NULL || key==NULL) return errval ;
|
|
|
|
c = qfits_header_getstr(hdr, key);
|
|
if (c==NULL) return errval ;
|
|
if (strlen(c)<1) return errval ;
|
|
|
|
if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
|
|
ret = 1 ;
|
|
} else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
|
|
ret = 0 ;
|
|
} else {
|
|
ret = errval ;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Build a FITS line from the information contained in a card.
|
|
@param line pointer to allocated string to be filled
|
|
@param node pointer to card node in qfits_header linked-list
|
|
@param conservative flag to indicate conservative behaviour
|
|
@return int 0 if Ok, anything else otherwise
|
|
|
|
Build a FITS line (80 chars) from the information contained in a
|
|
node of a qfits_header linked-list. If the mode is set to
|
|
conservative, the original FITS line will be used wherever present.
|
|
If conservative is set to 0, a new line will be formatted.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
static int qfits_header_makeline(
|
|
char * line,
|
|
keytuple * k,
|
|
int conservative)
|
|
{
|
|
char blankline[81];
|
|
int i ;
|
|
|
|
if (line==NULL || k==NULL) return -1 ;
|
|
|
|
/* If a previous line information is there, use it as is */
|
|
if (conservative) {
|
|
if (k->lin != NULL) {
|
|
memcpy(line, k->lin, 80);
|
|
line[80]=(char)0;
|
|
return 0 ;
|
|
}
|
|
}
|
|
/* Got to build keyword from scratch */
|
|
memset(blankline, 0, 81);
|
|
keytuple2str(blankline, k->key, k->val, k->com);
|
|
memset(line, ' ', 80);
|
|
i=0 ;
|
|
while (blankline[i] != (char)0) {
|
|
line[i] = blankline[i] ;
|
|
i++ ;
|
|
}
|
|
line[80]=(char)0;
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Write out a key tuple to a string on 80 chars.
|
|
@param line Allocated output character buffer.
|
|
@param key Key to write.
|
|
@param val Value to write.
|
|
@param com Comment to write.
|
|
@return void
|
|
|
|
Write out a key, value and comment into an allocated character buffer.
|
|
The buffer must be at least 80 chars to receive the information.
|
|
Formatting is done according to FITS standard.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
void keytuple2str(
|
|
char * line,
|
|
char * key,
|
|
char * val,
|
|
char * com)
|
|
{
|
|
int len ;
|
|
int hierarch = 0 ;
|
|
char cval[81];
|
|
char cval2[81];
|
|
char cval_q[81];
|
|
char ccom[81];
|
|
char safe_line[512];
|
|
int i, j ;
|
|
|
|
if (line==NULL || key==NULL) return ;
|
|
|
|
/* Set the line with zeroes */
|
|
memset(line, ' ', 80);
|
|
if (key==NULL) return ;
|
|
|
|
/* END keyword*/
|
|
if (!strcmp(key, "END")) {
|
|
/* Write key and return */
|
|
sprintf(line, "END") ;
|
|
return ;
|
|
}
|
|
/* HISTORY, COMMENT and blank keywords */
|
|
if (!strcmp(key, "HISTORY") ||
|
|
!strcmp(key, "COMMENT") ||
|
|
!strncmp(key, " ", 8)) {
|
|
/* Write key */
|
|
sprintf(line, "%s ", key);
|
|
if (val==NULL) return ;
|
|
|
|
/* There is a value to write, copy it correctly */
|
|
len = strlen(val);
|
|
/* 72 is 80 (FITS line size) - 8 (sizeof COMMENT or HISTORY) */
|
|
if (len>72) len=72 ;
|
|
strncpy(line+8, val, len);
|
|
return ;
|
|
}
|
|
|
|
/* Check for NULL values */
|
|
if (val==NULL) cval[0]=(char)0;
|
|
else if (strlen(val)<1) cval[0]=(char)0;
|
|
else strcpy(cval, val);
|
|
|
|
/* Check for NULL comments */
|
|
if (com==NULL) strcpy(ccom, "no comment");
|
|
else strcpy(ccom, com);
|
|
|
|
/* Set hierarch flag */
|
|
if (!strncmp(key, "HIERARCH", 8)) hierarch ++ ;
|
|
|
|
/* Boolean, int, float or complex */
|
|
if (qfits_is_int(cval) ||
|
|
qfits_is_float(cval) ||
|
|
qfits_is_boolean(cval) ||
|
|
qfits_is_complex(cval)) {
|
|
if (hierarch) sprintf(safe_line, "%-29s= %s / %s", key, cval, ccom);
|
|
else sprintf(safe_line, "%-8.8s= %20s / %-48s", key, cval, ccom);
|
|
strncpy(line, safe_line, 80);
|
|
line[80]=(char)0;
|
|
return ;
|
|
}
|
|
|
|
/* Blank or NULL values */
|
|
if (cval[0]==0) {
|
|
if (hierarch) {
|
|
sprintf(safe_line, "%-29s= / %s", key, ccom);
|
|
} else {
|
|
sprintf(safe_line, "%-8.8s= / %-48s", key, ccom);
|
|
}
|
|
strncpy(line, safe_line, 80);
|
|
line[80]=(char)0;
|
|
return ;
|
|
}
|
|
|
|
/* Can only be a string - Make simple quotes ['] as double [''] */
|
|
memset(cval_q, 0, 81);
|
|
strcpy(cval2, qfits_pretty_string(cval));
|
|
j=0 ;
|
|
i=0 ;
|
|
while (cval2[i] != (char)0) {
|
|
if (cval2[i]=='\'') {
|
|
cval_q[j]='\'';
|
|
j++ ;
|
|
cval_q[j]='\'';
|
|
} else {
|
|
cval_q[j] = cval2[i];
|
|
}
|
|
i++ ;
|
|
j++ ;
|
|
}
|
|
|
|
if (hierarch) {
|
|
sprintf(safe_line, "%-29s= '%s' / %s", key, cval_q, ccom);
|
|
if (strlen(key) + strlen(cval_q) + 3 >= 80)
|
|
safe_line[79] = '\'';
|
|
} else {
|
|
sprintf(safe_line, "%-8.8s= '%-8s' / %s", key, cval_q, ccom);
|
|
}
|
|
strncpy(line, safe_line, 80);
|
|
|
|
/* Null-terminate in any case */
|
|
line[80]=(char)0;
|
|
return ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Dump a FITS header to an opened file.
|
|
@param hdr FITS header to dump
|
|
@param out Opened file pointer
|
|
@return int 0 if Ok, -1 otherwise
|
|
Dumps a FITS header to an opened file pointer.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
int qfits_header_dump(
|
|
qfits_header * hdr,
|
|
FILE * out)
|
|
{
|
|
keytuple * k ;
|
|
char line[81];
|
|
int n_out ;
|
|
|
|
if (hdr==NULL) return -1 ;
|
|
if (out==NULL) out=stdout ;
|
|
|
|
k = (keytuple*)hdr->first ;
|
|
n_out = 0 ;
|
|
while (k!=NULL) {
|
|
/* Make line from information in the node */
|
|
qfits_header_makeline(line, k, 1);
|
|
if ((fwrite(line, 1, 80, out))!=80) {
|
|
fprintf(stderr, "error dumping FITS header");
|
|
return -1 ;
|
|
}
|
|
n_out ++;
|
|
k=k->next;
|
|
}
|
|
/* If printing out to a regular file, blank pad */
|
|
if (out!=stdout && out!=stderr) {
|
|
/* Blank-pad the output */
|
|
memset(line, ' ', 80);
|
|
while (n_out % 36) {
|
|
fwrite(line, 1, 80, out);
|
|
n_out++ ;
|
|
}
|
|
}
|
|
return 0 ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Dump a FITS header to an opened file (one card per line).
|
|
@param hdr FITS header to dump
|
|
@param out Opened file pointer
|
|
@return int 0 if Ok, -1 otherwise
|
|
@see qfits_header_dump()
|
|
Dumps a FITS header to an opened file pointer.
|
|
This function is meant to create hdr files.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
int qfits_header_dump_hdr(
|
|
qfits_header * hdr,
|
|
FILE * out)
|
|
{
|
|
keytuple * k ;
|
|
char line[81];
|
|
int n_out ;
|
|
|
|
if (hdr==NULL) return -1 ;
|
|
if (out==NULL) out=stdout ;
|
|
|
|
k = (keytuple*)hdr->first ;
|
|
n_out = 0 ;
|
|
while (k!=NULL) {
|
|
/* Make line from information in the node */
|
|
qfits_header_makeline(line, k, 1);
|
|
fprintf(out, line) ;
|
|
fprintf(out, "\n") ;
|
|
n_out ++;
|
|
k=k->next;
|
|
}
|
|
return 0 ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Dump a fits header into a memory block.
|
|
@param fh FITS header to dump
|
|
@param hsize Size of the returned header, in bytes (output).
|
|
@return 1 newly allocated memory block containing the FITS header.
|
|
|
|
This function dumps a FITS header structure into a newly allocated
|
|
memory block. The block is composed of characters, just as they would
|
|
appear in a FITS file. This function is useful to make a FITS header
|
|
in memory.
|
|
|
|
The returned block size is indicated in the passed output variable
|
|
'hsize'. The returned block must be deallocated using free().
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
char * qfits_header_to_memblock(qfits_header * fh, int * hsize)
|
|
{
|
|
char * buf ;
|
|
int sz ;
|
|
int nblocks ;
|
|
int ncards ;
|
|
keytuple * k ;
|
|
char * start ;
|
|
|
|
if (fh==NULL || hsize==NULL) return NULL ;
|
|
|
|
/* Count number of blocks in input (1 block is 36 cards) */
|
|
ncards = fh->n ;
|
|
nblocks = 1 ;
|
|
while (ncards>36) {
|
|
nblocks ++ ;
|
|
ncards -= 36 ;
|
|
}
|
|
sz = nblocks * 2880 ;
|
|
/* Allocate buffer and fill it with blanks */
|
|
buf = malloc(sz * sizeof(char));
|
|
memset(buf, ' ', sz);
|
|
|
|
/* Iterate on all cards in the header, dump them into buf */
|
|
start = buf ;
|
|
k = (keytuple*)fh->first ;
|
|
while (k!=NULL) {
|
|
qfits_header_makeline(start, k, 1);
|
|
k=k->next ;
|
|
start+=80 ;
|
|
}
|
|
/* Update output buffer size */
|
|
*hsize = sz ;
|
|
return buf ;
|
|
}
|
|
|
|
/* vim: set ts=4 et sw=4 tw=75 */
|