groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / roff / troff / reg.cpp
CommitLineData
92d0a6a6 1// -*- C++ -*-
4d3e9548 2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2004, 2009
92d0a6a6
JR
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
5
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
4d3e9548
JL
10Software Foundation, either version 3 of the License, or
11(at your option) any later version.
92d0a6a6
JR
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
4d3e9548
JL
18You should have received a copy of the GNU General Public License
19along with this program. If not, see <http://www.gnu.org/licenses/>. */
92d0a6a6
JR
20
21#include "troff.h"
22#include "dictionary.h"
23#include "token.h"
24#include "request.h"
25#include "reg.h"
26
27object_dictionary number_reg_dictionary(101);
28
29int reg::get_value(units * /*d*/)
30{
31 return 0;
32}
33
34void reg::increment()
35{
36 error("can't increment read-only register");
37}
38
39void reg::decrement()
40{
41 error("can't decrement read-only register");
42}
43
44void reg::set_increment(units /*n*/)
45{
46 error("can't auto increment read-only register");
47}
48
49void reg::alter_format(char /*f*/, int /*w*/)
50{
51 error("can't alter format of read-only register");
52}
53
54const char *reg::get_format()
55{
56 return "0";
57}
58
59void reg::set_value(units /*n*/)
60{
61 error("can't write read-only register");
62}
63
64general_reg::general_reg() : format('1'), width(0), inc(0)
65{
66}
67
68static char uppercase_array[] = {
69 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
70 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
71 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
72 'Y', 'Z',
73};
74
75static char lowercase_array[] = {
76 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
77 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
78 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
79 'y', 'z',
80};
81
82static const char *number_value_to_ascii(int value, char format, int width)
83{
84 static char buf[128]; // must be at least 21
85 switch(format) {
86 case '1':
87 if (width <= 0)
88 return i_to_a(value);
89 else if (width > int(sizeof(buf) - 2))
90 sprintf(buf, "%.*d", int(sizeof(buf) - 2), int(value));
91 else
92 sprintf(buf, "%.*d", width, int(value));
93 break;
94 case 'i':
95 case 'I':
96 {
97 char *p = buf;
98 // troff uses z and w to represent 10000 and 5000 in Roman
99 // numerals; I can find no historical basis for this usage
100 const char *s = format == 'i' ? "zwmdclxvi" : "ZWMDCLXVI";
101 int n = int(value);
102 if (n >= 40000 || n <= -40000) {
103 error("magnitude of `%1' too big for i or I format", n);
104 return i_to_a(n);
105 }
106 if (n == 0) {
107 *p++ = '0';
108 *p = 0;
109 break;
110 }
111 if (n < 0) {
112 *p++ = '-';
113 n = -n;
114 }
115 while (n >= 10000) {
116 *p++ = s[0];
117 n -= 10000;
118 }
119 for (int i = 1000; i > 0; i /= 10, s += 2) {
120 int m = n/i;
121 n -= m*i;
122 switch (m) {
123 case 3:
124 *p++ = s[2];
125 /* falls through */
126 case 2:
127 *p++ = s[2];
128 /* falls through */
129 case 1:
130 *p++ = s[2];
131 break;
132 case 4:
133 *p++ = s[2];
134 *p++ = s[1];
135 break;
136 case 8:
137 *p++ = s[1];
138 *p++ = s[2];
139 *p++ = s[2];
140 *p++ = s[2];
141 break;
142 case 7:
143 *p++ = s[1];
144 *p++ = s[2];
145 *p++ = s[2];
146 break;
147 case 6:
148 *p++ = s[1];
149 *p++ = s[2];
150 break;
151 case 5:
152 *p++ = s[1];
153 break;
154 case 9:
155 *p++ = s[2];
156 *p++ = s[0];
157 }
158 }
159 *p = 0;
160 break;
161 }
162 case 'a':
163 case 'A':
164 {
165 int n = value;
166 char *p = buf;
167 if (n == 0) {
168 *p++ = '0';
169 *p = 0;
170 }
171 else {
172 if (n < 0) {
173 n = -n;
174 *p++ = '-';
175 }
176 // this is a bit tricky
177 while (n > 0) {
178 int d = n % 26;
179 if (d == 0)
180 d = 26;
181 n -= d;
182 n /= 26;
183 *p++ = format == 'a' ? lowercase_array[d - 1] :
184 uppercase_array[d - 1];
185 }
186 *p-- = 0;
187 char *q = buf[0] == '-' ? buf + 1 : buf;
188 while (q < p) {
189 char temp = *q;
190 *q = *p;
191 *p = temp;
192 --p;
193 ++q;
194 }
195 }
196 break;
197 }
198 default:
199 assert(0);
200 break;
201 }
202 return buf;
203}
204
205const char *general_reg::get_string()
206{
207 units n;
208 if (!get_value(&n))
209 return "";
210 return number_value_to_ascii(n, format, width);
211}
212
213
214void general_reg::increment()
215{
216 int n;
217 if (get_value(&n))
218 set_value(n + inc);
219}
220
221void general_reg::decrement()
222{
223 int n;
224 if (get_value(&n))
225 set_value(n - inc);
226}
227
228void general_reg::set_increment(units n)
229{
230 inc = n;
231}
232
233void general_reg::alter_format(char f, int w)
234{
235 format = f;
236 width = w;
237}
238
239static const char *number_format_to_ascii(char format, int width)
240{
241 static char buf[24];
242 if (format == '1') {
243 if (width > 0) {
244 int n = width;
245 if (n > int(sizeof(buf)) - 1)
246 n = int(sizeof(buf)) - 1;
247 sprintf(buf, "%.*d", n, 0);
248 return buf;
249 }
250 else
251 return "0";
252 }
253 else {
254 buf[0] = format;
255 buf[1] = '\0';
256 return buf;
257 }
258}
259
260const char *general_reg::get_format()
261{
262 return number_format_to_ascii(format, width);
263}
264
265class number_reg : public general_reg {
266 units value;
267public:
268 number_reg();
269 int get_value(units *);
270 void set_value(units);
271};
272
273number_reg::number_reg() : value(0)
274{
275}
276
277int number_reg::get_value(units *res)
278{
279 *res = value;
280 return 1;
281}
282
283void number_reg::set_value(units n)
284{
285 value = n;
286}
287
288variable_reg::variable_reg(units *p) : ptr(p)
289{
290}
291
292void variable_reg::set_value(units n)
293{
294 *ptr = n;
295}
296
297int variable_reg::get_value(units *res)
298{
299 *res = *ptr;
300 return 1;
301}
302
303void define_number_reg()
304{
305 symbol nm = get_name(1);
306 if (nm.is_null()) {
307 skip_line();
308 return;
309 }
310 reg *r = (reg *)number_reg_dictionary.lookup(nm);
311 units v;
312 units prev_value;
313 if (!r || !r->get_value(&prev_value))
314 prev_value = 0;
315 if (get_number(&v, 'u', prev_value)) {
316 if (r == 0) {
317 r = new number_reg;
318 number_reg_dictionary.define(nm, r);
319 }
320 r->set_value(v);
321 if (tok.space() && has_arg() && get_number(&v, 'u'))
322 r->set_increment(v);
323 }
324 skip_line();
325}
326
327#if 0
328void inline_define_reg()
329{
330 token start;
331 start.next();
332 if (!start.delimiter(1))
333 return;
334 tok.next();
335 symbol nm = get_name(1);
336 if (nm.is_null())
337 return;
338 reg *r = (reg *)number_reg_dictionary.lookup(nm);
339 if (r == 0) {
340 r = new number_reg;
341 number_reg_dictionary.define(nm, r);
342 }
343 units v;
344 units prev_value;
345 if (!r->get_value(&prev_value))
346 prev_value = 0;
347 if (get_number(&v, 'u', prev_value)) {
348 r->set_value(v);
349 if (start != tok) {
350 if (get_number(&v, 'u')) {
351 r->set_increment(v);
352 if (start != tok)
353 warning(WARN_DELIM, "closing delimiter does not match");
354 }
355 }
356 }
357}
358#endif
359
360void set_number_reg(symbol nm, units n)
361{
362 reg *r = (reg *)number_reg_dictionary.lookup(nm);
363 if (r == 0) {
364 r = new number_reg;
365 number_reg_dictionary.define(nm, r);
366 }
367 r->set_value(n);
368}
369
370reg *lookup_number_reg(symbol nm)
371{
372 reg *r = (reg *)number_reg_dictionary.lookup(nm);
373 if (r == 0) {
374 warning(WARN_REG, "number register `%1' not defined", nm.contents());
375 r = new number_reg;
376 number_reg_dictionary.define(nm, r);
377 }
378 return r;
379}
380
381void alter_format()
382{
383 symbol nm = get_name(1);
384 if (nm.is_null()) {
385 skip_line();
386 return;
387 }
388 reg *r = (reg *)number_reg_dictionary.lookup(nm);
389 if (r == 0) {
390 r = new number_reg;
391 number_reg_dictionary.define(nm, r);
392 }
393 tok.skip();
394 char c = tok.ch();
395 if (csdigit(c)) {
396 int n = 0;
397 do {
398 ++n;
399 tok.next();
400 } while (csdigit(tok.ch()));
401 r->alter_format('1', n);
402 }
403 else if (c == 'i' || c == 'I' || c == 'a' || c == 'A')
404 r->alter_format(c);
405 else if (tok.newline() || tok.eof())
406 warning(WARN_MISSING, "missing number register format");
407 else
408 error("bad number register format (got %1)", tok.description());
409 skip_line();
410}
411
412void remove_reg()
413{
414 for (;;) {
415 symbol s = get_name();
416 if (s.is_null())
417 break;
418 number_reg_dictionary.remove(s);
419 }
420 skip_line();
421}
422
423void alias_reg()
424{
425 symbol s1 = get_name(1);
426 if (!s1.is_null()) {
427 symbol s2 = get_name(1);
428 if (!s2.is_null()) {
429 if (!number_reg_dictionary.alias(s1, s2))
430 warning(WARN_REG, "number register `%1' not defined", s2.contents());
431 }
432 }
433 skip_line();
434}
435
436void rename_reg()
437{
438 symbol s1 = get_name(1);
439 if (!s1.is_null()) {
440 symbol s2 = get_name(1);
441 if (!s2.is_null())
442 number_reg_dictionary.rename(s1, s2);
443 }
444 skip_line();
445}
446
447void print_number_regs()
448{
449 object_dictionary_iterator iter(number_reg_dictionary);
450 reg *r;
451 symbol s;
452 while (iter.get(&s, (object **)&r)) {
453 assert(!s.is_null());
454 errprint("%1\t", s.contents());
455 const char *p = r->get_string();
456 if (p)
457 errprint(p);
458 errprint("\n");
459 }
460 fflush(stderr);
461 skip_line();
462}
463
464void init_reg_requests()
465{
466 init_request("rr", remove_reg);
467 init_request("nr", define_number_reg);
468 init_request("af", alter_format);
469 init_request("aln", alias_reg);
470 init_request("rnn", rename_reg);
471 init_request("pnr", print_number_regs);
472}