number.cpp revision 114402
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
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with groff; see the file COPYING.  If not, write to the Free Software
20Foundation, 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
30vunits V0;
31hunits H0;
32
33int hresolution = 1;
34int vresolution = 1;
35int units_per_inch;
36int sizescale;
37
38static int parse_expr(units *v, int scale_indicator,
39		      int parenthesised, int rigid = 0);
40static int start_number();
41
42int 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
55int 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
70int 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
83int 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
96int 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
109enum incr_number_result { BAD, ABSOLUTE, INCREMENT, DECREMENT };
110
111static incr_number_result get_incr_number(units *res, unsigned char);
112
113int 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
134int 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
155int 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
176int 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
198static 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
217static 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
236enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' };
237
238#define SCALE_INDICATOR_CHARS "icfPmnpuvMsz"
239
240static int parse_term(units *v, int scale_indicator,
241		      int parenthesised, int rigid);
242
243static 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
399static 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
652units 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
677vunits::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
688hunits::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}
698