No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / groff / src / preproc / pic / pic.y
blob334769e534b033c9c18657d9919c207c6316eb70
1 /* $NetBSD: pic.y,v 1.1.1.4 2006/02/06 18:14:22 wiz Exp $ */
3 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 for more details.
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23 #include "pic.h"
24 #include "ptable.h"
25 #include "object.h"
27 extern int delim_flag;
28 extern void copy_rest_thru(const char *, const char *);
29 extern void copy_file_thru(const char *, const char *, const char *);
30 extern void push_body(const char *);
31 extern void do_for(char *var, double from, double to,
32 int by_is_multiplicative, double by, char *body);
33 extern void do_lookahead();
35 /* Maximum number of characters produced by printf("%g") */
36 #define GDIGITS 14
38 int yylex();
39 void yyerror(const char *);
41 void reset(const char *nm);
42 void reset_all();
44 place *lookup_label(const char *);
45 void define_label(const char *label, const place *pl);
47 direction current_direction;
48 position current_position;
50 implement_ptable(place)
52 PTABLE(place) top_table;
54 PTABLE(place) *current_table = &top_table;
55 saved_state *current_saved_state = 0;
57 object_list olist;
59 const char *ordinal_postfix(int n);
60 const char *object_type_name(object_type type);
61 char *format_number(const char *form, double n);
62 char *do_sprintf(const char *form, const double *v, int nv);
67 %union {
68 char *str;
69 int n;
70 double x;
71 struct { double x, y; } pair;
72 struct { double x; char *body; } if_data;
73 struct { char *str; const char *filename; int lineno; } lstr;
74 struct { double *v; int nv; int maxv; } dv;
75 struct { double val; int is_multiplicative; } by;
76 place pl;
77 object *obj;
78 corner crn;
79 path *pth;
80 object_spec *spec;
81 saved_state *pstate;
82 graphics_state state;
83 object_type obtype;
86 %token <str> LABEL
87 %token <str> VARIABLE
88 %token <x> NUMBER
89 %token <lstr> TEXT
90 %token <lstr> COMMAND_LINE
91 %token <str> DELIMITED
92 %token <n> ORDINAL
93 %token TH
94 %token LEFT_ARROW_HEAD
95 %token RIGHT_ARROW_HEAD
96 %token DOUBLE_ARROW_HEAD
97 %token LAST
98 %token UP
99 %token DOWN
100 %token LEFT
101 %token RIGHT
102 %token BOX
103 %token CIRCLE
104 %token ELLIPSE
105 %token ARC
106 %token LINE
107 %token ARROW
108 %token MOVE
109 %token SPLINE
110 %token HEIGHT
111 %token RADIUS
112 %token FIGNAME
113 %token WIDTH
114 %token DIAMETER
115 %token UP
116 %token DOWN
117 %token RIGHT
118 %token LEFT
119 %token FROM
120 %token TO
121 %token AT
122 %token WITH
123 %token BY
124 %token THEN
125 %token SOLID
126 %token DOTTED
127 %token DASHED
128 %token CHOP
129 %token SAME
130 %token INVISIBLE
131 %token LJUST
132 %token RJUST
133 %token ABOVE
134 %token BELOW
135 %token OF
136 %token THE
137 %token WAY
138 %token BETWEEN
139 %token AND
140 %token HERE
141 %token DOT_N
142 %token DOT_E
143 %token DOT_W
144 %token DOT_S
145 %token DOT_NE
146 %token DOT_SE
147 %token DOT_NW
148 %token DOT_SW
149 %token DOT_C
150 %token DOT_START
151 %token DOT_END
152 %token DOT_X
153 %token DOT_Y
154 %token DOT_HT
155 %token DOT_WID
156 %token DOT_RAD
157 %token SIN
158 %token COS
159 %token ATAN2
160 %token LOG
161 %token EXP
162 %token SQRT
163 %token K_MAX
164 %token K_MIN
165 %token INT
166 %token RAND
167 %token SRAND
168 %token COPY
169 %token THRU
170 %token TOP
171 %token BOTTOM
172 %token UPPER
173 %token LOWER
174 %token SH
175 %token PRINT
176 %token CW
177 %token CCW
178 %token FOR
179 %token DO
180 %token IF
181 %token ELSE
182 %token ANDAND
183 %token OROR
184 %token NOTEQUAL
185 %token EQUALEQUAL
186 %token LESSEQUAL
187 %token GREATEREQUAL
188 %token LEFT_CORNER
189 %token RIGHT_CORNER
190 %token NORTH
191 %token SOUTH
192 %token EAST
193 %token WEST
194 %token CENTER
195 %token END
196 %token START
197 %token RESET
198 %token UNTIL
199 %token PLOT
200 %token THICKNESS
201 %token FILL
202 %token COLORED
203 %token OUTLINED
204 %token SHADED
205 %token ALIGNED
206 %token SPRINTF
207 %token COMMAND
209 %token DEFINE
210 %token UNDEF
212 %left '.'
214 /* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
215 %left PLOT
216 %left TEXT SPRINTF
218 /* give text adjustments higher precedence than TEXT, so that
219 box "foo" above ljust == box ("foo" above ljust)
222 %left LJUST RJUST ABOVE BELOW
224 %left LEFT RIGHT
225 /* Give attributes that take an optional expression a higher
226 precedence than left and right, so that eg `line chop left'
227 parses properly. */
228 %left CHOP SOLID DASHED DOTTED UP DOWN FILL COLORED OUTLINED
229 %left LABEL
231 %left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST
232 %left ORDINAL HERE '`'
234 %left BOX CIRCLE ELLIPSE ARC LINE ARROW SPLINE '['
236 /* these need to be lower than '-' */
237 %left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
239 /* these must have higher precedence than CHOP so that `label %prec CHOP'
240 works */
241 %left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
242 %left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
243 %left UPPER LOWER NORTH SOUTH EAST WEST CENTER START END
245 %left ','
246 %left OROR
247 %left ANDAND
248 %left EQUALEQUAL NOTEQUAL
249 %left '<' '>' LESSEQUAL GREATEREQUAL
251 %left BETWEEN OF
252 %left AND
254 %left '+' '-'
255 %left '*' '/' '%'
256 %right '!'
257 %right '^'
259 %type <x> expr any_expr text_expr
260 %type <by> optional_by
261 %type <pair> expr_pair position_not_place
262 %type <if_data> simple_if
263 %type <obj> nth_primitive
264 %type <crn> corner
265 %type <pth> path label_path relative_path
266 %type <pl> place label element element_list middle_element_list
267 %type <spec> object_spec
268 %type <pair> position
269 %type <obtype> object_type
270 %type <n> optional_ordinal_last ordinal
271 %type <str> macro_name until
272 %type <dv> sprintf_args
273 %type <lstr> text print_args print_arg
277 top:
278 optional_separator
279 | element_list
281 if (olist.head)
282 print_picture(olist.head);
287 element_list:
288 optional_separator middle_element_list optional_separator
289 { $$ = $2; }
292 middle_element_list:
293 element
294 { $$ = $1; }
295 | middle_element_list separator element
296 { $$ = $1; }
299 optional_separator:
300 /* empty */
301 | separator
304 separator:
306 | separator ';'
309 placeless_element:
310 FIGNAME '=' macro_name
312 a_delete graphname;
313 graphname = new char[strlen($3) + 1];
314 strcpy(graphname, $3);
315 a_delete $3;
318 VARIABLE '=' any_expr
320 define_variable($1, $3);
321 a_delete $1;
323 | VARIABLE ':' '=' any_expr
325 place *p = lookup_label($1);
326 if (!p) {
327 lex_error("variable `%1' not defined", $1);
328 YYABORT;
330 p->obj = 0;
331 p->x = $4;
332 p->y = 0.0;
333 a_delete $1;
335 | UP
336 { current_direction = UP_DIRECTION; }
337 | DOWN
338 { current_direction = DOWN_DIRECTION; }
339 | LEFT
340 { current_direction = LEFT_DIRECTION; }
341 | RIGHT
342 { current_direction = RIGHT_DIRECTION; }
343 | COMMAND_LINE
345 olist.append(make_command_object($1.str, $1.filename,
346 $1.lineno));
348 | COMMAND print_args
350 olist.append(make_command_object($2.str, $2.filename,
351 $2.lineno));
353 | PRINT print_args
355 fprintf(stderr, "%s\n", $2.str);
356 a_delete $2.str;
357 fflush(stderr);
359 | SH
360 { delim_flag = 1; }
361 DELIMITED
363 delim_flag = 0;
364 if (safer_flag)
365 lex_error("unsafe to run command `%1'", $3);
366 else
367 system($3);
368 a_delete $3;
370 | COPY TEXT
372 if (yychar < 0)
373 do_lookahead();
374 do_copy($2.str);
375 // do not delete the filename
377 | COPY TEXT THRU
378 { delim_flag = 2; }
379 DELIMITED
380 { delim_flag = 0; }
381 until
383 if (yychar < 0)
384 do_lookahead();
385 copy_file_thru($2.str, $5, $7);
386 // do not delete the filename
387 a_delete $5;
388 a_delete $7;
390 | COPY THRU
391 { delim_flag = 2; }
392 DELIMITED
393 { delim_flag = 0; }
394 until
396 if (yychar < 0)
397 do_lookahead();
398 copy_rest_thru($4, $6);
399 a_delete $4;
400 a_delete $6;
402 | FOR VARIABLE '=' expr TO expr optional_by DO
403 { delim_flag = 1; }
404 DELIMITED
406 delim_flag = 0;
407 if (yychar < 0)
408 do_lookahead();
409 do_for($2, $4, $6, $7.is_multiplicative, $7.val, $10);
411 | simple_if
413 if (yychar < 0)
414 do_lookahead();
415 if ($1.x != 0.0)
416 push_body($1.body);
417 a_delete $1.body;
419 | simple_if ELSE
420 { delim_flag = 1; }
421 DELIMITED
423 delim_flag = 0;
424 if (yychar < 0)
425 do_lookahead();
426 if ($1.x != 0.0)
427 push_body($1.body);
428 else
429 push_body($4);
430 a_delete $1.body;
431 a_delete $4;
433 | reset_variables
434 | RESET
435 { define_variable("scale", 1.0); }
438 macro_name:
439 VARIABLE
440 | LABEL
443 reset_variables:
444 RESET VARIABLE
446 reset($2);
447 a_delete $2;
449 | reset_variables VARIABLE
451 reset($2);
452 a_delete $2;
454 | reset_variables ',' VARIABLE
456 reset($3);
457 a_delete $3;
461 print_args:
462 print_arg
463 { $$ = $1; }
464 | print_args print_arg
466 $$.str = new char[strlen($1.str) + strlen($2.str) + 1];
467 strcpy($$.str, $1.str);
468 strcat($$.str, $2.str);
469 a_delete $1.str;
470 a_delete $2.str;
471 if ($1.filename) {
472 $$.filename = $1.filename;
473 $$.lineno = $1.lineno;
475 else if ($2.filename) {
476 $$.filename = $2.filename;
477 $$.lineno = $2.lineno;
482 print_arg:
483 expr %prec ','
485 $$.str = new char[GDIGITS + 1];
486 sprintf($$.str, "%g", $1);
487 $$.filename = 0;
488 $$.lineno = 0;
490 | text
491 { $$ = $1; }
492 | position %prec ','
494 $$.str = new char[GDIGITS + 2 + GDIGITS + 1];
495 sprintf($$.str, "%g, %g", $1.x, $1.y);
496 $$.filename = 0;
497 $$.lineno = 0;
501 simple_if:
502 IF any_expr THEN
503 { delim_flag = 1; }
504 DELIMITED
506 delim_flag = 0;
507 $$.x = $2;
508 $$.body = $5;
512 until:
513 /* empty */
514 { $$ = 0; }
515 | UNTIL TEXT
516 { $$ = $2.str; }
519 any_expr:
520 expr
521 { $$ = $1; }
522 | text_expr
523 { $$ = $1; }
526 text_expr:
527 text EQUALEQUAL text
529 $$ = strcmp($1.str, $3.str) == 0;
530 a_delete $1.str;
531 a_delete $3.str;
533 | text NOTEQUAL text
535 $$ = strcmp($1.str, $3.str) != 0;
536 a_delete $1.str;
537 a_delete $3.str;
539 | text_expr ANDAND text_expr
540 { $$ = ($1 != 0.0 && $3 != 0.0); }
541 | text_expr ANDAND expr
542 { $$ = ($1 != 0.0 && $3 != 0.0); }
543 | expr ANDAND text_expr
544 { $$ = ($1 != 0.0 && $3 != 0.0); }
545 | text_expr OROR text_expr
546 { $$ = ($1 != 0.0 || $3 != 0.0); }
547 | text_expr OROR expr
548 { $$ = ($1 != 0.0 || $3 != 0.0); }
549 | expr OROR text_expr
550 { $$ = ($1 != 0.0 || $3 != 0.0); }
551 | '!' text_expr
552 { $$ = ($2 == 0.0); }
556 optional_by:
557 /* empty */
559 $$.val = 1.0;
560 $$.is_multiplicative = 0;
562 | BY expr
564 $$.val = $2;
565 $$.is_multiplicative = 0;
567 | BY '*' expr
569 $$.val = $3;
570 $$.is_multiplicative = 1;
574 element:
575 object_spec
577 $$.obj = $1->make_object(&current_position,
578 &current_direction);
579 if ($$.obj == 0)
580 YYABORT;
581 delete $1;
582 if ($$.obj)
583 olist.append($$.obj);
584 else {
585 $$.x = current_position.x;
586 $$.y = current_position.y;
589 | LABEL ':' optional_separator element
591 $$ = $4;
592 define_label($1, & $$);
593 a_delete $1;
595 | LABEL ':' optional_separator position_not_place
597 $$.obj = 0;
598 $$.x = $4.x;
599 $$.y = $4.y;
600 define_label($1, & $$);
601 a_delete $1;
603 | LABEL ':' optional_separator place
605 $$ = $4;
606 define_label($1, & $$);
607 a_delete $1;
609 | '{'
611 $<state>$.x = current_position.x;
612 $<state>$.y = current_position.y;
613 $<state>$.dir = current_direction;
615 element_list '}'
617 current_position.x = $<state>2.x;
618 current_position.y = $<state>2.y;
619 current_direction = $<state>2.dir;
621 optional_element
623 $$ = $3;
625 | placeless_element
627 $$.obj = 0;
628 $$.x = current_position.x;
629 $$.y = current_position.y;
633 optional_element:
634 /* empty */
636 | element
640 object_spec:
642 { $$ = new object_spec(BOX_OBJECT); }
643 | CIRCLE
644 { $$ = new object_spec(CIRCLE_OBJECT); }
645 | ELLIPSE
646 { $$ = new object_spec(ELLIPSE_OBJECT); }
647 | ARC
649 $$ = new object_spec(ARC_OBJECT);
650 $$->dir = current_direction;
652 | LINE
654 $$ = new object_spec(LINE_OBJECT);
655 lookup_variable("lineht", & $$->segment_height);
656 lookup_variable("linewid", & $$->segment_width);
657 $$->dir = current_direction;
659 | ARROW
661 $$ = new object_spec(ARROW_OBJECT);
662 lookup_variable("lineht", & $$->segment_height);
663 lookup_variable("linewid", & $$->segment_width);
664 $$->dir = current_direction;
666 | MOVE
668 $$ = new object_spec(MOVE_OBJECT);
669 lookup_variable("moveht", & $$->segment_height);
670 lookup_variable("movewid", & $$->segment_width);
671 $$->dir = current_direction;
673 | SPLINE
675 $$ = new object_spec(SPLINE_OBJECT);
676 lookup_variable("lineht", & $$->segment_height);
677 lookup_variable("linewid", & $$->segment_width);
678 $$->dir = current_direction;
680 | text %prec TEXT
682 $$ = new object_spec(TEXT_OBJECT);
683 $$->text = new text_item($1.str, $1.filename, $1.lineno);
685 | PLOT expr
687 $$ = new object_spec(TEXT_OBJECT);
688 $$->text = new text_item(format_number(0, $2), 0, -1);
690 | PLOT expr text
692 $$ = new object_spec(TEXT_OBJECT);
693 $$->text = new text_item(format_number($3.str, $2),
694 $3.filename, $3.lineno);
695 a_delete $3.str;
697 | '['
699 saved_state *p = new saved_state;
700 $<pstate>$ = p;
701 p->x = current_position.x;
702 p->y = current_position.y;
703 p->dir = current_direction;
704 p->tbl = current_table;
705 p->prev = current_saved_state;
706 current_position.x = 0.0;
707 current_position.y = 0.0;
708 current_table = new PTABLE(place);
709 current_saved_state = p;
710 olist.append(make_mark_object());
712 element_list ']'
714 current_position.x = $<pstate>2->x;
715 current_position.y = $<pstate>2->y;
716 current_direction = $<pstate>2->dir;
717 $$ = new object_spec(BLOCK_OBJECT);
718 olist.wrap_up_block(& $$->oblist);
719 $$->tbl = current_table;
720 current_table = $<pstate>2->tbl;
721 current_saved_state = $<pstate>2->prev;
722 delete $<pstate>2;
724 | object_spec HEIGHT expr
726 $$ = $1;
727 $$->height = $3;
728 $$->flags |= HAS_HEIGHT;
730 | object_spec RADIUS expr
732 $$ = $1;
733 $$->radius = $3;
734 $$->flags |= HAS_RADIUS;
736 | object_spec WIDTH expr
738 $$ = $1;
739 $$->width = $3;
740 $$->flags |= HAS_WIDTH;
742 | object_spec DIAMETER expr
744 $$ = $1;
745 $$->radius = $3/2.0;
746 $$->flags |= HAS_RADIUS;
748 | object_spec expr %prec HEIGHT
750 $$ = $1;
751 $$->flags |= HAS_SEGMENT;
752 switch ($$->dir) {
753 case UP_DIRECTION:
754 $$->segment_pos.y += $2;
755 break;
756 case DOWN_DIRECTION:
757 $$->segment_pos.y -= $2;
758 break;
759 case RIGHT_DIRECTION:
760 $$->segment_pos.x += $2;
761 break;
762 case LEFT_DIRECTION:
763 $$->segment_pos.x -= $2;
764 break;
767 | object_spec UP
769 $$ = $1;
770 $$->dir = UP_DIRECTION;
771 $$->flags |= HAS_SEGMENT;
772 $$->segment_pos.y += $$->segment_height;
774 | object_spec UP expr
776 $$ = $1;
777 $$->dir = UP_DIRECTION;
778 $$->flags |= HAS_SEGMENT;
779 $$->segment_pos.y += $3;
781 | object_spec DOWN
783 $$ = $1;
784 $$->dir = DOWN_DIRECTION;
785 $$->flags |= HAS_SEGMENT;
786 $$->segment_pos.y -= $$->segment_height;
788 | object_spec DOWN expr
790 $$ = $1;
791 $$->dir = DOWN_DIRECTION;
792 $$->flags |= HAS_SEGMENT;
793 $$->segment_pos.y -= $3;
795 | object_spec RIGHT
797 $$ = $1;
798 $$->dir = RIGHT_DIRECTION;
799 $$->flags |= HAS_SEGMENT;
800 $$->segment_pos.x += $$->segment_width;
802 | object_spec RIGHT expr
804 $$ = $1;
805 $$->dir = RIGHT_DIRECTION;
806 $$->flags |= HAS_SEGMENT;
807 $$->segment_pos.x += $3;
809 | object_spec LEFT
811 $$ = $1;
812 $$->dir = LEFT_DIRECTION;
813 $$->flags |= HAS_SEGMENT;
814 $$->segment_pos.x -= $$->segment_width;
816 | object_spec LEFT expr
818 $$ = $1;
819 $$->dir = LEFT_DIRECTION;
820 $$->flags |= HAS_SEGMENT;
821 $$->segment_pos.x -= $3;
823 | object_spec FROM position
825 $$ = $1;
826 $$->flags |= HAS_FROM;
827 $$->from.x = $3.x;
828 $$->from.y = $3.y;
830 | object_spec TO position
832 $$ = $1;
833 if ($$->flags & HAS_SEGMENT)
834 $$->segment_list = new segment($$->segment_pos,
835 $$->segment_is_absolute,
836 $$->segment_list);
837 $$->flags |= HAS_SEGMENT;
838 $$->segment_pos.x = $3.x;
839 $$->segment_pos.y = $3.y;
840 $$->segment_is_absolute = 1;
841 $$->flags |= HAS_TO;
842 $$->to.x = $3.x;
843 $$->to.y = $3.y;
845 | object_spec AT position
847 $$ = $1;
848 $$->flags |= HAS_AT;
849 $$->at.x = $3.x;
850 $$->at.y = $3.y;
851 if ($$->type != ARC_OBJECT) {
852 $$->flags |= HAS_FROM;
853 $$->from.x = $3.x;
854 $$->from.y = $3.y;
857 | object_spec WITH path
859 $$ = $1;
860 $$->flags |= HAS_WITH;
861 $$->with = $3;
863 | object_spec WITH position %prec ','
865 $$ = $1;
866 $$->flags |= HAS_WITH;
867 position pos;
868 pos.x = $3.x;
869 pos.y = $3.y;
870 $$->with = new path(pos);
872 | object_spec BY expr_pair
874 $$ = $1;
875 $$->flags |= HAS_SEGMENT;
876 $$->segment_pos.x += $3.x;
877 $$->segment_pos.y += $3.y;
879 | object_spec THEN
881 $$ = $1;
882 if ($$->flags & HAS_SEGMENT) {
883 $$->segment_list = new segment($$->segment_pos,
884 $$->segment_is_absolute,
885 $$->segment_list);
886 $$->flags &= ~HAS_SEGMENT;
887 $$->segment_pos.x = $$->segment_pos.y = 0.0;
888 $$->segment_is_absolute = 0;
891 | object_spec SOLID
893 $$ = $1; // nothing
895 | object_spec DOTTED
897 $$ = $1;
898 $$->flags |= IS_DOTTED;
899 lookup_variable("dashwid", & $$->dash_width);
901 | object_spec DOTTED expr
903 $$ = $1;
904 $$->flags |= IS_DOTTED;
905 $$->dash_width = $3;
907 | object_spec DASHED
909 $$ = $1;
910 $$->flags |= IS_DASHED;
911 lookup_variable("dashwid", & $$->dash_width);
913 | object_spec DASHED expr
915 $$ = $1;
916 $$->flags |= IS_DASHED;
917 $$->dash_width = $3;
919 | object_spec FILL
921 $$ = $1;
922 $$->flags |= IS_DEFAULT_FILLED;
924 | object_spec FILL expr
926 $$ = $1;
927 $$->flags |= IS_FILLED;
928 $$->fill = $3;
930 | object_spec SHADED text
932 $$ = $1;
933 $$->flags |= (IS_SHADED | IS_FILLED);
934 $$->shaded = new char[strlen($3.str)+1];
935 strcpy($$->shaded, $3.str);
937 | object_spec COLORED text
939 $$ = $1;
940 $$->flags |= (IS_SHADED | IS_OUTLINED | IS_FILLED);
941 $$->shaded = new char[strlen($3.str)+1];
942 strcpy($$->shaded, $3.str);
943 $$->outlined = new char[strlen($3.str)+1];
944 strcpy($$->outlined, $3.str);
946 | object_spec OUTLINED text
948 $$ = $1;
949 $$->flags |= IS_OUTLINED;
950 $$->outlined = new char[strlen($3.str)+1];
951 strcpy($$->outlined, $3.str);
953 | object_spec CHOP
955 $$ = $1;
956 // line chop chop means line chop 0 chop 0
957 if ($$->flags & IS_DEFAULT_CHOPPED) {
958 $$->flags |= IS_CHOPPED;
959 $$->flags &= ~IS_DEFAULT_CHOPPED;
960 $$->start_chop = $$->end_chop = 0.0;
962 else if ($$->flags & IS_CHOPPED) {
963 $$->end_chop = 0.0;
965 else {
966 $$->flags |= IS_DEFAULT_CHOPPED;
969 | object_spec CHOP expr
971 $$ = $1;
972 if ($$->flags & IS_DEFAULT_CHOPPED) {
973 $$->flags |= IS_CHOPPED;
974 $$->flags &= ~IS_DEFAULT_CHOPPED;
975 $$->start_chop = 0.0;
976 $$->end_chop = $3;
978 else if ($$->flags & IS_CHOPPED) {
979 $$->end_chop = $3;
981 else {
982 $$->start_chop = $$->end_chop = $3;
983 $$->flags |= IS_CHOPPED;
986 | object_spec SAME
988 $$ = $1;
989 $$->flags |= IS_SAME;
991 | object_spec INVISIBLE
993 $$ = $1;
994 $$->flags |= IS_INVISIBLE;
996 | object_spec LEFT_ARROW_HEAD
998 $$ = $1;
999 $$->flags |= HAS_LEFT_ARROW_HEAD;
1001 | object_spec RIGHT_ARROW_HEAD
1003 $$ = $1;
1004 $$->flags |= HAS_RIGHT_ARROW_HEAD;
1006 | object_spec DOUBLE_ARROW_HEAD
1008 $$ = $1;
1009 $$->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD);
1011 | object_spec CW
1013 $$ = $1;
1014 $$->flags |= IS_CLOCKWISE;
1016 | object_spec CCW
1018 $$ = $1;
1019 $$->flags &= ~IS_CLOCKWISE;
1021 | object_spec text %prec TEXT
1023 $$ = $1;
1024 text_item **p;
1025 for (p = & $$->text; *p; p = &(*p)->next)
1027 *p = new text_item($2.str, $2.filename, $2.lineno);
1029 | object_spec LJUST
1031 $$ = $1;
1032 if ($$->text) {
1033 text_item *p;
1034 for (p = $$->text; p->next; p = p->next)
1036 p->adj.h = LEFT_ADJUST;
1039 | object_spec RJUST
1041 $$ = $1;
1042 if ($$->text) {
1043 text_item *p;
1044 for (p = $$->text; p->next; p = p->next)
1046 p->adj.h = RIGHT_ADJUST;
1049 | object_spec ABOVE
1051 $$ = $1;
1052 if ($$->text) {
1053 text_item *p;
1054 for (p = $$->text; p->next; p = p->next)
1056 p->adj.v = ABOVE_ADJUST;
1059 | object_spec BELOW
1061 $$ = $1;
1062 if ($$->text) {
1063 text_item *p;
1064 for (p = $$->text; p->next; p = p->next)
1066 p->adj.v = BELOW_ADJUST;
1069 | object_spec THICKNESS expr
1071 $$ = $1;
1072 $$->flags |= HAS_THICKNESS;
1073 $$->thickness = $3;
1075 | object_spec ALIGNED
1077 $$ = $1;
1078 $$->flags |= IS_ALIGNED;
1082 text:
1083 TEXT
1084 { $$ = $1; }
1085 | SPRINTF '(' TEXT sprintf_args ')'
1087 $$.filename = $3.filename;
1088 $$.lineno = $3.lineno;
1089 $$.str = do_sprintf($3.str, $4.v, $4.nv);
1090 a_delete $4.v;
1091 a_delete $3.str;
1095 sprintf_args:
1096 /* empty */
1098 $$.v = 0;
1099 $$.nv = 0;
1100 $$.maxv = 0;
1102 | sprintf_args ',' expr
1104 $$ = $1;
1105 if ($$.nv >= $$.maxv) {
1106 if ($$.nv == 0) {
1107 $$.v = new double[4];
1108 $$.maxv = 4;
1110 else {
1111 double *oldv = $$.v;
1112 $$.maxv *= 2;
1113 #if 0
1114 $$.v = new double[$$.maxv];
1115 memcpy($$.v, oldv, $$.nv*sizeof(double));
1116 #else
1117 // workaround for bug in Compaq C++ V6.5-033
1118 // for Compaq Tru64 UNIX V5.1A (Rev. 1885)
1119 double *foo = new double[$$.maxv];
1120 memcpy(foo, oldv, $$.nv*sizeof(double));
1121 $$.v = foo;
1122 #endif
1123 a_delete oldv;
1126 $$.v[$$.nv] = $3;
1127 $$.nv += 1;
1131 position:
1132 position_not_place
1133 { $$ = $1; }
1134 | place
1136 position pos = $1;
1137 $$.x = pos.x;
1138 $$.y = pos.y;
1140 | '(' place ')'
1142 position pos = $2;
1143 $$.x = pos.x;
1144 $$.y = pos.y;
1148 position_not_place:
1149 expr_pair
1150 { $$ = $1; }
1151 | position '+' expr_pair
1153 $$.x = $1.x + $3.x;
1154 $$.y = $1.y + $3.y;
1156 | '(' position '+' expr_pair ')'
1158 $$.x = $2.x + $4.x;
1159 $$.y = $2.y + $4.y;
1161 | position '-' expr_pair
1163 $$.x = $1.x - $3.x;
1164 $$.y = $1.y - $3.y;
1166 | '(' position '-' expr_pair ')'
1168 $$.x = $2.x - $4.x;
1169 $$.y = $2.y - $4.y;
1171 | '(' position ',' position ')'
1173 $$.x = $2.x;
1174 $$.y = $4.y;
1176 | expr between position AND position
1178 $$.x = (1.0 - $1)*$3.x + $1*$5.x;
1179 $$.y = (1.0 - $1)*$3.y + $1*$5.y;
1181 | '(' expr between position AND position ')'
1183 $$.x = (1.0 - $2)*$4.x + $2*$6.x;
1184 $$.y = (1.0 - $2)*$4.y + $2*$6.y;
1186 | expr '<' position ',' position '>'
1188 $$.x = (1.0 - $1)*$3.x + $1*$5.x;
1189 $$.y = (1.0 - $1)*$3.y + $1*$5.y;
1191 | '(' expr '<' position ',' position '>' ')'
1193 $$.x = (1.0 - $2)*$4.x + $2*$6.x;
1194 $$.y = (1.0 - $2)*$4.y + $2*$6.y;
1198 between:
1199 BETWEEN
1200 | OF THE WAY BETWEEN
1203 expr_pair:
1204 expr ',' expr
1206 $$.x = $1;
1207 $$.y = $3;
1209 | '(' expr_pair ')'
1210 { $$ = $2; }
1213 place:
1214 /* line at A left == line (at A) left */
1215 label %prec CHOP
1216 { $$ = $1; }
1217 | label corner
1219 path pth($2);
1220 if (!pth.follow($1, & $$))
1221 YYABORT;
1223 | corner label
1225 path pth($1);
1226 if (!pth.follow($2, & $$))
1227 YYABORT;
1229 | corner OF label
1231 path pth($1);
1232 if (!pth.follow($3, & $$))
1233 YYABORT;
1235 | HERE
1237 $$.x = current_position.x;
1238 $$.y = current_position.y;
1239 $$.obj = 0;
1243 label:
1244 LABEL
1246 place *p = lookup_label($1);
1247 if (!p) {
1248 lex_error("there is no place `%1'", $1);
1249 YYABORT;
1251 $$ = *p;
1252 a_delete $1;
1254 | nth_primitive
1255 { $$.obj = $1; }
1256 | label '.' LABEL
1258 path pth($3);
1259 if (!pth.follow($1, & $$))
1260 YYABORT;
1264 ordinal:
1265 ORDINAL
1266 { $$ = $1; }
1267 | '`' any_expr TH
1269 // XXX Check for overflow (and non-integers?).
1270 $$ = (int)$2;
1274 optional_ordinal_last:
1275 LAST
1276 { $$ = 1; }
1277 | ordinal LAST
1278 { $$ = $1; }
1281 nth_primitive:
1282 ordinal object_type
1284 int count = 0;
1285 object *p;
1286 for (p = olist.head; p != 0; p = p->next)
1287 if (p->type() == $2 && ++count == $1) {
1288 $$ = p;
1289 break;
1291 if (p == 0) {
1292 lex_error("there is no %1%2 %3", $1, ordinal_postfix($1),
1293 object_type_name($2));
1294 YYABORT;
1297 | optional_ordinal_last object_type
1299 int count = 0;
1300 object *p;
1301 for (p = olist.tail; p != 0; p = p->prev)
1302 if (p->type() == $2 && ++count == $1) {
1303 $$ = p;
1304 break;
1306 if (p == 0) {
1307 lex_error("there is no %1%2 last %3", $1,
1308 ordinal_postfix($1), object_type_name($2));
1309 YYABORT;
1314 object_type:
1316 { $$ = BOX_OBJECT; }
1317 | CIRCLE
1318 { $$ = CIRCLE_OBJECT; }
1319 | ELLIPSE
1320 { $$ = ELLIPSE_OBJECT; }
1321 | ARC
1322 { $$ = ARC_OBJECT; }
1323 | LINE
1324 { $$ = LINE_OBJECT; }
1325 | ARROW
1326 { $$ = ARROW_OBJECT; }
1327 | SPLINE
1328 { $$ = SPLINE_OBJECT; }
1329 | '[' ']'
1330 { $$ = BLOCK_OBJECT; }
1331 | TEXT
1332 { $$ = TEXT_OBJECT; }
1335 label_path:
1336 '.' LABEL
1337 { $$ = new path($2); }
1338 | label_path '.' LABEL
1340 $$ = $1;
1341 $$->append($3);
1345 relative_path:
1346 corner %prec CHOP
1347 { $$ = new path($1); }
1348 /* give this a lower precedence than LEFT and RIGHT so that
1349 [A: box] with .A left == [A: box] with (.A left) */
1350 | label_path %prec TEXT
1351 { $$ = $1; }
1352 | label_path corner
1354 $$ = $1;
1355 $$->append($2);
1359 path:
1360 relative_path
1361 { $$ = $1; }
1362 | '(' relative_path ',' relative_path ')'
1364 $$ = $2;
1365 $$->set_ypath($4);
1367 /* The rest of these rules are a compatibility sop. */
1368 | ORDINAL LAST object_type relative_path
1370 lex_warning("`%1%2 last %3' in `with' argument ignored",
1371 $1, ordinal_postfix($1), object_type_name($3));
1372 $$ = $4;
1374 | LAST object_type relative_path
1376 lex_warning("`last %1' in `with' argument ignored",
1377 object_type_name($2));
1378 $$ = $3;
1380 | ORDINAL object_type relative_path
1382 lex_warning("`%1%2 %3' in `with' argument ignored",
1383 $1, ordinal_postfix($1), object_type_name($2));
1384 $$ = $3;
1386 | LABEL relative_path
1388 lex_warning("initial `%1' in `with' argument ignored", $1);
1389 a_delete $1;
1390 $$ = $2;
1394 corner:
1395 DOT_N
1396 { $$ = &object::north; }
1397 | DOT_E
1398 { $$ = &object::east; }
1399 | DOT_W
1400 { $$ = &object::west; }
1401 | DOT_S
1402 { $$ = &object::south; }
1403 | DOT_NE
1404 { $$ = &object::north_east; }
1405 | DOT_SE
1406 { $$ = &object:: south_east; }
1407 | DOT_NW
1408 { $$ = &object::north_west; }
1409 | DOT_SW
1410 { $$ = &object::south_west; }
1411 | DOT_C
1412 { $$ = &object::center; }
1413 | DOT_START
1414 { $$ = &object::start; }
1415 | DOT_END
1416 { $$ = &object::end; }
1417 | TOP
1418 { $$ = &object::north; }
1419 | BOTTOM
1420 { $$ = &object::south; }
1421 | LEFT
1422 { $$ = &object::west; }
1423 | RIGHT
1424 { $$ = &object::east; }
1425 | UPPER LEFT
1426 { $$ = &object::north_west; }
1427 | LOWER LEFT
1428 { $$ = &object::south_west; }
1429 | UPPER RIGHT
1430 { $$ = &object::north_east; }
1431 | LOWER RIGHT
1432 { $$ = &object::south_east; }
1433 | LEFT_CORNER
1434 { $$ = &object::west; }
1435 | RIGHT_CORNER
1436 { $$ = &object::east; }
1437 | UPPER LEFT_CORNER
1438 { $$ = &object::north_west; }
1439 | LOWER LEFT_CORNER
1440 { $$ = &object::south_west; }
1441 | UPPER RIGHT_CORNER
1442 { $$ = &object::north_east; }
1443 | LOWER RIGHT_CORNER
1444 { $$ = &object::south_east; }
1445 | NORTH
1446 { $$ = &object::north; }
1447 | SOUTH
1448 { $$ = &object::south; }
1449 | EAST
1450 { $$ = &object::east; }
1451 | WEST
1452 { $$ = &object::west; }
1453 | CENTER
1454 { $$ = &object::center; }
1455 | START
1456 { $$ = &object::start; }
1457 | END
1458 { $$ = &object::end; }
1461 expr:
1462 VARIABLE
1464 if (!lookup_variable($1, & $$)) {
1465 lex_error("there is no variable `%1'", $1);
1466 YYABORT;
1468 a_delete $1;
1470 | NUMBER
1471 { $$ = $1; }
1472 | place DOT_X
1474 if ($1.obj != 0)
1475 $$ = $1.obj->origin().x;
1476 else
1477 $$ = $1.x;
1479 | place DOT_Y
1481 if ($1.obj != 0)
1482 $$ = $1.obj->origin().y;
1483 else
1484 $$ = $1.y;
1486 | place DOT_HT
1488 if ($1.obj != 0)
1489 $$ = $1.obj->height();
1490 else
1491 $$ = 0.0;
1493 | place DOT_WID
1495 if ($1.obj != 0)
1496 $$ = $1.obj->width();
1497 else
1498 $$ = 0.0;
1500 | place DOT_RAD
1502 if ($1.obj != 0)
1503 $$ = $1.obj->radius();
1504 else
1505 $$ = 0.0;
1507 | expr '+' expr
1508 { $$ = $1 + $3; }
1509 | expr '-' expr
1510 { $$ = $1 - $3; }
1511 | expr '*' expr
1512 { $$ = $1 * $3; }
1513 | expr '/' expr
1515 if ($3 == 0.0) {
1516 lex_error("division by zero");
1517 YYABORT;
1519 $$ = $1/$3;
1521 | expr '%' expr
1523 if ($3 == 0.0) {
1524 lex_error("modulus by zero");
1525 YYABORT;
1527 $$ = fmod($1, $3);
1529 | expr '^' expr
1531 errno = 0;
1532 $$ = pow($1, $3);
1533 if (errno == EDOM) {
1534 lex_error("arguments to `^' operator out of domain");
1535 YYABORT;
1537 if (errno == ERANGE) {
1538 lex_error("result of `^' operator out of range");
1539 YYABORT;
1542 | '-' expr %prec '!'
1543 { $$ = -$2; }
1544 | '(' any_expr ')'
1545 { $$ = $2; }
1546 | SIN '(' any_expr ')'
1548 errno = 0;
1549 $$ = sin($3);
1550 if (errno == ERANGE) {
1551 lex_error("sin result out of range");
1552 YYABORT;
1555 | COS '(' any_expr ')'
1557 errno = 0;
1558 $$ = cos($3);
1559 if (errno == ERANGE) {
1560 lex_error("cos result out of range");
1561 YYABORT;
1564 | ATAN2 '(' any_expr ',' any_expr ')'
1566 errno = 0;
1567 $$ = atan2($3, $5);
1568 if (errno == EDOM) {
1569 lex_error("atan2 argument out of domain");
1570 YYABORT;
1572 if (errno == ERANGE) {
1573 lex_error("atan2 result out of range");
1574 YYABORT;
1577 | LOG '(' any_expr ')'
1579 errno = 0;
1580 $$ = log10($3);
1581 if (errno == ERANGE) {
1582 lex_error("log result out of range");
1583 YYABORT;
1586 | EXP '(' any_expr ')'
1588 errno = 0;
1589 $$ = pow(10.0, $3);
1590 if (errno == ERANGE) {
1591 lex_error("exp result out of range");
1592 YYABORT;
1595 | SQRT '(' any_expr ')'
1597 errno = 0;
1598 $$ = sqrt($3);
1599 if (errno == EDOM) {
1600 lex_error("sqrt argument out of domain");
1601 YYABORT;
1604 | K_MAX '(' any_expr ',' any_expr ')'
1605 { $$ = $3 > $5 ? $3 : $5; }
1606 | K_MIN '(' any_expr ',' any_expr ')'
1607 { $$ = $3 < $5 ? $3 : $5; }
1608 | INT '(' any_expr ')'
1609 { $$ = floor($3); }
1610 | RAND '(' any_expr ')'
1611 { $$ = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*$3); }
1612 | RAND '(' ')'
1614 /* return a random number in the range [0,1) */
1615 /* portable, but not very random */
1616 $$ = (rand() & 0x7fff) / double(0x8000);
1618 | SRAND '(' any_expr ')'
1620 $$ = 0;
1621 srand((unsigned int)$3);
1623 | expr '<' expr
1624 { $$ = ($1 < $3); }
1625 | expr LESSEQUAL expr
1626 { $$ = ($1 <= $3); }
1627 | expr '>' expr
1628 { $$ = ($1 > $3); }
1629 | expr GREATEREQUAL expr
1630 { $$ = ($1 >= $3); }
1631 | expr EQUALEQUAL expr
1632 { $$ = ($1 == $3); }
1633 | expr NOTEQUAL expr
1634 { $$ = ($1 != $3); }
1635 | expr ANDAND expr
1636 { $$ = ($1 != 0.0 && $3 != 0.0); }
1637 | expr OROR expr
1638 { $$ = ($1 != 0.0 || $3 != 0.0); }
1639 | '!' expr
1640 { $$ = ($2 == 0.0); }
1646 /* bison defines const to be empty unless __STDC__ is defined, which it
1647 isn't under cfront */
1649 #ifdef const
1650 #undef const
1651 #endif
1653 static struct {
1654 const char *name;
1655 double val;
1656 int scaled; // non-zero if val should be multiplied by scale
1657 } defaults_table[] = {
1658 { "arcrad", .25, 1 },
1659 { "arrowht", .1, 1 },
1660 { "arrowwid", .05, 1 },
1661 { "circlerad", .25, 1 },
1662 { "boxht", .5, 1 },
1663 { "boxwid", .75, 1 },
1664 { "boxrad", 0.0, 1 },
1665 { "dashwid", .05, 1 },
1666 { "ellipseht", .5, 1 },
1667 { "ellipsewid", .75, 1 },
1668 { "moveht", .5, 1 },
1669 { "movewid", .5, 1 },
1670 { "lineht", .5, 1 },
1671 { "linewid", .5, 1 },
1672 { "textht", 0.0, 1 },
1673 { "textwid", 0.0, 1 },
1674 { "scale", 1.0, 0 },
1675 { "linethick", -1.0, 0 }, // in points
1676 { "fillval", .5, 0 },
1677 { "arrowhead", 1.0, 0 },
1678 { "maxpswid", 8.5, 0 },
1679 { "maxpsht", 11.0, 0 },
1682 place *lookup_label(const char *label)
1684 saved_state *state = current_saved_state;
1685 PTABLE(place) *tbl = current_table;
1686 for (;;) {
1687 place *pl = tbl->lookup(label);
1688 if (pl)
1689 return pl;
1690 if (!state)
1691 return 0;
1692 tbl = state->tbl;
1693 state = state->prev;
1697 void define_label(const char *label, const place *pl)
1699 place *p = new place[1];
1700 *p = *pl;
1701 current_table->define(label, p);
1704 int lookup_variable(const char *name, double *val)
1706 place *pl = lookup_label(name);
1707 if (pl) {
1708 *val = pl->x;
1709 return 1;
1711 return 0;
1714 void define_variable(const char *name, double val)
1716 place *p = new place[1];
1717 p->obj = 0;
1718 p->x = val;
1719 p->y = 0.0;
1720 current_table->define(name, p);
1721 if (strcmp(name, "scale") == 0) {
1722 // When the scale changes, reset all scaled pre-defined variables to
1723 // their default values.
1724 for (unsigned int i = 0;
1725 i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1726 if (defaults_table[i].scaled)
1727 define_variable(defaults_table[i].name, val*defaults_table[i].val);
1731 // called once only (not once per parse)
1733 void parse_init()
1735 current_direction = RIGHT_DIRECTION;
1736 current_position.x = 0.0;
1737 current_position.y = 0.0;
1738 // This resets everything to its default value.
1739 reset_all();
1742 void reset(const char *nm)
1744 for (unsigned int i = 0;
1745 i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1746 if (strcmp(nm, defaults_table[i].name) == 0) {
1747 double val = defaults_table[i].val;
1748 if (defaults_table[i].scaled) {
1749 double scale;
1750 lookup_variable("scale", &scale);
1751 val *= scale;
1753 define_variable(defaults_table[i].name, val);
1754 return;
1756 lex_error("`%1' is not a predefined variable", nm);
1759 void reset_all()
1761 // We only have to explicitly reset the pre-defined variables that
1762 // aren't scaled because `scale' is not scaled, and changing the
1763 // value of `scale' will reset all the pre-defined variables that
1764 // are scaled.
1765 for (unsigned int i = 0;
1766 i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1767 if (!defaults_table[i].scaled)
1768 define_variable(defaults_table[i].name, defaults_table[i].val);
1771 // called after each parse
1773 void parse_cleanup()
1775 while (current_saved_state != 0) {
1776 delete current_table;
1777 current_table = current_saved_state->tbl;
1778 saved_state *tem = current_saved_state;
1779 current_saved_state = current_saved_state->prev;
1780 delete tem;
1782 assert(current_table == &top_table);
1783 PTABLE_ITERATOR(place) iter(current_table);
1784 const char *key;
1785 place *pl;
1786 while (iter.next(&key, &pl))
1787 if (pl->obj != 0) {
1788 position pos = pl->obj->origin();
1789 pl->obj = 0;
1790 pl->x = pos.x;
1791 pl->y = pos.y;
1793 while (olist.head != 0) {
1794 object *tem = olist.head;
1795 olist.head = olist.head->next;
1796 delete tem;
1798 olist.tail = 0;
1799 current_direction = RIGHT_DIRECTION;
1800 current_position.x = 0.0;
1801 current_position.y = 0.0;
1804 const char *ordinal_postfix(int n)
1806 if (n < 10 || n > 20)
1807 switch (n % 10) {
1808 case 1:
1809 return "st";
1810 case 2:
1811 return "nd";
1812 case 3:
1813 return "rd";
1815 return "th";
1818 const char *object_type_name(object_type type)
1820 switch (type) {
1821 case BOX_OBJECT:
1822 return "box";
1823 case CIRCLE_OBJECT:
1824 return "circle";
1825 case ELLIPSE_OBJECT:
1826 return "ellipse";
1827 case ARC_OBJECT:
1828 return "arc";
1829 case SPLINE_OBJECT:
1830 return "spline";
1831 case LINE_OBJECT:
1832 return "line";
1833 case ARROW_OBJECT:
1834 return "arrow";
1835 case MOVE_OBJECT:
1836 return "move";
1837 case TEXT_OBJECT:
1838 return "\"\"";
1839 case BLOCK_OBJECT:
1840 return "[]";
1841 case OTHER_OBJECT:
1842 case MARK_OBJECT:
1843 default:
1844 break;
1846 return "object";
1849 static char sprintf_buf[1024];
1851 char *format_number(const char *form, double n)
1853 if (form == 0)
1854 form = "%g";
1855 return do_sprintf(form, &n, 1);
1858 char *do_sprintf(const char *form, const double *v, int nv)
1860 string result;
1861 int i = 0;
1862 string one_format;
1863 while (*form) {
1864 if (*form == '%') {
1865 one_format += *form++;
1866 for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++)
1867 one_format += *form;
1868 if (*form == '\0' || strchr("eEfgG%", *form) == 0) {
1869 lex_error("bad sprintf format");
1870 result += one_format;
1871 result += form;
1872 break;
1874 if (*form == '%') {
1875 one_format += *form++;
1876 one_format += '\0';
1877 snprintf(sprintf_buf, sizeof(sprintf_buf),
1878 "%s", one_format.contents());
1880 else {
1881 if (i >= nv) {
1882 lex_error("too few arguments to snprintf");
1883 result += one_format;
1884 result += form;
1885 break;
1887 one_format += *form++;
1888 one_format += '\0';
1889 snprintf(sprintf_buf, sizeof(sprintf_buf),
1890 one_format.contents(), v[i++]);
1892 one_format.clear();
1893 result += sprintf_buf;
1895 else
1896 result += *form++;
1898 result += '\0';
1899 return strsave(result.contents());