📄 div.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. */// diversions#include "troff.h"#include "symbol.h"#include "dictionary.h"#include "hvunits.h"#include "env.h"#include "request.h"#include "node.h"#include "token.h"#include "div.h"#include "reg.h"int exit_started = 0; // the exit process has startedint done_end_macro = 0; // the end macro (if any) has finishedint seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookiestatic int began_page_in_end_macro = 0; // a new page was begun during the end macrostatic int last_post_line_extra_space = 0; // needed for \n(.astatic int nl_reg_contents = -1;static int dl_reg_contents = 0;static int dn_reg_contents = 0;static int vertical_position_traps_flag = 1;static vunits truncated_space;static vunits needed_space;diversion::diversion(symbol s) : nm(s), prev(0), vertical_position(V0), marked_place(V0), high_water_mark(V0){}struct vertical_size { vunits pre_extra, post_extra, pre, post; vertical_size(vunits vs, int ls);};vertical_size::vertical_size(vunits vs, int ls): pre_extra(V0), post_extra(V0), pre(vs){ if (ls > 1) post = vs*(ls - 1); else post = V0;}void node::set_vertical_size(vertical_size *){}void extra_size_node::set_vertical_size(vertical_size *v){ if (n < V0) { if (-n > v->pre_extra) v->pre_extra = -n; } else if (n > v->post_extra) v->post_extra = n;}void vertical_size_node::set_vertical_size(vertical_size *v){ if (n < V0) v->pre = -n; else v->post = n;}top_level_diversion *topdiv;diversion *curdiv;void do_divert(int append){ tok.skip(); symbol nm = get_name(); if (nm.is_null()) { if (curdiv->prev) { diversion *temp = curdiv; curdiv = curdiv->prev; delete temp; } else warning(WARN_DI, "diversion stack underflow"); } else { macro_diversion *md = new macro_diversion(nm, append); md->prev = curdiv; curdiv = md; } skip_line();}void divert(){ do_divert(0);}void divert_append(){ do_divert(1);} void diversion::need(vunits n){ vunits d = distance_to_next_trap(); if (d < n) { space(d, 1); truncated_space = -d; needed_space = n; }}macro_diversion::macro_diversion(symbol s, int append): diversion(s), max_width(H0){#if 0 if (append) { /* We don't allow recursive appends eg: .da a .a .di This causes an infinite loop in troff anyway. This is because the user could do .as a foo in the diversion, and this would mess things up royally, since there would be two things appending to the same macro_header. To make it work, we would have to copy the _contents_ of the macro into which we were diverting; this doesn't strike me as worthwhile. However, .di a .a .a .di will work and will make `a' contain two copies of what it contained before; in troff, `a' would contain nothing. */ request_or_macro *rm = (request_or_macro *)request_dictionary.remove(s); if (!rm || (mac = rm->to_macro()) == 0) mac = new macro; } else mac = new macro;#endif // We can now catch the situation described above by comparing // the length of the charlist in the macro_header with the length // stored in the macro. When we detect this, we copy the contents. mac = new macro; if (append) { request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(s); if (rm) { macro *m = rm->to_macro(); if (m) *mac = *m; } }}macro_diversion::~macro_diversion(){ request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm); macro *m = rm ? rm->to_macro() : 0; if (m) { *m = *mac; delete mac; } else request_dictionary.define(nm, mac); mac = 0; dl_reg_contents = max_width.to_units(); dn_reg_contents = vertical_position.to_units();}vunits macro_diversion::distance_to_next_trap(){ if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position) return diversion_trap_pos - vertical_position; else // Substract vresolution so that vunits::vunits does not overflow. return vunits(INT_MAX - vresolution);}void macro_diversion::transparent_output(unsigned char c){ mac->append(c);}void macro_diversion::transparent_output(node *n){ mac->append(n);}void macro_diversion::output(node *nd, int retain_size, vunits vs, int ls, hunits width){ vertical_size v(vs, ls); while (nd != 0) { nd->set_vertical_size(&v); node *temp = nd; nd = nd->next; if (temp->interpret(mac)) { delete temp; } else {#if 1 temp->freeze_space();#endif mac->append(temp); } } if (!v.post_extra.is_zero()) last_post_line_extra_space = v.post_extra.to_units(); if (!retain_size) { v.pre = vs; v.post = (ls > 1) ? vs*(ls - 1) : V0; } if (width > max_width) max_width = width; vunits x = v.pre + v.pre_extra + v.post + v.post_extra; if (vertical_position_traps_flag && !diversion_trap.is_null() && diversion_trap_pos > vertical_position && diversion_trap_pos <= vertical_position + x) { vunits trunc = vertical_position + x - diversion_trap_pos; if (trunc > v.post) trunc = v.post; v.post -= trunc; x -= trunc; truncated_space = trunc; spring_trap(diversion_trap); } mac->append(new vertical_size_node(-v.pre)); mac->append(new vertical_size_node(v.post)); mac->append('\n'); vertical_position += x; if (vertical_position - v.post > high_water_mark) high_water_mark = vertical_position - v.post;}void macro_diversion::space(vunits n, int){ if (vertical_position_traps_flag && !diversion_trap.is_null() && diversion_trap_pos > vertical_position && diversion_trap_pos <= vertical_position + n) { truncated_space = vertical_position + n - diversion_trap_pos; n = diversion_trap_pos - vertical_position; spring_trap(diversion_trap); } else if (n + vertical_position < V0) n = -vertical_position; mac->append(new diverted_space_node(n)); vertical_position += n;}void macro_diversion::copy_file(const char *filename){ mac->append(new diverted_copy_file_node(filename));}top_level_diversion::top_level_diversion(): page_count(0), have_next_page_number(0), page_length(units_per_inch*11), page_offset(units_per_inch), prev_page_offset(units_per_inch), ejecting_page(0), page_trap_list(0), before_first_page(1), no_space_mode(0), page_number(0), last_page_count(-1){}// find the next trap after postrap *top_level_diversion::find_next_trap(vunits *next_trap_pos){ trap *next_trap = 0; for (trap *pt = page_trap_list; pt != 0; pt = pt->next) if (!pt->nm.is_null()) { if (pt->position >= V0) { if (pt->position > vertical_position && pt->position < page_length && (next_trap == 0 || pt->position < *next_trap_pos)) { next_trap = pt; *next_trap_pos = pt->position; } } else { vunits pos = pt->position; pos += page_length; if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) { next_trap = pt; *next_trap_pos = pos; } } } return next_trap;}vunits top_level_diversion::distance_to_next_trap(){ vunits d; if (!find_next_trap(&d)) return page_length - vertical_position; else return d - vertical_position;}void top_level_diversion::output(node *nd, int retain_size, vunits vs, int ls, hunits /*width*/){ no_space_mode = 0; vunits next_trap_pos; trap *next_trap = find_next_trap(&next_trap_pos); if (before_first_page && begin_page()) fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); vertical_size v(vs, ls); for (node *tem = nd; tem != 0; tem = tem->next) tem->set_vertical_size(&v); if (!v.post_extra.is_zero()) last_post_line_extra_space = v.post_extra.to_units(); if (!retain_size) { v.pre = vs; v.post = (ls > 1) ? vs*(ls - 1) : V0; } vertical_position += v.pre; vertical_position += v.pre_extra; the_output->print_line(page_offset, vertical_position, nd, v.pre + v.pre_extra, v.post_extra); vertical_position += v.post_extra; if (vertical_position > high_water_mark) high_water_mark = vertical_position; if (vertical_position_traps_flag && vertical_position >= page_length) begin_page(); else if (vertical_position_traps_flag && next_trap != 0 && vertical_position >= next_trap_pos) { nl_reg_contents = vertical_position.to_units(); truncated_space = v.post; spring_trap(next_trap->nm); } else if (v.post > V0) { vertical_position += v.post; if (vertical_position_traps_flag && next_trap != 0 && vertical_position >= next_trap_pos) { truncated_space = vertical_position - next_trap_pos; vertical_position = next_trap_pos; nl_reg_contents = vertical_position.to_units(); spring_trap(next_trap->nm); } else if (vertical_position_traps_flag && vertical_position >= page_length) begin_page(); else nl_reg_contents = vertical_position.to_units(); } else nl_reg_contents = vertical_position.to_units();}void top_level_diversion::transparent_output(unsigned char c){ if (before_first_page && begin_page()) // This can only happen with the transparent() request. fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); const char *s = asciify(c); while (*s) the_output->transparent_char(*s++);}void top_level_diversion::transparent_output(node * /*n*/){ error("can't transparently output node at top level");}void top_level_diversion::copy_file(const char *filename){ if (before_first_page && begin_page()) fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); the_output->copy_file(page_offset, vertical_position, filename);}void top_level_diversion::space(vunits n, int forced){ if (no_space_mode) { if (!forced) return; else no_space_mode = 0; } if (before_first_page) { if (begin_page()) { // This happens if there's a top of page trap, and the first-page // transition is caused by `'sp'. truncated_space = n > V0 ? n : V0; return; } } vunits next_trap_pos; trap *next_trap = find_next_trap(&next_trap_pos); vunits y = vertical_position + n; if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) { vertical_position = next_trap_pos; nl_reg_contents = vertical_position.to_units(); truncated_space = y - vertical_position; spring_trap(next_trap->nm); } else if (y < V0) { vertical_position = V0; nl_reg_contents = vertical_position.to_units(); } else if (vertical_position_traps_flag && y >= page_length && n >= V0) begin_page(); else { vertical_position = y; nl_reg_contents = vertical_position.to_units(); }}trap::trap(symbol s, vunits n, trap *p) : nm(s), next(p), position(n){}void top_level_diversion::add_trap(symbol nm, vunits pos){ trap *first_free_slot = 0; for (trap **p = &page_trap_list; *p; p = &(*p)->next) { if ((*p)->nm.is_null()) { if (first_free_slot == 0) first_free_slot = *p; } else if ((*p)->position == pos) { (*p)->nm = nm; return; } } if (first_free_slot) { first_free_slot->nm = nm; first_free_slot->position = pos; } else *p = new trap(nm, pos, 0);} void top_level_diversion::remove_trap(symbol nm){ for (trap *p = page_trap_list; p; p = p->next) if (p->nm == nm) { p->nm = NULL_SYMBOL; return; }}void top_level_diversion::remove_trap_at(vunits pos){ for (trap *p = page_trap_list; p; p = p->next) if (p->position == pos) { p->nm = NULL_SYMBOL; return; }} void top_level_diversion::change_trap(symbol nm, vunits pos){ for (trap *p = page_trap_list; p; p = p->next) if (p->nm == nm) { p->position = pos; return; }}void top_level_diversion::print_traps(){ for (trap *p = page_trap_list; p; p = p->next) if (p->nm.is_null()) fprintf(stderr, " empty\n"); else fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units()); fflush(stderr);}void end_diversions(){ while (curdiv != topdiv) { error("automatically ending diversion `%1' on exit", curdiv->nm.contents()); diversion *tem = curdiv; curdiv = curdiv->prev; delete tem; }}NO_RETURN void cleanup_and_exit(int exit_code){ if (the_output) { the_output->trailer(topdiv->get_page_length()); delete the_output; } exit(exit_code);}// returns non-zero if it sprung a top of page trapint top_level_diversion::begin_page(){ if (exit_started) { if (page_count == last_page_count ? curenv->is_empty() : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro))) cleanup_and_exit(0); if (!done_end_macro) began_page_in_end_macro = 1; } if (!the_output) init_output(); ++page_count; if (have_next_page_number) { page_number = next_page_number; have_next_page_number = 0; } else if (before_first_page == 1) page_number = 1; else page_number++; // spring the top of page trap if there is one vunits next_trap_pos; vertical_position = -vresolution; trap *next_trap = find_next_trap(&next_trap_pos); vertical_position = V0; high_water_mark = V0; ejecting_page = 0; // If before_first_page was 2, then the top of page transition was undone // using eg .nr nl 0-1. See nl_reg::set_value. if (before_first_page != 2) the_output->begin_page(page_number, page_length); before_first_page = 0; nl_reg_contents = vertical_position.to_units(); if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) { truncated_space = V0; spring_trap(next_trap->nm); return 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -