fitsmaniplib/fitsfiles.c
2019-04-01 13:45:01 +03:00

301 lines
9.7 KiB
C

/*
* This file is part of the FITSmaniplib project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>, <eddy@sao.ru>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "FITSmanip.h"
#include "local.h"
/**************************************************************************************
* FITS files *
**************************************************************************************/
/*
* Functions for working with files I/O:
* read/write/rewrite/modify
*/
/**
* @brief FITS_addHDU - add new HDU to FITS file structure
* @param fits (io) - fits file to use
* @return pointer to new HDU or NULL in case of error
*/
FITSHDU *FITS_addHDU(FITS *fits){
if(fits->NHDUs < 0) fits->NHDUs = 0;
int hdunum = fits->NHDUs + 1;
// add 1 to `hdunum` because HDU numbering starts @1
FITSHDU *newhdu = realloc(fits->HDUs, sizeof(FITSHDU)*(1+hdunum));
if(!newhdu){
WARN("FITS_addHDU, realloc() failed");
return NULL;
}
fits->HDUs = newhdu;
fits->curHDU = &fits->HDUs[hdunum];
fits->NHDUs = hdunum;
return fits->curHDU;
}
/**
* @brief FITS_free - delete FITS structure from memory
* @param fits - address of FITS pointer
*/
void FITS_free(FITS **fits){
if(!fits || !*fits) return;
FITS *f = *fits;
FREE(f->filename);
int fst = 0;
fits_close_file(f->fp, &fst);
int n, N = f->NHDUs;
for(n = 1; n < N; ++n){
FITSHDU *hdu = &f->HDUs[n];
if(!hdu) continue;
keylist_free(&hdu->keylist);
if(!hdu->contents.image) continue;
switch(hdu->hdutype){
case IMAGE_HDU:
image_free(&hdu->contents.image);
break;
case BINARY_TBL:
case ASCII_TBL:
table_free(&hdu->contents.table);
break;
default: // do nothing
break;
}
}
FREE(*fits);
}
/**
TODO: what's about HCOMPRESS?
*/
/**
* @brief FITS_open - just open FITS file
* @param filename - file to open
* @return pointer to FITS structure or NULL
*/
FITS *FITS_open(char *filename){
FITS *fits = MALLOC(FITS, 1);
int fst = 0;
// use fits_open_diskfile instead of fits_open_file to prevent using of extended name syntax
fits_open_diskfile(&fits->fp, filename, READONLY, &fst);
if(fst){
FITS_reporterr(&fst);
FITS_free(&fits);
return NULL;
}
fits->filename = strdup(filename);
return fits;
}
/**
* @brief FITS_read - try to open & read all contents of FITS file
* This function won't work with unordinary files. Use it only with simple files with primitive structure.
* @param filename - file to open
* @return pointer to FITS structure or NULL
*/
FITS *FITS_read(char *filename){
int hdunum = 0, fst = 0;
FITS *fits = FITS_open(filename);
if(!fits) return NULL;
fits_get_num_hdus(fits->fp, &hdunum, &fst);
DBG("Got %d HDUs", hdunum);
if(fst || hdunum < 1){
if(!fst) FITS_free(&fits);
WARNX(_("Can't read HDU"));
goto returning;
}
fits->NHDUs = hdunum;
fits->HDUs = MALLOC(FITSHDU, hdunum+1);
int hdutype;
for(int i = 1; i <= hdunum && !(fits_movabs_hdu(fits->fp, i, &hdutype, &fst)); ++i){
FITSHDU *curHDU = &fits->HDUs[i];
fits->curHDU = curHDU;
DBG("try to read keys from HDU #%d (type: %d)", i, hdutype);
curHDU->hdutype = hdutype;
curHDU->keylist = keylist_read(fits);
// types: IMAGE_HDU , ASCII_TBL, BINARY_TBL
DBG("HDU[%d] type %d", i, hdutype);
switch(hdutype){
case IMAGE_HDU:
DBG("Image");
curHDU->contents.image = image_read(fits);
break;
case BINARY_TBL:
DBG("Binary table");
//curHDU->contents.table = table_read(fits);
break;
case ASCII_TBL:
DBG("ASCII table");
//curHDU->contents.table = table_read(fits);
break;
default:
WARNX(_("Unknown HDU type"));
}
}
if(fst == END_OF_FILE){
fst = 0;
}
returning:
if(fst){
FITS_reporterr(&fst);
FITS_free(&fits);
}
return fits;
}
static bool keylist_write(KeyList *kl, fitsfile *fp){
int st = 0;
bool ret = TRUE;
if(!fp || !kl) return FALSE;
while(kl){
if(kl->keyclass > TYP_CMPRS_KEY){ // this record should be written
fits_write_record(fp, kl->record, &st);
DBG("Write %s, st = %d", kl->record, st);
if(st){FITS_reporterr(&st); ret = FALSE;}
}
kl = kl->next;
}
return ret;
}
/**
* @brief FITS_write - write FITS file to disk
* @param filename - new filename (with possible cfitsio additions like ! and so on)
* @param fits - structure to write
* @return TRUE if all OK
*/
bool FITS_write(char *filename, FITS *fits){
if(!filename || !fits) return FALSE;
fitsfile *fp;
int fst = 0;
fits_create_file(&fp, filename, &fst);
DBG("create file %s", filename);
if(fst){FITS_reporterr(&fst); return FALSE;}
int N = fits->NHDUs;
for(int i = 1; i <= N; ++i){
FITSHDU *hdu = &fits->HDUs[i];
if(!hdu) continue;
FITSimage *img;
KeyList *records = hdu->keylist;
DBG("HDU #%d (type %d)", i, hdu->hdutype);
switch(hdu->hdutype){
case IMAGE_HDU:
img = hdu->contents.image;
if(!img && records){ // something wrong - just write keylist
DBG("create empty image with records");
fits_create_img(fp, SHORT_IMG, 0, NULL, &fst);
if(fst){FITS_reporterr(&fst); continue;}
keylist_write(records, fp);
DBG("OK");
continue;
}
DBG("create, bitpix: %d, naxis = %d, totpix = %ld", img->bitpix, img->naxis, img->totpix);
fits_create_img(fp, img->bitpix, img->naxis, img->naxes, &fst);
if(fst){FITS_reporterr(&fst); continue;}
keylist_write(records, fp);
DBG("OK, now write image");
//int bscale = 1, bzero = 32768, status = 0;
//fits_set_bscale(fp, bscale, bzero, &status);
if(img && img->data){
DBG("bitpix: %d, dtype: %d", img->bitpix, img->dtype);
fits_write_img(fp, img->dtype, 1, img->totpix, img->data, &fst);
DBG("status: %d", fst);
if(fst){FITS_reporterr(&fst); continue;}
}
break;
case BINARY_TBL:
case ASCII_TBL:
// TODO: save table
break;
}
}
fits_close_file(fp, &fst);
if(fst){FITS_reporterr(&fst);}
return TRUE;
}
/**
* @brief FITS_rewrite - rewrite file in place
* @param fits - pointer to FITS structure
* @return TRUE if all OK
*/
bool FITS_rewrite(FITS *fits){
FNAME();
char rlpath[PATH_MAX];
if(realpath(fits->filename, rlpath)){do{ // got real path - try to make link
char *d = strdup(rlpath);
if(!d){ WARN("strdup()"); FREE(d); break; }
char *dir = dirname(d);
if(!dir){ WARN("dirname()"); FREE(d); break; }
char newpath[PATH_MAX];
char *nm = tmpnam(NULL);
if(!nm){ WARN("tmpnam()"); FREE(d); break; }
char *fnm = basename(nm);
if(!fnm){ WARN("basename()"); FREE(d); break; }
snprintf(newpath, PATH_MAX, "%s/%s", dir, fnm);
FREE(d);
DBG("make link: %s -> %s", rlpath, newpath);
if(link(rlpath, newpath)){ WARN("link()"); break; }
if(unlink(rlpath)){ WARN("unlink()"); break; }
if(FITS_write(rlpath, fits)){
unlink(newpath);
return TRUE;
}
// problems: restore old file
if(link(newpath, rlpath)) WARN("link()");
if(unlink(newpath)) WARN("unlink()");
}while(0);}else WARN(_("Can't get real path for %s, use cfitsio to rewrite"), fits->filename);
// Can't get realpath or some other error, try to use cfitsio
snprintf(rlpath, PATH_MAX, "!%s", fits->filename);
DBG("PATH: %s", rlpath);
return FITS_write(rlpath, fits);
}
/*
* Different file functions
*/
/**
* @brief file_absent - check whether the file exists
* @param name - filename to check
* @return TRUE if file _name_ not exists
*/
bool file_absent(char *name){
struct stat filestat;
if(!stat(name, &filestat)) return FALSE;
if(errno == ENOENT) return TRUE;
return FALSE;
}
/**
* @brief make_filename - find the first non-existing filename
* @param buff - buffer for filename
* @param buflen - its length (including trailing zero)
* @param prefix - filename prefix
* @param suffix - filename suffix (e.g. "fits", "fit", "fit.gz" etc)
* @return
*/
char* make_filename(char *buff, size_t buflen, char *prefix, char *suffix){
int num;
for(num = 1; num < 10000; ++num){
if(snprintf(buff, buflen, "%s_%04d.%s", prefix, num, suffix) < 1)
return NULL;
if(file_absent(buff)) // OK, file not exists
return buff;
}
return NULL;
}