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