1/*	$NetBSD$	*/
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
8This file is part of groff.
9
10groff is free software; you can redistribute it and/or modify it under
11the terms of the GNU General Public License as published by the Free
12Software Foundation; either version 2, or (at your option) any later
13version.
14
15groff is distributed in the hope that it will be useful, but WITHOUT ANY
16WARRANTY; without even the implied warranty of MERCHANTABILITY or
17FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18for more details.
19
20You should have received a copy of the GNU General Public License along
21with groff; see the file COPYING.  If not, write to the Free Software
22Foundation, 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
33vunits V0;
34hunits H0;
35
36int hresolution = 1;
37int vresolution = 1;
38int units_per_inch;
39int sizescale;
40
41static int parse_expr(units *v, int scale_indicator,
42		      int parenthesised, int rigid = 0);
43static int start_number();
44
45int 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
58int 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
73int 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
86int 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
99int 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
112enum incr_number_result { BAD, ABSOLUTE, INCREMENT, DECREMENT };
113
114static incr_number_result get_incr_number(units *res, unsigned char);
115
116int 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
137int 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
158int 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
179int 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
201static 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
220static 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
239enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' };
240
241#define SCALE_INDICATOR_CHARS "icfPmnpuvMsz"
242
243static int parse_term(units *v, int scale_indicator,
244		      int parenthesised, int rigid);
245
246static 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
402static 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
655units 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
680vunits::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
691hunits::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