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