diff options
Diffstat (limited to 'src/roff/troff/reg.cpp')
-rw-r--r-- | src/roff/troff/reg.cpp | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/src/roff/troff/reg.cpp b/src/roff/troff/reg.cpp new file mode 100644 index 0000000..6b7689d --- /dev/null +++ b/src/roff/troff/reg.cpp @@ -0,0 +1,479 @@ +/* Copyright (C) 1989-2020 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 under +the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "troff.h" +#include "dictionary.h" +#include "token.h" +#include "request.h" +#include "reg.h" + +object_dictionary register_dictionary(101); + +bool reg::get_value(units * /*d*/) +{ + return false; +} + +void reg::increment() +{ + error("can't increment read-only register"); +} + +void reg::decrement() +{ + error("can't decrement read-only register"); +} + +void reg::set_increment(units /*n*/) +{ + error("can't automatically increment read-only register"); +} + +void reg::alter_format(char /*f*/, int /*w*/) +{ + error("can't assign format of read-only register"); +} + +const char *reg::get_format() +{ + return "0"; +} + +void reg::set_value(units /*n*/) +{ + error("can't write read-only register"); +} + +general_reg::general_reg() : format('1'), width(0), inc(0) +{ +} + +static char uppercase_array[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', +}; + +static char lowercase_array[] = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', +}; + +static const char *number_value_to_ascii(int value, char format, int width) +{ + static char buf[128]; // must be at least 21 + switch(format) { + case '1': + if (width <= 0) + return i_to_a(value); + else if (width > int(sizeof(buf) - 2)) + sprintf(buf, "%.*d", int(sizeof(buf) - 2), int(value)); + else + sprintf(buf, "%.*d", width, int(value)); + break; + case 'i': + case 'I': + { + char *p = buf; + // troff uses z and w to represent 10000 and 5000 in Roman + // numerals; I can find no historical basis for this usage + const char *s = format == 'i' ? "zwmdclxvi" : "ZWMDCLXVI"; + int n = int(value); + if (n >= 40000 || n <= -40000) { + error("magnitude of '%1' too big for i or I format", n); + return i_to_a(n); + } + if (n == 0) { + *p++ = '0'; + *p = 0; + break; + } + if (n < 0) { + *p++ = '-'; + n = -n; + } + while (n >= 10000) { + *p++ = s[0]; + n -= 10000; + } + for (int i = 1000; i > 0; i /= 10, s += 2) { + int m = n/i; + n -= m*i; + switch (m) { + case 3: + *p++ = s[2]; + /* falls through */ + case 2: + *p++ = s[2]; + /* falls through */ + case 1: + *p++ = s[2]; + break; + case 4: + *p++ = s[2]; + *p++ = s[1]; + break; + case 8: + *p++ = s[1]; + *p++ = s[2]; + *p++ = s[2]; + *p++ = s[2]; + break; + case 7: + *p++ = s[1]; + *p++ = s[2]; + *p++ = s[2]; + break; + case 6: + *p++ = s[1]; + *p++ = s[2]; + break; + case 5: + *p++ = s[1]; + break; + case 9: + *p++ = s[2]; + *p++ = s[0]; + } + } + *p = 0; + break; + } + case 'a': + case 'A': + { + int n = value; + char *p = buf; + if (n == 0) { + *p++ = '0'; + *p = 0; + } + else { + if (n < 0) { + n = -n; + *p++ = '-'; + } + // this is a bit tricky + while (n > 0) { + int d = n % 26; + if (d == 0) + d = 26; + n -= d; + n /= 26; + *p++ = format == 'a' ? lowercase_array[d - 1] : + uppercase_array[d - 1]; + } + *p-- = 0; + char *q = buf[0] == '-' ? buf + 1 : buf; + while (q < p) { + char temp = *q; + *q = *p; + *p = temp; + --p; + ++q; + } + } + break; + } + default: + assert(0); + break; + } + return buf; +} + +const char *general_reg::get_string() +{ + units n; + if (!get_value(&n)) + return ""; + return number_value_to_ascii(n, format, width); +} + + +void general_reg::increment() +{ + int n; + if (get_value(&n)) + set_value(n + inc); +} + +void general_reg::decrement() +{ + int n; + if (get_value(&n)) + set_value(n - inc); +} + +void general_reg::set_increment(units n) +{ + inc = n; +} + +void general_reg::alter_format(char f, int w) +{ + format = f; + width = w; +} + +static const char *number_format_to_ascii(char format, int width) +{ + static char buf[24]; + if (format == '1') { + if (width > 0) { + int n = width; + if (n > int(sizeof(buf)) - 1) + n = int(sizeof(buf)) - 1; + sprintf(buf, "%.*d", n, 0); + return buf; + } + else + return "0"; + } + else { + buf[0] = format; + buf[1] = '\0'; + return buf; + } +} + +const char *general_reg::get_format() +{ + return number_format_to_ascii(format, width); +} + +class number_reg : public general_reg { + units value; +public: + number_reg(); + bool get_value(units *); + void set_value(units); +}; + +number_reg::number_reg() : value(0) +{ +} + +bool number_reg::get_value(units *res) +{ + *res = value; + return true; +} + +void number_reg::set_value(units n) +{ + value = n; +} + +variable_reg::variable_reg(units *p) : ptr(p) +{ +} + +void variable_reg::set_value(units n) +{ + *ptr = n; +} + +bool variable_reg::get_value(units *res) +{ + *res = *ptr; + return true; +} + +void define_number_reg() +{ + symbol nm = get_name(true /* required */); + if (nm.is_null()) { + skip_line(); + return; + } + reg *r = (reg *)register_dictionary.lookup(nm); + units v; + units prev_value; + if (!r || !r->get_value(&prev_value)) + prev_value = 0; + if (get_number(&v, 'u', prev_value)) { + if (r == 0) { + r = new number_reg; + register_dictionary.define(nm, r); + } + r->set_value(v); + if (tok.is_space() && has_arg() && get_number(&v, 'u')) + r->set_increment(v); + } + skip_line(); +} + +#if 0 +void inline_define_reg() +{ + token start; + start.next(); + if (!start.delimiter(true /* report error */)) + return; + tok.next(); + symbol nm = get_name(true /* required */); + if (nm.is_null()) + return; + reg *r = (reg *)register_dictionary.lookup(nm); + if (r == 0) { + r = new number_reg; + register_dictionary.define(nm, r); + } + units v; + units prev_value; + if (!r->get_value(&prev_value)) + prev_value = 0; + if (get_number(&v, 'u', prev_value)) { + r->set_value(v); + if (start != tok) { + if (get_number(&v, 'u')) { + r->set_increment(v); + if (start != tok) + warning(WARN_DELIM, "closing delimiter does not match"); + } + } + } +} +#endif + +void set_number_reg(symbol nm, units n) +{ + reg *r = (reg *)register_dictionary.lookup(nm); + if (r == 0) { + r = new number_reg; + register_dictionary.define(nm, r); + } + r->set_value(n); +} + +reg *lookup_number_reg(symbol nm) +{ + reg *r = (reg *)register_dictionary.lookup(nm); + if (r == 0) { + warning(WARN_REG, "register '%1' not defined", nm.contents()); + r = new number_reg; + register_dictionary.define(nm, r); + } + return r; +} + +void alter_format() +{ + symbol nm = get_name(true /* required */); + if (nm.is_null()) { + skip_line(); + return; + } + reg *r = (reg *)register_dictionary.lookup(nm); + if (r == 0) { + r = new number_reg; + register_dictionary.define(nm, r); + } + tok.skip(); + char c = tok.ch(); + if (csdigit(c)) { + int n = 0; + do { + ++n; + tok.next(); + } while (csdigit(tok.ch())); + r->alter_format('1', n); + } + else if (c == 'i' || c == 'I' || c == 'a' || c == 'A') + r->alter_format(c); + else if (tok.is_newline() || tok.is_eof()) + warning(WARN_MISSING, "missing register format"); + else + if (!cscntrl(c)) + error("invalid register format '%1'", c); + else + error("invalid register format (got %1)", tok.description()); + skip_line(); +} + +void remove_reg() +{ + for (;;) { + symbol s = get_name(); + if (s.is_null()) + break; + register_dictionary.remove(s); + } + skip_line(); +} + +void alias_reg() +{ + symbol s1 = get_name(true /* required */); + if (!s1.is_null()) { + symbol s2 = get_name(true /* required */); + if (!s2.is_null()) { + if (!register_dictionary.alias(s1, s2)) + warning(WARN_REG, "register '%1' not defined", s2.contents()); + } + } + skip_line(); +} + +void rename_reg() +{ + symbol s1 = get_name(true /* required */); + if (!s1.is_null()) { + symbol s2 = get_name(true /* required */); + if (!s2.is_null()) + register_dictionary.rename(s1, s2); + } + skip_line(); +} + +void print_number_regs() +{ + object_dictionary_iterator iter(register_dictionary); + reg *r; + symbol s; + while (iter.get(&s, (object **)&r)) { + assert(!s.is_null()); + errprint("%1\t", s.contents()); + const char *p = r->get_string(); + if (p) + errprint(p); + errprint("\n"); + } + fflush(stderr); + skip_line(); +} + +void init_reg_requests() +{ + init_request("rr", remove_reg); + init_request("nr", define_number_reg); + init_request("af", alter_format); + init_request("aln", alias_reg); + init_request("rnn", rename_reg); + init_request("pnr", print_number_regs); +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: |