groff before CVS: release 1.06
[s-roff.git] / troff / number.cc
blobc0acd44860739b5f22fc4a7797da44c860789291
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
22 #include "troff.h"
23 #include "symbol.h"
24 #include "hvunits.h"
25 #include "env.h"
26 #include "token.h"
27 #include "div.h"
29 vunits V0;
30 hunits H0;
32 int hresolution = 1;
33 int vresolution = 1;
34 int units_per_inch;
35 int sizescale;
37 static int parse_expr(units *v, int scale_indicator, int parenthesised);
38 static int start_number();
40 int get_vunits(vunits *res, unsigned char si)
42 if (!start_number())
43 return 0;
44 units x;
45 if (parse_expr(&x, si, 0)) {
46 *res = vunits(x);
47 return 1;
49 else
50 return 0;
53 int get_hunits(hunits *res, unsigned char si)
55 if (!start_number())
56 return 0;
57 units x;
58 if (parse_expr(&x, si, 0)) {
59 *res = hunits(x);
60 return 1;
62 else
63 return 0;
66 int get_number(units *res, unsigned char si)
68 if (!start_number())
69 return 0;
70 units x;
71 if (parse_expr(&x, si, 0)) {
72 *res = x;
73 return 1;
75 else
76 return 0;
79 int get_integer(int *res)
81 if (!start_number())
82 return 0;
83 units x;
84 if (parse_expr(&x, 0, 0)) {
85 *res = x;
86 return 1;
88 else
89 return 0;
92 enum incr_number_result { BAD, ABSOLUTE, INCREMENT, DECREMENT };
94 static incr_number_result get_incr_number(units *res, unsigned char);
96 int get_vunits(vunits *res, unsigned char si, vunits prev_value)
98 units v;
99 switch (get_incr_number(&v, si)) {
100 case BAD:
101 return 0;
102 case ABSOLUTE:
103 *res = v;
104 break;
105 case INCREMENT:
106 *res = prev_value + v;
107 break;
108 case DECREMENT:
109 *res = prev_value - v;
110 break;
111 default:
112 assert(0);
114 return 1;
117 int get_hunits(hunits *res, unsigned char si, hunits prev_value)
119 units v;
120 switch (get_incr_number(&v, si)) {
121 case BAD:
122 return 0;
123 case ABSOLUTE:
124 *res = v;
125 break;
126 case INCREMENT:
127 *res = prev_value + v;
128 break;
129 case DECREMENT:
130 *res = prev_value - v;
131 break;
132 default:
133 assert(0);
135 return 1;
138 int get_number(units *res, unsigned char si, units prev_value)
140 units v;
141 switch (get_incr_number(&v, si)) {
142 case BAD:
143 return 0;
144 case ABSOLUTE:
145 *res = v;
146 break;
147 case INCREMENT:
148 *res = prev_value + v;
149 break;
150 case DECREMENT:
151 *res = prev_value - v;
152 break;
153 default:
154 assert(0);
156 return 1;
159 int get_integer(int *res, int prev_value)
161 units v;
162 switch (get_incr_number(&v, 0)) {
163 case BAD:
164 return 0;
165 case ABSOLUTE:
166 *res = v;
167 break;
168 case INCREMENT:
169 *res = prev_value + int(v);
170 break;
171 case DECREMENT:
172 *res = prev_value - int(v);
173 break;
174 default:
175 assert(0);
177 return 1;
181 static incr_number_result get_incr_number(units *res, unsigned char si)
183 if (!start_number())
184 return BAD;
185 incr_number_result result = ABSOLUTE;
186 if (tok.ch() == '+') {
187 tok.next();
188 result = INCREMENT;
190 else if (tok.ch() == '-') {
191 tok.next();
192 result = DECREMENT;
194 if (parse_expr(res, si, 0))
195 return result;
196 else
197 return BAD;
200 static int start_number()
202 while (tok.space())
203 tok.next();
204 if (tok.newline()) {
205 warning(WARN_MISSING, "missing number");
206 return 0;
208 if (tok.tab()) {
209 warning(WARN_TAB, "tab character where number expected");
210 return 0;
212 if (tok.right_brace()) {
213 warning(WARN_RIGHT_BRACE, "`\\}' where number expected");
214 return 0;
216 return 1;
219 enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' };
221 #define SCALE_INDICATOR_CHARS "icPmnpuvMsz"
223 static int parse_term(units *v, int scale_indicator, int parenthesised);
225 static int parse_expr(units *v, int scale_indicator, int parenthesised)
227 int result = parse_term(v, scale_indicator, parenthesised);
228 while (result) {
229 if (parenthesised)
230 tok.skip();
231 int op = tok.ch();
232 switch (op) {
233 case '+':
234 case '-':
235 case '/':
236 case '*':
237 case '%':
238 case ':':
239 case '&':
240 tok.next();
241 break;
242 case '>':
243 tok.next();
244 if (tok.ch() == '=') {
245 tok.next();
246 op = OP_GEQ;
248 else if (tok.ch() == '?') {
249 tok.next();
250 op = OP_MAX;
252 break;
253 case '<':
254 tok.next();
255 if (tok.ch() == '=') {
256 tok.next();
257 op = OP_LEQ;
259 else if (tok.ch() == '?') {
260 tok.next();
261 op = OP_MIN;
263 break;
264 case '=':
265 tok.next();
266 if (tok.ch() == '=')
267 tok.next();
268 break;
269 default:
270 return result;
272 units v2;
273 if (!parse_term(&v2, scale_indicator, parenthesised))
274 return 0;
275 int overflow = 0;
276 switch (op) {
277 case '<':
278 *v = *v < v2;
279 break;
280 case '>':
281 *v = *v > v2;
282 break;
283 case OP_LEQ:
284 *v = *v <= v2;
285 break;
286 case OP_GEQ:
287 *v = *v >= v2;
288 break;
289 case OP_MIN:
290 if (*v > v2)
291 *v = v2;
292 break;
293 case OP_MAX:
294 if (*v < v2)
295 *v = v2;
296 break;
297 case '=':
298 *v = *v == v2;
299 break;
300 case '&':
301 *v = *v > 0 && v2 > 0;
302 break;
303 case ':':
304 *v = *v > 0 || v2 > 0;
305 case '+':
306 if (v2 < 0) {
307 if (*v < INT_MIN - v2)
308 overflow = 1;
310 else if (v2 > 0) {
311 if (*v > INT_MAX - v2)
312 overflow = 1;
314 if (overflow) {
315 error("addition overflow");
316 return 0;
318 *v += v2;
319 break;
320 case '-':
321 if (v2 < 0) {
322 if (*v > INT_MAX + v2)
323 overflow = 1;
325 else if (v2 > 0) {
326 if (*v < INT_MIN + v2)
327 overflow = 1;
329 if (overflow) {
330 error("subtraction overflow");
331 return 0;
333 *v -= v2;
334 break;
335 case '*':
336 if (v2 < 0) {
337 if (*v > 0) {
338 if (*v > -(unsigned)INT_MIN / -(unsigned)v2)
339 overflow = 1;
341 else if (-(unsigned)*v > INT_MAX / -(unsigned)v2)
342 overflow = 1;
344 else if (v2 > 0) {
345 if (*v > 0) {
346 if (*v > INT_MAX / v2)
347 overflow = 1;
349 else if (-(unsigned)*v > -(unsigned)INT_MIN / v2)
350 overflow = 1;
352 if (overflow) {
353 error("multiplication overflow");
354 return 0;
356 *v *= v2;
357 break;
358 case '/':
359 if (v2 == 0) {
360 error("division by zero");
361 return 0;
363 *v /= v2;
364 break;
365 case '%':
366 if (v2 == 0) {
367 error("modulus by zero");
368 return 0;
370 *v %= v2;
371 break;
372 default:
373 assert(0);
376 return result;
379 static int parse_term(units *v, int scale_indicator, int parenthesised)
381 int negative = 0;
382 for (;;)
383 if (parenthesised && tok.space())
384 tok.next();
385 else if (tok.ch() == '+')
386 tok.next();
387 else if (tok.ch() == '-') {
388 tok.next();
389 negative = !negative;
391 else
392 break;
393 unsigned char c = tok.ch();
394 switch (c) {
395 case '|':
396 // | is not restricted to the outermost level
397 // tbl uses this
398 tok.next();
399 if (!parse_term(v, scale_indicator, parenthesised))
400 return 0;
401 int tem;
402 tem = (scale_indicator == 'v'
403 ? curdiv->get_vertical_position().to_units()
404 : curenv->get_input_line_position().to_units());
405 if (tem >= 0) {
406 if (*v < INT_MIN + tem) {
407 error("numeric overflow");
408 return 0;
411 else {
412 if (*v > INT_MAX + tem) {
413 error("numeric overflow");
414 return 0;
417 *v -= tem;
418 if (negative) {
419 if (*v == INT_MIN) {
420 error("numeric overflow");
421 return 0;
423 *v = -*v;
425 return 1;
426 case '(':
427 tok.next();
428 c = tok.ch();
429 if (c == ')') {
430 warning(WARN_SYNTAX, "empty parentheses");
431 tok.next();
432 *v = 0;
433 return 1;
435 else if (c != 0 && strchr(SCALE_INDICATOR_CHARS, c) != 0) {
436 tok.next();
437 if (tok.ch() == ';') {
438 tok.next();
439 scale_indicator = c;
441 else {
442 error("expected `;' after scale-indicator (got %1)",
443 tok.description());
444 return 0;
447 else if (c == ';') {
448 scale_indicator = 0;
449 tok.next();
451 if (!parse_expr(v, scale_indicator, 1))
452 return 0;
453 tok.skip();
454 if (tok.ch() != ')') {
455 warning(WARN_SYNTAX, "missing `)' (got %1)", tok.description());
457 else
458 tok.next();
459 if (negative) {
460 if (*v == INT_MIN) {
461 error("numeric overflow");
462 return 0;
464 *v = -*v;
466 return 1;
467 case '.':
468 *v = 0;
469 break;
470 case '0':
471 case '1':
472 case '2':
473 case '3':
474 case '4':
475 case '5':
476 case '6':
477 case '7':
478 case '8':
479 case '9':
480 *v = 0;
481 do {
482 if (*v > INT_MAX/10) {
483 error("numeric overflow");
484 return 0;
486 *v *= 10;
487 if (*v > INT_MAX - (c - '0')) {
488 error("numeric overflow");
489 return 0;
491 *v += c - '0';
492 tok.next();
493 c = tok.ch();
494 } while (csdigit(c));
495 break;
496 case '/':
497 case '*':
498 case '%':
499 case ':':
500 case '&':
501 case '>':
502 case '<':
503 case '=':
504 warning(WARN_SYNTAX, "empty left operand");
505 *v = 0;
506 return 1;
507 default:
508 warning(WARN_NUMBER, "numeric expression expected (got %1)",
509 tok.description());
510 return 0;
512 int divisor = 1;
513 if (tok.ch() == '.') {
514 tok.next();
515 for (;;) {
516 c = tok.ch();
517 if (!csdigit(c))
518 break;
519 // we may multiply the divisor by 254 later on
520 if (divisor <= INT_MAX/2540 && *v <= (INT_MAX - 9)/10) {
521 *v *= 10;
522 *v += c - '0';
523 divisor *= 10;
525 tok.next();
528 int si = scale_indicator;
529 int do_next = 0;
530 if ((c = tok.ch()) != 0 && strchr(SCALE_INDICATOR_CHARS, c) != 0) {
531 switch (scale_indicator) {
532 case 'z':
533 if (c != 'u' && c != 'z') {
534 warning(WARN_SCALE,
535 "only `z' and `u' scale indicators valid in this context");
536 break;
538 si = c;
539 break;
540 case 0:
541 warning(WARN_SCALE, "scale indicator invalid in this context");
542 break;
543 case 'u':
544 si = c;
545 break;
546 default:
547 if (c == 'z') {
548 warning(WARN_SCALE, "`z' scale indicator invalid in this context");
549 break;
551 si = c;
552 break;
554 // Don't do tok.next() here because the next token might be \s, which
555 // would affect the interpretation of m.
556 do_next = 1;
558 switch (si) {
559 case 'i':
560 *v = scale(*v, units_per_inch, divisor);
561 break;
562 case 'c':
563 *v = scale(*v, units_per_inch*100, divisor*254);
564 break;
565 case 0:
566 case 'u':
567 if (divisor != 1)
568 *v /= divisor;
569 break;
570 case 'p':
571 *v = scale(*v, units_per_inch, divisor*72);
572 break;
573 case 'P':
574 *v = scale(*v, units_per_inch, divisor*6);
575 break;
576 case 'm':
578 // Convert to hunits so that with -Tascii `m' behaves as in nroff.
579 hunits em = curenv->get_size();
580 *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor);
582 break;
583 case 'M':
585 hunits em = curenv->get_size();
586 *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor*100);
588 break;
589 case 'n':
591 // Convert to hunits so that with -Tascii `n' behaves as in nroff.
592 hunits en = curenv->get_size()/2;
593 *v = scale(*v, en.is_zero() ? hresolution : en.to_units(), divisor);
595 break;
596 case 'v':
597 *v = scale(*v, curenv->get_vertical_spacing().to_units(), divisor);
598 break;
599 case 's':
600 while (divisor > INT_MAX/(sizescale*72)) {
601 divisor /= 10;
602 *v /= 10;
604 *v = scale(*v, units_per_inch, divisor*sizescale*72);
605 break;
606 case 'z':
607 *v = scale(*v, sizescale, divisor);
608 break;
609 default:
610 assert(0);
612 if (do_next)
613 tok.next();
614 if (negative) {
615 if (*v == INT_MIN) {
616 error("numeric overflow");
617 return 0;
619 *v = -*v;
621 return 1;
624 units scale(units n, units x, units y)
626 assert(x >= 0 && y > 0);
627 if (x == 0)
628 return 0;
629 if (n >= 0) {
630 if (n <= INT_MAX/x)
631 return (n*x)/y;
633 else {
634 if (-(unsigned)n <= -(unsigned)INT_MIN/x)
635 return (n*x)/y;
637 double res = n*double(x)/double(y);
638 if (res > INT_MAX) {
639 error("numeric overflow");
640 return INT_MAX;
642 else if (res < INT_MIN) {
643 error("numeric overflow");
644 return INT_MIN;
646 return int(res);
649 vunits::vunits(units x)
651 // don't depend on the rounding direction for division of negative integers
652 if (vresolution == 1)
653 n = x;
654 else
655 n = (x < 0
656 ? -((-x + vresolution/2 - 1)/vresolution)
657 : (x + vresolution/2 - 1)/vresolution);
660 hunits::hunits(units x)
662 // don't depend on the rounding direction for division of negative integers
663 if (hresolution == 1)
664 n = x;
665 else
666 n = (x < 0
667 ? -((-x + hresolution/2 - 1)/hresolution)
668 : (x + hresolution/2 - 1)/hresolution);