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