1 /* This is a fairly simple hexdump program, it has command line options to
2  * enable printing of high latin symbols, and/or use mmap() to load the input
3  * data.
4  *
5  * Reads from stdin if no args are given.
6  *
7  * This shows how to use the Vstr library for simple data convertion.
8  *
9  * This file is more commented than normal code, so as to make it easy to follow
10  * while knowing almost nothing about Vstr or Linux IO programming.
11  */
12 
13 #include "ex_utils.h"
14 
15 #define CONF_USE_MMAP_DEF FALSE
16 #define CONF_PRNT_TYPE    PRNT_SPAC
17 
18 #include "hexdump.h"
19 
20 /* configure what ASCII characters we print */
21 static unsigned int prnt_high_chars = CONF_PRNT_TYPE;
22 
ex_hexdump_process_limit(Vstr_base * s1,Vstr_base * s2,unsigned int lim)23 static void ex_hexdump_process_limit(Vstr_base *s1, Vstr_base *s2,
24                                      unsigned int lim)
25 {
26   while (s2->len > lim)
27   { /* Finish processing read data (try writing if we need memory) */
28     int proc_data = ex_hexdump_process(s1, s1->len, s2, 1, s2->len,
29                                        prnt_high_chars, EX_MAX_W_DATA_INCORE,
30                                        TRUE, !lim);
31 
32     if (s1->conf->malloc_bad)
33       errno = ENOMEM, err(EXIT_FAILURE, "adding data");
34 
35     if (!proc_data && (io_put(s1, STDOUT_FILENO) == IO_BLOCK))
36       io_block(-1, STDOUT_FILENO);
37   }
38 }
39 
40 /* we process an entire file at a time... */
ex_hexdump_read_fd_write_stdout(Vstr_base * s1,Vstr_base * s2,int fd)41 static void ex_hexdump_read_fd_write_stdout(Vstr_base *s1, Vstr_base *s2,
42                                             int fd)
43 {
44   /* read/process/write loop */
45   while (TRUE)
46   {
47     int io_w_state = IO_OK;
48     int io_r_state = io_get(s2, fd);
49 
50     if (io_r_state == IO_EOF)
51       break;
52 
53     ex_hexdump_process(s1, s1->len, s2, 1, s2->len,
54                        prnt_high_chars, EX_MAX_W_DATA_INCORE,
55                        TRUE, FALSE);
56 
57     if (s1->conf->malloc_bad)
58       errno = ENOMEM, err(EXIT_FAILURE, "adding data");
59 
60     io_w_state = io_put(s1, STDOUT_FILENO);
61 
62     io_limit(io_r_state, fd, io_w_state, STDOUT_FILENO, s1);
63   }
64 
65   /* write out all of the end of the file,
66    * so the next file starts on a new line */
67   ex_hexdump_process_limit(s1, s2, 0);
68 }
69 
70 
main(int argc,char * argv[])71 int main(int argc, char *argv[])
72 { /* This is "hexdump", as it should be by default */
73   Vstr_base *s2 = NULL;
74   Vstr_base *s1 = ex_init(&s2); /* init the library, and create two strings */
75   int count = 1; /* skip the program name */
76   unsigned int use_mmap = CONF_USE_MMAP_DEF;
77 
78   /* parse command line arguments... */
79   while (count < argc)
80   { /* quick hack getopt_long */
81     if (!strcmp("--", argv[count]))
82     {
83       ++count;
84       break;
85     }
86     else if (!strcmp("--mmap", argv[count])) /* toggle use of mmap */
87       use_mmap = !use_mmap;
88 
89     else if (!strcmp("--none", argv[count])) /* choose what is displayed */
90       prnt_high_chars = PRNT_NONE; /* just simple 7 bit ASCII, no spaces */
91     else if (!strcmp("--space", argv[count]))
92       prnt_high_chars = PRNT_SPAC; /* allow spaces */
93     else if (!strcmp("--high", argv[count]))
94       prnt_high_chars = PRNT_HIGH; /* allow high bit characters */
95 
96     else if (!strcmp("--version", argv[count]))
97     { /* print version and exit */
98       vstr_add_fmt(s1, 0, "%s", "\
99 jhexdump 1.0.0\n\
100 Written by James Antill\n\
101 \n\
102 Uses Vstr string library.\n\
103 ");
104       goto out;
105     }
106     else if (!strcmp("--help", argv[count]))
107     { /* print version and exit */
108       vstr_add_fmt(s1, 0, "%s", "\
109 Usage: jhexdump [FILENAME]...\n\
110    or: jhexdump OPTION\n\
111 Output filenames in human hexdump format.\n\
112 \n\
113       --help     Display this help and exit\n\
114       --version  Output version information and exit\n\
115       --high     Allow space and high characters in ASCII output\n\
116       --none     Allow only small amount of characters ASCII output\n\
117       --space    Allow space characters in ASCII output (default)\n\
118       --mmap     Toggle use of mmap() to load input files\n\
119       --         Treat rest of cmd line as input filenames\n\
120 \n\
121 Report bugs to James Antill <james@and.org>.\n\
122 ");
123       goto out;
124     }
125     else
126       break;
127     ++count;
128   }
129 
130   /* if no arguments are given just do stdin to stdout */
131   if (count >= argc)
132   {
133     io_fd_set_o_nonblock(STDIN_FILENO);
134     ex_hexdump_read_fd_write_stdout(s1, s2, STDIN_FILENO);
135   }
136 
137   /* loop through all arguments, open the file specified
138    * and do the read/write loop */
139   while (count < argc)
140   {
141     unsigned int ern = 0;
142 
143     ASSERT(!s2->len); /* all input is fully processed before each new file */
144 
145     /* try to mmap the file */
146     if (use_mmap)
147       vstr_sc_mmap_file(s2, s2->len, argv[count], 0, 0, &ern);
148 
149     if (!use_mmap ||
150         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_FSTAT_ERRNO) ||
151         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_MMAP_ERRNO) ||
152         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_TOO_LARGE))
153     { /* if mmap didn't work ... do a read/alter/write loop */
154       int fd = io_open(argv[count]);
155 
156       ex_hexdump_read_fd_write_stdout(s1, s2, fd);
157 
158       if (close(fd) == -1)
159         warn("close(%s)", argv[count]);
160     }
161     else if (ern && (ern != VSTR_TYPE_SC_MMAP_FILE_ERR_CLOSE_ERRNO))
162       err(EXIT_FAILURE, "add");
163     else /* mmap worked so processes the entire file at once */
164       ex_hexdump_process_limit(s1, s2, 0);
165 
166     ++count;
167   }
168   ASSERT(!s2->len); /* all input is fully processed before each new file */
169 
170   /* Cleanup... */
171  out:
172   io_put_all(s1, STDOUT_FILENO);
173 
174   exit (ex_exit(s1, s2));
175 }
176