get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.1/patches/2230368/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2230368,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2230368/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/glibc/patch/20260429170428.1743554-1-avinal.xlvii@gmail.com/",
    "project": {
        "id": 41,
        "url": "http://patchwork.ozlabs.org/api/1.1/projects/41/?format=api",
        "name": "GNU C Library",
        "link_name": "glibc",
        "list_id": "libc-alpha.sourceware.org",
        "list_email": "libc-alpha@sourceware.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260429170428.1743554-1-avinal.xlvii@gmail.com>",
    "date": "2026-04-29T17:04:28",
    "name": "intl: Import plural expression hardening from GNU gettext",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "163fa8dfe860eb34f8d97497da7e194801f8581d",
    "submitter": {
        "id": 88549,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/88549/?format=api",
        "name": "Avinal Kumar",
        "email": "avinal.xlvii@gmail.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/glibc/patch/20260429170428.1743554-1-avinal.xlvii@gmail.com/mbox/",
    "series": [
        {
            "id": 502112,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/502112/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/glibc/list/?series=502112",
            "date": "2026-04-29T17:04:28",
            "name": "intl: Import plural expression hardening from GNU gettext",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/502112/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2230368/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2230368/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "libc-alpha@sourceware.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@legolas.ozlabs.org",
            "libc-alpha@sourceware.org"
        ],
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=odVovhkX;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=38.145.34.32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)",
            "sourceware.org;\n\tdkim=pass (2048-bit key,\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=odVovhkX",
            "sourceware.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com",
            "sourceware.org; spf=pass smtp.mailfrom=gmail.com",
            "server2.sourceware.org;\n arc=none smtp.remote-ip=2607:f8b0:4864:20::1035"
        ],
        "Received": [
            "from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g5NwP4r3pz1xqf\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 30 Apr 2026 03:05:25 +1000 (AEST)",
            "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id CF6804BB3B98\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 29 Apr 2026 17:05:23 +0000 (GMT)",
            "from mail-pj1-x1035.google.com (mail-pj1-x1035.google.com\n [IPv6:2607:f8b0:4864:20::1035])\n by sourceware.org (Postfix) with ESMTPS id 671B74B99F6A\n for <libc-alpha@sourceware.org>; Wed, 29 Apr 2026 17:05:01 +0000 (GMT)",
            "by mail-pj1-x1035.google.com with SMTP id\n 98e67ed59e1d1-35da9692ec3so10857679a91.1\n for <libc-alpha@sourceware.org>; Wed, 29 Apr 2026 10:05:01 -0700 (PDT)",
            "from fedoraemon.neon-universe.ts.net\n ([2406:7400:11d:a9dc:9a14:72eb:1a60:868a])\n by smtp.gmail.com with ESMTPSA id\n 98e67ed59e1d1-364bdf54203sm165135a91.7.2026.04.29.10.04.57\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Wed, 29 Apr 2026 10:04:58 -0700 (PDT)"
        ],
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 sourceware.org CF6804BB3B98",
            "OpenDKIM Filter v2.11.0 sourceware.org 671B74B99F6A"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 671B74B99F6A",
        "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 671B74B99F6A",
        "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777482301; cv=none;\n b=Zq5JT5Yn0sb0K1Cs/Qz764+k/mpg5Bhu0fHurnNTyND4Y0jjZcnGPSk3raMshRuSmmWMgS8pKbdQkhIQwduG3o1c7QfeLbWVP6gG5QJd9+dtQqAv9INaF555YfIiB514AlQ7hGeiX9azkSR70ApForcMI+z75ujoVNFvqm8y9bE=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1777482301; c=relaxed/simple;\n bh=mx4Cqt/6ca8P6eqNtYpp+QxBoGQB1VoLyC53W3PDcJ4=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=lLXjbgkT6yHwCCHAM/ubY54d6WAUjpyBN1TFW7V788UiWjKwHZE9du7rE3+vPYSikPDBBlxmylf3I8R3jPzRMWyMsSqokNACR96bJMOGkffB6DrAyM+fIrZW3glZPys8PV4SClFSV+wifcg4XNHUV8STblh1Kd5nH1PabDmtA+w=",
        "ARC-Authentication-Results": "i=1; server2.sourceware.org",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1777482300; x=1778087100; darn=sourceware.org;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:from:to:cc:subject:date:message-id:reply-to;\n bh=xKmy+L59oBnmu4lvxZIiGUZnkAoxEuFcIopbEqhJAz0=;\n b=odVovhkXmKP1qtJB0pxBh88qLVHV2NZ8OWUtZrT0nnOQbMvXJkjcGPd23IUYzNgDOy\n TDLvxx+/dKCgPAGru9BIqYGCQOzgKy9f3H9GXkReIghi8Lx8Zy2RS6dOXSO+Jf3tBHvL\n YCt3f7DKV0YuK0SXtFm00Axti16zUydIlNA6zCQSzbNdIQzCiDQo30Ju+pH1xTuHdhKt\n UFRiCBjkZTpG9l5DiVTH0dJpmfiHsoB7OfVN71BwGOJ430i1Hapgem6cBp7LEWYjZGlq\n zhVFYeiH6OOwi30e7N3im5KbjpFW1CrUGFrsXHVGnrspO2+nPMH2UrA9dsz/PMlAgpy7\n 0nOQ==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777482300; x=1778087100;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n :message-id:reply-to;\n bh=xKmy+L59oBnmu4lvxZIiGUZnkAoxEuFcIopbEqhJAz0=;\n b=E7ehC3wGvqtp8pBFLHLUYP0VdCJ23nJhPA8QHXb7O9C2sYSU/1NF0qmZ/hqFtZnp3r\n QLe8S9ocgHoxsQ+F+LSBuH+ZxKzs4r5vWG4qL5nMW5svZDGQ3rg2pbaxCsZKxtUCztCT\n ffgS95O693AS5BJq/bEbRwcbxWwBD9glqFFpGUo0tQNUMI+FOVRHq+5RXKG8A7hPV2h+\n nVzXCcv454UIMNYGS++uFPK4Pf1mecqNJySFe7e8q4LN6dKYXLLZaJdJSM9+KwEvYyEi\n Yb3t+7D6o5JsRPbjBvaTcr2HWDlUpFSTKvthMeAnBXgajMlXlDjioLKMp2tTcfZGGGq5\n yQIQ==",
        "X-Gm-Message-State": "AOJu0Yw4tzAXpk2FudjaHGBX5osmd0ioOIQw94f/4W03eDRM7Faufjpc\n tNY5FBrq81ZMt9XoLsZh/+IWtOAS06bWmqLAX7wXUvb+gW/6gof9qjSC3bIcGQ==",
        "X-Gm-Gg": "AeBDiev3+zNISVkAecBW/zFW2IbBIvKsKRQYLW7OXlqR+o++JThyDdTDZpu5uru6T3f\n zNJHBhClZM3MJG/avNf5t5twagvLALL5hYAM1h7M26zjPuwtZ2aY26EsDhubSOhCDuOwbLl1w6b\n EEYG67LyVTu7bgB9OiMdGT1HQUSxmIPM7PqgTuRwnKeIkLDFmfMe1Xw10T9mUBCqnb3kArivb5M\n NKBSI40VaeIRmP7OFAmvqawPPcD62WRaxBygtViPh3HAqrtidhjn3Mf+iOylZmb5arofPglLJ6n\n DCrJxVA7ENUGWQ3aj4yOu+coUYM2Fa4VDn1ARKzKNpb4M4gTjO8cvW6hwvLbT9KsZ6D1NCZlnW0\n MzPlL8V+UWKEZAAQjHSwqV3tdByuua2fPcxPNtowGBrXt0A/VrCt4DIZDmG9Ga+MzOq0ouetil+\n ctxW8ssU9Fhdwsp2OFJz9ApcRf83kRHUbkPauf5oFlpi1eQzNdUrHmfS4/LLrrYr1Xdw==",
        "X-Received": "by 2002:a17:90b:3f4f:b0:35f:b75e:fff5 with SMTP id\n 98e67ed59e1d1-364a0f3d15bmr5175729a91.22.1777482299418;\n Wed, 29 Apr 2026 10:04:59 -0700 (PDT)",
        "From": "Avinal Kumar <avinal.xlvii@gmail.com>",
        "To": "libc-alpha@sourceware.org",
        "Cc": "Avinal Kumar <avinal.xlvii@gmail.com>",
        "Subject": "[PATCH] intl: Import plural expression hardening from GNU gettext",
        "Date": "Wed, 29 Apr 2026 22:34:28 +0530",
        "Message-ID": "<20260429170428.1743554-1-avinal.xlvii@gmail.com>",
        "X-Mailer": "git-send-email 2.54.0",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "libc-alpha@sourceware.org",
        "X-Mailman-Version": "2.1.30",
        "Precedence": "list",
        "List-Id": "Libc-alpha mailing list <libc-alpha.sourceware.org>",
        "List-Unsubscribe": "<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>",
        "List-Archive": "<https://sourceware.org/pipermail/libc-alpha/>",
        "List-Post": "<mailto:libc-alpha@sourceware.org>",
        "List-Help": "<mailto:libc-alpha-request@sourceware.org?subject=help>",
        "List-Subscribe": "<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>",
        "Errors-To": "libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org"
    },
    "content": "The plural expression evaluator plural_eval() in eval-plural.h uses\nunbounded recursion, which can cause a stack overflow crash with\ndeeply nested expressions in malicious .mo files.  This is\nparticularly dangerous on threads with small stacks (musl libc\ndefault: 128 KB, AIX 7 default: 96 KB, glibc after ulimit -s 260:\n~3919 recursions max).\n\nAdditionally, division by zero in plural expressions triggers\nraise(SIGFPE), which is not multithread-safe, catching SIGFPE\nrequires per-process signal handlers that race with other threads.\n\nFix both by importing the hardening from GNU gettext:\n\n- Replace unbounded plural_eval() with depth-limited\n  plural_eval_recurse() (EVAL_MAXDEPTH=100), returning a\n  struct eval_result with status instead of a bare unsigned long.\n\n- Return PE_INTDIV status on division by zero instead of raising\n  SIGFPE.  Remove the architecture-specific INTDIV0_RAISES_SIGFPE\n  macro and the conditional #include <signal.h>.\n\n- Update plural_lookup() in dcigettext.c to handle the new return\n  type, falling back to index 0 on any evaluation failure.\n\nBased on GNU gettext commits ef37a1540 and 726bfb1d1.\nOriginal author: Bruno Haible <bruno@clisp.org>\n\nSigned-off-by: Avinal Kumar <avinal.xlvii@gmail.com>\n---\nThis is the first in a series of patches to sync glibc's intl/ with\nupstream GNU gettext.  The last sync was about 1.5 years ago.\nSince then, gettext has accumulated security fixes, bug fixes, and\ndead code cleanup that glibc lacks.\n\nAfter excluding Windows-specific and build system changes (which\naccount for ~80% of the raw diff), approximately 540 lines of\nmeaningful change need to be imported across ~10 files.\n\nThis particular change was already discussed and approved on\nhttps://sourceware.org/pipermail/libc-alpha/2023-October/152010.html\nbut wasn't followed by any patches.\n\n\n intl/dcigettext.c  |  27 +++------\n intl/eval-plural.h | 139 ++++++++++++++++++++++++++++++++++-----------\n intl/plural-exp.h  |  21 ++++++-\n 3 files changed, 134 insertions(+), 53 deletions(-)",
    "diff": "diff --git a/intl/dcigettext.c b/intl/dcigettext.c\nindex 9ebc54eeec..fb948c1092 100644\n--- a/intl/dcigettext.c\n+++ b/intl/dcigettext.c\n@@ -68,20 +68,6 @@ extern int errno;\n \n #include <locale.h>\n \n-#ifdef _LIBC\n-  /* Guess whether integer division by zero raises signal SIGFPE.\n-     Set to 1 only if you know for sure.  In case of doubt, set to 0.  */\n-# if defined __alpha__ || defined __arm__ || defined __i386__ \\\n-     || defined __m68k__ || defined __s390x__\n-#  define INTDIV0_RAISES_SIGFPE 1\n-# else\n-#  define INTDIV0_RAISES_SIGFPE 0\n-# endif\n-#endif\n-#if !INTDIV0_RAISES_SIGFPE\n-# include <signal.h>\n-#endif\n-\n #if defined HAVE_SYS_PARAM_H || defined _LIBC\n # include <sys/param.h>\n #endif\n@@ -1381,14 +1367,19 @@ plural_lookup (struct loaded_l10nfile *domain, unsigned long int n,\n \t       const char *translation, size_t translation_len)\n {\n   struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;\n+  struct eval_result result;\n   unsigned long int index;\n   const char *p;\n \n-  index = plural_eval (domaindata->plural, n);\n-  if (index >= domaindata->nplurals)\n-    /* This should never happen.  It means the plural expression and the\n-       given maximum value do not match.  */\n+  result = plural_eval (domaindata->plural, n);\n+  if (result.status != PE_OK)\n+    /* The plural expression evaluation failed.  */\n     index = 0;\n+  else if (result.value >= domaindata->nplurals)\n+    /* The plural expression and the given maximum value do not match.  */\n+    index = 0;\n+  else\n+    index = result.value;\n \n   /* Skip INDEX strings at TRANSLATION.  */\n   p = translation;\ndiff --git a/intl/eval-plural.h b/intl/eval-plural.h\nindex 243f6027a1..e231e9bfd3 100644\n--- a/intl/eval-plural.h\n+++ b/intl/eval-plural.h\n@@ -14,24 +14,59 @@\n    You should have received a copy of the GNU Lesser General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */\n \n+/* Written by Ulrich Drepper and Bruno Haible.  */\n+\n #ifndef STATIC\n #define STATIC static\n #endif\n \n-/* Evaluate the plural expression and return an index value.  */\n-STATIC\n-unsigned long int\n-plural_eval (const struct expression *pexp, unsigned long int n)\n+/* While the bison parser is able to support expressions of a maximum depth\n+   of YYMAXDEPTH = 10000, the runtime evaluation of a parsed plural expression\n+   has a smaller maximum recursion depth.\n+   If we did not limit the recursion depth, a program that just invokes\n+   ngettext() on a thread other than the main thread could get a crash by\n+   stack overflow in the following circumstances:\n+     - On systems with glibc, after the stack size has been reduced,\n+       e.g. on x86_64 systems after \"ulimit -s 260\".\n+       This stack size is only sufficient for ca. 3919 recursions.\n+       Cf. <https://unix.stackexchange.com/questions/620720/>\n+     - On systems with musl libc, because there the thread stack size (for a\n+       thread other than the main thread) by default is only 128 KB, see\n+       <https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size>.\n+     - On AIX 7 systems, because there the thread stack size (for a thread\n+       other than the main thread) by default is only 96 KB, see\n+       <https://www.ibm.com/docs/en/aix/7.1?topic=programming-threads-library-options>.\n+       This stack size is only sufficient for between 887 and 1363 recursions,\n+       depending on the compiler and compiler optimization options.\n+   A maximum depth of 100 is a large enough for all practical needs\n+   and also small enough to avoid stack overflow even with small thread stack\n+   sizes.  */\n+#ifndef EVAL_MAXDEPTH\n+# define EVAL_MAXDEPTH 100\n+#endif\n+\n+/* A shorthand that denotes a successful evaluation result with a value V.  */\n+#define OK(v) (struct eval_result) { .status = PE_OK, .value = (v) }\n+\n+/* Evaluates a plural expression PEXP for n=N, up to ALLOWED_DEPTH.  */\n+static struct eval_result\n+plural_eval_recurse (const struct expression *pexp, unsigned long int n,\n+\t\t     unsigned int allowed_depth)\n {\n+  if (allowed_depth == 0)\n+    /* The allowed recursion depth is exhausted.  */\n+    return (struct eval_result) { .status = PE_STACKOVF };\n+  allowed_depth--;\n+\n   switch (pexp->nargs)\n     {\n     case 0:\n       switch (pexp->operation)\n \t{\n \tcase var:\n-\t  return n;\n+\t  return OK (n);\n \tcase num:\n-\t  return pexp->val.num;\n+\t  return OK (pexp->val.num);\n \tdefault:\n \t  break;\n \t}\n@@ -40,52 +75,73 @@ plural_eval (const struct expression *pexp, unsigned long int n)\n     case 1:\n       {\n \t/* pexp->operation must be lnot.  */\n-\tunsigned long int arg = plural_eval (pexp->val.args[0], n);\n-\treturn ! arg;\n+\tstruct eval_result arg =\n+\t  plural_eval_recurse (pexp->val.args[0], n, allowed_depth);\n+\tif (arg.status != PE_OK)\n+\t  return arg;\n+\treturn OK (! arg.value);\n       }\n     case 2:\n       {\n-\tunsigned long int leftarg = plural_eval (pexp->val.args[0], n);\n+\tstruct eval_result leftarg =\n+\t  plural_eval_recurse (pexp->val.args[0], n, allowed_depth);\n+\tif (leftarg.status != PE_OK)\n+\t  return leftarg;\n \tif (pexp->operation == lor)\n-\t  return leftarg || plural_eval (pexp->val.args[1], n);\n+\t  {\n+\t    if (leftarg.value)\n+\t      return OK (1);\n+\t    struct eval_result rightarg =\n+\t      plural_eval_recurse (pexp->val.args[1], n, allowed_depth);\n+\t    if (rightarg.status != PE_OK)\n+\t      return rightarg;\n+\t    return OK (rightarg.value ? 1 : 0);\n+\t  }\n \telse if (pexp->operation == land)\n-\t  return leftarg && plural_eval (pexp->val.args[1], n);\n+\t  {\n+\t    if (!leftarg.value)\n+\t      return OK (0);\n+\t    struct eval_result rightarg =\n+\t      plural_eval_recurse (pexp->val.args[1], n, allowed_depth);\n+\t    if (rightarg.status != PE_OK)\n+\t      return rightarg;\n+\t    return OK (rightarg.value ? 1 : 0);\n+\t  }\n \telse\n \t  {\n-\t    unsigned long int rightarg = plural_eval (pexp->val.args[1], n);\n+\t    struct eval_result rightarg =\n+\t      plural_eval_recurse (pexp->val.args[1], n, allowed_depth);\n+\t    if (rightarg.status != PE_OK)\n+\t      return rightarg;\n \n \t    switch (pexp->operation)\n \t      {\n \t      case mult:\n-\t\treturn leftarg * rightarg;\n+\t\treturn OK (leftarg.value * rightarg.value);\n \t      case divide:\n-#if !INTDIV0_RAISES_SIGFPE\n-\t\tif (rightarg == 0)\n-\t\t  raise (SIGFPE);\n-#endif\n-\t\treturn leftarg / rightarg;\n+\t\tif (rightarg.value == 0)\n+\t\t  return (struct eval_result) { .status = PE_INTDIV };\n+\t\treturn OK (leftarg.value / rightarg.value);\n \t      case module:\n-#if !INTDIV0_RAISES_SIGFPE\n-\t\tif (rightarg == 0)\n-\t\t  raise (SIGFPE);\n-#endif\n-\t\treturn leftarg % rightarg;\n+\t\tif (rightarg.value == 0)\n+\t\t  return (struct eval_result) { .status = PE_INTDIV };\n+\t\treturn OK (leftarg.value % rightarg.value);\n \t      case plus:\n-\t\treturn leftarg + rightarg;\n+\t\treturn OK (leftarg.value + rightarg.value);\n \t      case minus:\n-\t\treturn leftarg - rightarg;\n+\t\treturn OK (leftarg.value - rightarg.value);\n \t      case less_than:\n-\t\treturn leftarg < rightarg;\n+\t\treturn OK (leftarg.value < rightarg.value);\n \t      case greater_than:\n-\t\treturn leftarg > rightarg;\n+\t\treturn OK (leftarg.value > rightarg.value);\n \t      case less_or_equal:\n-\t\treturn leftarg <= rightarg;\n+\t\treturn OK (leftarg.value <= rightarg.value);\n \t      case greater_or_equal:\n-\t\treturn leftarg >= rightarg;\n+\t\treturn OK (leftarg.value >= rightarg.value);\n \t      case equal:\n-\t\treturn leftarg == rightarg;\n+\t\treturn OK (leftarg.value == rightarg.value);\n \t      case not_equal:\n-\t\treturn leftarg != rightarg;\n+\t\treturn OK (leftarg.value != rightarg.value);\n \t      default:\n \t\tbreak;\n \t      }\n@@ -96,10 +152,25 @@ plural_eval (const struct expression *pexp, unsigned long int n)\n     case 3:\n       {\n \t/* pexp->operation must be qmop.  */\n-\tunsigned long int boolarg = plural_eval (pexp->val.args[0], n);\n-\treturn plural_eval (pexp->val.args[boolarg ? 1 : 2], n);\n+\tstruct eval_result boolarg =\n+\t  plural_eval_recurse (pexp->val.args[0], n, allowed_depth);\n+\tif (boolarg.status != PE_OK)\n+\t  return boolarg;\n+\treturn plural_eval_recurse (pexp->val.args[boolarg.value ? 1 : 2], n,\n+\t\t\t\t    allowed_depth);\n       }\n     }\n   /* NOTREACHED */\n-  return 0;\n+  return (struct eval_result) { .status = PE_ASSERT };\n+}\n+\n+/* Evaluates a plural expression PEXP for n=N.  */\n+STATIC\n+struct eval_result\n+plural_eval (const struct expression *pexp, unsigned long int n)\n+{\n+  return plural_eval_recurse (pexp, n, EVAL_MAXDEPTH);\n }\n+\n+#undef OK\n+\ndiff --git a/intl/plural-exp.h b/intl/plural-exp.h\nindex f0d3a611e1..e2e0c30563 100644\n--- a/intl/plural-exp.h\n+++ b/intl/plural-exp.h\n@@ -26,6 +26,7 @@\n extern \"C\" {\n #endif\n \n+/* Parsing a plural expression.  */\n \n enum expression_operator\n {\n@@ -109,8 +110,26 @@ extern void EXTRACT_PLURAL_EXPRESSION (const char *nullentry,\n \t\t\t\t       unsigned long int *npluralsp)\n      attribute_hidden;\n \n+/* Evaluating a parsed plural expression.  */\n+\n+enum eval_status\n+{\n+  PE_OK,        /* Evaluation succeeded, produced a value */\n+  PE_INTDIV,    /* Integer division by zero */\n+  PE_INTOVF,    /* Integer overflow */\n+  PE_STACKOVF,  /* Stack overflow */\n+  PE_ASSERT     /* Assertion failure */\n+};\n+\n+struct eval_result\n+{\n+  enum eval_status status;\n+  unsigned long int value;      /* Only relevant for status == PE_OK */\n+};\n+\n+\n #if !defined (_LIBC) && !defined (IN_LIBINTL) && !defined (IN_LIBGLOCALE)\n-extern unsigned long int plural_eval (const struct expression *pexp,\n+extern struct eval_result plural_eval (const struct expression *pexp,\n \t\t\t\t      unsigned long int n);\n #endif\n \n",
    "prefixes": []
}