PageRenderTime 47ms CodeModel.GetById 5ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/groff/src/roff/troff/number.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 698 lines | 640 code | 28 blank | 30 comment | 157 complexity | 146b9517f2017b14417908eeac07a7b3 MD5 | raw file
  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
  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, 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
 31vunits V0;
 32hunits H0;
 33
 34int hresolution = 1;
 35int vresolution = 1;
 36int units_per_inch;
 37int sizescale;
 38
 39static int parse_expr(units *v, int scale_indicator,
 40		      int parenthesised, int rigid = 0);
 41static int start_number();
 42
 43int 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
 56int 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
 71int 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
 84int 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
 97int 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
110enum incr_number_result { BAD, ABSOLUTE, INCREMENT, DECREMENT };
111
112static incr_number_result get_incr_number(units *res, unsigned char);
113
114int 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
135int 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
156int 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
177int 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
199static 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
218static 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
237enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' };
238
239#define SCALE_INDICATOR_CHARS "icfPmnpuvMsz"
240
241static int parse_term(units *v, int scale_indicator,
242		      int parenthesised, int rigid);
243
244static 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
400static 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
653units 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
678vunits::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
689hunits::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}