1// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
2//
3//  Copyright (c) 2001-2003, OpenBeOS
4//
5//  This software is part of the OpenBeOS distribution and is covered
6//  by the OpenBeOS 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
24void  display   (uint32, uint8 *);
25void  do_hd     (char *);
26void  dump_file (FILE *);
27char *hexbytes  (uint8 *);
28char *printable (uint8 *);
29void  usage     (void);
30
31
32static int BytesBetweenSpace = 1;
33
34
35void
36usage ()
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
45int
46main(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
95void
96do_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
119void
120dump_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
138void
139display(uint32 offset, uint8 *data)
140{
141	printf("%08lx ", offset);
142	printf("  %s ", hexbytes(data));
143	printf("%16s ", printable(data));
144
145	putchar('\n');
146}
147
148
149char *
150hexbytes(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
178char *
179printable (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