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