3 // <groff_src_dir>/src/libs/libdriver/input.cpp
5 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005
6 Free Software Foundation, Inc.
8 Written by James Clark (jjc@jclark.com)
9 Major rewrite 2001 by Bernd Warken (bwarken@mayn.de)
11 Last update: 15 Jun 2005
13 This file is part of groff, the GNU roff text processing system.
15 groff is free software; you can redistribute it and/or modify it
16 under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 2, or (at your option)
20 groff is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with groff; see the file COPYING. If not, write to the Free
27 Software Foundation, 51 Franklin St - Fifth Floor, Boston, MA
33 This file implements the parser for the intermediate groff output,
34 see groff_out(5), and does the printout for the given device.
36 All parsed information is processed within the function do_file().
37 A device postprocessor just needs to fill in the methods for the class
38 `printer' (or rather a derived class) without having to worry about
39 the syntax of the intermediate output format. Consequently, the
40 programming of groff postprocessors is similar to the development of
43 The prototyping for this file is done in driver.h (and error.h).
46 /* Changes of the 2001 rewrite of this file.
48 The interface to the outside and the handling of the global
49 variables was not changed, but internally many necessary changes
52 The main aim for this rewrite is to provide a first step towards
53 making groff fully compatible with classical troff without pain.
56 - Unknown subcommands of `D' and `x' are now ignored like in the
57 classical case, but a warning is issued. This was also
58 implemented for the other commands.
59 - A warning is emitted if `x stop' is missing.
60 - `DC' and `DE' commands didn't position to the right end after
61 drawing (now they do), see discussion below.
62 - So far, `x stop' was ignored. Now it terminates the processing
63 of the current intermediate output file like the classical troff.
64 - The command `c' didn't check correctly on white-space.
65 - The environment stack wasn't suitable for the color extensions
66 (replaced by a class).
67 - The old groff parser could only handle a prologue with the first
68 3 lines having a fixed structure, while classical troff specified
69 the sequence of the first 3 commands without further
70 restrictions. Now the parser is smart about additional
71 white space, comments, and empty lines in the prologue.
72 - The old parser allowed space characters only as syntactical
73 separators, while classical troff had tab characters as well.
74 Now any sequence of tabs and/or spaces is a syntactical
75 separator between commands and/or arguments.
76 - Range checks for numbers implemented.
78 New and improved features
79 - The color commands `m' and `DF' are added.
80 - The old color command `Df' is now converted and delegated to `DFg'.
81 - The command `F' is implemented as `use intended file name'. It
82 checks whether its argument agrees with the file name used so far,
83 otherwise a warning is issued. Then the new name is remembered
84 and used for the following error messages.
85 - For the positioning after drawing commands, an alternative, easier
86 scheme is provided, but not yet activated; it can be chosen by
87 undefining the preprocessor macro STUPID_DRAWING_POSITIONING.
88 It extends the rule of the classical troff output language in a
89 logical way instead of the rather strange actual positioning.
90 For details, see the discussion below.
91 - For the `D' commands that only set the environment, the calling of
92 pr->send_draw() was removed because this doesn't make sense for
93 the `DF' commands; the (changed) environment is sent with the
95 - Error handling was clearly separated into warnings and fatal.
96 - The error behavior on additional arguments for `D' and `x'
97 commands with a fixed number of arguments was changed from being
98 ignored (former groff) to issue a warning and ignore (now), see
99 skip_line_x(). No fatal was chosen because both string and
100 integer arguments can occur.
101 - The gtroff program issues a trailing dummy integer argument for
102 some drawing commands with an odd number of arguments to make the
103 number of arguments even, e.g. the DC and Dt commands; this is
105 - All D commands with a variable number of args expect an even
106 number of trailing integer arguments, so fatal on error was
108 - Disable environment stack and the commands `{' and `}' by making
109 them conditional on macro USE_ENV_STACK; actually, this is
110 undefined by default. There isn't any known application for these
114 - Nested `switch' commands are avoided by using more functions.
115 Dangerous 'fall-through's avoided.
116 - Commands and functions are sorted alphabetically (where possible).
117 - Dynamic arrays/buffers are now implemented as container classes.
118 - Some functions had an ugly return structure; this has been
119 streamlined by using classes.
120 - Use standard C math functions for number handling, so getting rid
121 of differences to '0'.
122 - The macro `IntArg' has been created for an easier transition
123 to guaranteed 32 bits integers (`int' is enough for GNU, while
124 ANSI only guarantees `long int' to have a length of 32 bits).
125 - The many usages of type `int' are differentiated by using `Char',
126 `bool', and `IntArg' where appropriate.
127 - To ease the calls of the local utility functions, the parser
128 variables `current_file', `npages', and `current_env'
129 (formerly env) were made global to the file (formerly they were
130 local to the do_file() function)
131 - Various comments were added.
134 - Get rid of the stupid drawing positioning.
135 - Can the `Dt' command be completely handled by setting environment
136 within do_file() instead of sending to pr?
137 - Integer arguments must be >= 32 bits, use conditional #define.
138 - Add scaling facility for classical device independence and
139 non-groff devices. Classical troff output had a quasi device
140 independence by scaling the intermediate output to the resolution
141 of the postprocessor device if different from the one specified
142 with `x T', groff have not. So implement full quasi device
143 indepedence, including the mapping of the strange classical
144 devices to the postprocessor device (seems to be reasonably
146 - The external, global pointer variables are not optimally handled.
147 - The global variables `current_filename',
148 `current_source_filename', and `current_lineno' are only used for
149 error reporting. So implement a static class `Error'
151 - The global `device' is the name used during the formatting
152 process; there should be a new variable for the device name used
153 during the postprocessing.
154 - Implement the B-spline drawing `D~' for all graphical devices.
155 - Make `environment' a class with an overflow check for its members
156 and a delete method to get rid of delete_current_env().
157 - Implement the `EnvStack' to use `new' instead of `malloc'.
158 - The class definitions of this document could go into a new file.
159 - The comments in this section should go to a `Changelog' or some
160 `README' file in this directory.
164 Discussion of the positioning by drawing commands
166 There was some confusion about the positioning of the graphical
167 pointer at the printout after having executed a `D' command.
168 The classical troff manual of Osanna & Kernighan specified,
170 `The position after a graphical object has been drawn is
171 at its end; for circles and ellipses, the "end" is at the
174 From this, it follows that
175 - all open figures (args, splines, and lines) should position at their
177 - all circles and ellipses should position at their right-most point
178 (as if 2 halves had been drawn).
179 - all closed figures apart from circles and ellipses shouldn't change
180 the position because they return to their origin.
181 - all setting commands should not change position because they do not
182 draw any graphical object.
184 In the case of the open figures, this means that the horizontal
185 displacement is the sum of all odd arguments and the vertical offset
186 the sum of all even arguments, called the alternate arguments sum
187 displacement in the following.
189 Unfortunately, groff did not implement this simple rule. The former
190 documentation in groff_out(5) differed from the source code, and
191 neither of them is compatible with the classical rule.
193 The former groff_out(5) specified to use the alternative arguments
194 sum displacement for calculating the drawing positioning of
195 non-classical commands, including the `Dt' command (setting-only)
196 and closed polygons. Applying this to the new groff color commands
197 will lead to disaster. For their arguments can take large values (>
198 65000). On low resolution devices, the displacement of such large
199 values will corrupt the display or kill the printer. So the
200 nonsense specification has come to a natural end anyway.
202 The groff source code, however, had no positioning for the
203 setting-only commands (esp. `Dt'), the right-end positioning for
204 outlined circles and ellipses, and the alternative argument sum
205 displacement for all other commands (including filled circles and
208 The reason why no one seems to have suffered from this mayhem so
209 far is that the graphical objects are usually generated by
210 preprocessors like pic that do not depend on the automatic
211 positioning. When using the low level `\D' escape sequences or `D'
212 output commands, the strange positionings can be circumvented by
213 absolute positionings or by tricks like `\Z'.
215 So doing an exorcism on the strange, incompatible displacements might
216 not harm any existing documents, but will make the usage of the
217 graphical escape sequences and commands natural.
219 That's why the rewrite of this file returned to the reasonable,
220 classical specification with its clear end-of-drawing rule that is
221 suitable for all cases. But a macro STUPID_DRAWING_POSITIONING is
222 provided for testing the funny former behavior.
224 The new rule implies the following behavior.
225 - Setting commands (`Dt', `Df', `DF') and polygons (`Dp' and `DP')
226 do not change position now.
227 - Filled circles and ellipses (`DC' and `DE') position at their
228 most right point (outlined ones `Dc' and `De' did this anyway).
229 - As before, all open graphical objects position to their final
230 drawing point (alternate sum of the command arguments).
234 #ifndef STUPID_DRAWING_POSITIONING
235 // uncomment next line if all non-classical D commands shall position
236 // to the strange alternate sum of args displacement
237 #define STUPID_DRAWING_POSITIONING
240 // Decide whether the commands `{' and `}' for different environments
253 /**********************************************************************
255 **********************************************************************/
257 // integer type used in the fields of struct environment (see printer.h)
260 // integer arguments of groff_out commands, must be >= 32 bits
263 // color components of groff_out color commands, must be >= 32 bits
264 typedef unsigned int ColorArg;
266 // Array for IntArg values.
268 size_t num_allocated;
273 IntArray(const size_t);
275 IntArg operator[](const size_t i) const
278 fatal("index out of range");
279 return (IntArg) data[i];
282 IntArg *get_data(void) const { return (IntArg *)data; }
283 size_t len(void) const { return num_stored; }
286 // Characters read from the input queue.
290 Char(void) : data('\0') {}
291 Char(const int c) : data(c) {}
292 bool operator==(char c) const { return (data == c) ? true : false; }
293 bool operator==(int c) const { return (data == c) ? true : false; }
294 bool operator==(const Char c) const
295 { return (data == c.data) ? true : false; }
296 bool operator!=(char c) const { return !(*this == c); }
297 bool operator!=(int c) const { return !(*this == c); }
298 bool operator!=(const Char c) const { return !(*this == c); }
299 operator int() const { return (int) data; }
300 operator unsigned char() const { return (unsigned char) data; }
301 operator char() const { return (char) data; }
304 // Buffer for string arguments (Char, not char).
306 size_t num_allocated;
308 Char *data; // not terminated by '\0'
310 StringBuf(void); // allocate without storing
312 void append(const Char); // append character to `data'
313 char *make_string(void); // return new copy of `data' with '\0'
314 bool is_empty(void) { // true if none stored
315 return (num_stored > 0) ? false : true;
317 void reset(void); // set `num_stored' to 0
323 size_t num_allocated;
328 environment *pop(void);
329 void push(environment *e);
331 #endif // USE_ENV_STACK
334 /**********************************************************************
336 **********************************************************************/
338 // exported as extern by error.h (called from driver.h)
339 // needed for error messages (see ../libgroff/error.cpp)
340 const char *current_filename = 0; // printable name of the current file
341 // printable name of current source file
342 const char *current_source_filename = 0;
343 int current_lineno = 0; // current line number of printout
345 // exported as extern by device.h;
346 const char *device = 0; // cancel former init with literal
352 // We rely on an implementation of the `new' operator which aborts
353 // gracefully if it can't allocate memory (e.g. from libgroff/new.cpp).
356 /**********************************************************************
357 static local variables
358 **********************************************************************/
360 FILE *current_file = 0; // current input stream for parser
362 // npages: number of pages processed so far (including current page),
363 // _not_ the page number in the printout (can be set with `p').
367 COLORARG_MAX = (ColorArg) 65536U; // == 0xFFFF + 1 == 0x10000
370 INTARG_MAX = (IntArg) 0x7FFFFFFF; // maximal signed 32 bits number
372 // parser environment, created and deleted by each run of do_file()
373 environment *current_env = 0;
377 envp_size = sizeof(environment *);
378 #endif // USE_ENV_STACK
381 /**********************************************************************
382 function declarations
383 **********************************************************************/
386 ColorArg color_from_Df_command(IntArg);
387 // transform old color into new
388 void delete_current_env(void); // delete global var current_env
389 void fatal_command(char); // abort for invalid command
390 inline Char get_char(void); // read next character from input stream
391 ColorArg get_color_arg(void); // read in argument for new color cmds
392 IntArray *get_D_fixed_args(const size_t);
393 // read in fixed number of integer
395 IntArray *get_D_fixed_args_odd_dummy(const size_t);
396 // read in a fixed number of integer
397 // arguments plus optional dummy
398 IntArray *get_D_variable_args(void);
399 // variable, even number of int args
400 char *get_extended_arg(void); // argument for `x X' (several lines)
401 IntArg get_integer_arg(void); // read in next integer argument
402 IntArray *get_possibly_integer_args();
403 // 0 or more integer arguments
404 char *get_string_arg(void); // read in next string arg, ended by WS
405 inline bool is_space_or_tab(const Char);
406 // test on space/tab char
407 Char next_arg_begin(void); // skip white space on current line
408 Char next_command(void); // go to next command, evt. diff. line
409 inline bool odd(const int); // test if integer is odd
410 void position_to_end_of_args(const IntArray * const);
411 // positioning after drawing
412 void remember_filename(const char *);
413 // set global current_filename
414 void remember_source_filename(const char *);
415 // set global current_source_filename
416 void send_draw(const Char, const IntArray * const);
418 void skip_line(void); // unconditionally skip to next line
419 bool skip_line_checked(void); // skip line, false if args are left
420 void skip_line_fatal(void); // skip line, fatal if args are left
421 void skip_line_warn(void); // skip line, warn if args are left
422 void skip_line_D(void); // skip line in D commands
423 void skip_line_x(void); // skip line in x commands
424 void skip_to_end_of_line(void); // skip to the end of the current line
425 inline void unget_char(const Char);
426 // restore character onto input
428 // parser subcommands
429 void parse_color_command(color *);
430 // color sub(sub)commands m and DF
431 void parse_D_command(void); // graphical subcommands
432 bool parse_x_command(void); // device controller subcommands
435 /**********************************************************************
437 **********************************************************************/
440 EnvStack::EnvStack(void)
443 // allocate pointer to array of num_allocated pointers to environment
444 data = (environment **)malloc(envp_size * num_allocated);
446 fatal("could not allocate environment data");
450 EnvStack::~EnvStack(void)
452 for (size_t i = 0; i < num_stored; i++)
457 // return top element from stack and decrease stack pointer
459 // the calling function must take care of properly deleting the result
464 environment *result = data[num_stored];
465 data[num_stored] = 0;
469 // copy argument and push this onto the stack
471 EnvStack::push(environment *e)
473 environment *e_copy = new environment;
474 if (num_stored >= num_allocated) {
475 environment **old_data = data;
477 data = (environment **)malloc(envp_size * num_allocated);
479 fatal("could not allocate data");
480 for (size_t i = 0; i < num_stored; i++)
481 data[i] = old_data[i];
484 e_copy->col = new color;
485 e_copy->fill = new color;
486 *e_copy->col = *e->col;
487 *e_copy->fill = *e->fill;
488 e_copy->fontno = e->fontno;
489 e_copy->height = e->height;
490 e_copy->hpos = e->hpos;
491 e_copy->size = e->size;
492 e_copy->slant = e->slant;
493 e_copy->vpos = e->vpos;
494 data[num_stored] = e_copy;
497 #endif // USE_ENV_STACK
499 IntArray::IntArray(void)
502 data = new IntArg[num_allocated];
506 IntArray::IntArray(const size_t n)
509 fatal("number of integers to be allocated must be > 0");
511 data = new IntArg[num_allocated];
515 IntArray::~IntArray(void)
521 IntArray::append(IntArg x)
523 if (num_stored >= num_allocated) {
524 IntArg *old_data = data;
526 data = new IntArg[num_allocated];
527 for (size_t i = 0; i < num_stored; i++)
528 data[i] = old_data[i];
531 data[num_stored] = x;
535 StringBuf::StringBuf(void)
539 data = new Char[num_allocated];
542 StringBuf::~StringBuf(void)
548 StringBuf::append(const Char c)
550 if (num_stored >= num_allocated) {
551 Char *old_data = data;
553 data = new Char[num_allocated];
554 for (size_t i = 0; i < num_stored; i++)
555 data[i] = old_data[i];
558 data[num_stored] = c;
563 StringBuf::make_string(void)
565 char *result = new char[num_stored + 1];
566 for (size_t i = 0; i < num_stored; i++)
567 result[i] = (char) data[i];
568 result[num_stored] = '\0';
573 StringBuf::reset(void)
578 /**********************************************************************
580 **********************************************************************/
582 //////////////////////////////////////////////////////////////////////
583 /* color_from_Df_command:
584 Process the gray shade setting command Df.
586 Transform Df style color into DF style color.
587 Df color: 0-1000, 0 is white
588 DF color: 0-65536, 0 is black
590 The Df command is obsoleted by command DFg, but kept for
594 color_from_Df_command(IntArg Df_gray)
596 return ColorArg((1000-Df_gray) * COLORARG_MAX / 1000); // scaling
599 //////////////////////////////////////////////////////////////////////
600 /* delete_current_env():
601 Delete global variable current_env and its pointer members.
603 This should be a class method of environment.
605 void delete_current_env(void)
607 delete current_env->col;
608 delete current_env->fill;
613 //////////////////////////////////////////////////////////////////////
615 Emit error message about invalid command and abort.
618 fatal_command(char command)
620 fatal("`%1' command invalid before first `p' command", command);
623 //////////////////////////////////////////////////////////////////////
625 Retrieve the next character from the input queue.
627 Return: The retrieved character (incl. EOF), converted to Char.
632 return (Char) getc(current_file);
635 //////////////////////////////////////////////////////////////////////
637 Retrieve an argument suitable for the color commands m and DF.
639 Return: The retrieved color argument.
644 IntArg x = get_integer_arg();
645 if (x < 0 || x > (IntArg)COLORARG_MAX) {
646 error("color component argument out of range");
652 //////////////////////////////////////////////////////////////////////
653 /* get_D_fixed_args():
654 Get a fixed number of integer arguments for D commands.
656 Fatal if wrong number of arguments.
657 Too many arguments on the line raise a warning.
660 number: In-parameter, the number of arguments to be retrieved.
661 ignore: In-parameter, ignore next argument -- GNU troff always emits
662 pairs of parameters for `D' extensions added by groff.
665 Return: New IntArray containing the arguments.
668 get_D_fixed_args(const size_t number)
671 fatal("requested number of arguments must be > 0");
672 IntArray *args = new IntArray(number);
673 for (size_t i = 0; i < number; i++)
674 args->append(get_integer_arg());
679 //////////////////////////////////////////////////////////////////////
680 /* get_D_fixed_args_odd_dummy():
681 Get a fixed number of integer arguments for D commands and optionally
682 ignore a dummy integer argument if the requested number is odd.
684 The gtroff program adds a dummy argument to some commands to get
685 an even number of arguments.
686 Error if the number of arguments differs from the scheme above.
689 number: In-parameter, the number of arguments to be retrieved.
691 Return: New IntArray containing the arguments.
694 get_D_fixed_args_odd_dummy(const size_t number)
697 fatal("requested number of arguments must be > 0");
698 IntArray *args = new IntArray(number);
699 for (size_t i = 0; i < number; i++)
700 args->append(get_integer_arg());
702 IntArray *a = get_possibly_integer_args();
704 error("too many arguments");
711 //////////////////////////////////////////////////////////////////////
712 /* get_D_variable_args():
713 Get a variable even number of integer arguments for D commands.
715 Get as many integer arguments as possible from the rest of the
717 - The arguments are separated by an arbitrary sequence of space or
719 - A comment, a newline, or EOF indicates the end of processing.
720 - Error on non-digit characters different from these.
721 - A final line skip is performed (except for EOF).
723 Return: New IntArray of the retrieved arguments.
726 get_D_variable_args()
728 IntArray *args = get_possibly_integer_args();
729 size_t n = args->len();
731 error("no arguments found");
733 error("even number of arguments expected");
738 //////////////////////////////////////////////////////////////////////
739 /* get_extended_arg():
740 Retrieve extended arg for `x X' command.
742 - Skip leading spaces and tabs, error on EOL or newline.
743 - Return everything before the next NL or EOF ('#' is not a comment);
744 as long as the following line starts with '+' this is returned
745 as well, with the '+' replaced by a newline.
746 - Final line skip is always performed.
748 Return: Allocated (new) string of retrieved text argument.
751 get_extended_arg(void)
753 StringBuf buf = StringBuf();
754 Char c = next_arg_begin();
755 while ((int) c != EOF) {
756 if ((int) c == '\n') {
760 buf.append((Char) '\n');
762 unget_char(c); // first character of next line
770 return buf.make_string();
773 //////////////////////////////////////////////////////////////////////
774 /* get_integer_arg(): Retrieve integer argument.
776 Skip leading spaces and tabs, collect an optional '-' and all
777 following decimal digits (at least one) up to the next non-digit,
778 which is restored onto the input queue.
780 Fatal error on all other situations.
782 Return: Retrieved integer.
785 get_integer_arg(void)
787 StringBuf buf = StringBuf();
788 Char c = next_arg_begin();
789 if ((int) c == '-') {
793 if (!isdigit((int) c))
794 error("integer argument expected");
795 while (isdigit((int) c)) {
801 char *s = buf.make_string();
803 long int number = strtol(s, 0, 10);
805 || number > INTARG_MAX || number < -INTARG_MAX) {
806 error("integer argument too large");
810 return (IntArg) number;
813 //////////////////////////////////////////////////////////////////////
814 /* get_possibly_integer_args():
815 Parse the rest of the input line as a list of integer arguments.
817 Get as many integer arguments as possible from the rest of the
818 current line, even none.
819 - The arguments are separated by an arbitrary sequence of space or
821 - A comment, a newline, or EOF indicates the end of processing.
822 - Error on non-digit characters different from these.
823 - No line skip is performed.
825 Return: New IntArray of the retrieved arguments.
828 get_possibly_integer_args()
831 StringBuf buf = StringBuf();
833 IntArray *args = new IntArray();
836 while (is_space_or_tab(c))
839 Char c1 = get_char();
840 if (isdigit((int) c1)) {
847 while (isdigit((int) c)) {
851 if (!buf.is_empty()) {
852 char *s = buf.make_string();
854 long int x = strtol(s, 0, 10);
856 || x > INTARG_MAX || x < -INTARG_MAX) {
857 error("invalid integer argument, set to 0");
860 args->append((IntArg) x);
863 // Here, c is not a digit.
864 // Terminate on comment, end of line, or end of file, while
865 // space or tab indicate continuation; otherwise error.
868 skip_to_end_of_line();
882 error("integer argument expected");
889 //////////////////////////////////////////////////////////////////////
893 - Skip leading spaces and tabs; error on EOL or newline.
894 - Return all following characters before the next space, tab,
895 newline, or EOF character (in-word '#' is not a comment character).
896 - The terminating space, tab, newline, or EOF character is restored
897 onto the input queue, so no line skip.
899 Return: Retrieved string as char *, allocated by 'new'.
904 StringBuf buf = StringBuf();
905 Char c = next_arg_begin();
906 while (!is_space_or_tab(c)
907 && c != Char('\n') && c != Char(EOF)) {
911 unget_char(c); // restore white space
912 return buf.make_string();
915 //////////////////////////////////////////////////////////////////////
916 /* is_space_or_tab():
917 Test a character if it is a space or tab.
919 c: In-parameter, character to be tested.
921 Return: True, if c is a space or tab character, false otherwise.
924 is_space_or_tab(const Char c)
926 return (c == Char(' ') || c == Char('\t')) ? true : false;
929 //////////////////////////////////////////////////////////////////////
931 Return first character of next argument.
933 Skip space and tab characters; error on newline or EOF.
935 Return: The first character different from these (including '#').
949 error("missing argument");
951 default: // first essential character
957 //////////////////////////////////////////////////////////////////////
959 Find the first character of the next command.
961 Skip spaces, tabs, comments (introduced by #), and newlines.
963 Return: The first character different from these (including EOF).
981 default: // EOF or first essential character
987 //////////////////////////////////////////////////////////////////////
989 Test whether argument is an odd number.
991 n: In-parameter, the integer to be tested.
993 Return: True if odd, false otherwise.
998 return (n & 1 == 1) ? true : false;
1001 //////////////////////////////////////////////////////////////////////
1002 /* position_to_end_of_args():
1003 Move graphical pointer to end of drawn figure.
1005 This is used by the D commands that draw open geometrical figures.
1006 The algorithm simply sums up all horizontal displacements (arguments
1007 with even number) for the horizontal component. Similarly, the
1008 vertical component is the sum of the odd arguments.
1010 args: In-parameter, the arguments of a former drawing command.
1013 position_to_end_of_args(const IntArray * const args)
1016 const size_t n = args->len();
1017 for (i = 0; i < n; i += 2)
1018 current_env->hpos += (*args)[i];
1019 for (i = 1; i < n; i += 2)
1020 current_env->vpos += (*args)[i];
1023 //////////////////////////////////////////////////////////////////////
1024 /* remember_filename():
1025 Set global variable current_filename.
1027 The actual filename is stored in current_filename. This is used by
1028 the postprocessors, expecting the name "<standard input>" for stdin.
1030 filename: In-out-parameter; is changed to the new value also.
1033 remember_filename(const char *filename)
1036 if (strcmp(filename, "-") == 0)
1037 fname = (char *)"<standard input>";
1039 fname = (char *)filename;
1040 size_t len = strlen(fname) + 1;
1041 if (current_filename != 0)
1042 free((char *)current_filename);
1043 current_filename = (const char *)malloc(len);
1044 if (current_filename == 0)
1045 fatal("can't malloc space for filename");
1046 strncpy((char *)current_filename, (char *)fname, len);
1049 //////////////////////////////////////////////////////////////////////
1050 /* remember_source_filename():
1051 Set global variable current_source_filename.
1053 The actual filename is stored in current_filename. This is used by
1054 the postprocessors, expecting the name "<standard input>" for stdin.
1056 filename: In-out-parameter; is changed to the new value also.
1059 remember_source_filename(const char *filename)
1062 if (strcmp(filename, "-") == 0)
1063 fname = (char *)"<standard input>";
1065 fname = (char *)filename;
1066 size_t len = strlen(fname) + 1;
1067 if (current_source_filename != 0)
1068 free((char *)current_source_filename);
1069 current_source_filename = (const char *)malloc(len);
1070 if (current_source_filename == 0)
1071 fatal("can't malloc space for filename");
1072 strncpy((char *)current_source_filename, (char *)fname, len);
1075 //////////////////////////////////////////////////////////////////////
1077 Call draw method of printer class.
1079 subcmd: Letter of actual D subcommand.
1080 args: Array of integer arguments of actual D subcommand.
1083 send_draw(const Char subcmd, const IntArray * const args)
1085 EnvInt n = (EnvInt) args->len();
1086 pr->draw((int) subcmd, (IntArg *)args->get_data(), n, current_env);
1089 //////////////////////////////////////////////////////////////////////
1091 Go to next line within the input queue.
1093 Skip the rest of the current line, including the newline character.
1094 The global variable current_lineno is adjusted.
1095 No errors are raised.
1100 Char c = get_char();
1112 //////////////////////////////////////////////////////////////////////
1113 /* skip_line_checked ():
1114 Check that there aren't any arguments left on the rest of the line,
1117 Spaces, tabs, and a comment are allowed before newline or EOF.
1118 All other characters raise an error.
1121 skip_line_checked(void)
1124 Char c = get_char();
1125 while (is_space_or_tab(c))
1128 case '#': // comment
1144 //////////////////////////////////////////////////////////////////////
1145 /* skip_line_fatal ():
1146 Fatal error if arguments left, otherwise skip line.
1148 Spaces, tabs, and a comment are allowed before newline or EOF.
1149 All other characters trigger the error.
1152 skip_line_fatal(void)
1154 bool ok = skip_line_checked();
1157 error("too many arguments");
1162 //////////////////////////////////////////////////////////////////////
1163 /* skip_line_warn ():
1164 Skip line, but warn if arguments are left on actual line.
1166 Spaces, tabs, and a comment are allowed before newline or EOF.
1167 All other characters raise a warning
1170 skip_line_warn(void)
1172 bool ok = skip_line_checked();
1175 warning("too many arguments on current line");
1180 //////////////////////////////////////////////////////////////////////
1182 Skip line in `D' commands.
1184 Decide whether in case of an additional argument a fatal error is
1185 raised (the documented classical behavior), only a warning is
1186 issued, or the line is just skipped (former groff behavior).
1187 Actually decided for the warning.
1193 // or: skip_line_fatal();
1197 //////////////////////////////////////////////////////////////////////
1199 Skip line in `x' commands.
1201 Decide whether in case of an additional argument a fatal error is
1202 raised (the documented classical behavior), only a warning is
1203 issued, or the line is just skipped (former groff behavior).
1204 Actually decided for the warning.
1210 // or: skip_line_fatal();
1214 //////////////////////////////////////////////////////////////////////
1215 /* skip_to_end_of_line():
1216 Go to the end of the current line.
1218 Skip the rest of the current line, excluding the newline character.
1219 The global variable current_lineno is not changed.
1220 No errors are raised.
1223 skip_to_end_of_line(void)
1225 Char c = get_char();
1237 //////////////////////////////////////////////////////////////////////
1239 Restore character c onto input queue.
1241 Write a character back onto the input stream.
1242 EOF is gracefully handled.
1244 c: In-parameter; character to be pushed onto the input queue.
1247 unget_char(const Char c)
1251 if (ungetc(ch, current_file) == EOF)
1252 fatal("could not unget character");
1257 /**********************************************************************
1259 **********************************************************************/
1261 //////////////////////////////////////////////////////////////////////
1262 /* parse_color_command:
1263 Process the commands m and DF, but not Df.
1265 col: In-out-parameter; the color object to be set, must have
1266 been initialized before.
1269 parse_color_command(color *col)
1272 ColorArg red = 0, green = 0, blue = 0;
1273 ColorArg cyan = 0, magenta = 0, yellow = 0, black = 0;
1274 Char subcmd = next_arg_begin();
1275 switch((int) subcmd) {
1276 case 'c': // DFc or mc: CMY
1277 cyan = get_color_arg();
1278 magenta = get_color_arg();
1279 yellow = get_color_arg();
1280 col->set_cmy(cyan, magenta, yellow);
1282 case 'd': // DFd or md: set default color
1285 case 'g': // DFg or mg: gray
1286 gray = get_color_arg();
1287 col->set_gray(gray);
1289 case 'k': // DFk or mk: CMYK
1290 cyan = get_color_arg();
1291 magenta = get_color_arg();
1292 yellow = get_color_arg();
1293 black = get_color_arg();
1294 col->set_cmyk(cyan, magenta, yellow, black);
1296 case 'r': // DFr or mr: RGB
1297 red = get_color_arg();
1298 green = get_color_arg();
1299 blue = get_color_arg();
1300 col->set_rgb(red, green, blue);
1303 error("invalid color scheme `%1'", (int) subcmd);
1305 } // end of color subcommands
1308 //////////////////////////////////////////////////////////////////////
1309 /* parse_D_command():
1310 Parse the subcommands of graphical command D.
1312 This is the part of the do_file() parser that scans the graphical
1314 - Error on lacking or wrong arguments.
1315 - Warning on too many arguments.
1316 - Line is always skipped.
1321 Char subcmd = next_arg_begin();
1322 switch((int) subcmd) {
1323 case '~': // D~: draw B-spline
1324 // actually, this isn't available for some postprocessors
1326 default: // unknown options are passed to device
1328 IntArray *args = get_D_variable_args();
1329 send_draw(subcmd, args);
1330 position_to_end_of_args(args);
1334 case 'a': // Da: draw arc
1336 IntArray *args = get_D_fixed_args(4);
1337 send_draw(subcmd, args);
1338 position_to_end_of_args(args);
1342 case 'c': // Dc: draw circle line
1344 IntArray *args = get_D_fixed_args(1);
1345 send_draw(subcmd, args);
1346 // move to right end
1347 current_env->hpos += (*args)[0];
1351 case 'C': // DC: draw solid circle
1353 IntArray *args = get_D_fixed_args_odd_dummy(1);
1354 send_draw(subcmd, args);
1355 // move to right end
1356 current_env->hpos += (*args)[0];
1360 case 'e': // De: draw ellipse line
1361 case 'E': // DE: draw solid ellipse
1363 IntArray *args = get_D_fixed_args(2);
1364 send_draw(subcmd, args);
1365 // move to right end
1366 current_env->hpos += (*args)[0];
1370 case 'f': // Df: set fill gray; obsoleted by DFg
1372 IntArg arg = get_integer_arg();
1373 if ((arg >= 0) && (arg <= 1000)) {
1374 // convert arg and treat it like DFg
1375 ColorArg gray = color_from_Df_command(arg);
1376 current_env->fill->set_gray(gray);
1379 // set fill color to the same value as the current outline color
1380 delete current_env->fill;
1381 current_env->fill = new color(current_env->col);
1383 pr->change_fill_color(current_env);
1384 // skip unused `vertical' component (\D'...' always emits pairs)
1385 (void) get_integer_arg();
1386 # ifdef STUPID_DRAWING_POSITIONING
1387 current_env->hpos += arg;
1392 case 'F': // DF: set fill color, several formats
1393 parse_color_command(current_env->fill);
1394 pr->change_fill_color(current_env);
1395 // no positioning (setting-only command)
1398 case 'l': // Dl: draw line
1400 IntArray *args = get_D_fixed_args(2);
1401 send_draw(subcmd, args);
1402 position_to_end_of_args(args);
1406 case 'p': // Dp: draw closed polygon line
1407 case 'P': // DP: draw solid closed polygon
1409 IntArray *args = get_D_variable_args();
1410 send_draw(subcmd, args);
1411 # ifdef STUPID_DRAWING_POSITIONING
1412 // final args positioning
1413 position_to_end_of_args(args);
1418 case 't': // Dt: set line thickness
1420 IntArray *args = get_D_fixed_args_odd_dummy(1);
1421 send_draw(subcmd, args);
1422 # ifdef STUPID_DRAWING_POSITIONING
1423 // final args positioning
1424 position_to_end_of_args(args);
1429 } // end of D subcommands
1432 //////////////////////////////////////////////////////////////////////
1433 /* parse_x_command():
1434 Parse subcommands of the device control command x.
1436 This is the part of the do_file() parser that scans the device
1437 controlling commands.
1438 - Error on duplicate prologue commands.
1439 - Error on wrong or lacking arguments.
1440 - Warning on too many arguments.
1441 - Line is always skipped.
1444 - current_env: is set by many subcommands.
1445 - npages: page counting variable
1447 Return: boolean in the meaning of `stopped'
1448 - true if parsing should be stopped (`x stop').
1449 - false if parsing should continue.
1452 parse_x_command(void)
1454 bool stopped = false;
1455 char *subcmd_str = get_string_arg();
1456 char subcmd = subcmd_str[0];
1458 case 'f': // x font: mount font
1460 IntArg n = get_integer_arg();
1461 char *name = get_string_arg();
1462 pr->load_font(n, name);
1467 case 'F': // x Filename: set filename for errors
1469 char *str_arg = get_string_arg();
1471 warning("empty argument for `x F' command");
1473 remember_source_filename(str_arg);
1478 case 'H': // x Height: set character height
1479 current_env->height = get_integer_arg();
1480 if (current_env->height == current_env->size)
1481 current_env->height = 0;
1484 case 'i': // x init: initialize device
1485 error("duplicate `x init' command");
1488 case 'p': // x pause: pause device
1491 case 'r': // x res: set resolution
1492 error("duplicate `x res' command");
1495 case 's': // x stop: stop device
1499 case 'S': // x Slant: set slant
1500 current_env->slant = get_integer_arg();
1503 case 't': // x trailer: generate trailer info
1506 case 'T': // x Typesetter: set typesetter
1507 error("duplicate `x T' command");
1510 case 'u': // x underline: from .cu
1512 char *str_arg = get_string_arg();
1513 pr->special(str_arg, current_env, 'u');
1518 case 'X': // x X: send uninterpretedly to device
1520 char *str_arg = get_extended_arg(); // includes line skip
1522 error("`x X' command invalid before first `p' command");
1523 else if (str_arg && (strncmp(str_arg, "devtag:",
1524 strlen("devtag:")) == 0))
1525 pr->devtag(str_arg, current_env);
1527 pr->special(str_arg, current_env);
1531 default: // ignore unknown x commands, but warn
1532 warning("unknown command `x %1'", subcmd);
1535 a_delete subcmd_str;
1540 /**********************************************************************
1541 exported part (by driver.h)
1542 **********************************************************************/
1544 ////////////////////////////////////////////////////////////////////////
1546 Parse and postprocess groff intermediate output.
1548 filename: "-" for standard input, normal file name otherwise
1551 do_file(const char *filename)
1554 bool stopped = false; // terminating condition
1556 #ifdef USE_ENV_STACK
1557 EnvStack env_stack = EnvStack();
1558 #endif // USE_ENV_STACK
1560 // setup of global variables
1563 // `pr' is initialized after the prologue.
1564 // `device' is set by the 1st prologue command.
1566 if (filename[0] == '-' && filename[1] == '\0')
1567 current_file = stdin;
1570 current_file = fopen(filename, "r");
1571 if (errno != 0 || current_file == 0) {
1572 error("can't open file `%1'", filename);
1576 remember_filename(filename);
1578 if (current_env != 0)
1579 delete_current_env();
1580 current_env = new environment;
1581 current_env->col = new color;
1582 current_env->fill = new color;
1583 current_env->fontno = -1;
1584 current_env->height = 0;
1585 current_env->hpos = -1;
1586 current_env->slant = 0;
1587 current_env->size = 0;
1588 current_env->vpos = -1;
1590 // parsing of prologue (first 3 commands)
1595 // 1st command `x T'
1596 command = next_command();
1597 if ((int) command == EOF)
1599 if ((int) command != 'x')
1600 fatal("the first command must be `x T'");
1601 str_arg = get_string_arg();
1602 if (str_arg[0] != 'T')
1603 fatal("the first command must be `x T'");
1605 char *tmp_dev = get_string_arg();
1606 if (pr == 0) { // note: `pr' initialized after prologue
1608 if (!font::load_desc())
1609 fatal("couldn't load DESC file, can't continue");
1612 if (device == 0 || strcmp(device, tmp_dev) != 0)
1613 fatal("all files must use the same device");
1616 skip_line_x(); // ignore further arguments
1617 current_env->size = 10 * font::sizescale;
1619 // 2nd command `x res'
1620 command = next_command();
1621 if ((int) command != 'x')
1622 fatal("the second command must be `x res'");
1623 str_arg = get_string_arg();
1624 if (str_arg[0] != 'r')
1625 fatal("the second command must be `x res'");
1627 int_arg = get_integer_arg();
1628 EnvInt font_res = font::res;
1629 if (int_arg != font_res)
1630 fatal("resolution does not match");
1631 int_arg = get_integer_arg();
1632 if (int_arg != font::hor)
1633 fatal("minimum horizontal motion does not match");
1634 int_arg = get_integer_arg();
1635 if (int_arg != font::vert)
1636 fatal("minimum vertical motion does not match");
1637 skip_line_x(); // ignore further arguments
1639 // 3rd command `x init'
1640 command = next_command();
1642 fatal("the third command must be `x init'");
1643 str_arg = get_string_arg();
1644 if (str_arg[0] != 'i')
1645 fatal("the third command must be `x init'");
1652 pr = make_printer();
1654 command = next_command();
1657 // spaces, tabs, comments, and newlines are skipped here
1658 switch ((int) command) {
1659 case '#': // #: comment, ignore up to end of line
1662 #ifdef USE_ENV_STACK
1663 case '{': // {: start a new environment (a copy)
1664 env_stack.push(current_env);
1666 case '}': // }: pop previous env from stack
1667 delete_current_env();
1668 current_env = env_stack.pop();
1670 #endif // USE_ENV_STACK
1671 case '0': // ddc: obsolete jump and print command
1681 { // expect 2 digits and a character
1683 Char c = next_arg_begin();
1685 fatal_command(command);
1686 if (!isdigit((int) c)) {
1687 error("digit expected");
1690 s[0] = (char) command;
1694 long int x = strtol(s, 0, 10);
1696 error("couldn't convert 2 digits");
1697 EnvInt hor_pos = (EnvInt) x;
1698 current_env->hpos += hor_pos;
1699 c = next_arg_begin();
1700 if ((int) c == '\n' || (int) c == EOF)
1701 error("character argument expected");
1703 pr->set_ascii_char((unsigned char) c, current_env);
1706 case 'c': // c: print ascii char without moving
1709 fatal_command(command);
1710 Char c = next_arg_begin();
1711 if (c == '\n' || c == EOF)
1712 error("missing argument to `c' command");
1714 pr->set_ascii_char((unsigned char) c, current_env);
1717 case 'C': // C: print named special character
1720 fatal_command(command);
1721 char *str_arg = get_string_arg();
1722 pr->set_special_char(str_arg, current_env);
1726 case 'D': // drawing commands
1728 fatal_command(command);
1731 case 'f': // f: set font to number
1732 current_env->fontno = get_integer_arg();
1734 case 'F': // F: obsolete, replaced by `x F'
1736 char *str_arg = get_string_arg();
1737 remember_source_filename(str_arg);
1741 case 'h': // h: relative horizontal move
1742 current_env->hpos += (EnvInt) get_integer_arg();
1744 case 'H': // H: absolute horizontal positioning
1745 current_env->hpos = (EnvInt) get_integer_arg();
1747 case 'm': // m: glyph color
1748 parse_color_command(current_env->col);
1749 pr->change_color(current_env);
1751 case 'n': // n: print end of line
1752 // ignore two arguments (historically)
1754 fatal_command(command);
1756 (void) get_integer_arg();
1757 (void) get_integer_arg();
1759 case 'N': // N: print char with given int code
1761 fatal_command(command);
1762 pr->set_numbered_char(get_integer_arg(), current_env);
1764 case 'p': // p: start new page with given number
1766 pr->end_page(current_env->vpos);
1767 npages++; // increment # of processed pages
1768 pr->begin_page(get_integer_arg());
1769 current_env->vpos = 0;
1771 case 's': // s: set point size
1772 current_env->size = get_integer_arg();
1773 if (current_env->height == current_env->size)
1774 current_env->height = 0;
1776 case 't': // t: print a text word
1780 fatal_command(command);
1781 char *str_arg = get_string_arg();
1783 while ((c = str_arg[i++]) != '\0') {
1785 pr->set_ascii_char((unsigned char) c, current_env, &w);
1786 current_env->hpos += w;
1791 case 'u': // u: print spaced word
1795 fatal_command(command);
1796 EnvInt kern = (EnvInt) get_integer_arg();
1797 char *str_arg = get_string_arg();
1799 while ((c = str_arg[i++]) != '\0') {
1801 pr->set_ascii_char((unsigned char) c, current_env, &w);
1802 current_env->hpos += w + kern;
1807 case 'v': // v: relative vertical move
1808 current_env->vpos += (EnvInt) get_integer_arg();
1810 case 'V': // V: absolute vertical positioning
1811 current_env->vpos = (EnvInt) get_integer_arg();
1813 case 'w': // w: inform about paddable space
1815 case 'x': // device controlling commands
1816 stopped = parse_x_command();
1819 warning("unrecognized command `%1'", (unsigned char) command);
1825 // end of file reached
1827 pr->end_page(current_env->vpos);
1830 fclose(current_file);
1831 // If `stopped' is not `true' here then there wasn't any `x stop'.
1833 warning("no final `x stop' command");
1834 delete_current_env();