1 ///////////////////////////////////////////////////////////////////////////////
6 // Author: Lasse Collin
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
16 /// Exit status to use. This can be changed with set_exit_status().
17 static enum exit_status_type exit_status
= E_SUCCESS
;
19 #if defined(_WIN32) && !defined(__CYGWIN__)
20 /// exit_status has to be protected with a critical section due to
21 /// how "signal handling" is done on Windows. See signals.c for details.
22 static CRITICAL_SECTION exit_status_cs
;
25 /// True if --no-warn is specified. When this is true, we don't set
26 /// the exit status to E_WARNING when something worth a warning happens.
27 static bool no_warn
= false;
31 set_exit_status(enum exit_status_type new_status
)
33 assert(new_status
== E_WARNING
|| new_status
== E_ERROR
);
35 #if defined(_WIN32) && !defined(__CYGWIN__)
36 EnterCriticalSection(&exit_status_cs
);
39 if (exit_status
!= E_ERROR
)
40 exit_status
= new_status
;
42 #if defined(_WIN32) && !defined(__CYGWIN__)
43 LeaveCriticalSection(&exit_status_cs
);
51 set_exit_no_warn(void)
59 read_name(const args_info
*args
)
61 // FIXME: Maybe we should have some kind of memory usage limit here
62 // like the tool has for the actual compression and decompression.
63 // Giving some huge text file with --files0 makes us to read the
65 static char *name
= NULL
;
66 static size_t size
= 256;
68 // Allocate the initial buffer. This is never freed, since after it
69 // is no longer needed, the program exits very soon. It is safe to
70 // use xmalloc() and xrealloc() in this function, because while
71 // executing this function, no files are open for writing, and thus
72 // there's no need to cleanup anything before exiting.
76 // Write position in name
79 // Read one character at a time into name.
81 const int c
= fgetc(args
->files_file
);
83 if (ferror(args
->files_file
)) {
84 // Take care of EINTR since we have established
85 // the signal handlers already.
89 message_error(_("%s: Error reading filenames: %s"),
90 args
->files_name
, strerror(errno
));
94 if (feof(args
->files_file
)) {
96 message_error(_("%s: Unexpected end of input "
97 "when reading filenames"),
103 if (c
== args
->files_delim
) {
104 // We allow consecutive newline (--files) or '\0'
105 // characters (--files0), and ignore such empty
110 // A non-empty name was read. Terminate it with '\0'
117 // A null character was found when using --files,
118 // which expects plain text input separated with
120 message_error(_("%s: Null character found when "
121 "reading filenames; maybe you meant "
122 "to use `--files0' instead "
123 "of `--files'?"), args
->files_name
);
129 // Allocate more memory if needed. There must always be space
130 // at least for one character to allow terminating the string
134 name
= xrealloc(name
, size
);
143 main(int argc
, char **argv
)
145 #if defined(_WIN32) && !defined(__CYGWIN__)
146 InitializeCriticalSection(&exit_status_cs
);
149 // Set up the progname variable.
150 tuklib_progname_init(argv
);
152 // Initialize the file I/O. This makes sure that
153 // stdin, stdout, and stderr are something valid.
156 // Set up the locale and message translations.
157 tuklib_gettext_init(PACKAGE
, LOCALEDIR
);
159 // Initialize handling of error/warning/other messages.
162 // Set hardware-dependent default values. These can be overriden
163 // on the command line, thus this must be done before args_parse().
166 // Parse the command line arguments and get an array of filenames.
167 // This doesn't return if something is wrong with the command line
168 // arguments. If there are no arguments, one filename ("-") is still
169 // returned to indicate stdin.
171 args_parse(&args
, argc
, argv
);
173 if (opt_mode
!= MODE_LIST
&& opt_robot
)
174 message_fatal(_("Compression and decompression with --robot "
175 "are not supported yet."));
177 // Tell the message handling code how many input files there are if
178 // we know it. This way the progress indicator can show it.
179 if (args
.files_name
!= NULL
)
180 message_set_files(0);
182 message_set_files(args
.arg_count
);
184 // Refuse to write compressed data to standard output if it is
186 if (opt_mode
== MODE_COMPRESS
) {
187 if (opt_stdout
|| (args
.arg_count
== 1
188 && strcmp(args
.arg_names
[0], "-") == 0)) {
189 if (is_tty_stdout()) {
191 tuklib_exit(E_ERROR
, E_ERROR
, false);
196 // Set up the signal handlers. We don't need these before we
197 // start the actual action and not in --list mode, so this is
198 // done after parsing the command line arguments.
200 // It's good to keep signal handlers in normal compression and
201 // decompression modes even when only writing to stdout, because
202 // we might need to restore O_APPEND flag on stdout before exiting.
203 // In --test mode, signal handlers aren't really needed, but let's
204 // keep them there for consistency with normal decompression.
205 if (opt_mode
!= MODE_LIST
)
208 // coder_run() handles compression, decompression, and testing.
209 // list_file() is for --list.
210 void (*run
)(const char *filename
) = opt_mode
== MODE_LIST
211 ? &list_file
: &coder_run
;
213 // Process the files given on the command line. Note that if no names
214 // were given, args_parse() gave us a fake "-" filename.
215 for (size_t i
= 0; i
< args
.arg_count
&& !user_abort
; ++i
) {
216 if (strcmp("-", args
.arg_names
[i
]) == 0) {
217 // Processing from stdin to stdout. Check that we
218 // aren't writing compressed data to a terminal or
219 // reading it from a terminal.
220 if (opt_mode
== MODE_COMPRESS
) {
223 } else if (is_tty_stdin()) {
227 // It doesn't make sense to compress data from stdin
228 // if we are supposed to read filenames from stdin
229 // too (enabled with --files or --files0).
230 if (args
.files_name
== stdin_filename
) {
231 message_error(_("Cannot read data from "
232 "standard input when "
234 "from standard input"));
238 // Replace the "-" with a special pointer, which is
239 // recognized by coder_run() and other things.
240 // This way error messages get a proper filename
241 // string and the code still knows that it is
242 // handling the special case of stdin.
243 args
.arg_names
[i
] = (char *)stdin_filename
;
246 // Do the actual compression or decompression.
247 run(args
.arg_names
[i
]);
250 // If --files or --files0 was used, process the filenames from the
251 // given file or stdin. Note that here we don't consider "-" to
252 // indicate stdin like we do with the command line arguments.
253 if (args
.files_name
!= NULL
) {
254 // read_name() checks for user_abort so we don't need to
255 // check it as loop termination condition.
257 const char *name
= read_name(&args
);
261 // read_name() doesn't return empty names.
262 assert(name
[0] != '\0');
266 if (args
.files_name
!= stdin_filename
)
267 (void)fclose(args
.files_file
);
270 // All files have now been handled. If in --list mode, display
271 // the totals before exiting. We don't have signal handlers
272 // enabled in --list mode, so we don't need to check user_abort.
273 if (opt_mode
== MODE_LIST
) {
278 // If we have got a signal, raise it to kill the program instead
279 // of calling tuklib_exit().
282 // Make a local copy of exit_status to keep the Windows code
283 // thread safe. At this point it is fine if we miss the user
284 // pressing C-c and don't set the exit_status to E_ERROR on
286 #if defined(_WIN32) && !defined(__CYGWIN__)
287 EnterCriticalSection(&exit_status_cs
);
290 enum exit_status_type es
= exit_status
;
292 #if defined(_WIN32) && !defined(__CYGWIN__)
293 LeaveCriticalSection(&exit_status_cs
);
296 // Suppress the exit status indicating a warning if --no-warn
298 if (es
== E_WARNING
&& no_warn
)
301 tuklib_exit(es
, E_ERROR
, message_verbosity_get() != V_SILENT
);