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