1/* $NetBSD: display.c,v 1.27 2024/01/14 17:40:17 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.27 2024/01/14 17:40:17 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 u_char *get(void); 66static inline void print(PR *, u_char *); 67 68void 69display(void) 70{ 71 FS *fs; 72 FU *fu; 73 PR *pr; 74 int cnt; 75 u_char *bp; 76 off_t saveaddress; 77 u_char savech, *savebp; 78 79 savech = 0; 80 while ((bp = get()) != NULL) 81 for (fs = fshead, savebp = bp, saveaddress = address; fs; 82 fs = fs->nextfs, bp = savebp, address = saveaddress) 83 for (fu = fs->nextfu; fu; fu = fu->nextfu) { 84 if (fu->flags&F_IGNORE) 85 break; 86 for (cnt = fu->reps; cnt; --cnt) 87 for (pr = fu->nextpr; pr; address += pr->bcnt, 88 bp += pr->bcnt, pr = pr->nextpr) { 89 if (eaddress && address >= eaddress && 90 !(pr->flags & (F_TEXT|F_BPAD))) 91 bpad(pr); 92 if (cnt == 1 && pr->nospace) { 93 savech = *pr->nospace; 94 *pr->nospace = '\0'; 95 } 96 print(pr, bp); 97 if (cnt == 1 && pr->nospace) 98 *pr->nospace = savech; 99 } 100 } 101 if (endfu) { 102 /* 103 * If eaddress not set, error or file size was multiple of 104 * blocksize, and no partial block ever found. 105 */ 106 if (!eaddress) { 107 if (!address) 108 return; 109 eaddress = address; 110 } 111 for (pr = endfu->nextpr; pr; pr = pr->nextpr) 112 switch(pr->flags) { 113 case F_ADDRESS: 114 (void)printf(pr->fmt, (int64_t)eaddress); 115 break; 116 case F_TEXT: 117 (void)printf("%s", pr->fmt); 118 break; 119 } 120 } 121} 122 123static inline void 124print(PR *pr, u_char *bp) 125{ 126 double f8; 127 float f4; 128 int16_t s2; 129 int32_t s4; 130 int64_t s8; 131 uint16_t u2; 132 uint32_t u4; 133 uint64_t u8; 134 135 switch(pr->flags) { 136 case F_ADDRESS: 137 (void)printf(pr->fmt, (int64_t)address); 138 break; 139 case F_BPAD: 140 (void)printf(pr->fmt, ""); 141 break; 142 case F_C: 143 conv_c(pr, bp); 144 break; 145 case F_CHAR: 146 (void)printf(pr->fmt, *bp); 147 break; 148 case F_DBL: 149 switch(pr->bcnt) { 150 case 4: 151 memmove(&f4, bp, sizeof(f4)); 152 (void)printf(pr->fmt, f4); 153 break; 154 case 8: 155 memmove(&f8, bp, sizeof(f8)); 156 (void)printf(pr->fmt, f8); 157 break; 158 } 159 break; 160 case F_INT: 161 switch(pr->bcnt) { 162 case 1: 163 (void)printf(pr->fmt, (int64_t)*bp); 164 break; 165 case 2: 166 memmove(&s2, bp, sizeof(s2)); 167 (void)printf(pr->fmt, (int64_t)s2); 168 break; 169 case 4: 170 memmove(&s4, bp, sizeof(s4)); 171 (void)printf(pr->fmt, (int64_t)s4); 172 break; 173 case 8: 174 memmove(&s8, bp, sizeof(s8)); 175 (void)printf(pr->fmt, (int64_t)s8); 176 break; 177 } 178 break; 179 case F_P: 180 (void)printf(pr->fmt, isprint(*bp) ? *bp : '.'); 181 break; 182 case F_STR: 183 (void)printf(pr->fmt, (char *)bp); 184 break; 185 case F_TEXT: 186 (void)printf("%s", pr->fmt); 187 break; 188 case F_U: 189 conv_u(pr, bp); 190 break; 191 case F_UINT: 192 switch(pr->bcnt) { 193 case 1: 194 (void)printf(pr->fmt, (uint64_t)*bp); 195 break; 196 case 2: 197 memmove(&u2, bp, sizeof(u2)); 198 (void)printf(pr->fmt, (uint64_t)u2); 199 break; 200 case 4: 201 memmove(&u4, bp, sizeof(u4)); 202 (void)printf(pr->fmt, (uint64_t)u4); 203 break; 204 case 8: 205 memmove(&u8, bp, sizeof(u8)); 206 (void)printf(pr->fmt, (uint64_t)u8); 207 break; 208 } 209 break; 210 } 211} 212 213void 214bpad(PR *pr) 215{ 216 static const char *spec = " -0+#"; 217 char *p1, *p2; 218 219 /* 220 * Remove all conversion flags; '-' is the only one valid 221 * with %s, and it's not useful here. 222 */ 223 pr->flags = F_BPAD; 224 pr->cchar[0] = 's'; 225 pr->cchar[1] = '\0'; 226 for (p1 = pr->fmt; *p1 != '%'; ++p1); 227 for (p2 = ++p1; *p1 && strchr(spec, *p1); ++p1); 228 while ((*p2++ = *p1++) != '\0'); 229} 230 231static char **_argv; 232 233static u_char * 234get(void) 235{ 236 static int ateof = 1; 237 static u_char *curp, *savp; 238 int n; 239 int need, nread; 240 u_char *tmpp; 241 242 if (!curp) { 243 curp = ecalloc(blocksize, 1); 244 savp = ecalloc(blocksize, 1); 245 } else { 246 tmpp = curp; 247 curp = savp; 248 savp = tmpp; 249 address += blocksize; 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())) { 258 if (need == blocksize) 259 return NULL ; 260 if (!need && vflag != ALL && 261 !memcmp(curp, savp, nread)) { 262 if (vflag != DUP) 263 (void)printf("*\n"); 264 return NULL ; 265 } 266 memset((char *)curp + nread, 0, need); 267 eaddress = address + nread; 268 return curp ; 269 } 270 n = fread((char *)curp + nread, sizeof(u_char), 271 length == -1 ? need : MIN(length, need), stdin); 272 if (!n) { 273 if (ferror(stdin)) 274 warn("%s", _argv[-1]); 275 ateof = 1; 276 continue; 277 } 278 ateof = 0; 279 if (length != -1) 280 length -= n; 281 if (!(need -= n)) { 282 if (vflag == ALL || vflag == FIRST || 283 memcmp(curp, savp, blocksize)) { 284 if (vflag == DUP || vflag == FIRST) 285 vflag = WAIT; 286 return curp ; 287 } 288 if (vflag == WAIT) 289 (void)printf("*\n"); 290 vflag = DUP; 291 address += blocksize; 292 need = blocksize; 293 nread = 0; 294 } 295 else 296 nread += n; 297 } 298} 299 300/* 301 * Save argv for later retrieval. 302 */ 303void 304stashargv(char **argv) 305{ 306 _argv = argv; 307} 308 309/* 310 * Get the next file. The idea with the twisty logic seems to be to 311 * either read N filenames from argv and then exit, or if there aren't 312 * any, to use stdin and then exit. It should probably be simplified. 313 * The "done" flag doesn't mean "we are done", it means "we are done 314 * once we run out of filenames". 315 * 316 * Is there any reason not to remove the logic that inhibits 317 * calling fstat if using stdin and not a filename? It should be safe 318 * to call fstat on any fd. Yes, because on stdin it st_size will not 319 * convey useful information. In addition on kernfs/procfs stat might 320 * not return proper size info. 321 * 322 * Note: I have ruled that if there is one file on the command line 323 * and it doesn't open, we should exit after complaining about it and 324 * not then proceed to read stdin; the latter seems like unexpected 325 * and undesirable behavior. Also, it didn't work anyway, because the 326 * freopen call clobbers stdin while failing. -- dholland 20160303 327 */ 328int 329next(void) 330{ 331 static int done; 332 int statok; 333 334 for (;;) { 335 if (*_argv) { 336 done = 1; 337 if (!(freopen(*_argv, "r", stdin))) { 338 warn("%s", *_argv); 339 exitval = 1; 340 ++_argv; 341 continue; 342 } 343 statok = 1; 344 } else { 345 if (done++) 346 return 0 ; 347 statok = 0; 348 } 349 if (skip) 350 doskip(statok ? *_argv : "stdin", statok); 351 if (*_argv) 352 ++_argv; 353 if (!skip) 354 return 1 ; 355 } 356 /* NOTREACHED */ 357} 358 359void 360doskip(const char *fname, int statok) 361{ 362 int cnt; 363 struct stat sb; 364 365 if (statok) { 366 if (fstat(fileno(stdin), &sb)) 367 err(EXIT_FAILURE, "fstat %s", fname); 368 if (sb.st_size != 0 && 369 S_ISREG(sb.st_mode) && skip >= sb.st_size) { 370 address += sb.st_size; 371 skip -= sb.st_size; 372 return; 373 } 374 } else 375 sb.st_mode = S_IFIFO; 376 377 if (S_ISREG(sb.st_mode)) { 378 if (fseek(stdin, skip, SEEK_SET)) 379 err(1, "fseek %s", fname); 380 address += skip; 381 skip = 0; 382 } else { 383 for (cnt = 0; cnt < skip; ++cnt) 384 if (getchar() == EOF) 385 break; 386 address += cnt; 387 skip -= cnt; 388 } 389} 390