PageRenderTime 45ms CodeModel.GetById 13ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/groff/src/roff/troff/reg.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 473 lines | 410 code | 38 blank | 25 comment | 80 complexity | 232144ef054a4b9010b038867df16702 MD5 | raw file
  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}