4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2002, 2003, 2004
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
8 This file is part of groff.
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
30 implement_ptable(char)
32 PTABLE(char) macro_table
;
34 class macro_input
: public input
{
38 macro_input(const char *);
44 class argument_macro_input
: public input
{
51 argument_macro_input(const char *, int, char **);
52 ~argument_macro_input();
57 input::input() : next(0)
65 int input::get_location(const char **, int *)
70 file_input::file_input(FILE *f
, const char *fn
)
71 : fp(f
), filename(fn
), lineno(0), ptr("")
75 file_input::~file_input()
80 int file_input::read_line()
89 else if (invalid_input_char(c
))
90 lex_error("invalid input character code %1", c
);
97 if (line
.length() == 0)
99 if (!(line
.length() >= 3 && line
[0] == '.' && line
[1] == 'P'
100 && (line
[2] == 'S' || line
[2] == 'E' || line
[2] == 'F')
101 && (line
.length() == 3 || line
[3] == ' ' || line
[3] == '\n'
102 || compatible_flag
))) {
104 ptr
= line
.contents();
110 int file_input::get()
112 if (*ptr
!= '\0' || read_line())
113 return (unsigned char)*ptr
++;
118 int file_input::peek()
120 if (*ptr
!= '\0' || read_line())
121 return (unsigned char)*ptr
;
126 int file_input::get_location(const char **fnp
, int *lnp
)
133 macro_input::macro_input(const char *str
)
135 p
= s
= strsave(str
);
138 macro_input::~macro_input()
143 int macro_input::get()
145 if (p
== 0 || *p
== '\0')
148 return (unsigned char)*p
++;
151 int macro_input::peek()
153 if (p
== 0 || *p
== '\0')
156 return (unsigned char)*p
;
159 // Character representing $1. Must be invalid input character.
162 char *process_body(const char *body
)
164 char *s
= strsave(body
);
166 for (int i
= 0; s
[i
] != '\0'; i
++)
167 if (s
[i
] == '$' && s
[i
+1] >= '0' && s
[i
+1] <= '9') {
169 s
[j
++] = ARG1
+ s
[++i
] - '1';
178 argument_macro_input::argument_macro_input(const char *body
, int ac
, char **av
)
181 for (int i
= 0; i
< argc
; i
++)
183 p
= s
= process_body(body
);
187 argument_macro_input::~argument_macro_input()
189 for (int i
= 0; i
< argc
; i
++)
194 int argument_macro_input::get()
198 return (unsigned char)*ap
++;
203 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
205 if (i
< argc
&& argv
[i
] != 0 && argv
[i
][0] != '\0') {
207 return (unsigned char)*ap
++;
212 return (unsigned char)*p
++;
215 int argument_macro_input::peek()
219 return (unsigned char)*ap
;
224 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
226 if (i
< argc
&& argv
[i
] != 0 && argv
[i
][0] != '\0') {
228 return (unsigned char)*ap
;
233 return (unsigned char)*p
;
237 static input
*current_input
;
240 static void push(input
*);
242 static int get_char();
243 static int peek_char();
244 static int get_location(const char **fnp
, int *lnp
);
245 static void push_back(unsigned char c
, int was_bol
= 0);
249 input
*input_stack::current_input
= 0;
250 int input_stack::bol_flag
= 0;
252 inline int input_stack::bol()
257 void input_stack::clear()
259 while (current_input
!= 0) {
260 input
*tem
= current_input
;
261 current_input
= current_input
->next
;
267 void input_stack::push(input
*in
)
269 in
->next
= current_input
;
273 void lex_init(input
*top
)
275 input_stack::clear();
276 input_stack::push(top
);
281 while (input_stack::get_char() != EOF
)
285 int input_stack::get_char()
287 while (current_input
!= 0) {
288 int c
= current_input
->get();
290 bol_flag
= c
== '\n';
293 // don't pop the top-level input off the stack
294 if (current_input
->next
== 0)
296 input
*tem
= current_input
;
297 current_input
= current_input
->next
;
303 int input_stack::peek_char()
305 while (current_input
!= 0) {
306 int c
= current_input
->peek();
309 if (current_input
->next
== 0)
311 input
*tem
= current_input
;
312 current_input
= current_input
->next
;
318 class char_input
: public input
{
326 char_input::char_input(int n
) : c((unsigned char)n
)
330 int char_input::get()
337 int char_input::peek()
342 void input_stack::push_back(unsigned char c
, int was_bol
)
344 push(new char_input(c
));
348 int input_stack::get_location(const char **fnp
, int *lnp
)
350 for (input
*p
= current_input
; p
; p
= p
->next
)
351 if (p
->get_location(fnp
, lnp
))
356 string context_buffer
;
362 void interpolate_macro_with_args(const char *body
)
367 for (i
= 0; i
< 9; i
++)
371 enum { NORMAL
, IN_STRING
, IN_STRING_QUOTED
} state
= NORMAL
;
373 token_buffer
.clear();
375 c
= input_stack::get_char();
377 lex_error("end of input while scanning macro arguments");
380 if (state
== NORMAL
&& level
== 0 && (c
== ',' || c
== ')')) {
381 if (token_buffer
.length() > 0) {
382 token_buffer
+= '\0';
383 argv
[argc
] = strsave(token_buffer
.contents());
385 // for `foo()', argc = 0
386 if (argc
> 0 || c
!= ')' || i
> 0)
390 token_buffer
+= char(c
);
404 state
= IN_STRING_QUOTED
;
406 case IN_STRING_QUOTED
:
411 } while (c
!= ')' && c
!= EOF
);
412 input_stack::push(new argument_macro_input(body
, argc
, argv
));
415 static int docmp(const char *s1
, int n1
, const char *s2
, int n2
)
418 int r
= memcmp(s1
, s2
, n1
);
422 int r
= memcmp(s1
, s2
, n2
);
426 return memcmp(s1
, s2
, n1
);
429 int lookup_keyword(const char *str
, int len
)
431 static struct keyword
{
437 { "aligned", ALIGNED
},
444 { "between", BETWEEN
},
445 { "bottom", BOTTOM
},
449 { "center", CENTER
},
451 { "circle", CIRCLE
},
452 { "color", COLORED
},
453 { "colored", COLORED
},
454 { "colour", COLORED
},
455 { "coloured", COLORED
},
456 { "command", COMMAND
},
460 { "dashed", DASHED
},
461 { "define", DEFINE
},
462 { "diam", DIAMETER
},
463 { "diameter", DIAMETER
},
465 { "dotted", DOTTED
},
468 { "ellipse", ELLIPSE
},
472 { "figname", FIGNAME
},
477 { "height", HEIGHT
},
481 { "invis", INVISIBLE
},
482 { "invisible", INVISIBLE
},
494 { "outline", OUTLINED
},
495 { "outlined", OUTLINED
},
499 { "radius", RADIUS
},
506 { "shaded", SHADED
},
510 { "spline", SPLINE
},
511 { "sprintf", SPRINTF
},
517 { "thick", THICKNESS
},
518 { "thickness", THICKNESS
},
533 const keyword
*start
= table
;
534 const keyword
*end
= table
+ sizeof(table
)/sizeof(table
[0]);
535 while (start
< end
) {
536 // start <= target < end
537 const keyword
*mid
= start
+ (end
- start
)/2;
539 int cmp
= docmp(str
, len
, mid
->name
, strlen(mid
->name
));
550 int get_token_after_dot(int c
)
552 // get_token deals with the case where c is a digit
555 input_stack::get_char();
556 c
= input_stack::peek_char();
558 input_stack::get_char();
559 context_buffer
= ".ht";
563 input_stack::get_char();
564 c
= input_stack::peek_char();
566 input_stack::get_char();
567 c
= input_stack::peek_char();
569 input_stack::get_char();
570 c
= input_stack::peek_char();
572 input_stack::get_char();
573 c
= input_stack::peek_char();
575 input_stack::get_char();
576 context_buffer
= ".height";
579 input_stack::push_back('h');
581 input_stack::push_back('g');
583 input_stack::push_back('i');
585 input_stack::push_back('e');
587 input_stack::push_back('h');
590 input_stack::get_char();
591 context_buffer
= ".x";
594 input_stack::get_char();
595 context_buffer
= ".y";
598 input_stack::get_char();
599 c
= input_stack::peek_char();
601 input_stack::get_char();
602 c
= input_stack::peek_char();
604 input_stack::get_char();
605 c
= input_stack::peek_char();
607 input_stack::get_char();
608 c
= input_stack::peek_char();
610 input_stack::get_char();
611 c
= input_stack::peek_char();
613 input_stack::get_char();
614 context_buffer
= ".center";
617 input_stack::push_back('e');
619 input_stack::push_back('t');
621 input_stack::push_back('n');
623 input_stack::push_back('e');
625 context_buffer
= ".c";
628 input_stack::get_char();
629 c
= input_stack::peek_char();
631 input_stack::get_char();
632 context_buffer
= ".ne";
636 input_stack::get_char();
637 context_buffer
= ".nw";
641 context_buffer
= ".n";
646 input_stack::get_char();
647 c
= input_stack::peek_char();
649 input_stack::get_char();
650 c
= input_stack::peek_char();
652 input_stack::get_char();
653 context_buffer
= ".end";
656 input_stack::push_back('n');
657 context_buffer
= ".e";
660 context_buffer
= ".e";
663 input_stack::get_char();
664 c
= input_stack::peek_char();
666 input_stack::get_char();
667 c
= input_stack::peek_char();
669 input_stack::get_char();
670 c
= input_stack::peek_char();
672 input_stack::get_char();
673 c
= input_stack::peek_char();
675 input_stack::get_char();
676 context_buffer
= ".width";
679 input_stack::push_back('t');
681 context_buffer
= ".wid";
684 input_stack::push_back('i');
686 context_buffer
= ".w";
689 input_stack::get_char();
690 c
= input_stack::peek_char();
692 input_stack::get_char();
693 context_buffer
= ".se";
697 input_stack::get_char();
698 context_buffer
= ".sw";
703 input_stack::get_char();
704 c
= input_stack::peek_char();
706 input_stack::get_char();
707 c
= input_stack::peek_char();
709 input_stack::get_char();
710 c
= input_stack::peek_char();
712 input_stack::get_char();
713 context_buffer
= ".start";
716 input_stack::push_back('r');
718 input_stack::push_back('a');
720 input_stack::push_back('t');
722 context_buffer
= ".s";
727 input_stack::get_char();
728 c
= input_stack::peek_char();
730 input_stack::get_char();
731 c
= input_stack::peek_char();
733 input_stack::get_char();
734 context_buffer
= ".top";
737 input_stack::push_back('o');
739 context_buffer
= ".t";
742 input_stack::get_char();
743 c
= input_stack::peek_char();
745 input_stack::get_char();
746 c
= input_stack::peek_char();
748 input_stack::get_char();
749 c
= input_stack::peek_char();
751 input_stack::get_char();
752 context_buffer
= ".left";
755 input_stack::push_back('f');
757 input_stack::push_back('e');
759 context_buffer
= ".l";
762 input_stack::get_char();
763 c
= input_stack::peek_char();
765 input_stack::get_char();
766 c
= input_stack::peek_char();
768 input_stack::get_char();
769 context_buffer
= ".rad";
772 input_stack::push_back('a');
775 input_stack::get_char();
776 c
= input_stack::peek_char();
778 input_stack::get_char();
779 c
= input_stack::peek_char();
781 input_stack::get_char();
782 c
= input_stack::peek_char();
784 input_stack::get_char();
785 context_buffer
= ".right";
788 input_stack::push_back('h');
790 input_stack::push_back('g');
792 input_stack::push_back('i');
794 context_buffer
= ".r";
797 input_stack::get_char();
798 c
= input_stack::peek_char();
800 input_stack::get_char();
801 c
= input_stack::peek_char();
803 input_stack::get_char();
804 c
= input_stack::peek_char();
806 input_stack::get_char();
807 c
= input_stack::peek_char();
809 input_stack::get_char();
810 c
= input_stack::peek_char();
812 input_stack::get_char();
813 context_buffer
= ".bottom";
816 input_stack::push_back('o');
818 input_stack::push_back('t');
820 context_buffer
= ".bot";
823 input_stack::push_back('o');
825 context_buffer
= ".b";
828 context_buffer
= '.';
833 int get_token(int lookup_flag
)
835 context_buffer
.clear();
838 int bol
= input_stack::bol();
839 int c
= input_stack::get_char();
840 if (bol
&& c
== command_char
) {
841 token_buffer
.clear();
843 // the newline is not part of the token
845 c
= input_stack::peek_char();
846 if (c
== EOF
|| c
== '\n')
848 input_stack::get_char();
849 token_buffer
+= char(c
);
851 context_buffer
= token_buffer
;
862 int d
= input_stack::peek_char();
864 context_buffer
= '\\';
867 input_stack::get_char();
872 c
= input_stack::get_char();
873 } while (c
!= '\n' && c
!= EOF
);
875 context_buffer
= '\n';
878 context_buffer
= '"';
879 token_buffer
.clear();
881 c
= input_stack::get_char();
883 context_buffer
+= '\\';
884 c
= input_stack::peek_char();
886 input_stack::get_char();
888 context_buffer
+= '"';
891 token_buffer
+= '\\';
893 else if (c
== '\n') {
894 error("newline in string");
898 error("missing `\"'");
902 context_buffer
+= '"';
906 context_buffer
+= char(c
);
907 token_buffer
+= char(c
);
925 if (n
> (INT_MAX
- 9)/10) {
931 context_buffer
+= char(c
);
932 c
= input_stack::peek_char();
933 if (c
== EOF
|| !csdigit(c
))
935 c
= input_stack::get_char();
940 token_double
*= 10.0;
941 token_double
+= c
- '0';
942 context_buffer
+= char(c
);
943 c
= input_stack::peek_char();
944 if (c
== EOF
|| !csdigit(c
))
946 c
= input_stack::get_char();
948 // if somebody asks for 1000000000000th, we will silently
949 // give them INT_MAXth
950 double temp
= token_double
; // work around gas 1.34/sparc bug
951 if (token_double
> INT_MAX
)
960 context_buffer
+= char(c
);
961 input_stack::get_char();
965 context_buffer
+= '.';
966 input_stack::get_char();
970 c
= input_stack::peek_char();
971 if (c
== EOF
|| !csdigit(c
))
973 input_stack::get_char();
974 context_buffer
+= char(c
);
977 token_double
+= factor
*(c
- '0');
979 if (c
!= 'e' && c
!= 'E') {
980 if (c
== 'i' || c
== 'I') {
981 context_buffer
+= char(c
);
982 input_stack::get_char();
992 input_stack::get_char();
993 c
= input_stack::peek_char();
995 if (c
== '+' || c
== '-') {
997 input_stack::get_char();
998 c
= input_stack::peek_char();
999 if (c
== EOF
|| !csdigit(c
)) {
1000 input_stack::push_back(sign
);
1001 input_stack::push_back(echar
);
1004 context_buffer
+= char(echar
);
1005 context_buffer
+= char(sign
);
1008 if (c
== EOF
|| !csdigit(c
)) {
1009 input_stack::push_back(echar
);
1012 context_buffer
+= char(echar
);
1014 input_stack::get_char();
1015 context_buffer
+= char(c
);
1018 c
= input_stack::peek_char();
1019 if (c
== EOF
|| !csdigit(c
))
1021 input_stack::get_char();
1022 context_buffer
+= char(c
);
1023 n
= n
*10 + (c
- '0');
1027 if (c
== 'i' || c
== 'I') {
1028 context_buffer
+= char(c
);
1029 input_stack::get_char();
1031 token_double
*= pow(10.0, n
);
1035 input_stack::get_char();
1036 c
= input_stack::peek_char();
1038 input_stack::get_char();
1040 context_buffer
+= "nd";
1043 input_stack::push_back('n');
1046 input_stack::get_char();
1047 c
= input_stack::peek_char();
1049 input_stack::get_char();
1051 context_buffer
+= "rd";
1054 input_stack::push_back('r');
1057 input_stack::get_char();
1058 c
= input_stack::peek_char();
1060 input_stack::get_char();
1062 context_buffer
+= "th";
1065 input_stack::push_back('t');
1068 input_stack::get_char();
1069 c
= input_stack::peek_char();
1071 input_stack::get_char();
1073 context_buffer
+= "st";
1076 input_stack::push_back('s');
1084 c
= input_stack::peek_char();
1086 input_stack::get_char();
1087 c
= input_stack::peek_char();
1089 input_stack::get_char();
1090 context_buffer
= "'th";
1094 input_stack::push_back('t');
1096 context_buffer
= "'";
1101 c
= input_stack::peek_char();
1102 if (c
!= EOF
&& csdigit(c
)) {
1105 context_buffer
= '.';
1108 return get_token_after_dot(c
);
1111 c
= input_stack::peek_char();
1113 input_stack::get_char();
1114 c
= input_stack::peek_char();
1116 input_stack::get_char();
1117 context_buffer
= "<->";
1118 return DOUBLE_ARROW_HEAD
;
1120 context_buffer
= "<-";
1121 return LEFT_ARROW_HEAD
;
1123 else if (c
== '=') {
1124 input_stack::get_char();
1125 context_buffer
= "<=";
1128 context_buffer
= "<";
1131 c
= input_stack::peek_char();
1133 input_stack::get_char();
1134 context_buffer
= "->";
1135 return RIGHT_ARROW_HEAD
;
1137 context_buffer
= "-";
1140 c
= input_stack::peek_char();
1142 input_stack::get_char();
1143 context_buffer
= "!=";
1146 context_buffer
= "!";
1149 c
= input_stack::peek_char();
1151 input_stack::get_char();
1152 context_buffer
= ">=";
1153 return GREATEREQUAL
;
1155 context_buffer
= ">";
1158 c
= input_stack::peek_char();
1160 input_stack::get_char();
1161 context_buffer
= "==";
1164 context_buffer
= "=";
1167 c
= input_stack::peek_char();
1169 input_stack::get_char();
1170 context_buffer
= "&&";
1173 context_buffer
= "&";
1176 c
= input_stack::peek_char();
1178 input_stack::get_char();
1179 context_buffer
= "||";
1182 context_buffer
= "|";
1185 if (c
!= EOF
&& csalpha(c
)) {
1186 token_buffer
.clear();
1189 c
= input_stack::peek_char();
1190 if (c
== EOF
|| (!csalnum(c
) && c
!= '_'))
1192 input_stack::get_char();
1193 token_buffer
+= char(c
);
1195 int tok
= lookup_keyword(token_buffer
.contents(),
1196 token_buffer
.length());
1198 context_buffer
= token_buffer
;
1203 token_buffer
+= '\0';
1204 def
= macro_table
.lookup(token_buffer
.contents());
1205 token_buffer
.set_length(token_buffer
.length() - 1);
1208 input_stack::get_char();
1209 interpolate_macro_with_args(def
);
1212 input_stack::push(new macro_input(def
));
1216 context_buffer
= token_buffer
;
1217 if (csupper(token_buffer
[0]))
1224 context_buffer
= char(c
);
1225 return (unsigned char)c
;
1234 token_buffer
.clear();
1235 int c
= input_stack::get_char();
1236 while (c
== ' ' || c
== '\t' || c
== '\n')
1237 c
= input_stack::get_char();
1239 lex_error("missing delimiter");
1242 context_buffer
= char(c
);
1243 int had_newline
= 0;
1246 enum { NORMAL
, IN_STRING
, IN_STRING_QUOTED
, DELIM_END
} state
= NORMAL
;
1248 c
= input_stack::get_char();
1250 lex_error("missing closing delimiter");
1255 else if (!had_newline
)
1256 context_buffer
+= char(c
);
1279 case IN_STRING_QUOTED
:
1286 if (c
== '"' || c
== '\n')
1289 state
= IN_STRING_QUOTED
;
1292 // This case it just to shut cfront 2.0 up.
1296 if (state
== DELIM_END
)
1305 int t
= get_token(0); // do not expand what we are defining
1306 if (t
!= VARIABLE
&& t
!= LABEL
) {
1307 lex_error("can only define variable or placename");
1310 token_buffer
+= '\0';
1311 string nm
= token_buffer
;
1312 const char *name
= nm
.contents();
1313 if (!get_delimited())
1315 token_buffer
+= '\0';
1316 macro_table
.define(name
, strsave(token_buffer
.contents()));
1321 int t
= get_token(0); // do not expand what we are undefining
1322 if (t
!= VARIABLE
&& t
!= LABEL
) {
1323 lex_error("can only define variable or placename");
1326 token_buffer
+= '\0';
1327 macro_table
.define(token_buffer
.contents(), 0);
1331 class for_input
: public input
{
1336 int by_is_multiplicative
;
1341 for_input(char *, double, double, int, double, char *);
1347 for_input::for_input(char *vr
, double f
, double t
,
1348 int bim
, double b
, char *bd
)
1349 : var(vr
), body(bd
), from(f
), to(t
), by_is_multiplicative(bim
), by(b
),
1350 p(body
), done_newline(0)
1354 for_input::~for_input()
1360 int for_input::get()
1366 return (unsigned char)*p
++;
1367 if (!done_newline
) {
1372 if (!lookup_variable(var
, &val
)) {
1373 lex_error("body of `for' terminated enclosing block");
1376 if (by_is_multiplicative
)
1380 define_variable(var
, val
);
1381 if ((from
<= to
&& val
> to
)
1382 || (from
>= to
&& val
< to
)) {
1391 int for_input::peek()
1396 return (unsigned char)*p
;
1400 if (!lookup_variable(var
, &val
))
1402 if (by_is_multiplicative
) {
1407 if ((from
<= to
&& val
+ by
> to
)
1408 || (from
>= to
&& val
+ by
< to
))
1413 return (unsigned char)*body
;
1416 void do_for(char *var
, double from
, double to
, int by_is_multiplicative
,
1417 double by
, char *body
)
1419 define_variable(var
, from
);
1420 if ((by_is_multiplicative
&& by
<= 0)
1421 || (by
> 0 && from
> to
)
1422 || (by
< 0 && from
< to
))
1424 input_stack::push(new for_input(var
, from
, to
,
1425 by_is_multiplicative
, by
, body
));
1429 void do_copy(const char *filename
)
1432 FILE *fp
= fopen(filename
, "r");
1434 lex_error("can't open `%1': %2", filename
, strerror(errno
));
1437 input_stack::push(new file_input(fp
, filename
));
1440 class copy_thru_input
: public input
{
1450 virtual int inget() = 0;
1452 copy_thru_input(const char *b
, const char *u
);
1458 class copy_file_thru_input
: public copy_thru_input
{
1461 copy_file_thru_input(input
*, const char *b
, const char *u
);
1462 ~copy_file_thru_input();
1466 copy_file_thru_input::copy_file_thru_input(input
*i
, const char *b
,
1468 : copy_thru_input(b
, u
), in(i
)
1472 copy_file_thru_input::~copy_file_thru_input()
1477 int copy_file_thru_input::inget()
1485 class copy_rest_thru_input
: public copy_thru_input
{
1487 copy_rest_thru_input(const char *, const char *u
);
1491 copy_rest_thru_input::copy_rest_thru_input(const char *b
, const char *u
)
1492 : copy_thru_input(b
, u
)
1496 int copy_rest_thru_input::inget()
1499 int c
= next
->get();
1502 if (next
->next
== 0)
1512 copy_thru_input::copy_thru_input(const char *b
, const char *u
)
1516 body
= process_body(b
);
1522 copy_thru_input::~copy_thru_input()
1528 int copy_thru_input::get()
1532 return (unsigned char)*ap
++;
1545 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
1546 int i
= *p
++ - ARG1
;
1547 if (i
< argc
&& line
[argv
[i
]] != '\0') {
1548 ap
= line
.contents() + argv
[i
];
1549 return (unsigned char)*ap
++;
1553 return (unsigned char)*p
++;
1558 int copy_thru_input::peek()
1562 return (unsigned char)*ap
;
1573 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
1574 int i
= *p
++ - ARG1
;
1575 if (i
< argc
&& line
[argv
[i
]] != '\0') {
1576 ap
= line
.contents() + argv
[i
];
1577 return (unsigned char)*ap
;
1581 return (unsigned char)*p
;
1586 int copy_thru_input::get_line()
1596 if (c
== EOF
|| c
== '\n')
1601 } while (c
!= '\n' && c
!= EOF
);
1604 argv
[argc
++] = line
.length();
1608 } while (c
!= ' ' && c
!= '\n');
1611 if (until
!= 0 && argc
> 0 && strcmp(&line
[argv
[0]], until
) == 0) {
1615 return argc
> 0 || c
== '\n';
1618 class simple_file_input
: public input
{
1619 const char *filename
;
1623 simple_file_input(FILE *, const char *);
1624 ~simple_file_input();
1627 int get_location(const char **, int *);
1630 simple_file_input::simple_file_input(FILE *p
, const char *s
)
1631 : filename(s
), lineno(1), fp(p
)
1635 simple_file_input::~simple_file_input()
1637 // don't delete the filename
1641 int simple_file_input::get()
1644 while (invalid_input_char(c
)) {
1645 error("invalid input character code %1", c
);
1653 int simple_file_input::peek()
1656 while (invalid_input_char(c
)) {
1657 error("invalid input character code %1", c
);
1665 int simple_file_input::get_location(const char **fnp
, int *lnp
)
1673 void copy_file_thru(const char *filename
, const char *body
, const char *until
)
1676 FILE *fp
= fopen(filename
, "r");
1678 lex_error("can't open `%1': %2", filename
, strerror(errno
));
1681 input
*in
= new copy_file_thru_input(new simple_file_input(fp
, filename
),
1683 input_stack::push(in
);
1686 void copy_rest_thru(const char *body
, const char *until
)
1688 input_stack::push(new copy_rest_thru_input(body
, until
));
1691 void push_body(const char *s
)
1693 input_stack::push(new char_input('\n'));
1694 input_stack::push(new macro_input(s
));
1699 char *get_thru_arg()
1701 int c
= input_stack::peek_char();
1703 input_stack::get_char();
1704 c
= input_stack::peek_char();
1706 if (c
!= EOF
&& csalpha(c
)) {
1707 // looks like a macro
1708 input_stack::get_char();
1711 c
= input_stack::peek_char();
1712 if (c
== EOF
|| (!csalnum(c
) && c
!= '_'))
1714 input_stack::get_char();
1715 token_buffer
+= char(c
);
1717 context_buffer
= token_buffer
;
1718 token_buffer
+= '\0';
1719 char *def
= macro_table
.lookup(token_buffer
.contents());
1721 return strsave(def
);
1722 // I guess it wasn't a macro after all; so push the macro name back.
1723 // -2 because we added a '\0'
1724 for (int i
= token_buffer
.length() - 2; i
>= 0; i
--)
1725 input_stack::push_back(token_buffer
[i
]);
1727 if (get_delimited()) {
1728 token_buffer
+= '\0';
1729 return strsave(token_buffer
.contents());
1735 int lookahead_token
= -1;
1736 string old_context_buffer
;
1740 if (lookahead_token
== -1) {
1741 old_context_buffer
= context_buffer
;
1742 lookahead_token
= get_token(1);
1749 assert(lookahead_token
== -1);
1750 if (delim_flag
== 2) {
1751 if ((yylval
.str
= get_thru_arg()) != 0)
1757 if (get_delimited()) {
1758 token_buffer
+= '\0';
1759 yylval
.str
= strsave(token_buffer
.contents());
1768 if (lookahead_token
>= 0) {
1769 t
= lookahead_token
;
1770 lookahead_token
= -1;
1786 yylval
.n
= token_int
;
1789 yylval
.x
= token_double
;
1793 token_buffer
+= '\0';
1794 if (!input_stack::get_location(&yylval
.lstr
.filename
,
1795 &yylval
.lstr
.lineno
)) {
1796 yylval
.lstr
.filename
= 0;
1797 yylval
.lstr
.lineno
= -1;
1799 yylval
.lstr
.str
= strsave(token_buffer
.contents());
1803 token_buffer
+= '\0';
1804 yylval
.str
= strsave(token_buffer
.contents());
1807 // change LEFT to LEFT_CORNER when followed by OF
1808 old_context_buffer
= context_buffer
;
1809 lookahead_token
= get_token(1);
1810 if (lookahead_token
== OF
)
1815 // change RIGHT to RIGHT_CORNER when followed by OF
1816 old_context_buffer
= context_buffer
;
1817 lookahead_token
= get_token(1);
1818 if (lookahead_token
== OF
)
1819 return RIGHT_CORNER
;
1823 // recognise UPPER only before LEFT or RIGHT
1824 old_context_buffer
= context_buffer
;
1825 lookahead_token
= get_token(1);
1826 if (lookahead_token
!= LEFT
&& lookahead_token
!= RIGHT
) {
1827 yylval
.str
= strsave("upper");
1833 // recognise LOWER only before LEFT or RIGHT
1834 old_context_buffer
= context_buffer
;
1835 lookahead_token
= get_token(1);
1836 if (lookahead_token
!= LEFT
&& lookahead_token
!= RIGHT
) {
1837 yylval
.str
= strsave("lower");
1843 // recognise NORTH only before OF
1844 old_context_buffer
= context_buffer
;
1845 lookahead_token
= get_token(1);
1846 if (lookahead_token
!= OF
) {
1847 yylval
.str
= strsave("north");
1853 // recognise SOUTH only before OF
1854 old_context_buffer
= context_buffer
;
1855 lookahead_token
= get_token(1);
1856 if (lookahead_token
!= OF
) {
1857 yylval
.str
= strsave("south");
1863 // recognise EAST only before OF
1864 old_context_buffer
= context_buffer
;
1865 lookahead_token
= get_token(1);
1866 if (lookahead_token
!= OF
) {
1867 yylval
.str
= strsave("east");
1873 // recognise WEST only before OF
1874 old_context_buffer
= context_buffer
;
1875 lookahead_token
= get_token(1);
1876 if (lookahead_token
!= OF
) {
1877 yylval
.str
= strsave("west");
1883 // recognise TOP only before OF
1884 old_context_buffer
= context_buffer
;
1885 lookahead_token
= get_token(1);
1886 if (lookahead_token
!= OF
) {
1887 yylval
.str
= strsave("top");
1893 // recognise BOTTOM only before OF
1894 old_context_buffer
= context_buffer
;
1895 lookahead_token
= get_token(1);
1896 if (lookahead_token
!= OF
) {
1897 yylval
.str
= strsave("bottom");
1903 // recognise CENTER only before OF
1904 old_context_buffer
= context_buffer
;
1905 lookahead_token
= get_token(1);
1906 if (lookahead_token
!= OF
) {
1907 yylval
.str
= strsave("center");
1913 // recognise START only before OF
1914 old_context_buffer
= context_buffer
;
1915 lookahead_token
= get_token(1);
1916 if (lookahead_token
!= OF
) {
1917 yylval
.str
= strsave("start");
1923 // recognise END only before OF
1924 old_context_buffer
= context_buffer
;
1925 lookahead_token
= get_token(1);
1926 if (lookahead_token
!= OF
) {
1927 yylval
.str
= strsave("end");
1938 void lex_error(const char *message
,
1943 const char *filename
;
1945 if (!input_stack::get_location(&filename
, &lineno
))
1946 error(message
, arg1
, arg2
, arg3
);
1948 error_with_file_and_line(filename
, lineno
, message
, arg1
, arg2
, arg3
);
1951 void lex_warning(const char *message
,
1956 const char *filename
;
1958 if (!input_stack::get_location(&filename
, &lineno
))
1959 warning(message
, arg1
, arg2
, arg3
);
1961 warning_with_file_and_line(filename
, lineno
, message
, arg1
, arg2
, arg3
);
1964 void yyerror(const char *s
)
1966 const char *filename
;
1968 const char *context
= 0;
1969 if (lookahead_token
== -1) {
1970 if (context_buffer
.length() > 0) {
1971 context_buffer
+= '\0';
1972 context
= context_buffer
.contents();
1976 if (old_context_buffer
.length() > 0) {
1977 old_context_buffer
+= '\0';
1978 context
= old_context_buffer
.contents();
1981 if (!input_stack::get_location(&filename
, &lineno
)) {
1983 if (context
[0] == '\n' && context
[1] == '\0')
1984 error("%1 before newline", s
);
1986 error("%1 before `%2'", s
, context
);
1989 error("%1 at end of picture", s
);
1993 if (context
[0] == '\n' && context
[1] == '\0')
1994 error_with_file_and_line(filename
, lineno
, "%1 before newline", s
);
1996 error_with_file_and_line(filename
, lineno
, "%1 before `%2'",
2000 error_with_file_and_line(filename
, lineno
, "%1 at end of picture", s
);