Rizin
unix-like reverse engineering framework and cli tools
main.c
Go to the documentation of this file.
1 //
5 //
6 // Author: Lasse Collin
7 //
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
10 //
12 
13 #include "private.h"
14 #include <ctype.h>
15 
18 
19 #if defined(_WIN32) && !defined(__CYGWIN__)
22 static CRITICAL_SECTION exit_status_cs;
23 #endif
24 
27 static bool no_warn = false;
28 
29 
30 extern void
32 {
33  assert(new_status == E_WARNING || new_status == E_ERROR);
34 
35 #if defined(_WIN32) && !defined(__CYGWIN__)
36  EnterCriticalSection(&exit_status_cs);
37 #endif
38 
39  if (exit_status != E_ERROR)
40  exit_status = new_status;
41 
42 #if defined(_WIN32) && !defined(__CYGWIN__)
43  LeaveCriticalSection(&exit_status_cs);
44 #endif
45 
46  return;
47 }
48 
49 
50 extern void
52 {
53  no_warn = true;
54  return;
55 }
56 
57 
58 static const char *
60 {
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
64  // whole file in RAM.
65  static char *name = NULL;
66  static size_t size = 256;
67 
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.
73  if (name == NULL)
74  name = xmalloc(size);
75 
76  // Write position in name
77  size_t pos = 0;
78 
79  // Read one character at a time into name.
80  while (!user_abort) {
81  const int c = fgetc(args->files_file);
82 
83  if (ferror(args->files_file)) {
84  // Take care of EINTR since we have established
85  // the signal handlers already.
86  if (errno == EINTR)
87  continue;
88 
89  message_error(_("%s: Error reading filenames: %s"),
90  args->files_name, strerror(errno));
91  return NULL;
92  }
93 
94  if (feof(args->files_file)) {
95  if (pos != 0)
96  message_error(_("%s: Unexpected end of input "
97  "when reading filenames"),
98  args->files_name);
99 
100  return NULL;
101  }
102 
103  if (c == args->files_delim) {
104  // We allow consecutive newline (--files) or '\0'
105  // characters (--files0), and ignore such empty
106  // filenames.
107  if (pos == 0)
108  continue;
109 
110  // A non-empty name was read. Terminate it with '\0'
111  // and return it.
112  name[pos] = '\0';
113  return name;
114  }
115 
116  if (c == '\0') {
117  // A null character was found when using --files,
118  // which expects plain text input separated with
119  // newlines.
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);
124  return NULL;
125  }
126 
127  name[pos++] = c;
128 
129  // Allocate more memory if needed. There must always be space
130  // at least for one character to allow terminating the string
131  // with '\0'.
132  if (pos == size) {
133  size *= 2;
134  name = xrealloc(name, size);
135  }
136  }
137 
138  return NULL;
139 }
140 
141 
142 int
143 main(int argc, char **argv)
144 {
145 #if defined(_WIN32) && !defined(__CYGWIN__)
146  InitializeCriticalSection(&exit_status_cs);
147 #endif
148 
149  // Set up the progname variable.
151 
152  // Initialize the file I/O. This makes sure that
153  // stdin, stdout, and stderr are something valid.
154  io_init();
155 
156  // Set up the locale and message translations.
157  tuklib_gettext_init(PACKAGE, LOCALEDIR);
158 
159  // Initialize handling of error/warning/other messages.
160  message_init();
161 
162  // Set hardware-dependent default values. These can be overridden
163  // on the command line, thus this must be done before args_parse().
164  hardware_init();
165 
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.
170  args_info args;
171  args_parse(&args, argc, argv);
172 
173  if (opt_mode != MODE_LIST && opt_robot)
174  message_fatal(_("Compression and decompression with --robot "
175  "are not supported yet."));
176 
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)
181  else
182  message_set_files(args.arg_count);
183 
184  // Refuse to write compressed data to standard output if it is
185  // a terminal.
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);
192  }
193  }
194  }
195 
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.
199  //
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)
206  signals_init();
207 
208 #ifdef ENABLE_SANDBOX
209  // Set a flag that sandboxing is allowed if all these are true:
210  // - --files or --files0 wasn't used.
211  // - There is exactly one input file or we are reading from stdin.
212  // - We won't create any files: output goes to stdout or --test
213  // or --list was used. Note that --test implies opt_stdout = true
214  // but --list doesn't.
215  //
216  // This is obviously not ideal but it was easy to implement and
217  // it covers the most common use cases.
218  //
219  // TODO: Make sandboxing work for other situations too.
220  if (args.files_name == NULL && args.arg_count == 1
221  && (opt_stdout || strcmp("-", args.arg_names[0]) == 0
222  || opt_mode == MODE_LIST))
223  io_allow_sandbox();
224 #endif
225 
226  // coder_run() handles compression, decompression, and testing.
227  // list_file() is for --list.
228  void (*run)(const char *filename) = &coder_run;
229 #ifdef HAVE_DECODERS
230  if (opt_mode == MODE_LIST)
231  run = &list_file;
232 #endif
233 
234  // Process the files given on the command line. Note that if no names
235  // were given, args_parse() gave us a fake "-" filename.
236  for (unsigned i = 0; i < args.arg_count && !user_abort; ++i) {
237  if (strcmp("-", args.arg_names[i]) == 0) {
238  // Processing from stdin to stdout. Check that we
239  // aren't writing compressed data to a terminal or
240  // reading it from a terminal.
241  if (opt_mode == MODE_COMPRESS) {
242  if (is_tty_stdout())
243  continue;
244  } else if (is_tty_stdin()) {
245  continue;
246  }
247 
248  // It doesn't make sense to compress data from stdin
249  // if we are supposed to read filenames from stdin
250  // too (enabled with --files or --files0).
251  if (args.files_name == stdin_filename) {
252  message_error(_("Cannot read data from "
253  "standard input when "
254  "reading filenames "
255  "from standard input"));
256  continue;
257  }
258 
259  // Replace the "-" with a special pointer, which is
260  // recognized by coder_run() and other things.
261  // This way error messages get a proper filename
262  // string and the code still knows that it is
263  // handling the special case of stdin.
264  args.arg_names[i] = (char *)stdin_filename;
265  }
266 
267  // Do the actual compression or decompression.
268  run(args.arg_names[i]);
269  }
270 
271  // If --files or --files0 was used, process the filenames from the
272  // given file or stdin. Note that here we don't consider "-" to
273  // indicate stdin like we do with the command line arguments.
274  if (args.files_name != NULL) {
275  // read_name() checks for user_abort so we don't need to
276  // check it as loop termination condition.
277  while (true) {
278  const char *name = read_name(&args);
279  if (name == NULL)
280  break;
281 
282  // read_name() doesn't return empty names.
283  assert(name[0] != '\0');
284  run(name);
285  }
286 
287  if (args.files_name != stdin_filename)
288  (void)fclose(args.files_file);
289  }
290 
291 #ifdef HAVE_DECODERS
292  // All files have now been handled. If in --list mode, display
293  // the totals before exiting. We don't have signal handlers
294  // enabled in --list mode, so we don't need to check user_abort.
295  if (opt_mode == MODE_LIST) {
296  assert(!user_abort);
297  list_totals();
298  }
299 #endif
300 
301 #ifndef NDEBUG
302  coder_free();
303  args_free();
304 #endif
305 
306  // If we have got a signal, raise it to kill the program instead
307  // of calling tuklib_exit().
308  signals_exit();
309 
310  // Make a local copy of exit_status to keep the Windows code
311  // thread safe. At this point it is fine if we miss the user
312  // pressing C-c and don't set the exit_status to E_ERROR on
313  // Windows.
314 #if defined(_WIN32) && !defined(__CYGWIN__)
315  EnterCriticalSection(&exit_status_cs);
316 #endif
317 
318  enum exit_status_type es = exit_status;
319 
320 #if defined(_WIN32) && !defined(__CYGWIN__)
321  LeaveCriticalSection(&exit_status_cs);
322 #endif
323 
324  // Suppress the exit status indicating a warning if --no-warn
325  // was specified.
326  if (es == E_WARNING && no_warn)
327  es = E_SUCCESS;
328 
330 }
lzma_index ** i
Definition: index.h:629
void args_parse(args_info *args, int argc, char **argv)
Definition: args.c:592
void args_free(void)
Definition: args.c:695
bool opt_robot
Definition: args.c:24
const char stdin_filename[]
Definition: args.c:29
bool opt_stdout
Definition: args.c:21
#define PACKAGE
Definition: config.h:59
void coder_free(void)
Free the memory allocated for the coder and kill the worker threads.
Definition: coder.c:939
void coder_run(const char *filename)
Compress or decompress the given file.
Definition: coder.c:869
enum operation_mode opt_mode
Definition: coder.c:24
@ MODE_COMPRESS
Definition: coder.h:14
@ MODE_LIST
Definition: coder.h:17
#define NULL
Definition: cris-opc.c:27
#define xmalloc
Definition: disas-asm.h:43
void io_init(void)
Initialize the I/O module.
Definition: file_io.c:95
void hardware_init(void)
Definition: hardware.c:169
voidpf void uLong size
Definition: ioapi.h:138
const char * filename
Definition: ioapi.h:137
void * xrealloc(void *, size_t)
Definition: util.c:25
int main(int argc, const char **argv)
Definition: main.c:340
static static fork const void static count static fd const char const char static newpath char char argv
Definition: sflib.h:40
exit_status_type
Possible exit status values. These are the same as used by gzip and bzip2.
Definition: main.h:14
@ E_WARNING
Definition: main.h:17
@ E_SUCCESS
Definition: main.h:15
assert(limit<=UINT32_MAX/2)
void message_set_files(unsigned int files)
Set the total number of files to be processed.
Definition: message.c:188
void message_init(void)
Initializes the message functions.
Definition: message.c:114
void message_error(const char *fmt,...)
Definition: message.c:764
void message_try_help(void)
Print a message that user should try –help.
Definition: message.c:1063
void message_fatal(const char *fmt,...)
Definition: message.c:777
enum message_verbosity message_verbosity_get(void)
Get the current verbosity level.
Definition: message.c:181
@ V_SILENT
No messages.
Definition: message.h:15
const char * name
Definition: op.c:541
#define _(String)
Definition: opintl.h:53
static int run(int i, const char *arg)
Definition: rz-bb.c:19
#define EINTR
Definition: sftypes.h:114
#define c(i)
Definition: sha256.c:43
volatile sig_atomic_t user_abort
Definition: signals.c:16
void signals_init(void)
Definition: signals.c:54
void signals_exit(void)
Definition: signals.c:153
Definition: args.h:13
Definition: z80asm.h:102
int pos
Definition: main.c:11
struct Arg * args
Definition: mipsasm.c:18
void list_file(const char *filename)
List information about the given .xz file.
Definition: list.c:1143
void list_totals(void)
Show the totals after all files have been listed.
Definition: list.c:1121
void set_exit_no_warn(void)
Definition: main.c:51
static const char * read_name(const args_info *args)
Definition: main.c:59
void set_exit_status(enum exit_status_type new_status)
Definition: main.c:31
static bool no_warn
Definition: main.c:27
static enum exit_status_type exit_status
Exit status to use. This can be changed with set_exit_status().
Definition: main.c:17
Common includes, definitions, and prototypes.
bool is_tty_stdin(void)
Test if stdin is a terminal.
Definition: util.c:276
bool is_tty_stdout(void)
Test if stdout is a terminal.
Definition: util.c:289
@ E_ERROR
Definition: transport.h:23
#define tuklib_exit
Definition: tuklib_exit.h:20
#define tuklib_gettext_init(package, localedir)
#define tuklib_progname_init