/* ========================== C MeatAxe =============================
   conv.c - Convert machine dependent binary files.

   (C) Copyright 1993 Michael Ringe, Lehrstuhl D fuer Mathematik,
   RWTH Aachen, Germany  <mringe@tiffy.math.rwth-aachen.de>
   This program is free software; see the file COPYING for details.
   ================================================================== */


/* $Id: conv.c,v 2.4 1994/03/26 06:33:05 mringe Exp $
 *
 * $Log: conv.c,v $
 * Revision 2.4  1994/03/26  06:33:05  mringe
 * Wieder auf die alte Version zur"uckgegriffen,
 * 2.3 lief nicht auf 64-Bit-Maschinen.
 *
 * Revision 2.2  1994/02/15  13:39:15  mringe
 * Benutze os_fseek().
 *
 * Revision 2.1  1994/02/13  18:26:56  mringe
 * Neu: os.c, os.h.
 *
 * Revision 2.0  1993/10/14  18:54:18  mringe
 * MeatAxe-2.0, Phase I
 *
 * Revision 1.17  1993/10/11  19:05:28  mringe
 * Neue Library-Struktur.
 *
 * Revision 1.16  1993/10/06  04:41:05  mringe
 * utils Library eliminiert.
 *
 * Revision 1.15  1993/08/14  11:50:26  mringe
 * Test des Fileheaders verbessert.
 *
 * Revision 1.14  1993/08/10  14:29:19  mringe
 * Include string.h
 *
 * Revision 1.13  1993/08/10  06:51:38  mringe
 * Hole SEEK_xxx aus unistd.h, falls nicht definiert.
 *
 * Revision 1.12  1993/08/06  14:01:59  mringe
 * Neuer File-header.
 *
 * Revision 1.11  1993/07/28  13:37:40  mringe
 * *** empty log message ***
 *
 * Revision 1.10  1993/07/23  13:46:27  mringe
 * OS-Symbole neu (SYS_xxx)
 *
 * Revision 1.9  1993/06/09  18:36:53  mringe
 * singed/unsigned Bug behoben.
 *
 * Revision 1.8  1993/06/09  13:37:27  mringe
 * Bug beim Erkennen der richtigen Bytefolge behoben.
 *
 * Revision 1.7  1993/05/18  12:17:50  mringe
 * Neues Fileformat. Benutze zreadlong(). Konvertiere nur fuer die
 *  Maschine, auf dem das Programm laeuft.
 *
 * Revision 1.6  1993/02/16  18:32:46  mringe
 * string.h und stdio.h werden jetzt in meataxe.h included.
 *
 * Revision 1.5  1993/02/16  17:33:20  mringe
 * Symbole SYS_BSD und SYS_SYSV eingefuehrt.
 *
 * Revision 1.4  1992/11/19  09:47:44  mringe
 * Diverse kleine "Anderungen; Neu: Option -c
 *
 * Revision 1.3  1992/11/19  09:34:26  mringe
 * Kleine "Anderungen, help().
 *
 */


#include <stdlib.h>
#include "meataxe.h"



#define BUFSIZE 4096


/* ------------------------------------------------------------------
   Function prototypes
   ------------------------------------------------------------------ */

static long swapl _PL((long l));
static int examine _PL((char *buf, long wordsize, long n));
static void intcopy _PL((long *dest, char *src, long wordsize,
	int swap, long n));
static int chkhdr _PL((long *hdr));
static int convert _PL((void));
static int convmatrix _PL((void));
static int convperm _PL((void));

/* ------------------------------------------------------------------
   Global data
   ------------------------------------------------------------------ */

static char filename[100];
static char tmpname[100] = "conv.tmp";
static FILE *f, *tmp;
static long flen;
static long hdr[3];

static char *helptext[] = {
"SYNTAX",
"    conv [<Options>] <File> ...",
"",
"OPTIONS",
"",
"FILES",
"    <File> is any MeatAxe binary data file (matrix or permutation).",
"    The converted file replaces the old one.",
NULL};


static proginfo_t pinfo =
   { "conv", "Convert Binary Files", "$Revision: 2.4 $", helptext };





/* ------------------------------------------------------------------
   swapl() - Swap bytes in a long integer
   ------------------------------------------------------------------ */

static long swapl(l)
long l;

{
    unsigned char a,b,c,d;

    a = (unsigned char) (l & 0xFF);
    b = (unsigned char) ((l>>8) & 0xFF);
    c = (unsigned char) ((l>>16) & 0xFF);
    d = (unsigned char) ((l>>24) & 0xFF);
    return ((unsigned long) d |
            ((unsigned long) c << 8) |
            ((unsigned long) b << 16) |
            ((unsigned long) a << 24));
}


/* ------------------------------------------------------------------
   examine() - Decide if swapping is necessary.
   ------------------------------------------------------------------ */

static int examine(buf,wordsize,n)
char *buf;
long wordsize;
long n;

{
    unsigned char *c = (unsigned char *) buf;
    int i, result1, result2;
    long l, count;

    for (count = n; count > 0; --count, c += wordsize)
    {
	for (l = 0, i = 0; i < wordsize; ++i)
	    l = (l << 8) | c[i];
	result1 = (l > 0 && l <= n);
	for (l = 0, i = wordsize-1; i >= 0; --i)
	    l = (l << 8) | c[i];
	result2 = (l > 0 && l <= n);

	if (result1 && !result2) return 1;	/* Swap */
	if (result2 && !result1) return 0;	/* Don't swap */
    }
    return -1;	/* Failed */
}



/* ------------------------------------------------------------------
   intcopy() - Copy integers with different word sizes and
	byte orders.
   ------------------------------------------------------------------ */

static void intcopy(dest, src, wordsize, swap, n)
long *dest;
char *src;
long wordsize;
int swap;
long n;

{
    unsigned char *c;
    int i;
    long count;
    long *d, l;

    d = dest;
    c = (unsigned char *) src;
    for (count = 0; count < n; ++count)
    {
	l = 0;
	if (swap)
            for (i = 0; i < wordsize; ++i)
		l = (l << 8) | c[i];
	else
            for (i = wordsize; i >= 0; --i)
		l = (l << 8) | c[i];
        *d++ = l;
	c += wordsize;
    }
}


/* ------------------------------------------------------------------
   chkhdr() - Check file header. Returns 1 if hdr is a valid matrix
	header, 2 for a valid permutation header.
   ------------------------------------------------------------------ */

static int chkhdr(hdr)
long *hdr;

{	if (hdr[0] > 1 && hdr[0] <= 256 && hdr[1] >= 0 && hdr[2] >= 0)
		return 1;	/* Matrix */
	if (hdr[0] == -1 && hdr[1] > 0 && hdr[2] > 0 &&
	    hdr[1] < 0x20000000 && hdr[2] < 0x20000000)
		return 2;		/* Permutation */
	return 0;
}


/* ------------------------------------------------------------------
   main()
   ------------------------------------------------------------------ */

int main(argc, argv)
int argc;
char *argv[];

{
    int i;


    /* Parse command line
       ------------------ */
    initargs(argc, argv, &pinfo);
    while (zgetopt("") != OPT_END);

    for (i = opt_ind; i < argc; ++i)
    {
	/* Open file, get file size
	   ------------------------ */
	strcpy(filename,argv[i]);
	f = os_fopen(filename,FM_READ);
	if (f == NULL)
	{	perror(filename);
		continue;
	}
	printf("%s: ",filename);
	os_fseek(f,(long)-1);	/* End of file */
	flen = ftell(f);

	/* Open temporary file
	   ------------------- */
	tmp = os_fopen(tmpname,FM_CREATE);
	if (tmp == NULL)
	{	perror(tmpname);
		fclose(f);
		continue;
	}

	/* Convert the file
	   ---------------- */
	if (convert() == 0)	/* Success */
	{
	    fclose(f);
	    fclose(tmp);
	    remove(filename);
	    rename(tmpname,filename);
	}
	else			/* Discard tmp file */
	{
	    fclose(f);
	    fclose(tmp);
	    remove(tmpname);
	    printf(" - Not converted.");
	}
	printf("\n");
    }
    return EXIT_OK;
}


static int convert()

{
    /* Lese File-Header, pruefe auf richtige Byte-Ordnung
       und schreibe den Header in die temoraere Datei.
       -------------------------------------------------- */
    os_fseek(f,(long)0);
    if (zreadlong(f,hdr,3) != 3)
    {
	printf("Error reading file header");
	return 1;
    }
    if (chkhdr(hdr) == 0)
    {
	hdr[0] = swapl(hdr[0]);
	hdr[1] = swapl(hdr[1]);
	hdr[2] = swapl(hdr[2]);
	if (chkhdr(hdr) == 0)
	{
	    printf("Not a MeatAxe file");
	    return 1;
	}
    }
    if (zwritelong(tmp,hdr,3) != 3)
    {
	printf("Error writing file header");
	return 1;
    }

    /* Konvertiere den Rest des Files
       ------------------------------ */
    if (hdr[0] >= 2) /* Matrix */
	return convmatrix();
    else	/* Permutation */
	return convperm();
}


static int convmatrix()

{
    long bpr, newbpr, i;
    char *buf;
	
    if (hdr[1] == 0) bpr = 0;
    else bpr = (flen - 12) / hdr[1];
    if ((flen - 12) % hdr[1])
    {
	printf("Bad file size");
	return 1;
    }
    for (newbpr = 0, i = hdr[0]; i <= 256; ++newbpr, i*=hdr[0]);
    newbpr = (hdr[2] - 1) / newbpr + 1;
    if (newbpr > bpr || bpr - newbpr > 8)
    {
	printf("Bad file size");
	return 1;
    }
    buf = (char *) malloc((size_t)bpr);
    for (i = 0; i < hdr[1]; ++i)
    {
	if (fread(buf,(size_t)bpr,1,f) != 1)
	{
	    printf("Error reading file");
	    return 1;
	}
	if (fwrite(buf,(size_t)newbpr,1,tmp) != 1)
	{
	    printf("Error writing temporary file");
	    return 1;
	}
    }
    printf("%ldx%ld matrix over GF(%ld)",hdr[1],hdr[2],hdr[0]);
    return 0;
}



static int convperm()


{
    char *rbuf;
    long *wbuf;
    long i;
    long wordsize;
    int swap = -1;
    static long test = 1;

    if (hdr[1]*hdr[2] == 0)
	wordsize = sizeof(long);	/* Egal, was hier steht! */
    else
	wordsize = (flen - 12) / (hdr[1]*hdr[2]);
    if (wordsize*hdr[1]*hdr[2] != flen - 12)
    {
	printf("Bad file size");
	return 1;
    }
    rbuf = (char *) malloc((size_t)hdr[1]*wordsize);
    wbuf = (long *) malloc((size_t)hdr[1]*sizeof(long));

    for (i = 0; i < hdr[2]; ++i)
    {
	if (fread(rbuf,(size_t)wordsize,(size_t)hdr[1],f) != hdr[1])
	{
	    printf("Error reading file");
	    return 1;
	}

	if (swap == -1)
	{
	    swap = examine(rbuf,wordsize,hdr[1]);
	    if (swap == -1)
	    {
	    	printf("Cannot decide if swapping is necessary");
	    	return 1;
	    }
	}
	intcopy(wbuf,rbuf,wordsize,swap,hdr[1]);
	if (zwritelong(tmp,wbuf,(size_t)hdr[1]) != hdr[1])
	{
	    printf("Error writing temporary file");
	    return 1;
	}
    }
    printf("%ld permutation(s) on %ld points",hdr[2],hdr[1]);
    if (wordsize != 4)
	printf(" (word size changed from %d to 4)",(int)wordsize);

    if (swap ^ (*((char*)&test) != 1))
	printf(" (swapped)");
    return 0;
}
