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