856 lines
22 KiB
C
856 lines
22 KiB
C
/*----------------------------------------------------------------------------*/
|
|
/**
|
|
@file fitsmd5.c
|
|
@author N. Devillard
|
|
@date May 2001
|
|
@version $Revision: 1.5 $
|
|
@brief Display/Add/Update the DATAMD5 keyword/value
|
|
This is a stand-alone utility. Compile it with any ANSI C compiler:
|
|
% cc -o fitsmd5 fitsmd5.c [optional optimization options]
|
|
*/
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
$Id: fitsmd5.c,v 1.5 2003/07/04 15:08:04 yjung Exp $
|
|
$Author: yjung $
|
|
$Date: 2003/07/04 15:08:04 $
|
|
$Revision: 1.5 $
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Includes
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Support for gzipped files if linked against zlib.
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
#if HAVE_ZLIB
|
|
#include "zlib.h"
|
|
|
|
#define FILE gzFile
|
|
#define fopen gzopen
|
|
#define fclose gzclose
|
|
#define fread(b,s,n,f) gzread(f,b,n*s)
|
|
|
|
#define GZIP_MAGIC1 0x1f
|
|
#define GZIP_MAGIC2 0x8b
|
|
|
|
static
|
|
is_gzipped (char *filename)
|
|
{
|
|
FILE *in;
|
|
unsigned char b1, b2;
|
|
int r1, r2;
|
|
|
|
if ((in = fopen (filename, "r")) == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
r1 = fread (&b1, 1, 1, in);
|
|
r2 = fread (&b2, 1, 1, in);
|
|
fclose (in);
|
|
if (r1 != 1 || r2 != 1)
|
|
return 0;
|
|
if ((int) b1 != GZIP_MAGIC1 || (int) b2 != GZIP_MAGIC2)
|
|
{
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Define
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/* Definitions related to FITS */
|
|
#define FITSLINESZ 80 /* a FITS line is 80 chars */
|
|
#define FITSCARDS 36 /* 36 cards per block */
|
|
#define FITSBLOCKSZ (FITSLINESZ*FITSCARDS) /* FITS block size=2880 */
|
|
|
|
/* Definitions related to MD5 */
|
|
#define MD5HASHSZ 32 /* an MD5 key length is 32 bytes = 128 bits */
|
|
|
|
/* FITS keyword used to store MD5 key */
|
|
#define FITSMD5KEY "DATAMD5 "
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
New types
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/* The following types defined for MD5 computation only */
|
|
typedef unsigned int word32;
|
|
|
|
struct MD5Context
|
|
{
|
|
word32 buf[4];
|
|
word32 bits[2];
|
|
unsigned char in[64];
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Private function prototypes
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
static void MD5Init (struct MD5Context *);
|
|
static void MD5Update (struct MD5Context *, unsigned char *, unsigned);
|
|
static void MD5Final (unsigned char *, struct MD5Context *);
|
|
static void MD5Transform (word32 *, word32 *);
|
|
static void byteReverse (unsigned char *, unsigned);
|
|
|
|
static int fits_md5_check (char *, int);
|
|
static char *fits_pretty_string (char *);
|
|
static char *fits_getvalue (char *);
|
|
|
|
static void usage (void);
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Global variables
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
static char *pname = NULL;
|
|
static char prog_desc[] = "Compute/Update the DATAMD5 keyword/value";
|
|
static int silent_process = 0;
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
MD5 function code
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/* Reverse bytes in a 32-bit word. Harmless on little endian machines */
|
|
static void
|
|
byteReverse (unsigned char *buf, unsigned longs)
|
|
{
|
|
word32 t;
|
|
do
|
|
{
|
|
t = (word32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
|
|
((unsigned) buf[1] << 8 | buf[0]);
|
|
*(word32 *) buf = t;
|
|
buf += 4;
|
|
}
|
|
while (--longs);
|
|
}
|
|
|
|
/* Start MD5 accumulation. Set bit count to 0 and buffer to MD5 init. const. */
|
|
static void
|
|
MD5Init (struct MD5Context *ctx)
|
|
{
|
|
ctx->buf[0] = 0x67452301;
|
|
ctx->buf[1] = 0xefcdab89;
|
|
ctx->buf[2] = 0x98badcfe;
|
|
ctx->buf[3] = 0x10325476;
|
|
ctx->bits[0] = 0;
|
|
ctx->bits[1] = 0;
|
|
}
|
|
|
|
/* Update to reflect the concatenation of another buffer full of bytes. */
|
|
static void
|
|
MD5Update (struct MD5Context *ctx, unsigned char *buf, unsigned len)
|
|
{
|
|
register word32 t;
|
|
|
|
/* Update bitcount */
|
|
t = ctx->bits[0];
|
|
if ((ctx->bits[0] = t + ((word32) len << 3)) < t)
|
|
ctx->bits[1]++; /* Carry from low to high */
|
|
ctx->bits[1] += len >> 29;
|
|
|
|
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
|
|
|
/* Handle any leading odd-sized chunks */
|
|
if (t)
|
|
{
|
|
unsigned char *p = (unsigned char *) ctx->in + t;
|
|
t = 64 - t;
|
|
if (len < t)
|
|
{
|
|
memmove (p, buf, len);
|
|
return;
|
|
}
|
|
memmove (p, buf, t);
|
|
byteReverse (ctx->in, 16);
|
|
MD5Transform (ctx->buf, (word32 *) ctx->in);
|
|
buf += t;
|
|
len -= t;
|
|
}
|
|
/* Process data in 64-byte chunks */
|
|
while (len >= 64)
|
|
{
|
|
memmove (ctx->in, buf, 64);
|
|
byteReverse (ctx->in, 16);
|
|
MD5Transform (ctx->buf, (word32 *) ctx->in);
|
|
buf += 64;
|
|
len -= 64;
|
|
}
|
|
|
|
/* Handle any remaining bytes of data. */
|
|
memmove (ctx->in, buf, len);
|
|
}
|
|
|
|
/* Final wrapup - pad to 64-byte boundary with the bit pattern 1 0* */
|
|
/* (64-bit count of bits processed, MSB-first) */
|
|
static void
|
|
MD5Final (unsigned char digest[16], struct MD5Context *ctx)
|
|
{
|
|
unsigned int count;
|
|
unsigned char *p;
|
|
|
|
/* Compute number of bytes mod 64 */
|
|
count = (ctx->bits[0] >> 3) & 0x3F;
|
|
|
|
/* Set the first char of padding to 0x80. This is safe since there is
|
|
always at least one byte free */
|
|
p = ctx->in + count;
|
|
*p++ = 0x80;
|
|
|
|
/* Bytes of padding needed to make 64 bytes */
|
|
count = 64 - 1 - count;
|
|
|
|
/* Pad out to 56 mod 64 */
|
|
if (count < 8)
|
|
{
|
|
/* Two lots of padding: Pad the first block to 64 bytes */
|
|
memset (p, 0, count);
|
|
byteReverse (ctx->in, 16);
|
|
MD5Transform (ctx->buf, (word32 *) ctx->in);
|
|
|
|
/* Now fill the next block with 56 bytes */
|
|
memset (ctx->in, 0, 56);
|
|
}
|
|
else
|
|
{
|
|
/* Pad block to 56 bytes */
|
|
memset (p, 0, count - 8);
|
|
}
|
|
byteReverse (ctx->in, 14);
|
|
|
|
/* Append length in bits and transform */
|
|
((word32 *) ctx->in)[14] = ctx->bits[0];
|
|
((word32 *) ctx->in)[15] = ctx->bits[1];
|
|
|
|
MD5Transform (ctx->buf, (word32 *) ctx->in);
|
|
byteReverse ((unsigned char *) ctx->buf, 4);
|
|
memmove (digest, ctx->buf, 16);
|
|
memset (ctx, 0, sizeof (ctx)); /* In case it's sensitive */
|
|
}
|
|
|
|
/* The four core functions - F1 is optimized somewhat */
|
|
|
|
/* #define F1(x, y, z) (x & y | ~x & z) */
|
|
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
|
#define F2(x, y, z) F1(z, x, y)
|
|
#define F3(x, y, z) (x ^ y ^ z)
|
|
#define F4(x, y, z) (y ^ (x | ~z))
|
|
|
|
/* This is the central step in the MD5 algorithm. */
|
|
#define MD5STEP(f, w, x, y, z, data, s) \
|
|
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
|
|
|
/*
|
|
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
|
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
|
* the data and converts bytes into longwords for this routine.
|
|
*/
|
|
static void
|
|
MD5Transform (word32 buf[4], word32 in[16])
|
|
{
|
|
register word32 a, b, c, d;
|
|
|
|
a = buf[0];
|
|
b = buf[1];
|
|
c = buf[2];
|
|
d = buf[3];
|
|
|
|
MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
|
MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
|
MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17);
|
|
MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
|
MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
|
MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
|
MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
|
MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
|
MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
|
MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
|
MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
|
MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
|
MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
|
MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
|
MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
|
MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
|
|
|
MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
|
MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
|
MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
|
MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
|
MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
|
MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9);
|
|
MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
|
MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
|
MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
|
MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
|
MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
|
MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
|
MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
|
MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
|
MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
|
MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
|
|
|
MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
|
MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
|
MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
|
MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
|
MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
|
MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
|
MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
|
MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
|
MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
|
MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
|
MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
|
MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
|
MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
|
MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
|
MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
|
MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
|
|
|
MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
|
MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
|
MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
|
MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
|
MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
|
MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
|
MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
|
MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
|
MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
|
MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
|
MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
|
MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
|
MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
|
MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
|
MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
|
MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
|
|
|
buf[0] += a;
|
|
buf[1] += b;
|
|
buf[2] += c;
|
|
buf[3] += d;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
FITS-related functions
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/* Pretty-print a FITS string value */
|
|
static char *
|
|
fits_pretty_string (char *s)
|
|
{
|
|
static char pretty[FITSLINESZ + 1];
|
|
int i, j;
|
|
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
pretty[0] = (char) 0;
|
|
if (s[0] != '\'')
|
|
return s;
|
|
|
|
/* skip first quote */
|
|
i = 1;
|
|
j = 0;
|
|
/* trim left-side blanks */
|
|
while (s[i] == ' ')
|
|
{
|
|
if (i == (int) strlen (s))
|
|
break;
|
|
i++;
|
|
}
|
|
if (i >= (int) (strlen (s) - 1))
|
|
return pretty;
|
|
/* copy string, changing double quotes to single ones */
|
|
while (i < (int) strlen (s))
|
|
{
|
|
if (s[i] == '\'')
|
|
i++;
|
|
pretty[j] = s[i];
|
|
i++;
|
|
j++;
|
|
}
|
|
/* NULL-terminate the pretty string */
|
|
pretty[j + 1] = (char) 0;
|
|
/* trim right-side blanks */
|
|
j = (int) strlen (pretty) - 1;
|
|
while (pretty[j] == ' ')
|
|
j--;
|
|
pretty[j + 1] = (char) 0;
|
|
return pretty;
|
|
}
|
|
|
|
/* Get the FITS value in a FITS card */
|
|
static char *
|
|
fits_getvalue (char *line)
|
|
{
|
|
static char value[FITSLINESZ + 1];
|
|
int from, to;
|
|
int inq;
|
|
int i;
|
|
|
|
if (line == NULL)
|
|
return NULL;
|
|
memset (value, 0, FITSLINESZ + 1);
|
|
/* Get past the keyword */
|
|
i = 0;
|
|
while (line[i] != '=' && i < FITSLINESZ)
|
|
i++;
|
|
if (i > FITSLINESZ)
|
|
return NULL;
|
|
i++;
|
|
while (line[i] == ' ' && i < FITSLINESZ)
|
|
i++;
|
|
if (i > FITSLINESZ)
|
|
return NULL;
|
|
from = i;
|
|
/* Now in the value section */
|
|
/* Look for the first slash '/' outside of a string */
|
|
inq = 0;
|
|
while (i < FITSLINESZ)
|
|
{
|
|
if (line[i] == '\'')
|
|
inq = !inq;
|
|
if (line[i] == '/')
|
|
if (!inq)
|
|
break;
|
|
i++;
|
|
}
|
|
i--;
|
|
/* Backtrack on blanks */
|
|
while (line[i] == ' ' && i >= 0)
|
|
i--;
|
|
if (i < 0)
|
|
return NULL;
|
|
to = i;
|
|
if (to < from)
|
|
return NULL;
|
|
/* Copy relevant characters into output buffer */
|
|
strncpy (value, line + from, to - from + 1);
|
|
/* Null-terminate the string */
|
|
value[to - from + 1] = (char) 0;
|
|
/*
|
|
* Make it pretty: remove head and tail quote, change double
|
|
* quotes to simple ones.
|
|
*/
|
|
strcpy (value, fits_pretty_string (value));
|
|
return value;
|
|
}
|
|
|
|
/* Replace the MD5 card in the input header */
|
|
static int
|
|
fits_replace_card (char *filename, int off_md5, char *datamd5)
|
|
{
|
|
char *buf;
|
|
int fd;
|
|
struct stat sta;
|
|
char card[FITSLINESZ];
|
|
int i;
|
|
int err;
|
|
|
|
/* Get file size */
|
|
if (stat (filename, &sta) == -1)
|
|
{
|
|
fprintf (stderr, "%s: cannot stat file [%s]: no update done\n",
|
|
pname, filename);
|
|
return 1;
|
|
}
|
|
/* Open file */
|
|
fd = open (filename, O_RDWR);
|
|
if (fd == -1)
|
|
{
|
|
fprintf (stderr,
|
|
"%s: cannot open file [%s] for modification: no update done\n",
|
|
pname, filename);
|
|
return 1;
|
|
}
|
|
/* Memory-map the file */
|
|
buf = (char *) mmap (0,
|
|
sta.st_size,
|
|
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (buf == (char *) -1 || buf == NULL)
|
|
{
|
|
perror ("mmap");
|
|
close (fd);
|
|
return 1;
|
|
}
|
|
/* sprintf should be safe, the MD5 signature size is 32 chars */
|
|
sprintf (card, "%s= '%s' / data MD5 signature", FITSMD5KEY, datamd5);
|
|
i = FITSLINESZ - 1;
|
|
while (card[i] != 'e')
|
|
{
|
|
card[i] = ' ';
|
|
i--;
|
|
}
|
|
/* Copy card into file */
|
|
memcpy (buf + off_md5, card, FITSLINESZ);
|
|
/* flush output, unmap buffer, close file and quit */
|
|
err = 0;
|
|
sync ();
|
|
if (close (fd) == -1)
|
|
{
|
|
fprintf (stderr, "%s: error closing modified file [%s]",
|
|
pname, filename);
|
|
err++;
|
|
}
|
|
if (munmap (buf, sta.st_size) == -1)
|
|
{
|
|
perror ("munmap");
|
|
err++;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/* Display or modify the DATAMD5 value. Returns the number of errors. */
|
|
static int
|
|
fits_md5_check (char *filename, int update_header)
|
|
{
|
|
FILE *in;
|
|
char buf[FITSBLOCKSZ];
|
|
char *buf_c;
|
|
int i;
|
|
int in_header;
|
|
char *hdrmd5;
|
|
struct MD5Context ctx;
|
|
unsigned char digest[16];
|
|
static char datamd5[MD5HASHSZ + 1];
|
|
int off_md5;
|
|
int cur_off;
|
|
int md5keysz;
|
|
int err;
|
|
int check_fits;
|
|
struct stat sta;
|
|
|
|
if (filename == NULL)
|
|
return 1;
|
|
|
|
/* Try to stat file */
|
|
if (stat (filename, &sta) != 0)
|
|
{
|
|
fprintf (stderr, "%s: cannot stat file %s\n", pname, filename);
|
|
return 1;
|
|
}
|
|
/* See if this is a regular file */
|
|
if (!S_ISREG (sta.st_mode))
|
|
{
|
|
fprintf (stderr, "%s: not a regular file: %s\n", pname, filename);
|
|
return 1;
|
|
}
|
|
/* Open input file */
|
|
if ((in = fopen (filename, "r")) == NULL)
|
|
{
|
|
fprintf (stderr, "%s: cannot open file [%s]\n", pname, filename);
|
|
return 1;
|
|
}
|
|
/* Initialize all variables */
|
|
MD5Init (&ctx);
|
|
in_header = 1;
|
|
hdrmd5 = NULL;
|
|
off_md5 = 0;
|
|
cur_off = 0;
|
|
md5keysz = (int) strlen (FITSMD5KEY);
|
|
check_fits = 0;
|
|
/* Loop over input file */
|
|
while (fread (buf, 1, FITSBLOCKSZ, in) == FITSBLOCKSZ)
|
|
{
|
|
/* First time in the loop: check the file is FITS */
|
|
if (check_fits == 0)
|
|
{
|
|
/* Examine first characters in block */
|
|
if (buf[0] != 'S' ||
|
|
buf[1] != 'I' ||
|
|
buf[2] != 'M' ||
|
|
buf[3] != 'P' ||
|
|
buf[4] != 'L' ||
|
|
buf[5] != 'E' ||
|
|
buf[6] != ' ' || buf[7] != ' ' || buf[8] != '=')
|
|
{
|
|
fprintf (stderr, "%s: file [%s] is not FITS\n",
|
|
pname, filename);
|
|
fclose (in);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
check_fits = 1;
|
|
}
|
|
}
|
|
/* If current block is a header block */
|
|
if (in_header)
|
|
{
|
|
buf_c = buf;
|
|
for (i = 0; i < FITSCARDS; i++)
|
|
{
|
|
/* Try to locate MD5 keyword if not located already */
|
|
if (hdrmd5 == NULL)
|
|
{
|
|
if (!strncmp (buf_c, FITSMD5KEY, md5keysz))
|
|
{
|
|
hdrmd5 = fits_getvalue (buf_c);
|
|
off_md5 = cur_off;
|
|
}
|
|
}
|
|
/* Try to locate an END key */
|
|
if (buf_c[0] == 'E' &&
|
|
buf_c[1] == 'N' && buf_c[2] == 'D' && buf_c[3] == ' ')
|
|
{
|
|
in_header = 0;
|
|
break;
|
|
}
|
|
buf_c += FITSLINESZ;
|
|
cur_off += FITSLINESZ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If current block is a data block */
|
|
/* Try to locate an extension header */
|
|
if (buf[0] == 'X' &&
|
|
buf[1] == 'T' &&
|
|
buf[2] == 'E' &&
|
|
buf[3] == 'N' &&
|
|
buf[4] == 'S' &&
|
|
buf[5] == 'I' &&
|
|
buf[6] == 'O' && buf[7] == 'N' && buf[8] == '=')
|
|
{
|
|
in_header = 1;
|
|
buf_c = buf;
|
|
for (i = 0; i < FITSCARDS; i++)
|
|
{
|
|
/* Try to find an END marker in this block */
|
|
if (buf_c[0] == 'E' &&
|
|
buf_c[1] == 'N' && buf_c[2] == 'D' && buf_c[3] == ' ')
|
|
{
|
|
/* Found END marker in same block as XTENSION */
|
|
in_header = 0;
|
|
break;
|
|
}
|
|
buf_c += FITSLINESZ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Data block: accumulate for MD5 */
|
|
MD5Update (&ctx, (unsigned char *) buf, FITSBLOCKSZ);
|
|
}
|
|
}
|
|
}
|
|
fclose (in);
|
|
if (check_fits == 0)
|
|
{
|
|
/* Never went through the read loop: file is not FITS */
|
|
fprintf (stderr, "%s: file [%s] is not FITS\n", pname, filename);
|
|
return 1;
|
|
}
|
|
/* Got to the end of file: summarize */
|
|
MD5Final (digest, &ctx);
|
|
|
|
/* Write digest into a string */
|
|
sprintf (datamd5,
|
|
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
|
digest[0], digest[1], digest[2], digest[3], digest[4],
|
|
digest[5], digest[6], digest[7], digest[8], digest[9],
|
|
digest[10], digest[11], digest[12], digest[13], digest[14],
|
|
digest[15]);
|
|
if (!silent_process)
|
|
{
|
|
printf ("%s %s", datamd5, filename);
|
|
if (hdrmd5)
|
|
{
|
|
if (!strcmp (hdrmd5, datamd5))
|
|
{
|
|
printf (" (header Ok)");
|
|
}
|
|
else
|
|
{
|
|
printf (" (header is wrong)");
|
|
}
|
|
}
|
|
printf ("\n");
|
|
}
|
|
/* Update header if requested */
|
|
err = 0;
|
|
if (update_header)
|
|
{
|
|
if (hdrmd5 == NULL)
|
|
{
|
|
fprintf (stderr, "%s: cannot update header: missing %s\n",
|
|
pname, FITSMD5KEY);
|
|
return 1;
|
|
}
|
|
#if HAVE_ZLIB
|
|
if (is_gzipped (filename))
|
|
{
|
|
fprintf (stderr, "%s: cannot update header in gzipped file\n");
|
|
return 1;
|
|
}
|
|
#endif
|
|
err = fits_replace_card (filename, off_md5, datamd5);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
/* Compute MD5 sum on the whole file and print out results on stdout */
|
|
static int
|
|
compute_md5 (char *filename)
|
|
{
|
|
struct MD5Context ctx;
|
|
unsigned char digest[16];
|
|
struct stat sta;
|
|
int fd;
|
|
unsigned char *buf;
|
|
|
|
/* Try to stat file */
|
|
if (stat (filename, &sta) != 0)
|
|
{
|
|
fprintf (stderr, "%s: cannot stat file %s\n", pname, filename);
|
|
return 1;
|
|
}
|
|
/* See if this is a regular file */
|
|
if (!S_ISREG (sta.st_mode))
|
|
{
|
|
fprintf (stderr, "%s: not a regular file: %s\n", pname, filename);
|
|
return 1;
|
|
}
|
|
/* Open file */
|
|
if ((fd = open (filename, O_RDONLY)) == -1)
|
|
{
|
|
fprintf (stderr, "%s: cannot open file %s\n", pname, filename);
|
|
return 1;
|
|
}
|
|
/* Memory-map the file */
|
|
buf =
|
|
(unsigned char *) mmap (0, sta.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if (buf == (unsigned char *) -1 || buf == NULL)
|
|
{
|
|
perror ("mmap");
|
|
close (fd);
|
|
return 1;
|
|
}
|
|
/* Initialize MD5 context */
|
|
MD5Init (&ctx);
|
|
/* Compute MD5 on all bits in the file */
|
|
MD5Update (&ctx, buf, sta.st_size);
|
|
/* Finalize and print results */
|
|
close (fd);
|
|
munmap ((char *) buf, sta.st_size);
|
|
MD5Final (digest, &ctx);
|
|
printf
|
|
("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x %s\n",
|
|
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5],
|
|
digest[6], digest[7], digest[8], digest[9], digest[10], digest[11],
|
|
digest[12], digest[13], digest[14], digest[15], filename);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
usage (void)
|
|
{
|
|
printf ("%s -- %s\n" "version: $Revision: 1.5 $\n", pname, prog_desc);
|
|
printf ("\n"
|
|
"use : %s [-u] [-s] [-a] <FITS files...>\n"
|
|
"options are:\n"
|
|
"\t-u update MD5 keyword in the file: %s\n"
|
|
"\t-s silent mode\n"
|
|
"\n"
|
|
"\t-a compute MD5 sum of the complete file (incl.header)\n"
|
|
"\n"
|
|
"This utility computes the MD5 checksum of all data sections\n"
|
|
"in a given FITS file, and compares it against the value\n"
|
|
"declared in DATAMD5 if present. It can also update the value\n"
|
|
"of this keyword (if present) with its own computed MD5 sum.\n"
|
|
"\n", pname, FITSMD5KEY);
|
|
printf ("You can also use it with the -a option to compute the MD5 sum\n"
|
|
"on the complete file (all bits). In this case, the file needs\n"
|
|
"not be FITS. This option is only provided to check this program\n"
|
|
"against other MD5 computation tools.\n"
|
|
"NB: Other options cannot be used together with -a.\n" "\n");
|
|
|
|
#if HAVE_ZLIB
|
|
printf ("\n"
|
|
"This program was compiled against zlib %s\n"
|
|
"which allows to process gzipped FITS files\n"
|
|
"as if they were normal FITS files.\n"
|
|
"Notice that you cannot use the -u option on\n"
|
|
"gzipped files, though.\n" "\n" "\n", ZLIB_VERSION);
|
|
#endif
|
|
exit (0);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Main
|
|
-----------------------------------------------------------------------------*/
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
int i;
|
|
int update_header;
|
|
int total_md5;
|
|
int err;
|
|
|
|
/* Initialize */
|
|
pname = argv[0];
|
|
update_header = 0;
|
|
total_md5 = 0;
|
|
|
|
if (argc < 2)
|
|
usage ();
|
|
|
|
/* Parse arguments for options */
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (!strcmp (argv[i], "-u"))
|
|
{
|
|
update_header = 1;
|
|
}
|
|
else if (!strcmp (argv[i], "-s"))
|
|
{
|
|
silent_process = 1;
|
|
}
|
|
else if (!strcmp (argv[i], "-a"))
|
|
{
|
|
total_md5 = 1;
|
|
}
|
|
}
|
|
/* Loop on input file names */
|
|
err = 0;
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
/* If not a command-line option */
|
|
if (strcmp (argv[i], "-u") &&
|
|
strcmp (argv[i], "-s") && strcmp (argv[i], "-a"))
|
|
{
|
|
/* Launch MD5 process on this file */
|
|
if (total_md5)
|
|
{
|
|
err += compute_md5 (argv[i]);
|
|
}
|
|
else
|
|
{
|
|
err += fits_md5_check (argv[i], update_header);
|
|
}
|
|
}
|
|
}
|
|
if (err > 0)
|
|
{
|
|
fprintf (stderr, "%s: %d error(s) during process\n", pname, err);
|
|
}
|
|
return err;
|
|
}
|