diff options
| author | christos <christos@NetBSD.org> | 2016-01-13 19:24:59 +0000 |
|---|---|---|
| committer | christos <christos@NetBSD.org> | 2016-01-13 19:24:59 +0000 |
| commit | a9cc03d5ae6c144ac3d14dbbe60d7922b532f3dc (patch) | |
| tree | 45905651b35cf9b92d48ecfed92e3c5037e7d2d9 /gnu/dist/groff/src/libs/libdriver/input.cpp | |
| parent | 64a6ddad5b422a192c6fc189ac147ecef15d0290 (diff) | |
groff has moved to external
Diffstat (limited to 'gnu/dist/groff/src/libs/libdriver/input.cpp')
| -rw-r--r-- | gnu/dist/groff/src/libs/libdriver/input.cpp | 1837 |
1 files changed, 0 insertions, 1837 deletions
diff --git a/gnu/dist/groff/src/libs/libdriver/input.cpp b/gnu/dist/groff/src/libs/libdriver/input.cpp deleted file mode 100644 index 0619a94b713..00000000000 --- a/gnu/dist/groff/src/libs/libdriver/input.cpp +++ /dev/null @@ -1,1837 +0,0 @@ -/* $NetBSD: input.cpp,v 1.2 2010/05/06 19:00:08 drochner Exp $ */ - -// -*- C++ -*- - -// <groff_src_dir>/src/libs/libdriver/input.cpp - -/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005 - Free Software Foundation, Inc. - - Written by James Clark (jjc@jclark.com) - Major rewrite 2001 by Bernd Warken (bwarken@mayn.de) - - Last update: 15 Jun 2005 - - This file is part of groff, the GNU roff text processing system. - - groff is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - groff is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with groff; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin St - Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -/* Description - - This file implements the parser for the intermediate groff output, - see groff_out(5), and does the printout for the given device. - - All parsed information is processed within the function do_file(). - A device postprocessor just needs to fill in the methods for the class - `printer' (or rather a derived class) without having to worry about - the syntax of the intermediate output format. Consequently, the - programming of groff postprocessors is similar to the development of - device drivers. - - The prototyping for this file is done in driver.h (and error.h). -*/ - -/* Changes of the 2001 rewrite of this file. - - The interface to the outside and the handling of the global - variables was not changed, but internally many necessary changes - were performed. - - The main aim for this rewrite is to provide a first step towards - making groff fully compatible with classical troff without pain. - - Bugs fixed - - Unknown subcommands of `D' and `x' are now ignored like in the - classical case, but a warning is issued. This was also - implemented for the other commands. - - A warning is emitted if `x stop' is missing. - - `DC' and `DE' commands didn't position to the right end after - drawing (now they do), see discussion below. - - So far, `x stop' was ignored. Now it terminates the processing - of the current intermediate output file like the classical troff. - - The command `c' didn't check correctly on white-space. - - The environment stack wasn't suitable for the color extensions - (replaced by a class). - - The old groff parser could only handle a prologue with the first - 3 lines having a fixed structure, while classical troff specified - the sequence of the first 3 commands without further - restrictions. Now the parser is smart about additional - white space, comments, and empty lines in the prologue. - - The old parser allowed space characters only as syntactical - separators, while classical troff had tab characters as well. - Now any sequence of tabs and/or spaces is a syntactical - separator between commands and/or arguments. - - Range checks for numbers implemented. - - New and improved features - - The color commands `m' and `DF' are added. - - The old color command `Df' is now converted and delegated to `DFg'. - - The command `F' is implemented as `use intended file name'. It - checks whether its argument agrees with the file name used so far, - otherwise a warning is issued. Then the new name is remembered - and used for the following error messages. - - For the positioning after drawing commands, an alternative, easier - scheme is provided, but not yet activated; it can be chosen by - undefining the preprocessor macro STUPID_DRAWING_POSITIONING. - It extends the rule of the classical troff output language in a - logical way instead of the rather strange actual positioning. - For details, see the discussion below. - - For the `D' commands that only set the environment, the calling of - pr->send_draw() was removed because this doesn't make sense for - the `DF' commands; the (changed) environment is sent with the - next command anyway. - - Error handling was clearly separated into warnings and fatal. - - The error behavior on additional arguments for `D' and `x' - commands with a fixed number of arguments was changed from being - ignored (former groff) to issue a warning and ignore (now), see - skip_line_x(). No fatal was chosen because both string and - integer arguments can occur. - - The gtroff program issues a trailing dummy integer argument for - some drawing commands with an odd number of arguments to make the - number of arguments even, e.g. the DC and Dt commands; this is - honored now. - - All D commands with a variable number of args expect an even - number of trailing integer arguments, so fatal on error was - implemented. - - Disable environment stack and the commands `{' and `}' by making - them conditional on macro USE_ENV_STACK; actually, this is - undefined by default. There isn't any known application for these - features. - - Cosmetics - - Nested `switch' commands are avoided by using more functions. - Dangerous 'fall-through's avoided. - - Commands and functions are sorted alphabetically (where possible). - - Dynamic arrays/buffers are now implemented as container classes. - - Some functions had an ugly return structure; this has been - streamlined by using classes. - - Use standard C math functions for number handling, so getting rid - of differences to '0'. - - The macro `IntArg' has been created for an easier transition - to guaranteed 32 bits integers (`int' is enough for GNU, while - ANSI only guarantees `long int' to have a length of 32 bits). - - The many usages of type `int' are differentiated by using `Char', - `bool', and `IntArg' where appropriate. - - To ease the calls of the local utility functions, the parser - variables `current_file', `npages', and `current_env' - (formerly env) were made global to the file (formerly they were - local to the do_file() function) - - Various comments were added. - - TODO - - Get rid of the stupid drawing positioning. - - Can the `Dt' command be completely handled by setting environment - within do_file() instead of sending to pr? - - Integer arguments must be >= 32 bits, use conditional #define. - - Add scaling facility for classical device independence and - non-groff devices. Classical troff output had a quasi device - independence by scaling the intermediate output to the resolution - of the postprocessor device if different from the one specified - with `x T', groff have not. So implement full quasi device - indepedence, including the mapping of the strange classical - devices to the postprocessor device (seems to be reasonably - easy). - - The external, global pointer variables are not optimally handled. - - The global variables `current_filename', - `current_source_filename', and `current_lineno' are only used for - error reporting. So implement a static class `Error' - (`::' calls). - - The global `device' is the name used during the formatting - process; there should be a new variable for the device name used - during the postprocessing. - - Implement the B-spline drawing `D~' for all graphical devices. - - Make `environment' a class with an overflow check for its members - and a delete method to get rid of delete_current_env(). - - Implement the `EnvStack' to use `new' instead of `malloc'. - - The class definitions of this document could go into a new file. - - The comments in this section should go to a `Changelog' or some - `README' file in this directory. -*/ - -/* - Discussion of the positioning by drawing commands - - There was some confusion about the positioning of the graphical - pointer at the printout after having executed a `D' command. - The classical troff manual of Osanna & Kernighan specified, - - `The position after a graphical object has been drawn is - at its end; for circles and ellipses, the "end" is at the - right side.' - - From this, it follows that - - all open figures (args, splines, and lines) should position at their - final point. - - all circles and ellipses should position at their right-most point - (as if 2 halves had been drawn). - - all closed figures apart from circles and ellipses shouldn't change - the position because they return to their origin. - - all setting commands should not change position because they do not - draw any graphical object. - - In the case of the open figures, this means that the horizontal - displacement is the sum of all odd arguments and the vertical offset - the sum of all even arguments, called the alternate arguments sum - displacement in the following. - - Unfortunately, groff did not implement this simple rule. The former - documentation in groff_out(5) differed from the source code, and - neither of them is compatible with the classical rule. - - The former groff_out(5) specified to use the alternative arguments - sum displacement for calculating the drawing positioning of - non-classical commands, including the `Dt' command (setting-only) - and closed polygons. Applying this to the new groff color commands - will lead to disaster. For their arguments can take large values (> - 65000). On low resolution devices, the displacement of such large - values will corrupt the display or kill the printer. So the - nonsense specification has come to a natural end anyway. - - The groff source code, however, had no positioning for the - setting-only commands (esp. `Dt'), the right-end positioning for - outlined circles and ellipses, and the alternative argument sum - displacement for all other commands (including filled circles and - ellipses). - - The reason why no one seems to have suffered from this mayhem so - far is that the graphical objects are usually generated by - preprocessors like pic that do not depend on the automatic - positioning. When using the low level `\D' escape sequences or `D' - output commands, the strange positionings can be circumvented by - absolute positionings or by tricks like `\Z'. - - So doing an exorcism on the strange, incompatible displacements might - not harm any existing documents, but will make the usage of the - graphical escape sequences and commands natural. - - That's why the rewrite of this file returned to the reasonable, - classical specification with its clear end-of-drawing rule that is - suitable for all cases. But a macro STUPID_DRAWING_POSITIONING is - provided for testing the funny former behavior. - - The new rule implies the following behavior. - - Setting commands (`Dt', `Df', `DF') and polygons (`Dp' and `DP') - do not change position now. - - Filled circles and ellipses (`DC' and `DE') position at their - most right point (outlined ones `Dc' and `De' did this anyway). - - As before, all open graphical objects position to their final - drawing point (alternate sum of the command arguments). - -*/ - -#ifndef STUPID_DRAWING_POSITIONING -// uncomment next line if all non-classical D commands shall position -// to the strange alternate sum of args displacement -#define STUPID_DRAWING_POSITIONING -#endif - -// Decide whether the commands `{' and `}' for different environments -// should be used. -#undef USE_ENV_STACK - -#include "driver.h" -#include "device.h" - -#include <stdlib.h> -#include <errno.h> -#include <ctype.h> -#include <math.h> - - -/********************************************************************** - local types - **********************************************************************/ - -// integer type used in the fields of struct environment (see printer.h) -typedef int EnvInt; - -// integer arguments of groff_out commands, must be >= 32 bits -typedef int IntArg; - -// color components of groff_out color commands, must be >= 32 bits -typedef unsigned int ColorArg; - -// Array for IntArg values. -class IntArray { - size_t num_allocated; - size_t num_stored; - IntArg *data; -public: - IntArray(void); - IntArray(const size_t); - ~IntArray(void); - IntArg operator[](const size_t i) const - { - if (i >= num_stored) - fatal("index out of range"); - return (IntArg) data[i]; - } - void append(IntArg); - IntArg *get_data(void) const { return (IntArg *)data; } - size_t len(void) const { return num_stored; } -}; - -// Characters read from the input queue. -class Char { - int data; -public: - Char(void) : data('\0') {} - Char(const int c) : data(c) {} - bool operator==(char c) const { return (data == c) ? true : false; } - bool operator==(int c) const { return (data == c) ? true : false; } - bool operator==(const Char c) const - { return (data == c.data) ? true : false; } - bool operator!=(char c) const { return !(*this == c); } - bool operator!=(int c) const { return !(*this == c); } - bool operator!=(const Char c) const { return !(*this == c); } - operator int() const { return (int) data; } - operator unsigned char() const { return (unsigned char) data; } - operator char() const { return (char) data; } -}; - -// Buffer for string arguments (Char, not char). -class StringBuf { - size_t num_allocated; - size_t num_stored; - Char *data; // not terminated by '\0' -public: - StringBuf(void); // allocate without storing - ~StringBuf(void); - void append(const Char); // append character to `data' - char *make_string(void); // return new copy of `data' with '\0' - bool is_empty(void) { // true if none stored - return (num_stored > 0) ? false : true; - } - void reset(void); // set `num_stored' to 0 -}; - -#ifdef USE_ENV_STACK -class EnvStack { - environment **data; - size_t num_allocated; - size_t num_stored; -public: - EnvStack(void); - ~EnvStack(void); - environment *pop(void); - void push(environment *e); -}; -#endif // USE_ENV_STACK - - -/********************************************************************** - external variables - **********************************************************************/ - -// exported as extern by error.h (called from driver.h) -// needed for error messages (see ../libgroff/error.cpp) -const char *current_filename = 0; // printable name of the current file - // printable name of current source file -const char *current_source_filename = 0; -int current_lineno = 0; // current line number of printout - -// exported as extern by device.h; -const char *device = 0; // cancel former init with literal - -printer *pr; - -// Note: -// -// We rely on an implementation of the `new' operator which aborts -// gracefully if it can't allocate memory (e.g. from libgroff/new.cpp). - - -/********************************************************************** - static local variables - **********************************************************************/ - -FILE *current_file = 0; // current input stream for parser - -// npages: number of pages processed so far (including current page), -// _not_ the page number in the printout (can be set with `p'). -int npages = 0; - -const ColorArg -COLORARG_MAX = (ColorArg) 65536U; // == 0xFFFF + 1 == 0x10000 - -const IntArg -INTARG_MAX = (IntArg) 0x7FFFFFFF; // maximal signed 32 bits number - -// parser environment, created and deleted by each run of do_file() -environment *current_env = 0; - -#ifdef USE_ENV_STACK -const size_t -envp_size = sizeof(environment *); -#endif // USE_ENV_STACK - - -/********************************************************************** - function declarations - **********************************************************************/ - -// utility functions -ColorArg color_from_Df_command(IntArg); - // transform old color into new -void delete_current_env(void); // delete global var current_env -void fatal_command(char); // abort for invalid command -inline Char get_char(void); // read next character from input stream -ColorArg get_color_arg(void); // read in argument for new color cmds -IntArray *get_D_fixed_args(const size_t); - // read in fixed number of integer - // arguments -IntArray *get_D_fixed_args_odd_dummy(const size_t); - // read in a fixed number of integer - // arguments plus optional dummy -IntArray *get_D_variable_args(void); - // variable, even number of int args -char *get_extended_arg(void); // argument for `x X' (several lines) -IntArg get_integer_arg(void); // read in next integer argument -IntArray *get_possibly_integer_args(); - // 0 or more integer arguments -char *get_string_arg(void); // read in next string arg, ended by WS -inline bool is_space_or_tab(const Char); - // test on space/tab char -Char next_arg_begin(void); // skip white space on current line -Char next_command(void); // go to next command, evt. diff. line -inline bool odd(const int); // test if integer is odd -void position_to_end_of_args(const IntArray * const); - // positioning after drawing -void remember_filename(const char *); - // set global current_filename -void remember_source_filename(const char *); - // set global current_source_filename -void send_draw(const Char, const IntArray * const); - // call pr->draw -void skip_line(void); // unconditionally skip to next line -bool skip_line_checked(void); // skip line, false if args are left -void skip_line_fatal(void); // skip line, fatal if args are left -void skip_line_warn(void); // skip line, warn if args are left -void skip_line_D(void); // skip line in D commands -void skip_line_x(void); // skip line in x commands -void skip_to_end_of_line(void); // skip to the end of the current line -inline void unget_char(const Char); - // restore character onto input - -// parser subcommands -void parse_color_command(color *); - // color sub(sub)commands m and DF -void parse_D_command(void); // graphical subcommands -bool parse_x_command(void); // device controller subcommands - - -/********************************************************************** - class methods - **********************************************************************/ - -#ifdef USE_ENV_STACK -EnvStack::EnvStack(void) -{ - num_allocated = 4; - // allocate pointer to array of num_allocated pointers to environment - data = (environment **)malloc(envp_size * num_allocated); - if (data == 0) - fatal("could not allocate environment data"); - num_stored = 0; -} - -EnvStack::~EnvStack(void) -{ - for (size_t i = 0; i < num_stored; i++) - delete data[i]; - free(data); -} - -// return top element from stack and decrease stack pointer -// -// the calling function must take care of properly deleting the result -environment * -EnvStack::pop(void) -{ - num_stored--; - environment *result = data[num_stored]; - data[num_stored] = 0; - return result; -} - -// copy argument and push this onto the stack -void -EnvStack::push(environment *e) -{ - environment *e_copy = new environment; - if (num_stored >= num_allocated) { - environment **old_data = data; - num_allocated *= 2; - data = (environment **)malloc(envp_size * num_allocated); - if (data == 0) - fatal("could not allocate data"); - for (size_t i = 0; i < num_stored; i++) - data[i] = old_data[i]; - free(old_data); - } - e_copy->col = new color; - e_copy->fill = new color; - *e_copy->col = *e->col; - *e_copy->fill = *e->fill; - e_copy->fontno = e->fontno; - e_copy->height = e->height; - e_copy->hpos = e->hpos; - e_copy->size = e->size; - e_copy->slant = e->slant; - e_copy->vpos = e->vpos; - data[num_stored] = e_copy; - num_stored++; -} -#endif // USE_ENV_STACK - -IntArray::IntArray(void) -{ - num_allocated = 4; - data = new IntArg[num_allocated]; - num_stored = 0; -} - -IntArray::IntArray(const size_t n) -{ - if (n <= 0) - fatal("number of integers to be allocated must be > 0"); - num_allocated = n; - data = new IntArg[num_allocated]; - num_stored = 0; -} - -IntArray::~IntArray(void) -{ - a_delete data; -} - -void -IntArray::append(IntArg x) -{ - if (num_stored >= num_allocated) { - IntArg *old_data = data; - num_allocated *= 2; - data = new IntArg[num_allocated]; - for (size_t i = 0; i < num_stored; i++) - data[i] = old_data[i]; - a_delete old_data; - } - data[num_stored] = x; - num_stored++; -} - -StringBuf::StringBuf(void) -{ - num_stored = 0; - num_allocated = 128; - data = new Char[num_allocated]; -} - -StringBuf::~StringBuf(void) -{ - a_delete data; -} - -void -StringBuf::append(const Char c) -{ - if (num_stored >= num_allocated) { - Char *old_data = data; - num_allocated *= 2; - data = new Char[num_allocated]; - for (size_t i = 0; i < num_stored; i++) - data[i] = old_data[i]; - a_delete old_data; - } - data[num_stored] = c; - num_stored++; -} - -char * -StringBuf::make_string(void) -{ - char *result = new char[num_stored + 1]; - for (size_t i = 0; i < num_stored; i++) - result[i] = (char) data[i]; - result[num_stored] = '\0'; - return result; -} - -void -StringBuf::reset(void) -{ - num_stored = 0; -} - -/********************************************************************** - utility functions - **********************************************************************/ - -////////////////////////////////////////////////////////////////////// -/* color_from_Df_command: - Process the gray shade setting command Df. - - Transform Df style color into DF style color. - Df color: 0-1000, 0 is white - DF color: 0-65536, 0 is black - - The Df command is obsoleted by command DFg, but kept for - compatibility. -*/ -ColorArg -color_from_Df_command(IntArg Df_gray) -{ - return ColorArg((1000-Df_gray) * COLORARG_MAX / 1000); // scaling -} - -////////////////////////////////////////////////////////////////////// -/* delete_current_env(): - Delete global variable current_env and its pointer members. - - This should be a class method of environment. -*/ -void delete_current_env(void) -{ - delete current_env->col; - delete current_env->fill; - delete current_env; - current_env = 0; -} - -////////////////////////////////////////////////////////////////////// -/* fatal_command(): - Emit error message about invalid command and abort. -*/ -void -fatal_command(char command) -{ - fatal("`%1' command invalid before first `p' command", command); -} - -////////////////////////////////////////////////////////////////////// -/* get_char(): - Retrieve the next character from the input queue. - - Return: The retrieved character (incl. EOF), converted to Char. -*/ -inline Char -get_char(void) -{ - return (Char) getc(current_file); -} - -////////////////////////////////////////////////////////////////////// -/* get_color_arg(): - Retrieve an argument suitable for the color commands m and DF. - - Return: The retrieved color argument. -*/ -ColorArg -get_color_arg(void) -{ - IntArg x = get_integer_arg(); - if (x < 0 || x > (IntArg)COLORARG_MAX) { - error("color component argument out of range"); - x = 0; - } - return (ColorArg) x; -} - -////////////////////////////////////////////////////////////////////// -/* get_D_fixed_args(): - Get a fixed number of integer arguments for D commands. - - Fatal if wrong number of arguments. - Too many arguments on the line raise a warning. - A line skip is done. - - number: In-parameter, the number of arguments to be retrieved. - ignore: In-parameter, ignore next argument -- GNU troff always emits - pairs of parameters for `D' extensions added by groff. - Default is `false'. - - Return: New IntArray containing the arguments. -*/ -IntArray * -get_D_fixed_args(const size_t number) -{ - if (number <= 0) - fatal("requested number of arguments must be > 0"); - IntArray *args = new IntArray(number); - for (size_t i = 0; i < number; i++) - args->append(get_integer_arg()); - skip_line_D(); - return args; -} - -////////////////////////////////////////////////////////////////////// -/* get_D_fixed_args_odd_dummy(): - Get a fixed number of integer arguments for D commands and optionally - ignore a dummy integer argument if the requested number is odd. - - The gtroff program adds a dummy argument to some commands to get - an even number of arguments. - Error if the number of arguments differs from the scheme above. - A line skip is done. - - number: In-parameter, the number of arguments to be retrieved. - - Return: New IntArray containing the arguments. -*/ -IntArray * -get_D_fixed_args_odd_dummy(const size_t number) -{ - if (number <= 0) - fatal("requested number of arguments must be > 0"); - IntArray *args = new IntArray(number); - for (size_t i = 0; i < number; i++) - args->append(get_integer_arg()); - if (odd(number)) { - IntArray *a = get_possibly_integer_args(); - if (a->len() > 1) - error("too many arguments"); - delete a; - } - skip_line_D(); - return args; -} - -////////////////////////////////////////////////////////////////////// -/* get_D_variable_args(): - Get a variable even number of integer arguments for D commands. - - Get as many integer arguments as possible from the rest of the - current line. - - The arguments are separated by an arbitrary sequence of space or - tab characters. - - A comment, a newline, or EOF indicates the end of processing. - - Error on non-digit characters different from these. - - A final line skip is performed (except for EOF). - - Return: New IntArray of the retrieved arguments. -*/ -IntArray * -get_D_variable_args() -{ - IntArray *args = get_possibly_integer_args(); - size_t n = args->len(); - if (n <= 0) - error("no arguments found"); - if (odd(n)) - error("even number of arguments expected"); - skip_line_D(); - return args; -} - -////////////////////////////////////////////////////////////////////// -/* get_extended_arg(): - Retrieve extended arg for `x X' command. - - - Skip leading spaces and tabs, error on EOL or newline. - - Return everything before the next NL or EOF ('#' is not a comment); - as long as the following line starts with '+' this is returned - as well, with the '+' replaced by a newline. - - Final line skip is always performed. - - Return: Allocated (new) string of retrieved text argument. -*/ -char * -get_extended_arg(void) -{ - StringBuf buf = StringBuf(); - Char c = next_arg_begin(); - while ((int) c != EOF) { - if ((int) c == '\n') { - current_lineno++; - c = get_char(); - if ((int) c == '+') - buf.append((Char) '\n'); - else { - unget_char(c); // first character of next line - break; - } - } - else - buf.append(c); - c = get_char(); - } - return buf.make_string(); -} - -////////////////////////////////////////////////////////////////////// -/* get_integer_arg(): Retrieve integer argument. - - Skip leading spaces and tabs, collect an optional '-' and all - following decimal digits (at least one) up to the next non-digit, - which is restored onto the input queue. - - Fatal error on all other situations. - - Return: Retrieved integer. -*/ -IntArg -get_integer_arg(void) -{ - StringBuf buf = StringBuf(); - Char c = next_arg_begin(); - if ((int) c == '-') { - buf.append(c); - c = get_char(); - } - if (!isdigit((int) c)) - error("integer argument expected"); - while (isdigit((int) c)) { - buf.append(c); - c = get_char(); - } - // c is not a digit - unget_char(c); - char *s = buf.make_string(); - errno = 0; - long int number = strtol(s, 0, 10); - if (errno != 0 - || number > INTARG_MAX || number < -INTARG_MAX) { - error("integer argument too large"); - number = 0; - } - a_delete s; - return (IntArg) number; -} - -////////////////////////////////////////////////////////////////////// -/* get_possibly_integer_args(): - Parse the rest of the input line as a list of integer arguments. - - Get as many integer arguments as possible from the rest of the - current line, even none. - - The arguments are separated by an arbitrary sequence of space or - tab characters. - - A comment, a newline, or EOF indicates the end of processing. - - Error on non-digit characters different from these. - - No line skip is performed. - - Return: New IntArray of the retrieved arguments. -*/ -IntArray * -get_possibly_integer_args() -{ - bool done = false; - StringBuf buf = StringBuf(); - Char c = get_char(); - IntArray *args = new IntArray(); - while (!done) { - buf.reset(); - while (is_space_or_tab(c)) - c = get_char(); - if (c == '-') { - Char c1 = get_char(); - if (isdigit((int) c1)) { - buf.append(c); - c = c1; - } - else - unget_char(c1); - } - while (isdigit((int) c)) { - buf.append(c); - c = get_char(); - } - if (!buf.is_empty()) { - char *s = buf.make_string(); - errno = 0; - long int x = strtol(s, 0, 10); - if (errno - || x > INTARG_MAX || x < -INTARG_MAX) { - error("invalid integer argument, set to 0"); - x = 0; - } - args->append((IntArg) x); - a_delete s; - } - // Here, c is not a digit. - // Terminate on comment, end of line, or end of file, while - // space or tab indicate continuation; otherwise error. - switch((int) c) { - case '#': - skip_to_end_of_line(); - done = true; - break; - case '\n': - done = true; - unget_char(c); - break; - case EOF: - done = true; - break; - case ' ': - case '\t': - break; - default: - error("integer argument expected"); - break; - } - } - return args; -} - -////////////////////////////////////////////////////////////////////// -/* get_string_arg(): - Retrieve string arg. - - - Skip leading spaces and tabs; error on EOL or newline. - - Return all following characters before the next space, tab, - newline, or EOF character (in-word '#' is not a comment character). - - The terminating space, tab, newline, or EOF character is restored - onto the input queue, so no line skip. - - Return: Retrieved string as char *, allocated by 'new'. -*/ -char * -get_string_arg(void) -{ - StringBuf buf = StringBuf(); - Char c = next_arg_begin(); - while (!is_space_or_tab(c) - && c != Char('\n') && c != Char(EOF)) { - buf.append(c); - c = get_char(); - } - unget_char(c); // restore white space - return buf.make_string(); -} - -////////////////////////////////////////////////////////////////////// -/* is_space_or_tab(): - Test a character if it is a space or tab. - - c: In-parameter, character to be tested. - - Return: True, if c is a space or tab character, false otherwise. -*/ -inline bool -is_space_or_tab(const Char c) -{ - return (c == Char(' ') || c == Char('\t')) ? true : false; -} - -////////////////////////////////////////////////////////////////////// -/* next_arg_begin(): - Return first character of next argument. - - Skip space and tab characters; error on newline or EOF. - - Return: The first character different from these (including '#'). -*/ -Char -next_arg_begin(void) -{ - Char c; - while (1) { - c = get_char(); - switch ((int) c) { - case ' ': - case '\t': - break; - case '\n': - case EOF: - error("missing argument"); - break; - default: // first essential character - return c; - } - } -} - -////////////////////////////////////////////////////////////////////// -/* next_command(): - Find the first character of the next command. - - Skip spaces, tabs, comments (introduced by #), and newlines. - - Return: The first character different from these (including EOF). -*/ -Char -next_command(void) -{ - Char c; - while (1) { - c = get_char(); - switch ((int) c) { - case ' ': - case '\t': - break; - case '\n': - current_lineno++; - break; - case '#': // comment - skip_line(); - break; - default: // EOF or first essential character - return c; - } - } -} - -////////////////////////////////////////////////////////////////////// -/* odd(): - Test whether argument is an odd number. - - n: In-parameter, the integer to be tested. - - Return: True if odd, false otherwise. -*/ -inline bool -odd(const int n) -{ - return (n & 1) ? true : false; -} - -////////////////////////////////////////////////////////////////////// -/* position_to_end_of_args(): - Move graphical pointer to end of drawn figure. - - This is used by the D commands that draw open geometrical figures. - The algorithm simply sums up all horizontal displacements (arguments - with even number) for the horizontal component. Similarly, the - vertical component is the sum of the odd arguments. - - args: In-parameter, the arguments of a former drawing command. -*/ -void -position_to_end_of_args(const IntArray * const args) -{ - size_t i; - const size_t n = args->len(); - for (i = 0; i < n; i += 2) - current_env->hpos += (*args)[i]; - for (i = 1; i < n; i += 2) - current_env->vpos += (*args)[i]; -} - -////////////////////////////////////////////////////////////////////// -/* remember_filename(): - Set global variable current_filename. - - The actual filename is stored in current_filename. This is used by - the postprocessors, expecting the name "<standard input>" for stdin. - - filename: In-out-parameter; is changed to the new value also. -*/ -void -remember_filename(const char *filename) -{ - char *fname; - if (strcmp(filename, "-") == 0) - fname = (char *)"<standard input>"; - else - fname = (char *)filename; - size_t len = strlen(fname) + 1; - if (current_filename != 0) - free((char *)current_filename); - current_filename = (const char *)malloc(len); - if (current_filename == 0) - fatal("can't malloc space for filename"); - strncpy((char *)current_filename, (char *)fname, len); -} - -////////////////////////////////////////////////////////////////////// -/* remember_source_filename(): - Set global variable current_source_filename. - - The actual filename is stored in current_filename. This is used by - the postprocessors, expecting the name "<standard input>" for stdin. - - filename: In-out-parameter; is changed to the new value also. -*/ -void -remember_source_filename(const char *filename) -{ - char *fname; - if (strcmp(filename, "-") == 0) - fname = (char *)"<standard input>"; - else - fname = (char *)filename; - size_t len = strlen(fname) + 1; - if (current_source_filename != 0) - free((char *)current_source_filename); - current_source_filename = (const char *)malloc(len); - if (current_source_filename == 0) - fatal("can't malloc space for filename"); - strncpy((char *)current_source_filename, (char *)fname, len); -} - -////////////////////////////////////////////////////////////////////// -/* send_draw(): - Call draw method of printer class. - - subcmd: Letter of actual D subcommand. - args: Array of integer arguments of actual D subcommand. -*/ -void -send_draw(const Char subcmd, const IntArray * const args) -{ - EnvInt n = (EnvInt) args->len(); - pr->draw((int) subcmd, (IntArg *)args->get_data(), n, current_env); -} - -////////////////////////////////////////////////////////////////////// -/* skip_line(): - Go to next line within the input queue. - - Skip the rest of the current line, including the newline character. - The global variable current_lineno is adjusted. - No errors are raised. -*/ -void -skip_line(void) -{ - Char c = get_char(); - while (1) { - if (c == '\n') { - current_lineno++; - break; - } - if (c == EOF) - break; - c = get_char(); - } -} - -////////////////////////////////////////////////////////////////////// -/* skip_line_checked (): - Check that there aren't any arguments left on the rest of the line, - then skip line. - - Spaces, tabs, and a comment are allowed before newline or EOF. - All other characters raise an error. -*/ -bool -skip_line_checked(void) -{ - bool ok = true; - Char c = get_char(); - while (is_space_or_tab(c)) - c = get_char(); - switch((int) c) { - case '#': // comment - skip_line(); - break; - case '\n': - current_lineno++; - break; - case EOF: - break; - default: - ok = false; - skip_line(); - break; - } - return ok; -} - -////////////////////////////////////////////////////////////////////// -/* skip_line_fatal (): - Fatal error if arguments left, otherwise skip line. - - Spaces, tabs, and a comment are allowed before newline or EOF. - All other characters trigger the error. -*/ -void -skip_line_fatal(void) -{ - bool ok = skip_line_checked(); - if (!ok) { - current_lineno--; - error("too many arguments"); - current_lineno++; - } -} - -////////////////////////////////////////////////////////////////////// -/* skip_line_warn (): - Skip line, but warn if arguments are left on actual line. - - Spaces, tabs, and a comment are allowed before newline or EOF. - All other characters raise a warning -*/ -void -skip_line_warn(void) -{ - bool ok = skip_line_checked(); - if (!ok) { - current_lineno--; - warning("too many arguments on current line"); - current_lineno++; - } -} - -////////////////////////////////////////////////////////////////////// -/* skip_line_D (): - Skip line in `D' commands. - - Decide whether in case of an additional argument a fatal error is - raised (the documented classical behavior), only a warning is - issued, or the line is just skipped (former groff behavior). - Actually decided for the warning. -*/ -void -skip_line_D(void) -{ - skip_line_warn(); - // or: skip_line_fatal(); - // or: skip_line(); -} - -////////////////////////////////////////////////////////////////////// -/* skip_line_x (): - Skip line in `x' commands. - - Decide whether in case of an additional argument a fatal error is - raised (the documented classical behavior), only a warning is - issued, or the line is just skipped (former groff behavior). - Actually decided for the warning. -*/ -void -skip_line_x(void) -{ - skip_line_warn(); - // or: skip_line_fatal(); - // or: skip_line(); -} - -////////////////////////////////////////////////////////////////////// -/* skip_to_end_of_line(): - Go to the end of the current line. - - Skip the rest of the current line, excluding the newline character. - The global variable current_lineno is not changed. - No errors are raised. -*/ -void -skip_to_end_of_line(void) -{ - Char c = get_char(); - while (1) { - if (c == '\n') { - unget_char(c); - return; - } - if (c == EOF) - return; - c = get_char(); - } -} - -////////////////////////////////////////////////////////////////////// -/* unget_char(c): - Restore character c onto input queue. - - Write a character back onto the input stream. - EOF is gracefully handled. - - c: In-parameter; character to be pushed onto the input queue. -*/ -inline void -unget_char(const Char c) -{ - if (c != EOF) { - int ch = (int) c; - if (ungetc(ch, current_file) == EOF) - fatal("could not unget character"); - } -} - - -/********************************************************************** - parser subcommands - **********************************************************************/ - -////////////////////////////////////////////////////////////////////// -/* parse_color_command: - Process the commands m and DF, but not Df. - - col: In-out-parameter; the color object to be set, must have - been initialized before. -*/ -void -parse_color_command(color *col) -{ - ColorArg gray = 0; - ColorArg red = 0, green = 0, blue = 0; - ColorArg cyan = 0, magenta = 0, yellow = 0, black = 0; - Char subcmd = next_arg_begin(); - switch((int) subcmd) { - case 'c': // DFc or mc: CMY - cyan = get_color_arg(); - magenta = get_color_arg(); - yellow = get_color_arg(); - col->set_cmy(cyan, magenta, yellow); - break; - case 'd': // DFd or md: set default color - col->set_default(); - break; - case 'g': // DFg or mg: gray - gray = get_color_arg(); - col->set_gray(gray); - break; - case 'k': // DFk or mk: CMYK - cyan = get_color_arg(); - magenta = get_color_arg(); - yellow = get_color_arg(); - black = get_color_arg(); - col->set_cmyk(cyan, magenta, yellow, black); - break; - case 'r': // DFr or mr: RGB - red = get_color_arg(); - green = get_color_arg(); - blue = get_color_arg(); - col->set_rgb(red, green, blue); - break; - default: - error("invalid color scheme `%1'", (int) subcmd); - break; - } // end of color subcommands -} - -////////////////////////////////////////////////////////////////////// -/* parse_D_command(): - Parse the subcommands of graphical command D. - - This is the part of the do_file() parser that scans the graphical - subcommands. - - Error on lacking or wrong arguments. - - Warning on too many arguments. - - Line is always skipped. -*/ -void -parse_D_command() -{ - Char subcmd = next_arg_begin(); - switch((int) subcmd) { - case '~': // D~: draw B-spline - // actually, this isn't available for some postprocessors - // fall through - default: // unknown options are passed to device - { - IntArray *args = get_D_variable_args(); - send_draw(subcmd, args); - position_to_end_of_args(args); - delete args; - break; - } - case 'a': // Da: draw arc - { - IntArray *args = get_D_fixed_args(4); - send_draw(subcmd, args); - position_to_end_of_args(args); - delete args; - break; - } - case 'c': // Dc: draw circle line - { - IntArray *args = get_D_fixed_args(1); - send_draw(subcmd, args); - // move to right end - current_env->hpos += (*args)[0]; - delete args; - break; - } - case 'C': // DC: draw solid circle - { - IntArray *args = get_D_fixed_args_odd_dummy(1); - send_draw(subcmd, args); - // move to right end - current_env->hpos += (*args)[0]; - delete args; - break; - } - case 'e': // De: draw ellipse line - case 'E': // DE: draw solid ellipse - { - IntArray *args = get_D_fixed_args(2); - send_draw(subcmd, args); - // move to right end - current_env->hpos += (*args)[0]; - delete args; - break; - } - case 'f': // Df: set fill gray; obsoleted by DFg - { - IntArg arg = get_integer_arg(); - if ((arg >= 0) && (arg <= 1000)) { - // convert arg and treat it like DFg - ColorArg gray = color_from_Df_command(arg); - current_env->fill->set_gray(gray); - } - else { - // set fill color to the same value as the current outline color - delete current_env->fill; - current_env->fill = new color(current_env->col); - } - pr->change_fill_color(current_env); - // skip unused `vertical' component (\D'...' always emits pairs) - (void) get_integer_arg(); -# ifdef STUPID_DRAWING_POSITIONING - current_env->hpos += arg; -# endif - skip_line_x(); - break; - } - case 'F': // DF: set fill color, several formats - parse_color_command(current_env->fill); - pr->change_fill_color(current_env); - // no positioning (setting-only command) - skip_line_x(); - break; - case 'l': // Dl: draw line - { - IntArray *args = get_D_fixed_args(2); - send_draw(subcmd, args); - position_to_end_of_args(args); - delete args; - break; - } - case 'p': // Dp: draw closed polygon line - case 'P': // DP: draw solid closed polygon - { - IntArray *args = get_D_variable_args(); - send_draw(subcmd, args); -# ifdef STUPID_DRAWING_POSITIONING - // final args positioning - position_to_end_of_args(args); -# endif - delete args; - break; - } - case 't': // Dt: set line thickness - { - IntArray *args = get_D_fixed_args_odd_dummy(1); - send_draw(subcmd, args); -# ifdef STUPID_DRAWING_POSITIONING - // final args positioning - position_to_end_of_args(args); -# endif - delete args; - break; - } - } // end of D subcommands -} - -////////////////////////////////////////////////////////////////////// -/* parse_x_command(): - Parse subcommands of the device control command x. - - This is the part of the do_file() parser that scans the device - controlling commands. - - Error on duplicate prologue commands. - - Error on wrong or lacking arguments. - - Warning on too many arguments. - - Line is always skipped. - - Globals: - - current_env: is set by many subcommands. - - npages: page counting variable - - Return: boolean in the meaning of `stopped' - - true if parsing should be stopped (`x stop'). - - false if parsing should continue. -*/ -bool -parse_x_command(void) -{ - bool stopped = false; - char *subcmd_str = get_string_arg(); - char subcmd = subcmd_str[0]; - switch (subcmd) { - case 'f': // x font: mount font - { - IntArg n = get_integer_arg(); - char *name = get_string_arg(); - pr->load_font(n, name); - a_delete name; - skip_line_x(); - break; - } - case 'F': // x Filename: set filename for errors - { - char *str_arg = get_string_arg(); - if (str_arg == 0) - warning("empty argument for `x F' command"); - else { - remember_source_filename(str_arg); - a_delete str_arg; - } - break; - } - case 'H': // x Height: set character height - current_env->height = get_integer_arg(); - if (current_env->height == current_env->size) - current_env->height = 0; - skip_line_x(); - break; - case 'i': // x init: initialize device - error("duplicate `x init' command"); - skip_line_x(); - break; - case 'p': // x pause: pause device - skip_line_x(); - break; - case 'r': // x res: set resolution - error("duplicate `x res' command"); - skip_line_x(); - break; - case 's': // x stop: stop device - stopped = true; - skip_line_x(); - break; - case 'S': // x Slant: set slant - current_env->slant = get_integer_arg(); - skip_line_x(); - break; - case 't': // x trailer: generate trailer info - skip_line_x(); - break; - case 'T': // x Typesetter: set typesetter - error("duplicate `x T' command"); - skip_line(); - break; - case 'u': // x underline: from .cu - { - char *str_arg = get_string_arg(); - pr->special(str_arg, current_env, 'u'); - a_delete str_arg; - skip_line_x(); - break; - } - case 'X': // x X: send uninterpretedly to device - { - char *str_arg = get_extended_arg(); // includes line skip - if (npages <= 0) - error("`x X' command invalid before first `p' command"); - else if (str_arg && (strncmp(str_arg, "devtag:", - strlen("devtag:")) == 0)) - pr->devtag(str_arg, current_env); - else - pr->special(str_arg, current_env); - a_delete str_arg; - break; - } - default: // ignore unknown x commands, but warn - warning("unknown command `x %1'", subcmd); - skip_line(); - } - a_delete subcmd_str; - return stopped; -} - - -/********************************************************************** - exported part (by driver.h) - **********************************************************************/ - -//////////////////////////////////////////////////////////////////////// -/* do_file(): - Parse and postprocess groff intermediate output. - - filename: "-" for standard input, normal file name otherwise -*/ -void -do_file(const char *filename) -{ - Char command; - bool stopped = false; // terminating condition - -#ifdef USE_ENV_STACK - EnvStack env_stack = EnvStack(); -#endif // USE_ENV_STACK - - // setup of global variables - npages = 0; - current_lineno = 1; - // `pr' is initialized after the prologue. - // `device' is set by the 1st prologue command. - - if (filename[0] == '-' && filename[1] == '\0') - current_file = stdin; - else { - errno = 0; - current_file = fopen(filename, "r"); - if (errno != 0 || current_file == 0) { - error("can't open file `%1'", filename); - return; - } - } - remember_filename(filename); - - if (current_env != 0) - delete_current_env(); - current_env = new environment; - current_env->col = new color; - current_env->fill = new color; - current_env->fontno = -1; - current_env->height = 0; - current_env->hpos = -1; - current_env->slant = 0; - current_env->size = 0; - current_env->vpos = -1; - - // parsing of prologue (first 3 commands) - { - char *str_arg; - IntArg int_arg; - - // 1st command `x T' - command = next_command(); - if ((int) command == EOF) - return; - if ((int) command != 'x') - fatal("the first command must be `x T'"); - str_arg = get_string_arg(); - if (str_arg[0] != 'T') - fatal("the first command must be `x T'"); - a_delete str_arg; - char *tmp_dev = get_string_arg(); - if (pr == 0) { // note: `pr' initialized after prologue - device = tmp_dev; - if (!font::load_desc()) - fatal("couldn't load DESC file, can't continue"); - } - else { - if (device == 0 || strcmp(device, tmp_dev) != 0) - fatal("all files must use the same device"); - a_delete tmp_dev; - } - skip_line_x(); // ignore further arguments - current_env->size = 10 * font::sizescale; - - // 2nd command `x res' - command = next_command(); - if ((int) command != 'x') - fatal("the second command must be `x res'"); - str_arg = get_string_arg(); - if (str_arg[0] != 'r') - fatal("the second command must be `x res'"); - a_delete str_arg; - int_arg = get_integer_arg(); - EnvInt font_res = font::res; - if (int_arg != font_res) - fatal("resolution does not match"); - int_arg = get_integer_arg(); - if (int_arg != font::hor) - fatal("minimum horizontal motion does not match"); - int_arg = get_integer_arg(); - if (int_arg != font::vert) - fatal("minimum vertical motion does not match"); - skip_line_x(); // ignore further arguments - - // 3rd command `x init' - command = next_command(); - if (command != 'x') - fatal("the third command must be `x init'"); - str_arg = get_string_arg(); - if (str_arg[0] != 'i') - fatal("the third command must be `x init'"); - a_delete str_arg; - skip_line_x(); - } - - // parsing of body - if (pr == 0) - pr = make_printer(); - while (!stopped) { - command = next_command(); - if (command == EOF) - break; - // spaces, tabs, comments, and newlines are skipped here - switch ((int) command) { - case '#': // #: comment, ignore up to end of line - skip_line(); - break; -#ifdef USE_ENV_STACK - case '{': // {: start a new environment (a copy) - env_stack.push(current_env); - break; - case '}': // }: pop previous env from stack - delete_current_env(); - current_env = env_stack.pop(); - break; -#endif // USE_ENV_STACK - case '0': // ddc: obsolete jump and print command - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { // expect 2 digits and a character - char s[3]; - Char c = next_arg_begin(); - if (npages <= 0) - fatal_command(command); - if (!isdigit((int) c)) { - error("digit expected"); - c = 0; - } - s[0] = (char) command; - s[1] = (char) c; - s[2] = '\0'; - errno = 0; - long int x = strtol(s, 0, 10); - if (errno != 0) - error("couldn't convert 2 digits"); - EnvInt hor_pos = (EnvInt) x; - current_env->hpos += hor_pos; - c = next_arg_begin(); - if ((int) c == '\n' || (int) c == EOF) - error("character argument expected"); - else - pr->set_ascii_char((unsigned char) c, current_env); - break; - } - case 'c': // c: print ascii char without moving - { - if (npages <= 0) - fatal_command(command); - Char c = next_arg_begin(); - if (c == '\n' || c == EOF) - error("missing argument to `c' command"); - else - pr->set_ascii_char((unsigned char) c, current_env); - break; - } - case 'C': // C: print named special character - { - if (npages <= 0) - fatal_command(command); - char *str_arg = get_string_arg(); - pr->set_special_char(str_arg, current_env); - a_delete str_arg; - break; - } - case 'D': // drawing commands - if (npages <= 0) - fatal_command(command); - parse_D_command(); - break; - case 'f': // f: set font to number - current_env->fontno = get_integer_arg(); - break; - case 'F': // F: obsolete, replaced by `x F' - { - char *str_arg = get_string_arg(); - remember_source_filename(str_arg); - a_delete str_arg; - break; - } - case 'h': // h: relative horizontal move - current_env->hpos += (EnvInt) get_integer_arg(); - break; - case 'H': // H: absolute horizontal positioning - current_env->hpos = (EnvInt) get_integer_arg(); - break; - case 'm': // m: glyph color - parse_color_command(current_env->col); - pr->change_color(current_env); - break; - case 'n': // n: print end of line - // ignore two arguments (historically) - if (npages <= 0) - fatal_command(command); - pr->end_of_line(); - (void) get_integer_arg(); - (void) get_integer_arg(); - break; - case 'N': // N: print char with given int code - if (npages <= 0) - fatal_command(command); - pr->set_numbered_char(get_integer_arg(), current_env); - break; - case 'p': // p: start new page with given number - if (npages > 0) - pr->end_page(current_env->vpos); - npages++; // increment # of processed pages - pr->begin_page(get_integer_arg()); - current_env->vpos = 0; - break; - case 's': // s: set point size - current_env->size = get_integer_arg(); - if (current_env->height == current_env->size) - current_env->height = 0; - break; - case 't': // t: print a text word - { - char c; - if (npages <= 0) - fatal_command(command); - char *str_arg = get_string_arg(); - size_t i = 0; - while ((c = str_arg[i++]) != '\0') { - EnvInt w; - pr->set_ascii_char((unsigned char) c, current_env, &w); - current_env->hpos += w; - } - a_delete str_arg; - break; - } - case 'u': // u: print spaced word - { - char c; - if (npages <= 0) - fatal_command(command); - EnvInt kern = (EnvInt) get_integer_arg(); - char *str_arg = get_string_arg(); - size_t i = 0; - while ((c = str_arg[i++]) != '\0') { - EnvInt w; - pr->set_ascii_char((unsigned char) c, current_env, &w); - current_env->hpos += w + kern; - } - a_delete str_arg; - break; - } - case 'v': // v: relative vertical move - current_env->vpos += (EnvInt) get_integer_arg(); - break; - case 'V': // V: absolute vertical positioning - current_env->vpos = (EnvInt) get_integer_arg(); - break; - case 'w': // w: inform about paddable space - break; - case 'x': // device controlling commands - stopped = parse_x_command(); - break; - default: - warning("unrecognized command `%1'", (unsigned char) command); - skip_line(); - break; - } // end of switch - } // end of while - - // end of file reached - if (npages > 0) - pr->end_page(current_env->vpos); - delete pr; - pr = 0; - fclose(current_file); - // If `stopped' is not `true' here then there wasn't any `x stop'. - if (!stopped) - warning("no final `x stop' command"); - delete_current_env(); -} |
