groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / roff / troff / reg.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2004, 2009
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 3 of the License, or
11 (at your option) any later 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
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20
21 #include "troff.h"
22 #include "dictionary.h"
23 #include "token.h"
24 #include "request.h"
25 #include "reg.h"
26
27 object_dictionary number_reg_dictionary(101);
28
29 int reg::get_value(units * /*d*/)
30 {
31   return 0;
32 }
33
34 void reg::increment()
35 {
36   error("can't increment read-only register");
37 }
38
39 void reg::decrement()
40 {
41   error("can't decrement read-only register");
42 }
43
44 void reg::set_increment(units /*n*/)
45 {
46   error("can't auto increment read-only register");
47 }
48
49 void reg::alter_format(char /*f*/, int /*w*/)
50 {
51   error("can't alter format of read-only register");
52 }
53
54 const char *reg::get_format()
55 {
56   return "0";
57 }
58
59 void reg::set_value(units /*n*/)
60 {
61   error("can't write read-only register");
62 }
63
64 general_reg::general_reg() : format('1'), width(0), inc(0)
65 {
66 }
67
68 static 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
75 static 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
82 static 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
205 const 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
214 void general_reg::increment()
215 {
216   int n;
217   if (get_value(&n))
218     set_value(n + inc);
219 }
220
221 void general_reg::decrement()
222 {
223   int n;
224   if (get_value(&n))
225     set_value(n - inc);
226 }
227
228 void general_reg::set_increment(units n)
229 {
230   inc = n;
231 }
232
233 void general_reg::alter_format(char f, int w)
234 {
235   format = f;
236   width = w;
237 }
238
239 static 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
260 const char *general_reg::get_format()
261 {
262   return number_format_to_ascii(format, width);
263 }
264
265 class number_reg : public general_reg {
266   units value;
267 public:
268   number_reg();
269   int get_value(units *);
270   void set_value(units);
271 };
272
273 number_reg::number_reg() : value(0)
274 {
275 }
276
277 int number_reg::get_value(units *res)
278 {
279   *res = value;
280   return 1;
281 }
282
283 void number_reg::set_value(units n)
284 {
285   value = n;
286 }
287
288 variable_reg::variable_reg(units *p) : ptr(p)
289 {
290 }
291
292 void variable_reg::set_value(units n)
293 {
294   *ptr = n;
295 }
296
297 int variable_reg::get_value(units *res)
298 {
299   *res = *ptr;
300   return 1;
301 }
302
303 void 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
328 void 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
360 void 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
370 reg *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
381 void 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
412 void 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
423 void 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
436 void 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
447 void 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
464 void 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 }