aboutsummaryrefslogblamecommitdiff
path: root/src/lzma/main.c
blob: 02891193ba161548ec4e36ff85acf52b3e928334 (plain) (tree)


























































































                                                                               
                                                                          

































































































































































                                                                              
///////////////////////////////////////////////////////////////////////////////
//
/// \file       main.c
/// \brief      main()
//
//  Copyright (C) 2007 Lasse Collin
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 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
//  Lesser General Public License for more details.
//
///////////////////////////////////////////////////////////////////////////////

#include "private.h"
#include "open_stdxxx.h"
#include <ctype.h>

static sig_atomic_t exit_signal = 0;


static void
signal_handler(int sig)
{
	// FIXME Is this thread-safe together with main()?
	exit_signal = sig;

	user_abort = 1;
	return;
}


static void
establish_signal_handlers(void)
{
	struct sigaction sa;
	sa.sa_handler = &signal_handler;
	sigfillset(&sa.sa_mask);
	sa.sa_flags = 0;

	static const int sigs[] = {
		SIGHUP,
		SIGINT,
		SIGPIPE,
		SIGTERM,
		SIGXCPU,
		SIGXFSZ,
	};

	for (size_t i = 0; i < sizeof(sigs) / sizeof(sigs[0]); ++i) {
		if (sigaction(sigs[i], &sa, NULL)) {
			errmsg(V_ERROR, _("Cannot establish signal handlers"));
			my_exit(ERROR);
		}
	}

	/*
	SIGINFO/SIGUSR1 for status reporting?
	*/
}


static bool
is_tty_stdin(void)
{
	const bool ret = isatty(STDIN_FILENO);
	if (ret) {
		// FIXME: Other threads may print between these lines.
		// Maybe that should be fixed. Not a big issue in practice.
		errmsg(V_ERROR, _("Compressed data not read from "
				"a terminal."));
		errmsg(V_ERROR, _("Use `--force' to force decompression."));
		show_try_help();
	}

	return ret;
}


static bool
is_tty_stdout(void)
{
	const bool ret = isatty(STDOUT_FILENO);
	if (ret) {
		errmsg(V_ERROR, _("Compressed data not written to "
				"a terminal."));
		errmsg(V_ERROR, _("Use `--force' to force compression."));
		show_try_help();
	}

	return ret;
}


static char *
read_name(void)
{
	size_t size = 256;
	size_t pos = 0;
	char *name = malloc(size);
	if (name == NULL) {
		out_of_memory();
		return NULL;
	}

	while (true) {
		const int c = fgetc(opt_files_file);
		if (c == EOF) {
			free(name);

			if (ferror(opt_files_file))
				errmsg(V_ERROR, _("%s: Error reading "
						"filenames: %s"),
						opt_files_name,
						strerror(errno));
			else if (pos != 0)
				errmsg(V_ERROR, _("%s: Unexpected end of "
						"input when reading "
						"filenames"), opt_files_name);

			return NULL;
		}

		if (c == '\0' || c == opt_files_split)
			break;

		name[pos++] = c;

		if (pos == size) {
			size *= 2;
			char *tmp = realloc(name, size);
			if (tmp == NULL) {
				free(name);
				out_of_memory();
				return NULL;
			}

			name = tmp;
		}
	}

	if (name != NULL)
		name[pos] = '\0';

	return name;
}


int
main(int argc, char **argv)
{
	// Make sure that stdin, stdout, and and stderr are connected to
	// a valid file descriptor. Exit immediatelly with exit code ERROR
	// if we cannot make the file descriptors valid. Maybe we should
	// print an error message, but our stderr could be screwed anyway.
	open_stdxxx(ERROR);

	// Set the program invocation name used in various messages.
	argv0 = argv[0];

	setlocale(LC_ALL, "en_US.UTF-8");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);

	// Set hardware-dependent default values. These can be overriden
	// on the command line, thus this must be done before parse_args().
	hardware_init();

	char **files = parse_args(argc, argv);

	if (opt_mode == MODE_COMPRESS && opt_stdout && is_tty_stdout())
		return ERROR;

	if (opt_mode == MODE_COMPRESS)
		lzma_init_encoder();
	else
		lzma_init_decoder();

	io_init();
	process_init();

	if (opt_mode == MODE_LIST) {
		errmsg(V_ERROR, "--list is not implemented yet.");
		my_exit(ERROR);
	}

	// Hook the signal handlers. We don't need these before we start
	// the actual action, so this is done after parsing the command
	// line arguments.
	establish_signal_handlers();

	while (*files != NULL && !user_abort) {
		if (strcmp("-", *files) == 0) {
			if (!opt_force) {
				if (opt_mode == MODE_COMPRESS) {
					if (is_tty_stdout()) {
						++files;
						continue;
					}
				} else if (is_tty_stdin()) {
					++files;
					continue;
				}
			}

			if (opt_files_name == stdin_filename) {
				errmsg(V_ERROR, _("Cannot read data from "
						"standard input when "
						"reading filenames "
						"from standard input"));
				++files;
				continue;
			}

			*files = (char *)stdin_filename;
		}

		process_file(*files++);
	}

	if (opt_files_name != NULL) {
		while (true) {
			char *name = read_name();
			if (name == NULL)
				break;

			if (name[0] != '\0')
				process_file(name);

			free(name);
		}

		if (opt_files_name != stdin_filename)
			(void)fclose(opt_files_file);
	}

	io_finish();

	if (exit_signal != 0) {
		struct sigaction sa;
		sa.sa_handler = SIG_DFL;
		sigfillset(&sa.sa_mask);
		sa.sa_flags = 0;
		sigaction(exit_signal, &sa, NULL);
		raise(exit_signal);
	}

	my_exit(exit_status);
}