
/* rawtohex.c -- program to turn binary images into Intel HEX files */
/* Copyright (C) 2009 Fred Barnes, University of Kent <frmb@kent.ac.uk> */

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


static char *progname = NULL;
static int verbose = 0;


int main (int argc, char **argv)
{
	int i;
	char *ifile = NULL;
	char *ofile = NULL;
	char **walk;
	int in_fd;
	FILE *out_f;
	struct stat stbuf;
	int saddr;
	char *ibuf = NULL;

	for (progname = *argv + (strlen (*argv) - 1); (progname > *argv) && (progname[-1] != '/'); progname--);

	for (i=argc-1, walk=argv+1; (i > 0) && *walk; i--, walk++) {
		if (!strcmp (*walk, "--help") || !strcmp (*walk, "-h")) {
			fprintf (stderr, "usage: %s [options] <input-file> [output-file]\n", progname);
			fprintf (stderr, "where options are:\n");
			fprintf (stderr, "  --help | -h      this help\n");
			fprintf (stderr, "  --verbose | -v   be verbose\n");
			exit (EXIT_SUCCESS);
		} else if (!strcmp (*walk, "--verbose") || !strcmp (*walk, "-v")) {
			verbose++;
		} else if (!ifile) {
			ifile = *walk;
		} else if (!ofile) {
			ofile = *walk;
		}
	}

	if (!ifile) {
		fprintf (stderr, "%s: input file must be specified!\n", progname);
		exit (EXIT_FAILURE);
	}

	if (access (ifile, R_OK)) {
		fprintf (stderr, "%s: cannot read from %s: %s\n", progname, ifile, strerror (errno));
		exit (EXIT_FAILURE);
	}

	in_fd = open (ifile, O_RDONLY);
	if (in_fd < 0) {
		fprintf (stderr, "%s: failed to open %s for read: %s\n", progname, ifile, strerror (errno));
		exit (EXIT_FAILURE);
	}

	if (fstat (in_fd, &stbuf) < 0) {
		fprintf (stderr, "%s: failed to stat %s: %s\n", progname, ifile, strerror (errno));
		close (in_fd);
		exit (EXIT_FAILURE);
	}

	if (verbose) {
		fprintf (stderr, "input is %d byte(s) long\n", (int)stbuf.st_size);
	}

	/* read input and close file */
	ibuf = (char *)malloc ((int)stbuf.st_size + 16);
	if (!ibuf) {
		fprintf (stderr, "%s: memory allocation error!\n", progname);
		exit (EXIT_FAILURE);
	}
	for (i=0; i<(int)stbuf.st_size; ) {
		int r = read (in_fd, ibuf + i, (int)stbuf.st_size - i);

		if (r < 0) {
			fprintf (stderr, "%s: read error from %s: %s\n", progname, ifile, strerror (errno));
			exit (EXIT_FAILURE);
		} else if (!r) {
			fprintf (stderr, "%s: unexpected EOF in %s\n", progname, ifile);
			exit (EXIT_FAILURE);
		} else {
			i += r;
		}
	}

	/* ASSERT: ibuf valid past here */

	close (in_fd);

	/* open output file, else we're dumping on stdout */
	if (!ofile) {
		out_f = stdout;
	} else {
		out_f = fopen (ofile, "wt");
		if (!out_f) {
			fprintf (stderr, "%s: failed to open %s for writing: %s\n", progname,
					ofile, strerror (errno));
			free (ibuf);
			exit (EXIT_FAILURE);
		}
	}

	/* off we go! */
	saddr = 0;
	for (i=0; i<(int)stbuf.st_size; i += 16) {
		int cs, k;
		unsigned char csum = 0x00;

		cs = (int)stbuf.st_size - i;
		if (cs > 16) {
			cs = 16;
		}

		fprintf (out_f, ":%2.2X%4.4X00", cs, saddr);
		csum = (unsigned char)cs;
		csum += (unsigned char)(saddr >> 8);
		csum += (unsigned char)(saddr & 0xff);
		csum += 0x00;

		for (k=0; k<cs; k++) {
			fprintf (out_f, "%2.2X", (unsigned char)ibuf[saddr + k]);
			csum += (unsigned char)ibuf[saddr + k];
		}

		/* checksum */
		csum = (~csum) + 1;
		fprintf (out_f, "%2.2X\n", csum);

		saddr += cs;
	}
	fprintf (out_f, ":00000001FF\n");

	free (ibuf);
	if (ofile) {
		fclose (out_f);
	}

	return EXIT_SUCCESS;
}


