1 ///////////////////////////////////////////////////////////////////////////////
6 // Copyright (C) 2007 Lasse Collin
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
18 ///////////////////////////////////////////////////////////////////////////////
24 /// Exit status to use. This can be changed with set_exit_status().
25 static enum exit_status_type exit_status = E_SUCCESS;
29 set_exit_status(enum exit_status_type new_status)
31 assert(new_status == E_WARNING || new_status == E_ERROR);
33 if (exit_status != E_ERROR)
34 exit_status = new_status;
41 my_exit(enum exit_status_type status)
43 // Close stdout. If something goes wrong, print an error message
46 const int ferror_err = ferror(stdout);
47 const int fclose_err = fclose(stdout);
48 if (ferror_err || fclose_err) {
49 // If it was fclose() that failed, we have the reason
50 // in errno. If only ferror() indicated an error,
51 // we have no idea what the reason was.
52 message(V_ERROR, _("Writing to standard output "
54 fclose_err ? strerror(errno)
55 : _("Unknown error"));
60 // Close stderr. If something goes wrong, there's nothing where we
61 // could print an error message. Just set the exit status.
63 const int ferror_err = ferror(stderr);
64 const int fclose_err = fclose(stderr);
65 if (fclose_err || ferror_err)
69 // If we have got a signal, raise it to kill the program.
70 // Otherwise we just call exit().
77 read_name(const args_info *args)
79 // FIXME: Maybe we should have some kind of memory usage limit here
80 // like the tool has for the actual compression and uncompression.
81 // Giving some huge text file with --files0 makes us to read the
83 static char *name = NULL;
84 static size_t size = 256;
86 // Allocate the initial buffer. This is never freed, since after it
87 // is no longer needed, the program exits very soon. It is safe to
88 // use xmalloc() and xrealloc() in this function, because while
89 // executing this function, no files are open for writing, and thus
90 // there's no need to cleanup anything before exiting.
94 // Write position in name
97 // Read one character at a time into name.
99 const int c = fgetc(args->files_file);
101 if (ferror(args->files_file)) {
102 // Take care of EINTR since we have established
103 // the signal handlers already.
107 message_error(_("%s: Error reading filenames: %s"),
108 args->files_name, strerror(errno));
112 if (feof(args->files_file)) {
114 message_error(_("%s: Unexpected end of input "
115 "when reading filenames"),
121 if (c == args->files_delim) {
122 // We allow consecutive newline (--files) or '\0'
123 // characters (--files0), and ignore such empty
128 // A non-empty name was read. Terminate it with '\0'
135 // A null character was found when using --files,
136 // which expects plain text input separated with
138 message_error(_("%s: Null character found when "
139 "reading filenames; maybe you meant "
140 "to use `--files0' instead "
141 "of `--files'?"), args->files_name);
147 // Allocate more memory if needed. There must always be space
148 // at least for one character to allow terminating the string
152 name = xrealloc(name, size);
161 main(int argc, char **argv)
163 // Initialize the file I/O as the very first step. This makes sure
164 // that stdin, stdout, and stderr are something valid.
167 // Set up the locale.
168 setlocale(LC_ALL, "");
171 // Set up the message translations too.
172 bindtextdomain(PACKAGE, LOCALEDIR);
176 // Set the program invocation name used in various messages, and
177 // do other message handling related initializations.
178 message_init(argv[0]);
180 // Set hardware-dependent default values. These can be overriden
181 // on the command line, thus this must be done before parse_args().
184 // Parse the command line arguments and get an array of filenames.
185 // This doesn't return if something is wrong with the command line
186 // arguments. If there are no arguments, one filename ("-") is still
187 // returned to indicate stdin.
189 args_parse(&args, argc, argv);
191 // Tell the message handling code how many input files there are if
192 // we know it. This way the progress indicator can show it.
193 if (args.files_name != NULL)
194 message_set_files(0);
196 message_set_files(args.arg_count);
198 // Refuse to write compressed data to standard output if it is
199 // a terminal and --force wasn't used.
200 if (opt_mode == MODE_COMPRESS && !opt_force) {
201 if (opt_stdout || (args.arg_count == 1
202 && strcmp(args.arg_names[0], "-") == 0)) {
203 if (is_tty_stdout()) {
210 if (opt_mode == MODE_LIST) {
211 message_fatal("--list is not implemented yet.");
214 // Hook the signal handlers. We don't need these before we start
215 // the actual action, so this is done after parsing the command
219 // Process the files given on the command line. Note that if no names
220 // were given, parse_args() gave us a fake "-" filename.
221 for (size_t i = 0; i < args.arg_count && !user_abort; ++i) {
222 if (strcmp("-", args.arg_names[i]) == 0) {
223 // Processing from stdin to stdout. Unless --force
224 // was used, check that we aren't writing compressed
225 // data to a terminal or reading it from terminal.
227 if (opt_mode == MODE_COMPRESS) {
230 } else if (is_tty_stdin()) {
235 // It doesn't make sense to compress data from stdin
236 // if we are supposed to read filenames from stdin
237 // too (enabled with --files or --files0).
238 if (args.files_name == stdin_filename) {
239 message_error(_("Cannot read data from "
240 "standard input when "
242 "from standard input"));
246 // Replace the "-" with a special pointer, which is
247 // recognized by process_file() and other things.
248 // This way error messages get a proper filename
249 // string and the code still knows that it is
250 // handling the special case of stdin.
251 args.arg_names[i] = (char *)stdin_filename;
254 // Do the actual compression or uncompression.
255 process_file(args.arg_names[i]);
258 // If --files or --files0 was used, process the filenames from the
259 // given file or stdin. Note that here we don't consider "-" to
260 // indicate stdin like we do with the command line arguments.
261 if (args.files_name != NULL) {
262 // read_name() checks for user_abort so we don't need to
263 // check it as loop termination condition.
265 const char *name = read_name(&args);
269 // read_name() doesn't return empty names.
270 assert(name[0] != '\0');
274 if (args.files_name != stdin_filename)
275 (void)fclose(args.files_file);
278 my_exit(exit_status);