aboutsummaryrefslogtreecommitdiff
/*
  ihex
  Copyright (C) 2025 Thomas Albers Raviola <thomas@thomaslabs.org>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>

#include <util.h>
#include <ihex.h>
#include <array.h>

static void
usage(void)
{
	fputs("\
Usage: ihex [OPTION]... [[BASE:]FILE]...\n", stdout);
	fputs("\
Convert ihex FILEs into binary data\n\n", stdout);
	fputs("\
  -h, --help                  display this help and exit\n", stdout);
	fputs("\
  -o, --output=NAME           write output to NAME\n", stdout);
	fputs("\n\
The BASE argument is a base 16 encoded number indicating where to place the\n\
file contents inside the resulting image\n\n", stdout);
	fputs("\
ihex home page: <https://thomaslabs.org/software/ihex>\n\
\n\
Copyright (C) 2025 Thomas Albers Raviola\n\
This program comes with ABSOLUTELY NO WARRANTY.\n\
This is free software: you free to change and redistribute it.\n",
	      stdout);
}

static const char *opts = "ho:";
static const struct option longopts[] = {
	{.name = "help", .has_arg = 0, .flag = NULL, .val = 'h'},
	{.name = "output", .has_arg = 1, .flag = NULL, .val = 'o'},
	{0, 0, 0, 0}
};

static const char *output;

void
decode_args(int argc, char *argv[])
{
	int c;
	while ((c = getopt_long(argc, argv, opts, longopts, NULL)) != -1) {
		switch (c) {
		case 0:
			break;
		case 'h':
			usage();
			exit(EXIT_SUCCESS);
		case 'o':
			output = optarg;
			break;
		case '?':
			usage();
			exit(EXIT_FAILURE);
		default:
			break;
		}
	}
}

int
decode_filename(char *arg, long *base_address, const char **filename)
{
	char *junk;
	char *sep = strchr(arg, ':');

	if (sep) {
		*sep++ = '\0';
		*filename = sep;
		*base_address = strtol(arg, &junk, 16);

		if (*junk) {
			fprintf(stderr, "Invalid base address for file %s\n",
				*filename);
			return -1;
		}
	} else {
		*filename = arg;
		*base_address = NO_BASE_ADDRESS;
	}

	return 0;
}

int
main(int argc, char *argv[])
{
	int err = 0;
	unsigned int i;
	struct array blocks;
	struct array data;

	decode_args(argc, argv);

	init_array(&blocks, sizeof(struct block));
	init_array(&data, sizeof(char));

	for (i = optind; i < argc; ++i) {
		long base_address;
		const char *filename;

		err = decode_filename(argv[i], &base_address, &filename);
		if (err < 0)
			goto error;

		err = read_ihex_file(filename, &blocks, &data, base_address);
		if (err < 0)
			goto error;
	}

	FILE *file = output ? fopen(output, "w") : stdout;

	if (!file) {
		err = 1;
		fprintf(stderr, "Could not open output file %s\n", output);
		goto error;
	}

	err = write_hex(&blocks, &data, file);

	if (output)
		fclose(file);

error:
	free_array(&blocks);
	free_array(&data);
	return err;
}