/////////////////////////////////////////////////////////////////////////////// // /// \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 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); }