/*
 * Copyright (c) 2013, 2015, 2016 Jonas 'Sortie' Termansen.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * srctix-create.c
 * Converts an archived port tix into an archived source tix.
 */

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "util.h"

static void help(FILE* fp, const char* argv0)
{
	fprintf(fp, "Usage: %s [OPTION]... PORT-TIX\n", argv0);
	fprintf(fp, "Converts an archived port tix into an archived source tix.\n");
}

static void version(FILE* fp, const char* argv0)
{
	fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
}

bool is_file_name(const char* path)
{
	return !(strchr(path, '/') || !strcmp(path, ".") || !strcmp(path, ".."));
}

int main(int argc, char* argv[])
{
	char* output_directory = strdup(".");
	char* output = NULL;
	char* tmp = strdup(getenv_def("TMPDIR", "/tmp"));

	const char* argv0 = argv[0];
	for ( int i = 0; i < argc; i++ )
	{
		const char* arg = argv[i];
		if ( arg[0] != '-' || !arg[1] )
			continue;
		argv[i] = NULL;
		if ( !strcmp(arg, "--") )
			break;
		if ( arg[1] != '-' )
		{
			char c;
			while ( (c = *++arg) ) switch ( c )
			{
			default:
				fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
				help(stderr, argv0);
				exit(1);
			}
		}
		else if ( !strcmp(arg, "--help") )
			help(stdout, argv0), exit(0);
		else if ( !strcmp(arg, "--version") )
			version(stdout, argv0), exit(0);
		else if ( GET_OPTION_VARIABLE("--output-directory", &output_directory) ) { }
		else if ( GET_OPTION_VARIABLE("--output", &output) ) { }
		else if ( GET_OPTION_VARIABLE("--tmp", &tmp) ) { }
		else
		{
			fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
			help(stderr, argv0);
			exit(1);
		}
	}

	if ( argc == 1 )
	{
		help(stdout, argv0);
		exit(0);
	}

	compact_arguments(&argc, &argv);

	if ( argc <= 1 )
		errx(1, "No archived port tix specified");

	if ( 3 <= argc )
		errx(1, "extra operand");

	initialize_tmp(tmp, "srctix");

	const char* porttix_path = argv[1];

	char* tmp_in_root = print_string("%s/in", tmp_root);
	if ( mkdir(tmp_in_root, 0777) < 0 )
		err(1, "mkdir: %s", tmp_in_root);

	if ( fork_and_wait_or_death() )
	{
		const char* cmd_argv[] =
		{
			"tar",
			"--extract",
			"--directory", tmp_in_root,
			"--file", porttix_path,
			"--strip-components=1",
			NULL,
		};
		execvp(cmd_argv[0], (char* const*) cmd_argv);
		err(127, "%s", cmd_argv[0]);
	}

	char* porttixinfo_path = join_paths(tmp_in_root, "porttixinfo");
	FILE* porttixinfo_fp = fopen(porttixinfo_path, "r");
	if ( !porttixinfo_fp )
	{
		if ( errno == ENOENT )
			warnx("`%s' doesn't appear to be an archived port tix",
			      porttix_path);
		err(1, "`%s'", porttixinfo_path);
	}

	char* tmp_out_root = print_string("%s/out", tmp_root);
	if ( mkdir(tmp_out_root, 0777) < 0 )
		err(1, "mkdir: %s", tmp_out_root);

	char* package_name = NULL;
	char* srctix_path = NULL;

	char* line = NULL;
	size_t line_size = 0;
	ssize_t line_len;
	while ( 0 < (line_len = getline(&line, &line_size, porttixinfo_fp)) )
	{
		if ( line[line_len-1] == '\n' )
			line[--line_len] = '\0';
		char* first_space = strchr(line, ' ');
		if ( !first_space )
			err(1, "`%s`: malformed line `%s'", porttixinfo_path, line);
		*first_space = '\0';
		const char* function = line;
		const char* parameter = first_space + 1;

		if ( !strcmp(function, "package_name") )
		{
			if ( package_name )
				err(1, "`%s`: unexpected additional package name `%s'",
				    porttixinfo_path, parameter);
			if ( !is_file_name(parameter) )
				err(1, "`%s`: malformed package name `%s'",
				    porttixinfo_path, parameter);
			package_name = strdup(parameter);
			srctix_path = join_paths(tmp_out_root, package_name);
			if ( mkdir_p(srctix_path, 0755) != 0 )
				err(1, "mkdir: `%s'", srctix_path);
		}
		else if ( !package_name )
			err(1, "`%s`: expected package name before `%s'",
			      porttixinfo_path, function);
		else if ( !strcmp(function, "tar_extract") )
		{
			if ( !is_file_name(parameter) )
				err(1, "`%s`: malformed tarball filename `%s'",
				      porttixinfo_path, parameter);
			char* tarball_path = join_paths(tmp_in_root, parameter);
			if ( fork_and_wait_or_death() )
			{
				const char* cmd_argv[] =
				{
					"tar",
					"--extract",
					"--directory", srctix_path,
					"--file", tarball_path,
					"--strip-components=1",
					NULL,
				};
				execvp(cmd_argv[0], (char* const*) cmd_argv);
				err(127, "%s", cmd_argv[0]);
			}
			free(tarball_path);
		}
		else if ( !strcmp(function, "apply_normalize") )
		{
			if ( !is_file_name(parameter) )
				err(1, "`%s`: malformed normalize filename `%s'",
				    porttixinfo_path, parameter);
			char* rmpatch_path = join_paths(tmp_in_root, parameter);
			if ( fork_and_wait_or_death() )
			{
				const char* cmd_argv[] =
				{
					"tix-rmpatch",
					"--directory", srctix_path,
					"--",
					rmpatch_path,
					NULL,
				};
				execvp(cmd_argv[0], (char* const*) cmd_argv);
				err(127, "%s", cmd_argv[0]);
			}
			free(rmpatch_path);
		}
		else if ( !strcmp(function, "apply_patch") )
		{
			if ( !is_file_name(parameter) )
				err(1, "`%s`: malformed patch filename `%s'",
				    porttixinfo_path, parameter);
			char* patch_path = join_paths(tmp_in_root, parameter);
			if ( fork_and_wait_or_death() )
			{
				const char* cmd_argv[] =
				{
					"patch",
					"--strip=1",
					"--silent",
					"--directory", srctix_path,
					"--input", patch_path,
					NULL,
				};
				execvp(cmd_argv[0], (char* const*) cmd_argv);
				err(127, "%s", cmd_argv[0]);
			}
			free(patch_path);
		}
		else if ( !strcmp(function, "apply_execpatch") )
		{
			if ( !is_file_name(parameter) )
				err(1, "`%s`: malformed execpatch filename `%s'",
				    porttixinfo_path, parameter);
			char* execpatch_path = join_paths(tmp_in_root, parameter);
			if ( fork_and_wait_or_death() )
			{
				const char* cmd_argv[] =
				{
					"tix-execpatch",
					"--directory", srctix_path,
					"--",
					execpatch_path,
					NULL,
				};
				execvp(cmd_argv[0], (char* const*) cmd_argv);
				err(127, "%s", cmd_argv[0]);
			}
			free(execpatch_path);
		}
		else
			err(1, "`%s`: unsupported function `%s'",
			    porttixinfo_path, function);
	}
	free(line);
	if ( ferror(porttixinfo_fp) )
		err(1, "%s", porttixinfo_path);

	fclose(porttixinfo_fp);
	free(porttixinfo_path);

	if ( !output )
		output = print_string("%s/%s.srctix.tar.xz", output_directory, package_name);

	if ( fork_and_wait_or_death() )
	{
		const char* cmd_argv[] =
		{
			"tar",
			"--create",
			"--xz",
			"--directory", tmp_out_root,
			"--file", output,
			"--",
			package_name,
			NULL,
		};
		execvp(cmd_argv[0], (char* const*) cmd_argv);
		err(127, "%s", cmd_argv[0]);
	}

	free(srctix_path);
	free(package_name);

	return 0;
}
