1: /* 2: * Copyright (c) 1989 The Regents of the University of California. 3: * All rights reserved. 4: * 5: * Redistribution and use in source and binary forms are permitted 6: * provided that: (1) source distributions retain this entire copyright 7: * notice and comment, and (2) distributions including binaries display 8: * the following acknowledgement: ``This product includes software 9: * developed by the University of California, Berkeley and its contributors'' 10: * in the documentation or other materials provided with the distribution 11: * and in all advertising materials mentioning features or use of this 12: * software. Neither the name of the University nor the names of its 13: * contributors may be used to endorse or promote products derived 14: * from this software without specific prior written permission. 15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 16: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 17: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 18: */ 19: 20: #ifndef lint 21: static char sccsid[] = "@(#)parse.c 5.4 (Berkeley) 6/1/90"; 22: #endif /* not lint */ 23: 24: #include <sys/types.h> 25: #include <sys/file.h> 26: #include <stdio.h> 27: #include <ctype.h> 28: #include <string.h> 29: #include "hexdump.h" 30: 31: FU *endfu; /* format at end-of-data */ 32: 33: addfile(name) 34: char *name; 35: { 36: register char *p; 37: FILE *fp; 38: int ch; 39: char buf[2048 + 1]; 40: 41: if (!(fp = fopen(name, "r"))) { 42: (void)fprintf(stderr, "hexdump: can't read %s.\n", name); 43: exit(1); 44: } 45: while (fgets(buf, sizeof(buf), fp)) { 46: if (!(p = index(buf, '\n'))) { 47: (void)fprintf(stderr, "hexdump: line too long.\n"); 48: while ((ch = getchar()) != '\n' && ch != EOF); 49: continue; 50: } 51: *p = '\0'; 52: for (p = buf; *p && isspace(*p); ++p); 53: if (!*p || *p == '#') 54: continue; 55: add(p); 56: } 57: (void)fclose(fp); 58: } 59: 60: add(fmt) 61: char *fmt; 62: { 63: register char *p; 64: static FS **nextfs; 65: FS *tfs; 66: FU *tfu, **nextfu; 67: char savech, *savep, *emalloc(), *strdup(); 68: 69: /* start new linked list of format units */ 70: /* NOSTRICT */ 71: tfs = (FS *)emalloc(sizeof(FS)); 72: if (!fshead) 73: fshead = tfs; 74: else 75: *nextfs = tfs; 76: nextfs = &tfs->nextfs; 77: nextfu = &tfs->nextfu; 78: 79: /* take the format string and break it up into format units */ 80: for (p = fmt;;) { 81: /* skip leading white space */ 82: for (; isspace(*p); ++p); 83: if (!*p) 84: break; 85: 86: /* allocate a new format unit and link it in */ 87: /* NOSTRICT */ 88: tfu = (FU *)emalloc(sizeof(FU)); 89: *nextfu = tfu; 90: nextfu = &tfu->nextfu; 91: tfu->reps = 1; 92: 93: /* if leading digit, repetition count */ 94: if (isdigit(*p)) { 95: for (savep = p; isdigit(*p); ++p); 96: if (!isspace(*p) && *p != '/') 97: badfmt(fmt); 98: /* may overwrite either white space or slash */ 99: savech = *p; 100: *p = '\0'; 101: tfu->reps = atoi(savep); 102: tfu->flags = F_SETREP; 103: *p = savech; 104: /* skip trailing white space */ 105: for (++p; isspace(*p); ++p); 106: } 107: 108: /* skip slash and trailing white space */ 109: if (*p == '/') 110: while (isspace(*++p)); 111: 112: /* byte count */ 113: if (isdigit(*p)) { 114: for (savep = p; isdigit(*p); ++p); 115: if (!isspace(*p)) 116: badfmt(fmt); 117: savech = *p; 118: *p = '\0'; 119: tfu->bcnt = atoi(savep); 120: *p = savech; 121: /* skip trailing white space */ 122: for (++p; isspace(*p); ++p); 123: } 124: 125: /* format */ 126: if (*p != '"') 127: badfmt(fmt); 128: for (savep = ++p; *p != '"'; ++p); 129: if (*p != '"') 130: badfmt(fmt); 131: savech = *p; 132: *p = '\0'; 133: if (!(tfu->fmt = strdup(savep))) 134: nomem(); 135: escape(tfu->fmt); 136: *p++ = savech; 137: } 138: } 139: 140: static char *spec = ".#-+ 0123456789"; 141: size(fs) 142: FS *fs; 143: { 144: register FU *fu; 145: register int bcnt, cursize; 146: register char *fmt; 147: int prec; 148: 149: /* figure out the data block size needed for each format unit */ 150: for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { 151: if (fu->bcnt) { 152: cursize += fu->bcnt * fu->reps; 153: continue; 154: } 155: for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) { 156: if (*fmt != '%') 157: continue; 158: /* 159: * skip any special chars -- save precision in 160: * case it's a %s format. 161: */ 162: while (index(spec + 1, *++fmt)); 163: if (*fmt == '.' && isdigit(*++fmt)) { 164: prec = atoi(fmt); 165: while (isdigit(*++fmt)); 166: } 167: switch(*fmt) { 168: case 'c': 169: bcnt += 1; 170: break; 171: case 'd': case 'i': case 'o': case 'u': 172: case 'x': case 'X': 173: bcnt += 4; 174: break; 175: case 'e': case 'E': case 'f': case 'g': case 'G': 176: bcnt += 8; 177: break; 178: case 's': 179: bcnt += prec; 180: break; 181: case '_': 182: switch(*++fmt) { 183: case 'c': case 'p': case 'u': 184: bcnt += 1; 185: break; 186: } 187: } 188: } 189: cursize += bcnt * fu->reps; 190: } 191: return(cursize); 192: } 193: 194: rewrite(fs) 195: FS *fs; 196: { 197: enum { NOTOKAY, USEBCNT, USEPREC } sokay; 198: register PR *pr, **nextpr; 199: register FU *fu; 200: register char *p1, *p2; 201: char savech, *fmtp; 202: int nconv, prec; 203: 204: for (fu = fs->nextfu; fu; fu = fu->nextfu) { 205: /* 206: * break each format unit into print units; each 207: * conversion character gets its own. 208: */ 209: for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { 210: /* NOSTRICT */ 211: pr = (PR *)emalloc(sizeof(PR)); 212: if (!fu->nextpr) 213: fu->nextpr = pr; 214: else 215: *nextpr = pr; 216: 217: /* skip preceding text and up to the next % sign */ 218: for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); 219: 220: /* only text in the string */ 221: if (!*p1) { 222: pr->fmt = fmtp; 223: pr->flags = F_TEXT; 224: break; 225: } 226: 227: /* 228: * get precision for %s -- if have a byte count, don't 229: * need it. 230: */ 231: if (fu->bcnt) { 232: sokay = USEBCNT; 233: /* skip to conversion character */ 234: for (++p1; index(spec, *p1); ++p1); 235: } else { 236: /* skip any special chars, field width */ 237: while (index(spec + 1, *++p1)); 238: if (*p1 == '.' && isdigit(*++p1)) { 239: sokay = USEPREC; 240: prec = atoi(p1); 241: while (isdigit(*++p1)); 242: } 243: else 244: sokay = NOTOKAY; 245: } 246: 247: p2 = p1 + 1; /* set end pointer */ 248: 249: /* 250: * figure out the byte count for each conversion; 251: * rewrite the format as necessary, set up blank- 252: * padding for end of data. 253: */ 254: switch(*p1) { 255: case 'c': 256: pr->flags = F_CHAR; 257: switch(fu->bcnt) { 258: case 0: case 1: 259: pr->bcnt = 1; 260: break; 261: default: 262: p1[1] = '\0'; 263: badcnt(p1); 264: } 265: break; 266: case 'd': case 'i': 267: pr->flags = F_INT; 268: goto sw1; 269: case 'l': 270: ++p2; 271: switch(p1[1]) { 272: case 'd': case 'i': 273: ++p1; 274: pr->flags = F_INT; 275: goto sw1; 276: case 'o': case 'u': case 'x': case 'X': 277: ++p1; 278: pr->flags = F_UINT; 279: goto sw1; 280: default: 281: p1[2] = '\0'; 282: badconv(p1); 283: } 284: /* NOTREACHED */ 285: case 'o': case 'u': case 'x': case 'X': 286: pr->flags = F_UINT; 287: sw1: switch(fu->bcnt) { 288: case 0: case 4: 289: pr->bcnt = 4; 290: break; 291: case 1: 292: pr->bcnt = 1; 293: break; 294: case 2: 295: pr->bcnt = 2; 296: break; 297: default: 298: p1[1] = '\0'; 299: badcnt(p1); 300: } 301: break; 302: case 'e': case 'E': case 'f': case 'g': case 'G': 303: pr->flags = F_DBL; 304: switch(fu->bcnt) { 305: case 0: case 8: 306: pr->bcnt = 8; 307: break; 308: case 4: 309: pr->bcnt = 4; 310: break; 311: default: 312: p1[1] = '\0'; 313: badcnt(p1); 314: } 315: break; 316: case 's': 317: pr->flags = F_STR; 318: switch(sokay) { 319: case NOTOKAY: 320: badsfmt(); 321: case USEBCNT: 322: pr->bcnt = fu->bcnt; 323: break; 324: case USEPREC: 325: pr->bcnt = prec; 326: break; 327: } 328: break; 329: case '_': 330: ++p2; 331: switch(p1[1]) { 332: case 'A': 333: endfu = fu; 334: fu->flags |= F_IGNORE; 335: /* FALLTHROUGH */ 336: case 'a': 337: pr->flags = F_ADDRESS; 338: ++p2; 339: switch(p1[2]) { 340: case 'd': case 'o': case'x': 341: *p1 = p1[2]; 342: break; 343: default: 344: p1[3] = '\0'; 345: badconv(p1); 346: } 347: break; 348: case 'c': 349: pr->flags = F_C; 350: /* *p1 = 'c'; set in conv_c */ 351: goto sw2; 352: case 'p': 353: pr->flags = F_P; 354: *p1 = 'c'; 355: goto sw2; 356: case 'u': 357: pr->flags = F_U; 358: /* *p1 = 'c'; set in conv_u */ 359: sw2: switch(fu->bcnt) { 360: case 0: case 1: 361: pr->bcnt = 1; 362: break; 363: default: 364: p1[2] = '\0'; 365: badcnt(p1); 366: } 367: break; 368: default: 369: p1[2] = '\0'; 370: badconv(p1); 371: } 372: break; 373: default: 374: p1[1] = '\0'; 375: badconv(p1); 376: } 377: 378: /* 379: * copy to PR format string, set conversion character 380: * pointer, update original. 381: */ 382: savech = *p2; 383: p1[1] = '\0'; 384: if (!(pr->fmt = strdup(fmtp))) 385: nomem(); 386: *p2 = savech; 387: pr->cchar = pr->fmt + (p1 - fmtp); 388: fmtp = p2; 389: 390: /* only one conversion character if byte count */ 391: if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) { 392: (void)fprintf(stderr, 393: "hexdump: byte count with multiple conversion characters.\n"); 394: exit(1); 395: } 396: } 397: /* 398: * if format unit byte count not specified, figure it out 399: * so can adjust rep count later. 400: */ 401: if (!fu->bcnt) 402: for (pr = fu->nextpr; pr; pr = pr->nextpr) 403: fu->bcnt += pr->bcnt; 404: } 405: /* 406: * if the format string interprets any data at all, and it's 407: * not the same as the blocksize, and its last format unit 408: * interprets any data at all, and has no iteration count, 409: * repeat it as necessary. 410: * 411: * if, rep count is greater than 1, no trailing whitespace 412: * gets output from the last iteration of the format unit. 413: */ 414: for (fu = fs->nextfu;; fu = fu->nextfu) { 415: if (!fu->nextfu && fs->bcnt < blocksize && 416: !(fu->flags&F_SETREP) && fu->bcnt) 417: fu->reps += (blocksize - fs->bcnt) / fu->bcnt; 418: if (fu->reps > 1) { 419: for (pr = fu->nextpr;; pr = pr->nextpr) 420: if (!pr->nextpr) 421: break; 422: for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) 423: p2 = isspace(*p1) ? p1 : NULL; 424: if (p2) 425: pr->nospace = p2; 426: } 427: if (!fu->nextfu) 428: break; 429: } 430: } 431: 432: 433: escape(p1) 434: register char *p1; 435: { 436: register char *p2; 437: 438: /* alphabetic escape sequences have to be done in place */ 439: for (p2 = p1;; ++p1, ++p2) { 440: if (!*p1) { 441: *p2 = *p1; 442: break; 443: } 444: if (*p1 == '\\') 445: switch(*++p1) { 446: case 'a': 447: /* *p2 = '\a'; */ 448: *p2 = '\007'; 449: break; 450: case 'b': 451: *p2 = '\b'; 452: break; 453: case 'f': 454: *p2 = '\f'; 455: break; 456: case 'n': 457: *p2 = '\n'; 458: break; 459: case 'r': 460: *p2 = '\r'; 461: break; 462: case 't': 463: *p2 = '\t'; 464: break; 465: case 'v': 466: *p2 = '\v'; 467: break; 468: default: 469: *p2 = *p1; 470: break; 471: } 472: } 473: } 474: 475: badcnt(s) 476: char *s; 477: { 478: (void)fprintf(stderr, 479: "hexdump: bad byte count for conversion character %s.\n", s); 480: exit(1); 481: } 482: 483: badsfmt() 484: { 485: (void)fprintf(stderr, 486: "hexdump: %%s requires a precision or a byte count.\n"); 487: exit(1); 488: } 489: 490: badfmt(fmt) 491: char *fmt; 492: { 493: (void)fprintf(stderr, "hexdump: bad format {%s}\n", fmt); 494: exit(1); 495: } 496: 497: badconv(ch) 498: char *ch; 499: { 500: (void)fprintf(stderr, "hexdump: bad conversion character %%%s.\n", ch); 501: exit(1); 502: }