/*
 * This program generates a configuration file for your system
 *
 * USAGE:
 *	defines [ -w ] [ -h hosttype ] [ -d description ]
 * Options are:
 *	-w	do not generate a file; just print the number of bits
 *		in a word and exit.
 *	-h
 *		save the output file in a file named HostType.h
 *	-d description
 *		this is put in the header comments
 */
/****************************************************************
 * Copyright notice.						*
 * This software is copyrighted (c) 1991 by Matt Bishop and the *
 * Trustees of Dartmouth College.  All rights reserved.		*
 *								*
 * Author:	Matt Bishop					*
 * Address:	Department of Mathematics and Computer Science	*
 *		Dartmouth College				*
 *		Hanover, NH  03755-1831				*
 *		USA						*
 * telephone:	+1 603 646 3267					*
 * fax:		+1 603 646 1312					*
 * internet:	Matt.Bishop@dartmouth.edu			*
 * usenet:	...!decvax!dartvax!Matt.Bishop			*
 ****************************************************************/
 
#ifndef lint
static char *version = "Version GAMMA 6/31/91 Matt.Bishop@dartmouth.edu";
#endif

#include <stdio.h>
#include <math.h>

/************************** M4 STUFF FOLLOWS ************************
 * all this is grist for m4's mill; the configuration file is pulled
 * in and substitutions made here based on what is in there.  Then 
 * this section puts in the appropriate defines
 *
 * This line hides the # in column 1 of the config file (otherwise
 * the preprocessor will go nuts!)
 */
define(IsCProgram,yes)
/*
 * you need the ifdef notdef ... endif to prevent the compiler from
 * trying to compile Makefile commands
 **/
#ifdef notdef
include(config.m4)
#endif
/*
 * set the appropriate flag for the operating system
 */
ifelse(OSType,BSD4,
#define BSD4
,OSType,SYSV,
#define SYSV
,
errput(You must set OSType to either BSD4 or SYSV or this won't compile!))
/*
 * handle to bzero/memzero/??? situation
 */
ifelse(ZeroMemory,,
#define PDECL	FILE *ifp;\
	int zlocate();
#define PSEARCH	\
	if (zlocate("nm CLibrary | grep bzero"))\
		fprintf(ofp, "#define\tBZERO\t\tbzero\t\t/* bzero(3) */\n\n");\
	else if (zlocate("nm CLibrary | grep memzero"))\
		fprintf(ofp, "#define\tBZERO\t\tmemzero\t\t/* memzero(3) */\n\n");\
	else\
		fprintf(ofp, "#undef\tBZERO\t\t\t/* no such function */\n\n");
#define PLOCATE \
int zlocate(str)\
char *str;\
{\
	register int i;\
	FILE *ifp;\
	char buf[BUFSIZ];\
\
	if ((ifp = popen(str, "r")) == NULL)\
		return(0);\
	for(i = 0; fgets(buf, sizeof(buf), ifp) != NULL; i++);\
	(void) fclose(ifp);\
	return(i);\
}
,ZeroMemory,none,
#define PDECL
#define PSEARCH 	fprintf(ofp, "#undef\tBZERO\t\t\t\t/* no such function */\n\n");
#define PLOCATE
,
#define PDECL
#define PSEARCH 	fprintf(ofp, "#define\tBZERO\t\tZeroMemory\t\t/* here it is */\n\n");
#define PLOCATE
)

/*
 * useful for being sure things are printed RIGHT
 */
define(cat,$1$2)
#define ZEROWORD	\
			"cat(0,SuffixWord)"
#define PRINTWORD	\
			"cat(PrintWord,SuffixWord)"
undefine(`cat')

/*
 * host name
 */
#define HOSTTYPE	\
			"HostType"


/*
 * no more m4 commands after this!!!
 */

/*
 * basic definitions
 * these get filled in by m4; note the break in the line is NECESSARY as
 * # begins a comment in m4, so if the define is on a single line, the
 * whole line will be passed through as is.  I could not find this documented
 * on the manual page, but ... it seems to be true
 */
#define	WORD			/* data type for word */\
		Word
#define	C_WORD			/* data type for word as a string */\
		"Word"
	
/*
 * two handy types
 */
union align {
	WORD xxx;
	char cp[1];
};
union str32order {
	struct {
		char x1;
		char x2;
		char x3;
		char x4;
	} c;
	WORD i;
};
union str64order {
	struct {
		char x1;
		char x2;
		char x3;
		char x4;
		char x5;
		char x6;
		char x7;
		char x8;
	} c;
	WORD i;
};

/*
 * forward declarations
 */
int getwordsize();		/* returns number of bits in a WORD */
int getbytesize();		/* returns number of bits in a byte */
char *gettype();		/* get a type corresponding to a size */
int lg();			/* compute the log base 2 */

/*
 * globals used for various nefarious purposes
 */
FILE *ofp = stdout;		/* output file pointer */

/*
 * library routines
 */
char getopt();			/* parse option line */
int strcpy();			/* copy a string */
int strcat();			/* append one string to another */
int strncpy();			/* copy n chars of a string */
void exit();			/* bye! */

/*
 * library variables
 */
extern char *optarg;		/* argument to option */

/*
 * the main routine
 */
void main(argc, argv)
int argc;
char **argv;
{
	register int i, j;	/* temporaries used everywhere */
	char desc[BUFSIZ];	/* machine description buffer */
	char ofile[BUFSIZ];	/* output file name, if any */
	long t;			/* used to hold current time */
	register char *p;	/* pointer to type name */
	PDECL			/* for seeing if there is a bzero */

	/*
	 * arguments
	 */
	while ((i = getopt(argc, argv, "d:htw")) != EOF){
		switch(i){
		case 'd':			/* get description */
			(void) strncpy(desc, optarg, sizeof(desc)-1);
			break;
		case 'h':			/* get file name */
			(void) strcpy(ofile, HOSTTYPE);
			(void) strcat(ofile, ".h");
			if ((ofp = fopen(ofile, "w")) == NULL){
				perror(ofile);
				exit(1);
			}
			break;
		case 'w':			/* word size in bits */
			printf("%d\n", getwordsize());
			exit(0);
		case '?':			/* ??? */
		default:
			fprintf(stderr, "Usage: %s [-hw ] [-d desc]\n",
								      argv[0]);
			exit(1);
		}
	}
	
	/*
	 * print the leading comments
	 */
	if ((ifp = popen("date", "r")) == NULL)
		(void) strncpy(ofile, "not available\n", sizeof(ofile));
	else{
		(void) fgets(ofile, BUFSIZ, ifp);
		(void) fclose(ifp);
	}
	if (desc[0])
		fprintf(ofp, "/*\n * %s\n * date: %s */\n", desc, ofile);
	else
		fprintf(ofp, "/*\n * date: %s */\n", ofile);

	/*
	 * bits per word and how to print a word
	 */
	fprintf(ofp, "#define BITSPERWORD\t%d\t\t/* %d bits per word */\n",
						getwordsize(), getwordsize());
	fprintf(ofp, "#define FORMAT\t\t\"\\t%s,\\n\"\t", PRINTWORD);
	fprintf(ofp, "/* write a WORD */\n");

	/*
	 * now see what order bytes are ordered in
	 */
	switch(getwordsize()){
	case 32:	order32(getbytesize());	break;
	case 64:	order64(getbytesize());	break;
	default:				break;
	}

	/*
	 * now the types
	 */
	if ((p = gettype(8)) != NULL)
		fprintf(ofp, "#define ONEBYTE\t\t%s\t/* 8 bits */\n", p);
	else{
		fprintf(stderr, "There is no 8 bit type\n");
		fprintf(ofp, "/*#error ONEBYTE\t\t???\t/* no 8 bit type */\n");
	}
	if ((p = gettype(16)) != NULL)
		fprintf(ofp, "#define TWOBYTES\t%s\t/* 16 bits */\n", p);
	else{
		fprintf(stderr, "There is no 16 bit type\n");
		fprintf(ofp, "/*#error TWOBYTES\t???\t/* no 16 bit type */\n");
	}
	if ((p = gettype(32)) != NULL)
		fprintf(ofp, "#define FOURBYTES\t%s\t/* 16 bits */\n", p);
	else{
		fprintf(stderr, "There is no 32 bit type\n");
		fprintf(ofp, "/*#error FOURBYTES\t???\t/* no 32 bit type */\n");
	}
	if ((p = gettype(getwordsize())) != NULL)
		fprintf(ofp, "#define WORD\t\t%s\t/* %d bits */\n",
							p, getwordsize());
	else{
		fprintf(stderr, "There is no %d bit type\n", getwordsize());
		fprintf(ofp, "/*#error WORD\t\t???\t/* no %d bit type */\n",
								getwordsize());
	}
	fprintf(ofp, "#define ZERO\t\t%s\t\t/* %d bits of 0 */\n\n",
						ZEROWORD, getwordsize());

	/*
	 * now bzero
	 */
	fprintf(ofp, "/*\n");
	fprintf(ofp, " * library function which fills array with NUL bytes\n");
	fprintf(ofp, " */\n");
	PSEARCH

	/*
	 * now the array offsets
	 * note that if the offset is too big,
	 * this won't work (the 6 is because each index will
	 * ALWAYS have at least 6 bits)
	 */
	i = lg(sizeof(WORD));
	j = lg(sizeof(unsigned char));
	if (i + 6 <= getbytesize() && i > -1 && j > -1){
		fprintf(ofp, "/*\n * for array accesses\n */\n");
		fprintf(ofp, "#define LOFFSET\t\t%d\t\t", i);
		fprintf(ofp, "/* unsigned longs are %d=2^%d bytes */\n",
						  sizeof(unsigned long), i);
		fprintf(ofp, "#define COFFSET\t\t%d\t\t", j);
		fprintf(ofp, "/* unsigned chars are %d=2^%d bytes */\n",
						  sizeof(unsigned char), j);
	}
	else{
		fprintf(ofp, "/*\n * must use indexing on this system!\n */\n");
		fprintf(ofp, "#define LOFFSET\t\t0\t\t");
		fprintf(ofp, "/* do not use type punning! */\n");
		fprintf(ofp, "#define COFFSET\t\t0\t\t");
		fprintf(ofp, "/* do not use type punning! */\n");
	}

	/*
	 * bye!
	 */
	exit(0);
	
}

/*
 * the locator routine, if needed
 */
PLOCATE

/*
 * get word size in bits
 * this assumes WORD is set properly!
 */
int getwordsize()
{
	register WORD ul;		/* a word */
	register int i;			/* counter */

	/*
	 * move bit left until the bit falls off
	 */
	for(ul = 1, i = 0; ul != 0; ul <<= 1, i++);
	/*
	 * return the number of left moves
	 */
	return(i);
}

/*
 * get byte size in bits
 * this assumes char is 1 byte, which in C is pretty standard
 */
int getbytesize()
{
	register unsigned char uc;		/* a byte */
	register int i;				/* counter */

	/*
	 * move bit left until the bit falls off
	 */
	for(uc = 1, i = 0; uc != 0; uc <<= 1, i++);
	/*
	 * return the number of left moves
	 */
	return(i);
}

/*
 * get name of type of given size
 * return it as a string (or a syntactically incorrect one
 * if there is no such type)
 */
char *gettype(nbits)
int nbits;				/* number of bits */
{
	register int i;
	unsigned char uc;		/* a list of the various types */
	unsigned short us;
	unsigned int ui;
	unsigned long ul;
	WORD uw;

	/*
	 * count the number of bits in each, and compare
	 */
				/* WORD */
	for(uw = 1, i = 0; uw != 0; uw <<= 1, i++);
	if (i == nbits)	return(C_WORD);
				/* unsigned char */
	for(uc = 1, i = 0; uc != 0; uc <<= 1, i++);
	if (i == nbits)	return("unsigned char");
				/* unsigned short */
	for(us = 1, i = 0; us != 0; us <<= 1, i++);
	if (i == nbits)	return("unsigned short");
				/* unsigned int */
	for(ui = 1, i = 0; ui != 0; ui <<= 1, i++);
	if (i == nbits)	return("unsigned int");
				/* unsigned long */
	for(ul = 1, i = 0; ul != 0; ul <<= 1, i++);
	if (i == nbits)	return("unsigned long");
				/* huh? */
	return(NULL);
}

/*
 * compute lg if possible
 */
int lg(n)
int n;			/* get lg (log base 2) of this */
{
	register int i, j;	/* counters in for loops */

	/*
	 * do it the stupid way
	 */
	for(i = 0, j = 1; j < n; i++, j *= 2);

	/*
	 * if you did not get it exactly, return -1
	 */
	return(j == n ? i : -1);
}

/*
 * determine byte and structure order for 32 bit machines
 */
order32(bs)
int bs;			/* byte size */
{
	union align bend32;	/* known big endian array/WORD */
	union align lend32;	/* known little endian array/WORD */
	WORD llendian;		/* LSB first as a long */
	WORD lbendian;		/* MSB first as a long */
	WORD bytes;		/* used to determine byte ordering */
	union str32order order;	/* used to determine field ordering */

	/*
	 * set up the big and little endian words
	 */
	lend32.cp[0] = 0x04; bend32.cp[0] = 0x01;
	lend32.cp[1] = 0x03; bend32.cp[1] = 0x02;
	lend32.cp[2] = 0x02; bend32.cp[2] = 0x03;
	lend32.cp[3] = 0x01; bend32.cp[3] = 0x04;
	/*
	 * first get the byte ordering for big and little endian
	 */
	lbendian = *(WORD *) bend32.cp;
	llendian = *(WORD *) lend32.cp;
	/*
	 * now construct the equivalenmt number in memory
	 */
	bytes  = 0x04; bytes *= (1<<bs);
	bytes += 0x03; bytes *= (1<<bs);
	bytes += 0x02; bytes *= (1<<bs);
	bytes += 0x01;
	/*
	 * compare and see what you got
	 */
	if (bytes == lbendian){
		fprintf(ofp, "#define\tBYTEORDER\tBIGENDIAN\t");
		fprintf(ofp, "/* MSB comes first */\n");
	}
	else if (bytes == llendian){
		fprintf(ofp, "#define\tBYTEORDER\tLITTLEENDIAN\t");
		fprintf(ofp, "/* LSB comes first */\n");
	}
	else{
		fprintf(stderr, "Byte ordering is not supported\n");
		fprintf(ofp, "#error\tNETORDER\t\t\t");
		fprintf(ofp, "/* byte order is UNSUPPORTED */\n");
	}
	/*
	 * Now we go for the structure order
	 * This is type punning (advanced) ...
	 */
	order.c.x1 = 0x4;
	order.c.x2 = 0x3;
	order.c.x3 = 0x2;
	order.c.x4 = 0x1;
	if (order.i == lbendian){
		fprintf(ofp, "#define\tSTRUCTFROMTOP\t\t\t");
		fprintf(ofp, "/* first in struct is left of word */\n");
	}
	else if (order.i == llendian){
		fprintf(ofp, "#undef\tSTRUCTFROMTOP\t\t\t");
		fprintf(ofp, "/* first in struct is right of word */\n");
	}
	else{
		fprintf(stderr, "Structure ordering is not supported\n");
		fprintf(ofp, "#error\tSTRUCTFROMTOP\t\t\t");
		fprintf(ofp, "/* structure field order is UNSUPPORTED */\n");
	}
}

/*
 * determine byte and structure order for 64 bit machines
 */
order64(bs)
int bs;			/* byte size */
{
	union align bend64;	/* known big endian array/WORD */
	union align lend64;	/* known little endian array/WORD */
	WORD llendian;		/* LSB first as a long */
	WORD lbendian;		/* MSB first as a long */
	WORD bytes;		/* used to determine byte ordering */
	union str64order order;	/* used to determine field ordering */

	/*
	 * set up the big and little endian words
	 */
	lend64.cp[0] = 0x01; bend64.cp[0] = 0x08;
	lend64.cp[1] = 0x02; bend64.cp[1] = 0x07;
	lend64.cp[2] = 0x03; bend64.cp[2] = 0x06;
	lend64.cp[3] = 0x04; bend64.cp[3] = 0x05;
	lend64.cp[4] = 0x05; bend64.cp[4] = 0x04;
	lend64.cp[5] = 0x06; bend64.cp[5] = 0x03;
	lend64.cp[6] = 0x07; bend64.cp[6] = 0x02;
	lend64.cp[7] = 0x08; bend64.cp[7] = 0x01;
	/*
	 * first get the byte ordering for big and little endian
	 */
	lbendian = *(WORD *) bend64.cp;
	llendian = *(WORD *) lend64.cp;
	/*
	 * now construct the equivalent number in memory
	 */
	bytes  = 0x08; bytes *= (1<<bs);
	bytes += 0x07; bytes *= (1<<bs);
	bytes += 0x06; bytes *= (1<<bs);
	bytes += 0x05; bytes *= (1<<bs);
	bytes += 0x04; bytes *= (1<<bs);
	bytes += 0x03; bytes *= (1<<bs);
	bytes += 0x02; bytes *= (1<<bs);
	bytes += 0x01;
	/*
	 * compare and see what you got
	 */
	if (bytes == lbendian){
		fprintf(ofp, "#define\tBYTEORDER\tBIGENDIAN\t");
		fprintf(ofp, "/* MSB comes first */\n");
	}
	else if (bytes == llendian){
		fprintf(ofp, "#define\tBYTEORDER\tLITTLEENDIAN\t");
		fprintf(ofp, "/* LSB comes first */\n");
	}
	else{
		fprintf(stderr, "Byte ordering is not supported\n");
		fprintf(ofp, "#error\tBYTEORDER\t\t\t");
		fprintf(ofp, "/* byte order is UNSUPPORTED */\n");
	}
	/*
	 * Now we go for the structure order
	 * This is type punning (advanced) ...
	 */
	order.c.x1 = 0x1;
	order.c.x2 = 0x2;
	order.c.x3 = 0x3;
	order.c.x4 = 0x4;
	order.c.x5 = 0x5;
	order.c.x6 = 0x6;
	order.c.x7 = 0x7;
	order.c.x8 = 0x8;
	if (order.i == llendian){
		fprintf(ofp, "#define\tSTRUCTFROMTOP\t\t\t");
		fprintf(ofp, "/* first in struct is left of word */\n");
	}
	else if (order.i == lbendian){
		fprintf(ofp, "#undef\tSTRUCTFROMTOP\t\t\t");
		fprintf(ofp, "/* first in struct is right of word */\n");
	}
	else{
		fprintf(stderr, "Structure ordering is not supported\n");
		fprintf(ofp, "#error\tSTRUCTFROMTOP\t\t\t");
		fprintf(ofp, "/* structure field order is UNSUPPORTED */\n");
	}
}

