xref: /haiku/src/bin/hd.c (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
2 //
3 //  Copyright (c) 2001-2003, Haiku
4 //
5 //  This software is part of the Haiku distribution and is covered
6 //  by the MIT License.
7 //
8 //
9 //  File:        hd.c
10 //  Author:      Daniel Reinhold (danielre@users.sf.net)
11 //  Description: hex dump utility
12 //
13 // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
14 
15 #include <OS.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <errno.h>
21 #include <sys/stat.h>
22 
23 
24 void  display   (uint32, uint8 *);
25 void  do_hd     (char *);
26 void  dump_file (FILE *);
27 char *hexbytes  (uint8 *);
28 char *printable (uint8 *);
29 void  usage     (void);
30 
31 
32 static int BytesBetweenSpace = 1;
33 
34 
35 void
36 usage ()
37 {
38 	printf ("Usage:\thd [-n N] [file]\n");
39 	printf("\t-n expects a number between 1 and 16 and specifies\n");
40     printf("\tthe number of bytes between spaces.\n");
41     printf("\n\tIf no file is specified, input is read from stdin\n");
42 }
43 
44 
45 int
46 main(int argc, char *argv[])
47 {
48 	char *arg = NULL;
49 
50 	if (argc == 1)
51 		dump_file(stdin);
52 
53 	else {
54 		char *first = *++argv;
55 
56 		if (strcmp(first, "--help") == 0) {
57 			usage();
58 			return 0;
59 		}
60 
61 		if (strcmp(first, "-n") == 0) {
62 			if (--argc > 1) {
63 				char *num = *++argv;
64 
65 				if (!isdigit(*num))
66 					printf("-n option needs a numeric argument\n");
67 				else {
68 					int b = atoi(num);
69 
70 					if (b < 1)  b = 1;
71 					if (b > 16) b = 16;
72 					BytesBetweenSpace = b;
73 
74 					if (--argc > 1)
75 						arg = *++argv;
76 					else
77 						printf("no file specified\n");
78 				}
79 			}
80 			else
81 				printf("-n option needs a numeric argument\n");
82 		}
83 		else
84 			arg = first;
85 
86 		if (arg)
87 			do_hd(arg);
88 	}
89 
90 	putchar('\n');
91 	return 0;
92 }
93 
94 
95 void
96 do_hd(char *fname)
97 {
98 	struct stat e;
99 
100 	if (stat(fname, &e) == -1) {
101 		fprintf(stderr, "'%s': no such file or directory\n", fname);
102 		return;
103 	}
104 
105 	if (S_ISDIR(e.st_mode))
106 		fprintf(stderr, "'%s' is a directory\n", fname);
107 	else {
108 		FILE *fp = fopen(fname, "rb");
109 		if (fp) {
110 			dump_file(fp);
111 			fclose(fp);
112 		}
113 		else
114 			fprintf(stderr, "'%s': %s\n", fname, strerror(errno));
115 	}
116 }
117 
118 
119 void
120 dump_file(FILE *fp)
121 {
122 	size_t got;
123 	uint32 offset = 0;
124 	uint8  data[16];
125 
126 	while ((got = fread(data, 1, 16, fp)) == 16) {
127 		display(offset, data);
128 		offset += 16;
129 	}
130 
131 	if (got > 0) {
132 		memset(data+got, ' ', 16-got);
133 		display(offset, data);
134 	}
135 }
136 
137 
138 void
139 display(uint32 offset, uint8 *data)
140 {
141 	printf("%08" B_PRIx32 " ", offset);
142 	printf("  %s ", hexbytes(data));
143 	printf("%16s ", printable(data));
144 
145 	putchar('\n');
146 }
147 
148 
149 char *
150 hexbytes(uint8 *s)
151 {
152 	static char buf[64];
153 	char *p = buf;
154 	uint8 c;
155 	int   i;
156 	int   n = 0;
157 
158 	for (i = 0; i < 16; ++i) {
159 		c = *s++;
160 		*p++ = "0123456789abcdef"[c/16];
161 		*p++ = "0123456789abcdef"[c%16];
162 
163 		if (++n == BytesBetweenSpace) {
164 			*p++ = ' ';
165 			n = 0;
166 		}
167 
168 		if ((i == 7) && (BytesBetweenSpace == 1))
169 			*p++ = ' ';
170 	}
171 	*p++ = ' ';
172 	*p = 0;
173 
174 	return buf;
175 }
176 
177 
178 char *
179 printable (uint8 *s)
180 {
181 	static char buf[16];
182 	char *p = buf;
183 	uint8 c;
184 	int   i = 16;
185 
186 	while (i--) {
187 		c = *s++;
188 		*p++ = (isgraph(c) ? c : '.');
189 	}
190 	*p = 0;
191 
192 	return buf;
193 }
194