523 lines
16 KiB
C
523 lines
16 KiB
C
/* $Id: qfits_rw.c,v 1.11 2006/02/23 11:08:59 yjung Exp $
|
|
*
|
|
* This file is part of the ESO QFITS Library
|
|
* Copyright (C) 2001-2004 European Southern Observatory
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
* $Author: yjung $
|
|
* $Date: 2006/02/23 11:08:59 $
|
|
* $Revision: 1.11 $
|
|
* $Name: qfits-6_1_0 $
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Includes
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "qfits_rw.h"
|
|
|
|
#include "qfits_cache.h"
|
|
#include "qfits_card.h"
|
|
#include "qfits_std.h"
|
|
#include "qfits_tools.h"
|
|
#include "qfits_error.h"
|
|
#include "qfits_memory.h"
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Define
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/* FITS magic number */
|
|
#define FITS_MAGIC "SIMPLE"
|
|
/* Size of the FITS magic number */
|
|
#define FITS_MAGIC_SZ 6
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Private to this module
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
static int is_blank_line(const char *) ;
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
* @defgroup qfits_rw FITS header reading/writing
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
/**@{*/
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Function codes
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Read a FITS header from a file to an internal structure.
|
|
@param filename Name of the file to be read
|
|
@return Pointer to newly allocated qfits_header or NULL in error case.
|
|
|
|
This function parses a FITS (main) header, and returns an allocated
|
|
qfits_header object. The qfits_header object contains a linked-list of
|
|
key "tuples". A key tuple contains:
|
|
|
|
- A keyword
|
|
- A value
|
|
- A comment
|
|
- An original FITS line (as read from the input file)
|
|
|
|
Direct access to the structure is not foreseen, use accessor
|
|
functions in fits_h.h
|
|
|
|
Value, comment, and original line might be NULL pointers.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
qfits_header * qfits_header_read(const char * filename)
|
|
{
|
|
/* Forward job to readext */
|
|
return qfits_header_readext(filename, 0);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Read a FITS header from a 'hdr' file.
|
|
@param filename Name of the file to be read
|
|
@return Pointer to newly allocated qfits_header or NULL in error case
|
|
|
|
This function parses a 'hdr' file, and returns an allocated qfits_header
|
|
object. A hdr file is an ASCII format were the header is written with a
|
|
carriage return after each line. The command dfits typically displays
|
|
a hdr file.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
qfits_header * qfits_header_read_hdr(const char * filename)
|
|
{
|
|
qfits_header * hdr ;
|
|
FILE * in ;
|
|
char line[81];
|
|
char * key,
|
|
* val,
|
|
* com ;
|
|
int i, j ;
|
|
|
|
/* Check input */
|
|
if (filename==NULL) return NULL ;
|
|
|
|
/* Initialise */
|
|
key = val = com = NULL ;
|
|
|
|
/* Open the file */
|
|
if ((in=fopen(filename, "r"))==NULL) {
|
|
qfits_error("cannot read [%s]", filename) ;
|
|
return NULL ;
|
|
}
|
|
|
|
/* Create the header */
|
|
hdr = qfits_header_new() ;
|
|
|
|
/* Go through the file */
|
|
while (fgets(line, 81, in)!=NULL) {
|
|
for (i=0 ; i<81 ; i++) {
|
|
if (line[i] == '\n') {
|
|
for (j=i ; j<81 ; j++) line[j] = ' ' ;
|
|
line[80] = (char)0 ;
|
|
break ;
|
|
}
|
|
}
|
|
if (!strcmp(line, "END")) {
|
|
line[3] = ' ';
|
|
line[4] = (char)0 ;
|
|
}
|
|
|
|
/* Rule out blank lines */
|
|
if (!is_blank_line(line)) {
|
|
|
|
/* Get key, value, comment for the current line */
|
|
key = qfits_getkey(line);
|
|
val = qfits_getvalue(line);
|
|
com = qfits_getcomment(line);
|
|
|
|
/* If key or value cannot be found, trigger an error */
|
|
if (key==NULL) {
|
|
qfits_header_destroy(hdr);
|
|
fclose(in) ;
|
|
return NULL ;
|
|
}
|
|
/* Append card to linked-list */
|
|
qfits_header_append(hdr, key, val, com, NULL);
|
|
}
|
|
}
|
|
fclose(in) ;
|
|
|
|
/* The last key should be 'END' */
|
|
if (strlen(key)!=3) {
|
|
qfits_header_destroy(hdr);
|
|
return NULL ;
|
|
}
|
|
if (key[0]!='E' || key[1]!='N' || key[2]!='D') {
|
|
qfits_header_destroy(hdr);
|
|
return NULL ;
|
|
}
|
|
|
|
return hdr ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Read a FITS header from a 'hdr' string
|
|
@param hdr_str String containing the hdr file
|
|
@param nb_char Number of characters in the string
|
|
@return Pointer to newly allocated qfits_header or NULL in error case
|
|
|
|
This function parses a 'hdr' string, and returns an allocated qfits_header
|
|
object.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
qfits_header * qfits_header_read_hdr_string(
|
|
const unsigned char * hdr_str,
|
|
int nb_char)
|
|
{
|
|
qfits_header * hdr ;
|
|
char line[81];
|
|
char * key,
|
|
* val,
|
|
* com ;
|
|
int ind ;
|
|
int i, j ;
|
|
|
|
/* Check input */
|
|
if (hdr_str==NULL) return NULL ;
|
|
|
|
/* Initialise */
|
|
key = val = com = NULL ;
|
|
|
|
/* Create the header */
|
|
hdr = qfits_header_new() ;
|
|
|
|
/* Go through the file */
|
|
ind = 0 ;
|
|
while (ind <= nb_char - 80) {
|
|
strncpy(line, (char*)hdr_str + ind, 80) ;
|
|
line[80] = (char)0 ;
|
|
for (i=0 ; i<81 ; i++) {
|
|
if (line[i] == '\n') {
|
|
for (j=i ; j<81 ; j++) line[j] = ' ' ;
|
|
line[80] = (char)0 ;
|
|
break ;
|
|
}
|
|
}
|
|
if (!strcmp(line, "END")) {
|
|
line[3] = ' ';
|
|
line[4] = (char)0 ;
|
|
}
|
|
|
|
/* Rule out blank lines */
|
|
if (!is_blank_line(line)) {
|
|
|
|
/* Get key, value, comment for the current line */
|
|
key = qfits_getkey(line);
|
|
val = qfits_getvalue(line);
|
|
com = qfits_getcomment(line);
|
|
|
|
/* If key or value cannot be found, trigger an error */
|
|
if (key==NULL) {
|
|
qfits_header_destroy(hdr);
|
|
return NULL ;
|
|
}
|
|
/* Append card to linked-list */
|
|
qfits_header_append(hdr, key, val, com, NULL);
|
|
}
|
|
ind += 80 ;
|
|
}
|
|
|
|
/* The last key should be 'END' */
|
|
if (strlen(key)!=3) {
|
|
qfits_header_destroy(hdr);
|
|
return NULL ;
|
|
}
|
|
if (key[0]!='E' || key[1]!='N' || key[2]!='D') {
|
|
qfits_header_destroy(hdr);
|
|
return NULL ;
|
|
}
|
|
|
|
return hdr ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Read an extension header from a FITS file.
|
|
@param filename Name of the FITS file to read
|
|
@param xtnum Extension number to read, starting from 0.
|
|
@return Newly allocated qfits_header structure.
|
|
|
|
Strictly similar to qfits_header_read() but reads headers from
|
|
extensions instead. If the requested xtension is 0, this function
|
|
returns the main header.
|
|
|
|
Returns NULL in case of error.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
qfits_header * qfits_header_readext(const char * filename, int xtnum)
|
|
{
|
|
qfits_header* hdr ;
|
|
int n_ext ;
|
|
char line[81];
|
|
char * where ;
|
|
char * start ;
|
|
char * key,
|
|
* val,
|
|
* com ;
|
|
int seg_start ;
|
|
int seg_size ;
|
|
size_t size ;
|
|
|
|
/* Check input */
|
|
if (filename==NULL || xtnum<0)
|
|
return NULL ;
|
|
|
|
/* Check that there are enough extensions */
|
|
if (xtnum>0) {
|
|
n_ext = qfits_query_n_ext(filename);
|
|
if (xtnum>n_ext) {
|
|
return NULL ;
|
|
}
|
|
}
|
|
|
|
/* Get offset to the extension header */
|
|
if (qfits_get_hdrinfo(filename, xtnum, &seg_start, &seg_size)!=0) {
|
|
return NULL ;
|
|
}
|
|
|
|
/* Memory-map the input file */
|
|
start = qfits_falloc((char *)filename, seg_start, &size) ;
|
|
if (start==NULL) return NULL ;
|
|
|
|
hdr = qfits_header_new() ;
|
|
where = start ;
|
|
while (1) {
|
|
memcpy(line, where, 80);
|
|
line[80] = (char)0;
|
|
|
|
/* Rule out blank lines */
|
|
if (!is_blank_line(line)) {
|
|
|
|
/* Get key, value, comment for the current line */
|
|
key = qfits_getkey(line);
|
|
val = qfits_getvalue(line);
|
|
com = qfits_getcomment(line);
|
|
|
|
/* If key or value cannot be found, trigger an error */
|
|
if (key==NULL) {
|
|
qfits_header_destroy(hdr);
|
|
hdr = NULL ;
|
|
break ;
|
|
}
|
|
/* Append card to linked-list */
|
|
qfits_header_append(hdr, key, val, com, line);
|
|
/* Check for END keyword */
|
|
if (strlen(key)==3)
|
|
if (key[0]=='E' &&
|
|
key[1]=='N' &&
|
|
key[2]=='D')
|
|
break ;
|
|
}
|
|
where += 80 ;
|
|
/* If reaching the end of file, trigger an error */
|
|
if ((int)(where-start)>=(int)(seg_size+80)) {
|
|
qfits_header_destroy(hdr);
|
|
hdr = NULL ;
|
|
break ;
|
|
}
|
|
}
|
|
qfits_fdealloc(start, seg_start, size) ;
|
|
return hdr ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Pad an existing file with zeros to a multiple of 2880.
|
|
@param filename Name of the file to pad.
|
|
@return void
|
|
|
|
This function simply pads an existing file on disk with enough zeros
|
|
for the file size to reach a multiple of 2880, as required by FITS.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
void qfits_zeropad(const char * filename)
|
|
{
|
|
struct stat sta ;
|
|
int size ;
|
|
int remaining;
|
|
FILE * out ;
|
|
char * buf;
|
|
|
|
if (filename==NULL) return ;
|
|
|
|
/* Get file size in bytes */
|
|
if (stat(filename, &sta)!=0) {
|
|
return ;
|
|
}
|
|
size = (int)sta.st_size ;
|
|
/* Compute number of zeros to pad */
|
|
remaining = size % FITS_BLOCK_SIZE ;
|
|
if (remaining==0) return ;
|
|
remaining = FITS_BLOCK_SIZE - remaining ;
|
|
|
|
/* Open file, dump zeros, exit */
|
|
if ((out=fopen(filename, "a"))==NULL)
|
|
return ;
|
|
buf = qfits_calloc(remaining, sizeof(char));
|
|
fwrite(buf, 1, remaining, out);
|
|
fclose(out);
|
|
qfits_free(buf);
|
|
return ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Identify if a file is a FITS file.
|
|
@param filename name of the file to check
|
|
@return int 0, 1, or -1
|
|
|
|
Returns 1 if the file name looks like a valid FITS file. Returns
|
|
0 else. If the file does not exist, returns -1.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
int qfits_is_fits(const char * filename)
|
|
{
|
|
FILE * fp ;
|
|
char * magic ;
|
|
int isfits ;
|
|
|
|
if (filename==NULL) return -1 ;
|
|
if ((fp = fopen(filename, "r"))==NULL) {
|
|
qfits_error("cannot open file [%s]", filename) ;
|
|
return -1 ;
|
|
}
|
|
|
|
magic = qfits_calloc(FITS_MAGIC_SZ+1, sizeof(char)) ;
|
|
fread(magic, 1, FITS_MAGIC_SZ, fp) ;
|
|
fclose(fp) ;
|
|
magic[FITS_MAGIC_SZ] = (char)0 ;
|
|
if (strstr(magic, FITS_MAGIC)!=NULL)
|
|
isfits = 1 ;
|
|
else
|
|
isfits = 0 ;
|
|
qfits_free(magic) ;
|
|
return isfits ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Retrieve offset to start and size of a header in a FITS file.
|
|
@param filename Name of the file to examine
|
|
@param xtnum Extension number (0 for main)
|
|
@param seg_start Segment start in bytes (output)
|
|
@param seg_size Segment size in bytes (output)
|
|
@return int 0 if Ok, -1 otherwise.
|
|
|
|
This function retrieves the two most important informations about
|
|
a header in a FITS file: the offset to its beginning, and the size
|
|
of the header in bytes. Both values are returned in the passed
|
|
pointers to ints. It is Ok to pass NULL for any pointer if you do
|
|
not want to retrieve the associated value.
|
|
|
|
You must provide an extension number for the header, 0 meaning the
|
|
main header in the file.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
int qfits_get_hdrinfo(
|
|
const char * filename,
|
|
int xtnum,
|
|
int * seg_start,
|
|
int * seg_size)
|
|
{
|
|
if (filename==NULL || xtnum<0 || (seg_start==NULL && seg_size==NULL)) {
|
|
return -1 ;
|
|
}
|
|
if (seg_start!=NULL) {
|
|
*seg_start = qfits_query(filename, QFITS_QUERY_HDR_START | xtnum);
|
|
if (*seg_start<0)
|
|
return -1 ;
|
|
}
|
|
if (seg_size!=NULL) {
|
|
*seg_size = qfits_query(filename, QFITS_QUERY_HDR_SIZE | xtnum);
|
|
if (*seg_size<0)
|
|
return -1 ;
|
|
}
|
|
return 0 ;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@brief Retrieve offset to start and size of a data section in a file.
|
|
@param filename Name of the file to examine.
|
|
@param xtnum Extension number (0 for main).
|
|
@param seg_start Segment start in bytes (output).
|
|
@param seg_size Segment size in bytes (output).
|
|
@return int 0 if Ok, -1 otherwise.
|
|
|
|
This function retrieves the two most important informations about
|
|
a data section in a FITS file: the offset to its beginning, and the size
|
|
of the section in bytes. Both values are returned in the passed
|
|
pointers to ints. It is Ok to pass NULL for any pointer if you do
|
|
not want to retrieve the associated value.
|
|
|
|
You must provide an extension number for the header, 0 meaning the
|
|
main header in the file.
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
int qfits_get_datinfo(
|
|
const char * filename,
|
|
int xtnum,
|
|
int * seg_start,
|
|
int * seg_size)
|
|
{
|
|
if (filename==NULL || xtnum<0 || (seg_start==NULL && seg_size==NULL)) {
|
|
return -1 ;
|
|
}
|
|
if (seg_start!=NULL) {
|
|
*seg_start = qfits_query(filename, QFITS_QUERY_DAT_START | xtnum);
|
|
if (*seg_start<0)
|
|
return -1 ;
|
|
}
|
|
if (seg_size!=NULL) {
|
|
*seg_size = qfits_query(filename, QFITS_QUERY_DAT_SIZE | xtnum);
|
|
if (*seg_size<0)
|
|
return -1 ;
|
|
}
|
|
return 0 ;
|
|
}
|
|
|
|
/**@}*/
|
|
|
|
static int is_blank_line(const char * s)
|
|
{
|
|
int i ;
|
|
|
|
for (i=0 ; i<(int)strlen(s) ; i++) {
|
|
if (s[i]!=' ') return 0 ;
|
|
}
|
|
return 1 ;
|
|
}
|