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