|
|
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.