Adapt src/roff (src/roff/groff)
[s-roff.git] / src / roff / troff / number.cpp
blob0a4563e4b31e41906f771fee7f41a54b111d1b98
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)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
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"
31 vunits V0;
32 hunits H0;
34 int hresolution = 1;
35 int vresolution = 1;
36 int units_per_inch;
37 int sizescale;
39 static int parse_expr(units *v, int scale_indicator,
40 int parenthesised, int rigid = 0);
41 static int start_number();
43 int get_vunits(vunits *res, unsigned char si)
45 if (!start_number())
46 return 0;
47 units x;
48 if (parse_expr(&x, si, 0)) {
49 *res = vunits(x);
50 return 1;
52 else
53 return 0;
56 int get_hunits(hunits *res, unsigned char si)
58 if (!start_number())
59 return 0;
60 units x;
61 if (parse_expr(&x, si, 0)) {
62 *res = hunits(x);
63 return 1;
65 else
66 return 0;
69 // for \B
71 int get_number_rigidly(units *res, unsigned char si)
73 if (!start_number())
74 return 0;
75 units x;
76 if (parse_expr(&x, si, 0, 1)) {
77 *res = x;
78 return 1;
80 else
81 return 0;
84 int get_number(units *res, unsigned char si)
86 if (!start_number())
87 return 0;
88 units x;
89 if (parse_expr(&x, si, 0)) {
90 *res = x;
91 return 1;
93 else
94 return 0;
97 int get_integer(int *res)
99 if (!start_number())
100 return 0;
101 units x;
102 if (parse_expr(&x, 0, 0)) {
103 *res = x;
104 return 1;
106 else
107 return 0;
110 enum incr_number_result { BAD, ABSOLUTE, INCREMENT, DECREMENT };
112 static incr_number_result get_incr_number(units *res, unsigned char);
114 int get_vunits(vunits *res, unsigned char si, vunits prev_value)
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);
132 return 1;
135 int get_hunits(hunits *res, unsigned char si, hunits prev_value)
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);
153 return 1;
156 int get_number(units *res, unsigned char si, units prev_value)
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);
174 return 1;
177 int get_integer(int *res, int prev_value)
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);
195 return 1;
199 static incr_number_result get_incr_number(units *res, unsigned char si)
201 if (!start_number())
202 return BAD;
203 incr_number_result result = ABSOLUTE;
204 if (tok.ch() == '+') {
205 tok.next();
206 result = INCREMENT;
208 else if (tok.ch() == '-') {
209 tok.next();
210 result = DECREMENT;
212 if (parse_expr(res, si, 0))
213 return result;
214 else
215 return BAD;
218 static int start_number()
220 while (tok.space())
221 tok.next();
222 if (tok.newline()) {
223 warning(WARN_MISSING, "missing number");
224 return 0;
226 if (tok.tab()) {
227 warning(WARN_TAB, "tab character where number expected");
228 return 0;
230 if (tok.right_brace()) {
231 warning(WARN_RIGHT_BRACE, "`\\}' where number expected");
232 return 0;
234 return 1;
237 enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' };
239 #define SCALE_INDICATOR_CHARS "icfPmnpuvMsz"
241 static int parse_term(units *v, int scale_indicator,
242 int parenthesised, int rigid);
244 static int parse_expr(units *v, int scale_indicator,
245 int parenthesised, int rigid)
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;
268 else if (tok.ch() == '?') {
269 tok.next();
270 op = OP_MAX;
272 break;
273 case '<':
274 tok.next();
275 if (tok.ch() == '=') {
276 tok.next();
277 op = OP_LEQ;
279 else if (tok.ch() == '?') {
280 tok.next();
281 op = OP_MIN;
283 break;
284 case '=':
285 tok.next();
286 if (tok.ch() == '=')
287 tok.next();
288 break;
289 default:
290 return result;
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;
331 else if (v2 > 0) {
332 if (*v > INT_MAX - v2)
333 overflow = 1;
335 if (overflow) {
336 error("addition overflow");
337 return 0;
339 *v += v2;
340 break;
341 case '-':
342 if (v2 < 0) {
343 if (*v > INT_MAX + v2)
344 overflow = 1;
346 else if (v2 > 0) {
347 if (*v < INT_MIN + v2)
348 overflow = 1;
350 if (overflow) {
351 error("subtraction overflow");
352 return 0;
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;
362 else if (-(unsigned)*v > INT_MAX / -(unsigned)v2)
363 overflow = 1;
365 else if (v2 > 0) {
366 if (*v > 0) {
367 if (*v > INT_MAX / v2)
368 overflow = 1;
370 else if (-(unsigned)*v > -(unsigned)INT_MIN / v2)
371 overflow = 1;
373 if (overflow) {
374 error("multiplication overflow");
375 return 0;
377 *v *= v2;
378 break;
379 case '/':
380 if (v2 == 0) {
381 error("division by zero");
382 return 0;
384 *v /= v2;
385 break;
386 case '%':
387 if (v2 == 0) {
388 error("modulus by zero");
389 return 0;
391 *v %= v2;
392 break;
393 default:
394 assert(0);
397 return result;
400 static int parse_term(units *v, int scale_indicator,
401 int parenthesised, int rigid)
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;
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;
433 else {
434 if (*v > INT_MAX + tem) {
435 error("numeric overflow");
436 return 0;
439 *v -= tem;
440 if (negative) {
441 if (*v == INT_MIN) {
442 error("numeric overflow");
443 return 0;
445 *v = -*v;
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;
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;
465 else {
466 error("expected `;' after scale-indicator (got %1)",
467 tok.description());
468 return 0;
471 else if (c == ';') {
472 scale_indicator = 0;
473 tok.next();
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());
483 else
484 tok.next();
485 if (negative) {
486 if (*v == INT_MIN) {
487 error("numeric overflow");
488 return 0;
490 *v = -*v;
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;
512 *v *= 10;
513 if (*v > INT_MAX - (int(c) - '0')) {
514 error("numeric overflow");
515 return 0;
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;
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;
551 tok.next();
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;
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;
577 si = c;
578 break;
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;
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':
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);
611 break;
612 case 'M':
614 hunits em = curenv->get_size();
615 *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor*100);
617 break;
618 case 'n':
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);
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;
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);
641 if (do_next)
642 tok.next();
643 if (negative) {
644 if (*v == INT_MIN) {
645 error("numeric overflow");
646 return 0;
648 *v = -*v;
650 return 1;
653 units scale(units n, units x, units y)
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;
662 else {
663 if (-(unsigned)n <= -(unsigned)INT_MIN/x)
664 return (n*x)/y;
666 double res = n*double(x)/double(y);
667 if (res > INT_MAX) {
668 error("numeric overflow");
669 return INT_MAX;
671 else if (res < INT_MIN) {
672 error("numeric overflow");
673 return INT_MIN;
675 return int(res);
678 vunits::vunits(units x)
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);
689 hunits::hunits(units x)
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);