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