number.cpp revision 114402
1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 21 22 23#include "troff.h" 24#include "symbol.h" 25#include "hvunits.h" 26#include "env.h" 27#include "token.h" 28#include "div.h" 29 30vunits V0; 31hunits H0; 32 33int hresolution = 1; 34int vresolution = 1; 35int units_per_inch; 36int sizescale; 37 38static int parse_expr(units *v, int scale_indicator, 39 int parenthesised, int rigid = 0); 40static int start_number(); 41 42int get_vunits(vunits *res, unsigned char si) 43{ 44 if (!start_number()) 45 return 0; 46 units x; 47 if (parse_expr(&x, si, 0)) { 48 *res = vunits(x); 49 return 1; 50 } 51 else 52 return 0; 53} 54 55int get_hunits(hunits *res, unsigned char si) 56{ 57 if (!start_number()) 58 return 0; 59 units x; 60 if (parse_expr(&x, si, 0)) { 61 *res = hunits(x); 62 return 1; 63 } 64 else 65 return 0; 66} 67 68// for \B 69 70int get_number_rigidly(units *res, unsigned char si) 71{ 72 if (!start_number()) 73 return 0; 74 units x; 75 if (parse_expr(&x, si, 0, 1)) { 76 *res = x; 77 return 1; 78 } 79 else 80 return 0; 81} 82 83int get_number(units *res, unsigned char si) 84{ 85 if (!start_number()) 86 return 0; 87 units x; 88 if (parse_expr(&x, si, 0)) { 89 *res = x; 90 return 1; 91 } 92 else 93 return 0; 94} 95 96int get_integer(int *res) 97{ 98 if (!start_number()) 99 return 0; 100 units x; 101 if (parse_expr(&x, 0, 0)) { 102 *res = x; 103 return 1; 104 } 105 else 106 return 0; 107} 108 109enum incr_number_result { BAD, ABSOLUTE, INCREMENT, DECREMENT }; 110 111static incr_number_result get_incr_number(units *res, unsigned char); 112 113int get_vunits(vunits *res, unsigned char si, vunits prev_value) 114{ 115 units v; 116 switch (get_incr_number(&v, si)) { 117 case BAD: 118 return 0; 119 case ABSOLUTE: 120 *res = v; 121 break; 122 case INCREMENT: 123 *res = prev_value + v; 124 break; 125 case DECREMENT: 126 *res = prev_value - v; 127 break; 128 default: 129 assert(0); 130 } 131 return 1; 132} 133 134int get_hunits(hunits *res, unsigned char si, hunits prev_value) 135{ 136 units v; 137 switch (get_incr_number(&v, si)) { 138 case BAD: 139 return 0; 140 case ABSOLUTE: 141 *res = v; 142 break; 143 case INCREMENT: 144 *res = prev_value + v; 145 break; 146 case DECREMENT: 147 *res = prev_value - v; 148 break; 149 default: 150 assert(0); 151 } 152 return 1; 153} 154 155int get_number(units *res, unsigned char si, units prev_value) 156{ 157 units v; 158 switch (get_incr_number(&v, si)) { 159 case BAD: 160 return 0; 161 case ABSOLUTE: 162 *res = v; 163 break; 164 case INCREMENT: 165 *res = prev_value + v; 166 break; 167 case DECREMENT: 168 *res = prev_value - v; 169 break; 170 default: 171 assert(0); 172 } 173 return 1; 174} 175 176int get_integer(int *res, int prev_value) 177{ 178 units v; 179 switch (get_incr_number(&v, 0)) { 180 case BAD: 181 return 0; 182 case ABSOLUTE: 183 *res = v; 184 break; 185 case INCREMENT: 186 *res = prev_value + int(v); 187 break; 188 case DECREMENT: 189 *res = prev_value - int(v); 190 break; 191 default: 192 assert(0); 193 } 194 return 1; 195} 196 197 198static incr_number_result get_incr_number(units *res, unsigned char si) 199{ 200 if (!start_number()) 201 return BAD; 202 incr_number_result result = ABSOLUTE; 203 if (tok.ch() == '+') { 204 tok.next(); 205 result = INCREMENT; 206 } 207 else if (tok.ch() == '-') { 208 tok.next(); 209 result = DECREMENT; 210 } 211 if (parse_expr(res, si, 0)) 212 return result; 213 else 214 return BAD; 215} 216 217static int start_number() 218{ 219 while (tok.space()) 220 tok.next(); 221 if (tok.newline()) { 222 warning(WARN_MISSING, "missing number"); 223 return 0; 224 } 225 if (tok.tab()) { 226 warning(WARN_TAB, "tab character where number expected"); 227 return 0; 228 } 229 if (tok.right_brace()) { 230 warning(WARN_RIGHT_BRACE, "`\\}' where number expected"); 231 return 0; 232 } 233 return 1; 234} 235 236enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' }; 237 238#define SCALE_INDICATOR_CHARS "icfPmnpuvMsz" 239 240static int parse_term(units *v, int scale_indicator, 241 int parenthesised, int rigid); 242 243static int parse_expr(units *v, int scale_indicator, 244 int parenthesised, int rigid) 245{ 246 int result = parse_term(v, scale_indicator, parenthesised, rigid); 247 while (result) { 248 if (parenthesised) 249 tok.skip(); 250 int op = tok.ch(); 251 switch (op) { 252 case '+': 253 case '-': 254 case '/': 255 case '*': 256 case '%': 257 case ':': 258 case '&': 259 tok.next(); 260 break; 261 case '>': 262 tok.next(); 263 if (tok.ch() == '=') { 264 tok.next(); 265 op = OP_GEQ; 266 } 267 else if (tok.ch() == '?') { 268 tok.next(); 269 op = OP_MAX; 270 } 271 break; 272 case '<': 273 tok.next(); 274 if (tok.ch() == '=') { 275 tok.next(); 276 op = OP_LEQ; 277 } 278 else if (tok.ch() == '?') { 279 tok.next(); 280 op = OP_MIN; 281 } 282 break; 283 case '=': 284 tok.next(); 285 if (tok.ch() == '=') 286 tok.next(); 287 break; 288 default: 289 return result; 290 } 291 units v2; 292 if (!parse_term(&v2, scale_indicator, parenthesised, rigid)) 293 return 0; 294 int overflow = 0; 295 switch (op) { 296 case '<': 297 *v = *v < v2; 298 break; 299 case '>': 300 *v = *v > v2; 301 break; 302 case OP_LEQ: 303 *v = *v <= v2; 304 break; 305 case OP_GEQ: 306 *v = *v >= v2; 307 break; 308 case OP_MIN: 309 if (*v > v2) 310 *v = v2; 311 break; 312 case OP_MAX: 313 if (*v < v2) 314 *v = v2; 315 break; 316 case '=': 317 *v = *v == v2; 318 break; 319 case '&': 320 *v = *v > 0 && v2 > 0; 321 break; 322 case ':': 323 *v = *v > 0 || v2 > 0; 324 break; 325 case '+': 326 if (v2 < 0) { 327 if (*v < INT_MIN - v2) 328 overflow = 1; 329 } 330 else if (v2 > 0) { 331 if (*v > INT_MAX - v2) 332 overflow = 1; 333 } 334 if (overflow) { 335 error("addition overflow"); 336 return 0; 337 } 338 *v += v2; 339 break; 340 case '-': 341 if (v2 < 0) { 342 if (*v > INT_MAX + v2) 343 overflow = 1; 344 } 345 else if (v2 > 0) { 346 if (*v < INT_MIN + v2) 347 overflow = 1; 348 } 349 if (overflow) { 350 error("subtraction overflow"); 351 return 0; 352 } 353 *v -= v2; 354 break; 355 case '*': 356 if (v2 < 0) { 357 if (*v > 0) { 358 if (*v > -(unsigned)INT_MIN / -(unsigned)v2) 359 overflow = 1; 360 } 361 else if (-(unsigned)*v > INT_MAX / -(unsigned)v2) 362 overflow = 1; 363 } 364 else if (v2 > 0) { 365 if (*v > 0) { 366 if (*v > INT_MAX / v2) 367 overflow = 1; 368 } 369 else if (-(unsigned)*v > -(unsigned)INT_MIN / v2) 370 overflow = 1; 371 } 372 if (overflow) { 373 error("multiplication overflow"); 374 return 0; 375 } 376 *v *= v2; 377 break; 378 case '/': 379 if (v2 == 0) { 380 error("division by zero"); 381 return 0; 382 } 383 *v /= v2; 384 break; 385 case '%': 386 if (v2 == 0) { 387 error("modulus by zero"); 388 return 0; 389 } 390 *v %= v2; 391 break; 392 default: 393 assert(0); 394 } 395 } 396 return result; 397} 398 399static int parse_term(units *v, int scale_indicator, 400 int parenthesised, int rigid) 401{ 402 int negative = 0; 403 for (;;) 404 if (parenthesised && tok.space()) 405 tok.next(); 406 else if (tok.ch() == '+') 407 tok.next(); 408 else if (tok.ch() == '-') { 409 tok.next(); 410 negative = !negative; 411 } 412 else 413 break; 414 unsigned char c = tok.ch(); 415 switch (c) { 416 case '|': 417 // | is not restricted to the outermost level 418 // tbl uses this 419 tok.next(); 420 if (!parse_term(v, scale_indicator, parenthesised, rigid)) 421 return 0; 422 int tem; 423 tem = (scale_indicator == 'v' 424 ? curdiv->get_vertical_position().to_units() 425 : curenv->get_input_line_position().to_units()); 426 if (tem >= 0) { 427 if (*v < INT_MIN + tem) { 428 error("numeric overflow"); 429 return 0; 430 } 431 } 432 else { 433 if (*v > INT_MAX + tem) { 434 error("numeric overflow"); 435 return 0; 436 } 437 } 438 *v -= tem; 439 if (negative) { 440 if (*v == INT_MIN) { 441 error("numeric overflow"); 442 return 0; 443 } 444 *v = -*v; 445 } 446 return 1; 447 case '(': 448 tok.next(); 449 c = tok.ch(); 450 if (c == ')') { 451 if (rigid) 452 return 0; 453 warning(WARN_SYNTAX, "empty parentheses"); 454 tok.next(); 455 *v = 0; 456 return 1; 457 } 458 else if (c != 0 && strchr(SCALE_INDICATOR_CHARS, c) != 0) { 459 tok.next(); 460 if (tok.ch() == ';') { 461 tok.next(); 462 scale_indicator = c; 463 } 464 else { 465 error("expected `;' after scale-indicator (got %1)", 466 tok.description()); 467 return 0; 468 } 469 } 470 else if (c == ';') { 471 scale_indicator = 0; 472 tok.next(); 473 } 474 if (!parse_expr(v, scale_indicator, 1, rigid)) 475 return 0; 476 tok.skip(); 477 if (tok.ch() != ')') { 478 if (rigid) 479 return 0; 480 warning(WARN_SYNTAX, "missing `)' (got %1)", tok.description()); 481 } 482 else 483 tok.next(); 484 if (negative) { 485 if (*v == INT_MIN) { 486 error("numeric overflow"); 487 return 0; 488 } 489 *v = -*v; 490 } 491 return 1; 492 case '.': 493 *v = 0; 494 break; 495 case '0': 496 case '1': 497 case '2': 498 case '3': 499 case '4': 500 case '5': 501 case '6': 502 case '7': 503 case '8': 504 case '9': 505 *v = 0; 506 do { 507 if (*v > INT_MAX/10) { 508 error("numeric overflow"); 509 return 0; 510 } 511 *v *= 10; 512 if (*v > INT_MAX - (int(c) - '0')) { 513 error("numeric overflow"); 514 return 0; 515 } 516 *v += c - '0'; 517 tok.next(); 518 c = tok.ch(); 519 } while (csdigit(c)); 520 break; 521 case '/': 522 case '*': 523 case '%': 524 case ':': 525 case '&': 526 case '>': 527 case '<': 528 case '=': 529 warning(WARN_SYNTAX, "empty left operand"); 530 *v = 0; 531 return rigid ? 0 : 1; 532 default: 533 warning(WARN_NUMBER, "numeric expression expected (got %1)", 534 tok.description()); 535 return 0; 536 } 537 int divisor = 1; 538 if (tok.ch() == '.') { 539 tok.next(); 540 for (;;) { 541 c = tok.ch(); 542 if (!csdigit(c)) 543 break; 544 // we may multiply the divisor by 254 later on 545 if (divisor <= INT_MAX/2540 && *v <= (INT_MAX - 9)/10) { 546 *v *= 10; 547 *v += c - '0'; 548 divisor *= 10; 549 } 550 tok.next(); 551 } 552 } 553 int si = scale_indicator; 554 int do_next = 0; 555 if ((c = tok.ch()) != 0 && strchr(SCALE_INDICATOR_CHARS, c) != 0) { 556 switch (scale_indicator) { 557 case 'z': 558 if (c != 'u' && c != 'z') { 559 warning(WARN_SCALE, 560 "only `z' and `u' scale indicators valid in this context"); 561 break; 562 } 563 si = c; 564 break; 565 case 0: 566 warning(WARN_SCALE, "scale indicator invalid in this context"); 567 break; 568 case 'u': 569 si = c; 570 break; 571 default: 572 if (c == 'z') { 573 warning(WARN_SCALE, "`z' scale indicator invalid in this context"); 574 break; 575 } 576 si = c; 577 break; 578 } 579 // Don't do tok.next() here because the next token might be \s, which 580 // would affect the interpretation of m. 581 do_next = 1; 582 } 583 switch (si) { 584 case 'i': 585 *v = scale(*v, units_per_inch, divisor); 586 break; 587 case 'c': 588 *v = scale(*v, units_per_inch*100, divisor*254); 589 break; 590 case 0: 591 case 'u': 592 if (divisor != 1) 593 *v /= divisor; 594 break; 595 case 'f': 596 *v = scale(*v, 65536, divisor); 597 break; 598 case 'p': 599 *v = scale(*v, units_per_inch, divisor*72); 600 break; 601 case 'P': 602 *v = scale(*v, units_per_inch, divisor*6); 603 break; 604 case 'm': 605 { 606 // Convert to hunits so that with -Tascii `m' behaves as in nroff. 607 hunits em = curenv->get_size(); 608 *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor); 609 } 610 break; 611 case 'M': 612 { 613 hunits em = curenv->get_size(); 614 *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor*100); 615 } 616 break; 617 case 'n': 618 { 619 // Convert to hunits so that with -Tascii `n' behaves as in nroff. 620 hunits en = curenv->get_size()/2; 621 *v = scale(*v, en.is_zero() ? hresolution : en.to_units(), divisor); 622 } 623 break; 624 case 'v': 625 *v = scale(*v, curenv->get_vertical_spacing().to_units(), divisor); 626 break; 627 case 's': 628 while (divisor > INT_MAX/(sizescale*72)) { 629 divisor /= 10; 630 *v /= 10; 631 } 632 *v = scale(*v, units_per_inch, divisor*sizescale*72); 633 break; 634 case 'z': 635 *v = scale(*v, sizescale, divisor); 636 break; 637 default: 638 assert(0); 639 } 640 if (do_next) 641 tok.next(); 642 if (negative) { 643 if (*v == INT_MIN) { 644 error("numeric overflow"); 645 return 0; 646 } 647 *v = -*v; 648 } 649 return 1; 650} 651 652units scale(units n, units x, units y) 653{ 654 assert(x >= 0 && y > 0); 655 if (x == 0) 656 return 0; 657 if (n >= 0) { 658 if (n <= INT_MAX/x) 659 return (n*x)/y; 660 } 661 else { 662 if (-(unsigned)n <= -(unsigned)INT_MIN/x) 663 return (n*x)/y; 664 } 665 double res = n*double(x)/double(y); 666 if (res > INT_MAX) { 667 error("numeric overflow"); 668 return INT_MAX; 669 } 670 else if (res < INT_MIN) { 671 error("numeric overflow"); 672 return INT_MIN; 673 } 674 return int(res); 675} 676 677vunits::vunits(units x) 678{ 679 // don't depend on the rounding direction for division of negative integers 680 if (vresolution == 1) 681 n = x; 682 else 683 n = (x < 0 684 ? -((-x + vresolution/2 - 1)/vresolution) 685 : (x + vresolution/2 - 1)/vresolution); 686} 687 688hunits::hunits(units x) 689{ 690 // don't depend on the rounding direction for division of negative integers 691 if (hresolution == 1) 692 n = x; 693 else 694 n = (x < 0 695 ? -((-x + hresolution/2 - 1)/hresolution) 696 : (x + hresolution/2 - 1)/hresolution); 697} 698