3 // <groff_src_dir>/src/libs/libdriver/input.cc
5 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002
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: 12 Apr 2002
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, 59 Temple Place - Suite 330, 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() by
37 using the global object `pr' of class `printer'. So a device
38 postprocessor just needs to fill in the methods for the class
39 `printer' without having to worry about the syntax of the
40 intermediate output format. Consequently, the programming of groff
41 postprocessors is similar to the development of device-drivers.
43 The prototyping for this file is done in driver.h (and error.h).
45 Postprocessor programs must deallocate the global variables `pr' and
46 `device' using `delete', and `current_filename' using
50 /* Changes of the 2001 rewrite of this file.
52 The interface to the outside and the handling of the global
53 variables was not changed, but internally many necessary changes
56 The main aim for this rewrite is to provide a first step towards
57 making groff fully compatible with classical troff without pain.
60 - Unknown subcommands of `D' and `x' are now ignored like in the
61 classical case, but a warning is issued. This was also
62 implemented for the other commands.
63 - A warning is emitted if `x stop' is missing.
64 - `DC' and `DE' commands didn't position to the right end after
65 drawing (now they do), see discussion below.
66 - So far, `x stop' was ignored. Now it terminates the processing
67 of the current intermediate output file like the classical troff.
68 - The command `c' didn't check correctly on white-space.
69 - The environment stack wasn't suitable for the color extensions
70 (replaced by a class).
71 - The old groff parser could only handle a prologue with the first
72 3 lines having a fixed structure, while classical troff specified
73 the sequence of the first 3 commands without further
74 restrictions. Now the parser is smart about additional
75 white space, comments, and empty lines in the prologue.
76 - The old parser allowed space characters only as syntactical
77 separators, while classical troff had tab characters as well.
78 Now any sequence of tabs and/or spaces is a syntactical
79 separator between commands and/or arguments.
80 - Range checks for numbers implemented.
82 New and improved features
83 - The color commands `m' and `DF' are added.
84 - The old color command `Df' is now converted and delegated to `DFg'.
85 - The command `F' is implemented as `use intended file name'. It
86 checks whether its argument agrees with the file name used so far,
87 otherwise a warning is issued. Then the new name is remembered
88 and used for the following error messages.
89 - For the positioning after drawing commands, an alternative, easier
90 scheme is provided, but not yet activated; it can be chosen by
91 undefining the preprocessor macro STUPID_DRAWING_POSITIONING.
92 It extends the rule of the classical troff output language in a
93 logical way instead of the rather strange actual positioning.
94 For details, see the discussion below.
95 - For the `D' commands that only set the environment, the calling of
96 pr->send_draw() was removed because this doesn't make sense for
97 the `DF' commands; the (changed) environment is sent with the
99 - Error handling was clearly separated into warnings and fatal.
100 - The error behavior on additional arguments for `D' and `x'
101 commands with a fixed number of arguments was changed from being
102 ignored (former groff) to issue a warning and ignore (now), see
103 skip_line_x(). No fatal was chosen because both string and
104 integer arguments can occur.
105 - The gtroff program issues a trailing dummy integer argument for
106 some drawing commands with an odd number of arguments to make the
107 number of arguments even, e.g. the DC and Dt commands; this is
109 - All D commands with a variable number of args expect an even
110 number of trailing integer arguments, so fatal on error was
112 - Disable environment stack and the commands `{' and `}' by making
113 them conditional on macro USE_ENV_STACK; actually, this is
114 undefined by default. There isn't any known application for these
118 - Nested `switch' commands are avoided by using more functions.
119 Dangerous 'fall-through's avoided.
120 - Commands and functions are sorted alphabetically (where possible).
121 - Dynamic arrays/buffers are now implemented as container classes.
122 - Some functions had an ugly return structure; this has been
123 streamlined by using classes.
124 - Use standard C math functions for number handling, so getting rid
125 of differences to '0'.
126 - The macro `IntArg' has been created for an easier transition
127 to guaranteed 32 bits integers (`int' is enough for GNU, while
128 ANSI only guarantees `long int' to have a length of 32 bits).
129 - The many usages of type `int' are differentiated by using `Char',
130 `bool', and `IntArg' where appropriate.
131 - To ease the calls of the local utility functions, the parser
132 variables `current_file', `npages', and `current_env'
133 (formerly env) were made global to the file (formerly they were
134 local to the do_file() function)
135 - Various comments were added.
138 - Get rid of the stupid drawing positioning.
139 - Can the `Dt' command be completely handled by setting environment
140 within do_file() instead of sending to pr?
141 - Integer arguments must be >= 32 bits, use conditional #define.
142 - Add scaling facility for classical device independence and
143 non-groff devices. Classical troff output had a quasi device
144 independence by scaling the intermediate output to the resolution
145 of the postprocessor device if different from the one specified
146 with `x T', groff have not. So implement full quasi device
147 indepedence, including the mapping of the strange classical
148 devices to the postprocessor device (seems to be reasonably
150 - The external, global pointer variables are not optimally handled.
151 - `pr' isn't used outside besides initialization and deletion.
152 So it could be replaced by a static local variable. For
153 example, a wrapper class `Postprocessor' for class `printer' with
154 internal make_printer() and automatic clean-up would make sense.
155 - The global variables `current_filename' and `current_lineno' are
156 only used for error reporting. So implement a static class
157 `Error' (`::' calls).
158 - The global `device' is the name used during the formatting
159 process; there should be a new variable for the device name used
160 during the postprocessing.
161 - Implement the B-spline drawing `D~' for all graphical devices.
162 - Make `environment' a class with an overflow check for its members
163 and a delete method to get rid of delete_current_env().
164 - Implement the `EnvStack' to use `new' instead of `malloc'.
165 - The class definitions of this document could go into a new file.
166 - The comments in this section should go to a `Changelog' or some
167 `README' file in this directory.
171 Discussion of the positioning by drawing commands
173 There was some confusion about the positioning of the graphical
174 pointer at the printout after having executed a `D' command.
175 The classical troff manual of Osanna & Kernighan specified,
177 `The position after a graphical object has been drawn is
178 at its end; for circles and ellipses, the "end" is at the
181 From this, it follows that
182 - all open figures (args, splines, and lines) should position at their
184 - all circles and ellipses should position at their right-most point
185 (as if 2 halves had been drawn).
186 - all closed figures apart from circles and ellipses shouldn't change
187 the position because they return to their origin.
188 - all setting commands should not change position because they do not
189 draw any graphical object.
191 In the case of the open figures, this means that the horizontal
192 displacement is the sum of all odd arguments and the vertical offset
193 the sum of all even arguments, called the alternate arguments sum
194 displacement in the following.
196 Unfortunately, groff did not implement this simple rule. The former
197 documentation in groff_out(5) differed from the source code, and
198 neither of them is compatible with the classical rule.
200 The former groff_out(5) specified to use the alternative arguments
201 sum displacement for calculating the drawing positioning of
202 non-classical commands, including the `Dt' command (setting-only)
203 and closed polygons. Applying this to the new groff color commands
204 will lead to disaster. For their arguments can take large values (>
205 65000). On low resolution devices, the displacement of such large
206 values will corrupt the display or kill the printer. So the
207 nonsense specification has come to a natural end anyway.
209 The groff source code, however, had no positioning for the
210 setting-only commands (esp. `Dt'), the right-end positioning for
211 outlined circles and ellipses, and the alternative argument sum
212 displacement for all other commands (including filled circles and
215 The reason why no one seems to have suffered from this mayhem so
216 far is that the graphical objects are usually generated by
217 preprocessors like pic that do not depend on the automatic
218 positioning. When using the low level `\D' escape sequences or `D'
219 output commands, the strange positionings can be circumvented by
220 absolute positionings or by tricks like `\Z'.
222 So doing an exorcism on the strange, incompatible displacements might
223 not harm any existing documents, but will make the usage of the
224 graphical escape sequences and commands natural.
226 That's why the rewrite of this file returned to the reasonable,
227 classical specification with its clear end-of-drawing rule that is
228 suitable for all cases. But a macro STUPID_DRAWING_POSITIONING is
229 provided for testing the funny former behavior.
231 The new rule implies the following behavior.
232 - Setting commands (`Dt', `Df', `DF') and polygons (`Dp' and `DP')
233 do not change position now.
234 - Filled circles and ellipses (`DC' and `DE') position at their
235 most right point (outlined ones `Dc' and `De' did this anyway).
236 - As before, all open graphical objects position to their final
237 drawing point (alternate sum of the command arguments).
241 #ifndef STUPID_DRAWING_POSITIONING
242 // uncomment next line if all non-classical D commands shall position
243 // to the strange alternate sum of args displacement
244 #define STUPID_DRAWING_POSITIONING
247 // Decide whether the commands `{' and `}' for different environments
260 /**********************************************************************
262 **********************************************************************/
264 // integer type used in the fields of struct environment (see printer.h)
267 // integer arguments of groff_out commands, must be >= 32 bits
270 // color components of groff_out color commands, must be >= 32 bits
271 typedef unsigned int ColorArg;
273 // Array for IntArg values.
275 size_t num_allocated;
280 IntArray(const size_t);
282 const IntArg operator[](const size_t i) const
284 if (i >= num_stored || i < 0)
285 fatal("index out of range");
286 return (const IntArg) data[i];
290 get_data(void) const { return (const IntArg * const) data; }
291 const size_t len(void) const { return num_stored; }
294 // Characters read from the input queue.
298 Char(void) : data('\0') {}
299 Char(const int c) : data(c) {}
300 bool operator==(char c) const { return (data == c) ? true : false; }
301 bool operator==(int c) const { return (data == c) ? true : false; }
302 bool operator==(const Char c) const
303 { return (data == c.data) ? true : false; }
304 bool operator!=(char c) const { return !(*this == c); }
305 bool operator!=(int c) const { return !(*this == c); }
306 bool operator!=(const Char c) const { return !(*this == c); }
307 operator int() const { return (int) data; }
308 operator unsigned char() const { return (unsigned char) data; }
309 operator char() const { return (char) data; }
312 // Buffer for string arguments (Char, not char).
314 size_t num_allocated;
316 Char *data; // not terminated by '\0'
318 StringBuf(void); // allocate without storing
320 void append(const Char); // append character to `data'
321 char *make_string(void); // return new copy of `data' with '\0'
322 bool is_empty(void) { // true if none stored
323 return (num_stored > 0) ? false : true;
325 void reset(void); // set `num_stored' to 0
331 size_t num_allocated;
336 environment *pop(void);
337 void push(environment *e);
339 #endif // USE_ENV_STACK
342 /**********************************************************************
344 **********************************************************************/
346 // exported as extern by error.h (called from driver.h)
347 // needed for error messages (see ../libgroff/error.cc)
348 const char *current_filename = 0; // printable name of the current file
349 int current_lineno = 0; // current line number of printout
351 // exported as extern by device.h;
352 const char *device = 0; // cancel former init with literal
354 // from driver.h; pr is kept between several runs of do_file()
355 // extern printer *pr;
359 // We rely on an implementation of the `new' operator which aborts
360 // gracefully if it can't allocate memory (e.g. from libgroff/new.cc).
363 /**********************************************************************
364 static local variables
365 **********************************************************************/
367 FILE *current_file = 0; // current input stream for parser
369 // npages: number of pages processed so far (including current page),
370 // _not_ the page number in the printout (can be set with `p').
374 COLORARG_MAX = (ColorArg) 65536U; // == 0xFFFF + 1 == 0x10000
377 INTARG_MAX = (IntArg) 0x7FFFFFFF; // maximal signed 32 bits number
379 // parser environment, created and deleted by each run of do_file()
380 environment *current_env = 0;
384 envp_size = sizeof(environment *);
385 #endif // USE_ENV_STACK
388 /**********************************************************************
389 function declarations
390 **********************************************************************/
393 ColorArg color_from_Df_command(IntArg);
394 // transform old color into new
395 void delete_current_env(void); // delete global var current_env
396 void fatal_command(char); // abort for invalid command
397 inline Char get_char(void); // read next character from input stream
398 ColorArg get_color_arg(void); // read in argument for new color cmds
399 IntArray *get_D_fixed_args(const size_t);
400 // read in fixed number of integer
402 IntArray *get_D_fixed_args_odd_dummy(const size_t);
403 // read in a fixed number of integer
404 // arguments plus optional dummy
405 IntArray *get_D_variable_args(void);
406 // variable, even number of int args
407 char *get_extended_arg(void); // argument for `x X' (several lines)
408 IntArg get_integer_arg(void); // read in next integer argument
409 IntArray *get_possibly_integer_args();
410 // 0 or more integer arguments
411 char *get_string_arg(void); // read in next string arg, ended by WS
412 inline bool is_space_or_tab(const Char);
413 // test on space/tab char
414 Char next_arg_begin(void); // skip white space on current line
415 Char next_command(void); // go to next command, evt. diff. line
416 inline bool odd(const int); // test if integer is odd
417 void position_to_end_of_args(const IntArray * const);
418 // positioning after drawing
419 void remember_filename(const char *);
420 // set global current_filename
421 void send_draw(const Char, const IntArray * const);
423 void skip_line(void); // unconditionally skip to next line
424 bool skip_line_checked(void); // skip line, false if args are left
425 void skip_line_fatal(void); // skip line, fatal if args are left
426 void skip_line_warn(void); // skip line, warn if args are left
427 void skip_line_D(void); // skip line in D commands
428 void skip_line_x(void); // skip line in x commands
429 void skip_to_end_of_line(void); // skip to the end of the current line
430 inline void unget_char(const Char);
431 // restore character onto input
433 // parser subcommands
434 void parse_color_command(color *);
435 // color sub(sub)commands m and DF
436 void parse_D_command(void); // graphical subcommands
437 bool parse_x_command(void); // device controller subcommands
440 /**********************************************************************
442 **********************************************************************/
445 EnvStack::EnvStack(void)
448 // allocate pointer to array of num_allocated pointers to environment
449 data = (environment **) malloc(envp_size * num_allocated);
451 fatal("could not allocate environment data");
455 EnvStack::~EnvStack(void)
457 for (size_t i = 0; i < num_stored; i++)
462 // return top element from stack and decrease stack pointer
464 // the calling function must take care of properly deleting the result
469 environment *result = data[num_stored];
470 data[num_stored] = 0;
474 // copy argument and push this onto the stack
476 EnvStack::push(environment *e)
478 environment *e_copy = new environment;
479 if (num_stored >= num_allocated) {
480 environment **old_data = data;
482 data = (environment **) malloc(envp_size * num_allocated);
484 fatal("could not allocate data");
485 for (size_t i = 0; i < num_stored; i++)
486 data[i] = old_data[i];
489 e_copy->col = new color;
490 e_copy->fill = new color;
491 *e_copy->col = *e->col;
492 *e_copy->fill = *e->fill;
493 e_copy->fontno = e->fontno;
494 e_copy->height = e->height;
495 e_copy->hpos = e->hpos;
496 e_copy->size = e->size;
497 e_copy->slant = e->slant;
498 e_copy->vpos = e->vpos;
499 data[num_stored] = e_copy;
502 #endif // USE_ENV_STACK
504 IntArray::IntArray(void)
507 data = new IntArg[num_allocated];
511 IntArray::IntArray(const size_t n)
514 fatal("number of integers to be allocated must be > 0");
516 data = new IntArg[num_allocated];
520 IntArray::~IntArray(void)
526 IntArray::append(IntArg x)
528 if (num_stored >= num_allocated) {
529 IntArg *old_data = data;
531 data = new IntArg[num_allocated];
532 for (size_t i = 0; i < num_stored; i++)
533 data[i] = old_data[i];
536 data[num_stored] = x;
540 StringBuf::StringBuf(void)
544 data = new Char[num_allocated];
547 StringBuf::~StringBuf(void)
553 StringBuf::append(const Char c)
555 if (num_stored >= num_allocated) {
556 Char *old_data = data;
558 data = new Char[num_allocated];
559 for (size_t i = 0; i < num_stored; i++)
560 data[i] = old_data[i];
563 data[num_stored] = c;
568 StringBuf::make_string(void)
570 char *result = new char[num_stored + 1];
571 for (size_t i = 0; i < num_stored; i++)
572 result[i] = (char) data[i];
573 result[num_stored] = '\0';
578 StringBuf::reset(void)
583 /**********************************************************************
585 **********************************************************************/
587 //////////////////////////////////////////////////////////////////////
588 /* color_from_Df_command:
589 Process the gray shade setting command Df.
591 Transform Df style color into DF style color.
592 Df color: 0-1000, 0 is white
593 DF color: 0-65536, 0 is black
595 The Df command is obsoleted by command DFg, but kept for
598 XXX: Add proper handling for values < 0 or > 1000 as documented in
602 color_from_Df_command(IntArg Df_gray)
608 return ColorArg((1000-Df_gray) * COLORARG_MAX / 1000); // scaling
611 //////////////////////////////////////////////////////////////////////
612 /* delete_current_env():
613 Delete global variable current_env and its pointer members.
615 This should be a class method of environment.
617 void delete_current_env(void)
619 delete current_env->col;
620 delete current_env->fill;
624 //////////////////////////////////////////////////////////////////////
626 Emit error message about invalid command and abort.
629 fatal_command(char command)
631 fatal("`%1' command invalid before first `p' command", command);
634 //////////////////////////////////////////////////////////////////////
636 Retrieve the next character from the input queue.
638 Return: The retrieved character (incl. EOF), converted to Char.
643 return (Char) getc(current_file);
646 //////////////////////////////////////////////////////////////////////
648 Retrieve an argument suitable for the color commands m and DF.
650 Return: The retrieved color argument.
655 IntArg x = get_integer_arg();
656 if (x < 0 || x > (IntArg)COLORARG_MAX) {
657 error("color component argument out of range");
663 //////////////////////////////////////////////////////////////////////
664 /* get_D_fixed_args():
665 Get a fixed number of integer arguments for D commands.
667 Fatal if wrong number of arguments.
668 Too many arguments on the line raise a warning.
671 number: In-parameter, the number of arguments to be retrieved.
672 ignore: In-parameter, ignore next argument -- GNU troff always emits
673 pairs of parameters for `D' extensions added by groff.
676 Return: New IntArray containing the arguments.
679 get_D_fixed_args(const size_t number)
682 fatal("requested number of arguments must be > 0");
683 IntArray *args = new IntArray(number);
684 for (size_t i = 0; i < number; i++)
685 args->append(get_integer_arg());
690 //////////////////////////////////////////////////////////////////////
691 /* get_D_fixed_args_odd_dummy():
692 Get a fixed number of integer arguments for D commands and optionally
693 ignore a dummy integer argument if the requested number is odd.
695 The gtroff program adds a dummy argument to some commands to get
696 an even number of arguments.
697 Error if the number of arguments differs from the scheme above.
700 number: In-parameter, the number of arguments to be retrieved.
702 Return: New IntArray containing the arguments.
705 get_D_fixed_args_odd_dummy(const size_t number)
708 fatal("requested number of arguments must be > 0");
709 IntArray *args = new IntArray(number);
710 for (size_t i = 0; i < number; i++)
711 args->append(get_integer_arg());
713 IntArray *a = get_possibly_integer_args();
715 error("too many arguments");
722 //////////////////////////////////////////////////////////////////////
723 /* get_D_variable_args():
724 Get a variable even number of integer arguments for D commands.
726 Get as many integer arguments as possible from the rest of the
728 - The arguments are separated by an arbitrary sequence of space or
730 - A comment, a newline, or EOF indicates the end of processing.
731 - Error on non-digit characters different from these.
732 - A final line skip is performed (except for EOF).
734 Return: New IntArray of the retrieved arguments.
737 get_D_variable_args()
739 IntArray *args = get_possibly_integer_args();
740 size_t n = args->len();
742 error("no arguments found");
744 error("even number of arguments expected");
749 //////////////////////////////////////////////////////////////////////
750 /* get_extended_arg():
751 Retrieve extended arg for `x X' command.
753 - Skip leading spaces and tabs, error on EOL or newline.
754 - Return everything before the next NL or EOF ('#' is not a comment);
755 as long as the following line starts with '+' this is returned
756 as well, with the '+' replaced by a newline.
757 - Final line skip is always performed.
759 Return: Allocated (new) string of retrieved text argument.
762 get_extended_arg(void)
764 StringBuf buf = StringBuf();
765 Char c = next_arg_begin();
766 while ((int) c != EOF) {
767 if ((int) c == '\n') {
771 buf.append((Char) '\n');
773 unget_char(c); // first character of next line
781 return buf.make_string();
784 //////////////////////////////////////////////////////////////////////
785 /* get_integer_arg(): Retrieve integer argument.
787 Skip leading spaces and tabs, collect an optional '-' and all
788 following decimal digits (at least one) up to the next non-digit,
789 which is restored onto the input queue.
791 Fatal error on all other situations.
793 Return: Retrieved integer.
796 get_integer_arg(void)
798 StringBuf buf = StringBuf();
799 Char c = next_arg_begin();
800 if ((int) c == '-') {
804 if (!isdigit((int) c))
805 error("integer argument expected");
806 while (isdigit((int) c)) {
812 char *s = buf.make_string();
814 long int number = strtol(s, 0, 10);
816 || number > INTARG_MAX || number < -INTARG_MAX) {
817 error("integer argument too large");
821 return (IntArg) number;
824 //////////////////////////////////////////////////////////////////////
825 /* get_possibly_integer_args():
826 Parse the rest of the input line as a list of integer arguments.
828 Get as many integer arguments as possible from the rest of the
829 current line, even none.
830 - The arguments are separated by an arbitrary sequence of space or
832 - A comment, a newline, or EOF indicates the end of processing.
833 - Error on non-digit characters different from these.
834 - No line skip is performed.
836 Return: New IntArray of the retrieved arguments.
839 get_possibly_integer_args()
842 StringBuf buf = StringBuf();
844 IntArray *args = new IntArray();
847 while (is_space_or_tab(c))
850 Char c1 = get_char();
851 if (isdigit((int) c1)) {
858 while (isdigit((int) c)) {
862 if (!buf.is_empty()) {
863 char *s = buf.make_string();
865 long int x = strtol(s, 0, 10);
867 || x > INTARG_MAX || x < -INTARG_MAX) {
868 error("invalid integer argument, set to 0");
871 args->append((IntArg) x);
874 // Here, c is not a digit.
875 // Terminate on comment, end of line, or end of file, while
876 // space or tab indicate continuation; otherwise error.
879 skip_to_end_of_line();
893 error("integer argument expected");
900 //////////////////////////////////////////////////////////////////////
904 - Skip leading spaces and tabs; error on EOL or newline.
905 - Return all following characters before the next space, tab,
906 newline, or EOF character (in-word '#' is not a comment character).
907 - The terminating space, tab, newline, or EOF character is restored
908 onto the input queue, so no line skip.
910 Return: Retrieved string as char *, allocated by 'new'.
915 StringBuf buf = StringBuf();
916 Char c = next_arg_begin();
917 while (!is_space_or_tab(c)
918 && c != Char('\n') && c != Char(EOF)) {
922 unget_char(c); // restore white space
923 return buf.make_string();
926 //////////////////////////////////////////////////////////////////////
927 /* is_space_or_tab():
928 Test a character if it is a space or tab.
930 c: In-parameter, character to be tested.
932 Return: True, if c is a space or tab character, false otherwise.
935 is_space_or_tab(const Char c)
937 return (c == Char(' ') || c == Char('\t')) ? true : false;
940 //////////////////////////////////////////////////////////////////////
942 Return first character of next argument.
944 Skip space and tab characters; error on newline or EOF.
946 Return: The first character different from these (including '#').
960 error("missing argument");
962 default: // first essential character
968 //////////////////////////////////////////////////////////////////////
970 Find the first character of the next command.
972 Skip spaces, tabs, comments (introduced by #), and newlines.
974 Return: The first character different from these (including EOF).
992 default: // EOF or first essential character
998 //////////////////////////////////////////////////////////////////////
1000 Test whether argument is an odd number.
1002 n: In-parameter, the integer to be tested.
1004 Return: True if odd, false otherwise.
1009 return (n & 1 == 1) ? true : false;
1012 //////////////////////////////////////////////////////////////////////
1013 /* position_to_end_of_args():
1014 Move graphical pointer to end of drawn figure.
1016 This is used by the D commands that draw open geometrical figures.
1017 The algorithm simply sums up all horizontal displacements (arguments
1018 with even number) for the horizontal component. Similarly, the
1019 vertical component is the sum of the odd arguments.
1021 args: In-parameter, the arguments of a former drawing command.
1024 position_to_end_of_args(const IntArray * const args)
1027 const size_t n = args->len();
1028 for (i = 0; i < n; i += 2)
1029 current_env->hpos += (*args)[i];
1030 for (i = 1; i < n; i += 2)
1031 current_env->vpos += (*args)[i];
1034 //////////////////////////////////////////////////////////////////////
1035 /* remember_filename():
1036 Set global variable current_filename.
1038 The actual filename is stored in current_filename. This is used by
1039 the postprocessors, expecting the name "<standard input>" for stdin.
1041 filename: In-out-parameter; is changed to the new value also.
1044 remember_filename(const char *filename)
1047 if (strcmp(filename, "-") == 0)
1048 fname = "<standard input>";
1050 fname = (char *) filename;
1051 size_t len = strlen(fname) + 1;
1052 if (current_filename != 0)
1053 free((char *)current_filename);
1054 current_filename = (const char *) malloc(len);
1055 if (current_filename == 0)
1056 fatal("can't malloc space for filename");
1057 strncpy((char *)current_filename, (char *)fname, len);
1060 //////////////////////////////////////////////////////////////////////
1062 Call draw method of printer class.
1064 subcmd: Letter of actual D subcommand.
1065 args: Array of integer arguments of actual D subcommand.
1068 send_draw(const Char subcmd, const IntArray * const args)
1070 EnvInt n = (EnvInt) args->len();
1071 pr->draw((int) subcmd, (IntArg *) args->get_data(), n, current_env);
1074 //////////////////////////////////////////////////////////////////////
1076 Go to next line within the input queue.
1078 Skip the rest of the current line, including the newline character.
1079 The global variable current_lineno is adjusted.
1080 No errors are raised.
1085 Char c = get_char();
1097 //////////////////////////////////////////////////////////////////////
1098 /* skip_line_checked ():
1099 Check that there aren't any arguments left on the rest of the line,
1102 Spaces, tabs, and a comment are allowed before newline or EOF.
1103 All other characters raise an error.
1106 skip_line_checked(void)
1109 Char c = get_char();
1110 while (is_space_or_tab(c))
1113 case '#': // comment
1129 //////////////////////////////////////////////////////////////////////
1130 /* skip_line_fatal ():
1131 Fatal error if arguments left, otherwise skip line.
1133 Spaces, tabs, and a comment are allowed before newline or EOF.
1134 All other characters trigger the error.
1137 skip_line_fatal(void)
1139 bool ok = skip_line_checked();
1142 error("too many arguments");
1147 //////////////////////////////////////////////////////////////////////
1148 /* skip_line_warn ():
1149 Skip line, but warn if arguments are left on actual line.
1151 Spaces, tabs, and a comment are allowed before newline or EOF.
1152 All other characters raise a warning
1155 skip_line_warn(void)
1157 bool ok = skip_line_checked();
1160 warning("too many arguments on current line");
1165 //////////////////////////////////////////////////////////////////////
1167 Skip line in `D' commands.
1169 Decide whether in case of an additional argument a fatal error is
1170 raised (the documented classical behavior), only a warning is
1171 issued, or the line is just skipped (former groff behavior).
1172 Actually decided for the warning.
1178 // or: skip_line_fatal();
1182 //////////////////////////////////////////////////////////////////////
1184 Skip line in `x' commands.
1186 Decide whether in case of an additional argument a fatal error is
1187 raised (the documented classical behavior), only a warning is
1188 issued, or the line is just skipped (former groff behavior).
1189 Actually decided for the warning.
1195 // or: skip_line_fatal();
1199 //////////////////////////////////////////////////////////////////////
1200 /* skip_to_end_of_line():
1201 Go to the end of the current line.
1203 Skip the rest of the current line, excluding the newline character.
1204 The global variable current_lineno is not changed.
1205 No errors are raised.
1208 skip_to_end_of_line(void)
1210 Char c = get_char();
1222 //////////////////////////////////////////////////////////////////////
1224 Restore character c onto input queue.
1226 Write a character back onto the input stream.
1227 EOF is gracefully handled.
1229 c: In-parameter; character to be pushed onto the input queue.
1232 unget_char(const Char c)
1236 if (ungetc(ch, current_file) == EOF)
1237 fatal("could not unget character");
1242 /**********************************************************************
1244 **********************************************************************/
1246 //////////////////////////////////////////////////////////////////////
1247 /* parse_color_command:
1248 Process the commands m and DF, but not Df.
1250 col: In-out-parameter; the color object to be set, must have
1251 been initialized before.
1254 parse_color_command(color *col)
1257 ColorArg red = 0, green = 0, blue = 0;
1258 ColorArg cyan = 0, magenta = 0, yellow = 0, black = 0;
1259 Char subcmd = next_arg_begin();
1260 switch((int) subcmd) {
1261 case 'c': // DFc or mc: CMY
1262 cyan = get_color_arg();
1263 magenta = get_color_arg();
1264 yellow = get_color_arg();
1265 col->set_cmy(cyan, magenta, yellow);
1267 case 'd': // DFd or md: set default color
1270 case 'g': // DFg or mg: gray
1271 gray = get_color_arg();
1272 col->set_gray(gray);
1274 case 'k': // DFk or mk: CMYK
1275 cyan = get_color_arg();
1276 magenta = get_color_arg();
1277 yellow = get_color_arg();
1278 black = get_color_arg();
1279 col->set_cmyk(cyan, magenta, yellow, black);
1281 case 'r': // DFr or mr: RGB
1282 red = get_color_arg();
1283 green = get_color_arg();
1284 blue = get_color_arg();
1285 col->set_rgb(red, green, blue);
1288 error("invalid color scheme `%1'", (int) subcmd);
1290 } // end of color subcommands
1293 //////////////////////////////////////////////////////////////////////
1294 /* parse_D_command():
1295 Parse the subcommands of graphical command D.
1297 This is the part of the do_file() parser that scans the graphical
1299 - Error on lacking or wrong arguments.
1300 - Warning on too many arguments.
1301 - Line is always skipped.
1306 Char subcmd = next_arg_begin();
1307 switch((int) subcmd) {
1308 case '~': // D~: draw B-spline
1309 // actually, this isn't available for some postprocessors
1311 default: // unknown options are passed to device
1313 IntArray *args = get_D_variable_args();
1314 send_draw(subcmd, args);
1315 position_to_end_of_args(args);
1319 case 'a': // Da: draw arc
1321 IntArray *args = get_D_fixed_args(4);
1322 send_draw(subcmd, args);
1323 position_to_end_of_args(args);
1327 case 'c': // Dc: draw circle line
1329 IntArray *args = get_D_fixed_args(1);
1330 send_draw(subcmd, args);
1331 // move to right end
1332 current_env->hpos += (*args)[0];
1336 case 'C': // DC: draw solid circle
1338 IntArray *args = get_D_fixed_args_odd_dummy(1);
1339 send_draw(subcmd, args);
1340 // move to right end
1341 current_env->hpos += (*args)[0];
1345 case 'e': // De: draw ellipse line
1346 case 'E': // DE: draw solid ellipse
1348 IntArray *args = get_D_fixed_args(2);
1349 send_draw(subcmd, args);
1350 // move to right end
1351 current_env->hpos += (*args)[0];
1355 case 'f': // Df: set fill gray; obsoleted by DFg
1357 IntArg arg = get_integer_arg();
1358 if ((arg >= 0) && (arg <= 1000)) {
1359 // convert arg and treat it like DFg
1360 ColorArg gray = color_from_Df_command(arg);
1361 current_env->fill->set_gray(gray);
1364 // set fill color to the same value as the current outline color
1365 delete current_env->fill;
1366 current_env->fill = new color(current_env->col);
1368 pr->change_fill_color(current_env);
1369 // skip unused `vertical' component (\D'...' always emits pairs)
1370 (void) get_integer_arg();
1375 case 'F': // DF: set fill color, several formats
1376 parse_color_command(current_env->fill);
1377 pr->change_fill_color(current_env);
1378 // no positioning (setting-only command)
1381 case 'l': // Dl: draw line
1383 IntArray *args = get_D_fixed_args(2);
1384 send_draw(subcmd, args);
1385 position_to_end_of_args(args);
1389 case 'p': // Dp: draw closed polygon line
1390 case 'P': // DP: draw solid closed polygon
1392 IntArray *args = get_D_variable_args();
1393 send_draw(subcmd, args);
1394 # ifdef STUPID_DRAWING_POSITIONING
1395 // final args positioning
1396 position_to_end_of_args(args);
1401 case 't': // Dt: set line thickness
1403 IntArray *args = get_D_fixed_args_odd_dummy(1);
1404 send_draw(subcmd, args);
1405 # ifdef STUPID_DRAWING_POSITIONING
1406 // final args positioning
1407 position_to_end_of_args(args);
1413 } // end of D subcommands
1416 //////////////////////////////////////////////////////////////////////
1417 /* parse_x_command():
1418 Parse subcommands of the device control command x.
1420 This is the part of the do_file() parser that scans the device
1421 controlling commands.
1422 - Error on duplicate prologue commands.
1423 - Error on wrong or lacking arguments.
1424 - Warning on too many arguments.
1425 - Line is always skipped.
1428 - current_env: is set by many subcommands.
1429 - npages: page counting variable
1431 Return: boolean in the meaning of `stopped'
1432 - true if parsing should be stopped (`x stop').
1433 - false if parsing should continue.
1436 parse_x_command(void)
1438 bool stopped = false;
1439 char *subcmd_str = get_string_arg();
1440 char subcmd = subcmd_str[0];
1442 case 'f': // x font: mount font
1444 IntArg n = get_integer_arg();
1445 char *name = get_string_arg();
1446 pr->load_font(n, name);
1451 case 'F': // x Filename: set filename for errors
1453 char *str_arg = get_string_arg();
1455 warning("empty argument for `x F' command");
1457 remember_filename(str_arg);
1462 case 'H': // x Height: set character height
1463 current_env->height = get_integer_arg();
1464 if (current_env->height == current_env->size)
1465 current_env->height = 0;
1468 case 'i': // x init: initialize device
1469 error("duplicate `x init' command");
1472 case 'p': // x pause: pause device
1475 case 'r': // x res: set resolution
1476 error("duplicate `x res' command");
1479 case 's': // x stop: stop device
1483 case 'S': // x Slant: set slant
1484 current_env->slant = get_integer_arg();
1487 case 't': // x trailer: generate trailer info
1490 case 'T': // x Typesetter: set typesetter
1491 error("duplicate `x T' command");
1494 case 'u': // x underline: from .cu
1496 char *str_arg = get_string_arg();
1497 pr->special(str_arg, current_env, 'u');
1502 case 'X': // x X: send uninterpretedly to device
1504 char *str_arg = get_extended_arg(); // includes line skip
1506 error("`x X' command invalid before first `p' command");
1508 pr->special(str_arg, current_env);
1512 default: // ignore unknown x commands, but warn
1513 warning("unknown command `x %1'", subcmd);
1521 /**********************************************************************
1522 exported part (by driver.h)
1523 **********************************************************************/
1525 ////////////////////////////////////////////////////////////////////////
1527 Parse and postprocess groff intermediate output.
1529 filename: "-" for standard input, normal file name otherwise
1532 do_file(const char *filename)
1535 bool stopped = false; // terminating condition
1537 #ifdef USE_ENV_STACK
1538 EnvStack env_stack = EnvStack();
1539 #endif // USE_ENV_STACK
1541 // setup of global variables
1544 // `pr' is initialized after the prologue.
1545 // `device' is set by the 1st prologue command.
1547 if (filename[0] == '-' && filename[1] == '\0')
1548 current_file = stdin;
1551 current_file = fopen(filename, "r");
1552 if (errno != 0 || current_file == 0) {
1553 error("can't open file `%1'", filename);
1557 remember_filename(filename);
1559 if (current_env != 0)
1560 delete_current_env();
1561 current_env = new environment;
1562 current_env->col = new color;
1563 current_env->fill = new color;
1564 current_env->fontno = -1;
1565 current_env->height = 0;
1566 current_env->hpos = -1;
1567 current_env->slant = 0;
1568 current_env->size = 0;
1569 current_env->vpos = -1;
1571 // parsing of prologue (first 3 commands)
1576 // 1st command `x T'
1577 command = next_command();
1578 if ((int) command == EOF)
1580 if ((int) command != 'x')
1581 fatal("the first command must be `x T'");
1582 str_arg = get_string_arg();
1583 if (str_arg[0] != 'T')
1584 fatal("the first command must be `x T'");
1586 char *tmp_dev = get_string_arg();
1587 if (pr == 0) { // note: `pr' initialized after prologue
1589 if (!font::load_desc())
1590 fatal("couldn't load DESC file, can't continue");
1593 if (device == 0 || strcmp(device, tmp_dev) != 0)
1594 fatal("all files must use the same device");
1597 skip_line_x(); // ignore further arguments
1598 current_env->size = 10 * font::sizescale;
1600 // 2nd command `x res'
1601 command = next_command();
1602 if ((int) command != 'x')
1603 fatal("the second command must be `x res'");
1604 str_arg = get_string_arg();
1605 if (str_arg[0] != 'r')
1606 fatal("the second command must be `x res'");
1608 int_arg = get_integer_arg();
1609 EnvInt font_res = font::res;
1610 if (int_arg != font_res)
1611 fatal("resolution does not match");
1612 int_arg = get_integer_arg();
1613 if (int_arg != font::hor)
1614 fatal("minimum horizontal motion does not match");
1615 int_arg = get_integer_arg();
1616 if (int_arg != font::vert)
1617 fatal("minimum vertical motion does not match");
1618 skip_line_x(); // ignore further arguments
1620 // 3rd command `x init'
1621 command = next_command();
1623 fatal("the third command must be `x init'");
1624 str_arg = get_string_arg();
1625 if (str_arg[0] != 'i')
1626 fatal("the third command must be `x init'");
1633 pr = make_printer();
1635 command = next_command();
1638 // spaces, tabs, comments, and newlines are skipped here
1639 switch ((int) command) {
1640 case '#': // #: comment, ignore up to end of line
1643 #ifdef USE_ENV_STACK
1644 case '{': // {: start a new environment (a copy)
1645 env_stack.push(current_env);
1647 case '}': // }: pop previous env from stack
1648 delete_current_env();
1649 current_env = env_stack.pop();
1651 #endif // USE_ENV_STACK
1652 case '0': // ddc: obsolete jump and print command
1662 { // expect 2 digits and a character
1664 Char c = next_arg_begin();
1666 fatal_command(command);
1667 if (!isdigit((int) c)) {
1668 error("digit expected");
1671 s[0] = (char) command;
1675 long int x = strtol(s, 0, 10);
1677 error("couldn't convert 2 digits");
1678 EnvInt hor_pos = (EnvInt) x;
1679 current_env->hpos += hor_pos;
1680 c = next_arg_begin();
1681 if ((int) c == '\n' || (int) c == EOF)
1682 error("character argument expected");
1684 pr->set_ascii_char((unsigned char) c, current_env);
1687 case 'c': // c: print ascii char without moving
1690 fatal_command(command);
1691 Char c = next_arg_begin();
1692 if (c == '\n' || c == EOF)
1693 error("missing argument to `c' command");
1695 pr->set_ascii_char((unsigned char) c, current_env);
1698 case 'C': // C: print named special character
1701 fatal_command(command);
1702 char *str_arg = get_string_arg();
1703 pr->set_special_char(str_arg, current_env);
1707 case 'D': // drawing commands
1709 fatal_command(command);
1712 case 'f': // f: set font to number
1713 current_env->fontno = get_integer_arg();
1715 case 'F': // F: obsolete, replaced by `x F'
1717 char *str_arg = get_string_arg();
1718 remember_filename(str_arg);
1722 case 'h': // h: relative horizontal move
1723 current_env->hpos += (EnvInt) get_integer_arg();
1725 case 'H': // H: absolute horizontal positioning
1726 current_env->hpos = (EnvInt) get_integer_arg();
1728 case 'm': // m: glyph color
1729 parse_color_command(current_env->col);
1730 pr->change_color(current_env);
1732 case 'n': // n: print end of line
1733 // ignore two arguments (historically)
1735 fatal_command(command);
1737 (void) get_integer_arg();
1738 (void) get_integer_arg();
1740 case 'N': // N: print char with given int code
1742 fatal_command(command);
1743 pr->set_numbered_char(get_integer_arg(), current_env);
1745 case 'p': // p: start new page with given number
1747 pr->end_page(current_env->vpos);
1748 npages++; // increment # of processed pages
1749 pr->begin_page(get_integer_arg());
1750 current_env->vpos = 0;
1752 case 's': // s: set point size
1753 current_env->size = get_integer_arg();
1754 if (current_env->height == current_env->size)
1755 current_env->height = 0;
1757 case 't': // t: print a text word
1761 fatal_command(command);
1762 char *str_arg = get_string_arg();
1764 while ((c = str_arg[i++]) != '\0') {
1766 pr->set_ascii_char((unsigned char) c, current_env, &w);
1767 current_env->hpos += w;
1772 case 'u': // u: print spaced word
1776 fatal_command(command);
1777 EnvInt kern = (EnvInt) get_integer_arg();
1778 char *str_arg = get_string_arg();
1780 while ((c = str_arg[i++]) != '\0') {
1782 pr->set_ascii_char((unsigned char) c, current_env, &w);
1783 current_env->hpos += w + kern;
1788 case 'v': // v: relative vertical move
1789 current_env->vpos += (EnvInt) get_integer_arg();
1791 case 'V': // V: absolute vertical positioning
1792 current_env->vpos = (EnvInt) get_integer_arg();
1794 case 'w': // w: inform about paddable space
1796 case 'x': // device controlling commands
1797 stopped = parse_x_command();
1800 warning("unrecognized command `%1'", (unsigned char) command);
1806 // end of file reached
1808 pr->end_page(current_env->vpos);
1809 fclose(current_file);
1810 // If `stopped' is not `true' here then there wasn't any `x stop'.
1812 warning("no final `x stop' command");
1813 delete_current_env();