// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
 */

#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

static void usage(const char *const name)
{
	printf("Usage: %s OutputFile (PolicyFile)\n", name);
	exit(EINVAL);
}

static int policy_to_buffer(const char *pathname, char **buffer, size_t *size)
{
	size_t fsize;
	size_t read;
	char *lbuf;
	int rc = 0;
	FILE *fd;

	fd = fopen(pathname, "r");
	if (!fd) {
		rc = errno;
		goto out;
	}

	fseek(fd, 0, SEEK_END);
	fsize = ftell(fd);
	rewind(fd);

	lbuf = malloc(fsize);
	if (!lbuf) {
		rc = ENOMEM;
		goto out_close;
	}

	read = fread((void *)lbuf, sizeof(*lbuf), fsize, fd);
	if (read != fsize) {
		rc = -1;
		goto out_free;
	}

	*buffer = lbuf;
	*size = fsize;
	fclose(fd);

	return rc;

out_free:
	free(lbuf);
out_close:
	fclose(fd);
out:
	return rc;
}

static int write_boot_policy(const char *pathname, const char *buf, size_t size)
{
	FILE *fd;
	size_t i;

	fd = fopen(pathname, "w");
	if (!fd)
		return errno;

	fprintf(fd, "/* This file is automatically generated.");
	fprintf(fd, " Do not edit. */\n");
	fprintf(fd, "#include <linux/stddef.h>\n");
	fprintf(fd, "\nextern const char *const ipe_boot_policy;\n\n");
	fprintf(fd, "const char *const ipe_boot_policy =\n");

	if (!buf || size == 0) {
		fprintf(fd, "\tNULL;\n");
		fclose(fd);
		return 0;
	}

	fprintf(fd, "\t\"");

	for (i = 0; i < size; ++i) {
		switch (buf[i]) {
		case '"':
			fprintf(fd, "\\\"");
			break;
		case '\'':
			fprintf(fd, "'");
			break;
		case '\n':
			fprintf(fd, "\\n\"\n\t\"");
			break;
		case '\\':
			fprintf(fd, "\\\\");
			break;
		case '\t':
			fprintf(fd, "\\t");
			break;
		case '\?':
			fprintf(fd, "\\?");
			break;
		default:
			fprintf(fd, "%c", buf[i]);
		}
	}
	fprintf(fd, "\";\n");
	fclose(fd);

	return 0;
}

int main(int argc, const char *const argv[])
{
	char *policy = NULL;
	size_t len = 0;
	int rc = 0;

	if (argc < 2)
		usage(argv[0]);

	if (argc > 2) {
		rc = policy_to_buffer(argv[2], &policy, &len);
		if (rc != 0)
			goto cleanup;
	}

	rc = write_boot_policy(argv[1], policy, len);
cleanup:
	if (policy)
		free(policy);
	if (rc != 0)
		perror("An error occurred during policy conversion: ");
	return rc;
}
