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