source: trunk/yacc/reader.c@ 3022

Last change on this file since 3022 was 2464, checked in by bird, 20 years ago

FreeBSD CVS 2005-07-07

File size: 35.7 KB
Line 
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Robert Paul Corbett.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#if 0
38#ifndef lint
39static char sccsid[] = "@(#)reader.c 5.7 (Berkeley) 1/20/91";
40#endif
41#endif
42
43#include <sys/cdefs.h>
44__FBSDID("$FreeBSD: src/usr.bin/yacc/reader.c,v 1.19 2002/08/25 13:23:09 charnier Exp $");
45
46#include <limits.h>
47#include <stdlib.h>
48#include <string.h>
49#include "defs.h"
50
51/* The line size must be a positive integer. One hundred was chosen */
52/* because few lines in Yacc input grammars exceed 100 characters. */
53/* Note that if a line exceeds LINESIZE characters, the line buffer */
54/* will be expanded to accomodate it. */
55
56#define LINESIZE 100
57
58char *cache;
59int cinc, cache_size;
60
61int ntags, tagmax;
62char **tag_table;
63
64char saw_eof, unionized;
65char *cptr, *line;
66int linesize;
67
68bucket *goal;
69int prec;
70int gensym;
71char last_was_action;
72
73int maxitems;
74bucket **pitem;
75
76int maxrules;
77bucket **plhs;
78
79int name_pool_size;
80char *name_pool;
81
82static const char line_format[] = "#line %d \"%s\"\n";
83
84static void add_symbol(void);
85static void advance_to_start(void);
86static void cachec(int);
87static void check_symbols(void);
88static void copy_action(void);
89static void copy_ident(void);
90static void copy_text(void);
91static void copy_union(void);
92static void declare_expect(int);
93static void declare_start(void);
94static void declare_tokens(int);
95static void declare_types(void);
96static char *dup_line(void);
97static void end_rule(void);
98static void expand_items(void);
99static void expand_rules(void);
100static void free_tags(void);
101static void get_line(void);
102static bucket *get_literal(void);
103static bucket *get_name(void);
104static int get_number(void);
105static char *get_tag(void);
106static int hexval(int);
107static void initialize_grammar(void);
108static void insert_empty_rule(void);
109static int is_reserved(char *);
110static int keyword(void);
111static int mark_symbol(void);
112static int nextc(void);
113static void pack_grammar(void);
114static void pack_names(void);
115static void pack_symbols(void);
116static void print_grammar(void);
117static void read_declarations(void);
118static void read_grammar(void);
119static void skip_comment(void);
120static void start_rule(bucket *, int);
121
122static void
123cachec(c)
124int c;
125{
126 assert(cinc >= 0);
127 if (cinc >= cache_size)
128 {
129 cache_size += 256;
130 cache = REALLOC(cache, cache_size);
131 if (cache == 0) no_space();
132 }
133 cache[cinc] = c;
134 ++cinc;
135}
136
137
138static void
139get_line()
140{
141 FILE *f = input_file;
142 int c;
143 int i;
144
145 if (saw_eof || (c = getc(f)) == EOF)
146 {
147 if (line) { FREE(line); line = 0; }
148 cptr = 0;
149 saw_eof = 1;
150 return;
151 }
152
153 if (line == 0 || linesize != (LINESIZE + 1))
154 {
155 if (line) FREE(line);
156 linesize = LINESIZE + 1;
157 line = MALLOC(linesize);
158 if (line == 0) no_space();
159 }
160
161 i = 0;
162 ++lineno;
163 for (;;)
164 {
165 line[i] = c;
166 if (c == '\n') { cptr = line; return; }
167 if (++i >= linesize)
168 {
169 linesize += LINESIZE;
170 line = REALLOC(line, linesize);
171 if (line == 0) no_space();
172 }
173 c = getc(f);
174 if (c == EOF)
175 {
176 line[i] = '\n';
177 saw_eof = 1;
178 cptr = line;
179 return;
180 }
181 }
182}
183
184
185static char *
186dup_line()
187{
188 char *p, *s, *t;
189
190 if (line == 0) return (0);
191 s = line;
192 while (*s != '\n') ++s;
193 p = MALLOC(s - line + 1);
194 if (p == 0) no_space();
195
196 s = line;
197 t = p;
198 while ((*t++ = *s++) != '\n') continue;
199 return (p);
200}
201
202
203static void
204skip_comment()
205{
206 char *s;
207
208 int st_lineno = lineno;
209 char *st_line = dup_line();
210 char *st_cptr = st_line + (cptr - line);
211
212 s = cptr + 2;
213 for (;;)
214 {
215 if (*s == '*' && s[1] == '/')
216 {
217 cptr = s + 2;
218 FREE(st_line);
219 return;
220 }
221 if (*s == '\n')
222 {
223 get_line();
224 if (line == 0)
225 unterminated_comment(st_lineno, st_line, st_cptr);
226 s = cptr;
227 }
228 else
229 ++s;
230 }
231}
232
233
234static int
235nextc()
236{
237 char *s;
238
239 if (line == 0)
240 {
241 get_line();
242 if (line == 0)
243 return (EOF);
244 }
245
246 s = cptr;
247 for (;;)
248 {
249 switch (*s)
250 {
251 case '\n':
252 get_line();
253 if (line == 0) return (EOF);
254 s = cptr;
255 break;
256
257 case ' ':
258 case '\t':
259 case '\f':
260 case '\r':
261 case '\v':
262 case ',':
263 case ';':
264 ++s;
265 break;
266
267 case '\\':
268 cptr = s;
269 return ('%');
270
271 case '/':
272 if (s[1] == '*')
273 {
274 cptr = s;
275 skip_comment();
276 s = cptr;
277 break;
278 }
279 else if (s[1] == '/')
280 {
281 get_line();
282 if (line == 0) return (EOF);
283 s = cptr;
284 break;
285 }
286 /* FALLTHROUGH */
287
288 default:
289 cptr = s;
290 return (*s);
291 }
292 }
293}
294
295
296static int
297keyword()
298{
299 int c;
300 char *t_cptr = cptr;
301
302 c = *++cptr;
303 if (isalpha(c))
304 {
305 cinc = 0;
306 for (;;)
307 {
308 if (isalpha(c))
309 {
310 if (isupper(c)) c = tolower(c);
311 cachec(c);
312 }
313 else if (isdigit(c) || c == '_' || c == '.' || c == '$')
314 cachec(c);
315 else
316 break;
317 c = *++cptr;
318 }
319 cachec(NUL);
320
321 if (strcmp(cache, "token") == 0 || strcmp(cache, "term") == 0)
322 return (TOKEN);
323 if (strcmp(cache, "type") == 0)
324 return (TYPE);
325 if (strcmp(cache, "left") == 0)
326 return (LEFT);
327 if (strcmp(cache, "right") == 0)
328 return (RIGHT);
329 if (strcmp(cache, "nonassoc") == 0 || strcmp(cache, "binary") == 0)
330 return (NONASSOC);
331 if (strcmp(cache, "start") == 0)
332 return (START);
333 if (strcmp(cache, "union") == 0)
334 return (UNION);
335 if (strcmp(cache, "ident") == 0)
336 return (IDENT);
337 if (strcmp(cache, "expect") == 0)
338 return (EXPECT);
339 }
340 else
341 {
342 ++cptr;
343 if (c == '{')
344 return (TEXT);
345 if (c == '%' || c == '\\')
346 return (MARK);
347 if (c == '<')
348 return (LEFT);
349 if (c == '>')
350 return (RIGHT);
351 if (c == '0')
352 return (TOKEN);
353 if (c == '2')
354 return (NONASSOC);
355 }
356 syntax_error(lineno, line, t_cptr);
357 /*NOTREACHED*/
358 return (0);
359}
360
361
362static void
363copy_ident()
364{
365 int c;
366 FILE *f = output_file;
367
368 c = nextc();
369 if (c == EOF) unexpected_EOF();
370 if (c != '"') syntax_error(lineno, line, cptr);
371 ++outline;
372 fprintf(f, "#ident \"");
373 for (;;)
374 {
375 c = *++cptr;
376 if (c == '\n')
377 {
378 fprintf(f, "\"\n");
379 return;
380 }
381 putc(c, f);
382 if (c == '"')
383 {
384 putc('\n', f);
385 ++cptr;
386 return;
387 }
388 }
389}
390
391
392static void
393copy_text()
394{
395 int c;
396 int quote;
397 FILE *f = text_file;
398 int need_newline = 0;
399 int t_lineno = lineno;
400 char *t_line = dup_line();
401 char *t_cptr = t_line + (cptr - line - 2);
402
403 if (*cptr == '\n')
404 {
405 get_line();
406 if (line == 0)
407 unterminated_text(t_lineno, t_line, t_cptr);
408 }
409 if (!lflag) fprintf(f, line_format, lineno, input_file_name);
410
411loop:
412 c = *cptr++;
413 switch (c)
414 {
415 case '\n':
416 next_line:
417 putc('\n', f);
418 need_newline = 0;
419 get_line();
420 if (line) goto loop;
421 unterminated_text(t_lineno, t_line, t_cptr);
422
423 case '\'':
424 case '"':
425 {
426 int s_lineno = lineno;
427 char *s_line = dup_line();
428 char *s_cptr = s_line + (cptr - line - 1);
429
430 quote = c;
431 putc(c, f);
432 for (;;)
433 {
434 c = *cptr++;
435 putc(c, f);
436 if (c == quote)
437 {
438 need_newline = 1;
439 FREE(s_line);
440 goto loop;
441 }
442 if (c == '\n')
443 unterminated_string(s_lineno, s_line, s_cptr);
444 if (c == '\\')
445 {
446 c = *cptr++;
447 putc(c, f);
448 if (c == '\n')
449 {
450 get_line();
451 if (line == 0)
452 unterminated_string(s_lineno, s_line, s_cptr);
453 }
454 }
455 }
456 }
457
458 case '/':
459 putc(c, f);
460 need_newline = 1;
461 c = *cptr;
462 if (c == '/')
463 {
464 putc('*', f);
465 while ((c = *++cptr) != '\n')
466 {
467 if (c == '*' && cptr[1] == '/')
468 fprintf(f, "* ");
469 else
470 putc(c, f);
471 }
472 fprintf(f, "*/");
473 goto next_line;
474 }
475 if (c == '*')
476 {
477 int c_lineno = lineno;
478 char *c_line = dup_line();
479 char *c_cptr = c_line + (cptr - line - 1);
480
481 putc('*', f);
482 ++cptr;
483 for (;;)
484 {
485 c = *cptr++;
486 putc(c, f);
487 if (c == '*' && *cptr == '/')
488 {
489 putc('/', f);
490 ++cptr;
491 FREE(c_line);
492 goto loop;
493 }
494 if (c == '\n')
495 {
496 get_line();
497 if (line == 0)
498 unterminated_comment(c_lineno, c_line, c_cptr);
499 }
500 }
501 }
502 need_newline = 1;
503 goto loop;
504
505 case '%':
506 case '\\':
507 if (*cptr == '}')
508 {
509 if (need_newline) putc('\n', f);
510 ++cptr;
511 FREE(t_line);
512 return;
513 }
514 /* FALLTHROUGH */
515
516 default:
517 putc(c, f);
518 need_newline = 1;
519 goto loop;
520 }
521}
522
523
524static void
525copy_union()
526{
527 int c;
528 int quote;
529 int depth;
530 int u_lineno = lineno;
531 char *u_line = dup_line();
532 char *u_cptr = u_line + (cptr - line - 6);
533
534 if (unionized) over_unionized(cptr - 6);
535 unionized = 1;
536
537 if (!lflag)
538 fprintf(text_file, line_format, lineno, input_file_name);
539
540 fprintf(text_file, "typedef union");
541 if (dflag) fprintf(union_file, "typedef union");
542
543 depth = 0;
544loop:
545 c = *cptr++;
546 putc(c, text_file);
547 if (dflag) putc(c, union_file);
548 switch (c)
549 {
550 case '\n':
551 next_line:
552 get_line();
553 if (line == 0) unterminated_union(u_lineno, u_line, u_cptr);
554 goto loop;
555
556 case '{':
557 ++depth;
558 goto loop;
559
560 case '}':
561 if (--depth == 0)
562 {
563 fprintf(text_file, " YYSTYPE;\n");
564 FREE(u_line);
565 return;
566 }
567 goto loop;
568
569 case '\'':
570 case '"':
571 {
572 int s_lineno = lineno;
573 char *s_line = dup_line();
574 char *s_cptr = s_line + (cptr - line - 1);
575
576 quote = c;
577 for (;;)
578 {
579 c = *cptr++;
580 putc(c, text_file);
581 if (dflag) putc(c, union_file);
582 if (c == quote)
583 {
584 FREE(s_line);
585 goto loop;
586 }
587 if (c == '\n')
588 unterminated_string(s_lineno, s_line, s_cptr);
589 if (c == '\\')
590 {
591 c = *cptr++;
592 putc(c, text_file);
593 if (dflag) putc(c, union_file);
594 if (c == '\n')
595 {
596 get_line();
597 if (line == 0)
598 unterminated_string(s_lineno, s_line, s_cptr);
599 }
600 }
601 }
602 }
603
604 case '/':
605 c = *cptr;
606 if (c == '/')
607 {
608 putc('*', text_file);
609 if (dflag) putc('*', union_file);
610 while ((c = *++cptr) != '\n')
611 {
612 if (c == '*' && cptr[1] == '/')
613 {
614 fprintf(text_file, "* ");
615 if (dflag) fprintf(union_file, "* ");
616 }
617 else
618 {
619 putc(c, text_file);
620 if (dflag) putc(c, union_file);
621 }
622 }
623 fprintf(text_file, "*/\n");
624 if (dflag) fprintf(union_file, "*/\n");
625 goto next_line;
626 }
627 if (c == '*')
628 {
629 int c_lineno = lineno;
630 char *c_line = dup_line();
631 char *c_cptr = c_line + (cptr - line - 1);
632
633 putc('*', text_file);
634 if (dflag) putc('*', union_file);
635 ++cptr;
636 for (;;)
637 {
638 c = *cptr++;
639 putc(c, text_file);
640 if (dflag) putc(c, union_file);
641 if (c == '*' && *cptr == '/')
642 {
643 putc('/', text_file);
644 if (dflag) putc('/', union_file);
645 ++cptr;
646 FREE(c_line);
647 goto loop;
648 }
649 if (c == '\n')
650 {
651 get_line();
652 if (line == 0)
653 unterminated_comment(c_lineno, c_line, c_cptr);
654 }
655 }
656 }
657 goto loop;
658
659 default:
660 goto loop;
661 }
662}
663
664
665static int
666hexval(c)
667int c;
668{
669 if (c >= '0' && c <= '9')
670 return (c - '0');
671 if (c >= 'A' && c <= 'F')
672 return (c - 'A' + 10);
673 if (c >= 'a' && c <= 'f')
674 return (c - 'a' + 10);
675 return (-1);
676}
677
678
679static bucket *
680get_literal()
681{
682 int c, quote;
683 int i;
684 int n;
685 char *s;
686 bucket *bp;
687 int s_lineno = lineno;
688 char *s_line = dup_line();
689 char *s_cptr = s_line + (cptr - line);
690
691 quote = *cptr++;
692 cinc = 0;
693 for (;;)
694 {
695 c = *cptr++;
696 if (c == quote) break;
697 if (c == '\n') unterminated_string(s_lineno, s_line, s_cptr);
698 if (c == '\\')
699 {
700 char *c_cptr = cptr - 1;
701
702 c = *cptr++;
703 switch (c)
704 {
705 case '\n':
706 get_line();
707 if (line == 0) unterminated_string(s_lineno, s_line, s_cptr);
708 continue;
709
710 case '0': case '1': case '2': case '3':
711 case '4': case '5': case '6': case '7':
712 n = c - '0';
713 c = *cptr;
714 if (IS_OCTAL(c))
715 {
716 n = (n << 3) + (c - '0');
717 c = *++cptr;
718 if (IS_OCTAL(c))
719 {
720 n = (n << 3) + (c - '0');
721 ++cptr;
722 }
723 }
724 if (n > UCHAR_MAX) illegal_character(c_cptr);
725 c = n;
726 break;
727
728 case 'x':
729 c = *cptr++;
730 n = hexval(c);
731 if (n < 0 || n >= 16)
732 illegal_character(c_cptr);
733 for (;;)
734 {
735 c = *cptr;
736 i = hexval(c);
737 if (i < 0 || i >= 16) break;
738 ++cptr;
739 n = (n << 4) + i;
740 if (n > UCHAR_MAX) illegal_character(c_cptr);
741 }
742 c = n;
743 break;
744
745 case 'a': c = 7; break;
746 case 'b': c = '\b'; break;
747 case 'f': c = '\f'; break;
748 case 'n': c = '\n'; break;
749 case 'r': c = '\r'; break;
750 case 't': c = '\t'; break;
751 case 'v': c = '\v'; break;
752 }
753 }
754 cachec(c);
755 }
756 FREE(s_line);
757
758 n = cinc;
759 s = MALLOC(n);
760 if (s == 0) no_space();
761
762 for (i = 0; i < n; ++i)
763 s[i] = cache[i];
764
765 cinc = 0;
766 if (n == 1)
767 cachec('\'');
768 else
769 cachec('"');
770
771 for (i = 0; i < n; ++i)
772 {
773 c = ((unsigned char *)s)[i];
774 if (c == '\\' || c == cache[0])
775 {
776 cachec('\\');
777 cachec(c);
778 }
779 else if (isprint(c))
780 cachec(c);
781 else
782 {
783 cachec('\\');
784 switch (c)
785 {
786 case 7: cachec('a'); break;
787 case '\b': cachec('b'); break;
788 case '\f': cachec('f'); break;
789 case '\n': cachec('n'); break;
790 case '\r': cachec('r'); break;
791 case '\t': cachec('t'); break;
792 case '\v': cachec('v'); break;
793 default:
794 cachec(((c >> 6) & 7) + '0');
795 cachec(((c >> 3) & 7) + '0');
796 cachec((c & 7) + '0');
797 break;
798 }
799 }
800 }
801
802 if (n == 1)
803 cachec('\'');
804 else
805 cachec('"');
806
807 cachec(NUL);
808 bp = lookup(cache);
809 bp->class = TERM;
810 if (n == 1 && bp->value == UNDEFINED)
811 bp->value = *(unsigned char *)s;
812 FREE(s);
813
814 return (bp);
815}
816
817
818static int
819is_reserved(name)
820char *name;
821{
822 char *s;
823
824 if (strcmp(name, ".") == 0 ||
825 strcmp(name, "$accept") == 0 ||
826 strcmp(name, "$end") == 0)
827 return (1);
828
829 if (name[0] == '$' && name[1] == '$' && isdigit(name[2]))
830 {
831 s = name + 3;
832 while (isdigit(*s)) ++s;
833 if (*s == NUL) return (1);
834 }
835
836 return (0);
837}
838
839
840static bucket *
841get_name()
842{
843 int c;
844
845 cinc = 0;
846 for (c = *cptr; IS_IDENT(c); c = *++cptr)
847 cachec(c);
848 cachec(NUL);
849
850 if (is_reserved(cache)) used_reserved(cache);
851
852 return (lookup(cache));
853}
854
855
856static int
857get_number()
858{
859 int c;
860 int n;
861
862 n = 0;
863 for (c = *cptr; isdigit(c); c = *++cptr)
864 n = 10*n + (c - '0');
865
866 return (n);
867}
868
869
870static char *
871get_tag()
872{
873 int c;
874 int i;
875 char *s;
876 int t_lineno = lineno;
877 char *t_line = dup_line();
878 char *t_cptr = t_line + (cptr - line);
879
880 ++cptr;
881 c = nextc();
882 if (c == EOF) unexpected_EOF();
883 if (!isalpha(c) && c != '_' && c != '$')
884 illegal_tag(t_lineno, t_line, t_cptr);
885
886 cinc = 0;
887 do { cachec(c); c = *++cptr; } while (IS_IDENT(c));
888 cachec(NUL);
889
890 c = nextc();
891 if (c == EOF) unexpected_EOF();
892 if (c != '>')
893 illegal_tag(t_lineno, t_line, t_cptr);
894 ++cptr;
895
896 for (i = 0; i < ntags; ++i)
897 {
898 if (strcmp(cache, tag_table[i]) == 0)
899 return (tag_table[i]);
900 }
901
902 if (ntags >= tagmax)
903 {
904 tagmax += 16;
905 tag_table = (char **)
906 (tag_table ? REALLOC(tag_table, tagmax*sizeof(char *))
907 : MALLOC(tagmax*sizeof(char *)));
908 if (tag_table == 0) no_space();
909 }
910
911 s = MALLOC(cinc);
912 if (s == 0) no_space();
913 strcpy(s, cache);
914 tag_table[ntags] = s;
915 ++ntags;
916 FREE(t_line);
917 return (s);
918}
919
920
921static void
922declare_tokens(assoc)
923int assoc;
924{
925 int c;
926 bucket *bp;
927 int value;
928 char *tag = 0;
929
930 if (assoc != TOKEN) ++prec;
931
932 c = nextc();
933 if (c == EOF) unexpected_EOF();
934 if (c == '<')
935 {
936 tag = get_tag();
937 c = nextc();
938 if (c == EOF) unexpected_EOF();
939 }
940
941 for (;;)
942 {
943 if (isalpha(c) || c == '_' || c == '.' || c == '$')
944 bp = get_name();
945 else if (c == '\'' || c == '"')
946 bp = get_literal();
947 else
948 return;
949
950 if (bp == goal) tokenized_start(bp->name);
951 bp->class = TERM;
952
953 if (tag)
954 {
955 if (bp->tag && tag != bp->tag)
956 retyped_warning(bp->name);
957 bp->tag = tag;
958 }
959
960 if (assoc != TOKEN)
961 {
962 if (bp->prec && prec != bp->prec)
963 reprec_warning(bp->name);
964 bp->assoc = assoc;
965 bp->prec = prec;
966 }
967
968 c = nextc();
969 if (c == EOF) unexpected_EOF();
970 value = UNDEFINED;
971 if (isdigit(c))
972 {
973 value = get_number();
974 if (bp->value != UNDEFINED && value != bp->value)
975 revalued_warning(bp->name);
976 bp->value = value;
977 c = nextc();
978 if (c == EOF) unexpected_EOF();
979 }
980 }
981}
982
983
984/*
985 * %expect requires special handling
986 * as it really isn't part of the yacc
987 * grammar only a flag for yacc proper.
988 */
989static void
990declare_expect(assoc)
991int assoc;
992{
993 int c;
994
995 if (assoc != EXPECT) ++prec;
996
997 /*
998 * Stay away from nextc - doesn't
999 * detect EOL and will read to EOF.
1000 */
1001 c = *++cptr;
1002 if (c == EOF) unexpected_EOF();
1003
1004 for(;;)
1005 {
1006 if (isdigit(c))
1007 {