groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / roff / troff / number.cpp
CommitLineData
92d0a6a6 1// -*- C++ -*-
4d3e9548 2/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2004, 2009
92d0a6a6
JR
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
4d3e9548
JL
10Software Foundation, either version 3 of the License, or
11(at your option) any later version.
92d0a6a6
JR
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
4d3e9548
JL
18You should have received a copy of the GNU General Public License
19along with this program. If not, see <http://www.gnu.org/licenses/>. */
92d0a6a6
JR
20
21
22#include "troff.h"
23#include "hvunits.h"
465b256c
JR
24#include "stringclass.h"
25#include "mtsm.h"
92d0a6a6
JR
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}