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