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 #ifndef lint 33 #endif /* not lint */ 34 #include <sys/cdefs.h> 35 #include <sys/types.h> 36 37 #include <err.h> 38 #include <fcntl.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <ctype.h> 42 #include <string.h> 43 #include "hexdump.h" 44 45 FU *endfu; /* format at end-of-data */ 46 47 void 48 addfile(const char *name) 49 { 50 unsigned char *p; 51 FILE *fp; 52 int ch; 53 char buf[2048 + 1]; 54 55 if ((fp = fopen(name, "r")) == NULL) 56 err(1, "%s", name); 57 while (fgets(buf, sizeof(buf), fp)) { 58 if (!(p = strchr(buf, '\n'))) { 59 warnx("line too long"); 60 while ((ch = getchar()) != '\n' && ch != EOF); 61 continue; 62 } 63 *p = '\0'; 64 for (p = buf; *p && isspace(*p); ++p); 65 if (!*p || *p == '#') 66 continue; 67 add(p); 68 } 69 (void)fclose(fp); 70 } 71 72 void 73 add(const char *fmt) 74 { 75 unsigned const char *p, *savep; 76 static FS **nextfs; 77 FS *tfs; 78 FU *tfu, **nextfu; 79 80 /* start new linked list of format units */ 81 if ((tfs = calloc(1, sizeof(FS))) == NULL) 82 err(1, NULL); 83 if (!fshead) 84 fshead = tfs; 85 else 86 *nextfs = tfs; 87 nextfs = &tfs->nextfs; 88 nextfu = &tfs->nextfu; 89 90 /* take the format string and break it up into format units */ 91 for (p = fmt;;) { 92 /* skip leading white space */ 93 for (; isspace(*p); ++p); 94 if (!*p) 95 break; 96 97 /* allocate a new format unit and link it in */ 98 if ((tfu = calloc(1, sizeof(FU))) == NULL) 99 err(1, NULL); 100 *nextfu = tfu; 101 nextfu = &tfu->nextfu; 102 tfu->reps = 1; 103 104 /* if leading digit, repetition count */ 105 if (isdigit(*p)) { 106 for (savep = p; isdigit(*p); ++p); 107 if (!isspace(*p) && *p != '/') 108 badfmt(fmt); 109 /* may overwrite either white space or slash */ 110 tfu->reps = atoi(savep); 111 tfu->flags = F_SETREP; 112 /* skip trailing white space */ 113 for (++p; isspace(*p); ++p); 114 } 115 116 /* skip slash and trailing white space */ 117 if (*p == '/') 118 while (isspace(*++p)); 119 120 /* byte count */ 121 if (isdigit(*p)) { 122 for (savep = p; isdigit(*p); ++p); 123 if (!isspace(*p)) 124 badfmt(fmt); 125 tfu->bcnt = atoi(savep); 126 /* skip trailing white space */ 127 for (++p; isspace(*p); ++p); 128 } 129 130 /* format */ 131 if (*p != '"') 132 badfmt(fmt); 133 for (savep = ++p; *p != '"';) 134 if (*p++ == 0) 135 badfmt(fmt); 136 if (!(tfu->fmt = malloc(p - savep + 1))) 137 err(1, NULL); 138 (void) strlcpy(tfu->fmt, savep, p - savep + 1); 139 escape(tfu->fmt); 140 p++; 141 } 142 } 143 144 static const char *spec = ".#-+ 0123456789"; 145 146 int 147 size(FS *fs) 148 { 149 FU *fu; 150 int bcnt, cursize; 151 unsigned char *fmt; 152 int prec; 153 154 /* figure out the data block size needed for each format unit */ 155 for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { 156 if (fu->bcnt) { 157 cursize += fu->bcnt * fu->reps; 158 continue; 159 } 160 for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) { 161 if (*fmt != '%') 162 continue; 163 /* 164 * skip any special chars -- save precision in 165 * case it's a %s format. 166 */ 167 while (*++fmt != 0 && strchr(spec + 1, *fmt) != NULL) 168 ; 169 if (*fmt == 0) 170 badnoconv(); 171 if (*fmt == '.' && isdigit(*++fmt)) { 172 prec = atoi(fmt); 173 while (isdigit(*++fmt)); 174 } 175 switch(*fmt) { 176 case 'c': 177 bcnt += 1; 178 break; 179 case 'd': case 'i': case 'o': case 'u': 180 case 'x': case 'X': 181 bcnt += 4; 182 break; 183 case 'e': case 'E': case 'f': case 'g': case 'G': 184 bcnt += 8; 185 break; 186 case 's': 187 bcnt += prec; 188 break; 189 case '_': 190 switch(*++fmt) { 191 case 'c': case 'p': case 'u': 192 bcnt += 1; 193 break; 194 } 195 } 196 } 197 cursize += bcnt * fu->reps; 198 } 199 return (cursize); 200 } 201 202 void 203 rewrite(FS *fs) 204 { 205 enum { NOTOKAY, USEBCNT, USEPREC } sokay; 206 PR *pr, **nextpr; 207 FU *fu; 208 unsigned char *p1, *p2, *fmtp; 209 char savech, cs[3]; 210 int nconv, prec; 211 212 prec = 0; 213 214 for (fu = fs->nextfu; fu; fu = fu->nextfu) { 215 /* 216 * Break each format unit into print units; each conversion 217 * character gets its own. 218 */ 219 nextpr = &fu->nextpr; 220 for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { 221 if ((pr = calloc(1, sizeof(PR))) == NULL) 222 err(1, NULL); 223 *nextpr = pr; 224 225 /* Skip preceding text and up to the next % sign. */ 226 for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); 227 228 /* Only text in the string. */ 229 if (!*p1) { 230 pr->fmt = fmtp; 231 pr->flags = F_TEXT; 232 break; 233 } 234 235 /* 236 * Get precision for %s -- if have a byte count, don't 237 * need it. 238 */ 239 if (fu->bcnt) { 240 sokay = USEBCNT; 241 /* Skip to conversion character. */ 242 while (*++p1 != 0 && strchr(spec, *p1) != NULL) 243 ; 244 if (*p1 == 0) 245 badnoconv(); 246 } else { 247 /* Skip any special chars, field width. */ 248 while (*++p1 != 0 && strchr(spec + 1, *p1) != NULL) 249 ; 250 if (*p1 == 0) 251 badnoconv(); 252 if (*p1 == '.' && isdigit(*++p1)) { 253 sokay = USEPREC; 254 prec = atoi(p1); 255 while (isdigit(*++p1)); 256 } else 257 sokay = NOTOKAY; 258 } 259 260 p2 = *p1 ? p1 + 1 : p1; /* Set end pointer -- make sure 261 * that it's non-NUL/-NULL first 262 * though. */ 263 cs[0] = *p1; /* Set conversion string. */ 264 cs[1] = '\0'; 265 266 /* 267 * Figure out the byte count for each conversion; 268 * rewrite the format as necessary, set up blank- 269 * padding for end of data. 270 */ 271 switch(cs[0]) { 272 case 'c': 273 pr->flags = F_CHAR; 274 switch(fu->bcnt) { 275 case 0: case 1: 276 pr->bcnt = 1; 277 break; 278 default: 279 p1[1] = '\0'; 280 badcnt(p1); 281 } 282 break; 283 case 'd': case 'i': 284 pr->flags = F_INT; 285 goto isint; 286 case 'o': case 'u': case 'x': case 'X': 287 pr->flags = F_UINT; 288 isint: cs[2] = '\0'; 289 cs[1] = cs[0]; 290 cs[0] = 'q'; 291 switch(fu->bcnt) { 292 case 0: case 4: 293 pr->bcnt = 4; 294 break; 295 case 1: 296 pr->bcnt = 1; 297 break; 298 case 2: 299 pr->bcnt = 2; 300 break; 301 case 8: 302 pr->bcnt = 8; 303 break; 304 default: 305 p1[1] = '\0'; 306 badcnt(p1); 307 } 308 break; 309 case 'e': case 'E': case 'f': case 'g': case 'G': 310 pr->flags = F_DBL; 311 switch(fu->bcnt) { 312 case 0: case 8: 313 pr->bcnt = 8; 314 break; 315 case 4: 316 pr->bcnt = 4; 317 break; 318 default: 319 if (fu->bcnt == sizeof(long double)) { 320 cs[2] = '\0'; 321 cs[1] = cs[0]; 322 cs[0] = 'L'; 323 pr->bcnt = sizeof(long double); 324 } else { 325 p1[1] = '\0'; 326 badcnt(p1); 327 } 328 } 329 break; 330 case 's': 331 pr->flags = F_STR; 332 switch(sokay) { 333 case NOTOKAY: 334 badsfmt(); 335 case USEBCNT: 336 pr->bcnt = fu->bcnt; 337 break; 338 case USEPREC: 339 pr->bcnt = prec; 340 break; 341 } 342 break; 343 case '_': 344 ++p2; 345 switch(p1[1]) { 346 case 'A': 347 endfu = fu; 348 fu->flags |= F_IGNORE; 349 /* FALLTHROUGH */ 350 case 'a': 351 pr->flags = F_ADDRESS; 352 ++p2; 353 switch(p1[2]) { 354 case 'd': case 'o': case'x': 355 cs[0] = 'q'; 356 cs[1] = p1[2]; 357 cs[2] = '\0'; 358 break; 359 default: 360 p1[3] = '\0'; 361 badconv(p1); 362 } 363 break; 364 case 'c': 365 pr->flags = F_C; 366 /* cs[0] = 'c'; set in conv_c */ 367 goto isint2; 368 case 'p': 369 pr->flags = F_P; 370 cs[0] = 'c'; 371 goto isint2; 372 case 'u': 373 pr->flags = F_U; 374 /* cs[0] = 'c'; set in conv_u */ 375 isint2: switch(fu->bcnt) { 376 case 0: case 1: 377 pr->bcnt = 1; 378 break; 379 default: 380 p1[2] = '\0'; 381 badcnt(p1); 382 } 383 break; 384 default: 385 p1[2] = '\0'; 386 badconv(p1); 387 } 388 break; 389 default: 390 p1[1] = '\0'; 391 badconv(p1); 392 } 393 394 /* 395 * Copy to PR format string, set conversion character 396 * pointer, update original. 397 */ 398 savech = *p2; 399 p1[0] = '\0'; 400 if (asprintf(&pr->fmt, "%s%s", fmtp, cs) == -1) 401 err(1, NULL); 402 *p2 = savech; 403 pr->cchar = pr->fmt + (p1 - fmtp); 404 fmtp = p2; 405 406 /* Only one conversion character if byte count. */ 407 if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) 408 errx(1, "byte count with multiple conversion characters"); 409 } 410 /* 411 * If format unit byte count not specified, figure it out 412 * so can adjust rep count later. 413 */ 414 if (!fu->bcnt) 415 for (pr = fu->nextpr; pr; pr = pr->nextpr) 416 fu->bcnt += pr->bcnt; 417 } 418 /* 419 * If the format string interprets any data at all, and it's 420 * not the same as the blocksize, and its last format unit 421 * interprets any data at all, and has no iteration count, 422 * repeat it as necessary. 423 * 424 * If, rep count is greater than 1, no trailing whitespace 425 * gets output from the last iteration of the format unit. 426 */ 427 for (fu = fs->nextfu; fu; fu = fu->nextfu) { 428 if (!fu->nextfu && fs->bcnt < blocksize && 429 !(fu->flags&F_SETREP) && fu->bcnt) 430 fu->reps += (blocksize - fs->bcnt) / fu->bcnt; 431 if (fu->reps > 1) { 432 for (pr = fu->nextpr;; pr = pr->nextpr) 433 if (!pr->nextpr) 434 break; 435 for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) 436 p2 = isspace(*p1) ? p1 : NULL; 437 if (p2) 438 pr->nospace = p2; 439 } 440 } 441 #ifdef DEBUG 442 for (fu = fs->nextfu; fu; fu = fu->nextfu) { 443 (void)printf("fmt:"); 444 for (pr = fu->nextpr; pr; pr = pr->nextpr) 445 (void)printf(" {%s}", pr->fmt); 446 (void)printf("\n"); 447 } 448 #endif 449 } 450 451 void 452 escape(char *p1) 453 { 454 char *p2; 455 456 /* alphabetic escape sequences have to be done in place */ 457 for (p2 = p1;; p1++, p2++) { 458 if (*p1 == '\\') { 459 p1++; 460 switch(*p1) { 461 case '\0': 462 *p2 = '\\'; 463 *++p2 = '\0'; 464 return; 465 case 'a': 466 /* *p2 = '\a'; */ 467 *p2 = '\007'; 468 break; 469 case 'b': 470 *p2 = '\b'; 471 break; 472 case 'f': 473 *p2 = '\f'; 474 break; 475 case 'n': 476 *p2 = '\n'; 477 break; 478 case 'r': 479 *p2 = '\r'; 480 break; 481 case 't': 482 *p2 = '\t'; 483 break; 484 case 'v': 485 *p2 = '\v'; 486 break; 487 default: 488 *p2 = *p1; 489 break; 490 } 491 } else { 492 *p2 = *p1; 493 if (*p1 == '\0') 494 return; 495 } 496 } 497 } 498 499 void 500 badcnt(const char *s) 501 { 502 errx(1, "%s: bad byte count", s); 503 } 504 505 void 506 badsfmt(void) 507 { 508 errx(1, "%%s: requires a precision or a byte count"); 509 } 510 511 void 512 badfmt(const char *fmt) 513 { 514 errx(1, "\"%s\": bad format", fmt); 515 } 516 517 void 518 badconv(const char *ch) 519 { 520 errx(1, "%%%s: bad conversion character", ch); 521 } 522 523 void 524 badnoconv(void) 525 { 526 errx(1, "missing conversion character"); 527 } 528