xref: /386bsd/usr/src/usr.bin/groff/troff/number.cc (revision a2142627)
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4 
5 This file is part of groff.
6 
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.
11 
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.
16 
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. */
20 
21 
22 #include "troff.h"
23 #include "symbol.h"
24 #include "hvunits.h"
25 #include "env.h"
26 #include "token.h"
27 #include "div.h"
28 
29 vunits V0;
30 hunits H0;
31 
32 int hresolution = 1;
33 int vresolution = 1;
34 int units_per_inch;
35 int sizescale;
36 
37 static int parse_expr(units *v, int scale_indicator, int parenthesised);
38 static int start_number();
39 
get_vunits(vunits * res,unsigned char si)40 int get_vunits(vunits *res, unsigned char si)
41 {
42   if (!start_number())
43     return 0;
44   units x;
45   if (parse_expr(&x, si, 0)) {
46     *res = vunits(x);
47     return 1;
48   }
49   else
50     return 0;
51 }
52 
get_hunits(hunits * res,unsigned char si)53 int get_hunits(hunits *res, unsigned char si)
54 {
55   if (!start_number())
56     return 0;
57   units x;
58   if (parse_expr(&x, si, 0)) {
59     *res = hunits(x);
60     return 1;
61   }
62   else
63     return 0;
64 }
65 
get_number(units * res,unsigned char si)66 int get_number(units *res, unsigned char si)
67 {
68   if (!start_number())
69     return 0;
70   units x;
71   if (parse_expr(&x, si, 0)) {
72     *res = x;
73     return 1;
74   }
75   else
76     return 0;
77 }
78 
get_integer(int * res)79 int get_integer(int *res)
80 {
81   if (!start_number())
82     return 0;
83   units x;
84   if (parse_expr(&x, 0, 0)) {
85     *res = x;
86     return 1;
87   }
88   else
89     return 0;
90 }
91 
92 enum incr_number_result { BAD, ABSOLUTE, INCREMENT, DECREMENT };
93 
94 static incr_number_result get_incr_number(units *res, unsigned char);
95 
get_vunits(vunits * res,unsigned char si,vunits prev_value)96 int get_vunits(vunits *res, unsigned char si, vunits prev_value)
97 {
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);
113   }
114   return 1;
115 }
116 
get_hunits(hunits * res,unsigned char si,hunits prev_value)117 int get_hunits(hunits *res, unsigned char si, hunits prev_value)
118 {
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);
134   }
135   return 1;
136 }
137 
get_number(units * res,unsigned char si,units prev_value)138 int get_number(units *res, unsigned char si, units prev_value)
139 {
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);
155   }
156   return 1;
157 }
158 
get_integer(int * res,int prev_value)159 int get_integer(int *res, int prev_value)
160 {
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);
176   }
177   return 1;
178 }
179 
180 
get_incr_number(units * res,unsigned char si)181 static incr_number_result get_incr_number(units *res, unsigned char si)
182 {
183   if (!start_number())
184     return BAD;
185   incr_number_result result = ABSOLUTE;
186   if (tok.ch() == '+') {
187     tok.next();
188     result = INCREMENT;
189   }
190   else if (tok.ch() == '-') {
191     tok.next();
192     result = DECREMENT;
193   }
194   if (parse_expr(res, si, 0))
195     return result;
196   else
197     return BAD;
198 }
199 
start_number()200 static int start_number()
201 {
202   while (tok.space())
203     tok.next();
204   if (tok.newline()) {
205     warning(WARN_MISSING, "missing number");
206     return 0;
207   }
208   if (tok.tab()) {
209     warning(WARN_TAB, "tab character where number expected");
210     return 0;
211   }
212   if (tok.right_brace()) {
213     warning(WARN_RIGHT_BRACE, "`\\}' where number expected");
214     return 0;
215   }
216   return 1;
217 }
218 
219 enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' };
220 
221 #define SCALE_INDICATOR_CHARS "icPmnpuvMsz"
222 
223 static int parse_term(units *v, int scale_indicator, int parenthesised);
224 
parse_expr(units * v,int scale_indicator,int parenthesised)225 static int parse_expr(units *v, int scale_indicator, int parenthesised)
226 {
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;
247       }
248       else if (tok.ch() == '?') {
249 	tok.next();
250 	op = OP_MAX;
251       }
252       break;
253     case '<':
254       tok.next();
255       if (tok.ch() == '=') {
256 	tok.next();
257 	op = OP_LEQ;
258       }
259       else if (tok.ch() == '?') {
260 	tok.next();
261 	op = OP_MIN;
262       }
263       break;
264     case '=':
265       tok.next();
266       if (tok.ch() == '=')
267 	tok.next();
268       break;
269     default:
270       return result;
271     }
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;
309       }
310       else if (v2 > 0) {
311 	if (*v > INT_MAX - v2)
312 	  overflow = 1;
313       }
314       if (overflow) {
315 	error("addition overflow");
316 	return 0;
317       }
318       *v += v2;
319       break;
320     case '-':
321       if (v2 < 0) {
322 	if (*v > INT_MAX + v2)
323 	  overflow = 1;
324       }
325       else if (v2 > 0) {
326 	if (*v < INT_MIN + v2)
327 	  overflow = 1;
328       }
329       if (overflow) {
330 	error("subtraction overflow");
331 	return 0;
332       }
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;
340 	}
341 	else if (-(unsigned)*v > INT_MAX / -(unsigned)v2)
342 	  overflow = 1;
343       }
344       else if (v2 > 0) {
345 	if (*v > 0) {
346 	  if (*v > INT_MAX / v2)
347 	    overflow = 1;
348 	}
349 	else if (-(unsigned)*v > -(unsigned)INT_MIN / v2)
350 	  overflow = 1;
351       }
352       if (overflow) {
353 	error("multiplication overflow");
354 	return 0;
355       }
356       *v *= v2;
357       break;
358     case '/':
359       if (v2 == 0) {
360 	error("division by zero");
361 	return 0;
362       }
363       *v /= v2;
364       break;
365     case '%':
366       if (v2 == 0) {
367 	error("modulus by zero");
368 	return 0;
369       }
370       *v %= v2;
371       break;
372     default:
373       assert(0);
374     }
375   }
376   return result;
377 }
378 
parse_term(units * v,int scale_indicator,int parenthesised)379 static int parse_term(units *v, int scale_indicator, int parenthesised)
380 {
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;
390     }
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;
409       }
410     }
411     else {
412       if (*v > INT_MAX + tem) {
413 	error("numeric overflow");
414 	return 0;
415       }
416     }
417     *v -= tem;
418     if (negative) {
419       if (*v == INT_MIN) {
420 	error("numeric overflow");
421 	return 0;
422       }
423       *v = -*v;
424     }
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;
434     }
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;
440       }
441       else {
442 	error("expected `;' after scale-indicator (got %1)",
443 	      tok.description());
444 	return 0;
445       }
446     }
447     else if (c == ';') {
448       scale_indicator = 0;
449       tok.next();
450     }
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());
456     }
457     else
458       tok.next();
459     if (negative) {
460       if (*v == INT_MIN) {
461 	error("numeric overflow");
462 	return 0;
463       }
464       *v = -*v;
465     }
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;
485       }
486       *v *= 10;
487       if (*v > INT_MAX - (int(c) - '0')) {
488 	error("numeric overflow");
489 	return 0;
490       }
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;
511   }
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;
524       }
525       tok.next();
526     }
527   }
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;
537       }
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;
550       }
551       si = c;
552       break;
553     }
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;
557   }
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':
577     {
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);
581     }
582     break;
583   case 'M':
584     {
585       hunits em = curenv->get_size();
586       *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor*100);
587     }
588     break;
589   case 'n':
590     {
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);
594     }
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;
603     }
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);
611   }
612   if (do_next)
613     tok.next();
614   if (negative) {
615     if (*v == INT_MIN) {
616       error("numeric overflow");
617       return 0;
618     }
619     *v = -*v;
620   }
621   return 1;
622 }
623 
scale(units n,units x,units y)624 units scale(units n, units x, units y)
625 {
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;
632   }
633   else {
634     if (-(unsigned)n <= -(unsigned)INT_MIN/x)
635       return (n*x)/y;
636   }
637   double res = n*double(x)/double(y);
638   if (res > INT_MAX) {
639     error("numeric overflow");
640     return INT_MAX;
641   }
642   else if (res < INT_MIN) {
643     error("numeric overflow");
644     return INT_MIN;
645   }
646   return int(res);
647 }
648 
vunits(units x)649 vunits::vunits(units x)
650 {
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);
658 }
659 
hunits(units x)660 hunits::hunits(units x)
661 {
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);
669 }
670