From nobody Thu Apr 30 15:04:12 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4g5yBB1XzTz6c49h for ; Thu, 30 Apr 2026 15:04:18 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4g5yB964B3z419c for ; Thu, 30 Apr 2026 15:04:17 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1777561457; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=oAqlOStjlbqPmxgFf4xAxQEwSEPPPi2uF+63tEA3UOY=; b=Ipw4+wjU7FGvNJDqwLC4AZjvnnrkalXd/8dyV0csvzv92S+QRfEV4uZ8wGgvoRgoclw/Aj erGgt2E2w4DTj7ThZ1S7KTr2rDgNaSVchBTzwl9zVvtLlq8LFTCBhm5euRWsNXF7cxDgDW x/v+arfU1Usie4jHXmRMkCopDhAe+Se3N/vdKl9aY11mPqKPh6t76Lj54/ny5UV/RG6kfq y1NEIN4tBh/92OSMax0sGFi2WnPkhUQDmNdEPWdDnmKarvLtM+Tm8ZCpg0867Mp3Qya92t nChUDzwl5FSwiAlwjpQHgtmT7C6bs8d0TQJn3o/ektyXCNNhsNIixKYqP8ATtw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1777561457; a=rsa-sha256; cv=none; b=PoamF4nrYi3MAafaDKIlM09U3X9lt50VaOnxG1hSqIxbR/U3uNZ1Tqywfvff/b8JQS6aaM +CssiWyPI+HZdy02l7atdH+NgVc91YsM2MLE4kXQeMGVpvZ/15EbHMyyHssx8XVqcZHfx+ LhAAaM6bJZPOtwxAQ+4eFJ3q98np/aCQSfeL8DR5rlGWUrb2ci1XWfu6IxAPfi8y1tLB5H duGoD3lh61U3kWzb+lcIr6b1QTsSF+DHjwDHduOXqi/q46Ui4oPJp9He4bHqYV9T6r9/Tg /FM2+e+ts8pRXl4yCHw7ZSIwG1iHX9E4On6BWN080NsDj+rRysI71hGC+QQcPw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1777561457; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=oAqlOStjlbqPmxgFf4xAxQEwSEPPPi2uF+63tEA3UOY=; b=XwuAJj/t8tI2QYxLGX7NZ2N1H85/5HJ71xbjk+hKrzPcJEYtf1Qct1A21mpaVZqHQXeWp6 HSRirxRr7grMilMynzD/ZDYTavooC5bJfSTYHD63oh6G+xAyzeInE3lBWQ2UwlgxKuz6z4 /xHl5EikZF03ax7t3ClAfsysNAmtffoJs7LPNw2tmVUbUjZhNqkRdatUbQx0ZsJ0Fz3zAn eN672+SuUjDo91meBithirERs32pgZkSHtNRc6sLhQrsiAAjMjnBsNO97TFEhr+2MSshKN jpdJPlU5D+JkZbtfnnDuKl2+vtsvjxbT992v0qr0y8uzJ/bEH/v8EYLkjSePEw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4g5yB95Vd4z21J for ; Thu, 30 Apr 2026 15:04:17 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3fb61 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 30 Apr 2026 15:04:12 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Dag-Erling=?utf-8?Q? Sm=C3=B8rg?=rav Subject: git: b72580fcb0a7 - stable/15 - tzcode: Update to 2026b List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: b72580fcb0a7647dcc2b18861ac02eb9cfcf5835 Auto-Submitted: auto-generated Date: Thu, 30 Apr 2026 15:04:12 +0000 Message-Id: <69f36f6c.3fb61.643fa71c@gitrepo.freebsd.org> The branch stable/15 has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=b72580fcb0a7647dcc2b18861ac02eb9cfcf5835 commit b72580fcb0a7647dcc2b18861ac02eb9cfcf5835 Author: Dag-Erling Smørgrav AuthorDate: 2026-04-24 20:07:20 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2026-04-30 15:02:03 +0000 tzcode: Update to 2026b MFC after: 1 week (cherry picked from commit 76f642310d55b1d3892c1b1626c427d12f97de3a) --- contrib/tzcode/NEWS | 53 ++++++++++++--- contrib/tzcode/tz-art.html | 2 +- contrib/tzcode/tz-link.html | 22 +++++-- contrib/tzcode/version | 2 +- contrib/tzcode/zic.c | 156 +++++++++++++++++++++++++++++--------------- 5 files changed, 166 insertions(+), 69 deletions(-) diff --git a/contrib/tzcode/NEWS b/contrib/tzcode/NEWS index acd22280cb3a..ea52e67e804a 100644 --- a/contrib/tzcode/NEWS +++ b/contrib/tzcode/NEWS @@ -1,10 +1,47 @@ News for the tz database +Release 2026b - 2026-04-22 23:06:43 -0700 + + Briefly: + British Columbia moved to permanent -07 on 2026-03-09. + Some more overflow bugs have been fixed in zic. + + Changes to future timestamps + + British Columbia’s 2026-03-08 spring forward was its last + foreseeable clock change, as it moved to permanent -07 thereafter. + (Thanks to Arthur David Olson.) Although the change to permanent + -07 legally took place on 2026-03-09, temporarily model the change + to occur on 2026-11-01 at 02:00 instead. This works around a + limitation in CLDR v48.2 (2026-03-17). This temporary hack is + planned to be removed after CLDR is fixed. + + Changes to code + + zic no longer mishandles a last transition to a new time type. + + zic no longer overflows a buffer when generating a TZ string like + "PST-167:59:58PDT-167:59:59,M11.5.6/-167:59:59,M12.5.6/-167:59:59", + which can occur with adversarial input. (Thanks to Naveed Khan.) + + zic no longer generates a longer TZif file than necessary when + an earlier time zone abbreviation is a suffix of a later one. + As a nice side effect, zic no longer overflows a buffer when given + a long series of abbreviations, each a suffix of the next. + (Buffer overflow reported by Arthur Chan.) + + zic no longer overflows an int when processing input like ‘Zone + Ouch 2147483648:00:00 - LMT’. The int overflow can lead to buffer + overflow in adversarial cases. (Thanks to Naveed Khan.) + + zic now checks for signals more often. + + Release 2026a - 2026-03-01 22:59:49 -0800 Briefly: Moldova has used EU transition times since 2022. - The "right" TZif files are no longer installed by default. + The “right” TZif files are no longer installed by default. -DTZ_RUNTIME_LEAPS=0 disables runtime support for leap seconds. TZif files are no longer limited to 50 bytes of abbreviations. zic is no longer limited to 50 leap seconds. @@ -25,23 +62,23 @@ Release 2026a - 2026-03-01 22:59:49 -0800 The Makefile no longer by default installs an alternate set of TZif files for system clocks that count leap seconds. - Install with 'make REDO=posix_right' to get the old default, + Install with ‘make REDO=posix_right’ to get the old default, which is rarely used in major downstream distributions. If your system clock counts leap seconds (contrary to POSIX), - it is better to install with 'make REDO=right_only'. + it is better to install with ‘make REDO=right_only’. This change does not affect the leapseconds file, which is still installed as before. - The Makefile's POSIXRULES option, which was declared obsolete in - release 2019b, has been removed. The Makefile's build procedure + The Makefile’s POSIXRULES option, which was declared obsolete in + release 2019b, has been removed. The Makefile’s build procedure thus no longer optionally installs the obsolete posixrules file. Changes to code Compiling with the new option -DTZ_RUNTIME_LEAPS=0 disables runtime support for leap seconds. Although this conforms to - POSIX, shrinks tzcode's attack surface, and is more efficient, - it fails to support Internet RFC 9636's leap seconds. + POSIX, shrinks tzcode’s attack surface, and is more efficient, + it fails to support Internet RFC 9636’s leap seconds. zic now can generate, and localtime.c can now use, TZif files that hold up to 256 bytes of abbreviations, counting trailing NULs. @@ -51,7 +88,7 @@ Release 2026a - 2026-03-01 22:59:49 -0800 zic -L can now generate TZif files with more than 50 leap seconds. This helps test TZif readers not limited to 50 leap seconds, as - tzcode's localtime.c is; it has little immediate need for + tzcode’s localtime.c is; it has little immediate need for practical timekeeping as there have been only 27 leap seconds and possibly there will be no more, due to planned changes to UTC. zic -v warns if its output exceeds the old 50-second limit. diff --git a/contrib/tzcode/tz-art.html b/contrib/tzcode/tz-art.html index 1bd1bf781166..7870a6187390 100644 --- a/contrib/tzcode/tz-art.html +++ b/contrib/tzcode/tz-art.html @@ -446,7 +446,7 @@ Includes the song “Does Anybody Really Know What Time It Is?”.
  • Emanuele Arciuli, -The Time Curve Preludes (2023). +The Time Curve Preludes (2023). Neuma 174, 44:46. The title piece, composed by William diff --git a/contrib/tzcode/tz-link.html b/contrib/tzcode/tz-link.html index 5f1989f014b3..4d2c12b09452 100644 --- a/contrib/tzcode/tz-link.html +++ b/contrib/tzcode/tz-link.html @@ -441,7 +441,7 @@ transition in the tz database.
  • Database Parser is a C++ parser and runtime library with a std::chrono API +href="https://en.cppreference.com/cpp/chrono">std::chrono API that is a standard part of C++. It is freely available under the MIT license. @@ -1135,8 +1135,7 @@ Network Time Protocol Best Current Practices applications requiring accurate UTC or civil time, and is intended for use only in single, well-controlled environments.
  • The Leap -Second Discussion List covers LEAPSECS List covers McCarthy and Klepczynski’s 1999 proposal to discontinue leap seconds, discussed further in @@ -1146,21 +1145,30 @@ leap second: its history and possible future. might be redefined without Leap Seconds gives pointers on this contentious issue. -The General Conference on Weights and Measures +The General Conference on Weights and Measures (CGPM) decided in 2022 to discontinue the use of leap seconds by 2035, and requested that no discontinuous adjustments be made to UTC for at least a century. The World Radiocommunication Conference resolved -in 2023 to cooperate with this process. One proposal to implement this +in 2023 to cooperate with this process. A draft Resolution +C to make continuous UTC effective on 2027-05-20, +and thereby discontinue leap seconds, +has been scheduled for the 28th CGPM starting 2026-10-13 in Paris. +One proposal to implement this would replace leap seconds with seven 13-second leap smears occurring once per decade until 2100, with leap smears after that gradually increasing in size. See: However, there is still no consensus on whether this is the best way to replace leap seconds. diff --git a/contrib/tzcode/version b/contrib/tzcode/version index 5d9126009e7f..75d34ee38931 100644 --- a/contrib/tzcode/version +++ b/contrib/tzcode/version @@ -1 +1 @@ -2026a +2026b diff --git a/contrib/tzcode/zic.c b/contrib/tzcode/zic.c index d1084b4a25e9..f4c1aa6d54e6 100644 --- a/contrib/tzcode/zic.c +++ b/contrib/tzcode/zic.c @@ -253,11 +253,13 @@ symlink(char const *target, char const *linkname) (errno = ENOTSUP, -1) #endif +static int addabbr(char[TZ_MAX_CHARS], int *, char const *); static void addtt(zic_t starttime, int type); static int addtype(zic_t, char const *, bool, bool, bool); -static void leapadd(zic_t, int, int); static void adjleap(void); static void associate(void); +static void checkabbr(char const *); +static void check_for_signal(void); static void dolink(const char *, const char *, bool); static int getfields(char *, char **, int); static zic_t gethms(const char * string, const char * errstring); @@ -270,11 +272,11 @@ static void inrule(char ** fields, int nfields); static bool inzcont(char ** fields, int nfields); static bool inzone(char ** fields, int nfields); static bool inzsub(char **, int, bool); -static int itssymlink(char const *, int *); static bool is_alpha(char a); +static int itssymlink(char const *, int *); +static void leapadd(zic_t, int, int); static char lowerit(char); static void mkdirs(char const *, bool); -static void newabbr(const char * abbr); static zic_t oadd(zic_t t1, zic_t t2); static zic_t omul(zic_t, zic_t); static void outzone(const struct zone * zp, ptrdiff_t ntzones); @@ -704,6 +706,7 @@ eat(int fnum, lineno num) ATTRIBUTE_FORMAT((printf, 1, 0)) static void verror(const char *const string, va_list args) { + check_for_signal(); /* ** Match the format of "cc" to allow sh users to ** zic ... 2>&1 | error -t "*" -v @@ -1074,6 +1077,7 @@ make_links(void) warning(_("link %s targeting link %s"), links[i].l_linkname, links[i].l_target); } + check_for_signal(); } } @@ -1391,6 +1395,7 @@ main(int argc, char **argv) for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j) continue; outzone(&zones[i], j - i); + check_for_signal(); } make_links(); if (lcltime != NULL) { @@ -1486,9 +1491,11 @@ get_rand_u64(void) static int nwords; if (!nwords) { ssize_t s; - do + for (;; check_for_signal()) { s = getrandom(entropy_buffer, sizeof entropy_buffer, 0); - while (s < 0 && errno == EINTR); + if (! (s < 0 && errno == EINTR)) + break; + } if (s < 0) nwords = -1; @@ -1519,7 +1526,7 @@ get_rand_u64(void) rmod = INT_MAX < UINT_FAST64_MAX ? 0 : UINT_FAST64_MAX / nrand + 1, r = 0, rmax = 0; - do { + for (;; check_for_signal()) { uint_fast64_t rmax1 = rmax; if (rmod) { /* Avoid signed integer overflow on theoretical platforms @@ -1530,7 +1537,9 @@ get_rand_u64(void) rmax1 = nrand * rmax1 + rand_max; r = nrand * r + rand(); rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX; - } while (rmax < UINT_FAST64_MAX); + if (UINT_FAST64_MAX <= rmax) + break; + } return r; } @@ -1577,9 +1586,11 @@ random_dirent(char const **name, char **namealloc) *name = *namealloc = dst; } - do + for (;; check_for_signal()) { r = get_rand_u64(); - while (unfair_min <= r); + if (r < unfair_min) + break; + } for (i = 0; i < suffixlen; i++) { dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen]; @@ -1622,7 +1633,7 @@ open_outfile(char const **outname, char **tempname) exit(EXIT_FAILURE); } - while (true) { + for (;; check_for_signal()) { int oflags = O_WRONLY | O_BINARY | O_CREAT | O_EXCL; int fd = open(*outname, oflags, creat_perms); int err; @@ -1736,8 +1747,6 @@ dolink(char const *target, char const *linkname, bool staysymlink) char const *outname = linkname; int targetissym = -2, linknameissym = -2; - check_for_signal(); - if (strcmp(target, "-") == 0) { if (remove(linkname) == 0 || errno == ENOENT || errno == ENOTDIR) return; @@ -1750,7 +1759,7 @@ dolink(char const *target, char const *linkname, bool staysymlink) } } - while (true) { + for (;; check_for_signal()) { if (linkat(AT_FDCWD, target, AT_FDCWD, outname, AT_SYMLINK_FOLLOW) == 0) { link_errno = 0; @@ -1802,7 +1811,7 @@ dolink(char const *target, char const *linkname, bool staysymlink) int symlink_errno = -1; if (contents) { - while (true) { + for (;; check_for_signal()) { if (symlink(contents, outname) == 0) { symlink_errno = 0; break; @@ -1833,7 +1842,7 @@ dolink(char const *target, char const *linkname, bool staysymlink) exit(EXIT_FAILURE); } tp = open_outfile(&outname, &tempname); - while ((c = getc(fp)) != EOF) + for (; (c = getc(fp)) != EOF; check_for_signal()) putc(c, tp); close_file(tp, directory, linkname, tempname); close_file(fp, directory, target, NULL); @@ -1957,7 +1966,7 @@ static bool inputline(FILE *fp, char *buf, ptrdiff_t bufsize) { ptrdiff_t linelen = 0, ch; - while ((ch = getc(fp)) != '\n') { + for (; (ch = getc(fp)) != '\n'; check_for_signal()) { if (ch < 0) { if (ferror(fp)) { error(_("input error")); @@ -2044,6 +2053,7 @@ infile(int fnum, char const *name) default: unreachable(); } } + check_for_signal(); } close_file(fp, NULL, filename(fnum), NULL); if (wantcont) @@ -2657,7 +2667,6 @@ writezone(const char *const name, const char *const string, char version, { register FILE * fp; register ptrdiff_t i, j; - register size_t u; register int pass; char *tempname = NULL; char const *outname = name; @@ -2939,30 +2948,27 @@ writezone(const char *const name, const char *const string, char version, : i == thisdefaulttype ? old0 : i] = thistypecnt++; - for (u = 0; u < sizeof indmap / sizeof indmap[0]; ++u) - indmap[u] = -1; thischarcnt = stdcnt = utcnt = 0; for (i = old0; i < typecnt; i++) { - register char * thisabbr; - if (omittype[i]) continue; if (ttisstds[i]) stdcnt = thistypecnt; if (ttisuts[i]) utcnt = thistypecnt; - if (indmap[desigidx[i]] >= 0) - continue; - thisabbr = &chars[desigidx[i]]; - for (j = 0; j < thischarcnt; ++j) - if (strcmp(&thischars[j], thisabbr) == 0) - break; - if (j == thischarcnt) { - strcpy(&thischars[thischarcnt], thisabbr); - thischarcnt += strlen(thisabbr) + 1; - } - indmap[desigidx[i]] = j; + addabbr(thischars, &thischarcnt, &chars[desigidx[i]]); } + + /* Now that all abbrevs have been added to THISCHARS, + it is safe to set INDMAP without worrying about + whether the abbrevs might move later. */ + for (i = 0; i < TZ_MAX_CHARS; i++) + indmap[i] = -1; + for (i = old0; i < typecnt; i++) + if (!omittype[i] && indmap[desigidx[i]] < 0) + indmap[desigidx[i]] = addabbr(thischars, &thischarcnt, + &chars[desigidx[i]]); + if (pass == 1 && !want_bloat()) { hicut = thisleapexpiry = false; pretranstype = -1; @@ -3185,11 +3191,11 @@ stringoffset(char *result, zic_t offset) offset /= SECSPERMIN; minutes = offset % MINSPERHOUR; offset /= MINSPERHOUR; - hours = offset; - if (hours >= HOURSPERDAY * DAYSPERWEEK) { + if (offset >= HOURSPERDAY * DAYSPERWEEK) { result[0] = '\0'; return 0; } + hours = offset; len += sprintf(result + len, "%d", hours); if (minutes != 0 || seconds != 0) { len += sprintf(result + len, ":%02d", minutes); @@ -3428,12 +3434,16 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) int nonTZlimtype = -1; zic_t max_year0; int defaulttype = -1; + int max_stringoffset_len = sizeof "-167:59:59" - 1; + int max_comma_stringrule_len = (sizeof ",M12.5.6/" - 1 + + max_stringoffset_len); check_for_signal(); /* This cannot overflow; see FORMAT_LEN_GROWTH_BOUND. */ max_abbr_len = 2 + max_format_len + max_abbrvar_len; - max_envvar_len = 2 * max_abbr_len + 5 * 9; + max_envvar_len = 2 * (max_abbr_len + max_stringoffset_len + + max_comma_stringrule_len); startbuf = xmalloc(max_abbr_len + 1); ab = xmalloc(max_abbr_len + 1); @@ -3534,7 +3544,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) startttisut); if (usestart) { addtt(starttime, type); - if (useuntil && nonTZlimtime < starttime) { + if (nonTZlimtime < starttime) { nonTZlimtime = starttime; nonTZlimtype = type; } @@ -3781,6 +3791,7 @@ static int addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut) { register int i, j; + int charcnt0; /* RFC 9636 section 3.2 specifies this range for utoff. */ if (! (-TWO_31_MINUS_1 <= utoff && utoff <= TWO_31_MINUS_1)) { @@ -3790,12 +3801,18 @@ addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut) if (!want_bloat()) ttisstd = ttisut = false; - for (j = 0; j < charcnt; ++j) - if (strcmp(&chars[j], abbr) == 0) - break; - if (j == charcnt) - newabbr(abbr); - else { + checkabbr(abbr); + + charcnt0 = charcnt; + j = addabbr(chars, &charcnt, abbr); + if (charcnt0 < charcnt) { + /* If an abbreviation was inserted, increment indexes no + earlier than the insert by the size of the insertion, + so that they continue to point to the same contents. */ + for (i = 0; i < typecnt; i++) + if (j <= desigidx[i]) + desigidx[i] += charcnt - charcnt0; + } else { /* If there's already an entry, return its index. */ for (i = 0; i < typecnt; i++) if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i] @@ -4180,10 +4197,8 @@ will not work with pre-2004 versions of zic")); } static void -newabbr(const char *string) +checkabbr(char const *string) { - register int i; - if (strcmp(string, GRANDPARENTED) != 0) { register const char * cp; const char * mp; @@ -4202,13 +4217,50 @@ mp = _("time zone abbreviation differs from POSIX standard"); if (mp != NULL) warning("%s (%s)", mp, string); } - i = strnlen(string, TZ_MAX_CHARS - charcnt) + 1; - if (charcnt + i > TZ_MAX_CHARS) { - error(_("too many, or too long, time zone abbreviations")); - exit(EXIT_FAILURE); - } - strcpy(&chars[charcnt], string); - charcnt += i; +} + +/* Put into CHS, which currently contains *PNCHS bytes containing + NUL-terminated abbreviations none of which are suffixes of another, + the abbreviation ABBR including its trailing NUL. + If ABBR does not already appear in CHS, + possibly as a suffix of an existing abbreviation, + add ABBR to CHS, remove from CHS any abbreviation + that is a suffix of ABBR, and increment *PNCHS accordingly. + Return the index of ABBR after any modifications to CHS are made. + + If all abbreviations have already been added, this function + lets the caller look up the index of an existing abbreviation. */ +static int +addabbr(char chs[TZ_MAX_CHARS], int *pnchs, char const *abbr) +{ + int nchs = *pnchs; + int alen = strlen(abbr), nchs_incr = alen + 1; + int i; + for (i = 0; i < nchs; ) { + int clen = strlen(&chs[i]); + if (alen <= clen) { + /* If ABBR is a suffix of an abbreviation in CHS, + return the index of ABBR in CHS. */ + int isuff = i + (clen - alen); + if (memcmp(&chs[isuff], abbr, alen) == 0) + return isuff; + } else if (memcmp(&chs[i], &abbr[alen - clen], clen) == 0) { + /* An abbreviation in CHS is a substring of ABBR. + Replace it with ABBR, instead of the more-common + actions of appending ABBR or doing nothing. */ + nchs_incr = alen - clen; + break; + } + i += clen + 1; + } + if (TZ_MAX_CHARS < nchs + nchs_incr) { + error(_("too many, or too long, time zone abbreviations")); + exit(EXIT_FAILURE); + } + memmove(&chs[i + nchs_incr], &chs[i], nchs - i); + memcpy(&chs[i], abbr, nchs_incr); + *pnchs = nchs + nchs_incr; + return i; } /* Ensure that the directories of ARGNAME exist, by making any missing