fat: Improved debug info
[deark.git] / src / deark-cmd.c
blobeca4af6e6b37cfb22f71d10f322ff1bec95c6ae2
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 X-Face decoder uses code from Compface, Copyright (c) 1990 James Ashton.\n");
162 static void print_modules(deark *c)
164 de_print_module_list(c);
167 static void initialize_output_stream(struct cmdctx *cc)
169 #ifdef DE_WINDOWS
170 int ansi_is_enabled = 0;
171 #endif
173 if(cc->msgs_to_stderr) {
174 cc->msgs_FILE = stderr;
176 else {
177 cc->msgs_FILE = stdout;
180 cc->color_method = CM_NOCOLOR; // start with default
182 #ifdef DE_WINDOWS
183 de_winconsole_init_handle(cc->plctx, cc->msgs_to_stderr ? 2 : 1);
184 cc->have_windows_console = de_winconsole_is_console(cc->plctx);
186 // If appropriate, call _setmode so that Unicode output to the console
187 // works correctly (provided we use Unicode functions like fputws()).
188 if(cc->have_windows_console && !cc->to_ascii && !cc->to_oem) {
189 cc->use_fwputs = 1;
190 (void)_setmode(_fileno(cc->msgs_FILE), _O_U16TEXT);
193 if(!cc->have_windows_console && !cc->to_ascii && !cc->to_oem && !cc->no_chcp) {
194 // There are some situations in which it helps to declare the code page
195 // that our output uses.
196 de_winconsole_set_UTF8_CP(cc->plctx);
199 switch(cc->color_method_req) {
200 case CM_AUTOCOLOR:
201 if(cc->have_windows_console) {
202 if(de_winconsole_try_enable_ansi24(cc->plctx)) {
203 cc->color_method = CM_ANSI24;
204 ansi_is_enabled = 1;
206 else {
207 cc->color_method = CM_WINCONSOLE;
210 else {
211 cc->color_method = CM_ANSI24;
213 break;
214 case CM_WINCONSOLE:
215 if(cc->have_windows_console) {
216 cc->color_method = CM_WINCONSOLE;
218 break;
219 case CM_ANSI:
220 cc->color_method = CM_ANSI;
221 break;
222 case CM_ANSI24:
223 cc->color_method = CM_ANSI24;
224 break;
225 default:
226 ; // leave at CM_NOCOLOR
229 if(cc->color_method==CM_WINCONSOLE) {
230 de_winconsole_record_current_attributes(cc->plctx);
233 if((cc->color_method==CM_ANSI || cc->color_method==CM_ANSI24) && !ansi_is_enabled) {
234 de_winconsole_enable_ansi(cc->plctx);
237 #else
238 switch(cc->color_method_req) {
239 case CM_NOCOLOR:
240 case CM_WINCONSOLE:
241 cc->color_method = CM_NOCOLOR;
242 break;
243 case CM_ANSI:
244 cc->color_method = CM_ANSI;
245 break;
246 default:
247 cc->color_method = CM_ANSI24;
249 #endif
251 if(cc->color_method==CM_ANSI || cc->color_method==CM_ANSI24) {
252 // If using ANSI codes, start by resetting all attributes
253 emit_sz(cc, "\x1b[0m");
256 cc->have_initialized_output_stream = 1;
259 static void our_specialmsgfn(deark *c, unsigned int flags, unsigned int code,
260 u32 param1)
262 struct cmdctx *cc;
264 cc = de_get_userdata(c);
265 if(cc->color_method==CM_NOCOLOR) return;
267 if(!cc->have_initialized_output_stream) {
268 initialize_output_stream(cc);
271 #ifdef DE_WINDOWS
272 if(cc->color_method==CM_WINCONSOLE) {
273 if(code==DE_MSGCODE_HL) {
274 de_winconsole_highlight(cc->plctx, 1);
276 else if(code==DE_MSGCODE_UNHL) {
277 de_winconsole_highlight(cc->plctx, 0);
279 else if(code==DE_MSGCODE_RGBSAMPLE) {
280 // There's no way to get 24-bit color using Windows console
281 // commands. Have to use ANSI24 instead.
284 return;
286 #endif
288 // TODO: Maybe move the DE_COLOR_* macros to deark.h.
289 #define X_DE_COLOR_R(x) (unsigned int)(((x)>>16)&0xff)
290 #define X_DE_COLOR_G(x) (unsigned int)(((x)>>8)&0xff)
291 #define X_DE_COLOR_B(x) (unsigned int)((x)&0xff)
292 if(code==DE_MSGCODE_HL) {
293 emit_sz(cc, "\x1b[7m");
295 else if(code==DE_MSGCODE_UNHL) {
296 emit_sz(cc, "\x1b[27m");
298 else if(code==DE_MSGCODE_RGBSAMPLE && cc->color_method==CM_ANSI24) {
299 char buf[64];
301 de_snprintf(buf, sizeof(buf), "\x1b[48;2;%u;%u;%um \x1b[0m",
302 X_DE_COLOR_R(param1), X_DE_COLOR_G(param1), X_DE_COLOR_B(param1));
303 emit_sz(cc, buf);
307 static void our_msgfn(deark *c, unsigned int flags, const char *s1)
309 struct cmdctx *cc;
310 const char *s;
312 cc = de_get_userdata(c);
314 if(!cc->have_initialized_output_stream) {
315 initialize_output_stream(cc);
318 if(cc->to_ascii) {
319 // Note - It doesn't seem quite right to have this functionality be separate
320 // from the library's *to_printable* functions, but they don't quite have
321 // the same purposes, and it would be tricky to combine them.
322 // This is really just a quick and dirty way to deal with systems that don't
323 // support Unicode, or don't support the Unicode characters we use.
325 // TODO: It's inconsistent that the de_utf8_to_ascii() and de_utf8_to_oem()
326 // code paths have a size limit, while the de_utf8_to_utf16_to_FILE() and
327 // fputs() paths do not.
328 de_utf8_to_ascii(s1, cc->msgbuf, sizeof(cc->msgbuf), 0);
329 s = cc->msgbuf;
331 #ifdef DE_WINDOWS
332 else if(cc->to_oem) {
333 de_utf8_to_oem(c, s1, cc->msgbuf, sizeof(cc->msgbuf));
334 s = cc->msgbuf;
336 #endif
337 else {
338 s = s1;
341 emit_sz(cc, s);
344 static void our_fatalerrorfn(deark *c)
346 de_puts(c, DE_MSGTYPE_MESSAGE, "Exiting\n");
347 de_exitprocess(1);
350 static void set_ext_option(deark *c, struct cmdctx *cc, const char *optionstring)
352 char *tmp;
353 char *eqpos;
355 tmp = de_strdup(c, optionstring);
356 if(!tmp) return;
358 eqpos = strchr(tmp, '=');
359 if(eqpos) {
360 *eqpos = '\0';
361 de_set_ext_option(c, tmp, eqpos+1);
363 else {
364 // No "=" symbol
365 de_set_ext_option(c, tmp, "");
367 de_free(c, tmp);
370 static void set_encoding_option(deark *c, struct cmdctx *cc, const char *s)
372 if(!strcmp(s, "ascii")) {
373 cc->to_ascii = 1;
375 else if(!strcmp(s, "oem")) {
376 cc->to_oem = 1;
378 else if(!strcmp(s, "utf8") || !strcmp(s, "unicode")) {
379 cc->to_ascii = 0;
380 cc->to_oem = 0;
382 else {
383 de_puts(c, DE_MSGTYPE_MESSAGE, "Error: Unknown encoding\n");
384 cc->error_flag = 1;
388 enum opt_id_enum {
389 DE_OPT_NULL=0, DE_OPT_D, DE_OPT_D2, DE_OPT_D3, DE_OPT_D4, DE_OPT_L,
390 DE_OPT_NOINFO, DE_OPT_NOWARN,
391 DE_OPT_NOBOM, DE_OPT_NODENS, DE_OPT_ASCIIHTML, DE_OPT_NONAMES,
392 DE_OPT_PADPIX,
393 DE_OPT_NOOVERWRITE, DE_OPT_MODTIME, DE_OPT_NOMODTIME,
394 DE_OPT_Q, DE_OPT_VERSION, DE_OPT_HELP, DE_OPT_LICENSE, DE_OPT_ID,
395 DE_OPT_MAINONLY, DE_OPT_AUXONLY, DE_OPT_EXTRACTALL, DE_OPT_ZIP, DE_OPT_TAR,
396 DE_OPT_TOSTDOUT, DE_OPT_MSGSTOSTDERR, DE_OPT_FROMSTDIN, DE_OPT_COLOR,
397 DE_OPT_NOCHCP, DE_OPT_ENCODING,
398 DE_OPT_EXTOPT, DE_OPT_FILE, DE_OPT_FILE2, DE_OPT_INENC, DE_OPT_INTZ,
399 DE_OPT_START, DE_OPT_SIZE, DE_OPT_M, DE_OPT_MODCODES, DE_OPT_O, DE_OPT_OD,
400 DE_OPT_K, DE_OPT_K2, DE_OPT_K3, DE_OPT_KA, DE_OPT_KA2, DE_OPT_KA3,
401 DE_OPT_T, DE_OPT_ARCFN, DE_OPT_GET, DE_OPT_FIRSTFILE, DE_OPT_MAXFILES,
402 DE_OPT_MAXFILESIZE, DE_OPT_MAXTOTALSIZE, DE_OPT_MAXIMGDIM,
403 DE_OPT_PRINTMODULES, DE_OPT_DPREFIX, DE_OPT_EXTRLIST,
404 DE_OPT_ONLYMODS, DE_OPT_DISABLEMODS, DE_OPT_ONLYDETECT, DE_OPT_NODETECT,
405 DE_OPT_COLORMODE
408 struct opt_struct {
409 const char *string;
410 enum opt_id_enum id;
411 int extra_args;
414 struct opt_struct option_array[] = {
415 { "d", DE_OPT_D, 0 },
416 { "d2", DE_OPT_D2, 0 },
417 { "d3", DE_OPT_D3, 0 },
418 { "d4", DE_OPT_D4, 0 },
419 { "l", DE_OPT_L, 0 },
420 { "noinfo", DE_OPT_NOINFO, 0 },
421 { "nowarn", DE_OPT_NOWARN, 0 },
422 { "nobom", DE_OPT_NOBOM, 0 },
423 { "nodens", DE_OPT_NODENS, 0 },
424 { "asciihtml", DE_OPT_ASCIIHTML, 0 },
425 { "nonames", DE_OPT_NONAMES, 0 },
426 { "n", DE_OPT_NOOVERWRITE, 0 },
427 { "modtime", DE_OPT_MODTIME, 0 },
428 { "nomodtime", DE_OPT_NOMODTIME, 0 },
429 { "padpix", DE_OPT_PADPIX, 0 },
430 { "q", DE_OPT_Q, 0 },
431 { "version", DE_OPT_VERSION, 0 },
432 { "h", DE_OPT_HELP, 0 },
433 { "help", DE_OPT_HELP, 0 },
434 { "?", DE_OPT_HELP, 0 },
435 { "modules", DE_OPT_PRINTMODULES, 0 },
436 { "main", DE_OPT_MAINONLY, 0 },
437 { "aux", DE_OPT_AUXONLY, 0 },
438 { "a", DE_OPT_EXTRACTALL, 0 },
439 { "extractall", DE_OPT_EXTRACTALL, 0 },
440 { "zip", DE_OPT_ZIP, 0 },
441 { "tar", DE_OPT_TAR, 0 },
442 { "tostdout", DE_OPT_TOSTDOUT, 0 },
443 { "msgstostderr", DE_OPT_MSGSTOSTDERR, 0 },
444 { "fromstdin", DE_OPT_FROMSTDIN, 0 },
445 { "color", DE_OPT_COLOR, 0 },
446 { "k", DE_OPT_K, 0 },
447 { "k2", DE_OPT_K2, 0 },
448 { "k3", DE_OPT_K3, 0 },
449 { "ka", DE_OPT_KA, 0 },
450 { "ka2", DE_OPT_KA2, 0 },
451 { "ka3", DE_OPT_KA3, 0 },
452 { "license", DE_OPT_LICENSE, 0 },
453 { "id", DE_OPT_ID, 0 },
454 { "nochcp", DE_OPT_NOCHCP, 0 },
455 { "enc", DE_OPT_ENCODING, 1 },
456 { "opt", DE_OPT_EXTOPT, 1 },
457 { "file", DE_OPT_FILE, 1 },
458 { "file2", DE_OPT_FILE2, 1 },
459 { "inenc", DE_OPT_INENC, 1 },
460 { "intz", DE_OPT_INTZ, 1 },
461 { "start", DE_OPT_START, 1 },
462 { "size", DE_OPT_SIZE, 1 },
463 { "m", DE_OPT_M, 1 },
464 { "modcodes", DE_OPT_MODCODES, 1 },
465 { "o", DE_OPT_O, 1 },
466 { "basefn", DE_OPT_O, 1 }, // Deprecated
467 { "od", DE_OPT_OD, 1 },
468 { "t", DE_OPT_T, 1 },
469 { "ta", DE_OPT_ARCFN, 1 },
470 { "arcfn", DE_OPT_ARCFN, 1 },
471 { "get", DE_OPT_GET, 1 },
472 { "firstfile", DE_OPT_FIRSTFILE, 1 },
473 { "maxfiles", DE_OPT_MAXFILES, 1 },
474 { "maxfilesize", DE_OPT_MAXFILESIZE, 1 },
475 { "maxtotalsize", DE_OPT_MAXTOTALSIZE, 1 },
476 { "maxdim", DE_OPT_MAXIMGDIM, 1 },
477 { "dprefix", DE_OPT_DPREFIX, 1 },
478 { "extrlist", DE_OPT_EXTRLIST, 1 },
479 { "onlymods", DE_OPT_ONLYMODS, 1 },
480 { "disablemods", DE_OPT_DISABLEMODS, 1 },
481 { "onlydetect", DE_OPT_ONLYDETECT, 1 },
482 { "nodetect", DE_OPT_NODETECT, 1 },
483 { "colormode", DE_OPT_COLORMODE, 1 },
484 { NULL, DE_OPT_NULL, 0 }
487 static struct opt_struct *opt_string_to_opt_struct(const char *s)
489 int k;
491 for(k=0; option_array[k].id!=DE_OPT_NULL; k++) {
492 if(!strcmp(s, option_array[k].string)) {
493 return &option_array[k];
496 return NULL;
499 static void send_msgs_to_stderr(deark *c, struct cmdctx *cc)
501 cc->msgs_to_stderr = 1;
502 cc->have_initialized_output_stream = 0;
503 cc->msgs_FILE = NULL;
504 #ifdef DE_WINDOWS
505 cc->have_windows_console = 0;
506 #endif
509 static void colormode_opt(struct cmdctx *cc, const char *modestr)
511 if(!strcmp(modestr, "auto")) {
512 cc->color_method_req = CM_AUTOCOLOR;
514 else if(!strcmp(modestr, "ansi")) {
515 cc->color_method_req = CM_ANSI;
517 else if(!strcmp(modestr, "ansi24")) {
518 cc->color_method_req = CM_ANSI24;
520 else if(!strcmp(modestr, "winconsole")) {
521 cc->color_method_req = CM_WINCONSOLE;
523 else if(!strcmp(modestr, "none")) {
524 cc->color_method_req = CM_NOCOLOR;
526 else {
527 de_printf(cc->c, DE_MSGTYPE_MESSAGE, "Invalid colormode: %s\n", modestr);
528 cc->error_flag = 1;
529 return;
533 static void set_output_basename(struct cmdctx *cc)
535 const char *outputbasefn = cc->base_output_filename; // default, could be NULL
536 const char *outdirname;
537 unsigned int flags = 0;
539 if(cc->option_k_level && cc->input_filename) {
540 if(cc->option_k_level==1) {
541 // Use base input filename in output filenames.
542 outputbasefn = cc->input_filename;
543 flags |= 0x1;
545 else if(cc->option_k_level==2) {
546 // Use full input filename path, but not as an actual path.
547 outputbasefn = cc->input_filename;
548 flags |= 0x2;
550 else if(cc->option_k_level==3) {
551 // Use full input filename path, as-is.
552 outputbasefn = cc->input_filename;
556 if(cc->to_zip || cc->to_tar) {
557 // In this case, -od refers to the archive filename, not to the base
558 // filename that we're dealing with here.
559 outdirname = NULL;
561 else if(cc->to_stdout) {
562 // -od is incompatible with -tostdout
563 outdirname = NULL;
565 else {
566 outdirname = cc->output_dirname;
569 de_set_output_filename_pattern(cc->c, outdirname, outputbasefn, flags);
572 static void set_output_archive_name(struct cmdctx *cc)
574 const char *arcfn = cc->archive_filename; // default, could be NULL
575 unsigned int flags = 0;
577 if(!cc->to_zip && !cc->to_tar) return;
578 if(cc->to_stdout) return;
580 if(cc->option_ka_level && cc->input_filename)
582 if(cc->option_ka_level==1) {
583 // Use base input filename in output filenames.
584 arcfn = cc->input_filename;
585 flags |= 0x21;
587 else if(cc->option_ka_level==2) {
588 // Use full input filename path, but not as an actual path.
589 arcfn = cc->input_filename;
590 flags |= 0x22;
592 else if(cc->option_ka_level==3) {
593 // Use full input filename path, as-is.
594 arcfn = cc->input_filename;
595 flags |= 0x20;
599 if(!arcfn) {
600 arcfn = "output";
601 flags |= 0x20;
604 de_set_output_archive_filename(cc->c, cc->output_dirname, arcfn, flags);
607 static void handle_special_1st_filename(struct cmdctx *cc)
609 if(!cc->output_special_1st_filename) return;
610 de_set_output_special_1st_filename(cc->c,
611 ((cc->to_zip || cc->to_tar || cc->to_stdout) ? NULL : cc->output_dirname),
612 cc->output_special_1st_filename);
613 if(!cc->set_MAXFILES) {
614 de_set_max_output_files(cc->c, 1);
618 static void parse_cmdline(deark *c, struct cmdctx *cc, int argc, char **argv)
620 int i;
621 int help_flag = 0;
622 int module_flag = 0;
623 const struct opt_struct *opt;
625 for(i=1;i<argc;i++) {
626 if(argv[i][0]=='-') {
627 if(argv[i][1]=='-') // Allow a superfluous second '-'
628 opt = opt_string_to_opt_struct(argv[i]+2);
629 else
630 opt = opt_string_to_opt_struct(argv[i]+1);
632 if(!opt) {
633 de_printf(c, DE_MSGTYPE_MESSAGE, "Unrecognized option: %s\n", argv[i]);
634 if(!strcmp(argv[i], "-")) {
635 // I don't want "-" to be an alias for "-fromstdin", because it
636 // would have different syntax rules than it does in most programs.
637 // (Question: Is "-" an *option*, or a kind of *filename*?)
638 de_printf(c, DE_MSGTYPE_MESSAGE,
639 "Note: To use stdin/stdout, use \"-fromstdin\"/\"-tostdout\".\n");
641 cc->error_flag = 1;
642 cc->show_usage_message = 1;
643 return;
645 if(i>=argc-opt->extra_args) {
646 de_printf(c, DE_MSGTYPE_MESSAGE, "Option %s needs an argument\n", argv[i]);
647 cc->error_flag = 1;
648 cc->show_usage_message = 1;
649 return;
652 switch(opt->id) {
653 case DE_OPT_D:
654 de_set_std_option_int(c, DE_STDOPT_DEBUG_LEVEL, 1);
655 break;
656 case DE_OPT_D2:
657 de_set_std_option_int(c, DE_STDOPT_DEBUG_LEVEL, 2);
658 break;
659 case DE_OPT_D3:
660 de_set_std_option_int(c, DE_STDOPT_DEBUG_LEVEL, 3);
661 break;
662 case DE_OPT_D4:
663 de_set_std_option_int(c, DE_STDOPT_DEBUG_LEVEL, 4);
664 break;
665 case DE_OPT_L:
666 de_set_std_option_int(c, DE_STDOPT_LISTMODE, 1);
667 break;
668 case DE_OPT_NOINFO:
669 de_set_std_option_int(c, DE_STDOPT_INFOMESSAGES, 0);
670 break;
671 case DE_OPT_NOWARN:
672 de_set_std_option_int(c, DE_STDOPT_WARNINGS, 0);
673 break;
674 case DE_OPT_NOBOM:
675 de_set_std_option_int(c, DE_STDOPT_WRITE_BOM, 0);
676 break;
677 case DE_OPT_NODENS:
678 de_set_std_option_int(c, DE_STDOPT_WRITE_DENSITY, 0);
679 break;
680 case DE_OPT_ASCIIHTML:
681 de_set_std_option_int(c, DE_STDOPT_ASCII_HTML, 1);
682 break;
683 case DE_OPT_NONAMES:
684 de_set_std_option_int(c, DE_STDOPT_FILENAMES_FROM_FILE, 0);
685 break;
686 case DE_OPT_PADPIX:
687 de_set_std_option_int(c, DE_STDOPT_PADPIX, 1);
688 break;
689 case DE_OPT_NOOVERWRITE:
690 de_set_std_option_int(c, DE_STDOPT_OVERWRITE_MODE, DE_OVERWRITEMODE_NEVER);
691 break;
692 case DE_OPT_MODTIME:
693 de_set_preserve_file_times(c, 0, 1);
694 de_set_preserve_file_times(c, 1, 1);
695 break;
696 case DE_OPT_NOMODTIME:
697 de_set_preserve_file_times(c, 0, 0);
698 de_set_preserve_file_times(c, 1, 0);
699 break;
700 case DE_OPT_Q:
701 de_set_std_option_int(c, DE_STDOPT_INFOMESSAGES, 0);
702 de_set_std_option_int(c, DE_STDOPT_WARNINGS, 0);
703 break;
704 case DE_OPT_VERSION:
705 cc->special_command_flag = 1;
706 cc->special_command_code = CMD_PRINTVERSION;
707 break;
708 case DE_OPT_PRINTMODULES:
709 cc->special_command_flag = 1;
710 cc->special_command_code = CMD_PRINTMODULES;
711 break;
712 case DE_OPT_HELP:
713 // At this point, we don't know whether this will be general help,
714 // or module-specific help. So just set a flag for later.
715 help_flag = 1;
716 break;
717 case DE_OPT_LICENSE:
718 cc->special_command_flag = 1;
719 cc->special_command_code = CMD_PRINTLICENSE;
720 break;
721 case DE_OPT_ID:
722 de_set_std_option_int(c, DE_STDOPT_ID_MODE, 1);
723 break;
724 case DE_OPT_NOCHCP:
725 cc->no_chcp = 1;
726 break;
727 case DE_OPT_MAINONLY:
728 de_set_std_option_int(c, DE_STDOPT_EXTRACT_POLICY, DE_EXTRACTPOLICY_MAINONLY);
729 break;
730 case DE_OPT_AUXONLY:
731 de_set_std_option_int(c, DE_STDOPT_EXTRACT_POLICY, DE_EXTRACTPOLICY_AUXONLY);
732 break;
733 case DE_OPT_EXTRACTALL:
734 de_set_std_option_int(c, DE_STDOPT_EXTRACT_LEVEL, 2);
735 break;
736 case DE_OPT_ZIP:
737 de_set_output_style(c, DE_OUTPUTSTYLE_ARCHIVE, DE_ARCHIVEFMT_ZIP);
738 cc->to_zip = 1;
739 break;
740 case DE_OPT_TAR:
741 de_set_output_style(c, DE_OUTPUTSTYLE_ARCHIVE, DE_ARCHIVEFMT_TAR);
742 cc->to_tar = 1;
743 break;
744 case DE_OPT_TOSTDOUT:
745 send_msgs_to_stderr(c, cc);
746 cc->to_stdout = 1;
747 break;
748 case DE_OPT_MSGSTOSTDERR:
749 send_msgs_to_stderr(c, cc);
750 break;
751 case DE_OPT_FROMSTDIN:
752 de_set_input_style(c, DE_INPUTSTYLE_STDIN);
753 cc->from_stdin = 1;
754 break;
755 case DE_OPT_COLOR:
756 colormode_opt(cc, "auto");
757 break;
758 case DE_OPT_K:
759 cc->option_k_level = 1;
760 break;
761 case DE_OPT_K2:
762 cc->option_k_level = 2;
763 break;
764 case DE_OPT_K3:
765 cc->option_k_level = 3;
766 break;
767 case DE_OPT_KA:
768 cc->option_ka_level = 1;
769 break;
770 case DE_OPT_KA2:
771 cc->option_ka_level = 2;
772 break;
773 case DE_OPT_KA3:
774 cc->option_ka_level = 3;
775 break;
776 case DE_OPT_ENCODING:
777 set_encoding_option(c, cc, argv[i+1]);
778 if(cc->error_flag) return;
779 break;
780 case DE_OPT_EXTOPT:
781 set_ext_option(c, cc, argv[i+1]);
782 break;
783 case DE_OPT_FILE:
784 cc->input_filename = argv[i+1];
785 de_set_input_filename(c, cc->input_filename);
786 break;
787 case DE_OPT_FILE2:
788 de_set_ext_option(c, "file2", argv[i+1]);
789 break;
790 case DE_OPT_INENC:
791 if(!de_set_input_encoding(c, argv[i+1], 0)) {
792 de_printf(c, DE_MSGTYPE_MESSAGE,
793 "Error: Unknown input encoding: %s\n", argv[i+1]);
794 cc->error_flag = 1;
795 return;
797 break;
798 case DE_OPT_INTZ:
799 de_set_input_timezone(c, (i64)(3600.0*atof(argv[i+1])));
800 break;
801 case DE_OPT_START:
802 de_set_input_file_slice_start(c, de_atoi64(argv[i+1]));
803 break;
804 case DE_OPT_SIZE:
805 de_set_input_file_slice_size(c, de_atoi64(argv[i+1]));
806 break;
807 case DE_OPT_M:
808 module_flag = 1;
809 de_set_input_format(c, argv[i+1]);
810 break;
811 case DE_OPT_MODCODES:
812 de_set_module_init_codes(c, argv[i+1]);
813 break;
814 case DE_OPT_O:
815 cc->base_output_filename = argv[i+1];
816 break;
817 case DE_OPT_OD:
818 cc->output_dirname = argv[i+1];
819 break;
820 case DE_OPT_T:
821 cc->output_special_1st_filename = argv[i+1];
822 break;
823 case DE_OPT_ARCFN:
824 // Relevant e.g. if the -zip option is used.
825 cc->archive_filename = argv[i+1];
826 break;
827 case DE_OPT_GET:
828 de_set_first_output_file(c, de_atoi(argv[i+1]));
829 de_set_max_output_files(c, 1);
830 break;
831 case DE_OPT_FIRSTFILE:
832 de_set_first_output_file(c, de_atoi(argv[i+1]));
833 break;
834 case DE_OPT_MAXFILES:
835 de_set_max_output_files(c, de_atoi64(argv[i+1]));
836 cc->set_MAXFILES = 1;
837 break;
838 case DE_OPT_MAXFILESIZE:
839 de_set_max_output_file_size(c, de_atoi64(argv[i+1]));
840 break;
841 case DE_OPT_MAXTOTALSIZE:
842 de_set_max_total_output_size(c, de_atoi64(argv[i+1]));
843 break;
844 case DE_OPT_MAXIMGDIM:
845 de_set_max_image_dimension(c, de_atoi64(argv[i+1]));
846 break;
847 case DE_OPT_DPREFIX:
848 de_set_dprefix(c, argv[i+1]);
849 break;
850 case DE_OPT_EXTRLIST:
851 de_set_extrlist_filename(c, argv[i+1]);
852 break;
853 case DE_OPT_ONLYMODS:
854 de_set_disable_mods(c, argv[i+1], 1);
855 break;
856 case DE_OPT_DISABLEMODS:
857 de_set_disable_mods(c, argv[i+1], 0);
858 break;
859 case DE_OPT_ONLYDETECT:
860 de_set_disable_moddetect(c, argv[i+1], 1);
861 break;
862 case DE_OPT_NODETECT:
863 de_set_disable_moddetect(c, argv[i+1], 0);
864 break;
865 case DE_OPT_COLORMODE:
866 colormode_opt(cc, argv[i+1]);
867 if(cc->error_flag) return;
868 break;
869 default:
870 de_printf(c, DE_MSGTYPE_MESSAGE, "Unrecognized option: %s\n", argv[i]);
871 cc->error_flag = 1;
872 cc->show_usage_message = 1;
873 return;
876 i += opt->extra_args;
878 else {
879 if(cc->input_filename) {
880 cc->error_flag = 1;
881 cc->show_usage_message = 1;
882 return;
884 cc->input_filename = argv[i];
885 de_set_input_filename(c, cc->input_filename);
889 if(help_flag) {
890 if(module_flag || cc->input_filename || cc->from_stdin) {
891 de_set_std_option_int(c, DE_STDOPT_WANT_MODHELP, 1);
893 else {
894 cc->special_command_flag = 1;
895 cc->special_command_code = CMD_PRINTHELP;
897 return;
900 if(!cc->input_filename && !cc->special_command_flag && !cc->from_stdin) {
901 de_puts(c, DE_MSGTYPE_MESSAGE, "Error: Need an input filename\n");
902 cc->error_flag = 1;
903 cc->show_usage_message = 1;
904 return;
907 if(cc->to_stdout) {
908 if(cc->to_zip || cc->to_tar) {
909 de_set_output_archive_filename(c, NULL, NULL, 0x10);
911 else {
912 de_set_output_style(c, DE_OUTPUTSTYLE_STDOUT, 0);
913 if(!cc->set_MAXFILES) {
914 de_set_max_output_files(c, 1);
919 set_output_basename(cc);
920 set_output_archive_name(cc);
921 handle_special_1st_filename(cc);
924 static int main2(int argc, char **argv)
926 deark *c = NULL;
927 struct cmdctx *cc = NULL;
928 int ret;
929 int exit_status = 0;
931 cc = de_malloc(NULL, sizeof(struct cmdctx));
932 c = de_create();
933 cc->c = c;
935 de_set_userdata(c, (void*)cc);
936 de_set_fatalerror_callback(c, our_fatalerrorfn);
937 de_set_messages_callback(c, our_msgfn);
938 de_set_special_messages_callback(c, our_specialmsgfn);
939 cc->plctx = de_platformdata_create();
941 if(argc<2) { // Empty command line
942 print_help(c);
943 goto done;
946 parse_cmdline(c, cc, argc, argv);
948 if(cc->error_flag) {
949 if(cc->show_usage_message) {
950 print_usage_error(c);
952 goto done;
955 if(cc->special_command_flag) {
956 switch(cc->special_command_code) {
957 case CMD_PRINTHELP:
958 print_help(c);
959 break;
960 case CMD_PRINTVERSION:
961 print_version(c, 1);
962 break;
963 case CMD_PRINTLICENSE:
964 print_license(c);
965 break;
966 case CMD_PRINTMODULES:
967 print_modules(c);
968 break;
969 default:
970 break;
972 goto done;
975 #ifdef DE_WINDOWS
976 if(cc->to_stdout) {
977 (void)_setmode(_fileno(stdout), _O_BINARY);
979 if(cc->from_stdin) {
980 (void)_setmode(_fileno(stdin), _O_BINARY);
982 #endif
984 ret = de_run(c);
985 if(!ret) {
986 exit_status = 1;
989 done:
990 de_destroy(c);
991 de_platformdata_destroy(cc->plctx);
992 cc->plctx = NULL;
993 if(cc->error_flag) exit_status = 1;
994 de_free(NULL, cc);
995 return exit_status;
998 #ifdef DE_WINDOWS
1000 // This prototype is to silence a possible -Wmissing-prototypes warning.
1001 int wmain(int argc, wchar_t **argvW);
1003 int wmain(int argc, wchar_t **argvW)
1005 char **argv;
1006 int exit_status;
1008 argv = de_convert_args_to_utf8(argc, argvW);
1009 exit_status = main2(argc, argv);
1010 de_free_utf8_args(argc, argv);
1011 return exit_status;
1014 #else
1016 int main(int argc, char **argv)
1018 return main2(argc, argv);
1021 #endif