1 /*@ This file implements the parser for the intermediate roff output, see
2 *@ XYroff_out(5), and does the printout for the given device.
3 *@ All parsed information is processed within the function do_file().
4 *@ A device postprocessor just needs to fill in the methods for the class
5 *@ `printer' (or rather a derived class) without having to worry about the
6 *@ syntax of the intermediate output format. Consequently, the programming of
7 *@ roff postprocessors is similar to the development of device drivers.
9 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
11 * Copyright (C) 1989 - 1992, 2001 - 2006, 2008
12 * Free Software Foundation, Inc.
14 * Written by James Clark (jjc@jclark.com)
15 * Major rewrite 2001 by Bernd Warken (bwarken@mayn.de)
17 * This is free software; you can redistribute it and/or modify it
18 * under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2, or (at your option)
22 * This is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with groff; see the file COPYING. If not, write to the Free
29 * Software Foundation, 51 Franklin St - Fifth Floor, Boston, MA
36 * Discussion of the positioning by drawing commands
38 * There was some confusion about the positioning of the graphical
39 * pointer at the printout after having executed a `D' command.
40 * The classical troff manual of Ossanna & Kernighan specified,
42 * `The position after a graphical object has been drawn is
43 * at its end; for circles and ellipses, the "end" is at the
46 * From this, it follows that
47 * - all open figures (args, splines, and lines) should position at their
49 * - all circles and ellipses should position at their right-most point
50 * (as if 2 halves had been drawn).
51 * - all closed figures apart from circles and ellipses shouldn't change
52 * the position because they return to their origin.
53 * - all setting commands should not change position because they do not
54 * draw any graphical object.
56 * In the case of the open figures, this means that the horizontal
57 * displacement is the sum of all odd arguments and the vertical offset
58 * the sum of all even arguments, called the alternate arguments sum
59 * displacement in the following.
61 * Unfortunately, groff did not implement this simple rule. The former
62 * documentation in groff_out(5) differed from the source code, and
63 * neither of them is compatible with the classical rule.
65 * The former groff_out(5) specified to use the alternative arguments
66 * sum displacement for calculating the drawing positioning of
67 * non-classical commands, including the `Dt' command (setting-only)
68 * and closed polygons. Applying this to the new groff color commands
69 * will lead to disaster. For their arguments can take large values (>
70 * 65000). On low resolution devices, the displacement of such large
71 * values will corrupt the display or kill the printer. So the
72 * nonsense specification has come to a natural end anyway.
74 * The groff source code, however, had no positioning for the
75 * setting-only commands (esp. `Dt'), the right-end positioning for
76 * outlined circles and ellipses, and the alternative argument sum
77 * displacement for all other commands (including filled circles and
80 * The reason why no one seems to have suffered from this mayhem so
81 * far is that the graphical objects are usually generated by
82 * preprocessors like pic that do not depend on the automatic
83 * positioning. When using the low level `\D' escape sequences or `D'
84 * output commands, the strange positionings can be circumvented by
85 * absolute positionings or by tricks like `\Z'.
87 * So doing an exorcism on the strange, incompatible displacements might
88 * not harm any existing documents, but will make the usage of the
89 * graphical escape sequences and commands natural.
91 * That's why the rewrite of this file returned to the reasonable,
92 * classical specification with its clear end-of-drawing rule that is
93 * suitable for all cases. But a macro STUPID_DRAWING_POSITIONING is
94 * provided for testing the funny former behavior.
96 * The new rule implies the following behavior.
97 * - Setting commands (`Dt', `Df', `DF') and polygons (`Dp' and `DP')
98 * do not change position now.
99 * - Filled circles and ellipses (`DC' and `DE') position at their
100 * most right point (outlined ones `Dc' and `De' did this anyway).
101 * - As before, all open graphical objects position to their final
102 * drawing point (alternate sum of the command arguments).
105 #ifndef STUPID_DRAWING_POSITIONING
106 // uncomment next line if all non-classical D commands shall position
107 // to the strange alternate sum of args displacement
108 #define STUPID_DRAWING_POSITIONING
111 // Decide whether the commands `{' and `}' for different environments
123 /**********************************************************************
125 **********************************************************************/
127 // integer type used in the fields of struct environment (see printer.h)
130 // integer arguments of groff_out commands, must be >= 32 bits
133 // color components of groff_out color commands, must be >= 32 bits
134 typedef unsigned int ColorArg
;
136 // Array for IntArg values.
139 size_t num_allocated
;
145 IntArray(const size_t);
147 IntArg
operator[](const size_t i
) const
150 fatal("index out of range");
151 return (IntArg
) data
[i
];
154 IntArg
*get_data(void) const { return (IntArg
*)data
; }
155 size_t len(void) const { return num_stored
; }
158 // Characters read from the input queue.
164 Char(void) : data('\0') {}
165 Char(const int c
) : data(c
) {}
166 bool operator==(char c
) const { return (data
== c
) ? true : false; }
167 bool operator==(int c
) const { return (data
== c
) ? true : false; }
168 bool operator==(const Char c
) const
169 { return (data
== c
.data
) ? true : false; }
170 bool operator!=(char c
) const { return !(*this == c
); }
171 bool operator!=(int c
) const { return !(*this == c
); }
172 bool operator!=(const Char c
) const { return !(*this == c
); }
173 operator int() const { return (int) data
; }
174 operator unsigned char() const { return (unsigned char) data
; }
175 operator char() const { return (char) data
; }
178 // Buffer for string arguments (Char, not char).
181 size_t num_allocated
;
183 Char
*data
; // not terminated by '\0'
186 StringBuf(void); // allocate without storing
188 void append(const Char
); // append character to `data'
189 char *make_string(void); // return new copy of `data' with '\0'
190 bool is_empty(void) { // true if none stored
191 return (num_stored
> 0) ? false : true;
193 void reset(void); // set `num_stored' to 0
200 size_t num_allocated
;
206 environment
*pop(void);
207 void push(environment
*e
);
209 #endif // USE_ENV_STACK
212 /**********************************************************************
214 **********************************************************************/
216 // exported as extern by error.h (called from driver.h)
217 // needed for error messages (see ../lib-roff/error.cpp)
218 const char *current_filename
= 0; // printable name of the current file
219 // printable name of current source file
220 const char *current_source_filename
= 0;
221 int current_lineno
= 0; // current line number of printout
223 // exported as extern by device.h;
224 const char *device
= 0; // cancel former init with literal
230 // We rely on an implementation of the `new' operator which aborts
231 // gracefully if it can't allocate memory (e.g. from lib-roff/new.cpp).
233 /**********************************************************************
234 static local variables
235 **********************************************************************/
237 FILE *current_file
= 0; // current input stream for parser
239 // npages: number of pages processed so far (including current page),
240 // _not_ the page number in the printout (can be set with `p').
244 COLORARG_MAX
= (ColorArg
) 65536U; // == 0xFFFF + 1 == 0x10000
247 INTARG_MAX
= (IntArg
) 0x7FFFFFFF; // maximal signed 32 bits number
249 // parser environment, created and deleted by each run of do_file()
250 environment
*current_env
= 0;
254 envp_size
= sizeof(environment
*);
255 #endif // USE_ENV_STACK
257 /**********************************************************************
258 function declarations
259 **********************************************************************/
262 ColorArg
color_from_Df_command(IntArg
);
263 // transform old color into new
264 void delete_current_env(void); // delete global var current_env
265 void fatal_command(char); // abort for invalid command
266 inline Char
get_char(void); // read next character from input stream
267 ColorArg
get_color_arg(void); // read in argument for new color cmds
268 IntArray
*get_D_fixed_args(const size_t);
269 // read in fixed number of integer
271 IntArray
*get_D_fixed_args_odd_dummy(const size_t);
272 // read in a fixed number of integer
273 // arguments plus optional dummy
274 IntArray
*get_D_variable_args(void);
275 // variable, even number of int args
276 char *get_extended_arg(void); // argument for `x X' (several lines)
277 IntArg
get_integer_arg(void); // read in next integer argument
278 IntArray
*get_possibly_integer_args();
279 // 0 or more integer arguments
280 char *get_string_arg(void); // read in next string arg, ended by WS
281 inline bool is_space_or_tab(const Char
);
282 // test on space/tab char
283 Char
next_arg_begin(void); // skip white space on current line
284 Char
next_command(void); // go to next command, evt. diff. line
285 inline bool odd(const int); // test if integer is odd
286 void position_to_end_of_args(const IntArray
* const);
287 // positioning after drawing
288 void remember_filename(const char *);
289 // set global current_filename
290 void remember_source_filename(const char *);
291 // set global current_source_filename
292 void send_draw(const Char
, const IntArray
* const);
294 void skip_line(void); // unconditionally skip to next line
295 bool skip_line_checked(void); // skip line, false if args are left
296 void skip_line_fatal(void); // skip line, fatal if args are left
297 void skip_line_warn(void); // skip line, warn if args are left
298 void skip_line_D(void); // skip line in D commands
299 void skip_line_x(void); // skip line in x commands
300 void skip_to_end_of_line(void); // skip to the end of the current line
301 inline void unget_char(const Char
);
302 // restore character onto input
304 // parser subcommands
305 void parse_color_command(color
*);
306 // color sub(sub)commands m and DF
307 void parse_D_command(void); // graphical subcommands
308 bool parse_x_command(void); // device controller subcommands
310 /**********************************************************************
312 **********************************************************************/
315 EnvStack::EnvStack(void)
318 // allocate pointer to array of num_allocated pointers to environment
319 data
= (environment
**)malloc(envp_size
* num_allocated
);
321 fatal("could not allocate environment data");
325 EnvStack::~EnvStack(void)
327 for (size_t i
= 0; i
< num_stored
; i
++)
332 // return top element from stack and decrease stack pointer
334 // the calling function must take care of properly deleting the result
339 environment
*result
= data
[num_stored
];
340 data
[num_stored
] = 0;
344 // copy argument and push this onto the stack
346 EnvStack::push(environment
*e
)
348 environment
*e_copy
= new environment
;
349 if (num_stored
>= num_allocated
) {
350 environment
**old_data
= data
;
352 data
= (environment
**)malloc(envp_size
* num_allocated
);
354 fatal("could not allocate data");
355 for (size_t i
= 0; i
< num_stored
; i
++)
356 data
[i
] = old_data
[i
];
359 e_copy
->col
= new color
;
360 e_copy
->fill
= new color
;
361 *e_copy
->col
= *e
->col
;
362 *e_copy
->fill
= *e
->fill
;
363 e_copy
->fontno
= e
->fontno
;
364 e_copy
->height
= e
->height
;
365 e_copy
->hpos
= e
->hpos
;
366 e_copy
->size
= e
->size
;
367 e_copy
->slant
= e
->slant
;
368 e_copy
->vpos
= e
->vpos
;
369 data
[num_stored
] = e_copy
;
372 #endif // USE_ENV_STACK
374 IntArray::IntArray(void)
377 data
= new IntArg
[num_allocated
];
381 IntArray::IntArray(const size_t n
)
384 fatal("number of integers to be allocated must be > 0");
386 data
= new IntArg
[num_allocated
];
390 IntArray::~IntArray(void)
396 IntArray::append(IntArg x
)
398 if (num_stored
>= num_allocated
) {
399 IntArg
*old_data
= data
;
401 data
= new IntArg
[num_allocated
];
402 for (size_t i
= 0; i
< num_stored
; i
++)
403 data
[i
] = old_data
[i
];
406 data
[num_stored
] = x
;
410 StringBuf::StringBuf(void)
414 data
= new Char
[num_allocated
];
417 StringBuf::~StringBuf(void)
423 StringBuf::append(const Char c
)
425 if (num_stored
>= num_allocated
) {
426 Char
*old_data
= data
;
428 data
= new Char
[num_allocated
];
429 for (size_t i
= 0; i
< num_stored
; i
++)
430 data
[i
] = old_data
[i
];
433 data
[num_stored
] = c
;
438 StringBuf::make_string(void)
440 char *result
= new char[num_stored
+ 1];
441 for (size_t i
= 0; i
< num_stored
; i
++)
442 result
[i
] = (char) data
[i
];
443 result
[num_stored
] = '\0';
448 StringBuf::reset(void)
453 /**********************************************************************
455 **********************************************************************/
457 /* color_from_Df_command:
458 Process the gray shade setting command Df.
460 Transform Df style color into DF style color.
461 Df color: 0-1000, 0 is white
462 DF color: 0-65536, 0 is black
464 The Df command is obsoleted by command DFg, but kept for
468 color_from_Df_command(IntArg Df_gray
)
470 return ColorArg((1000-Df_gray
) * COLORARG_MAX
/ 1000); // scaling
473 /* delete_current_env():
474 Delete global variable current_env and its pointer members.
476 This should be a class method of environment.
478 void delete_current_env(void)
480 delete current_env
->col
;
481 delete current_env
->fill
;
487 Emit error message about invalid command and abort.
490 fatal_command(char command
)
492 fatal("`%1' command invalid before first `p' command", command
);
496 Retrieve the next character from the input queue.
498 Return: The retrieved character (incl. EOF), converted to Char.
503 return (Char
) getc(current_file
);
507 Retrieve an argument suitable for the color commands m and DF.
509 Return: The retrieved color argument.
514 IntArg x
= get_integer_arg();
515 if (x
< 0 || x
> (IntArg
)COLORARG_MAX
) {
516 error("color component argument out of range");
522 /* get_D_fixed_args():
523 Get a fixed number of integer arguments for D commands.
525 Fatal if wrong number of arguments.
526 Too many arguments on the line raise a warning.
529 number: In-parameter, the number of arguments to be retrieved.
530 ignore: In-parameter, ignore next argument -- GNU troff always emits
531 pairs of parameters for `D' extensions added by groff.
534 Return: New IntArray containing the arguments.
537 get_D_fixed_args(const size_t number
)
540 fatal("requested number of arguments must be > 0");
541 IntArray
*args
= new IntArray(number
);
542 for (size_t i
= 0; i
< number
; i
++)
543 args
->append(get_integer_arg());
548 /* get_D_fixed_args_odd_dummy():
549 Get a fixed number of integer arguments for D commands and optionally
550 ignore a dummy integer argument if the requested number is odd.
552 The gtroff program adds a dummy argument to some commands to get
553 an even number of arguments.
554 Error if the number of arguments differs from the scheme above.
557 number: In-parameter, the number of arguments to be retrieved.
559 Return: New IntArray containing the arguments.
562 get_D_fixed_args_odd_dummy(const size_t number
)
565 fatal("requested number of arguments must be > 0");
566 IntArray
*args
= new IntArray(number
);
567 for (size_t i
= 0; i
< number
; i
++)
568 args
->append(get_integer_arg());
570 IntArray
*a
= get_possibly_integer_args();
572 error("too many arguments");
579 /* get_D_variable_args():
580 Get a variable even number of integer arguments for D commands.
582 Get as many integer arguments as possible from the rest of the
584 - The arguments are separated by an arbitrary sequence of space or
586 - A comment, a newline, or EOF indicates the end of processing.
587 - Error on non-digit characters different from these.
588 - A final line skip is performed (except for EOF).
590 Return: New IntArray of the retrieved arguments.
593 get_D_variable_args()
595 IntArray
*args
= get_possibly_integer_args();
596 size_t n
= args
->len();
598 error("no arguments found");
600 error("even number of arguments expected");
605 /* get_extended_arg():
606 Retrieve extended arg for `x X' command.
608 - Skip leading spaces and tabs, error on EOL or newline.
609 - Return everything before the next NL or EOF ('#' is not a comment);
610 as long as the following line starts with '+' this is returned
611 as well, with the '+' replaced by a newline.
612 - Final line skip is always performed.
614 Return: Allocated (new) string of retrieved text argument.
617 get_extended_arg(void)
619 StringBuf buf
= StringBuf();
620 Char c
= next_arg_begin();
621 while ((int) c
!= EOF
) {
622 if ((int) c
== '\n') {
626 buf
.append((Char
) '\n');
628 unget_char(c
); // first character of next line
636 return buf
.make_string();
639 /* get_integer_arg(): Retrieve integer argument.
641 Skip leading spaces and tabs, collect an optional '-' and all
642 following decimal digits (at least one) up to the next non-digit,
643 which is restored onto the input queue.
645 Fatal error on all other situations.
647 Return: Retrieved integer.
650 get_integer_arg(void)
652 StringBuf buf
= StringBuf();
653 Char c
= next_arg_begin();
654 if ((int) c
== '-') {
658 if (!isdigit((int) c
))
659 error("integer argument expected");
660 while (isdigit((int) c
)) {
666 char *s
= buf
.make_string();
668 long int number
= strtol(s
, 0, 10);
670 || number
> INTARG_MAX
|| number
< -INTARG_MAX
) {
671 error("integer argument too large");
675 return (IntArg
) number
;
678 /* get_possibly_integer_args():
679 Parse the rest of the input line as a list of integer arguments.
681 Get as many integer arguments as possible from the rest of the
682 current line, even none.
683 - The arguments are separated by an arbitrary sequence of space or
685 - A comment, a newline, or EOF indicates the end of processing.
686 - Error on non-digit characters different from these.
687 - No line skip is performed.
689 Return: New IntArray of the retrieved arguments.
692 get_possibly_integer_args()
695 StringBuf buf
= StringBuf();
697 IntArray
*args
= new IntArray();
700 while (is_space_or_tab(c
))
703 Char c1
= get_char();
704 if (isdigit((int) c1
)) {
711 while (isdigit((int) c
)) {
715 if (!buf
.is_empty()) {
716 char *s
= buf
.make_string();
718 long int x
= strtol(s
, 0, 10);
720 || x
> INTARG_MAX
|| x
< -INTARG_MAX
) {
721 error("invalid integer argument, set to 0");
724 args
->append((IntArg
) x
);
727 // Here, c is not a digit.
728 // Terminate on comment, end of line, or end of file, while
729 // space or tab indicate continuation; otherwise error.
732 skip_to_end_of_line();
746 error("integer argument expected");
756 - Skip leading spaces and tabs; error on EOL or newline.
757 - Return all following characters before the next space, tab,
758 newline, or EOF character (in-word '#' is not a comment character).
759 - The terminating space, tab, newline, or EOF character is restored
760 onto the input queue, so no line skip.
762 Return: Retrieved string as char *, allocated by 'new'.
767 StringBuf buf
= StringBuf();
768 Char c
= next_arg_begin();
769 while (!is_space_or_tab(c
)
770 && c
!= Char('\n') && c
!= Char(EOF
)) {
774 unget_char(c
); // restore white space
775 return buf
.make_string();
778 /* is_space_or_tab():
779 Test a character if it is a space or tab.
781 c: In-parameter, character to be tested.
783 Return: True, if c is a space or tab character, false otherwise.
786 is_space_or_tab(const Char c
)
788 return (c
== Char(' ') || c
== Char('\t')) ? true : false;
792 Return first character of next argument.
794 Skip space and tab characters; error on newline or EOF.
796 Return: The first character different from these (including '#').
810 error("missing argument");
812 default: // first essential character
819 Find the first character of the next command.
821 Skip spaces, tabs, comments (introduced by #), and newlines.
823 Return: The first character different from these (including EOF).
841 default: // EOF or first essential character
848 Test whether argument is an odd number.
850 n: In-parameter, the integer to be tested.
852 Return: True if odd, false otherwise.
857 return (n
& 1) ? true : false;
860 /* position_to_end_of_args():
861 Move graphical pointer to end of drawn figure.
863 This is used by the D commands that draw open geometrical figures.
864 The algorithm simply sums up all horizontal displacements (arguments
865 with even number) for the horizontal component. Similarly, the
866 vertical component is the sum of the odd arguments.
868 args: In-parameter, the arguments of a former drawing command.
871 position_to_end_of_args(const IntArray
* const args
)
874 const size_t n
= args
->len();
875 for (i
= 0; i
< n
; i
+= 2)
876 current_env
->hpos
+= (*args
)[i
];
877 for (i
= 1; i
< n
; i
+= 2)
878 current_env
->vpos
+= (*args
)[i
];
881 /* remember_filename():
882 Set global variable current_filename.
884 The actual filename is stored in current_filename. This is used by
885 the postprocessors, expecting the name "<standard input>" for stdin.
887 filename: In-out-parameter; is changed to the new value also.
890 remember_filename(const char *filename
)
893 if (strcmp(filename
, "-") == 0)
894 fname
= (char *)"<standard input>";
896 fname
= (char *)filename
;
897 size_t len
= strlen(fname
) + 1;
898 if (current_filename
!= 0)
899 free((char *)current_filename
);
900 current_filename
= (const char *)malloc(len
);
901 if (current_filename
== 0)
902 fatal("can't malloc space for filename");
903 strncpy((char *)current_filename
, (char *)fname
, len
);
906 /* remember_source_filename():
907 Set global variable current_source_filename.
909 The actual filename is stored in current_filename. This is used by
910 the postprocessors, expecting the name "<standard input>" for stdin.
912 filename: In-out-parameter; is changed to the new value also.
915 remember_source_filename(const char *filename
)
918 if (strcmp(filename
, "-") == 0)
919 fname
= (char *)"<standard input>";
921 fname
= (char *)filename
;
922 size_t len
= strlen(fname
) + 1;
923 if (current_source_filename
!= 0)
924 free((char *)current_source_filename
);
925 current_source_filename
= (const char *)malloc(len
);
926 if (current_source_filename
== 0)
927 fatal("can't malloc space for filename");
928 strncpy((char *)current_source_filename
, (char *)fname
, len
);
932 Call draw method of printer class.
934 subcmd: Letter of actual D subcommand.
935 args: Array of integer arguments of actual D subcommand.
938 send_draw(const Char subcmd
, const IntArray
* const args
)
940 EnvInt n
= (EnvInt
) args
->len();
941 pr
->draw((int) subcmd
, (IntArg
*)args
->get_data(), n
, current_env
);
945 Go to next line within the input queue.
947 Skip the rest of the current line, including the newline character.
948 The global variable current_lineno is adjusted.
949 No errors are raised.
966 /* skip_line_checked ():
967 Check that there aren't any arguments left on the rest of the line,
970 Spaces, tabs, and a comment are allowed before newline or EOF.
971 All other characters raise an error.
974 skip_line_checked(void)
978 while (is_space_or_tab(c
))
997 /* skip_line_fatal ():
998 Fatal error if arguments left, otherwise skip line.
1000 Spaces, tabs, and a comment are allowed before newline or EOF.
1001 All other characters trigger the error.
1004 skip_line_fatal(void)
1006 bool ok
= skip_line_checked();
1009 error("too many arguments");
1014 /* skip_line_warn ():
1015 Skip line, but warn if arguments are left on actual line.
1017 Spaces, tabs, and a comment are allowed before newline or EOF.
1018 All other characters raise a warning
1021 skip_line_warn(void)
1023 bool ok
= skip_line_checked();
1026 warning("too many arguments on current line");
1032 Skip line in `D' commands.
1034 Decide whether in case of an additional argument a fatal error is
1035 raised (the documented classical behavior), only a warning is
1036 issued, or the line is just skipped (former groff behavior).
1037 Actually decided for the warning.
1043 // or: skip_line_fatal();
1048 Skip line in `x' commands.
1050 Decide whether in case of an additional argument a fatal error is
1051 raised (the documented classical behavior), only a warning is
1052 issued, or the line is just skipped (former groff behavior).
1053 Actually decided for the warning.
1059 // or: skip_line_fatal();
1063 /* skip_to_end_of_line():
1064 Go to the end of the current line.
1066 Skip the rest of the current line, excluding the newline character.
1067 The global variable current_lineno is not changed.
1068 No errors are raised.
1071 skip_to_end_of_line(void)
1073 Char c
= get_char();
1086 Restore character c onto input queue.
1088 Write a character back onto the input stream.
1089 EOF is gracefully handled.
1091 c: In-parameter; character to be pushed onto the input queue.
1094 unget_char(const Char c
)
1098 if (ungetc(ch
, current_file
) == EOF
)
1099 fatal("could not unget character");
1103 /**********************************************************************
1105 **********************************************************************/
1107 /* parse_color_command:
1108 Process the commands m and DF, but not Df.
1110 col: In-out-parameter; the color object to be set, must have
1111 been initialized before.
1114 parse_color_command(color
*col
)
1117 ColorArg red
= 0, green
= 0, blue
= 0;
1118 ColorArg cyan
= 0, magenta
= 0, yellow
= 0, black
= 0;
1119 Char subcmd
= next_arg_begin();
1120 switch((int) subcmd
) {
1121 case 'c': // DFc or mc: CMY
1122 cyan
= get_color_arg();
1123 magenta
= get_color_arg();
1124 yellow
= get_color_arg();
1125 col
->set_cmy(cyan
, magenta
, yellow
);
1127 case 'd': // DFd or md: set default color
1130 case 'g': // DFg or mg: gray
1131 gray
= get_color_arg();
1132 col
->set_gray(gray
);
1134 case 'k': // DFk or mk: CMYK
1135 cyan
= get_color_arg();
1136 magenta
= get_color_arg();
1137 yellow
= get_color_arg();
1138 black
= get_color_arg();
1139 col
->set_cmyk(cyan
, magenta
, yellow
, black
);
1141 case 'r': // DFr or mr: RGB
1142 red
= get_color_arg();
1143 green
= get_color_arg();
1144 blue
= get_color_arg();
1145 col
->set_rgb(red
, green
, blue
);
1148 error("invalid color scheme `%1'", (int) subcmd
);
1150 } // end of color subcommands
1153 /* parse_D_command():
1154 Parse the subcommands of graphical command D.
1156 This is the part of the do_file() parser that scans the graphical
1158 - Error on lacking or wrong arguments.
1159 - Warning on too many arguments.
1160 - Line is always skipped.
1165 Char subcmd
= next_arg_begin();
1166 switch((int) subcmd
) {
1167 case '~': // D~: draw B-spline
1168 // actually, this isn't available for some postprocessors
1170 default: // unknown options are passed to device
1172 IntArray
*args
= get_D_variable_args();
1173 send_draw(subcmd
, args
);
1174 position_to_end_of_args(args
);
1178 case 'a': // Da: draw arc
1180 IntArray
*args
= get_D_fixed_args(4);
1181 send_draw(subcmd
, args
);
1182 position_to_end_of_args(args
);
1186 case 'c': // Dc: draw circle line
1188 IntArray
*args
= get_D_fixed_args(1);
1189 send_draw(subcmd
, args
);
1190 // move to right end
1191 current_env
->hpos
+= (*args
)[0];
1195 case 'C': // DC: draw solid circle
1197 IntArray
*args
= get_D_fixed_args_odd_dummy(1);
1198 send_draw(subcmd
, args
);
1199 // move to right end
1200 current_env
->hpos
+= (*args
)[0];
1204 case 'e': // De: draw ellipse line
1205 case 'E': // DE: draw solid ellipse
1207 IntArray
*args
= get_D_fixed_args(2);
1208 send_draw(subcmd
, args
);
1209 // move to right end
1210 current_env
->hpos
+= (*args
)[0];
1214 case 'f': // Df: set fill gray; obsoleted by DFg
1216 IntArg arg
= get_integer_arg();
1217 if ((arg
>= 0) && (arg
<= 1000)) {
1218 // convert arg and treat it like DFg
1219 ColorArg gray
= color_from_Df_command(arg
);
1220 current_env
->fill
->set_gray(gray
);
1223 // set fill color to the same value as the current outline color
1224 delete current_env
->fill
;
1225 current_env
->fill
= new color(current_env
->col
);
1227 pr
->change_fill_color(current_env
);
1228 // skip unused `vertical' component (\D'...' always emits pairs)
1229 (void) get_integer_arg();
1230 # ifdef STUPID_DRAWING_POSITIONING
1231 current_env
->hpos
+= arg
;
1236 case 'F': // DF: set fill color, several formats
1237 parse_color_command(current_env
->fill
);
1238 pr
->change_fill_color(current_env
);
1239 // no positioning (setting-only command)
1242 case 'l': // Dl: draw line
1244 IntArray
*args
= get_D_fixed_args(2);
1245 send_draw(subcmd
, args
);
1246 position_to_end_of_args(args
);
1250 case 'p': // Dp: draw closed polygon line
1251 case 'P': // DP: draw solid closed polygon
1253 IntArray
*args
= get_D_variable_args();
1254 send_draw(subcmd
, args
);
1255 # ifdef STUPID_DRAWING_POSITIONING
1256 // final args positioning
1257 position_to_end_of_args(args
);
1262 case 't': // Dt: set line thickness
1264 IntArray
*args
= get_D_fixed_args_odd_dummy(1);
1265 send_draw(subcmd
, args
);
1266 # ifdef STUPID_DRAWING_POSITIONING
1267 // final args positioning
1268 position_to_end_of_args(args
);
1273 } // end of D subcommands
1276 /* parse_x_command():
1277 Parse subcommands of the device control command x.
1279 This is the part of the do_file() parser that scans the device
1280 controlling commands.
1281 - Error on duplicate prologue commands.
1282 - Error on wrong or lacking arguments.
1283 - Warning on too many arguments.
1284 - Line is always skipped.
1287 - current_env: is set by many subcommands.
1288 - npages: page counting variable
1290 Return: boolean in the meaning of `stopped'
1291 - true if parsing should be stopped (`x stop').
1292 - false if parsing should continue.
1295 parse_x_command(void)
1297 bool stopped
= false;
1298 char *subcmd_str
= get_string_arg();
1299 char subcmd
= subcmd_str
[0];
1301 case 'f': // x font: mount font
1303 IntArg n
= get_integer_arg();
1304 char *name
= get_string_arg();
1305 pr
->load_font(n
, name
);
1310 case 'F': // x Filename: set filename for errors
1312 char *str_arg
= get_extended_arg();
1314 warning("empty argument for `x F' command");
1316 remember_source_filename(str_arg
);
1321 case 'H': // x Height: set character height
1322 current_env
->height
= get_integer_arg();
1323 if (current_env
->height
== current_env
->size
)
1324 current_env
->height
= 0;
1327 case 'i': // x init: initialize device
1328 error("duplicate `x init' command");
1331 case 'p': // x pause: pause device
1334 case 'r': // x res: set resolution
1335 error("duplicate `x res' command");
1338 case 's': // x stop: stop device
1342 case 'S': // x Slant: set slant
1343 current_env
->slant
= get_integer_arg();
1346 case 't': // x trailer: generate trailer info
1349 case 'T': // x Typesetter: set typesetter
1350 error("duplicate `x T' command");
1353 case 'u': // x underline: from .cu
1355 char *str_arg
= get_string_arg();
1356 pr
->special(str_arg
, current_env
, 'u');
1361 case 'X': // x X: send uninterpretedly to device
1363 char *str_arg
= get_extended_arg(); // includes line skip
1365 error("`x X' command invalid before first `p' command");
1366 else if (str_arg
&& (strncmp(str_arg
, "devtag:",
1367 strlen("devtag:")) == 0))
1368 pr
->devtag(str_arg
, current_env
);
1370 pr
->special(str_arg
, current_env
);
1374 default: // ignore unknown x commands, but warn
1375 warning("unknown command `x %1'", subcmd
);
1378 a_delete subcmd_str
;
1382 /**********************************************************************
1383 exported part (by driver.h)
1384 **********************************************************************/
1387 Parse and postprocess groff intermediate output.
1389 filename: "-" for standard input, normal file name otherwise
1392 do_file(const char *filename
)
1395 bool stopped
= false; // terminating condition
1397 #ifdef USE_ENV_STACK
1398 EnvStack env_stack
= EnvStack();
1399 #endif // USE_ENV_STACK
1401 // setup of global variables
1404 // `pr' is initialized after the prologue.
1405 // `device' is set by the 1st prologue command.
1407 if (filename
[0] == '-' && filename
[1] == '\0')
1408 current_file
= stdin
;
1411 current_file
= fopen(filename
, "r");
1412 if (errno
!= 0 || current_file
== 0) {
1413 error("can't open file `%1'", filename
);
1417 remember_filename(filename
);
1419 if (current_env
!= 0)
1420 delete_current_env();
1421 current_env
= new environment
;
1422 current_env
->col
= new color
;
1423 current_env
->fill
= new color
;
1424 current_env
->fontno
= -1;
1425 current_env
->height
= 0;
1426 current_env
->hpos
= -1;
1427 current_env
->slant
= 0;
1428 current_env
->size
= 0;
1429 current_env
->vpos
= -1;
1431 // parsing of prologue (first 3 commands)
1436 // 1st command `x T'
1437 command
= next_command();
1438 if ((int) command
== EOF
)
1440 if ((int) command
!= 'x')
1441 fatal("the first command must be `x T'");
1442 str_arg
= get_string_arg();
1443 if (str_arg
[0] != 'T')
1444 fatal("the first command must be `x T'");
1446 char *tmp_dev
= get_string_arg();
1447 if (pr
== 0) { // note: `pr' initialized after prologue
1449 if (!font::load_desc())
1450 fatal("couldn't load DESC file, can't continue");
1453 if (device
== 0 || strcmp(device
, tmp_dev
) != 0)
1454 fatal("all files must use the same device");
1457 skip_line_x(); // ignore further arguments
1458 current_env
->size
= 10 * font::sizescale
;
1460 // 2nd command `x res'
1461 command
= next_command();
1462 if ((int) command
!= 'x')
1463 fatal("the second command must be `x res'");
1464 str_arg
= get_string_arg();
1465 if (str_arg
[0] != 'r')
1466 fatal("the second command must be `x res'");
1468 int_arg
= get_integer_arg();
1469 EnvInt font_res
= font::res
;
1470 if (int_arg
!= font_res
)
1471 fatal("resolution does not match");
1472 int_arg
= get_integer_arg();
1473 if (int_arg
!= font::hor
)
1474 fatal("minimum horizontal motion does not match");
1475 int_arg
= get_integer_arg();
1476 if (int_arg
!= font::vert
)
1477 fatal("minimum vertical motion does not match");
1478 skip_line_x(); // ignore further arguments
1480 // 3rd command `x init'
1481 command
= next_command();
1483 fatal("the third command must be `x init'");
1484 str_arg
= get_string_arg();
1485 if (str_arg
[0] != 'i')
1486 fatal("the third command must be `x init'");
1493 pr
= make_printer();
1495 command
= next_command();
1498 // spaces, tabs, comments, and newlines are skipped here
1499 switch ((int) command
) {
1500 case '#': // #: comment, ignore up to end of line
1503 #ifdef USE_ENV_STACK
1504 case '{': // {: start a new environment (a copy)
1505 env_stack
.push(current_env
);
1507 case '}': // }: pop previous env from stack
1508 delete_current_env();
1509 current_env
= env_stack
.pop();
1511 #endif // USE_ENV_STACK
1512 case '0': // ddc: obsolete jump and print command
1522 { // expect 2 digits and a character
1524 Char c
= next_arg_begin();
1526 fatal_command(command
);
1527 if (!isdigit((int) c
)) {
1528 error("digit expected");
1531 s
[0] = (char) command
;
1535 long int x
= strtol(s
, 0, 10);
1537 error("couldn't convert 2 digits");
1538 EnvInt hor_pos
= (EnvInt
) x
;
1539 current_env
->hpos
+= hor_pos
;
1540 c
= next_arg_begin();
1541 if ((int) c
== '\n' || (int) c
== EOF
)
1542 error("character argument expected");
1544 pr
->set_ascii_char((unsigned char) c
, current_env
);
1547 case 'c': // c: print ascii char without moving
1550 fatal_command(command
);
1551 Char c
= next_arg_begin();
1552 if (c
== '\n' || c
== EOF
)
1553 error("missing argument to `c' command");
1555 pr
->set_ascii_char((unsigned char) c
, current_env
);
1558 case 'C': // C: print named special character
1561 fatal_command(command
);
1562 char *str_arg
= get_string_arg();
1563 pr
->set_special_char(str_arg
, current_env
);
1567 case 'D': // drawing commands
1569 fatal_command(command
);
1572 case 'f': // f: set font to number
1573 current_env
->fontno
= get_integer_arg();
1575 case 'F': // F: obsolete, replaced by `x F'
1577 char *str_arg
= get_extended_arg();
1578 remember_source_filename(str_arg
);
1582 case 'h': // h: relative horizontal move
1583 current_env
->hpos
+= (EnvInt
) get_integer_arg();
1585 case 'H': // H: absolute horizontal positioning
1586 current_env
->hpos
= (EnvInt
) get_integer_arg();
1588 case 'm': // m: glyph color
1589 parse_color_command(current_env
->col
);
1590 pr
->change_color(current_env
);
1592 case 'n': // n: print end of line
1593 // ignore two arguments (historically)
1595 fatal_command(command
);
1597 (void) get_integer_arg();
1598 (void) get_integer_arg();
1600 case 'N': // N: print char with given int code
1602 fatal_command(command
);
1603 pr
->set_numbered_char(get_integer_arg(), current_env
);
1605 case 'p': // p: start new page with given number
1607 pr
->end_page(current_env
->vpos
);
1608 npages
++; // increment # of processed pages
1609 pr
->begin_page(get_integer_arg());
1610 current_env
->vpos
= 0;
1612 case 's': // s: set point size
1613 current_env
->size
= get_integer_arg();
1614 if (current_env
->height
== current_env
->size
)
1615 current_env
->height
= 0;
1617 case 't': // t: print a text word
1621 fatal_command(command
);
1622 char *str_arg
= get_string_arg();
1624 while ((c
= str_arg
[i
++]) != '\0') {
1626 pr
->set_ascii_char((unsigned char) c
, current_env
, &w
);
1627 current_env
->hpos
+= w
;
1632 case 'u': // u: print spaced word
1636 fatal_command(command
);
1637 EnvInt kern
= (EnvInt
) get_integer_arg();
1638 char *str_arg
= get_string_arg();
1640 while ((c
= str_arg
[i
++]) != '\0') {
1642 pr
->set_ascii_char((unsigned char) c
, current_env
, &w
);
1643 current_env
->hpos
+= w
+ kern
;
1648 case 'v': // v: relative vertical move
1649 current_env
->vpos
+= (EnvInt
) get_integer_arg();
1651 case 'V': // V: absolute vertical positioning
1652 current_env
->vpos
= (EnvInt
) get_integer_arg();
1654 case 'w': // w: inform about paddable space
1656 case 'x': // device controlling commands
1657 stopped
= parse_x_command();
1660 warning("unrecognized command `%1'", (unsigned char) command
);
1666 // end of file reached
1668 pr
->end_page(current_env
->vpos
);
1671 fclose(current_file
);
1672 // If `stopped' is not `true' here then there wasn't any `x stop'.
1674 warning("no final `x stop' command");
1675 delete_current_env();