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