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