1/*	$NetBSD: display.c,v 1.20 2006/08/26 18:17:42 christos Exp $	*/
2
3/*
4 * Copyright (c) 1989, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#if HAVE_NBTOOL_CONFIG_H
33#include "nbtool_config.h"
34#endif
35
36#include <sys/cdefs.h>
37#if !defined(lint)
38#if 0
39static char sccsid[] = "@(#)display.c	8.1 (Berkeley) 6/6/93";
40#else
41__RCSID("$NetBSD: display.c,v 1.20 2006/08/26 18:17:42 christos Exp $");
42#endif
43#endif /* not lint */
44
45#include <sys/param.h>
46#include <sys/stat.h>
47
48#include <ctype.h>
49#include <err.h>
50#include <errno.h>
51#include <inttypes.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <unistd.h>
56#include <util.h>
57
58#include "hexdump.h"
59
60enum _vflag vflag = FIRST;
61
62static off_t address;			/* address/offset in stream */
63static off_t eaddress;			/* end address */
64
65static inline void print(PR *, u_char *);
66
67void
68display(void)
69{
70	FS *fs;
71	FU *fu;
72	PR *pr;
73	int cnt;
74	u_char *bp;
75	off_t saveaddress;
76	u_char savech, *savebp;
77
78	savech = 0;
79	while ((bp = get()) != NULL)
80	    for (fs = fshead, savebp = bp, saveaddress = address; fs;
81		fs = fs->nextfs, bp = savebp, address = saveaddress)
82		    for (fu = fs->nextfu; fu; fu = fu->nextfu) {
83			if (fu->flags&F_IGNORE)
84				break;
85			for (cnt = fu->reps; cnt; --cnt)
86			    for (pr = fu->nextpr; pr; address += pr->bcnt,
87				bp += pr->bcnt, pr = pr->nextpr) {
88				    if (eaddress && address >= eaddress &&
89					!(pr->flags & (F_TEXT|F_BPAD)))
90					    bpad(pr);
91				    if (cnt == 1 && pr->nospace) {
92					savech = *pr->nospace;
93					*pr->nospace = '\0';
94				    }
95				    print(pr, bp);
96				    if (cnt == 1 && pr->nospace)
97					*pr->nospace = savech;
98			    }
99		    }
100	if (endfu) {
101		/*
102		 * If eaddress not set, error or file size was multiple of
103		 * blocksize, and no partial block ever found.
104		 */
105		if (!eaddress) {
106			if (!address)
107				return;
108			eaddress = address;
109		}
110		for (pr = endfu->nextpr; pr; pr = pr->nextpr)
111			switch(pr->flags) {
112			case F_ADDRESS:
113				(void)printf(pr->fmt, (int64_t)eaddress);
114				break;
115			case F_TEXT:
116				(void)printf("%s", pr->fmt);
117				break;
118			}
119	}
120}
121
122static inline void
123print(PR *pr, u_char *bp)
124{
125	   double f8;
126	    float f4;
127	  int16_t s2;
128	  int32_t s4;
129	  int64_t s8;
130	 uint16_t u2;
131	 uint32_t u4;
132	 uint64_t u8;
133
134	switch(pr->flags) {
135	case F_ADDRESS:
136		(void)printf(pr->fmt, (int64_t)address);
137		break;
138	case F_BPAD:
139		(void)printf(pr->fmt, "");
140		break;
141	case F_C:
142		conv_c(pr, bp);
143		break;
144	case F_CHAR:
145		(void)printf(pr->fmt, *bp);
146		break;
147	case F_DBL:
148		switch(pr->bcnt) {
149		case 4:
150			memmove(&f4, bp, sizeof(f4));
151			(void)printf(pr->fmt, f4);
152			break;
153		case 8:
154			memmove(&f8, bp, sizeof(f8));
155			(void)printf(pr->fmt, f8);
156			break;
157		}
158		break;
159	case F_INT:
160		switch(pr->bcnt) {
161		case 1:
162			(void)printf(pr->fmt, (int64_t)*bp);
163			break;
164		case 2:
165			memmove(&s2, bp, sizeof(s2));
166			(void)printf(pr->fmt, (int64_t)s2);
167			break;
168		case 4:
169			memmove(&s4, bp, sizeof(s4));
170			(void)printf(pr->fmt, (int64_t)s4);
171			break;
172		case 8:
173			memmove(&s8, bp, sizeof(s8));
174			(void)printf(pr->fmt, (int64_t)s8);
175			break;
176		}
177		break;
178	case F_P:
179		(void)printf(pr->fmt, isprint(*bp) ? *bp : '.');
180		break;
181	case F_STR:
182		(void)printf(pr->fmt, (char *)bp);
183		break;
184	case F_TEXT:
185		(void)printf("%s", pr->fmt);
186		break;
187	case F_U:
188		conv_u(pr, bp);
189		break;
190	case F_UINT:
191		switch(pr->bcnt) {
192		case 1:
193			(void)printf(pr->fmt, (uint64_t)*bp);
194			break;
195		case 2:
196			memmove(&u2, bp, sizeof(u2));
197			(void)printf(pr->fmt, (uint64_t)u2);
198			break;
199		case 4:
200			memmove(&u4, bp, sizeof(u4));
201			(void)printf(pr->fmt, (uint64_t)u4);
202			break;
203		case 8:
204			memmove(&u8, bp, sizeof(u8));
205			(void)printf(pr->fmt, (uint64_t)u8);
206			break;
207		}
208		break;
209	}
210}
211
212void
213bpad(PR *pr)
214{
215	static const char *spec = " -0+#";
216	char *p1, *p2;
217
218	/*
219	 * Remove all conversion flags; '-' is the only one valid
220	 * with %s, and it's not useful here.
221	 */
222	pr->flags = F_BPAD;
223	pr->cchar[0] = 's';
224	pr->cchar[1] = '\0';
225	for (p1 = pr->fmt; *p1 != '%'; ++p1);
226	for (p2 = ++p1; *p1 && strchr(spec, *p1); ++p1);
227	while ((*p2++ = *p1++) != '\0');
228}
229
230static char **_argv;
231
232u_char *
233get(void)
234{
235	static int ateof = 1;
236	static u_char *curp, *savp;
237	int n;
238	int need, nread;
239	u_char *tmpp;
240
241	if (!curp) {
242		curp = ecalloc(blocksize, 1);
243		savp = ecalloc(blocksize, 1);
244	} else {
245		tmpp = curp;
246		curp = savp;
247		savp = tmpp;
248		address += blocksize;
249	}
250	for (need = blocksize, nread = 0;;) {
251		/*
252		 * if read the right number of bytes, or at EOF for one file,
253		 * and no other files are available, zero-pad the rest of the
254		 * block and set the end flag.
255		 */
256		if (!length || (ateof && !next(NULL))) {
257			if (need == blocksize)
258				return(NULL);
259			if (!need && vflag != ALL &&
260			    !memcmp(curp, savp, nread)) {
261				if (vflag != DUP)
262					(void)printf("*\n");
263				return(NULL);
264			}
265			memset((char *)curp + nread, 0, need);
266			eaddress = address + nread;
267			return(curp);
268		}
269		n = fread((char *)curp + nread, sizeof(u_char),
270		    length == -1 ? need : MIN(length, need), stdin);
271		if (!n) {
272			if (ferror(stdin))
273				warn("%s", _argv[-1]);
274			ateof = 1;
275			continue;
276		}
277		ateof = 0;
278		if (length != -1)
279			length -= n;
280		if (!(need -= n)) {
281			if (vflag == ALL || vflag == FIRST ||
282			    memcmp(curp, savp, blocksize)) {
283				if (vflag == DUP || vflag == FIRST)
284					vflag = WAIT;
285				return(curp);
286			}
287			if (vflag == WAIT)
288				(void)printf("*\n");
289			vflag = DUP;
290			address += blocksize;
291			need = blocksize;
292			nread = 0;
293		}
294		else
295			nread += n;
296	}
297}
298
299int
300next(char **argv)
301{
302	static int done;
303	int statok;
304
305	if (argv) {
306		_argv = argv;
307		return(1);
308	}
309	for (;;) {
310		if (*_argv) {
311			if (!(freopen(*_argv, "r", stdin))) {
312				warn("%s", *_argv);
313				exitval = 1;
314				++_argv;
315				continue;
316			}
317			statok = done = 1;
318		} else {
319			if (done++)
320				return(0);
321			statok = 0;
322		}
323		if (skip)
324			doskip(statok ? *_argv : "stdin", statok);
325		if (*_argv)
326			++_argv;
327		if (!skip)
328			return(1);
329	}
330	/* NOTREACHED */
331}
332
333void
334doskip(const char *fname, int statok)
335{
336	int cnt;
337	struct stat sb;
338
339	if (statok) {
340		if (fstat(fileno(stdin), &sb))
341			err(1, "fstat %s", fname);
342		if (S_ISREG(sb.st_mode) && skip >= sb.st_size) {
343			address += sb.st_size;
344			skip -= sb.st_size;
345			return;
346		}
347	}
348	if (S_ISREG(sb.st_mode)) {
349		if (fseek(stdin, skip, SEEK_SET))
350			err(1, "fseek %s", fname);
351		address += skip;
352		skip = 0;
353	} else {
354		for (cnt = 0; cnt < skip; ++cnt)
355			if (getchar() == EOF)
356				break;
357		address += cnt;
358		skip -= cnt;
359	}
360}
361