ea_data: Fixed a bug
[deark.git] / src / deark-cmd.c
blob5158f67943e90a1ba22f5b2709c43b6ffa5c72a7
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Command-line interface
7 #define DE_NOT_IN_MODULE
8 #include "deark-config.h"
9 #include "deark-user.h"
10 #include "deark-version.h"
12 #ifdef DE_WINDOWS
13 #include <fcntl.h>
14 #include <io.h> // for _setmode
15 #endif
17 enum color_method_enum {
18 CM_NOCOLOR=0,
19 CM_AUTOCOLOR,
20 CM_ANSI,
21 CM_ANSI24,
22 CM_WINCONSOLE
25 enum special_command_code_enum {
26 CMD_NONE = 0, CMD_PRINTHELP, CMD_PRINTVERSION, CMD_PRINTLICENSE,
27 CMD_PRINTMODULES
30 struct cmdctx {
31 deark *c;
32 struct de_platform_data *plctx;
33 const char *input_filename;
34 int error_flag;
35 int show_usage_message;
36 int special_command_flag;
37 enum special_command_code_enum special_command_code;
38 int msgs_to_stderr;
40 // Have we set msgs_FILE and have_windows_console, and called _setmode if needed?
41 int have_initialized_output_stream;
43 FILE *msgs_FILE; // Where to print (error, etc.) messages
44 #ifdef DE_WINDOWS
45 int have_windows_console; // Is msgs_FILE a console?
46 int use_fwputs;
47 #endif
49 const char *output_dirname;
50 const char *base_output_filename;
51 const char *output_special_1st_filename;
52 const char *archive_filename;
53 int option_k_level; // Use input filename in output filenames
54 int option_ka_level; // Use input filename in output archive filenames
55 u8 set_MAXFILES;
57 int to_stdout;
58 int to_zip;
59 int to_tar;
60 int from_stdin;
61 int to_ascii;
62 int to_oem;
63 int no_chcp;
64 enum color_method_enum color_method_req;
65 enum color_method_enum color_method;
66 char msgbuf[1000];
69 // Low-level print function
70 static void emit_sz(struct cmdctx *cc, const char *sz)
72 #ifdef DE_WINDOWS
73 if(cc->use_fwputs) {
74 de_utf8_to_utf16_to_FILE(cc->c, sz, cc->msgs_FILE);
75 return;
77 #endif
78 fputs(sz, cc->msgs_FILE);
81 static void print_version(deark *c, int verbose)
83 char vbuf[80];
85 de_printf(c, DE_MSGTYPE_MESSAGE, "Deark version: %s\n",
86 de_get_version_string(vbuf, sizeof(vbuf)));
87 if(!verbose) return;
88 de_printf(c, DE_MSGTYPE_MESSAGE, "platform API: %s\n",
89 #ifdef DE_WINDOWS
90 "Windows"
91 #else
92 "Unix-like"
93 #endif
95 de_printf(c, DE_MSGTYPE_MESSAGE, "platform bits: %u\n",
96 (unsigned int)(8*sizeof(void*)));
97 #ifdef _DEBUG
98 de_printf(c, DE_MSGTYPE_MESSAGE, "build type: debug\n");
99 #endif
102 static void print_usage_oneline(deark *c) {
103 de_puts(c, DE_MSGTYPE_MESSAGE, "Usage: deark [options] <input-file> [options]\n");
106 static void print_usage_error(deark *c)
108 print_usage_oneline(c);
109 de_puts(c, DE_MSGTYPE_MESSAGE, "\"deark -h\" for help.\n");
112 static void print_help(deark *c)
114 print_version(c, 0);
115 de_puts(c, DE_MSGTYPE_MESSAGE,
116 "A utility for extracting data from various file formats\n\n");
117 print_usage_oneline(c);
118 de_puts(c, DE_MSGTYPE_MESSAGE,
119 "\nCommonly used options:\n"
120 " -l: Instead of extracting, list the files that would be extracted.\n"
121 " -k, -ka: Start output (-k) or .zip (-ka) filenames with the input filename.\n"
122 " -o <base-filename>: Start output filenames with this string.\n"
123 " -od <directory>: Write files to this directory.\n"
124 " -zip: Write files to a .zip file (output.zip by default).\n"
125 " -a, -main: Extract more (-a) or less (-main) data than usual.\n"
126 " -get <n>: Extract only file number <n>.\n"
127 " -maxfiles <n>: Extract at most <n> files.\n"
128 " -d, -d2, -d3: Print additional information about the file.\n"
129 " -q, -noinfo, -nowarn: Print fewer messages than usual.\n"
130 " -color: Allow color in printed messages.\n"
131 " -m <module>: Assume input file is this format, instead of autodetecting.\n"
132 " -modules: Print the names of all available modules.\n"
133 " -h, -version, -license: Print this message / version info / terms of use.\n"
137 static void print_license(deark *c)
139 de_puts(c, DE_MSGTYPE_MESSAGE, "Deark\n"
140 "Copyright (C) 2016-"DE_COPYRIGHT_YEAR_STRING" Jason Summers\n\n"
141 "Permission is hereby granted, free of charge, to any person obtaining a copy\n"
142 "of this software and associated documentation files (the \"Software\"), to deal\n"
143 "in the Software without restriction, including without limitation the rights\n"
144 "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
145 "copies of the Software, and to permit persons to whom the Software is\n"
146 "furnished to do so, subject to the following conditions:\n\n"
147 "The above copyright notice and this permission notice shall be included in\n"
148 "all copies or substantial portions of the Software.\n\n"
149 "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
150 "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
151 "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
152 "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
153 "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
154 "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n"
155 "THE SOFTWARE.\n\n"
156 "----------\n"
157 "The zlib and Deflate encoder and decoder use MIT-licensed code from miniz,\n"
158 "(c) Rich Geldreich, Tenacious Software, RAD Game Tools, Valve Software.\n\n"
159 "The LoadDskF module uses public domain code from the dskdcmps utility, by an\n"
160 "unknown author.\n\n"
161 "The X-Face decoder uses code from Compface, Copyright (c) 1990 James Ashton.\n");
164 static void print_modules(deark *c)
166 de_print_module_list(c);
169 static void initialize_output_stream(struct cmdctx *cc)
171 #ifdef DE_WINDOWS
172 int ansi_is_enabled = 0;
173 #endif
175 if(cc->msgs_to_stderr) {
176 cc->msgs_FILE = stderr;
178 else {
179 cc->msgs_FILE = stdout;
182 cc->color_method = CM_NOCOLOR; // start with default
184 #ifdef DE_WINDOWS
185 de_winconsole_init_handle(cc->plctx, cc->msgs_to_stderr ? 2 : 1);
186 cc->have_windows_console = de_winconsole_is_console(cc->plctx);
188 // If appropriate, call _setmode so that Unicode output to the console
189 // works correctly (provided we use Unicode functions like fputws()).
190 if(cc->have_windows_console && !cc->to_ascii && !cc->to_oem) {
191 cc->use_fwputs = 1;
192 (void)_setmode(_fileno(cc->msgs_FILE), _O_U16TEXT);
195 if(!cc->have_windows_console && !cc->to_ascii && !cc->to_oem && !cc->no_chcp) {
196 // There are some situations in which it helps to declare the code page
197 // that our output uses.
198 de_winconsole_set_UTF8_CP(cc->plctx);
201 switch(cc->color_method_req) {
202 case CM_AUTOCOLOR:
203 if(cc->have_windows_console) {
204 if(de_winconsole_try_enable_ansi24(cc->plctx)) {
205 cc->color_method = CM_ANSI24;
206 ansi_is_enabled = 1;
208 else {
209 cc->color_method = CM_WINCONSOLE;
212 else {
213 cc->color_method = CM_ANSI24;
215 break;
216 case CM_WINCONSOLE:
217 if(cc->have_windows_console) {
218 cc->color_method = CM_WINCONSOLE;
220 break;
221 case CM_ANSI:
222 cc->color_method = CM_ANSI;
223 break;
224 case CM_ANSI24:
225 cc->color_method = CM_ANSI24;
226 break;
227 default:
228 ; // leave at CM_NOCOLOR
231 if(cc->color_method==CM_WINCONSOLE) {
232 de_winconsole_record_current_attributes(cc->plctx);
235 if((cc->color_method==CM_ANSI || cc->color_method==CM_ANSI24) && !ansi_is_enabled) {
236 de_winconsole_enable_ansi(cc->plctx);
239 #else
240 switch(cc->color_method_req) {
241 case CM_NOCOLOR:
242 case CM_WINCONSOLE:
243 cc->color_method = CM_NOCOLOR;
244 break;
245 case CM_ANSI:
246 cc->color_method = CM_ANSI;
247 break;
248 default:
249 cc->color_method = CM_ANSI24;
251 #endif
253 if(cc->color_method==CM_ANSI || cc->color_method==CM_ANSI24) {
254 // If using ANSI codes, start by resetting all attributes
255 emit_sz(cc, "\x1b[0m");
258 cc->have_initialized_output_stream = 1;
261 static void our_specialmsgfn(deark *c, unsigned int flags, unsigned int code,
262 u32 param1)
264 struct cmdctx *cc;
266 cc = de_get_userdata(c);
267 if(cc->color_method==CM_NOCOLOR) return;
269 if(!cc->have_initialized_output_stream) {
270 initialize_output_stream(cc);
273 #ifdef DE_WINDOWS
274 if(cc->color_method==CM_WINCONSOLE) {
275 if(code==DE_MSGCODE_HL) {
276 de_winconsole_highlight(cc->plctx, 1);
278 else if(code==DE_MSGCODE_UNHL) {
279 de_winconsole_highlight(cc->plctx, 0);
281 else if(code==DE_MSGCODE_RGBSAMPLE) {
282 // There's no way to get 24-bit color using Windows console
283 // commands. Have to use ANSI24 instead.
286 return;
288 #endif
290 // TODO: Maybe move the DE_COLOR_* macros to deark.h.
291 #define X_DE_COLOR_R(x) (unsigned int)(((x)>>16)&0xff)
292 #define X_DE_COLOR_G(x) (unsigned int)(((x)>>8)&0xff)
293 #define X_DE_COLOR_B(x) (unsigned int)((x)&0xff)
294 if(code==DE_MSGCODE_HL) {
295 emit_sz(cc, "\x1b[7m");
297 else if(code==DE_MSGCODE_UNHL) {
298 emit_sz(cc, "\x1b[27m");
300 else if(code==DE_MSGCODE_RGBSAMPLE && cc->color_method==CM_ANSI24) {
301 char buf[64];
303 de_snprintf(buf, sizeof(buf), "\x1b[48;2;%u;%u;%um \x1b[0m",
304 X_DE_COLOR_R(param1), X_DE_COLOR_G(param1), X_DE_COLOR_B(param1));
305 emit_sz(cc, buf);
309 static void our_msgfn(deark *c, unsigned int flags, const char *s1)
311 struct cmdctx *cc;
312 const char *s;
314 cc = de_get_userdata(c);
316 if(!cc->have_initialized_output_stream) {
317 initialize_output_stream(cc);
320 if(cc->to_ascii) {
321 // Note - It doesn't seem quite right to have this functionality be separate
322 // from the library's *to_printable* functions, but they don't quite have
323 // the same purposes, and it would be tricky to combine them.
324 // This is really just a quick and dirty way to deal with systems that don't
325 // support Unicode, or don't support the Unicode characters we use.
327 // TODO: It's inconsistent that the de_utf8_to_ascii() and de_utf8_to_oem()
328 // code paths have a size limit, while the de_utf8_to_utf16_to_FILE() and
329 // fputs() paths do not.
330 de_utf8_to_ascii(s1, cc->msgbuf, sizeof(cc->msgbuf), 0);
331 s = cc->msgbuf;
333 #ifdef DE_WINDOWS
334 else if(cc->to_oem) {
335 de_utf8_to_oem(c, s1, cc->msgbuf, sizeof(cc->msgbuf));
336 s = cc->msgbuf;
338 #endif
339 else {
340 s = s1;
343 emit_sz(cc, s);
346 static void our_fatalerrorfn(deark *c)
348 de_puts(c, DE_MSGTYPE_MESSAGE, "Exiting\n");
349 de_exitprocess(1);
352 static void set_ext_option(deark *c, struct cmdctx *cc, const char *optionstring)
354 char *tmp;
355 char *eqpos;
357 tmp = de_strdup(c, optionstring);
358 if(!tmp) return;
360 eqpos = strchr(tmp, '=');
361 if(eqpos) {
362 *eqpos = '\0';
363 de_set_ext_option(c, tmp, eqpos+1);
365 else {
366 // No "=" symbol
367 de_set_ext_option(c, tmp, "");
369 de_free(c, tmp);
372 static void set_encoding_option(deark *c, struct cmdctx *cc, const char *s)
374 if(!strcmp(s, "ascii")) {
375 cc->to_ascii = 1;
377 else if(!strcmp(s, "oem")) {
378 cc->to_oem = 1;
380 else if(!strcmp(s, "utf8") || !strcmp(s, "unicode")) {
381 cc->to_ascii = 0;
382 cc->to_oem = 0;
384 else {
385 de_puts(c, DE_MSGTYPE_MESSAGE, "Error: Unknown encoding\n");
386 cc->error_flag = 1;
390 enum opt_id_enum {
391 DE_OPT_NULL=0, DE_OPT_D, DE_OPT_D2, DE_OPT_D3, DE_OPT_D4, DE_OPT_L,
392 DE_OPT_NOINFO, DE_OPT_NOWARN,
393 DE_OPT_NOBOM, DE_OPT_NODENS, DE_OPT_ASCIIHTML, DE_OPT_NONAMES,
394 DE_OPT_PADPIX,
395 DE_OPT_NOOVERWRITE, DE_OPT_MODTIME, DE_OPT_NOMODTIME,
396 DE_OPT_Q, DE_OPT_VERSION, DE_OPT_HELP, DE_OPT_LICENSE, DE_OPT_ID,
397 DE_OPT_MAINONLY, DE_OPT_AUXONLY, DE_OPT_EXTRACTALL, DE_OPT_ZIP, DE_OPT_TAR,
398 DE_OPT_TOSTDOUT, DE_OPT_MSGSTOSTDERR, DE_OPT_FROMSTDIN, DE_OPT_COLOR,
399 DE_OPT_NOCHCP, DE_OPT_ENCODING,
400 DE_OPT_EXTOPT, DE_OPT_FILE, DE_OPT_FILE2, DE_OPT_INENC, DE_OPT_INTZ,
401 DE_OPT_START, DE_OPT_SIZE, DE_OPT_M, DE_OPT_MODCODES, DE_OPT_O, DE_OPT_OD,
402 DE_OPT_K, DE_OPT_K2, DE_OPT_K3, DE_OPT_KA, DE_OPT_KA2, DE_OPT_KA3,
403 DE_OPT_T, DE_OPT_ARCFN, DE_OPT_GET, DE_OPT_FIRSTFILE, DE_OPT_MAXFILES,
404 DE_OPT_MAXFILESIZE, DE_OPT_MAXTOTALSIZE, DE_OPT_MAXIMGDIM,
405 DE_OPT_PRINTMODULES, DE_OPT_DPREFIX, DE_OPT_EXTRLIST,
406 DE_OPT_ONLYMODS, DE_OPT_DISABLEMODS, DE_OPT_ONLYDETECT, DE_OPT_NODETECT,
407 DE_OPT_COLORMODE
410 struct opt_struct {
411 const char *string;
412 enum opt_id_enum id;
413 int extra_args;
416 struct opt_struct option_array[] = {
417 { "d", DE_OPT_D, 0 },
418 { "d2", DE_OPT_D2, 0 },
419 { "d3", DE_OPT_D3, 0 },
420 { "d4", DE_OPT_D4, 0 },
421 { "l", DE_OPT_L, 0 },
422 { "noinfo", DE_OPT_NOINFO, 0 },
423 { "nowarn", DE_OPT_NOWARN, 0 },
424 { "nobom", DE_OPT_NOBOM, 0 },
425 { "nodens", DE_OPT_NODENS, 0 },
426 { "asciihtml", DE_OPT_ASCIIHTML, 0 },
427 { "nonames", DE_OPT_NONAMES, 0 },
428 { "n", DE_OPT_NOOVERWRITE, 0 },
429 { "modtime", DE_OPT_MODTIME, 0 },
430 { "nomodtime", DE_OPT_NOMODTIME, 0 },
431 { "padpix", DE_OPT_PADPIX, 0 },
432 { "q", DE_OPT_Q, 0 },
433 { "version", DE_OPT_VERSION, 0 },
434 { "h", DE_OPT_HELP, 0 },
435 { "help", DE_OPT_HELP, 0 },
436 { "?", DE_OPT_HELP, 0 },
437 { "modules", DE_OPT_PRINTMODULES, 0 },
438 { "main", DE_OPT_MAINONLY, 0 },
439 { "aux", DE_OPT_AUXONLY, 0 },
440 { "a", DE_OPT_EXTRACTALL, 0 },
441 { "extractall", DE_OPT_EXTRACTALL, 0 },
442 { "zip", DE_OPT_ZIP, 0 },
443 { "tar", DE_OPT_TAR, 0 },
444 { "tostdout", DE_OPT_TOSTDOUT, 0 },
445 { "msgstostderr", DE_OPT_MSGSTOSTDERR, 0 },
446 { "fromstdin", DE_OPT_FROMSTDIN, 0 },
447 { "color", DE_OPT_COLOR, 0 },
448 { "k", DE_OPT_K, 0 },
449 { "k2", DE_OPT_K2, 0 },
450 { "k3", DE_OPT_K3, 0 },
451 { "ka", DE_OPT_KA, 0 },
452 { "ka2", DE_OPT_KA2, 0 },
453 { "ka3", DE_OPT_KA3, 0 },
454 { "license", DE_OPT_LICENSE, 0 },
455 { "id", DE_OPT_ID, 0 },
456 { "nochcp", DE_OPT_NOCHCP, 0 },
457 { "enc", DE_OPT_ENCODING, 1 },
458 { "opt", DE_OPT_EXTOPT, 1 },
459 { "file", DE_OPT_FILE, 1 },
460 { "file2", DE_OPT_FILE2, 1 },
461 { "inenc", DE_OPT_INENC, 1 },
462 { "intz", DE_OPT_INTZ, 1 },
463 { "start", DE_OPT_START, 1 },
464 { "size", DE_OPT_SIZE, 1 },
465 { "m", DE_OPT_M, 1 },
466 { "modcodes", DE_OPT_MODCODES, 1 },
467 { "o", DE_OPT_O, 1 },
468 { "basefn", DE_OPT_O, 1 }, // Deprecated
469 { "od", DE_OPT_OD, 1 },
470 { "t", DE_OPT_T, 1 },
471 { "ta", DE_OPT_ARCFN, 1 },
472 { "arcfn", DE_OPT_ARCFN, 1 },
473 { "get", DE_OPT_GET, 1 },
474 { "firstfile", DE_OPT_FIRSTFILE, 1 },
475 { "maxfiles", DE_OPT_MAXFILES, 1 },
476 { "maxfilesize", DE_OPT_MAXFILESIZE, 1 },
477 { "maxtotalsize", DE_OPT_MAXTOTALSIZE, 1 },
478 { "maxdim", DE_OPT_MAXIMGDIM, 1 },
479 { "dprefix", DE_OPT_DPREFIX, 1 },
480 { "extrlist", DE_OPT_EXTRLIST, 1 },
481 { "onlymods", DE_OPT_ONLYMODS, 1 },
482 { "disablemods", DE_OPT_DISABLEMODS, 1 },
483 { "onlydetect", DE_OPT_ONLYDETECT, 1 },
484 { "nodetect", DE_OPT_NODETECT, 1 },
485 { "colormode", DE_OPT_COLORMODE, 1 },
486 { NULL, DE_OPT_NULL, 0 }
489 static struct opt_struct *opt_string_to_opt_struct(const char *s)
491 int k;
493 for(k=0; option_array[k].id!=DE_OPT_NULL; k++) {
494 if(!strcmp(s, option_array[k].string)) {
495 return &option_array[k];
498 return NULL;
501 static void send_msgs_to_stderr(deark *c, struct cmdctx *cc)
503 cc->msgs_to_stderr = 1;
504 cc->have_initialized_output_stream = 0;
505 cc->msgs_FILE = NULL;
506 #ifdef DE_WINDOWS
507 cc->have_windows_console = 0;
508 #endif
511 static void colormode_opt(struct cmdctx *cc, const char *modestr)
513 if(!strcmp(modestr, "auto")) {
514 cc->color_method_req = CM_AUTOCOLOR;
516 else if(!strcmp(modestr, "ansi")) {
517 cc->color_method_req = CM_ANSI;
519 else if(!strcmp(modestr, "ansi24")) {
520 cc->color_method_req = CM_ANSI24;
522 else if(!strcmp(modestr, "winconsole")) {
523 cc->color_method_req = CM_WINCONSOLE;
525 else if(!strcmp(modestr, "none")) {
526 cc->color_method_req = CM_NOCOLOR;
528 else {
529 de_printf(cc->c, DE_MSGTYPE_MESSAGE, "Invalid colormode: %s\n", modestr);
530 cc->error_flag = 1;
531 return;
535 static void set_output_basename(struct cmdctx *cc)
537 const char *outputbasefn = cc->base_output_filename; // default, could be NULL
538 const char *outdirname;
539 unsigned int flags = 0;
541 if(cc->option_k_level && cc->input_filename) {
542 if(cc->option_k_level==1) {
543 // Use base input filename in output filenames.
544 outputbasefn = cc->input_filename;
545 flags |= 0x1;
547 else if(cc->option_k_level==2) {
548 // Use full input filename path, but not as an actual path.
549 outputbasefn = cc->input_filename;
550 flags |= 0x2;
552 else if(cc->option_k_level==3) {
553 // Use full input filename path, as-is.
554 outputbasefn = cc->input_filename;
558 if(cc->to_zip || cc->to_tar) {
559 // In this case, -od refers to the archive filename, not to the base
560 // filename that we're dealing with here.
561 outdirname = NULL;
563 else if(cc->to_stdout) {
564 // -od is incompatible with -tostdout
565 outdirname = NULL;
567 else {
568 outdirname = cc->output_dirname;
571 de_set_output_filename_pattern(cc->c, outdirname, outputbasefn, flags);
574 static void set_output_archive_name(struct cmdctx *cc)
576 const char *arcfn = cc->archive_filename; // default, could be NULL
577 unsigned int flags = 0;
579 if(!cc->to_zip && !cc->to_tar) return;
580 if(cc->to_stdout) return;
582 if(cc->option_ka_level && cc->input_filename)
584 if(cc->option_ka_level==1) {
585 // Use base input filename in output filenames.
586 arcfn = cc->input_filename;
587 flags |= 0x21;
589 else if(cc->option_ka_level==2) {
590 // Use full input filename path, but not as an actual path.
591 arcfn = cc->input_filename;
592 flags |= 0x22;
594 else if(cc->option_ka_level==3) {
595 // Use full input filename path, as-is.
596 arcfn = cc->input_filename;
597 flags |= 0x20;
601 if(!arcfn) {
602 arcfn = "output";
603 flags |= 0x20;
606 de_set_output_archive_filename(cc->c, cc->output_dirname, arcfn, flags);
609 static void handle_special_1st_filename(struct cmdctx *cc)
611 if(!cc->output_special_1st_filename) return;
612 de_set_output_special_1st_filename(cc->c,
613 ((cc->to_zip || cc->to_tar || cc->to_stdout) ? NULL : cc->output_dirname),
614 cc->output_special_1st_filename);
615 if(!cc->set_MAXFILES) {
616 de_set_max_output_files(cc->c, 1);
620 static void parse_cmdline(deark *c, struct cmdctx *cc, int argc, char **argv)
622 int i;
623 int help_flag = 0;
624 int module_flag = 0;
625 const struct opt_struct *opt;
627 for(i=1;i<argc;i++) {
628 if(argv[i][0]=='-') {
629 if(argv[i][1]=='-') // Allow a superfluous second '-'
630 opt = opt_string_to_opt_struct(argv[i]+2);
631 else
632 opt = opt_string_to_opt_struct(argv[i]+1);
634 if(!opt) {
635 de_printf(c, DE_MSGTYPE_MESSAGE, "Unrecognized option: %s\n", argv[i]);
636 if(!strcmp(argv[i], "-")) {
637 // I don't want "-" to be an alias for "-fromstdin", because it
638 // would have different syntax rules than it does in most programs.
639 // (Question: Is "-" an *option*, or a kind of *filename*?)
640 de_printf(c, DE_MSGTYPE_MESSAGE,
641 "Note: To use stdin/stdout, use \"-fromstdin\"/\"-tostdout\".\n");
643 cc->error_flag = 1;
644 cc->show_usage_message = 1;
645 return;
647 if(i>=argc-opt->extra_args) {
648 de_printf(c, DE_MSGTYPE_MESSAGE, "Option %s needs an argument\n", argv[i]);
649 cc->error_flag = 1;
650 cc->show_usage_message = 1;
651 return;
654 switch(opt->id) {
655 case DE_OPT_D:
656 de_set_std_option_int(c, DE_STDOPT_DEBUG_LEVEL, 1);
657 break;
658 case DE_OPT_D2:
659 de_set_std_option_int(c, DE_STDOPT_DEBUG_LEVEL, 2);
660 break;
661 case DE_OPT_D3:
662 de_set_std_option_int(c, DE_STDOPT_DEBUG_LEVEL, 3);
663 break;
664 case DE_OPT_D4:
665 de_set_std_option_int(c, DE_STDOPT_DEBUG_LEVEL, 4);
666 break;
667 case DE_OPT_L:
668 de_set_std_option_int(c, DE_STDOPT_LISTMODE, 1);
669 break;
670 case DE_OPT_NOINFO:
671 de_set_std_option_int(c, DE_STDOPT_INFOMESSAGES, 0);
672 break;
673 case DE_OPT_NOWARN:
674 de_set_std_option_int(c, DE_STDOPT_WARNINGS, 0);
675 break;
676 case DE_OPT_NOBOM:
677 de_set_std_option_int(c, DE_STDOPT_WRITE_BOM, 0);
678 break;
679 case DE_OPT_NODENS:
680 de_set_std_option_int(c, DE_STDOPT_WRITE_DENSITY, 0);
681 break;
682 case DE_OPT_ASCIIHTML:
683 de_set_std_option_int(c, DE_STDOPT_ASCII_HTML, 1);
684 break;
685 case DE_OPT_NONAMES:
686 de_set_std_option_int(c, DE_STDOPT_FILENAMES_FROM_FILE, 0);
687 break;
688 case DE_OPT_PADPIX:
689 de_set_std_option_int(c, DE_STDOPT_PADPIX, 1);
690 break;
691 case DE_OPT_NOOVERWRITE:
692 de_set_std_option_int(c, DE_STDOPT_OVERWRITE_MODE, DE_OVERWRITEMODE_NEVER);
693 break;
694 case DE_OPT_MODTIME:
695 de_set_preserve_file_times(c, 0, 1);
696 de_set_preserve_file_times(c, 1, 1);
697 break;
698 case DE_OPT_NOMODTIME:
699 de_set_preserve_file_times(c, 0, 0);
700 de_set_preserve_file_times(c, 1, 0);
701 break;
702 case DE_OPT_Q:
703 de_set_std_option_int(c, DE_STDOPT_INFOMESSAGES, 0);
704 de_set_std_option_int(c, DE_STDOPT_WARNINGS, 0);
705 break;
706 case DE_OPT_VERSION:
707 cc->special_command_flag = 1;
708 cc->special_command_code = CMD_PRINTVERSION;
709 break;
710 case DE_OPT_PRINTMODULES:
711 cc->special_command_flag = 1;
712 cc->special_command_code = CMD_PRINTMODULES;
713 break;
714 case DE_OPT_HELP:
715 // At this point, we don't know whether this will be general help,
716 // or module-specific help. So just set a flag for later.
717 help_flag = 1;
718 break;
719 case DE_OPT_LICENSE:
720 cc->special_command_flag = 1;
721 cc->special_command_code = CMD_PRINTLICENSE;
722 break;
723 case DE_OPT_ID:
724 de_set_std_option_int(c, DE_STDOPT_ID_MODE, 1);
725 break;
726 case DE_OPT_NOCHCP:
727 cc->no_chcp = 1;
728 break;
729 case DE_OPT_MAINONLY:
730 de_set_std_option_int(c, DE_STDOPT_EXTRACT_POLICY, DE_EXTRACTPOLICY_MAINONLY);
731 break;
732 case DE_OPT_AUXONLY:
733 de_set_std_option_int(c, DE_STDOPT_EXTRACT_POLICY, DE_EXTRACTPOLICY_AUXONLY);
734 break;
735 case DE_OPT_EXTRACTALL:
736 de_set_std_option_int(c, DE_STDOPT_EXTRACT_LEVEL, 2);
737 break;
738 case DE_OPT_ZIP:
739 de_set_output_style(c, DE_OUTPUTSTYLE_ARCHIVE, DE_ARCHIVEFMT_ZIP);
740 cc->to_zip = 1;
741 break;
742 case DE_OPT_TAR:
743 de_set_output_style(c, DE_OUTPUTSTYLE_ARCHIVE, DE_ARCHIVEFMT_TAR);
744 cc->to_tar = 1;
745 break;
746 case DE_OPT_TOSTDOUT:
747 send_msgs_to_stderr(c, cc);
748 cc->to_stdout = 1;
749 break;
750 case DE_OPT_MSGSTOSTDERR:
751 send_msgs_to_stderr(c, cc);
752 break;
753 case DE_OPT_FROMSTDIN:
754 de_set_input_style(c, DE_INPUTSTYLE_STDIN);
755 cc->from_stdin = 1;
756 break;
757 case DE_OPT_COLOR:
758 colormode_opt(cc, "auto");
759 break;
760 case DE_OPT_K:
761 cc->option_k_level = 1;
762 break;
763 case DE_OPT_K2:
764 cc->option_k_level = 2;
765 break;
766 case DE_OPT_K3:
767 cc->option_k_level = 3;
768 break;
769 case DE_OPT_KA:
770 cc->option_ka_level = 1;
771 break;
772 case DE_OPT_KA2:
773 cc->option_ka_level = 2;
774 break;
775 case DE_OPT_KA3:
776 cc->option_ka_level = 3;
777 break;
778 case DE_OPT_ENCODING:
779 set_encoding_option(c, cc, argv[i+1]);
780 if(cc->error_flag) return;
781 break;
782 case DE_OPT_EXTOPT:
783 set_ext_option(c, cc, argv[i+1]);
784 break;
785 case DE_OPT_FILE:
786 cc->input_filename = argv[i+1];
787 de_set_input_filename(c, cc->input_filename);
788 break;
789 case DE_OPT_FILE2:
790 de_set_ext_option(c, "file2", argv[i+1]);
791 break;
792 case DE_OPT_INENC:
793 if(!de_set_input_encoding(c, argv[i+1], 0)) {
794 de_printf(c, DE_MSGTYPE_MESSAGE,
795 "Error: Unknown input encoding: %s\n", argv[i+1]);
796 cc->error_flag = 1;
797 return;
799 break;
800 case DE_OPT_INTZ:
801 de_set_input_timezone(c, (i64)(3600.0*atof(argv[i+1])));
802 break;
803 case DE_OPT_START:
804 de_set_input_file_slice_start(c, de_atoi64(argv[i+1]));
805 break;
806 case DE_OPT_SIZE:
807 de_set_input_file_slice_size(c, de_atoi64(argv[i+1]));
808 break;
809 case DE_OPT_M:
810 module_flag = 1;
811 de_set_input_format(c, argv[i+1]);
812 break;
813 case DE_OPT_MODCODES:
814 de_set_module_init_codes(c, argv[i+1]);
815 break;
816 case DE_OPT_O:
817 cc->base_output_filename = argv[i+1];
818 break;
819 case DE_OPT_OD:
820 cc->output_dirname = argv[i+1];
821 break;
822 case DE_OPT_T:
823 cc->output_special_1st_filename = argv[i+1];
824 break;
825 case DE_OPT_ARCFN:
826 // Relevant e.g. if the -zip option is used.
827 cc->archive_filename = argv[i+1];
828 break;
829 case DE_OPT_GET:
830 de_set_first_output_file(c, de_atoi(argv[i+1]));
831 de_set_max_output_files(c, 1);
832 break;
833 case DE_OPT_FIRSTFILE:
834 de_set_first_output_file(c, de_atoi(argv[i+1]));
835 break;
836 case DE_OPT_MAXFILES:
837 de_set_max_output_files(c, de_atoi64(argv[i+1]));
838 cc->set_MAXFILES = 1;
839 break;
840 case DE_OPT_MAXFILESIZE:
841 de_set_max_output_file_size(c, de_atoi64(argv[i+1]));
842 break;
843 case DE_OPT_MAXTOTALSIZE:
844 de_set_max_total_output_size(c, de_atoi64(argv[i+1]));
845 break;
846 case DE_OPT_MAXIMGDIM:
847 de_set_max_image_dimension(c, de_atoi64(argv[i+1]));
848 break;
849 case DE_OPT_DPREFIX:
850 de_set_dprefix(c, argv[i+1]);
851 break;
852 case DE_OPT_EXTRLIST:
853 de_set_extrlist_filename(c, argv[i+1]);
854 break;
855 case DE_OPT_ONLYMODS:
856 de_set_disable_mods(c, argv[i+1], 1);
857 break;
858 case DE_OPT_DISABLEMODS:
859 de_set_disable_mods(c, argv[i+1], 0);
860 break;
861 case DE_OPT_ONLYDETECT:
862 de_set_disable_moddetect(c, argv[i+1], 1);
863 break;
864 case DE_OPT_NODETECT:
865 de_set_disable_moddetect(c, argv[i+1], 0);
866 break;
867 case DE_OPT_COLORMODE:
868 colormode_opt(cc, argv[i+1]);
869 if(cc->error_flag) return;
870 break;
871 default:
872 de_printf(c, DE_MSGTYPE_MESSAGE, "Unrecognized option: %s\n", argv[i]);
873 cc->error_flag = 1;
874 cc->show_usage_message = 1;
875 return;
878 i += opt->extra_args;
880 else {
881 if(cc->input_filename) {
882 cc->error_flag = 1;
883 cc->show_usage_message = 1;
884 return;
886 cc->input_filename = argv[i];
887 de_set_input_filename(c, cc->input_filename);
891 if(help_flag) {
892 if(module_flag || cc->input_filename || cc->from_stdin) {
893 de_set_std_option_int(c, DE_STDOPT_WANT_MODHELP, 1);
895 else {
896 cc->special_command_flag = 1;
897 cc->special_command_code = CMD_PRINTHELP;
899 return;
902 if(!cc->input_filename && !cc->special_command_flag && !cc->from_stdin) {
903 de_puts(c, DE_MSGTYPE_MESSAGE, "Error: Need an input filename\n");
904 cc->error_flag = 1;
905 cc->show_usage_message = 1;
906 return;
909 if(cc->to_stdout) {
910 if(cc->to_zip || cc->to_tar) {
911 de_set_output_archive_filename(c, NULL, NULL, 0x10);
913 else {
914 de_set_output_style(c, DE_OUTPUTSTYLE_STDOUT, 0);
915 if(!cc->set_MAXFILES) {
916 de_set_max_output_files(c, 1);
921 set_output_basename(cc);
922 set_output_archive_name(cc);
923 handle_special_1st_filename(cc);
926 static int main2(int argc, char **argv)
928 deark *c = NULL;
929 struct cmdctx *cc = NULL;
930 int ret;
931 int exit_status = 0;
933 cc = de_malloc(NULL, sizeof(struct cmdctx));
934 c = de_create();
935 cc->c = c;
937 de_set_userdata(c, (void*)cc);
938 de_set_fatalerror_callback(c, our_fatalerrorfn);
939 de_set_messages_callback(c, our_msgfn);
940 de_set_special_messages_callback(c, our_specialmsgfn);
941 cc->plctx = de_platformdata_create();
943 if(argc<2) { // Empty command line
944 print_help(c);
945 goto done;
948 parse_cmdline(c, cc, argc, argv);
950 if(cc->error_flag) {
951 if(cc->show_usage_message) {
952 print_usage_error(c);
954 goto done;
957 if(cc->special_command_flag) {
958 switch(cc->special_command_code) {
959 case CMD_PRINTHELP:
960 print_help(c);
961 break;
962 case CMD_PRINTVERSION:
963 print_version(c, 1);
964 break;
965 case CMD_PRINTLICENSE:
966 print_license(c);
967 break;
968 case CMD_PRINTMODULES:
969 print_modules(c);
970 break;
971 default:
972 break;
974 goto done;
977 #ifdef DE_WINDOWS
978 if(cc->to_stdout) {
979 (void)_setmode(_fileno(stdout), _O_BINARY);
981 if(cc->from_stdin) {
982 (void)_setmode(_fileno(stdin), _O_BINARY);
984 #endif
986 ret = de_run(c);
987 if(!ret) {
988 exit_status = 1;
991 done:
992 de_destroy(c);
993 de_platformdata_destroy(cc->plctx);
994 cc->plctx = NULL;
995 if(cc->error_flag) exit_status = 1;
996 de_free(NULL, cc);
997 return exit_status;
1000 #ifdef DE_WINDOWS
1002 // This prototype is to silence a possible -Wmissing-prototypes warning.
1003 int wmain(int argc, wchar_t **argvW);
1005 int wmain(int argc, wchar_t **argvW)
1007 char **argv;
1008 int exit_status;
1010 argv = de_convert_args_to_utf8(argc, argvW);
1011 exit_status = main2(argc, argv);
1012 de_free_utf8_args(argc, argv);
1013 return exit_status;
1016 #else
1018 int main(int argc, char **argv)
1020 return main2(argc, argv);
1023 #endif