3 // <groff_src_dir>/src/libs/libdriver/input.cpp
5 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005,
7 Free Software Foundation, Inc.
9 Written by James Clark (jjc@jclark.com)
10 Major rewrite 2001 by Bernd Warken (bwarken@mayn.de)
12 Last update: 5 Jan 2009
14 This file is part of groff, the GNU roff text processing system.
16 groff is free software; you can redistribute it and/or modify it
17 under the terms of the GNU General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
21 groff is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
32 This file implements the parser for the intermediate groff output,
33 see groff_out(5), and does the printout for the given device.
35 All parsed information is processed within the function do_file().
36 A device postprocessor just needs to fill in the methods for the class
37 `printer' (or rather a derived class) without having to worry about
38 the syntax of the intermediate output format. Consequently, the
39 programming of groff postprocessors is similar to the development of
42 The prototyping for this file is done in driver.h (and error.h).
45 /* Changes of the 2001 rewrite of this file.
47 The interface to the outside and the handling of the global
48 variables was not changed, but internally many necessary changes
51 The main aim for this rewrite is to provide a first step towards
52 making groff fully compatible with classical troff without pain.
55 - Unknown subcommands of `D' and `x' are now ignored like in the
56 classical case, but a warning is issued. This was also
57 implemented for the other commands.
58 - A warning is emitted if `x stop' is missing.
59 - `DC' and `DE' commands didn't position to the right end after
60 drawing (now they do), see discussion below.
61 - So far, `x stop' was ignored. Now it terminates the processing
62 of the current intermediate output file like the classical troff.
63 - The command `c' didn't check correctly on white-space.
64 - The environment stack wasn't suitable for the color extensions
65 (replaced by a class).
66 - The old groff parser could only handle a prologue with the first
67 3 lines having a fixed structure, while classical troff specified
68 the sequence of the first 3 commands without further
69 restrictions. Now the parser is smart about additional
70 white space, comments, and empty lines in the prologue.
71 - The old parser allowed space characters only as syntactical
72 separators, while classical troff had tab characters as well.
73 Now any sequence of tabs and/or spaces is a syntactical
74 separator between commands and/or arguments.
75 - Range checks for numbers implemented.
77 New and improved features
78 - The color commands `m' and `DF' are added.
79 - The old color command `Df' is now converted and delegated to `DFg'.
80 - The command `F' is implemented as `use intended file name'. It
81 checks whether its argument agrees with the file name used so far,
82 otherwise a warning is issued. Then the new name is remembered
83 and used for the following error messages.
84 - For the positioning after drawing commands, an alternative, easier
85 scheme is provided, but not yet activated; it can be chosen by
86 undefining the preprocessor macro STUPID_DRAWING_POSITIONING.
87 It extends the rule of the classical troff output language in a
88 logical way instead of the rather strange actual positioning.
89 For details, see the discussion below.
90 - For the `D' commands that only set the environment, the calling of
91 pr->send_draw() was removed because this doesn't make sense for
92 the `DF' commands; the (changed) environment is sent with the
94 - Error handling was clearly separated into warnings and fatal.
95 - The error behavior on additional arguments for `D' and `x'
96 commands with a fixed number of arguments was changed from being
97 ignored (former groff) to issue a warning and ignore (now), see
98 skip_line_x(). No fatal was chosen because both string and
99 integer arguments can occur.
100 - The gtroff program issues a trailing dummy integer argument for
101 some drawing commands with an odd number of arguments to make the
102 number of arguments even, e.g. the DC and Dt commands; this is
104 - All D commands with a variable number of args expect an even
105 number of trailing integer arguments, so fatal on error was
107 - Disable environment stack and the commands `{' and `}' by making
108 them conditional on macro USE_ENV_STACK; actually, this is
109 undefined by default. There isn't any known application for these
113 - Nested `switch' commands are avoided by using more functions.
114 Dangerous 'fall-through's avoided.
115 - Commands and functions are sorted alphabetically (where possible).
116 - Dynamic arrays/buffers are now implemented as container classes.
117 - Some functions had an ugly return structure; this has been
118 streamlined by using classes.
119 - Use standard C math functions for number handling, so getting rid
120 of differences to '0'.
121 - The macro `IntArg' has been created for an easier transition
122 to guaranteed 32 bits integers (`int' is enough for GNU, while
123 ANSI only guarantees `long int' to have a length of 32 bits).
124 - The many usages of type `int' are differentiated by using `Char',
125 `bool', and `IntArg' where appropriate.
126 - To ease the calls of the local utility functions, the parser
127 variables `current_file', `npages', and `current_env'
128 (formerly env) were made global to the file (formerly they were
129 local to the do_file() function)
130 - Various comments were added.
133 - Get rid of the stupid drawing positioning.
134 - Can the `Dt' command be completely handled by setting environment
135 within do_file() instead of sending to pr?
136 - Integer arguments must be >= 32 bits, use conditional #define.
137 - Add scaling facility for classical device independence and
138 non-groff devices. Classical troff output had a quasi device
139 independence by scaling the intermediate output to the resolution
140 of the postprocessor device if different from the one specified
141 with `x T', groff have not. So implement full quasi device
142 indepedence, including the mapping of the strange classical
143 devices to the postprocessor device (seems to be reasonably
145 - The external, global pointer variables are not optimally handled.
146 - The global variables `current_filename',
147 `current_source_filename', and `current_lineno' are only used for
148 error reporting. So implement a static class `Error'
150 - The global `device' is the name used during the formatting
151 process; there should be a new variable for the device name used
152 during the postprocessing.
153 - Implement the B-spline drawing `D~' for all graphical devices.
154 - Make `environment' a class with an overflow check for its members
155 and a delete method to get rid of delete_current_env().
156 - Implement the `EnvStack' to use `new' instead of `malloc'.
157 - The class definitions of this document could go into a new file.
158 - The comments in this section should go to a `Changelog' or some
159 `README' file in this directory.
163 Discussion of the positioning by drawing commands
165 There was some confusion about the positioning of the graphical
166 pointer at the printout after having executed a `D' command.
167 The classical troff manual of Ossanna & Kernighan specified,
169 `The position after a graphical object has been drawn is
170 at its end; for circles and ellipses, the "end" is at the
173 From this, it follows that
174 - all open figures (args, splines, and lines) should position at their
176 - all circles and ellipses should position at their right-most point
177 (as if 2 halves had been drawn).
178 - all closed figures apart from circles and ellipses shouldn't change
179 the position because they return to their origin.
180 - all setting commands should not change position because they do not
181 draw any graphical object.
183 In the case of the open figures, this means that the horizontal
184 displacement is the sum of all odd arguments and the vertical offset
185 the sum of all even arguments, called the alternate arguments sum
186 displacement in the following.
188 Unfortunately, groff did not implement this simple rule. The former
189 documentation in groff_out(5) differed from the source code, and
190 neither of them is compatible with the classical rule.
192 The former groff_out(5) specified to use the alternative arguments
193 sum displacement for calculating the drawing positioning of
194 non-classical commands, including the `Dt' command (setting-only)
195 and closed polygons. Applying this to the new groff color commands
196 will lead to disaster. For their arguments can take large values (>
197 65000). On low resolution devices, the displacement of such large
198 values will corrupt the display or kill the printer. So the
199 nonsense specification has come to a natural end anyway.
201 The groff source code, however, had no positioning for the
202 setting-only commands (esp. `Dt'), the right-end positioning for
203 outlined circles and ellipses, and the alternative argument sum
204 displacement for all other commands (including filled circles and
207 The reason why no one seems to have suffered from this mayhem so
208 far is that the graphical objects are usually generated by
209 preprocessors like pic that do not depend on the automatic
210 positioning. When using the low level `\D' escape sequences or `D'
211 output commands, the strange positionings can be circumvented by
212 absolute positionings or by tricks like `\Z'.
214 So doing an exorcism on the strange, incompatible displacements might
215 not harm any existing documents, but will make the usage of the
216 graphical escape sequences and commands natural.
218 That's why the rewrite of this file returned to the reasonable,
219 classical specification with its clear end-of-drawing rule that is
220 suitable for all cases. But a macro STUPID_DRAWING_POSITIONING is
221 provided for testing the funny former behavior.
223 The new rule implies the following behavior.
224 - Setting commands (`Dt', `Df', `DF') and polygons (`Dp' and `DP')
225 do not change position now.
226 - Filled circles and ellipses (`DC' and `DE') position at their
227 most right point (outlined ones `Dc' and `De' did this anyway).
228 - As before, all open graphical objects position to their final
229 drawing point (alternate sum of the command arguments).
233 #ifndef STUPID_DRAWING_POSITIONING
234 // uncomment next line if all non-classical D commands shall position
235 // to the strange alternate sum of args displacement
236 #define STUPID_DRAWING_POSITIONING
239 // Decide whether the commands `{' and `}' for different environments
252 /**********************************************************************
254 **********************************************************************/
256 // integer type used in the fields of struct environment (see printer.h)
259 // integer arguments of groff_out commands, must be >= 32 bits
262 // color components of groff_out color commands, must be >= 32 bits
263 typedef unsigned int ColorArg;
265 // Array for IntArg values.
267 size_t num_allocated;
272 IntArray(const size_t);
274 IntArg operator[](const size_t i) const
277 fatal("index out of range");
278 return (IntArg) data[i];
281 IntArg *get_data(void) const { return (IntArg *)data; }
282 size_t len(void) const { return num_stored; }
285 // Characters read from the input queue.
289 Char(void) : data('\0') {}
290 Char(const int c) : data(c) {}
291 bool operator==(char c) const { return (data == c) ? true : false; }
292 bool operator==(int c) const { return (data == c) ? true : false; }
293 bool operator==(const Char c) const
294 { return (data == c.data) ? true : false; }
295 bool operator!=(char c) const { return !(*this == c); }
296 bool operator!=(int c) const { return !(*this == c); }
297 bool operator!=(const Char c) const { return !(*this == c); }
298 operator int() const { return (int) data; }
299 operator unsigned char() const { return (unsigned char) data; }
300 operator char() const { return (char) data; }
303 // Buffer for string arguments (Char, not char).
305 size_t num_allocated;
307 Char *data; // not terminated by '\0'
309 StringBuf(void); // allocate without storing
311 void append(const Char); // append character to `data'
312 char *make_string(void); // return new copy of `data' with '\0'
313 bool is_empty(void) { // true if none stored
314 return (num_stored > 0) ? false : true;
316 void reset(void); // set `num_stored' to 0
322 size_t num_allocated;
327 environment *pop(void);
328 void push(environment *e);
330 #endif // USE_ENV_STACK
333 /**********************************************************************
335 **********************************************************************/
337 // exported as extern by error.h (called from driver.h)
338 // needed for error messages (see ../libgroff/error.cpp)
339 const char *current_filename = 0; // printable name of the current file
340 // printable name of current source file
341 const char *current_source_filename = 0;
342 int current_lineno = 0; // current line number of printout
344 // exported as extern by device.h;
345 const char *device = 0; // cancel former init with literal
351 // We rely on an implementation of the `new' operator which aborts
352 // gracefully if it can't allocate memory (e.g. from libgroff/new.cpp).
355 /**********************************************************************
356 static local variables
357 **********************************************************************/
359 FILE *current_file = 0; // current input stream for parser
361 // npages: number of pages processed so far (including current page),
362 // _not_ the page number in the printout (can be set with `p').
366 COLORARG_MAX = (ColorArg) 65536U; // == 0xFFFF + 1 == 0x10000
369 INTARG_MAX = (IntArg) 0x7FFFFFFF; // maximal signed 32 bits number
371 // parser environment, created and deleted by each run of do_file()
372 environment *current_env = 0;
376 envp_size = sizeof(environment *);
377 #endif // USE_ENV_STACK
380 /**********************************************************************
381 function declarations
382 **********************************************************************/
385 ColorArg color_from_Df_command(IntArg);
386 // transform old color into new
387 void delete_current_env(void); // delete global var current_env
388 void fatal_command(char); // abort for invalid command
389 inline Char get_char(void); // read next character from input stream
390 ColorArg get_color_arg(void); // read in argument for new color cmds
391 IntArray *get_D_fixed_args(const size_t);
392 // read in fixed number of integer
394 IntArray *get_D_fixed_args_odd_dummy(const size_t);
395 // read in a fixed number of integer
396 // arguments plus optional dummy
397 IntArray *get_D_variable_args(void);
398 // variable, even number of int args
399 char *get_extended_arg(void); // argument for `x X' (several lines)
400 IntArg get_integer_arg(void); // read in next integer argument
401 IntArray *get_possibly_integer_args();
402 // 0 or more integer arguments
403 char *get_string_arg(void); // read in next string arg, ended by WS
404 inline bool is_space_or_tab(const Char);
405 // test on space/tab char
406 Char next_arg_begin(void); // skip white space on current line
407 Char next_command(void); // go to next command, evt. diff. line
408 inline bool odd(const int); // test if integer is odd
409 void position_to_end_of_args(const IntArray * const);
410 // positioning after drawing
411 void remember_filename(const char *);
412 // set global current_filename
413 void remember_source_filename(const char *);
414 // set global current_source_filename
415 void send_draw(const Char, const IntArray * const);
417 void skip_line(void); // unconditionally skip to next line
418 bool skip_line_checked(void); // skip line, false if args are left
419 void skip_line_fatal(void); // skip line, fatal if args are left
420 void skip_line_warn(void); // skip line, warn if args are left
421 void skip_line_D(void); // skip line in D commands
422 void skip_line_x(void); // skip line in x commands
423 void skip_to_end_of_line(void); // skip to the end of the current line
424 inline void unget_char(const Char);
425 // restore character onto input
427 // parser subcommands
428 void parse_color_command(color *);
429 // color sub(sub)commands m and DF
430 void parse_D_command(void); // graphical subcommands
431 bool parse_x_command(void); // device controller subcommands
434 /**********************************************************************
436 **********************************************************************/
439 EnvStack::EnvStack(void)
442 // allocate pointer to array of num_allocated pointers to environment
443 data = (environment **)malloc(envp_size * num_allocated);
445 fatal("could not allocate environment data");
449 EnvStack::~EnvStack(void)
451 for (size_t i = 0; i < num_stored; i++)
456 // return top element from stack and decrease stack pointer
458 // the calling function must take care of properly deleting the result
463 environment *result = data[num_stored];
464 data[num_stored] = 0;
468 // copy argument and push this onto the stack
470 EnvStack::push(environment *e)
472 environment *e_copy = new environment;
473 if (num_stored >= num_allocated) {
474 environment **old_data = data;
476 data = (environment **)malloc(envp_size * num_allocated);
478 fatal("could not allocate data");
479 for (size_t i = 0; i < num_stored; i++)
480 data[i] = old_data[i];
483 e_copy->col = new color;
484 e_copy->fill = new color;
485 *e_copy->col = *e->col;
486 *e_copy->fill = *e->fill;
487 e_copy->fontno = e->fontno;
488 e_copy->height = e->height;
489 e_copy->hpos = e->hpos;
490 e_copy->size = e->size;
491 e_copy->slant = e->slant;
492 e_copy->vpos = e->vpos;
493 data[num_stored] = e_copy;
496 #endif // USE_ENV_STACK
498 IntArray::IntArray(void)
501 data = new IntArg[num_allocated];
505 IntArray::IntArray(const size_t n)
508 fatal("number of integers to be allocated must be > 0");
510 data = new IntArg[num_allocated];
514 IntArray::~IntArray(void)
520 IntArray::append(IntArg x)
522 if (num_stored >= num_allocated) {
523 IntArg *old_data = data;
525 data = new IntArg[num_allocated];
526 for (size_t i = 0; i < num_stored; i++)
527 data[i] = old_data[i];
530 data[num_stored] = x;
534 StringBuf::StringBuf(void)
538 data = new Char[num_allocated];
541 StringBuf::~StringBuf(void)
547 StringBuf::append(const Char c)
549 if (num_stored >= num_allocated) {
550 Char *old_data = data;
552 data = new Char[num_allocated];
553 for (size_t i = 0; i < num_stored; i++)
554 data[i] = old_data[i];
557 data[num_stored] = c;
562 StringBuf::make_string(void)
564 char *result = new char[num_stored + 1];
565 for (size_t i = 0; i < num_stored; i++)
566 result[i] = (char) data[i];
567 result[num_stored] = '\0';
572 StringBuf::reset(void)
577 /**********************************************************************
579 **********************************************************************/
581 //////////////////////////////////////////////////////////////////////
582 /* color_from_Df_command:
583 Process the gray shade setting command Df.
585 Transform Df style color into DF style color.
586 Df color: 0-1000, 0 is white
587 DF color: 0-65536, 0 is black
589 The Df command is obsoleted by command DFg, but kept for
593 color_from_Df_command(IntArg Df_gray)
595 return ColorArg((1000-Df_gray) * COLORARG_MAX / 1000); // scaling
598 //////////////////////////////////////////////////////////////////////
599 /* delete_current_env():
600 Delete global variable current_env and its pointer members.
602 This should be a class method of environment.
604 void delete_current_env(void)
606 delete current_env->col;
607 delete current_env->fill;
612 //////////////////////////////////////////////////////////////////////
614 Emit error message about invalid command and abort.
617 fatal_command(char command)
619 fatal("`%1' command invalid before first `p' command", command);
622 //////////////////////////////////////////////////////////////////////
624 Retrieve the next character from the input queue.
626 Return: The retrieved character (incl. EOF), converted to Char.
631 return (Char) getc(current_file);
634 //////////////////////////////////////////////////////////////////////
636 Retrieve an argument suitable for the color commands m and DF.
638 Return: The retrieved color argument.
643 IntArg x = get_integer_arg();
644 if (x < 0 || x > (IntArg)COLORARG_MAX) {
645 error("color component argument out of range");
651 //////////////////////////////////////////////////////////////////////
652 /* get_D_fixed_args():
653 Get a fixed number of integer arguments for D commands.
655 Fatal if wrong number of arguments.
656 Too many arguments on the line raise a warning.
659 number: In-parameter, the number of arguments to be retrieved.
660 ignore: In-parameter, ignore next argument -- GNU troff always emits
661 pairs of parameters for `D' extensions added by groff.
664 Return: New IntArray containing the arguments.
667 get_D_fixed_args(const size_t number)
670 fatal("requested number of arguments must be > 0");
671 IntArray *args = new IntArray(number);
672 for (size_t i = 0; i < number; i++)
673 args->append(get_integer_arg());
678 //////////////////////////////////////////////////////////////////////
679 /* get_D_fixed_args_odd_dummy():
680 Get a fixed number of integer arguments for D commands and optionally
681 ignore a dummy integer argument if the requested number is odd.
683 The gtroff program adds a dummy argument to some commands to get
684 an even number of arguments.
685 Error if the number of arguments differs from the scheme above.
688 number: In-parameter, the number of arguments to be retrieved.
690 Return: New IntArray containing the arguments.
693 get_D_fixed_args_odd_dummy(const size_t number)
696 fatal("requested number of arguments must be > 0");
697 IntArray *args = new IntArray(number);
698 for (size_t i = 0; i < number; i++)
699 args->append(get_integer_arg());
701 IntArray *a = get_possibly_integer_args();
703 error("too many arguments");
710 //////////////////////////////////////////////////////////////////////
711 /* get_D_variable_args():
712 Get a variable even number of integer arguments for D commands.
714 Get as many integer arguments as possible from the rest of the
716 - The arguments are separated by an arbitrary sequence of space or
718 - A comment, a newline, or EOF indicates the end of processing.
719 - Error on non-digit characters different from these.
720 - A final line skip is performed (except for EOF).
722 Return: New IntArray of the retrieved arguments.
725 get_D_variable_args()
727 IntArray *args = get_possibly_integer_args();
728 size_t n = args->len();
730 error("no arguments found");
732 error("even number of arguments expected");
737 //////////////////////////////////////////////////////////////////////
738 /* get_extended_arg():
739 Retrieve extended arg for `x X' command.
741 - Skip leading spaces and tabs, error on EOL or newline.
742 - Return everything before the next NL or EOF ('#' is not a comment);
743 as long as the following line starts with '+' this is returned
744 as well, with the '+' replaced by a newline.
745 - Final line skip is always performed.
747 Return: Allocated (new) string of retrieved text argument.
750 get_extended_arg(void)
752 StringBuf buf = StringBuf();
753 Char c = next_arg_begin();
754 while ((int) c != EOF) {
755 if ((int) c == '\n') {
759 buf.append((Char) '\n');
761 unget_char(c); // first character of next line
769 return buf.make_string();
772 //////////////////////////////////////////////////////////////////////
773 /* get_integer_arg(): Retrieve integer argument.
775 Skip leading spaces and tabs, collect an optional '-' and all
776 following decimal digits (at least one) up to the next non-digit,
777 which is restored onto the input queue.
779 Fatal error on all other situations.
781 Return: Retrieved integer.
784 get_integer_arg(void)
786 StringBuf buf = StringBuf();
787 Char c = next_arg_begin();
788 if ((int) c == '-') {
792 if (!isdigit((int) c))
793 error("integer argument expected");
794 while (isdigit((int) c)) {
800 char *s = buf.make_string();
802 long int number = strtol(s, 0, 10);
804 || number > INTARG_MAX || number < -INTARG_MAX) {
805 error("integer argument too large");
809 return (IntArg) number;
812 //////////////////////////////////////////////////////////////////////
813 /* get_possibly_integer_args():
814 Parse the rest of the input line as a list of integer arguments.
816 Get as many integer arguments as possible from the rest of the
817 current line, even none.
818 - The arguments are separated by an arbitrary sequence of space or
820 - A comment, a newline, or EOF indicates the end of processing.
821 - Error on non-digit characters different from these.
822 - No line skip is performed.
824 Return: New IntArray of the retrieved arguments.
827 get_possibly_integer_args()
830 StringBuf buf = StringBuf();
832 IntArray *args = new IntArray();
835 while (is_space_or_tab(c))
838 Char c1 = get_char();
839 if (isdigit((int) c1)) {
846 while (isdigit((int) c)) {
850 if (!buf.is_empty()) {
851 char *s = buf.make_string();
853 long int x = strtol(s, 0, 10);
855 || x > INTARG_MAX || x < -INTARG_MAX) {
856 error("invalid integer argument, set to 0");
859 args->append((IntArg) x);
862 // Here, c is not a digit.
863 // Terminate on comment, end of line, or end of file, while
864 // space or tab indicate continuation; otherwise error.
867 skip_to_end_of_line();
881 error("integer argument expected");
888 //////////////////////////////////////////////////////////////////////
892 - Skip leading spaces and tabs; error on EOL or newline.
893 - Return all following characters before the next space, tab,
894 newline, or EOF character (in-word '#' is not a comment character).
895 - The terminating space, tab, newline, or EOF character is restored
896 onto the input queue, so no line skip.
898 Return: Retrieved string as char *, allocated by 'new'.
903 StringBuf buf = StringBuf();
904 Char c = next_arg_begin();
905 while (!is_space_or_tab(c)
906 && c != Char('\n') && c != Char(EOF)) {
910 unget_char(c); // restore white space
911 return buf.make_string();
914 //////////////////////////////////////////////////////////////////////
915 /* is_space_or_tab():
916 Test a character if it is a space or tab.
918 c: In-parameter, character to be tested.
920 Return: True, if c is a space or tab character, false otherwise.
923 is_space_or_tab(const Char c)
925 return (c == Char(' ') || c == Char('\t')) ? true : false;
928 //////////////////////////////////////////////////////////////////////
930 Return first character of next argument.
932 Skip space and tab characters; error on newline or EOF.
934 Return: The first character different from these (including '#').
948 error("missing argument");
950 default: // first essential character
956 //////////////////////////////////////////////////////////////////////
958 Find the first character of the next command.
960 Skip spaces, tabs, comments (introduced by #), and newlines.
962 Return: The first character different from these (including EOF).
980 default: // EOF or first essential character
986 //////////////////////////////////////////////////////////////////////
988 Test whether argument is an odd number.
990 n: In-parameter, the integer to be tested.
992 Return: True if odd, false otherwise.
997 return (n & 1 == 1) ? true : false;
1000 //////////////////////////////////////////////////////////////////////
1001 /* position_to_end_of_args():
1002 Move graphical pointer to end of drawn figure.
1004 This is used by the D commands that draw open geometrical figures.
1005 The algorithm simply sums up all horizontal displacements (arguments
1006 with even number) for the horizontal component. Similarly, the
1007 vertical component is the sum of the odd arguments.
1009 args: In-parameter, the arguments of a former drawing command.
1012 position_to_end_of_args(const IntArray * const args)
1015 const size_t n = args->len();
1016 for (i = 0; i < n; i += 2)
1017 current_env->hpos += (*args)[i];
1018 for (i = 1; i < n; i += 2)
1019 current_env->vpos += (*args)[i];
1022 //////////////////////////////////////////////////////////////////////
1023 /* remember_filename():
1024 Set global variable current_filename.
1026 The actual filename is stored in current_filename. This is used by
1027 the postprocessors, expecting the name "<standard input>" for stdin.
1029 filename: In-out-parameter; is changed to the new value also.
1032 remember_filename(const char *filename)
1035 if (strcmp(filename, "-") == 0)
1036 fname = (char *)"<standard input>";
1038 fname = (char *)filename;
1039 size_t len = strlen(fname) + 1;
1040 if (current_filename != 0)
1041 free((char *)current_filename);
1042 current_filename = (const char *)malloc(len);
1043 if (current_filename == 0)
1044 fatal("can't malloc space for filename");
1045 strncpy((char *)current_filename, (char *)fname, len);
1048 //////////////////////////////////////////////////////////////////////
1049 /* remember_source_filename():
1050 Set global variable current_source_filename.
1052 The actual filename is stored in current_filename. This is used by
1053 the postprocessors, expecting the name "<standard input>" for stdin.
1055 filename: In-out-parameter; is changed to the new value also.
1058 remember_source_filename(const char *filename)
1061 if (strcmp(filename, "-") == 0)
1062 fname = (char *)"<standard input>";
1064 fname = (char *)filename;
1065 size_t len = strlen(fname) + 1;
1066 if (current_source_filename != 0)
1067 free((char *)current_source_filename);
1068 current_source_filename = (const char *)malloc(len);
1069 if (current_source_filename == 0)
1070 fatal("can't malloc space for filename");
1071 strncpy((char *)current_source_filename, (char *)fname, len);
1074 //////////////////////////////////////////////////////////////////////
1076 Call draw method of printer class.
1078 subcmd: Letter of actual D subcommand.
1079 args: Array of integer arguments of actual D subcommand.
1082 send_draw(const Char subcmd, const IntArray * const args)
1084 EnvInt n = (EnvInt) args->len();
1085 pr->draw((int) subcmd, (IntArg *)args->get_data(), n, current_env);
1088 //////////////////////////////////////////////////////////////////////
1090 Go to next line within the input queue.
1092 Skip the rest of the current line, including the newline character.
1093 The global variable current_lineno is adjusted.
1094 No errors are raised.
1099 Char c = get_char();
1111 //////////////////////////////////////////////////////////////////////
1112 /* skip_line_checked ():
1113 Check that there aren't any arguments left on the rest of the line,
1116 Spaces, tabs, and a comment are allowed before newline or EOF.
1117 All other characters raise an error.
1120 skip_line_checked(void)
1123 Char c = get_char();
1124 while (is_space_or_tab(c))
1127 case '#': // comment
1143 //////////////////////////////////////////////////////////////////////
1144 /* skip_line_fatal ():
1145 Fatal error if arguments left, otherwise skip line.
1147 Spaces, tabs, and a comment are allowed before newline or EOF.
1148 All other characters trigger the error.
1151 skip_line_fatal(void)
1153 bool ok = skip_line_checked();
1156 error("too many arguments");
1161 //////////////////////////////////////////////////////////////////////
1162 /* skip_line_warn ():
1163 Skip line, but warn if arguments are left on actual line.
1165 Spaces, tabs, and a comment are allowed before newline or EOF.
1166 All other characters raise a warning
1169 skip_line_warn(void)
1171 bool ok = skip_line_checked();
1174 warning("too many arguments on current line");
1179 //////////////////////////////////////////////////////////////////////
1181 Skip line in `D' commands.
1183 Decide whether in case of an additional argument a fatal error is
1184 raised (the documented classical behavior), only a warning is
1185 issued, or the line is just skipped (former groff behavior).
1186 Actually decided for the warning.
1192 // or: skip_line_fatal();
1196 //////////////////////////////////////////////////////////////////////
1198 Skip line in `x' commands.
1200 Decide whether in case of an additional argument a fatal error is
1201 raised (the documented classical behavior), only a warning is
1202 issued, or the line is just skipped (former groff behavior).
1203 Actually decided for the warning.
1209 // or: skip_line_fatal();
1213 //////////////////////////////////////////////////////////////////////
1214 /* skip_to_end_of_line():
1215 Go to the end of the current line.
1217 Skip the rest of the current line, excluding the newline character.
1218 The global variable current_lineno is not changed.
1219 No errors are raised.
1222 skip_to_end_of_line(void)
1224 Char c = get_char();
1236 //////////////////////////////////////////////////////////////////////
1238 Restore character c onto input queue.
1240 Write a character back onto the input stream.
1241 EOF is gracefully handled.
1243 c: In-parameter; character to be pushed onto the input queue.
1246 unget_char(const Char c)
1250 if (ungetc(ch, current_file) == EOF)
1251 fatal("could not unget character");
1256 /**********************************************************************
1258 **********************************************************************/
1260 //////////////////////////////////////////////////////////////////////
1261 /* parse_color_command:
1262 Process the commands m and DF, but not Df.
1264 col: In-out-parameter; the color object to be set, must have
1265 been initialized before.
1268 parse_color_command(color *col)
1271 ColorArg red = 0, green = 0, blue = 0;
1272 ColorArg cyan = 0, magenta = 0, yellow = 0, black = 0;
1273 Char subcmd = next_arg_begin();
1274 switch((int) subcmd) {
1275 case 'c': // DFc or mc: CMY
1276 cyan = get_color_arg();
1277 magenta = get_color_arg();
1278 yellow = get_color_arg();
1279 col->set_cmy(cyan, magenta, yellow);
1281 case 'd': // DFd or md: set default color
1284 case 'g': // DFg or mg: gray
1285 gray = get_color_arg();
1286 col->set_gray(gray);
1288 case 'k': // DFk or mk: CMYK
1289 cyan = get_color_arg();
1290 magenta = get_color_arg();
1291 yellow = get_color_arg();
1292 black = get_color_arg();
1293 col->set_cmyk(cyan, magenta, yellow, black);
1295 case 'r': // DFr or mr: RGB
1296 red = get_color_arg();
1297 green = get_color_arg();
1298 blue = get_color_arg();
1299 col->set_rgb(red, green, blue);
1302 error("invalid color scheme `%1'", (int) subcmd);
1304 } // end of color subcommands
1307 //////////////////////////////////////////////////////////////////////
1308 /* parse_D_command():
1309 Parse the subcommands of graphical command D.
1311 This is the part of the do_file() parser that scans the graphical
1313 - Error on lacking or wrong arguments.
1314 - Warning on too many arguments.
1315 - Line is always skipped.
1320 Char subcmd = next_arg_begin();
1321 switch((int) subcmd) {
1322 case '~': // D~: draw B-spline
1323 // actually, this isn't available for some postprocessors
1325 default: // unknown options are passed to device
1327 IntArray *args = get_D_variable_args();
1328 send_draw(subcmd, args);
1329 position_to_end_of_args(args);
1333 case 'a': // Da: draw arc
1335 IntArray *args = get_D_fixed_args(4);
1336 send_draw(subcmd, args);
1337 position_to_end_of_args(args);
1341 case 'c': // Dc: draw circle line
1343 IntArray *args = get_D_fixed_args(1);
1344 send_draw(subcmd, args);
1345 // move to right end
1346 current_env->hpos += (*args)[0];
1350 case 'C': // DC: draw solid circle
1352 IntArray *args = get_D_fixed_args_odd_dummy(1);
1353 send_draw(subcmd, args);
1354 // move to right end
1355 current_env->hpos += (*args)[0];
1359 case 'e': // De: draw ellipse line
1360 case 'E': // DE: draw solid ellipse
1362 IntArray *args = get_D_fixed_args(2);
1363 send_draw(subcmd, args);
1364 // move to right end
1365 current_env->hpos += (*args)[0];
1369 case 'f': // Df: set fill gray; obsoleted by DFg
1371 IntArg arg = get_integer_arg();
1372 if ((arg >= 0) && (arg <= 1000)) {
1373 // convert arg and treat it like DFg
1374 ColorArg gray = color_from_Df_command(arg);
1375 current_env->fill->set_gray(gray);
1378 // set fill color to the same value as the current outline color
1379 delete current_env->fill;
1380 current_env->fill = new color(current_env->col);
1382 pr->change_fill_color(current_env);
1383 // skip unused `vertical' component (\D'...' always emits pairs)
1384 (void) get_integer_arg();
1385 # ifdef STUPID_DRAWING_POSITIONING
1386 current_env->hpos += arg;
1391 case 'F': // DF: set fill color, several formats
1392 parse_color_command(current_env->fill);
1393 pr->change_fill_color(current_env);
1394 // no positioning (setting-only command)
1397 case 'l': // Dl: draw line
1399 IntArray *args = get_D_fixed_args(2);
1400 send_draw(subcmd, args);
1401 position_to_end_of_args(args);
1405 case 'p': // Dp: draw closed polygon line
1406 case 'P': // DP: draw solid closed polygon
1408 IntArray *args = get_D_variable_args();
1409 send_draw(subcmd, args);
1410 # ifdef STUPID_DRAWING_POSITIONING
1411 // final args positioning
1412 position_to_end_of_args(args);
1417 case 't': // Dt: set line thickness
1419 IntArray *args = get_D_fixed_args_odd_dummy(1);
1420 send_draw(subcmd, args);
1421 # ifdef STUPID_DRAWING_POSITIONING
1422 // final args positioning
1423 position_to_end_of_args(args);
1428 } // end of D subcommands
1431 //////////////////////////////////////////////////////////////////////
1432 /* parse_x_command():
1433 Parse subcommands of the device control command x.
1435 This is the part of the do_file() parser that scans the device
1436 controlling commands.
1437 - Error on duplicate prologue commands.
1438 - Error on wrong or lacking arguments.
1439 - Warning on too many arguments.
1440 - Line is always skipped.
1443 - current_env: is set by many subcommands.
1444 - npages: page counting variable
1446 Return: boolean in the meaning of `stopped'
1447 - true if parsing should be stopped (`x stop').
1448 - false if parsing should continue.
1451 parse_x_command(void)
1453 bool stopped = false;
1454 char *subcmd_str = get_string_arg();
1455 char subcmd = subcmd_str[0];
1457 case 'f': // x font: mount font
1459 IntArg n = get_integer_arg();
1460 char *name = get_string_arg();
1461 pr->load_font(n, name);
1466 case 'F': // x Filename: set filename for errors
1468 char *str_arg = get_extended_arg();
1470 warning("empty argument for `x F' command");
1472 remember_source_filename(str_arg);
1477 case 'H': // x Height: set character height
1478 current_env->height = get_integer_arg();
1479 if (current_env->height == current_env->size)
1480 current_env->height = 0;
1483 case 'i': // x init: initialize device
1484 error("duplicate `x init' command");
1487 case 'p': // x pause: pause device
1490 case 'r': // x res: set resolution
1491 error("duplicate `x res' command");
1494 case 's': // x stop: stop device
1498 case 'S': // x Slant: set slant
1499 current_env->slant = get_integer_arg();
1502 case 't': // x trailer: generate trailer info
1505 case 'T': // x Typesetter: set typesetter
1506 error("duplicate `x T' command");
1509 case 'u': // x underline: from .cu
1511 char *str_arg = get_string_arg();
1512 pr->special(str_arg, current_env, 'u');
1517 case 'X': // x X: send uninterpretedly to device
1519 char *str_arg = get_extended_arg(); // includes line skip
1521 error("`x X' command invalid before first `p' command");
1522 else if (str_arg && (strncmp(str_arg, "devtag:",
1523 strlen("devtag:")) == 0))
1524 pr->devtag(str_arg, current_env);
1526 pr->special(str_arg, current_env);
1530 default: // ignore unknown x commands, but warn
1531 warning("unknown command `x %1'", subcmd);
1534 a_delete subcmd_str;
1539 /**********************************************************************
1540 exported part (by driver.h)
1541 **********************************************************************/
1543 ////////////////////////////////////////////////////////////////////////
1545 Parse and postprocess groff intermediate output.
1547 filename: "-" for standard input, normal file name otherwise
1550 do_file(const char *filename)
1553 bool stopped = false; // terminating condition
1555 #ifdef USE_ENV_STACK
1556 EnvStack env_stack = EnvStack();
1557 #endif // USE_ENV_STACK
1559 // setup of global variables
1562 // `pr' is initialized after the prologue.
1563 // `device' is set by the 1st prologue command.
1565 if (filename[0] == '-' && filename[1] == '\0')
1566 current_file = stdin;
1569 current_file = fopen(filename, "r");
1570 if (errno != 0 || current_file == 0) {
1571 error("can't open file `%1'", filename);
1575 remember_filename(filename);
1577 if (current_env != 0)
1578 delete_current_env();
1579 current_env = new environment;
1580 current_env->col = new color;
1581 current_env->fill = new color;
1582 current_env->fontno = -1;
1583 current_env->height = 0;
1584 current_env->hpos = -1;
1585 current_env->slant = 0;
1586 current_env->size = 0;
1587 current_env->vpos = -1;
1589 // parsing of prologue (first 3 commands)
1594 // 1st command `x T'
1595 command = next_command();
1596 if ((int) command == EOF)
1598 if ((int) command != 'x')
1599 fatal("the first command must be `x T'");
1600 str_arg = get_string_arg();
1601 if (str_arg[0] != 'T')
1602 fatal("the first command must be `x T'");
1604 char *tmp_dev = get_string_arg();
1605 if (pr == 0) { // note: `pr' initialized after prologue
1607 if (!font::load_desc())
1608 fatal("couldn't load DESC file, can't continue");
1611 if (device == 0 || strcmp(device, tmp_dev) != 0)
1612 fatal("all files must use the same device");
1615 skip_line_x(); // ignore further arguments
1616 current_env->size = 10 * font::sizescale;
1618 // 2nd command `x res'
1619 command = next_command();
1620 if ((int) command != 'x')
1621 fatal("the second command must be `x res'");
1622 str_arg = get_string_arg();
1623 if (str_arg[0] != 'r')
1624 fatal("the second command must be `x res'");
1626 int_arg = get_integer_arg();
1627 EnvInt font_res = font::res;
1628 if (int_arg != font_res)
1629 fatal("resolution does not match");
1630 int_arg = get_integer_arg();
1631 if (int_arg != font::hor)
1632 fatal("minimum horizontal motion does not match");
1633 int_arg = get_integer_arg();
1634 if (int_arg != font::vert)
1635 fatal("minimum vertical motion does not match");
1636 skip_line_x(); // ignore further arguments
1638 // 3rd command `x init'
1639 command = next_command();
1641 fatal("the third command must be `x init'");
1642 str_arg = get_string_arg();
1643 if (str_arg[0] != 'i')
1644 fatal("the third command must be `x init'");
1651 pr = make_printer();
1653 command = next_command();
1656 // spaces, tabs, comments, and newlines are skipped here
1657 switch ((int) command) {
1658 case '#': // #: comment, ignore up to end of line
1661 #ifdef USE_ENV_STACK
1662 case '{': // {: start a new environment (a copy)
1663 env_stack.push(current_env);
1665 case '}': // }: pop previous env from stack
1666 delete_current_env();
1667 current_env = env_stack.pop();
1669 #endif // USE_ENV_STACK
1670 case '0': // ddc: obsolete jump and print command
1680 { // expect 2 digits and a character
1682 Char c = next_arg_begin();
1684 fatal_command(command);
1685 if (!isdigit((int) c)) {
1686 error("digit expected");
1689 s[0] = (char) command;
1693 long int x = strtol(s, 0, 10);
1695 error("couldn't convert 2 digits");
1696 EnvInt hor_pos = (EnvInt) x;
1697 current_env->hpos += hor_pos;
1698 c = next_arg_begin();
1699 if ((int) c == '\n' || (int) c == EOF)
1700 error("character argument expected");
1702 pr->set_ascii_char((unsigned char) c, current_env);
1705 case 'c': // c: print ascii char without moving
1708 fatal_command(command);
1709 Char c = next_arg_begin();
1710 if (c == '\n' || c == EOF)
1711 error("missing argument to `c' command");
1713 pr->set_ascii_char((unsigned char) c, current_env);
1716 case 'C': // C: print named special character
1719 fatal_command(command);
1720 char *str_arg = get_string_arg();
1721 pr->set_special_char(str_arg, current_env);
1725 case 'D': // drawing commands
1727 fatal_command(command);
1730 case 'f': // f: set font to number
1731 current_env->fontno = get_integer_arg();
1733 case 'F': // F: obsolete, replaced by `x F'
1735 char *str_arg = get_extended_arg();
1736 remember_source_filename(str_arg);
1740 case 'h': // h: relative horizontal move
1741 current_env->hpos += (EnvInt) get_integer_arg();
1743 case 'H': // H: absolute horizontal positioning
1744 current_env->hpos = (EnvInt) get_integer_arg();
1746 case 'm': // m: glyph color
1747 parse_color_command(current_env->col);
1748 pr->change_color(current_env);
1750 case 'n': // n: print end of line
1751 // ignore two arguments (historically)
1753 fatal_command(command);
1755 (void) get_integer_arg();
1756 (void) get_integer_arg();
1758 case 'N': // N: print char with given int code
1760 fatal_command(command);
1761 pr->set_numbered_char(get_integer_arg(), current_env);
1763 case 'p': // p: start new page with given number
1765 pr->end_page(current_env->vpos);
1766 npages++; // increment # of processed pages
1767 pr->begin_page(get_integer_arg());
1768 current_env->vpos = 0;
1770 case 's': // s: set point size
1771 current_env->size = get_integer_arg();
1772 if (current_env->height == current_env->size)
1773 current_env->height = 0;
1775 case 't': // t: print a text word
1779 fatal_command(command);
1780 char *str_arg = get_string_arg();
1782 while ((c = str_arg[i++]) != '\0') {
1784 pr->set_ascii_char((unsigned char) c, current_env, &w);
1785 current_env->hpos += w;
1790 case 'u': // u: print spaced word
1794 fatal_command(command);
1795 EnvInt kern = (EnvInt) get_integer_arg();
1796 char *str_arg = get_string_arg();
1798 while ((c = str_arg[i++]) != '\0') {
1800 pr->set_ascii_char((unsigned char) c, current_env, &w);
1801 current_env->hpos += w + kern;
1806 case 'v': // v: relative vertical move
1807 current_env->vpos += (EnvInt) get_integer_arg();
1809 case 'V': // V: absolute vertical positioning
1810 current_env->vpos = (EnvInt) get_integer_arg();
1812 case 'w': // w: inform about paddable space
1814 case 'x': // device controlling commands
1815 stopped = parse_x_command();
1818 warning("unrecognized command `%1'", (unsigned char) command);
1824 // end of file reached
1826 pr->end_page(current_env->vpos);
1829 fclose(current_file);
1830 // If `stopped' is not `true' here then there wasn't any `x stop'.
1832 warning("no final `x stop' command");
1833 delete_current_env();