1 // Copyright 2020 Michael Reilly (mreilly@resiliware.com).
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions
5 // are met:
6 // 1. Redistributions of source code must retain the above copyright
7 //    notice, this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright
9 //    notice, this list of conditions and the following disclaimer in the
10 //    documentation and/or other materials provided with the distribution.
11 // 3. Neither the names of the copyright holders nor the names of the
12 //    contributors may be used to endorse or promote products derived from
13 //    this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18 // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
19 // OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 #define SRCNAME "hexpeek_settings.c"
28 
29 #include <hexpeek.h>
30 
31 #include <stdlib.h>
32 #include <libgen.h>
33 #include <limits.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 
outputWidth(int part,int formode,hoff_t linewh)38 hoff_t outputWidth(int part, int formode, hoff_t linewh)
39 {
40     hoff_t result = 0;
41 
42     switch(part)
43     {
44     case 0:
45         if(Params.margin)
46             result += (Params.margin + strlen(MarginPost));
47         break;
48     case 1:
49         if(Params.mode_groups[formode])
50         {
51             hoff_t seps =  linewh / Params.mode_groups[formode] +
52                           (linewh % Params.mode_groups[formode] ? 1 : 0);
53             if(seps > 0)
54                 result += (strlen(GroupPre(0)) + strlen(GroupTerm));
55             seps--;
56             if(seps > 0)
57                 result += (strlen(GroupPre(1)) + strlen(GroupTerm)) * seps;
58         }
59         else
60         {
61             result += strlen(GroupPre(0));
62         }
63         result += linewh * MODE_CHCNT(formode);
64         break;
65     case 2:
66         if(Params.print_text)
67             result += 2 + linewh;
68         break;
69     default:
70         die();
71     }
72 
73     assert(result >= 0);
74     return result;
75 }
76 
totalWidth(int formode,hoff_t linewh)77 static hoff_t totalWidth(int formode, hoff_t linewh)
78 {
79     hoff_t result = 0;
80     for(int part = 0; part < 3; part++)
81         result += outputWidth(part, formode, linewh);
82     return result;
83 }
84 
ascertainShared(char const * str,int * subtype,char const ** post)85 int ascertainShared(char const *str, int *subtype, char const **post)
86 {
87     assert(str);
88 
89     int cmd = CMD_NONE;
90     int subtp = 0;
91 
92     if(strnconsume(&str, "endianb", 7) == 0)
93     {
94         cmd = CMD_ENDIAN;
95         subtp = 1;
96     }
97     else if(strnconsume(&str, "endianl", 7) == 0)
98     {
99         cmd = CMD_ENDIAN;
100         subtp = -1;
101     }
102     else if(strnconsume(&str, "hexl", 4) == 0)
103     {
104         cmd = CMD_HEX;
105         subtp = 1;
106     }
107     else if(strnconsume(&str, "hexu", 4) == 0)
108     {
109         cmd = CMD_HEX;
110         subtp = -1;
111     }
112     else if(strnconsume(&str, "hex", 3) == 0)
113         cmd = CMD_HEX;
114     else if(strnconsume(&str, "bits", 4) == 0)
115         cmd = CMD_BITS;
116     else if(strnconsume(&str, "rlen", 4) == 0)
117         cmd = CMD_RLEN;
118     else if(strnconsume(&str, "slen", 4) == 0)
119         cmd = CMD_SLEN;
120     else if(strnconsume(&str, "line", 4) == 0)
121         cmd = CMD_LINE;
122     else if(strnconsume(&str, "cols", 4) == 0)
123         cmd = CMD_COLS;
124     else if(strnconsume(&str, "group", 5) == 0)
125         cmd = CMD_GROUP;
126     else if(strnconsume(&str, "margin", 6) == 0)
127         cmd = CMD_MARGIN;
128     else if(strnconsume(&str, "scalar", 6) == 0)
129         cmd = CMD_SCALAR;
130     else if(strnconsume(&str, "prefix", 6) == 0)
131     {
132         cmd = CMD_PREFIX;
133         subtp = 1;
134     }
135     else if(strnconsume(&str, "+prefix", 7) == 0)
136     {
137         cmd = CMD_PREFIX;
138         subtp = -1;
139     }
140     else if(strnconsume(&str, "autoskip", 8) == 0)
141     {
142         cmd = CMD_AUTOSKIP;
143         subtp = 1;
144     }
145     else if(strnconsume(&str, "+autoskip", 9) == 0)
146     {
147         cmd = CMD_AUTOSKIP;
148         subtp = -1;
149     }
150     else if(strnconsume(&str, "diffskip", 8) == 0)
151     {
152         cmd = CMD_DIFFSKIP;
153         subtp = 1;
154     }
155     else if(strnconsume(&str, "+diffskip", 9) == 0)
156     {
157         cmd = CMD_DIFFSKIP;
158         subtp = -1;
159     }
160     else if(strnconsume(&str, "text", 4) == 0)
161     {
162         cmd = CMD_TEXT;
163         if(strnconsume(&str, "=ascii", 6) == 0)
164             subtp = CODEPAGE_ASCII;
165         else if(strnconsume(&str, "=ebcdic", 7) == 0)
166             subtp = CODEPAGE_EBCDIC;
167         else
168             subtp = CODEPAGE_NIL;
169     }
170     else if(strnconsume(&str, "+text", 5) == 0)
171     {
172         cmd = CMD_TEXT;
173         if(strnconsume(&str, "=ascii", 6) == 0)
174             subtp = -CODEPAGE_ASCII;
175         else if(strnconsume(&str, "=ebcdic", 7) == 0)
176             subtp = -CODEPAGE_EBCDIC;
177         else
178             subtp = -CODEPAGE_NIL;
179     }
180     else if(strnconsume(&str, "ruler", 5) == 0)
181     {
182         cmd = CMD_RULER;
183         subtp = 1;
184     }
185     else if(strnconsume(&str, "+ruler", 6) == 0)
186     {
187         cmd = CMD_RULER;
188         subtp = -1;
189     }
190 
191     if(cmd != CMD_NONE)
192     {
193         if(subtype)
194             *subtype = subtp;
195         if(post)
196             *post = str;
197     }
198     return cmd;
199 }
200 
setModeVar(hoff_t * arr,int formode,hoff_t value)201 static rc_t setModeVar(hoff_t *arr, int formode, hoff_t value)
202 {
203     rc_t rc = RC_UNSPEC;
204 
205     if(arr == Params.mode_lines)
206     {
207         if(value > MAXW_LINE)
208         {
209             rc = RC_USER;
210             prerr("line width may not exceed " MS(MAXW_LINE) " octets\n");
211             goto end;
212         }
213     }
214     if(arr == Params.mode_groups)
215     {
216         if(value > MAXW_GROUP)
217         {
218             rc = RC_USER;
219             prerr("group width may not exceed " MS(MAXW_GROUP) " octets\n");
220             goto end;
221         }
222     }
223 
224     if(formode < 0)
225     {
226         for(formode = 0; formode < MODE_COUNT; formode++)
227             arr[formode] = value;
228     }
229     else
230     {
231         assert(formode < MODE_COUNT);
232         arr[formode] = value;
233     }
234 
235     rc = RC_OK;
236 
237 end:
238     return rc;
239 }
240 
processShared(int cmd,int subtype,char const * arg,int formode)241 rc_t processShared(int cmd, int subtype, char const *arg, int formode)
242 {
243     rc_t rc = RC_UNSPEC;
244     hoff_t tmph = 0;
245 
246     if(cmd == CMD_ENDIAN)
247     {
248         Params.endian_big = (subtype > 0);
249     }
250     else if(cmd == CMD_HEX)
251     {
252         Params.disp_mode = MODE_HEX;
253         if(subtype)
254             Params.hexlower = (subtype > 0);
255     }
256     else if(cmd == CMD_BITS)
257     {
258         Params.disp_mode = MODE_BITS;
259     }
260     else if(cmd == CMD_RLEN)
261     {
262         if((rc = strtosz(arg, &tmph)))
263             goto end;
264         if((rc = setModeVar(Params.mode_print_defs, formode, tmph)))
265             goto end;
266     }
267     else if(cmd == CMD_SLEN)
268     {
269         if((rc = strtosz(arg, &tmph)))
270             goto end;
271         if((rc = setModeVar(Params.mode_search_defs, formode, tmph)))
272             goto end;
273     }
274     else if(cmd == CMD_LINE)
275     {
276         if((rc = strtosz(arg, &tmph)))
277             goto end;
278         if((rc = setModeVar(Params.mode_lines, formode, tmph)))
279             goto end;
280     }
281     else if(cmd == CMD_COLS)
282     {
283         if((rc = strtosz(arg, &tmph)))
284             goto end;
285         if((rc = setModeVar(Params.mode_lines, formode, tmph)))
286             goto end;
287         if((rc = setModeVar(Params.mode_print_defs, formode, tmph)))
288             goto end;
289         if((rc = setModeVar(Params.mode_search_defs, formode, tmph)))
290             goto end;
291     }
292     else if(cmd == CMD_GROUP)
293     {
294         if((rc = strtosz(arg, &tmph)))
295             goto end;
296         if((rc = setModeVar(Params.mode_groups, formode, tmph)))
297             goto end;
298     }
299     else if(cmd == CMD_MARGIN)
300     {
301         assert(arg);
302         if(streq(arg, "full"))
303         {
304             Params.margin = HOFF_HEX_FULL_WIDTH;
305         }
306         else
307         {
308             rc = strtosz(arg, &tmph);
309             checkrc(rc);
310             // Argument is taken in octet length, but saved in char length
311             if(tmph >= INT_MAX / MODE_CHCNT(MODE_HEX))
312             {
313                 rc = RC_USER;
314                 prerr("excessive margin width\n");
315                 goto end;
316             }
317             Params.margin = MODE_CHCNT(MODE_HEX) * (int)tmph;
318         }
319     }
320     else if(cmd == CMD_SCALAR)
321     {
322         assert(arg);
323         if(streq(arg, MS(DEF_SCALAR_BASE)))
324             Params.scalar_base = DEF_SCALAR_BASE;
325         else if(streq(arg, "0"))
326             Params.scalar_base = 0;
327         else
328         {
329             rc = RC_USER;
330             prerr("invalid argument to scalar\n");
331             goto end;
332         }
333     }
334     else if(cmd == CMD_PREFIX)
335     {
336         Params.print_prefix = (subtype > 0);
337     }
338     else if(cmd == CMD_AUTOSKIP)
339     {
340         Params.autoskip = (subtype > 0);
341     }
342     else if(cmd == CMD_DIFFSKIP)
343     {
344         Params.diffskip = (subtype > 0);
345     }
346     else if(cmd == CMD_TEXT)
347     {
348         Params.print_text = (subtype > 0);
349         subtype = abs(subtype);
350         if(subtype != CODEPAGE_NIL)
351             Params.text_encoding = subtype;
352     }
353     else if(cmd == CMD_RULER)
354     {
355         Params.ruler = (subtype > 0);
356     }
357     else
358     {
359         rc = RC_NIL;
360         goto end;
361     }
362 
363     rc = RC_OK;
364 
365 end:
366     return rc;
367 }
368 
generateCommand(int op,char * at,char * len)369 void generateCommand(int op, char *at, char *len)
370 {
371     traceEntry("%d, '%s', '%s'", op, at, len);
372 
373     assert(op > 0);
374     assert( ! GeneratedCommand_mal);
375     assert( ! Params.command);
376 
377     if( ! at)
378         at = "0";
379 
380     size_t s_len = 0;
381     s_len += 3;                // "$0@"
382     s_len += strlen(at);       // <AT>
383     s_len += 1;                // "," or ":"
384     if(len)
385         s_len += strlen(len);  // <LEN>
386     else
387         s_len += 3;            // "max"
388     s_len += 1;                // "~" or "\0"
389 
390     s_len *= op;
391 
392     GeneratedCommand_mal = Malloc(s_len);
393 
394     for(int ix = 0; ix < op; ix++)
395     {
396         if(ix == 0)
397             strcat(GeneratedCommand_mal, "$0@");
398         else
399             strcat(GeneratedCommand_mal, "~$1@");
400         strcat(GeneratedCommand_mal, at);
401         if(len)
402         {
403             strcat(GeneratedCommand_mal, ",");
404             strcat(GeneratedCommand_mal, len);
405         }
406         else
407             strcat(GeneratedCommand_mal, ":max");
408     }
409 
410     GeneratedCommand_mal[s_len - 1] = '\0';
411     Params.command = GeneratedCommand_mal;
412 
413     traceExit("'%s'", Params.command);
414 }
415 
parseDescriptor(char const * string)416 int parseDescriptor(char const *string)
417 {
418     char *endptr = NULL;
419     long tmpl = strtol(string, &endptr, 10); // always decimal
420     if(endptr == string || *endptr != '\0' || tmpl < 0 || tmpl > INT_MAX)
421         return -1;
422     return (int)tmpl;
423 }
424 
425 // Macros for simple, but unavoidable, repetition
426 #define advanceArgs() \
427     if(ix + 1 >= argc) \
428     { \
429         rc = RC_USER; \
430         prerr("missing argument to '%s'\n", argv[ix]); \
431         goto end; \
432     } \
433     ix++;
434 #define setupDump() \
435     subsequent_open_flags = O_RDONLY; \
436     do_dump = true;
437 #define setupPack() \
438     subsequent_open_flags = O_RDONLY; \
439     Params.do_pack = true;
440 #define setupDiff() \
441     subsequent_open_flags = O_RDONLY; \
442     do_diff = true;
443 
444 // Only sets members of Params indicated by arguments, leaves other members at
445 // their initial values. Do not pass read-only strings in argv.
parseArgv(int argc,char ** argv)446 rc_t parseArgv(int argc, char **argv)
447 {
448     rc_t rc = RC_UNSPEC;
449     int ix = 0, counter = 0;
450     int subsequent_open_flags = -1, file_count = 0, pending = -1;
451     bool flags_done = false, do_dump = false, do_diff = false;
452     bool line_z = false, group_z = false;
453     char *cmd_at = NULL, *cmd_len = NULL, *found = NULL;
454 
455     assert(argv);
456 
457     // Alternate invocations
458     char const *invok = basename(argv[0]);
459     if(streq(invok, viwNM))
460     {
461         subsequent_open_flags = O_RDONLY;
462     }
463     else if(streq(invok, DmpNM))
464     {
465         setupDump();
466     }
467     else if(streq(invok, pckNM))
468     {
469         setupPack();
470     }
471     else if(streq(invok, dffNM))
472     {
473         setupDiff();
474     }
475 
476     // Flag parser
477     for(ix = 1; ix < argc; )
478     {
479         bool isfd = false;
480         if(flags_done)
481             goto process_infile;
482 
483         rc = plugin_argv(argc, argv, &ix);
484         if(rc == RC_OK)
485             continue;
486         else if(rc != RC_NIL)
487             goto end;
488 
489         if(streq(argv[ix], "-dump"))
490         {
491             setupDump();
492         }
493         else if(streq(argv[ix], "-pack"))
494         {
495             setupPack();
496         }
497         else if(streq(argv[ix], "-diff"))
498         {
499             setupDiff();
500         }
501         else if(streq(argv[ix], "-s"))
502         {
503             advanceArgs();
504             cmd_at = argv[ix];
505         }
506         else if(streq(argv[ix], "-l"))
507         {
508             advanceArgs();
509             cmd_len = argv[ix];
510         }
511         else if(streq(argv[ix], "-r"))
512         {
513             subsequent_open_flags = O_RDONLY;
514             pending = file_count;
515         }
516         else if(streq(argv[ix], "-w"))
517         {
518             subsequent_open_flags = O_RDWR | O_CREAT;
519             pending = file_count;
520         }
521         else if(streq(argv[ix], "-W"))
522         {
523             subsequent_open_flags = O_RDWR;
524             pending = file_count;
525         }
526         else if(streq(argv[ix], "-ik"))
527         {
528             Params.allow_ik = true;
529         }
530         else if(streq(argv[ix], "+ik"))
531         {
532             Params.allow_ik = false;
533         }
534         else if(streq(argv[ix], "-x"))
535         {
536             advanceArgs();
537             Params.command = argv[ix];
538         }
539         else if(streq(argv[ix], "-o"))
540         {
541             int tmpfd = -1;
542             advanceArgs();
543             if(streq(argv[ix], "-d"))
544             {
545                 advanceArgs();
546                 tmpfd = parseDescriptor(argv[ix]);
547                 if(tmpfd < 0)
548                 {
549                     rc = RC_USER;
550                     prerr("bad input to '-o -d'\n");
551                     goto end;
552                 }
553             }
554             else
555             {
556                 int o_flags = O_WRONLY | O_CREAT;
557                 if( ! Params.do_pack)
558                     o_flags |= O_TRUNC;
559                 rc = hexpeek_open(argv[ix], o_flags, PERM, &tmpfd);
560                 if(rc)
561                     goto end;
562             }
563             if(tmpfd != STDOUT_FILENO)
564                 assert(dup2(tmpfd, STDOUT_FILENO) == STDOUT_FILENO);
565         }
566         else if(streq(argv[ix], "+lineterm"))
567         {
568             Params.line_term = "";
569         }
570         else if(streq(argv[ix], "-format"))
571         {
572             advanceArgs();
573             if(strnconsume((char const **)&argv[ix],
574                            GroupFmtLiTern, strlen(GroupFmtLiTern)) == 0)
575                 Params.group_pre[0] = argv[ix] + 1;
576             else
577                 Params.group_pre[0] = argv[ix];
578             Params.group_pre[1] = argv[ix];
579             char *first = NULL;
580             for(found = argv[ix]; ; )
581             {
582                 found = strchr(found, GroupFmtGroup[0]);
583                 if( ! found)
584                     break;
585                 if(strncmp(found, GroupFmtGroup, strlen(GroupFmtGroup)) == 0)
586                 {
587                     if(first)
588                     {
589                         rc = RC_USER;
590                         prerr("duplicate '%s'\n", GroupFmtGroup);
591                         goto end;
592                     }
593                     first = found;
594                     found += strlen(GroupFmtGroup);
595                 }
596                 else if(found[1] == '%')
597                 {
598                     memmove(found, found + 1, strlen(found));
599                     found++;
600                 }
601                 else
602                 {
603                     rc = RC_USER;
604                     prerr("unrecognized format specifier\n");
605                     goto end;
606                 }
607             }
608             if( ! first)
609             {
610                 rc = RC_USER;
611                 prerr("format string must contain '%s'\n", GroupFmtGroup);
612                 goto end;
613             }
614             *first = '\0';
615             Params.group_term = first + strlen(GroupFmtGroup);
616         }
617         else if(streq(argv[ix], "-pedantic"))
618         {
619             Params.infer = false;
620             Params.tolerate_eof = false;
621         }
622         else if(streq(argv[ix], "+pedantic"))
623         {
624             Params.infer = true;
625             Params.tolerate_eof = true;
626         }
627         else if(streq(argv[ix], "-strict"))
628         {
629             Params.fail_strict = true;
630         }
631         else if(streq(argv[ix], "+strict"))
632         {
633             Params.fail_strict = false;
634         }
635         else if(streq(argv[ix], "-unique"))
636         {
637             Params.assume_unique_infiles = true;
638         }
639         else if(streq(argv[ix], "+tty"))
640         {
641             Params.assume_ttys = 0;
642         }
643         else if(streq(argv[ix], "-backup"))
644         {
645             advanceArgs();
646             if(streq(argv[ix], "sync"))
647                 Params.backup_sync = true;
648             else if(streq(argv[ix], "max"))
649                 Params.backup_depth = MAX_BACKUP_DEPTH;
650             else
651             {
652                 char *endptr = NULL;
653                 long tmpl = strtol(argv[ix], &endptr, Params.scalar_base);
654                 if(endptr != argv[ix] && *endptr == '\0' &&
655                    tmpl >= 0 && tmpl <= MAX_BACKUP_DEPTH)
656                 {
657                     Params.backup_depth = tmpl;
658                 }
659                 else
660                 {
661                     rc = RC_USER;
662                     prerr("invalid argument to -backup\n");
663                     goto end;
664                 }
665             }
666         }
667         else if(streq(argv[ix], "-recover"))
668         {
669             if(Params.recover_interactive)
670             {
671                 rc = RC_USER;
672                 prerr("duplicate -recover flag\n");
673                 goto end;
674             }
675             Params.recover_interactive = true;
676         }
677         else if(streq(argv[ix], "-AutoRecover"))
678         {
679             if(Params.recover_auto)
680             {
681                 rc = RC_USER;
682                 prerr("duplicate -AutoRecover flag\n");
683                 goto end;
684             }
685             Params.recover_auto = true;
686         }
687 #ifdef HEXPEEK_TRACE
688         else if(streq(argv[ix], "-trace"))
689         {
690             advanceArgs();
691             Params.trace_fp = fopen(argv[ix], "w");
692             if( ! Params.trace_fp)
693             {
694                 rc = RC_CRIT;
695                 prerr("error opening file \"%s\": %s\n", cleanstring(argv[ix]),
696                       strerror(errno));
697                 goto end;
698             }
699             trace("TRACE START\n");
700             trace("Invocation:");
701             for(int trace_idx = 0; trace_idx < argc; trace_idx++)
702                 fprintf(Params.trace_fp, " '%s'", argv[trace_idx]);
703             fprintf(Params.trace_fp, "\n");
704             trace("HOFF_MAX = " TRC_hoff "\n", trchoff(HOFF_MAX));
705         }
706 #endif
707         else if(streq(argv[ix], "-p"))
708         {
709             rc = processShared(CMD_COLS, 0, "0", -1);
710             checkrc(rc);
711             rc = processShared(CMD_GROUP, 0, "0", -1);
712             checkrc(rc);
713             Params.margin     = 0;
714             Params.autoskip   = false;
715             Params.diffskip   = false;
716             Params.print_text = false;
717             Params.ruler      = false;
718         }
719         else if(streq(argv[ix], "-d"))
720         {
721             advanceArgs();
722             isfd = true;
723             goto process_infile;
724         }
725         else if(streq(argv[ix], "--"))
726         {
727             flags_done = true;
728         }
729         else if((*argv[ix] == '-' || *argv[ix] == '+') && argv[ix][1] != '\0')
730         {
731             int tmpcmd = CMD_NONE, tmpst = 0;
732             char const *postflag = NULL;
733             if(*argv[ix] == '+')
734                 tmpcmd = ascertainShared(argv[ix], &tmpst, &postflag);
735             else
736                 tmpcmd = ascertainShared(argv[ix] + 1, &tmpst, &postflag);
737             if(tmpcmd == CMD_NONE)
738             {
739                 // No match, try short flags
740                 if(streq(argv[ix], "-b"))
741                     tmpcmd = CMD_BITS;
742                 else if(streq(argv[ix], "-c"))
743                     tmpcmd = CMD_COLS;
744                 else if(streq(argv[ix], "-g"))
745                     tmpcmd = CMD_GROUP;
746             }
747             if(tmpcmd == CMD_NONE)
748             {
749                 rc = RC_USER;
750                 prerr("unrecognized flag '%s'\n", argv[ix]);
751                 goto end;
752             }
753             else if(postflag && *postflag != '\0')
754             {
755                 rc = RC_USER;
756                 prerr("trailing text to setting flag\n");
757                 goto end;
758             }
759             else
760             {
761                 char const *tmparg = NULL;
762                 switch(tmpcmd)
763                 {
764                 case CMD_RLEN:
765                 case CMD_SLEN:
766                 case CMD_LINE:
767                 case CMD_COLS:
768                 case CMD_GROUP:
769                 case CMD_MARGIN:
770                 case CMD_SCALAR:
771                     advanceArgs();
772                     tmparg = argv[ix];
773                     break;
774                 }
775                 rc = processShared(tmpcmd, tmpst, tmparg, -1);
776                 checkrc(rc);
777             }
778         }
779         else
780         {
781 process_infile:
782             if(file_count >= MAX_INFILES)
783             {
784                 rc = RC_USER;
785                 prerr("too many infiles\n");
786                 goto end;
787             }
788             if(isfd)
789             {
790                 // non-path infile from -d
791                 Params.infiles[file_count].fd = parseDescriptor(argv[ix]);
792                 if(Params.infiles[file_count].fd < 0)
793                 {
794                     rc = RC_USER;
795                     prerr("bad input to '-d'\n");
796                     goto end;
797                 }
798             }
799             else
800             {
801                 Params.infiles[file_count].path = argv[ix];
802             }
803             Params.infiles[file_count].open_flags = subsequent_open_flags;
804             file_count++;
805         }
806 
807         ix++;
808     }
809 
810     // Check hanging flags
811     if(pending >= file_count)
812     {
813         rc = RC_USER;
814         prerr("-r, -w, or -W after infiles has no effect!\n");
815         goto end;
816     }
817 
818     // Special operations
819     if((cmd_at || cmd_len) && ! do_diff)
820         do_dump = true;
821     if(Params.command)
822         counter++;
823     if(do_dump)
824         counter++;
825     if(do_diff)
826         counter++;
827     if(Params.do_pack)
828         counter++;
829     if(Params.recover_interactive || Params.recover_auto)
830         counter++;
831     if(counter > 1)
832     {
833         rc = RC_USER;
834         prerr("more than one of -x, -dump, -diff, -pack, and -recover"
835               " specified\n");
836         goto end;
837     }
838     if(do_dump)
839     {
840         if(file_count > 1)
841         {
842             rc = RC_USER;
843             prerr("cannot dump more than one file\n");
844             goto end;
845         }
846         generateCommand(1, cmd_at, cmd_len);
847     }
848     else if(Params.do_pack)
849     {
850         if(file_count > 1)
851         {
852             rc = RC_USER;
853             prerr("cannot pack more than one input file\n");
854             goto end;
855         }
856     }
857     else if(do_diff)
858     {
859         if(file_count != 2)
860         {
861             rc = RC_USER;
862             prerr("need two files to diff\n");
863             goto end;
864         }
865         generateCommand(2, cmd_at, cmd_len);
866     }
867 
868     // Recovery mode
869     if(Params.recover_interactive && Params.recover_auto)
870     {
871         rc = RC_USER;
872         prerr("-recover and -AutoRecover conflict\n");
873         goto end;
874     }
875     if(Params.recover_interactive || Params.recover_auto)
876     {
877         if(file_count > 1)
878         {
879             rc = RC_USER;
880             prerr("only one file can be recovered at a time\n");
881             goto end;
882         }
883         if(Params.backup_depth > 0)
884         {
885             rc = RC_USER;
886             prerr("Recovery mode and backup depth > 0 conflict\n");
887             goto end;
888         }
889         Params.backup_depth = 0;
890         if(Params.infiles[0].open_flags < 0)
891         {
892             Params.infiles[0].open_flags = O_RDWR;
893         }
894         else if(Params.infiles[0].open_flags != O_RDWR)
895         {
896             rc = RC_USER;
897             prerr("Recovery mode requires write permission to data file\n");
898             goto end;
899         }
900     }
901     // Set default open flags if not given
902     else
903     {
904         if(Params.backup_depth < 0)
905             Params.backup_depth = DEFAULT_BACKUP_DEPTH;
906         for(int fi = 0; fi < MAX_INFILES; fi++)
907         {
908             if(Params.infiles[fi].path)
909             {
910                 if(Params.infiles[fi].open_flags < 0)
911                 {
912                     if(access(Params.infiles[fi].path, F_OK) == 0 &&
913                        access(Params.infiles[fi].path, W_OK))
914                         Params.infiles[fi].open_flags = O_RDONLY;
915                     else
916                         Params.infiles[fi].open_flags = O_RDWR | O_CREAT;
917                 }
918             }
919             else if(Params.infiles[fi].open_flags < 0)
920             {
921                 Params.infiles[fi].open_flags =
922                     fcntl(Params.infiles[fi].fd, F_GETFL);
923             }
924         }
925     }
926 
927     // Set default print format variables if not given
928     if(Params.margin < 0)
929         Params.margin = HOFF_HEX_DEFAULT_WIDTH;
930     for(int md = 0; md < MODE_COUNT; md++)
931     {
932         hoff_t divisor = (do_diff ? 2 : 1);
933         for(hoff_t guess = 0x20; guess > 1; guess /= 2)
934         {
935             if(totalWidth(md, guess) <= TERMINAL_WIDTH)
936             {
937                 if(Params.mode_print_defs[md] < 0)
938                     Params.mode_print_defs[md]  = guess / divisor;
939                 if(Params.mode_search_defs[md] < 0)
940                     Params.mode_search_defs[md] = guess / divisor;
941                 if(Params.mode_lines[md] < 0)
942                     Params.mode_lines[md]       = guess / divisor;
943                 break;
944             }
945         }
946     }
947 
948     // Check if line or group width is zero
949     for(int md = 0; md < MODE_COUNT; md++)
950     {
951         if(Params.mode_lines[md] == 0)
952             line_z = true;
953         if(Params.mode_groups[md] == 0)
954             group_z = true;
955     }
956 
957     // Set other defaults
958     if(Params.autoskip < 0)
959         Params.autoskip = interactive();
960     if(Params.print_text < 0 && ! line_z)
961         Params.print_text = interactive();
962     if(Params.fail_strict < 0)
963         Params.fail_strict = ! interactive();
964 #ifdef HEXPEEK_EDITABLE_CONSOLE
965     if(Params.editable_console < 0)
966         Params.editable_console = interactive();
967 #endif
968 
969     // Non-critical warnings
970     if(line_z && Params.print_text)
971         prwarn("zero line width disables text output\n");
972     if(group_z && ! Params.endian_big)
973         prwarn("zero group width disables little endian mode\n");
974 
975     rc = RC_OK;
976 
977 end:
978     if(rc && ix < argc)
979     {
980         prerr("error while processing argument '%s' at position %d\n",
981               argv[ix], ix);
982     }
983     if(Params.mode_print_defs[MODE_HEX] < 0)
984         Params.mode_print_defs[MODE_HEX]  = 0x20;
985     if(Params.mode_search_defs[MODE_HEX] < 0)
986         Params.mode_search_defs[MODE_HEX]  = 0x20;
987     if(Params.mode_lines[MODE_HEX] < 0)
988         Params.mode_lines[MODE_HEX]       = 0x20;
989     if(Params.mode_print_defs[MODE_BITS] < 0)
990         Params.mode_print_defs[MODE_BITS] = 0x8;
991     if(Params.mode_search_defs[MODE_BITS] < 0)
992         Params.mode_search_defs[MODE_BITS] = 0x8;
993     if(Params.mode_lines[MODE_BITS] < 0)
994         Params.mode_lines[MODE_BITS]      = 0x8;
995     return rc;
996 }
997