1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
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#include <sys/param.h>
33#include <sys/capsicum.h>
34#include <sys/conf.h>
35#include <sys/ioctl.h>
36#include <sys/stat.h>
37
38#include <capsicum_helpers.h>
39#include <ctype.h>
40#include <err.h>
41#include <errno.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46#include "hexdump.h"
47
48enum _vflag vflag = FIRST;
49
50static off_t address;			/* address/offset in stream */
51static off_t eaddress;			/* end address */
52
53static void print(PR *, u_char *);
54static void noseek(void);
55
56void
57display(void)
58{
59	FS *fs;
60	FU *fu;
61	PR *pr;
62	int cnt;
63	u_char *bp;
64	off_t saveaddress;
65	u_char savech, *savebp;
66
67	savech = 0;
68	while ((bp = get()))
69	    for (fs = fshead, savebp = bp, saveaddress = address; fs;
70		fs = fs->nextfs, bp = savebp, address = saveaddress)
71		    for (fu = fs->nextfu; fu; fu = fu->nextfu) {
72			if (fu->flags&F_IGNORE)
73				break;
74			for (cnt = fu->reps; cnt; --cnt)
75			    for (pr = fu->nextpr; pr; address += pr->bcnt,
76				bp += pr->bcnt, pr = pr->nextpr) {
77				    if (eaddress && address >= eaddress &&
78					!(pr->flags & (F_TEXT|F_BPAD)))
79					    bpad(pr);
80				    if (cnt == 1 && pr->nospace) {
81					savech = *pr->nospace;
82					*pr->nospace = '\0';
83				    }
84				    print(pr, bp);
85				    if (cnt == 1 && pr->nospace)
86					*pr->nospace = savech;
87			    }
88		    }
89	if (endfu) {
90		/*
91		 * If eaddress not set, error or file size was multiple of
92		 * blocksize, and no partial block ever found.
93		 */
94		if (!eaddress) {
95			if (!address)
96				return;
97			eaddress = address;
98		}
99		for (pr = endfu->nextpr; pr; pr = pr->nextpr)
100			switch(pr->flags) {
101			case F_ADDRESS:
102				(void)printf(pr->fmt, (quad_t)eaddress);
103				break;
104			case F_TEXT:
105				(void)printf("%s", pr->fmt);
106				break;
107			}
108	}
109}
110
111static void
112print(PR *pr, u_char *bp)
113{
114	long double ldbl;
115	   double f8;
116	    float f4;
117	  int16_t s2;
118	  int32_t s4;
119	  int64_t s8;
120	u_int16_t u2;
121	u_int32_t u4;
122	u_int64_t u8;
123
124	switch(pr->flags) {
125	case F_ADDRESS:
126		(void)printf(pr->fmt, (quad_t)address);
127		break;
128	case F_BPAD:
129		(void)printf(pr->fmt, "");
130		break;
131	case F_C:
132		conv_c(pr, bp, eaddress ? eaddress - address :
133		    blocksize - address % blocksize);
134		break;
135	case F_CHAR:
136		(void)printf(pr->fmt, *bp);
137		break;
138	case F_DBL:
139		switch(pr->bcnt) {
140		case 4:
141			bcopy(bp, &f4, sizeof(f4));
142			(void)printf(pr->fmt, f4);
143			break;
144		case 8:
145			bcopy(bp, &f8, sizeof(f8));
146			(void)printf(pr->fmt, f8);
147			break;
148		default:
149			if (pr->bcnt == sizeof(long double)) {
150				bcopy(bp, &ldbl, sizeof(ldbl));
151				(void)printf(pr->fmt, ldbl);
152			}
153			break;
154		}
155		break;
156	case F_INT:
157		switch(pr->bcnt) {
158		case 1:
159			(void)printf(pr->fmt, (quad_t)(signed char)*bp);
160			break;
161		case 2:
162			bcopy(bp, &s2, sizeof(s2));
163			(void)printf(pr->fmt, (quad_t)s2);
164			break;
165		case 4:
166			bcopy(bp, &s4, sizeof(s4));
167			(void)printf(pr->fmt, (quad_t)s4);
168			break;
169		case 8:
170			bcopy(bp, &s8, sizeof(s8));
171			(void)printf(pr->fmt, s8);
172			break;
173		}
174		break;
175	case F_P:
176		(void)printf(pr->fmt, isprint(*bp) ? *bp : '.');
177		break;
178	case F_STR:
179		(void)printf(pr->fmt, (char *)bp);
180		break;
181	case F_TEXT:
182		(void)printf("%s", pr->fmt);
183		break;
184	case F_U:
185		conv_u(pr, bp);
186		break;
187	case F_UINT:
188		switch(pr->bcnt) {
189		case 1:
190			(void)printf(pr->fmt, (u_quad_t)*bp);
191			break;
192		case 2:
193			bcopy(bp, &u2, sizeof(u2));
194			(void)printf(pr->fmt, (u_quad_t)u2);
195			break;
196		case 4:
197			bcopy(bp, &u4, sizeof(u4));
198			(void)printf(pr->fmt, (u_quad_t)u4);
199			break;
200		case 8:
201			bcopy(bp, &u8, sizeof(u8));
202			(void)printf(pr->fmt, u8);
203			break;
204		}
205		break;
206	}
207}
208
209void
210bpad(PR *pr)
211{
212	static char const *spec = " -0+#";
213	char *p1, *p2;
214
215	/*
216	 * Remove all conversion flags; '-' is the only one valid
217	 * with %s, and it's not useful here.
218	 */
219	pr->flags = F_BPAD;
220	pr->cchar[0] = 's';
221	pr->cchar[1] = '\0';
222	for (p1 = pr->fmt; *p1 != '%'; ++p1);
223	for (p2 = ++p1; *p1 && strchr(spec, *p1); ++p1);
224	while ((*p2++ = *p1++));
225}
226
227static char **_argv;
228
229u_char *
230get(void)
231{
232	static int ateof = 1;
233	static u_char *curp, *savp;
234	int n;
235	int need, nread;
236	int valid_save = 0;
237	u_char *tmpp;
238
239	if (!curp) {
240		if ((curp = calloc(1, blocksize)) == NULL)
241			err(1, NULL);
242		if ((savp = calloc(1, blocksize)) == NULL)
243			err(1, NULL);
244	} else {
245		tmpp = curp;
246		curp = savp;
247		savp = tmpp;
248		address += blocksize;
249		valid_save = 1;
250	}
251	for (need = blocksize, nread = 0;;) {
252		/*
253		 * if read the right number of bytes, or at EOF for one file,
254		 * and no other files are available, zero-pad the rest of the
255		 * block and set the end flag.
256		 */
257		if (!length || (ateof && !next((char **)NULL))) {
258			if (odmode && skip > 0)
259				errx(1, "cannot skip past end of input");
260			if (need == blocksize)
261				return((u_char *)NULL);
262			/*
263			 * XXX bcmp() is not quite right in the presence
264			 * of multibyte characters.
265			 */
266			if (need == 0 && vflag != ALL &&
267			    valid_save &&
268			    bcmp(curp, savp, nread) == 0) {
269				if (vflag != DUP) {
270					(void)printf("*\n");
271					(void)fflush(stdout);
272				}
273				return((u_char *)NULL);
274			}
275			bzero((char *)curp + nread, need);
276			eaddress = address + nread;
277			return(curp);
278		}
279		n = fread((char *)curp + nread, sizeof(u_char),
280		    length == -1 ? need : MIN(length, need), stdin);
281		if (!n) {
282			if (ferror(stdin))
283				warn("%s", _argv[-1]);
284			ateof = 1;
285			continue;
286		}
287		ateof = 0;
288		if (length != -1)
289			length -= n;
290		if (!(need -= n)) {
291			/*
292			 * XXX bcmp() is not quite right in the presence
293			 * of multibyte characters.
294			 */
295			if (vflag == ALL || vflag == FIRST ||
296			    valid_save == 0 ||
297			    bcmp(curp, savp, blocksize) != 0) {
298				if (vflag == DUP || vflag == FIRST)
299					vflag = WAIT;
300				return(curp);
301			}
302			if (vflag == WAIT) {
303				(void)printf("*\n");
304				(void)fflush(stdout);
305			}
306			vflag = DUP;
307			address += blocksize;
308			need = blocksize;
309			nread = 0;
310		}
311		else
312			nread += n;
313	}
314}
315
316size_t
317peek(u_char *buf, size_t nbytes)
318{
319	size_t n, nread;
320	int c;
321
322	if (length != -1 && nbytes > (unsigned int)length)
323		nbytes = length;
324	nread = 0;
325	while (nread < nbytes && (c = getchar()) != EOF) {
326		*buf++ = c;
327		nread++;
328	}
329	n = nread;
330	while (n-- > 0) {
331		c = *--buf;
332		ungetc(c, stdin);
333	}
334	return (nread);
335}
336
337int
338next(char **argv)
339{
340	static int done;
341	int statok;
342
343	if (argv) {
344		_argv = argv;
345		return(1);
346	}
347	for (;;) {
348		if (*_argv) {
349			done = 1;
350			if (!(freopen(*_argv, "r", stdin))) {
351				warn("%s", *_argv);
352				exitval = 1;
353				++_argv;
354				continue;
355			}
356			statok = 1;
357		} else {
358			if (done++)
359				return(0);
360			statok = 0;
361		}
362
363		if (caph_limit_stream(fileno(stdin), CAPH_READ) < 0)
364			err(1, "unable to restrict %s",
365			    statok ? *_argv : "stdin");
366
367		/*
368		 * We've opened our last input file; enter capsicum sandbox.
369		 */
370		if (statok == 0 || *(_argv + 1) == NULL) {
371			if (caph_enter() < 0)
372				err(1, "unable to enter capability mode");
373		}
374
375		if (skip)
376			doskip(statok ? *_argv : "stdin", statok);
377		if (*_argv)
378			++_argv;
379		if (!skip)
380			return(1);
381	}
382	/* NOTREACHED */
383}
384
385void
386doskip(const char *fname, int statok)
387{
388	int type;
389	struct stat sb;
390
391	if (statok) {
392		if (fstat(fileno(stdin), &sb))
393			err(1, "%s", fname);
394		if (S_ISREG(sb.st_mode) && skip > sb.st_size && sb.st_size > 0) {
395			address += sb.st_size;
396			skip -= sb.st_size;
397			return;
398		}
399	}
400	if (!statok || S_ISFIFO(sb.st_mode) || S_ISSOCK(sb.st_mode) || \
401	    (S_ISREG(sb.st_mode) && sb.st_size == 0)) {
402		noseek();
403		return;
404	}
405	if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) {
406		if (ioctl(fileno(stdin), FIODTYPE, &type))
407			err(1, "%s", fname);
408		/*
409		 * Most tape drives don't support seeking,
410		 * yet fseek() would succeed.
411		 */
412		if (type & D_TAPE) {
413			noseek();
414			return;
415		}
416	}
417	if (fseeko(stdin, skip, SEEK_SET)) {
418		noseek();
419		return;
420	}
421	address += skip;
422	skip = 0;
423}
424
425static void
426noseek(void)
427{
428	int count;
429	for (count = 0; count < skip; ++count)
430		if (getchar() == EOF)
431			break;
432	address += count;
433	skip -= count;
434}
435