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