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 my_exit(enum exit_status_type status)
48 // Close stdout. If something goes wrong, print an error message
51 const int ferror_err = ferror(stdout);
52 const int fclose_err = fclose(stdout);
53 if (ferror_err || fclose_err) {
54 // If it was fclose() that failed, we have the reason
55 // in errno. If only ferror() indicated an error,
56 // we have no idea what the reason was.
57 message(V_ERROR, _("Writing to standard output "
59 fclose_err ? strerror(errno)
60 : _("Unknown error"));
65 // Close stderr. If something goes wrong, there's nothing where we
66 // could print an error message. Just set the exit status.
68 const int ferror_err = ferror(stderr);
69 const int fclose_err = fclose(stderr);
70 if (fclose_err || ferror_err)
74 // Suppress the exit status indicating a warning if --no-warn
76 if (status == E_WARNING && no_warn)
79 // If we have got a signal, raise it to kill the program.
80 // Otherwise we just call exit().
87 read_name(const args_info *args)
89 // FIXME: Maybe we should have some kind of memory usage limit here
90 // like the tool has for the actual compression and uncompression.
91 // Giving some huge text file with --files0 makes us to read the
93 static char *name = NULL;
94 static size_t size = 256;
96 // Allocate the initial buffer. This is never freed, since after it
97 // is no longer needed, the program exits very soon. It is safe to
98 // use xmalloc() and xrealloc() in this function, because while
99 // executing this function, no files are open for writing, and thus
100 // there's no need to cleanup anything before exiting.
102 name = xmalloc(size);
104 // Write position in name
107 // Read one character at a time into name.
108 while (!user_abort) {
109 const int c = fgetc(args->files_file);
111 if (ferror(args->files_file)) {
112 // Take care of EINTR since we have established
113 // the signal handlers already.
117 message_error(_("%s: Error reading filenames: %s"),
118 args->files_name, strerror(errno));
122 if (feof(args->files_file)) {
124 message_error(_("%s: Unexpected end of input "
125 "when reading filenames"),
131 if (c == args->files_delim) {
132 // We allow consecutive newline (--files) or '\0'
133 // characters (--files0), and ignore such empty
138 // A non-empty name was read. Terminate it with '\0'
145 // A null character was found when using --files,
146 // which expects plain text input separated with
148 message_error(_("%s: Null character found when "
149 "reading filenames; maybe you meant "
150 "to use `--files0' instead "
151 "of `--files'?"), args->files_name);
157 // Allocate more memory if needed. There must always be space
158 // at least for one character to allow terminating the string
162 name = xrealloc(name, size);
171 main(int argc, char **argv)
173 // Initialize the file I/O as the very first step. This makes sure
174 // that stdin, stdout, and stderr are something valid.
178 // Adjust argv[0] to make it look nicer in messages, and also to
179 // help the code in args.c.
181 // Strip the leading path.
182 char *p = argv[0] + strlen(argv[0]);
183 while (argv[0] < p && p[-1] != '/' && p[-1] != '\\')
188 // Strip the .exe suffix.
193 // Make it lowercase.
194 for (p = argv[0]; *p != '\0'; ++p)
195 if (*p >= 'A' && *p <= 'Z')
200 // Set up the locale.
201 setlocale(LC_ALL, "");
204 // Set up the message translations too.
205 bindtextdomain(PACKAGE, LOCALEDIR);
209 // Set the program invocation name used in various messages, and
210 // do other message handling related initializations.
211 message_init(argv[0]);
213 // Set hardware-dependent default values. These can be overriden
214 // on the command line, thus this must be done before parse_args().
217 // Parse the command line arguments and get an array of filenames.
218 // This doesn't return if something is wrong with the command line
219 // arguments. If there are no arguments, one filename ("-") is still
220 // returned to indicate stdin.
222 args_parse(&args, argc, argv);
224 // Tell the message handling code how many input files there are if
225 // we know it. This way the progress indicator can show it.
226 if (args.files_name != NULL)
227 message_set_files(0);
229 message_set_files(args.arg_count);
231 // Refuse to write compressed data to standard output if it is
232 // a terminal and --force wasn't used.
233 if (opt_mode == MODE_COMPRESS && !opt_force) {
234 if (opt_stdout || (args.arg_count == 1
235 && strcmp(args.arg_names[0], "-") == 0)) {
236 if (is_tty_stdout()) {
243 if (opt_mode == MODE_LIST) {
244 message_fatal("--list is not implemented yet.");
247 // Hook the signal handlers. We don't need these before we start
248 // the actual action, so this is done after parsing the command
252 // Process the files given on the command line. Note that if no names
253 // were given, parse_args() gave us a fake "-" filename.
254 for (size_t i = 0; i < args.arg_count && !user_abort; ++i) {
255 if (strcmp("-", args.arg_names[i]) == 0) {
256 // Processing from stdin to stdout. Unless --force
257 // was used, check that we aren't writing compressed
258 // data to a terminal or reading it from terminal.
260 if (opt_mode == MODE_COMPRESS) {
263 } else if (is_tty_stdin()) {
268 // It doesn't make sense to compress data from stdin
269 // if we are supposed to read filenames from stdin
270 // too (enabled with --files or --files0).
271 if (args.files_name == stdin_filename) {
272 message_error(_("Cannot read data from "
273 "standard input when "
275 "from standard input"));
279 // Replace the "-" with a special pointer, which is
280 // recognized by coder_run() and other things.
281 // This way error messages get a proper filename
282 // string and the code still knows that it is
283 // handling the special case of stdin.
284 args.arg_names[i] = (char *)stdin_filename;
287 // Do the actual compression or uncompression.
288 coder_run(args.arg_names[i]);
291 // If --files or --files0 was used, process the filenames from the
292 // given file or stdin. Note that here we don't consider "-" to
293 // indicate stdin like we do with the command line arguments.
294 if (args.files_name != NULL) {
295 // read_name() checks for user_abort so we don't need to
296 // check it as loop termination condition.
298 const char *name = read_name(&args);
302 // read_name() doesn't return empty names.
303 assert(name[0] != '\0');
307 if (args.files_name != stdin_filename)
308 (void)fclose(args.files_file);
311 my_exit(exit_status);