/*
* fitscheck.c
*
* Functions related for FITS file integrity.
*
*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
*
* This file part of: AstrOmatic FITS/LDAC library
*
* Copyright: (C) 1995-2020 IAP/CNRS/SorbonneU
*
* License: GNU General Public License
*
* AstrOmatic software 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.
* AstrOmatic software 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 AstrOmatic software.
* If not, see .
*
* Last modified: 11/02/2020
*
*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include
#include
#include "fitscat_defs.h"
#include "fitscat.h"
#define ENCODE_OFFSET 0x30
unsigned int exclude[13] = {0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60};
/****** encode_checksum *****************************************************
PROTO void encode_checksum(unsigned int sum, char *str)
PURPOSE Encode a checksum to ASCII
INPUT Checksum,
Destination string.
OUTPUT -.
NOTES Straightforward copy of Seaman & Pence 1995
(ftp://iraf.noao.edu/misc/checksum/).
AUTHOR E. Bertin (IAP)
VERSION 08/05/2001
***/
void encode_checksum(unsigned int sum, char *str)
{
int ch[4],
i,j,k, byte, check;
for (i=0; i<4; i++)
{
/*-- Each byte becomes four */
byte = (sum << 8*i) >> 24;
ch[0] = (ch[1] = ch[2] = ch[3] = byte/4 + ENCODE_OFFSET) + (byte%4);
for (check=1; check;) /* avoid ASCII punctuation */
for (check=k=0; k<13; k++)
{
if (ch[0]==exclude[k] || ch[1]==exclude[k])
{
ch[0]++;
ch[1]--;
check++;
}
if (ch[2]==exclude[k] || ch[3]==exclude[k])
{
ch[2]++;
ch[3]--;
check++;
}
}
for (j=0; j<4; j++) /* assign the bytes */
str[(4*j+i+1)%16] = ch[j]; /* permute the bytes for FITS */
}
str[16] = 0;
return;
}
/****** decode_checksum *****************************************************
PROTO unsigned int decode_checksum(char *str)
PURPOSE Decode an ASCII checksum
INPUT Checksum string.
OUTPUT Checksum.
NOTES Straightforward copy of Seaman & Pence 1995
(ftp://iraf.noao.edu/misc/checksum/).
AUTHOR E. Bertin (IAP)
VERSION 11/02/2020
***/
unsigned int decode_checksum(char *str)
{
char cbuf[16];
unsigned short *sbuf,
los,his,
ashort = 1;
unsigned int hi,lo, hicarry,locarry;
int i;
/* Remove the permuted FITS byte alignment and the ASCII 0 offset */
for (i=0; i<16; i++)
cbuf[i] = str[(i+1)%16] - 0x30;
sbuf = (unsigned short *)cbuf;
hi = lo = 0;
if (*((char *)&ashort)) // Byte-swapping required
for (i=4; i--;)
{
his = *(sbuf++);
los = *(sbuf++);
hi += (*((unsigned char *)&his)<<8) + *((unsigned char *)&his+1);
lo += (*((unsigned char *)&los)<<8) + *((unsigned char *)&los+1);
}
else
for (i=4; i--;)
{
hi += *(sbuf++);
lo += *(sbuf++);
}
hicarry = hi>>16;
locarry = lo>>16;
while (hicarry || locarry)
{
hi = (hi & 0xffff) + locarry;
lo = (lo & 0xffff) + hicarry;
hicarry = hi >> 16;
locarry = lo >> 16;
}
return (hi<<16) + lo;
}
/****** compute_blocksum *****************************************************
PROTO unsigned int compute_blocksum(char *buf, unsigned int sum)
PURPOSE Compute the checksum of a FITS block (2880 bytes)
INPUT Pointer to the block,
The previous checksum.
OUTPUT The new computed checksum.
NOTES From Seaman & Pence 1995 (ftp://iraf.noao.edu/misc/checksum/). But
contrarily to what is stated by the authors, the original algorithm
depends on the endianity of the machine. The routine below adds
support for ix386-like processors (non-IEEE).
AUTHOR E. Bertin (IAP)
VERSION 11/02/2020
***/
unsigned int compute_blocksum(char *buf, unsigned int sum)
{
unsigned short *sbuf,
his,los,
ashort = 1;
unsigned int hi,lo, hicarry,locarry;
int i;
sbuf = (unsigned short *)buf;
hi = (sum >> 16);
lo = (sum << 16) >> 16;
if (*((char *)&ashort)) // Byte-swapping required
for (i=FBSIZE/4; i--;)
{
his = *(sbuf++);
los = *(sbuf++);
hi += (*((unsigned char *)&his)<<8) + *((unsigned char *)&his+1);
lo += (*((unsigned char *)&los)<<8) + *((unsigned char *)&los+1);
}
else
for (i=FBSIZE/4; i--;)
{
hi += *(sbuf++);
lo += *(sbuf++);
}
hicarry = hi>>16; /* fold carry bits in */
locarry = lo>>16;
while (hicarry || locarry)
{
hi = (hi & 0xFFFF) + locarry;
lo = (lo & 0xFFFF) + hicarry;
hicarry = hi >> 16;
locarry = lo >> 16;
}
return (hi << 16) + lo;
}
/****** compute_bodysum *****************************************************
PROTO unsigned int compute_bodysum(tabstruct *tab, unsigned int sum)
PURPOSE Compute the checksum of a FITS body
INPUT Pointer to the tab,
Checksum from a previous iteration.
OUTPUT The computed checksum.
NOTES -.
AUTHOR E. Bertin (IAP)
VERSION 15/08/2003
***/
unsigned int compute_bodysum(tabstruct *tab, unsigned int sum)
{
catstruct *cat;
char *buf;
KINGSIZE_T size;
int n, nblock;
/* FITS data are generally padded */
nblock = (tab->tabsize+FBSIZE-1)/FBSIZE;
/* 2 cases: either the data are in memory or still on disk */
if (tab->bodybuf)
{
/*-- In memory: they are probably not padded */
buf = (char *)tab->bodybuf;
for (n=nblock-1; n--; buf+=FBSIZE)
sum = compute_blocksum(buf, sum);
if ((size=PADEXTRA(tab->tabsize)))
{
QCALLOC(buf, char, FBSIZE);
size = FBSIZE-size;
memcpy(buf, (char *)tab->bodybuf+tab->tabsize-size, size);
sum = compute_blocksum(buf, sum);
free(buf);
}
}
else
{
/*-- On disk: they are padded */
/*-- We open the file (nothing is done if already open) */
if (!(cat=tab->cat))
{
warning("Cannot access file while computing the checksum in HDU ",
tab->extname);
return 0;
}
open_cat(cat, READ_ONLY);
QFSEEK(cat->file, tab->bodypos, SEEK_SET, cat->filename);
QMALLOC(buf, char, FBSIZE);
for (n=nblock; n--;)
{
QFREAD(buf, FBSIZE, cat->file, cat->filename);
/*---- No need to swap bytes */
sum = compute_blocksum(buf, sum);
}
}
return sum;
}
/****** write_checksum *****************************************************
PROTO void write_checksum(tabstruct *tab)
PURPOSE Compute and write the checksum to a FITS table
INPUT Pointer to the tab.
OUTPUT -.
NOTES -.
AUTHOR E. Bertin (IAP)
VERSION 04/06/2001
***/
void write_checksum(tabstruct *tab)
{
char str[32],
*buf;
unsigned int sum;
int i;
/* Keep some margin */
QREALLOC(tab->headbuf, char, 80*(tab->headnblock*36+3));
/* Add or update keywords in the header */
fitsadd(tab->headbuf, "CHECKSUM", "ASCII 1's complement checksum");
fitswrite(tab->headbuf, "CHECKSUM", "0000000000000000",
H_STRING, T_STRING);
fitsadd(tab->headbuf, "DATASUM ", "Checksum of data records");
fitswrite(tab->headbuf, "DATASUM ", "0", H_STRING, T_STRING);
fitsadd(tab->headbuf, "CHECKVER", "Checksum version ID");
fitswrite(tab->headbuf, "CHECKVER", "COMPLEMENT", H_STRING, T_STRING);
/* Keep only what's necessary */
tab->headnblock = ((fitsfind(tab->headbuf, "END ")+36)*80)/FBSIZE;
QREALLOC(tab->headbuf, char, tab->headnblock*FBSIZE);
/* First: the data */
tab->bodysum = sum = compute_bodysum(tab, 0);
sprintf(str, "%u", sum);
fitswrite(tab->headbuf, "DATASUM ", str, H_STRING, T_STRING);
/* Now the header */
buf = tab->headbuf;
for (i=tab->headnblock; i--; buf+=FBSIZE)
sum = compute_blocksum(buf, sum);
/* Complement to 1 */
encode_checksum(~sum, str);
fitswrite(tab->headbuf, "CHECKSUM", str, H_STRING, T_STRING);
return;
}
/****** verify_checksum *****************************************************
PROTO int verify_checksum(tabstruct *tab)
PURPOSE Compute and check the checksum of a FITS table
INPUT Pointer to the tab.
OUTPUT RETURN_OK if the checksum is correct, RETURN_ERROR if it is
incorrect, or RETURN_FATAL_ERROR if no checksum found.
NOTES -.
AUTHOR E. Bertin (IAP)
VERSION 07/05/2001
***/
int verify_checksum(tabstruct *tab)
{
char *buf;
unsigned int sum;
int i;
if (fitsfind(tab->headbuf, "CHECKSUM")==RETURN_ERROR)
return RETURN_FATAL_ERROR;
/* First: the data */
sum = compute_bodysum(tab, 0);
/* Now the header */
buf = tab->headbuf;
for (i=tab->headnblock; i--; buf+=FBSIZE)
sum = compute_blocksum(buf, sum);
/* The result should sum to 0 */
sum = ~sum;
return sum? RETURN_ERROR : RETURN_OK;
}