#include <sys/types.h>

#include <err.h>
#include <stdio.h>
#include <stdlib.h>

#ifndef FILENAME
#define FILENAME	"endian.out"
#endif

/* a type that allows us to access individual bytes of a 32 bit number */
union u_int32_bytes_t {
	u_int32_t original;
	u_int8_t bytes[4];
};

/* swap bytes from big to little and vice versa */
u_int32_t swap32(u_int32_t in) {
	union u_int32_bytes_t out;

	/* cast our 32bit number to the union so we can access each byte and
	 * assign them in reverse order to our return variable */
	out.bytes[0] = ((union u_int32_bytes_t)in).bytes[3];
	out.bytes[1] = ((union u_int32_bytes_t)in).bytes[2];
	out.bytes[2] = ((union u_int32_bytes_t)in).bytes[1];
	out.bytes[3] = ((union u_int32_bytes_t)in).bytes[0];

	/* return the whole number */
	return(out.original);
}

int big_endian(void) {
	union u_int32_bytes_t one;

	/* store 0x00000001 */
	one.original = 1U;

	/* if this is big endian the 1 (the LSB) will be in the last byte of one
	 * and vice versa */
	return (one.bytes[3] == '\001');
}

static void usage(void) {

	fprintf(stderr, "usage: endian [number]\n");
	exit(1);
}

int main(int argc, char *argv[]) {
	FILE *f;			/* output file */
	u_int32_t big,		/* our number in big endian format */
			  little;	/* our number in little endian format */

	/* indicate our byte order according to the system */
	printf("The standard header files say this machine is ");
	switch (BYTE_ORDER) {
	case BIG_ENDIAN:
		/* MSB first */
		printf("big endian");
		break;
	case LITTLE_ENDIAN:
		/* LSB first */
		printf("little endian");
		break;
	case PDP_ENDIAN:
		/* LSB first but MSW first (in a multiword variable) */
		printf("pdp endian");
		break;
	default:
		printf("strange");
		break;
	}
	printf(".\n");

	/* indicate our byte order according to reality - of course its a bit
	 * dodgy saying that just because we're not big endian we are
	 * little endian */
	printf("I just checked and I say this machine is %s endian.\n", (big_endian() ? "big" : "little"));

	/* open a file to write our numbers into */
	if ((f = fopen(FILENAME, "wb")) == NULL) {
		err(1, "fopen");
	}

	/* determine number to write to file (store it in big for now regardless
	   of our byte order...we'll fix it later) */
	switch (argc) {
	case 1:
		/* user didn't specify - default is 1 */
		big = 1;
		break;
	case 2:
		/* user specified number */
		big = atoi(argv[1]);
		break;
	default:
		/* user is mad */
		usage();
		break;
	}

	/* assign the number, with the appropriate byte order to big and little.
	 * network order is big endian so we can use htonl to make sure big really is
	 * big endian */
	big = htonl(big);
	/* litte must be big in reverse */
	little = swap32(big);

	/* write to file in such a way that it looks good in vi(1) */
	if ((fprintf(f, "Big Endian: ") < 1) ||
		(fwrite(&big, sizeof(big), 1, f) != 1) ||
		(fprintf(f, "\nLittle Endian: ") < 1) ||
		(fwrite(&little, sizeof(little), 1, f) != 1)) {
			err(1, "file I/O");
	}

	(void)fclose(f);

	printf("\nTake a look at %s now to see how numbers are layed out.\n", FILENAME);

	return 0;
}

