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