Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / groff / src / roff / troff / reg.cc
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
31 int reg::get_value(units * /*d*/)
32 {
33   return 0;
34 }
35
36 void reg::increment()
37 {
38   error("can't increment read-only register");
39 }
40
41 void reg::decrement()
42 {
43   error("can't decrement read-only register");
44 }
45
46 void reg::set_increment(units /*n*/)
47 {
48   error("can't auto increment read-only register");
49 }
50
51 void reg::alter_format(char /*f*/, int /*w*/)
52 {
53   error("can't alter format of read-only register");
54 }
55
56 const char *reg::get_format()
57 {
58   return "0";
59 }
60
61 void reg::set_value(units /*n*/)
62 {
63   error("can't write read-only register");
64 }
65
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
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
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
216 void general_reg::increment()
217 {
218   int n;
219   if (get_value(&n))
220     set_value(n + inc);
221 }
222
223 void general_reg::decrement()
224 {
225   int n;
226   if (get_value(&n))
227     set_value(n - inc);
228 }
229
230 void general_reg::set_increment(units n)
231 {
232   inc = n;
233 }
234
235 void general_reg::alter_format(char f, int w)
236 {
237   format = f;
238   width = w;
239 }
240
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
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
275 number_reg::number_reg() : value(0)
276 {
277 }
278
279 int number_reg::get_value(units *res)
280 {
281   *res = value;
282   return 1;
283 }
284
285 void number_reg::set_value(units n)
286 {
287   value = n;
288 }
289
290 variable_reg::variable_reg(units *p) : ptr(p)
291 {
292 }
293
294 void variable_reg::set_value(units n)
295 {
296   *ptr = n;
297 }
298
299 int variable_reg::get_value(units *res)
300 {
301   *res = *ptr;
302   return 1;
303 }
304
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
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
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
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
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
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
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
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
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 }