Adapt src/roff (src/roff/groff)
[s-roff.git] / src / roff / troff / reg.cpp
blob4501090a5ffeb0ca743bf0b64829cc30f2010682
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2004
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22 #include "troff.h"
23 #include "dictionary.h"
24 #include "token.h"
25 #include "request.h"
26 #include "reg.h"
28 object_dictionary number_reg_dictionary(101);
30 int reg::get_value(units * /*d*/)
32 return 0;
35 void reg::increment()
37 error("can't increment read-only register");
40 void reg::decrement()
42 error("can't decrement read-only register");
45 void reg::set_increment(units /*n*/)
47 error("can't auto increment read-only register");
50 void reg::alter_format(char /*f*/, int /*w*/)
52 error("can't alter format of read-only register");
55 const char *reg::get_format()
57 return "0";
60 void reg::set_value(units /*n*/)
62 error("can't write read-only register");
65 general_reg::general_reg() : format('1'), width(0), inc(0)
69 static char uppercase_array[] = {
70 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
71 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
72 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
73 'Y', 'Z',
76 static char lowercase_array[] = {
77 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
78 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
79 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
80 'y', 'z',
83 static const char *number_value_to_ascii(int value, char format, int width)
85 static char buf[128]; // must be at least 21
86 switch(format) {
87 case '1':
88 if (width <= 0)
89 return i_to_a(value);
90 else if (width > int(sizeof(buf) - 2))
91 sprintf(buf, "%.*d", int(sizeof(buf) - 2), int(value));
92 else
93 sprintf(buf, "%.*d", width, int(value));
94 break;
95 case 'i':
96 case 'I':
98 char *p = buf;
99 // troff uses z and w to represent 10000 and 5000 in Roman
100 // numerals; I can find no historical basis for this usage
101 const char *s = format == 'i' ? "zwmdclxvi" : "ZWMDCLXVI";
102 int n = int(value);
103 if (n >= 40000 || n <= -40000) {
104 error("magnitude of `%1' too big for i or I format", n);
105 return i_to_a(n);
107 if (n == 0) {
108 *p++ = '0';
109 *p = 0;
110 break;
112 if (n < 0) {
113 *p++ = '-';
114 n = -n;
116 while (n >= 10000) {
117 *p++ = s[0];
118 n -= 10000;
120 for (int i = 1000; i > 0; i /= 10, s += 2) {
121 int m = n/i;
122 n -= m*i;
123 switch (m) {
124 case 3:
125 *p++ = s[2];
126 /* falls through */
127 case 2:
128 *p++ = s[2];
129 /* falls through */
130 case 1:
131 *p++ = s[2];
132 break;
133 case 4:
134 *p++ = s[2];
135 *p++ = s[1];
136 break;
137 case 8:
138 *p++ = s[1];
139 *p++ = s[2];
140 *p++ = s[2];
141 *p++ = s[2];
142 break;
143 case 7:
144 *p++ = s[1];
145 *p++ = s[2];
146 *p++ = s[2];
147 break;
148 case 6:
149 *p++ = s[1];
150 *p++ = s[2];
151 break;
152 case 5:
153 *p++ = s[1];
154 break;
155 case 9:
156 *p++ = s[2];
157 *p++ = s[0];
160 *p = 0;
161 break;
163 case 'a':
164 case 'A':
166 int n = value;
167 char *p = buf;
168 if (n == 0) {
169 *p++ = '0';
170 *p = 0;
172 else {
173 if (n < 0) {
174 n = -n;
175 *p++ = '-';
177 // this is a bit tricky
178 while (n > 0) {
179 int d = n % 26;
180 if (d == 0)
181 d = 26;
182 n -= d;
183 n /= 26;
184 *p++ = format == 'a' ? lowercase_array[d - 1] :
185 uppercase_array[d - 1];
187 *p-- = 0;
188 char *q = buf[0] == '-' ? buf + 1 : buf;
189 while (q < p) {
190 char temp = *q;
191 *q = *p;
192 *p = temp;
193 --p;
194 ++q;
197 break;
199 default:
200 assert(0);
201 break;
203 return buf;
206 const char *general_reg::get_string()
208 units n;
209 if (!get_value(&n))
210 return "";
211 return number_value_to_ascii(n, format, width);
215 void general_reg::increment()
217 int n;
218 if (get_value(&n))
219 set_value(n + inc);
222 void general_reg::decrement()
224 int n;
225 if (get_value(&n))
226 set_value(n - inc);
229 void general_reg::set_increment(units n)
231 inc = n;
234 void general_reg::alter_format(char f, int w)
236 format = f;
237 width = w;
240 static const char *number_format_to_ascii(char format, int width)
242 static char buf[24];
243 if (format == '1') {
244 if (width > 0) {
245 int n = width;
246 if (n > int(sizeof(buf)) - 1)
247 n = int(sizeof(buf)) - 1;
248 sprintf(buf, "%.*d", n, 0);
249 return buf;
251 else
252 return "0";
254 else {
255 buf[0] = format;
256 buf[1] = '\0';
257 return buf;
261 const char *general_reg::get_format()
263 return number_format_to_ascii(format, width);
266 class number_reg : public general_reg {
267 units value;
268 public:
269 number_reg();
270 int get_value(units *);
271 void set_value(units);
274 number_reg::number_reg() : value(0)
278 int number_reg::get_value(units *res)
280 *res = value;
281 return 1;
284 void number_reg::set_value(units n)
286 value = n;
289 variable_reg::variable_reg(units *p) : ptr(p)
293 void variable_reg::set_value(units n)
295 *ptr = n;
298 int variable_reg::get_value(units *res)
300 *res = *ptr;
301 return 1;
304 void define_number_reg()
306 symbol nm = get_name(1);
307 if (nm.is_null()) {
308 skip_line();
309 return;
311 reg *r = (reg *)number_reg_dictionary.lookup(nm);
312 units v;
313 units prev_value;
314 if (!r || !r->get_value(&prev_value))
315 prev_value = 0;
316 if (get_number(&v, 'u', prev_value)) {
317 if (r == 0) {
318 r = new number_reg;
319 number_reg_dictionary.define(nm, r);
321 r->set_value(v);
322 if (tok.space() && has_arg() && get_number(&v, 'u'))
323 r->set_increment(v);
325 skip_line();
328 #if 0
329 void inline_define_reg()
331 token start;
332 start.next();
333 if (!start.delimiter(1))
334 return;
335 tok.next();
336 symbol nm = get_name(1);
337 if (nm.is_null())
338 return;
339 reg *r = (reg *)number_reg_dictionary.lookup(nm);
340 if (r == 0) {
341 r = new number_reg;
342 number_reg_dictionary.define(nm, r);
344 units v;
345 units prev_value;
346 if (!r->get_value(&prev_value))
347 prev_value = 0;
348 if (get_number(&v, 'u', prev_value)) {
349 r->set_value(v);
350 if (start != tok) {
351 if (get_number(&v, 'u')) {
352 r->set_increment(v);
353 if (start != tok)
354 warning(WARN_DELIM, "closing delimiter does not match");
359 #endif
361 void set_number_reg(symbol nm, units n)
363 reg *r = (reg *)number_reg_dictionary.lookup(nm);
364 if (r == 0) {
365 r = new number_reg;
366 number_reg_dictionary.define(nm, r);
368 r->set_value(n);
371 reg *lookup_number_reg(symbol nm)
373 reg *r = (reg *)number_reg_dictionary.lookup(nm);
374 if (r == 0) {
375 warning(WARN_REG, "number register `%1' not defined", nm.contents());
376 r = new number_reg;
377 number_reg_dictionary.define(nm, r);
379 return r;
382 void alter_format()
384 symbol nm = get_name(1);
385 if (nm.is_null()) {
386 skip_line();
387 return;
389 reg *r = (reg *)number_reg_dictionary.lookup(nm);
390 if (r == 0) {
391 r = new number_reg;
392 number_reg_dictionary.define(nm, r);
394 tok.skip();
395 char c = tok.ch();
396 if (csdigit(c)) {
397 int n = 0;
398 do {
399 ++n;
400 tok.next();
401 } while (csdigit(tok.ch()));
402 r->alter_format('1', n);
404 else if (c == 'i' || c == 'I' || c == 'a' || c == 'A')
405 r->alter_format(c);
406 else if (tok.newline() || tok.eof())
407 warning(WARN_MISSING, "missing number register format");
408 else
409 error("bad number register format (got %1)", tok.description());
410 skip_line();
413 void remove_reg()
415 for (;;) {
416 symbol s = get_name();
417 if (s.is_null())
418 break;
419 number_reg_dictionary.remove(s);
421 skip_line();
424 void alias_reg()
426 symbol s1 = get_name(1);
427 if (!s1.is_null()) {
428 symbol s2 = get_name(1);
429 if (!s2.is_null()) {
430 if (!number_reg_dictionary.alias(s1, s2))
431 warning(WARN_REG, "number register `%1' not defined", s2.contents());
434 skip_line();
437 void rename_reg()
439 symbol s1 = get_name(1);
440 if (!s1.is_null()) {
441 symbol s2 = get_name(1);
442 if (!s2.is_null())
443 number_reg_dictionary.rename(s1, s2);
445 skip_line();
448 void print_number_regs()
450 object_dictionary_iterator iter(number_reg_dictionary);
451 reg *r;
452 symbol s;
453 while (iter.get(&s, (object **)&r)) {
454 assert(!s.is_null());
455 errprint("%1\t", s.contents());
456 const char *p = r->get_string();
457 if (p)
458 errprint(p);
459 errprint("\n");
461 fflush(stderr);
462 skip_line();
465 void init_reg_requests()
467 init_request("rr", remove_reg);
468 init_request("nr", define_number_reg);
469 init_request("af", alter_format);
470 init_request("aln", alias_reg);
471 init_request("rnn", rename_reg);
472 init_request("pnr", print_number_regs);