LCOV - code coverage report
Current view: top level - roff/troff - number.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 380 466 81.5 %
Date: 2026-01-16 17:51:41 Functions: 15 15 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright 1989-2020 Free Software Foundation, Inc.
       2             :              2021-2025 G. Branden Robinson
       3             : 
       4             :      Written by James Clark (jjc@jclark.com)
       5             : 
       6             : This file is part of groff, the GNU roff typesetting system.
       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 3 of the License, or
      11             : (at your option) any later 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
      19             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      20             : 
      21             : #ifdef HAVE_CONFIG_H
      22             : #include <config.h>
      23             : #endif
      24             : 
      25             : #include <stdckdint.h>
      26             : 
      27             : #include "troff.h" // units
      28             : #include "hvunits.h" // hunits, vunits
      29             : #include "mtsm.h" // state_set
      30             : #include "env.h" // curenv
      31             : #include "token.h" // tok
      32             : #include "div.h" // curdiv
      33             : 
      34             : const vunits V0; // zero in vertical units
      35             : const hunits H0; // zero in horizontal units
      36             : 
      37             : units hresolution = 1;
      38             : units vresolution = 1;
      39             : units units_per_inch;
      40             : int sizescale; // subdivisions per point
      41             : 
      42             : static bool is_valid_expression(units *u, int scaling_unit,
      43             :                                 bool is_parenthesized,
      44             :                                 bool is_mandatory = false);
      45             : static bool is_valid_expression_start();
      46             : 
      47      446546 : bool read_vunits(vunits *res, unsigned char si)
      48             : {
      49      446546 :   if (!is_valid_expression_start())
      50           0 :     return false;
      51             :   units x;
      52      446546 :   if (is_valid_expression(&x, si, false /* is_parenthesized */)) {
      53      446546 :     *res = vunits(x);
      54      446546 :     return true;
      55             :   }
      56             :   else
      57           0 :     return false;
      58             : }
      59             : 
      60      298847 : bool read_hunits(hunits *res, unsigned char si)
      61             : {
      62      298847 :   if (!is_valid_expression_start())
      63           0 :     return false;
      64             :   units x;
      65      298847 :   if (is_valid_expression(&x, si, false /* is_parenthesized */)) {
      66      298847 :     *res = hunits(x);
      67      298847 :     return true;
      68             :   }
      69             :   else
      70           0 :     return false;
      71             : }
      72             : 
      73     2856012 : bool read_measurement(units *res, unsigned char si, bool is_mandatory)
      74             : {
      75     2856012 :   if (!is_valid_expression_start())
      76           0 :     return false;
      77             :   units x;
      78     2856012 :   if (is_valid_expression(&x, si, false /* is_parenthesized */,
      79             :                           is_mandatory)) {
      80     2855749 :     *res = x;
      81     2855749 :     return true;
      82             :   }
      83             :   else
      84         263 :     return false;
      85             : }
      86             : 
      87      564336 : bool read_integer(int *res)
      88             : {
      89      564336 :   if (!is_valid_expression_start())
      90           0 :     return false;
      91             :   units x;
      92      564336 :   if (is_valid_expression(&x, 0 /* dimensionless */,
      93             :                           false /* is_parenthesized */)) {
      94      564336 :     *res = x;
      95      564336 :     return true;
      96             :   }
      97             :   else
      98           0 :     return false;
      99             : }
     100             : 
     101             : enum incr_number_result { INVALID, ASSIGN, INCREMENT, DECREMENT };
     102             : 
     103             : static incr_number_result get_incr_number(units *res, unsigned char);
     104             : 
     105       97764 : bool read_vunits(vunits *res, unsigned char si, vunits prev_value)
     106             : {
     107             :   units v;
     108             :   // Use a primitive temporary because having the ckd macros store to
     109             :   // &(res->n) requires `friend` access and produces wrong results.
     110             :   int i;
     111       97764 :   switch (get_incr_number(&v, si)) {
     112           1 :   case INVALID:
     113           1 :     return false;
     114       97408 :   case ASSIGN:
     115       97408 :     *res = v;
     116       97408 :     break;
     117         168 :   case INCREMENT:
     118         168 :     if (ckd_add(&i, prev_value.to_units(), v))
     119           0 :       warning(WARN_RANGE, "integer incrementation saturated");
     120         168 :     *res = i;
     121         168 :     break;
     122         187 :   case DECREMENT:
     123         187 :     if (ckd_sub(&i, prev_value.to_units(), v))
     124           0 :       warning(WARN_RANGE, "integer decrementation saturated");
     125         187 :     *res = i;
     126         187 :     break;
     127           0 :   default:
     128           0 :     assert(0 == "unhandled case in read_vunits()");
     129             :   }
     130       97763 :   return true;
     131             : }
     132             : 
     133      258705 : bool read_hunits(hunits *res, unsigned char si, hunits prev_value)
     134             : {
     135             :   units h;
     136             :   // Use a primitive temporary because having the ckd macros store to
     137             :   // &(res->n) requires `friend` access and produces wrong results.
     138             :   int i;
     139      258705 :   switch (get_incr_number(&h, si)) {
     140           0 :   case INVALID:
     141           0 :     return false;
     142      209503 :   case ASSIGN:
     143      209503 :     *res = h;
     144      209503 :     break;
     145       36284 :   case INCREMENT:
     146       36284 :     if (ckd_add(&i, prev_value.to_units(), h))
     147           0 :       warning(WARN_RANGE, "integer incrementation saturated");
     148       36284 :     *res = i;
     149       36284 :     break;
     150       12918 :   case DECREMENT:
     151       12918 :     if (ckd_sub(&i, prev_value.to_units(), h))
     152           0 :       warning(WARN_RANGE, "integer decrementation saturated");
     153       12918 :     *res = i;
     154       12918 :     break;
     155           0 :   default:
     156           0 :     assert(0 == "unhandled case in read_hunits()");
     157             :   }
     158      258705 :   return true;
     159             : }
     160             : 
     161             : // TODO: Default `prev_value` to 0.
     162     2349779 : bool read_measurement(units *res, unsigned char si, units prev_value)
     163             : {
     164             :   units u;
     165     2349779 :   switch (get_incr_number(&u, si)) {
     166           0 :   case INVALID:
     167           0 :     return false;
     168     2093684 :   case ASSIGN:
     169     2093684 :     *res = u;
     170     2093684 :     break;
     171      231391 :   case INCREMENT:
     172      231391 :     if (ckd_add(res, prev_value, u))
     173           1 :       warning(WARN_RANGE, "integer incrementation saturated");
     174      231391 :     break;
     175       24704 :   case DECREMENT:
     176       24704 :     if (ckd_sub(res, prev_value, u))
     177           0 :       warning(WARN_RANGE, "integer decrementation saturated");
     178       24704 :     break;
     179           0 :   default:
     180           0 :     assert(0 == "unhandled case in read_measurement()");
     181             :   }
     182     2349779 :   return true;
     183             : }
     184             : 
     185         233 : bool read_integer(int *res, int prev_value)
     186             : {
     187             :   units i;
     188         233 :   switch (get_incr_number(&i, 0)) {
     189           0 :   case INVALID:
     190           0 :     return false;
     191         233 :   case ASSIGN:
     192         233 :     *res = i;
     193         233 :     break;
     194           0 :   case INCREMENT:
     195           0 :     if (ckd_add(res, prev_value, i))
     196           0 :       warning(WARN_RANGE, "integer incrementation saturated");
     197           0 :     break;
     198           0 :   case DECREMENT:
     199           0 :     if (ckd_sub(res, prev_value, i))
     200           0 :       warning(WARN_RANGE, "integer decrementation saturated");
     201           0 :     break;
     202           0 :   default:
     203           0 :     assert(0 == "unhandled case in read_integer()");
     204             :   }
     205         233 :   return true;
     206             : }
     207             : 
     208             : 
     209     2706481 : static incr_number_result get_incr_number(units *res, unsigned char si)
     210             : {
     211     2706481 :   if (!is_valid_expression_start())
     212           0 :     return INVALID;
     213     2706481 :   incr_number_result result = ASSIGN;
     214     2706481 :   if (tok.ch() == int('+')) { // TODO: grochar
     215      267843 :     tok.next();
     216      267843 :     result = INCREMENT;
     217             :   }
     218     2438638 :   else if (tok.ch() == int('-')) { // TODO: grochar
     219       37809 :     tok.next();
     220       37809 :     result = DECREMENT;
     221             :   }
     222     2706481 :   if (is_valid_expression(res, si, false /* is_parenthesized */))
     223     2706480 :     return result;
     224             :   else
     225           1 :     return INVALID;
     226             : }
     227             : 
     228     6872222 : static bool is_valid_expression_start()
     229             : {
     230     6872222 :   tok.skip_spaces();
     231     6872222 :   if (tok.is_newline()) {
     232           0 :     warning(WARN_MISSING, "numeric expression missing");
     233           0 :     return false;
     234             :   }
     235     6872222 :   return true;
     236             : }
     237             : 
     238             : enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' };
     239             : 
     240             : static const char valid_scaling_units[] = "icfPmnpuvMsz";
     241             : 
     242             : static bool is_valid_term(units *u, int scaling_unit,
     243             :                           bool is_parenthesized, bool is_mandatory);
     244             : 
     245     9281822 : static bool is_valid_expression(units *u, int scaling_unit,
     246             :                                 bool is_parenthesized,
     247             :                                 bool is_mandatory)
     248             : {
     249     9281822 :   int result = is_valid_term(u, scaling_unit, is_parenthesized,
     250     9281822 :                              is_mandatory);
     251    12622199 :   while (result) {
     252    12621935 :     if (is_parenthesized)
     253     5377620 :       tok.skip_spaces();
     254    12621935 :     int op = tok.ch();// safely compares to char literals; TODO: grochar
     255    12621935 :     switch (op) {
     256     2057543 :     case int('+'): // TODO: grochar
     257             :     case int('-'): // TODO: grochar
     258             :     case int('/'): // TODO: grochar
     259             :     case int('*'): // TODO: grochar
     260             :     case int('%'): // TODO: grochar
     261             :     case int(':'): // TODO: grochar
     262             :     case int('&'): // TODO: grochar
     263     2057543 :       tok.next();
     264     2057543 :       break;
     265      436445 :     case int('>'): // TODO: grochar
     266      436445 :       tok.next();
     267      436445 :       if (tok.ch() == int('=')) { // TODO: grochar
     268      130125 :         tok.next();
     269      130125 :         op = OP_GEQ;
     270             :       }
     271      306320 :       else if (tok.ch() == int('?')) { // TODO: grochar
     272      158803 :         tok.next();
     273      158803 :         op = OP_MAX;
     274             :       }
     275      436445 :       break;
     276      623178 :     case int('<'): // TODO: grochar
     277      623178 :       tok.next();
     278      623178 :       if (tok.ch() == int('=')) { // TODO: grochar
     279      277180 :         tok.next();
     280      277180 :         op = OP_LEQ;
     281             :       }
     282      345998 :       else if (tok.ch() == int('?')) { // TODO: grochar
     283       57988 :         tok.next();
     284       57988 :         op = OP_MIN;
     285             :       }
     286      623178 :       break;
     287      223221 :     case int('='): // TODO: grochar
     288      223221 :       tok.next();
     289      223221 :       if (tok.ch() == int('=')) // TODO: grochar
     290       76684 :         tok.next();
     291      223221 :       break;
     292     9281548 :     default:
     293     9281558 :       return result;
     294             :     }
     295             :     units u2;
     296     3340387 :     if (!is_valid_term(&u2, scaling_unit, is_parenthesized,
     297             :                        is_mandatory))
     298          10 :       return false;
     299     3340377 :     switch (op) {
     300      288009 :     case int('<'): // TODO: grochar
     301      288009 :       *u = *u < u2;
     302      288009 :       break;
     303      147516 :     case int('>'): // TODO: grochar
     304      147516 :       *u = *u > u2;
     305      147516 :       break;
     306      277180 :     case OP_LEQ:
     307      277180 :       *u = *u <= u2;
     308      277180 :       break;
     309      130125 :     case OP_GEQ:
     310      130125 :       *u = *u >= u2;
     311      130125 :       break;
     312       57988 :     case OP_MIN:
     313       57988 :       if (*u > u2)
     314         184 :         *u = u2;
     315       57988 :       break;
     316      158803 :     case OP_MAX:
     317      158803 :       if (*u < u2)
     318       44924 :         *u = u2;
     319      158803 :       break;
     320      223220 :     case int('='): // TODO: grochar
     321      223220 :       *u = (*u == u2);
     322      223220 :       break;
     323       57299 :     case int('&'): // TODO: grochar
     324       57299 :       *u = (*u > 0) && (u2 > 0);
     325       57299 :       break;
     326        8235 :     case int(':'): // TODO: grochar
     327        8235 :       *u = (*u > 0) || (u2 > 0);
     328        8235 :       break;
     329      592344 :     case int('+'): // TODO: grochar
     330      592344 :       if (ckd_add(u, *u, u2)) {
     331           0 :         warning(WARN_RANGE, "integer addition saturated");
     332           0 :         return false;
     333             :       }
     334      592344 :       break;
     335      633135 :     case int('-'): // TODO: grochar
     336      633135 :       if (ckd_sub(u, *u, u2)) {
     337           0 :         warning(WARN_RANGE, "integer subtraction saturated");
     338           0 :         return false;
     339             :       }
     340      633135 :       break;
     341      436600 :     case int('*'): // TODO: grochar
     342      436600 :       if (ckd_mul(u, *u, u2)) {
     343           0 :         warning(WARN_RANGE, "integer multiplication saturated");
     344           0 :         return false;
     345             :       }
     346      436600 :       break;
     347      187710 :     case int('/'): // TODO: grochar
     348      187710 :       if (0 == u2) {
     349           0 :         error("division by zero");
     350           0 :         return false;
     351             :       }
     352      187710 :       *u /= u2;
     353      187710 :       break;
     354      142213 :     case int('%'): // TODO: grochar
     355      142213 :       if (0 == u2) {
     356           0 :         error("modulus by zero");
     357           0 :         return false;
     358             :       }
     359      142213 :       *u %= u2;
     360      142213 :       break;
     361           0 :     default:
     362           0 :       assert(0 == "unhandled case of operator");
     363             :     }
     364             :   }
     365         264 :   return result;
     366             : }
     367             : 
     368    12684531 : static bool is_valid_term(units *u, int scaling_unit,
     369             :                           bool is_parenthesized, bool is_mandatory)
     370             : {
     371    12684531 :   bool is_negative = false;
     372    12684531 :   bool is_overflowing = false;
     373    12684531 :   units saved_u = 0; // for use when reading an overlong number
     374             :   for (;;)
     375    15840261 :     if (is_parenthesized && tok.is_space())
     376     2864479 :       tok.next();
     377    12975782 :     else if (tok.ch() == int('+')) // TODO: grochar
     378         527 :       tok.next();
     379    12975255 :     else if (tok.ch() == int('-')) { // TODO: grochar
     380      290724 :       tok.next();
     381      290724 :       is_negative = !is_negative;
     382             :     }
     383             :     else
     384    12684531 :       break;
     385    12684531 :   int c = tok.ch(); // safely compares to char literals; TODO: grochar
     386    12684531 :   switch (c) {
     387       62322 :   case int('|'): // TODO: grochar
     388             :     // | is not restricted to the outermost level
     389             :     // tbl uses this
     390       62322 :     tok.next();
     391       62322 :     if (!is_valid_term(u, scaling_unit, is_parenthesized, is_mandatory))
     392     2472181 :       return false;
     393             :     int tmp, position;
     394       62321 :     position = (('v' == scaling_unit)
     395       62321 :                 ? curdiv->get_vertical_position().to_units()
     396       15412 :                 : curenv->get_input_line_position().to_units());
     397       62321 :     if (ckd_sub(&tmp, *u, position)) {
     398           0 :       tmp = INT_MAX;
     399           0 :       warning(WARN_RANGE, "integer value saturated");
     400             :     }
     401       62321 :     *u = tmp;
     402       62321 :     if (is_negative)
     403           0 :       *u = -*u;
     404       62321 :     return true;
     405     2409609 :   case int('('): // TODO: grochar
     406     2409609 :     tok.next();
     407     2409609 :     c = tok.ch();
     408     2409609 :     if (int(')') == c) { // TODO: grochar
     409           0 :       if (is_mandatory)
     410           0 :         return false;
     411           0 :       warning(WARN_SYNTAX, "empty parentheses");
     412           0 :       tok.next();
     413           0 :       *u = 0;
     414           0 :       return true;
     415             :     }
     416     2409609 :     else if ((c != 0) && (strchr(valid_scaling_units, char(c)) != 0)) {
     417       27967 :       tok.next();
     418       27967 :       if (tok.ch() == int(';')) { // TODO: grochar
     419       27958 :         tok.next();
     420       27958 :         scaling_unit = c;
     421             :       }
     422             :       else {
     423           9 :         error("expected ';' after scaling unit, got %1",
     424           9 :               tok.description());
     425           9 :         return false;
     426             :       }
     427             :     }
     428     2381642 :     else if (';' == c) {
     429         127 :       scaling_unit = 0;
     430         127 :       tok.next();
     431             :     }
     432     2409600 :     if (!is_valid_expression(u, scaling_unit,
     433             :                              true /* is_parenthesized */, is_mandatory))
     434          10 :       return false;
     435     2409590 :     tok.skip_spaces();
     436     2409590 :     if (tok.ch() != int(')')) { // TODO: grochar
     437          15 :       if (is_mandatory)
     438          15 :         return false;
     439           0 :       warning(WARN_SYNTAX, "expected ')', got %1", tok.description());
     440             :     }
     441             :     else
     442     2409575 :       tok.next();
     443     2409575 :     if (is_negative) {
     444             :       // Why?  Consider -(INT_MIN) in two's complement.
     445         645 :       if (ckd_mul(u, *u, -1))
     446           0 :         warning(WARN_RANGE, "integer multiplication saturated");
     447             :     }
     448     2409575 :     return true;
     449      137489 :   case int('.'): // TODO: grochar
     450      137489 :     *u = 0;
     451      137489 :     break;
     452    10074861 :   case int('0'): // TODO: grochar
     453             :   case int('1'): // TODO: grochar
     454             :   case int('2'): // TODO: grochar
     455             :   case int('3'): // TODO: grochar
     456             :   case int('4'): // TODO: grochar
     457             :   case int('5'): // TODO: grochar
     458             :   case int('6'): // TODO: grochar
     459             :   case int('7'): // TODO: grochar
     460             :   case int('8'): // TODO: grochar
     461             :   case int('9'): // TODO: grochar
     462    10074861 :     *u = 0;
     463    12662192 :     do {
     464    22737053 :       if (!is_overflowing) {
     465    22736993 :           saved_u = *u;
     466    22736993 :           if (ckd_mul(u, *u, 10))
     467           6 :             is_overflowing = true;
     468    22736993 :           if (ckd_add(u, *u, c - '0'))
     469           0 :             is_overflowing = true;
     470    22736993 :           if (is_overflowing)
     471           6 :             *u = saved_u;
     472             :       }
     473             :       // No `else` on overflow; consume and discard further digits.
     474    22737053 :       tok.next();
     475    22737053 :       c = tok.ch();
     476    22737053 :     } while (csdigit(c));
     477    10074861 :     if (is_overflowing)
     478           6 :       warning(WARN_RANGE, "integer value saturated");
     479    10074861 :     break;
     480          10 :   case int('/'): // TODO: grochar
     481             :   case int('*'): // TODO: grochar
     482             :   case int('%'): // TODO: grochar
     483             :   case int(':'): // TODO: grochar
     484             :   case int('&'): // TODO: grochar
     485             :   case int('>'): // TODO: grochar
     486             :   case int('<'): // TODO: grochar
     487             :   case int('='): // TODO: grochar
     488          10 :     warning(WARN_SYNTAX, "empty left operand to '%1' operator",
     489          10 :             char(c));
     490          10 :     *u = 0;
     491          10 :     return !is_mandatory;
     492         240 :   default:
     493         240 :     error("ignoring invalid numeric expression starting with %1",
     494         240 :           tok.description());
     495         240 :     return false;
     496             :   }
     497    10212350 :   int divisor = 1;
     498    10212350 :   if (tok.ch() == int('.')) { // TODO: grochar
     499      292864 :     tok.next();
     500             :     for (;;) {
     501      803664 :       c = tok.ch();
     502      803664 :       if (!csdigit(c))
     503      292864 :         break;
     504             :       // we may multiply the divisor by 254 later on
     505      510800 :       if ((divisor <= (INT_MAX / 2540)) && (*u <= ((INT_MAX - 9) / 10)))
     506             :       {
     507      510800 :         *u *= 10;
     508      510800 :         *u += c - '0';
     509      510800 :         divisor *= 10;
     510             :       }
     511      510800 :       tok.next();
     512             :     }
     513             :   }
     514    10212350 :   int si = scaling_unit;
     515    10212350 :   bool do_next = false;
     516    10212350 :   if (((c = tok.ch()) != 0)
     517    10212350 :       && (strchr(valid_scaling_units, c) != 0 /* nullptr */)) {
     518     1946788 :     switch (scaling_unit) {
     519         123 :     case int(0): // TODO: grochar; null character, not digit zero
     520             :       // We know it's a recognized scaling unit because it matched the
     521             :       // `strchr()` above, so we don't use `tok.description()`.
     522         123 :       warning(WARN_SCALE, "a scaling unit is not valid in this context"
     523         123 :               " (got '%1')", char(c));
     524         123 :       break;
     525        1020 :     case int('f'): // TODO: grochar
     526        1020 :       if (c != int('f') && c != int('u')) { // TODO: grochar
     527           0 :         warning(WARN_SCALE, "'%1' scaling unit invalid in this context;"
     528           0 :                 " use 'f' or 'u'", char(c));
     529           0 :         break;
     530             :       }
     531        1020 :       si = c;
     532        1020 :       break;
     533      144970 :     case int('z'): // TODO: grochar
     534      144970 :       if (c != int('u')
     535       65774 :           && c != int('z')
     536           0 :           && c != int('p')
     537           0 :           && c != int('s')) { // TODO: grochar
     538           0 :         warning(WARN_SCALE, "'%1' scaling unit invalid in this context;"
     539           0 :                 " use 'z', 'p', 's', or 'u'", char(c));
     540           0 :         break;
     541             :       }
     542      144970 :       si = c;
     543      144970 :       break;
     544      350770 :     case int('u'): // TODO: grochar
     545      350770 :       si = c;
     546      350770 :       break;
     547     1449905 :     default:
     548     1449905 :       if (int('z') == c) { // TODO: grochar
     549           0 :         warning(WARN_SCALE, "'z' scaling unit invalid in this context");
     550           0 :         break;
     551             :       }
     552     1449905 :       si = c;
     553     1449905 :       break;
     554             :     }
     555             :     // Don't do tok.next() here because the next token might be \s,
     556             :     // which would affect the interpretation of 'm'.
     557     1946788 :     do_next = true;
     558             :   }
     559    10212350 :   switch (si) {
     560       36723 :   case int('i'): // TODO: grochar
     561       36723 :     *u = scale(*u, units_per_inch, divisor);
     562       36723 :     break;
     563        1054 :   case int('c'): // TODO: grochar
     564        1054 :     *u = scale(*u, (units_per_inch * 100), (divisor * 254));
     565        1054 :     break;
     566     9277502 :   case int(0): // TODO: grochar; null character, not digit zero
     567             :   case int('u'): // TODO: grochar
     568     9277502 :     if (divisor != 1)
     569         100 :       *u /= divisor;
     570     9277502 :     break;
     571        2340 :   case int('f'): // TODO: grochar
     572        2340 :     *u = scale(*u, 65536, divisor);
     573        2340 :     break;
     574      139639 :   case int('p'): // TODO: grochar
     575      139639 :     *u = scale(*u, units_per_inch, divisor * 72);
     576      139639 :     break;
     577         366 :   case int('P'): // TODO: grochar
     578         366 :     *u = scale(*u, units_per_inch, divisor * 6);
     579         366 :     break;
     580      125584 :   case int('m'): // TODO: grochar
     581             :     {
     582             :       // Convert to hunits so that with -Tascii 'm' behaves as in nroff.
     583      125584 :       hunits em = curenv->get_size();
     584      125584 :       *u = scale(*u, em.is_zero() ? hresolution : em.to_units(),
     585             :                  divisor);
     586             :     }
     587      125584 :     break;
     588        1912 :   case int('M'): // TODO: grochar
     589             :     {
     590        1912 :       hunits em = curenv->get_size();
     591        1912 :       *u = scale(*u, em.is_zero() ? hresolution : em.to_units(),
     592             :                  (divisor * 100));
     593             :     }
     594        1912 :     break;
     595       57048 :   case int('n'): // TODO: grochar
     596             :     {
     597             :       // Convert to hunits so that with -Tascii 'n' behaves as in nroff.
     598       57048 :       hunits en = curenv->get_size() / 2;
     599       57048 :       *u = scale(*u, en.is_zero() ? hresolution : en.to_units(),
     600             :                  divisor);
     601             :     }
     602       57048 :     break;
     603      331556 :   case int('v'): // TODO: grochar
     604      331556 :     *u = scale(*u, curenv->get_vertical_spacing().to_units(), divisor);
     605      331556 :     break;
     606          81 :   case int('s'): // TODO: grochar
     607          81 :     while (divisor > INT_MAX / (sizescale * 72)) {
     608           0 :       divisor /= 10;
     609           0 :       *u /= 10;
     610             :     }
     611          81 :     *u = scale(*u, units_per_inch, divisor * sizescale * 72);
     612          81 :     break;
     613      238545 :   case int('z'): // TODO: grochar
     614      238545 :     *u = scale(*u, sizescale, divisor);
     615      238545 :     break;
     616           0 :   default:
     617           0 :     assert(0 == "unhandled case of scaling unit");
     618             :   }
     619    10212350 :   if (do_next)
     620     1946788 :     tok.next();
     621    10212350 :   if (is_negative) {
     622      289228 :     if (ckd_mul(u, *u, -1))
     623           0 :       warning(WARN_RANGE, "integer multiplication saturated");
     624             :   }
     625    10212350 :   return true;
     626             : }
     627             : 
     628     2728333 : units scale(units n, units x, units y)
     629             : {
     630     2728333 :   assert(x >= 0);
     631     2728333 :   assert(y > 0);
     632     2728333 :   if (0 == x)
     633       53924 :     return 0;
     634     2674409 :   if (n >= 0) {
     635     2674409 :     if (n <= (INT_MAX / x))
     636     2674388 :       return ((n * x) / y);
     637             :   }
     638             :   else {
     639           0 :     if ((-(unsigned int)(n)) <= ((-(unsigned int)(INT_MIN)) / x))
     640           0 :       return ((n * x) / y);
     641             :   }
     642          21 :   double res = n * double(x) / double(y);
     643          21 :   if (res > INT_MAX) {
     644           4 :     warning(WARN_RANGE, "integer value saturated");
     645           4 :     return INT_MAX;
     646             :   }
     647          17 :   else if (res < INT_MIN) {
     648           0 :     warning(WARN_RANGE, "integer value saturated");
     649           0 :     return INT_MIN;
     650             :   }
     651          17 :   return units(res);
     652             : }
     653             : 
     654     6809676 : vunits::vunits(units x)
     655             : {
     656     6809676 :   if (1 == vresolution)
     657     4292735 :     n = x;
     658             :   else {
     659             :     // Don't depend on rounding direction when dividing neg integers.
     660     2516941 :     int vcrement = (vresolution / 2) - 1;
     661     2516941 :     bool is_overflowing = false;
     662     2516941 :     if (x < 0) {
     663        8773 :       if (ckd_add(&n, -x, vcrement))
     664           0 :         is_overflowing = true;
     665        8773 :       n = -n;
     666             :     }
     667             :     else {
     668     2508168 :       if (ckd_add(&n, x, vcrement))
     669           1 :         is_overflowing = true;
     670             :     }
     671     2516941 :     if (is_overflowing) {
     672           1 :       if (x < 0) {
     673           0 :         warning(WARN_RANGE, "integer value saturated");
     674           0 :         n = INT_MIN;
     675             :       }
     676             :       else {
     677           1 :         warning(WARN_RANGE, "integer value saturated");
     678           1 :         n = INT_MAX;
     679             :       }
     680             :     }
     681     2516941 :     n /= vresolution;
     682             :   }
     683     6809676 : }
     684             : 
     685    77273865 : hunits::hunits(units x)
     686             : {
     687    77273865 :   if (1 == hresolution)
     688    24491366 :     n = x;
     689             :   else {
     690             :     // Don't depend on rounding direction when dividing neg integers.
     691    52782499 :     int hcrement = (hresolution / 2) - 1;
     692    52782499 :     bool is_overflowing = false;
     693    52782499 :     if (x < 0) {
     694         518 :       if (ckd_add(&n, -x, hcrement))
     695           0 :         is_overflowing = true;
     696         518 :       n = -n;
     697             :     }
     698             :     else {
     699    52781981 :       if (ckd_add(&n, x, hcrement))
     700           1 :         is_overflowing = true;
     701             :     }
     702    52782499 :     if (is_overflowing) {
     703           1 :       if (x < 0) {
     704           0 :         warning(WARN_RANGE, "integer value saturated");
     705           0 :         n = INT_MIN;
     706             :       }
     707             :       else {
     708           1 :         warning(WARN_RANGE, "integer value saturated");
     709           1 :         n = INT_MAX;
     710             :       }
     711             :     }
     712    52782499 :     n /= hresolution;
     713             :   }
     714    77273865 : }
     715             : 
     716             : // Local Variables:
     717             : // fill-column: 72
     718             : // mode: C++
     719             : // End:
     720             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14