/* ovw - file over write utility */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <math.h>

typedef short I16;
typedef int   I32;
typedef unsigned int U32;
typedef unsigned short U16;

void ConvertToIeeeExtended(double num, char* bytes);
double ConvertFromIeeeExtended(unsigned char* bytes);
int atoint(const char* x);
void pack(int fd, const char* format, char** values, int n);


void usage(void)
{
    fputs("Usage: ovw filename address format value [ value ... ]\n", stderr);
    fputs("format:\n", stderr);

    fputs("A	An ascii string, will be space padded.\n", stderr);
    fputs("a	An ascii string, will be null padded.\n", stderr);
/*    fputs("b	A bit string (ascending bit order, like vec()).\n", stderr); */
/*    fputs("B	A bit string (descending bit order).\n", stderr); */
/*    fputs("h	A hex string (low nybble first).\n", stderr); */
/*    fputs("H	A hex string (high nybble first).\n", stderr); */
    fputs("\n", stderr);
    fputs("c	A signed char value.\n", stderr);
    fputs("C	An unsigned char value.\n", stderr);
/*    fputs("s	A signed short value.\n", stderr); */
/*    fputs("S	An unsigned short value.\n", stderr); */
/*    fputs("i	A signed integer value.\n", stderr); */
/*    fputs("I	An unsigned integer value.\n", stderr); */
/*    fputs("l	A signed long value.\n", stderr); */
/*    fputs("L	An unsigned long value.\n", stderr); */
    fputs("\n", stderr);
    fputs("n	A short in \"network\" order.\n", stderr);
    fputs("N	A long in \"network\" order.\n", stderr);
    fputs("v	A short in \"VAX\" (little-endian) order.\n", stderr);
    fputs("V	A long in \"VAX\" (little-endian) order.\n", stderr);
    fputs("\n", stderr);
/*    fputs("f	A single-precision float in the native format.\n", stderr); */
/*    fputs("d	A double-precision float in the native format.\n", stderr); */
/*    fputs("\n", stderr); */
/*    fputs("p	A pointer to a null-terminated string.\n", stderr); */
/*    fputs("P	A pointer to a structure (fixed-length string).\n", stderr); */
/*    fputs("\n", stderr); */
/*    fputs("u	A uuencoded string.\n", stderr); */
/*    fputs("\n", stderr); */
    fputs("x	A null byte.\n", stderr);
    fputs("X	Back up a byte.\n", stderr);
    fputs("@	Null fill to absolute position.\n", stderr);
}


int main(int argc, char** argv)
{
    int fd;
    int addr;
    char* fname;
    char* format;
    int whence;

    if(argc < 5)
    {
	usage();
	return 1;
    }

    fname = argv[1];

    addr = atoint(argv[2]);
    if(argv[2][0] == '-' || addr < 0)
	whence = SEEK_END;
    else
	whence = SEEK_CUR;

    format = argv[3];

    argc -= 4;
    argv += 4;

    if((fd = open(fname, O_RDWR | O_CREAT, 0644)) < 0)
    {
	perror(fname);
	return 1;
    }

    if(whence == SEEK_END)
	lseek(fd, addr, SEEK_END);
    else
	lseek(fd, addr, SEEK_SET);

    pack(fd, format, argv, argc);
    return 0;
}

int atoint(const char* x)
{
    int sig;
    int value;

    value = 0;
    while(*x != '-' && !isdigit(*x))
	x++;
    if(*x == '-')
    {
	sig = 1;
	x++;
    }
    else
	sig = 1;

    if(*x == '0')
    {
	if(*(x + 1) == 'x' || *(x + 1) == 'X') /* 0x... 0X... */
	    return sig * strtol(x, NULL, 16);
	return sig * strtol(x, NULL, 8);
    }
    return sig * atoi(x);
}


void pack(int fd, const char* pat, char** values, int items)
{
    int len;
    int datumtype;
    char* formstr;
    int formlen;

    I16 ashort;
    U32 aulong;
    double adouble;
    char ieee_buff[10];

    while(items > 0 && *pat)
    {
	datumtype = *pat++;
	if(isspace(datumtype))
	    continue;
	if(*pat == '*')
	{
	    len = strchr("@Xxu", datumtype) ? 0 : items;
	    pat++;
	}
	else if(isdigit(*pat))
	{
	    len = *pat++ - '0';
	    while(isdigit(*pat))
		len = (len * 10) + (*pat++ - '0');
	}
	else
	    len = 1;
	switch(datumtype)
	{
	  default:
	    fprintf(stderr, "Invalid type in pack: '%c'", datumtype);
	    return;
/*	case '@': */
	  case 'X':
	    lseek(fd, -len, SEEK_CUR);
	    break;
	  case 'x':
	    while(len-- > 0)
		write(fd, "\0", 1);
	    break;
	  case 'A':
	  case 'a':
	    formstr = *values++; items--;
	    formlen = strlen(formstr);
	    if(pat[-1] == '*')
		len = formlen;
	    if(formlen >= len)
		write(fd, formstr, len);
	    else
	    {
		write(fd, formstr, formlen);
		len -= formlen;
		if(datumtype == 'a')
		    while(len-- > 0)
			write(fd, "\0", 1);
		else
		    while(len-- > 0)
			write(fd, " ", 1);
	    }
	    break;
/*	case 'B': */
/*	case 'b': */
/*	case 'H': */
/*	case 'h': */
	  case 'C':
	  case 'c':
	    while(len-- > 0)
	    {
		char c;
		formstr = *values++; items--;
		c = atoint(formstr);
		write(fd, &c, 1);
	    }
	    break;
/*	case 'f': */
/*	case 'F': */
/*	case 'd': */
/*	case 'D': */
	  case 'n':
	    while(len-- > 0)
	    {
		formstr = *values++; items--;
		ashort = atoint(formstr);
		ashort = htons(ashort);
		write(fd, &ashort, 2);
	    }
	    break;
	  case 'v':
	    while(len-- > 0)
	    {
		formstr = *values++; items--;
		ashort = atoint(formstr);
		ashort = htons(ashort);
		ashort = (((U16)ashort)<<8 | ((U16)ashort)>>8);
		write(fd, &ashort, 2);
	    }
	    break;
/*	case 'S': */
/*	case 's': */
/*	case 'I': */
/*	case 'w': */
/*	case 'i': */
	  case 'N':
	    while(len-- > 0)
	    {
		formstr = *values++; items--;
		aulong = atoint(formstr);
		aulong = htonl(aulong);
		write(fd, &aulong, 4);
	    }
	    break;
	  case 'V':
	    while(len-- > 0)
	    {
		formstr = *values++; items--;
		aulong = atoint(formstr);
		aulong = htons(aulong);
		aulong = ((aulong>>24) |
			  ((aulong&0x00ff0000)>>8) |
			  ((aulong&0x0000ff00)<<8) |
			  (aulong<<24));
		write(fd, &aulong, 4);
	    }
	    break;
/*	case 'L': */
/*	case 'l': */
/*	case 'Q': */
/*	case 'q': */
/*	case 'P': */
/*	case 'p': */
/*	case 'u': */
	  case 'E': /* IEEE Extended */
	    while(len-- > 0)
	    {
		formstr = *values++; items--;
		adouble = atof(formstr);
		ConvertToIeeeExtended(adouble, ieee_buff);
		write(fd, ieee_buff, 10);
	    }
	    break;
	}
    }
}



/*
 * C O N V E R T   T O   I E E E   E X T E N D E D
 */

/* Copyright (C) 1988-1991 Apple Computer, Inc.
 * All rights reserved.
 *
 * Machine-independent I/O routines for IEEE floating-point numbers.
 *
 * NaN's and infinities are converted to HUGE_VAL or HUGE, which
 * happens to be infinity on IEEE machines.  Unfortunately, it is
 * impossible to preserve NaN's in a machine-independent way.
 * Infinities are, however, preserved on IEEE machines.
 *
 * These routines have been tested on the following machines:
 *    Apple Macintosh, MPW 3.1 C compiler
 *    Apple Macintosh, THINK C compiler
 *    Silicon Graphics IRIS, MIPS compiler
 *    Cray X/MP and Y/MP
 *    Digital Equipment VAX
 *
 *
 * Implemented by Malcolm Slaney and Ken Turkowski.
 *
 * Malcolm Slaney contributions during 1988-1990 include big- and little-
 * endian file I/O, conversion to and from Motorola's extended 80-bit
 * floating-point format, and conversions to and from IEEE single-
 * precision floating-point format.
 *
 * In 1991, Ken Turkowski implemented the conversions to and from
 * IEEE double-precision format, added more precision to the extended
 * conversions, and accommodated conversions involving +/- infinity,
 * NaN's, and denormalized numbers.
 */

#ifndef HUGE_VAL
# define HUGE_VAL HUGE
#endif /*HUGE_VAL*/

# define FloatToUnsigned(f)      ((unsigned long)(((long)(f - 2147483648.0)) + 2147483647L) + 1)

void ConvertToIeeeExtended(double num, char* bytes)
{
    int    sign;
    int expon;
    double fMant, fsMant;
    unsigned long hiMant, loMant;

    if (num < 0) {
        sign = 0x8000;
        num *= -1;
    } else {
        sign = 0;
    }

    if (num == 0) {
        expon = 0; hiMant = 0; loMant = 0;
    }
    else {
        fMant = frexp(num, &expon);
        if ((expon > 16384) || !(fMant < 1)) {    /* Infinity or NaN */
            expon = sign|0x7FFF; hiMant = 0; loMant = 0; /* infinity */
        }
        else {    /* Finite */
            expon += 16382;
            if (expon < 0) {    /* denormalized */
                fMant = ldexp(fMant, expon);
                expon = 0;
            }
            expon |= sign;
            fMant = ldexp(fMant, 32);          
            fsMant = floor(fMant); 
            hiMant = FloatToUnsigned(fsMant);
            fMant = ldexp(fMant - fsMant, 32); 
            fsMant = floor(fMant); 
            loMant = FloatToUnsigned(fsMant);
        }
    }
    
    bytes[0] = expon >> 8;
    bytes[1] = expon;
    bytes[2] = hiMant >> 24;
    bytes[3] = hiMant >> 16;
    bytes[4] = hiMant >> 8;
    bytes[5] = hiMant;
    bytes[6] = loMant >> 24;
    bytes[7] = loMant >> 16;
    bytes[8] = loMant >> 8;
    bytes[9] = loMant;
}


/*
 * C O N V E R T   F R O M   I E E E   E X T E N D E D  
 */

/* 
 * Copyright (C) 1988-1991 Apple Computer, Inc.
 * All rights reserved.
 *
 * Machine-independent I/O routines for IEEE floating-point numbers.
 *
 * NaN's and infinities are converted to HUGE_VAL or HUGE, which
 * happens to be infinity on IEEE machines.  Unfortunately, it is
 * impossible to preserve NaN's in a machine-independent way.
 * Infinities are, however, preserved on IEEE machines.
 *
 * These routines have been tested on the following machines:
 *    Apple Macintosh, MPW 3.1 C compiler
 *    Apple Macintosh, THINK C compiler
 *    Silicon Graphics IRIS, MIPS compiler
 *    Cray X/MP and Y/MP
 *    Digital Equipment VAX
 *
 *
 * Implemented by Malcolm Slaney and Ken Turkowski.
 *
 * Malcolm Slaney contributions during 1988-1990 include big- and little-
 * endian file I/O, conversion to and from Motorola's extended 80-bit
 * floating-point format, and conversions to and from IEEE single-
 * precision floating-point format.
 *
 * In 1991, Ken Turkowski implemented the conversions to and from
 * IEEE double-precision format, added more precision to the extended
 * conversions, and accommodated conversions involving +/- infinity,
 * NaN's, and denormalized numbers.
 */

#ifndef HUGE_VAL
# define HUGE_VAL HUGE
#endif /*HUGE_VAL*/

# define UnsignedToFloat(u)         (((double)((long)(u - 2147483647L - 1))) + 2147483648.0)

/****************************************************************
 * Extended precision IEEE floating-point conversion routine.
 ****************************************************************/

double ConvertFromIeeeExtended(unsigned char* bytes /* LCN */)
{
    double    f;
    int    expon;
    unsigned long hiMant, loMant;
    
    expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
    hiMant    =    ((unsigned long)(bytes[2] & 0xFF) << 24)
            |    ((unsigned long)(bytes[3] & 0xFF) << 16)
            |    ((unsigned long)(bytes[4] & 0xFF) << 8)
            |    ((unsigned long)(bytes[5] & 0xFF));
    loMant    =    ((unsigned long)(bytes[6] & 0xFF) << 24)
            |    ((unsigned long)(bytes[7] & 0xFF) << 16)
            |    ((unsigned long)(bytes[8] & 0xFF) << 8)
            |    ((unsigned long)(bytes[9] & 0xFF));

    if (expon == 0 && hiMant == 0 && loMant == 0) {
        f = 0;
    }
    else {
        if (expon == 0x7FFF) {    /* Infinity or NaN */
            f = HUGE_VAL;
        }
        else {
            expon -= 16383;
            f  = ldexp(UnsignedToFloat(hiMant), expon-=31);
            f += ldexp(UnsignedToFloat(loMant), expon-=32);
        }
    }

    if (bytes[0] & 0x80)
        return -f;
    else
        return f;
}

