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