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 ///////////////////////////////////////////////////////////////////////////////
17 /// Exit status to use. This can be changed with set_exit_status().
18 static enum exit_status_type exit_status = E_SUCCESS;
20 /// True if --no-warn is specified. When this is true, we don't set
21 /// the exit status to E_WARNING when something worth a warning happens.
22 static bool no_warn = false;
26 set_exit_status(enum exit_status_type new_status)
28 assert(new_status == E_WARNING || new_status == E_ERROR);
30 if (exit_status != E_ERROR)
31 exit_status = new_status;
38 set_exit_no_warn(void)
46 read_name(const args_info *args)
48 // FIXME: Maybe we should have some kind of memory usage limit here
49 // like the tool has for the actual compression and uncompression.
50 // Giving some huge text file with --files0 makes us to read the
52 static char *name = NULL;
53 static size_t size = 256;
55 // Allocate the initial buffer. This is never freed, since after it
56 // is no longer needed, the program exits very soon. It is safe to
57 // use xmalloc() and xrealloc() in this function, because while
58 // executing this function, no files are open for writing, and thus
59 // there's no need to cleanup anything before exiting.
63 // Write position in name
66 // Read one character at a time into name.
68 const int c = fgetc(args->files_file);
70 if (ferror(args->files_file)) {
71 // Take care of EINTR since we have established
72 // the signal handlers already.
76 message_error(_("%s: Error reading filenames: %s"),
77 args->files_name, strerror(errno));
81 if (feof(args->files_file)) {
83 message_error(_("%s: Unexpected end of input "
84 "when reading filenames"),
90 if (c == args->files_delim) {
91 // We allow consecutive newline (--files) or '\0'
92 // characters (--files0), and ignore such empty
97 // A non-empty name was read. Terminate it with '\0'
104 // A null character was found when using --files,
105 // which expects plain text input separated with
107 message_error(_("%s: Null character found when "
108 "reading filenames; maybe you meant "
109 "to use `--files0' instead "
110 "of `--files'?"), args->files_name);
116 // Allocate more memory if needed. There must always be space
117 // at least for one character to allow terminating the string
121 name = xrealloc(name, size);
130 main(int argc, char **argv)
132 // Set up the progname variable.
133 tuklib_progname_init(argv);
135 // Initialize the file I/O. This makes sure that
136 // stdin, stdout, and stderr are something valid.
139 // Set up the locale and message translations.
140 tuklib_gettext_init(PACKAGE, LOCALEDIR);
142 // Initialize handling of error/warning/other messages.
145 // Set hardware-dependent default values. These can be overriden
146 // on the command line, thus this must be done before args_parse().
149 // Parse the command line arguments and get an array of filenames.
150 // This doesn't return if something is wrong with the command line
151 // arguments. If there are no arguments, one filename ("-") is still
152 // returned to indicate stdin.
154 args_parse(&args, argc, argv);
156 if (opt_mode != MODE_LIST && opt_robot)
157 message_fatal(_("Compression and decompression with --robot "
158 "are not supported yet."));
160 // Tell the message handling code how many input files there are if
161 // we know it. This way the progress indicator can show it.
162 if (args.files_name != NULL)
163 message_set_files(0);
165 message_set_files(args.arg_count);
167 // Refuse to write compressed data to standard output if it is
169 if (opt_mode == MODE_COMPRESS) {
170 if (opt_stdout || (args.arg_count == 1
171 && strcmp(args.arg_names[0], "-") == 0)) {
172 if (is_tty_stdout()) {
174 tuklib_exit(E_ERROR, E_ERROR, false);
179 // Set up the signal handlers. We don't need these before we
180 // start the actual action and not in --list mode, so this is
181 // done after parsing the command line arguments.
183 // It's good to keep signal handlers in normal compression and
184 // decompression modes even when only writing to stdout, because
185 // we might need to restore O_APPEND flag on stdout before exiting.
186 // In --test mode, signal handlers aren't really needed, but let's
187 // keep them there for consistency with normal decompression.
188 if (opt_mode != MODE_LIST)
191 // coder_run() handles compression, decopmression, and testing.
192 // list_file() is for --list.
193 void (*run)(const char *filename) = opt_mode == MODE_LIST
194 ? &list_file : &coder_run;
196 // Process the files given on the command line. Note that if no names
197 // were given, args_parse() gave us a fake "-" filename.
198 for (size_t i = 0; i < args.arg_count && !user_abort; ++i) {
199 if (strcmp("-", args.arg_names[i]) == 0) {
200 // Processing from stdin to stdout. Check that we
201 // aren't writing compressed data to a terminal or
202 // reading it from a terminal.
203 if (opt_mode == MODE_COMPRESS) {
206 } else if (is_tty_stdin()) {
210 // It doesn't make sense to compress data from stdin
211 // if we are supposed to read filenames from stdin
212 // too (enabled with --files or --files0).
213 if (args.files_name == stdin_filename) {
214 message_error(_("Cannot read data from "
215 "standard input when "
217 "from standard input"));
221 // Replace the "-" with a special pointer, which is
222 // recognized by coder_run() and other things.
223 // This way error messages get a proper filename
224 // string and the code still knows that it is
225 // handling the special case of stdin.
226 args.arg_names[i] = (char *)stdin_filename;
229 // Do the actual compression or uncompression.
230 run(args.arg_names[i]);
233 // If --files or --files0 was used, process the filenames from the
234 // given file or stdin. Note that here we don't consider "-" to
235 // indicate stdin like we do with the command line arguments.
236 if (args.files_name != NULL) {
237 // read_name() checks for user_abort so we don't need to
238 // check it as loop termination condition.
240 const char *name = read_name(&args);
244 // read_name() doesn't return empty names.
245 assert(name[0] != '\0');
249 if (args.files_name != stdin_filename)
250 (void)fclose(args.files_file);
253 // All files have now been handled. If in --list mode, display
254 // the totals before exiting. We don't have signal handlers
255 // enabled in --list mode, so we don't need to check user_abort.
256 if (opt_mode == MODE_LIST) {
261 // If we have got a signal, raise it to kill the program instead
262 // of calling tuklib_exit().
265 // Suppress the exit status indicating a warning if --no-warn
267 if (exit_status == E_WARNING && no_warn)
268 exit_status = E_SUCCESS;
270 tuklib_exit(exit_status, E_ERROR,
271 message_verbosity_get() != V_SILENT);