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 ///////////////////////////////////////////////////////////////////////////////
21 #include "open_stdxxx.h"
25 volatile sig_atomic_t user_abort = false;
27 /// Exit status to use. This can be changed with set_exit_status().
28 static enum exit_status_type exit_status = E_SUCCESS;
30 /// If we were interrupted by a signal, we store the signal number so that
31 /// we can raise that signal to kill the program when all cleanups have
33 static volatile sig_atomic_t exit_signal = 0;
35 /// Mask of signals for which have have established a signal handler to set
36 /// user_abort to true.
37 static sigset_t hooked_signals;
39 /// signals_block() and signals_unblock() can be called recursively.
40 static size_t signals_block_count = 0;
44 signal_handler(int sig)
53 establish_signal_handlers(void)
55 // List of signals for which we establish the signal handler.
56 static const int sigs[] = {
73 // Mask of the signals for which we have established a signal handler.
74 sigemptyset(&hooked_signals);
75 for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i)
76 sigaddset(&hooked_signals, sigs[i]);
80 // All the signals that we handle we also blocked while the signal
82 sa.sa_mask = hooked_signals;
84 // Don't set SA_RESTART, because we want EINTR so that we can check
85 // for user_abort and cleanup before exiting. We block the signals
86 // for which we have established a handler when we don't want EINTR.
88 sa.sa_handler = &signal_handler;
90 for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) {
91 // If the parent process has left some signals ignored,
92 // we don't unignore them.
94 if (sigaction(sigs[i], NULL, &old) == 0
95 && old.sa_handler == SIG_IGN)
98 // Establish the signal handler.
99 if (sigaction(sigs[i], &sa, NULL))
100 message_signal_handler();
110 if (signals_block_count++ == 0) {
111 const int saved_errno = errno;
112 sigprocmask(SIG_BLOCK, &hooked_signals, NULL);
121 signals_unblock(void)
123 assert(signals_block_count > 0);
125 if (--signals_block_count == 0) {
126 const int saved_errno = errno;
127 sigprocmask(SIG_UNBLOCK, &hooked_signals, NULL);
136 set_exit_status(enum exit_status_type new_status)
138 assert(new_status == E_WARNING || new_status == E_ERROR);
140 if (exit_status != E_ERROR)
141 exit_status = new_status;
148 my_exit(enum exit_status_type status)
150 // Close stdout. If something goes wrong, print an error message
153 const int ferror_err = ferror(stdout);
154 const int fclose_err = fclose(stdout);
155 if (ferror_err || fclose_err) {
156 // If it was fclose() that failed, we have the reason
157 // in errno. If only ferror() indicated an error,
158 // we have no idea what the reason was.
159 message(V_ERROR, _("Writing to standard output "
161 fclose_err ? strerror(errno)
162 : _("Unknown error"));
167 // Close stderr. If something goes wrong, there's nothing where we
168 // could print an error message. Just set the exit status.
170 const int ferror_err = ferror(stderr);
171 const int fclose_err = fclose(stderr);
172 if (fclose_err || ferror_err)
176 // If we have got a signal, raise it to kill the program.
177 const int sig = exit_signal;
180 sa.sa_handler = SIG_DFL;
181 sigfillset(&sa.sa_mask);
183 sigaction(sig, &sa, NULL);
186 // If, for some weird reason, the signal doesn't kill us,
187 // we safely fall to the exit below.
195 read_name(const args_info *args)
197 // FIXME: Maybe we should have some kind of memory usage limit here
198 // like the tool has for the actual compression and uncompression.
199 // Giving some huge text file with --files0 makes us to read the
200 // whole file in RAM.
201 static char *name = NULL;
202 static size_t size = 256;
204 // Allocate the initial buffer. This is never freed, since after it
205 // is no longer needed, the program exits very soon. It is safe to
206 // use xmalloc() and xrealloc() in this function, because while
207 // executing this function, no files are open for writing, and thus
208 // there's no need to cleanup anything before exiting.
210 name = xmalloc(size);
212 // Write position in name
215 // Read one character at a time into name.
216 while (!user_abort) {
217 const int c = fgetc(args->files_file);
219 if (ferror(args->files_file)) {
220 // Take care of EINTR since we have established
221 // the signal handlers already.
225 message_error(_("%s: Error reading filenames: %s"),
226 args->files_name, strerror(errno));
230 if (feof(args->files_file)) {
232 message_error(_("%s: Unexpected end of input "
233 "when reading filenames"),
239 if (c == args->files_delim) {
240 // We allow consecutive newline (--files) or '\0'
241 // characters (--files0), and ignore such empty
246 // A non-empty name was read. Terminate it with '\0'
253 // A null character was found when using --files,
254 // which expects plain text input separated with
256 message_error(_("%s: Null character found when "
257 "reading filenames; maybe you meant "
258 "to use `--files0' instead "
259 "of `--files'?"), args->files_name);
265 // Allocate more memory if needed. There must always be space
266 // at least for one character to allow terminating the string
270 name = xrealloc(name, size);
279 main(int argc, char **argv)
281 // Make sure that stdin, stdout, and and stderr are connected to
282 // a valid file descriptor. Exit immediatelly with exit code ERROR
283 // if we cannot make the file descriptors valid. Maybe we should
284 // print an error message, but our stderr could be screwed anyway.
285 open_stdxxx(E_ERROR);
287 // This has to be done before calling any liblzma functions.
290 // Set up the locale.
291 setlocale(LC_ALL, "");
294 // Set up the message translations too.
295 bindtextdomain(PACKAGE, LOCALEDIR);
299 // Set the program invocation name used in various messages, and
300 // do other message handling related initializations.
301 message_init(argv[0]);
303 // Set hardware-dependent default values. These can be overriden
304 // on the command line, thus this must be done before parse_args().
307 // Parse the command line arguments and get an array of filenames.
308 // This doesn't return if something is wrong with the command line
309 // arguments. If there are no arguments, one filename ("-") is still
310 // returned to indicate stdin.
312 args_parse(&args, argc, argv);
314 // Tell the message handling code how many input files there are if
315 // we know it. This way the progress indicator can show it.
316 if (args.files_name != NULL)
317 message_set_files(0);
319 message_set_files(args.arg_count);
321 // Refuse to write compressed data to standard output if it is
322 // a terminal and --force wasn't used.
323 if (opt_mode == MODE_COMPRESS) {
324 if (opt_stdout || (args.arg_count == 1
325 && strcmp(args.arg_names[0], "-") == 0)) {
326 if (is_tty_stdout()) {
333 if (opt_mode == MODE_LIST) {
334 message_fatal("--list is not implemented yet.");
337 // Hook the signal handlers. We don't need these before we start
338 // the actual action, so this is done after parsing the command
340 establish_signal_handlers();
342 // Process the files given on the command line. Note that if no names
343 // were given, parse_args() gave us a fake "-" filename.
344 for (size_t i = 0; i < args.arg_count && !user_abort; ++i) {
345 if (strcmp("-", args.arg_names[i]) == 0) {
346 // Processing from stdin to stdout. Unless --force
347 // was used, check that we aren't writing compressed
348 // data to a terminal or reading it from terminal.
350 if (opt_mode == MODE_COMPRESS) {
353 } else if (is_tty_stdin()) {
358 // It doesn't make sense to compress data from stdin
359 // if we are supposed to read filenames from stdin
360 // too (enabled with --files or --files0).
361 if (args.files_name == stdin_filename) {
362 message_error(_("Cannot read data from "
363 "standard input when "
365 "from standard input"));
369 // Replace the "-" with a special pointer, which is
370 // recognized by process_file() and other things.
371 // This way error messages get a proper filename
372 // string and the code still knows that it is
373 // handling the special case of stdin.
374 args.arg_names[i] = (char *)stdin_filename;
377 // Do the actual compression or uncompression.
378 process_file(args.arg_names[i]);
381 // If --files or --files0 was used, process the filenames from the
382 // given file or stdin. Note that here we don't consider "-" to
383 // indicate stdin like we do with the command line arguments.
384 if (args.files_name != NULL) {
385 // read_name() checks for user_abort so we don't need to
386 // check it as loop termination condition.
388 const char *name = read_name(&args);
392 // read_name() doesn't return empty names.
393 assert(name[0] != '\0');
397 if (args.files_name != stdin_filename)
398 (void)fclose(args.files_file);
401 my_exit(exit_status);