Index: xz/src/xz/main.c |
=================================================================== |
--- xz/src/xz/main.c (revision 0) |
+++ xz/src/xz/main.c (revision 0) |
@@ -0,0 +1,302 @@ |
+/////////////////////////////////////////////////////////////////////////////// |
+// |
+/// \file main.c |
+/// \brief main() |
+// |
+// Author: Lasse Collin |
+// |
+// This file has been put into the public domain. |
+// You can do whatever you want with this file. |
+// |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+#include "private.h" |
+#include <ctype.h> |
+ |
+/// Exit status to use. This can be changed with set_exit_status(). |
+static enum exit_status_type exit_status = E_SUCCESS; |
+ |
+#if defined(_WIN32) && !defined(__CYGWIN__) |
+/// exit_status has to be protected with a critical section due to |
+/// how "signal handling" is done on Windows. See signals.c for details. |
+static CRITICAL_SECTION exit_status_cs; |
+#endif |
+ |
+/// True if --no-warn is specified. When this is true, we don't set |
+/// the exit status to E_WARNING when something worth a warning happens. |
+static bool no_warn = false; |
+ |
+ |
+extern void |
+set_exit_status(enum exit_status_type new_status) |
+{ |
+ assert(new_status == E_WARNING || new_status == E_ERROR); |
+ |
+#if defined(_WIN32) && !defined(__CYGWIN__) |
+ EnterCriticalSection(&exit_status_cs); |
+#endif |
+ |
+ if (exit_status != E_ERROR) |
+ exit_status = new_status; |
+ |
+#if defined(_WIN32) && !defined(__CYGWIN__) |
+ LeaveCriticalSection(&exit_status_cs); |
+#endif |
+ |
+ return; |
+} |
+ |
+ |
+extern void |
+set_exit_no_warn(void) |
+{ |
+ no_warn = true; |
+ return; |
+} |
+ |
+ |
+static const char * |
+read_name(const args_info *args) |
+{ |
+ // FIXME: Maybe we should have some kind of memory usage limit here |
+ // like the tool has for the actual compression and decompression. |
+ // Giving some huge text file with --files0 makes us to read the |
+ // whole file in RAM. |
+ static char *name = NULL; |
+ static size_t size = 256; |
+ |
+ // Allocate the initial buffer. This is never freed, since after it |
+ // is no longer needed, the program exits very soon. It is safe to |
+ // use xmalloc() and xrealloc() in this function, because while |
+ // executing this function, no files are open for writing, and thus |
+ // there's no need to cleanup anything before exiting. |
+ if (name == NULL) |
+ name = xmalloc(size); |
+ |
+ // Write position in name |
+ size_t pos = 0; |
+ |
+ // Read one character at a time into name. |
+ while (!user_abort) { |
+ const int c = fgetc(args->files_file); |
+ |
+ if (ferror(args->files_file)) { |
+ // Take care of EINTR since we have established |
+ // the signal handlers already. |
+ if (errno == EINTR) |
+ continue; |
+ |
+ message_error(_("%s: Error reading filenames: %s"), |
+ args->files_name, strerror(errno)); |
+ return NULL; |
+ } |
+ |
+ if (feof(args->files_file)) { |
+ if (pos != 0) |
+ message_error(_("%s: Unexpected end of input " |
+ "when reading filenames"), |
+ args->files_name); |
+ |
+ return NULL; |
+ } |
+ |
+ if (c == args->files_delim) { |
+ // We allow consecutive newline (--files) or '\0' |
+ // characters (--files0), and ignore such empty |
+ // filenames. |
+ if (pos == 0) |
+ continue; |
+ |
+ // A non-empty name was read. Terminate it with '\0' |
+ // and return it. |
+ name[pos] = '\0'; |
+ return name; |
+ } |
+ |
+ if (c == '\0') { |
+ // A null character was found when using --files, |
+ // which expects plain text input separated with |
+ // newlines. |
+ message_error(_("%s: Null character found when " |
+ "reading filenames; maybe you meant " |
+ "to use `--files0' instead " |
+ "of `--files'?"), args->files_name); |
+ return NULL; |
+ } |
+ |
+ name[pos++] = c; |
+ |
+ // Allocate more memory if needed. There must always be space |
+ // at least for one character to allow terminating the string |
+ // with '\0'. |
+ if (pos == size) { |
+ size *= 2; |
+ name = xrealloc(name, size); |
+ } |
+ } |
+ |
+ return NULL; |
+} |
+ |
+ |
+int |
+main(int argc, char **argv) |
+{ |
+#if defined(_WIN32) && !defined(__CYGWIN__) |
+ InitializeCriticalSection(&exit_status_cs); |
+#endif |
+ |
+ // Set up the progname variable. |
+ tuklib_progname_init(argv); |
+ |
+ // Initialize the file I/O. This makes sure that |
+ // stdin, stdout, and stderr are something valid. |
+ io_init(); |
+ |
+ // Set up the locale and message translations. |
+ tuklib_gettext_init(PACKAGE, LOCALEDIR); |
+ |
+ // Initialize handling of error/warning/other messages. |
+ message_init(); |
+ |
+ // Set hardware-dependent default values. These can be overriden |
+ // on the command line, thus this must be done before args_parse(). |
+ hardware_init(); |
+ |
+ // Parse the command line arguments and get an array of filenames. |
+ // This doesn't return if something is wrong with the command line |
+ // arguments. If there are no arguments, one filename ("-") is still |
+ // returned to indicate stdin. |
+ args_info args; |
+ args_parse(&args, argc, argv); |
+ |
+ if (opt_mode != MODE_LIST && opt_robot) |
+ message_fatal(_("Compression and decompression with --robot " |
+ "are not supported yet.")); |
+ |
+ // Tell the message handling code how many input files there are if |
+ // we know it. This way the progress indicator can show it. |
+ if (args.files_name != NULL) |
+ message_set_files(0); |
+ else |
+ message_set_files(args.arg_count); |
+ |
+ // Refuse to write compressed data to standard output if it is |
+ // a terminal. |
+ if (opt_mode == MODE_COMPRESS) { |
+ if (opt_stdout || (args.arg_count == 1 |
+ && strcmp(args.arg_names[0], "-") == 0)) { |
+ if (is_tty_stdout()) { |
+ message_try_help(); |
+ tuklib_exit(E_ERROR, E_ERROR, false); |
+ } |
+ } |
+ } |
+ |
+ // Set up the signal handlers. We don't need these before we |
+ // start the actual action and not in --list mode, so this is |
+ // done after parsing the command line arguments. |
+ // |
+ // It's good to keep signal handlers in normal compression and |
+ // decompression modes even when only writing to stdout, because |
+ // we might need to restore O_APPEND flag on stdout before exiting. |
+ // In --test mode, signal handlers aren't really needed, but let's |
+ // keep them there for consistency with normal decompression. |
+ if (opt_mode != MODE_LIST) |
+ signals_init(); |
+ |
+ // coder_run() handles compression, decompression, and testing. |
+ // list_file() is for --list. |
+ void (*run)(const char *filename) = opt_mode == MODE_LIST |
+ ? &list_file : &coder_run; |
+ |
+ // Process the files given on the command line. Note that if no names |
+ // were given, args_parse() gave us a fake "-" filename. |
+ for (size_t i = 0; i < args.arg_count && !user_abort; ++i) { |
+ if (strcmp("-", args.arg_names[i]) == 0) { |
+ // Processing from stdin to stdout. Check that we |
+ // aren't writing compressed data to a terminal or |
+ // reading it from a terminal. |
+ if (opt_mode == MODE_COMPRESS) { |
+ if (is_tty_stdout()) |
+ continue; |
+ } else if (is_tty_stdin()) { |
+ continue; |
+ } |
+ |
+ // It doesn't make sense to compress data from stdin |
+ // if we are supposed to read filenames from stdin |
+ // too (enabled with --files or --files0). |
+ if (args.files_name == stdin_filename) { |
+ message_error(_("Cannot read data from " |
+ "standard input when " |
+ "reading filenames " |
+ "from standard input")); |
+ continue; |
+ } |
+ |
+ // Replace the "-" with a special pointer, which is |
+ // recognized by coder_run() and other things. |
+ // This way error messages get a proper filename |
+ // string and the code still knows that it is |
+ // handling the special case of stdin. |
+ args.arg_names[i] = (char *)stdin_filename; |
+ } |
+ |
+ // Do the actual compression or decompression. |
+ run(args.arg_names[i]); |
+ } |
+ |
+ // If --files or --files0 was used, process the filenames from the |
+ // given file or stdin. Note that here we don't consider "-" to |
+ // indicate stdin like we do with the command line arguments. |
+ if (args.files_name != NULL) { |
+ // read_name() checks for user_abort so we don't need to |
+ // check it as loop termination condition. |
+ while (true) { |
+ const char *name = read_name(&args); |
+ if (name == NULL) |
+ break; |
+ |
+ // read_name() doesn't return empty names. |
+ assert(name[0] != '\0'); |
+ run(name); |
+ } |
+ |
+ if (args.files_name != stdin_filename) |
+ (void)fclose(args.files_file); |
+ } |
+ |
+ // All files have now been handled. If in --list mode, display |
+ // the totals before exiting. We don't have signal handlers |
+ // enabled in --list mode, so we don't need to check user_abort. |
+ if (opt_mode == MODE_LIST) { |
+ assert(!user_abort); |
+ list_totals(); |
+ } |
+ |
+ // If we have got a signal, raise it to kill the program instead |
+ // of calling tuklib_exit(). |
+ signals_exit(); |
+ |
+ // Make a local copy of exit_status to keep the Windows code |
+ // thread safe. At this point it is fine if we miss the user |
+ // pressing C-c and don't set the exit_status to E_ERROR on |
+ // Windows. |
+#if defined(_WIN32) && !defined(__CYGWIN__) |
+ EnterCriticalSection(&exit_status_cs); |
+#endif |
+ |
+ enum exit_status_type es = exit_status; |
+ |
+#if defined(_WIN32) && !defined(__CYGWIN__) |
+ LeaveCriticalSection(&exit_status_cs); |
+#endif |
+ |
+ // Suppress the exit status indicating a warning if --no-warn |
+ // was specified. |
+ if (es == E_WARNING && no_warn) |
+ es = E_SUCCESS; |
+ |
+ tuklib_exit(es, E_ERROR, message_verbosity_get() != V_SILENT); |
+} |
Property changes on: xz/src/xz/main.c |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |