output command
[hexdump.git] / hexdump.c
1 /* depend: */
2 /* cflags: */
3 /* linker: debug.o */
4
5 #include <assert.h>
6 #include <getopt.h>
7 #include <malloc.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "debug.h"
13
14 /* macros */
15
16 #define CEIL(x, y) (((x) + (y) - 1) / (y))
17 #define MIN(x, y) (((x) < (y)) ? (x) : (y))
18 #define MAX(x, y) (((x) > (y)) ? (x) : (y))
19
20 //#define BUFFERSIZE 4096
21 #define BUFFERSIZE 256
22 #define NBCOLS 8
23
24 /* gobal variables */
25
26 char *buffer[BUFFERSIZE] = {0};
27 FILE *fin = NULL;
28 FILE *fout = NULL;
29 char *progname = NULL;
30
31 /* help function */
32
33 void usage (int ret)
34 {
35 FILE *fd = ret ? stderr : stdout;
36 fprintf (fd, "usage: %s [-i file] [-h] [-n nbcols] [-o file] [-v]\n", progname);
37 fprintf (fd, " -i: input file\n");
38 fprintf (fd, " -h: help message\n");
39 fprintf (fd, " -n: number of columns\n");
40 fprintf (fd, " -e: commands\n");
41 fprintf (fd, " -o: output file\n");
42 fprintf (fd, " -v: verbose level (%d)\n", verbose);
43 fprintf (fd, "\n");
44 fprintf (fd, "commands: [/hstr/|0xaddr] [a hstr] [d nb|-] [i hstr] [p nb|-] [s/h1/h2/[g]]\n");
45 fprintf (fd, " 0x: move to address addr\n");
46 fprintf (fd, " //: move to hexa stringi hstr\n");
47 fprintf (fd, " a : append hexa string hstr to current address\n");
48 fprintf (fd, " d : delete nb bytes (- until end file)\n");
49 fprintf (fd, " i : insert hexa string hstr to current address\n");
50 fprintf (fd, " p : print nb bytes (- until end file)\n");
51 fprintf (fd, " s : substitute h1 by h2 (g for globally)\n");
52
53 exit (ret);
54 }
55
56 /* get number of digits */
57
58 int getnbdigits (long int l) {
59 int n = 0;
60 while (l) {
61 n += 2;
62 l /= 256;
63 }
64 return n;
65 }
66
67 /* print a line */
68
69 void printline (char *buffer, int nbcols, int nb, int addr, int nbdigits) {
70 int i;
71
72 printf ("0x%0*x:", nbdigits, addr);
73 for (i = 0; i < nb; i++) {
74 printf (" %02x", buffer[i]);
75 }
76 for (i = nb; i < nbcols; i++) {
77 printf (" ");
78 }
79 printf (" ");
80 for (i = 0; i < nb; i++) {
81 char c = buffer[i];
82 printf ("%c", (c > 31) && (c < 127) ? c : '.');
83 }
84 printf ("\n");
85 }
86
87 /* write file function */
88
89 int writefile (char *pt, int nb) {
90 if (fout) {
91 fwrite (pt, 1, nb, fout);
92 }
93 return 1;
94 }
95
96 /* hexadecimal dump function */
97
98 int hexdump (int nbcols, int len) {
99 char buffer[BUFFERSIZE] = {0};
100 int i;
101
102 char *pt = buffer;
103
104 /* get file size */
105 int nbdigits = 0;
106 if (fin != stdin) {
107 fseek (fin, 0 , SEEK_END);
108 long int filesize = ftell (fin);
109 fseek (fin, 0 , SEEK_SET);
110 nbdigits = getnbdigits (filesize);
111 } else {
112 nbdigits = 6;
113 }
114
115 int addr = 0;
116 int nb = 0;
117 while (!feof (fin)) {
118 int nbtoread = BUFFERSIZE - (pt - buffer);
119 if ((len > 0) && (nbtoread > len)) {
120 nbtoread = len;
121 }
122 int nbread = fread (pt, 1, nbtoread, fin);
123 if (len > 0) {
124 len -= nbread;
125 }
126 nb += nbread;
127 pt = buffer;
128
129 /* print line */
130 while ((nb - (int)(pt - buffer)) / nbcols > 0) {
131 printline (pt, nbcols, nbcols, addr, nbdigits);
132 writefile (pt, nbcols);
133 pt += nbcols;
134 addr += nbcols;
135 }
136
137 /* copy end buffer */
138 nb -= pt - buffer;
139 for (i = 0; i < nb; i++) {
140 buffer[i] = pt[i];
141 }
142 pt = buffer + nb;
143
144 /* end partial reading */
145 if (len == 0) {
146 break;
147 }
148 }
149
150 /* last line */
151 if (nb > 0) {
152 printline (buffer, nbcols, nb, addr, nbdigits);
153 writefile (pt, nb);
154 }
155
156 return 0;
157 }
158
159 /* main function */
160
161 int main (int argc, char *argv[])
162 {
163 int rc = 0;
164 char *input = NULL;
165 char *output = NULL;
166 int nbcols = NBCOLS;
167 char *commands = NULL;
168 int printlen = -1;
169
170 /* get basename */
171 char *pt = progname = argv[0];
172 while (*pt) {
173 if ((*pt == '/') || (*pt == '\\')) {
174 progname = pt + 1;
175 }
176 pt++;
177 }
178
179 int c;
180 while ((c = getopt(argc, argv, "e:i:hn:o:v:")) != EOF) {
181 switch (c) {
182 case 'i':
183 input = optarg;
184 break;
185 case 'e':
186 if (commands == NULL) {
187 commands = optarg;
188 } else {
189 strcat (commands, " ");
190 strcat (commands, optarg);
191 }
192 break;
193 case 'n':
194 nbcols = atoi (optarg);
195 break;
196 case 'o':
197 output = optarg;
198 break;
199 case 'v':
200 verbose = atoi (optarg);
201 break;
202 case 'h':
203 default:
204 usage (c != 'h');
205 }
206 }
207 if (argc - optind != 0) {
208 fprintf (stderr, "%s: invalid option -- %s\n", progname, argv[optind]);
209 usage (1);
210 }
211
212 /* check input */
213 if (input) {
214 fin = fopen (input, "rb");
215 if (!fin) {
216 VERBOSE (ERROR, fprintf (stderr, "error: can't open file '%s'\n", input));
217 return 1;
218 }
219 } else {
220 fin = stdin;
221 }
222
223 /* check output */
224 if (output) {
225 fout = fopen (output, "wb");
226 if (!fout) {
227 VERBOSE (ERROR, fprintf (stderr, "error: can't open file '%s'\n", output));
228 fclose (fin);
229 return 1;
230 }
231 } else {
232 //fout = stdout;
233 }
234
235 if (commands == NULL) {
236 hexdump (nbcols, -1);
237 } else {
238 VERBOSE (DEBUG, printf ("commands: %s\n", commands));
239 while ((*commands != '\0') && (rc == 0)) {
240 switch (*commands++) {
241 case ' ':
242 case '\t':
243 break;
244
245 case '/': /* read patern */
246 break;
247
248 case '0': /* read address */
249 break;
250
251 case 'a': /* append mode */
252 break;
253
254 case 'd': /* delete mode */
255 break;
256
257 case 'i': /* insert mode */
258 break;
259
260 case 'p': /* print mode */
261 printlen = -1;
262 while (*commands != '\0') {
263 if ((*commands == ' ') || (*commands == '\t')) {
264 commands++;
265 } else if ((*commands >= '0') && (*commands <= '9')) {
266 printlen = strtol (commands, &commands, 10);
267 break;
268 } else if (*commands == '-') {
269 printlen = -1;
270 commands++;
271 break;
272 } else {
273 VERBOSE (ERROR, fprintf (stderr, "unkown print lenght (%s)\n", commands));
274 rc = 1;
275 break;
276 }
277 }
278 if (rc == 0) hexdump (nbcols, printlen);
279 break;
280
281 case 's': /* substitute mode */
282 break;
283
284 default:
285 VERBOSE (ERROR, fprintf (stderr, "unknown command (%c)\n", commands[-1]));
286 rc = 1;
287 }
288 }
289 }
290
291 /* end of file */
292 if ((rc == 0) && (fout != NULL)) {
293 while (!feof (fin)) {
294 int nbread = fread (buffer, 1, BUFFERSIZE, fin);
295 if (nbread) {
296 fwrite (buffer, 1, nbread, fout);
297 }
298 }
299 }
300
301 /* close all */
302 if (fin) fclose (fin);
303 if (fout) fclose (fout);
304
305 return rc;
306 }
307
308 // test: hexdump.exe -h
309 // test: hexdump.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
310 // test: hexdump.exe -_ 2> /dev/null | awk 'END { if (NR == 0) { exit(0) } else exit (1) }'
311 // test: hexdump.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
312 // test: hexdump.exe -i hexdump.c | grep -q '0x[0-9a-f]*: '
313 // test: hexdump.exe -i hexdump.c -n 3 | head -2 | tail -1 | grep -q '0x0003: 64 65 70 dep'
314 // test: hexdump.exe -i hexdump.c -o test.c -e 'p 200' | tail -1 | grep -q '0x00c0:'
315 // test: cmp hexdump.c test.c
316 // test: rm test.c
317
318 /* vim: set ts=4 sw=4 et: */