xref: /plan9-contrib/sys/src/cmd/xd.c (revision a1e175a14cc0a444823b3090c7dc9d55076f6121)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 enum {
6 	Nsee = 4*4,  /* process this many bytes at a time; see swizz */
7 };
8 
9 uchar		odata[Nsee];
10 uchar		data[2*Nsee];
11 int		ndata;
12 int		nread;
13 ulong		addr;
14 int		repeats;
15 int		swizzle;
16 int		flush;
17 int		abase=2;
18 
19 int		xd(char *, int);
20 void		xprint(char *, ...);
21 void		initarg(void), swizz(void);
22 
23 enum{
24 	Narg=10,
25 
26 	TNone=0,
27 	TAscii,
28 	TRune,
29 };
30 typedef struct Arg Arg;
31 typedef void fmtfn(char *);
32 struct Arg
33 {
34 	int	chartype;	/* TNone, TAscii, TRunes */
35 	int	loglen;		/* 0==1, 1==2, 2==4, 3==8 */
36 	int	base;		/* 0==8, 1==10, 2==16 */
37 	fmtfn	*fn;		/* function to call with data */
38 	char	*afmt;		/* format to use to print address */
39 	char	*fmt;		/* format to use to print data */
40 }arg[Narg];
41 int	narg;
42 
43 fmtfn	fmt0, fmt1, fmt2, fmt3, fmtc, fmtr;
44 fmtfn *fmt[4] = {
45 	fmt0,
46 	fmt1,
47 	fmt2,
48 	fmt3
49 };
50 
51 char *dfmt[4][3] = {
52 	" %.3uo",	" %.3ud",	" %.2ux",
53 	" %.6uo",	" %.5ud",	" %.4ux",
54 	" %.11luo",	" %.10lud",	" %.8lux",
55 	" %.22lluo",	" %.20llud",	" %.16llux",
56 };
57 
58 char *cfmt[3][3] = {
59 	"   %c",	"   %c", 	"  %c",
60 	" %.3s",	" %.3s",	" %.2s",
61 	" %.3uo",	" %.3ud",	" %.2ux",
62 };
63 
64 char *rfmt[1][1] = {
65 	" %2.2C",
66 };
67 
68 char *afmt[2][3] = {
69 	"%.7luo ",	"%.7lud ",	"%.7lux ",
70 	"%7luo ",	"%7lud ",	"%7lux ",
71 };
72 
73 Biobuf	bin;
74 Biobuf	bout;
75 
76 void
main(int argc,char * argv[])77 main(int argc, char *argv[])
78 {
79 	int i, err;
80 	Arg *ap;
81 
82 	Binit(&bout, 1, OWRITE);
83 	err = 0;
84 	ap = 0;
85 	while(argc>1 && argv[1][0]=='-' && argv[1][1]){
86 		--argc;
87 		argv++;
88 		argv[0]++;
89 		if(argv[0][0] == 'r'){
90 			repeats = 1;
91 			if(argv[0][1])
92 				goto Usage;
93 			continue;
94 		}
95 		if(argv[0][0] == 's'){
96 			swizzle = 1;
97 			if(argv[0][1])
98 				goto Usage;
99 			continue;
100 		}
101 		if(argv[0][0] == 'u'){
102 			flush = 1;
103 			if(argv[0][1])
104 				goto Usage;
105 			continue;
106 		}
107 		if(argv[0][0] == 'a'){
108 			argv[0]++;
109 			switch(argv[0][0]){
110 			case 'o':
111 				abase = 0;
112 				break;
113 			case 'd':
114 				abase = 1;
115 				break;
116 			case 'x':
117 				abase = 2;
118 				break;
119 			default:
120 				goto Usage;
121 			}
122 			if(argv[0][1])
123 				goto Usage;
124 			continue;
125 		}
126 		ap = &arg[narg];
127 		initarg();
128 		while(argv[0][0]){
129 			switch(argv[0][0]){
130 			case 'c':
131 				ap->chartype = TAscii;
132 				ap->loglen = 0;
133 				if(argv[0][1] || argv[0][-1]!='-')
134 					goto Usage;
135 				break;
136 			case 'R':
137 				ap->chartype = TRune;
138 				ap->loglen = 0;
139 				if(argv[0][1] || argv[0][-1]!='-')
140 					goto Usage;
141 				break;
142 			case 'o':
143 				ap->base = 0;
144 				break;
145 			case 'd':
146 				ap->base = 1;
147 				break;
148 			case 'x':
149 				ap->base = 2;
150 				break;
151 			case 'b':
152 			case '1':
153 				ap->loglen = 0;
154 				break;
155 			case 'w':
156 			case '2':
157 				ap->loglen = 1;
158 				break;
159 			case 'l':
160 			case '4':
161 				ap->loglen = 2;
162 				break;
163 			case 'v':
164 			case '8':
165 				ap->loglen = 3;
166 				break;
167 			default:
168 			Usage:
169    fprint(2, "usage: xd [-u] [-r] [-s] [-a{odx}] [-c|{b1w2l4v8}{odx}] ... file ...\n");
170 				exits("usage");
171 			}
172 			argv[0]++;
173 		}
174 		if(ap->chartype == TRune)
175 			ap->fn = fmtr;
176 		else if(ap->chartype == TAscii)
177 			ap->fn = fmtc;
178 		else
179 			ap->fn = fmt[ap->loglen];
180 		ap->fmt = dfmt[ap->loglen][ap->base];
181 		ap->afmt = afmt[ap>arg][abase];
182 	}
183 	if(narg == 0)
184 		initarg();
185 	if(argc == 1)
186 		err = xd(0, 0);
187 	else if(argc == 2)
188 		err = xd(argv[1], 0);
189 	else for(i=1; i<argc; i++)
190 		err |= xd(argv[i], 1);
191 	exits(err? "error" : 0);
192 }
193 
194 void
initarg(void)195 initarg(void)
196 {
197 	Arg *ap;
198 
199 	ap = &arg[narg++];
200 	if(narg >= Narg){
201 		fprint(2, "xd: too many formats (max %d)\n", Narg);
202 		exits("usage");
203 	}
204 	ap->chartype = TNone;
205 	ap->loglen = 2;
206 	ap->base = 2;
207 	ap->fn = fmt2;
208 	ap->fmt = dfmt[ap->loglen][ap->base];
209 	ap->afmt = afmt[narg>1][abase];
210 }
211 
212 /*
213  * format the first ndata bytes in data[] (at most Nsee bytes).
214  * increment addr to account for them.
215  */
216 void
fmtdata(void)217 fmtdata(void)
218 {
219 	Arg *ap;
220 
221 	for(ap=arg; ap<&arg[narg]; ap++){
222 		xprint(ap->afmt, addr);
223 		(*ap->fn)(ap->fmt);
224 		xprint("\n", 0);
225 		if(flush)
226 			Bflush(&bout);
227 	}
228 	addr += ndata;
229 	if(ndata<Nsee){			/* short read? print fragment */
230 		xprint(afmt[0][abase], addr);
231 		xprint("\n", 0);
232 		if(flush)
233 			Bflush(&bout);
234 	}
235 }
236 
237 int
xd(char * name,int title)238 xd(char *name, int title)
239 {
240 	int fd, star, nleft, doprint;
241 	Biobuf *bp;
242 
243 	fd = 0;
244 	if(name){
245 		bp = Bopen(name, OREAD);
246 		if(bp == 0){
247 			fprint(2, "xd: can't open %s\n", name);
248 			return 1;
249 		}
250 	}else{
251 		bp = &bin;
252 		Binit(bp, fd, OREAD);
253 	}
254 	if(title)
255 		xprint("%s\n", name);
256 	addr = 0;
257 	star = 0;
258 	nleft = 0;
259 	/* read 2*Nsee but see only Nsee so that runes are happy */
260 	while((ndata=Bread(bp, data + nleft, 2*Nsee - nleft)) >= 0){
261 		ndata += nleft;
262 		nleft = 0;
263 		nread = ndata;
264 		if(ndata>Nsee)
265 			ndata = Nsee;
266 		else if(ndata<Nsee)
267 			memset(data + ndata, '\0', Nsee - ndata);
268 		if(swizzle)
269 			swizz();
270 		doprint = 1;
271 		if(ndata==Nsee && repeats)
272 			if(addr>0 && memcmp(odata, data, Nsee) == 0){
273 				doprint = 0;
274 				if(star == 0){
275 					star++;
276 					xprint("*\n", 0);
277 				}
278 			} else {	/* not a repetition */
279 				memmove(odata, data, Nsee);
280 				star = 0;
281 			}
282 		if (doprint)
283 			fmtdata();	/* also increments addr */
284 		else
285 			addr += Nsee;
286 		if(ndata<Nsee)		/* short read? done */
287 			break;
288 		if(nread>Nsee){
289 			nleft = nread - Nsee;
290 			memmove(data, data + Nsee, nleft);
291 		}
292 	}
293 	Bterm(bp);
294 	return 0;
295 }
296 
297 void
swizz(void)298 swizz(void)
299 {
300 	uchar *p, *q;
301 	int i;
302 	uchar swdata[Nsee];
303 
304 	p = data;
305 	q = swdata;
306 	for(i=0; i<Nsee; i++)
307 		*q++ = *p++;
308 	p = data;
309 	q = swdata;
310 	for(i=0; i<4; i++){
311 		p[0] = q[3];
312 		p[1] = q[2];
313 		p[2] = q[1];
314 		p[3] = q[0];
315 		p += 4;
316 		q += 4;
317 	}
318 }
319 
320 void
fmt0(char * f)321 fmt0(char *f)
322 {
323 	int i;
324 	for(i=0; i<ndata; i++)
325 		xprint(f, data[i]);
326 }
327 
328 void
fmt1(char * f)329 fmt1(char *f)
330 {
331 	int i;
332 	for(i=0; i<ndata; i+=sizeof(ushort))
333 		xprint(f, (data[i]<<8)|data[i+1]);
334 }
335 
336 void
fmt2(char * f)337 fmt2(char *f)
338 {
339 	int i;
340 	for(i=0; i<ndata; i+=sizeof(ulong))
341 		xprint(f, (data[i]<<24)|(data[i+1]<<16)|(data[i+2]<<8)|data[i+3]);
342 }
343 
344 void
fmt3(char * f)345 fmt3(char *f)
346 {
347 	int i;
348 	uvlong v;
349 
350 	for(i=0; i<ndata; i+=sizeof(uvlong)){
351 		v = (data[i]<<24)|(data[i+1]<<16)|(data[i+2]<<8)|data[i+3];
352 		v <<= 32;
353 		v |= (data[i+4]<<24)|(data[i+1+4]<<16)|(data[i+2+4]<<8)|data[i+3+4];
354 		if(Bprint(&bout, f, v)<0){
355 			fprint(2, "xd: i/o error\n");
356 			exits("i/o error");
357 		}
358 	}
359 }
360 
361 void
onefmtc(uchar c)362 onefmtc(uchar c)
363 {
364 	switch(c){
365 	case '\t':
366 		xprint(cfmt[1][2], "\\t");
367 		break;
368 	case '\r':
369 		xprint(cfmt[1][2], "\\r");
370 		break;
371 	case '\n':
372 		xprint(cfmt[1][2], "\\n");
373 		break;
374 	case '\b':
375 		xprint(cfmt[1][2], "\\b");
376 		break;
377 	default:
378 		if(c>=0x7F || ' '>c)
379 			xprint(cfmt[2][2], c);
380 		else
381 			xprint(cfmt[0][2], c);
382 		break;
383 	}
384 }
385 
386 void
fmtc(char * f)387 fmtc(char *f)
388 {
389 	int i;
390 
391 	USED(f);
392 	for(i=0; i<ndata; i++)
393 		onefmtc(data[i]);
394 }
395 
396 void
fmtr(char * f)397 fmtr(char *f)
398 {
399 	int i, w, cw;
400 	Rune r;
401 	static int nstart;
402 
403 	USED(f);
404 	if(nstart)
405 		xprint("%*c", 3*nstart, ' ');
406 	for(i=nstart; i<ndata; )
407 		if(data[i] < Runeself)
408 			onefmtc(data[i++]);
409 		else{
410 			w = chartorune(&r, (char *)data+i);
411 			if(w == 1 || i + w>nread)
412 				onefmtc(data[i++]);
413 			else{
414 				cw = w;
415 				if(i + w>ndata)
416 					cw = ndata - i;
417 				xprint(rfmt[0][0], r);
418 				xprint("%*c", 3*cw-3, ' ');
419 				i += w;
420 			}
421 		}
422 	if(i > ndata)
423 		nstart = i - ndata;
424 	else
425 		nstart = 0;
426 }
427 
428 void
xprint(char * fmt,...)429 xprint(char *fmt, ...)
430 {
431 	va_list arglist;
432 
433 	va_start(arglist, fmt);
434 	if(Bvprint(&bout, fmt, arglist)<0){
435 		fprint(2, "xd: i/o error\n");
436 		exits("i/o error");
437 	}
438 	va_end(arglist);
439 }
440