Adapt src/troff (src/roff/troff)
[s-roff.git] / src / troff / reg.cpp
blobad5727536313dfb297c6a8d3de81cff0263d614a
1 /*@
2 * Copyright (c) 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
4 * Copyright (C) 1989 - 1992, 2000, 2001, 2004
5 * Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
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.
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.
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.
23 #include "config.h"
24 #include "troff-config.h"
26 #include "dictionary.h"
27 #include "request.h"
28 #include "token.h"
29 #include "troff.h"
31 #include "reg.h"
33 object_dictionary number_reg_dictionary(101); // FIXME static init -> init fun!
35 int reg::get_value(units * /*d*/)
37 return 0;
40 void reg::increment()
42 error("can't increment read-only register");
45 void reg::decrement()
47 error("can't decrement read-only register");
50 void reg::set_increment(units /*n*/)
52 error("can't auto increment read-only register");
55 void reg::alter_format(char /*f*/, int /*w*/)
57 error("can't alter format of read-only register");
60 const char *reg::get_format()
62 return "0";
65 void reg::set_value(units /*n*/)
67 error("can't write read-only register");
70 general_reg::general_reg() : format('1'), width(0), inc(0)
74 static char uppercase_array[] = { // FIXME const
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',
81 static char lowercase_array[] = { // FIXME const
82 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
83 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
84 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
85 'y', 'z',
88 static const char *number_value_to_ascii(int value, char format, int width)
90 static char buf[128]; // must be at least 21
91 switch(format) {
92 case '1':
93 if (width <= 0)
94 return i_to_a(value);
95 else if (width > int(sizeof(buf) - 2))
96 sprintf(buf, "%.*d", int(sizeof(buf) - 2), int(value));
97 else
98 sprintf(buf, "%.*d", width, int(value));
99 break;
100 case 'i':
101 case 'I':
103 char *p = buf;
104 // troff uses z and w to represent 10000 and 5000 in Roman
105 // numerals; I can find no historical basis for this usage
106 const char *s = format == 'i' ? "zwmdclxvi" : "ZWMDCLXVI";
107 int n = int(value);
108 if (n >= 40000 || n <= -40000) {
109 error("magnitude of `%1' too big for i or I format", n);
110 return i_to_a(n);
112 if (n == 0) {
113 *p++ = '0';
114 *p = 0;
115 break;
117 if (n < 0) {
118 *p++ = '-';
119 n = -n;
121 while (n >= 10000) {
122 *p++ = s[0];
123 n -= 10000;
125 for (int i = 1000; i > 0; i /= 10, s += 2) {
126 int m = n/i;
127 n -= m*i;
128 switch (m) {
129 case 3:
130 *p++ = s[2];
131 /* falls through */
132 case 2:
133 *p++ = s[2];
134 /* falls through */
135 case 1:
136 *p++ = s[2];
137 break;
138 case 4:
139 *p++ = s[2];
140 *p++ = s[1];
141 break;
142 case 8:
143 *p++ = s[1];
144 *p++ = s[2];
145 *p++ = s[2];
146 *p++ = s[2];
147 break;
148 case 7:
149 *p++ = s[1];
150 *p++ = s[2];
151 *p++ = s[2];
152 break;
153 case 6:
154 *p++ = s[1];
155 *p++ = s[2];
156 break;
157 case 5:
158 *p++ = s[1];
159 break;
160 case 9:
161 *p++ = s[2];
162 *p++ = s[0];
165 *p = 0;
166 break;
168 case 'a':
169 case 'A':
171 int n = value;
172 char *p = buf;
173 if (n == 0) {
174 *p++ = '0';
175 *p = 0;
177 else {
178 if (n < 0) {
179 n = -n;
180 *p++ = '-';
182 // this is a bit tricky
183 while (n > 0) {
184 int d = n % 26;
185 if (d == 0)
186 d = 26;
187 n -= d;
188 n /= 26;
189 *p++ = format == 'a' ? lowercase_array[d - 1] :
190 uppercase_array[d - 1];
192 *p-- = 0;
193 char *q = buf[0] == '-' ? buf + 1 : buf;
194 while (q < p) {
195 char temp = *q;
196 *q = *p;
197 *p = temp;
198 --p;
199 ++q;
202 break;
204 default:
205 assert(0);
206 break;
208 return buf;
211 const char *general_reg::get_string()
213 units n;
214 if (!get_value(&n))
215 return "";
216 return number_value_to_ascii(n, format, width);
219 void general_reg::increment()
221 int n;
222 if (get_value(&n))
223 set_value(n + inc);
226 void general_reg::decrement()
228 int n;
229 if (get_value(&n))
230 set_value(n - inc);
233 void general_reg::set_increment(units n)
235 inc = n;
238 void general_reg::alter_format(char f, int w)
240 format = f;
241 width = w;
244 static const char *number_format_to_ascii(char format, int width)
246 static char buf[24];
247 if (format == '1') {
248 if (width > 0) {
249 int n = width;
250 if (n > int(sizeof(buf)) - 1)
251 n = int(sizeof(buf)) - 1;
252 sprintf(buf, "%.*d", n, 0);
253 return buf;
255 else
256 return "0";
258 else {
259 buf[0] = format;
260 buf[1] = '\0';
261 return buf;
265 const char *general_reg::get_format()
267 return number_format_to_ascii(format, width);
270 class number_reg
271 : public general_reg
273 units value;
275 public:
276 number_reg();
277 int get_value(units *);
278 void set_value(units);
281 number_reg::number_reg() : value(0)
285 int number_reg::get_value(units *res)
287 *res = value;
288 return 1;
291 void number_reg::set_value(units n)
293 value = n;
296 variable_reg::variable_reg(units *p) : ptr(p)
300 void variable_reg::set_value(units n)
302 *ptr = n;
305 int variable_reg::get_value(units *res)
307 *res = *ptr;
308 return 1;
311 void define_number_reg()
313 symbol nm = get_name(1);
314 if (nm.is_null()) {
315 skip_line();
316 return;
318 reg *r = (reg *)number_reg_dictionary.lookup(nm);
319 units v;
320 units prev_value;
321 if (!r || !r->get_value(&prev_value))
322 prev_value = 0;
323 if (get_number(&v, 'u', prev_value)) {
324 if (r == 0) {
325 r = new number_reg;
326 number_reg_dictionary.define(nm, r);
328 r->set_value(v);
329 if (tok.space() && has_arg() && get_number(&v, 'u'))
330 r->set_increment(v);
332 skip_line();
335 #if 0
336 void inline_define_reg()
338 token start;
339 start.next();
340 if (!start.delimiter(1))
341 return;
342 tok.next();
343 symbol nm = get_name(1);
344 if (nm.is_null())
345 return;
346 reg *r = (reg *)number_reg_dictionary.lookup(nm);
347 if (r == 0) {
348 r = new number_reg;
349 number_reg_dictionary.define(nm, r);
351 units v;
352 units prev_value;
353 if (!r->get_value(&prev_value))
354 prev_value = 0;
355 if (get_number(&v, 'u', prev_value)) {
356 r->set_value(v);
357 if (start != tok) {
358 if (get_number(&v, 'u')) {
359 r->set_increment(v);
360 if (start != tok)
361 warning(WARN_DELIM, "closing delimiter does not match");
366 #endif
368 void set_number_reg(symbol nm, units n)
370 reg *r = (reg *)number_reg_dictionary.lookup(nm);
371 if (r == 0) {
372 r = new number_reg;
373 number_reg_dictionary.define(nm, r);
375 r->set_value(n);
378 reg *lookup_number_reg(symbol nm)
380 reg *r = (reg *)number_reg_dictionary.lookup(nm);
381 if (r == 0) {
382 warning(WARN_REG, "number register `%1' not defined", nm.contents());
383 r = new number_reg;
384 number_reg_dictionary.define(nm, r);
386 return r;
389 void alter_format()
391 symbol nm = get_name(1);
392 if (nm.is_null()) {
393 skip_line();
394 return;
396 reg *r = (reg *)number_reg_dictionary.lookup(nm);
397 if (r == 0) {
398 r = new number_reg;
399 number_reg_dictionary.define(nm, r);
401 tok.skip();
402 char c = tok.ch();
403 if (csdigit(c)) {
404 int n = 0;
405 do {
406 ++n;
407 tok.next();
408 } while (csdigit(tok.ch()));
409 r->alter_format('1', n);
411 else if (c == 'i' || c == 'I' || c == 'a' || c == 'A')
412 r->alter_format(c);
413 else if (tok.newline() || tok.eof())
414 warning(WARN_MISSING, "missing number register format");
415 else
416 error("bad number register format (got %1)", tok.description());
417 skip_line();
420 void remove_reg()
422 for (;;) {
423 symbol s = get_name();
424 if (s.is_null())
425 break;
426 number_reg_dictionary.remove(s);
428 skip_line();
431 void alias_reg()
433 symbol s1 = get_name(1);
434 if (!s1.is_null()) {
435 symbol s2 = get_name(1);
436 if (!s2.is_null()) {
437 if (!number_reg_dictionary.alias(s1, s2))
438 warning(WARN_REG, "number register `%1' not defined", s2.contents());
441 skip_line();
444 void rename_reg()
446 symbol s1 = get_name(1);
447 if (!s1.is_null()) {
448 symbol s2 = get_name(1);
449 if (!s2.is_null())
450 number_reg_dictionary.rename(s1, s2);
452 skip_line();
455 void print_number_regs()
457 object_dictionary_iterator iter(number_reg_dictionary);
458 reg *r;
459 symbol s;
460 while (iter.get(&s, (object **)&r)) {
461 assert(!s.is_null());
462 errprint("%1\t", s.contents());
463 const char *p = r->get_string();
464 if (p)
465 errprint(p);
466 errprint("\n");
468 fflush(stderr);
469 skip_line();
472 void init_reg_requests()
474 init_request("rr", remove_reg);
475 init_request("nr", define_number_reg);
476 init_request("af", alter_format);
477 init_request("aln", alias_reg);
478 init_request("rnn", rename_reg);
479 init_request("pnr", print_number_regs);
482 // s-it2-mode