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