]> icculus.org git repositories - icculus/xz.git/blob - src/lzma/main.c
Imported to git.
[icculus/xz.git] / src / lzma / main.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       main.c
4 /// \brief      main()
5 //
6 //  Copyright (C) 2007 Lasse Collin
7 //
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.
12 //
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.
17 //
18 ///////////////////////////////////////////////////////////////////////////////
19
20 #include "private.h"
21 #include "open_stdxxx.h"
22 #include <ctype.h>
23
24 static sig_atomic_t exit_signal = 0;
25
26
27 static void
28 signal_handler(int sig)
29 {
30         // FIXME Is this thread-safe together with main()?
31         exit_signal = sig;
32
33         user_abort = 1;
34         return;
35 }
36
37
38 static void
39 establish_signal_handlers(void)
40 {
41         struct sigaction sa;
42         sa.sa_handler = &signal_handler;
43         sigfillset(&sa.sa_mask);
44         sa.sa_flags = 0;
45
46         static const int sigs[] = {
47                 SIGHUP,
48                 SIGINT,
49                 SIGPIPE,
50                 SIGTERM,
51                 SIGXCPU,
52                 SIGXFSZ,
53         };
54
55         for (size_t i = 0; i < sizeof(sigs) / sizeof(sigs[0]); ++i) {
56                 if (sigaction(sigs[i], &sa, NULL)) {
57                         errmsg(V_ERROR, _("Cannot establish signal handlers"));
58                         my_exit(ERROR);
59                 }
60         }
61
62         /*
63         SIGINFO/SIGUSR1 for status reporting?
64         */
65 }
66
67
68 static bool
69 is_tty_stdin(void)
70 {
71         const bool ret = isatty(STDIN_FILENO);
72         if (ret) {
73                 // FIXME: Other threads may print between these lines.
74                 // Maybe that should be fixed. Not a big issue in practice.
75                 errmsg(V_ERROR, _("Compressed data not read from "
76                                 "a terminal."));
77                 errmsg(V_ERROR, _("Use `--force' to force decompression."));
78                 show_try_help();
79         }
80
81         return ret;
82 }
83
84
85 static bool
86 is_tty_stdout(void)
87 {
88         const bool ret = isatty(STDOUT_FILENO);
89         if (ret) {
90                 errmsg(V_ERROR, _("Compressed data not written to "
91                                 "a terminal."));
92                 errmsg(V_ERROR, _("Use `--force' to force decompression."));
93                 show_try_help();
94         }
95
96         return ret;
97 }
98
99
100 static char *
101 read_name(void)
102 {
103         size_t size = 256;
104         size_t pos = 0;
105         char *name = malloc(size);
106         if (name == NULL) {
107                 out_of_memory();
108                 return NULL;
109         }
110
111         while (true) {
112                 const int c = fgetc(opt_files_file);
113                 if (c == EOF) {
114                         free(name);
115
116                         if (ferror(opt_files_file))
117                                 errmsg(V_ERROR, _("%s: Error reading "
118                                                 "filenames: %s"),
119                                                 opt_files_name,
120                                                 strerror(errno));
121                         else if (pos != 0)
122                                 errmsg(V_ERROR, _("%s: Unexpected end of "
123                                                 "input when reading "
124                                                 "filenames"), opt_files_name);
125
126                         return NULL;
127                 }
128
129                 if (c == '\0' || c == opt_files_split)
130                         break;
131
132                 name[pos++] = c;
133
134                 if (pos == size) {
135                         size *= 2;
136                         char *tmp = realloc(name, size);
137                         if (tmp == NULL) {
138                                 free(name);
139                                 out_of_memory();
140                                 return NULL;
141                         }
142
143                         name = tmp;
144                 }
145         }
146
147         if (name != NULL)
148                 name[pos] = '\0';
149
150         return name;
151 }
152
153
154 int
155 main(int argc, char **argv)
156 {
157         // Make sure that stdin, stdout, and and stderr are connected to
158         // a valid file descriptor. Exit immediatelly with exit code ERROR
159         // if we cannot make the file descriptors valid. Maybe we should
160         // print an error message, but our stderr could be screwed anyway.
161         open_stdxxx(ERROR);
162
163         // Set the program invocation name used in various messages.
164         argv0 = argv[0];
165
166         setlocale(LC_ALL, "en_US.UTF-8");
167         bindtextdomain(PACKAGE, LOCALEDIR);
168         textdomain(PACKAGE);
169
170         // Set hardware-dependent default values. These can be overriden
171         // on the command line, thus this must be done before parse_args().
172         hardware_init();
173
174         char **files = parse_args(argc, argv);
175
176         if (opt_mode == MODE_COMPRESS && opt_stdout && is_tty_stdout())
177                 return ERROR;
178
179         if (opt_mode == MODE_COMPRESS)
180                 lzma_init_encoder();
181         else
182                 lzma_init_decoder();
183
184         io_init();
185         process_init();
186
187         if (opt_mode == MODE_LIST) {
188                 errmsg(V_ERROR, "--list is not implemented yet.");
189                 my_exit(ERROR);
190         }
191
192         // Hook the signal handlers. We don't need these before we start
193         // the actual action, so this is done after parsing the command
194         // line arguments.
195         establish_signal_handlers();
196
197         while (*files != NULL && !user_abort) {
198                 if (strcmp("-", *files) == 0) {
199                         if (!opt_force) {
200                                 if (opt_mode == MODE_COMPRESS) {
201                                         if (is_tty_stdout()) {
202                                                 ++files;
203                                                 continue;
204                                         }
205                                 } else if (is_tty_stdin()) {
206                                         ++files;
207                                         continue;
208                                 }
209                         }
210
211                         if (opt_files_name == stdin_filename) {
212                                 errmsg(V_ERROR, _("Cannot read data from "
213                                                 "standard input when "
214                                                 "reading filenames "
215                                                 "from standard input"));
216                                 ++files;
217                                 continue;
218                         }
219
220                         *files = (char *)stdin_filename;
221                 }
222
223                 process_file(*files++);
224         }
225
226         if (opt_files_name != NULL) {
227                 while (true) {
228                         char *name = read_name();
229                         if (name == NULL)
230                                 break;
231
232                         if (name[0] != '\0')
233                                 process_file(name);
234
235                         free(name);
236                 }
237
238                 if (opt_files_name != stdin_filename)
239                         (void)fclose(opt_files_file);
240         }
241
242         io_finish();
243
244         if (exit_signal != 0) {
245                 struct sigaction sa;
246                 sa.sa_handler = SIG_DFL;
247                 sigfillset(&sa.sa_mask);
248                 sa.sa_flags = 0;
249                 sigaction(exit_signal, &sa, NULL);
250                 raise(exit_signal);
251         }
252
253         my_exit(exit_status);
254 }