📄 input.cc
字号:
// -*- C++ -*-/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com)This file is part of groff.groff is free software; you can redistribute it and/or modify it underthe terms of the GNU General Public License as published by the FreeSoftware Foundation; either version 2, or (at your option) any laterversion.groff is distributed in the hope that it will be useful, but WITHOUT ANYWARRANTY; without even the implied warranty of MERCHANTABILITY orFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licensefor more details.You should have received a copy of the GNU General Public License alongwith groff; see the file COPYING. If not, write to the Free SoftwareFoundation, 675 Mass Ave, Cambridge, MA 02139, USA. */#include "troff.h"#include "symbol.h"#include "dictionary.h"#include "hvunits.h"#include "env.h"#include "request.h"#include "node.h"#include "reg.h"#include "token.h"#include "div.h"#include "charinfo.h"#include "font.h"#include "searchpath.h"#include "macropath.h"#include "defs.h"// Needed for getpid().#include "posix.h"#define USAGE_EXIT_CODE 1#define MACRO_PREFIX "tmac."#define STARTUP_FILE "troffrc"#define DEFAULT_INPUT_STACK_LIMIT 1000#ifndef DEFAULT_WARNING_MASK// warnings that are enabled by default#define DEFAULT_WARNING_MASK \ (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)#endif// initial size of buffer for reading names; expanded as necessary#define ABUF_SIZE 16#ifdef COLUMNvoid init_column_requests();#endif /* COLUMN */static node *read_draw_node();void handle_first_page_transition();static void push_token(const token &);void copy_file();#ifdef COLUMNvoid vjustify();#endif /* COLUMN */void transparent();void transparent_file();const char *program_name = 0;token tok;int break_flag = 0;static int backtrace_flag = 0;char *pipe_command = 0;charinfo *charset_table[256];static int warning_mask = DEFAULT_WARNING_MASK;static int inhibit_errors = 0;static void enable_warning(const char *);static void disable_warning(const char *);static int escape_char = '\\';static symbol end_macro_name;static int compatible_flag = 0;static void process_input_stack();int ascii_output_flag = 0;int suppress_output_flag = 0;int tcommand_flag = 0;static int get_copy(node**, int = 0);static symbol read_escape_name();static void interpolate_string(symbol);static void interpolate_macro(symbol);static void interpolate_number_format(symbol);static void interpolate_environment_variable(symbol);static void interpolate_arg(symbol);static request_or_macro *lookup_request(symbol);static int get_delim_number(units *, int);static int get_delim_number(units *, int, units);static int get_line_arg(units *res, int si, charinfo **cp);static int read_size(int *);static symbol get_delim_name();static void init_registers();struct input_iterator;input_iterator *make_temp_iterator(const char *);const char *input_char_description(int);const int ESCAPE_QUESTION = 015;const int BEGIN_TRAP = 016;const int END_TRAP = 017;const int PAGE_EJECTOR = 020;const int ESCAPE_NEWLINE = 021;const int ESCAPE_AMPERSAND = 022;const int ESCAPE_UNDERSCORE = 023;const int ESCAPE_BAR = 024;const int ESCAPE_CIRCUMFLEX = 025;const int ESCAPE_LEFT_BRACE = 026;const int ESCAPE_RIGHT_BRACE = 027;const int ESCAPE_LEFT_QUOTE = 030;const int ESCAPE_RIGHT_QUOTE = 031;const int ESCAPE_HYPHEN = 032;const int ESCAPE_BANG = 033;const int ESCAPE_c = 034;const int ESCAPE_e = 035;const int ESCAPE_PERCENT = 036;const int ESCAPE_SPACE = 037;const int TITLE_REQUEST = 0200;const int COPY_FILE_REQUEST = 0201;const int TRANSPARENT_FILE_REQUEST = 0202;#ifdef COLUMNconst int VJUSTIFY_REQUEST = 0203;#endif /* COLUMN */const int ESCAPE_E = 0204;const int LAST_PAGE_EJECTOR = 0205;const int ESCAPE_RIGHT_PARENTHESIS = 0206;void set_escape_char(){ if (has_arg()) { if (tok.ch() == 0) { error("bad escape character"); escape_char = '\\'; } else escape_char = tok.ch(); } else escape_char = '\\'; skip_line();}void escape_off(){ escape_char = 0; skip_line();}class input_iterator {public: input_iterator(); virtual ~input_iterator(); int get(node **); friend class input_stack;protected: const unsigned char *ptr; const unsigned char *eptr; input_iterator *next;private: virtual int fill(node **); virtual int peek(); virtual int has_args() { return 0; } virtual int nargs() { return 0; } virtual input_iterator *get_arg(int) { return NULL; } virtual int get_location(int, const char **, int *) { return 0; } virtual void backtrace() {} virtual int set_location(const char *, int) { return 0; } virtual int next_file(FILE *, const char *) { return 0; } virtual void shift(int) {} virtual int is_boundary(); virtual int internal_level() { return 0; } virtual int is_file() { return 0; }};input_iterator::input_iterator(): ptr(0), eptr(0){}input_iterator::~input_iterator(){}int input_iterator::fill(node **){ return EOF;}int input_iterator::peek(){ return EOF;}int input_iterator::is_boundary(){ return 0;}inline int input_iterator::get(node **p){ return ptr < eptr ? *ptr++ : fill(p);}class input_boundary : public input_iterator {public: int is_boundary() { return 1; }};class file_iterator : public input_iterator { FILE *fp; int lineno; const char *filename; int newline_flag; enum { BUF_SIZE = 512 }; unsigned char buf[BUF_SIZE];public: file_iterator(FILE *, const char *); ~file_iterator(); int fill(node **); int peek(); int get_location(int, const char **, int *); void backtrace(); int set_location(const char *, int); int next_file(FILE *, const char *); int is_file();};file_iterator::file_iterator(FILE *f, const char *fn): fp(f), filename(fn), lineno(1), newline_flag(0){}file_iterator::~file_iterator(){ if (fp != stdin) fclose(fp); else clearerr(stdin);}int file_iterator::is_file(){ return 1;}int file_iterator::next_file(FILE *f, const char *s){ if (fp != stdin) fclose(fp); else clearerr(stdin); filename = s; fp = f; lineno = 1; newline_flag = 0; ptr = 0; eptr = 0; return 1;}int file_iterator::fill(node **){ if (newline_flag) lineno++; newline_flag = 0; unsigned char *p = buf; ptr = p; unsigned char *e = p + BUF_SIZE; while (p < e) { int c = getc(fp); if (c == EOF) break; if (illegal_input_char(c)) warning(WARN_INPUT, "illegal input character code %1", int(c)); else { *p++ = c; if (c == '\n') { newline_flag = 1; break; } } } if (p > buf) { eptr = p; return *ptr++; } else { eptr = p; return EOF; }}int file_iterator::peek(){ int c = getc(fp); while (illegal_input_char(c)) { warning(WARN_INPUT, "illegal input character code %1", int(c)); c = getc(fp); } if (c != EOF) ungetc(c, fp); return c;}int file_iterator::get_location(int /*allow_macro*/, const char **filenamep, int *linenop){ *linenop = lineno; if (filename != 0 && strcmp(filename, "-") == 0) *filenamep = "<standard input>"; else *filenamep = filename; return 1;}void file_iterator::backtrace(){ errprint("%1:%2: backtrace: file `%1'\n", filename, lineno);}int file_iterator::set_location(const char *f, int ln){ if (f) filename = f; lineno = ln; return 1;}input_iterator nil_iterator; class input_stack {public: static int get(node **); static int peek(); static void push(input_iterator *); static input_iterator *get_arg(int); static int nargs(); static int get_location(int, const char **, int *); static int set_location(const char *, int); static void backtrace(); static void backtrace_all(); static void next_file(FILE *, const char *); static void end_file(); static void shift(int n); static void add_boundary(); static void remove_boundary(); static int get_level(); static void clear(); static int limit;private: static input_iterator *top; static int level; static int finish_get(node **); static int finish_peek();};input_iterator *input_stack::top = &nil_iterator;int input_stack::level = 0;int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;inline int input_stack::get_level(){ return level + top->internal_level();}inline int input_stack::get(node **np){ return (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);}int input_stack::finish_get(node **np){ for (;;) { int c = top->fill(np); if (c != EOF || top->is_boundary()) return c; if (top == &nil_iterator) break; input_iterator *tem = top; top = top->next; level--; delete tem; if (top->ptr < top->eptr) return *top->ptr++; } assert(level == 0); return EOF;}inline int input_stack::peek(){ return (top->ptr < top->eptr) ? *top->ptr : finish_peek();}int input_stack::finish_peek(){ for (;;) { int c = top->peek(); if (c != EOF || top->is_boundary()) return c; if (top == &nil_iterator) break; input_iterator *tem = top; top = top->next; level--; delete tem; if (top->ptr < top->eptr) return *top->ptr; } assert(level == 0); return EOF;}void input_stack::add_boundary(){ push(new input_boundary);}void input_stack::remove_boundary(){ assert(top->is_boundary()); input_iterator *temp = top->next; delete top; top = temp; level--;}void input_stack::push(input_iterator *in){ if (in == 0) return; if (++level > limit && limit > 0) fatal("input stack limit exceeded (probable infinite loop)"); in->next = top; top = in;}input_iterator *input_stack::get_arg(int i){ input_iterator *p; for (p = top; p != NULL; p = p->next) if (p->has_args()) return p->get_arg(i); return 0;}void input_stack::shift(int n){ for (input_iterator *p = top; p; p = p->next) if (p->has_args()) { p->shift(n); return; }}int input_stack::nargs(){ for (input_iterator *p =top; p != 0; p = p->next) if (p->has_args()) return p->nargs(); return 0;}int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop){ for (input_iterator *p = top; p; p = p->next) if (p->get_location(allow_macro, filenamep, linenop)) return 1; return 0;}void input_stack::backtrace(){ const char *f; int n; // only backtrace down to (not including) the topmost file for (input_iterator *p = top; p && !p->get_location(0, &f, &n); p = p->next) p->backtrace();}void input_stack::backtrace_all(){ for (input_iterator *p = top; p; p = p->next) p->backtrace();}int input_stack::set_location(const char *filename, int lineno){ for (input_iterator *p = top; p; p = p->next) if (p->set_location(filename, lineno)) return 1; return 0;}void input_stack::next_file(FILE *fp, const char *s){ for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next) if ((*pp)->next_file(fp, s)) return; if (++level > limit && limit > 0) fatal("input stack limit exceeded"); *pp = new file_iterator(fp, s); (*pp)->next = &nil_iterator;}void input_stack::end_file(){ for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next) if ((*pp)->is_file()) { input_iterator *tem = *pp; *pp = (*pp)->next; delete tem; level--; return; }}void input_stack::clear(){ int nboundaries = 0; while (top != &nil_iterator) { if (top->is_boundary()) nboundaries++; input_iterator *tem = top; top = top->next; level--; delete tem; } // Keep while_request happy. for (; nboundaries > 0; --nboundaries) add_boundary();}void backtrace_request(){ input_stack::backtrace_all(); fflush(stderr); skip_line();}void next_file(){ symbol nm = get_long_name(0); while (!tok.newline() && !tok.eof()) tok.next(); if (nm.is_null()) input_stack::end_file(); else { errno = 0; FILE *fp = fopen(nm.contents(), "r"); if (!fp) error("can't open `%1': %2", nm.contents(), strerror(errno)); else input_stack::next_file(fp, nm.contents()); } tok.next();}void shift(){ int n; if (!has_arg() || !get_integer(&n)) n = 1; input_stack::shift(n); skip_line();}static int get_char_for_escape_name(){ int c = get_copy(NULL); switch (c) { case EOF: error("end of input in escape name"); return '\0'; default: if (!illegal_input_char(c)) break; // fall through case ' ': case '\n': case '\t': case '\001': case '\b': error("%1 is not allowed in an escape name", input_char_description(c)); return '\0'; } return c;}static symbol read_two_char_escape_name(){ char buf[3]; buf[0] = get_char_for_escape_name(); if (buf[0] != '\0') { buf[1] = get_char_for_escape_name(); if (buf[1] == '\0') buf[0] = 0; else buf[2] = 0; } return symbol(buf);}static symbol read_long_escape_name(){ int start_level = input_stack::get_level(); char abuf[ABUF_SIZE]; char *buf = abuf; int buf_size = ABUF_SIZE; int i = 0; for (;;) { int c = get_char_for_escape_name(); if (c == 0) { if (buf != abuf) a_delete buf; return NULL_SYMBOL; } if (i + 2 > buf_size) { if (buf == abuf) { buf = new char [ABUF_SIZE*2]; memcpy(buf, abuf, buf_size); buf_size = ABUF_SIZE*2; } else { char *old_buf = buf; buf = new char[buf_size*2]; memcpy(buf, old_buf, buf_size); buf_size *= 2; a_delete old_buf; } } if (c == ']' && input_stack::get_level() == start_level) break; buf[i++] = c; } buf[i] = 0; if (buf == abuf) { if (i == 0) { error("empty escape name"); return NULL_SYMBOL; } return symbol(abuf); } else { symbol s(buf); a_delete buf; return s; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -