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;
22 set_exit_status(enum exit_status_type new_status)
24 assert(new_status == E_WARNING || new_status == E_ERROR);
26 if (exit_status != E_ERROR)
27 exit_status = new_status;
34 my_exit(enum exit_status_type status)
36 // Close stdout. If something goes wrong, print an error message
39 const int ferror_err = ferror(stdout);
40 const int fclose_err = fclose(stdout);
41 if (ferror_err || fclose_err) {
42 // If it was fclose() that failed, we have the reason
43 // in errno. If only ferror() indicated an error,
44 // we have no idea what the reason was.
45 message(V_ERROR, _("Writing to standard output "
47 fclose_err ? strerror(errno)
48 : _("Unknown error"));
53 // Close stderr. If something goes wrong, there's nothing where we
54 // could print an error message. Just set the exit status.
56 const int ferror_err = ferror(stderr);
57 const int fclose_err = fclose(stderr);
58 if (fclose_err || ferror_err)
62 // If we have got a signal, raise it to kill the program.
63 // Otherwise we just call exit().
70 read_name(const args_info *args)
72 // FIXME: Maybe we should have some kind of memory usage limit here
73 // like the tool has for the actual compression and uncompression.
74 // Giving some huge text file with --files0 makes us to read the
76 static char *name = NULL;
77 static size_t size = 256;
79 // Allocate the initial buffer. This is never freed, since after it
80 // is no longer needed, the program exits very soon. It is safe to
81 // use xmalloc() and xrealloc() in this function, because while
82 // executing this function, no files are open for writing, and thus
83 // there's no need to cleanup anything before exiting.
87 // Write position in name
90 // Read one character at a time into name.
92 const int c = fgetc(args->files_file);
94 if (ferror(args->files_file)) {
95 // Take care of EINTR since we have established
96 // the signal handlers already.
100 message_error(_("%s: Error reading filenames: %s"),
101 args->files_name, strerror(errno));
105 if (feof(args->files_file)) {
107 message_error(_("%s: Unexpected end of input "
108 "when reading filenames"),
114 if (c == args->files_delim) {
115 // We allow consecutive newline (--files) or '\0'
116 // characters (--files0), and ignore such empty
121 // A non-empty name was read. Terminate it with '\0'
128 // A null character was found when using --files,
129 // which expects plain text input separated with
131 message_error(_("%s: Null character found when "
132 "reading filenames; maybe you meant "
133 "to use `--files0' instead "
134 "of `--files'?"), args->files_name);
140 // Allocate more memory if needed. There must always be space
141 // at least for one character to allow terminating the string
145 name = xrealloc(name, size);
154 main(int argc, char **argv)
156 // Initialize the file I/O as the very first step. This makes sure
157 // that stdin, stdout, and stderr are something valid.
161 // Adjust argv[0] to make it look nicer in messages, and also to
162 // help the code in args.c.
164 // Strip the leading path.
165 char *p = argv[0] + strlen(argv[0]);
166 while (argv[0] < p && p[-1] != '/' && p[-1] != '\\')
171 // Strip the .exe suffix.
176 // Make it lowercase.
177 for (p = argv[0]; *p != '\0'; ++p)
178 if (*p >= 'A' && *p <= 'Z')
183 // Set up the locale.
184 setlocale(LC_ALL, "");
187 // Set up the message translations too.
188 bindtextdomain(PACKAGE, LOCALEDIR);
192 // Set the program invocation name used in various messages, and
193 // do other message handling related initializations.
194 message_init(argv[0]);
196 // Set hardware-dependent default values. These can be overriden
197 // on the command line, thus this must be done before parse_args().
200 // Parse the command line arguments and get an array of filenames.
201 // This doesn't return if something is wrong with the command line
202 // arguments. If there are no arguments, one filename ("-") is still
203 // returned to indicate stdin.
205 args_parse(&args, argc, argv);
207 // Tell the message handling code how many input files there are if
208 // we know it. This way the progress indicator can show it.
209 if (args.files_name != NULL)
210 message_set_files(0);
212 message_set_files(args.arg_count);
214 // Refuse to write compressed data to standard output if it is
215 // a terminal and --force wasn't used.
216 if (opt_mode == MODE_COMPRESS && !opt_force) {
217 if (opt_stdout || (args.arg_count == 1
218 && strcmp(args.arg_names[0], "-") == 0)) {
219 if (is_tty_stdout()) {
226 if (opt_mode == MODE_LIST) {
227 message_fatal("--list is not implemented yet.");
230 // Hook the signal handlers. We don't need these before we start
231 // the actual action, so this is done after parsing the command
235 // Process the files given on the command line. Note that if no names
236 // were given, parse_args() gave us a fake "-" filename.
237 for (size_t i = 0; i < args.arg_count && !user_abort; ++i) {
238 if (strcmp("-", args.arg_names[i]) == 0) {
239 // Processing from stdin to stdout. Unless --force
240 // was used, check that we aren't writing compressed
241 // data to a terminal or reading it from terminal.
243 if (opt_mode == MODE_COMPRESS) {
246 } else if (is_tty_stdin()) {
251 // It doesn't make sense to compress data from stdin
252 // if we are supposed to read filenames from stdin
253 // too (enabled with --files or --files0).
254 if (args.files_name == stdin_filename) {
255 message_error(_("Cannot read data from "
256 "standard input when "
258 "from standard input"));
262 // Replace the "-" with a special pointer, which is
263 // recognized by process_file() and other things.
264 // This way error messages get a proper filename
265 // string and the code still knows that it is
266 // handling the special case of stdin.
267 args.arg_names[i] = (char *)stdin_filename;
270 // Do the actual compression or uncompression.
271 process_file(args.arg_names[i]);
274 // If --files or --files0 was used, process the filenames from the
275 // given file or stdin. Note that here we don't consider "-" to
276 // indicate stdin like we do with the command line arguments.
277 if (args.files_name != NULL) {
278 // read_name() checks for user_abort so we don't need to
279 // check it as loop termination condition.
281 const char *name = read_name(&args);
285 // read_name() doesn't return empty names.
286 assert(name[0] != '\0');
290 if (args.files_name != stdin_filename)
291 (void)fclose(args.files_file);
294 my_exit(exit_status);