PKG_CPE_ID:=cpe:/a:gnu:$(PKG_NAME)
PKG_SOURCE_URL=git://git.git.savannah.gnu.org/$(PKG_NAME).git
-PKG_SOURCE_DATE:=2025-07-01
-PKG_SOURCE_VERSION:=a3151d456d6919c9066b54dc6f680452168165cf# # stable-202501
-PKG_MIRROR_HASH:=b695d96e915ecd6c4551436f417cb2c0879aef4ef6318721c8d5cc86cb44ba9d
+PKG_SOURCE_DATE:=2026-07-04
+PKG_SOURCE_VERSION:=b22f5a3037712a3c957a03071ce0b219cef4d65b# # stable-202507
+PKG_MIRROR_HASH:=d1423a784794e3e08bd03b397544821ece257aa748566386bd97219d1edf98d5
include $(INCLUDE_DIR)/host-build.mk
--- a/build-aux/bootstrap
+++ b/build-aux/bootstrap
-@@ -237,14 +237,14 @@ test -r "$conffile" && . "$conffile"
+@@ -241,14 +241,14 @@ test -r "$conffile" && . "$conffile"
check_exists() {
if test "$1" = "--verbose"; then
fi
test $? -lt 126
-@@ -309,7 +309,7 @@ p
+@@ -313,7 +313,7 @@ p
q'
get_version() {
$app --version >/dev/null 2>&1 || { $app --version; return 1; }
-@@ -366,13 +366,13 @@ check_versions() {
+@@ -370,13 +370,13 @@ check_versions() {
if [ "$req_ver" = "-" ]; then
# Merely require app to exist; not all prereq apps are well-behaved
# so we have to rely on $? rather than get_version.
if [ ! "$inst_ver" ]; then
warn_ "Error: '$app' not found"
ret=1
-@@ -1205,7 +1205,7 @@ autogen()
+@@ -1223,7 +1223,7 @@ autogen()
# two just-pre-run programs.
# Import from gettext.
# func_version
# outputs to stdout the --version message.
func_version ()
-@@ -1666,6 +1694,9 @@ func_determine_path_separator
+@@ -1673,6 +1701,9 @@ func_determine_path_separator
fi
case "$autoconf_minversion" in
1.* | 2.[0-5]* | 2.6[0-3]*)
---- /dev/null
-+++ b/lib/fts.h
-@@ -0,0 +1,6 @@
-+#ifdef __APPLE__
-+# define _FTS_H_ 1
-+# include <fts_.h>
-+#else
-+# include_next <fts.h>
-+#endif
+--- a/lib/fts.c
++++ b/lib/fts.c
+@@ -50,7 +50,7 @@
+ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
+ #endif
+
+-#include "fts_.h"
++#include "fts.h"
+
+ #if HAVE_SYS_PARAM_H || defined _LIBC
+ # include <sys/param.h>
+--- a/lib/fts.in.h
++++ b/lib/fts.in.h
+@@ -48,6 +48,11 @@
+
+ #ifndef _FTS_H
+ # define _FTS_H 1
++# ifdef __APPLE__
++# ifndef _FTS_H_
++# define _FTS_H_ 1
++# endif
++# endif
+
+ /* This file uses _GL_ATTRIBUTE_DEALLOC, _GL_ATTRIBUTE_NODISCARD. */
+ # if !_LIBC && !_GL_CONFIG_H_INCLUDED
--- a/modules/fts
+++ b/modules/fts
-@@ -2,6 +2,7 @@ Description:
- Traverse a file hierarchy.
-
- Files:
-+lib/fts.h
- lib/fts_.h
- lib/fts.c
- lib/fts-cycle.c
+@@ -48,21 +48,21 @@ dnl NetBSD versions have bugs and/or unn
+ gl_CONDITIONAL([GL_COND_OBJ_FTS], [true])
+
+ Makefile.am:
+-BUILT_SOURCES += fts_.h
++BUILT_SOURCES += fts.h
+ if GL_COND_OBJ_FTS
+ lib_SOURCES += fts.c
+ endif
+
+-fts_.h: fts.in.h
++fts.h: fts.in.h
+ @NMD@ $(AM_V_GEN)$(MKDIR_P) '%reldir%'
+ $(gl_V_at)$(SED_HEADER_STDOUT) \
+ -e 's|@''HAVE_SYS_CDEFS_H''@|$(HAVE_SYS_CDEFS_H)|g' \
+ $(srcdir)/fts.in.h > $@-t
+ $(AM_V_at)mv $@-t $@
+-MOSTLYCLEANFILES += fts_.h fts_.h-t
++MOSTLYCLEANFILES += fts.h fts.h-t
+
+ Include:
+-"fts_.h"
++"fts.h"
+
+ License:
+ GPL
#endif
--- a/lib/xmalloc.c
+++ b/lib/xmalloc.c
-@@ -51,12 +51,16 @@ ximalloc (idx_t s)
+@@ -57,12 +57,16 @@ ximalloc (idx_t s)
return check_nonnull (imalloc (s));
}
/* Change the size of an allocated block of memory P to S bytes,
with error checking. */
-@@ -75,6 +79,8 @@ xirealloc (void *p, idx_t s)
+@@ -81,6 +85,8 @@ xirealloc (void *p, idx_t s)
return check_nonnull (irealloc (p, s));
}
/* Change the size of an allocated block of memory P to an array of N
objects each of S bytes, with error checking. */
-@@ -205,6 +211,8 @@ x2nrealloc (void *p, size_t *pn, size_t
+@@ -211,6 +217,8 @@ x2nrealloc (void *p, size_t *pn, size_t
return p;
}
static void addtok_wc (struct dfa *dfa, wint_t wc);
/* Add the given token to the parse tree, maintaining the depth count and
-@@ -2934,6 +2938,8 @@ dfaanalyze (struct dfa *d, bool searchfl
+@@ -2935,6 +2939,8 @@ dfaanalyze (struct dfa *d, bool searchfl
free (tmp.elems);
}
/* Make sure D's state arrays are large enough to hold NEW_STATE. */
static void
realloc_trans_if_necessary (struct dfa *d)
-@@ -2969,6 +2975,8 @@ realloc_trans_if_necessary (struct dfa *
+@@ -2970,6 +2976,8 @@ realloc_trans_if_necessary (struct dfa *
}
}
/*
Calculate the transition table for a new state derived from state s
for a compiled dfa d after input character uc, and return the new
-@@ -4010,6 +4018,8 @@ freelist (char **cpp)
+@@ -4011,6 +4019,8 @@ freelist (char **cpp)
free (*cpp++);
}
static char **
enlistnew (char **cpp, char *new)
{
-@@ -4046,6 +4056,8 @@ enlist (char **cpp, char const *str, idx
+@@ -4047,6 +4057,8 @@ enlist (char **cpp, char const *str, idx
return enlistnew (cpp, ximemdup0 (str, len));
}
--- a/lib/fcntl.c
+++ b/lib/fcntl.c
-@@ -198,6 +198,8 @@ static int klibc_fcntl (int fd, int acti
+@@ -197,6 +197,8 @@ static int klibc_fcntl (int fd, int acti
FD_CLOEXEC is portable, but other flags may be present); otherwise
return -1 and set errno. */
int
fcntl (int fd, int action, /* arg */...)
#undef fcntl
-@@ -443,6 +445,8 @@ fcntl (int fd, int action, /* arg */...)
+@@ -442,6 +444,8 @@ fcntl (int fd, int action, /* arg */...)
return result;
}
{
--- a/lib/stdlib.in.h
+++ b/lib/stdlib.in.h
-@@ -1549,11 +1549,17 @@ _GL_FUNCDECL_RPL (reallocarray, void *,
+@@ -1577,11 +1577,17 @@ _GL_FUNCDECL_RPL (reallocarray, void *,
_GL_ATTRIBUTE_NODISCARD);
_GL_CXXALIAS_RPL (reallocarray, void *,
(void *ptr, size_t nmemb, size_t size));
-e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
--- a/lib/fcntl.in.h
+++ b/lib/fcntl.in.h
-@@ -241,6 +241,33 @@ _GL_WARN_ON_USE (openat, "openat is not
+@@ -249,6 +249,33 @@ _GL_WARN_ON_USE (openat, "openat is not
# endif
#endif
+++ /dev/null
-From 5a842672e79a7a5f6be837c483be4f9901a4ecc0 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Wed, 30 Apr 2025 03:19:10 +0200
-Subject: [PATCH] New module mem-hash-map.
-
-* lib/mem-hash-map.h: New file, from GNU gettext.
-* lib/mem-hash-map.c: New file, from GNU gettext.
-* modules/mem-hash-map: New file, from GNU gettext.
----
- ChangeLog | 7 +
- lib/mem-hash-map.c | 352 +++++++++++++++++++++++++++++++++++++++++++
- lib/mem-hash-map.h | 90 +++++++++++
- modules/mem-hash-map | 25 +++
- 4 files changed, 474 insertions(+)
- create mode 100644 lib/mem-hash-map.c
- create mode 100644 lib/mem-hash-map.h
- create mode 100644 modules/mem-hash-map
-
---- /dev/null
-+++ b/lib/mem-hash-map.c
-@@ -0,0 +1,352 @@
-+/* Simple hash table (no removals) where the keys are memory blocks.
-+ Copyright (C) 1994-2025 Free Software Foundation, Inc.
-+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, October 1994.
-+
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published
-+ by the Free Software Foundation, either version 3 of the License,
-+ or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU General Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+#include <config.h>
-+
-+/* Specification. */
-+#include "mem-hash-map.h"
-+
-+#include <stdlib.h>
-+#include <string.h>
-+#include <stdio.h>
-+#include <limits.h>
-+#include <sys/types.h>
-+
-+#include "next-prime.h"
-+
-+/* Since this simple implementation of hash tables allows only insertion, no
-+ removal of entries, the right data structure for the memory holding all keys
-+ is an obstack. */
-+#include "obstack.h"
-+
-+/* Use checked memory allocation. */
-+#include "xalloc.h"
-+
-+#define obstack_chunk_alloc xmalloc
-+#define obstack_chunk_free free
-+
-+
-+typedef struct hash_entry
-+{
-+ size_t used; /* Hash code of the key, or 0 for an unused entry. */
-+ const void *key; /* Key. */
-+ size_t keylen;
-+ void *data; /* Value. */
-+ struct hash_entry *next;
-+}
-+hash_entry;
-+
-+
-+/* Initialize a hash table. INIT_SIZE > 1 is the initial number of available
-+ entries.
-+ Return 0 always. */
-+int
-+hash_init (hash_table *htab, size_t init_size)
-+{
-+ /* We need the size to be a prime. */
-+ init_size = next_prime (init_size);
-+
-+ /* Initialize the data structure. */
-+ htab->size = init_size;
-+ htab->filled = 0;
-+ htab->first = NULL;
-+ htab->table = XCALLOC (init_size + 1, hash_entry);
-+
-+ obstack_init (&htab->mem_pool);
-+
-+ return 0;
-+}
-+
-+
-+/* Delete a hash table's contents.
-+ Return 0 always. */
-+int
-+hash_destroy (hash_table *htab)
-+{
-+ free (htab->table);
-+ obstack_free (&htab->mem_pool, NULL);
-+ return 0;
-+}
-+
-+
-+/* Compute a hash code for a key consisting of KEYLEN bytes starting at KEY
-+ in memory. */
-+static size_t
-+compute_hashval (const void *key, size_t keylen)
-+{
-+ size_t cnt;
-+ size_t hval;
-+
-+ /* Compute the hash value for the given string. The algorithm
-+ is taken from [Aho,Sethi,Ullman], fixed according to
-+ https://haible.de/bruno/hashfunc.html. */
-+ cnt = 0;
-+ hval = keylen;
-+ while (cnt < keylen)
-+ {
-+ hval = (hval << 9) | (hval >> (sizeof (size_t) * CHAR_BIT - 9));
-+ hval += (size_t) *(((const char *) key) + cnt++);
-+ }
-+ return hval != 0 ? hval : ~((size_t) 0);
-+}
-+
-+
-+/* References:
-+ [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986
-+ [Knuth] The Art of Computer Programming, part3 (6.4) */
-+
-+/* Look up a given key in the hash table.
-+ Return the index of the entry, if present, or otherwise the index a free
-+ entry where it could be inserted. */
-+static size_t
-+lookup (const hash_table *htab,
-+ const void *key, size_t keylen,
-+ size_t hval)
-+{
-+ size_t hash;
-+ size_t idx;
-+ hash_entry *table = htab->table;
-+
-+ /* First hash function: simply take the modul but prevent zero. */
-+ hash = 1 + hval % htab->size;
-+
-+ idx = hash;
-+
-+ if (table[idx].used)
-+ {
-+ if (table[idx].used == hval && table[idx].keylen == keylen
-+ && memcmp (table[idx].key, key, keylen) == 0)
-+ return idx;
-+
-+ /* Second hash function as suggested in [Knuth]. */
-+ hash = 1 + hval % (htab->size - 2);
-+
-+ do
-+ {
-+ if (idx <= hash)
-+ idx = htab->size + idx - hash;
-+ else
-+ idx -= hash;
-+
-+ /* If entry is found use it. */
-+ if (table[idx].used == hval && table[idx].keylen == keylen
-+ && memcmp (table[idx].key, key, keylen) == 0)
-+ return idx;
-+ }
-+ while (table[idx].used);
-+ }
-+ return idx;
-+}
-+
-+
-+/* Look up the value of a key in the given table.
-+ If found, return 0 and set *RESULT to it. Otherwise return -1. */
-+int
-+hash_find_entry (const hash_table *htab, const void *key, size_t keylen,
-+ void **result)
-+{
-+ hash_entry *table = htab->table;
-+ size_t idx = lookup (htab, key, keylen, compute_hashval (key, keylen));
-+
-+ if (table[idx].used == 0)
-+ return -1;
-+
-+ *result = table[idx].data;
-+ return 0;
-+}
-+
-+
-+/* Insert the pair (KEY[0..KEYLEN-1], DATA) in the hash table at index IDX.
-+ HVAL is the key's hash code. IDX depends on it. The table entry at index
-+ IDX is known to be unused. */
-+static void
-+insert_entry_2 (hash_table *htab,
-+ const void *key, size_t keylen,
-+ size_t hval, size_t idx, void *data)
-+{
-+ hash_entry *table = htab->table;
-+
-+ table[idx].used = hval;
-+ table[idx].key = key;
-+ table[idx].keylen = keylen;
-+ table[idx].data = data;
-+
-+ /* List the new value in the list. */
-+ if (htab->first == NULL)
-+ {
-+ table[idx].next = &table[idx];
-+ htab->first = &table[idx];
-+ }
-+ else
-+ {
-+ table[idx].next = htab->first->next;
-+ htab->first->next = &table[idx];
-+ htab->first = &table[idx];
-+ }
-+
-+ ++htab->filled;
-+}
-+
-+
-+/* Grow the hash table. */
-+static void
-+resize (hash_table *htab)
-+{
-+ size_t old_size = htab->size;
-+ hash_entry *table = htab->table;
-+ size_t idx;
-+
-+ htab->size = next_prime (htab->size * 2);
-+ htab->filled = 0;
-+ htab->first = NULL;
-+ htab->table = XCALLOC (1 + htab->size, hash_entry);
-+
-+ for (idx = 1; idx <= old_size; ++idx)
-+ if (table[idx].used)
-+ insert_entry_2 (htab, table[idx].key, table[idx].keylen,
-+ table[idx].used,
-+ lookup (htab, table[idx].key, table[idx].keylen,
-+ table[idx].used),
-+ table[idx].data);
-+
-+ free (table);
-+}
-+
-+
-+/* Try to insert the pair (KEY[0..KEYLEN-1], DATA) in the hash table.
-+ Return non-NULL (more precisely, the address of the KEY inside the table's
-+ memory pool) if successful, or NULL if there is already an entry with the
-+ given key. */
-+const void *
-+hash_insert_entry (hash_table *htab,
-+ const void *key, size_t keylen,
-+ void *data)
-+{
-+ size_t hval = compute_hashval (key, keylen);
-+ hash_entry *table = htab->table;
-+ size_t idx = lookup (htab, key, keylen, hval);
-+
-+ if (table[idx].used)
-+ /* We don't want to overwrite the old value. */
-+ return NULL;
-+ else
-+ {
-+ /* An empty bucket has been found. */
-+ void *keycopy = obstack_copy (&htab->mem_pool, key, keylen);
-+ insert_entry_2 (htab, keycopy, keylen, hval, idx, data);
-+ if (100 * htab->filled > 75 * htab->size)
-+ /* Table is filled more than 75%. Resize the table. */
-+ resize (htab);
-+ return keycopy;
-+ }
-+}
-+
-+
-+/* Insert the pair (KEY[0..KEYLEN-1], DATA) in the hash table.
-+ Return 0. */
-+int
-+hash_set_value (hash_table *htab,
-+ const void *key, size_t keylen,
-+ void *data)
-+{
-+ size_t hval = compute_hashval (key, keylen);
-+ hash_entry *table = htab->table;
-+ size_t idx = lookup (htab, key, keylen, hval);
-+
-+ if (table[idx].used)
-+ {
-+ /* Overwrite the old value. */
-+ table[idx].data = data;
-+ return 0;
-+ }
-+ else
-+ {
-+ /* An empty bucket has been found. */
-+ void *keycopy = obstack_copy (&htab->mem_pool, key, keylen);
-+ insert_entry_2 (htab, keycopy, keylen, hval, idx, data);
-+ if (100 * htab->filled > 75 * htab->size)
-+ /* Table is filled more than 75%. Resize the table. */
-+ resize (htab);
-+ return 0;
-+ }
-+}
-+
-+
-+/* Steps *PTR forward to the next used entry in the given hash table. *PTR
-+ should be initially set to NULL. Store information about the next entry
-+ in *KEY, *KEYLEN, *DATA.
-+ Return 0 normally, -1 when the whole hash table has been traversed. */
-+int
-+hash_iterate (hash_table *htab, void **ptr, const void **key, size_t *keylen,
-+ void **data)
-+{
-+ hash_entry *curr;
-+
-+ if (*ptr == NULL)
-+ {
-+ if (htab->first == NULL)
-+ return -1;
-+ curr = htab->first;
-+ }
-+ else
-+ {
-+ if (*ptr == htab->first)
-+ return -1;
-+ curr = (hash_entry *) *ptr;
-+ }
-+ curr = curr->next;
-+ *ptr = (void *) curr;
-+
-+ *key = curr->key;
-+ *keylen = curr->keylen;
-+ *data = curr->data;
-+ return 0;
-+}
-+
-+
-+/* Steps *PTR forward to the next used entry in the given hash table. *PTR
-+ should be initially set to NULL. Store information about the next entry
-+ in *KEY, *KEYLEN, *DATAP. *DATAP is set to point to the storage of the
-+ value; modifying **DATAP will modify the value of the entry.
-+ Return 0 normally, -1 when the whole hash table has been traversed. */
-+int
-+hash_iterate_modify (hash_table *htab, void **ptr,
-+ const void **key, size_t *keylen,
-+ void ***datap)
-+{
-+ hash_entry *curr;
-+
-+ if (*ptr == NULL)
-+ {
-+ if (htab->first == NULL)
-+ return -1;
-+ curr = htab->first;
-+ }
-+ else
-+ {
-+ if (*ptr == htab->first)
-+ return -1;
-+ curr = (hash_entry *) *ptr;
-+ }
-+ curr = curr->next;
-+ *ptr = (void *) curr;
-+
-+ *key = curr->key;
-+ *keylen = curr->keylen;
-+ *datap = &curr->data;
-+ return 0;
-+}
---- /dev/null
-+++ b/lib/mem-hash-map.h
-@@ -0,0 +1,90 @@
-+/* Simple hash table (no removals) where the keys are memory blocks.
-+ Copyright (C) 1995-2025 Free Software Foundation, Inc.
-+
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published
-+ by the Free Software Foundation, either version 3 of the License,
-+ or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU General Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+#ifndef _GL_MEM_HASH_MAP_H
-+#define _GL_MEM_HASH_MAP_H
-+
-+#include <stddef.h>
-+
-+#include "obstack.h"
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif
-+
-+struct hash_entry;
-+
-+typedef struct hash_table
-+{
-+ size_t size; /* Number of allocated entries. */
-+ size_t filled; /* Number of used entries. */
-+ struct hash_entry *first; /* Pointer to head of list of entries. */
-+ struct hash_entry *table; /* Pointer to array of entries. */
-+ struct obstack mem_pool; /* Memory pool holding the keys. */
-+}
-+hash_table;
-+
-+/* Initialize a hash table. INIT_SIZE > 1 is the initial number of available
-+ entries.
-+ Return 0 always. */
-+extern int hash_init (hash_table *htab, size_t init_size);
-+
-+/* Delete a hash table's contents.
-+ Return 0 always. */
-+extern int hash_destroy (hash_table *htab);
-+
-+/* Look up the value of a key in the given table.
-+ If found, return 0 and set *RESULT to it. Otherwise return -1. */
-+extern int hash_find_entry (const hash_table *htab,
-+ const void *key, size_t keylen,
-+ void **result);
-+
-+/* Try to insert the pair (KEY[0..KEYLEN-1], DATA) in the hash table.
-+ Return non-NULL (more precisely, the address of the KEY inside the table's
-+ memory pool) if successful, or NULL if there is already an entry with the
-+ given key. */
-+extern const void * hash_insert_entry (hash_table *htab,
-+ const void *key, size_t keylen,
-+ void *data);
-+
-+/* Insert the pair (KEY[0..KEYLEN-1], DATA) in the hash table.
-+ Return 0. */
-+extern int hash_set_value (hash_table *htab,
-+ const void *key, size_t keylen,
-+ void *data);
-+
-+/* Steps *PTR forward to the next used entry in the given hash table. *PTR
-+ should be initially set to NULL. Store information about the next entry
-+ in *KEY, *KEYLEN, *DATA.
-+ Return 0 normally, -1 when the whole hash table has been traversed. */
-+extern int hash_iterate (hash_table *htab, void **ptr,
-+ const void **key, size_t *keylen,
-+ void **data);
-+
-+/* Steps *PTR forward to the next used entry in the given hash table. *PTR
-+ should be initially set to NULL. Store information about the next entry
-+ in *KEY, *KEYLEN, *DATAP. *DATAP is set to point to the storage of the
-+ value; modifying **DATAP will modify the value of the entry.
-+ Return 0 normally, -1 when the whole hash table has been traversed. */
-+extern int hash_iterate_modify (hash_table *htab, void **ptr,
-+ const void **key, size_t *keylen,
-+ void ***datap);
-+
-+#ifdef __cplusplus
-+}
-+#endif
-+
-+#endif /* not _GL_MEM_HASH_MAP_H */
---- /dev/null
-+++ b/modules/mem-hash-map
-@@ -0,0 +1,25 @@
-+Description:
-+Simple hash table (no removals) where the keys are memory blocks.
-+
-+Files:
-+lib/mem-hash-map.h
-+lib/mem-hash-map.c
-+
-+Depends-on:
-+next-prime
-+obstack
-+xalloc
-+
-+configure.ac:
-+
-+Makefile.am:
-+lib_SOURCES += mem-hash-map.h mem-hash-map.c
-+
-+Include:
-+"mem-hash-map.h"
-+
-+License:
-+GPL
-+
-+Maintainer:
-+Bruno Haible
+++ /dev/null
-From 0b953ba82830f51ce9b939700705d238f9b0c0ba Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Wed, 30 Apr 2025 01:52:17 +0200
-Subject: [PATCH] New module next-prime.
-
-* lib/next-prime.h: New file, based on lib/hash.c.
-* lib/next-prime.c: New file, based on lib/hash.c.
-* modules/next-prime: New file.
-* lib/hash.c: Include next-prime.h.
-(is_prime, next_prime): Remove functions.
-* modules/hash (Depends-on): Add next-prime.
----
- ChangeLog | 10 +++++++++
- lib/hash.c | 39 +-------------------------------
- lib/next-prime.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++
- lib/next-prime.h | 41 +++++++++++++++++++++++++++++++++
- modules/hash | 1 +
- modules/next-prime | 24 ++++++++++++++++++++
- 6 files changed, 133 insertions(+), 38 deletions(-)
- create mode 100644 lib/next-prime.c
- create mode 100644 lib/next-prime.h
- create mode 100644 modules/next-prime
-
---- a/lib/hash.c
-+++ b/lib/hash.c
-@@ -27,6 +27,7 @@
- #include "hash.h"
-
- #include "bitrotate.h"
-+#include "next-prime.h"
- #include "xalloc-oversized.h"
-
- #include <errno.h>
-@@ -390,44 +391,6 @@ hash_string (const char *string, size_t
-
- #endif /* not USE_DIFF_HASH */
-
--/* Return true if CANDIDATE is a prime number. CANDIDATE should be an odd
-- number at least equal to 11. */
--
--static bool _GL_ATTRIBUTE_CONST
--is_prime (size_t candidate)
--{
-- size_t divisor = 3;
-- size_t square = divisor * divisor;
--
-- while (square < candidate && (candidate % divisor))
-- {
-- divisor++;
-- square += 4 * divisor;
-- divisor++;
-- }
--
-- return (candidate % divisor ? true : false);
--}
--
--/* Round a given CANDIDATE number up to the nearest prime, and return that
-- prime. Primes lower than 10 are merely skipped. */
--
--static size_t _GL_ATTRIBUTE_CONST
--next_prime (size_t candidate)
--{
-- /* Skip small primes. */
-- if (candidate < 10)
-- candidate = 10;
--
-- /* Make it definitely odd. */
-- candidate |= 1;
--
-- while (SIZE_MAX != candidate && !is_prime (candidate))
-- candidate += 2;
--
-- return candidate;
--}
--
- void
- hash_reset_tuning (Hash_tuning *tuning)
- {
---- /dev/null
-+++ b/lib/next-prime.c
-@@ -0,0 +1,56 @@
-+/* Finding the next prime >= a given small integer.
-+ Copyright (C) 1995-2025 Free Software Foundation, Inc.
-+
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as
-+ published by the Free Software Foundation; either version 2.1 of the
-+ License, or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU Lesser General Public License for more details.
-+
-+ You should have received a copy of the GNU Lesser General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+#include <config.h>
-+
-+/* Specification. */
-+#include "next-prime.h"
-+
-+#include <stdint.h> /* for SIZE_MAX */
-+
-+/* Return true if CANDIDATE is a prime number. CANDIDATE should be an odd
-+ number at least equal to 11. */
-+static bool _GL_ATTRIBUTE_CONST
-+is_prime (size_t candidate)
-+{
-+ size_t divisor = 3;
-+ size_t square = divisor * divisor;
-+
-+ while (square < candidate && (candidate % divisor))
-+ {
-+ divisor++;
-+ square += 4 * divisor;
-+ divisor++;
-+ }
-+
-+ return (candidate % divisor ? true : false);
-+}
-+
-+size_t _GL_ATTRIBUTE_CONST
-+next_prime (size_t candidate)
-+{
-+ /* Skip small primes. */
-+ if (candidate < 10)
-+ candidate = 10;
-+
-+ /* Make it definitely odd. */
-+ candidate |= 1;
-+
-+ while (SIZE_MAX != candidate && !is_prime (candidate))
-+ candidate += 2;
-+
-+ return candidate;
-+}
---- /dev/null
-+++ b/lib/next-prime.h
-@@ -0,0 +1,41 @@
-+/* Finding the next prime >= a given small integer.
-+ Copyright (C) 1995-2025 Free Software Foundation, Inc.
-+
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as
-+ published by the Free Software Foundation; either version 2.1 of the
-+ License, or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU Lesser General Public License for more details.
-+
-+ You should have received a copy of the GNU Lesser General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+#ifndef _GL_NEXT_PRIME_H
-+#define _GL_NEXT_PRIME_H
-+
-+/* This file uses _GL_ATTRIBUTE_CONST. */
-+#if !_GL_CONFIG_H_INCLUDED
-+ #error "Please include config.h first."
-+#endif
-+
-+#include <stddef.h>
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif
-+
-+
-+/* Round a given CANDIDATE number up to the nearest prime, and return that
-+ prime. Primes lower than 10 are merely skipped. */
-+extern size_t _GL_ATTRIBUTE_CONST next_prime (size_t candidate);
-+
-+
-+#ifdef __cplusplus
-+}
-+#endif
-+
-+#endif /* _GL_NEXT_PRIME_H */
---- a/modules/hash
-+++ b/modules/hash
-@@ -10,6 +10,7 @@ bitrotate
- calloc-posix
- free-posix
- malloc-posix
-+next-prime
- bool
- stdint-h
- xalloc-oversized
---- /dev/null
-+++ b/modules/next-prime
-@@ -0,0 +1,24 @@
-+Description:
-+Finding the next prime >= a given small integer.
-+
-+Files:
-+lib/next-prime.h
-+lib/next-prime.c
-+
-+Depends-on:
-+bool
-+stdint-h
-+
-+configure.ac:
-+
-+Makefile.am:
-+lib_SOURCES += next-prime.h next-prime.c
-+
-+Include:
-+"next-prime.h"
-+
-+License:
-+LGPLv2+
-+
-+Maintainer:
-+all
+++ /dev/null
-From 64042bb91aea5f854ca8a8938e2b3f7d1935e4f1 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Wed, 30 Apr 2025 12:47:37 +0200
-Subject: [PATCH] New module hashcode-string1.
-
-* lib/hashcode-string1.h: New file.
-* lib/hashcode-string1.c: New file, based on lib/hash.c.
-* modules/hashcode-string1: New file.
-* lib/hash.h: Include hashcode-string1.h.
-(hash_string): Remove declaration.
-* lib/hash.c (hash_string): Remove function.
-* modules/hash (Depends-on): Add hashcode-string1.
-* lib/exclude.c: Include hashcode-string1.h.
-* modules/exclude (Depends-on): Add hashcode-string1.
----
- ChangeLog | 13 +++++++++
- lib/exclude.c | 1 +
- lib/hash.c | 59 ++++++--------------------------------
- lib/hash.h | 11 +++----
- lib/hashcode-string1.c | 62 ++++++++++++++++++++++++++++++++++++++++
- lib/hashcode-string1.h | 38 ++++++++++++++++++++++++
- modules/exclude | 1 +
- modules/hash | 1 +
- modules/hashcode-string1 | 24 ++++++++++++++++
- 9 files changed, 154 insertions(+), 56 deletions(-)
- create mode 100644 lib/hashcode-string1.c
- create mode 100644 lib/hashcode-string1.h
- create mode 100644 modules/hashcode-string1
-
---- a/lib/exclude.c
-+++ b/lib/exclude.c
-@@ -36,6 +36,7 @@
- #include "filename.h"
- #include <fnmatch.h>
- #include "hash.h"
-+#include "hashcode-string1.h"
- #if GNULIB_MCEL_PREFER
- # include "mcel.h"
- #else
---- a/lib/hash.c
-+++ b/lib/hash.c
-@@ -345,57 +345,6 @@ hash_do_for_each (const Hash_table *tabl
- return counter;
- }
-
--/* Allocation and clean-up. */
--
--#if USE_DIFF_HASH
--
--/* About hashings, Paul Eggert writes to me (FP), on 1994-01-01: "Please see
-- B. J. McKenzie, R. Harries & T. Bell, Selecting a hashing algorithm,
-- Software--practice & experience 20, 2 (Feb 1990), 209-224. Good hash
-- algorithms tend to be domain-specific, so what's good for [diffutils'] io.c
-- may not be good for your application." */
--
--size_t
--hash_string (const char *string, size_t n_buckets)
--{
--# define HASH_ONE_CHAR(Value, Byte) \
-- ((Byte) + rotl_sz (Value, 7))
--
-- size_t value = 0;
-- unsigned char ch;
--
-- for (; (ch = *string); string++)
-- value = HASH_ONE_CHAR (value, ch);
-- return value % n_buckets;
--
--# undef HASH_ONE_CHAR
--}
--
--#else /* not USE_DIFF_HASH */
--
--/* This one comes from 'recode', and performs a bit better than the above as
-- per a few experiments. It is inspired from a hashing routine found in the
-- very old Cyber 'snoop', itself written in typical Greg Mansfield style.
-- (By the way, what happened to this excellent man? Is he still alive?) */
--
--size_t
--hash_string (const char *string, size_t n_buckets)
--{
-- size_t value = 0;
-- unsigned char ch;
--
-- for (; (ch = *string); string++)
-- value = (value * 31 + ch) % n_buckets;
-- return value;
--}
--
--#endif /* not USE_DIFF_HASH */
--
--void
--hash_reset_tuning (Hash_tuning *tuning)
--{
-- *tuning = default_tuning;
--}
-
- /* If the user passes a NULL hasher, we hash the raw pointer. */
- static size_t
-@@ -418,6 +367,14 @@ raw_comparator (const void *a, const voi
- }
-
-
-+/* Allocation and clean-up. */
-+
-+void
-+hash_reset_tuning (Hash_tuning *tuning)
-+{
-+ *tuning = default_tuning;
-+}
-+
- /* For the given hash TABLE, check the user supplied tuning structure for
- reasonable values, and return true if there is no gross error with it.
- Otherwise, definitively reset the TUNING field to some acceptable default
---- a/lib/hash.h
-+++ b/lib/hash.h
-@@ -134,11 +134,6 @@ extern size_t hash_do_for_each (const Ha
- * Allocation and clean-up.
- */
-
--/* Return a hash index for a NUL-terminated STRING between 0 and N_BUCKETS-1.
-- This is a convenience routine for constructing other hashing functions. */
--extern size_t hash_string (const char *string, size_t n_buckets)
-- _GL_ATTRIBUTE_PURE;
--
- extern void hash_reset_tuning (Hash_tuning *tuning);
-
- typedef size_t (*Hash_hasher) (const void *entry, size_t table_size);
-@@ -266,6 +261,12 @@ extern void *hash_remove (Hash_table *ta
- _GL_ATTRIBUTE_DEPRECATED
- extern void *hash_delete (Hash_table *table, const void *entry);
-
-+
-+# if GNULIB_HASHCODE_STRING1
-+/* Include declarations of module 'hashcode-string1'. */
-+# include "hashcode-string1.h"
-+# endif
-+
- # ifdef __cplusplus
- }
- # endif
---- /dev/null
-+++ b/lib/hashcode-string1.c
-@@ -0,0 +1,62 @@
-+/* hashcode-string1.c -- compute a hash value from a NUL-terminated string.
-+
-+ Copyright (C) 1998-2004, 2006-2007, 2009-2025 Free Software Foundation, Inc.
-+
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as
-+ published by the Free Software Foundation; either version 2.1 of the
-+ License, or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU Lesser General Public License for more details.
-+
-+ You should have received a copy of the GNU Lesser General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+#include <config.h>
-+
-+/* Specification. */
-+#include "hashcode-string1.h"
-+
-+#if USE_DIFF_HASH
-+
-+# include "bitrotate.h"
-+
-+/* About hashings, Paul Eggert writes to me (FP), on 1994-01-01: "Please see
-+ B. J. McKenzie, R. Harries & T. Bell, Selecting a hashing algorithm,
-+ Software--practice & experience 20, 2 (Feb 1990), 209-224. Good hash
-+ algorithms tend to be domain-specific, so what's good for [diffutils'] io.c
-+ may not be good for your application." */
-+
-+size_t
-+hash_string (const char *string, size_t tablesize)
-+{
-+ size_t value = 0;
-+ unsigned char ch;
-+
-+ for (; (ch = *string); string++)
-+ value = ch + rotl_sz (value, 7);
-+ return value % tablesize;
-+}
-+
-+#else /* not USE_DIFF_HASH */
-+
-+/* This one comes from 'recode', and performs a bit better than the above as
-+ per a few experiments. It is inspired from a hashing routine found in the
-+ very old Cyber 'snoop', itself written in typical Greg Mansfield style.
-+ (By the way, what happened to this excellent man? Is he still alive?) */
-+
-+size_t
-+hash_string (const char *string, size_t tablesize)
-+{
-+ size_t value = 0;
-+ unsigned char ch;
-+
-+ for (; (ch = *string); string++)
-+ value = (value * 31 + ch) % tablesize;
-+ return value;
-+}
-+
-+#endif /* not USE_DIFF_HASH */
---- /dev/null
-+++ b/lib/hashcode-string1.h
-@@ -0,0 +1,38 @@
-+/* hashcode-string1.h -- declaration for a simple hash function
-+ Copyright (C) 1998-2004, 2006-2007, 2009-2025 Free Software Foundation, Inc.
-+
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as
-+ published by the Free Software Foundation; either version 2.1 of the
-+ License, or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU Lesser General Public License for more details.
-+
-+ You should have received a copy of the GNU Lesser General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+/* This file uses _GL_ATTRIBUTE_PURE. */
-+#if !_GL_CONFIG_H_INCLUDED
-+ #error "Please include config.h first."
-+#endif
-+
-+#include <stddef.h>
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif
-+
-+
-+/* Compute a hash code for a NUL-terminated string S,
-+ and return the hash code modulo TABLESIZE.
-+ The result is platform dependent: it depends on the size of the 'size_t'
-+ type. */
-+extern size_t hash_string (char const *s, size_t tablesize) _GL_ATTRIBUTE_PURE;
-+
-+
-+#ifdef __cplusplus
-+}
-+#endif
---- a/modules/exclude
-+++ b/modules/exclude
-@@ -12,6 +12,7 @@ filename
- fnmatch
- fopen-gnu
- hash
-+hashcode-string1
- mbscasecmp
- mbuiter [test "$GNULIB_MCEL_PREFER" != yes]
- nullptr
---- a/modules/hash
-+++ b/modules/hash
-@@ -14,6 +14,7 @@ next-prime
- bool
- stdint-h
- xalloc-oversized
-+hashcode-string1
-
- configure.ac:
-
---- /dev/null
-+++ b/modules/hashcode-string1
-@@ -0,0 +1,24 @@
-+Description:
-+Compute a hash value for a NUL-terminated string.
-+
-+Files:
-+lib/hashcode-string1.h
-+lib/hashcode-string1.c
-+
-+Depends-on:
-+bitrotate
-+
-+configure.ac:
-+gl_MODULE_INDICATOR([hashcode-string1])
-+
-+Makefile.am:
-+lib_SOURCES += hashcode-string1.h hashcode-string1.c
-+
-+Include:
-+"hashcode-string1.h"
-+
-+License:
-+LGPLv2+
-+
-+Maintainer:
-+Jim Meyering
+++ /dev/null
-From 52738dcd0f522b16653cc8b21adfcb758702f2ab Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Wed, 30 Apr 2025 01:20:17 +0200
-Subject: [PATCH] New module hashkey-string.
-
-* lib/hashkey-string.h: New file.
-* lib/hashkey-string.c: New file, based on lib/clean-temp-simple.c.
-* modules/hashkey-string: New file.
-* lib/clean-temp-simple.c: Include hashkey-string.h. Don't include
-<limits.h>.
-(clean_temp_string_equals, clean_temp_string_hash): Remove functions.
-(SIZE_BITS): Remove macro.
-(register_temporary_file): Use hashkey_string_equals and
-hashkey_string_hash.
-* modules/clean-temp-simple (Depends-on): Add hashkey-string.
----
- ChangeLog | 14 ++++++++++++
- lib/clean-temp-simple.c | 33 +++------------------------
- lib/hashkey-string.c | 48 +++++++++++++++++++++++++++++++++++++++
- lib/hashkey-string.h | 35 ++++++++++++++++++++++++++++
- modules/clean-temp-simple | 1 +
- modules/hashkey-string | 23 +++++++++++++++++++
- 6 files changed, 124 insertions(+), 30 deletions(-)
- create mode 100644 lib/hashkey-string.c
- create mode 100644 lib/hashkey-string.h
- create mode 100644 modules/hashkey-string
-
---- a/lib/clean-temp-simple.c
-+++ b/lib/clean-temp-simple.c
-@@ -22,7 +22,6 @@
- #include "clean-temp-private.h"
-
- #include <errno.h>
--#include <limits.h>
- #include <signal.h>
- #include <stdlib.h>
- #include <string.h>
-@@ -36,6 +35,7 @@
- #include "thread-optim.h"
- #include "gl_list.h"
- #include "gl_linkedhash_list.h"
-+#include "hashkey-string.h"
- #include "gettext.h"
-
- #define _(msgid) dgettext ("gnulib", msgid)
-@@ -106,33 +106,6 @@ gl_list_t /* <closeable_fd *> */ volatil
- asynchronous signal.
- */
-
--/* String equality and hash code functions used by the lists. */
--
--bool
--clean_temp_string_equals (const void *x1, const void *x2)
--{
-- const char *s1 = (const char *) x1;
-- const char *s2 = (const char *) x2;
-- return strcmp (s1, s2) == 0;
--}
--
--#define SIZE_BITS (sizeof (size_t) * CHAR_BIT)
--
--/* A hash function for NUL-terminated char* strings using
-- the method described by Bruno Haible.
-- See https://www.haible.de/bruno/hashfunc.html. */
--size_t
--clean_temp_string_hash (const void *x)
--{
-- const char *s = (const char *) x;
-- size_t h = 0;
--
-- for (; *s; s++)
-- h = *s + ((h << 9) | (h >> (SIZE_BITS - 9)));
--
-- return h;
--}
--
-
- /* The set of fatal signal handlers.
- Cached here because we are not allowed to call get_fatal_signal_set ()
-@@ -326,8 +299,8 @@ register_temporary_file (const char *abs
- }
- file_cleanup_list =
- gl_list_nx_create_empty (GL_LINKEDHASH_LIST,
-- clean_temp_string_equals,
-- clean_temp_string_hash,
-+ hashkey_string_equals,
-+ hashkey_string_hash,
- NULL, false);
- if (file_cleanup_list == NULL)
- {
---- /dev/null
-+++ b/lib/hashkey-string.c
-@@ -0,0 +1,48 @@
-+/* Support for using a string as a hash key.
-+ Copyright (C) 2006-2025 Free Software Foundation, Inc.
-+
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as
-+ published by the Free Software Foundation; either version 2.1 of the
-+ License, or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU Lesser General Public License for more details.
-+
-+ You should have received a copy of the GNU Lesser General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+#include <config.h>
-+
-+/* Specification. */
-+#include "hashkey-string.h"
-+
-+#include <limits.h>
-+#include <string.h>
-+
-+bool
-+hashkey_string_equals (const void *x1, const void *x2)
-+{
-+ const char *s1 = (const char *) x1;
-+ const char *s2 = (const char *) x2;
-+ return strcmp (s1, s2) == 0;
-+}
-+
-+#define SIZE_BITS (sizeof (size_t) * CHAR_BIT)
-+
-+/* A hash function for NUL-terminated 'const char *' strings using
-+ the method described by Bruno Haible.
-+ See https://www.haible.de/bruno/hashfunc.html. */
-+size_t
-+hashkey_string_hash (const void *x)
-+{
-+ const char *s = (const char *) x;
-+ size_t h = 0;
-+
-+ for (; *s; s++)
-+ h = *s + ((h << 9) | (h >> (SIZE_BITS - 9)));
-+
-+ return h;
-+}
---- /dev/null
-+++ b/lib/hashkey-string.h
-@@ -0,0 +1,35 @@
-+/* Support for using a string as a hash key.
-+ Copyright (C) 2006-2025 Free Software Foundation, Inc.
-+
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as
-+ published by the Free Software Foundation; either version 2.1 of the
-+ License, or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU Lesser General Public License for more details.
-+
-+ You should have received a copy of the GNU Lesser General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+#ifndef _GL_HASHKEY_STRING_H
-+#define _GL_HASHKEY_STRING_H
-+
-+#include <stddef.h>
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif
-+
-+/* String equality and hash code functions that operate on plain C strings
-+ ('const char *'). */
-+extern bool hashkey_string_equals (const void *x1, const void *x2);
-+extern size_t hashkey_string_hash (const void *x);
-+
-+#ifdef __cplusplus
-+}
-+#endif
-+
-+#endif /* _GL_HASHKEY_STRING_H */
---- a/modules/clean-temp-simple
-+++ b/modules/clean-temp-simple
-@@ -19,6 +19,7 @@ error
- fatal-signal
- rmdir
- linkedhash-list
-+hashkey-string
- gettext-h
- gnulib-i18n
-
---- /dev/null
-+++ b/modules/hashkey-string
-@@ -0,0 +1,23 @@
-+Description:
-+Support for using a string as a hash key in the hash-set and hash-map modules.
-+
-+Files:
-+lib/hashkey-string.h
-+lib/hashkey-string.c
-+
-+Depends-on:
-+bool
-+
-+configure.ac:
-+
-+Makefile.am:
-+lib_SOURCES += hashkey-string.h hashkey-string.c
-+
-+Include:
-+"hashkey-string.h"
-+
-+License:
-+LGPLv2+
-+
-+Maintainer:
-+all
+++ /dev/null
-From e518788ad085e02b046e42889039a1f671e4619a Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Wed, 22 Jan 2025 21:21:59 +0100
-Subject: New module 'package-version'.
-
-* m4/init-package-version.m4: New file, from GNU libunistring.
-* modules/package-version: New file.
-* modules/git-version-gen (Depends-on): Add it.
----
- ChangeLog | 7 +++
- m4/init-package-version.m4 | 124 +++++++++++++++++++++++++++++++++++++++++++++
- modules/git-version-gen | 1 +
- modules/package-version | 19 +++++++
- 4 files changed, 151 insertions(+)
- create mode 100644 m4/init-package-version.m4
- create mode 100644 modules/package-version
-
---- /dev/null
-+++ b/m4/init-package-version.m4
-@@ -0,0 +1,124 @@
-+# init-package-version.m4
-+# serial 3
-+dnl Copyright (C) 1992-2025 Free Software Foundation, Inc.
-+dnl This file is free software, distributed under the terms of the GNU
-+dnl General Public License. As a special exception to the GNU General
-+dnl Public License, this file may be distributed as part of a program
-+dnl that contains a configuration script generated by Autoconf, under
-+dnl the same distribution terms as the rest of that program.
-+
-+# Make it possible to pass version numbers extracted from a file in
-+# $(srcdir) to autoconf.
-+#
-+# Autoconf insists on passing the package name and version number to
-+# every generated .h file and every Makefile. This was a reasonable
-+# design at times when a version number was changed only once a month.
-+# Nowadays, people often assign a new version number once a week, or
-+# even change it each time a 'git' commit is made. Regenerating all
-+# the files that depend on configure.ac (aclocal.m4, configure,
-+# config.status, config.h, all Makefiles) may take 15 minutes. These
-+# delays can severely hamper development.
-+#
-+# An alternative is to store the version number in a file in $(srcdir)
-+# that is separate from configure.ac. It can be a data file, a shell
-+# script, a .m4 file, or other. The essential point is that the maintainer
-+# is responsible for creating Makefile dependencies to this version file
-+# for every file that needs to be rebuilt when the version changes. This
-+# typically includes
-+# - distributable documentation files that carry the version number,
-+# but does not include
-+# - aclocal.m4, configure, config.status, config.h, all Makefiles,
-+# - executables.
-+#
-+# autoconf and automake make it hard to follow this approach:
-+#
-+# - If AC_INIT is used with arguments, there is a chicken-and-egg problem:
-+# The arguments need to be read from a file in $(srcdir). The location
-+# of $(srcdir) is only determined by AC_CONFIG_SRCDIR. AC_CONFIG_SRCDIR
-+# can only appear after AC_INIT (otherwise aclocal gives an error:
-+# "error: m4_defn: undefined macro: _m4_divert_diversion").
-+# Furthermore, the arguments passed to AC_INIT must be literals; for
-+# example, the assignment to PACKAGE_VERSION looks like this:
-+# [PACKAGE_VERSION=']AC_PACKAGE_VERSION[']
-+#
-+# - If AC_INIT is used without arguments:
-+# Automake provides its own variables, PACKAGE and VERSION, and uses them
-+# instead of PACKAGE_NAME and PACKAGE_VERSION that come from Autoconf.
-+# - If AM_INIT_AUTOMAKE is used with two arguments, automake options
-+# like 'silent-rules' cannot be specified.
-+# - If AM_INIT_AUTOMAKE is used in its one-argument form or without
-+# arguments at all, it triggers an error
-+# "error: AC_INIT should be called with package and version arguments".
-+# - If AM_INIT_AUTOMAKE is used in its one-argument form or without
-+# arguments at all, and _AC_INIT_PACKAGE is used before it, with
-+# the package and version number from the file as arguments, we get
-+# a warning: "warning: AC_INIT: not a literal: $VERSION_NUMBER".
-+# The arguments passed to _AC_INIT_PACKAGE must be literals.
-+#
-+# With the macro defined in this file, the approach can be coded like this:
-+#
-+# AC_INIT
-+# AC_CONFIG_SRCDIR(WITNESS)
-+# . $srcdir/../version.sh
-+# gl_INIT_PACKAGE(PACKAGE, $VERSION_NUMBER)
-+# AM_INIT_AUTOMAKE([OPTIONS])
-+#
-+# and after changing version.sh, the developer can directly configure and build:
-+#
-+# make distclean
-+# ./configure
-+# make
-+#
-+# Some other packages use another approach:
-+#
-+# AC_INIT(PACKAGE,
-+# m4_normalize(m4_esyscmd([. ./version.sh; echo $VERSION_NUMBER])))
-+# AC_CONFIG_SRCDIR(WITNESS)
-+# AM_INIT_AUTOMAKE([OPTIONS])
-+#
-+# but here, after changing version.sh, the developer must first regenerate the
-+# configure file:
-+#
-+# make distclean
-+# ./autogen.sh --skip-gnulib
-+# ./configure
-+# make
-+#
-+
-+# gl_INIT_PACKAGE(PACKAGE-NAME, VERSION)
-+# --------------------------------------
-+# followed by an AM_INIT_AUTOMAKE invocation,
-+# is like calling AM_INIT_AUTOMAKE(PACKAGE-NAME, VERSION)
-+# except that it can use computed non-literal arguments.
-+AC_DEFUN([gl_INIT_PACKAGE],
-+[
-+ AC_BEFORE([$0], [AM_INIT_AUTOMAKE])
-+ dnl Redefine AM_INIT_AUTOMAKE.
-+ m4_define([gl_AM_INIT_AUTOMAKE],
-+ m4_bpatsubst(m4_dquote(
-+ m4_bpatsubst(m4_dquote(
-+ m4_bpatsubst(m4_dquote(
-+ m4_defn([AM_INIT_AUTOMAKE])),
-+ [AC_PACKAGE_NAME], [gl_INIT_DUMMY])),
-+ [AC_PACKAGE_TARNAME], [gl_INIT_EMPTY])),
-+ [AC_PACKAGE_VERSION], [gl_INIT_DUMMY])
-+ [AC_SUBST([PACKAGE], [$1])
-+ AC_SUBST([VERSION], [$2])
-+ ])
-+ m4_define([AM_INIT_AUTOMAKE],
-+ m4_defn([gl_RPL_INIT_AUTOMAKE]))
-+])
-+m4_define([gl_INIT_EMPTY], [])
-+dnl Automake 1.16.4 no longer accepts an empty value for gl_INIT_DUMMY.
-+dnl But a macro that later expands to empty works.
-+m4_define([gl_INIT_DUMMY], [gl_INIT_DUMMY2])
-+m4_define([gl_INIT_DUMMY2], [])
-+AC_DEFUN([gl_RPL_INIT_AUTOMAKE], [
-+ m4_ifval([$2],
-+ [m4_fatal([After gl_INIT_PACKAGE, the two-argument form of AM_INIT_AUTOMAKE cannot be used.])])
-+ gl_AM_INIT_AUTOMAKE([$1 no-define])
-+ m4_if(m4_index([ $1 ], [ no-define ]), [-1],
-+ [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
-+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])
-+ ])
-+])
---- a/modules/git-version-gen
-+++ b/modules/git-version-gen
-@@ -5,6 +5,7 @@ Files:
- build-aux/git-version-gen
-
- Depends-on:
-+package-version
-
- configure.ac:
-
---- /dev/null
-+++ b/modules/package-version
-@@ -0,0 +1,19 @@
-+Description:
-+Support for a computed version string.
-+
-+Files:
-+m4/init-package-version.m4
-+
-+Depends-on:
-+
-+configure.ac:
-+
-+Makefile.am:
-+
-+Include:
-+
-+License:
-+GPLed build tool
-+
-+Maintainer:
-+Bruno Haible
+++ /dev/null
-From bb0f82be83d43db9cd77049be32ffd0b92ab5bb7 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Fri, 24 Jan 2025 22:03:29 +0100
-Subject: package-version: Simplify its use.
-
-Reported by Basil L. Contovounesios <basil@contovou.net> in
-<https://lists.gnu.org/archive/html/bug-gnulib/2025-01/msg00195.html>.
-
-* doc/package-version.texi (Propagating the package version): Recommend
-to pass the usual arguments to AC_INIT.
-* m4/init-package-version.m4: Likewise.
-(gl_INIT_PACKAGE): Define PACKAGE_VERSION and PACKAGE_STRING as needed.
-(gl_RPL_INIT_AUTOMAKE): Improve quoting.
----
- ChangeLog | 11 +++++++++++
- doc/package-version.texi | 2 +-
- m4/init-package-version.m4 | 20 ++++++++++++++------
- 3 files changed, 26 insertions(+), 7 deletions(-)
-
---- a/m4/init-package-version.m4
-+++ b/m4/init-package-version.m4
-@@ -1,5 +1,5 @@
- # init-package-version.m4
--# serial 3
-+# serial 4
- dnl Copyright (C) 1992-2025 Free Software Foundation, Inc.
- dnl This file is free software, distributed under the terms of the GNU
- dnl General Public License. As a special exception to the GNU General
-@@ -57,7 +57,7 @@ dnl the same distribution terms as the r
- #
- # With the macro defined in this file, the approach can be coded like this:
- #
--# AC_INIT
-+# AC_INIT(PACKAGE, [dummy], [MORE OPTIONS])
- # AC_CONFIG_SRCDIR(WITNESS)
- # . $srcdir/../version.sh
- # gl_INIT_PACKAGE(PACKAGE, $VERSION_NUMBER)
-@@ -102,8 +102,16 @@ AC_DEFUN([gl_INIT_PACKAGE],
- [AC_PACKAGE_NAME], [gl_INIT_DUMMY])),
- [AC_PACKAGE_TARNAME], [gl_INIT_EMPTY])),
- [AC_PACKAGE_VERSION], [gl_INIT_DUMMY])
-- [AC_SUBST([PACKAGE], [$1])
-- AC_SUBST([VERSION], [$2])
-+ [dnl Set variables documented in Automake.
-+ AC_SUBST([PACKAGE], [$1])
-+ AC_SUBST([VERSION], ["$2"])
-+ dnl Set variables documented in Autoconf.
-+ AC_SUBST([PACKAGE_VERSION], ["$2"])
-+ AC_SUBST([PACKAGE_STRING], ["$1 $2"])
-+ AC_DEFINE_UNQUOTED([PACKAGE_VERSION], ["$2"],
-+ [Define to the version of this package.])
-+ AC_DEFINE_UNQUOTED([PACKAGE_STRING], ["$1 $2"],
-+ [Define to the full name and version of this package.])
- ])
- m4_define([AM_INIT_AUTOMAKE],
- m4_defn([gl_RPL_INIT_AUTOMAKE]))
-@@ -118,7 +126,7 @@ AC_DEFUN([gl_RPL_INIT_AUTOMAKE], [
- [m4_fatal([After gl_INIT_PACKAGE, the two-argument form of AM_INIT_AUTOMAKE cannot be used.])])
- gl_AM_INIT_AUTOMAKE([$1 no-define])
- m4_if(m4_index([ $1 ], [ no-define ]), [-1],
-- [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
-- AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])
-+ [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
-+ AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])
- ])
- ])
+++ /dev/null
-From 48648b4b9b3fd79a5c68913deb28678bd9d8eb34 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Sat, 25 Jan 2025 04:07:32 +0100
-Subject: package-version: Simplify further.
-
-* doc/package-version.texi (Propagating the package version): Recommend
-use of gl_INIT_PACKAGE_VERSION instead of gl_INIT_PACKAGE.
-* build-aux/git-version-gen: Likewise.
-* m4/init-package-version.m4: Likewise.
-(gl_INIT_PACKAGE_VERSION): Renamed from gl_INIT_PACKAGE. Take only one
-argument. Don't fiddle with AC_PACKAGE_NAME, AC_PACKAGE_TARNAME,
-PACKAGE.
-(gl_RPL_INIT_AUTOMAKE): Update.
----
- ChangeLog | 10 ++++++++++
- build-aux/git-version-gen | 4 ++--
- doc/package-version.texi | 4 ++--
- m4/init-package-version.m4 | 30 ++++++++++++------------------
- 4 files changed, 26 insertions(+), 22 deletions(-)
-
---- a/m4/init-package-version.m4
-+++ b/m4/init-package-version.m4
-@@ -1,5 +1,5 @@
- # init-package-version.m4
--# serial 4
-+# serial 5
- dnl Copyright (C) 1992-2025 Free Software Foundation, Inc.
- dnl This file is free software, distributed under the terms of the GNU
- dnl General Public License. As a special exception to the GNU General
-@@ -60,7 +60,7 @@ dnl the same distribution terms as the r
- # AC_INIT(PACKAGE, [dummy], [MORE OPTIONS])
- # AC_CONFIG_SRCDIR(WITNESS)
- # . $srcdir/../version.sh
--# gl_INIT_PACKAGE(PACKAGE, $VERSION_NUMBER)
-+# gl_INIT_PACKAGE_VERSION($VERSION_NUMBER)
- # AM_INIT_AUTOMAKE([OPTIONS])
- #
- # and after changing version.sh, the developer can directly configure and build:
-@@ -85,32 +85,26 @@ dnl the same distribution terms as the r
- # make
- #
-
--# gl_INIT_PACKAGE(PACKAGE-NAME, VERSION)
--# --------------------------------------
-+# gl_INIT_PACKAGE_VERSION(VERSION)
-+# --------------------------------
- # followed by an AM_INIT_AUTOMAKE invocation,
- # is like calling AM_INIT_AUTOMAKE(PACKAGE-NAME, VERSION)
- # except that it can use computed non-literal arguments.
--AC_DEFUN([gl_INIT_PACKAGE],
-+AC_DEFUN([gl_INIT_PACKAGE_VERSION],
- [
- AC_BEFORE([$0], [AM_INIT_AUTOMAKE])
- dnl Redefine AM_INIT_AUTOMAKE.
- m4_define([gl_AM_INIT_AUTOMAKE],
-- m4_bpatsubst(m4_dquote(
-- m4_bpatsubst(m4_dquote(
-- m4_bpatsubst(m4_dquote(
-- m4_defn([AM_INIT_AUTOMAKE])),
-- [AC_PACKAGE_NAME], [gl_INIT_DUMMY])),
-- [AC_PACKAGE_TARNAME], [gl_INIT_EMPTY])),
-+ m4_bpatsubst(m4_dquote(m4_defn([AM_INIT_AUTOMAKE])),
- [AC_PACKAGE_VERSION], [gl_INIT_DUMMY])
- [dnl Set variables documented in Automake.
-- AC_SUBST([PACKAGE], [$1])
-- AC_SUBST([VERSION], ["$2"])
-+ AC_SUBST([VERSION], ["$1"])
- dnl Set variables documented in Autoconf.
-- AC_SUBST([PACKAGE_VERSION], ["$2"])
-- AC_SUBST([PACKAGE_STRING], ["$1 $2"])
-- AC_DEFINE_UNQUOTED([PACKAGE_VERSION], ["$2"],
-+ AC_SUBST([PACKAGE_VERSION], ["$1"])
-+ AC_SUBST([PACKAGE_STRING], ["AC_PACKAGE_NAME $1"])
-+ AC_DEFINE_UNQUOTED([PACKAGE_VERSION], ["$1"],
- [Define to the version of this package.])
-- AC_DEFINE_UNQUOTED([PACKAGE_STRING], ["$1 $2"],
-+ AC_DEFINE_UNQUOTED([PACKAGE_STRING], ["AC_PACKAGE_NAME $1"],
- [Define to the full name and version of this package.])
- ])
- m4_define([AM_INIT_AUTOMAKE],
-@@ -123,7 +117,7 @@ m4_define([gl_INIT_DUMMY], [gl_INIT_DUMM
- m4_define([gl_INIT_DUMMY2], [])
- AC_DEFUN([gl_RPL_INIT_AUTOMAKE], [
- m4_ifval([$2],
-- [m4_fatal([After gl_INIT_PACKAGE, the two-argument form of AM_INIT_AUTOMAKE cannot be used.])])
-+ [m4_fatal([After gl_INIT_PACKAGE_VERSION, the two-argument form of AM_INIT_AUTOMAKE cannot be used.])])
- gl_AM_INIT_AUTOMAKE([$1 no-define])
- m4_if(m4_index([ $1 ], [ no-define ]), [-1],
- [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
+++ /dev/null
-From 2e46209809f751087ca27523283bd5c3e9071d31 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Sun, 26 Jan 2025 13:26:35 +0100
-Subject: package-version: Avoid compiler warnings in config.log.
-
-* m4/init-package-version.m4 (gl_INIT_PACKAGE_VERSION): Undefine
-PACKAGE_VERSION and PACKAGE_STRING before redefining them.
----
- ChangeLog | 6 ++++++
- m4/init-package-version.m4 | 4 +++-
- 2 files changed, 9 insertions(+), 1 deletion(-)
-
---- a/m4/init-package-version.m4
-+++ b/m4/init-package-version.m4
-@@ -1,5 +1,5 @@
- # init-package-version.m4
--# serial 5
-+# serial 6
- dnl Copyright (C) 1992-2025 Free Software Foundation, Inc.
- dnl This file is free software, distributed under the terms of the GNU
- dnl General Public License. As a special exception to the GNU General
-@@ -102,8 +102,10 @@ AC_DEFUN([gl_INIT_PACKAGE_VERSION],
- dnl Set variables documented in Autoconf.
- AC_SUBST([PACKAGE_VERSION], ["$1"])
- AC_SUBST([PACKAGE_STRING], ["AC_PACKAGE_NAME $1"])
-+ _AC_DEFINE([#undef PACKAGE_VERSION])
- AC_DEFINE_UNQUOTED([PACKAGE_VERSION], ["$1"],
- [Define to the version of this package.])
-+ _AC_DEFINE([#undef PACKAGE_STRING])
- AC_DEFINE_UNQUOTED([PACKAGE_STRING], ["AC_PACKAGE_NAME $1"],
- [Define to the full name and version of this package.])
- ])
+++ /dev/null
-From 85599643e2fbf70f7f0bd58831993132ef335705 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Wed, 22 Jan 2025 21:25:27 +0100
-Subject: New module 'version-stamp'.
-
-* m4/version-stamp.m4: New file.
-* modules/version-stamp: New file.
----
- ChangeLog | 6 ++++++
- m4/version-stamp.m4 | 35 +++++++++++++++++++++++++++++++++++
- modules/version-stamp | 19 +++++++++++++++++++
- 3 files changed, 60 insertions(+)
- create mode 100644 m4/version-stamp.m4
- create mode 100644 modules/version-stamp
-
---- /dev/null
-+++ b/m4/version-stamp.m4
-@@ -0,0 +1,35 @@
-+# version-stamp.m4
-+# serial 1
-+dnl Copyright (C) 2025 Free Software Foundation, Inc.
-+dnl This file is free software, distributed under the terms of the GNU
-+dnl General Public License. As a special exception to the GNU General
-+dnl Public License, this file may be distributed as part of a program
-+dnl that contains a configuration script generated by Autoconf, under
-+dnl the same distribution terms as the rest of that program.
-+
-+# Manages a stamp file, that keeps track when $(VERSION) was last changed.
-+#
-+# gl_CONFIG_VERSION_STAMP
-+# needs to be invoked near the end of the package's top-level configure.ac,
-+# before AC_OUTPUT.
-+# It makes sure that during the build,
-+# - $(top_srcdir)/.version exists, and
-+# - when $(VERSION) is changed, $(top_srcdir)/.version gets modified.
-+#
-+# $(top_srcdir)/.version is a stamp file. Its contents wouldn't matter,
-+# except that for detecting the change, we store the value of $(VERSION)
-+# in it (but we could just as well store it in a different file).
-+AC_DEFUN([gl_CONFIG_VERSION_STAMP],
-+[
-+ AC_CONFIG_COMMANDS([version-timestamp],
-+ [if test -f "$ac_top_srcdir/.version" \
-+ && test `cat "$ac_top_srcdir/.version"` = "$gl_version"; then
-+ # The value of $(VERSION) is the same as last time.
-+ :
-+ else
-+ # The value of $(VERSION) has changed. Update the stamp.
-+ echo "$gl_version" > "$ac_top_srcdir/.version"
-+ fi
-+ ],
-+ [gl_version="$VERSION"])
-+])
---- /dev/null
-+++ b/modules/version-stamp
-@@ -0,0 +1,19 @@
-+Description:
-+Optimized rebuilding of artifacts that depend on $(VERSION).
-+
-+Files:
-+m4/version-stamp.m4
-+
-+Depends-on:
-+
-+configure.ac:
-+
-+Makefile.am:
-+
-+Include:
-+
-+License:
-+GPLed build tool
-+
-+Maintainer:
-+Bruno Haible
+++ /dev/null
-From 701d20aaf579bb71f35209dd63a272c3d9d21096 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Mon, 24 Feb 2025 19:03:17 +0100
-Subject: [PATCH] vc-mtime: New module.
-
-* lib/vc-mtime.h: New file.
-* lib/vc-mtime.c: New file.
-* modules/vc-mtime: New file.
----
- ChangeLog | 7 ++
- lib/vc-mtime.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++
- lib/vc-mtime.h | 97 ++++++++++++++++++++++
- modules/vc-mtime | 34 ++++++++
- 4 files changed, 346 insertions(+)
- create mode 100644 lib/vc-mtime.c
- create mode 100644 lib/vc-mtime.h
- create mode 100644 modules/vc-mtime
-
---- /dev/null
-+++ b/lib/vc-mtime.c
-@@ -0,0 +1,208 @@
-+/* Return the version-control based modification time of a file.
-+ Copyright (C) 2025 Free Software Foundation, Inc.
-+
-+ This program is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published by
-+ the Free Software Foundation, either version 3 of the License, or
-+ (at your option) any later version.
-+
-+ This program is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU General Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+/* Written by Bruno Haible <bruno@clisp.org>, 2025. */
-+
-+#include <config.h>
-+
-+/* Specification. */
-+#include "vc-mtime.h"
-+
-+#include <stdlib.h>
-+#include <unistd.h>
-+
-+#include <error.h>
-+#include "spawn-pipe.h"
-+#include "wait-process.h"
-+#include "execute.h"
-+#include "safe-read.h"
-+#include "xstrtol.h"
-+#include "stat-time.h"
-+#include "gettext.h"
-+
-+#define _(msgid) dgettext ("gnulib", msgid)
-+
-+
-+/* Determines whether the specified file is under version control. */
-+static bool
-+git_vc_controlled (const char *filename)
-+{
-+ /* Run "git ls-files FILENAME" and return true if the exit code is 0
-+ and the output is non-empty. */
-+ const char *argv[4];
-+ pid_t child;
-+ int fd[1];
-+
-+ argv[0] = "git";
-+ argv[1] = "ls-files";
-+ argv[2] = filename;
-+ argv[3] = NULL;
-+ child = create_pipe_in ("git", "git", argv, NULL, NULL,
-+ DEV_NULL, true, true, false, fd);
-+ if (child == -1)
-+ return false;
-+
-+ /* Read the subprocess output, and test whether it is non-empty. */
-+ size_t count = 0;
-+ char c;
-+
-+ while (safe_read (fd[0], &c, 1) > 0)
-+ count++;
-+
-+ close (fd[0]);
-+
-+ /* Remove zombie process from process list, and retrieve exit status. */
-+ int exitstatus =
-+ wait_subprocess (child, "git", false, true, true, false, NULL);
-+ return (exitstatus == 0 && count > 0);
-+}
-+
-+/* Determines whether the specified file is unmodified, compared to the
-+ last version in version control. */
-+static bool
-+git_unmodified (const char *filename)
-+{
-+ /* Run "git diff --quiet -- HEAD FILENAME"
-+ (or "git diff --quiet HEAD FILENAME")
-+ and return true if the exit code is 0.
-+ The '--' option is for the case that the specified file was removed. */
-+ const char *argv[7];
-+ int exitstatus;
-+
-+ argv[0] = "git";
-+ argv[1] = "diff";
-+ argv[2] = "--quiet";
-+ argv[3] = "--";
-+ argv[4] = "HEAD";
-+ argv[5] = filename;
-+ argv[6] = NULL;
-+ exitstatus = execute ("git", "git", argv, NULL, NULL,
-+ false, false, true, true,
-+ true, false, NULL);
-+ return (exitstatus == 0);
-+}
-+
-+/* Stores in *MTIME the time of last modification in version control of the
-+ specified file, and returns 0.
-+ Upon failure, it returns -1. */
-+static int
-+git_mtime (struct timespec *mtime, const char *filename)
-+{
-+ /* Run "git log -1 --format=%ct -- FILENAME". It prints the time of last
-+ modification, as the number of seconds since the Epoch.
-+ The '--' option is for the case that the specified file was removed. */
-+ const char *argv[7];
-+ pid_t child;
-+ int fd[1];
-+
-+ argv[0] = "git";
-+ argv[1] = "log";
-+ argv[2] = "-1";
-+ argv[3] = "--format=%ct";
-+ argv[4] = "--";
-+ argv[5] = filename;
-+ argv[6] = NULL;
-+ child = create_pipe_in ("git", "git", argv, NULL, NULL,
-+ DEV_NULL, true, true, false, fd);
-+ if (child == -1)
-+ return -1;
-+
-+ /* Retrieve its result. */
-+ FILE *fp;
-+ char *line;
-+ size_t linesize;
-+ size_t linelen;
-+
-+ fp = fdopen (fd[0], "r");
-+ if (fp == NULL)
-+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
-+
-+ line = NULL; linesize = 0;
-+ linelen = getline (&line, &linesize, fp);
-+ if (linelen == (size_t)(-1))
-+ {
-+ error (0, 0, _("%s subprocess I/O error"), "git");
-+ fclose (fp);
-+ wait_subprocess (child, "git", true, false, true, false, NULL);
-+ }
-+ else
-+ {
-+ int exitstatus;
-+
-+ if (linelen > 0 && line[linelen - 1] == '\n')
-+ line[linelen - 1] = '\0';
-+
-+ fclose (fp);
-+
-+ /* Remove zombie process from process list, and retrieve exit status. */
-+ exitstatus =
-+ wait_subprocess (child, "git", true, false, true, false, NULL);
-+ if (exitstatus == 0)
-+ {
-+ char *endptr;
-+ unsigned long git_log_time;
-+ if (xstrtoul (line, &endptr, 10, &git_log_time, NULL) == LONGINT_OK
-+ && endptr == line + strlen (line))
-+ {
-+ mtime->tv_sec = git_log_time;
-+ mtime->tv_nsec = 0;
-+ free (line);
-+ return 0;
-+ }
-+ }
-+ }
-+ free (line);
-+ return -1;
-+}
-+
-+int
-+vc_mtime (struct timespec *mtime, const char *filename)
-+{
-+ static bool git_tested;
-+ static bool git_present;
-+
-+ if (!git_tested)
-+ {
-+ /* Test for presence of git:
-+ "git --version >/dev/null 2>/dev/null" */
-+ const char *argv[3];
-+ int exitstatus;
-+
-+ argv[0] = "git";
-+ argv[1] = "--version";
-+ argv[2] = NULL;
-+ exitstatus = execute ("git", "git", argv, NULL, NULL,
-+ false, false, true, true,
-+ true, false, NULL);
-+ git_present = (exitstatus == 0);
-+ git_tested = true;
-+ }
-+
-+ if (git_present
-+ && git_vc_controlled (filename)
-+ && git_unmodified (filename))
-+ {
-+ if (git_mtime (mtime, filename) == 0)
-+ return 0;
-+ }
-+ struct stat statbuf;
-+ if (stat (filename, &statbuf) == 0)
-+ {
-+ *mtime = get_stat_mtime (&statbuf);
-+ return 0;
-+ }
-+ return -1;
-+}
---- /dev/null
-+++ b/lib/vc-mtime.h
-@@ -0,0 +1,97 @@
-+/* Return the version-control based modification time of a file.
-+ Copyright (C) 2025 Free Software Foundation, Inc.
-+
-+ This program is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published by
-+ the Free Software Foundation, either version 3 of the License, or
-+ (at your option) any later version.
-+
-+ This program is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU General Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+/* Written by Bruno Haible <bruno@clisp.org>, 2025. */
-+
-+#ifndef _VC_MTIME_H
-+#define _VC_MTIME_H
-+
-+/* Get struct timespec. */
-+#include <time.h>
-+
-+/* The "version-controlled modification time" vc_mtime(F) of a file F
-+ is defined as:
-+ - If F is under version control and not modified locally:
-+ the time of the last change of F in the version control system.
-+ - Otherwise: The modification time of F on disk.
-+
-+ For now, the only VCS supported by this module is git. (hg and svn are
-+ hardly in use any more.)
-+
-+ This has the properties that:
-+ - Different users who have checked out the same git repo on different
-+ machines, at different times, and not done local modifications,
-+ get the same vc_mtime(F).
-+ - If a user has modified F locally, the modification time of that file
-+ counts.
-+ - If that user then reverts the modification, they then again get the
-+ same vc_mtime(F) as everyone else.
-+ - Different users who have unpacked the same tarball (without .git
-+ directory) on different machines, at different times, also get the same
-+ vc_mtime(F) [but possibly a different one than when the .git directory
-+ was present]. (Assuming a POSIX compliant file system.)
-+ - When a user commits local modifications into git, this only increases
-+ (not decreases) the vc_mtime(F).
-+
-+ The purpose of the version-controlled modification time is to produce a
-+ reproducible timestamp(Z) of a file Z that depends on files X1, ..., Xn,
-+ in such a way that
-+ - timestamp(Z) is reproducible, that is, different users on different
-+ machines get the same value.
-+ - timestamp(Z) is related to reality. It's not just a dummy, like what
-+ is suggested in <https://reproducible-builds.org/docs/timestamps/>.
-+ - One can arrange for timestamp(Z) to respect the modification time
-+ relations of a build system.
-+
-+ There are two uses of such a timestamp:
-+ - It can be set as the modification time of file Z in a file system, or
-+ - It can be embedded in Z, with the purpose of telling a user how old
-+ the file Z is. For example, in PDF files or in generated documentation,
-+ such a time is embedded in a special place.
-+
-+ The simplest example is a file Z that depends on files X1, ..., Xn.
-+ Generally one will define
-+ timestamp(Z) = max (vc_mtime(X1), ..., vc_mtime(Xn))
-+ for an embedded timestamp, or
-+ timestamp(Z) = max (vc_mtime(X1), ..., vc_mtime(Xn)) + 1 second
-+ for a time stamp in a file system. The added second
-+ 1. accounts for fractional seconds in mtime(X1), ..., mtime(Xn),
-+ 2. allows for 'make' implementation that attempt to rebuild Z
-+ if mtime(Z) == mtime(Xi).
-+
-+ A more complicated example is when there are intermediate built files, not
-+ under version control. For example, if the build process produces
-+ X1, X2 -> Y1
-+ X3, X4 -> Y2
-+ Y1, Y2, X5 -> Z
-+ where Y1 and Y2 are intermediate built files, you should ignore the
-+ mtime(Y1), mtime(Y2), and consider only the vc_mtime(X1), ..., vc_mtime(X5).
-+ */
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif
-+
-+/* Determines the version-controlled modification time of FILENAME, stores it
-+ in *MTIME, and returns 0.
-+ Upon failure, it returns -1. */
-+extern int vc_mtime (struct timespec *mtime, const char *filename);
-+
-+#ifdef __cplusplus
-+}
-+#endif
-+
-+#endif /* _VC_MTIME_H */
---- /dev/null
-+++ b/modules/vc-mtime
-@@ -0,0 +1,34 @@
-+Description:
-+Returns the version-control based modification time of a file.
-+
-+Files:
-+lib/vc-mtime.h
-+lib/vc-mtime.c
-+
-+Depends-on:
-+time-h
-+bool
-+spawn-pipe
-+wait-process
-+execute
-+safe-read
-+error
-+getline
-+xstrtol
-+stat-time
-+gettext-h
-+gnulib-i18n
-+
-+configure.ac:
-+
-+Makefile.am:
-+lib_SOURCES += vc-mtime.c
-+
-+Include:
-+"vm-mtime.h"
-+
-+License:
-+GPL
-+
-+Maintainer:
-+Bruno Haible
+++ /dev/null
-From f47c5f2e21d0ccedb271b406e35b6963b23a64c4 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Wed, 30 Apr 2025 13:11:01 +0200
-Subject: [PATCH] clean-temp: Fix link error (regression yesterday).
-
-* lib/clean-temp.c: Include hashkey-string.h.
-(create_temp_dir): Use hashkey_string_* functions instead of
-clean_temp_string_*.
-* lib/clean-temp-private.h (clean_temp_string_equals,
-clean_temp_string_hash): Remove declarations.
-* modules/clean-temp (Depends-on): Add hashkey-string.
----
- ChangeLog | 10 ++++++++++
- lib/clean-temp-private.h | 3 ---
- lib/clean-temp.c | 5 +++--
- modules/clean-temp | 1 +
- 4 files changed, 14 insertions(+), 5 deletions(-)
-
---- a/lib/clean-temp-private.h
-+++ b/lib/clean-temp-private.h
-@@ -68,9 +68,6 @@ struct closeable_fd
- #define descriptors clean_temp_descriptors
- extern gl_list_t /* <closeable_fd *> */ volatile descriptors;
-
--extern bool clean_temp_string_equals (const void *x1, const void *x2);
--extern size_t clean_temp_string_hash (const void *x);
--
- extern _GL_ASYNC_SAFE int clean_temp_asyncsafe_close (struct closeable_fd *element);
- extern void clean_temp_init_asyncsafe_close (void);
-
---- a/lib/clean-temp.c
-+++ b/lib/clean-temp.c
-@@ -45,6 +45,7 @@
- #include "xmalloca.h"
- #include "glthread/lock.h"
- #include "thread-optim.h"
-+#include "hashkey-string.h"
- #include "gl_xlist.h"
- #include "gl_linkedhash_list.h"
- #include "gl_linked_list.h"
-@@ -221,11 +222,11 @@ create_temp_dir (const char *prefix, con
- tmpdir->cleanup_verbose = cleanup_verbose;
- tmpdir->subdirs =
- gl_list_create_empty (GL_LINKEDHASH_LIST,
-- clean_temp_string_equals, clean_temp_string_hash,
-+ hashkey_string_equals, hashkey_string_hash,
- NULL, false);
- tmpdir->files =
- gl_list_create_empty (GL_LINKEDHASH_LIST,
-- clean_temp_string_equals, clean_temp_string_hash,
-+ hashkey_string_equals, hashkey_string_hash,
- NULL, false);
-
- /* Create the temporary directory. */
---- a/modules/clean-temp
-+++ b/modules/clean-temp
-@@ -24,6 +24,7 @@ rmdir
- xalloc
- xalloc-die
- xmalloca
-+hashkey-string
- linkedhash-list
- linked-list
- xlist
+++ /dev/null
-From 9a26e7043fa95b4c9ee4576ce8c0ac15668e695e Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Thu, 2 Jan 2025 13:54:54 +0100
-Subject: [PATCH] string-desc, xstring-desc, string-desc-quotearg: Rename
- functions.
-
-* lib/string-desc.h (sd_equals): Renamed from string_desc_equals.
-(sd_startswith): Renamed from string_desc_startswith.
-(sd_endswith): Renamed from string_desc_endswith.
-(sd_cmp): Renamed from string_desc_cmp.
-(sd_c_casecmp): Renamed from string_desc_c_casecmp.
-(sd_index): Renamed from string_desc_index.
-(sd_last_index): Renamed from string_desc_last_index.
-(sd_contains): Renamed from string_desc_contains.
-(sd_new_empty): Renamed from string_desc_new_empty.
-(sd_new_addr): Renamed from string_desc_new_addr.
-(sd_from_c): Renamed from string_desc_from_c.
-(sd_substring): Renamed from string_desc_substring.
-(sd_write): Renamed from string_desc_write.
-(sd_fwrite): Renamed from string_desc_fwrite.
-(sd_new): Renamed from string_desc_new.
-(sd_new_filled): Renamed from string_desc_new_filled.
-(sd_copy): Renamed from string_desc_copy.
-(sd_concat): Renamed from string_desc_concat.
-(sd_c): Renamed from string_desc_c.
-(sd_set_char_at): Renamed from string_desc_set_char_at.
-(sd_fill): Renamed from string_desc_fill.
-(sd_overwrite): Renamed from string_desc_overwrite.
-(sd_free): Renamed from string_desc_free.
-(sd_length): Renamed from string_desc_length.
-(sd_char_at): Renamed from string_desc_char_at.
-(sd_data): Renamed from string_desc_data.
-(sd_is_empty): Renamed from string_desc_is_empty.
-* lib/string-desc.c (sd_equals): Renamed from string_desc_equals.
-(sd_startswith): Renamed from string_desc_startswith.
-(sd_endswith): Renamed from string_desc_endswith.
-(sd_cmp): Renamed from string_desc_cmp.
-(sd_c_casecmp): Renamed from string_desc_c_casecmp.
-(sd_index): Renamed from string_desc_index.
-(sd_last_index): Renamed from string_desc_last_index.
-(sd_new_empty): Renamed from string_desc_new_empty.
-(sd_new_addr): Renamed from string_desc_new_addr.
-(sd_from_c): Renamed from string_desc_from_c.
-(sd_substring): Renamed from string_desc_substring.
-(sd_write): Renamed from string_desc_write.
-(sd_fwrite): Renamed from string_desc_fwrite.
-(sd_new): Renamed from string_desc_new.
-(sd_new_filled): Renamed from string_desc_new_filled.
-(sd_copy): Renamed from string_desc_copy.
-(sd_concat): Renamed from string_desc_concat.
-(sd_c): Renamed from string_desc_c.
-(sd_set_char_at): Renamed from string_desc_set_char_at.
-(sd_fill): Renamed from string_desc_fill.
-(sd_overwrite): Renamed from string_desc_overwrite.
-(sd_free): Renamed from string_desc_free.
-* lib/xstring-desc.h (xsd_concat): Renamed from xstring_desc_concat.
-(xsd_new): Renamed from xstring_desc_new.
-(xsd_new_filled): Renamed from xstring_desc_new_filled.
-(xsd_copy): Renamed from xstring_desc_copy.
-(xsd_c): Renamed from xstring_desc_c.
-* lib/xstring-desc.c (xsd_concat): Renamed from xstring_desc_concat.
-* lib/string-desc-quotearg.h (sd_quotearg_buffer): Renamed from
-string_desc_quotearg_buffer.
-(sd_quotearg_alloc): Renamed from string_desc_quotearg_alloc.
-(sd_quotearg_n): Renamed from string_desc_quotearg_n.
-(sd_quotearg): Renamed from string_desc_quotearg.
-(sd_quotearg_n_style): Renamed from string_desc_quotearg_n_style.
-(sd_quotearg_style): Renamed from string_desc_quotearg_style.
-(sd_quotearg_char): Renamed from string_desc_quotearg_char.
-(sd_quotearg_colon): Renamed from string_desc_quotearg_colon.
-(sd_quotearg_n_custom): Renamed from string_desc_quotearg_n_custom.
-(sd_quotearg_custom): Renamed from sd_quotearg_n_custom.
-* lib/string-desc-contains.c (sd_contains): Renamed from
-string_desc_contains.
-* lib/string-buffer.h: Update.
-* lib/string-buffer.c (sb_append_desc, sb_contents, sb_dupfree): Update.
-* lib/xstring-buffer.c (sb_xdupfree): Update.
-* lib/sf-istream.c (sf_istream_init_from_string_desc): Update.
-* tests/test-string-desc.c (main): Update.
-* tests/test-string-desc.sh: Update.
-* tests/test-xstring-desc.c (main): Update.
-* tests/test-string-desc-quotearg.c (main): Update.
-* tests/test-string-buffer.c (main): Update.
-* tests/test-sf-istream.c (main): Update.
-* tests/test-sfl-istream.c (main): Update.
-* doc/string-desc.texi: Update.
-* doc/strings.texi: Update.
-* NEWS: Mention the change.
----
- ChangeLog | 86 +++++++++
- NEWS | 4 +
- doc/string-desc.texi | 4 +-
- doc/strings.texi | 18 +-
- lib/sf-istream.c | 4 +-
- lib/string-buffer.c | 12 +-
- lib/string-buffer.h | 4 +-
- lib/string-desc-contains.c | 2 +-
- lib/string-desc-quotearg.h | 114 ++++++------
- lib/string-desc.c | 52 +++---
- lib/string-desc.h | 62 +++----
- lib/xstring-buffer.c | 4 +-
- lib/xstring-desc.c | 2 +-
- lib/xstring-desc.h | 26 +--
- tests/test-sf-istream.c | 4 +-
- tests/test-sfl-istream.c | 4 +-
- tests/test-string-buffer.c | 18 +-
- tests/test-string-desc-quotearg.c | 44 ++---
- tests/test-string-desc.c | 296 +++++++++++++++---------------
- tests/test-string-desc.sh | 4 +-
- tests/test-xstring-desc.c | 68 +++----
- 21 files changed, 461 insertions(+), 371 deletions(-)
-
---- a/lib/sf-istream.c
-+++ b/lib/sf-istream.c
-@@ -46,8 +46,8 @@ sf_istream_init_from_string_desc (sf_ist
- string_desc_t input)
- {
- stream->fp = NULL;
-- stream->input = string_desc_data (input);
-- stream->input_end = stream->input + string_desc_length (input);
-+ stream->input = sd_data (input);
-+ stream->input_end = stream->input + sd_length (input);
- }
-
- int
---- a/lib/string-buffer.c
-+++ b/lib/string-buffer.c
-@@ -101,13 +101,13 @@ sb_append1 (struct string_buffer *buffer
- int
- sb_append_desc (struct string_buffer *buffer, string_desc_t s)
- {
-- size_t len = string_desc_length (s);
-+ size_t len = sd_length (s);
- if (sb_ensure_more_bytes (buffer, len) < 0)
- {
- buffer->error = true;
- return -1;
- }
-- memcpy (buffer->data + buffer->length, string_desc_data (s), len);
-+ memcpy (buffer->data + buffer->length, sd_data (s), len);
- buffer->length += len;
- return 0;
- }
-@@ -136,7 +136,7 @@ sb_free (struct string_buffer *buffer)
- string_desc_t
- sb_contents (struct string_buffer *buffer)
- {
-- return string_desc_new_addr (buffer->length, buffer->data);
-+ return sd_new_addr (buffer->length, buffer->data);
- }
-
- const char *
-@@ -162,7 +162,7 @@ sb_dupfree (struct string_buffer *buffer
- if (copy == NULL)
- goto fail;
- memcpy (copy, buffer->data, length);
-- return string_desc_new_addr (length, copy);
-+ return sd_new_addr (length, copy);
- }
- else
- {
-@@ -174,12 +174,12 @@ sb_dupfree (struct string_buffer *buffer
- if (contents == NULL)
- goto fail;
- }
-- return string_desc_new_addr (length, contents);
-+ return sd_new_addr (length, contents);
- }
-
- fail:
- sb_free (buffer);
-- return string_desc_new_addr (0, NULL);
-+ return sd_new_addr (0, NULL);
- }
-
- char *
---- a/lib/string-buffer.h
-+++ b/lib/string-buffer.h
-@@ -115,7 +115,7 @@ extern const char * sb_contents_c (struc
-
- /* Returns the contents of BUFFER and frees all other memory held by BUFFER.
- Returns NULL upon failure or if there was an error earlier.
-- It is the responsibility of the caller to string_desc_free() the result. */
-+ It is the responsibility of the caller to sd_free() the result. */
- extern string_desc_t sb_dupfree (struct string_buffer *buffer)
- _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
-
-@@ -182,7 +182,7 @@ extern const char * sb_xcontents_c (stru
-
- /* Returns the contents of BUFFER and frees all other memory held by BUFFER.
- Returns (0, NULL) if there was an error earlier.
-- It is the responsibility of the caller to string_desc_free() the result. */
-+ It is the responsibility of the caller to sd_free() the result. */
- extern string_desc_t sb_xdupfree (struct string_buffer *buffer)
- _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
-
---- a/lib/string-desc-contains.c
-+++ b/lib/string-desc-contains.c
-@@ -31,7 +31,7 @@
- which — depending on platforms — costs up to 2 KB of binary code. */
-
- ptrdiff_t
--string_desc_contains (string_desc_t haystack, string_desc_t needle)
-+sd_contains (string_desc_t haystack, string_desc_t needle)
- {
- if (needle._nbytes == 0)
- return 0;
---- a/lib/string-desc-quotearg.h
-+++ b/lib/string-desc-quotearg.h
-@@ -50,22 +50,22 @@ extern "C" {
- does not use backslash escapes and the flags of O do not request
- elision of null bytes. */
- #if 0
--extern size_t string_desc_quotearg_buffer (char *restrict buffer,
-- size_t buffersize,
-- string_desc_t arg,
-- struct quoting_options const *o);
-+extern size_t sd_quotearg_buffer (char *restrict buffer,
-+ size_t buffersize,
-+ string_desc_t arg,
-+ struct quoting_options const *o);
- #endif
-
--/* Like string_desc_quotearg_buffer, except return the result in a newly
-+/* Like sd_quotearg_buffer, except return the result in a newly
- allocated buffer and store its length, excluding the terminating null
- byte, in *SIZE. It is the caller's responsibility to free the result.
- The result might contain embedded null bytes if the style of O does
- not use backslash escapes and the flags of O do not request elision
- of null bytes. */
- #if 0
--extern char *string_desc_quotearg_alloc (string_desc_t arg,
-- size_t *size,
-- struct quoting_options const *o)
-+extern char *sd_quotearg_alloc (string_desc_t arg,
-+ size_t *size,
-+ struct quoting_options const *o)
- _GL_ATTRIBUTE_NONNULL ((2))
- _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE
- _GL_ATTRIBUTE_RETURNS_NONNULL;
-@@ -77,68 +77,68 @@ extern char *string_desc_quotearg_alloc
- reused by the next call to this function with the same value of N.
- N must be nonnegative. */
- #if 0
--extern char *string_desc_quotearg_n (int n, string_desc_t arg);
-+extern char *sd_quotearg_n (int n, string_desc_t arg);
- #endif
-
--/* Equivalent to string_desc_quotearg_n (0, ARG). */
-+/* Equivalent to sd_quotearg_n (0, ARG). */
- #if 0
--extern char *string_desc_quotearg (string_desc_t arg);
-+extern char *sd_quotearg (string_desc_t arg);
- #endif
-
- /* Use style S and storage slot N to return a quoted version of the string ARG.
-- This is like string_desc_quotearg_n (N, ARG), except that it uses S
-+ This is like sd_quotearg_n (N, ARG), except that it uses S
- with no other options to specify the quoting method. */
- #if 0
--extern char *string_desc_quotearg_n_style (int n, enum quoting_style s,
-- string_desc_t arg);
-+extern char *sd_quotearg_n_style (int n, enum quoting_style s,
-+ string_desc_t arg);
- #endif
-
--/* Equivalent to string_desc_quotearg_n_style (0, S, ARG). */
-+/* Equivalent to sd_quotearg_n_style (0, S, ARG). */
- #if 0
--extern char *string_desc_quotearg_style (enum quoting_style s,
-- string_desc_t arg);
-+extern char *sd_quotearg_style (enum quoting_style s,
-+ string_desc_t arg);
- #endif
-
--/* Like string_desc_quotearg (ARG), except also quote any instances of CH.
-+/* Like sd_quotearg (ARG), except also quote any instances of CH.
- See set_char_quoting for a description of acceptable CH values. */
- #if 0
--extern char *string_desc_quotearg_char (string_desc_t arg, char ch);
-+extern char *sd_quotearg_char (string_desc_t arg, char ch);
- #endif
-
--/* Equivalent to string_desc_quotearg_char (ARG, ':'). */
-+/* Equivalent to sd_quotearg_char (ARG, ':'). */
- #if 0
--extern char *string_desc_quotearg_colon (string_desc_t arg);
-+extern char *sd_quotearg_colon (string_desc_t arg);
- #endif
-
--/* Like string_desc_quotearg_n_style (N, S, ARG) but with S as
-+/* Like sd_quotearg_n_style (N, S, ARG) but with S as
- custom_quoting_style with left quote as LEFT_QUOTE and right quote
- as RIGHT_QUOTE. See set_custom_quoting for a description of acceptable
- LEFT_QUOTE and RIGHT_QUOTE values. */
- #if 0
--extern char *string_desc_quotearg_n_custom (int n,
-- char const *left_quote,
-- char const *right_quote,
-- string_desc_t arg);
-+extern char *sd_quotearg_n_custom (int n,
-+ char const *left_quote,
-+ char const *right_quote,
-+ string_desc_t arg);
- #endif
-
- /* Equivalent to
-- string_desc_quotearg_n_custom (0, LEFT_QUOTE, RIGHT_QUOTE, ARG). */
-+ sd_quotearg_n_custom (0, LEFT_QUOTE, RIGHT_QUOTE, ARG). */
- #if 0
--extern char *string_desc_quotearg_custom (char const *left_quote,
-- char const *right_quote,
-- string_desc_t arg);
-+extern char *sd_quotearg_custom (char const *left_quote,
-+ char const *right_quote,
-+ string_desc_t arg);
- #endif
-
-
- /* ==== Inline function definitions ==== */
-
- GL_STRING_DESC_QUOTEARG_INLINE size_t
--string_desc_quotearg_buffer (char *restrict buffer, size_t buffersize,
-- string_desc_t arg,
-- struct quoting_options const *o)
-+sd_quotearg_buffer (char *restrict buffer, size_t buffersize,
-+ string_desc_t arg,
-+ struct quoting_options const *o)
- {
- return quotearg_buffer (buffer, buffersize,
-- string_desc_data (arg), string_desc_length (arg),
-+ sd_data (arg), sd_length (arg),
- o);
- }
-
-@@ -147,69 +147,69 @@ _GL_ATTRIBUTE_NONNULL ((2))
- _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE
- _GL_ATTRIBUTE_RETURNS_NONNULL
- char *
--string_desc_quotearg_alloc (string_desc_t arg,
-- size_t *size,
-- struct quoting_options const *o)
-+sd_quotearg_alloc (string_desc_t arg,
-+ size_t *size,
-+ struct quoting_options const *o)
- {
-- return quotearg_alloc_mem (string_desc_data (arg), string_desc_length (arg),
-+ return quotearg_alloc_mem (sd_data (arg), sd_length (arg),
- size,
- o);
- }
-
- GL_STRING_DESC_QUOTEARG_INLINE char *
--string_desc_quotearg_n (int n, string_desc_t arg)
-+sd_quotearg_n (int n, string_desc_t arg)
- {
-- return quotearg_n_mem (n, string_desc_data (arg), string_desc_length (arg));
-+ return quotearg_n_mem (n, sd_data (arg), sd_length (arg));
- }
-
- GL_STRING_DESC_QUOTEARG_INLINE char *
--string_desc_quotearg (string_desc_t arg)
-+sd_quotearg (string_desc_t arg)
- {
-- return quotearg_mem (string_desc_data (arg), string_desc_length (arg));
-+ return quotearg_mem (sd_data (arg), sd_length (arg));
- }
-
- GL_STRING_DESC_QUOTEARG_INLINE char *
--string_desc_quotearg_n_style (int n, enum quoting_style s, string_desc_t arg)
-+sd_quotearg_n_style (int n, enum quoting_style s, string_desc_t arg)
- {
- return quotearg_n_style_mem (n, s,
-- string_desc_data (arg), string_desc_length (arg));
-+ sd_data (arg), sd_length (arg));
- }
-
- GL_STRING_DESC_QUOTEARG_INLINE char *
--string_desc_quotearg_style (enum quoting_style s, string_desc_t arg)
-+sd_quotearg_style (enum quoting_style s, string_desc_t arg)
- {
- return quotearg_style_mem (s,
-- string_desc_data (arg), string_desc_length (arg));
-+ sd_data (arg), sd_length (arg));
- }
-
- GL_STRING_DESC_QUOTEARG_INLINE char *
--string_desc_quotearg_char (string_desc_t arg, char ch)
-+sd_quotearg_char (string_desc_t arg, char ch)
- {
-- return quotearg_char_mem (string_desc_data (arg), string_desc_length (arg),
-+ return quotearg_char_mem (sd_data (arg), sd_length (arg),
- ch);
- }
-
- GL_STRING_DESC_QUOTEARG_INLINE char *
--string_desc_quotearg_colon (string_desc_t arg)
-+sd_quotearg_colon (string_desc_t arg)
- {
-- return quotearg_colon_mem (string_desc_data (arg), string_desc_length (arg));
-+ return quotearg_colon_mem (sd_data (arg), sd_length (arg));
- }
-
- GL_STRING_DESC_QUOTEARG_INLINE char *
--string_desc_quotearg_n_custom (int n,
-- char const *left_quote, char const *right_quote,
-- string_desc_t arg)
-+sd_quotearg_n_custom (int n,
-+ char const *left_quote, char const *right_quote,
-+ string_desc_t arg)
- {
- return quotearg_n_custom_mem (n, left_quote, right_quote,
-- string_desc_data (arg), string_desc_length (arg));
-+ sd_data (arg), sd_length (arg));
- }
-
- GL_STRING_DESC_QUOTEARG_INLINE char *
--string_desc_quotearg_custom (char const *left_quote, char const *right_quote,
-- string_desc_t arg)
-+sd_quotearg_custom (char const *left_quote, char const *right_quote,
-+ string_desc_t arg)
- {
- return quotearg_custom_mem (left_quote, right_quote,
-- string_desc_data (arg), string_desc_length (arg));
-+ sd_data (arg), sd_length (arg));
- }
-
-
---- a/lib/string-desc.c
-+++ b/lib/string-desc.c
-@@ -39,14 +39,14 @@
-
- /* Return true if A and B are equal. */
- bool
--string_desc_equals (string_desc_t a, string_desc_t b)
-+sd_equals (string_desc_t a, string_desc_t b)
- {
- return (a._nbytes == b._nbytes
- && (a._nbytes == 0 || memcmp (a._data, b._data, a._nbytes) == 0));
- }
-
- bool
--string_desc_startswith (string_desc_t s, string_desc_t prefix)
-+sd_startswith (string_desc_t s, string_desc_t prefix)
- {
- return (s._nbytes >= prefix._nbytes
- && (prefix._nbytes == 0
-@@ -54,7 +54,7 @@ string_desc_startswith (string_desc_t s,
- }
-
- bool
--string_desc_endswith (string_desc_t s, string_desc_t suffix)
-+sd_endswith (string_desc_t s, string_desc_t suffix)
- {
- return (s._nbytes >= suffix._nbytes
- && (suffix._nbytes == 0
-@@ -63,7 +63,7 @@ string_desc_endswith (string_desc_t s, s
- }
-
- int
--string_desc_cmp (string_desc_t a, string_desc_t b)
-+sd_cmp (string_desc_t a, string_desc_t b)
- {
- if (a._nbytes > b._nbytes)
- {
-@@ -86,14 +86,14 @@ string_desc_cmp (string_desc_t a, string
- }
-
- int
--string_desc_c_casecmp (string_desc_t a, string_desc_t b)
-+sd_c_casecmp (string_desc_t a, string_desc_t b)
- {
- /* Don't use memcasecmp here, since it uses the current locale, not the
- "C" locale. */
-- idx_t an = string_desc_length (a);
-- idx_t bn = string_desc_length (b);
-- const char *ap = string_desc_data (a);
-- const char *bp = string_desc_data (b);
-+ idx_t an = sd_length (a);
-+ idx_t bn = sd_length (b);
-+ const char *ap = sd_data (a);
-+ const char *bp = sd_data (b);
- idx_t n = (an < bn ? an : bn);
- idx_t i;
- for (i = 0; i < n; i++)
-@@ -108,7 +108,7 @@ string_desc_c_casecmp (string_desc_t a,
- }
-
- ptrdiff_t
--string_desc_index (string_desc_t s, char c)
-+sd_index (string_desc_t s, char c)
- {
- if (s._nbytes > 0)
- {
-@@ -120,7 +120,7 @@ string_desc_index (string_desc_t s, char
- }
-
- ptrdiff_t
--string_desc_last_index (string_desc_t s, char c)
-+sd_last_index (string_desc_t s, char c)
- {
- if (s._nbytes > 0)
- {
-@@ -132,7 +132,7 @@ string_desc_last_index (string_desc_t s,
- }
-
- string_desc_t
--string_desc_new_empty (void)
-+sd_new_empty (void)
- {
- string_desc_t result;
-
-@@ -144,7 +144,7 @@ string_desc_new_empty (void)
- }
-
- string_desc_t
--string_desc_new_addr (idx_t n, char *addr)
-+sd_new_addr (idx_t n, char *addr)
- {
- string_desc_t result;
-
-@@ -158,7 +158,7 @@ string_desc_new_addr (idx_t n, char *add
- }
-
- string_desc_t
--string_desc_from_c (const char *s)
-+sd_from_c (const char *s)
- {
- string_desc_t result;
-
-@@ -169,7 +169,7 @@ string_desc_from_c (const char *s)
- }
-
- string_desc_t
--string_desc_substring (string_desc_t s, idx_t start, idx_t end)
-+sd_substring (string_desc_t s, idx_t start, idx_t end)
- {
- string_desc_t result;
-
-@@ -184,7 +184,7 @@ string_desc_substring (string_desc_t s,
- }
-
- int
--string_desc_write (int fd, string_desc_t s)
-+sd_write (int fd, string_desc_t s)
- {
- if (s._nbytes > 0)
- if (full_write (fd, s._data, s._nbytes) != s._nbytes)
-@@ -194,7 +194,7 @@ string_desc_write (int fd, string_desc_t
- }
-
- int
--string_desc_fwrite (FILE *fp, string_desc_t s)
-+sd_fwrite (FILE *fp, string_desc_t s)
- {
- if (s._nbytes > 0)
- if (fwrite (s._data, 1, s._nbytes, fp) != s._nbytes)
-@@ -206,7 +206,7 @@ string_desc_fwrite (FILE *fp, string_des
- /* ==== Memory-allocating operations on string descriptors ==== */
-
- int
--string_desc_new (string_desc_t *resultp, idx_t n)
-+sd_new (string_desc_t *resultp, idx_t n)
- {
- string_desc_t result;
-
-@@ -230,7 +230,7 @@ string_desc_new (string_desc_t *resultp,
- }
-
- int
--string_desc_new_filled (string_desc_t *resultp, idx_t n, char c)
-+sd_new_filled (string_desc_t *resultp, idx_t n, char c)
- {
- string_desc_t result;
-
-@@ -251,7 +251,7 @@ string_desc_new_filled (string_desc_t *r
- }
-
- int
--string_desc_copy (string_desc_t *resultp, string_desc_t s)
-+sd_copy (string_desc_t *resultp, string_desc_t s)
- {
- string_desc_t result;
- idx_t n = s._nbytes;
-@@ -273,7 +273,7 @@ string_desc_copy (string_desc_t *resultp
- }
-
- int
--string_desc_concat (string_desc_t *resultp, idx_t n, string_desc_t string1, ...)
-+sd_concat (string_desc_t *resultp, idx_t n, string_desc_t string1, ...)
- {
- if (n <= 0)
- /* Invalid argument. */
-@@ -327,7 +327,7 @@ string_desc_concat (string_desc_t *resul
- }
-
- char *
--string_desc_c (string_desc_t s)
-+sd_c (string_desc_t s)
- {
- idx_t n = s._nbytes;
- char *result = (char *) imalloc (n + 1);
-@@ -345,7 +345,7 @@ string_desc_c (string_desc_t s)
- /* ==== Operations with side effects on string descriptors ==== */
-
- void
--string_desc_set_char_at (string_desc_t s, idx_t i, char c)
-+sd_set_char_at (string_desc_t s, idx_t i, char c)
- {
- if (!(i >= 0 && i < s._nbytes))
- /* Invalid argument. */
-@@ -354,7 +354,7 @@ string_desc_set_char_at (string_desc_t s
- }
-
- void
--string_desc_fill (string_desc_t s, idx_t start, idx_t end, char c)
-+sd_fill (string_desc_t s, idx_t start, idx_t end, char c)
- {
- if (!(start >= 0 && start <= end))
- /* Invalid arguments. */
-@@ -365,7 +365,7 @@ string_desc_fill (string_desc_t s, idx_t
- }
-
- void
--string_desc_overwrite (string_desc_t s, idx_t start, string_desc_t t)
-+sd_overwrite (string_desc_t s, idx_t start, string_desc_t t)
- {
- if (!(start >= 0 && start + t._nbytes <= s._nbytes))
- /* Invalid arguments. */
-@@ -376,7 +376,7 @@ string_desc_overwrite (string_desc_t s,
- }
-
- void
--string_desc_free (string_desc_t s)
-+sd_free (string_desc_t s)
- {
- free (s._data);
- }
---- a/lib/string-desc.h
-+++ b/lib/string-desc.h
-@@ -69,82 +69,82 @@ struct string_desc_t
-
- /* Return the length of the string S. */
- #if 0 /* Defined inline below. */
--extern idx_t string_desc_length (string_desc_t s);
-+extern idx_t sd_length (string_desc_t s);
- #endif
-
- /* Return the byte at index I of string S.
- I must be < length(S). */
- #if 0 /* Defined inline below. */
--extern char string_desc_char_at (string_desc_t s, idx_t i);
-+extern char sd_char_at (string_desc_t s, idx_t i);
- #endif
-
- /* Return a read-only view of the bytes of S. */
- #if 0 /* Defined inline below. */
--extern const char * string_desc_data (string_desc_t s);
-+extern const char * sd_data (string_desc_t s);
- #endif
-
- /* Return true if S is the empty string. */
- #if 0 /* Defined inline below. */
--extern bool string_desc_is_empty (string_desc_t s);
-+extern bool sd_is_empty (string_desc_t s);
- #endif
-
- /* Return true if A and B are equal. */
--extern bool string_desc_equals (string_desc_t a, string_desc_t b);
-+extern bool sd_equals (string_desc_t a, string_desc_t b);
-
- /* Return true if S starts with PREFIX. */
--extern bool string_desc_startswith (string_desc_t s, string_desc_t prefix);
-+extern bool sd_startswith (string_desc_t s, string_desc_t prefix);
-
- /* Return true if S ends with SUFFIX. */
--extern bool string_desc_endswith (string_desc_t s, string_desc_t suffix);
-+extern bool sd_endswith (string_desc_t s, string_desc_t suffix);
-
- /* Return > 0, == 0, or < 0 if A > B, A == B, A < B.
- This uses a lexicographic ordering, where the bytes are compared as
- 'unsigned char'. */
--extern int string_desc_cmp (string_desc_t a, string_desc_t b);
-+extern int sd_cmp (string_desc_t a, string_desc_t b);
-
- /* Return > 0, == 0, or < 0 if A > B, A == B, A < B.
- Either A or B must be entirely ASCII.
- This uses a lexicographic ordering, where the bytes are compared as
- 'unsigned char', ignoring case, in the "C" locale. */
--extern int string_desc_c_casecmp (string_desc_t a, string_desc_t b);
-+extern int sd_c_casecmp (string_desc_t a, string_desc_t b);
-
- /* Return the index of the first occurrence of C in S,
- or -1 if there is none. */
--extern ptrdiff_t string_desc_index (string_desc_t s, char c);
-+extern ptrdiff_t sd_index (string_desc_t s, char c);
-
- /* Return the index of the last occurrence of C in S,
- or -1 if there is none. */
--extern ptrdiff_t string_desc_last_index (string_desc_t s, char c);
-+extern ptrdiff_t sd_last_index (string_desc_t s, char c);
-
- /* Return the index of the first occurrence of NEEDLE in HAYSTACK,
- or -1 if there is none. */
--extern ptrdiff_t string_desc_contains (string_desc_t haystack, string_desc_t needle);
-+extern ptrdiff_t sd_contains (string_desc_t haystack, string_desc_t needle);
-
- /* Return an empty string. */
--extern string_desc_t string_desc_new_empty (void);
-+extern string_desc_t sd_new_empty (void);
-
- /* Construct and return a string of length N, at the given memory address. */
--extern string_desc_t string_desc_new_addr (idx_t n, char *addr);
-+extern string_desc_t sd_new_addr (idx_t n, char *addr);
-
- /* Return a string that represents the C string S, of length strlen (S). */
--extern string_desc_t string_desc_from_c (const char *s);
-+extern string_desc_t sd_from_c (const char *s);
-
- /* Return the substring of S, starting at offset START and ending at offset END.
- START must be <= END.
- The result is of length END - START.
- The result must not be freed (since its storage is part of the storage
- of S). */
--extern string_desc_t string_desc_substring (string_desc_t s, idx_t start, idx_t end);
-+extern string_desc_t sd_substring (string_desc_t s, idx_t start, idx_t end);
-
- /* Output S to the file descriptor FD.
- Return 0 if successful.
- Upon error, return -1 with errno set. */
--extern int string_desc_write (int fd, string_desc_t s);
-+extern int sd_write (int fd, string_desc_t s);
-
- /* Output S to the FILE stream FP.
- Return 0 if successful.
- Upon error, return -1. */
--extern int string_desc_fwrite (FILE *fp, string_desc_t s);
-+extern int sd_fwrite (FILE *fp, string_desc_t s);
-
-
- /* ==== Memory-allocating operations on string descriptors ==== */
-@@ -153,61 +153,61 @@ extern int string_desc_fwrite (FILE *fp,
- Return 0 if successful.
- Upon error, return -1 with errno set. */
- _GL_ATTRIBUTE_NODISCARD
--extern int string_desc_new (string_desc_t *resultp, idx_t n);
-+extern int sd_new (string_desc_t *resultp, idx_t n);
-
- /* Construct a string of length N, filled with C.
- Return 0 if successful.
- Upon error, return -1 with errno set. */
- _GL_ATTRIBUTE_NODISCARD
--extern int string_desc_new_filled (string_desc_t *resultp, idx_t n, char c);
-+extern int sd_new_filled (string_desc_t *resultp, idx_t n, char c);
-
- /* Construct a copy of string S.
- Return 0 if successful.
- Upon error, return -1 with errno set. */
- _GL_ATTRIBUTE_NODISCARD
--extern int string_desc_copy (string_desc_t *resultp, string_desc_t s);
-+extern int sd_copy (string_desc_t *resultp, string_desc_t s);
-
- /* Construct the concatenation of N strings. N must be > 0.
- Return 0 if successful.
- Upon error, return -1 with errno set. */
- _GL_ATTRIBUTE_NODISCARD
--extern int string_desc_concat (string_desc_t *resultp, idx_t n, string_desc_t string1, ...);
-+extern int sd_concat (string_desc_t *resultp, idx_t n, string_desc_t string1, ...);
-
- /* Construct a copy of string S, as a NUL-terminated C string.
- Return it is successful.
- Upon error, return NULL with errno set. */
--extern char * string_desc_c (string_desc_t s) _GL_ATTRIBUTE_DEALLOC_FREE;
-+extern char * sd_c (string_desc_t s) _GL_ATTRIBUTE_DEALLOC_FREE;
-
-
- /* ==== Operations with side effects on string descriptors ==== */
-
- /* Overwrite the byte at index I of string S with C.
- I must be < length(S). */
--extern void string_desc_set_char_at (string_desc_t s, idx_t i, char c);
-+extern void sd_set_char_at (string_desc_t s, idx_t i, char c);
-
- /* Fill part of S, starting at offset START and ending at offset END,
- with copies of C.
- START must be <= END. */
--extern void string_desc_fill (string_desc_t s, idx_t start, idx_t end, char c);
-+extern void sd_fill (string_desc_t s, idx_t start, idx_t end, char c);
-
- /* Overwrite part of S with T, starting at offset START.
- START + length(T) must be <= length (S). */
--extern void string_desc_overwrite (string_desc_t s, idx_t start, string_desc_t t);
-+extern void sd_overwrite (string_desc_t s, idx_t start, string_desc_t t);
-
- /* Free S. */
--extern void string_desc_free (string_desc_t s);
-+extern void sd_free (string_desc_t s);
-
-
- /* ==== Inline function definitions ==== */
-
- GL_STRING_DESC_INLINE idx_t
--string_desc_length (string_desc_t s)
-+sd_length (string_desc_t s)
- {
- return s._nbytes;
- }
-
- GL_STRING_DESC_INLINE char
--string_desc_char_at (string_desc_t s, idx_t i)
-+sd_char_at (string_desc_t s, idx_t i)
- {
- if (!(i >= 0 && i < s._nbytes))
- /* Invalid argument. */
-@@ -216,13 +216,13 @@ string_desc_char_at (string_desc_t s, id
- }
-
- GL_STRING_DESC_INLINE const char *
--string_desc_data (string_desc_t s)
-+sd_data (string_desc_t s)
- {
- return s._data;
- }
-
- GL_STRING_DESC_INLINE bool
--string_desc_is_empty (string_desc_t s)
-+sd_is_empty (string_desc_t s)
- {
- return s._nbytes == 0;
- }
---- a/lib/xstring-buffer.c
-+++ b/lib/xstring-buffer.c
-@@ -59,10 +59,10 @@ sb_xdupfree (struct string_buffer *buffe
- if (buffer->error)
- {
- sb_free (buffer);
-- return string_desc_new_addr (0, NULL);
-+ return sd_new_addr (0, NULL);
- }
- string_desc_t contents = sb_dupfree (buffer);
-- if (string_desc_data (contents) == NULL)
-+ if (sd_data (contents) == NULL)
- xalloc_die ();
- return contents;
- }
---- a/lib/xstring-desc.c
-+++ b/lib/xstring-desc.c
-@@ -22,7 +22,7 @@
- #include "ialloc.h"
-
- string_desc_t
--xstring_desc_concat (idx_t n, string_desc_t string1, ...)
-+xsd_concat (idx_t n, string_desc_t string1, ...)
- {
- if (n <= 0)
- /* Invalid argument. */
---- a/lib/xstring-desc.h
-+++ b/lib/xstring-desc.h
-@@ -43,53 +43,53 @@ extern "C" {
-
- /* Return a string of length N, with uninitialized contents. */
- #if 0 /* Defined inline below. */
--extern string_desc_t xstring_desc_new (idx_t n);
-+extern string_desc_t xsd_new (idx_t n);
- #endif
-
- /* Return a string of length N, filled with C. */
- #if 0 /* Defined inline below. */
--extern string_desc_t xstring_desc_new_filled (idx_t n, char c);
-+extern string_desc_t xsd_new_filled (idx_t n, char c);
- #endif
-
- /* Return a copy of string S. */
- #if 0 /* Defined inline below. */
--extern string_desc_t xstring_desc_copy (string_desc_t s);
-+extern string_desc_t xsd_copy (string_desc_t s);
- #endif
-
- /* Return the concatenation of N strings. N must be > 0. */
--extern string_desc_t xstring_desc_concat (idx_t n, string_desc_t string1, ...);
-+extern string_desc_t xsd_concat (idx_t n, string_desc_t string1, ...);
-
- /* Construct and return a copy of string S, as a NUL-terminated C string. */
- #if 0 /* Defined inline below. */
--extern char * xstring_desc_c (string_desc_t s) _GL_ATTRIBUTE_DEALLOC_FREE;
-+extern char * xsd_c (string_desc_t s) _GL_ATTRIBUTE_DEALLOC_FREE;
- #endif
-
-
- /* ==== Inline function definitions ==== */
-
- GL_XSTRING_DESC_INLINE string_desc_t
--xstring_desc_new (idx_t n)
-+xsd_new (idx_t n)
- {
- string_desc_t result;
-- if (string_desc_new (&result, n) < 0)
-+ if (sd_new (&result, n) < 0)
- xalloc_die ();
- return result;
- }
-
- GL_XSTRING_DESC_INLINE string_desc_t
--xstring_desc_new_filled (idx_t n, char c)
-+xsd_new_filled (idx_t n, char c)
- {
- string_desc_t result;
-- if (string_desc_new_filled (&result, n, c) < 0)
-+ if (sd_new_filled (&result, n, c) < 0)
- xalloc_die ();
- return result;
- }
-
- GL_XSTRING_DESC_INLINE string_desc_t
--xstring_desc_copy (string_desc_t s)
-+xsd_copy (string_desc_t s)
- {
- string_desc_t result;
-- if (string_desc_copy (&result, s) < 0)
-+ if (sd_copy (&result, s) < 0)
- xalloc_die ();
- return result;
- }
-@@ -97,9 +97,9 @@ xstring_desc_copy (string_desc_t s)
- GL_XSTRING_DESC_INLINE
- _GL_ATTRIBUTE_DEALLOC_FREE
- char *
--xstring_desc_c (string_desc_t s)
-+xsd_c (string_desc_t s)
- {
-- char *result = string_desc_c (s);
-+ char *result = sd_c (s);
- if (result == NULL)
- xalloc_die ();
- return result;
---- a/tests/test-string-desc-quotearg.c
-+++ b/tests/test-string-desc-quotearg.c
-@@ -28,75 +28,75 @@
- int
- main (void)
- {
-- string_desc_t s1 = string_desc_from_c ("Hello world!");
-- string_desc_t s2 = string_desc_new_addr (21, "The\0quick\0brown\0\0fox");
-+ string_desc_t s1 = sd_from_c ("Hello world!");
-+ string_desc_t s2 = sd_new_addr (21, "The\0quick\0brown\0\0fox");
-
-- /* Test string_desc_quotearg_buffer. */
-+ /* Test sd_quotearg_buffer. */
- {
- char buf[80];
-- size_t n = string_desc_quotearg_buffer (buf, sizeof (buf), s2, NULL);
-+ size_t n = sd_quotearg_buffer (buf, sizeof (buf), s2, NULL);
- ASSERT (n == 21);
- ASSERT (memcmp (buf, "The\0quick\0brown\0\0fox", n) == 0);
- }
-
-- /* Test string_desc_quotearg_alloc. */
-+ /* Test sd_quotearg_alloc. */
- {
- size_t n;
-- char *ret = string_desc_quotearg_alloc (s2, &n, NULL);
-+ char *ret = sd_quotearg_alloc (s2, &n, NULL);
- ASSERT (n == 21);
- ASSERT (memcmp (ret, "The\0quick\0brown\0\0fox", n) == 0);
- free (ret);
- }
-
-- /* Test string_desc_quotearg_n. */
-+ /* Test sd_quotearg_n. */
- {
-- char *ret = string_desc_quotearg_n (1, s2);
-+ char *ret = sd_quotearg_n (1, s2);
- ASSERT (memcmp (ret, "Thequickbrownfox", 16 + 1) == 0);
- }
-
-- /* Test string_desc_quotearg. */
-+ /* Test sd_quotearg. */
- {
-- char *ret = string_desc_quotearg (s2);
-+ char *ret = sd_quotearg (s2);
- ASSERT (memcmp (ret, "Thequickbrownfox", 16 + 1) == 0);
- }
-
-- /* Test string_desc_quotearg_n_style. */
-+ /* Test sd_quotearg_n_style. */
- {
-- char *ret = string_desc_quotearg_n_style (1, clocale_quoting_style, s2);
-+ char *ret = sd_quotearg_n_style (1, clocale_quoting_style, s2);
- ASSERT (memcmp (ret, "\"The\\0quick\\0brown\\0\\0fox\\0\"", 28 + 1) == 0
- || /* if the locale has UTF-8 encoding */
- memcmp (ret, "\342\200\230The\\0quick\\0brown\\0\\0fox\\0\342\200\231", 32 + 1) == 0);
- }
-
-- /* Test string_desc_quotearg_style. */
-+ /* Test sd_quotearg_style. */
- {
-- char *ret = string_desc_quotearg_style (clocale_quoting_style, s2);
-+ char *ret = sd_quotearg_style (clocale_quoting_style, s2);
- ASSERT (memcmp (ret, "\"The\\0quick\\0brown\\0\\0fox\\0\"", 28 + 1) == 0
- || /* if the locale has UTF-8 encoding */
- memcmp (ret, "\342\200\230The\\0quick\\0brown\\0\\0fox\\0\342\200\231", 32 + 1) == 0);
- }
-
-- /* Test string_desc_quotearg_char. */
-+ /* Test sd_quotearg_char. */
- {
-- char *ret = string_desc_quotearg_char (s1, ' ');
-+ char *ret = sd_quotearg_char (s1, ' ');
- ASSERT (memcmp (ret, "Hello world!", 12 + 1) == 0); /* ' ' not quoted?! */
- }
-
-- /* Test string_desc_quotearg_colon. */
-+ /* Test sd_quotearg_colon. */
- {
-- char *ret = string_desc_quotearg_colon (string_desc_from_c ("a:b"));
-+ char *ret = sd_quotearg_colon (sd_from_c ("a:b"));
- ASSERT (memcmp (ret, "a:b", 3 + 1) == 0); /* ':' not quoted?! */
- }
-
-- /* Test string_desc_quotearg_n_custom. */
-+ /* Test sd_quotearg_n_custom. */
- {
-- char *ret = string_desc_quotearg_n_custom (2, "<", ">", s1);
-+ char *ret = sd_quotearg_n_custom (2, "<", ">", s1);
- ASSERT (memcmp (ret, "<Hello world!>", 14 + 1) == 0);
- }
-
-- /* Test string_desc_quotearg_n_custom. */
-+ /* Test sd_quotearg_n_custom. */
- {
-- char *ret = string_desc_quotearg_custom ("[[", "]]", s1);
-+ char *ret = sd_quotearg_custom ("[[", "]]", s1);
- ASSERT (memcmp (ret, "[[Hello world!]]", 16 + 1) == 0);
- }
-
---- a/tests/test-string-desc.sh
-+++ b/tests/test-string-desc.sh
-@@ -6,7 +6,7 @@ ${CHECKER} test-string-desc${EXEEXT} tes
- printf 'Hello world!The\0quick\0brown\0\0fox\0' > test-string-desc.ok
-
- : "${DIFF=diff}"
--${DIFF} test-string-desc.ok test-string-desc-1.tmp || { echo "string_desc_fwrite KO" 1>&2; Exit 1; }
--${DIFF} test-string-desc.ok test-string-desc-3.tmp || { echo "string_desc_write KO" 1>&2; Exit 1; }
-+${DIFF} test-string-desc.ok test-string-desc-1.tmp || { echo "sd_fwrite KO" 1>&2; Exit 1; }
-+${DIFF} test-string-desc.ok test-string-desc-3.tmp || { echo "sd_write KO" 1>&2; Exit 1; }
-
- Exit 0
---- a/tests/test-xstring-desc.c
-+++ b/tests/test-xstring-desc.c
-@@ -28,53 +28,53 @@
- int
- main (void)
- {
-- string_desc_t s0 = string_desc_new_empty ();
-- string_desc_t s1 = string_desc_from_c ("Hello world!");
-- string_desc_t s2 = string_desc_new_addr (21, "The\0quick\0brown\0\0fox");
-+ string_desc_t s0 = sd_new_empty ();
-+ string_desc_t s1 = sd_from_c ("Hello world!");
-+ string_desc_t s2 = sd_new_addr (21, "The\0quick\0brown\0\0fox");
-
-- /* Test xstring_desc_new. */
-- string_desc_t s4 = xstring_desc_new (5);
-- string_desc_set_char_at (s4, 0, 'H');
-- string_desc_set_char_at (s4, 4, 'o');
-- string_desc_set_char_at (s4, 1, 'e');
-- string_desc_fill (s4, 2, 4, 'l');
-- ASSERT (string_desc_length (s4) == 5);
-- ASSERT (string_desc_startswith (s1, s4));
-+ /* Test xsd_new. */
-+ string_desc_t s4 = xsd_new (5);
-+ sd_set_char_at (s4, 0, 'H');
-+ sd_set_char_at (s4, 4, 'o');
-+ sd_set_char_at (s4, 1, 'e');
-+ sd_fill (s4, 2, 4, 'l');
-+ ASSERT (sd_length (s4) == 5);
-+ ASSERT (sd_startswith (s1, s4));
-
-- /* Test xstring_desc_new_filled. */
-- string_desc_t s5 = xstring_desc_new_filled (5, 'l');
-- string_desc_set_char_at (s5, 0, 'H');
-- string_desc_set_char_at (s5, 4, 'o');
-- string_desc_set_char_at (s5, 1, 'e');
-- ASSERT (string_desc_length (s5) == 5);
-- ASSERT (string_desc_startswith (s1, s5));
-+ /* Test xsd_new_filled. */
-+ string_desc_t s5 = xsd_new_filled (5, 'l');
-+ sd_set_char_at (s5, 0, 'H');
-+ sd_set_char_at (s5, 4, 'o');
-+ sd_set_char_at (s5, 1, 'e');
-+ ASSERT (sd_length (s5) == 5);
-+ ASSERT (sd_startswith (s1, s5));
-
-- /* Test xstring_desc_copy. */
-+ /* Test xsd_copy. */
- {
-- string_desc_t s6 = xstring_desc_copy (s0);
-- ASSERT (string_desc_is_empty (s6));
-- string_desc_free (s6);
-+ string_desc_t s6 = xsd_copy (s0);
-+ ASSERT (sd_is_empty (s6));
-+ sd_free (s6);
- }
- {
-- string_desc_t s6 = xstring_desc_copy (s2);
-- ASSERT (string_desc_equals (s6, s2));
-- string_desc_free (s6);
-+ string_desc_t s6 = xsd_copy (s2);
-+ ASSERT (sd_equals (s6, s2));
-+ sd_free (s6);
- }
-
-- /* Test xstring_desc_concat. */
-+ /* Test xsd_concat. */
- {
- string_desc_t s8 =
-- xstring_desc_concat (3, string_desc_new_addr (10, "The\0quick"),
-- string_desc_new_addr (7, "brown\0"),
-- string_desc_new_addr (4, "fox"),
-- string_desc_new_addr (7, "unused"));
-- ASSERT (string_desc_equals (s8, s2));
-- string_desc_free (s8);
-+ xsd_concat (3, sd_new_addr (10, "The\0quick"),
-+ sd_new_addr (7, "brown\0"),
-+ sd_new_addr (4, "fox"),
-+ sd_new_addr (7, "unused"));
-+ ASSERT (sd_equals (s8, s2));
-+ sd_free (s8);
- }
-
-- /* Test xstring_desc_c. */
-+ /* Test xsd_c. */
- {
-- char *ptr = xstring_desc_c (s2);
-+ char *ptr = xsd_c (s2);
- ASSERT (ptr != NULL);
- ASSERT (memcmp (ptr, "The\0quick\0brown\0\0fox\0", 22) == 0);
- free (ptr);
+++ /dev/null
-From 60cd34886c2c9f509974239fcf64a61f9a507d14 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Tue, 25 Feb 2025 09:04:28 +0100
-Subject: [PATCH] vc-mtime: Reduce number of read() system calls.
-
-* lib/vc-mtime.c: Include <stddef.h>.
-(git_vc_controlled): Read bytes into a buffer, not one-by-one.
----
- ChangeLog | 6 ++++++
- lib/vc-mtime.c | 15 +++++++++++----
- 2 files changed, 17 insertions(+), 4 deletions(-)
-
---- a/lib/vc-mtime.c
-+++ b/lib/vc-mtime.c
-@@ -21,6 +21,7 @@
- /* Specification. */
- #include "vc-mtime.h"
-
-+#include <stddef.h>
- #include <stdlib.h>
- #include <unistd.h>
-
-@@ -56,11 +57,17 @@ git_vc_controlled (const char *filename)
- return false;
-
- /* Read the subprocess output, and test whether it is non-empty. */
-- size_t count = 0;
-- char c;
-+ ptrdiff_t count = 0;
-
-- while (safe_read (fd[0], &c, 1) > 0)
-- count++;
-+ for (;;)
-+ {
-+ char buf[1024];
-+ ptrdiff_t n = safe_read (fd[0], buf, sizeof (buf));
-+ if (n > 0)
-+ count += n;
-+ else
-+ break;
-+ }
-
- close (fd[0]);
-
+++ /dev/null
-From 78269749030dde23182c29376d1410592436eb5d Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Thu, 1 May 2025 17:26:27 +0200
-Subject: [PATCH] vc-mtime: Add API for more efficient use of git.
-
-Reported by Serhii Tereshchenko, Arthur, Adam YS, Foucauld Degeorges
-at <https://savannah.gnu.org/bugs/?66865>.
-
-* lib/vc-mtime.h (max_vc_mtime): New declaration.
-* lib/vc-mtime.c: Include <errno.h>, <stdio.h>, <string.h>, filename.h,
-xalloc.h, xgetcwd.h, xvasprintf.h, gl_map.h, gl_xmap.h, gl_hash_map.h,
-hashkey-string.h, unlocked-io.h.
-(is_git_present): New function, extracted from vc_mtime.
-(vc_mtime): Invoke it.
-(MAX_COMMAND_LENGTH, MAX_CMD_LEN): New macros.
-(abs_git_checkout): New function, based on execute_and_read_line in
-lib/javacomp.c.
-(ancestor_level, relativize): New functions.
-(struct accumulator): New type.
-(accumulate): New function.
-(max_vc_mtime): New function.
-(test_ancestor_level, test_relativize, main) [TEST]: New functions.
-* modules/vc-mtime (Depends-on): Add filename, xalloc, xgetcwd,
-canonicalize-lgpl, xvasprintf, str_startswith, map, xmap, hash-map,
-hashkey-string, getdelim.
----
- ChangeLog | 23 ++
- lib/vc-mtime.c | 866 +++++++++++++++++++++++++++++++++++++++++++++--
- lib/vc-mtime.h | 7 +
- modules/vc-mtime | 11 +
- 4 files changed, 886 insertions(+), 21 deletions(-)
-
---- a/lib/vc-mtime.c
-+++ b/lib/vc-mtime.c
-@@ -21,8 +21,11 @@
- /* Specification. */
- #include "vc-mtime.h"
-
-+#include <errno.h>
- #include <stddef.h>
-+#include <stdio.h>
- #include <stdlib.h>
-+#include <string.h>
- #include <unistd.h>
-
- #include <error.h>
-@@ -32,11 +35,51 @@
- #include "safe-read.h"
- #include "xstrtol.h"
- #include "stat-time.h"
-+#include "filename.h"
-+#include "xalloc.h"
-+#include "xgetcwd.h"
-+#include "xvasprintf.h"
-+#include "gl_map.h"
-+#include "gl_xmap.h"
-+#include "gl_hash_map.h"
-+#include "hashkey-string.h"
-+#if USE_UNLOCKED_IO
-+# include "unlocked-io.h"
-+#endif
- #include "gettext.h"
-
- #define _(msgid) dgettext ("gnulib", msgid)
-
-
-+/* ========================================================================== */
-+
-+/* Determines whether git is present. */
-+static bool
-+is_git_present (void)
-+{
-+ static bool git_tested;
-+ static bool git_present;
-+
-+ if (!git_tested)
-+ {
-+ /* Test for presence of git:
-+ "git --version >/dev/null 2>/dev/null" */
-+ const char *argv[3];
-+ int exitstatus;
-+
-+ argv[0] = "git";
-+ argv[1] = "--version";
-+ argv[2] = NULL;
-+ exitstatus = execute ("git", "git", argv, NULL, NULL,
-+ false, false, true, true,
-+ true, false, NULL);
-+ git_present = (exitstatus == 0);
-+ git_tested = true;
-+ }
-+
-+ return git_present;
-+}
-+
- /* Determines whether the specified file is under version control. */
- static bool
- git_vc_controlled (const char *filename)
-@@ -178,27 +221,7 @@ git_mtime (struct timespec *mtime, const
- int
- vc_mtime (struct timespec *mtime, const char *filename)
- {
-- static bool git_tested;
-- static bool git_present;
--
-- if (!git_tested)
-- {
-- /* Test for presence of git:
-- "git --version >/dev/null 2>/dev/null" */
-- const char *argv[3];
-- int exitstatus;
--
-- argv[0] = "git";
-- argv[1] = "--version";
-- argv[2] = NULL;
-- exitstatus = execute ("git", "git", argv, NULL, NULL,
-- false, false, true, true,
-- true, false, NULL);
-- git_present = (exitstatus == 0);
-- git_tested = true;
-- }
--
-- if (git_present
-+ if (is_git_present ()
- && git_vc_controlled (filename)
- && git_unmodified (filename))
- {
-@@ -213,3 +236,804 @@ vc_mtime (struct timespec *mtime, const
- }
- return -1;
- }
-+
-+/* ========================================================================== */
-+
-+/* Maximum length of a command that is guaranteed to work. */
-+#if defined _WIN32 || defined __CYGWIN__
-+/* Windows */
-+# define MAX_COMMAND_LENGTH 8192
-+#else
-+/* Unix platforms */
-+# define MAX_COMMAND_LENGTH 32768
-+#endif
-+/* Keep some safe distance to this maximum. */
-+#define MAX_CMD_LEN ((int) (MAX_COMMAND_LENGTH * 0.8))
-+
-+/* Returns the directory name of the git checkout that contains tha current
-+ directory, as an absolute file name, or NULL if the current directory is
-+ not in a git checkout. */
-+static char *
-+abs_git_checkout (void)
-+{
-+ /* Run "git rev-parse --show-toplevel 2>/dev/null" and return its output,
-+ without the trailing newline. */
-+ const char *argv[4];
-+ pid_t child;
-+ int fd[1];
-+
-+ argv[0] = "git";
-+ argv[1] = "rev-parse";
-+ argv[2] = "--show-toplevel";
-+ argv[3] = NULL;
-+ child = create_pipe_in ("git", "git", argv, NULL, NULL,
-+ DEV_NULL, true, true, false, fd);
-+
-+ if (child == -1)
-+ return NULL;
-+
-+ /* Retrieve its result. */
-+ FILE *fp = fdopen (fd[0], "r");
-+ if (fp == NULL)
-+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
-+
-+ char *line = NULL;
-+ size_t linesize = 0;
-+ size_t linelen = getline (&line, &linesize, fp);
-+ if (linelen == (size_t)(-1))
-+ {
-+ fclose (fp);
-+ wait_subprocess (child, "git", true, true, true, false, NULL);
-+ return NULL;
-+ }
-+ else
-+ {
-+ int exitstatus;
-+
-+ if (linelen > 0 && line[linelen - 1] == '\n')
-+ line[linelen - 1] = '\0';
-+
-+ /* Read until EOF (otherwise the child process may get a SIGPIPE signal). */
-+ while (getc (fp) != EOF)
-+ ;
-+
-+ fclose (fp);
-+
-+ /* Remove zombie process from process list, and retrieve exit status. */
-+ exitstatus =
-+ wait_subprocess (child, "git", true, true, true, false, NULL);
-+ if (exitstatus == 0)
-+ return line;
-+ }
-+ free (line);
-+ return NULL;
-+}
-+
-+/* Given an absolute canonicalized directory DIR1 and an absolute canonicalized
-+ directory DIR2, returns N where DIR1 = DIR2 "/.." ... "/.." with N times
-+ "/..", or -1 if DIR1 is not an ancestor directory of DIR2. */
-+static long
-+ancestor_level (const char *dir1, const char *dir2)
-+{
-+ if (strcmp (dir1, "/") == 0)
-+ dir1 = "";
-+ if (strcmp (dir2, "/") == 0)
-+ dir2 = "";
-+ size_t dir1_len = strlen (dir1);
-+ if (strncmp (dir1, dir2, dir1_len) == 0)
-+ {
-+ /* DIR2 starts with DIR1. */
-+ const char *p = dir2 + dir1_len;
-+ if (*p == '\0')
-+ /* DIR2 and DIR1 are the same. */
-+ return 0;
-+ if (ISSLASH (*p))
-+ {
-+ /* Return the number of slashes in the tail of DIR2 that starts
-+ at P. */
-+ long n = 1;
-+ p++;
-+ for (; *p != '\0'; p++)
-+ if (ISSLASH (*p))
-+ n++;
-+ return n;
-+ }
-+ }
-+ return -1;
-+}
-+
-+/* Given an absolute canolicalized FILENAME that starts with DIR1, returns the
-+ same file name relative to DIR2, where DIR1 = DIR2 "/.." ... "/.." with
-+ N times "/..", as a freshly allocated string. */
-+static char *
-+relativize (const char *filename,
-+ unsigned long n, const char *dir1, const char *dir2)
-+{
-+ if (strcmp (dir1, "/") == 0)
-+ dir1 = "";
-+ size_t dir1_len = strlen (dir1);
-+ if (!(strncmp (filename, dir1, dir1_len) == 0
-+ && (filename[dir1_len] == '\0' || ISSLASH (filename[dir1_len]))))
-+ /* Invalid argument. */
-+ abort ();
-+ if (strcmp (dir2, "/") == 0)
-+ dir2 = "";
-+
-+ dir2 += dir1_len;
-+ filename += dir1_len;
-+ for (;;)
-+ {
-+ /* Invariant: The result will be N times "../" followed by FILENAME. */
-+ if (*filename == '\0')
-+ break;
-+ if (!ISSLASH (*filename))
-+ abort ();
-+ filename++;
-+ if (*dir2 == '\0')
-+ break;
-+ if (!ISSLASH (*dir2))
-+ abort ();
-+ dir2++;
-+ /* Skip one component in DIR2. */
-+ const char *dir2_s;
-+ for (dir2_s = dir2; *dir2_s != '\0'; dir2_s++)
-+ if (ISSLASH (*dir2_s))
-+ break;
-+ /* Skip one component in FILENAME, at P. */
-+ const char *filename_s;
-+ for (filename_s = filename; *filename_s != '\0'; filename_s++)
-+ if (ISSLASH (*filename_s))
-+ break;
-+ /* Did the components match? */
-+ if (!(filename_s - filename == dir2_s - dir2
-+ && memcmp (filename, dir2, dir2_s - dir2) == 0))
-+ break;
-+ dir2 = dir2_s;
-+ filename = filename_s;
-+ n--;
-+ }
-+
-+ if (n == 0 && *filename == '\0')
-+ return xstrdup (".");
-+
-+ char *result = (char *) xmalloc (3 * n + strlen (filename) + 1);
-+ {
-+ char *q = result;
-+ for (; n > 0; n--)
-+ {
-+ q[0] = '.'; q[1] = '.'; q[2] = '/'; q += 3;
-+ }
-+ strcpy (q, filename);
-+ }
-+ return result;
-+}
-+
-+/* Accumulating mtimes. */
-+struct accumulator
-+{
-+ bool has_some_mtimes;
-+ struct timespec max_of_mtimes;
-+};
-+
-+static void
-+accumulate (struct accumulator *accu, struct timespec mtime)
-+{
-+ if (accu->has_some_mtimes)
-+ {
-+ /* Compute the maximum of accu->max_of_mtimes and mtime. */
-+ if (accu->max_of_mtimes.tv_sec < mtime.tv_sec
-+ || (accu->max_of_mtimes.tv_sec == mtime.tv_sec
-+ && accu->max_of_mtimes.tv_nsec < mtime.tv_nsec))
-+ accu->max_of_mtimes = mtime;
-+ }
-+ else
-+ {
-+ accu->max_of_mtimes = mtime;
-+ accu->has_some_mtimes = true;
-+ }
-+}
-+
-+int
-+max_vc_mtime (struct timespec *max_of_mtimes,
-+ size_t nfiles, const char * const *filenames)
-+{
-+ if (nfiles == 0)
-+ /* Invalid argument. */
-+ abort ();
-+
-+ struct accumulator accu = { false };
-+
-+ /* Determine which of the specified files are under version control,
-+ and which are duplicates. (The case of duplicates is rare, but it needs
-+ special attention, because 'git ls-files' eliminates duplicates.)
-+ vc_controlled[n] = 1 means that filenames[n] is under version control.
-+ vc_controlled[n] = 0 means that filenames[n] is not under version control.
-+ vc_controlled[n] = -1 means that filenames[n] is a duplicate. */
-+ signed char *vc_controlled = XNMALLOC (nfiles, signed char);
-+ for (size_t n = 0; n < nfiles; n++)
-+ vc_controlled[n] = 0;
-+
-+ if (is_git_present ())
-+ {
-+ /* Since 'git ls-files' produces an error when at least one of the files
-+ is outside the git checkout that contains tha current directory, we
-+ need to filter out such files. This is most easily done by converting
-+ each file name to a canonical file name first and then comparing with
-+ the directory name of said git checkout. */
-+ char *git_checkout = abs_git_checkout ();
-+ if (git_checkout != NULL)
-+ {
-+ char *currdir = xgetcwd ();
-+ /* git_checkout is expected to be an ancestor directory of the
-+ current directory. */
-+ long ancestor = ancestor_level (git_checkout, currdir);
-+ if (ancestor >= 0)
-+ {
-+ char **canonical_filenames = XNMALLOC (nfiles, char *);
-+ for (size_t n = 0; n < nfiles; n++)
-+ {
-+ char *canonical = canonicalize_file_name (filenames[n]);
-+ if (canonical == NULL)
-+ {
-+ if (errno == ENOMEM)
-+ xalloc_die ();
-+ /* The file filenames[n] does not exist. */
-+ for (size_t k = n; k > 0; )
-+ free (canonical_filenames[--k]);
-+ free (canonical_filenames);
-+ free (currdir);
-+ free (git_checkout);
-+ free (vc_controlled);
-+ return -1;
-+ }
-+ canonical_filenames[n] = canonical;
-+ }
-+
-+ /* Test which of these absolute file names are outside of the
-+ git_checkout. */
-+ char *git_checkout_slash =
-+ (strcmp (git_checkout, "/") == 0
-+ ? xstrdup (git_checkout)
-+ : xasprintf ("%s/", git_checkout));
-+
-+ char **checkout_relative_filenames = XNMALLOC (nfiles, char *);
-+ char **currdir_relative_filenames = XNMALLOC (nfiles, char *);
-+ for (size_t n = 0; n < nfiles; n++)
-+ {
-+ if (str_startswith (canonical_filenames[n], git_checkout_slash))
-+ {
-+ vc_controlled[n] = 1;
-+ checkout_relative_filenames[n] =
-+ relativize (canonical_filenames[n],
-+ 0, git_checkout, git_checkout);
-+ currdir_relative_filenames[n] =
-+ relativize (canonical_filenames[n],
-+ ancestor, git_checkout, currdir);
-+ }
-+ else
-+ {
-+ vc_controlled[n] = 0;
-+ checkout_relative_filenames[n] = NULL;
-+ currdir_relative_filenames[n] = NULL;
-+ }
-+ }
-+
-+ /* Room for passing arguments to git commands. */
-+ const char **argv = XNMALLOC (6 + nfiles + 1, const char *);
-+
-+ {
-+ /* Put the relative file names into a hash table. This is needed
-+ because 'git ls-files' returns the files in a different order
-+ than the one we provide in the command. */
-+ gl_map_t relative_filenames_ht =
-+ gl_map_create_empty (GL_HASH_MAP,
-+ hashkey_string_equals, hashkey_string_hash,
-+ NULL, NULL);
-+ for (size_t n = 0; n < nfiles; n++)
-+ if (currdir_relative_filenames[n] != NULL)
-+ {
-+ if (gl_map_get (relative_filenames_ht, currdir_relative_filenames[n]) != NULL)
-+ {
-+ /* It's already in the table. */
-+ vc_controlled[n] = -1;
-+ }
-+ else
-+ gl_map_put (relative_filenames_ht, currdir_relative_filenames[n], &vc_controlled[n]);
-+ }
-+
-+ /* Run "git ls-files -c -o -t -z FILE1..." for as many files as
-+ possible, and inspect the output. */
-+ size_t n0 = 0;
-+ do
-+ {
-+ size_t i = 0;
-+ argv[i++] = "git";
-+ argv[i++] = "ls-files";
-+ argv[i++] = "-c";
-+ argv[i++] = "-o";
-+ argv[i++] = "-t";
-+ argv[i++] = "-z";
-+ size_t i0 = i;
-+
-+ size_t n = n0;
-+ size_t cmd_len = 25;
-+ for (; n < nfiles; n++)
-+ {
-+ if (vc_controlled[n] == 1)
-+ {
-+ if (cmd_len + strlen (currdir_relative_filenames[n]) >= MAX_CMD_LEN
-+ && i > i0)
-+ break;
-+ argv[i++] = currdir_relative_filenames[n];
-+ cmd_len += 1 + strlen (currdir_relative_filenames[n]);
-+ }
-+ n++;
-+ }
-+ if (i > i0)
-+ {
-+ pid_t child;
-+ int fd[1];
-+
-+ argv[i] = NULL;
-+ child = create_pipe_in ("git", "git", argv, NULL, NULL,
-+ DEV_NULL, true, true, false, fd);
-+ if (child == -1)
-+ break;
-+
-+ /* Read the subprocess output. It is expected to be of the form
-+ T1 <space> <currdir_relative_filename1> NUL
-+ T2 <space> <currdir_relative_filename2> NUL
-+ ...
-+ where the relative filenames correspond to the given file
-+ names (because we have already relativized them). */
-+ FILE *fp = fdopen (fd[0], "r");
-+ if (fp == NULL)
-+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
-+
-+ char *fn = NULL;
-+ size_t fn_size = 0;
-+ for (;;)
-+ {
-+ int status = fgetc (fp);
-+ if (status == EOF)
-+ break;
-+ /* status is a status tag, as documented in
-+ "man git-ls-files". */
-+
-+ int space = fgetc (fp);
-+ if (space != ' ')
-+ {
-+ fprintf (stderr, "vc-mtime: git ls-files output not as expected\n");
-+ break;
-+ }
-+
-+ if (getdelim (&fn, &fn_size, '\0', fp) == -1)
-+ {
-+ if (errno == ENOMEM)
-+ xalloc_die ();
-+ fprintf (stderr, "vc-mtime: failed to read git ls-files output\n");
-+ break;
-+ }
-+ signed char *vc_controlled_p =
-+ (signed char *) gl_map_get (relative_filenames_ht, fn);
-+ if (vc_controlled_p == NULL)
-+ fprintf (stderr, "vc-mtime: git ls-files returned an unexpected file name: %s\n", fn);
-+ else
-+ *vc_controlled_p = (status == 'H' ? 1 : 0);
-+ }
-+
-+ free (fn);
-+ fclose (fp);
-+
-+ /* Remove zombie process from process list, and retrieve exit status. */
-+ int exitstatus =
-+ wait_subprocess (child, "git", false, true, true, false, NULL);
-+ if (exitstatus != 0)
-+ fprintf (stderr, "vc-mtime: git ls-files failed with exit code %d\n", exitstatus);
-+ }
-+ n0 = n;
-+ }
-+ while (n0 < nfiles);
-+
-+ gl_map_free (relative_filenames_ht);
-+ }
-+
-+ {
-+ /* Put the relative file names into a hash table. This is needed
-+ because 'git diff' returns the files in a different order
-+ than the one we provide in the command. */
-+ gl_map_t relative_filenames_ht =
-+ gl_map_create_empty (GL_HASH_MAP,
-+ hashkey_string_equals, hashkey_string_hash,
-+ NULL, NULL);
-+ for (size_t n = 0; n < nfiles; n++)
-+ if (vc_controlled[n] == 1)
-+ {
-+ /* No need to test for duplicates here. We have already set
-+ vc_controlled[n] to -1 for duplicates, above. */
-+ gl_map_put (relative_filenames_ht, checkout_relative_filenames[n], &vc_controlled[n]);
-+ }
-+
-+ /* Run "git diff --name-only --no-relative -z HEAD -- FILE1..." for
-+ as many files as possible, and inspect the output. */
-+ size_t n0 = 0;
-+ do
-+ {
-+ size_t i = 0;
-+ argv[i++] = "git";
-+ argv[i++] = "diff";
-+ argv[i++] = "--name-only";
-+ argv[i++] = "--no-relative";
-+ argv[i++] = "-z";
-+ argv[i++] = "HEAD";
-+ argv[i++] = "--";
-+ size_t i0 = i;
-+
-+ size_t n = n0;
-+ size_t cmd_len = 46;
-+ for (; n < nfiles; n++)
-+ {
-+ if (vc_controlled[n] == 1)
-+ {
-+ if (cmd_len + strlen (currdir_relative_filenames[n]) >= MAX_CMD_LEN
-+ && i > i0)
-+ break;
-+ argv[i++] = currdir_relative_filenames[n];
-+ cmd_len += 1 + strlen (currdir_relative_filenames[n]);
-+ }
-+ n++;
-+ }
-+ if (i > i0)
-+ {
-+ pid_t child;
-+ int fd[1];
-+
-+ argv[i] = NULL;
-+ child = create_pipe_in ("git", "git", argv, NULL, NULL,
-+ DEV_NULL, true, true, false, fd);
-+ if (child == -1)
-+ break;
-+
-+ /* Read the subprocess output. It is expected to be of the form
-+ <checkout_relative_filename1> NUL
-+ <checkout_relative_filename2> NUL
-+ ...
-+ where the relative filenames are relative to the git
-+ checkout dir, not to currdir! */
-+ FILE *fp = fdopen (fd[0], "r");
-+ if (fp == NULL)
-+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
-+
-+ char *fn = NULL;
-+ size_t fn_size = 0;
-+ for (;;)
-+ {
-+ /* Test for EOF. */
-+ int c = fgetc (fp);
-+ if (c == EOF)
-+ break;
-+ ungetc (c, fp);
-+
-+ if (getdelim (&fn, &fn_size, '\0', fp) == -1)
-+ {
-+ if (errno == ENOMEM)
-+ xalloc_die ();
-+ fprintf (stderr, "vc-mtime: failed to read git diff output\n");
-+ break;
-+ }
-+ signed char *vc_controlled_p =
-+ (signed char *) gl_map_get (relative_filenames_ht, fn);
-+ if (vc_controlled_p == NULL)
-+ fprintf (stderr, "vc-mtime: git diff returned an unexpected file name: %s\n", fn);
-+ else
-+ /* filenames[n] is under version control but is modified.
-+ Treat it like a file not under version control. */
-+ *vc_controlled_p = 0;
-+ }
-+
-+ free (fn);
-+ fclose (fp);
-+
-+ /* Remove zombie process from process list, and retrieve exit status. */
-+ int exitstatus =
-+ wait_subprocess (child, "git", false, true, true, false, NULL);
-+ if (exitstatus != 0)
-+ fprintf (stderr, "vc-mtime: git diff failed with exit code %d\n", exitstatus);
-+ }
-+ n0 = n;
-+ }
-+ while (n0 < nfiles);
-+
-+ gl_map_free (relative_filenames_ht);
-+ }
-+
-+ {
-+ /* Run "git log -1 --format=%ct -- FILE1...". It prints the
-+ time of last modification (the 'CommitDate', not the
-+ 'AuthorDate' which merely represents the time at which the
-+ author locally committed the first version of the change),
-+ as the number of seconds since the Epoch. The '--' option
-+ is for the case that the specified file was removed. */
-+ size_t n0 = 0;
-+ do
-+ {
-+ size_t i = 0;
-+ argv[i++] = "git";
-+ argv[i++] = "log";
-+ argv[i++] = "-1";
-+ argv[i++] = "--format=%ct";
-+ argv[i++] = "--";
-+ size_t i0 = i;
-+
-+ size_t n = n0;
-+ size_t cmd_len = 27;
-+ for (; n < nfiles; n++)
-+ {
-+ if (vc_controlled[n] == 1)
-+ {
-+ if (cmd_len + strlen (currdir_relative_filenames[n]) >= MAX_CMD_LEN
-+ && i > i0)
-+ break;
-+ argv[i++] = currdir_relative_filenames[n];
-+ cmd_len += 1 + strlen (currdir_relative_filenames[n]);
-+ }
-+ n++;
-+ }
-+ if (i > i0)
-+ {
-+ pid_t child;
-+ int fd[1];
-+
-+ argv[i] = NULL;
-+ child = create_pipe_in ("git", "git", argv, NULL, NULL,
-+ DEV_NULL, true, true, false, fd);
-+ if (child == -1)
-+ break;
-+
-+ /* Read the subprocess output. It is expected to be a
-+ single line, containing a positive integer. */
-+ FILE *fp = fdopen (fd[0], "r");
-+ if (fp == NULL)
-+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
-+
-+ char *line = NULL;
-+ size_t linesize = 0;
-+ size_t linelen = getline (&line, &linesize, fp);
-+ if (linelen == (size_t)(-1))
-+ {
-+ if (errno == ENOMEM)
-+ xalloc_die ();
-+ fprintf (stderr, "vc-mtime: failed to read git log output\n");
-+ git_log_fail1:
-+ free (line);
-+ fclose (fp);
-+ wait_subprocess (child, "git", true, false, true, false, NULL);
-+ git_log_fail2:
-+ free (argv);
-+ for (size_t k = nfiles; k > 0; )
-+ free (currdir_relative_filenames[--k]);
-+ free (currdir_relative_filenames);
-+ for (size_t k = nfiles; k > 0; )
-+ free (checkout_relative_filenames[--k]);
-+ free (checkout_relative_filenames);
-+ free (git_checkout_slash);
-+ for (size_t k = nfiles; k > 0; )
-+ free (canonical_filenames[--k]);
-+ free (canonical_filenames);
-+ free (currdir);
-+ free (git_checkout);
-+ free (vc_controlled);
-+ return -1;
-+ }
-+ if (linelen > 0 && line[linelen - 1] == '\n')
-+ line[linelen - 1] = '\0';
-+
-+ char *endptr;
-+ unsigned long git_log_time;
-+ if (!(xstrtoul (line, &endptr, 10, &git_log_time, NULL) == LONGINT_OK
-+ && endptr == line + strlen (line)))
-+ {
-+ fprintf (stderr, "vc-mtime: git log output not as expected\n");
-+ goto git_log_fail1;
-+ }
-+
-+ struct timespec mtime;
-+ mtime.tv_sec = git_log_time;
-+ mtime.tv_nsec = 0;
-+ accumulate (&accu, mtime);
-+
-+ free (line);
-+ fclose (fp);
-+
-+ /* Remove zombie process from process list, and retrieve exit status. */
-+ int exitstatus =
-+ wait_subprocess (child, "git", false, true, true, false, NULL);
-+ if (exitstatus != 0)
-+ {
-+ fprintf (stderr, "vc-mtime: git log failed with exit code %d\n", exitstatus);
-+ goto git_log_fail2;
-+ }
-+ }
-+ n0 = n;
-+ }
-+ while (n0 < nfiles);
-+ }
-+
-+ free (argv);
-+ for (size_t k = nfiles; k > 0; )
-+ free (currdir_relative_filenames[--k]);
-+ free (currdir_relative_filenames);
-+ for (size_t k = nfiles; k > 0; )
-+ free (checkout_relative_filenames[--k]);
-+ free (checkout_relative_filenames);
-+ free (git_checkout_slash);
-+ for (size_t k = nfiles; k > 0; )
-+ free (canonical_filenames[--k]);
-+ free (canonical_filenames);
-+ }
-+ free (currdir);
-+ }
-+ free (git_checkout);
-+ }
-+
-+ /* For the files that are not under version control, or that are modified
-+ compared to HEAD, use the file's time stamp. */
-+ for (size_t n = 0; n < nfiles; n++)
-+ if (vc_controlled[n] == 0)
-+ {
-+ struct stat statbuf;
-+ if (stat (filenames[n], &statbuf) < 0)
-+ {
-+ free (vc_controlled);
-+ return -1;
-+ }
-+
-+ struct timespec mtime = get_stat_mtime (&statbuf);
-+ accumulate (&accu, mtime);
-+ }
-+
-+ free (vc_controlled);
-+
-+ /* Since nfiles > 0, we must have accumulated at least one mtime. */
-+ if (!accu.has_some_mtimes)
-+ abort ();
-+ *max_of_mtimes = accu.max_of_mtimes;
-+ return 0;
-+}
-+
-+/* ========================================================================== */
-+
-+#ifdef TEST
-+
-+#include <assert.h>
-+#include <stdio.h>
-+#include <time.h>
-+
-+/* Some unit tests for internal functions. */
-+
-+static void
-+test_ancestor_level (void)
-+{
-+ assert (ancestor_level ("/home/user/projects/gnulib", "/home/user/projects/gnulib") == 0);
-+ assert (ancestor_level ("/", "/") == 0);
-+
-+ assert (ancestor_level ("/home/user/projects/gnulib", "/home/user/projects/gnulib/lib/crypto") == 2);
-+ assert (ancestor_level ("/", "/home/user") == 2);
-+
-+ assert (ancestor_level ("/home/user/.local", "/home/user/projects/gnulib") == -1);
-+ assert (ancestor_level ("/.local", "/home/user") == -1);
-+ assert (ancestor_level ("/.local", "/") == -1);
-+}
-+
-+static void
-+test_relativize (void)
-+{
-+ assert (strcmp (relativize ("/home/user/projects/gnulib",
-+ 0, "/home/user/projects/gnulib", "/home/user/projects/gnulib"),
-+ ".") == 0);
-+ assert (strcmp (relativize ("/home/user/projects/gnulib/NEWS",
-+ 0, "/home/user/projects/gnulib", "/home/user/projects/gnulib"),
-+ "NEWS") == 0);
-+ assert (strcmp (relativize ("/home/user/projects/gnulib/doc/Makefile",
-+ 0, "/home/user/projects/gnulib", "/home/user/projects/gnulib"),
-+ "doc/Makefile") == 0);
-+
-+ assert (strcmp (relativize ("/",
-+ 0, "/", "/"),
-+ ".") == 0);
-+ assert (strcmp (relativize ("/swapfile",
-+ 0, "/", "/"),
-+ "swapfile") == 0);
-+ assert (strcmp (relativize ("/etc/passwd",
-+ 0, "/", "/"),
-+ "etc/passwd") == 0);
-+
-+ assert (strcmp (relativize ("/home/user/projects/gnulib",
-+ 2, "/home/user/projects/gnulib", "/home/user/projects/gnulib/lib/crypto"),
-+ "../../") == 0);
-+ assert (strcmp (relativize ("/home/user/projects/gnulib/lib",
-+ 2, "/home/user/projects/gnulib", "/home/user/projects/gnulib/lib/crypto"),
-+ "../") == 0);
-+ assert (strcmp (relativize ("/home/user/projects/gnulib/lib/crypto",
-+ 2, "/home/user/projects/gnulib", "/home/user/projects/gnulib/lib/crypto"),
-+ ".") == 0);
-+ assert (strcmp (relativize ("/home/user/projects/gnulib/lib/malloc",
-+ 2, "/home/user/projects/gnulib", "/home/user/projects/gnulib/lib/crypto"),
-+ "../malloc") == 0);
-+ assert (strcmp (relativize ("/home/user/projects/gnulib/lib/cr",
-+ 2, "/home/user/projects/gnulib", "/home/user/projects/gnulib/lib/crypto"),
-+ "../cr") == 0);
-+ assert (strcmp (relativize ("/home/user/projects/gnulib/lib/cryptography",
-+ 2, "/home/user/projects/gnulib", "/home/user/projects/gnulib/lib/crypto"),
-+ "../cryptography") == 0);
-+ assert (strcmp (relativize ("/home/user/projects/gnulib/doc",
-+ 2, "/home/user/projects/gnulib", "/home/user/projects/gnulib/lib/crypto"),
-+ "../../doc") == 0);
-+ assert (strcmp (relativize ("/home/user/projects/gnulib/doc/Makefile",
-+ 2, "/home/user/projects/gnulib", "/home/user/projects/gnulib/lib/crypto"),
-+ "../../doc/Makefile") == 0);
-+
-+ assert (strcmp (relativize ("/",
-+ 2, "/", "/home/user"),
-+ "../../") == 0);
-+ assert (strcmp (relativize ("/home",
-+ 2, "/", "/home/user"),
-+ "../") == 0);
-+ assert (strcmp (relativize ("/home/user",
-+ 2, "/", "/home/user"),
-+ ".") == 0);
-+ assert (strcmp (relativize ("/home/root",
-+ 2, "/", "/home/user"),
-+ "../root") == 0);
-+ assert (strcmp (relativize ("/home/us",
-+ 2, "/", "/home/user"),
-+ "../us") == 0);
-+ assert (strcmp (relativize ("/home/users",
-+ 2, "/", "/home/user"),
-+ "../users") == 0);
-+ assert (strcmp (relativize ("/etc",
-+ 2, "/", "/home/user"),
-+ "../../etc") == 0);
-+ assert (strcmp (relativize ("/etc/passwd",
-+ 2, "/", "/home/user"),
-+ "../../etc/passwd") == 0);
-+}
-+
-+/* Usage: ./a.out FILE[...]
-+ */
-+int
-+main (int argc, char *argv[])
-+{
-+ test_ancestor_level ();
-+ test_relativize ();
-+
-+ if (argc == 1)
-+ {
-+ fprintf (stderr, "Usage: ./a.out FILE[...]\n");
-+ return 1;
-+ }
-+ struct timespec mtime;
-+ int ret = max_vc_mtime (&mtime, argc - 1, (const char **) argv + 1);
-+ if (ret == 0)
-+ {
-+ time_t t = mtime.tv_sec;
-+ struct tm *gmt = gmtime (&t);
-+ printf ("mtime = %04d-%02d-%02d %02d:%02d:%02d UTC\n",
-+ gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
-+ gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
-+ return 0;
-+ }
-+ else
-+ {
-+ printf ("failed\n");
-+ return 1;
-+ }
-+}
-+
-+/*
-+ * Local Variables:
-+ * compile-command: "gcc -ggdb -DTEST -Wall -I. -I.. vc-mtime.c libgnu.a"
-+ * End:
-+ */
-+
-+#endif
---- a/lib/vc-mtime.h
-+++ b/lib/vc-mtime.h
-@@ -90,6 +90,13 @@ extern "C" {
- Upon failure, it returns -1. */
- extern int vc_mtime (struct timespec *mtime, const char *filename);
-
-+/* Determines the maximum of the version-controlled modification times of
-+ FILENAMES[0..NFILES-1], and returns 0.
-+ Upon failure, it returns -1.
-+ NFILES must be > 0. */
-+extern int max_vc_mtime (struct timespec *max_of_mtimes,
-+ size_t nfiles, const char * const *filenames);
-+
- #ifdef __cplusplus
- }
- #endif
---- a/modules/vc-mtime
-+++ b/modules/vc-mtime
-@@ -16,6 +16,17 @@ error
- getline
- xstrtol
- stat-time
-+filename
-+xalloc
-+xgetcwd
-+canonicalize-lgpl
-+xvasprintf
-+str_startswith
-+map
-+xmap
-+hash-map
-+hashkey-string
-+getdelim
- gettext-h
- gnulib-i18n
-
+++ /dev/null
-From f4c40c2d6aabef8e587176bbf5226c8bc6649574 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Fri, 2 May 2025 02:43:23 +0200
-Subject: [PATCH] vc-mtime: Add API for more efficient use of git, part 2.
-
-* lib/vc-mtime.c (max_vc_mtime): Don't skip the odd-numbered arguments.
----
- ChangeLog | 5 +++++
- lib/vc-mtime.c | 57 +++++++++++++++++++++-----------------------------
- 2 files changed, 29 insertions(+), 33 deletions(-)
-
---- a/lib/vc-mtime.c
-+++ b/lib/vc-mtime.c
-@@ -558,17 +558,14 @@ max_vc_mtime (struct timespec *max_of_mt
- size_t n = n0;
- size_t cmd_len = 25;
- for (; n < nfiles; n++)
-- {
-- if (vc_controlled[n] == 1)
-- {
-- if (cmd_len + strlen (currdir_relative_filenames[n]) >= MAX_CMD_LEN
-- && i > i0)
-- break;
-- argv[i++] = currdir_relative_filenames[n];
-- cmd_len += 1 + strlen (currdir_relative_filenames[n]);
-- }
-- n++;
-- }
-+ if (vc_controlled[n] == 1)
-+ {
-+ if (cmd_len + strlen (currdir_relative_filenames[n]) >= MAX_CMD_LEN
-+ && i > i0)
-+ break;
-+ argv[i++] = currdir_relative_filenames[n];
-+ cmd_len += 1 + strlen (currdir_relative_filenames[n]);
-+ }
- if (i > i0)
- {
- pid_t child;
-@@ -672,17 +669,14 @@ max_vc_mtime (struct timespec *max_of_mt
- size_t n = n0;
- size_t cmd_len = 46;
- for (; n < nfiles; n++)
-- {
-- if (vc_controlled[n] == 1)
-- {
-- if (cmd_len + strlen (currdir_relative_filenames[n]) >= MAX_CMD_LEN
-- && i > i0)
-- break;
-- argv[i++] = currdir_relative_filenames[n];
-- cmd_len += 1 + strlen (currdir_relative_filenames[n]);
-- }
-- n++;
-- }
-+ if (vc_controlled[n] == 1)
-+ {
-+ if (cmd_len + strlen (currdir_relative_filenames[n]) >= MAX_CMD_LEN
-+ && i > i0)
-+ break;
-+ argv[i++] = currdir_relative_filenames[n];
-+ cmd_len += 1 + strlen (currdir_relative_filenames[n]);
-+ }
- if (i > i0)
- {
- pid_t child;
-@@ -768,17 +762,14 @@ max_vc_mtime (struct timespec *max_of_mt
- size_t n = n0;
- size_t cmd_len = 27;
- for (; n < nfiles; n++)
-- {
-- if (vc_controlled[n] == 1)
-- {
-- if (cmd_len + strlen (currdir_relative_filenames[n]) >= MAX_CMD_LEN
-- && i > i0)
-- break;
-- argv[i++] = currdir_relative_filenames[n];
-- cmd_len += 1 + strlen (currdir_relative_filenames[n]);
-- }
-- n++;
-- }
-+ if (vc_controlled[n] == 1)
-+ {
-+ if (cmd_len + strlen (currdir_relative_filenames[n]) >= MAX_CMD_LEN
-+ && i > i0)
-+ break;
-+ argv[i++] = currdir_relative_filenames[n];
-+ cmd_len += 1 + strlen (currdir_relative_filenames[n]);
-+ }
- if (i > i0)
- {
- pid_t child;
+++ /dev/null
-From 47548a77525a0f4489c9c420ccc2159079365da8 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Fri, 2 May 2025 12:09:40 +0200
-Subject: [PATCH] vc-mtime: Make it work with git versions < 2.28.
-
-* lib/vc-mtime.c (git_version): New variable.
-(is_git_present): Read the output of "git --version", and set
-git_version.
-(max_vc_mtime): Don't pass option --no-relative if the git version
-is < 2.28.
----
- ChangeLog | 9 ++++++
- lib/vc-mtime.c | 82 +++++++++++++++++++++++++++++++++++++++++++++-----
- 2 files changed, 83 insertions(+), 8 deletions(-)
-
---- a/lib/vc-mtime.c
-+++ b/lib/vc-mtime.c
-@@ -53,7 +53,9 @@
-
- /* ========================================================================== */
-
--/* Determines whether git is present. */
-+static const char *git_version;
-+
-+/* Determines whether git is present, and sets git_version if so. */
- static bool
- is_git_present (void)
- {
-@@ -63,17 +65,67 @@ is_git_present (void)
- if (!git_tested)
- {
- /* Test for presence of git:
-- "git --version >/dev/null 2>/dev/null" */
-+ "git --version 2>/dev/null" */
- const char *argv[3];
-- int exitstatus;
-+ pid_t child;
-+ int fd[1];
-
- argv[0] = "git";
- argv[1] = "--version";
- argv[2] = NULL;
-- exitstatus = execute ("git", "git", argv, NULL, NULL,
-- false, false, true, true,
-- true, false, NULL);
-- git_present = (exitstatus == 0);
-+ child = create_pipe_in ("git", "git", argv, NULL, NULL,
-+ DEV_NULL, true, true, false, fd);
-+ if (child == -1)
-+ git_present = false;
-+ else
-+ {
-+ /* Retrieve its result. */
-+ FILE *fp = fdopen (fd[0], "r");
-+ if (fp == NULL)
-+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
-+
-+ char *line = NULL;
-+ size_t linesize = 0;
-+ size_t linelen = getline (&line, &linesize, fp);
-+ if (linelen == (size_t)(-1))
-+ {
-+ fclose (fp);
-+ wait_subprocess (child, "git", true, true, true, false, NULL);
-+ git_present = false;
-+ }
-+ else
-+ {
-+ if (linelen > 0 && line[linelen - 1] == '\n')
-+ line[linelen - 1] = '\0';
-+
-+ /* Read until EOF (otherwise the child process may get a SIGPIPE
-+ signal). */
-+ while (getc (fp) != EOF)
-+ ;
-+
-+ fclose (fp);
-+
-+ /* Remove zombie process from process list, and retrieve exit
-+ status. */
-+ int exitstatus =
-+ wait_subprocess (child, "git", true, true, true, false, NULL);
-+ if (exitstatus != 0)
-+ {
-+ free (line);
-+ git_present = false;
-+ }
-+ else
-+ {
-+ /* The version starts at the first digit in the line. */
-+ const char *p = line;
-+ for (; *p != '0'; p++)
-+ if (*p >= '0' && *p <= '9')
-+ break;
-+ git_version = p;
-+ git_present = true;
-+ }
-+ }
-+ }
- git_tested = true;
- }
-
-@@ -660,7 +712,21 @@ max_vc_mtime (struct timespec *max_of_mt
- argv[i++] = "git";
- argv[i++] = "diff";
- argv[i++] = "--name-only";
-- argv[i++] = "--no-relative";
-+ /* With git versions >= 2.28, we pass option --no-relative,
-+ in order to neutralize any possible customization of the
-+ "diff.relative" property. With git versions < 2.28, this
-+ is not needed, and the option --no-relative does not
-+ exist. */
-+ if (!(git_version[0] <= '1'
-+ || (git_version[0] == '2' && git_version[1] == '.'
-+ && ((git_version[2] >= '0' && git_version[2] <= '9'
-+ && !(git_version[3] >= '0' && git_version[3] <= '9'))
-+ || (((git_version[2] == '1'
-+ && git_version[3] >= '0' && git_version[3] <= '9')
-+ || (git_version[2] == '2'
-+ && git_version[3] >= '0' && git_version[3] <= '7'))
-+ && !(git_version[4] >= '0' && git_version[4] <= '9'))))))
-+ argv[i++] = "--no-relative";
- argv[i++] = "-z";
- argv[i++] = "HEAD";
- argv[i++] = "--";
+++ /dev/null
-From 24010120fab36721caaf92be076655571e44da07 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Fri, 3 Jan 2025 09:26:14 +0100
-Subject: [PATCH] str_startswith: New module.
-
-* lib/string.in.h (str_startswith): New declaration.
-* lib/str_startswith.c: New file.
-* m4/string_h.m4 (gl_STRING_H_REQUIRE_DEFAULTS): Initialize
-GNULIB_STR_STARTSWITH.
-* modules/string-h (Makefile.am): Substitute GNULIB_STR_STARTSWITH.
-* modules/str_startswith: New file.
----
- ChangeLog | 10 ++++++++++
- lib/str_startswith.c | 29 +++++++++++++++++++++++++++++
- lib/string.in.h | 8 ++++++++
- m4/string_h.m4 | 3 ++-
- modules/str_startswith | 23 +++++++++++++++++++++++
- modules/string-h | 1 +
- 6 files changed, 73 insertions(+), 1 deletion(-)
- create mode 100644 lib/str_startswith.c
- create mode 100644 modules/str_startswith
-
---- /dev/null
-+++ b/lib/str_startswith.c
-@@ -0,0 +1,29 @@
-+/* str_startswith function.
-+ Copyright (C) 2025 Free Software Foundation, Inc.
-+
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as
-+ published by the Free Software Foundation, either version 3 of the
-+ License, or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU Lesser General Public License for more details.
-+
-+ You should have received a copy of the GNU Lesser General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+/* Written by Bruno Haible <bruno@clisp.org>, 2025. */
-+
-+#include "config.h"
-+
-+/* Specification. */
-+#include <string.h>
-+
-+
-+int
-+str_startswith (const char *string, const char *prefix)
-+{
-+ return strncmp (string, prefix, strlen (prefix)) == 0;
-+}
---- a/lib/string.in.h
-+++ b/lib/string.in.h
-@@ -1079,6 +1079,14 @@ _GL_WARN_ON_USE (strtok_r, "strtok_r is
- /* The following functions are not specified by POSIX. They are gnulib
- extensions. */
-
-+#if @GNULIB_STR_STARTSWITH@
-+/* Returns true if STRING starts with PREFIX.
-+ Returns false otherwise. */
-+_GL_EXTERN_C int str_startswith (const char *string, const char *prefix)
-+ _GL_ATTRIBUTE_PURE
-+ _GL_ARG_NONNULL ((1, 2));
-+#endif
-+
- #if @GNULIB_MBSLEN@
- /* Return the number of multibyte characters in the character string STRING.
- This considers multibyte characters, unlike strlen, which counts bytes. */
---- a/m4/string_h.m4
-+++ b/m4/string_h.m4
-@@ -70,6 +70,7 @@ AC_DEFUN([gl_STRING_H_REQUIRE_DEFAULTS],
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRSTR])
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRCASESTR])
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRTOK_R])
-+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STR_STARTSWITH])
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MBSLEN])
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MBSNLEN])
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MBSCHR])
---- /dev/null
-+++ b/modules/str_startswith
-@@ -0,0 +1,23 @@
-+Description:
-+str_startswith() function: test whether a string starts with a given prefix.
-+
-+Files:
-+lib/str_startswith.c
-+
-+Depends-on:
-+string-h
-+
-+configure.ac:
-+gl_STRING_MODULE_INDICATOR([str_startswith])
-+
-+Makefile.am:
-+lib_SOURCES += str_startswith.c
-+
-+Include:
-+<string.h>
-+
-+License:
-+LGPLv2+
-+
-+Maintainer:
-+all
---- a/modules/string-h
-+++ b/modules/string-h
-@@ -69,6 +69,7 @@ string.h: string.in.h $(top_builddir)/co
- -e 's/@''GNULIB_STRSTR''@/$(GNULIB_STRSTR)/g' \
- -e 's/@''GNULIB_STRCASESTR''@/$(GNULIB_STRCASESTR)/g' \
- -e 's/@''GNULIB_STRTOK_R''@/$(GNULIB_STRTOK_R)/g' \
-+ -e 's/@''GNULIB_STR_STARTSWITH''@/$(GNULIB_STR_STARTSWITH)/g' \
- -e 's/@''GNULIB_STRERROR''@/$(GNULIB_STRERROR)/g' \
- -e 's/@''GNULIB_STRERROR_R''@/$(GNULIB_STRERROR_R)/g' \
- -e 's/@''GNULIB_STRERRORNAME_NP''@/$(GNULIB_STRERRORNAME_NP)/g' \
+++ /dev/null
-From d89ac9373d9748f7601babf52c9129fcbcf0c907 Mon Sep 17 00:00:00 2001
-From: Bruno Haible <bruno@clisp.org>
-Date: Fri, 3 Jan 2025 09:54:14 +0100
-Subject: [PATCH] str_endswith: New module.
-
-* lib/string.in.h (str_endswith): New declaration.
-* lib/str_endswith.c: New file.
-* m4/string_h.m4 (gl_STRING_H_REQUIRE_DEFAULTS): Initialize
-GNULIB_STR_ENDSWITH.
-* modules/string-h (Makefile.am): Substitute GNULIB_STR_ENDSWITH.
-* modules/str_endswith: New file.
----
- ChangeLog | 10 ++++++++++
- lib/str_endswith.c | 31 +++++++++++++++++++++++++++++++
- lib/string.in.h | 8 ++++++++
- m4/string_h.m4 | 3 ++-
- modules/str_endswith | 23 +++++++++++++++++++++++
- modules/string-h | 1 +
- 6 files changed, 75 insertions(+), 1 deletion(-)
- create mode 100644 lib/str_endswith.c
- create mode 100644 modules/str_endswith
-
---- /dev/null
-+++ b/lib/str_endswith.c
-@@ -0,0 +1,31 @@
-+/* str_endswith function.
-+ Copyright (C) 2025 Free Software Foundation, Inc.
-+
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as
-+ published by the Free Software Foundation, either version 3 of the
-+ License, or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU Lesser General Public License for more details.
-+
-+ You should have received a copy of the GNU Lesser General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+/* Written by Bruno Haible <bruno@clisp.org>, 2025. */
-+
-+#include "config.h"
-+
-+/* Specification. */
-+#include <string.h>
-+
-+
-+int
-+str_endswith (const char *string, const char *suffix)
-+{
-+ size_t len = strlen (string);
-+ size_t n = strlen (suffix);
-+ return len >= n && strcmp (string + len - n, suffix) == 0;
-+}
---- a/lib/string.in.h
-+++ b/lib/string.in.h
-@@ -1087,6 +1087,14 @@ _GL_EXTERN_C int str_startswith (const c
- _GL_ARG_NONNULL ((1, 2));
- #endif
-
-+#if @GNULIB_STR_ENDSWITH@
-+/* Returns true if STRING ends with SUFFIX.
-+ Returns false otherwise. */
-+_GL_EXTERN_C int str_endswith (const char *string, const char *prefix)
-+ _GL_ATTRIBUTE_PURE
-+ _GL_ARG_NONNULL ((1, 2));
-+#endif
-+
- #if @GNULIB_MBSLEN@
- /* Return the number of multibyte characters in the character string STRING.
- This considers multibyte characters, unlike strlen, which counts bytes. */
---- a/m4/string_h.m4
-+++ b/m4/string_h.m4
-@@ -71,6 +71,7 @@ AC_DEFUN([gl_STRING_H_REQUIRE_DEFAULTS],
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRCASESTR])
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRTOK_R])
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STR_STARTSWITH])
-+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STR_ENDSWITH])
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MBSLEN])
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MBSNLEN])
- gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MBSCHR])
---- /dev/null
-+++ b/modules/str_endswith
-@@ -0,0 +1,23 @@
-+Description:
-+str_endswith() function: test whether a string ends with a given suffix.
-+
-+Files:
-+lib/str_endswith.c
-+
-+Depends-on:
-+string-h
-+
-+configure.ac:
-+gl_STRING_MODULE_INDICATOR([str_endswith])
-+
-+Makefile.am:
-+lib_SOURCES += str_endswith.c
-+
-+Include:
-+<string.h>
-+
-+License:
-+LGPLv2+
-+
-+Maintainer:
-+all
---- a/modules/string-h
-+++ b/modules/string-h
-@@ -69,6 +69,7 @@ string.h: string.in.h $(top_builddir)/co
- -e 's/@''GNULIB_STRSTR''@/$(GNULIB_STRSTR)/g' \
- -e 's/@''GNULIB_STRCASESTR''@/$(GNULIB_STRCASESTR)/g' \
- -e 's/@''GNULIB_STRTOK_R''@/$(GNULIB_STRTOK_R)/g' \
-+ -e 's/@''GNULIB_STR_ENDSWITH''@/$(GNULIB_STR_ENDSWITH)/g' \
- -e 's/@''GNULIB_STR_STARTSWITH''@/$(GNULIB_STR_STARTSWITH)/g' \
- -e 's/@''GNULIB_STRERROR''@/$(GNULIB_STRERROR)/g' \
- -e 's/@''GNULIB_STRERROR_R''@/$(GNULIB_STRERROR_R)/g' \