Coverage Report

Created: 2026-03-12 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gettext/gettext-runtime/intl/eval-plural.h
Line
Count
Source
1
/* Plural expression evaluation.
2
   Copyright (C) 2000-2026 Free Software Foundation, Inc.
3
4
   This program is free software: you can redistribute it and/or modify
5
   it under the terms of the GNU Lesser General Public License as published by
6
   the Free Software Foundation; either version 2.1 of the License, or
7
   (at your option) any later version.
8
9
   This program is distributed in the hope that it will be useful,
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
   GNU Lesser General Public License for more details.
13
14
   You should have received a copy of the GNU Lesser General Public License
15
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16
17
/* Written by Ulrich Drepper and Bruno Haible.  */
18
19
#ifndef STATIC
20
#define STATIC static
21
#endif
22
23
/* While the bison parser is able to support expressions of a maximum depth
24
   of YYMAXDEPTH = 10000, the runtime evaluation of a parsed plural expression
25
   has a smaller maximum recursion depth.
26
   If we did not limit the recursion depth, a program that just invokes
27
   ngettext() on a thread other than the main thread could get a crash by
28
   stack overflow in the following circumstances:
29
     - On systems with glibc, after the stack size has been reduced,
30
       e.g. on x86_64 systems after "ulimit -s 260".
31
       This stack size is only sufficient for ca. 3919 recursions.
32
       Cf. <https://unix.stackexchange.com/questions/620720/>
33
     - On systems with musl libc, because there the thread stack size (for a
34
       thread other than the main thread) by default is only 128 KB, see
35
       <https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size>.
36
     - On AIX 7 systems, because there the thread stack size (for a thread
37
       other than the main thread) by default is only 96 KB, see
38
       <https://www.ibm.com/docs/en/aix/7.1?topic=programming-threads-library-options>.
39
       This stack size is only sufficient for between 887 and 1363 recursions,
40
       depending on the compiler and compiler optimization options.
41
   A maximum depth of 100 is a large enough for all practical needs
42
   and also small enough to avoid stack overflow even with small thread stack
43
   sizes.  */
44
#ifndef EVAL_MAXDEPTH
45
0
# define EVAL_MAXDEPTH 100
46
#endif
47
48
/* A shorthand that denotes a successful evaluation result with a value V.  */
49
0
#define OK(v) (struct eval_result) { .status = PE_OK, .value = (v) }
50
51
/* Evaluates a plural expression PEXP for n=N, up to ALLOWED_DEPTH.  */
52
static struct eval_result
53
plural_eval_recurse (const struct expression *pexp, unsigned long int n,
54
         unsigned int allowed_depth)
55
0
{
56
0
  if (allowed_depth == 0)
57
    /* The allowed recursion depth is exhausted.  */
58
0
    return (struct eval_result) { .status = PE_STACKOVF };
59
0
  allowed_depth--;
60
61
0
  switch (pexp->nargs)
62
0
    {
63
0
    case 0:
64
0
      switch (pexp->operation)
65
0
  {
66
0
  case var:
67
0
    return OK (n);
68
0
  case num:
69
0
    return OK (pexp->val.num);
70
0
  default:
71
0
    break;
72
0
  }
73
      /* NOTREACHED */
74
0
      break;
75
0
    case 1:
76
0
      {
77
  /* pexp->operation must be lnot.  */
78
0
  struct eval_result arg =
79
0
    plural_eval_recurse (pexp->val.args[0], n, allowed_depth);
80
0
  if (arg.status != PE_OK)
81
0
    return arg;
82
0
  return OK (! arg.value);
83
0
      }
84
0
    case 2:
85
0
      {
86
0
  struct eval_result leftarg =
87
0
    plural_eval_recurse (pexp->val.args[0], n, allowed_depth);
88
0
  if (leftarg.status != PE_OK)
89
0
    return leftarg;
90
0
  if (pexp->operation == lor)
91
0
    {
92
0
      if (leftarg.value)
93
0
        return OK (1);
94
0
      struct eval_result rightarg =
95
0
        plural_eval_recurse (pexp->val.args[1], n, allowed_depth);
96
0
      if (rightarg.status != PE_OK)
97
0
        return rightarg;
98
0
      return OK (rightarg.value ? 1 : 0);
99
0
    }
100
0
  else if (pexp->operation == land)
101
0
    {
102
0
      if (!leftarg.value)
103
0
        return OK (0);
104
0
      struct eval_result rightarg =
105
0
        plural_eval_recurse (pexp->val.args[1], n, allowed_depth);
106
0
      if (rightarg.status != PE_OK)
107
0
        return rightarg;
108
0
      return OK (rightarg.value ? 1 : 0);
109
0
    }
110
0
  else
111
0
    {
112
0
      struct eval_result rightarg =
113
0
        plural_eval_recurse (pexp->val.args[1], n, allowed_depth);
114
0
      if (rightarg.status != PE_OK)
115
0
        return rightarg;
116
117
0
      switch (pexp->operation)
118
0
        {
119
0
        case mult:
120
0
    return OK (leftarg.value * rightarg.value);
121
0
        case divide:
122
0
    if (rightarg.value == 0)
123
0
      return (struct eval_result) { .status = PE_INTDIV };
124
0
    return OK (leftarg.value / rightarg.value);
125
0
        case module:
126
0
    if (rightarg.value == 0)
127
0
      return (struct eval_result) { .status = PE_INTDIV };
128
0
    return OK (leftarg.value % rightarg.value);
129
0
        case plus:
130
0
    return OK (leftarg.value + rightarg.value);
131
0
        case minus:
132
0
    return OK (leftarg.value - rightarg.value);
133
0
        case less_than:
134
0
    return OK (leftarg.value < rightarg.value);
135
0
        case greater_than:
136
0
    return OK (leftarg.value > rightarg.value);
137
0
        case less_or_equal:
138
0
    return OK (leftarg.value <= rightarg.value);
139
0
        case greater_or_equal:
140
0
    return OK (leftarg.value >= rightarg.value);
141
0
        case equal:
142
0
    return OK (leftarg.value == rightarg.value);
143
0
        case not_equal:
144
0
    return OK (leftarg.value != rightarg.value);
145
0
        default:
146
0
    break;
147
0
        }
148
0
    }
149
  /* NOTREACHED */
150
0
  break;
151
0
      }
152
0
    case 3:
153
0
      {
154
  /* pexp->operation must be qmop.  */
155
0
  struct eval_result boolarg =
156
0
    plural_eval_recurse (pexp->val.args[0], n, allowed_depth);
157
0
  if (boolarg.status != PE_OK)
158
0
    return boolarg;
159
0
  return plural_eval_recurse (pexp->val.args[boolarg.value ? 1 : 2], n,
160
0
            allowed_depth);
161
0
      }
162
0
    }
163
  /* NOTREACHED */
164
0
  return (struct eval_result) { .status = PE_ASSERT };
165
0
}
166
167
/* Evaluates a plural expression PEXP for n=N.  */
168
STATIC
169
struct eval_result
170
plural_eval (const struct expression *pexp, unsigned long int n)
171
0
{
172
0
  return plural_eval_recurse (pexp, n, EVAL_MAXDEPTH);
173
0
}
174
175
#undef OK