xref: /netbsd-src/external/gpl2/groff/dist/src/roff/troff/number.cpp (revision 89a07cf815a29524268025a1139fac4c5190f765)
1 /*	$NetBSD: number.cpp,v 1.1.1.1 2016/01/13 18:41:48 christos Exp $	*/
2 
3 // -*- C++ -*-
4 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2004
5    Free Software Foundation, Inc.
6      Written by James Clark (jjc@jclark.com)
7 
8 This file is part of groff.
9 
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
13 version.
14 
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18 for more details.
19 
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING.  If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23 
24 
25 #include "troff.h"
26 #include "hvunits.h"
27 #include "stringclass.h"
28 #include "mtsm.h"
29 #include "env.h"
30 #include "token.h"
31 #include "div.h"
32 
33 vunits V0;
34 hunits H0;
35 
36 int hresolution = 1;
37 int vresolution = 1;
38 int units_per_inch;
39 int sizescale;
40 
41 static int parse_expr(units *v, int scale_indicator,
42 		      int parenthesised, int rigid = 0);
43 static int start_number();
44 
get_vunits(vunits * res,unsigned char si)45 int get_vunits(vunits *res, unsigned char si)
46 {
47   if (!start_number())
48     return 0;
49   units x;
50   if (parse_expr(&x, si, 0)) {
51     *res = vunits(x);
52     return 1;
53   }
54   else
55     return 0;
56 }
57 
get_hunits(hunits * res,unsigned char si)58 int get_hunits(hunits *res, unsigned char si)
59 {
60   if (!start_number())
61     return 0;
62   units x;
63   if (parse_expr(&x, si, 0)) {
64     *res = hunits(x);
65     return 1;
66   }
67   else
68     return 0;
69 }
70 
71 // for \B
72 
get_number_rigidly(units * res,unsigned char si)73 int get_number_rigidly(units *res, unsigned char si)
74 {
75   if (!start_number())
76     return 0;
77   units x;
78   if (parse_expr(&x, si, 0, 1)) {
79     *res = x;
80     return 1;
81   }
82   else
83     return 0;
84 }
85 
get_number(units * res,unsigned char si)86 int get_number(units *res, unsigned char si)
87 {
88   if (!start_number())
89     return 0;
90   units x;
91   if (parse_expr(&x, si, 0)) {
92     *res = x;
93     return 1;
94   }
95   else
96     return 0;
97 }
98 
get_integer(int * res)99 int get_integer(int *res)
100 {
101   if (!start_number())
102     return 0;
103   units x;
104   if (parse_expr(&x, 0, 0)) {
105     *res = x;
106     return 1;
107   }
108   else
109     return 0;
110 }
111 
112 enum incr_number_result { BAD, ABSOLUTE, INCREMENT, DECREMENT };
113 
114 static incr_number_result get_incr_number(units *res, unsigned char);
115 
get_vunits(vunits * res,unsigned char si,vunits prev_value)116 int get_vunits(vunits *res, unsigned char si, vunits prev_value)
117 {
118   units v;
119   switch (get_incr_number(&v, si)) {
120   case BAD:
121     return 0;
122   case ABSOLUTE:
123     *res = v;
124     break;
125   case INCREMENT:
126     *res = prev_value + v;
127     break;
128   case DECREMENT:
129     *res = prev_value - v;
130     break;
131   default:
132     assert(0);
133   }
134   return 1;
135 }
136 
get_hunits(hunits * res,unsigned char si,hunits prev_value)137 int get_hunits(hunits *res, unsigned char si, hunits prev_value)
138 {
139   units v;
140   switch (get_incr_number(&v, si)) {
141   case BAD:
142     return 0;
143   case ABSOLUTE:
144     *res = v;
145     break;
146   case INCREMENT:
147     *res = prev_value + v;
148     break;
149   case DECREMENT:
150     *res = prev_value - v;
151     break;
152   default:
153     assert(0);
154   }
155   return 1;
156 }
157 
get_number(units * res,unsigned char si,units prev_value)158 int get_number(units *res, unsigned char si, units prev_value)
159 {
160   units v;
161   switch (get_incr_number(&v, si)) {
162   case BAD:
163     return 0;
164   case ABSOLUTE:
165     *res = v;
166     break;
167   case INCREMENT:
168     *res = prev_value + v;
169     break;
170   case DECREMENT:
171     *res = prev_value - v;
172     break;
173   default:
174     assert(0);
175   }
176   return 1;
177 }
178 
get_integer(int * res,int prev_value)179 int get_integer(int *res, int prev_value)
180 {
181   units v;
182   switch (get_incr_number(&v, 0)) {
183   case BAD:
184     return 0;
185   case ABSOLUTE:
186     *res = v;
187     break;
188   case INCREMENT:
189     *res = prev_value + int(v);
190     break;
191   case DECREMENT:
192     *res = prev_value - int(v);
193     break;
194   default:
195     assert(0);
196   }
197   return 1;
198 }
199 
200 
get_incr_number(units * res,unsigned char si)201 static incr_number_result get_incr_number(units *res, unsigned char si)
202 {
203   if (!start_number())
204     return BAD;
205   incr_number_result result = ABSOLUTE;
206   if (tok.ch() == '+') {
207     tok.next();
208     result = INCREMENT;
209   }
210   else if (tok.ch() == '-') {
211     tok.next();
212     result = DECREMENT;
213   }
214   if (parse_expr(res, si, 0))
215     return result;
216   else
217     return BAD;
218 }
219 
start_number()220 static int start_number()
221 {
222   while (tok.space())
223     tok.next();
224   if (tok.newline()) {
225     warning(WARN_MISSING, "missing number");
226     return 0;
227   }
228   if (tok.tab()) {
229     warning(WARN_TAB, "tab character where number expected");
230     return 0;
231   }
232   if (tok.right_brace()) {
233     warning(WARN_RIGHT_BRACE, "`\\}' where number expected");
234     return 0;
235   }
236   return 1;
237 }
238 
239 enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' };
240 
241 #define SCALE_INDICATOR_CHARS "icfPmnpuvMsz"
242 
243 static int parse_term(units *v, int scale_indicator,
244 		      int parenthesised, int rigid);
245 
parse_expr(units * v,int scale_indicator,int parenthesised,int rigid)246 static int parse_expr(units *v, int scale_indicator,
247 		      int parenthesised, int rigid)
248 {
249   int result = parse_term(v, scale_indicator, parenthesised, rigid);
250   while (result) {
251     if (parenthesised)
252       tok.skip();
253     int op = tok.ch();
254     switch (op) {
255     case '+':
256     case '-':
257     case '/':
258     case '*':
259     case '%':
260     case ':':
261     case '&':
262       tok.next();
263       break;
264     case '>':
265       tok.next();
266       if (tok.ch() == '=') {
267 	tok.next();
268 	op = OP_GEQ;
269       }
270       else if (tok.ch() == '?') {
271 	tok.next();
272 	op = OP_MAX;
273       }
274       break;
275     case '<':
276       tok.next();
277       if (tok.ch() == '=') {
278 	tok.next();
279 	op = OP_LEQ;
280       }
281       else if (tok.ch() == '?') {
282 	tok.next();
283 	op = OP_MIN;
284       }
285       break;
286     case '=':
287       tok.next();
288       if (tok.ch() == '=')
289 	tok.next();
290       break;
291     default:
292       return result;
293     }
294     units v2;
295     if (!parse_term(&v2, scale_indicator, parenthesised, rigid))
296       return 0;
297     int overflow = 0;
298     switch (op) {
299     case '<':
300       *v = *v < v2;
301       break;
302     case '>':
303       *v = *v > v2;
304       break;
305     case OP_LEQ:
306       *v = *v <= v2;
307       break;
308     case OP_GEQ:
309       *v = *v >= v2;
310       break;
311     case OP_MIN:
312       if (*v > v2)
313 	*v = v2;
314       break;
315     case OP_MAX:
316       if (*v < v2)
317 	*v = v2;
318       break;
319     case '=':
320       *v = *v == v2;
321       break;
322     case '&':
323       *v = *v > 0 && v2 > 0;
324       break;
325     case ':':
326       *v = *v > 0 || v2 > 0;
327       break;
328     case '+':
329       if (v2 < 0) {
330 	if (*v < INT_MIN - v2)
331 	  overflow = 1;
332       }
333       else if (v2 > 0) {
334 	if (*v > INT_MAX - v2)
335 	  overflow = 1;
336       }
337       if (overflow) {
338 	error("addition overflow");
339 	return 0;
340       }
341       *v += v2;
342       break;
343     case '-':
344       if (v2 < 0) {
345 	if (*v > INT_MAX + v2)
346 	  overflow = 1;
347       }
348       else if (v2 > 0) {
349 	if (*v < INT_MIN + v2)
350 	  overflow = 1;
351       }
352       if (overflow) {
353 	error("subtraction overflow");
354 	return 0;
355       }
356       *v -= v2;
357       break;
358     case '*':
359       if (v2 < 0) {
360 	if (*v > 0) {
361 	  if (*v > -(unsigned)INT_MIN / -(unsigned)v2)
362 	    overflow = 1;
363 	}
364 	else if (-(unsigned)*v > INT_MAX / -(unsigned)v2)
365 	  overflow = 1;
366       }
367       else if (v2 > 0) {
368 	if (*v > 0) {
369 	  if (*v > INT_MAX / v2)
370 	    overflow = 1;
371 	}
372 	else if (-(unsigned)*v > -(unsigned)INT_MIN / v2)
373 	  overflow = 1;
374       }
375       if (overflow) {
376 	error("multiplication overflow");
377 	return 0;
378       }
379       *v *= v2;
380       break;
381     case '/':
382       if (v2 == 0) {
383 	error("division by zero");
384 	return 0;
385       }
386       *v /= v2;
387       break;
388     case '%':
389       if (v2 == 0) {
390 	error("modulus by zero");
391 	return 0;
392       }
393       *v %= v2;
394       break;
395     default:
396       assert(0);
397     }
398   }
399   return result;
400 }
401 
parse_term(units * v,int scale_indicator,int parenthesised,int rigid)402 static int parse_term(units *v, int scale_indicator,
403 		      int parenthesised, int rigid)
404 {
405   int negative = 0;
406   for (;;)
407     if (parenthesised && tok.space())
408       tok.next();
409     else if (tok.ch() == '+')
410       tok.next();
411     else if (tok.ch() == '-') {
412       tok.next();
413       negative = !negative;
414     }
415     else
416       break;
417   unsigned char c = tok.ch();
418   switch (c) {
419   case '|':
420     // | is not restricted to the outermost level
421     // tbl uses this
422     tok.next();
423     if (!parse_term(v, scale_indicator, parenthesised, rigid))
424       return 0;
425     int tem;
426     tem = (scale_indicator == 'v'
427 	   ? curdiv->get_vertical_position().to_units()
428 	   : curenv->get_input_line_position().to_units());
429     if (tem >= 0) {
430       if (*v < INT_MIN + tem) {
431 	error("numeric overflow");
432 	return 0;
433       }
434     }
435     else {
436       if (*v > INT_MAX + tem) {
437 	error("numeric overflow");
438 	return 0;
439       }
440     }
441     *v -= tem;
442     if (negative) {
443       if (*v == INT_MIN) {
444 	error("numeric overflow");
445 	return 0;
446       }
447       *v = -*v;
448     }
449     return 1;
450   case '(':
451     tok.next();
452     c = tok.ch();
453     if (c == ')') {
454       if (rigid)
455 	return 0;
456       warning(WARN_SYNTAX, "empty parentheses");
457       tok.next();
458       *v = 0;
459       return 1;
460     }
461     else if (c != 0 && strchr(SCALE_INDICATOR_CHARS, c) != 0) {
462       tok.next();
463       if (tok.ch() == ';') {
464 	tok.next();
465 	scale_indicator = c;
466       }
467       else {
468 	error("expected `;' after scale-indicator (got %1)",
469 	      tok.description());
470 	return 0;
471       }
472     }
473     else if (c == ';') {
474       scale_indicator = 0;
475       tok.next();
476     }
477     if (!parse_expr(v, scale_indicator, 1, rigid))
478       return 0;
479     tok.skip();
480     if (tok.ch() != ')') {
481       if (rigid)
482 	return 0;
483       warning(WARN_SYNTAX, "missing `)' (got %1)", tok.description());
484     }
485     else
486       tok.next();
487     if (negative) {
488       if (*v == INT_MIN) {
489 	error("numeric overflow");
490 	return 0;
491       }
492       *v = -*v;
493     }
494     return 1;
495   case '.':
496     *v = 0;
497     break;
498   case '0':
499   case '1':
500   case '2':
501   case '3':
502   case '4':
503   case '5':
504   case '6':
505   case '7':
506   case '8':
507   case '9':
508     *v = 0;
509     do {
510       if (*v > INT_MAX/10) {
511 	error("numeric overflow");
512 	return 0;
513       }
514       *v *= 10;
515       if (*v > INT_MAX - (int(c) - '0')) {
516 	error("numeric overflow");
517 	return 0;
518       }
519       *v += c - '0';
520       tok.next();
521       c = tok.ch();
522     } while (csdigit(c));
523     break;
524   case '/':
525   case '*':
526   case '%':
527   case ':':
528   case '&':
529   case '>':
530   case '<':
531   case '=':
532     warning(WARN_SYNTAX, "empty left operand");
533     *v = 0;
534     return rigid ? 0 : 1;
535   default:
536     warning(WARN_NUMBER, "numeric expression expected (got %1)",
537 	    tok.description());
538     return 0;
539   }
540   int divisor = 1;
541   if (tok.ch() == '.') {
542     tok.next();
543     for (;;) {
544       c = tok.ch();
545       if (!csdigit(c))
546 	break;
547       // we may multiply the divisor by 254 later on
548       if (divisor <= INT_MAX/2540 && *v <= (INT_MAX - 9)/10) {
549 	*v *= 10;
550 	*v += c - '0';
551 	divisor *= 10;
552       }
553       tok.next();
554     }
555   }
556   int si = scale_indicator;
557   int do_next = 0;
558   if ((c = tok.ch()) != 0 && strchr(SCALE_INDICATOR_CHARS, c) != 0) {
559     switch (scale_indicator) {
560     case 'z':
561       if (c != 'u' && c != 'z') {
562 	warning(WARN_SCALE,
563 		"only `z' and `u' scale indicators valid in this context");
564 	break;
565       }
566       si = c;
567       break;
568     case 0:
569       warning(WARN_SCALE, "scale indicator invalid in this context");
570       break;
571     case 'u':
572       si = c;
573       break;
574     default:
575       if (c == 'z') {
576 	warning(WARN_SCALE, "`z' scale indicator invalid in this context");
577 	break;
578       }
579       si = c;
580       break;
581     }
582     // Don't do tok.next() here because the next token might be \s, which
583     // would affect the interpretation of m.
584     do_next = 1;
585   }
586   switch (si) {
587   case 'i':
588     *v = scale(*v, units_per_inch, divisor);
589     break;
590   case 'c':
591     *v = scale(*v, units_per_inch*100, divisor*254);
592     break;
593   case 0:
594   case 'u':
595     if (divisor != 1)
596       *v /= divisor;
597     break;
598   case 'f':
599     *v = scale(*v, 65536, divisor);
600     break;
601   case 'p':
602     *v = scale(*v, units_per_inch, divisor*72);
603     break;
604   case 'P':
605     *v = scale(*v, units_per_inch, divisor*6);
606     break;
607   case 'm':
608     {
609       // Convert to hunits so that with -Tascii `m' behaves as in nroff.
610       hunits em = curenv->get_size();
611       *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor);
612     }
613     break;
614   case 'M':
615     {
616       hunits em = curenv->get_size();
617       *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor*100);
618     }
619     break;
620   case 'n':
621     {
622       // Convert to hunits so that with -Tascii `n' behaves as in nroff.
623       hunits en = curenv->get_size()/2;
624       *v = scale(*v, en.is_zero() ? hresolution : en.to_units(), divisor);
625     }
626     break;
627   case 'v':
628     *v = scale(*v, curenv->get_vertical_spacing().to_units(), divisor);
629     break;
630   case 's':
631     while (divisor > INT_MAX/(sizescale*72)) {
632       divisor /= 10;
633       *v /= 10;
634     }
635     *v = scale(*v, units_per_inch, divisor*sizescale*72);
636     break;
637   case 'z':
638     *v = scale(*v, sizescale, divisor);
639     break;
640   default:
641     assert(0);
642   }
643   if (do_next)
644     tok.next();
645   if (negative) {
646     if (*v == INT_MIN) {
647       error("numeric overflow");
648       return 0;
649     }
650     *v = -*v;
651   }
652   return 1;
653 }
654 
scale(units n,units x,units y)655 units scale(units n, units x, units y)
656 {
657   assert(x >= 0 && y > 0);
658   if (x == 0)
659     return 0;
660   if (n >= 0) {
661     if (n <= INT_MAX/x)
662       return (n*x)/y;
663   }
664   else {
665     if (-(unsigned)n <= -(unsigned)INT_MIN/x)
666       return (n*x)/y;
667   }
668   double res = n*double(x)/double(y);
669   if (res > INT_MAX) {
670     error("numeric overflow");
671     return INT_MAX;
672   }
673   else if (res < INT_MIN) {
674     error("numeric overflow");
675     return INT_MIN;
676   }
677   return int(res);
678 }
679 
vunits(units x)680 vunits::vunits(units x)
681 {
682   // don't depend on the rounding direction for division of negative integers
683   if (vresolution == 1)
684     n = x;
685   else
686     n = (x < 0
687 	 ? -((-x + vresolution/2 - 1)/vresolution)
688 	 : (x + vresolution/2 - 1)/vresolution);
689 }
690 
hunits(units x)691 hunits::hunits(units x)
692 {
693   // don't depend on the rounding direction for division of negative integers
694   if (hresolution == 1)
695     n = x;
696   else
697     n = (x < 0
698 	 ? -((-x + hresolution/2 - 1)/hresolution)
699 	 : (x + hresolution/2 - 1)/hresolution);
700 }
701