Sync-to-go: update copyright for 2015
[s-roff.git] / src / lib-driver / input.cpp
blobacaa97519ac9e1a00a1b6f6137bbf2e97bd83349
1 /*@ This file implements the parser for the intermediate roff output, see
2 *@ XYroff_out(5), and does the printout for the given device.
3 *@ All parsed information is processed within the function do_file().
4 *@ A device postprocessor just needs to fill in the methods for the class
5 *@ `printer' (or rather a derived class) without having to worry about the
6 *@ syntax of the intermediate output format. Consequently, the programming of
7 *@ roff postprocessors is similar to the development of device drivers.
9 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
11 * Copyright (C) 1989 - 1992, 2001 - 2006, 2008
12 * Free Software Foundation, Inc.
14 * Written by James Clark (jjc@jclark.com)
15 * Major rewrite 2001 by Bernd Warken (bwarken@mayn.de)
17 * This is free software; you can redistribute it and/or modify it
18 * under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2, or (at your option)
20 * any later version.
22 * This is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with groff; see the file COPYING. If not, write to the Free
29 * Software Foundation, 51 Franklin St - Fifth Floor, Boston, MA
30 * 02110-1301, USA.
33 #include "config.h"
36 * Discussion of the positioning by drawing commands
38 * There was some confusion about the positioning of the graphical
39 * pointer at the printout after having executed a `D' command.
40 * The classical troff manual of Ossanna & Kernighan specified,
42 * `The position after a graphical object has been drawn is
43 * at its end; for circles and ellipses, the "end" is at the
44 * right side.'
46 * From this, it follows that
47 * - all open figures (args, splines, and lines) should position at their
48 * final point.
49 * - all circles and ellipses should position at their right-most point
50 * (as if 2 halves had been drawn).
51 * - all closed figures apart from circles and ellipses shouldn't change
52 * the position because they return to their origin.
53 * - all setting commands should not change position because they do not
54 * draw any graphical object.
56 * In the case of the open figures, this means that the horizontal
57 * displacement is the sum of all odd arguments and the vertical offset
58 * the sum of all even arguments, called the alternate arguments sum
59 * displacement in the following.
61 * Unfortunately, groff did not implement this simple rule. The former
62 * documentation in groff_out(5) differed from the source code, and
63 * neither of them is compatible with the classical rule.
65 * The former groff_out(5) specified to use the alternative arguments
66 * sum displacement for calculating the drawing positioning of
67 * non-classical commands, including the `Dt' command (setting-only)
68 * and closed polygons. Applying this to the new groff color commands
69 * will lead to disaster. For their arguments can take large values (>
70 * 65000). On low resolution devices, the displacement of such large
71 * values will corrupt the display or kill the printer. So the
72 * nonsense specification has come to a natural end anyway.
74 * The groff source code, however, had no positioning for the
75 * setting-only commands (esp. `Dt'), the right-end positioning for
76 * outlined circles and ellipses, and the alternative argument sum
77 * displacement for all other commands (including filled circles and
78 * ellipses).
80 * The reason why no one seems to have suffered from this mayhem so
81 * far is that the graphical objects are usually generated by
82 * preprocessors like pic that do not depend on the automatic
83 * positioning. When using the low level `\D' escape sequences or `D'
84 * output commands, the strange positionings can be circumvented by
85 * absolute positionings or by tricks like `\Z'.
87 * So doing an exorcism on the strange, incompatible displacements might
88 * not harm any existing documents, but will make the usage of the
89 * graphical escape sequences and commands natural.
91 * That's why the rewrite of this file returned to the reasonable,
92 * classical specification with its clear end-of-drawing rule that is
93 * suitable for all cases. But a macro STUPID_DRAWING_POSITIONING is
94 * provided for testing the funny former behavior.
96 * The new rule implies the following behavior.
97 * - Setting commands (`Dt', `Df', `DF') and polygons (`Dp' and `DP')
98 * do not change position now.
99 * - Filled circles and ellipses (`DC' and `DE') position at their
100 * most right point (outlined ones `Dc' and `De' did this anyway).
101 * - As before, all open graphical objects position to their final
102 * drawing point (alternate sum of the command arguments).
105 #ifndef STUPID_DRAWING_POSITIONING
106 // uncomment next line if all non-classical D commands shall position
107 // to the strange alternate sum of args displacement
108 #define STUPID_DRAWING_POSITIONING
109 #endif
111 // Decide whether the commands `{' and `}' for different environments
112 // should be used.
113 #undef USE_ENV_STACK
115 #include "driver.h"
116 #include "device.h"
118 #include <ctype.h>
119 #include <errno.h>
120 #include <math.h>
121 #include <stdlib.h>
123 /**********************************************************************
124 local types
125 **********************************************************************/
127 // integer type used in the fields of struct environment (see printer.h)
128 typedef int EnvInt;
130 // integer arguments of groff_out commands, must be >= 32 bits
131 typedef int IntArg;
133 // color components of groff_out color commands, must be >= 32 bits
134 typedef unsigned int ColorArg;
136 // Array for IntArg values.
137 class IntArray
139 size_t num_allocated;
140 size_t num_stored;
141 IntArg *data;
143 public:
144 IntArray(void);
145 IntArray(const size_t);
146 ~IntArray(void);
147 IntArg operator[](const size_t i) const
149 if (i >= num_stored)
150 fatal("index out of range");
151 return (IntArg) data[i];
153 void append(IntArg);
154 IntArg *get_data(void) const { return (IntArg *)data; }
155 size_t len(void) const { return num_stored; }
158 // Characters read from the input queue.
159 class Char
161 int data;
163 public:
164 Char(void) : data('\0') {}
165 Char(const int c) : data(c) {}
166 bool operator==(char c) const { return (data == c) ? true : false; }
167 bool operator==(int c) const { return (data == c) ? true : false; }
168 bool operator==(const Char c) const
169 { return (data == c.data) ? true : false; }
170 bool operator!=(char c) const { return !(*this == c); }
171 bool operator!=(int c) const { return !(*this == c); }
172 bool operator!=(const Char c) const { return !(*this == c); }
173 operator int() const { return (int) data; }
174 operator unsigned char() const { return (unsigned char) data; }
175 operator char() const { return (char) data; }
178 // Buffer for string arguments (Char, not char).
179 class StringBuf
181 size_t num_allocated;
182 size_t num_stored;
183 Char *data; // not terminated by '\0'
185 public:
186 StringBuf(void); // allocate without storing
187 ~StringBuf(void);
188 void append(const Char); // append character to `data'
189 char *make_string(void); // return new copy of `data' with '\0'
190 bool is_empty(void) { // true if none stored
191 return (num_stored > 0) ? false : true;
193 void reset(void); // set `num_stored' to 0
196 #ifdef USE_ENV_STACK
197 class EnvStack
199 environment **data;
200 size_t num_allocated;
201 size_t num_stored;
203 public:
204 EnvStack(void);
205 ~EnvStack(void);
206 environment *pop(void);
207 void push(environment *e);
209 #endif // USE_ENV_STACK
212 /**********************************************************************
213 external variables
214 **********************************************************************/
216 // exported as extern by error.h (called from driver.h)
217 // needed for error messages (see ../lib-roff/error.cpp)
218 const char *current_filename = 0; // printable name of the current file
219 // printable name of current source file
220 const char *current_source_filename = 0;
221 int current_lineno = 0; // current line number of printout
223 // exported as extern by device.h;
224 const char *device = 0; // cancel former init with literal
226 printer *pr;
228 // Note:
230 // We rely on an implementation of the `new' operator which aborts
231 // gracefully if it can't allocate memory (e.g. from lib-roff/new.cpp).
233 /**********************************************************************
234 static local variables
235 **********************************************************************/
237 FILE *current_file = 0; // current input stream for parser
239 // npages: number of pages processed so far (including current page),
240 // _not_ the page number in the printout (can be set with `p').
241 int npages = 0;
243 const ColorArg
244 COLORARG_MAX = (ColorArg) 65536U; // == 0xFFFF + 1 == 0x10000
246 const IntArg
247 INTARG_MAX = (IntArg) 0x7FFFFFFF; // maximal signed 32 bits number
249 // parser environment, created and deleted by each run of do_file()
250 environment *current_env = 0;
252 #ifdef USE_ENV_STACK
253 const size_t
254 envp_size = sizeof(environment *);
255 #endif // USE_ENV_STACK
257 /**********************************************************************
258 function declarations
259 **********************************************************************/
261 // utility functions
262 ColorArg color_from_Df_command(IntArg);
263 // transform old color into new
264 void delete_current_env(void); // delete global var current_env
265 void fatal_command(char); // abort for invalid command
266 inline Char get_char(void); // read next character from input stream
267 ColorArg get_color_arg(void); // read in argument for new color cmds
268 IntArray *get_D_fixed_args(const size_t);
269 // read in fixed number of integer
270 // arguments
271 IntArray *get_D_fixed_args_odd_dummy(const size_t);
272 // read in a fixed number of integer
273 // arguments plus optional dummy
274 IntArray *get_D_variable_args(void);
275 // variable, even number of int args
276 char *get_extended_arg(void); // argument for `x X' (several lines)
277 IntArg get_integer_arg(void); // read in next integer argument
278 IntArray *get_possibly_integer_args();
279 // 0 or more integer arguments
280 char *get_string_arg(void); // read in next string arg, ended by WS
281 inline bool is_space_or_tab(const Char);
282 // test on space/tab char
283 Char next_arg_begin(void); // skip white space on current line
284 Char next_command(void); // go to next command, evt. diff. line
285 inline bool odd(const int); // test if integer is odd
286 void position_to_end_of_args(const IntArray * const);
287 // positioning after drawing
288 void remember_filename(const char *);
289 // set global current_filename
290 void remember_source_filename(const char *);
291 // set global current_source_filename
292 void send_draw(const Char, const IntArray * const);
293 // call pr->draw
294 void skip_line(void); // unconditionally skip to next line
295 bool skip_line_checked(void); // skip line, false if args are left
296 void skip_line_fatal(void); // skip line, fatal if args are left
297 void skip_line_warn(void); // skip line, warn if args are left
298 void skip_line_D(void); // skip line in D commands
299 void skip_line_x(void); // skip line in x commands
300 void skip_to_end_of_line(void); // skip to the end of the current line
301 inline void unget_char(const Char);
302 // restore character onto input
304 // parser subcommands
305 void parse_color_command(color *);
306 // color sub(sub)commands m and DF
307 void parse_D_command(void); // graphical subcommands
308 bool parse_x_command(void); // device controller subcommands
310 /**********************************************************************
311 class methods
312 **********************************************************************/
314 #ifdef USE_ENV_STACK
315 EnvStack::EnvStack(void)
317 num_allocated = 4;
318 // allocate pointer to array of num_allocated pointers to environment
319 data = (environment **)malloc(envp_size * num_allocated);
320 if (data == 0)
321 fatal("could not allocate environment data");
322 num_stored = 0;
325 EnvStack::~EnvStack(void)
327 for (size_t i = 0; i < num_stored; i++)
328 delete data[i];
329 free(data);
332 // return top element from stack and decrease stack pointer
334 // the calling function must take care of properly deleting the result
335 environment *
336 EnvStack::pop(void)
338 num_stored--;
339 environment *result = data[num_stored];
340 data[num_stored] = 0;
341 return result;
344 // copy argument and push this onto the stack
345 void
346 EnvStack::push(environment *e)
348 environment *e_copy = new environment;
349 if (num_stored >= num_allocated) {
350 environment **old_data = data;
351 num_allocated *= 2;
352 data = (environment **)malloc(envp_size * num_allocated);
353 if (data == 0)
354 fatal("could not allocate data");
355 for (size_t i = 0; i < num_stored; i++)
356 data[i] = old_data[i];
357 free(old_data);
359 e_copy->col = new color;
360 e_copy->fill = new color;
361 *e_copy->col = *e->col;
362 *e_copy->fill = *e->fill;
363 e_copy->fontno = e->fontno;
364 e_copy->height = e->height;
365 e_copy->hpos = e->hpos;
366 e_copy->size = e->size;
367 e_copy->slant = e->slant;
368 e_copy->vpos = e->vpos;
369 data[num_stored] = e_copy;
370 num_stored++;
372 #endif // USE_ENV_STACK
374 IntArray::IntArray(void)
376 num_allocated = 4;
377 data = new IntArg[num_allocated];
378 num_stored = 0;
381 IntArray::IntArray(const size_t n)
383 if (n <= 0)
384 fatal("number of integers to be allocated must be > 0");
385 num_allocated = n;
386 data = new IntArg[num_allocated];
387 num_stored = 0;
390 IntArray::~IntArray(void)
392 a_delete data;
395 void
396 IntArray::append(IntArg x)
398 if (num_stored >= num_allocated) {
399 IntArg *old_data = data;
400 num_allocated *= 2;
401 data = new IntArg[num_allocated];
402 for (size_t i = 0; i < num_stored; i++)
403 data[i] = old_data[i];
404 a_delete old_data;
406 data[num_stored] = x;
407 num_stored++;
410 StringBuf::StringBuf(void)
412 num_stored = 0;
413 num_allocated = 128;
414 data = new Char[num_allocated];
417 StringBuf::~StringBuf(void)
419 a_delete data;
422 void
423 StringBuf::append(const Char c)
425 if (num_stored >= num_allocated) {
426 Char *old_data = data;
427 num_allocated *= 2;
428 data = new Char[num_allocated];
429 for (size_t i = 0; i < num_stored; i++)
430 data[i] = old_data[i];
431 a_delete old_data;
433 data[num_stored] = c;
434 num_stored++;
437 char *
438 StringBuf::make_string(void)
440 char *result = new char[num_stored + 1];
441 for (size_t i = 0; i < num_stored; i++)
442 result[i] = (char) data[i];
443 result[num_stored] = '\0';
444 return result;
447 void
448 StringBuf::reset(void)
450 num_stored = 0;
453 /**********************************************************************
454 utility functions
455 **********************************************************************/
457 /* color_from_Df_command:
458 Process the gray shade setting command Df.
460 Transform Df style color into DF style color.
461 Df color: 0-1000, 0 is white
462 DF color: 0-65536, 0 is black
464 The Df command is obsoleted by command DFg, but kept for
465 compatibility.
467 ColorArg
468 color_from_Df_command(IntArg Df_gray)
470 return ColorArg((1000-Df_gray) * COLORARG_MAX / 1000); // scaling
473 /* delete_current_env():
474 Delete global variable current_env and its pointer members.
476 This should be a class method of environment.
478 void delete_current_env(void)
480 delete current_env->col;
481 delete current_env->fill;
482 delete current_env;
483 current_env = 0;
486 /* fatal_command():
487 Emit error message about invalid command and abort.
489 void
490 fatal_command(char command)
492 fatal("`%1' command invalid before first `p' command", command);
495 /* get_char():
496 Retrieve the next character from the input queue.
498 Return: The retrieved character (incl. EOF), converted to Char.
500 inline Char
501 get_char(void)
503 return (Char) getc(current_file);
506 /* get_color_arg():
507 Retrieve an argument suitable for the color commands m and DF.
509 Return: The retrieved color argument.
511 ColorArg
512 get_color_arg(void)
514 IntArg x = get_integer_arg();
515 if (x < 0 || x > (IntArg)COLORARG_MAX) {
516 error("color component argument out of range");
517 x = 0;
519 return (ColorArg) x;
522 /* get_D_fixed_args():
523 Get a fixed number of integer arguments for D commands.
525 Fatal if wrong number of arguments.
526 Too many arguments on the line raise a warning.
527 A line skip is done.
529 number: In-parameter, the number of arguments to be retrieved.
530 ignore: In-parameter, ignore next argument -- GNU troff always emits
531 pairs of parameters for `D' extensions added by groff.
532 Default is `false'.
534 Return: New IntArray containing the arguments.
536 IntArray *
537 get_D_fixed_args(const size_t number)
539 if (number <= 0)
540 fatal("requested number of arguments must be > 0");
541 IntArray *args = new IntArray(number);
542 for (size_t i = 0; i < number; i++)
543 args->append(get_integer_arg());
544 skip_line_D();
545 return args;
548 /* get_D_fixed_args_odd_dummy():
549 Get a fixed number of integer arguments for D commands and optionally
550 ignore a dummy integer argument if the requested number is odd.
552 The gtroff program adds a dummy argument to some commands to get
553 an even number of arguments.
554 Error if the number of arguments differs from the scheme above.
555 A line skip is done.
557 number: In-parameter, the number of arguments to be retrieved.
559 Return: New IntArray containing the arguments.
561 IntArray *
562 get_D_fixed_args_odd_dummy(const size_t number)
564 if (number <= 0)
565 fatal("requested number of arguments must be > 0");
566 IntArray *args = new IntArray(number);
567 for (size_t i = 0; i < number; i++)
568 args->append(get_integer_arg());
569 if (odd(number)) {
570 IntArray *a = get_possibly_integer_args();
571 if (a->len() > 1)
572 error("too many arguments");
573 delete a;
575 skip_line_D();
576 return args;
579 /* get_D_variable_args():
580 Get a variable even number of integer arguments for D commands.
582 Get as many integer arguments as possible from the rest of the
583 current line.
584 - The arguments are separated by an arbitrary sequence of space or
585 tab characters.
586 - A comment, a newline, or EOF indicates the end of processing.
587 - Error on non-digit characters different from these.
588 - A final line skip is performed (except for EOF).
590 Return: New IntArray of the retrieved arguments.
592 IntArray *
593 get_D_variable_args()
595 IntArray *args = get_possibly_integer_args();
596 size_t n = args->len();
597 if (n <= 0)
598 error("no arguments found");
599 if (odd(n))
600 error("even number of arguments expected");
601 skip_line_D();
602 return args;
605 /* get_extended_arg():
606 Retrieve extended arg for `x X' command.
608 - Skip leading spaces and tabs, error on EOL or newline.
609 - Return everything before the next NL or EOF ('#' is not a comment);
610 as long as the following line starts with '+' this is returned
611 as well, with the '+' replaced by a newline.
612 - Final line skip is always performed.
614 Return: Allocated (new) string of retrieved text argument.
616 char *
617 get_extended_arg(void)
619 StringBuf buf = StringBuf();
620 Char c = next_arg_begin();
621 while ((int) c != EOF) {
622 if ((int) c == '\n') {
623 current_lineno++;
624 c = get_char();
625 if ((int) c == '+')
626 buf.append((Char) '\n');
627 else {
628 unget_char(c); // first character of next line
629 break;
632 else
633 buf.append(c);
634 c = get_char();
636 return buf.make_string();
639 /* get_integer_arg(): Retrieve integer argument.
641 Skip leading spaces and tabs, collect an optional '-' and all
642 following decimal digits (at least one) up to the next non-digit,
643 which is restored onto the input queue.
645 Fatal error on all other situations.
647 Return: Retrieved integer.
649 IntArg
650 get_integer_arg(void)
652 StringBuf buf = StringBuf();
653 Char c = next_arg_begin();
654 if ((int) c == '-') {
655 buf.append(c);
656 c = get_char();
658 if (!isdigit((int) c))
659 error("integer argument expected");
660 while (isdigit((int) c)) {
661 buf.append(c);
662 c = get_char();
664 // c is not a digit
665 unget_char(c);
666 char *s = buf.make_string();
667 errno = 0;
668 long int number = strtol(s, 0, 10);
669 if (errno != 0
670 || number > INTARG_MAX || number < -INTARG_MAX) {
671 error("integer argument too large");
672 number = 0;
674 a_delete s;
675 return (IntArg) number;
678 /* get_possibly_integer_args():
679 Parse the rest of the input line as a list of integer arguments.
681 Get as many integer arguments as possible from the rest of the
682 current line, even none.
683 - The arguments are separated by an arbitrary sequence of space or
684 tab characters.
685 - A comment, a newline, or EOF indicates the end of processing.
686 - Error on non-digit characters different from these.
687 - No line skip is performed.
689 Return: New IntArray of the retrieved arguments.
691 IntArray *
692 get_possibly_integer_args()
694 bool done = false;
695 StringBuf buf = StringBuf();
696 Char c = get_char();
697 IntArray *args = new IntArray();
698 while (!done) {
699 buf.reset();
700 while (is_space_or_tab(c))
701 c = get_char();
702 if (c == '-') {
703 Char c1 = get_char();
704 if (isdigit((int) c1)) {
705 buf.append(c);
706 c = c1;
708 else
709 unget_char(c1);
711 while (isdigit((int) c)) {
712 buf.append(c);
713 c = get_char();
715 if (!buf.is_empty()) {
716 char *s = buf.make_string();
717 errno = 0;
718 long int x = strtol(s, 0, 10);
719 if (errno
720 || x > INTARG_MAX || x < -INTARG_MAX) {
721 error("invalid integer argument, set to 0");
722 x = 0;
724 args->append((IntArg) x);
725 a_delete s;
727 // Here, c is not a digit.
728 // Terminate on comment, end of line, or end of file, while
729 // space or tab indicate continuation; otherwise error.
730 switch((int) c) {
731 case '#':
732 skip_to_end_of_line();
733 done = true;
734 break;
735 case '\n':
736 done = true;
737 unget_char(c);
738 break;
739 case EOF:
740 done = true;
741 break;
742 case ' ':
743 case '\t':
744 break;
745 default:
746 error("integer argument expected");
747 break;
750 return args;
753 /* get_string_arg():
754 Retrieve string arg.
756 - Skip leading spaces and tabs; error on EOL or newline.
757 - Return all following characters before the next space, tab,
758 newline, or EOF character (in-word '#' is not a comment character).
759 - The terminating space, tab, newline, or EOF character is restored
760 onto the input queue, so no line skip.
762 Return: Retrieved string as char *, allocated by 'new'.
764 char *
765 get_string_arg(void)
767 StringBuf buf = StringBuf();
768 Char c = next_arg_begin();
769 while (!is_space_or_tab(c)
770 && c != Char('\n') && c != Char(EOF)) {
771 buf.append(c);
772 c = get_char();
774 unget_char(c); // restore white space
775 return buf.make_string();
778 /* is_space_or_tab():
779 Test a character if it is a space or tab.
781 c: In-parameter, character to be tested.
783 Return: True, if c is a space or tab character, false otherwise.
785 inline bool
786 is_space_or_tab(const Char c)
788 return (c == Char(' ') || c == Char('\t')) ? true : false;
791 /* next_arg_begin():
792 Return first character of next argument.
794 Skip space and tab characters; error on newline or EOF.
796 Return: The first character different from these (including '#').
798 Char
799 next_arg_begin(void)
801 Char c;
802 while (1) {
803 c = get_char();
804 switch ((int) c) {
805 case ' ':
806 case '\t':
807 break;
808 case '\n':
809 case EOF:
810 error("missing argument");
811 break;
812 default: // first essential character
813 return c;
818 /* next_command():
819 Find the first character of the next command.
821 Skip spaces, tabs, comments (introduced by #), and newlines.
823 Return: The first character different from these (including EOF).
825 Char
826 next_command(void)
828 Char c;
829 while (1) {
830 c = get_char();
831 switch ((int) c) {
832 case ' ':
833 case '\t':
834 break;
835 case '\n':
836 current_lineno++;
837 break;
838 case '#': // comment
839 skip_line();
840 break;
841 default: // EOF or first essential character
842 return c;
847 /* odd():
848 Test whether argument is an odd number.
850 n: In-parameter, the integer to be tested.
852 Return: True if odd, false otherwise.
854 inline bool
855 odd(const int n)
857 return (n & 1) ? true : false;
860 /* position_to_end_of_args():
861 Move graphical pointer to end of drawn figure.
863 This is used by the D commands that draw open geometrical figures.
864 The algorithm simply sums up all horizontal displacements (arguments
865 with even number) for the horizontal component. Similarly, the
866 vertical component is the sum of the odd arguments.
868 args: In-parameter, the arguments of a former drawing command.
870 void
871 position_to_end_of_args(const IntArray * const args)
873 size_t i;
874 const size_t n = args->len();
875 for (i = 0; i < n; i += 2)
876 current_env->hpos += (*args)[i];
877 for (i = 1; i < n; i += 2)
878 current_env->vpos += (*args)[i];
881 /* remember_filename():
882 Set global variable current_filename.
884 The actual filename is stored in current_filename. This is used by
885 the postprocessors, expecting the name "<standard input>" for stdin.
887 filename: In-out-parameter; is changed to the new value also.
889 void
890 remember_filename(const char *filename)
892 char *fname;
893 if (strcmp(filename, "-") == 0)
894 fname = (char *)"<standard input>";
895 else
896 fname = (char *)filename;
897 size_t len = strlen(fname) + 1;
898 if (current_filename != 0)
899 free((char *)current_filename);
900 current_filename = (const char *)malloc(len);
901 if (current_filename == 0)
902 fatal("can't malloc space for filename");
903 strncpy((char *)current_filename, (char *)fname, len);
906 /* remember_source_filename():
907 Set global variable current_source_filename.
909 The actual filename is stored in current_filename. This is used by
910 the postprocessors, expecting the name "<standard input>" for stdin.
912 filename: In-out-parameter; is changed to the new value also.
914 void
915 remember_source_filename(const char *filename)
917 char *fname;
918 if (strcmp(filename, "-") == 0)
919 fname = (char *)"<standard input>";
920 else
921 fname = (char *)filename;
922 size_t len = strlen(fname) + 1;
923 if (current_source_filename != 0)
924 free((char *)current_source_filename);
925 current_source_filename = (const char *)malloc(len);
926 if (current_source_filename == 0)
927 fatal("can't malloc space for filename");
928 strncpy((char *)current_source_filename, (char *)fname, len);
931 /* send_draw():
932 Call draw method of printer class.
934 subcmd: Letter of actual D subcommand.
935 args: Array of integer arguments of actual D subcommand.
937 void
938 send_draw(const Char subcmd, const IntArray * const args)
940 EnvInt n = (EnvInt) args->len();
941 pr->draw((int) subcmd, (IntArg *)args->get_data(), n, current_env);
944 /* skip_line():
945 Go to next line within the input queue.
947 Skip the rest of the current line, including the newline character.
948 The global variable current_lineno is adjusted.
949 No errors are raised.
951 void
952 skip_line(void)
954 Char c = get_char();
955 while (1) {
956 if (c == '\n') {
957 current_lineno++;
958 break;
960 if (c == EOF)
961 break;
962 c = get_char();
966 /* skip_line_checked ():
967 Check that there aren't any arguments left on the rest of the line,
968 then skip line.
970 Spaces, tabs, and a comment are allowed before newline or EOF.
971 All other characters raise an error.
973 bool
974 skip_line_checked(void)
976 bool ok = true;
977 Char c = get_char();
978 while (is_space_or_tab(c))
979 c = get_char();
980 switch((int) c) {
981 case '#': // comment
982 skip_line();
983 break;
984 case '\n':
985 current_lineno++;
986 break;
987 case EOF:
988 break;
989 default:
990 ok = false;
991 skip_line();
992 break;
994 return ok;
997 /* skip_line_fatal ():
998 Fatal error if arguments left, otherwise skip line.
1000 Spaces, tabs, and a comment are allowed before newline or EOF.
1001 All other characters trigger the error.
1003 void
1004 skip_line_fatal(void)
1006 bool ok = skip_line_checked();
1007 if (!ok) {
1008 current_lineno--;
1009 error("too many arguments");
1010 current_lineno++;
1014 /* skip_line_warn ():
1015 Skip line, but warn if arguments are left on actual line.
1017 Spaces, tabs, and a comment are allowed before newline or EOF.
1018 All other characters raise a warning
1020 void
1021 skip_line_warn(void)
1023 bool ok = skip_line_checked();
1024 if (!ok) {
1025 current_lineno--;
1026 warning("too many arguments on current line");
1027 current_lineno++;
1031 /* skip_line_D ():
1032 Skip line in `D' commands.
1034 Decide whether in case of an additional argument a fatal error is
1035 raised (the documented classical behavior), only a warning is
1036 issued, or the line is just skipped (former groff behavior).
1037 Actually decided for the warning.
1039 void
1040 skip_line_D(void)
1042 skip_line_warn();
1043 // or: skip_line_fatal();
1044 // or: skip_line();
1047 /* skip_line_x ():
1048 Skip line in `x' commands.
1050 Decide whether in case of an additional argument a fatal error is
1051 raised (the documented classical behavior), only a warning is
1052 issued, or the line is just skipped (former groff behavior).
1053 Actually decided for the warning.
1055 void
1056 skip_line_x(void)
1058 skip_line_warn();
1059 // or: skip_line_fatal();
1060 // or: skip_line();
1063 /* skip_to_end_of_line():
1064 Go to the end of the current line.
1066 Skip the rest of the current line, excluding the newline character.
1067 The global variable current_lineno is not changed.
1068 No errors are raised.
1070 void
1071 skip_to_end_of_line(void)
1073 Char c = get_char();
1074 while (1) {
1075 if (c == '\n') {
1076 unget_char(c);
1077 return;
1079 if (c == EOF)
1080 return;
1081 c = get_char();
1085 /* unget_char(c):
1086 Restore character c onto input queue.
1088 Write a character back onto the input stream.
1089 EOF is gracefully handled.
1091 c: In-parameter; character to be pushed onto the input queue.
1093 inline void
1094 unget_char(const Char c)
1096 if (c != EOF) {
1097 int ch = (int) c;
1098 if (ungetc(ch, current_file) == EOF)
1099 fatal("could not unget character");
1103 /**********************************************************************
1104 parser subcommands
1105 **********************************************************************/
1107 /* parse_color_command:
1108 Process the commands m and DF, but not Df.
1110 col: In-out-parameter; the color object to be set, must have
1111 been initialized before.
1113 void
1114 parse_color_command(color *col)
1116 ColorArg gray = 0;
1117 ColorArg red = 0, green = 0, blue = 0;
1118 ColorArg cyan = 0, magenta = 0, yellow = 0, black = 0;
1119 Char subcmd = next_arg_begin();
1120 switch((int) subcmd) {
1121 case 'c': // DFc or mc: CMY
1122 cyan = get_color_arg();
1123 magenta = get_color_arg();
1124 yellow = get_color_arg();
1125 col->set_cmy(cyan, magenta, yellow);
1126 break;
1127 case 'd': // DFd or md: set default color
1128 col->set_default();
1129 break;
1130 case 'g': // DFg or mg: gray
1131 gray = get_color_arg();
1132 col->set_gray(gray);
1133 break;
1134 case 'k': // DFk or mk: CMYK
1135 cyan = get_color_arg();
1136 magenta = get_color_arg();
1137 yellow = get_color_arg();
1138 black = get_color_arg();
1139 col->set_cmyk(cyan, magenta, yellow, black);
1140 break;
1141 case 'r': // DFr or mr: RGB
1142 red = get_color_arg();
1143 green = get_color_arg();
1144 blue = get_color_arg();
1145 col->set_rgb(red, green, blue);
1146 break;
1147 default:
1148 error("invalid color scheme `%1'", (int) subcmd);
1149 break;
1150 } // end of color subcommands
1153 /* parse_D_command():
1154 Parse the subcommands of graphical command D.
1156 This is the part of the do_file() parser that scans the graphical
1157 subcommands.
1158 - Error on lacking or wrong arguments.
1159 - Warning on too many arguments.
1160 - Line is always skipped.
1162 void
1163 parse_D_command()
1165 Char subcmd = next_arg_begin();
1166 switch((int) subcmd) {
1167 case '~': // D~: draw B-spline
1168 // actually, this isn't available for some postprocessors
1169 // fall through
1170 default: // unknown options are passed to device
1172 IntArray *args = get_D_variable_args();
1173 send_draw(subcmd, args);
1174 position_to_end_of_args(args);
1175 delete args;
1176 break;
1178 case 'a': // Da: draw arc
1180 IntArray *args = get_D_fixed_args(4);
1181 send_draw(subcmd, args);
1182 position_to_end_of_args(args);
1183 delete args;
1184 break;
1186 case 'c': // Dc: draw circle line
1188 IntArray *args = get_D_fixed_args(1);
1189 send_draw(subcmd, args);
1190 // move to right end
1191 current_env->hpos += (*args)[0];
1192 delete args;
1193 break;
1195 case 'C': // DC: draw solid circle
1197 IntArray *args = get_D_fixed_args_odd_dummy(1);
1198 send_draw(subcmd, args);
1199 // move to right end
1200 current_env->hpos += (*args)[0];
1201 delete args;
1202 break;
1204 case 'e': // De: draw ellipse line
1205 case 'E': // DE: draw solid ellipse
1207 IntArray *args = get_D_fixed_args(2);
1208 send_draw(subcmd, args);
1209 // move to right end
1210 current_env->hpos += (*args)[0];
1211 delete args;
1212 break;
1214 case 'f': // Df: set fill gray; obsoleted by DFg
1216 IntArg arg = get_integer_arg();
1217 if ((arg >= 0) && (arg <= 1000)) {
1218 // convert arg and treat it like DFg
1219 ColorArg gray = color_from_Df_command(arg);
1220 current_env->fill->set_gray(gray);
1222 else {
1223 // set fill color to the same value as the current outline color
1224 delete current_env->fill;
1225 current_env->fill = new color(current_env->col);
1227 pr->change_fill_color(current_env);
1228 // skip unused `vertical' component (\D'...' always emits pairs)
1229 (void) get_integer_arg();
1230 # ifdef STUPID_DRAWING_POSITIONING
1231 current_env->hpos += arg;
1232 # endif
1233 skip_line_x();
1234 break;
1236 case 'F': // DF: set fill color, several formats
1237 parse_color_command(current_env->fill);
1238 pr->change_fill_color(current_env);
1239 // no positioning (setting-only command)
1240 skip_line_x();
1241 break;
1242 case 'l': // Dl: draw line
1244 IntArray *args = get_D_fixed_args(2);
1245 send_draw(subcmd, args);
1246 position_to_end_of_args(args);
1247 delete args;
1248 break;
1250 case 'p': // Dp: draw closed polygon line
1251 case 'P': // DP: draw solid closed polygon
1253 IntArray *args = get_D_variable_args();
1254 send_draw(subcmd, args);
1255 # ifdef STUPID_DRAWING_POSITIONING
1256 // final args positioning
1257 position_to_end_of_args(args);
1258 # endif
1259 delete args;
1260 break;
1262 case 't': // Dt: set line thickness
1264 IntArray *args = get_D_fixed_args_odd_dummy(1);
1265 send_draw(subcmd, args);
1266 # ifdef STUPID_DRAWING_POSITIONING
1267 // final args positioning
1268 position_to_end_of_args(args);
1269 # endif
1270 delete args;
1271 break;
1273 } // end of D subcommands
1276 /* parse_x_command():
1277 Parse subcommands of the device control command x.
1279 This is the part of the do_file() parser that scans the device
1280 controlling commands.
1281 - Error on duplicate prologue commands.
1282 - Error on wrong or lacking arguments.
1283 - Warning on too many arguments.
1284 - Line is always skipped.
1286 Globals:
1287 - current_env: is set by many subcommands.
1288 - npages: page counting variable
1290 Return: boolean in the meaning of `stopped'
1291 - true if parsing should be stopped (`x stop').
1292 - false if parsing should continue.
1294 bool
1295 parse_x_command(void)
1297 bool stopped = false;
1298 char *subcmd_str = get_string_arg();
1299 char subcmd = subcmd_str[0];
1300 switch (subcmd) {
1301 case 'f': // x font: mount font
1303 IntArg n = get_integer_arg();
1304 char *name = get_string_arg();
1305 pr->load_font(n, name);
1306 a_delete name;
1307 skip_line_x();
1308 break;
1310 case 'F': // x Filename: set filename for errors
1312 char *str_arg = get_extended_arg();
1313 if (str_arg == 0)
1314 warning("empty argument for `x F' command");
1315 else {
1316 remember_source_filename(str_arg);
1317 a_delete str_arg;
1319 break;
1321 case 'H': // x Height: set character height
1322 current_env->height = get_integer_arg();
1323 if (current_env->height == current_env->size)
1324 current_env->height = 0;
1325 skip_line_x();
1326 break;
1327 case 'i': // x init: initialize device
1328 error("duplicate `x init' command");
1329 skip_line_x();
1330 break;
1331 case 'p': // x pause: pause device
1332 skip_line_x();
1333 break;
1334 case 'r': // x res: set resolution
1335 error("duplicate `x res' command");
1336 skip_line_x();
1337 break;
1338 case 's': // x stop: stop device
1339 stopped = true;
1340 skip_line_x();
1341 break;
1342 case 'S': // x Slant: set slant
1343 current_env->slant = get_integer_arg();
1344 skip_line_x();
1345 break;
1346 case 't': // x trailer: generate trailer info
1347 skip_line_x();
1348 break;
1349 case 'T': // x Typesetter: set typesetter
1350 error("duplicate `x T' command");
1351 skip_line();
1352 break;
1353 case 'u': // x underline: from .cu
1355 char *str_arg = get_string_arg();
1356 pr->special(str_arg, current_env, 'u');
1357 a_delete str_arg;
1358 skip_line_x();
1359 break;
1361 case 'X': // x X: send uninterpretedly to device
1363 char *str_arg = get_extended_arg(); // includes line skip
1364 if (npages <= 0)
1365 error("`x X' command invalid before first `p' command");
1366 else if (str_arg && (strncmp(str_arg, "devtag:",
1367 strlen("devtag:")) == 0))
1368 pr->devtag(str_arg, current_env);
1369 else
1370 pr->special(str_arg, current_env);
1371 a_delete str_arg;
1372 break;
1374 default: // ignore unknown x commands, but warn
1375 warning("unknown command `x %1'", subcmd);
1376 skip_line();
1378 a_delete subcmd_str;
1379 return stopped;
1382 /**********************************************************************
1383 exported part (by driver.h)
1384 **********************************************************************/
1386 /* do_file():
1387 Parse and postprocess groff intermediate output.
1389 filename: "-" for standard input, normal file name otherwise
1391 void
1392 do_file(const char *filename)
1394 Char command;
1395 bool stopped = false; // terminating condition
1397 #ifdef USE_ENV_STACK
1398 EnvStack env_stack = EnvStack();
1399 #endif // USE_ENV_STACK
1401 // setup of global variables
1402 npages = 0;
1403 current_lineno = 1;
1404 // `pr' is initialized after the prologue.
1405 // `device' is set by the 1st prologue command.
1407 if (filename[0] == '-' && filename[1] == '\0')
1408 current_file = stdin;
1409 else {
1410 errno = 0;
1411 current_file = fopen(filename, "r");
1412 if (errno != 0 || current_file == 0) {
1413 error("can't open file `%1'", filename);
1414 return;
1417 remember_filename(filename);
1419 if (current_env != 0)
1420 delete_current_env();
1421 current_env = new environment;
1422 current_env->col = new color;
1423 current_env->fill = new color;
1424 current_env->fontno = -1;
1425 current_env->height = 0;
1426 current_env->hpos = -1;
1427 current_env->slant = 0;
1428 current_env->size = 0;
1429 current_env->vpos = -1;
1431 // parsing of prologue (first 3 commands)
1433 char *str_arg;
1434 IntArg int_arg;
1436 // 1st command `x T'
1437 command = next_command();
1438 if ((int) command == EOF)
1439 return;
1440 if ((int) command != 'x')
1441 fatal("the first command must be `x T'");
1442 str_arg = get_string_arg();
1443 if (str_arg[0] != 'T')
1444 fatal("the first command must be `x T'");
1445 a_delete str_arg;
1446 char *tmp_dev = get_string_arg();
1447 if (pr == 0) { // note: `pr' initialized after prologue
1448 device = tmp_dev;
1449 if (!font::load_desc())
1450 fatal("couldn't load DESC file, can't continue");
1452 else {
1453 if (device == 0 || strcmp(device, tmp_dev) != 0)
1454 fatal("all files must use the same device");
1455 a_delete tmp_dev;
1457 skip_line_x(); // ignore further arguments
1458 current_env->size = 10 * font::sizescale;
1460 // 2nd command `x res'
1461 command = next_command();
1462 if ((int) command != 'x')
1463 fatal("the second command must be `x res'");
1464 str_arg = get_string_arg();
1465 if (str_arg[0] != 'r')
1466 fatal("the second command must be `x res'");
1467 a_delete str_arg;
1468 int_arg = get_integer_arg();
1469 EnvInt font_res = font::res;
1470 if (int_arg != font_res)
1471 fatal("resolution does not match");
1472 int_arg = get_integer_arg();
1473 if (int_arg != font::hor)
1474 fatal("minimum horizontal motion does not match");
1475 int_arg = get_integer_arg();
1476 if (int_arg != font::vert)
1477 fatal("minimum vertical motion does not match");
1478 skip_line_x(); // ignore further arguments
1480 // 3rd command `x init'
1481 command = next_command();
1482 if (command != 'x')
1483 fatal("the third command must be `x init'");
1484 str_arg = get_string_arg();
1485 if (str_arg[0] != 'i')
1486 fatal("the third command must be `x init'");
1487 a_delete str_arg;
1488 skip_line_x();
1491 // parsing of body
1492 if (pr == 0)
1493 pr = make_printer();
1494 while (!stopped) {
1495 command = next_command();
1496 if (command == EOF)
1497 break;
1498 // spaces, tabs, comments, and newlines are skipped here
1499 switch ((int) command) {
1500 case '#': // #: comment, ignore up to end of line
1501 skip_line();
1502 break;
1503 #ifdef USE_ENV_STACK
1504 case '{': // {: start a new environment (a copy)
1505 env_stack.push(current_env);
1506 break;
1507 case '}': // }: pop previous env from stack
1508 delete_current_env();
1509 current_env = env_stack.pop();
1510 break;
1511 #endif // USE_ENV_STACK
1512 case '0': // ddc: obsolete jump and print command
1513 case '1':
1514 case '2':
1515 case '3':
1516 case '4':
1517 case '5':
1518 case '6':
1519 case '7':
1520 case '8':
1521 case '9':
1522 { // expect 2 digits and a character
1523 char s[3];
1524 Char c = next_arg_begin();
1525 if (npages <= 0)
1526 fatal_command(command);
1527 if (!isdigit((int) c)) {
1528 error("digit expected");
1529 c = 0;
1531 s[0] = (char) command;
1532 s[1] = (char) c;
1533 s[2] = '\0';
1534 errno = 0;
1535 long int x = strtol(s, 0, 10);
1536 if (errno != 0)
1537 error("couldn't convert 2 digits");
1538 EnvInt hor_pos = (EnvInt) x;
1539 current_env->hpos += hor_pos;
1540 c = next_arg_begin();
1541 if ((int) c == '\n' || (int) c == EOF)
1542 error("character argument expected");
1543 else
1544 pr->set_ascii_char((unsigned char) c, current_env);
1545 break;
1547 case 'c': // c: print ascii char without moving
1549 if (npages <= 0)
1550 fatal_command(command);
1551 Char c = next_arg_begin();
1552 if (c == '\n' || c == EOF)
1553 error("missing argument to `c' command");
1554 else
1555 pr->set_ascii_char((unsigned char) c, current_env);
1556 break;
1558 case 'C': // C: print named special character
1560 if (npages <= 0)
1561 fatal_command(command);
1562 char *str_arg = get_string_arg();
1563 pr->set_special_char(str_arg, current_env);
1564 a_delete str_arg;
1565 break;
1567 case 'D': // drawing commands
1568 if (npages <= 0)
1569 fatal_command(command);
1570 parse_D_command();
1571 break;
1572 case 'f': // f: set font to number
1573 current_env->fontno = get_integer_arg();
1574 break;
1575 case 'F': // F: obsolete, replaced by `x F'
1577 char *str_arg = get_extended_arg();
1578 remember_source_filename(str_arg);
1579 a_delete str_arg;
1580 break;
1582 case 'h': // h: relative horizontal move
1583 current_env->hpos += (EnvInt) get_integer_arg();
1584 break;
1585 case 'H': // H: absolute horizontal positioning
1586 current_env->hpos = (EnvInt) get_integer_arg();
1587 break;
1588 case 'm': // m: glyph color
1589 parse_color_command(current_env->col);
1590 pr->change_color(current_env);
1591 break;
1592 case 'n': // n: print end of line
1593 // ignore two arguments (historically)
1594 if (npages <= 0)
1595 fatal_command(command);
1596 pr->end_of_line();
1597 (void) get_integer_arg();
1598 (void) get_integer_arg();
1599 break;
1600 case 'N': // N: print char with given int code
1601 if (npages <= 0)
1602 fatal_command(command);
1603 pr->set_numbered_char(get_integer_arg(), current_env);
1604 break;
1605 case 'p': // p: start new page with given number
1606 if (npages > 0)
1607 pr->end_page(current_env->vpos);
1608 npages++; // increment # of processed pages
1609 pr->begin_page(get_integer_arg());
1610 current_env->vpos = 0;
1611 break;
1612 case 's': // s: set point size
1613 current_env->size = get_integer_arg();
1614 if (current_env->height == current_env->size)
1615 current_env->height = 0;
1616 break;
1617 case 't': // t: print a text word
1619 char c;
1620 if (npages <= 0)
1621 fatal_command(command);
1622 char *str_arg = get_string_arg();
1623 size_t i = 0;
1624 while ((c = str_arg[i++]) != '\0') {
1625 EnvInt w;
1626 pr->set_ascii_char((unsigned char) c, current_env, &w);
1627 current_env->hpos += w;
1629 a_delete str_arg;
1630 break;
1632 case 'u': // u: print spaced word
1634 char c;
1635 if (npages <= 0)
1636 fatal_command(command);
1637 EnvInt kern = (EnvInt) get_integer_arg();
1638 char *str_arg = get_string_arg();
1639 size_t i = 0;
1640 while ((c = str_arg[i++]) != '\0') {
1641 EnvInt w;
1642 pr->set_ascii_char((unsigned char) c, current_env, &w);
1643 current_env->hpos += w + kern;
1645 a_delete str_arg;
1646 break;
1648 case 'v': // v: relative vertical move
1649 current_env->vpos += (EnvInt) get_integer_arg();
1650 break;
1651 case 'V': // V: absolute vertical positioning
1652 current_env->vpos = (EnvInt) get_integer_arg();
1653 break;
1654 case 'w': // w: inform about paddable space
1655 break;
1656 case 'x': // device controlling commands
1657 stopped = parse_x_command();
1658 break;
1659 default:
1660 warning("unrecognized command `%1'", (unsigned char) command);
1661 skip_line();
1662 break;
1663 } // end of switch
1664 } // end of while
1666 // end of file reached
1667 if (npages > 0)
1668 pr->end_page(current_env->vpos);
1669 delete pr;
1670 pr = 0;
1671 fclose(current_file);
1672 // If `stopped' is not `true' here then there wasn't any `x stop'.
1673 if (!stopped)
1674 warning("no final `x stop' command");
1675 delete_current_env();
1678 // s-it2-mode