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
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with groff; see the file COPYING.  If not, write to the Free Software
20Foundation, 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
28object_dictionary number_reg_dictionary(101);
29
30int reg::get_value(units * /*d*/)
31{
32  return 0;
33}
34
35void reg::increment()
36{
37  error("can't increment read-only register");
38}
39
40void reg::decrement()
41{
42  error("can't decrement read-only register");
43}
44
45void reg::set_increment(units /*n*/)
46{
47  error("can't auto increment read-only register");
48}
49
50void reg::alter_format(char /*f*/, int /*w*/)
51{
52  error("can't alter format of read-only register");
53}
54
55const char *reg::get_format()
56{
57  return "0";
58}
59
60void reg::set_value(units /*n*/)
61{
62  error("can't write read-only register");
63}
64
65general_reg::general_reg() : format('1'), width(0), inc(0)
66{
67}
68
69static 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
76static 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
83static 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
206const 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
215void general_reg::increment()
216{
217  int n;
218  if (get_value(&n))
219    set_value(n + inc);
220}
221
222void general_reg::decrement()
223{
224  int n;
225  if (get_value(&n))
226    set_value(n - inc);
227}
228
229void general_reg::set_increment(units n)
230{
231  inc = n;
232}
233
234void general_reg::alter_format(char f, int w)
235{
236  format = f;
237  width = w;
238}
239
240static 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
261const char *general_reg::get_format()
262{
263  return number_format_to_ascii(format, width);
264}
265
266class number_reg : public general_reg {
267  units value;
268public:
269  number_reg();
270  int get_value(units *);
271  void set_value(units);
272};
273
274number_reg::number_reg() : value(0)
275{
276}
277
278int number_reg::get_value(units *res)
279{
280  *res = value;
281  return 1;
282}
283
284void number_reg::set_value(units n)
285{
286  value = n;
287}
288
289variable_reg::variable_reg(units *p) : ptr(p)
290{
291}
292
293void variable_reg::set_value(units n)
294{
295  *ptr = n;
296}
297
298int variable_reg::get_value(units *res)
299{
300  *res = *ptr;
301  return 1;
302}
303
304void 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
329void 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
361void 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
371reg *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
382void 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
413void 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
424void 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
437void 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
448void 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
465void 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}
474