
/*
** (c) 2004 Herbert Poetzl
**
** V0.01    fix checksum
**
*/

#define VERSION "V0.01"

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>



static	char	err_flag = 0;

static	char	opt_fix = 0;
static	char	opt_verb = 0;

static	char *	cmd_name = NULL;
static	char *	file_arg = NULL;

static	int 	num_csum = 12;
static	int 	num_offset = 0x1400;




#define OPTIONS "+hvFO:S:"

int 	position(int fd, int offset)
{
    	int r;

	r = lseek(fd, offset, SEEK_SET);
	if (r == -1) {
	    perror(file_arg);
 	    return -1;
	}
    	return 0;
}

int 	buffer_fill(int fd, char *buffer, int size)
{
    	int r;

	r = read(fd, buffer, size);
	if (r == -1) {
	    perror(file_arg);
	}
    	return r;
}


int	main(int argc, char *argv[])
{
        extern int optind;
        extern char *optarg;
        int c, errflg = 0;
	int r, i, fd;


        cmd_name = argv[0];
        while ((c = getopt(argc, argv, OPTIONS)) != EOF) {
            switch (c) {
            case 'h':
                fprintf(stderr,
                    "This is %s " VERSION "\n"
                    "options are:\n"
                    "-h        print this help message\n"
                    "-v        report file type\n"
                    "-F        fix checksum in place\n"
                    "-O <val>  start offset for sum\n"
                    "-S <val>  location of check sum\n"
                    "--        end of options\n"
                    ,cmd_name);
                exit(0);
                break;
            case 'v':
                opt_verb = 1;
                break;
            case 'F':
                opt_fix = 1;
                break;
            case 'O':
                num_offset = atol(optarg);
                break;
            case 'S':
                num_csum = atol(optarg);
                break;
            case '?':
            default:
                errflg++;
                break;
            }
        }
        if (errflg) {
            fprintf(stderr, 
                "Usage: %s -[" OPTIONS "] <path> ...\n"
                "%s -h for help.\n",
                cmd_name, cmd_name);
            exit(2);
        }


        for (; optind < argc; optind++) {
	    char sig[12];
	    unsigned char buffer[256];
	    unsigned long csum, nsum;
	
	    file_arg = argv[optind];
	    
	    fd = open(file_arg, (opt_fix)?O_RDWR:O_RDONLY);
	    if (fd < 0) {
	    	perror(file_arg);
    	    	continue;
	    }
	    
	    
	    r = buffer_fill(fd, sig, 12);
	    if (r != 12)
    	    	continue;
	    
	    if (position(fd, num_csum))
	    	continue;
	    r = buffer_fill(fd, buffer, 4);
	    
 	    for (i=4, csum=0; i>0; )
	    	csum = (csum << 8) + buffer[--i];
	    
 	    printf("%38.38s\t%.12s 0x%08lx ",
	    	file_arg, sig, csum);
	    // if (opt_verb) {}
	    
	    if (position(fd, num_offset))
	    	continue;
	    
	    nsum = 0;
    	    while ((r = buffer_fill(fd, buffer, 
	    	sizeof(buffer))) > 0) {
	    	for (i=0; i<r; i++)
		    nsum += buffer[i];
	    }
	    if (r == -1)
    	    	continue;
	    
	    if (nsum != csum) {
	    	printf("(bad)\n\t\t\t\t\t"
		    "...should be 0x%08lx ", nsum);
	    	if (opt_fix) {
		    if (position(fd, num_csum))
		    	continue;
 		    for (i=0; i<4; i++)
		    	buffer[i] = (nsum >> (8*i)) & 0xff;
   	    	    r = write(fd, buffer, 4);
		    if (r != 4) {
		    	perror(file_arg);
			continue;
		    }
		    printf("(fixed)\n");
		} else {
		    printf("\n");
		}
    	    } else {
		printf("(ok)\n");
	    }  
    	}
	
	exit((err_flag)?1:0);
}

