|
|
1.1 ! root 1: /* ! 2: * Input stream abstraction. ! 3: */ ! 4: ! 5: /* ! 6: *-IMPORTS: ! 7: * <sys/compat.h> ! 8: * CONST ! 9: * USE_PROTO ! 10: * ARGS () ! 11: * LOCAL ! 12: * <stddef.h> ! 13: * NULL ! 14: * offsetof () ! 15: * <stdio.h> ! 16: * EOF ! 17: * FILE ! 18: * stderr ! 19: * fprintf () ! 20: * fputs () ! 21: * getc () ! 22: * putc () ! 23: * <stdlib.h> ! 24: * free () ! 25: * malloc () ! 26: * <string.h> ! 27: * memcpy () ! 28: * strlen () ! 29: */ ! 30: ! 31: #include <common/tricks.h> ! 32: #include <sys/compat.h> ! 33: #include <stddef.h> ! 34: #include <stdio.h> ! 35: #include <stdlib.h> ! 36: #include <string.h> ! 37: #include <stdarg.h> ! 38: ! 39: #include "devadm.h" ! 40: ! 41: #include "lex.h" ! 42: ! 43: #include "input.h" ! 44: #include "mkinput.h" ! 45: ! 46: ! 47: /* ! 48: * Structures for dealing with input from a file. ! 49: * ! 50: * A new feature added to this for the 'devadm' utility is support for an ! 51: * output file attached to the input file that can be used to support input ! 52: * filtering effectively. Comments in the input source are copied to the ! 53: * output by the lower layer, while higher-level software can perform semantic ! 54: * processing and then copy the resulting changes out, retaining the relative ! 55: * ordering of the components. This may cause some reordering (moving comments ! 56: * at the end of a line to the beginning of a line), but will in general be ! 57: * benign. ! 58: */ ! 59: ! 60: typedef struct fileinput filein_t; ! 61: ! 62: struct fileinput { ! 63: input_t fi_input; ! 64: ! 65: char * fi_name; ! 66: FILE * fi_infile; /* input stream */ ! 67: int fi_inclose; /* close when stream is closed? */ ! 68: FILE * fi_outfile; /* output stream */ ! 69: int fi_outclose; /* close when stream is closed? */ ! 70: int fi_lineno; /* line number in file */ ! 71: int fi_colno; /* current column number */ ! 72: int fi_prevline; ! 73: int fi_prevcol; ! 74: int fi_comment; /* comment character (maybe none) */ ! 75: int fi_prevchar; /* previous character */ ! 76: int fi_outcol; /* output column number */ ! 77: ! 78: char fi_line [64]; /* line buffer for errors */ ! 79: }; ! 80: ! 81: #define ARRAY_LEN(a) (sizeof (a) / sizeof (* a)) ! 82: ! 83: ! 84: /* ! 85: * Structure for processing input from a string. ! 86: */ ! 87: ! 88: typedef struct strinput strin_t; ! 89: ! 90: struct strinput { ! 91: input_t si_input; ! 92: ! 93: CONST unsigned char ! 94: * si_data; /* data being read */ ! 95: size_t si_pos; /* position in input stream */ ! 96: size_t si_len; /* length of input stream */ ! 97: }; ! 98: ! 99: ! 100: /* ! 101: * Send a single character to the filter output. ! 102: */ ! 103: ! 104: #if USE_PROTO ! 105: LOCAL int _fi_putc (int ch, filein_t * fi) ! 106: #else ! 107: LOCAL int ! 108: _fi_putc (ch, fi) ! 109: int ch; ! 110: filein_t * fi; ! 111: #endif ! 112: { ! 113: FILE * file; ! 114: ! 115: if ((file = fi->fi_outfile) == NULL) { ! 116: #if TRACE ! 117: if (do_debug == 0) ! 118: return 0; ! 119: file = stderr; ! 120: #else ! 121: return 0; ! 122: #endif ! 123: } ! 124: ! 125: if (fputc (ch, file) < 0) ! 126: return -1; ! 127: ! 128: switch (ch) { ! 129: case '\n': ! 130: fi->fi_outcol = 0; ! 131: break; ! 132: ! 133: case '\t': ! 134: fi->fi_outcol = ROUND_UP_TO_MULTIPLE (fi->fi_outcol + 1, 8); ! 135: break; ! 136: ! 137: default: ! 138: fi->fi_outcol ++; ! 139: break; ! 140: } ! 141: ! 142: return 0; ! 143: } ! 144: ! 145: ! 146: /* ! 147: * Read a character from a file. ! 148: */ ! 149: ! 150: #if USE_PROTO ! 151: LOCAL int fi_read (input_t * input) ! 152: #else ! 153: LOCAL int ! 154: fi_read (input) ! 155: input_t * input; ! 156: #endif ! 157: { ! 158: filein_t * fi = (filein_t *) input; ! 159: int ch; ! 160: ! 161: /* ! 162: * Get a character from the input file, recording the data for the ! 163: * purpose of error reporting. ! 164: */ ! 165: ! 166: fi->fi_prevcol = fi->fi_colno; ! 167: fi->fi_prevline = fi->fi_lineno; ! 168: ! 169: another_line: ! 170: if ((ch = getc (fi->fi_infile)) == EOF) ! 171: return IN_EOF; ! 172: ! 173: fi->fi_line [fi->fi_colno ++ % ARRAY_LEN (fi->fi_line)] = ch; ! 174: ! 175: if (ch == fi->fi_comment) { ! 176: /* ! 177: * Consume data to EOL, then return EOL. ! 178: */ ! 179: ! 180: do { ! 181: _fi_putc (ch, fi); ! 182: ! 183: if ((ch = getc (fi->fi_infile)) == EOF) ! 184: return IN_EOF; ! 185: } while (ch != '\n'); ! 186: ! 187: if (fi->fi_colno > 1) ! 188: _fi_putc (ch, fi); ! 189: } ! 190: ! 191: ! 192: if (ch == '\n') { ! 193: ! 194: fi->fi_lineno ++; ! 195: ! 196: if (fi->fi_colno == 1) { ! 197: ! 198: _fi_putc (ch, fi); ! 199: goto another_line; ! 200: }; ! 201: ! 202: fi->fi_colno = 0; ! 203: } ! 204: ! 205: fi->fi_prevchar = ch; ! 206: return ch; ! 207: } ! 208: ! 209: ! 210: /* ! 211: * Return the last read character to the input stream. ! 212: */ ! 213: ! 214: #if USE_PROTO ! 215: LOCAL void fi_unread (input_t * input) ! 216: #else ! 217: LOCAL void ! 218: fi_unread (input) ! 219: input_t * input; ! 220: #endif ! 221: { ! 222: filein_t * fi = (filein_t *) input; ! 223: ! 224: if (fi->fi_prevchar != IN_EOF) { ! 225: ! 226: ungetc (fi->fi_prevchar, fi->fi_infile); ! 227: ! 228: fi->fi_prevchar = IN_EOF; ! 229: fi->fi_colno = fi->fi_prevcol; ! 230: fi->fi_lineno = fi->fi_prevline; ! 231: } ! 232: } ! 233: ! 234: ! 235: /* ! 236: * Close the input file. ! 237: */ ! 238: ! 239: #if USE_PROTO ! 240: LOCAL void fi_close (input_t * input) ! 241: #else ! 242: LOCAL void ! 243: fi_close (input) ! 244: input_t * input; ! 245: #endif ! 246: { ! 247: filein_t * fi = (filein_t *) input; ! 248: ! 249: if (fi->fi_infile != NULL && fi->fi_inclose) ! 250: (void) fclose (fi->fi_infile); ! 251: if (fi->fi_outfile != NULL && fi->fi_outclose) ! 252: (void) fclose (fi->fi_outfile); ! 253: free (fi); ! 254: } ! 255: ! 256: ! 257: /* ! 258: * Produce an error report for this input source. ! 259: */ ! 260: ! 261: #if USE_PROTO ! 262: LOCAL void fi_error (input_t * input) ! 263: #else ! 264: LOCAL void ! 265: fi_error (input) ! 266: input_t * input; ! 267: #endif ! 268: { ! 269: filein_t * fi = (filein_t *) input; ! 270: int col; ! 271: int ch; ! 272: int end; ! 273: ! 274: (void) fprintf (stderr, "Error vicinity : file \"%s\", line %d, column %d\n", ! 275: fi->fi_name, fi->fi_prevline, fi->fi_prevcol); ! 276: ! 277: col = fi->fi_prevcol < 40 ? 0 : fi->fi_prevcol - 40; ! 278: ! 279: if (fi->fi_colno > (end = fi->fi_prevcol)) ! 280: end ++; ! 281: ! 282: while (col < end) { ! 283: /* ! 284: * Should think about escaping controls. ! 285: */ ! 286: ! 287: ch = fi->fi_line [col ++ % ARRAY_LEN (fi->fi_line)]; ! 288: (void) putc (ch, stderr); ! 289: } ! 290: ! 291: ! 292: fputs (" <---\n", stderr); ! 293: } ! 294: ! 295: ! 296: /* ! 297: * Perform columnar formatted output. ! 298: */ ! 299: ! 300: #if USE_PROTO ! 301: LOCAL int fi_filter (input_t * input, CONST char * format, ...) ! 302: #else ! 303: LOCAL int ! 304: fi_filter (input, format) ! 305: input_t * input; ! 306: CONST char * format; ! 307: #endif ! 308: { ! 309: filein_t * fi = (filein_t *) input; ! 310: va_list args; ! 311: FILE * file; ! 312: ! 313: if ((file = fi->fi_outfile) == NULL) { ! 314: #if TRACE ! 315: if (do_debug == 0) ! 316: return 0; ! 317: file = stderr; ! 318: #else ! 319: return 0; ! 320: #endif ! 321: } ! 322: ! 323: va_start (args, format); ! 324: ! 325: while (* format) { ! 326: char ch; ! 327: int cols; ! 328: char * tmp; ! 329: static char digits [] = "0123456789"; ! 330: int long_datum; ! 331: ! 332: /* ! 333: * We accept either a '%' or '<' sign to introduce a format ! 334: * so that we don't clutter things up with too many '%'-signs. ! 335: */ ! 336: ! 337: if (((ch = * format ++) != '%' && ch != '<') || ! 338: * format == ch) { ! 339: _fi_putc (ch, fi); ! 340: continue; ! 341: } ! 342: ! 343: if ((ch = * format ++) == 0) ! 344: return -1; ! 345: ! 346: /* ! 347: * After a percent-sign, look for a number of tab-columns to ! 348: * align the output to. ! 349: */ ! 350: ! 351: cols = 0; ! 352: while ((tmp = strchr (digits, ch)) != NULL) { ! 353: ! 354: cols = (cols * 10) + (tmp - digits); ! 355: if ((ch = * format ++) == 0) ! 356: break; ! 357: } ! 358: ! 359: /* ! 360: * Dispatch on the format character and record the number of ! 361: * characters written (which will be negative on error). ! 362: */ ! 363: ! 364: if ((long_datum = ch == 'l') != 0) ! 365: ch = * format ++; ! 366: ! 367: switch (ch) { ! 368: case 'c': ! 369: ch = fprintf (file, "%*c", cols, ! 370: va_arg (args, int)); ! 371: break; ! 372: ! 373: case 's': ! 374: ch = fprintf (file, "%*s", cols, ! 375: va_arg (args, char *)); ! 376: break; ! 377: ! 378: case 'd': ! 379: ch = fprintf (file, "%*ld", cols, ! 380: long_datum ? va_arg (args, long) : ! 381: (long) va_arg (args, int)); ! 382: break; ! 383: ! 384: case 'x': ! 385: ch = fprintf (file, "%*lx", cols, ! 386: long_datum ? va_arg (args, long) : ! 387: (long) va_arg (args, int)); ! 388: break; ! 389: ! 390: case '>': ! 391: /* ! 392: * Here is what we add: <{digits}> indicates a column ! 393: * number to seek forward to. ! 394: */ ! 395: ! 396: cols = cols * 8 - fi->fi_outcol; ! 397: ! 398: if (cols <= 0) ! 399: _fi_putc (' ', fi); ! 400: ! 401: while (cols > 0) { ! 402: _fi_putc ('\t', fi); ! 403: cols -= 8; ! 404: } ! 405: ! 406: ch = 0; ! 407: break; ! 408: ! 409: default: ! 410: return -1; ! 411: } ! 412: ! 413: if (ch < 0) ! 414: return -1; ! 415: ! 416: fi->fi_outcol += ch; ! 417: } ! 418: ! 419: va_end (args); ! 420: return 0; ! 421: } ! 422: ! 423: ! 424: /* ! 425: * Create a source of input from a file. The 'comment' parameter gives the ! 426: * code of a character which can be used to indicate input sections that are ! 427: * to be ignored. The 'filter' parameter points to an optional FILE where the ! 428: * input is going to be copied. ! 429: */ ! 430: ! 431: #if USE_PROTO ! 432: input_t * (make_filter) (FILE * infile, CONST char * inname, int inclose, ! 433: int comment, FILE * outfile, int outclose) ! 434: #else ! 435: input_t * ! 436: make_filter ARGS ((infile, inname, inclose, comment, outfile, outclose)) ! 437: FILE * infile; ! 438: CONST char * inname; ! 439: int inclose; ! 440: int comment; ! 441: FILE * outfile; ! 442: int outclose; ! 443: #endif ! 444: { ! 445: filein_t * fi; ! 446: size_t namelen; ! 447: ! 448: /* ASSERT that fi_input is the first member of the structure */ ! 449: ! 450: if (offsetof (filein_t, fi_input) != 0) ! 451: return NULL; ! 452: ! 453: if (inname == NULL) ! 454: inname = "no name"; ! 455: ! 456: namelen = strlen (inname) + 1; ! 457: ! 458: if ((fi = (filein_t *) malloc (sizeof (* fi) + namelen)) == NULL) ! 459: return NULL; ! 460: ! 461: fi->fi_lineno = 1; ! 462: fi->fi_colno = 0; ! 463: fi->fi_infile = infile; ! 464: fi->fi_inclose = inclose; ! 465: fi->fi_outfile = outfile; ! 466: fi->fi_outclose = outclose; ! 467: fi->fi_name = (char *) (fi + 1); ! 468: ! 469: fi->fi_comment = comment; ! 470: fi->fi_prevchar = IN_EOF; ! 471: ! 472: if (namelen > 0) ! 473: memcpy (fi->fi_name, inname, namelen); ! 474: ! 475: fi->fi_input.in_read = fi_read; ! 476: fi->fi_input.in_unread = fi_unread; ! 477: fi->fi_input.in_readtok = NULL; ! 478: fi->fi_input.in_close = fi_close; ! 479: fi->fi_input.in_error = fi_error; ! 480: fi->fi_input.in_filter = fi_filter; ! 481: ! 482: return & fi->fi_input; ! 483: } ! 484: ! 485: ! 486: /* ! 487: * Return a character from an input string, or EOF when the end-of-string is ! 488: * reached. ! 489: */ ! 490: ! 491: #if USE_PROTO ! 492: LOCAL int si_read (input_t * input) ! 493: #else ! 494: LOCAL int ! 495: si_read (input) ! 496: input_t * input; ! 497: #endif ! 498: { ! 499: strin_t * si = (strin_t *) input; ! 500: ! 501: if (si->si_pos >= si->si_len) ! 502: return IN_EOF; ! 503: ! 504: return si->si_data [si->si_pos ++]; ! 505: } ! 506: ! 507: ! 508: /* ! 509: * Return a character to the input stream to be read again. ! 510: */ ! 511: ! 512: #if USE_PROTO ! 513: LOCAL void si_unread (input_t * input) ! 514: #else ! 515: LOCAL void ! 516: si_unread (input) ! 517: input_t * input; ! 518: #endif ! 519: { ! 520: strin_t * si = (strin_t *) input; ! 521: ! 522: if (si->si_pos > 0) ! 523: si->si_pos --; ! 524: } ! 525: ! 526: ! 527: /* ! 528: * Reading a token, specialized to string input streams. ! 529: */ ! 530: ! 531: #if USE_PROTO ! 532: LOCAL CONST unsigned char * si_readtok (input_t * input, lex_t * lexp, ! 533: size_t * lengthp) ! 534: #else ! 535: LOCAL CONST unsigned char * ! 536: si_readtok (input, lexp, lengthp) ! 537: input_t * input; ! 538: lex_t * lexp; ! 539: size_t * lengthp; ! 540: #endif ! 541: { ! 542: strin_t * si = (strin_t *) input; ! 543: int start = -1; ! 544: int ch; ! 545: ! 546: for (;;) { ! 547: if ((ch = si_read (input)) == IN_EOF) ! 548: break; ! 549: ! 550: switch (classify (lexp, ch, start == -1)) { ! 551: ! 552: case CLASS_FLUSH: ! 553: continue; ! 554: ! 555: case CLASS_SEP: ! 556: si_unread (input); ! 557: goto separator; ! 558: ! 559: default: ! 560: break; ! 561: } ! 562: ! 563: if (start == -1) ! 564: start = si->si_pos - 1; ! 565: } ! 566: ! 567: separator: ! 568: * lengthp = start == -1 ? 0 : si->si_pos - start; ! 569: return start == -1 ? NULL : & si->si_data [start]; ! 570: } ! 571: ! 572: ! 573: /* ! 574: * Finish with a string input stream. ! 575: */ ! 576: ! 577: #if USE_PROTO ! 578: LOCAL void si_close (input_t * input) ! 579: #else ! 580: LOCAL void ! 581: si_close (input) ! 582: input_t * input; ! 583: #endif ! 584: { ! 585: free (input); ! 586: } ! 587: ! 588: ! 589: /* ! 590: * Display an error locus in a string input stream. ! 591: */ ! 592: ! 593: #if USE_PROTO ! 594: LOCAL void si_error (input_t * input) ! 595: #else ! 596: LOCAL void ! 597: si_error (input) ! 598: input_t * input; ! 599: #endif ! 600: { ! 601: strin_t * si = (strin_t *) input; ! 602: int col; ! 603: ! 604: col = si->si_pos < 40 ? 0 : si->si_pos - 40; ! 605: ! 606: (void) fprintf (stderr, "Error vicinity in input string : %.*s <---\n", ! 607: (int) si->si_pos - col, & si->si_data [col]); ! 608: } ! 609: ! 610: ! 611: /* ! 612: * The filtering concept does not really apply to strings yet. ! 613: */ ! 614: ! 615: #if USE_PROTO ! 616: LOCAL int si_filter (input_t * NOTUSED (input), CONST char * NOTUSED (format), ! 617: ...) ! 618: #else ! 619: LOCAL int ! 620: si_filter (input, format) ! 621: input_t * input; ! 622: CONST char * format; ! 623: #endif ! 624: { ! 625: return -1; ! 626: } ! 627: ! 628: ! 629: /* ! 630: * Create a source of input from a string. Depending on the "action" code, ! 631: * the string may or may not be copied or left alone. If the user wants some ! 632: * clean processing to be done at close time, then another version of this ! 633: * code should be set in place which allows registration of a close-time ! 634: * callback (which is messy, so it hasn't been done). ! 635: */ ! 636: ! 637: #if USE_PROTO ! 638: input_t * (make_string_input) (CONST unsigned char * str, int copy) ! 639: #else ! 640: input_t * ! 641: make_string_input ARGS ((str, copy)) ! 642: CONST unsigned char ! 643: * str; ! 644: int copy; ! 645: #endif ! 646: { ! 647: strin_t * si; ! 648: size_t len; ! 649: ! 650: /* ASSERT that si_input is the first member of the structure */ ! 651: ! 652: if (offsetof (strin_t, si_input) != 0) ! 653: return NULL; ! 654: ! 655: len = strlen ((char *) str); ! 656: ! 657: if ((si = (strin_t *) malloc (sizeof (* si) + ! 658: (copy ? len : 0))) == NULL) ! 659: return NULL; ! 660: ! 661: si->si_pos = 0; ! 662: si->si_len = len; ! 663: ! 664: if (copy) { ! 665: ! 666: si->si_data = (CONST unsigned char *) (si + 1); ! 667: memcpy (si + 1, str, len); ! 668: } else ! 669: si->si_data = str; ! 670: ! 671: si->si_input.in_read = si_read; ! 672: si->si_input.in_unread = si_unread; ! 673: si->si_input.in_readtok = si_readtok; ! 674: si->si_input.in_close = si_close; ! 675: si->si_input.in_error = si_error; ! 676: si->si_input.in_filter = si_filter; ! 677: ! 678: return & si->si_input; ! 679: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.