groff before CVS: release 1.06
[s-roff.git] / troff / reg.cc
blobcefeb87ed5f2490a37c6ea18ce4bce29a8b33a8a
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include "troff.h"
22 #include "symbol.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 const char *number_value_to_ascii(int value, char format, int width)
71 static char buf[128]; // must be at least 21
72 switch(format) {
73 case '1':
74 if (width <= 0)
75 return itoa(value);
76 else if (width > sizeof(buf) - 2)
77 sprintf(buf, "%.*d", sizeof(buf) - 2, int(value));
78 else
79 sprintf(buf, "%.*d", width, int(value));
80 break;
81 case 'i':
82 case 'I':
84 char *p = buf;
85 // troff uses z and w to represent 10000 and 5000 in Roman
86 // numerals; I can find no historical basis for this usage
87 const char *s = format == 'i' ? "zwmdclxvi" : "ZWMDCLXVI";
88 int n = int(value);
89 if (n >= 40000 || n <= -40000) {
90 error("magnitude of `%1' too big for i or I format", n);
91 return itoa(n);
93 if (n == 0) {
94 *p++ = '0';
95 *p = 0;
96 break;
98 if (n < 0) {
99 *p++ = '-';
100 n = -n;
102 while (n >= 10000) {
103 *p++ = s[0];
104 n -= 10000;
106 for (int i = 1000; i > 0; i /= 10, s += 2) {
107 int m = n/i;
108 n -= m*i;
109 switch (m) {
110 case 3:
111 *p++ = s[2];
112 /* falls through */
113 case 2:
114 *p++ = s[2];
115 /* falls through */
116 case 1:
117 *p++ = s[2];
118 break;
119 case 4:
120 *p++ = s[2];
121 *p++ = s[1];
122 break;
123 case 8:
124 *p++ = s[1];
125 *p++ = s[2];
126 *p++ = s[2];
127 *p++ = s[2];
128 break;
129 case 7:
130 *p++ = s[1];
131 *p++ = s[2];
132 *p++ = s[2];
133 break;
134 case 6:
135 *p++ = s[1];
136 *p++ = s[2];
137 break;
138 case 5:
139 *p++ = s[1];
140 break;
141 case 9:
142 *p++ = s[2];
143 *p++ = s[0];
146 *p = 0;
147 break;
149 case 'a':
150 case 'A':
152 int n = value;
153 char *p = buf;
154 if (n == 0) {
155 *p++ = '0';
156 *p = 0;
158 else {
159 if (n < 0) {
160 n = -n;
161 *p++ = '-';
163 // this is a bit tricky
164 while (n > 0) {
165 int d = n % 26;
166 if (d == 0)
167 d = 26;
168 n -= d;
169 n /= 26;
170 *p++ = format + d - 1;
172 *p-- = 0;
173 char *q = buf[0] == '-' ? buf+1 : buf;
174 while (q < p) {
175 char temp = *q;
176 *q = *p;
177 *p = temp;
178 --p;
179 ++q;
182 break;
184 default:
185 assert(0);
186 break;
188 return buf;
191 const char *general_reg::get_string()
193 units n;
194 if (!get_value(&n))
195 return "";
196 return number_value_to_ascii(n, format, width);
200 void general_reg::increment()
202 int n;
203 if (get_value(&n))
204 set_value(n + inc);
207 void general_reg::decrement()
209 int n;
210 if (get_value(&n))
211 set_value(n - inc);
214 void general_reg::set_increment(units n)
216 inc = n;
219 void general_reg::alter_format(char f, int w)
221 format = f;
222 width = w;
225 static const char *number_format_to_ascii(char format, int width)
227 static char buf[24];
228 if (format == '1') {
229 if (width > 0) {
230 int n = width;
231 if (n > int(sizeof(buf)) - 1)
232 n = int(sizeof(buf)) - 1;
233 sprintf(buf, "%.*d", n, 0);
234 return buf;
236 else
237 return "0";
239 else {
240 buf[0] = format;
241 buf[1] = '\0';
242 return buf;
246 const char *general_reg::get_format()
248 return number_format_to_ascii(format, width);
251 class number_reg : public general_reg {
252 units value;
253 public:
254 number_reg();
255 int get_value(units *);
256 void set_value(units);
259 number_reg::number_reg() : value(0)
263 int number_reg::get_value(units *res)
265 *res = value;
266 return 1;
269 void number_reg::set_value(units n)
271 value = n;
274 variable_reg::variable_reg(units *p) : ptr(p)
278 void variable_reg::set_value(units n)
280 *ptr = n;
283 int variable_reg::get_value(units *res)
285 *res = *ptr;
286 return 1;
289 void define_number_reg()
291 symbol nm = get_name(1);
292 if (nm.is_null()) {
293 skip_line();
294 return;
296 reg *r = (reg *)number_reg_dictionary.lookup(nm);
297 units v;
298 units prev_value;
299 if (!r || !r->get_value(&prev_value))
300 prev_value = 0;
301 if (get_number(&v, 'u', prev_value)) {
302 if (r == 0) {
303 r = new number_reg;
304 number_reg_dictionary.define(nm, r);
306 r->set_value(v);
307 if (tok.space() && has_arg() && get_number(&v, 'u'))
308 r->set_increment(v);
310 skip_line();
313 #if 0
314 void inline_define_reg()
316 token start;
317 start.next();
318 if (!start.delimiter(1))
319 return;
320 tok.next();
321 symbol nm = get_name(1);
322 if (nm.is_null())
323 return;
324 reg *r = (reg *)number_reg_dictionary.lookup(nm);
325 if (r == 0) {
326 r = new number_reg;
327 number_reg_dictionary.define(nm, r);
329 units v;
330 units prev_value;
331 if (!r->get_value(&prev_value))
332 prev_value = 0;
333 if (get_number(&v, 'u', prev_value)) {
334 r->set_value(v);
335 if (start != tok) {
336 if (get_number(&v, 'u')) {
337 r->set_increment(v);
338 if (start != tok)
339 warning(WARN_DELIM, "closing delimiter does not match");
344 #endif
346 void set_number_reg(symbol nm, units n)
348 reg *r = (reg *)number_reg_dictionary.lookup(nm);
349 if (r == 0) {
350 r = new number_reg;
351 number_reg_dictionary.define(nm, r);
353 r->set_value(n);
356 reg *lookup_number_reg(symbol nm)
358 reg *r = (reg *)number_reg_dictionary.lookup(nm);
359 if (r == 0) {
360 warning(WARN_REG, "number register `%1' not defined", nm.contents());
361 r = new number_reg;
362 number_reg_dictionary.define(nm, r);
364 return r;
367 void alter_format()
369 symbol nm = get_name(1);
370 if (nm.is_null()) {
371 skip_line();
372 return;
374 reg *r = (reg *)number_reg_dictionary.lookup(nm);
375 if (r == 0) {
376 r = new number_reg;
377 number_reg_dictionary.define(nm, r);
379 tok.skip();
380 char c = tok.ch();
381 if (csdigit(c)) {
382 int n = 0;
383 do {
384 ++n;
385 tok.next();
386 } while (csdigit(tok.ch()));
387 r->alter_format('1', n);
389 else if (c == 'i' || c == 'I' || c == 'a' || c == 'A')
390 r->alter_format(c);
391 else if (tok.newline() || tok.eof())
392 warning(WARN_MISSING, "missing number register format");
393 else
394 error("bad number register format (got %1)", tok.description());
395 skip_line();
398 void remove_reg()
400 for (;;) {
401 symbol s = get_name();
402 if (s.is_null())
403 break;
404 number_reg_dictionary.remove(s);
406 skip_line();
409 void alias_reg()
411 symbol s1 = get_name(1);
412 if (!s1.is_null()) {
413 symbol s2 = get_name(1);
414 if (!s2.is_null()) {
415 if (!number_reg_dictionary.alias(s1, s2))
416 warning(WARN_REG, "number register `%1' not defined", s2.contents());
419 skip_line();
422 void rename_reg()
424 symbol s1 = get_name(1);
425 if (!s1.is_null()) {
426 symbol s2 = get_name(1);
427 if (!s2.is_null())
428 number_reg_dictionary.rename(s1, s2);
430 skip_line();
433 void print_number_regs()
435 object_dictionary_iterator iter(number_reg_dictionary);
436 reg *r;
437 symbol s;
438 while (iter.get(&s, (object **)&r)) {
439 assert(!s.is_null());
440 errprint("%1\t", s.contents());
441 const char *p = r->get_string();
442 if (p)
443 errprint(p);
444 errprint("\n");
446 fflush(stderr);
447 skip_line();
450 void init_reg_requests()
452 init_request("rr", remove_reg);
453 init_request("nr", define_number_reg);
454 init_request("af", alter_format);
455 init_request("aln", alias_reg);
456 init_request("rnn", rename_reg);
457 init_request("pnr", print_number_regs);