summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--ChangeLog678
-rwxr-xr-xMODULES.html.sh11
-rw-r--r--NEWS17
-rwxr-xr-xbuild-aux/config.guess12
-rwxr-xr-xbuild-aux/config.sub579
-rw-r--r--build-aux/gcc-warning.spec144
-rwxr-xr-xbuild-aux/git-version-gen2
-rw-r--r--config/srclist.txt3
-rwxr-xr-xconfig/srclistvars.sh1
-rw-r--r--doc/glibc-functions/getumask.texi30
-rw-r--r--doc/gnulib.texi2
-rw-r--r--doc/posix-functions/basename.texi4
-rw-r--r--doc/posix-functions/chdir.texi2
-rw-r--r--doc/posix-functions/close.texi4
-rw-r--r--doc/posix-functions/dup.texi4
-rw-r--r--doc/posix-functions/dup2.texi15
-rw-r--r--doc/posix-functions/getcwd.texi2
-rw-r--r--doc/posix-functions/gethostname.texi4
-rw-r--r--doc/posix-functions/isatty.texi4
-rw-r--r--doc/posix-functions/lseek.texi4
-rw-r--r--doc/posix-functions/memchr.texi10
-rw-r--r--doc/posix-functions/memcmp.texi2
-rw-r--r--doc/posix-functions/memcpy.texi2
-rw-r--r--doc/posix-functions/memmove.texi2
-rw-r--r--doc/posix-functions/memset.texi2
-rw-r--r--doc/posix-functions/read.texi12
-rw-r--r--doc/posix-functions/sigprocmask.texi7
-rw-r--r--doc/posix-functions/unlink.texi4
-rw-r--r--doc/posix-functions/write.texi10
-rw-r--r--doc/standards.texi313
-rw-r--r--lib/alloca.in.h15
-rw-r--r--lib/areadlink-with-size.c5
-rw-r--r--lib/areadlinkat-with-size.c2
-rw-r--r--lib/argp-namefrob.h2
-rw-r--r--lib/asyncsafe-spin.c340
-rw-r--r--lib/asyncsafe-spin.h69
-rw-r--r--lib/backupfile.c2
-rw-r--r--lib/basename-lgpl.c20
-rw-r--r--lib/basename-lgpl.h78
-rw-r--r--lib/c-dtoastr.c3
-rw-r--r--lib/c-ldtoastr.c3
-rw-r--r--lib/canonicalize.h3
-rw-r--r--lib/clean-temp.c629
-rw-r--r--lib/clean-temp.h82
-rw-r--r--lib/dev-ino.h18
-rw-r--r--lib/di-set.h18
-rw-r--r--lib/dirname.h7
-rw-r--r--lib/dup2.c100
-rw-r--r--lib/fatal-signal.c84
-rw-r--r--lib/fatal-signal.h11
-rw-r--r--lib/fchdir.c7
-rw-r--r--lib/fchown-stub.c18
-rw-r--r--lib/file-set.h18
-rw-r--r--lib/filenamecat-lgpl.c25
-rw-r--r--lib/fopen.c29
-rw-r--r--lib/ftoastr.c23
-rw-r--r--lib/ftoastr.h6
-rw-r--r--lib/getcwd.c8
-rw-r--r--lib/getloadavg.c21
-rw-r--r--lib/getprogname.c2
-rw-r--r--lib/getrandom.c11
-rw-r--r--lib/getumask.c140
-rw-r--r--lib/glthread/thread.h4
-rw-r--r--lib/hash-triple-simple.c59
-rw-r--r--lib/hash-triple.c35
-rw-r--r--lib/hash-triple.h28
-rw-r--r--lib/idcache.h18
-rw-r--r--lib/ino-map.h18
-rw-r--r--lib/javacomp.c2
-rw-r--r--lib/mini-gmp-gnulib.c37
-rw-r--r--lib/mini-gmp.c4615
-rw-r--r--lib/mini-gmp.h308
-rw-r--r--lib/mkancesdirs.c2
-rw-r--r--lib/mkancesdirs.h18
-rw-r--r--lib/pthread-spin.c103
-rw-r--r--lib/scratch_buffer.h18
-rw-r--r--lib/se-context.in.h18
-rw-r--r--lib/stdopen.h18
-rw-r--r--lib/string.in.h5
-rw-r--r--lib/supersede.c469
-rw-r--r--lib/supersede.h157
-rw-r--r--lib/sys_socket.in.h7
-rw-r--r--lib/sys_stat.in.h17
-rw-r--r--lib/tmpfile.c4
-rw-r--r--lib/tzset.c3
-rw-r--r--lib/unictype/joininggroup_name.c2
-rw-r--r--lib/unistd.in.h3
-rw-r--r--lib/userspec.h18
-rw-r--r--lib/windows-spin.c1
-rw-r--r--lib/windows-thread.c2
-rw-r--r--lib/windows-thread.h2
-rw-r--r--lib/xgetcwd.c2
-rw-r--r--m4/dirname.m419
-rw-r--r--m4/dup2.m4181
-rw-r--r--m4/getrandom.m45
-rw-r--r--m4/getumask.m424
-rw-r--r--m4/lchmod.m43
-rw-r--r--m4/libgmp.m444
-rw-r--r--m4/manywarnings.m4206
-rw-r--r--m4/memchr-obsolete.m411
-rw-r--r--m4/memchr.m466
-rw-r--r--m4/strcasestr.m44
-rw-r--r--m4/string_h.m43
-rw-r--r--m4/strstr.m44
-rw-r--r--m4/supersede.m4 (renamed from m4/dup2-obsolete.m4)9
-rw-r--r--m4/sys_stat_h.m48
-rw-r--r--m4/unistd_h.m43
-rw-r--r--modules/argp2
-rw-r--r--modules/argv-iter-tests1
-rw-r--r--modules/asyncsafe-spin28
-rw-r--r--modules/asyncsafe-spin-tests20
-rw-r--r--modules/at-internal3
-rw-r--r--modules/backup-rename2
-rw-r--r--modules/backupfile2
-rw-r--r--modules/basename-lgpl25
-rw-r--r--modules/bitset3
-rw-r--r--modules/c-dtoastr27
-rw-r--r--modules/c-dtoastr-tests18
-rw-r--r--modules/c-ldtoastr27
-rw-r--r--modules/c-ldtoastr-tests18
-rw-r--r--modules/canonicalize4
-rw-r--r--modules/canonicalize-lgpl19
-rw-r--r--modules/clean-temp6
-rw-r--r--modules/copy-file3
-rw-r--r--modules/dfa1
-rw-r--r--modules/dfa-tests16
-rw-r--r--modules/dirname1
-rw-r--r--modules/dirname-lgpl6
-rw-r--r--modules/dup27
-rw-r--r--modules/dup2-obsolete28
-rw-r--r--modules/fatal-signal1
-rw-r--r--modules/file-set2
-rw-r--r--modules/filenamecat-lgpl4
-rw-r--r--modules/fopen1
-rw-r--r--modules/gen-uni-tables1
-rw-r--r--modules/getloadavg10
-rw-r--r--modules/getprogname2
-rw-r--r--modules/getrandom5
-rw-r--r--modules/getumask32
-rw-r--r--modules/getumask-tests13
-rw-r--r--modules/hash-triple3
-rw-r--r--modules/hash-triple-simple24
-rw-r--r--modules/javacomp1
-rw-r--r--modules/libgmp42
-rw-r--r--modules/libgmp-tests12
-rw-r--r--modules/lock-tests1
-rw-r--r--modules/memchr3
-rw-r--r--modules/memchr-obsolete28
-rw-r--r--modules/mkancesdirs2
-rw-r--r--modules/mkdtemp3
-rw-r--r--modules/mkostemp3
-rw-r--r--modules/mkostemps3
-rw-r--r--modules/mkstemp3
-rw-r--r--modules/mkstemps3
-rw-r--r--modules/mtx-tests1
-rw-r--r--modules/pthread-mutex-tests1
-rw-r--r--modules/pthread-rwlock-tests1
-rw-r--r--modules/pthread-spin1
-rw-r--r--modules/pthread-spin-tests20
-rw-r--r--modules/stdlib-safer3
-rw-r--r--modules/string1
-rw-r--r--modules/strnlen1
-rw-r--r--modules/supersede41
-rw-r--r--modules/supersede-tests21
-rw-r--r--modules/sys_stat2
-rw-r--r--modules/tempname4
-rw-r--r--modules/tmpfile3
-rw-r--r--modules/tmpfile-safer3
-rw-r--r--modules/unicodeio3
-rw-r--r--modules/unicodeio-tests21
-rw-r--r--modules/unistd1
-rw-r--r--tests/atomic-int-gnulib.h173
-rw-r--r--tests/atomic-int-isoc.h173
-rw-r--r--tests/atomic-int-posix.h178
-rw-r--r--tests/test-asyncsafe-spin1.c61
-rw-r--r--tests/test-asyncsafe-spin2.c245
-rw-r--r--tests/test-c-dtoastr.c58
-rw-r--r--tests/test-c-dtoastr.sh15
-rw-r--r--tests/test-c-ldtoastr.c58
-rw-r--r--tests/test-c-ldtoastr.sh15
-rw-r--r--tests/test-calloc-gnu.c28
-rwxr-xr-xtests/test-dfa-invalid-char-class.sh (renamed from tests/dfa-invalid-char-class.sh)4
-rw-r--r--tests/test-dfa-match-aux.c (renamed from tests/dfa-match-aux.c)0
-rwxr-xr-xtests/test-dfa-match.sh (renamed from tests/dfa-match.sh)6
-rw-r--r--tests/test-getloadavg.c2
-rw-r--r--tests/test-getumask.c52
-rw-r--r--tests/test-libgmp.c24
-rw-r--r--tests/test-lock.c156
-rw-r--r--tests/test-memcasecmp.c5
-rw-r--r--tests/test-memchr.c7
-rw-r--r--tests/test-memchr2.c4
-rw-r--r--tests/test-memcmp.c5
-rw-r--r--tests/test-memmem.c22
-rw-r--r--tests/test-memrchr.c4
-rw-r--r--tests/test-mtx.c156
-rw-r--r--tests/test-pthread-mutex.c161
-rw-r--r--tests/test-pthread-rwlock.c161
-rw-r--r--tests/test-pthread-spin.c231
-rw-r--r--tests/test-supersede-fopen.h265
-rw-r--r--tests/test-supersede-open.h262
-rw-r--r--tests/test-supersede.c63
-rw-r--r--tests/test-sys_stat-c++.cc4
-rw-r--r--tests/test-unicodeio.c81
-rwxr-xr-xtests/test-unicodeio1.sh5
-rwxr-xr-xtests/test-unicodeio2.sh15
-rwxr-xr-xtests/test-unicodeio3.sh15
-rw-r--r--tests/unistr/test-chr.h4
-rw-r--r--tests/unistr/test-cmp.h5
-rw-r--r--top/maint.mk4
209 files changed, 11745 insertions, 1847 deletions
diff --git a/ChangeLog b/ChangeLog
index 7e3e708..c2182f0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,681 @@
+2020-07-08 Bruno Haible <bruno@clisp.org>
+
+ unicodeio: Add tests.
+ * tests/test-unicodeio.c: New file.
+ * tests/test-unicodeio1.sh: New file.
+ * tests/test-unicodeio2.sh: New file.
+ * tests/test-unicodeio3.sh: New file.
+ * modules/unicodeio-tests: New file.
+
+2020-07-08 Bruno Haible <bruno@clisp.org>
+
+ unicodeio: Document link requirements.
+ * modules/unicodeio (Link): New section.
+
+2020-07-07 Bruno Haible <bruno@clisp.org>
+
+ doc: Remove support for some very old platforms.
+ * doc/posix-functions/memcmp.texi: Don't mention "older platforms".
+ * doc/posix-functions/memcpy.texi: Likewise.
+ * doc/posix-functions/memmove.texi: Likewise.
+ * doc/posix-functions/memset.texi: Likewise.
+ * doc/posix-functions/getcwd.texi: Likewise.
+
+ memchr: Remove support for some very old platforms.
+ * m4/memchr-obsolete.m4: Remove file.
+ * modules/memchr-obsolete: Remove file.
+ * m4/memchr.m4 (gl_FUNC_MEMCHR): Assume module 'memchr-obsolete' is
+ absent. Don't define HAVE_MEMCHR.
+ * lib/string.in.h (memchr): Assume HAVE_MEMCHR is 1.
+ * modules/memchr (Depends-on): Remove memchr-obsolete.
+ (configure.ac): Assume HAVE_MEMCHR is 1.
+ * m4/string_h.m4 (gl_HEADER_STRING_H_DEFAULTS): Don't initialize
+ HAVE_MEMCHR.
+ * modules/string (Makefile.am): Don't substitute HAVE_MEMCHR.
+ * doc/posix-functions/memchr.texi: Don't mention module
+ 'memchr-obsolete'.
+ * m4/strcasestr.m4 (gl_FUNC_STRCASESTR_SIMPLE): Assume HAVE_MEMCHR is 1.
+ * m4/strstr.m4 (gl_FUNC_STRSTR_SIMPLE): Assume HAVE_MEMCHR is 1.
+ * modules/strnlen (Depends-on): Remove memchr-obsolete.
+
+ dup2: Remove support for some very old platforms.
+ * m4/dup2-obsolete.m4: Remove file.
+ * modules/dup2-obsolete: Remove file.
+ * m4/dup2.m4 (gl_FUNC_DUP2): Assume module 'dup2-obsolete' is absent.
+ Don't define HAVE_DUP2.
+ * lib/unistd.in.h (dup2): Assume HAVE_DUP2 is 1.
+ * lib/dup2.c: Likewise.
+ * modules/dup2 (Depends-on, configure.ac): Likewise.
+ (Depends-on): Remove dup2-obsolete.
+ * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Don't initialize HAVE_DUP2.
+ * modules/unistd (Makefile.am): Don't substitute HAVE_DUP2.
+ * doc/posix-functions/dup2.texi: Don't mention module 'dup2-obsolete'.
+
+2020-07-07 Bruno Haible <bruno@clisp.org>
+
+ canonicalize: Trim module dependencies.
+ * lib/hash-triple.h: Group declarations.
+ * lib/hash-triple-simple.c: New file, extracted from lib/hash-triple.c.
+ * lib/hash-triple.c: Don't include <stdlib.h>, <string.h>, hash-pjw.h.
+ (STREQ): Remove macro.
+ (triple_hash, triple_compare_ino_str, triple_free): Remove functions.
+ * modules/hash-triple-simple: New file, based on modules/hash-triple.
+ * modules/hash-triple (Files): Remove lib/hash-triple.h.
+ (Depends-on): Add hash-triple-simple. Remove hash-pjw.
+ * modules/canonicalize (Depends-on): Remove hash-triple. Add
+ hash-triple-simple.
+ * modules/file-set (Depends-on): Likewise.
+
+2020-07-07 Bruno Haible <bruno@clisp.org>
+
+ Clarify dependencies to double-slash-root.
+ * modules/canonicalize (Files): Remove m4/double-slash-root.m4.
+ (Depends-on): Add double-slash-root.
+ * modules/canonicalize-lgpl (Depends-on): Add double-slash-root.
+ * modules/dirname-lgpl (Depends-on): Add double-slash-root.
+
+2020-07-06 Paul Eggert <eggert@cs.ucla.edu>
+
+ libgmp: new module
+ The idea is to let programs simply include <gmp.h>, and
+ so long as they live within the mini-gmp subset they need
+ not worry about whether the GMP libraries are installed.
+ * MODULES.html.sh: Mention it.
+ * config/srclist.txt: Mention files copied from GMP source.
+ * config/srclistvars.sh (GMP): New var.
+ * lib/mini-gmp-gnulib.c, m4/libgmp.m4, modules/libgmp:
+ * modules/libgmp-tests, tests/test-libgmp.c: New files.
+ * lib/mini-gmp.c, lib/mini-gmp.h: New files, copied from GMP.
+
+2020-07-05 Bruno Haible <bruno@clisp.org>
+
+ mkancesdirs: Trim module dependencies.
+ * lib/mkancesdirs.c: Include filename.h instead of dirname.h.
+ * modules/mkancesdirs (Depends-on): Remove dirname-lgpl. Add filename.
+
+2020-07-05 Bruno Haible <bruno@clisp.org>
+
+ getprogname: Trim module dependencies.
+ * lib/getprogname.c: Include basename-lgpl.h instead of dirname.h.
+ * modules/getprogname (Depends-on): Remove dirname-lgpl. Add
+ basename-lgpl.
+
+2020-07-05 Bruno Haible <bruno@clisp.org>
+
+ filenamecat-lgpl: Trim module dependencies.
+ * lib/filenamecat-lgpl.c: Include basename-lgpl.h, filename.h instead of
+ dirname.h.
+ * modules/filenamecat-lgpl (Depends-on): Remove dirname-lgpl. Add
+ basename-lgpl, filename.
+
+2020-07-05 Bruno Haible <bruno@clisp.org>
+
+ backupfile, backup-rename: Trim module dependencies.
+ * lib/backupfile.c: Include basename-lgpl.h instead of dirname.h.
+ * modules/backupfile (Depends-on): Remove dirname-lgpl. Add
+ basename-lgpl.
+ * modules/backup-rename (Depends-on): Likewise.
+
+2020-07-05 Bruno Haible <bruno@clisp.org>
+
+ argp: Trim module dependencies.
+ * lib/argp-namefrob.h: Include basename-lgpl.h instead of dirname.h.
+ * modules/argp (Depends-on): Remove dirname-lgpl. Add basename-lgpl.
+
+2020-07-05 Bruno Haible <bruno@clisp.org>
+
+ basename-lgpl: New module.
+ * lib/basename-lgpl.h: New file, based on lib/dirname.h and
+ lib/basename-lgpl.c.
+ * lib/basename-lgpl.c: Include basename-lgpl.h, not dirname.h. Include
+ <stdbool.h>, filename.h.
+ (last_component): Rename a local variable.
+ * lib/dirname.h: Include basename-lgpl.h.
+ (DOUBLE_SLASH_IS_DISTINCT_ROOT): Remove macro.
+ (last_component, base_len): Remove declarations.
+ * modules/basename-lgpl: New file.
+ * modules/dirname-lgpl (Files): Remove lib/basename-lgpl.c.
+ (Depends-on): Add basename-lgpl. Remove double-slash-root.
+ (Makefile.am): Don't compile basename-lgpl.c.
+ * doc/posix-functions/basename.texi: Mention the module 'basename-lgpl',
+ not 'dirname'.
+
+2020-07-05 Bruno Haible <bruno@clisp.org>
+
+ dirname, dirname-lgpl: Simplify.
+ * m4/dirname.m4: Remove file.
+ * modules/dirname (configure.ac): Don't invoke gl_DIRNAME.
+ * modules/dirname-lgpl (Files): Remove m4/dirname.m4.
+ (configure.ac): Don't invoke gl_DIRNAME_LGPL.
+
+2020-07-05 Bernhard Voelker <mail@bernhard-voelker.de>
+
+ tests: avoid shadowing warning
+ * tests/test-memchr.c (main): Give page_boundary variable a tight scope.
+
+2020-07-05 Bruno Haible <bruno@clisp.org>
+
+ supersede: Add tests.
+ * tests/test-supersede.c: New file.
+ * tests/test-supersede-open.h: New file.
+ * tests/test-supersede-fopen.h: New file.
+ * modules/supersede-tests: New file.
+
+ supersede: New module.
+ * lib/supersede.h: New file.
+ * lib/supersede.c: New file.
+ * m4/supersede.m4: New file.
+ * modules/supersede: New file.
+
+2020-07-05 Bruno Haible <bruno@clisp.org>
+
+ Add some copyright headers.
+ * lib/dev-ino.h: Add copyright header.
+ * lib/di-set.h: Likewise.
+ * lib/fchown-stub.c: Likewise.
+ * lib/file-set.h: Likewise.
+ * lib/hash-triple.h: Likewise.
+ * lib/idcache.h: Likewise.
+ * lib/ino-map.h: Likewise.
+ * lib/mkancesdirs.h: Likewise.
+ * lib/scratch_buffer.h: Likewise.
+ * lib/se-context.in.h: Likewise.
+ * lib/stdopen.h: Likewise.
+ * lib/userspec.h: Likewise.
+
+2020-07-04 Bruno Haible <bruno@clisp.org>
+
+ getrandom: Relicense under LGPLv2+.
+ Paul Eggert's approval is in
+ <https://lists.gnu.org/archive/html/bug-gnulib/2020-07/msg00023.html>.
+ * modules/getrandom (License): Change to LGPLv2+.
+
+2020-07-04 Bruno Haible <bruno@clisp.org>
+
+ getumask: Add tests.
+ * tests/test-getumask.c: New file.
+ * modules/getumask-tests: New file.
+
+ getumask: New module.
+ * lib/sys_stat.in.h (getumask): New declaration.
+ * lib/getumask.c: New file.
+ * m4/getumask.m4: New file.
+ * m4/sys_stat_h.m4 (gl_HEADER_SYS_STAT_H): Test whether getumask is
+ declared.
+ (gl_SYS_STAT_H_DEFAULTS): Initialize GNULIB_GETUMASK, HAVE_GETUMASK.
+ * modules/sys_stat (Makefile.am): Substitute GNULIB_GETUMASK,
+ HAVE_GETUMASK.
+ * modules/getumask: New file.
+ * tests/test-sys_stat-c++.cc (getumask): Check signature.
+ * doc/glibc-functions/getumask.texi: New file.
+ * doc/gnulib.texi (Glibc sys/stat.h): Include it.
+
+2020-07-04 Bruno Haible <bruno@clisp.org>
+
+ clean-temp: Add support for temporary files with given mode.
+ * lib/clean-temp.h (gen_register_open_temp): Add mode argument.
+ * lib/clean-temp.c (struct try_create_file_params): New type.
+ (try_create_file): New function.
+ (gen_register_open_temp): Add mode argument. Use try_tempname instead of
+ gen_tempname.
+
+2020-07-04 Bruno Haible <bruno@clisp.org>
+
+ clean-temp: Document limitations.
+ * lib/clean-temp.h: Document limitations.
+
+2020-07-04 Bruno Haible <bruno@clisp.org>
+
+ clean-temp: Add support for temporary files with unpredictable names.
+ * lib/clean-temp.h (gen_register_open_temp): New declaration.
+ * lib/clean-temp.c: Include tempname.h.
+ (gen_register_open_temp): New function.
+ * modules/tempname (configure.ac): Define a module indicator.
+
+2020-07-04 Bruno Haible <bruno@clisp.org>
+
+ clean-temp: Add support for temporary files anywhere in the file system.
+ * lib/clean-temp.h (register_temporary_file, unregister_temporary_file,
+ cleanup_temporary_file): New declarations.
+ * lib/clean-temp.c (file_cleanup_list_lock, file_cleanup_list): New
+ variables.
+ (dir_cleanup_list_lock): Renamed from cleanup_list_lock.
+ (dir_cleanup_list): Renamed from cleanup_list.
+ (cleanup_action): Process the file_cleanup_list as well.
+ (do_init_clean_temp): New function.
+ (clean_temp_once): New variable.
+ (init_clean_temp): New function.
+ (create_temp_dir): Invoke it.
+ (register_temporary_file, unregister_temporary_file,
+ cleanup_temporary_file): New functions.
+ (do_unlink, do_rmdir): Remove 'dir' argument. Add 'cleanup_verbose'
+ argument.
+
+2020-07-04 Bruno Haible <bruno@clisp.org>
+
+ clean-temp: Improve comments.
+ * lib/clean-temp.h (open_temp, fopen_temp, close_temp, fclose_temp,
+ fwriteerror_temp, close_stream_temp): Clarify intended use.
+ * lib/clean-temp.c: Likewise.
+
+2020-07-04 Bruno Haible <bruno@clisp.org>
+
+ clean-temp: Make multithread-safe, part 2.
+ * lib/fatal-signal.h: Include <signal.h>.
+ (get_fatal_signal_set): New declaration.
+ * lib/fatal-signal.c (get_fatal_signal_set): New function.
+ * lib/clean-temp.c: Include asyncsafe-spin.h, gl_linked_list.h.
+ (struct closeable_fd): New type.
+ (fatal_signal_set): New variable.
+ (init_fatal_signal_set): New function.
+ (asyncsafe_close, asyncsafe_fclose_variant): New functions.
+ (cleanup_action): Invoke asyncsafe_close instead of close.
+ (create_temp_dir): Invoke init_fatal_signal_set.
+ (register_fd): Use a plain linked list. Add a 'struct closeable_fd *'
+ element.
+ (unregister_fd): Remove function.
+ (close_temp): Cleanup descriptors list on the fly. Invoke
+ init_fatal_signal_set. Invoke asyncsafe_close instead of close.
+ (fclose_variant_temp): New function.
+ (fclose_temp, fwriteerror_temp, close_stream_temp): Use it.
+ * modules/clean-temp (Depends-on): Add asyncsafe-spin, linked-list.
+
+2020-07-04 Bruno Haible <bruno@clisp.org>
+
+ clean-temp: Make multithread-safe, part 1.
+ * lib/clean-temp.c: Include glthread/lock.h.
+ (cleanup_list_lock): New variable.
+ (register_temp_file, unregister_temp_file, register_temp_subdir,
+ unregister_temp_subdir, cleanup_temp_dir_contents): Use it.
+ (create_temp_dir): Likewise. Don't free the old array.
+ (descriptors_lock): New variable.
+ (register_fd, unregister_fd): Use it.
+ * modules/clean-temp (Depends-on): Add lock.
+
+2020-07-04 Bruno Haible <bruno@clisp.org>
+
+ fatal-signal: Make multithread-safe.
+ * lib/fatal-signal.c (init_fatal_signals): Add comment.
+ (do_init_fatal_signal_set): New function, extracted from
+ init_fatal_signal_set.
+ (fatal_signal_set_once): New variable.
+ (init_fatal_signal_set): Use gl_once.
+
+2020-07-03 Bruno Haible <bruno@clisp.org>
+
+ getrandom: Fix compilation error on native Windows (regr. 2020-06-28).
+ * lib/getrandom.c: Don't include <ntdef.h>. Instead, define NTSTATUS.
+ * m4/getrandom.m4 (gl_FUNC_GETRANDOM): Include <windows.h> before
+ <bcrypt.h>.
+
+2020-07-03 Bruno Haible <bruno@clisp.org>
+
+ dfa tests: Follow common file naming conventions.
+ * tests/test-dfa-match-aux.c: Renamed from tests/dfa-match-aux.c.
+ * tests/test-dfa-match.sh: Renamed from tests/dfa-match.sh. Update.
+ * tests/test-dfa-invalid-char-class.sh: Renamed from
+ tests/dfa-invalid-char-class.sh. Update.
+ * modules/dfa-tests (Files, Makefile.am): Update.
+
+2020-07-03 Bruno Haible <bruno@clisp.org>
+
+ asyncsafe-spin: Use GCC extended asm syntax for SunStudio 12 compiler.
+ * lib/asyncsafe-spin.c (memory_barrier, atomic_compare_and_swap): Use
+ the GCC extended asm syntax also for the Sun Studio 12 compilers.
+
+2020-07-03 Bruno Haible <bruno@clisp.org>
+
+ asyncsafe-spin: Reduce code duplication.
+ * lib/asyncsafe-spin.c (do_lock, do_unlock): New functions.
+ (asyncsafe_spin_lock, asyncsafe_spin_unlock): Use them.
+ * modules/asyncsafe-spin (configure.ac): Require AC_C_INLINE.
+
+2020-07-03 Bruno Haible <bruno@clisp.org>
+
+ lchmod: Simplify after 2020-02-22 change.
+ * m4/lchmod.m4 (gl_FUNC_LCHMOD): Don't require AC_C_INLINE.
+
+2020-07-03 Bruno Haible <bruno@clisp.org>
+
+ gen-uni-tables: Make sure the compiler does not barf on 'inline'.
+ * modules/gen-uni-tables (configure.ac): Require AC_C_INLINE.
+
+2020-07-03 Bruno Haible <bruno@clisp.org>
+
+ dfa: Make sure the compiler does not barf on 'inline'.
+ * modules/dfa (configure.ac): Require AC_C_INLINE.
+
+2020-07-03 Bruno Haible <bruno@clisp.org>
+
+ bitset: Make sure the compiler does not barf on 'inline'.
+ * modules/bitset (configure.ac): New section.
+
+2020-07-01 Paul Eggert <eggert@cs.ucla.edu>
+
+ manywarnings: improve port to GCC 10.1
+ * build-aux/gcc-warning.spec: Also list warnings that are default
+ or are enabled by already-given flags. This lets us speed up
+ checking for attributes, and makes the generated compilation
+ commands shorter. Add -Wanalyzer-too-complex (too much noise).
+ * m4/manywarnings.m4 (gl_MANYWARN_COMPLEMENT)
+ (gl_MANYWARN_ALL_GCC): Use gl_AS_VAR_APPEND to append
+ to shell variables that may have long values.
+ (gl_MANYWARN_ALL_GCC): Omit flags that are default or are
+ consequences of other flags, to speed up checking and
+ shorten commands.
+
+ tests: pacify gcc -fanalyzer on zerosize_ptr
+ * tests/test-memcasecmp.c (main):
+ * tests/test-memchr.c (main):
+ * tests/test-memchr2.c (main):
+ * tests/test-memcmp.c (main):
+ * tests/test-memmem.c (main):
+ * tests/test-memrchr.c (main):
+ * tests/unistr/test-chr.h (main):
+ * tests/unistr/test-cmp.h (test_cmp):
+ Check whether zerosize_ptr returns NULL before using it.
+ This pacifies GCC 10.1’s new fanalyzer option, and matches
+ other uses of zerosize_ptr.
+
+2020-07-01 Bruno Haible <bruno@clisp.org>
+
+ asyncsafe-spin: Add tests.
+ * tests/test-asyncsafe-spin1.c: New file.
+ * tests/test-asyncsafe-spin2.c: New file, based on tests/test-lock.c and
+ tests/test-pthread-spin.c.
+ * modules/asyncsafe-spin-tests: New file.
+
+2020-07-01 Bruno Haible <bruno@clisp.org>
+
+ asyncsafe-spin: New module.
+ * lib/asyncsafe-spin.h: New file.
+ * lib/asyncsafe-spin.c: New file, based on lib/pthread-spin.c.
+ * modules/asyncsafe-spin: New file.
+
+2020-07-01 Bruno Haible <bruno@clisp.org>
+
+ windows-spin: Fix race condition on multiprocessor systems.
+ * lib/windows-spin.c (glwthread_spin_init): Add a memory barrier.
+
+2020-07-01 Bruno Haible <bruno@clisp.org>
+
+ pthread-spin: Add optimized fallback for GCC versions >= 4.1, < 4.7.
+ * lib/pthread-spin.c (pthread_spin_init, pthread_spin_lock,
+ pthread_spin_trylock, pthread_spin_unlock): For GCC >= 4.1, < 4.7, use
+ an implementation based on other GCC built-ins.
+
+2020-07-01 Bruno Haible <bruno@clisp.org>
+
+ pthread-spin: Optimize fallback for GCC versions >= 4.7.
+ * lib/pthread-spin.c (pthread_spin_init, pthread_spin_lock,
+ pthread_spin_trylock, pthread_spin_unlock): Use a lock word instead of a
+ lock byte.
+
+2020-07-01 Bruno Haible <bruno@clisp.org>
+
+ pthread-spin: Add error checking.
+ * lib/pthread-spin.c: Include <stdbool.h>.
+ (pthread_spin_init, pthread_spin_lock, pthread_spin_trylock,
+ pthread_spin_unlock) [GCC>=4.7]: Prefer an implementation that verifies
+ the unlocks.
+ * modules/pthread-spin (Depends-on): Add stdbool.
+
+2020-07-01 Bruno Haible <bruno@clisp.org>
+
+ pthread-spin: Add tests.
+ * tests/test-pthread-spin.c: New file, based on tests/test-lock.c.
+ * modules/pthread-spin-tests: New file.
+
+2020-07-01 Bruno Haible <bruno@clisp.org>
+
+ tests: Reduce code duplication.
+ * tests/atomic-int-posix.h: New file, extracted from
+ tests/test-pthread-mutex.c.
+ * tests/test-pthread-mutex.c: Include it. Remove the corresponding code.
+ * tests/test-pthread-rwlock.c: Likewise.
+ * modules/pthread-mutex-tests (Files): Add tests/atomic-int-posix.h.
+ * modules/pthread-rwlock-tests (Files): Likewise.
+
+2020-07-01 Bruno Haible <bruno@clisp.org>
+
+ tests: Refactor.
+ * tests/atomic-int-isoc.h: New file, extracted from tests/test-mtx.c.
+ * tests/test-mtx.c: Include it. Remove the corresponding code.
+ * modules/mtx-tests (Files): Add tests/atomic-int-isoc.h.
+
+2020-07-01 Bruno Haible <bruno@clisp.org>
+
+ tests: Refactor.
+ * tests/atomic-int-gnulib.h: New file, extracted from tests/test-lock.c.
+ * tests/test-lock.c: Include it. Remove the corresponding code.
+ * modules/lock-tests (Files): Add tests/atomic-int-gnulib.h.
+
+2020-06-29 Bruno Haible <bruno@clisp.org>
+
+ sys_socket: Don't define socklen_t if it is already defined on mingw.
+ Reported by Keith Marshall <keith.d.marshall@ntlworld.com> in
+ <https://savannah.gnu.org/bugs/?57725>,
+ by Rahul Das <bokul_4u@yahoo.com> in
+ <https://lists.gnu.org/archive/html/bug-gnulib/2020-04/msg00081.html>,
+ and by Eli Zaretskii <eliz@gnu.org> in
+ <https://lists.gnu.org/archive/html/bug-gnulib/2020-06/msg00068.html>.
+ * lib/sys_socket.in.h (socklen_t): Remove definition.
+
+2020-06-29 Bruno Haible <bruno@clisp.org>
+
+ alloca-opt: Fix warning on mingw.
+ Reported and solution by Eli Zaretskii <eliz@gnu.org> in
+ <https://lists.gnu.org/archive/html/bug-gnulib/2020-06/msg00069.html>.
+ * lib/alloca.in.h: On mingw, include <alloca.h> and then test again
+ whether alloca is defined.
+
+2020-06-28 Paul Eggert <eggert@cs.ucla.edu>
+
+ getrandom: do not depend on ‘open’ on mingw
+ Similarly for at-internal, getloadavg. These modules do not call
+ the ‘open’ function when they are compiled on mingw. On mingw,
+ this avoids having to compile open.c when building Emacs, which
+ does its own thing with ‘open’.
+ * modules/at-internal, modules/getloadavg, modules/getrandom:
+ (Depends-on): Don’t depend on ‘open’ on mingw.
+ (Depends-on): Require AC_CANONICAL_HOST, for host_os.
+ * modules/getloadavg (Depends-on):
+ Depend on intprops, open, stdbool, stdlib only if compiling
+ getloadavg.c.
+
+2020-06-28 Bruno Haible <bruno@clisp.org>
+
+ doc: Add a note about sigprocmask vs. pthread_sigmask.
+ * doc/posix-functions/sigprocmask.texi: Add note.
+
+2020-06-28 Bruno Haible <bruno@clisp.org>
+
+ getrandom: Fix compilation errors on older versions of mingw.
+ Reported by Eli Zaretskii <eliz@gnu.org> in
+ <https://lists.gnu.org/archive/html/bug-gnulib/2020-06/msg00059.html>.
+ * m4/getrandom.m4 (gl_FUNC_GETRANDOM): Test whether <bcrypt.h> exists.
+ * lib/getrandom.c: If <bcrypt.h> is not available, include <ntdef.h> and
+ define/declare BCRYPT_ALG_HANDLE, BCRYPT_USE_SYSTEM_PREFERRED_RNG,
+ BCryptGenRandom ourselves.
+
+2020-06-28 Bruno Haible <bruno@clisp.org>
+
+ clean-temp: Fix wrong errno in error message.
+ * lib/clean-temp.c (create_temp_dir): Save errno around
+ unblock_fatal_signals call.
+
+2020-06-27 Bruno Haible <bruno@clisp.org>
+
+ fatal-signal: Make multithread-safe.
+ * lib/fatal-signal.c (at_fatal_signal): Don't free the old actions array.
+
+2020-06-27 Bruno Haible <bruno@clisp.org>
+
+ clean-temp: Don't force deletion of temporary files on native Windows.
+ * lib/clean-temp.h (open_temp, fopen_temp): Add delete_on_close
+ argument.
+ * lib/clean-temp.c (open_temp, fopen_temp): Likewise.
+ * NEWS: Mention the change.
+ * lib/javacomp.c (write_temp_file): Update.
+
+2020-06-27 Bruno Haible <bruno@clisp.org>
+
+ fatal-signal: Make multithread-safe.
+ * lib/fatal-signal.c: Include glthread/lock.h.
+ (at_fatal_signal_lock): New variable.
+ (at_fatal_signal): Use it.
+ (fatal_signals_block_lock, fatal_signals_block_counter): New variables.
+ (block_fatal_signals, unblock_fatal_signals): Use them.
+ * modules/fatal-signal (Depends-on): Add lock.
+
+2020-06-27 Paul Eggert <eggert@cs.ucla.edu>
+
+ getloadavg: don’t depend on fopen-gnu
+ This is for Emacs, which does not need fopen-gnu for anything else,
+ and which would need it only on a NetBSD platform where getloadavg
+ does not work (does that even happen?).
+ * lib/getloadavg.c (getloadavg) [__NetBSD__]: Use open, not fopen.
+ * modules/getloadavg (Depends-on): Remove fopen-gnu.
+
+ * tests/test-getloadavg.c (main): Fix typo.
+
+2020-06-27 Bruno Haible <bruno@clisp.org>
+
+ tempname et al.: Fix link errors on MSVC (regression from 2020-05-31).
+ * modules/tempname (Link): New section.
+ * modules/mkdtemp (Link): Likewise.
+ * modules/clean-temp (Link): Likewise.
+ * modules/mkstemp (Link): Likewise.
+ * modules/stdlib-safer (Link): Likewise.
+ * modules/mkstemps (Link): Likewise.
+ * modules/mkostemp (Link): Likewise.
+ * modules/mkostemps (Link): Likewise.
+ * modules/tmpfile (Link): Likewise.
+ * modules/tmpfile-safer (Link): Likewise.
+ * modules/javacomp (Link): Add $(LIB_GETRANDOM).
+ * modules/argv-iter-tests (Makefile.am): Link test-argv-iter with
+ $(LIB_GETRANDOM).
+ * NEWS: Mention the changes.
+
+2020-06-27 Bruno Haible <bruno@clisp.org>
+
+ fopen-gnu: Simplify code.
+ * lib/fopen.c: Include <stdbool.h>.
+ (rpl_fopen): Use a single variable open_flags instead of
+ open_flags_standard and open_flags_gnu. Make open_flags_gnu a bool.
+ * modules/fopen (Depends-on): Add stdbool.
+
+2020-06-26 Bruno Haible <bruno@clisp.org>
+
+ canonicalize: Improve documentation.
+ * lib/canonicalize.h (canonicalize_filename_mode): Document the failure
+ return convention.
+
+2020-06-26 Bruno Haible <bruno@clisp.org>
+
+ xgetcwd: Improve documentation.
+ * lib/xgetcwd.c (xgetcwd): Document the failure return convention.
+
+2020-06-26 Bruno Haible <bruno@clisp.org>
+
+ getcwd: Improve documentation.
+ * lib/getcwd.c (__getcwd): Document the failure return convention.
+
+2020-06-26 Bruno Haible <bruno@clisp.org>
+
+ fchdir: Improve documentation.
+ * lib/fchdir.c (get_name, _gl_register_fd): Document the failure return
+ convention.
+
+2020-06-26 Bruno Haible <bruno@clisp.org>
+
+ filenamecat-lgpl: Set errno upon failure.
+ * lib/filenamecat-lgpl.c (mfile_name_concat): Document the failure
+ return convention.
+ * modules/filenamecat-lgpl (Depends-on): Add malloc-posix.
+
+2020-06-26 Bruno Haible <bruno@clisp.org>
+
+ areadlink-with-size: Set errno upon failure.
+ * lib/areadlink-with-size.c (areadlink_with_size): Set errno when malloc
+ fails.
+ * lib/areadlinkat-with-size.c (areadlinkat_with_size): Add comment.
+
+2020-06-26 Bruno Haible <bruno@clisp.org>
+
+ copy-file: Shrink dependencies.
+ * modules/copy-file (Depends-on): Remove acl. Add acl-permissions,
+ qcopy-acl.
+
+2020-06-26 Bruno Haible <bruno@clisp.org>
+
+ doc: Mention declaration fixes implemented by some modules.
+ * doc/posix-functions/chdir.texi: Mention the module 'chdir'.
+ * doc/posix-functions/close.texi: Mention that Gnulib makes the function
+ declaration appear in <unistd.h>.
+ * doc/posix-functions/dup.texi: Likewise.
+ * doc/posix-functions/dup2.texi: Likewise.
+ * doc/posix-functions/gethostname.texi: Likewise.
+ * doc/posix-functions/isatty.texi: Likewise.
+ * doc/posix-functions/lseek.texi: Likewise.
+ * doc/posix-functions/unlink.texi: Likewise.
+ * doc/posix-functions/read.texi: Mention the module 'read'.
+ * doc/posix-functions/write.texi: Mention the effects of the module
+ 'write'.
+
+2020-06-25 Marc Nieper-Wißkirchen <marc@nieper-wisskirchen.de>
+
+ c-dtoastr, c-ldtoastr: new modules
+ These modules provide the same functionality as the modules
+ dtoastr and ldtoastr except for the formatting taking place in the
+ C locale.
+ * MODULES.html.sh: Add c-dtoastr and c-ldtoastr.
+ * lib/c-dtoastr.c, lib/c-ldtoastr.c: New files.
+ * lib/ftoastr.c: Prefix exported functions when the macro C_LOCALE is
+ defined. Use c_snprintf and c_strtod/c_strtold instead of
+ snprintf and strtod/strtold whhen the macro C_LOCALE is defined.
+ * lib/ftoastr.h: Add prototypes for c_dtoastr and c_ldtoastr.
+ * modules/c-dtoastr, modules/c-dtoastr-tests, modules/c-ldtoastr,
+ modules/c-ldtoastr-tests: New files.
+ * tests/test-c-dtoastr.c, tests/test-c-dtoastr.sh,
+ tests-c-ldtoastr.c tests-c-ldtoastr.sh: New files.
+
+2020-06-21 Bruno Haible <bruno@clisp.org>
+
+ tzset: Fix compilation warnings on mingw (regression from 2017-05-01).
+ * lib/tzset.c: Include <stdlib.h>, <string.h>.
+
+2020-06-16 Bruno Haible <bruno@clisp.org>
+
+ thread: Avoid possible compiler warnings in uses of gl_thread_exit.
+ * lib/glthread/thread.h (gl_thread_exit): Add a cast to void.
+
+2020-06-16 Bruno Haible <bruno@clisp.org>
+
+ thread, thrd: Avoid a compiler warning.
+ * lib/windows-thread.h (glwthread_thread_exit): Mark as non-returning.
+
+2020-06-16 Biswapriyo Nath <nathbappai@gmail.com> (tiny change)
+
+ windows-thread: Avoid a compiler warning.
+ * lib/windows-thread.h (glwthread_thread_exit): Change return type to
+ void.
+ * lib/windows-thread.c (glwthread_thread_exit): Likewise.
+
+2020-06-15 Bruno Haible <bruno@clisp.org>
+
+ unictype/joininggroup-name: Fix warning on 64-bit mingw.
+ Reported by Biswapriyo Nath <nathbappai@gmail.com> in
+ <https://lists.gnu.org/archive/html/bug-gnulib/2020-06/msg00036.html>.
+ * lib/unictype/joininggroup_name.c (ELEM): Cast struct offset to size_t
+ first.
+
+2020-06-06 Bruno Haible <bruno@clisp.org>
+
+ calloc-gnu tests: Avoid a test failure with clang.
+ * tests/test-calloc-gnu.c (main): Mark the pointer variable as
+ 'volatile', to defeat compiler optimizations.
+
2020-06-01 Paul Eggert <eggert@cs.ucla.edu>
getloadavg: fix double-increment bug
diff --git a/MODULES.html.sh b/MODULES.html.sh
index 4db2a77..43a6dba 100755
--- a/MODULES.html.sh
+++ b/MODULES.html.sh
@@ -2319,6 +2319,8 @@ func_all_modules ()
func_echo "$element"
func_begin_table
+ func_module c-dtoastr
+ func_module c-ldtoast
func_module dtoastr
func_module ftoastr
func_module intprops
@@ -2370,6 +2372,15 @@ func_all_modules ()
func_module limits-h
func_end_table
+ element="Support for GNU multiple precision arithmetic"
+ func_section_wrap gmp
+ func_wrap H2
+ func_echo "$element"
+
+ func_begin_table
+ func_module libgmp
+ func_end_table
+
element="Support for sharing code with the GNU C Library"
func_section_wrap glibc
func_wrap H2
diff --git a/NEWS b/NEWS
index c8f78ea..668f2d6 100644
--- a/NEWS
+++ b/NEWS
@@ -58,6 +58,23 @@ User visible incompatible changes
Date Modules Changes
+2020-06-27 clean-temp The functions open_temp, fopen_temp now take a
+ 'bool delete_on_close' argument. If in doubt, pass
+ false.
+
+2020-06-27 tempname The link requirements of these modules are changed
+ mkdtemp from empty to $(LIB_GETRANDOM).
+ mkstemp
+ mkstemps
+ mkostemp
+ mkostemps
+ tmpfile
+ stdlib-safer
+ tmpfile-safer
+ clean-temp
+ javacomp $(LIB_GETRANDOM) was added to the link requirements
+ of this module.
+
2020-05-27 read-file The functions provided by this module now take an
'int flags' argument to modify the file reading
behavior. The read_binary_file function has been
diff --git a/build-aux/config.guess b/build-aux/config.guess
index 11fda52..92bfc33 100755
--- a/build-aux/config.guess
+++ b/build-aux/config.guess
@@ -1095,7 +1095,17 @@ EOF
echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
exit ;;
x86_64:Linux:*:*)
- echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+ set_cc_for_build
+ LIBCABI=$LIBC
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_X32 >/dev/null
+ then
+ LIBCABI="$LIBC"x32
+ fi
+ fi
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI"
exit ;;
xtensa*:Linux:*:*)
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
diff --git a/build-aux/config.sub b/build-aux/config.sub
index 973a298..ce89d5c 100755
--- a/build-aux/config.sub
+++ b/build-aux/config.sub
@@ -2,7 +2,7 @@
# Configuration validation subroutine script.
# Copyright 1992-2020 Free Software Foundation, Inc.
-timestamp='2020-05-04'
+timestamp='2020-06-28'
# 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
@@ -124,28 +124,27 @@ case $1 in
;;
*-*-*-*)
basic_machine=$field1-$field2
- os=$field3-$field4
+ basic_os=$field3-$field4
;;
*-*-*)
# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
# parts
maybe_os=$field2-$field3
case $maybe_os in
- nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \
- | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \
+ nto-qnx* | linux-* | uclinux-uclibc* \
| uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
| netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
| storm-chaos* | os2-emx* | rtmk-nova*)
basic_machine=$field1
- os=$maybe_os
+ basic_os=$maybe_os
;;
android-linux)
basic_machine=$field1-unknown
- os=linux-android
+ basic_os=linux-android
;;
*)
basic_machine=$field1-$field2
- os=$field3
+ basic_os=$field3
;;
esac
;;
@@ -154,7 +153,7 @@ case $1 in
case $field1-$field2 in
decstation-3100)
basic_machine=mips-dec
- os=
+ basic_os=
;;
*-*)
# Second component is usually, but not always the OS
@@ -162,7 +161,7 @@ case $1 in
# Prevent following clause from handling this valid os
sun*os*)
basic_machine=$field1
- os=$field2
+ basic_os=$field2
;;
# Manufacturers
dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
@@ -175,11 +174,11 @@ case $1 in
| microblaze* | sim | cisco \
| oki | wec | wrs | winbond)
basic_machine=$field1-$field2
- os=
+ basic_os=
;;
*)
basic_machine=$field1
- os=$field2
+ basic_os=$field2
;;
esac
;;
@@ -191,447 +190,451 @@ case $1 in
case $field1 in
386bsd)
basic_machine=i386-pc
- os=bsd
+ basic_os=bsd
;;
a29khif)
basic_machine=a29k-amd
- os=udi
+ basic_os=udi
;;
adobe68k)
basic_machine=m68010-adobe
- os=scout
+ basic_os=scout
;;
alliant)
basic_machine=fx80-alliant
- os=
+ basic_os=
;;
altos | altos3068)
basic_machine=m68k-altos
- os=
+ basic_os=
;;
am29k)
basic_machine=a29k-none
- os=bsd
+ basic_os=bsd
;;
amdahl)
basic_machine=580-amdahl
- os=sysv
+ basic_os=sysv
;;
amiga)
basic_machine=m68k-unknown
- os=
+ basic_os=
;;
amigaos | amigados)
basic_machine=m68k-unknown
- os=amigaos
+ basic_os=amigaos
;;
amigaunix | amix)
basic_machine=m68k-unknown
- os=sysv4
+ basic_os=sysv4
;;
apollo68)
basic_machine=m68k-apollo
- os=sysv
+ basic_os=sysv
;;
apollo68bsd)
basic_machine=m68k-apollo
- os=bsd
+ basic_os=bsd
;;
aros)
basic_machine=i386-pc
- os=aros
+ basic_os=aros
;;
aux)
basic_machine=m68k-apple
- os=aux
+ basic_os=aux
;;
balance)
basic_machine=ns32k-sequent
- os=dynix
+ basic_os=dynix
;;
blackfin)
basic_machine=bfin-unknown
- os=linux
+ basic_os=linux
;;
cegcc)
basic_machine=arm-unknown
- os=cegcc
+ basic_os=cegcc
;;
convex-c1)
basic_machine=c1-convex
- os=bsd
+ basic_os=bsd
;;
convex-c2)
basic_machine=c2-convex
- os=bsd
+ basic_os=bsd
;;
convex-c32)
basic_machine=c32-convex
- os=bsd
+ basic_os=bsd
;;
convex-c34)
basic_machine=c34-convex
- os=bsd
+ basic_os=bsd
;;
convex-c38)
basic_machine=c38-convex
- os=bsd
+ basic_os=bsd
;;
cray)
basic_machine=j90-cray
- os=unicos
+ basic_os=unicos
;;
crds | unos)
basic_machine=m68k-crds
- os=
+ basic_os=
;;
da30)
basic_machine=m68k-da30
- os=
+ basic_os=
;;
decstation | pmax | pmin | dec3100 | decstatn)
basic_machine=mips-dec
- os=
+ basic_os=
;;
delta88)
basic_machine=m88k-motorola
- os=sysv3
+ basic_os=sysv3
;;
dicos)
basic_machine=i686-pc
- os=dicos
+ basic_os=dicos
;;
djgpp)
basic_machine=i586-pc
- os=msdosdjgpp
+ basic_os=msdosdjgpp
;;
ebmon29k)
basic_machine=a29k-amd
- os=ebmon
+ basic_os=ebmon
;;
es1800 | OSE68k | ose68k | ose | OSE)
basic_machine=m68k-ericsson
- os=ose
+ basic_os=ose
;;
gmicro)
basic_machine=tron-gmicro
- os=sysv
+ basic_os=sysv
;;
go32)
basic_machine=i386-pc
- os=go32
+ basic_os=go32
;;
h8300hms)
basic_machine=h8300-hitachi
- os=hms
+ basic_os=hms
;;
h8300xray)
basic_machine=h8300-hitachi
- os=xray
+ basic_os=xray
;;
h8500hms)
basic_machine=h8500-hitachi
- os=hms
+ basic_os=hms
;;
harris)
basic_machine=m88k-harris
- os=sysv3
+ basic_os=sysv3
;;
hp300 | hp300hpux)
basic_machine=m68k-hp
- os=hpux
+ basic_os=hpux
;;
hp300bsd)
basic_machine=m68k-hp
- os=bsd
+ basic_os=bsd
;;
hppaosf)
basic_machine=hppa1.1-hp
- os=osf
+ basic_os=osf
;;
hppro)
basic_machine=hppa1.1-hp
- os=proelf
+ basic_os=proelf
;;
i386mach)
basic_machine=i386-mach
- os=mach
+ basic_os=mach
;;
isi68 | isi)
basic_machine=m68k-isi
- os=sysv
+ basic_os=sysv
;;
m68knommu)
basic_machine=m68k-unknown
- os=linux
+ basic_os=linux
;;
magnum | m3230)
basic_machine=mips-mips
- os=sysv
+ basic_os=sysv
;;
merlin)
basic_machine=ns32k-utek
- os=sysv
+ basic_os=sysv
;;
mingw64)
basic_machine=x86_64-pc
- os=mingw64
+ basic_os=mingw64
;;
mingw32)
basic_machine=i686-pc
- os=mingw32
+ basic_os=mingw32
;;
mingw32ce)
basic_machine=arm-unknown
- os=mingw32ce
+ basic_os=mingw32ce
;;
monitor)
basic_machine=m68k-rom68k
- os=coff
+ basic_os=coff
;;
morphos)
basic_machine=powerpc-unknown
- os=morphos
+ basic_os=morphos
;;
moxiebox)
basic_machine=moxie-unknown
- os=moxiebox
+ basic_os=moxiebox
;;
msdos)
basic_machine=i386-pc
- os=msdos
+ basic_os=msdos
;;
msys)
basic_machine=i686-pc
- os=msys
+ basic_os=msys
;;
mvs)
basic_machine=i370-ibm
- os=mvs
+ basic_os=mvs
;;
nacl)
basic_machine=le32-unknown
- os=nacl
+ basic_os=nacl
;;
ncr3000)
basic_machine=i486-ncr
- os=sysv4
+ basic_os=sysv4
;;
netbsd386)
basic_machine=i386-pc
- os=netbsd
+ basic_os=netbsd
;;
netwinder)
basic_machine=armv4l-rebel
- os=linux
+ basic_os=linux
;;
news | news700 | news800 | news900)
basic_machine=m68k-sony
- os=newsos
+ basic_os=newsos
;;
news1000)
basic_machine=m68030-sony
- os=newsos
+ basic_os=newsos
;;
necv70)
basic_machine=v70-nec
- os=sysv
+ basic_os=sysv
;;
nh3000)
basic_machine=m68k-harris
- os=cxux
+ basic_os=cxux
;;
nh[45]000)
basic_machine=m88k-harris
- os=cxux
+ basic_os=cxux
;;
nindy960)
basic_machine=i960-intel
- os=nindy
+ basic_os=nindy
;;
mon960)
basic_machine=i960-intel
- os=mon960
+ basic_os=mon960
;;
nonstopux)
basic_machine=mips-compaq
- os=nonstopux
+ basic_os=nonstopux
;;
os400)
basic_machine=powerpc-ibm
- os=os400
+ basic_os=os400
;;
OSE68000 | ose68000)
basic_machine=m68000-ericsson
- os=ose
+ basic_os=ose
;;
os68k)
basic_machine=m68k-none
- os=os68k
+ basic_os=os68k
;;
paragon)
basic_machine=i860-intel
- os=osf
+ basic_os=osf
;;
parisc)
basic_machine=hppa-unknown
- os=linux
+ basic_os=linux
+ ;;
+ psp)
+ basic_machine=mipsallegrexel-sony
+ basic_os=psp
;;
pw32)
basic_machine=i586-unknown
- os=pw32
+ basic_os=pw32
;;
rdos | rdos64)
basic_machine=x86_64-pc
- os=rdos
+ basic_os=rdos
;;
rdos32)
basic_machine=i386-pc
- os=rdos
+ basic_os=rdos
;;
rom68k)
basic_machine=m68k-rom68k
- os=coff
+ basic_os=coff
;;
sa29200)
basic_machine=a29k-amd
- os=udi
+ basic_os=udi
;;
sei)
basic_machine=mips-sei
- os=seiux
+ basic_os=seiux
;;
sequent)
basic_machine=i386-sequent
- os=
+ basic_os=
;;
sps7)
basic_machine=m68k-bull
- os=sysv2
+ basic_os=sysv2
;;
st2000)
basic_machine=m68k-tandem
- os=
+ basic_os=
;;
stratus)
basic_machine=i860-stratus
- os=sysv4
+ basic_os=sysv4
;;
sun2)
basic_machine=m68000-sun
- os=
+ basic_os=
;;
sun2os3)
basic_machine=m68000-sun
- os=sunos3
+ basic_os=sunos3
;;
sun2os4)
basic_machine=m68000-sun
- os=sunos4
+ basic_os=sunos4
;;
sun3)
basic_machine=m68k-sun
- os=
+ basic_os=
;;
sun3os3)
basic_machine=m68k-sun
- os=sunos3
+ basic_os=sunos3
;;
sun3os4)
basic_machine=m68k-sun
- os=sunos4
+ basic_os=sunos4
;;
sun4)
basic_machine=sparc-sun
- os=
+ basic_os=
;;
sun4os3)
basic_machine=sparc-sun
- os=sunos3
+ basic_os=sunos3
;;
sun4os4)
basic_machine=sparc-sun
- os=sunos4
+ basic_os=sunos4
;;
sun4sol2)
basic_machine=sparc-sun
- os=solaris2
+ basic_os=solaris2
;;
sun386 | sun386i | roadrunner)
basic_machine=i386-sun
- os=
+ basic_os=
;;
sv1)
basic_machine=sv1-cray
- os=unicos
+ basic_os=unicos
;;
symmetry)
basic_machine=i386-sequent
- os=dynix
+ basic_os=dynix
;;
t3e)
basic_machine=alphaev5-cray
- os=unicos
+ basic_os=unicos
;;
t90)
basic_machine=t90-cray
- os=unicos
+ basic_os=unicos
;;
toad1)
basic_machine=pdp10-xkl
- os=tops20
+ basic_os=tops20
;;
tpf)
basic_machine=s390x-ibm
- os=tpf
+ basic_os=tpf
;;
udi29k)
basic_machine=a29k-amd
- os=udi
+ basic_os=udi
;;
ultra3)
basic_machine=a29k-nyu
- os=sym1
+ basic_os=sym1
;;
v810 | necv810)
basic_machine=v810-nec
- os=none
+ basic_os=none
;;
vaxv)
basic_machine=vax-dec
- os=sysv
+ basic_os=sysv
;;
vms)
basic_machine=vax-dec
- os=vms
+ basic_os=vms
;;
vsta)
basic_machine=i386-pc
- os=vsta
+ basic_os=vsta
;;
vxworks960)
basic_machine=i960-wrs
- os=vxworks
+ basic_os=vxworks
;;
vxworks68)
basic_machine=m68k-wrs
- os=vxworks
+ basic_os=vxworks
;;
vxworks29k)
basic_machine=a29k-wrs
- os=vxworks
+ basic_os=vxworks
;;
xbox)
basic_machine=i686-pc
- os=mingw32
+ basic_os=mingw32
;;
ymp)
basic_machine=ymp-cray
- os=unicos
+ basic_os=unicos
;;
*)
basic_machine=$1
- os=
+ basic_os=
;;
esac
;;
@@ -683,17 +686,17 @@ case $basic_machine in
bluegene*)
cpu=powerpc
vendor=ibm
- os=cnk
+ basic_os=cnk
;;
decsystem10* | dec10*)
cpu=pdp10
vendor=dec
- os=tops10
+ basic_os=tops10
;;
decsystem20* | dec20*)
cpu=pdp10
vendor=dec
- os=tops20
+ basic_os=tops20
;;
delta | 3300 | motorola-3300 | motorola-delta \
| 3300-motorola | delta-motorola)
@@ -703,7 +706,7 @@ case $basic_machine in
dpx2*)
cpu=m68k
vendor=bull
- os=sysv3
+ basic_os=sysv3
;;
encore | umax | mmax)
cpu=ns32k
@@ -712,7 +715,7 @@ case $basic_machine in
elxsi)
cpu=elxsi
vendor=elxsi
- os=${os:-bsd}
+ basic_os=${basic_os:-bsd}
;;
fx2800)
cpu=i860
@@ -725,7 +728,7 @@ case $basic_machine in
h3050r* | hiux*)
cpu=hppa1.1
vendor=hitachi
- os=hiuxwe2
+ basic_os=hiuxwe2
;;
hp3k9[0-9][0-9] | hp9[0-9][0-9])
cpu=hppa1.0
@@ -768,36 +771,36 @@ case $basic_machine in
i*86v32)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
- os=sysv32
+ basic_os=sysv32
;;
i*86v4*)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
- os=sysv4
+ basic_os=sysv4
;;
i*86v)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
- os=sysv
+ basic_os=sysv
;;
i*86sol2)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
- os=solaris2
+ basic_os=solaris2
;;
j90 | j90-cray)
cpu=j90
vendor=cray
- os=${os:-unicos}
+ basic_os=${basic_os:-unicos}
;;
iris | iris4d)
cpu=mips
vendor=sgi
- case $os in
+ case $basic_os in
irix*)
;;
*)
- os=irix4
+ basic_os=irix4
;;
esac
;;
@@ -808,26 +811,26 @@ case $basic_machine in
*mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
cpu=m68k
vendor=atari
- os=mint
+ basic_os=mint
;;
news-3600 | risc-news)
cpu=mips
vendor=sony
- os=newsos
+ basic_os=newsos
;;
next | m*-next)
cpu=m68k
vendor=next
- case $os in
+ case $basic_os in
openstep*)
;;
nextstep*)
;;
ns2*)
- os=nextstep2
+ basic_os=nextstep2
;;
*)
- os=nextstep3
+ basic_os=nextstep3
;;
esac
;;
@@ -838,12 +841,12 @@ case $basic_machine in
op50n-* | op60c-*)
cpu=hppa1.1
vendor=oki
- os=proelf
+ basic_os=proelf
;;
pa-hitachi)
cpu=hppa1.1
vendor=hitachi
- os=hiuxwe2
+ basic_os=hiuxwe2
;;
pbd)
cpu=sparc
@@ -880,12 +883,12 @@ case $basic_machine in
sde)
cpu=mipsisa32
vendor=sde
- os=${os:-elf}
+ basic_os=${basic_os:-elf}
;;
simso-wrs)
cpu=sparclite
vendor=wrs
- os=vxworks
+ basic_os=vxworks
;;
tower | tower-32)
cpu=m68k
@@ -902,7 +905,7 @@ case $basic_machine in
w89k-*)
cpu=hppa1.1
vendor=winbond
- os=proelf
+ basic_os=proelf
;;
none)
cpu=none
@@ -955,11 +958,11 @@ case $cpu-$vendor in
# some cases the only manufacturer, in others, it is the most popular.
craynv-unknown)
vendor=cray
- os=${os:-unicosmp}
+ basic_os=${basic_os:-unicosmp}
;;
c90-unknown | c90-cray)
vendor=cray
- os=${os:-unicos}
+ basic_os=${Basic_os:-unicos}
;;
fx80-unknown)
vendor=alliant
@@ -1003,7 +1006,7 @@ case $cpu-$vendor in
dpx20-unknown | dpx20-bull)
cpu=rs6000
vendor=bull
- os=${os:-bosx}
+ basic_os=${basic_os:-bosx}
;;
# Here we normalize CPU types irrespective of the vendor
@@ -1012,7 +1015,7 @@ case $cpu-$vendor in
;;
blackfin-*)
cpu=bfin
- os=linux
+ basic_os=linux
;;
c54x-*)
cpu=tic54x
@@ -1025,7 +1028,7 @@ case $cpu-$vendor in
;;
e500v[12]-*)
cpu=powerpc
- os=$os"spe"
+ basic_os=${basic_os}"spe"
;;
mips3*-*)
cpu=mips64
@@ -1035,7 +1038,7 @@ case $cpu-$vendor in
;;
m68knommu-*)
cpu=m68k
- os=linux
+ basic_os=linux
;;
m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
cpu=s12z
@@ -1045,7 +1048,7 @@ case $cpu-$vendor in
;;
parisc-*)
cpu=hppa
- os=linux
+ basic_os=linux
;;
pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
cpu=i586
@@ -1105,7 +1108,7 @@ case $cpu-$vendor in
# Recognize the canonical CPU Types that limit and/or modify the
# company names they are paired with.
cr16-*)
- os=${os:-elf}
+ basic_os=${basic_os:-elf}
;;
crisv32-* | etraxfs*-*)
cpu=crisv32
@@ -1116,7 +1119,7 @@ case $cpu-$vendor in
vendor=axis
;;
crx-*)
- os=${os:-elf}
+ basic_os=${basic_os:-elf}
;;
neo-tandem)
cpu=neo
@@ -1138,16 +1141,12 @@ case $cpu-$vendor in
cpu=nsx
vendor=tandem
;;
- s390-*)
- cpu=s390
- vendor=ibm
- ;;
- s390x-*)
- cpu=s390x
- vendor=ibm
+ mipsallegrexel-sony)
+ cpu=mipsallegrexel
+ vendor=sony
;;
tile*-*)
- os=${os:-linux-gnu}
+ basic_os=${basic_os:-linux-gnu}
;;
*)
@@ -1229,6 +1228,7 @@ case $cpu-$vendor in
| pyramid \
| riscv | riscv32 | riscv64 \
| rl78 | romp | rs6000 | rx \
+ | s390 | s390x \
| score \
| sh | shl \
| sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
@@ -1275,8 +1275,43 @@ esac
# Decode manufacturer-specific aliases for certain operating systems.
-if [ x$os != x ]
+if [ x$basic_os != x ]
then
+
+# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just
+# set os.
+case $basic_os in
+ gnu/linux*)
+ kernel=linux
+ os=`echo $basic_os | sed -e 's|gnu/linux|gnu|'`
+ ;;
+ nto-qnx*)
+ kernel=nto
+ os=`echo $basic_os | sed -e 's|nto-qnx|qnx|'`
+ ;;
+ *-*)
+ # shellcheck disable=SC2162
+ IFS="-" read kernel os <<EOF
+$basic_os
+EOF
+ ;;
+ # Default OS when just kernel was specified
+ nto*)
+ kernel=nto
+ os=`echo $basic_os | sed -e 's|nto|qnx|'`
+ ;;
+ linux*)
+ kernel=linux
+ os=`echo $basic_os | sed -e 's|linux|gnu|'`
+ ;;
+ *)
+ kernel=
+ os=$basic_os
+ ;;
+esac
+
+# Now, normalize the OS (knowing we just have one component, it's not a kernel,
+# etc.)
case $os in
# First match some system type aliases that might get confused
# with valid system types.
@@ -1296,9 +1331,6 @@ case $os in
unixware*)
os=sysv4.2uw
;;
- gnu/linux*)
- os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
- ;;
# es1800 is here to avoid being matched by es* (a different OS)
es1800*)
os=ose
@@ -1322,10 +1354,7 @@ case $os in
sco3.2.[4-9]*)
os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
;;
- sco3.2v[4-9]* | sco5v6*)
- # Don't forget version if it is 3.2v4 or newer.
- ;;
- scout)
+ sco*v* | scout)
# Don't match below
;;
sco*)
@@ -1334,41 +1363,6 @@ case $os in
psos*)
os=psos
;;
- # Now accept the basic system types.
- # The portable systems comes first.
- # Each alternative MUST end in a * to match a version number.
- # sysv* is not here because it comes later, after sysvr4.
- gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
- | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\
- | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
- | sym* | kopensolaris* | plan9* \
- | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
- | aos* | aros* | cloudabi* | sortix* | twizzler* \
- | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
- | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
- | knetbsd* | mirbsd* | netbsd* \
- | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \
- | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \
- | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
- | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
- | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \
- | chorusrdb* | cegcc* | glidix* \
- | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
- | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \
- | linux-newlib* | linux-musl* | linux-uclibc* \
- | uxpv* | beos* | mpeix* | udk* | moxiebox* \
- | interix* | uwin* | mks* | rhapsody* | darwin* \
- | openstep* | oskit* | conix* | pw32* | nonstopux* \
- | storm-chaos* | tops10* | tenex* | tops20* | its* \
- | os2* | vos* | palmos* | uclinux* | nucleus* \
- | morphos* | superux* | rtmk* | windiss* \
- | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
- | skyos* | haiku* | rdos* | toppers* | drops* | es* \
- | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
- | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
- | nsk* | powerunix* | genode*)
- # Remember, each alternative MUST END IN *, to match a version number.
- ;;
qnx*)
case $cpu in
x86 | i*86)
@@ -1381,31 +1375,19 @@ case $os in
hiux*)
os=hiuxwe2
;;
- nto-qnx*)
- ;;
- nto*)
- os=`echo $os | sed -e 's|nto|nto-qnx|'`
- ;;
- sim | xray | os68k* | v88r* \
- | windows* | osx | abug | netware* | os9* \
- | macos* | mpw* | magic* | mmixware* | mon960* | lnews*)
- ;;
- linux-dietlibc)
- os=linux-dietlibc
- ;;
- linux*)
- os=`echo $os | sed -e 's|linux|linux-gnu|'`
- ;;
lynx*178)
os=lynxos178
;;
lynx*5)
os=lynxos5
;;
+ lynxos*)
+ # don't get caught up in next wildcard
+ ;;
lynx*)
os=lynxos
;;
- mac*)
+ mac[0-9]*)
os=`echo "$os" | sed -e 's|mac|macos|'`
;;
opened*)
@@ -1475,18 +1457,12 @@ case $os in
sysvr4)
os=sysv4
;;
- # This must come after sysvr4.
- sysv*)
- ;;
ose*)
os=ose
;;
*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
os=mint
;;
- zvmoe)
- os=zvmoe
- ;;
dicos*)
os=dicos
;;
@@ -1503,19 +1479,11 @@ case $os in
;;
esac
;;
- nacl*)
- ;;
- ios)
- ;;
- none)
- ;;
- *-eabi)
- ;;
*)
- echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
- exit 1
+ # No normalization, but not necessarily accepted, that comes below.
;;
esac
+
else
# Here we handle the default operating systems that come with various machines.
@@ -1528,6 +1496,7 @@ else
# will signal an error saying that MANUFACTURER isn't an operating
# system, and we'll never get to this point.
+kernel=
case $cpu-$vendor in
score-*)
os=elf
@@ -1539,7 +1508,8 @@ case $cpu-$vendor in
os=riscix1.2
;;
arm*-rebel)
- os=linux
+ kernel=linux
+ os=gnu
;;
arm*-semi)
os=aout
@@ -1705,84 +1675,169 @@ case $cpu-$vendor in
os=none
;;
esac
+
fi
+# Now, validate our (potentially fixed-up) OS.
+case $os in
+ # Sometimes we do "kernel-abi", so those need to count as OSes.
+ musl* | newlib* | uclibc*)
+ ;;
+ # Likewise for "kernel-libc"
+ eabi | eabihf | gnueabi | gnueabihf)
+ ;;
+ # Now accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST end in a * to match a version number.
+ gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+ | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \
+ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \
+ | hiux* | abug | nacl* | netware* | windows* \
+ | os9* | macos* | osx* | ios* \
+ | mpw* | magic* | mmixware* | mon960* | lnews* \
+ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+ | aos* | aros* | cloudabi* | sortix* | twizzler* \
+ | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+ | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+ | mirbsd* | netbsd* | dicos* | openedition* | ose* \
+ | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \
+ | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
+ | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+ | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+ | udi* | lites* | ieee* | go32* | aux* | hcos* \
+ | chorusrdb* | cegcc* | glidix* \
+ | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+ | midipix* | mingw32* | mingw64* | mint* \
+ | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+ | interix* | uwin* | mks* | rhapsody* | darwin* \
+ | openstep* | oskit* | conix* | pw32* | nonstopux* \
+ | storm-chaos* | tops10* | tenex* | tops20* | its* \
+ | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \
+ | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \
+ | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+ | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
+ | nsk* | powerunix* | genode* | zvmoe* )
+ ;;
+ # This one is extra strict with allowed versions
+ sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ ;;
+ none)
+ ;;
+ *)
+ echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# As a final step for OS-related things, validate the OS-kernel combination
+# (given a valid OS), if there is a kernel.
+case $kernel-$os in
+ linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* )
+ ;;
+ -dietlibc* | -newlib* | -musl* | -uclibc* )
+ # These are just libc implementations, not actual OSes, and thus
+ # require a kernel.
+ echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2
+ exit 1
+ ;;
+ kfreebsd*-gnu* | kopensolaris*-gnu*)
+ ;;
+ nto-qnx*)
+ ;;
+ *-eabi* | *-gnueabi*)
+ ;;
+ -*)
+ # Blank kernel with real OS is always fine.
+ ;;
+ *-*)
+ echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2
+ exit 1
+ ;;
+esac
+
# Here we handle the case where we know the os, and the CPU type, but not the
# manufacturer. We pick the logical manufacturer.
case $vendor in
unknown)
- case $os in
- riscix*)
+ case $cpu-$os in
+ *-riscix*)
vendor=acorn
;;
- sunos*)
+ *-sunos*)
vendor=sun
;;
- cnk*|-aix*)
+ *-cnk* | *-aix*)
vendor=ibm
;;
- beos*)
+ *-beos*)
vendor=be
;;
- hpux*)
+ *-hpux*)
vendor=hp
;;
- mpeix*)
+ *-mpeix*)
vendor=hp
;;
- hiux*)
+ *-hiux*)
vendor=hitachi
;;
- unos*)
+ *-unos*)
vendor=crds
;;
- dgux*)
+ *-dgux*)
vendor=dg
;;
- luna*)
+ *-luna*)
vendor=omron
;;
- genix*)
+ *-genix*)
vendor=ns
;;
- clix*)
+ *-clix*)
vendor=intergraph
;;
- mvs* | opened*)
+ *-mvs* | *-opened*)
+ vendor=ibm
+ ;;
+ *-os400*)
vendor=ibm
;;
- os400*)
+ s390-* | s390x-*)
vendor=ibm
;;
- ptx*)
+ *-ptx*)
vendor=sequent
;;
- tpf*)
+ *-tpf*)
vendor=ibm
;;
- vxsim* | vxworks* | windiss*)
+ *-vxsim* | *-vxworks* | *-windiss*)
vendor=wrs
;;
- aux*)
+ *-aux*)
vendor=apple
;;
- hms*)
+ *-hms*)
vendor=hitachi
;;
- mpw* | macos*)
+ *-mpw* | *-macos*)
vendor=apple
;;
- *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
vendor=atari
;;
- vos*)
+ *-vos*)
vendor=stratus
;;
esac
;;
esac
-echo "$cpu-$vendor-$os"
+echo "$cpu-$vendor-${kernel:+$kernel-}$os"
exit
# Local variables:
diff --git a/build-aux/gcc-warning.spec b/build-aux/gcc-warning.spec
index 89a0bc7..6c0602b 100644
--- a/build-aux/gcc-warning.spec
+++ b/build-aux/gcc-warning.spec
@@ -1,24 +1,51 @@
# options to filter out, and why
--all-warnings alias for -Wall
--extra-warnings alias for -Wextra
+-W alias for -Wextra
-Wabi this is now a no-op
-Wabi-tag c++
-Wabi= c++
+-Wabsolute-value enabled by -Wextra
+-Waddress enabled by -Wall
+-Waddress-of-packed-member default
-Waggregate-return obsolescent
+-Waggressive-loop-optimizations default
-Waliasing fortran
-Walign-commons fortran
-Waligned-new=[none|global|all] c++
--Walloc-size-larger-than=<bytes> handled specially by gl_MANYWARN_ALL_GCC
+-Walloc-size-larger-than=<bytes> defaults to PTRDIFF_MAX
-Walloc-zero Gnulib fixes this problem
-Walloca we like alloca in small doses
-Walloca-larger-than=<number> FIXME: choose something sane?
-Wampersand fortran
+-Wanalyzer-double-fclose enabled by -fanalyzer
+-Wanalyzer-double-free enabled by -fanalyzer
+-Wanalyzer-exposure-through-output-file enabled by -fanalyzer
+-Wanalyzer-file-leak enabled by -fanalyzer
+-Wanalyzer-free-of-non-heap enabled by -fanalyzer
+-Wanalyzer-malloc-leak enabled by -fanalyzer
+-Wanalyzer-null-argument enabled by -fanalyzer
+-Wanalyzer-null-dereference enabled by -fanalyzer
+-Wanalyzer-possible-null-argument enabled by -fanalyzer
+-Wanalyzer-possible-null-dereference enabled by -fanalyzer
+-Wanalyzer-stale-setjmp-buffer implied by -fanalyzer
+-Wanalyzer-tainted-array-index FIXME maybe? too much noise
+-Wanalyzer-too-complex enabled by -fanalyzer
+-Wanalyzer-unsafe-call-within-signal-handler enabled by -fanalyzer
+-Wanalyzer-use-after-free enabled by -fanalyzer
+-Wanalyzer-use-of-pointer-in-stale-stack-frame enabled by -fanalyzer
-Warray-bounds covered by -Warray-bounds=
-Warray-bounds=<0,2> handled specially by gl_MANYWARN_ALL_GCC
-Warray-temporaries fortran
-Wassign-intercept objc/objc++
-Wattribute-alias covered by -Wattribute-alias=2
-Wattribute-alias=<0,2> handled specially by gl_MANYWARN_ALL_GCC
+-Wattribute-warning default
+-Wattributes default
+-Wbool-compare enabled by -Wall
+-Wbool-operation enabled by -Wall
+-Wbuiltin-declaration-mismatch default
+-Wbuiltin-macro-redefined default
-Wc++-compat only useful for code meant to be compiled by a C++ compiler
-Wc++0x-compat c++
-Wc++11-compat c++
@@ -31,63 +58,120 @@
-Wc11-c2x-compat c compatibility
-Wc90-c99-compat c compatibility
-Wc99-c11-compat c compatibility
+-Wcannot-profile default
+-Wcast-align enabled by -Wcast-align=strict
+-Wcast-function-type enabled by -Wextra
-Wcast-qual FIXME maybe? too much noise; encourages bad changes
-Wcast-result D
-Wcatch-value c++
-Wcatch-value=<0,3> c++
+-Wchar-subscripts enabled by -Wall
-Wcharacter-truncation fortran
-Wchkp deprecated
-Wclass-conversion c++ and objc++
-Wclass-memaccess c++
+-Wclobbered enabled by -Wextra
-Wcomma-subscript c++ and objc++
+-Wcomment enabled by -Wall
+-Wcomments alias for -Wcomment
-Wcompare-reals fortran
-Wconditionally-supported c++ and objc++
-Wconversion FIXME maybe? too much noise; encourages bad changes
-Wconversion-extra fortran
-Wconversion-null c++ and objc++
+-Wcoverage-mismatch default
+-Wcpp default
-Wctor-dtor-privacy c++
+-Wdangling-else enabled by -Wparentheses
-Wdeclaration-after-statement FIXME: do not want. others may
-Wdelete-incomplete c++ and objc++
-Wdelete-non-virtual-dtor c++
+-Wdeprecated default
-Wdeprecated-copy c++ and objc++
-Wdeprecated-copy-dtor c++ and objc++
+-Wdeprecated-declarations default
+-Wdesignated-init default
+-Wdiscarded-array-qualifiers default
+-Wdiscarded-qualifiers default
-Wdo-subscript fortran
+-Wdiv-by-zero default
+-Wduplicate-decl-specifier enabled by -Wall
-Weffc++ c++
+-Wempty-body enabled by -Wextra
+-Wenum-compare enabled by -Wall
+-Wenum-conversion enabled by -Wextra
+-Wendif-labels default
-Werror-implicit-function-declaration deprecated
+-Wexpansion-to-defined enabled by -Wextra
-Wextra-semi c++
-Wfloat-conversion FIXME maybe? borderline. some will want this
-Wfloat-equal FIXME maybe? borderline. some will want this
-Wformat covered by -Wformat=2
+-Wformat-contains-nul default
+-Wformat-diag enabled by -Wformat=2
+-Wformat-extra-args enabled by -Wformat=2
+-Wformat-nonliteral enabled by -Wformat=2
-Wformat-overflow<0,2> gcc --help=warnings artifact
-Wformat-overflow=<0,2> handled specially by gl_MANYWARN_ALL_GCC
+-Wformat-security enabled by -Wformat=2
-Wformat-truncation covered by -Wformat-truncation=2
-Wformat-truncation=<0,2> handled specially by gl_MANYWARN_ALL_GCC
+-Wformat-y2k enabled by -Wformat=2
+-Wformat-zero-length enabled by -Wformat=2
-Wformat=<0,2> gcc --help=warnings artifact
+-Wframe-address enabled by -Wall
-Wframe-larger-than=<byte-size> FIXME: choose something sane?
+-Wfree-nonheap-object default
-Wfunction-elimination fortran
+-Whsa default
+-Wif-not-aligned default
+-Wignored-attributes default
+-Wignored-qualifiers enabled by -Wextra
+-Wimplicit enabled by -Wall
-Wimplicit-fallthrough covered by -Wimplicit-fallthrough=2
-Wimplicit-fallthrough=<0,5> handled specially by gl_MANYWARN_ALL_GCC
+-Wimplicit-function-declaration enabled by -Wimplicit
+-Wimplicit-int enabled by -Wimplicit
-Wimplicit-interface fortran
-Wimplicit-procedure fortran
-Winaccessible-base c++ and objc++
+-Wincompatible-pointer-types default
-Winherited-variadic-ctor c++
-Winit-list-lifetime c++ and objc++
+-Wint-conversion default
+-Wint-in-bool-context enabled by -Wall
+-Wint-to-pointer-cast default
-Winteger-division fortran
-Wintrinsic-shadow fortran
-Wintrinsics-std fortran
+-Winvalid-memory-model default
-Winvalid-offsetof c++ and objc++
-Wjump-misses-init only useful for code meant to be compiled by a C++ compiler
-Wlarger-than- gcc --help=warnings artifact
-Wlarger-than=<byte-size> FIXME: choose something sane?
-Wline-truncation fortran
-Wliteral-suffix c++ and objc++
+-Wlogical-not-parentheses enabled by -Wall
-Wlong-long obsolescent
-Wlto-type-mismatch c++ and objc++
+-Wmain enabled by -Wall
+-Wmaybe-uninitialized enabled by -Wall or -Wextra
+-Wmemset-elt-size enabled by -Wall
+-Wmemset-transposed-args enabled by -Wall
+-Wmisleading-indentation enabled by -Wall
-Wmismatched-tags c++ and objc++
+-Wmissing-attributes enabled by -Wall
+-Wmissing-braces enabled by -Wall
+-Wmissing-field-initializers enabled by -Wextra
-Wmissing-format-attribute obsolescent
-Wmissing-noreturn obsolescent
+-Wmissing-parameter-type enabled by -Wextra
+-Wmissing-profile default
+-Wmultichar default
-Wmultiple-inheritance c++ and objc++
+-Wmultistatement-macros enabled by -Wall
-Wnamespaces c++
+-Wnarrowing enabled by -Wall
-Wno-alloc-size-larger-than see -Walloc-size-larger-than
-Wno-alloca-larger-than see -Walloca-larger-than
-Wno-frame-larger-than see -Wframe-larger-than
@@ -98,21 +182,35 @@
-Wnoexcept-type c++
-Wnon-template-friend c++
-Wnon-virtual-dtor c++
--Wnormalized covered by -Wnormalized=
--Wnormalized=[none|id|nfc|nfkc] handled specially by gl_MANYWARN_ALL_GCC
+-Wnonnull enabled by -Wall or -Wformat
+-Wnonnull-compare enabled by -Wall
+-Wnormalized default
+-Wnormalized=[none|id|nfc|nfkc] defaults to nfc
+-Wodr default
-Wold-style-cast c++ and objc++
+-Wold-style-declaration enabled by -Wextra
+-Woverflow default
-Woverloaded-virtual c++
+-Woverride-init enabled by -Wextra
-Woverride-init-side-effects c++ and objc++
-Woverwrite-recursive fortran
+-Wpacked-bitfield-compat default
+-Wpacked-not-aligned enabled by -Wall
-Wpadded FIXME maybe? warns about "stabil" member in /usr/include/bits/timex.h
+-Wparentheses enabled by -Wall
-Wpedantic FIXME: too strict?
-Wpessimizing-move c++ and objc++
-Wplacement-new c++
-Wplacement-new=<0,2> c++
-Wpmf-conversions c++ and objc++
+-Wpointer-compare default
+-Wpointer-sign enabled by -Wall
+-Wpointer-to-int-cast default
+-Wpragmas default
-Wprio-ctor-dtor c++
-Wproperty-assign-default objc++
-Wprotocol objc++
+-Wpsabi default
-Wreal-q-constant fortran
-Wrealloc-lhs fortran
-Wrealloc-lhs-all fortran
@@ -121,53 +219,87 @@
-Wredundant-tags c++ and objc++
-Wregister c++ and objc++
-Wreorder c++ and objc++
+-Wrestrict enabled by -Wall
+-Wreturn-local-addr default
+-Wreturn-type enabled by -Wall
+-Wscalar-storage-order default
-Wselector objc and objc++
+-Wsequence-point enabled by -Wall
-Wshadow-compatible-local covered by -Wshadow
-Wshadow-ivar objc
-Wshadow-local covered by -Wshadow
-Wshadow=compatible-local covered by -Wshadow
-Wshadow=global covered by -Wshadow
-Wshadow=local covered by -Wshadow
+-Wshift-count-negative default
+-Wshift-count-overflow default
+-Wshift-negative-value enabled by -Wextra
-Wshift-overflow covered by -Wshift-overflow=2
-Wshift-overflow=<0,2> gcc --help=warnings artifact
-Wsign-compare FIXME maybe? borderline. some will want this
-Wsign-conversion FIXME maybe? borderline. some will want this
-Wsign-promo c++ and objc++
-Wsized-deallocation c++ and objc++
+-Wsizeof-array-argument default
+-Wsizeof-pointer-div enabled by -Wall
+-Wsizeof-pointer-memaccess enabled by -Wall
-Wstack-usage=<byte-size> FIXME: choose something sane?
+-Wstrict-aliasing enabled by -Wall
-Wstrict-aliasing=<0,3> FIXME: choose something sane?
-Wstrict-null-sentinel c++ and objc++
-Wstrict-overflow=<0,5> FIXME: choose something sane?
-Wstrict-selector-match objc and objc++
--Wstringop-overflow covered by -Wstringop-overflow=
--Wstringop-overflow=<0,4> handled specially by gl_MANYWARN_ALL_GCC
+-Wstring-compare enabled by -Wextra
+-Wstringop-overflow covered by -Wstringop-overflow=2
+-Wstringop-overflow=<0,4> defaults to 2
+-Wstringop-truncation default
-Wsubobject-linkage c++ and objc++
-Wsuggest-override c++ and objc++
-Wsurprising fortran
+-Wswitch enabled by -Wall
+-Wswitch-bool default
-Wswitch-default https://lists.gnu.org/r/bug-gnulib/2018-05/msg00179.html
-Wswitch-enum FIXME maybe? borderline. some will want this
+-Wswitch-outside-range default
+-Wswitch-unreachable default
-Wsynth deprecated
-Wtabs fortran
-Wtarget-lifetime fortran
+-Wtautological-compare enabled by -Wall
-Wtemplates c++ and objc++
-Wterminate c++ and objc++
-Wtraditional obsolescent
-Wtraditional-conversion obsolescent
+-Wtrigraphs enabled by -Wall
+-Wtype-limits enabled by -Wextra
-Wundeclared-selector objc and objc++
-Wundef FIXME maybe? too many false positives
-Wundefined-do-loop fortran
-Wunderflow fortran
-Wunreachable-code obsolescent no-op
-Wunsuffixed-float-constants triggers warning in gnulib's timespec.h
--Wunused-const-variable covered by -Wunusec-const-variable=2
+-Wunused enabled by -Wall
+-Wunused-but-set-parameter enabled by -Wunused
+-Wunused-but-set-variable enabled by -Wunused
+-Wunused-const-variable covered by -Wunused-const-variable=2
-Wunused-const-variable=<0,2> gcc --help=warnings artifact
-Wunused-dummy-argument fortran
+-Wunused-function enabled by -Wunused
+-Wunused-label enabled by -Wunused
+-Wunused-local-typedefs enabled by -Wunused
+-Wunused-parameter enabled by -Wunused
+-Wunused-result enabled by -Wunused
+-Wunused-value enabled by -Wunused
+-Wunused-variable enabled by -Wunused
-Wuse-without-only fortran
-Wuseless-cast c++ and objc++
+-Wvarargs default
-Wvirtual-inheritance c++
-Wvirtual-move-assign c++
-Wvla-larger-than=<number> handled specially by gl_MANYWARN_ALL_GCC
-Wvolatile c++ and objc++
+-Wvolatile-register-var enabled by -Wall
-Wzero-as-null-pointer-constant c++ and objc++
+-Wzero-length-bounds enabled by -Wall
-Wzerotrip fortran
-frequire-return-statement go
diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen
index d90b0ef..f4e8592 100755
--- a/build-aux/git-version-gen
+++ b/build-aux/git-version-gen
@@ -1,6 +1,6 @@
#!/bin/sh
# Print a version string.
-scriptversion=2018-03-07.03; # UTC
+scriptversion=2019-10-13.15; # UTC
# Copyright (C) 2007-2020 Free Software Foundation, Inc.
#
diff --git a/config/srclist.txt b/config/srclist.txt
index 16a799c..1ff511b 100644
--- a/config/srclist.txt
+++ b/config/srclist.txt
@@ -17,6 +17,9 @@ $TEXINFOTEX texinfo.tex build-aux strip-trailing-space
# we generate INSTALL from this via a rule in doc/Makefile.
$AUTOCONF doc/install.texi doc
+$GMP mini-gmp/mini-gmp.c lib
+$GMP mini-gmp/mini-gmp.h lib
+
$GNUSTANDARDS maintain.texi doc strip-trailing-space
$GNUSTANDARDS standards.texi doc strip-trailing-space
$GNUSTANDARDS make-stds.texi doc
diff --git a/config/srclistvars.sh b/config/srclistvars.sh
index ce31637..72cf051 100755
--- a/config/srclistvars.sh
+++ b/config/srclistvars.sh
@@ -27,6 +27,7 @@ esac
# Default to sibling (of parent) directories.
: ${AUTOCONF=../autoconf}
: ${AUTOMAKE=../automake}
+: ${GMP=../gmp}
: ${GNUCONFIG=../config}
: ${GNULIBSRC=../gnulib}
: ${GNUORG=../gnuorg}
diff --git a/doc/glibc-functions/getumask.texi b/doc/glibc-functions/getumask.texi
new file mode 100644
index 0000000..2fbf9e3
--- a/dev/null
+++ b/doc/glibc-functions/getumask.texi
@@ -0,0 +1,30 @@
+@node getumask
+@subsection @code{getumask}
+@findex getumask
+
+Documentation:
+@itemize
+@item
+@ifinfo
+@ref{Setting Permissions,,Assigning File Permissions,libc},
+@end ifinfo
+@ifnotinfo
+@url{https://www.gnu.org/software/libc/manual/html_node/Setting-Permissions.html},
+@end ifnotinfo
+@item
+@uref{https://www.kernel.org/doc/man-pages/online/pages/man3/getumask.3.html,,man getumask}.
+@end itemize
+
+Gnulib module: getumask
+
+Portability problems fixed by Gnulib:
+@itemize
+@item
+This function exists only on Hurd and is therefore
+missing on all non-glibc platforms:
+glibc/Linux, glibc/kFreeBSD, Mac OS X 10.13, FreeBSD 12.0, NetBSD 9.0, OpenBSD 6.7, Minix 3.3, AIX 7.2, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin, mingw, MSVC 14, Android 9.0.
+@end itemize
+
+Portability problems not fixed by Gnulib:
+@itemize
+@end itemize
diff --git a/doc/gnulib.texi b/doc/gnulib.texi
index ec6e633..812e7d0 100644
--- a/doc/gnulib.texi
+++ b/doc/gnulib.texi
@@ -6119,10 +6119,12 @@ This list of functions is sorted according to the header that declares them.
@section Glibc Extensions to @code{<sys/stat.h>}
@menu
+* getumask::
* lchmod::
* statx::
@end menu
+@include glibc-functions/getumask.texi
@include glibc-functions/lchmod.texi
@include glibc-functions/statx.texi
diff --git a/doc/posix-functions/basename.texi b/doc/posix-functions/basename.texi
index e75b510..ebc616e 100644
--- a/doc/posix-functions/basename.texi
+++ b/doc/posix-functions/basename.texi
@@ -23,5 +23,5 @@ version and the GNU version.
names in Windows syntax.
@end itemize
-The Gnulib module @code{dirname} provides similar API, with function
-@code{base_name}, that also works with Windows file names.
+The Gnulib module @code{basename-lgpl} provides similar API, with a function
+@code{last_component}, that also works with Windows file names.
diff --git a/doc/posix-functions/chdir.texi b/doc/posix-functions/chdir.texi
index 9a65578..3963271 100644
--- a/doc/posix-functions/chdir.texi
+++ b/doc/posix-functions/chdir.texi
@@ -4,7 +4,7 @@
POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html}
-Gnulib module: ---
+Gnulib module: chdir
Portability problems fixed by Gnulib:
@itemize
diff --git a/doc/posix-functions/close.texi b/doc/posix-functions/close.texi
index 9f9f66c..58f8e9a 100644
--- a/doc/posix-functions/close.texi
+++ b/doc/posix-functions/close.texi
@@ -9,6 +9,10 @@ Gnulib module: close
Portability problems fixed by Gnulib:
@itemize
@item
+This function is declared in a different header file (namely, @code{<io.h>})
+on some platforms:
+MSVC 14.
+@item
This function crashes when invoked with invalid arguments on some platforms:
MSVC 14.
@item
diff --git a/doc/posix-functions/dup.texi b/doc/posix-functions/dup.texi
index e57a8d5..c43ba8c 100644
--- a/doc/posix-functions/dup.texi
+++ b/doc/posix-functions/dup.texi
@@ -9,6 +9,10 @@ Gnulib module: dup
Portability problems fixed by Gnulib:
@itemize
@item
+This function is declared in a different header file (namely, @code{<io.h>})
+on some platforms:
+MSVC 14.
+@item
This function crashes when invoked with invalid arguments on some platforms:
MSVC 14.
@end itemize
diff --git a/doc/posix-functions/dup2.texi b/doc/posix-functions/dup2.texi
index c7bc212..d81def5 100644
--- a/doc/posix-functions/dup2.texi
+++ b/doc/posix-functions/dup2.texi
@@ -4,11 +4,16 @@
POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup2.html}
-Gnulib module: dup2 or dup2-obsolete
+Gnulib module: dup2
-Portability problems fixed by either Gnulib module @code{dup2} or @code{dup2-obsolete}:
+Portability problems fixed by Gnulib:
@itemize
@item
+This function is declared in a different header file (namely, @code{<io.h>})
+on some platforms:
+MSVC 14.
+
+@item
This function always returns 0 for success on some platforms:
mingw, MSVC 14.
@@ -48,12 +53,6 @@ large targets, which interferes with using
AIX 7.1, FreeBSD 6.1, Cygwin 1.5.
@end itemize
-Portability problems fixed by Gnulib module @code{dup2-obsolete}:
-@itemize
-@item
-This function is missing on some older platforms.
-@end itemize
-
Portability problems not fixed by Gnulib:
@itemize
@end itemize
diff --git a/doc/posix-functions/getcwd.texi b/doc/posix-functions/getcwd.texi
index 5ad8c9d..35b23eb 100644
--- a/doc/posix-functions/getcwd.texi
+++ b/doc/posix-functions/getcwd.texi
@@ -34,8 +34,6 @@ mingw.
Portability problems fixed by Gnulib module @code{getcwd}:
@itemize
@item
-This function is missing on some older platforms.
-@item
This function does not handle long file names (greater than @code{PATH_MAX})
correctly on some platforms:
glibc on Linux 2.4.20, Mac OS X 10.5, FreeBSD 6.4, NetBSD 5.1, OpenBSD 4.9, AIX 7.1.
diff --git a/doc/posix-functions/gethostname.texi b/doc/posix-functions/gethostname.texi
index a7ff3f6..d162cea 100644
--- a/doc/posix-functions/gethostname.texi
+++ b/doc/posix-functions/gethostname.texi
@@ -9,6 +9,10 @@ Gnulib module: gethostname
Portability problems fixed by Gnulib:
@itemize
@item
+This function is declared in a different header file (namely,
+@code{<winsock2.h>}) on some platforms:
+MSVC 14.
+@item
On mingw and MSVC 14, this function has a prototype that differs from that
specified by POSIX, and it is defined only in the ws2_32 library.
@end itemize
diff --git a/doc/posix-functions/isatty.texi b/doc/posix-functions/isatty.texi
index 29eee74..d01f8e3 100644
--- a/doc/posix-functions/isatty.texi
+++ b/doc/posix-functions/isatty.texi
@@ -9,6 +9,10 @@ Gnulib module: isatty
Portability problems fixed by Gnulib:
@itemize
@item
+This function is declared in a different header file (namely, @code{<io.h>})
+on some platforms:
+MSVC 14.
+@item
On native Windows, this function also returns true for character devices such
as @file{NUL}.
@item
diff --git a/doc/posix-functions/lseek.texi b/doc/posix-functions/lseek.texi
index f5f06ce..4a9d55d 100644
--- a/doc/posix-functions/lseek.texi
+++ b/doc/posix-functions/lseek.texi
@@ -9,6 +9,10 @@ Gnulib module: lseek
Portability problems fixed by Gnulib:
@itemize
@item
+This function is declared in a different header file (namely, @code{<io.h>})
+on some platforms:
+MSVC 14.
+@item
On platforms where @code{off_t} is a 32-bit type, @code{lseek} does not work
correctly with files larger than 2 GB@. (Cf. @code{AC_SYS_LARGEFILE}.)
@item
diff --git a/doc/posix-functions/memchr.texi b/doc/posix-functions/memchr.texi
index 38d5b66..5ceb71f 100644
--- a/doc/posix-functions/memchr.texi
+++ b/doc/posix-functions/memchr.texi
@@ -4,9 +4,9 @@
POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/memchr.html}
-Gnulib module: memchr or memchr-obsolete
+Gnulib module: memchr
-Portability problems fixed by either Gnulib module @code{memchr} or @code{memchr-obsolete}:
+Portability problems fixed by Gnulib:
@itemize
@item
This function dereferences too much memory on some platforms:
@@ -17,12 +17,6 @@ of an @code{unsigned char} on some platforms:
Android 5.0.
@end itemize
-Portability problems fixed by Gnulib module @code{memchr-obsolete}:
-@itemize
-@item
-This function is missing on some older platforms.
-@end itemize
-
Portability problems not fixed by Gnulib:
@itemize
@end itemize
diff --git a/doc/posix-functions/memcmp.texi b/doc/posix-functions/memcmp.texi
index 99fb810..204208c 100644
--- a/doc/posix-functions/memcmp.texi
+++ b/doc/posix-functions/memcmp.texi
@@ -9,8 +9,6 @@ Gnulib module: memcmp
Portability problems fixed by Gnulib:
@itemize
@item
-This function is missing on some older platforms.
-@item
This function fails when comparing 16 bytes or more and with at least one
buffer not starting on a 4-byte boundary on some older platforms:
NeXTstep/x86.
diff --git a/doc/posix-functions/memcpy.texi b/doc/posix-functions/memcpy.texi
index 5200f69..ac68a70 100644
--- a/doc/posix-functions/memcpy.texi
+++ b/doc/posix-functions/memcpy.texi
@@ -9,8 +9,6 @@ Gnulib module: memcpy
Portability problems fixed by Gnulib:
@itemize
@item
-This function is missing on some older platforms.
-@item
This function cannot be called from plain inline or extern inline functions
on some platforms:
OS X 10.8.
diff --git a/doc/posix-functions/memmove.texi b/doc/posix-functions/memmove.texi
index 7da46c6..bad1428 100644
--- a/doc/posix-functions/memmove.texi
+++ b/doc/posix-functions/memmove.texi
@@ -9,8 +9,6 @@ Gnulib module: memmove
Portability problems fixed by Gnulib:
@itemize
@item
-This function is missing on some older platforms.
-@item
This function cannot be called from plain inline or extern inline functions
on some platforms:
OS X 10.8.
diff --git a/doc/posix-functions/memset.texi b/doc/posix-functions/memset.texi
index 5e3cc80..6658b09 100644
--- a/doc/posix-functions/memset.texi
+++ b/doc/posix-functions/memset.texi
@@ -9,8 +9,6 @@ Gnulib module: memset
Portability problems fixed by Gnulib:
@itemize
@item
-This function is missing on some older platforms.
-@item
This function cannot be called from plain inline or extern inline functions
on some platforms:
OS X 10.8.
diff --git a/doc/posix-functions/read.texi b/doc/posix-functions/read.texi
index 901470f..a48e5ea 100644
--- a/doc/posix-functions/read.texi
+++ b/doc/posix-functions/read.texi
@@ -4,13 +4,21 @@
POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html}
-Gnulib module: stdio, nonblocking
+Gnulib module: read, stdio, nonblocking
-Portability problems fixed by Gnulib module @code{stdio}, together with module @code{nonblocking}:
+Portability problems fixed by Gnulib module @code{read}:
@itemize
@item
+This function is declared in a different header file (namely, @code{<io.h>})
+on some platforms:
+mingw, MSVC 14.
+@item
This function crashes when invoked with invalid arguments on some platforms:
MSVC 14.
+@end itemize
+
+Portability problems fixed by Gnulib module @code{stdio}, together with module @code{nonblocking}:
+@itemize
@item
When reading from a non-blocking pipe whose buffer is empty, this function
fails with @code{errno} being set to @code{EINVAL} instead of @code{EAGAIN} on
diff --git a/doc/posix-functions/sigprocmask.texi b/doc/posix-functions/sigprocmask.texi
index 20b5405..1a1379a 100644
--- a/doc/posix-functions/sigprocmask.texi
+++ b/doc/posix-functions/sigprocmask.texi
@@ -16,3 +16,10 @@ mingw, MSVC 14.
Portability problems not fixed by Gnulib:
@itemize
@end itemize
+
+Note: Although @code{sigprocmask} officially has undefined behaviour in
+multi-threaded programs, in practice it is essentially equivalent to
+@code{pthread_sigmask}, with only a difference regarding the error
+return convention. It's simpler to use @code{sigprocmask}, since it does
+not require linking with @code{-lpthread} on some platforms:
+glibc, NetBSD, OpenBSD, AIX, IRIX.
diff --git a/doc/posix-functions/unlink.texi b/doc/posix-functions/unlink.texi
index 4ee0003..15cfd84 100644
--- a/doc/posix-functions/unlink.texi
+++ b/doc/posix-functions/unlink.texi
@@ -9,6 +9,10 @@ Gnulib module: unlink
Portability problems fixed by Gnulib:
@itemize
@item
+This function is declared in a different header file (namely, @code{<stdio.h>})
+on some platforms:
+MSVC 14.
+@item
Some systems mistakenly succeed on @code{unlink("link-to-file/")}:
GNU/Hurd, FreeBSD 7.2, AIX 7.1, Solaris 9.
@item
diff --git a/doc/posix-functions/write.texi b/doc/posix-functions/write.texi
index 1ff9021..e3042a5 100644
--- a/doc/posix-functions/write.texi
+++ b/doc/posix-functions/write.texi
@@ -6,11 +6,19 @@ POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/fun
Gnulib module: write, nonblocking, sigpipe
-Portability problems fixed by Gnulib module @code{stdio}, together with module @code{nonblocking}:
+Portability problems fixed by Gnulib module @code{write}:
@itemize
@item
+This function is declared in a different header file (namely, @code{<io.h>})
+on some platforms:
+mingw, MSVC 14.
+@item
This function crashes when invoked with invalid arguments on some platforms:
MSVC 14.
+@end itemize
+
+Portability problems fixed by Gnulib module @code{stdio}, together with module @code{nonblocking}:
+@itemize
@item
When writing to a non-blocking pipe whose buffer is full, this function fails
with @code{errno} being set to @code{ENOSPC} instead of @code{EAGAIN} on some
diff --git a/doc/standards.texi b/doc/standards.texi
index c7021fd..1826fd3 100644
--- a/doc/standards.texi
+++ b/doc/standards.texi
@@ -3,7 +3,7 @@
@setfilename standards.info
@settitle GNU Coding Standards
@c This date is automagically updated when you save this file:
-@set lastupdate April 24, 2020
+@set lastupdate June 12, 2020
@c %**end of header
@dircategory GNU organization
@@ -28,8 +28,8 @@ The GNU coding standards, last updated @value{lastupdate}.
Copyright @copyright{} 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Free Software
-Foundation, Inc.
+2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Free
+Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -2959,7 +2959,7 @@ Avoid using the format of semi-internal data bases (e.g., directories)
when there is a higher-level alternative (@code{readdir}).
@cindex non-POSIX systems, and portability
-As for systems that are not like Unix, such as MSDOS, Windows, VMS, MVS,
+As for systems that are not like Unix, such as MS-DOS, Windows, VMS, MVS,
and older Macintosh systems, supporting them is often a lot of work.
When that is the case, it is better to spend your time adding features
that will be useful on GNU and GNU/Linux, rather than on supporting
@@ -3635,7 +3635,76 @@ future will know about the changes that might have introduced the bug.
Often a new bug can be found by looking at what was recently changed.
More importantly, change logs can help you eliminate conceptual
inconsistencies between different parts of a program, by giving you a
-history of how the conflicting concepts arose and who they came from.
+history of how the conflicting concepts arose, who they came from, and
+why the conflicting changes were made.
+
+@cindex software forensics, and change logs
+Therefore, change logs should be detailed enough and accurate enough
+to provide the information commonly required for such @dfn{software
+forensics}. Specifically, change logs should make finding answers to
+the following questions easy:
+
+@itemize @bullet
+@item
+What changes affected a particular source file?
+
+@item
+Was a particular source file renamed or moved, and if so, as part of
+what change?
+
+@item
+What changes affected a given function or macro or definition of a
+data structure?
+
+@item
+Was a function (or a macro or the definition of a data structure)
+renamed or moved from another file, and if so, as part of which
+change?
+
+@item
+What changes deleted a function (or macro or data structure)?
+
+@item
+What was the rationale for a given change, and what were its main
+ideas?
+
+@item
+Is there any additional information regarding the change, and if so,
+where can it be found?
+@end itemize
+
+@cindex VCS
+@cindex version control system, for keeping change logs
+Historically, change logs were maintained on specially formatted
+files. Nowadays, projects commonly keep their source files under a
+@dfn{version control system} (VCS), such as Git,
+Subversion, or Mercurial. If the VCS repository is publicly
+accessible, and changes are committed to it separately (one commit for
+each logical changeset) and record the authors of each change, then
+the information recorded by the VCS can be used to produce
+the change logs out of VCS logs, and to answer the above
+questions by using the suitable VCS commands. (However, the
+VCS log messages still need to provide some supporting
+information, as described below.) Projects that maintain such
+VCS repositories can decide not to maintain separate change
+log files, and instead rely on the VCS to keep the change
+logs.
+
+If you decide not to maintain separate change log files, you should
+still consider providing them in the release tarballs, for the benefit
+of users who'd like to review the change logs without accessing the
+project's VCS repository. Scripts exist that can produce
+@file{ChangeLog} files from the VCS logs; for example, the
+@file{gitlog-to-changelog} script, which is part of Gnulib, can do
+that for Git repositories. In Emacs, the command @kbd{C-x v a}
+(@code{vc-update-change-log}) does the job of incrementally updating a
+@file{ChangeLog} file from the VCS logs.
+
+If separate change log files @emph{are} maintained, they are normally
+called @file{ChangeLog}, and each such file covers an entire
+directory. Each directory can have its own change log file, or a
+directory can use the change log of its parent directory---it's up to
+you.
@menu
* Change Log Concepts::
@@ -3646,39 +3715,41 @@ history of how the conflicting concepts arose and who they came from.
@end menu
@node Change Log Concepts
-@subsection Change Log Concepts
+@subsection Change Log Concepts and Conventions
-@cindex change set
-@cindex batch of changes
+@cindex changeset, in a change log
+@cindex batch of changes, in a change log
You can think of the change log as a conceptual ``undo list'' which
states how earlier versions were different from the current version.
People can see the current version; they don't need the change log to
tell them what is in it. What they want from a change log is a clear
explanation of how the earlier version differed. Each @dfn{entry} in
a change log describes either an individual change or the smallest
-batch of changes that belong together, also known as a @dfn{change
-set}.
+batch of changes that belong together, also known as a @dfn{changeset}.
@cindex title, change log entry
-@cindex description, change log entry
-It is a good idea to start the change log entry with a description
-of the overall change. This should be as long as needed to give
-a clear description.
-
-Then give a list of names of the entities or definitions that you
-changed, according to the files they are in, and what was changed
-in each one. @xref{Style of Change Logs}.
-
-The change log file is normally called @file{ChangeLog} and covers an
-entire directory. Each directory can have its own change log, or a
-directory can use the change log of its parent directory---it's up to
-you.
+@cindex header line, change log entry
+It is a good idea to start the change log entry with a @dfn{header
+line}: a single line that is a complete sentence which summarizes the
+changeset. If you keep the change log in a VCS, this
+should be a requirement, as VCS commands that show the
+change log in abbreviated form, such as @kbd{git log --oneline}, treat
+the header line specially. (In a @file{ChangeLog} file, the header
+line follows a line that says who was the author of the change and
+when it was installed.)
-Instead of using a file named @file{ChangeLog}, you can record the
-change log information as log entries in a version control system such
-as RCS or CVS@. This can be converted automatically to a
-@file{ChangeLog} file using @code{rcs2log}; in Emacs, the command
-@kbd{C-x v a} (@code{vc-update-change-log}) does the job.
+@cindex description, change log entry
+Follow the change log entry's header line with a description of the
+overall change. This should be as long as needed to give a clear
+description. Pay special attention to aspects of the changeset not
+easily gleaned from the diffs or from the names of modified files and
+functions: the overall idea of the change and the need for it, and the
+relations, if any, between changes made to different files/functions.
+If the change or its reasons were discussed on some public forum, such
+as the project's issue tracker or mailing list, it is a good idea to
+summarize the main points of that discussion in the change's
+description, and include a pointer to that discussion or the issue ID
+for those who'd like to read it in full.
The best place to explain how parts of the new code work with other code
is in comments in the code, not in the change log.
@@ -3693,14 +3764,78 @@ tree.'' (Though such a simple reason would not need this kind of
explanation.)
The best place for other kinds of explanation of the change is in the
-change log entry.
+change log entry. In particular, comments usually will not say why
+some code was deleted or moved to another place---that belongs to the
+description of the change which did that.
+
+Following the free-text description of the change, it is a good idea
+to give a list of names of the entities or definitions that you
+changed, according to the files they are in, and what was changed in
+each one. @xref{Style of Change Logs}. If a project uses a modern
+VCS to keep the change log information, as described in
+@ref{Change Logs}, explicitly listing the files and functions that
+were changed is not strictly necessary, and in some cases (like
+identical mechanical changes in many places) even tedious. It is up
+to you to decide whether to allow your project's developers to omit
+the list of changed files and functions from the log entries, and
+whether to allow such omissions under some specific conditions.
+However, while making this decision, please consider the following
+benefits of providing the list of changed entities with each change:
+
+@itemize @bullet
+@item
+Generation of useful @file{ChangeLog} files from VCS logs
+becomes more difficult if the change log entries don't list the
+modified functions/macros, because VCS commands cannot
+reliably reproduce their names from the commit information alone. For
+example, when there is a change in the header part of a function
+definition, the heading of the diff hunk as shown in the VCS log
+commands will name the wrong function as being modified (usually, the
+function defined before the one being modified), so using those diffs
+to glean the names of the modified functions will produce inaccurate
+results. You will need to use specialized scripts, such as gnulib's
+@file{vcs-to-changelog.py}, mentioned below, to solve these
+difficulties, and make sure it supports the source languages used by
+your project.
+
+@item
+While modern VCS commands, such as Git's @kbd{git log -L}
+and @kbd{git log -G}, provide powerful means for finding changes that
+affected a certain function or macro or data structure (and thus might
+make @file{ChangeLog} files unnecessary if you have the repository
+available), they can sometimes fail. For example, @kbd{git log -L}
+doesn't support syntax of some programming languages out of the box.
+Mentioning the modified functions/macros explicitly allows finding the
+related changes simply and reliably.
+
+@item
+Some VCS commands have difficulties or limitations when
+tracking changes across file moves or renames. Again, if the entities
+are mentioned explicitly, those difficulties can be overcome.
+
+@item
+Users that review changes using the generated @file{ChangeLog} files
+may not have the repository and the VCS commands available
+to them. Naming the modified entities alleviates that problem.
+@end itemize
-The easiest way to add an entry to @file{ChangeLog} is with the Emacs
-command @kbd{M-x add-change-log-entry}. An individual change should
-have an asterisk, the name of the changed file, and then in
-parentheses the name of the changed functions, variables or whatever,
-followed by a colon. Then describe the changes you made to that
-function or variable.
+@noindent
+For these reasons, providing lists of modified files and functions
+with each change makes the change logs more useful, and we therefore
+recommend to include them whenever possible and practical.
+
+It is also possible to generate the lists naming the modified entities
+by running a script. One such script is @file{mklog.py} (written in
+Python 3); it is used by the @code{GCC} project. Gnulib provides
+another variant of such a script, called @file{vcs-to-changelog.py},
+part of the @code{vcs-to-changelog} module. Note that these scripts
+currently support fewer programming languages than the manual commands
+provided by Emacs (@pxref{Style of Change Logs}). Therefore, the
+above mentioned method of generating the @code{ChangeLog} file from
+the VCS commit history, for instance via the
+@code{gitlog-to-changelog} script, usually gives better
+results---provided that the contributors stick to providing good
+commit messages.
@node Style of Change Logs
@subsection Style of Change Logs
@@ -3709,30 +3844,47 @@ function or variable.
Here are some simple examples of change log entries, starting with the
header line that says who made the change and when it was installed,
followed by descriptions of specific changes. (These examples are
-drawn from Emacs and GCC.)
+drawn from Emacs.) Keep in mind that the line which shows the date of
+the change and the author's name and email address is needed only in a
+separate @file{ChangeLog} file, not when the change logs are kept in a
+VCS.
@example
-1998-08-17 Richard Stallman <rms@@gnu.org>
+2019-08-29 Noam Postavsky <npostavs@@gmail.com>
+
+ Handle completely undecoded input in term (Bug#29918)
+
+ * lisp/term.el (term-emulate-terminal): Avoid errors if the whole
+ decoded string is eight-bit characters. Don't attempt to save the
+ string for next iteration in that case.
+ * test/lisp/term-tests.el (term-decode-partial)
+ (term-undecodable-input): New tests.
+
+2019-06-15 Paul Eggert <eggert@@cs.ucla.edu>
+
+ Port to platforms where tputs is in libtinfow
-* register.el (insert-register): Return nil.
-(jump-to-register): Likewise.
+ * configure.ac (tputs_library): Also try tinfow, ncursesw (Bug#33977).
-* sort.el (sort-subr): Return nil.
+2019-02-08 Eli Zaretskii <eliz@@gnu.org>
-* tex-mode.el (tex-bibtex-file, tex-file, tex-region):
-Restart the tex shell if process is gone or stopped.
-(tex-shell-running): New function.
+ Improve documentation of 'date-to-time' and 'parse-time-string'
-* expr.c (store_one_arg): Round size up for move_block_to_reg.
-(expand_call): Round up when emitting USE insns.
-* stmt.c (assign_parms): Round size up for move_block_from_reg.
+ * doc/lispref/os.texi (Time Parsing): Document
+ 'parse-time-string', and refer to it for the description of
+ the argument of 'date-to-time'.
+
+ * lisp/calendar/time-date.el (date-to-time): Refer in the doc
+ string to 'parse-time-string' for more information about the
+ format of the DATE argument. (Bug#34303)
@end example
-It's important to name the changed function or variable in full. Don't
-abbreviate function or variable names, and don't combine them.
-Subsequent maintainers will often search for a function name to find all
-the change log entries that pertain to it; if you abbreviate the name,
-they won't find it when they search.
+If you mention the names of the modified functions or variables, it's
+important to name them in full. Don't abbreviate function or variable
+names, and don't combine them. Subsequent maintainers will often
+search for a function name to find all the change log entries that
+pertain to it; if you abbreviate the name, they won't find it when
+they search.
For example, some people are tempted to abbreviate groups of function
names by writing @samp{* register.el (@{insert,jump-to@}-register)};
@@ -3746,13 +3898,22 @@ the same file.
Break long lists of function names by closing continued lines with
@samp{)}, rather than @samp{,}, and opening the continuation with
-@samp{(} as in this example:
+@samp{(}. This makes highlighting in Emacs work better.
+Here is an example:
@example
-* keyboard.c (menu_bar_items, tool_bar_items)
+* src/keyboard.c (menu_bar_items, tool_bar_items)
(Fexecute_extended_command): Deal with 'keymap' property.
@end example
+The easiest way to add an entry to @file{ChangeLog} is with the Emacs
+command @kbd{M-x add-change-log-entry}, or its variant @kbd{C-x 4 a}
+(@code{add-change-log-entry-other-window}). This automatically
+collects the name of the changed file and the changed function or
+variable, and formats a change log entry according to the conventions
+described above, leaving it up to you to describe the changes you made
+to that function or variable.
+
When you install someone else's changes, put the contributor's name in
the change log entry rather than in the text of the entry. In other
words, write this:
@@ -3772,7 +3933,24 @@ rather than this:
* sewing.c: Make it sew. Patch by jdoe@@gnu.org.
@end example
+When committing someone else's changes into a VCS, use the
+VCS features to specify the author. For example, with Git,
+use @kbd{git commit --author=@var{author}}.
+
As for the date, that should be the date you applied the change.
+(With a VCS, use the appropriate command-line switches,
+e.g., @kbd{git commit --date=@var{date}}.)
+
+Modern VCS have commands to apply changes sent via email
+(e.g., Git has @kbd{git am}); in that case the author of the changeset
+and the date it was made will be automatically gleaned from the email
+message and recorded in the repository. If the patches are prepared
+with suitable VCS commands, such as @kbd{git format-patch},
+the email message body will also have the original author of the
+changeset, so resending or forwarding the message will not interfere
+with attributing the changes to their author. Thus, we recommend that
+you request your contributors to use commands such as @kbd{git
+format-patch} to prepare the patches.
@node Simple Changes
@subsection Simple Changes
@@ -3780,6 +3958,15 @@ As for the date, that should be the date you applied the change.
Certain simple kinds of changes don't need much detail in the change
log.
+If the description of the change is short enough, it can serve as its
+own header line:
+
+@example
+2019-08-29 Eli Zaretskii <eliz@@gnu.org>
+
+ * lisp/simple.el (kill-do-not-save-duplicates): Doc fix. (Bug#36827)
+@end example
+
When you change the calling sequence of a function in a simple fashion,
and you change all the callers of the function to use the new calling
sequence, there is no need to make individual entries for all the
@@ -3795,6 +3982,22 @@ When you change just comments or doc strings, it is enough to write an
entry for the file, without mentioning the functions. Just ``Doc
fixes'' is enough for the change log.
+When you make changes in many files that follow mechanically from one
+underlying change, it is enough to describe the underlying change.
+Here's an example of a change that affects all of the files in the
+repository:
+
+@example
+2019-01-07 Paul Eggert <eggert@@cs.ucla.edu>
+
+ Update copyright year to 2019
+
+ Run 'TZ=UTC0 admin/update-copyright $(git ls-files)'.
+@end example
+
+Test suite files are part of the software, so we recommend treating
+them as code for change-log purposes.
+
There's no technical need to make change log entries for non-software
files (manuals, help files, media files, etc.). This is because they
are not susceptible to bugs that are hard to understand. To correct
@@ -3803,7 +4006,9 @@ is enough to compare what the file says with the actual facts.
However, you should keep change logs for non-software files when the
project gets copyright assignments from its contributors, so as to
-make the records of authorship more accurate.
+make the records of authorship more accurate. For that reason, we
+recommend to keep change logs for Texinfo sources of your project's
+manuals.
@node Conditional Changes
@subsection Conditional Changes
diff --git a/lib/alloca.in.h b/lib/alloca.in.h
index 73516ca..beb022c 100644
--- a/lib/alloca.in.h
+++ b/lib/alloca.in.h
@@ -35,13 +35,16 @@
*/
#ifndef alloca
+ /* Some version of mingw have an <alloca.h> that causes trouble when
+ included after 'alloca' gets defined as a macro. As a workaround,
+ include this <alloca.h> first and define 'alloca' as a macro afterwards
+ if needed. */
+# if defined __GNUC__ && (defined _WIN32 && ! defined __CYGWIN__) && @HAVE_ALLOCA_H@
+# include_next <alloca.h>
+# endif
+#endif
+#ifndef alloca
# ifdef __GNUC__
- /* Some version of mingw have an <alloca.h> that causes trouble when
- included after 'alloca' gets defined as a macro. As a workaround, include
- this <alloca.h> first and define 'alloca' as a macro afterwards. */
-# if (defined _WIN32 && ! defined __CYGWIN__) && @HAVE_ALLOCA_H@
-# include_next <alloca.h>
-# endif
# define alloca __builtin_alloca
# elif defined _AIX
# define alloca __alloca
diff --git a/lib/areadlink-with-size.c b/lib/areadlink-with-size.c
index d177009..86ddd60 100644
--- a/lib/areadlink-with-size.c
+++ b/lib/areadlink-with-size.c
@@ -79,7 +79,10 @@ areadlink_with_size (char const *file, size_t size)
{
buf = buffer = malloc (buf_size);
if (!buffer)
- return NULL;
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
}
r = readlink (file, buf, buf_size);
diff --git a/lib/areadlinkat-with-size.c b/lib/areadlinkat-with-size.c
index 142b9dc..b41a5e0 100644
--- a/lib/areadlinkat-with-size.c
+++ b/lib/areadlinkat-with-size.c
@@ -84,6 +84,8 @@ areadlinkat_with_size (int fd, char const *file, size_t size)
{
buf = buffer = malloc (buf_size);
if (!buffer)
+ /* We can assume errno == ENOMEM here, since all platforms that have
+ readlinkat() have a POSIX compliant malloc(). */
return NULL;
}
diff --git a/lib/argp-namefrob.h b/lib/argp-namefrob.h
index 022b880..50c62e9 100644
--- a/lib/argp-namefrob.h
+++ b/lib/argp-namefrob.h
@@ -146,7 +146,7 @@
#elif defined GNULIB_ARGP_EXTERN_BASENAME
extern char *__argp_base_name (const char *arg);
#else
-# include "dirname.h"
+# include "basename-lgpl.h"
# define __argp_base_name last_component
#endif
diff --git a/lib/asyncsafe-spin.c b/lib/asyncsafe-spin.c
new file mode 100644
index 0000000..6ea5781
--- a/dev/null
+++ b/lib/asyncsafe-spin.c
@@ -0,0 +1,340 @@
+/* Spin locks for communication between threads and signal handlers.
+ Copyright (C) 2020 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 2, 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>, 2020. */
+
+#include <config.h>
+
+/* Specification. */
+#include "asyncsafe-spin.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#if defined _AIX
+# include <sys/atomic_op.h>
+#endif
+
+#if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads. */
+
+void
+asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
+{
+ glwthread_spin_init (lock);
+}
+
+static inline void
+do_lock (asyncsafe_spinlock_t *lock)
+{
+ glwthread_spin_lock (lock);
+}
+
+static inline void
+do_unlock (asyncsafe_spinlock_t *lock)
+{
+ if (glwthread_spin_unlock (lock))
+ abort ();
+}
+
+void
+asyncsafe_spin_destroy (asyncsafe_spinlock_t *lock)
+{
+ glwthread_spin_destroy (lock);
+}
+
+#else
+
+# if HAVE_PTHREAD_H
+/* Use POSIX threads. */
+
+/* We don't use semaphores (although sem_post() is allowed in signal handlers),
+ because it would require to link with -lrt on HP-UX 11, OSF/1, Solaris 10,
+ and also because on macOS only named semaphores work.
+
+ We don't use the C11 <stdatomic.h> (available in GCC >= 4.9) because it would
+ require to link with -latomic. */
+
+# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && !defined __ibmxl__
+/* Use GCC built-ins (available in GCC >= 4.7) that operate on the first byte of
+ the lock.
+ Documentation:
+ <https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/_005f_005fatomic-Builtins.html>
+ */
+
+# if 1
+/* An implementation that verifies the unlocks. */
+
+void
+asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
+{
+ __atomic_store_n (lock, 0, __ATOMIC_SEQ_CST);
+}
+
+static inline void
+do_lock (asyncsafe_spinlock_t *lock)
+{
+ /* Wait until *lock becomes 0, then replace it with 1. */
+ asyncsafe_spinlock_t zero;
+ while (!(zero = 0,
+ __atomic_compare_exchange_n (lock, &zero, 1, false,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)))
+ ;
+}
+
+static inline void
+do_unlock (asyncsafe_spinlock_t *lock)
+{
+ /* If *lock is 1, then replace it with 0. */
+ asyncsafe_spinlock_t one = 1;
+ if (!__atomic_compare_exchange_n (lock, &one, 0, false,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+ abort ();
+}
+
+# else
+/* An implementation that is a little bit more optimized, but does not verify
+ the unlocks. */
+
+void
+asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
+{
+ __atomic_clear (lock, __ATOMIC_SEQ_CST);
+}
+
+static inline void
+do_lock (asyncsafe_spinlock_t *lock)
+{
+ while (__atomic_test_and_set (lock, __ATOMIC_SEQ_CST))
+ ;
+}
+
+static inline void
+do_unlock (asyncsafe_spinlock_t *lock)
+{
+ __atomic_clear (lock, __ATOMIC_SEQ_CST);
+}
+
+# endif
+
+# elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined __ibmxl__
+/* Use GCC built-ins (available in GCC >= 4.1).
+ Documentation:
+ <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html> */
+
+void
+asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
+{
+ volatile unsigned int *vp = lock;
+ *vp = 0;
+ __sync_synchronize ();
+}
+
+static inline void
+do_lock (asyncsafe_spinlock_t *lock)
+{
+ /* Wait until *lock becomes 0, then replace it with 1. */
+ while (__sync_val_compare_and_swap (lock, 0, 1) != 0)
+ ;
+}
+
+static inline void
+do_unlock (asyncsafe_spinlock_t *lock)
+{
+ /* If *lock is 1, then replace it with 0. */
+ if (__sync_val_compare_and_swap (lock, 1, 0) != 1)
+ abort ();
+}
+
+# elif defined _AIX
+/* AIX */
+
+void
+asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
+{
+ atomic_p vp = (int *) lock;
+ _clear_lock (vp, 0);
+}
+
+static inline void
+do_lock (asyncsafe_spinlock_t *lock)
+{
+ atomic_p vp = (int *) lock;
+ while (_check_lock (vp, 0, 1))
+ ;
+}
+
+static inline void
+do_unlock (asyncsafe_spinlock_t *lock)
+{
+ atomic_p vp = (int *) lock;
+ if (_check_lock (vp, 1, 0))
+ abort ();
+}
+
+# elif (defined __GNUC__ || defined __SUNPRO_C) && (defined __sparc || defined __i386 || defined __x86_64__)
+/* For older versions of GCC, use inline assembly.
+ GCC and the Oracle Studio C 12 compiler understand GCC's extended asm syntax,
+ but the plain Oracle Studio C 11 compiler understands only simple asm. */
+/* An implementation that verifies the unlocks. */
+
+static void
+memory_barrier (void)
+{
+# if defined __GNUC__ || __SUNPRO_C >= 0x590
+# if defined __i386 || defined __x86_64__
+ asm volatile ("mfence");
+# endif
+# if defined __sparc
+ asm volatile ("membar 2");
+# endif
+# else
+# if defined __i386 || defined __x86_64__
+ asm ("mfence");
+# endif
+# if defined __sparc
+ asm ("membar 2");
+# endif
+# endif
+}
+
+/* Store NEWVAL in *VP if the old value *VP is == CMP.
+ Return the old value. */
+static unsigned int
+atomic_compare_and_swap (volatile unsigned int *vp, unsigned int cmp,
+ unsigned int newval)
+{
+# if defined __GNUC__ || __SUNPRO_C >= 0x590
+ unsigned int oldval;
+# if defined __i386 || defined __x86_64__
+ asm volatile (" lock\n cmpxchgl %3,(%1)"
+ : "=a" (oldval) : "r" (vp), "a" (cmp), "r" (newval) : "memory");
+# endif
+# if defined __sparc
+ asm volatile (" cas [%1],%2,%3\n"
+ " mov %3,%0"
+ : "=r" (oldval) : "r" (vp), "r" (cmp), "r" (newval) : "memory");
+# endif
+ return oldval;
+# else /* __SUNPRO_C */
+# if defined __x86_64__
+ asm (" movl %esi,%eax\n"
+ " lock\n cmpxchgl %edx,(%rdi)");
+# elif defined __i386
+ asm (" movl 16(%ebp),%ecx\n"
+ " movl 12(%ebp),%eax\n"
+ " movl 8(%ebp),%edx\n"
+ " lock\n cmpxchgl %ecx,(%edx)");
+# endif
+# if defined __sparc
+ asm (" cas [%i0],%i1,%i2\n"
+ " mov %i2,%i0");
+# endif
+# endif
+}
+
+void
+asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
+{
+ volatile unsigned int *vp = lock;
+ *vp = 0;
+ memory_barrier ();
+}
+
+static inline void
+do_lock (asyncsafe_spinlock_t *lock)
+{
+ volatile unsigned int *vp = lock;
+ while (atomic_compare_and_swap (vp, 0, 1) != 0)
+ ;
+}
+
+static inline void
+do_unlock (asyncsafe_spinlock_t *lock)
+{
+ volatile unsigned int *vp = lock;
+ if (atomic_compare_and_swap (vp, 1, 0) != 1)
+ abort ();
+}
+
+# else
+/* Fallback code. It has some race conditions. */
+
+void
+asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
+{
+ volatile unsigned int *vp = lock;
+ *vp = 0;
+}
+
+static inline void
+do_lock (asyncsafe_spinlock_t *lock)
+{
+ volatile unsigned int *vp = lock;
+ while (*vp)
+ ;
+ *vp = 1;
+}
+
+static inline void
+do_unlock (asyncsafe_spinlock_t *lock)
+{
+ volatile unsigned int *vp = lock;
+ *vp = 0;
+}
+
+# endif
+
+# else
+/* Provide a dummy implementation for single-threaded applications. */
+
+void
+asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
+{
+}
+
+static inline void
+do_lock (asyncsafe_spinlock_t *lock)
+{
+}
+
+static inline void
+do_unlock (asyncsafe_spinlock_t *lock)
+{
+}
+
+# endif
+
+void
+asyncsafe_spin_destroy (asyncsafe_spinlock_t *lock)
+{
+}
+
+#endif
+
+void
+asyncsafe_spin_lock (asyncsafe_spinlock_t *lock,
+ const sigset_t *mask, sigset_t *saved_mask)
+{
+ sigprocmask (SIG_BLOCK, mask, saved_mask); /* equivalent to pthread_sigmask */
+ do_lock (lock);
+}
+
+void
+asyncsafe_spin_unlock (asyncsafe_spinlock_t *lock, const sigset_t *saved_mask)
+{
+ do_unlock (lock);
+ sigprocmask (SIG_SETMASK, saved_mask, NULL); /* equivalent to pthread_sigmask */
+}
diff --git a/lib/asyncsafe-spin.h b/lib/asyncsafe-spin.h
new file mode 100644
index 0000000..7e3693f
--- a/dev/null
+++ b/lib/asyncsafe-spin.h
@@ -0,0 +1,69 @@
+/* Spin locks for communication between threads and signal handlers.
+ Copyright (C) 2020 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 2, 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>, 2020. */
+
+#ifndef _ASYNCSAFE_SPIN_H
+#define _ASYNCSAFE_SPIN_H
+
+/* Usual spin locks are not allowed for communication between threads and signal
+ handlers, because the pthread_spin_* functions are not async-safe; see
+ <https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html>
+ section 2.4.3 Signal Actions.
+
+ This module provides spin locks with a similar API. It can be used like this,
+ both in regular multithreaded code and in signal handlers:
+
+ sigset_t saved_mask;
+ asyncsafe_spin_lock (&lock, &mask, &saved_mask);
+ do_something_contentious ();
+ asyncsafe_spin_unlock (&lock, &saved_mask);
+
+ The mask you specify here is the set of signals whose handlers might want to
+ take the same lock.
+
+ asyncsafe_spin_lock/unlock use pthread_sigmask, to ensure that while a thread
+ is executing such code, no signal handler will start such code for the same
+ lock *in the same thread* (because if this happened, the signal handler would
+ hang!). */
+
+#include <signal.h>
+
+#if defined _WIN32 && ! defined __CYGWIN__
+# include "windows-spin.h"
+typedef glwthread_spinlock_t asyncsafe_spinlock_t;
+# define ASYNCSAFE_SPIN_INIT GLWTHREAD_SPIN_INIT
+#else
+typedef unsigned int asyncsafe_spinlock_t;
+# define ASYNCSAFE_SPIN_INIT 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void asyncsafe_spin_init (asyncsafe_spinlock_t *lock);
+extern void asyncsafe_spin_lock (asyncsafe_spinlock_t *lock,
+ const sigset_t *mask, sigset_t *saved_mask);
+extern void asyncsafe_spin_unlock (asyncsafe_spinlock_t *lock,
+ const sigset_t *saved_mask);
+extern void asyncsafe_spin_destroy (asyncsafe_spinlock_t *lock);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ASYNCSAFE_SPIN_H */
diff --git a/lib/backupfile.c b/lib/backupfile.c
index 7edd9c3..2a88248 100644
--- a/lib/backupfile.c
+++ b/lib/backupfile.c
@@ -23,7 +23,7 @@
#include "backup-internal.h"
#include "attribute.h"
-#include "dirname.h"
+#include "basename-lgpl.h"
#include "opendirat.h"
#include "renameatu.h"
#include "xalloc-oversized.h"
diff --git a/lib/basename-lgpl.c b/lib/basename-lgpl.c
index 565469e..6bb28d4 100644
--- a/lib/basename-lgpl.c
+++ b/lib/basename-lgpl.c
@@ -18,20 +18,20 @@
#include <config.h>
-#include "dirname.h"
+/* Specification. */
+#include "basename-lgpl.h"
+#include <stdbool.h>
#include <string.h>
-/* Return the address of the last file name component of NAME. If
- NAME has no relative file name components because it is a file
- system root, return the empty string. */
+#include "filename.h"
char *
last_component (char const *name)
{
char const *base = name + FILE_SYSTEM_PREFIX_LEN (name);
char const *p;
- bool saw_slash = false;
+ bool last_was_slash = false;
while (ISSLASH (*base))
base++;
@@ -39,21 +39,17 @@ last_component (char const *name)
for (p = base; *p; p++)
{
if (ISSLASH (*p))
- saw_slash = true;
- else if (saw_slash)
+ last_was_slash = true;
+ else if (last_was_slash)
{
base = p;
- saw_slash = false;
+ last_was_slash = false;
}
}
return (char *) base;
}
-/* Return the length of the basename NAME. Typically NAME is the
- value returned by base_name or last_component. Act like strlen
- (NAME), except omit all trailing slashes. */
-
size_t
base_len (char const *name)
{
diff --git a/lib/basename-lgpl.h b/lib/basename-lgpl.h
new file mode 100644
index 0000000..3807094
--- a/dev/null
+++ b/lib/basename-lgpl.h
@@ -0,0 +1,78 @@
+/* Extract the last component (base name) of a file name.
+
+ Copyright (C) 1998, 2001, 2003-2006, 2009-2020 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/>. */
+
+#ifndef _BASENAME_LGPL_H
+#define _BASENAME_LGPL_H
+
+#include <stddef.h>
+
+#ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
+# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Return the address of the last file name component of FILENAME.
+ If FILENAME has some trailing slash(es), they are considered to be
+ part of the last component.
+ If FILENAME has no relative file name components because it is a file
+ system root, return the empty string.
+ Examples:
+ FILENAME RESULT
+ "foo.c" "foo.c"
+ "foo/bar.c" "bar.c"
+ "/foo/bar.c" "bar.c"
+ "foo/bar/" "bar/"
+ "foo/bar//" "bar//"
+ "/" ""
+ "//" ""
+ "" ""
+ The return value is a tail of the given FILENAME; do NOT free() it! */
+
+/* This function was traditionally called 'basename', but we avoid this
+ function name because
+ * Various platforms have different functions in their libc.
+ In particular, the glibc basename(), defined in <string.h>, does
+ not consider trailing slashes to be part of the component:
+ FILENAME RESULT
+ "foo/bar/" ""
+ "foo/bar//" ""
+ * The 'basename' command eliminates trailing slashes and for a root
+ produces a non-empty result:
+ FILENAME RESULT
+ "foo/bar/" "bar"
+ "foo/bar//" "bar"
+ "/" "/"
+ "//" "/"
+ */
+extern char *last_component (char const *filename) _GL_ATTRIBUTE_PURE;
+
+/* Return the length of the basename FILENAME.
+ Typically FILENAME is the value returned by base_name or last_component.
+ Act like strlen (FILENAME), except omit all trailing slashes. */
+extern size_t base_len (char const *filename) _GL_ATTRIBUTE_PURE;
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* _BASENAME_LGPL_H */
diff --git a/lib/c-dtoastr.c b/lib/c-dtoastr.c
new file mode 100644
index 0000000..b57524f
--- a/dev/null
+++ b/lib/c-dtoastr.c
@@ -0,0 +1,3 @@
+#define LENGTH 2
+#define C_LOCALE 1
+#include "ftoastr.c"
diff --git a/lib/c-ldtoastr.c b/lib/c-ldtoastr.c
new file mode 100644
index 0000000..5446fc3
--- a/dev/null
+++ b/lib/c-ldtoastr.c
@@ -0,0 +1,3 @@
+#define LENGTH 3
+#define C_LOCALE 1
+#include "ftoastr.c"
diff --git a/lib/canonicalize.h b/lib/canonicalize.h
index 950d117..1815297 100644
--- a/lib/canonicalize.h
+++ b/lib/canonicalize.h
@@ -46,7 +46,8 @@ typedef enum canonicalize_mode_t canonicalize_mode_t;
does not contain any `.', `..' components nor any repeated file name
separators ('/') or, depending on other CAN_MODE flags, symlinks.
Whether components must exist or not depends on canonicalize mode.
- The result is malloc'd. */
+ The result is malloc'd.
+ Upon failure, return NULL with errno set. */
char *canonicalize_filename_mode (const char *, canonicalize_mode_t);
#ifdef __cplusplus
diff --git a/lib/clean-temp.c b/lib/clean-temp.c
index c57d658..34ebb9b 100644
--- a/lib/clean-temp.c
+++ b/lib/clean-temp.c
@@ -38,13 +38,19 @@
#include "error.h"
#include "fatal-signal.h"
+#include "asyncsafe-spin.h"
#include "pathmax.h"
#include "tmpdir.h"
#include "xalloc.h"
#include "xmalloca.h"
+#include "glthread/lock.h"
#include "gl_xlist.h"
#include "gl_linkedhash_list.h"
+#include "gl_linked_list.h"
#include "gettext.h"
+#if GNULIB_TEMPNAME
+# include "tempname.h"
+#endif
#if GNULIB_FWRITEERROR
# include "fwriteerror.h"
#endif
@@ -81,6 +87,14 @@
signal handler can rely on these field values to be up to date. */
+/* Lock that protects the file_cleanup_list from concurrent modification in
+ different threads. */
+gl_lock_define_initialized (static, file_cleanup_list_lock)
+
+/* List of all temporary files without temporary directories. */
+static gl_list_t /* <char *> */ volatile file_cleanup_list;
+
+
/* Registry for a single temporary directory.
'struct temp_dir' from the public header file overlaps with this. */
struct tempdir
@@ -95,16 +109,41 @@ struct tempdir
gl_list_t /* <char *> */ volatile files;
};
+/* Lock that protects the dir_cleanup_list from concurrent modification in
+ different threads. */
+gl_lock_define_initialized (static, dir_cleanup_list_lock)
+
/* List of all temporary directories. */
static struct
{
struct tempdir * volatile * volatile tempdir_list;
size_t volatile tempdir_count;
size_t tempdir_allocated;
-} cleanup_list /* = { NULL, 0, 0 } */;
+} dir_cleanup_list /* = { NULL, 0, 0 } */;
+
+
+/* A file descriptor to be closed.
+ In multithreaded programs, it is forbidden to close the same fd twice,
+ because you never know what unrelated open() calls are being executed in
+ other threads. So, the 'close (fd)' must be guarded by a once-only guard. */
+struct closeable_fd
+{
+ /* The file descriptor to close. */
+ int volatile fd;
+ /* Set to true when it has been closed. */
+ bool volatile closed;
+ /* Lock that protects the fd from being closed twice. */
+ asyncsafe_spinlock_t lock;
+ /* Tells whether this list element has been done and can be freed. */
+ bool volatile done;
+};
+
+/* Lock that protects the descriptors list from concurrent modification in
+ different threads. */
+gl_lock_define_initialized (static, descriptors_lock)
/* List of all open file descriptors to temporary files. */
-static gl_list_t /* <int> */ volatile descriptors;
+static gl_list_t /* <closeable_fd *> */ volatile descriptors;
/* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH.
@@ -184,6 +223,84 @@ string_hash (const void *x)
}
+/* The set of fatal signal handlers.
+ Cached here because we are not allowed to call get_fatal_signal_set ()
+ from a signal handler. */
+static const sigset_t *fatal_signal_set /* = NULL */;
+
+static void
+init_fatal_signal_set (void)
+{
+ if (fatal_signal_set == NULL)
+ fatal_signal_set = get_fatal_signal_set ();
+}
+
+
+/* Close a file descriptor.
+ Avoids race conditions with normal thread code or signal-handler code that
+ might want to close the same file descriptor. */
+static _GL_ASYNC_SAFE int
+asyncsafe_close (struct closeable_fd *element)
+{
+ sigset_t saved_mask;
+ int ret;
+ int saved_errno;
+
+ asyncsafe_spin_lock (&element->lock, fatal_signal_set, &saved_mask);
+ if (!element->closed)
+ {
+ ret = close (element->fd);
+ saved_errno = errno;
+ element->closed = true;
+ }
+ else
+ {
+ ret = 0;
+ saved_errno = 0;
+ }
+ asyncsafe_spin_unlock (&element->lock, &saved_mask);
+ element->done = true;
+
+ errno = saved_errno;
+ return ret;
+}
+
+/* Close a file descriptor and the stream that contains it.
+ Avoids race conditions with signal-handler code that might want to close the
+ same file descriptor. */
+static int
+asyncsafe_fclose_variant (struct closeable_fd *element, FILE *fp,
+ int (*fclose_variant) (FILE *))
+{
+ if (fileno (fp) != element->fd)
+ abort ();
+
+ /* Flush buffered data first, to minimize the duration of the spin lock. */
+ fflush (fp);
+
+ sigset_t saved_mask;
+ int ret;
+ int saved_errno;
+
+ asyncsafe_spin_lock (&element->lock, fatal_signal_set, &saved_mask);
+ if (!element->closed)
+ {
+ ret = fclose_variant (fp); /* invokes close (element->fd) */
+ saved_errno = errno;
+ element->closed = true;
+ }
+ else
+ {
+ ret = 0;
+ saved_errno = 0;
+ }
+ asyncsafe_spin_unlock (&element->lock, &saved_mask);
+ element->done = true;
+
+ errno = saved_errno;
+ return ret;
+}
+
/* The signal handler. It gets called asynchronously. */
static _GL_ASYNC_SAFE void
cleanup_action (int sig _GL_UNUSED)
@@ -202,16 +319,33 @@ cleanup_action (int sig _GL_UNUSED)
iter = gl_list_iterator (fds);
while (gl_list_iterator_next (&iter, &element, NULL))
{
- int fd = (int) (uintptr_t) element;
- close (fd);
+ asyncsafe_close ((struct closeable_fd *) element);
+ }
+ gl_list_iterator_free (&iter);
+ }
+ }
+
+ {
+ gl_list_t files = file_cleanup_list;
+
+ if (files != NULL)
+ {
+ gl_list_iterator_t iter;
+ const void *element;
+
+ iter = gl_list_iterator (files);
+ while (gl_list_iterator_next (&iter, &element, NULL))
+ {
+ const char *file = (const char *) element;
+ unlink (file);
}
gl_list_iterator_free (&iter);
}
}
- for (i = 0; i < cleanup_list.tempdir_count; i++)
+ for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
{
- struct tempdir *dir = cleanup_list.tempdir_list[i];
+ struct tempdir *dir = dir_cleanup_list.tempdir_list[i];
if (dir != NULL)
{
@@ -242,6 +376,110 @@ cleanup_action (int sig _GL_UNUSED)
}
}
+
+/* Initializes this facility. */
+static void
+do_init_clean_temp (void)
+{
+ /* Initialize the data used by the cleanup handler. */
+ init_fatal_signal_set ();
+ /* Register the cleanup handler. */
+ at_fatal_signal (&cleanup_action);
+}
+
+/* Ensure that do_init_clean_temp is called once only. */
+gl_once_define(static, clean_temp_once)
+
+/* Initializes this facility upon first use. */
+static void
+init_clean_temp (void)
+{
+ gl_once (clean_temp_once, do_init_clean_temp);
+}
+
+
+/* ============= Temporary files without temporary directories ============= */
+
+/* Register the given ABSOLUTE_FILE_NAME as being a file that needs to be
+ removed.
+ Should be called before the file ABSOLUTE_FILE_NAME is created. */
+void
+register_temporary_file (const char *absolute_file_name)
+{
+ gl_lock_lock (file_cleanup_list_lock);
+
+ /* Make sure that this facility and the file_cleanup_list are initialized. */
+ if (file_cleanup_list == NULL)
+ {
+ init_clean_temp ();
+ file_cleanup_list =
+ gl_list_create_empty (GL_LINKEDHASH_LIST,
+ string_equals, string_hash, NULL, false);
+ }
+
+ /* Add absolute_file_name to file_cleanup_list, without duplicates. */
+ if (gl_list_search (file_cleanup_list, absolute_file_name) == NULL)
+ gl_list_add_first (file_cleanup_list, xstrdup (absolute_file_name));
+
+ gl_lock_unlock (file_cleanup_list_lock);
+}
+
+/* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be
+ removed.
+ Should be called when the file ABSOLUTE_FILE_NAME could not be created. */
+void
+unregister_temporary_file (const char *absolute_file_name)
+{
+ gl_lock_lock (file_cleanup_list_lock);
+
+ gl_list_t list = file_cleanup_list;
+ if (list != NULL)
+ {
+ gl_list_node_t node = gl_list_search (list, absolute_file_name);
+ if (node != NULL)
+ {
+ char *old_string = (char *) gl_list_node_value (list, node);
+
+ gl_list_remove_node (list, node);
+ free (old_string);
+ }
+ }
+
+ gl_lock_unlock (file_cleanup_list_lock);
+}
+
+/* Remove a file, with optional error message.
+ Return 0 upon success, or -1 if there was some problem. */
+static int
+do_unlink (const char *absolute_file_name, bool cleanup_verbose)
+{
+ if (unlink (absolute_file_name) < 0 && cleanup_verbose
+ && errno != ENOENT)
+ {
+ error (0, errno,
+ _("cannot remove temporary file %s"), absolute_file_name);
+ return -1;
+ }
+ return 0;
+}
+
+/* Remove the given ABSOLUTE_FILE_NAME and unregister it.
+ CLEANUP_VERBOSE determines whether errors are reported to standard error.
+ Return 0 upon success, or -1 if there was some problem. */
+int
+cleanup_temporary_file (const char *absolute_file_name, bool cleanup_verbose)
+{
+ int err;
+
+ err = do_unlink (absolute_file_name, cleanup_verbose);
+ unregister_temporary_file (absolute_file_name);
+
+ return err;
+}
+
+
+/* ========= Temporary directories and temporary files inside them ========= */
+
/* Create a temporary directory.
PREFIX is used as a prefix for the name of the temporary directory. It
should be short and still give an indication about the program.
@@ -255,6 +493,8 @@ struct temp_dir *
create_temp_dir (const char *prefix, const char *parentdir,
bool cleanup_verbose)
{
+ gl_lock_lock (dir_cleanup_list_lock);
+
struct tempdir * volatile *tmpdirp = NULL;
struct tempdir *tmpdir;
size_t i;
@@ -263,28 +503,30 @@ create_temp_dir (const char *prefix, const char *parentdir,
/* See whether it can take the slot of an earlier temporary directory
already cleaned up. */
- for (i = 0; i < cleanup_list.tempdir_count; i++)
- if (cleanup_list.tempdir_list[i] == NULL)
+ for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
+ if (dir_cleanup_list.tempdir_list[i] == NULL)
{
- tmpdirp = &cleanup_list.tempdir_list[i];
+ tmpdirp = &dir_cleanup_list.tempdir_list[i];
break;
}
if (tmpdirp == NULL)
{
/* See whether the array needs to be extended. */
- if (cleanup_list.tempdir_count == cleanup_list.tempdir_allocated)
+ if (dir_cleanup_list.tempdir_count == dir_cleanup_list.tempdir_allocated)
{
/* Note that we cannot use xrealloc(), because then the cleanup()
function could access an already deallocated array. */
- struct tempdir * volatile *old_array = cleanup_list.tempdir_list;
- size_t old_allocated = cleanup_list.tempdir_allocated;
- size_t new_allocated = 2 * cleanup_list.tempdir_allocated + 1;
+ struct tempdir * volatile *old_array = dir_cleanup_list.tempdir_list;
+ size_t old_allocated = dir_cleanup_list.tempdir_allocated;
+ size_t new_allocated = 2 * dir_cleanup_list.tempdir_allocated + 1;
struct tempdir * volatile *new_array =
XNMALLOC (new_allocated, struct tempdir * volatile);
if (old_allocated == 0)
- /* First use of this facility. Register the cleanup handler. */
- at_fatal_signal (&cleanup_action);
+ {
+ /* First use of this facility. */
+ init_clean_temp ();
+ }
else
{
/* Don't use memcpy() here, because memcpy takes non-volatile
@@ -296,19 +538,26 @@ create_temp_dir (const char *prefix, const char *parentdir,
new_array[k] = old_array[k];
}
- cleanup_list.tempdir_list = new_array;
- cleanup_list.tempdir_allocated = new_allocated;
+ dir_cleanup_list.tempdir_list = new_array;
+ dir_cleanup_list.tempdir_allocated = new_allocated;
/* Now we can free the old array. */
+ /* No, we can't do that. If cleanup_action is running in a different
+ thread and has already fetched the tempdir_list pointer (getting
+ old_array) but not yet accessed its i-th element, that thread may
+ crash when accessing an element of the already freed old_array
+ array. */
+ #if 0
if (old_array != NULL)
free ((struct tempdir **) old_array);
+ #endif
}
- tmpdirp = &cleanup_list.tempdir_list[cleanup_list.tempdir_count];
+ tmpdirp = &dir_cleanup_list.tempdir_list[dir_cleanup_list.tempdir_count];
/* Initialize *tmpdirp before incrementing tempdir_count, so that
cleanup() will skip this entry before it is fully initialized. */
*tmpdirp = NULL;
- cleanup_list.tempdir_count++;
+ dir_cleanup_list.tempdir_count++;
}
/* Initialize a 'struct tempdir'. */
@@ -332,6 +581,7 @@ create_temp_dir (const char *prefix, const char *parentdir,
}
block_fatal_signals ();
tmpdirname = mkdtemp (xtemplate);
+ int saved_errno = errno;
if (tmpdirname != NULL)
{
tmpdir->dirname = tmpdirname;
@@ -340,7 +590,7 @@ create_temp_dir (const char *prefix, const char *parentdir,
unblock_fatal_signals ();
if (tmpdirname == NULL)
{
- error (0, errno,
+ error (0, saved_errno,
_("cannot create a temporary directory using template \"%s\""),
xtemplate);
goto quit;
@@ -350,10 +600,12 @@ create_temp_dir (const char *prefix, const char *parentdir,
block because then the cleanup handler would not remove the directory
if xstrdup fails. */
tmpdir->dirname = xstrdup (tmpdirname);
+ gl_lock_unlock (dir_cleanup_list_lock);
freea (xtemplate);
return (struct temp_dir *) tmpdir;
quit:
+ gl_lock_unlock (dir_cleanup_list_lock);
freea (xtemplate);
return NULL;
}
@@ -367,9 +619,13 @@ register_temp_file (struct temp_dir *dir,
{
struct tempdir *tmpdir = (struct tempdir *)dir;
+ gl_lock_lock (dir_cleanup_list_lock);
+
/* Add absolute_file_name to tmpdir->files, without duplicates. */
if (gl_list_search (tmpdir->files, absolute_file_name) == NULL)
gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name));
+
+ gl_lock_unlock (dir_cleanup_list_lock);
}
/* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
@@ -380,6 +636,9 @@ unregister_temp_file (struct temp_dir *dir,
const char *absolute_file_name)
{
struct tempdir *tmpdir = (struct tempdir *)dir;
+
+ gl_lock_lock (dir_cleanup_list_lock);
+
gl_list_t list = tmpdir->files;
gl_list_node_t node;
@@ -391,6 +650,8 @@ unregister_temp_file (struct temp_dir *dir,
gl_list_remove_node (list, node);
free (old_string);
}
+
+ gl_lock_unlock (dir_cleanup_list_lock);
}
/* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
@@ -402,9 +663,13 @@ register_temp_subdir (struct temp_dir *dir,
{
struct tempdir *tmpdir = (struct tempdir *)dir;
+ gl_lock_lock (dir_cleanup_list_lock);
+
/* Add absolute_dir_name to tmpdir->subdirs, without duplicates. */
if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL)
gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name));
+
+ gl_lock_unlock (dir_cleanup_list_lock);
}
/* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
@@ -416,6 +681,9 @@ unregister_temp_subdir (struct temp_dir *dir,
const char *absolute_dir_name)
{
struct tempdir *tmpdir = (struct tempdir *)dir;
+
+ gl_lock_lock (dir_cleanup_list_lock);
+
gl_list_t list = tmpdir->subdirs;
gl_list_node_t node;
@@ -427,28 +695,16 @@ unregister_temp_subdir (struct temp_dir *dir,
gl_list_remove_node (list, node);
free (old_string);
}
-}
-/* Remove a file, with optional error message.
- Return 0 upon success, or -1 if there was some problem. */
-static int
-do_unlink (struct temp_dir *dir, const char *absolute_file_name)
-{
- if (unlink (absolute_file_name) < 0 && dir->cleanup_verbose
- && errno != ENOENT)
- {
- error (0, errno, _("cannot remove temporary file %s"), absolute_file_name);
- return -1;
- }
- return 0;
+ gl_lock_unlock (dir_cleanup_list_lock);
}
/* Remove a directory, with optional error message.
Return 0 upon success, or -1 if there was some problem. */
static int
-do_rmdir (struct temp_dir *dir, const char *absolute_dir_name)
+do_rmdir (const char *absolute_dir_name, bool cleanup_verbose)
{
- if (rmdir (absolute_dir_name) < 0 && dir->cleanup_verbose
+ if (rmdir (absolute_dir_name) < 0 && cleanup_verbose
&& errno != ENOENT)
{
error (0, errno,
@@ -466,7 +722,7 @@ cleanup_temp_file (struct temp_dir *dir,
{
int err;
- err = do_unlink (dir, absolute_file_name);
+ err = do_unlink (absolute_file_name, dir->cleanup_verbose);
unregister_temp_file (dir, absolute_file_name);
return err;
@@ -480,13 +736,14 @@ cleanup_temp_subdir (struct temp_dir *dir,
{
int err;
- err = do_rmdir (dir, absolute_dir_name);
+ err = do_rmdir (absolute_dir_name, dir->cleanup_verbose);
unregister_temp_subdir (dir, absolute_dir_name);
return err;
}
/* Remove all registered files and subdirectories inside DIR.
+ Only to be called with dir_cleanup_list_lock locked.
Return 0 upon success, or -1 if there was some problem. */
int
cleanup_temp_dir_contents (struct temp_dir *dir)
@@ -505,7 +762,7 @@ cleanup_temp_dir_contents (struct temp_dir *dir)
{
char *file = (char *) element;
- err |= do_unlink (dir, file);
+ err |= do_unlink (file, dir->cleanup_verbose);
gl_list_remove_node (list, node);
/* Now only we can free file. */
free (file);
@@ -519,7 +776,7 @@ cleanup_temp_dir_contents (struct temp_dir *dir)
{
char *subdir = (char *) element;
- err |= do_rmdir (dir, subdir);
+ err |= do_rmdir (subdir, dir->cleanup_verbose);
gl_list_remove_node (list, node);
/* Now only we can free subdir. */
free (subdir);
@@ -535,31 +792,34 @@ cleanup_temp_dir_contents (struct temp_dir *dir)
int
cleanup_temp_dir (struct temp_dir *dir)
{
+ gl_lock_lock (dir_cleanup_list_lock);
+
struct tempdir *tmpdir = (struct tempdir *)dir;
int err = 0;
size_t i;
err |= cleanup_temp_dir_contents (dir);
- err |= do_rmdir (dir, tmpdir->dirname);
+ err |= do_rmdir (tmpdir->dirname, dir->cleanup_verbose);
- for (i = 0; i < cleanup_list.tempdir_count; i++)
- if (cleanup_list.tempdir_list[i] == tmpdir)
+ for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
+ if (dir_cleanup_list.tempdir_list[i] == tmpdir)
{
- /* Remove cleanup_list.tempdir_list[i]. */
- if (i + 1 == cleanup_list.tempdir_count)
+ /* Remove dir_cleanup_list.tempdir_list[i]. */
+ if (i + 1 == dir_cleanup_list.tempdir_count)
{
- while (i > 0 && cleanup_list.tempdir_list[i - 1] == NULL)
+ while (i > 0 && dir_cleanup_list.tempdir_list[i - 1] == NULL)
i--;
- cleanup_list.tempdir_count = i;
+ dir_cleanup_list.tempdir_count = i;
}
else
- cleanup_list.tempdir_list[i] = NULL;
+ dir_cleanup_list.tempdir_list[i] = NULL;
/* Now only we can free the tmpdir->dirname, tmpdir->subdirs,
tmpdir->files, and tmpdir itself. */
gl_list_free (tmpdir->files);
gl_list_free (tmpdir->subdirs);
free (tmpdir->dirname);
free (tmpdir);
+ gl_lock_unlock (dir_cleanup_list_lock);
return err;
}
@@ -568,6 +828,8 @@ cleanup_temp_dir (struct temp_dir *dir)
}
+/* ================== Opening and closing temporary files ================== */
+
#if defined _WIN32 && ! defined __CYGWIN__
/* On Windows, opening a file with _O_TEMPORARY has the effect of passing
@@ -604,33 +866,30 @@ supports_delete_on_close ()
static void
register_fd (int fd)
{
+ gl_lock_lock (descriptors_lock);
+
if (descriptors == NULL)
- descriptors = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, NULL,
+ descriptors = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, NULL,
false);
- gl_list_add_first (descriptors, (void *) (uintptr_t) fd);
-}
-/* Unregister a file descriptor to be closed. */
-static void
-unregister_fd (int fd)
-{
- gl_list_t fds = descriptors;
- gl_list_node_t node;
+ struct closeable_fd *element = XMALLOC (struct closeable_fd);
+ element->fd = fd;
+ element->closed = false;
+ asyncsafe_spin_init (&element->lock);
+ element->done = false;
- if (fds == NULL)
- /* descriptors should already contain fd. */
- abort ();
- node = gl_list_search (fds, (void *) (uintptr_t) fd);
- if (node == NULL)
- /* descriptors should already contain fd. */
- abort ();
- gl_list_remove_node (fds, node);
+ gl_list_add_first (descriptors, element);
+
+ gl_lock_unlock (descriptors_lock);
}
/* Open a temporary file in a temporary directory.
- Registers the resulting file descriptor to be closed. */
+ FILE_NAME must already have been passed to register_temp_file.
+ Registers the resulting file descriptor to be closed.
+ DELETE_ON_CLOSE indicates whether the file can be deleted when the resulting
+ file descriptor or stream is closed. */
int
-open_temp (const char *file_name, int flags, mode_t mode)
+open_temp (const char *file_name, int flags, mode_t mode, bool delete_on_close)
{
int fd;
int saved_errno;
@@ -640,7 +899,7 @@ open_temp (const char *file_name, int flags, mode_t mode)
#if defined _WIN32 && ! defined __CYGWIN__
/* Use _O_TEMPORARY when possible, to increase the chances that the
temporary file is removed when the process crashes. */
- if (supports_delete_on_close ())
+ if (delete_on_close && supports_delete_on_close ())
fd = open (file_name, flags | _O_TEMPORARY, mode);
else
#endif
@@ -654,9 +913,12 @@ open_temp (const char *file_name, int flags, mode_t mode)
}
/* Open a temporary file in a temporary directory.
- Registers the resulting file descriptor to be closed. */
+ FILE_NAME must already have been passed to register_temp_file.
+ Registers the resulting file descriptor to be closed.
+ DELETE_ON_CLOSE indicates whether the file can be deleted when the resulting
+ file descriptor or stream is closed. */
FILE *
-fopen_temp (const char *file_name, const char *mode)
+fopen_temp (const char *file_name, const char *mode, bool delete_on_close)
{
FILE *fp;
int saved_errno;
@@ -666,7 +928,7 @@ fopen_temp (const char *file_name, const char *mode)
#if defined _WIN32 && ! defined __CYGWIN__
/* Use _O_TEMPORARY when possible, to increase the chances that the
temporary file is removed when the process crashes. */
- if (supports_delete_on_close ())
+ if (delete_on_close && supports_delete_on_close ())
{
size_t mode_len = strlen (mode);
char *augmented_mode = (char *) xmalloca (mode_len + 2);
@@ -699,90 +961,215 @@ fopen_temp (const char *file_name, const char *mode)
return fp;
}
-/* Close a temporary file in a temporary directory.
- Unregisters the previously registered file descriptor. */
+#if GNULIB_TEMPNAME
+
+struct try_create_file_params
+{
+ int flags;
+ mode_t mode;
+};
+
+static int
+try_create_file (char *file_name_tmpl, void *params_)
+{
+ struct try_create_file_params *params = params_;
+ return open (file_name_tmpl,
+ (params->flags & ~O_ACCMODE) | O_RDWR | O_CREAT | O_EXCL,
+ params->mode);
+}
+
+/* Open a temporary file, generating its name based on FILE_NAME_TMPL.
+ FILE_NAME_TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX",
+ possibly with a suffix). The name constructed does not exist at the time
+ of the call. FILE_NAME_TMPL is overwritten with the result.
+ A safe choice for MODE is S_IRUSR | S_IWUSR, a.k.a. 0600.
+ Registers the file for deletion.
+ Opens the file, with the given FLAGS and mode MODE.
+ Registers the resulting file descriptor to be closed. */
int
-close_temp (int fd)
+gen_register_open_temp (char *file_name_tmpl, int suffixlen,
+ int flags, mode_t mode)
{
- if (fd >= 0)
- {
- /* No blocking of signals is needed here, since a double close of a
- file descriptor is harmless. */
- int result = close (fd);
- int saved_errno = errno;
+ block_fatal_signals ();
- /* No race condition here: we assume a single-threaded program, hence
- fd cannot be re-opened here. */
+ struct try_create_file_params params;
+ params.flags = flags;
+ params.mode = mode;
- unregister_fd (fd);
+ int fd = try_tempname (file_name_tmpl, suffixlen, &params, try_create_file);
- errno = saved_errno;
- return result;
+ int saved_errno = errno;
+ if (fd >= 0)
+ {
+ init_clean_temp ();
+ register_fd (fd);
+ register_temporary_file (file_name_tmpl);
}
- else
- return close (fd);
+ unblock_fatal_signals ();
+ errno = saved_errno;
+ return fd;
}
-/* Close a temporary file in a temporary directory.
+#endif
+
+/* Close a temporary file.
+ FD must have been returned by open_temp or gen_register_open_temp.
Unregisters the previously registered file descriptor. */
int
-fclose_temp (FILE *fp)
+close_temp (int fd)
{
- int fd = fileno (fp);
- /* No blocking of signals is needed here, since a double close of a
- file descriptor is harmless. */
- int result = fclose (fp);
- int saved_errno = errno;
+ if (fd < 0)
+ return close (fd);
+
+ init_fatal_signal_set ();
+
+ int result = 0;
+ int saved_errno = 0;
- /* No race condition here: we assume a single-threaded program, hence
- fd cannot be re-opened here. */
+ gl_lock_lock (descriptors_lock);
- unregister_fd (fd);
+ gl_list_t list = descriptors;
+ if (list == NULL)
+ /* descriptors should already contain fd. */
+ abort ();
+
+ /* Search through the list, and clean it up on the fly. */
+ bool found = false;
+ gl_list_iterator_t iter = gl_list_iterator (list);
+ const void *elt;
+ gl_list_node_t node;
+ if (gl_list_iterator_next (&iter, &elt, &node))
+ for (;;)
+ {
+ struct closeable_fd *element = (struct closeable_fd *) elt;
+
+ /* Close the file descriptor, avoiding races with the signal
+ handler. */
+ if (element->fd == fd)
+ {
+ found = true;
+ result = asyncsafe_close (element);
+ saved_errno = errno;
+ }
+
+ bool free_this_node = element->done;
+ struct closeable_fd *element_to_free = element;
+ gl_list_node_t node_to_free = node;
+
+ bool have_next = gl_list_iterator_next (&iter, &elt, &node);
+
+ if (free_this_node)
+ {
+ free (element_to_free);
+ gl_list_remove_node (list, node_to_free);
+ }
+
+ if (!have_next)
+ break;
+ }
+ gl_list_iterator_free (&iter);
+ if (!found)
+ /* descriptors should already contain fd. */
+ abort ();
+
+ gl_lock_unlock (descriptors_lock);
errno = saved_errno;
return result;
}
-#if GNULIB_FWRITEERROR
-/* Like fwriteerror.
- Unregisters the previously registered file descriptor. */
-int
-fwriteerror_temp (FILE *fp)
+static int
+fclose_variant_temp (FILE *fp, int (*fclose_variant) (FILE *))
{
int fd = fileno (fp);
- /* No blocking of signals is needed here, since a double close of a
- file descriptor is harmless. */
- int result = fwriteerror (fp);
- int saved_errno = errno;
- /* No race condition here: we assume a single-threaded program, hence
- fd cannot be re-opened here. */
+ init_fatal_signal_set ();
+
+ int result = 0;
+ int saved_errno = 0;
+
+ gl_lock_lock (descriptors_lock);
- unregister_fd (fd);
+ gl_list_t list = descriptors;
+ if (list == NULL)
+ /* descriptors should already contain fd. */
+ abort ();
+
+ /* Search through the list, and clean it up on the fly. */
+ bool found = false;
+ gl_list_iterator_t iter = gl_list_iterator (list);
+ const void *elt;
+ gl_list_node_t node;
+ if (gl_list_iterator_next (&iter, &elt, &node))
+ for (;;)
+ {
+ struct closeable_fd *element = (struct closeable_fd *) elt;
+
+ /* Close the file descriptor and the stream, avoiding races with the
+ signal handler. */
+ if (element->fd == fd)
+ {
+ found = true;
+ result = asyncsafe_fclose_variant (element, fp, fclose_variant);
+ saved_errno = errno;
+ }
+
+ bool free_this_node = element->done;
+ struct closeable_fd *element_to_free = element;
+ gl_list_node_t node_to_free = node;
+
+ bool have_next = gl_list_iterator_next (&iter, &elt, &node);
+
+ if (free_this_node)
+ {
+ free (element_to_free);
+ gl_list_remove_node (list, node_to_free);
+ }
+
+ if (!have_next)
+ break;
+ }
+ gl_list_iterator_free (&iter);
+ if (!found)
+ /* descriptors should have contained fd. */
+ abort ();
+
+ gl_lock_unlock (descriptors_lock);
errno = saved_errno;
return result;
}
+
+/* Close a temporary file.
+ FP must have been returned by fopen_temp, or by fdopen on a file descriptor
+ returned by open_temp or gen_register_open_temp.
+ Unregisters the previously registered file descriptor. */
+int
+fclose_temp (FILE *fp)
+{
+ return fclose_variant_temp (fp, fclose);
+}
+
+#if GNULIB_FWRITEERROR
+/* Like fwriteerror.
+ FP must have been returned by fopen_temp, or by fdopen on a file descriptor
+ returned by open_temp or gen_register_open_temp.
+ Unregisters the previously registered file descriptor. */
+int
+fwriteerror_temp (FILE *fp)
+{
+ return fclose_variant_temp (fp, fwriteerror);
+}
#endif
#if GNULIB_CLOSE_STREAM
/* Like close_stream.
+ FP must have been returned by fopen_temp, or by fdopen on a file descriptor
+ returned by open_temp or gen_register_open_temp.
Unregisters the previously registered file descriptor. */
int
close_stream_temp (FILE *fp)
{
- int fd = fileno (fp);
- /* No blocking of signals is needed here, since a double close of a
- file descriptor is harmless. */
- int result = close_stream (fp);
- int saved_errno = errno;
-
- /* No race condition here: we assume a single-threaded program, hence
- fd cannot be re-opened here. */
-
- unregister_fd (fd);
-
- errno = saved_errno;
- return result;
+ return fclose_variant_temp (fp, close_stream);
}
#endif
diff --git a/lib/clean-temp.h b/lib/clean-temp.h
index 620bd08..d660b18 100644
--- a/lib/clean-temp.h
+++ b/lib/clean-temp.h
@@ -37,18 +37,50 @@ extern "C" {
and the temporary directories can be removed, because only on Unix
(excluding Cygwin) can one remove directories containing open files.
- This module provides support for temporary directories and temporary files
- inside these temporary directories. Temporary files without temporary
- directories are not supported here. The temporary directories and files
- are automatically cleaned up (at the latest) when the program exits or
- dies from a fatal signal such as SIGINT, SIGTERM, SIGHUP, but not if it
- dies from a fatal signal such as SIGQUIT, SIGKILL, or SIGABRT, SIGSEGV,
- SIGBUS, SIGILL, SIGFPE.
+ This module provides support for
+ - temporary directories and temporary files inside these temporary
+ directories,
+ - temporary files without temporary directories.
+ The temporary directories and files are automatically cleaned up (at the
+ latest) when the program exits or dies from a fatal signal such as SIGINT,
+ SIGTERM, SIGHUP, but not if it dies from a fatal signal such as SIGQUIT,
+ SIGKILL, or SIGABRT, SIGSEGV, SIGBUS, SIGILL, SIGFPE.
For the cleanup in the normal case, programs that use this module need to
call 'cleanup_temp_dir' for each successful return of 'create_temp_dir'.
The cleanup in the case of a fatal signal such as SIGINT, SIGTERM, SIGHUP,
- is done entirely automatically by the functions of this module. */
+ is done entirely automatically by the functions of this module.
+
+ Limitations: Files or directories can still be left over if
+ - the program is dies from a fatal signal such as SIGQUIT, SIGKILL, or
+ SIGABRT, SIGSEGV, SIGBUS, SIGILL, SIGFPE, or
+ - in a multithreaded program, the fatal signal handler is already running
+ while another thread of the program creates a new temporary directory
+ or temporary file, or
+ - on native Windows, some temporary files are used by a subprocess while
+ the fatal signal interrupts the program.
+ */
+
+
+/* ============= Temporary files without temporary directories ============= */
+
+/* Register the given ABSOLUTE_FILE_NAME as being a file that needs to be
+ removed.
+ Should be called before the file ABSOLUTE_FILE_NAME is created. */
+extern void register_temporary_file (const char *absolute_file_name);
+
+/* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be
+ removed.
+ Should be called when the file ABSOLUTE_FILE_NAME could not be created. */
+extern void unregister_temporary_file (const char *absolute_file_name);
+
+/* Remove the given ABSOLUTE_FILE_NAME and unregister it.
+ CLEANUP_VERBOSE determines whether errors are reported to standard error.
+ Return 0 upon success, or -1 if there was some problem. */
+extern int cleanup_temporary_file (const char *absolute_file_name,
+ bool cleanup_verbose);
+
+/* ========= Temporary directories and temporary files inside them ========= */
struct temp_dir
{
@@ -116,21 +148,49 @@ extern int cleanup_temp_dir_contents (struct temp_dir *dir);
Return 0 upon success, or -1 if there was some problem. */
extern int cleanup_temp_dir (struct temp_dir *dir);
+/* ================== Opening and closing temporary files ================== */
+
/* Open a temporary file in a temporary directory.
+ FILE_NAME must already have been passed to register_temp_file.
+ Registers the resulting file descriptor to be closed.
+ DELETE_ON_CLOSE indicates whether the file can be deleted when the resulting
+ file descriptor or stream is closed. */
+extern int open_temp (const char *file_name, int flags, mode_t mode,
+ bool delete_on_close);
+extern FILE * fopen_temp (const char *file_name, const char *mode,
+ bool delete_on_close);
+
+/* Open a temporary file, generating its name based on FILE_NAME_TMPL.
+ FILE_NAME_TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX",
+ possibly with a suffix). The name constructed does not exist at the time
+ of the call. FILE_NAME_TMPL is overwritten with the result.
+ A safe choice for MODE is S_IRUSR | S_IWUSR, a.k.a. 0600.
+ Registers the file for deletion.
+ Opens the file, with the given FLAGS and mode MODE.
Registers the resulting file descriptor to be closed. */
-extern int open_temp (const char *file_name, int flags, mode_t mode);
-extern FILE * fopen_temp (const char *file_name, const char *mode);
+extern int gen_register_open_temp (char *file_name_tmpl, int suffixlen,
+ int flags, mode_t mode);
-/* Close a temporary file in a temporary directory.
+/* Close a temporary file.
+ FD must have been returned by open_temp or gen_register_open_temp.
Unregisters the previously registered file descriptor. */
extern int close_temp (int fd);
+
+/* Close a temporary file.
+ FP must have been returned by fopen_temp, or by fdopen on a file descriptor
+ returned by open_temp or gen_register_open_temp.
+ Unregisters the previously registered file descriptor. */
extern int fclose_temp (FILE *fp);
/* Like fwriteerror.
+ FP must have been returned by fopen_temp, or by fdopen on a file descriptor
+ returned by open_temp or gen_register_open_temp.
Unregisters the previously registered file descriptor. */
extern int fwriteerror_temp (FILE *fp);
/* Like close_stream.
+ FP must have been returned by fopen_temp, or by fdopen on a file descriptor
+ returned by open_temp or gen_register_open_temp.
Unregisters the previously registered file descriptor. */
extern int close_stream_temp (FILE *fp);
diff --git a/lib/dev-ino.h b/lib/dev-ino.h
index 695d38c..7b62d48 100644
--- a/lib/dev-ino.h
+++ b/lib/dev-ino.h
@@ -1,3 +1,21 @@
+/* A simple (device, inode) struct.
+ Copyright (C) 2003-2020 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 Jim Meyering, 2003. */
+
#ifndef DEV_INO_H
# define DEV_INO_H 1
diff --git a/lib/di-set.h b/lib/di-set.h
index bec5b3f..743bc7d 100644
--- a/lib/di-set.h
+++ b/lib/di-set.h
@@ -1,3 +1,21 @@
+/* Manipulate sets of device-inode pairs efficiently.
+ Copyright (C) 2010-2020 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 Jim Meyering, 2010. */
+
#ifndef _GL_DI_SET_H
# define _GL_DI_SET_H
diff --git a/lib/dirname.h b/lib/dirname.h
index 5379e8e..b2a91fd 100644
--- a/lib/dirname.h
+++ b/lib/dirname.h
@@ -22,15 +22,12 @@
# include <stdbool.h>
# include <stddef.h>
# include "filename.h"
+# include "basename-lgpl.h"
# ifndef DIRECTORY_SEPARATOR
# define DIRECTORY_SEPARATOR '/'
# endif
-# ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
-# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
-# endif
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -41,9 +38,7 @@ char *dir_name (char const *file);
# endif
char *mdir_name (char const *file);
-size_t base_len (char const *file) _GL_ATTRIBUTE_PURE;
size_t dir_len (char const *file) _GL_ATTRIBUTE_PURE;
-char *last_component (char const *file) _GL_ATTRIBUTE_PURE;
bool strip_trailing_slashes (char *file);
diff --git a/lib/dup2.c b/lib/dup2.c
index 88ef259..9bc3951 100644
--- a/lib/dup2.c
+++ b/lib/dup2.c
@@ -25,28 +25,26 @@
#include <errno.h>
#include <fcntl.h>
-#if HAVE_DUP2
+#undef dup2
-# undef dup2
-
-# if defined _WIN32 && ! defined __CYGWIN__
+#if defined _WIN32 && ! defined __CYGWIN__
/* Get declarations of the native Windows API functions. */
-# define WIN32_LEAN_AND_MEAN
-# include <windows.h>
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
-# if HAVE_MSVC_INVALID_PARAMETER_HANDLER
-# include "msvc-inval.h"
-# endif
+# if HAVE_MSVC_INVALID_PARAMETER_HANDLER
+# include "msvc-inval.h"
+# endif
/* Get _get_osfhandle. */
-# if GNULIB_MSVC_NOTHROW
-# include "msvc-nothrow.h"
-# else
-# include <io.h>
-# endif
+# if GNULIB_MSVC_NOTHROW
+# include "msvc-nothrow.h"
+# else
+# include <io.h>
+# endif
-# if HAVE_MSVC_INVALID_PARAMETER_HANDLER
+# if HAVE_MSVC_INVALID_PARAMETER_HANDLER
static int
dup2_nothrow (int fd, int desired_fd)
{
@@ -65,9 +63,9 @@ dup2_nothrow (int fd, int desired_fd)
return result;
}
-# else
-# define dup2_nothrow dup2
-# endif
+# else
+# define dup2_nothrow dup2
+# endif
static int
ms_windows_dup2 (int fd, int desired_fd)
@@ -103,11 +101,11 @@ ms_windows_dup2 (int fd, int desired_fd)
return result;
}
-# define dup2 ms_windows_dup2
+# define dup2 ms_windows_dup2
-# elif defined __KLIBC__
+#elif defined __KLIBC__
-# include <InnoTekLIBC/backend.h>
+# include <InnoTekLIBC/backend.h>
static int
klibc_dup2dirfd (int fd, int desired_fd)
@@ -155,81 +153,37 @@ klibc_dup2 (int fd, int desired_fd)
return dupfd;
}
-# define dup2 klibc_dup2
-# endif
+# define dup2 klibc_dup2
+#endif
int
rpl_dup2 (int fd, int desired_fd)
{
int result;
-# ifdef F_GETFL
+#ifdef F_GETFL
/* On Linux kernels 2.6.26-2.6.29, dup2 (fd, fd) returns -EBADF.
On Cygwin 1.5.x, dup2 (1, 1) returns 0.
On Cygwin 1.7.17, dup2 (1, -1) dumps core.
On Cygwin 1.7.25, dup2 (1, 256) can dump core.
On Haiku, dup2 (fd, fd) mistakenly clears FD_CLOEXEC. */
-# if HAVE_SETDTABLESIZE
+# if HAVE_SETDTABLESIZE
setdtablesize (desired_fd + 1);
-# endif
+# endif
if (desired_fd < 0)
fd = desired_fd;
if (fd == desired_fd)
return fcntl (fd, F_GETFL) == -1 ? -1 : fd;
-# endif
+#endif
result = dup2 (fd, desired_fd);
/* Correct an errno value on FreeBSD 6.1 and Cygwin 1.5.x. */
if (result == -1 && errno == EMFILE)
errno = EBADF;
-# if REPLACE_FCHDIR
+#if REPLACE_FCHDIR
if (fd != desired_fd && result != -1)
result = _gl_register_dup (fd, result);
-# endif
- return result;
-}
-
-#else /* !HAVE_DUP2 */
-
-/* On older platforms, dup2 did not exist. */
-
-# ifndef F_DUPFD
-static int
-dupfd (int fd, int desired_fd)
-{
- int duplicated_fd = dup (fd);
- if (duplicated_fd < 0 || duplicated_fd == desired_fd)
- return duplicated_fd;
- else
- {
- int r = dupfd (fd, desired_fd);
- int e = errno;
- close (duplicated_fd);
- errno = e;
- return r;
- }
-}
-# endif
-
-int
-dup2 (int fd, int desired_fd)
-{
- int result = fcntl (fd, F_GETFL) < 0 ? -1 : fd;
- if (result == -1 || fd == desired_fd)
- return result;
- close (desired_fd);
-# ifdef F_DUPFD
- result = fcntl (fd, F_DUPFD, desired_fd);
-# if REPLACE_FCHDIR
- if (0 <= result)
- result = _gl_register_dup (fd, result);
-# endif
-# else
- result = dupfd (fd, desired_fd);
-# endif
- if (result == -1 && (errno == EMFILE || errno == EINVAL))
- errno = EBADF;
+#endif
return result;
}
-#endif /* !HAVE_DUP2 */
diff --git a/lib/fatal-signal.c b/lib/fatal-signal.c
index c8ff338..29184e0 100644
--- a/lib/fatal-signal.c
+++ b/lib/fatal-signal.c
@@ -26,6 +26,7 @@
#include <signal.h>
#include <unistd.h>
+#include "glthread/lock.h"
#include "sig-handler.h"
#include "xalloc.h"
@@ -85,6 +86,10 @@ static int fatal_signals[] =
static void
init_fatal_signals (void)
{
+ /* This function is multithread-safe even without synchronization, because
+ if two threads execute it simultaneously, the fatal_signals[] array will
+ not change any more after the first of the threads has completed this
+ function. */
static bool fatal_signals_initialized = false;
if (!fatal_signals_initialized)
{
@@ -200,11 +205,16 @@ install_handlers (void)
}
+/* Lock that makes at_fatal_signal multi-thread safe. */
+gl_lock_define_initialized (static, at_fatal_signal_lock)
+
/* Register a cleanup function to be executed when a catchable fatal signal
occurs. */
void
at_fatal_signal (action_t action)
{
+ gl_lock_lock (at_fatal_signal_lock);
+
static bool cleanup_initialized = false;
if (!cleanup_initialized)
{
@@ -233,8 +243,15 @@ at_fatal_signal (action_t action)
actions = new_actions;
actions_allocated = new_actions_allocated;
/* Now we can free the old actions array. */
+ /* No, we can't do that. If fatal_signal_handler is running in a
+ different thread and has already fetched the actions pointer (getting
+ old_actions) but not yet accessed its n-th element, that thread may
+ crash when accessing an element of the already freed old_actions
+ array. */
+ #if 0
if (old_actions != static_actions)
free (old_actions);
+ #endif
}
/* The two uses of 'volatile' in the types above (and ISO C 99 section
5.1.2.3.(5)) ensure that we increment the actions_count only after
@@ -242,6 +259,8 @@ at_fatal_signal (action_t action)
actions[actions_count]. */
actions[actions_count].action = action;
actions_count++;
+
+ gl_lock_unlock (at_fatal_signal_lock);
}
@@ -251,38 +270,64 @@ at_fatal_signal (action_t action)
static sigset_t fatal_signal_set;
static void
-init_fatal_signal_set (void)
+do_init_fatal_signal_set (void)
{
- static bool fatal_signal_set_initialized = false;
- if (!fatal_signal_set_initialized)
- {
- size_t i;
+ size_t i;
- init_fatal_signals ();
+ init_fatal_signals ();
- sigemptyset (&fatal_signal_set);
- for (i = 0; i < num_fatal_signals; i++)
- if (fatal_signals[i] >= 0)
- sigaddset (&fatal_signal_set, fatal_signals[i]);
+ sigemptyset (&fatal_signal_set);
+ for (i = 0; i < num_fatal_signals; i++)
+ if (fatal_signals[i] >= 0)
+ sigaddset (&fatal_signal_set, fatal_signals[i]);
+}
- fatal_signal_set_initialized = true;
- }
+/* Ensure that do_init_fatal_signal_set is called once only. */
+gl_once_define(static, fatal_signal_set_once)
+
+static void
+init_fatal_signal_set (void)
+{
+ gl_once (fatal_signal_set_once, do_init_fatal_signal_set);
}
+/* Lock and counter that allow block_fatal_signals/unblock_fatal_signals pairs
+ to occur in different threads and even overlap in time. */
+gl_lock_define_initialized (static, fatal_signals_block_lock)
+static unsigned int fatal_signals_block_counter = 0;
+
/* Temporarily delay the catchable fatal signals. */
void
block_fatal_signals (void)
{
- init_fatal_signal_set ();
- sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
+ gl_lock_lock (fatal_signals_block_lock);
+
+ if (fatal_signals_block_counter++ == 0)
+ {
+ init_fatal_signal_set ();
+ sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
+ }
+
+ gl_lock_unlock (fatal_signals_block_lock);
}
/* Stop delaying the catchable fatal signals. */
void
unblock_fatal_signals (void)
{
- init_fatal_signal_set ();
- sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
+ gl_lock_lock (fatal_signals_block_lock);
+
+ if (fatal_signals_block_counter == 0)
+ /* There are more calls to unblock_fatal_signals() than to
+ block_fatal_signals(). */
+ abort ();
+ if (--fatal_signals_block_counter == 0)
+ {
+ init_fatal_signal_set ();
+ sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
+ }
+
+ gl_lock_unlock (fatal_signals_block_lock);
}
@@ -301,3 +346,10 @@ get_fatal_signals (int signals[64])
return p - signals;
}
}
+
+const sigset_t *
+get_fatal_signal_set (void)
+{
+ init_fatal_signal_set ();
+ return &fatal_signal_set;
+}
diff --git a/lib/fatal-signal.h b/lib/fatal-signal.h
index 2c0154f..54270fa 100644
--- a/lib/fatal-signal.h
+++ b/lib/fatal-signal.h
@@ -16,6 +16,11 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
+#ifndef _FATAL_SIGNAL_H
+#define _FATAL_SIGNAL_H
+
+#include <signal.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -77,7 +82,13 @@ extern void unblock_fatal_signals (void);
Fills signals[0..count-1] and returns count. */
extern unsigned int get_fatal_signals (int signals[64]);
+/* Return the list of signals that block_fatal_signals/unblock_fatal_signals
+ would block or unblock. */
+extern const sigset_t * get_fatal_signal_set (void);
+
#ifdef __cplusplus
}
#endif
+
+#endif /* _FATAL_SIGNAL_H */
diff --git a/lib/fchdir.c b/lib/fchdir.c
index 20ecd3c..1fa9a7e 100644
--- a/lib/fchdir.c
+++ b/lib/fchdir.c
@@ -84,7 +84,8 @@ ensure_dirs_slot (size_t fd)
return true;
}
-/* Return an absolute name of DIR in malloc'd storage. */
+/* Return an absolute name of DIR in malloc'd storage.
+ Upon failure, return NULL with errno set. */
static char *
get_name (char const *dir)
{
@@ -125,8 +126,8 @@ _gl_unregister_fd (int fd)
/* Mark FD as visiting FILENAME. FD must be non-negative, and refer
to an open file descriptor. If REPLACE_OPEN_DIRECTORY is non-zero,
this should only be called if FD is visiting a directory. Close FD
- and return -1 if there is insufficient memory to track the
- directory name; otherwise return FD. */
+ and return -1 with errno set if there is insufficient memory to track
+ the directory name; otherwise return FD. */
int
_gl_register_fd (int fd, const char *filename)
{
diff --git a/lib/fchown-stub.c b/lib/fchown-stub.c
index 62b6969..65be66e 100644
--- a/lib/fchown-stub.c
+++ b/lib/fchown-stub.c
@@ -1,3 +1,21 @@
+/* Change ownership of a file.
+ Copyright (C) 2004-2020 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 Paul Eggert, 2004. */
+
#include <config.h>
#include <sys/types.h>
diff --git a/lib/file-set.h b/lib/file-set.h
index 4e47d95..244951f 100644
--- a/lib/file-set.h
+++ b/lib/file-set.h
@@ -1,3 +1,21 @@
+/* Very specialized set-of-files code.
+ Copyright (C) 2007-2020 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 Jim Meyering, 2007. */
+
#include <sys/types.h>
#include <sys/stat.h>
#include <stdbool.h>
diff --git a/lib/filenamecat-lgpl.c b/lib/filenamecat-lgpl.c
index 6f666f2..7e77d35 100644
--- a/lib/filenamecat-lgpl.c
+++ b/lib/filenamecat-lgpl.c
@@ -25,7 +25,8 @@
#include <stdlib.h>
#include <string.h>
-#include "dirname.h"
+#include "basename-lgpl.h"
+#include "filename.h"
#if ! HAVE_MEMPCPY && ! defined mempcpy
# define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
@@ -41,7 +42,7 @@
*BASE_IN_RESULT to point to the copy of BASE at the end of the
returned concatenation.
- Return NULL if malloc fails. */
+ If malloc fails, return NULL with errno set. */
char *
mfile_name_concat (char const *dir, char const *base, char **base_in_result)
@@ -68,20 +69,22 @@ mfile_name_concat (char const *dir, char const *base, char **base_in_result)
}
char *p_concat = malloc (dirlen + (sep != '\0') + baselen + 1);
- char *p;
-
if (p_concat == NULL)
return NULL;
- p = mempcpy (p_concat, dir, dirlen);
- *p = sep;
- p += sep != '\0';
+ {
+ char *p;
+
+ p = mempcpy (p_concat, dir, dirlen);
+ *p = sep;
+ p += sep != '\0';
- if (base_in_result)
- *base_in_result = p;
+ if (base_in_result)
+ *base_in_result = p;
- p = mempcpy (p, base, baselen);
- *p = '\0';
+ p = mempcpy (p, base, baselen);
+ *p = '\0';
+ }
return p_concat;
}
diff --git a/lib/fopen.c b/lib/fopen.c
index 47d7f19..0acffa2 100644
--- a/lib/fopen.c
+++ b/lib/fopen.c
@@ -39,6 +39,7 @@ orig_fopen (const char *filename, const char *mode)
#include <errno.h>
#include <fcntl.h>
+#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
@@ -48,13 +49,12 @@ FILE *
rpl_fopen (const char *filename, const char *mode)
{
int open_direction;
- int open_flags_standard;
+ int open_flags;
#if GNULIB_FOPEN_GNU
- int open_flags_gnu;
+ bool open_flags_gnu;
# define BUF_SIZE 80
char fdopen_mode_buf[BUF_SIZE + 1];
#endif
- int open_flags;
#if defined _WIN32 && ! defined __CYGWIN__
if (strcmp (filename, "/dev/null") == 0)
@@ -63,9 +63,9 @@ rpl_fopen (const char *filename, const char *mode)
/* Parse the mode. */
open_direction = 0;
- open_flags_standard = 0;
+ open_flags = 0;
#if GNULIB_FOPEN_GNU
- open_flags_gnu = 0;
+ open_flags_gnu = false;
#endif
{
const char *p = mode;
@@ -86,7 +86,7 @@ rpl_fopen (const char *filename, const char *mode)
continue;
case 'w':
open_direction = O_WRONLY;
- open_flags_standard |= O_CREAT | O_TRUNC;
+ open_flags |= O_CREAT | O_TRUNC;
#if GNULIB_FOPEN_GNU
if (q < fdopen_mode_buf + BUF_SIZE)
*q++ = *p;
@@ -94,7 +94,7 @@ rpl_fopen (const char *filename, const char *mode)
continue;
case 'a':
open_direction = O_WRONLY;
- open_flags_standard |= O_CREAT | O_APPEND;
+ open_flags |= O_CREAT | O_APPEND;
#if GNULIB_FOPEN_GNU
if (q < fdopen_mode_buf + BUF_SIZE)
*q++ = *p;
@@ -104,7 +104,7 @@ rpl_fopen (const char *filename, const char *mode)
/* While it is non-standard, O_BINARY is guaranteed by
gnulib <fcntl.h>. We can also assume that orig_fopen
supports the 'b' flag. */
- open_flags_standard |= O_BINARY;
+ open_flags |= O_BINARY;
#if GNULIB_FOPEN_GNU
if (q < fdopen_mode_buf + BUF_SIZE)
*q++ = *p;
@@ -119,10 +119,12 @@ rpl_fopen (const char *filename, const char *mode)
continue;
#if GNULIB_FOPEN_GNU
case 'x':
- open_flags_gnu |= O_EXCL;
+ open_flags |= O_EXCL;
+ open_flags_gnu = true;
continue;
case 'e':
- open_flags_gnu |= O_CLOEXEC;
+ open_flags |= O_CLOEXEC;
+ open_flags_gnu = true;
continue;
#endif
default:
@@ -145,11 +147,6 @@ rpl_fopen (const char *filename, const char *mode)
*q = '\0';
#endif
}
-#if GNULIB_FOPEN_GNU
- open_flags = open_flags_standard | open_flags_gnu;
-#else
- open_flags = open_flags_standard;
-#endif
#if FOPEN_TRAILING_SLASH_BUG
/* Fail if the mode requires write access and the filename ends in a slash,
@@ -207,7 +204,7 @@ rpl_fopen (const char *filename, const char *mode)
#endif
#if GNULIB_FOPEN_GNU
- if (open_flags_gnu != 0)
+ if (open_flags_gnu)
{
int fd;
FILE *fp;
diff --git a/lib/ftoastr.c b/lib/ftoastr.c
index 7a7d411..47a8315 100644
--- a/lib/ftoastr.c
+++ b/lib/ftoastr.c
@@ -33,20 +33,28 @@
#include <stdio.h>
#include <stdlib.h>
+#ifdef C_LOCALE
+# include "c-snprintf.h"
+# include "c-strtod.h"
+# define PREFIX(name) c_ ## name
+#else
+# define PREFIX(name) name
+#endif
+
#if LENGTH == 3
# define FLOAT long double
# define FLOAT_DIG LDBL_DIG
# define FLOAT_MIN LDBL_MIN
# define FLOAT_PREC_BOUND _GL_LDBL_PREC_BOUND
-# define FTOASTR ldtoastr
+# define FTOASTR PREFIX (ldtoastr)
# define PROMOTED_FLOAT long double
-# define STRTOF strtold
+# define STRTOF PREFIX (strtold)
#elif LENGTH == 2
# define FLOAT double
# define FLOAT_DIG DBL_DIG
# define FLOAT_MIN DBL_MIN
# define FLOAT_PREC_BOUND _GL_DBL_PREC_BOUND
-# define FTOASTR dtoastr
+# define FTOASTR PREFIX (dtoastr)
# define PROMOTED_FLOAT double
#else
# define LENGTH 1
@@ -54,7 +62,7 @@
# define FLOAT_DIG FLT_DIG
# define FLOAT_MIN FLT_MIN
# define FLOAT_PREC_BOUND _GL_FLT_PREC_BOUND
-# define FTOASTR ftoastr
+# define FTOASTR PREFIX (ftoastr)
# define PROMOTED_FLOAT double
# if HAVE_STRTOF
# define STRTOF strtof
@@ -65,13 +73,16 @@
may generate one or two extra digits, but that's better than not
working at all. */
#ifndef STRTOF
-# define STRTOF strtod
+# define STRTOF PREFIX (strtod)
#endif
/* On hosts where it's not known that snprintf works, use sprintf to
implement the subset needed here. Typically BUFSIZE is big enough
and there's little or no performance hit. */
-#if ! GNULIB_SNPRINTF
+#ifdef C_LOCALE
+# undef snprintf
+# define snprintf c_snprintf
+#elif ! GNULIB_SNPRINTF
# undef snprintf
# define snprintf ftoastr_snprintf
static int
diff --git a/lib/ftoastr.h b/lib/ftoastr.h
index 852e400..78b569f 100644
--- a/lib/ftoastr.h
+++ b/lib/ftoastr.h
@@ -49,6 +49,12 @@ int ftoastr (char *buf, size_t bufsize, int flags, int width, float x);
int dtoastr (char *buf, size_t bufsize, int flags, int width, double x);
int ldtoastr (char *buf, size_t bufsize, int flags, int width, long double x);
+/* The last two functions except that the formatting takes place in
+ the C locale. */
+int c_dtoastr (char *buf, size_t bufsize, int flags, int width, double x);
+int c_ldtoastr (char *buf, size_t bufsize, int flags, int width, long double x);
+
+
/* Flag values for ftoastr etc. These can be ORed together. */
enum
{
diff --git a/lib/getcwd.c b/lib/getcwd.c
index 45470fc..1a42ef1 100644
--- a/lib/getcwd.c
+++ b/lib/getcwd.c
@@ -133,10 +133,10 @@ getcwd_nothrow (char *buf, size_t size)
#endif
/* Get the name of the current working directory, and put it in SIZE
- bytes of BUF. Returns NULL if the directory couldn't be determined or
- SIZE was too small. If successful, returns BUF. In GNU, if BUF is
- NULL, an array is allocated with 'malloc'; the array is SIZE bytes long,
- unless SIZE == 0, in which case it is as big as necessary. */
+ bytes of BUF. Returns NULL with errno set if the directory couldn't be
+ determined or SIZE was too small. If successful, returns BUF. In GNU,
+ if BUF is NULL, an array is allocated with 'malloc'; the array is SIZE
+ bytes long, unless SIZE == 0, in which case it is as big as necessary. */
char *
__getcwd (char *buf, size_t size)
diff --git a/lib/getloadavg.c b/lib/getloadavg.c
index aeb7070..468e250 100644
--- a/lib/getloadavg.c
+++ b/lib/getloadavg.c
@@ -567,15 +567,22 @@ getloadavg (double loadavg[], int nelem)
unsigned long int load_ave[3], scale;
int count;
- FILE *fp;
-
- fp = fopen (NETBSD_LDAV_FILE, "re");
- if (fp == NULL)
- return -1;
- count = fscanf (fp, "%lu %lu %lu %lu\n",
+ char readbuf[4 * INT_BUFSIZE_BOUND (unsigned long int) + 1];
+ int fd = open (NETBSD_LDAV_FILE, O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+ int nread = read (fd, readbuf, sizeof readbuf - 1);
+ int err = errno;
+ close (fd);
+ if (nread < 0)
+ {
+ errno = err;
+ return -1;
+ }
+ readbuf[nread] = '\0';
+ count = sscanf (readbuf, "%lu %lu %lu %lu\n",
&load_ave[0], &load_ave[1], &load_ave[2],
&scale);
- (void) fclose (fp);
if (count != 4)
{
errno = ENOTSUP;
diff --git a/lib/getprogname.c b/lib/getprogname.c
index 377e216..744466e 100644
--- a/lib/getprogname.c
+++ b/lib/getprogname.c
@@ -51,7 +51,7 @@
# include <sys/procfs.h>
#endif
-#include "dirname.h"
+#include "basename-lgpl.h"
#ifndef HAVE_GETPROGNAME /* not Mac OS X, FreeBSD, NetBSD, OpenBSD >= 5.4, Cygwin */
char const *
diff --git a/lib/getrandom.c b/lib/getrandom.c
index f0b3f53..f8695ab 100644
--- a/lib/getrandom.c
+++ b/lib/getrandom.c
@@ -29,7 +29,16 @@
#if defined _WIN32 && ! defined __CYGWIN__
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
-# include <bcrypt.h>
+# if HAVE_BCRYPT_H
+# include <bcrypt.h>
+# else
+# define NTSTATUS LONG
+typedef void * BCRYPT_ALG_HANDLE;
+# define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
+# if HAVE_LIB_BCRYPT
+extern NTSTATUS WINAPI BCryptGenRandom (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG);
+# endif
+# endif
# if !HAVE_LIB_BCRYPT
# include <wincrypt.h>
# ifndef CRYPT_VERIFY_CONTEXT
diff --git a/lib/getumask.c b/lib/getumask.c
new file mode 100644
index 0000000..6168ef6
--- a/dev/null
+++ b/lib/getumask.c
@@ -0,0 +1,140 @@
+/* Retrieve the umask of the process (multithread-safe).
+ Copyright (C) 2020 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>, 2020. */
+
+/* There are three ways to implement a getumask() function on systems that
+ don't have it:
+ (a) Through system calls on the file system.
+ (b) Through a global variable that the main() function has to set,
+ together with an override of the umask() function.
+ (c) Through { mode_t mask = umask (0); umask (mask); }.
+
+ Each has its drawbacks:
+ (a) Causes additional system calls. May fail in some rare cases.
+ (b) Causes globally visible code complexity / maintainer effort.
+ (c) Is not multithread-safe: open() calls in other threads may
+ create files with wrong access permissions.
+
+ Here we implement (a), as the least evil. */
+
+#include <config.h>
+/* The package may define ASSUME_UMASK_CONSTANT to 1, to indicate that the
+ program does not call umask(). */
+/* #define ASSUME_UMASK_CONSTANT 1 */
+
+/* Specification. */
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "clean-temp.h"
+#include "tempname.h"
+
+mode_t
+getumask (void)
+{
+#if 0
+ /* This is not multithread-safe! */
+ mode_t mask = umask (0);
+ umask (mask);
+ return mask;
+#else
+# if ASSUME_UMASK_CONSTANT
+ static int cached_umask = -1;
+ if (cached_umask >= 0)
+ return cached_umask;
+# endif
+
+ int mask = -1;
+# if defined __linux__
+ {
+ /* In Linux >= 4.7, the umask can be retrieved from an "Umask:" line in the
+ /proc/self/status file. */
+ char buf[4096];
+ int fd = open ("/proc/self/status", O_RDONLY);
+ if (fd >= 0)
+ {
+ ssize_t n = read (fd, buf, sizeof (buf));
+ if (n > 0)
+ {
+ const char *p_end = buf + n;
+ const char *p = buf;
+
+ for (;;)
+ {
+ /* Here we're at the beginning of a line. */
+ if (p_end - p > 8 && memcmp (p, "Umask:\t0", 8) == 0)
+ {
+ unsigned int value = 0;
+ p += 8;
+ for (; p < p_end && *p >= '0' && *p <= '7'; p++)
+ value = 8 * value + (*p - '0');
+ if (p < p_end && *p == '\n')
+ mask = value;
+ break;
+ }
+ /* Search the start of the next line. */
+ for (; p < p_end && *p != '\n'; p++)
+ ;
+ if (p == p_end)
+ break;
+ p++;
+ }
+ }
+ close (fd);
+ }
+ }
+# endif
+ if (mask < 0)
+ {
+ /* Create a temporary file and inspect its access permissions. */
+ const char *tmpdir = getenv ("TMPDIR");
+ if (tmpdir == NULL || *tmpdir == '\0')
+ tmpdir = "/tmp";
+ size_t tmpdir_length = strlen (tmpdir);
+ char *temp_filename = (char *) malloc (tmpdir_length + 15 + 1);
+ if (temp_filename != NULL)
+ {
+ memcpy (temp_filename, tmpdir, tmpdir_length);
+ strcpy (temp_filename + tmpdir_length, "/gtumask.XXXXXX");
+ int fd = gen_register_open_temp (temp_filename, 0, O_RDWR,
+ S_IRWXU|S_IRWXG|S_IRWXO);
+ if (fd >= 0)
+ {
+ struct stat statbuf;
+ if (fstat (fd, &statbuf) >= 0)
+ mask = (S_IRWXU|S_IRWXG|S_IRWXO) & ~statbuf.st_mode;
+ close_temp (fd);
+ cleanup_temporary_file (temp_filename, false);
+ }
+ free (temp_filename);
+ }
+ }
+ if (mask < 0)
+ {
+ /* We still don't know! Assume a paranoid user. */
+ mask = 077;
+ }
+# if ASSUME_UMASK_CONSTANT
+ cached_umask = mask;
+# endif
+ return mask;
+#endif
+}
diff --git a/lib/glthread/thread.h b/lib/glthread/thread.h
index 99a5074..4077b45 100644
--- a/lib/glthread/thread.h
+++ b/lib/glthread/thread.h
@@ -237,7 +237,7 @@ extern const gl_thread_t gl_null_thread;
(pthread_in_use () ? (void *) pthread_self () : NULL)
# endif
# define gl_thread_exit(RETVAL) \
- (pthread_in_use () ? pthread_exit (RETVAL) : 0)
+ (void) (pthread_in_use () ? (pthread_exit (RETVAL), 0) : 0)
# if HAVE_PTHREAD_ATFORK
# define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) \
@@ -301,7 +301,7 @@ typedef int gl_thread_t;
# define gl_thread_self() 0
# define gl_thread_self_pointer() \
((void *) gl_thread_self ())
-# define gl_thread_exit(RETVAL) 0
+# define gl_thread_exit(RETVAL) (void)0
# define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) 0
#endif
diff --git a/lib/hash-triple-simple.c b/lib/hash-triple-simple.c
new file mode 100644
index 0000000..98728f2
--- a/dev/null
+++ b/lib/hash-triple-simple.c
@@ -0,0 +1,59 @@
+/* Hash functions for file-related triples: name, device, inode.
+ Copyright (C) 2007, 2009-2020 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 Jim Meyering */
+
+#include <config.h>
+
+/* Specification. */
+#include "hash-triple.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "hash-pjw.h"
+#include "same-inode.h"
+
+#define STREQ(a, b) (strcmp (a, b) == 0)
+
+/* Hash an F_triple, and *do* consider the file name. */
+size_t
+triple_hash (void const *x, size_t table_size)
+{
+ struct F_triple const *p = x;
+ size_t tmp = hash_pjw (p->name, table_size);
+
+ /* Ignoring the device number here should be fine. */
+ return (tmp ^ p->st_ino) % table_size;
+}
+
+/* Compare two F_triple structs. */
+bool
+triple_compare_ino_str (void const *x, void const *y)
+{
+ struct F_triple const *a = x;
+ struct F_triple const *b = y;
+ return (SAME_INODE (*a, *b) && STREQ (a->name, b->name)) ? true : false;
+}
+
+/* Free an F_triple. */
+void
+triple_free (void *x)
+{
+ struct F_triple *a = x;
+ free (a->name);
+ free (a);
+}
diff --git a/lib/hash-triple.c b/lib/hash-triple.c
index 560e442..ad1a559 100644
--- a/lib/hash-triple.c
+++ b/lib/hash-triple.c
@@ -18,28 +18,12 @@
#include <config.h>
+/* Specification. */
#include "hash-triple.h"
-#include <stdlib.h>
-#include <string.h>
-
-#include "hash-pjw.h"
#include "same.h"
#include "same-inode.h"
-#define STREQ(a, b) (strcmp (a, b) == 0)
-
-/* Hash an F_triple, and *do* consider the file name. */
-size_t
-triple_hash (void const *x, size_t table_size)
-{
- struct F_triple const *p = x;
- size_t tmp = hash_pjw (p->name, table_size);
-
- /* Ignoring the device number here should be fine. */
- return (tmp ^ p->st_ino) % table_size;
-}
-
/* Hash an F_triple, without considering the file name. */
size_t
triple_hash_no_name (void const *x, size_t table_size)
@@ -58,20 +42,3 @@ triple_compare (void const *x, void const *y)
struct F_triple const *b = y;
return (SAME_INODE (*a, *b) && same_name (a->name, b->name)) ? true : false;
}
-
-bool
-triple_compare_ino_str (void const *x, void const *y)
-{
- struct F_triple const *a = x;
- struct F_triple const *b = y;
- return (SAME_INODE (*a, *b) && STREQ (a->name, b->name)) ? true : false;
-}
-
-/* Free an F_triple. */
-void
-triple_free (void *x)
-{
- struct F_triple *a = x;
- free (a->name);
- free (a);
-}
diff --git a/lib/hash-triple.h b/lib/hash-triple.h
index 0658d81..16f5330 100644
--- a/lib/hash-triple.h
+++ b/lib/hash-triple.h
@@ -1,3 +1,21 @@
+/* Hash functions for file-related (name, device, inode) triples.
+ Copyright (C) 2007-2020 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 Jim Meyering, 2007. */
+
#ifndef HASH_TRIPLE_H
#define HASH_TRIPLE_H
@@ -13,12 +31,16 @@ struct F_triple
dev_t st_dev;
};
+/* Defined in module 'hash-triple-simple'. */
+
extern size_t triple_hash (void const *x, size_t table_size) _GL_ATTRIBUTE_PURE;
-extern size_t triple_hash_no_name (void const *x, size_t table_size)
- _GL_ATTRIBUTE_PURE;
-extern bool triple_compare (void const *x, void const *y);
extern bool triple_compare_ino_str (void const *x, void const *y)
_GL_ATTRIBUTE_PURE;
extern void triple_free (void *x);
+/* Defined in module 'hash-triple'. */
+extern size_t triple_hash_no_name (void const *x, size_t table_size)
+ _GL_ATTRIBUTE_PURE;
+extern bool triple_compare (void const *x, void const *y);
+
#endif
diff --git a/lib/idcache.h b/lib/idcache.h
index e7573b8..012c792 100644
--- a/lib/idcache.h
+++ b/lib/idcache.h
@@ -1,3 +1,21 @@
+/* Username <--> uid and groupname <--> gid conversions, with cache for speed.
+ Copyright (C) 2007-2020 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 Jim Meyering, 2007. */
+
#ifndef IDCACHE_H
# define IDCACHE_H 1
diff --git a/lib/ino-map.h b/lib/ino-map.h
index 1bdf886..9f7d5e8 100644
--- a/lib/ino-map.h
+++ b/lib/ino-map.h
@@ -1,3 +1,21 @@
+/* Maintain a mapping of ino_t numbers to small integers.
+ Copyright (C) 2010-2020 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 Paul Eggert, 2010. */
+
#ifndef _GL_INO_MAP_H
# define _GL_INO_MAP_H
diff --git a/lib/javacomp.c b/lib/javacomp.c
index 4717a5f..63efc2d 100644
--- a/lib/javacomp.c
+++ b/lib/javacomp.c
@@ -573,7 +573,7 @@ write_temp_file (struct temp_dir *tmpdir, const char *file_name,
FILE *fp;
register_temp_file (tmpdir, file_name);
- fp = fopen_temp (file_name, "we");
+ fp = fopen_temp (file_name, "we", false);
if (fp == NULL)
{
error (0, errno, _("failed to create \"%s\""), file_name);
diff --git a/lib/mini-gmp-gnulib.c b/lib/mini-gmp-gnulib.c
new file mode 100644
index 0000000..5019be5
--- a/dev/null
+++ b/lib/mini-gmp-gnulib.c
@@ -0,0 +1,37 @@
+/* Tailor mini-gmp.c for Gnulib-using applications.
+
+ Copyright 2018-2020 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/>. */
+
+#include <config.h>
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "mini-gmp.h"
+
+/* Pacify GCC -Wsuggest-attribute=const, malloc, pure. */
+#if 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
+# pragma GCC diagnostic ignored "-Wsuggest-attribute=const"
+# pragma GCC diagnostic ignored "-Wsuggest-attribute=malloc"
+# pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
+#endif
+
+/* Pacify GCC -Wunused-variable for variables used only in 'assert' calls. */
+#if defined NDEBUG && 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
+# pragma GCC diagnostic ignored "-Wunused-variable"
+#endif
+
+#include "mini-gmp.c"
diff --git a/lib/mini-gmp.c b/lib/mini-gmp.c
new file mode 100644
index 0000000..a7b7190
--- a/dev/null
+++ b/lib/mini-gmp.c
@@ -0,0 +1,4615 @@
+/* mini-gmp, a minimalistic implementation of a GNU GMP subset.
+
+ Contributed to the GNU project by Niels Möller
+
+Copyright 1991-1997, 1999-2020 Free Software Foundation, Inc.
+
+This file is part of the GNU MP Library.
+
+The GNU MP Library is free software; you can redistribute it and/or modify
+it under the terms of either:
+
+ * 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.
+
+or
+
+ * the GNU General Public License as published by the Free Software
+ Foundation; either version 2 of the License, or (at your option) any
+ later version.
+
+or both in parallel, as here.
+
+The GNU MP Library 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 copies of the GNU General Public License and the
+GNU Lesser General Public License along with the GNU MP Library. If not,
+see https://www.gnu.org/licenses/. */
+
+/* NOTE: All functions in this file which are not declared in
+ mini-gmp.h are internal, and are not intended to be compatible
+ neither with GMP nor with future versions of mini-gmp. */
+
+/* Much of the material copied from GMP files, including: gmp-impl.h,
+ longlong.h, mpn/generic/add_n.c, mpn/generic/addmul_1.c,
+ mpn/generic/lshift.c, mpn/generic/mul_1.c,
+ mpn/generic/mul_basecase.c, mpn/generic/rshift.c,
+ mpn/generic/sbpi1_div_qr.c, mpn/generic/sub_n.c,
+ mpn/generic/submul_1.c. */
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mini-gmp.h"
+
+#if !defined(MINI_GMP_DONT_USE_FLOAT_H)
+#include <float.h>
+#endif
+
+
+/* Macros */
+#define GMP_LIMB_BITS (sizeof(mp_limb_t) * CHAR_BIT)
+
+#define GMP_LIMB_MAX ((mp_limb_t) ~ (mp_limb_t) 0)
+#define GMP_LIMB_HIGHBIT ((mp_limb_t) 1 << (GMP_LIMB_BITS - 1))
+
+#define GMP_HLIMB_BIT ((mp_limb_t) 1 << (GMP_LIMB_BITS / 2))
+#define GMP_LLIMB_MASK (GMP_HLIMB_BIT - 1)
+
+#define GMP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT)
+#define GMP_ULONG_HIGHBIT ((unsigned long) 1 << (GMP_ULONG_BITS - 1))
+
+#define GMP_ABS(x) ((x) >= 0 ? (x) : -(x))
+#define GMP_NEG_CAST(T,x) (-((T)((x) + 1) - 1))
+
+#define GMP_MIN(a, b) ((a) < (b) ? (a) : (b))
+#define GMP_MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define GMP_CMP(a,b) (((a) > (b)) - ((a) < (b)))
+
+#if defined(DBL_MANT_DIG) && FLT_RADIX == 2
+#define GMP_DBL_MANT_BITS DBL_MANT_DIG
+#else
+#define GMP_DBL_MANT_BITS (53)
+#endif
+
+/* Return non-zero if xp,xsize and yp,ysize overlap.
+ If xp+xsize<=yp there's no overlap, or if yp+ysize<=xp there's no
+ overlap. If both these are false, there's an overlap. */
+#define GMP_MPN_OVERLAP_P(xp, xsize, yp, ysize) \
+ ((xp) + (xsize) > (yp) && (yp) + (ysize) > (xp))
+
+#define gmp_assert_nocarry(x) do { \
+ mp_limb_t __cy = (x); \
+ assert (__cy == 0); \
+ } while (0)
+
+#define gmp_clz(count, x) do { \
+ mp_limb_t __clz_x = (x); \
+ unsigned __clz_c = 0; \
+ int LOCAL_SHIFT_BITS = 8; \
+ if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS) \
+ for (; \
+ (__clz_x & ((mp_limb_t) 0xff << (GMP_LIMB_BITS - 8))) == 0; \
+ __clz_c += 8) \
+ { __clz_x <<= LOCAL_SHIFT_BITS; } \
+ for (; (__clz_x & GMP_LIMB_HIGHBIT) == 0; __clz_c++) \
+ __clz_x <<= 1; \
+ (count) = __clz_c; \
+ } while (0)
+
+#define gmp_ctz(count, x) do { \
+ mp_limb_t __ctz_x = (x); \
+ unsigned __ctz_c = 0; \
+ gmp_clz (__ctz_c, __ctz_x & - __ctz_x); \
+ (count) = GMP_LIMB_BITS - 1 - __ctz_c; \
+ } while (0)
+
+#define gmp_add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ mp_limb_t __x; \
+ __x = (al) + (bl); \
+ (sh) = (ah) + (bh) + (__x < (al)); \
+ (sl) = __x; \
+ } while (0)
+
+#define gmp_sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ mp_limb_t __x; \
+ __x = (al) - (bl); \
+ (sh) = (ah) - (bh) - ((al) < (bl)); \
+ (sl) = __x; \
+ } while (0)
+
+#define gmp_umul_ppmm(w1, w0, u, v) \
+ do { \
+ int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; \
+ if (sizeof(unsigned int) * CHAR_BIT >= 2 * GMP_LIMB_BITS) \
+ { \
+ unsigned int __ww = (unsigned int) (u) * (v); \
+ w0 = (mp_limb_t) __ww; \
+ w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \
+ } \
+ else if (GMP_ULONG_BITS >= 2 * GMP_LIMB_BITS) \
+ { \
+ unsigned long int __ww = (unsigned long int) (u) * (v); \
+ w0 = (mp_limb_t) __ww; \
+ w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \
+ } \
+ else { \
+ mp_limb_t __x0, __x1, __x2, __x3; \
+ unsigned __ul, __vl, __uh, __vh; \
+ mp_limb_t __u = (u), __v = (v); \
+ \
+ __ul = __u & GMP_LLIMB_MASK; \
+ __uh = __u >> (GMP_LIMB_BITS / 2); \
+ __vl = __v & GMP_LLIMB_MASK; \
+ __vh = __v >> (GMP_LIMB_BITS / 2); \
+ \
+ __x0 = (mp_limb_t) __ul * __vl; \
+ __x1 = (mp_limb_t) __ul * __vh; \
+ __x2 = (mp_limb_t) __uh * __vl; \
+ __x3 = (mp_limb_t) __uh * __vh; \
+ \
+ __x1 += __x0 >> (GMP_LIMB_BITS / 2);/* this can't give carry */ \
+ __x1 += __x2; /* but this indeed can */ \
+ if (__x1 < __x2) /* did we get it? */ \
+ __x3 += GMP_HLIMB_BIT; /* yes, add it in the proper pos. */ \
+ \
+ (w1) = __x3 + (__x1 >> (GMP_LIMB_BITS / 2)); \
+ (w0) = (__x1 << (GMP_LIMB_BITS / 2)) + (__x0 & GMP_LLIMB_MASK); \
+ } \
+ } while (0)
+
+#define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di) \
+ do { \
+ mp_limb_t _qh, _ql, _r, _mask; \
+ gmp_umul_ppmm (_qh, _ql, (nh), (di)); \
+ gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl)); \
+ _r = (nl) - _qh * (d); \
+ _mask = -(mp_limb_t) (_r > _ql); /* both > and >= are OK */ \
+ _qh += _mask; \
+ _r += _mask & (d); \
+ if (_r >= (d)) \
+ { \
+ _r -= (d); \
+ _qh++; \
+ } \
+ \
+ (r) = _r; \
+ (q) = _qh; \
+ } while (0)
+
+#define gmp_udiv_qr_3by2(q, r1, r0, n2, n1, n0, d1, d0, dinv) \
+ do { \
+ mp_limb_t _q0, _t1, _t0, _mask; \
+ gmp_umul_ppmm ((q), _q0, (n2), (dinv)); \
+ gmp_add_ssaaaa ((q), _q0, (q), _q0, (n2), (n1)); \
+ \
+ /* Compute the two most significant limbs of n - q'd */ \
+ (r1) = (n1) - (d1) * (q); \
+ gmp_sub_ddmmss ((r1), (r0), (r1), (n0), (d1), (d0)); \
+ gmp_umul_ppmm (_t1, _t0, (d0), (q)); \
+ gmp_sub_ddmmss ((r1), (r0), (r1), (r0), _t1, _t0); \
+ (q)++; \
+ \
+ /* Conditionally adjust q and the remainders */ \
+ _mask = - (mp_limb_t) ((r1) >= _q0); \
+ (q) += _mask; \
+ gmp_add_ssaaaa ((r1), (r0), (r1), (r0), _mask & (d1), _mask & (d0)); \
+ if ((r1) >= (d1)) \
+ { \
+ if ((r1) > (d1) || (r0) >= (d0)) \
+ { \
+ (q)++; \
+ gmp_sub_ddmmss ((r1), (r0), (r1), (r0), (d1), (d0)); \
+ } \
+ } \
+ } while (0)
+
+/* Swap macros. */
+#define MP_LIMB_T_SWAP(x, y) \
+ do { \
+ mp_limb_t __mp_limb_t_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mp_limb_t_swap__tmp; \
+ } while (0)
+#define MP_SIZE_T_SWAP(x, y) \
+ do { \
+ mp_size_t __mp_size_t_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mp_size_t_swap__tmp; \
+ } while (0)
+#define MP_BITCNT_T_SWAP(x,y) \
+ do { \
+ mp_bitcnt_t __mp_bitcnt_t_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mp_bitcnt_t_swap__tmp; \
+ } while (0)
+#define MP_PTR_SWAP(x, y) \
+ do { \
+ mp_ptr __mp_ptr_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mp_ptr_swap__tmp; \
+ } while (0)
+#define MP_SRCPTR_SWAP(x, y) \
+ do { \
+ mp_srcptr __mp_srcptr_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mp_srcptr_swap__tmp; \
+ } while (0)
+
+#define MPN_PTR_SWAP(xp,xs, yp,ys) \
+ do { \
+ MP_PTR_SWAP (xp, yp); \
+ MP_SIZE_T_SWAP (xs, ys); \
+ } while(0)
+#define MPN_SRCPTR_SWAP(xp,xs, yp,ys) \
+ do { \
+ MP_SRCPTR_SWAP (xp, yp); \
+ MP_SIZE_T_SWAP (xs, ys); \
+ } while(0)
+
+#define MPZ_PTR_SWAP(x, y) \
+ do { \
+ mpz_ptr __mpz_ptr_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mpz_ptr_swap__tmp; \
+ } while (0)
+#define MPZ_SRCPTR_SWAP(x, y) \
+ do { \
+ mpz_srcptr __mpz_srcptr_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mpz_srcptr_swap__tmp; \
+ } while (0)
+
+const int mp_bits_per_limb = GMP_LIMB_BITS;
+
+
+/* Memory allocation and other helper functions. */
+static void
+gmp_die (const char *msg)
+{
+ fprintf (stderr, "%s\n", msg);
+ abort();
+}
+
+static void *
+gmp_default_alloc (size_t size)
+{
+ void *p;
+
+ assert (size > 0);
+
+ p = malloc (size);
+ if (!p)
+ gmp_die("gmp_default_alloc: Virtual memory exhausted.");
+
+ return p;
+}
+
+static void *
+gmp_default_realloc (void *old, size_t unused_old_size, size_t new_size)
+{
+ void * p;
+
+ p = realloc (old, new_size);
+
+ if (!p)
+ gmp_die("gmp_default_realloc: Virtual memory exhausted.");
+
+ return p;
+}
+
+static void
+gmp_default_free (void *p, size_t unused_size)
+{
+ free (p);
+}
+
+static void * (*gmp_allocate_func) (size_t) = gmp_default_alloc;
+static void * (*gmp_reallocate_func) (void *, size_t, size_t) = gmp_default_realloc;
+static void (*gmp_free_func) (void *, size_t) = gmp_default_free;
+
+void
+mp_get_memory_functions (void *(**alloc_func) (size_t),
+ void *(**realloc_func) (void *, size_t, size_t),
+ void (**free_func) (void *, size_t))
+{
+ if (alloc_func)
+ *alloc_func = gmp_allocate_func;
+
+ if (realloc_func)
+ *realloc_func = gmp_reallocate_func;
+
+ if (free_func)
+ *free_func = gmp_free_func;
+}
+
+void
+mp_set_memory_functions (void *(*alloc_func) (size_t),
+ void *(*realloc_func) (void *, size_t, size_t),
+ void (*free_func) (void *, size_t))
+{
+ if (!alloc_func)
+ alloc_func = gmp_default_alloc;
+ if (!realloc_func)
+ realloc_func = gmp_default_realloc;
+ if (!free_func)
+ free_func = gmp_default_free;
+
+ gmp_allocate_func = alloc_func;
+ gmp_reallocate_func = realloc_func;
+ gmp_free_func = free_func;
+}
+
+#define gmp_alloc(size) ((*gmp_allocate_func)((size)))
+#define gmp_free(p, size) ((*gmp_free_func) ((p), (size)))
+#define gmp_realloc(ptr, old_size, size) ((*gmp_reallocate_func)(ptr, old_size, size))
+
+static mp_ptr
+gmp_alloc_limbs (mp_size_t size)
+{
+ return (mp_ptr) gmp_alloc (size * sizeof (mp_limb_t));
+}
+
+static mp_ptr
+gmp_realloc_limbs (mp_ptr old, mp_size_t old_size, mp_size_t size)
+{
+ assert (size > 0);
+ return (mp_ptr) gmp_realloc (old, old_size * sizeof (mp_limb_t), size * sizeof (mp_limb_t));
+}
+
+static void
+gmp_free_limbs (mp_ptr old, mp_size_t size)
+{
+ gmp_free (old, size * sizeof (mp_limb_t));
+}
+
+
+/* MPN interface */
+
+void
+mpn_copyi (mp_ptr d, mp_srcptr s, mp_size_t n)
+{
+ mp_size_t i;
+ for (i = 0; i < n; i++)
+ d[i] = s[i];
+}
+
+void
+mpn_copyd (mp_ptr d, mp_srcptr s, mp_size_t n)
+{
+ while (--n >= 0)
+ d[n] = s[n];
+}
+
+int
+mpn_cmp (mp_srcptr ap, mp_srcptr bp, mp_size_t n)
+{
+ while (--n >= 0)
+ {
+ if (ap[n] != bp[n])
+ return ap[n] > bp[n] ? 1 : -1;
+ }
+ return 0;
+}
+
+static int
+mpn_cmp4 (mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn)
+{
+ if (an != bn)
+ return an < bn ? -1 : 1;
+ else
+ return mpn_cmp (ap, bp, an);
+}
+
+static mp_size_t
+mpn_normalized_size (mp_srcptr xp, mp_size_t n)
+{
+ while (n > 0 && xp[n-1] == 0)
+ --n;
+ return n;
+}
+
+int
+mpn_zero_p(mp_srcptr rp, mp_size_t n)
+{
+ return mpn_normalized_size (rp, n) == 0;
+}
+
+void
+mpn_zero (mp_ptr rp, mp_size_t n)
+{
+ while (--n >= 0)
+ rp[n] = 0;
+}
+
+mp_limb_t
+mpn_add_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b)
+{
+ mp_size_t i;
+
+ assert (n > 0);
+ i = 0;
+ do
+ {
+ mp_limb_t r = ap[i] + b;
+ /* Carry out */
+ b = (r < b);
+ rp[i] = r;
+ }
+ while (++i < n);
+
+ return b;
+}
+
+mp_limb_t
+mpn_add_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n)
+{
+ mp_size_t i;
+ mp_limb_t cy;
+
+ for (i = 0, cy = 0; i < n; i++)
+ {
+ mp_limb_t a, b, r;
+ a = ap[i]; b = bp[i];
+ r = a + cy;
+ cy = (r < cy);
+ r += b;
+ cy += (r < b);
+ rp[i] = r;
+ }
+ return cy;
+}
+
+mp_limb_t
+mpn_add (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn)
+{
+ mp_limb_t cy;
+
+ assert (an >= bn);
+
+ cy = mpn_add_n (rp, ap, bp, bn);
+ if (an > bn)
+ cy = mpn_add_1 (rp + bn, ap + bn, an - bn, cy);
+ return cy;
+}
+
+mp_limb_t
+mpn_sub_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b)
+{
+ mp_size_t i;
+
+ assert (n > 0);
+
+ i = 0;
+ do
+ {
+ mp_limb_t a = ap[i];
+ /* Carry out */
+ mp_limb_t cy = a < b;
+ rp[i] = a - b;
+ b = cy;
+ }
+ while (++i < n);
+
+ return b;
+}
+
+mp_limb_t
+mpn_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n)
+{
+ mp_size_t i;
+ mp_limb_t cy;
+
+ for (i = 0, cy = 0; i < n; i++)
+ {
+ mp_limb_t a, b;
+ a = ap[i]; b = bp[i];
+ b += cy;
+ cy = (b < cy);
+ cy += (a < b);
+ rp[i] = a - b;
+ }
+ return cy;
+}
+
+mp_limb_t
+mpn_sub (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn)
+{
+ mp_limb_t cy;
+
+ assert (an >= bn);
+
+ cy = mpn_sub_n (rp, ap, bp, bn);
+ if (an > bn)
+ cy = mpn_sub_1 (rp + bn, ap + bn, an - bn, cy);
+ return cy;
+}
+
+mp_limb_t
+mpn_mul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl)
+{
+ mp_limb_t ul, cl, hpl, lpl;
+
+ assert (n >= 1);
+
+ cl = 0;
+ do
+ {
+ ul = *up++;
+ gmp_umul_ppmm (hpl, lpl, ul, vl);
+
+ lpl += cl;
+ cl = (lpl < cl) + hpl;
+
+ *rp++ = lpl;
+ }
+ while (--n != 0);
+
+ return cl;
+}
+
+mp_limb_t
+mpn_addmul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl)
+{
+ mp_limb_t ul, cl, hpl, lpl, rl;
+
+ assert (n >= 1);
+
+ cl = 0;
+ do
+ {
+ ul = *up++;
+ gmp_umul_ppmm (hpl, lpl, ul, vl);
+
+ lpl += cl;
+ cl = (lpl < cl) + hpl;
+
+ rl = *rp;
+ lpl = rl + lpl;
+ cl += lpl < rl;
+ *rp++ = lpl;
+ }
+ while (--n != 0);
+
+ return cl;
+}
+
+mp_limb_t
+mpn_submul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl)
+{
+ mp_limb_t ul, cl, hpl, lpl, rl;
+
+ assert (n >= 1);
+
+ cl = 0;
+ do
+ {
+ ul = *up++;
+ gmp_umul_ppmm (hpl, lpl, ul, vl);
+
+ lpl += cl;
+ cl = (lpl < cl) + hpl;
+
+ rl = *rp;
+ lpl = rl - lpl;
+ cl += lpl > rl;
+ *rp++ = lpl;
+ }
+ while (--n != 0);
+
+ return cl;
+}
+
+mp_limb_t
+mpn_mul (mp_ptr rp, mp_srcptr up, mp_size_t un, mp_srcptr vp, mp_size_t vn)
+{
+ assert (un >= vn);
+ assert (vn >= 1);
+ assert (!GMP_MPN_OVERLAP_P(rp, un + vn, up, un));
+ assert (!GMP_MPN_OVERLAP_P(rp, un + vn, vp, vn));
+
+ /* We first multiply by the low order limb. This result can be
+ stored, not added, to rp. We also avoid a loop for zeroing this
+ way. */
+
+ rp[un] = mpn_mul_1 (rp, up, un, vp[0]);
+
+ /* Now accumulate the product of up[] and the next higher limb from
+ vp[]. */
+
+ while (--vn >= 1)
+ {
+ rp += 1, vp += 1;
+ rp[un] = mpn_addmul_1 (rp, up, un, vp[0]);
+ }
+ return rp[un];
+}
+
+void
+mpn_mul_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n)
+{
+ mpn_mul (rp, ap, n, bp, n);
+}
+
+void
+mpn_sqr (mp_ptr rp, mp_srcptr ap, mp_size_t n)
+{
+ mpn_mul (rp, ap, n, ap, n);
+}
+
+mp_limb_t
+mpn_lshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt)
+{
+ mp_limb_t high_limb, low_limb;
+ unsigned int tnc;
+ mp_limb_t retval;
+
+ assert (n >= 1);
+ assert (cnt >= 1);
+ assert (cnt < GMP_LIMB_BITS);
+
+ up += n;
+ rp += n;
+
+ tnc = GMP_LIMB_BITS - cnt;
+ low_limb = *--up;
+ retval = low_limb >> tnc;
+ high_limb = (low_limb << cnt);
+
+ while (--n != 0)
+ {
+ low_limb = *--up;
+ *--rp = high_limb | (low_limb >> tnc);
+ high_limb = (low_limb << cnt);
+ }
+ *--rp = high_limb;
+
+ return retval;
+}
+
+mp_limb_t
+mpn_rshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt)
+{
+ mp_limb_t high_limb, low_limb;
+ unsigned int tnc;
+ mp_limb_t retval;
+
+ assert (n >= 1);
+ assert (cnt >= 1);
+ assert (cnt < GMP_LIMB_BITS);
+
+ tnc = GMP_LIMB_BITS - cnt;
+ high_limb = *up++;
+ retval = (high_limb << tnc);
+ low_limb = high_limb >> cnt;
+
+ while (--n != 0)
+ {
+ high_limb = *up++;
+ *rp++ = low_limb | (high_limb << tnc);
+ low_limb = high_limb >> cnt;
+ }
+ *rp = low_limb;
+
+ return retval;
+}
+
+static mp_bitcnt_t
+mpn_common_scan (mp_limb_t limb, mp_size_t i, mp_srcptr up, mp_size_t un,
+ mp_limb_t ux)
+{
+ unsigned cnt;
+
+ assert (ux == 0 || ux == GMP_LIMB_MAX);
+ assert (0 <= i && i <= un );
+
+ while (limb == 0)
+ {
+ i++;
+ if (i == un)
+ return (ux == 0 ? ~(mp_bitcnt_t) 0 : un * GMP_LIMB_BITS);
+ limb = ux ^ up[i];
+ }
+ gmp_ctz (cnt, limb);
+ return (mp_bitcnt_t) i * GMP_LIMB_BITS + cnt;
+}
+
+mp_bitcnt_t
+mpn_scan1 (mp_srcptr ptr, mp_bitcnt_t bit)
+{
+ mp_size_t i;
+ i = bit / GMP_LIMB_BITS;
+
+ return mpn_common_scan ( ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)),
+ i, ptr, i, 0);
+}
+
+mp_bitcnt_t
+mpn_scan0 (mp_srcptr ptr, mp_bitcnt_t bit)
+{
+ mp_size_t i;
+ i = bit / GMP_LIMB_BITS;
+
+ return mpn_common_scan (~ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)),
+ i, ptr, i, GMP_LIMB_MAX);
+}
+
+void
+mpn_com (mp_ptr rp, mp_srcptr up, mp_size_t n)
+{
+ while (--n >= 0)
+ *rp++ = ~ *up++;
+}
+
+mp_limb_t
+mpn_neg (mp_ptr rp, mp_srcptr up, mp_size_t n)
+{
+ while (*up == 0)
+ {
+ *rp = 0;
+ if (!--n)
+ return 0;
+ ++up; ++rp;
+ }
+ *rp = - *up;
+ mpn_com (++rp, ++up, --n);
+ return 1;
+}
+
+
+/* MPN division interface. */
+
+/* The 3/2 inverse is defined as
+
+ m = floor( (B^3-1) / (B u1 + u0)) - B
+*/
+mp_limb_t
+mpn_invert_3by2 (mp_limb_t u1, mp_limb_t u0)
+{
+ mp_limb_t r, m;
+
+ {
+ mp_limb_t p, ql;
+ unsigned ul, uh, qh;
+
+ /* For notation, let b denote the half-limb base, so that B = b^2.
+ Split u1 = b uh + ul. */
+ ul = u1 & GMP_LLIMB_MASK;
+ uh = u1 >> (GMP_LIMB_BITS / 2);
+
+ /* Approximation of the high half of quotient. Differs from the 2/1
+ inverse of the half limb uh, since we have already subtracted
+ u0. */
+ qh = (u1 ^ GMP_LIMB_MAX) / uh;
+
+ /* Adjust to get a half-limb 3/2 inverse, i.e., we want
+
+ qh' = floor( (b^3 - 1) / u) - b = floor ((b^3 - b u - 1) / u
+ = floor( (b (~u) + b-1) / u),
+
+ and the remainder
+
+ r = b (~u) + b-1 - qh (b uh + ul)
+ = b (~u - qh uh) + b-1 - qh ul
+
+ Subtraction of qh ul may underflow, which implies adjustments.
+ But by normalization, 2 u >= B > qh ul, so we need to adjust by
+ at most 2.
+ */
+
+ r = ((~u1 - (mp_limb_t) qh * uh) << (GMP_LIMB_BITS / 2)) | GMP_LLIMB_MASK;
+
+ p = (mp_limb_t) qh * ul;
+ /* Adjustment steps taken from udiv_qrnnd_c */
+ if (r < p)
+ {
+ qh--;
+ r += u1;
+ if (r >= u1) /* i.e. we didn't get carry when adding to r */
+ if (r < p)
+ {
+ qh--;
+ r += u1;
+ }
+ }
+ r -= p;
+
+ /* Low half of the quotient is
+
+ ql = floor ( (b r + b-1) / u1).
+
+ This is a 3/2 division (on half-limbs), for which qh is a
+ suitable inverse. */
+
+ p = (r >> (GMP_LIMB_BITS / 2)) * qh + r;
+ /* Unlike full-limb 3/2, we can add 1 without overflow. For this to
+ work, it is essential that ql is a full mp_limb_t. */
+ ql = (p >> (GMP_LIMB_BITS / 2)) + 1;
+
+ /* By the 3/2 trick, we don't need the high half limb. */
+ r = (r << (GMP_LIMB_BITS / 2)) + GMP_LLIMB_MASK - ql * u1;
+
+ if (r >= (GMP_LIMB_MAX & (p << (GMP_LIMB_BITS / 2))))
+ {
+ ql--;
+ r += u1;
+ }
+ m = ((mp_limb_t) qh << (GMP_LIMB_BITS / 2)) + ql;
+ if (r >= u1)
+ {
+ m++;
+ r -= u1;
+ }
+ }
+
+ /* Now m is the 2/1 inverse of u1. If u0 > 0, adjust it to become a
+ 3/2 inverse. */
+ if (u0 > 0)
+ {
+ mp_limb_t th, tl;
+ r = ~r;
+ r += u0;
+ if (r < u0)
+ {
+ m--;
+ if (r >= u1)
+ {
+ m--;
+ r -= u1;
+ }
+ r -= u1;
+ }
+ gmp_umul_ppmm (th, tl, u0, m);
+ r += th;
+ if (r < th)
+ {
+ m--;
+ m -= ((r > u1) | ((r == u1) & (tl > u0)));
+ }
+ }
+
+ return m;
+}
+
+struct gmp_div_inverse
+{
+ /* Normalization shift count. */
+ unsigned shift;
+ /* Normalized divisor (d0 unused for mpn_div_qr_1) */
+ mp_limb_t d1, d0;
+ /* Inverse, for 2/1 or 3/2. */
+ mp_limb_t di;
+};
+
+static void
+mpn_div_qr_1_invert (struct gmp_div_inverse *inv, mp_limb_t d)
+{
+ unsigned shift;
+
+ assert (d > 0);
+ gmp_clz (shift, d);
+ inv->shift = shift;
+ inv->d1 = d << shift;
+ inv->di = mpn_invert_limb (inv->d1);
+}
+
+static void
+mpn_div_qr_2_invert (struct gmp_div_inverse *inv,
+ mp_limb_t d1, mp_limb_t d0)
+{
+ unsigned shift;
+
+ assert (d1 > 0);
+ gmp_clz (shift, d1);
+ inv->shift = shift;
+ if (shift > 0)
+ {
+ d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift));
+ d0 <<= shift;
+ }
+ inv->d1 = d1;
+ inv->d0 = d0;
+ inv->di = mpn_invert_3by2 (d1, d0);
+}
+
+static void
+mpn_div_qr_invert (struct gmp_div_inverse *inv,
+ mp_srcptr dp, mp_size_t dn)
+{
+ assert (dn > 0);
+
+ if (dn == 1)
+ mpn_div_qr_1_invert (inv, dp[0]);
+ else if (dn == 2)
+ mpn_div_qr_2_invert (inv, dp[1], dp[0]);
+ else
+ {
+ unsigned shift;
+ mp_limb_t d1, d0;
+
+ d1 = dp[dn-1];
+ d0 = dp[dn-2];
+ assert (d1 > 0);
+ gmp_clz (shift, d1);
+ inv->shift = shift;
+ if (shift > 0)
+ {
+ d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift));
+ d0 = (d0 << shift) | (dp[dn-3] >> (GMP_LIMB_BITS - shift));
+ }
+ inv->d1 = d1;
+ inv->d0 = d0;
+ inv->di = mpn_invert_3by2 (d1, d0);
+ }
+}
+
+/* Not matching current public gmp interface, rather corresponding to
+ the sbpi1_div_* functions. */
+static mp_limb_t
+mpn_div_qr_1_preinv (mp_ptr qp, mp_srcptr np, mp_size_t nn,
+ const struct gmp_div_inverse *inv)
+{
+ mp_limb_t d, di;
+ mp_limb_t r;
+ mp_ptr tp = NULL;
+ mp_size_t tn = 0;
+
+ if (inv->shift > 0)
+ {
+ /* Shift, reusing qp area if possible. In-place shift if qp == np. */
+ tp = qp;
+ if (!tp)
+ {
+ tn = nn;
+ tp = gmp_alloc_limbs (tn);
+ }
+ r = mpn_lshift (tp, np, nn, inv->shift);
+ np = tp;
+ }
+ else
+ r = 0;
+
+ d = inv->d1;
+ di = inv->di;
+ while (--nn >= 0)
+ {
+ mp_limb_t q;
+
+ gmp_udiv_qrnnd_preinv (q, r, r, np[nn], d, di);
+ if (qp)
+ qp[nn] = q;
+ }
+ if (tn)
+ gmp_free_limbs (tp, tn);
+
+ return r >> inv->shift;
+}
+
+static void
+mpn_div_qr_2_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn,
+ const struct gmp_div_inverse *inv)
+{
+ unsigned shift;
+ mp_size_t i;
+ mp_limb_t d1, d0, di, r1, r0;
+
+ assert (nn >= 2);
+ shift = inv->shift;
+ d1 = inv->d1;
+ d0 = inv->d0;
+ di = inv->di;
+
+ if (shift > 0)
+ r1 = mpn_lshift (np, np, nn, shift);
+ else
+ r1 = 0;
+
+ r0 = np[nn - 1];
+
+ i = nn - 2;
+ do
+ {
+ mp_limb_t n0, q;
+ n0 = np[i];
+ gmp_udiv_qr_3by2 (q, r1, r0, r1, r0, n0, d1, d0, di);
+
+ if (qp)
+ qp[i] = q;
+ }
+ while (--i >= 0);
+
+ if (shift > 0)
+ {
+ assert ((r0 & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - shift))) == 0);
+ r0 = (r0 >> shift) | (r1 << (GMP_LIMB_BITS - shift));
+ r1 >>= shift;
+ }
+
+ np[1] = r1;
+ np[0] = r0;
+}
+
+static void
+mpn_div_qr_pi1 (mp_ptr qp,
+ mp_ptr np, mp_size_t nn, mp_limb_t n1,
+ mp_srcptr dp, mp_size_t dn,
+ mp_limb_t dinv)
+{
+ mp_size_t i;
+
+ mp_limb_t d1, d0;
+ mp_limb_t cy, cy1;
+ mp_limb_t q;
+
+ assert (dn > 2);
+ assert (nn >= dn);
+
+ d1 = dp[dn - 1];
+ d0 = dp[dn - 2];
+
+ assert ((d1 & GMP_LIMB_HIGHBIT) != 0);
+ /* Iteration variable is the index of the q limb.
+ *
+ * We divide <n1, np[dn-1+i], np[dn-2+i], np[dn-3+i],..., np[i]>
+ * by <d1, d0, dp[dn-3], ..., dp[0] >
+ */
+
+ i = nn - dn;
+ do
+ {
+ mp_limb_t n0 = np[dn-1+i];
+
+ if (n1 == d1 && n0 == d0)
+ {
+ q = GMP_LIMB_MAX;
+ mpn_submul_1 (np+i, dp, dn, q);
+ n1 = np[dn-1+i]; /* update n1, last loop's value will now be invalid */
+ }
+ else
+ {
+ gmp_udiv_qr_3by2 (q, n1, n0, n1, n0, np[dn-2+i], d1, d0, dinv);
+
+ cy = mpn_submul_1 (np + i, dp, dn-2, q);
+
+ cy1 = n0 < cy;
+ n0 = n0 - cy;
+ cy = n1 < cy1;
+ n1 = n1 - cy1;
+ np[dn-2+i] = n0;
+
+ if (cy != 0)
+ {
+ n1 += d1 + mpn_add_n (np + i, np + i, dp, dn - 1);
+ q--;
+ }
+ }
+
+ if (qp)
+ qp[i] = q;
+ }
+ while (--i >= 0);
+
+ np[dn - 1] = n1;
+}
+
+static void
+mpn_div_qr_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn,
+ mp_srcptr dp, mp_size_t dn,
+ const struct gmp_div_inverse *inv)
+{
+ assert (dn > 0);
+ assert (nn >= dn);
+
+ if (dn == 1)
+ np[0] = mpn_div_qr_1_preinv (qp, np, nn, inv);
+ else if (dn == 2)
+ mpn_div_qr_2_preinv (qp, np, nn, inv);
+ else
+ {
+ mp_limb_t nh;
+ unsigned shift;
+
+ assert (inv->d1 == dp[dn-1]);
+ assert (inv->d0 == dp[dn-2]);
+ assert ((inv->d1 & GMP_LIMB_HIGHBIT) != 0);
+
+ shift = inv->shift;
+ if (shift > 0)
+ nh = mpn_lshift (np, np, nn, shift);
+ else
+ nh = 0;
+
+ mpn_div_qr_pi1 (qp, np, nn, nh, dp, dn, inv->di);
+
+ if (shift > 0)
+ gmp_assert_nocarry (mpn_rshift (np, np, dn, shift));
+ }
+}
+
+static void
+mpn_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn)
+{
+ struct gmp_div_inverse inv;
+ mp_ptr tp = NULL;
+
+ assert (dn > 0);
+ assert (nn >= dn);
+
+ mpn_div_qr_invert (&inv, dp, dn);
+ if (dn > 2 && inv.shift > 0)
+ {
+ tp = gmp_alloc_limbs (dn);
+ gmp_assert_nocarry (mpn_lshift (tp, dp, dn, inv.shift));
+ dp = tp;
+ }
+ mpn_div_qr_preinv (qp, np, nn, dp, dn, &inv);
+ if (tp)
+ gmp_free_limbs (tp, dn);
+}
+
+
+/* MPN base conversion. */
+static unsigned
+mpn_base_power_of_two_p (unsigned b)
+{
+ switch (b)
+ {
+ case 2: return 1;
+ case 4: return 2;
+ case 8: return 3;
+ case 16: return 4;
+ case 32: return 5;
+ case 64: return 6;
+ case 128: return 7;
+ case 256: return 8;
+ default: return 0;
+ }
+}
+
+struct mpn_base_info
+{
+ /* bb is the largest power of the base which fits in one limb, and
+ exp is the corresponding exponent. */
+ unsigned exp;
+ mp_limb_t bb;
+};
+
+static void
+mpn_get_base_info (struct mpn_base_info *info, mp_limb_t b)
+{
+ mp_limb_t m;
+ mp_limb_t p;
+ unsigned exp;
+
+ m = GMP_LIMB_MAX / b;
+ for (exp = 1, p = b; p <= m; exp++)
+ p *= b;
+
+ info->exp = exp;
+ info->bb = p;
+}
+
+static mp_bitcnt_t
+mpn_limb_size_in_base_2 (mp_limb_t u)
+{
+ unsigned shift;
+
+ assert (u > 0);
+ gmp_clz (shift, u);
+ return GMP_LIMB_BITS - shift;
+}
+
+static size_t
+mpn_get_str_bits (unsigned char *sp, unsigned bits, mp_srcptr up, mp_size_t un)
+{
+ unsigned char mask;
+ size_t sn, j;
+ mp_size_t i;
+ unsigned shift;
+
+ sn = ((un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1])
+ + bits - 1) / bits;
+
+ mask = (1U << bits) - 1;
+
+ for (i = 0, j = sn, shift = 0; j-- > 0;)
+ {
+ unsigned char digit = up[i] >> shift;
+
+ shift += bits;
+
+ if (shift >= GMP_LIMB_BITS && ++i < un)
+ {
+ shift -= GMP_LIMB_BITS;
+ digit |= up[i] << (bits - shift);
+ }
+ sp[j] = digit & mask;
+ }
+ return sn;
+}
+
+/* We generate digits from the least significant end, and reverse at
+ the end. */
+static size_t
+mpn_limb_get_str (unsigned char *sp, mp_limb_t w,
+ const struct gmp_div_inverse *binv)
+{
+ mp_size_t i;
+ for (i = 0; w > 0; i++)
+ {
+ mp_limb_t h, l, r;
+
+ h = w >> (GMP_LIMB_BITS - binv->shift);
+ l = w << binv->shift;
+
+ gmp_udiv_qrnnd_preinv (w, r, h, l, binv->d1, binv->di);
+ assert ((r & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - binv->shift))) == 0);
+ r >>= binv->shift;
+
+ sp[i] = r;
+ }
+ return i;
+}
+
+static size_t
+mpn_get_str_other (unsigned char *sp,
+ int base, const struct mpn_base_info *info,
+ mp_ptr up, mp_size_t un)
+{
+ struct gmp_div_inverse binv;
+ size_t sn;
+ size_t i;
+
+ mpn_div_qr_1_invert (&binv, base);
+
+ sn = 0;
+
+ if (un > 1)
+ {
+ struct gmp_div_inverse bbinv;
+ mpn_div_qr_1_invert (&bbinv, info->bb);
+
+ do
+ {
+ mp_limb_t w;
+ size_t done;
+ w = mpn_div_qr_1_preinv (up, up, un, &bbinv);
+ un -= (up[un-1] == 0);
+ done = mpn_limb_get_str (sp + sn, w, &binv);
+
+ for (sn += done; done < info->exp; done++)
+ sp[sn++] = 0;
+ }
+ while (un > 1);
+ }
+ sn += mpn_limb_get_str (sp + sn, up[0], &binv);
+
+ /* Reverse order */
+ for (i = 0; 2*i + 1 < sn; i++)
+ {
+ unsigned char t = sp[i];
+ sp[i] = sp[sn - i - 1];
+ sp[sn - i - 1] = t;
+ }
+
+ return sn;
+}
+
+size_t
+mpn_get_str (unsigned char *sp, int base, mp_ptr up, mp_size_t un)
+{
+ unsigned bits;
+
+ assert (un > 0);
+ assert (up[un-1] > 0);
+
+ bits = mpn_base_power_of_two_p (base);
+ if (bits)
+ return mpn_get_str_bits (sp, bits, up, un);
+ else
+ {
+ struct mpn_base_info info;
+
+ mpn_get_base_info (&info, base);
+ return mpn_get_str_other (sp, base, &info, up, un);
+ }
+}
+
+static mp_size_t
+mpn_set_str_bits (mp_ptr rp, const unsigned char *sp, size_t sn,
+ unsigned bits)
+{
+ mp_size_t rn;
+ size_t j;
+ unsigned shift;
+
+ for (j = sn, rn = 0, shift = 0; j-- > 0; )
+ {
+ if (shift == 0)
+ {
+ rp[rn++] = sp[j];
+ shift += bits;
+ }
+ else
+ {
+ rp[rn-1] |= (mp_limb_t) sp[j] << shift;
+ shift += bits;
+ if (shift >= GMP_LIMB_BITS)
+ {
+ shift -= GMP_LIMB_BITS;
+ if (shift > 0)
+ rp[rn++] = (mp_limb_t) sp[j] >> (bits - shift);
+ }
+ }
+ }
+ rn = mpn_normalized_size (rp, rn);
+ return rn;
+}
+
+/* Result is usually normalized, except for all-zero input, in which
+ case a single zero limb is written at *RP, and 1 is returned. */
+static mp_size_t
+mpn_set_str_other (mp_ptr rp, const unsigned char *sp, size_t sn,
+ mp_limb_t b, const struct mpn_base_info *info)
+{
+ mp_size_t rn;
+ mp_limb_t w;
+ unsigned k;
+ size_t j;
+
+ assert (sn > 0);
+
+ k = 1 + (sn - 1) % info->exp;
+
+ j = 0;
+ w = sp[j++];
+ while (--k != 0)
+ w = w * b + sp[j++];
+
+ rp[0] = w;
+
+ for (rn = 1; j < sn;)
+ {
+ mp_limb_t cy;
+
+ w = sp[j++];
+ for (k = 1; k < info->exp; k++)
+ w = w * b + sp[j++];
+
+ cy = mpn_mul_1 (rp, rp, rn, info->bb);
+ cy += mpn_add_1 (rp, rp, rn, w);
+ if (cy > 0)
+ rp[rn++] = cy;
+ }
+ assert (j == sn);
+
+ return rn;
+}
+
+mp_size_t
+mpn_set_str (mp_ptr rp, const unsigned char *sp, size_t sn, int base)
+{
+ unsigned bits;
+
+ if (sn == 0)
+ return 0;
+
+ bits = mpn_base_power_of_two_p (base);
+ if (bits)
+ return mpn_set_str_bits (rp, sp, sn, bits);
+ else
+ {
+ struct mpn_base_info info;
+
+ mpn_get_base_info (&info, base);
+ return mpn_set_str_other (rp, sp, sn, base, &info);
+ }
+}
+
+
+/* MPZ interface */
+void
+mpz_init (mpz_t r)
+{
+ static const mp_limb_t dummy_limb = GMP_LIMB_MAX & 0xc1a0;
+
+ r->_mp_alloc = 0;
+ r->_mp_size = 0;
+ r->_mp_d = (mp_ptr) &dummy_limb;
+}
+
+/* The utility of this function is a bit limited, since many functions
+ assigns the result variable using mpz_swap. */
+void
+mpz_init2 (mpz_t r, mp_bitcnt_t bits)
+{
+ mp_size_t rn;
+
+ bits -= (bits != 0); /* Round down, except if 0 */
+ rn = 1 + bits / GMP_LIMB_BITS;
+
+ r->_mp_alloc = rn;
+ r->_mp_size = 0;
+ r->_mp_d = gmp_alloc_limbs (rn);
+}
+
+void
+mpz_clear (mpz_t r)
+{
+ if (r->_mp_alloc)
+ gmp_free_limbs (r->_mp_d, r->_mp_alloc);
+}
+
+static mp_ptr
+mpz_realloc (mpz_t r, mp_size_t size)
+{
+ size = GMP_MAX (size, 1);
+
+ if (r->_mp_alloc)
+ r->_mp_d = gmp_realloc_limbs (r->_mp_d, r->_mp_alloc, size);
+ else
+ r->_mp_d = gmp_alloc_limbs (size);
+ r->_mp_alloc = size;
+
+ if (GMP_ABS (r->_mp_size) > size)
+ r->_mp_size = 0;
+
+ return r->_mp_d;
+}
+
+/* Realloc for an mpz_t WHAT if it has less than NEEDED limbs. */
+#define MPZ_REALLOC(z,n) ((n) > (z)->_mp_alloc \
+ ? mpz_realloc(z,n) \
+ : (z)->_mp_d)
+
+/* MPZ assignment and basic conversions. */
+void
+mpz_set_si (mpz_t r, signed long int x)
+{
+ if (x >= 0)
+ mpz_set_ui (r, x);
+ else /* (x < 0) */
+ if (GMP_LIMB_BITS < GMP_ULONG_BITS)
+ {
+ mpz_set_ui (r, GMP_NEG_CAST (unsigned long int, x));
+ mpz_neg (r, r);
+ }
+ else
+ {
+ r->_mp_size = -1;
+ MPZ_REALLOC (r, 1)[0] = GMP_NEG_CAST (unsigned long int, x);
+ }
+}
+
+void
+mpz_set_ui (mpz_t r, unsigned long int x)
+{
+ if (x > 0)
+ {
+ r->_mp_size = 1;
+ MPZ_REALLOC (r, 1)[0] = x;
+ if (GMP_LIMB_BITS < GMP_ULONG_BITS)
+ {
+ int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS;
+ while (x >>= LOCAL_GMP_LIMB_BITS)
+ {
+ ++ r->_mp_size;
+ MPZ_REALLOC (r, r->_mp_size)[r->_mp_size - 1] = x;
+ }
+ }
+ }
+ else
+ r->_mp_size = 0;
+}
+
+void
+mpz_set (mpz_t r, const mpz_t x)
+{
+ /* Allow the NOP r == x */
+ if (r != x)
+ {
+ mp_size_t n;
+ mp_ptr rp;
+
+ n = GMP_ABS (x->_mp_size);
+ rp = MPZ_REALLOC (r, n);
+
+ mpn_copyi (rp, x->_mp_d, n);
+ r->_mp_size = x->_mp_size;
+ }
+}
+
+void
+mpz_init_set_si (mpz_t r, signed long int x)
+{
+ mpz_init (r);
+ mpz_set_si (r, x);
+}
+
+void
+mpz_init_set_ui (mpz_t r, unsigned long int x)
+{
+ mpz_init (r);
+ mpz_set_ui (r, x);
+}
+
+void
+mpz_init_set (mpz_t r, const mpz_t x)
+{
+ mpz_init (r);
+ mpz_set (r, x);
+}
+
+int
+mpz_fits_slong_p (const mpz_t u)
+{
+ return mpz_cmp_si (u, LONG_MAX) <= 0 && mpz_cmp_si (u, LONG_MIN) >= 0;
+}
+
+static int
+mpn_absfits_ulong_p (mp_srcptr up, mp_size_t un)
+{
+ int ulongsize = GMP_ULONG_BITS / GMP_LIMB_BITS;
+ mp_limb_t ulongrem = 0;
+
+ if (GMP_ULONG_BITS % GMP_LIMB_BITS != 0)
+ ulongrem = (mp_limb_t) (ULONG_MAX >> GMP_LIMB_BITS * ulongsize) + 1;
+
+ return un <= ulongsize || (up[ulongsize] < ulongrem && un == ulongsize + 1);
+}
+
+int
+mpz_fits_ulong_p (const mpz_t u)
+{
+ mp_size_t us = u->_mp_size;
+
+ return us >= 0 && mpn_absfits_ulong_p (u->_mp_d, us);
+}
+
+int
+mpz_fits_sint_p (const mpz_t u)
+{
+ return mpz_cmp_si (u, INT_MAX) <= 0 && mpz_cmp_si (u, INT_MIN) >= 0;
+}
+
+int
+mpz_fits_uint_p (const mpz_t u)
+{
+ return u->_mp_size >= 0 && mpz_cmpabs_ui (u, UINT_MAX) <= 0;
+}
+
+int
+mpz_fits_sshort_p (const mpz_t u)
+{
+ return mpz_cmp_si (u, SHRT_MAX) <= 0 && mpz_cmp_si (u, SHRT_MIN) >= 0;
+}
+
+int
+mpz_fits_ushort_p (const mpz_t u)
+{
+ return u->_mp_size >= 0 && mpz_cmpabs_ui (u, USHRT_MAX) <= 0;
+}
+
+long int
+mpz_get_si (const mpz_t u)
+{
+ unsigned long r = mpz_get_ui (u);
+ unsigned long c = -LONG_MAX - LONG_MIN;
+
+ if (u->_mp_size < 0)
+ /* This expression is necessary to properly handle -LONG_MIN */
+ return -(long) c - (long) ((r - c) & LONG_MAX);
+ else
+ return (long) (r & LONG_MAX);
+}
+
+unsigned long int
+mpz_get_ui (const mpz_t u)
+{
+ if (GMP_LIMB_BITS < GMP_ULONG_BITS)
+ {
+ int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS;
+ unsigned long r = 0;
+ mp_size_t n = GMP_ABS (u->_mp_size);
+ n = GMP_MIN (n, 1 + (mp_size_t) (GMP_ULONG_BITS - 1) / GMP_LIMB_BITS);
+ while (--n >= 0)
+ r = (r << LOCAL_GMP_LIMB_BITS) + u->_mp_d[n];
+ return r;
+ }
+
+ return u->_mp_size == 0 ? 0 : u->_mp_d[0];
+}
+
+size_t
+mpz_size (const mpz_t u)
+{
+ return GMP_ABS (u->_mp_size);
+}
+
+mp_limb_t
+mpz_getlimbn (const mpz_t u, mp_size_t n)
+{
+ if (n >= 0 && n < GMP_ABS (u->_mp_size))
+ return u->_mp_d[n];
+ else
+ return 0;
+}
+
+void
+mpz_realloc2 (mpz_t x, mp_bitcnt_t n)
+{
+ mpz_realloc (x, 1 + (n - (n != 0)) / GMP_LIMB_BITS);
+}
+
+mp_srcptr
+mpz_limbs_read (mpz_srcptr x)
+{
+ return x->_mp_d;
+}
+
+mp_ptr
+mpz_limbs_modify (mpz_t x, mp_size_t n)
+{
+ assert (n > 0);
+ return MPZ_REALLOC (x, n);
+}
+
+mp_ptr
+mpz_limbs_write (mpz_t x, mp_size_t n)
+{
+ return mpz_limbs_modify (x, n);
+}
+
+void
+mpz_limbs_finish (mpz_t x, mp_size_t xs)
+{
+ mp_size_t xn;
+ xn = mpn_normalized_size (x->_mp_d, GMP_ABS (xs));
+ x->_mp_size = xs < 0 ? -xn : xn;
+}
+
+static mpz_srcptr
+mpz_roinit_normal_n (mpz_t x, mp_srcptr xp, mp_size_t xs)
+{
+ x->_mp_alloc = 0;
+ x->_mp_d = (mp_ptr) xp;
+ x->_mp_size = xs;
+ return x;
+}
+
+mpz_srcptr
+mpz_roinit_n (mpz_t x, mp_srcptr xp, mp_size_t xs)
+{
+ mpz_roinit_normal_n (x, xp, xs);
+ mpz_limbs_finish (x, xs);
+ return x;
+}
+
+
+/* Conversions and comparison to double. */
+void
+mpz_set_d (mpz_t r, double x)
+{
+ int sign;
+ mp_ptr rp;
+ mp_size_t rn, i;
+ double B;
+ double Bi;
+ mp_limb_t f;
+
+ /* x != x is true when x is a NaN, and x == x * 0.5 is true when x is
+ zero or infinity. */
+ if (x != x || x == x * 0.5)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ sign = x < 0.0 ;
+ if (sign)
+ x = - x;
+
+ if (x < 1.0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+ B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1);
+ Bi = 1.0 / B;
+ for (rn = 1; x >= B; rn++)
+ x *= Bi;
+
+ rp = MPZ_REALLOC (r, rn);
+
+ f = (mp_limb_t) x;
+ x -= f;
+ assert (x < 1.0);
+ i = rn-1;
+ rp[i] = f;
+ while (--i >= 0)
+ {
+ x = B * x;
+ f = (mp_limb_t) x;
+ x -= f;
+ assert (x < 1.0);
+ rp[i] = f;
+ }
+
+ r->_mp_size = sign ? - rn : rn;
+}
+
+void
+mpz_init_set_d (mpz_t r, double x)
+{
+ mpz_init (r);
+ mpz_set_d (r, x);
+}
+
+double
+mpz_get_d (const mpz_t u)
+{
+ int m;
+ mp_limb_t l;
+ mp_size_t un;
+ double x;
+ double B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1);
+
+ un = GMP_ABS (u->_mp_size);
+
+ if (un == 0)
+ return 0.0;
+
+ l = u->_mp_d[--un];
+ gmp_clz (m, l);
+ m = m + GMP_DBL_MANT_BITS - GMP_LIMB_BITS;
+ if (m < 0)
+ l &= GMP_LIMB_MAX << -m;
+
+ for (x = l; --un >= 0;)
+ {
+ x = B*x;
+ if (m > 0) {
+ l = u->_mp_d[un];
+ m -= GMP_LIMB_BITS;
+ if (m < 0)
+ l &= GMP_LIMB_MAX << -m;
+ x += l;
+ }
+ }
+
+ if (u->_mp_size < 0)
+ x = -x;
+
+ return x;
+}
+
+int
+mpz_cmpabs_d (const mpz_t x, double d)
+{
+ mp_size_t xn;
+ double B, Bi;
+ mp_size_t i;
+
+ xn = x->_mp_size;
+ d = GMP_ABS (d);
+
+ if (xn != 0)
+ {
+ xn = GMP_ABS (xn);
+
+ B = 4.0 * (double) (GMP_LIMB_HIGHBIT >> 1);
+ Bi = 1.0 / B;
+
+ /* Scale d so it can be compared with the top limb. */
+ for (i = 1; i < xn; i++)
+ d *= Bi;
+
+ if (d >= B)
+ return -1;
+
+ /* Compare floor(d) to top limb, subtract and cancel when equal. */
+ for (i = xn; i-- > 0;)
+ {
+ mp_limb_t f, xl;
+
+ f = (mp_limb_t) d;
+ xl = x->_mp_d[i];
+ if (xl > f)
+ return 1;
+ else if (xl < f)
+ return -1;
+ d = B * (d - f);
+ }
+ }
+ return - (d > 0.0);
+}
+
+int
+mpz_cmp_d (const mpz_t x, double d)
+{
+ if (x->_mp_size < 0)
+ {
+ if (d >= 0.0)
+ return -1;
+ else
+ return -mpz_cmpabs_d (x, d);
+ }
+ else
+ {
+ if (d < 0.0)
+ return 1;
+ else
+ return mpz_cmpabs_d (x, d);
+ }
+}
+
+
+/* MPZ comparisons and the like. */
+int
+mpz_sgn (const mpz_t u)
+{
+ return GMP_CMP (u->_mp_size, 0);
+}
+
+int
+mpz_cmp_si (const mpz_t u, long v)
+{
+ mp_size_t usize = u->_mp_size;
+
+ if (v >= 0)
+ return mpz_cmp_ui (u, v);
+ else if (usize >= 0)
+ return 1;
+ else
+ return - mpz_cmpabs_ui (u, GMP_NEG_CAST (unsigned long int, v));
+}
+
+int
+mpz_cmp_ui (const mpz_t u, unsigned long v)
+{
+ mp_size_t usize = u->_mp_size;
+
+ if (usize < 0)
+ return -1;
+ else
+ return mpz_cmpabs_ui (u, v);
+}
+
+int
+mpz_cmp (const mpz_t a, const mpz_t b)
+{
+ mp_size_t asize = a->_mp_size;
+ mp_size_t bsize = b->_mp_size;
+
+ if (asize != bsize)
+ return (asize < bsize) ? -1 : 1;
+ else if (asize >= 0)
+ return mpn_cmp (a->_mp_d, b->_mp_d, asize);
+ else
+ return mpn_cmp (b->_mp_d, a->_mp_d, -asize);
+}
+
+int
+mpz_cmpabs_ui (const mpz_t u, unsigned long v)
+{
+ mp_size_t un = GMP_ABS (u->_mp_size);
+
+ if (! mpn_absfits_ulong_p (u->_mp_d, un))
+ return 1;
+ else
+ {
+ unsigned long uu = mpz_get_ui (u);
+ return GMP_CMP(uu, v);
+ }
+}
+
+int
+mpz_cmpabs (const mpz_t u, const mpz_t v)
+{
+ return mpn_cmp4 (u->_mp_d, GMP_ABS (u->_mp_size),
+ v->_mp_d, GMP_ABS (v->_mp_size));
+}
+
+void
+mpz_abs (mpz_t r, const mpz_t u)
+{
+ mpz_set (r, u);
+ r->_mp_size = GMP_ABS (r->_mp_size);
+}
+
+void
+mpz_neg (mpz_t r, const mpz_t u)
+{
+ mpz_set (r, u);
+ r->_mp_size = -r->_mp_size;
+}
+
+void
+mpz_swap (mpz_t u, mpz_t v)
+{
+ MP_SIZE_T_SWAP (u->_mp_size, v->_mp_size);
+ MP_SIZE_T_SWAP (u->_mp_alloc, v->_mp_alloc);
+ MP_PTR_SWAP (u->_mp_d, v->_mp_d);
+}
+
+
+/* MPZ addition and subtraction */
+
+
+void
+mpz_add_ui (mpz_t r, const mpz_t a, unsigned long b)
+{
+ mpz_t bb;
+ mpz_init_set_ui (bb, b);
+ mpz_add (r, a, bb);
+ mpz_clear (bb);
+}
+
+void
+mpz_sub_ui (mpz_t r, const mpz_t a, unsigned long b)
+{
+ mpz_ui_sub (r, b, a);
+ mpz_neg (r, r);
+}
+
+void
+mpz_ui_sub (mpz_t r, unsigned long a, const mpz_t b)
+{
+ mpz_neg (r, b);
+ mpz_add_ui (r, r, a);
+}
+
+static mp_size_t
+mpz_abs_add (mpz_t r, const mpz_t a, const mpz_t b)
+{
+ mp_size_t an = GMP_ABS (a->_mp_size);
+ mp_size_t bn = GMP_ABS (b->_mp_size);
+ mp_ptr rp;
+ mp_limb_t cy;
+
+ if (an < bn)
+ {
+ MPZ_SRCPTR_SWAP (a, b);
+ MP_SIZE_T_SWAP (an, bn);
+ }
+
+ rp = MPZ_REALLOC (r, an + 1);
+ cy = mpn_add (rp, a->_mp_d, an, b->_mp_d, bn);
+
+ rp[an] = cy;
+
+ return an + cy;
+}
+
+static mp_size_t
+mpz_abs_sub (mpz_t r, const mpz_t a, const mpz_t b)
+{
+ mp_size_t an = GMP_ABS (a->_mp_size);
+ mp_size_t bn = GMP_ABS (b->_mp_size);
+ int cmp;
+ mp_ptr rp;
+
+ cmp = mpn_cmp4 (a->_mp_d, an, b->_mp_d, bn);
+ if (cmp > 0)
+ {
+ rp = MPZ_REALLOC (r, an);
+ gmp_assert_nocarry (mpn_sub (rp, a->_mp_d, an, b->_mp_d, bn));
+ return mpn_normalized_size (rp, an);
+ }
+ else if (cmp < 0)
+ {
+ rp = MPZ_REALLOC (r, bn);
+ gmp_assert_nocarry (mpn_sub (rp, b->_mp_d, bn, a->_mp_d, an));
+ return -mpn_normalized_size (rp, bn);
+ }
+ else
+ return 0;
+}
+
+void
+mpz_add (mpz_t r, const mpz_t a, const mpz_t b)
+{
+ mp_size_t rn;
+
+ if ( (a->_mp_size ^ b->_mp_size) >= 0)
+ rn = mpz_abs_add (r, a, b);
+ else
+ rn = mpz_abs_sub (r, a, b);
+
+ r->_mp_size = a->_mp_size >= 0 ? rn : - rn;
+}
+
+void
+mpz_sub (mpz_t r, const mpz_t a, const mpz_t b)
+{
+ mp_size_t rn;
+
+ if ( (a->_mp_size ^ b->_mp_size) >= 0)
+ rn = mpz_abs_sub (r, a, b);
+ else
+ rn = mpz_abs_add (r, a, b);
+
+ r->_mp_size = a->_mp_size >= 0 ? rn : - rn;
+}
+
+
+/* MPZ multiplication */
+void
+mpz_mul_si (mpz_t r, const mpz_t u, long int v)
+{
+ if (v < 0)
+ {
+ mpz_mul_ui (r, u, GMP_NEG_CAST (unsigned long int, v));
+ mpz_neg (r, r);
+ }
+ else
+ mpz_mul_ui (r, u, v);
+}
+
+void
+mpz_mul_ui (mpz_t r, const mpz_t u, unsigned long int v)
+{
+ mpz_t vv;
+ mpz_init_set_ui (vv, v);
+ mpz_mul (r, u, vv);
+ mpz_clear (vv);
+ return;
+}
+
+void
+mpz_mul (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ int sign;
+ mp_size_t un, vn, rn;
+ mpz_t t;
+ mp_ptr tp;
+
+ un = u->_mp_size;
+ vn = v->_mp_size;
+
+ if (un == 0 || vn == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ sign = (un ^ vn) < 0;
+
+ un = GMP_ABS (un);
+ vn = GMP_ABS (vn);
+
+ mpz_init2 (t, (un + vn) * GMP_LIMB_BITS);
+
+ tp = t->_mp_d;
+ if (un >= vn)
+ mpn_mul (tp, u->_mp_d, un, v->_mp_d, vn);
+ else
+ mpn_mul (tp, v->_mp_d, vn, u->_mp_d, un);
+
+ rn = un + vn;
+ rn -= tp[rn-1] == 0;
+
+ t->_mp_size = sign ? - rn : rn;
+ mpz_swap (r, t);
+ mpz_clear (t);
+}
+
+void
+mpz_mul_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bits)
+{
+ mp_size_t un, rn;
+ mp_size_t limbs;
+ unsigned shift;
+ mp_ptr rp;
+
+ un = GMP_ABS (u->_mp_size);
+ if (un == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ limbs = bits / GMP_LIMB_BITS;
+ shift = bits % GMP_LIMB_BITS;
+
+ rn = un + limbs + (shift > 0);
+ rp = MPZ_REALLOC (r, rn);
+ if (shift > 0)
+ {
+ mp_limb_t cy = mpn_lshift (rp + limbs, u->_mp_d, un, shift);
+ rp[rn-1] = cy;
+ rn -= (cy == 0);
+ }
+ else
+ mpn_copyd (rp + limbs, u->_mp_d, un);
+
+ mpn_zero (rp, limbs);
+
+ r->_mp_size = (u->_mp_size < 0) ? - rn : rn;
+}
+
+void
+mpz_addmul_ui (mpz_t r, const mpz_t u, unsigned long int v)
+{
+ mpz_t t;
+ mpz_init_set_ui (t, v);
+ mpz_mul (t, u, t);
+ mpz_add (r, r, t);
+ mpz_clear (t);
+}
+
+void
+mpz_submul_ui (mpz_t r, const mpz_t u, unsigned long int v)
+{
+ mpz_t t;
+ mpz_init_set_ui (t, v);
+ mpz_mul (t, u, t);
+ mpz_sub (r, r, t);
+ mpz_clear (t);
+}
+
+void
+mpz_addmul (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ mpz_t t;
+ mpz_init (t);
+ mpz_mul (t, u, v);
+ mpz_add (r, r, t);
+ mpz_clear (t);
+}
+
+void
+mpz_submul (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ mpz_t t;
+ mpz_init (t);
+ mpz_mul (t, u, v);
+ mpz_sub (r, r, t);
+ mpz_clear (t);
+}
+
+
+/* MPZ division */
+enum mpz_div_round_mode { GMP_DIV_FLOOR, GMP_DIV_CEIL, GMP_DIV_TRUNC };
+
+/* Allows q or r to be zero. Returns 1 iff remainder is non-zero. */
+static int
+mpz_div_qr (mpz_t q, mpz_t r,
+ const mpz_t n, const mpz_t d, enum mpz_div_round_mode mode)
+{
+ mp_size_t ns, ds, nn, dn, qs;
+ ns = n->_mp_size;
+ ds = d->_mp_size;
+
+ if (ds == 0)
+ gmp_die("mpz_div_qr: Divide by zero.");
+
+ if (ns == 0)
+ {
+ if (q)
+ q->_mp_size = 0;
+ if (r)
+ r->_mp_size = 0;
+ return 0;
+ }
+
+ nn = GMP_ABS (ns);
+ dn = GMP_ABS (ds);
+
+ qs = ds ^ ns;
+
+ if (nn < dn)
+ {
+ if (mode == GMP_DIV_CEIL && qs >= 0)
+ {
+ /* q = 1, r = n - d */
+ if (r)
+ mpz_sub (r, n, d);
+ if (q)
+ mpz_set_ui (q, 1);
+ }
+ else if (mode == GMP_DIV_FLOOR && qs < 0)
+ {
+ /* q = -1, r = n + d */
+ if (r)
+ mpz_add (r, n, d);
+ if (q)
+ mpz_set_si (q, -1);
+ }
+ else
+ {
+ /* q = 0, r = d */
+ if (r)
+ mpz_set (r, n);
+ if (q)
+ q->_mp_size = 0;
+ }
+ return 1;
+ }
+ else
+ {
+ mp_ptr np, qp;
+ mp_size_t qn, rn;
+ mpz_t tq, tr;
+
+ mpz_init_set (tr, n);
+ np = tr->_mp_d;
+
+ qn = nn - dn + 1;
+
+ if (q)
+ {
+ mpz_init2 (tq, qn * GMP_LIMB_BITS);
+ qp = tq->_mp_d;
+ }
+ else
+ qp = NULL;
+
+ mpn_div_qr (qp, np, nn, d->_mp_d, dn);
+
+ if (qp)
+ {
+ qn -= (qp[qn-1] == 0);
+
+ tq->_mp_size = qs < 0 ? -qn : qn;
+ }
+ rn = mpn_normalized_size (np, dn);
+ tr->_mp_size = ns < 0 ? - rn : rn;
+
+ if (mode == GMP_DIV_FLOOR && qs < 0 && rn != 0)
+ {
+ if (q)
+ mpz_sub_ui (tq, tq, 1);
+ if (r)
+ mpz_add (tr, tr, d);
+ }
+ else if (mode == GMP_DIV_CEIL && qs >= 0 && rn != 0)
+ {
+ if (q)
+ mpz_add_ui (tq, tq, 1);
+ if (r)
+ mpz_sub (tr, tr, d);
+ }
+
+ if (q)
+ {
+ mpz_swap (tq, q);
+ mpz_clear (tq);
+ }
+ if (r)
+ mpz_swap (tr, r);
+
+ mpz_clear (tr);
+
+ return rn != 0;
+ }
+}
+
+void
+mpz_cdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, r, n, d, GMP_DIV_CEIL);
+}
+
+void
+mpz_fdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, r, n, d, GMP_DIV_FLOOR);
+}
+
+void
+mpz_tdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, r, n, d, GMP_DIV_TRUNC);
+}
+
+void
+mpz_cdiv_q (mpz_t q, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, NULL, n, d, GMP_DIV_CEIL);
+}
+
+void
+mpz_fdiv_q (mpz_t q, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, NULL, n, d, GMP_DIV_FLOOR);
+}
+
+void
+mpz_tdiv_q (mpz_t q, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC);
+}
+
+void
+mpz_cdiv_r (mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (NULL, r, n, d, GMP_DIV_CEIL);
+}
+
+void
+mpz_fdiv_r (mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (NULL, r, n, d, GMP_DIV_FLOOR);
+}
+
+void
+mpz_tdiv_r (mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (NULL, r, n, d, GMP_DIV_TRUNC);
+}
+
+void
+mpz_mod (mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (NULL, r, n, d, d->_mp_size >= 0 ? GMP_DIV_FLOOR : GMP_DIV_CEIL);
+}
+
+static void
+mpz_div_q_2exp (mpz_t q, const mpz_t u, mp_bitcnt_t bit_index,
+ enum mpz_div_round_mode mode)
+{
+ mp_size_t un, qn;
+ mp_size_t limb_cnt;
+ mp_ptr qp;
+ int adjust;
+
+ un = u->_mp_size;
+ if (un == 0)
+ {
+ q->_mp_size = 0;
+ return;
+ }
+ limb_cnt = bit_index / GMP_LIMB_BITS;
+ qn = GMP_ABS (un) - limb_cnt;
+ bit_index %= GMP_LIMB_BITS;
+
+ if (mode == ((un > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* un != 0 here. */
+ /* Note: Below, the final indexing at limb_cnt is valid because at
+ that point we have qn > 0. */
+ adjust = (qn <= 0
+ || !mpn_zero_p (u->_mp_d, limb_cnt)
+ || (u->_mp_d[limb_cnt]
+ & (((mp_limb_t) 1 << bit_index) - 1)));
+ else
+ adjust = 0;
+
+ if (qn <= 0)
+ qn = 0;
+ else
+ {
+ qp = MPZ_REALLOC (q, qn);
+
+ if (bit_index != 0)
+ {
+ mpn_rshift (qp, u->_mp_d + limb_cnt, qn, bit_index);
+ qn -= qp[qn - 1] == 0;
+ }
+ else
+ {
+ mpn_copyi (qp, u->_mp_d + limb_cnt, qn);
+ }
+ }
+
+ q->_mp_size = qn;
+
+ if (adjust)
+ mpz_add_ui (q, q, 1);
+ if (un < 0)
+ mpz_neg (q, q);
+}
+
+static void
+mpz_div_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bit_index,
+ enum mpz_div_round_mode mode)
+{
+ mp_size_t us, un, rn;
+ mp_ptr rp;
+ mp_limb_t mask;
+
+ us = u->_mp_size;
+ if (us == 0 || bit_index == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+ rn = (bit_index + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS;
+ assert (rn > 0);
+
+ rp = MPZ_REALLOC (r, rn);
+ un = GMP_ABS (us);
+
+ mask = GMP_LIMB_MAX >> (rn * GMP_LIMB_BITS - bit_index);
+
+ if (rn > un)
+ {
+ /* Quotient (with truncation) is zero, and remainder is
+ non-zero */
+ if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */
+ {
+ /* Have to negate and sign extend. */
+ mp_size_t i;
+
+ gmp_assert_nocarry (! mpn_neg (rp, u->_mp_d, un));
+ for (i = un; i < rn - 1; i++)
+ rp[i] = GMP_LIMB_MAX;
+
+ rp[rn-1] = mask;
+ us = -us;
+ }
+ else
+ {
+ /* Just copy */
+ if (r != u)
+ mpn_copyi (rp, u->_mp_d, un);
+
+ rn = un;
+ }
+ }
+ else
+ {
+ if (r != u)
+ mpn_copyi (rp, u->_mp_d, rn - 1);
+
+ rp[rn-1] = u->_mp_d[rn-1] & mask;
+
+ if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */
+ {
+ /* If r != 0, compute 2^{bit_count} - r. */
+ mpn_neg (rp, rp, rn);
+
+ rp[rn-1] &= mask;
+
+ /* us is not used for anything else, so we can modify it
+ here to indicate flipped sign. */
+ us = -us;
+ }
+ }
+ rn = mpn_normalized_size (rp, rn);
+ r->_mp_size = us < 0 ? -rn : rn;
+}
+
+void
+mpz_cdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_q_2exp (r, u, cnt, GMP_DIV_CEIL);
+}
+
+void
+mpz_fdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_q_2exp (r, u, cnt, GMP_DIV_FLOOR);
+}
+
+void
+mpz_tdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_q_2exp (r, u, cnt, GMP_DIV_TRUNC);
+}
+
+void
+mpz_cdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_r_2exp (r, u, cnt, GMP_DIV_CEIL);
+}
+
+void
+mpz_fdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_r_2exp (r, u, cnt, GMP_DIV_FLOOR);
+}
+
+void
+mpz_tdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_r_2exp (r, u, cnt, GMP_DIV_TRUNC);
+}
+
+void
+mpz_divexact (mpz_t q, const mpz_t n, const mpz_t d)
+{
+ gmp_assert_nocarry (mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC));
+}
+
+int
+mpz_divisible_p (const mpz_t n, const mpz_t d)
+{
+ return mpz_div_qr (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0;
+}
+
+int
+mpz_congruent_p (const mpz_t a, const mpz_t b, const mpz_t m)
+{
+ mpz_t t;
+ int res;
+
+ /* a == b (mod 0) iff a == b */
+ if (mpz_sgn (m) == 0)
+ return (mpz_cmp (a, b) == 0);
+
+ mpz_init (t);
+ mpz_sub (t, a, b);
+ res = mpz_divisible_p (t, m);
+ mpz_clear (t);
+
+ return res;
+}
+
+static unsigned long
+mpz_div_qr_ui (mpz_t q, mpz_t r,
+ const mpz_t n, unsigned long d, enum mpz_div_round_mode mode)
+{
+ unsigned long ret;
+ mpz_t rr, dd;
+
+ mpz_init (rr);
+ mpz_init_set_ui (dd, d);
+ mpz_div_qr (q, rr, n, dd, mode);
+ mpz_clear (dd);
+ ret = mpz_get_ui (rr);
+
+ if (r)
+ mpz_swap (r, rr);
+ mpz_clear (rr);
+
+ return ret;
+}
+
+unsigned long
+mpz_cdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, r, n, d, GMP_DIV_CEIL);
+}
+
+unsigned long
+mpz_fdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, r, n, d, GMP_DIV_FLOOR);
+}
+
+unsigned long
+mpz_tdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, r, n, d, GMP_DIV_TRUNC);
+}
+
+unsigned long
+mpz_cdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_CEIL);
+}
+
+unsigned long
+mpz_fdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_FLOOR);
+}
+
+unsigned long
+mpz_tdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC);
+}
+
+unsigned long
+mpz_cdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_CEIL);
+}
+unsigned long
+mpz_fdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR);
+}
+unsigned long
+mpz_tdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_TRUNC);
+}
+
+unsigned long
+mpz_cdiv_ui (const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_CEIL);
+}
+
+unsigned long
+mpz_fdiv_ui (const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_FLOOR);
+}
+
+unsigned long
+mpz_tdiv_ui (const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC);
+}
+
+unsigned long
+mpz_mod_ui (mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR);
+}
+
+void
+mpz_divexact_ui (mpz_t q, const mpz_t n, unsigned long d)
+{
+ gmp_assert_nocarry (mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC));
+}
+
+int
+mpz_divisible_ui_p (const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0;
+}
+
+
+/* GCD */
+static mp_limb_t
+mpn_gcd_11 (mp_limb_t u, mp_limb_t v)
+{
+ unsigned shift;
+
+ assert ( (u | v) > 0);
+
+ if (u == 0)
+ return v;
+ else if (v == 0)
+ return u;
+
+ gmp_ctz (shift, u | v);
+
+ u >>= shift;
+ v >>= shift;
+
+ if ( (u & 1) == 0)
+ MP_LIMB_T_SWAP (u, v);
+
+ while ( (v & 1) == 0)
+ v >>= 1;
+
+ while (u != v)
+ {
+ if (u > v)
+ {
+ u -= v;
+ do
+ u >>= 1;
+ while ( (u & 1) == 0);
+ }
+ else
+ {
+ v -= u;
+ do
+ v >>= 1;
+ while ( (v & 1) == 0);
+ }
+ }
+ return u << shift;
+}
+
+unsigned long
+mpz_gcd_ui (mpz_t g, const mpz_t u, unsigned long v)
+{
+ mpz_t t;
+ mpz_init_set_ui(t, v);
+ mpz_gcd (t, u, t);
+ if (v > 0)
+ v = mpz_get_ui (t);
+
+ if (g)
+ mpz_swap (t, g);
+
+ mpz_clear (t);
+
+ return v;
+}
+
+static mp_bitcnt_t
+mpz_make_odd (mpz_t r)
+{
+ mp_bitcnt_t shift;
+
+ assert (r->_mp_size > 0);
+ /* Count trailing zeros, equivalent to mpn_scan1, because we know that there is a 1 */
+ shift = mpn_common_scan (r->_mp_d[0], 0, r->_mp_d, 0, 0);
+ mpz_tdiv_q_2exp (r, r, shift);
+
+ return shift;
+}
+
+void
+mpz_gcd (mpz_t g, const mpz_t u, const mpz_t v)
+{
+ mpz_t tu, tv;
+ mp_bitcnt_t uz, vz, gz;
+
+ if (u->_mp_size == 0)
+ {
+ mpz_abs (g, v);
+ return;
+ }
+ if (v->_mp_size == 0)
+ {
+ mpz_abs (g, u);
+ return;
+ }
+
+ mpz_init (tu);
+ mpz_init (tv);
+
+ mpz_abs (tu, u);
+ uz = mpz_make_odd (tu);
+ mpz_abs (tv, v);
+ vz = mpz_make_odd (tv);
+ gz = GMP_MIN (uz, vz);
+
+ if (tu->_mp_size < tv->_mp_size)
+ mpz_swap (tu, tv);
+
+ mpz_tdiv_r (tu, tu, tv);
+ if (tu->_mp_size == 0)
+ {
+ mpz_swap (g, tv);
+ }
+ else
+ for (;;)
+ {
+ int c;
+
+ mpz_make_odd (tu);
+ c = mpz_cmp (tu, tv);
+ if (c == 0)
+ {
+ mpz_swap (g, tu);
+ break;
+ }
+ if (c < 0)
+ mpz_swap (tu, tv);
+
+ if (tv->_mp_size == 1)
+ {
+ mp_limb_t vl = tv->_mp_d[0];
+ mp_limb_t ul = mpz_tdiv_ui (tu, vl);
+ mpz_set_ui (g, mpn_gcd_11 (ul, vl));
+ break;
+ }
+ mpz_sub (tu, tu, tv);
+ }
+ mpz_clear (tu);
+ mpz_clear (tv);
+ mpz_mul_2exp (g, g, gz);
+}
+
+void
+mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, const mpz_t u, const mpz_t v)
+{
+ mpz_t tu, tv, s0, s1, t0, t1;
+ mp_bitcnt_t uz, vz, gz;
+ mp_bitcnt_t power;
+
+ if (u->_mp_size == 0)
+ {
+ /* g = 0 u + sgn(v) v */
+ signed long sign = mpz_sgn (v);
+ mpz_abs (g, v);
+ if (s)
+ s->_mp_size = 0;
+ if (t)
+ mpz_set_si (t, sign);
+ return;
+ }
+
+ if (v->_mp_size == 0)
+ {
+ /* g = sgn(u) u + 0 v */
+ signed long sign = mpz_sgn (u);
+ mpz_abs (g, u);
+ if (s)
+ mpz_set_si (s, sign);
+ if (t)
+ t->_mp_size = 0;
+ return;
+ }
+
+ mpz_init (tu);
+ mpz_init (tv);
+ mpz_init (s0);
+ mpz_init (s1);
+ mpz_init (t0);
+ mpz_init (t1);
+
+ mpz_abs (tu, u);
+ uz = mpz_make_odd (tu);
+ mpz_abs (tv, v);
+ vz = mpz_make_odd (tv);
+ gz = GMP_MIN (uz, vz);
+
+ uz -= gz;
+ vz -= gz;
+
+ /* Cofactors corresponding to odd gcd. gz handled later. */
+ if (tu->_mp_size < tv->_mp_size)
+ {
+ mpz_swap (tu, tv);
+ MPZ_SRCPTR_SWAP (u, v);
+ MPZ_PTR_SWAP (s, t);
+ MP_BITCNT_T_SWAP (uz, vz);
+ }
+
+ /* Maintain
+ *
+ * u = t0 tu + t1 tv
+ * v = s0 tu + s1 tv
+ *
+ * where u and v denote the inputs with common factors of two
+ * eliminated, and det (s0, t0; s1, t1) = 2^p. Then
+ *
+ * 2^p tu = s1 u - t1 v
+ * 2^p tv = -s0 u + t0 v
+ */
+
+ /* After initial division, tu = q tv + tu', we have
+ *
+ * u = 2^uz (tu' + q tv)
+ * v = 2^vz tv
+ *
+ * or
+ *
+ * t0 = 2^uz, t1 = 2^uz q
+ * s0 = 0, s1 = 2^vz
+ */
+
+ mpz_setbit (t0, uz);
+ mpz_tdiv_qr (t1, tu, tu, tv);
+ mpz_mul_2exp (t1, t1, uz);
+
+ mpz_setbit (s1, vz);
+ power = uz + vz;
+
+ if (tu->_mp_size > 0)
+ {
+ mp_bitcnt_t shift;
+ shift = mpz_make_odd (tu);
+ mpz_mul_2exp (t0, t0, shift);
+ mpz_mul_2exp (s0, s0, shift);
+ power += shift;
+
+ for (;;)
+ {
+ int c;
+ c = mpz_cmp (tu, tv);
+ if (c == 0)
+ break;
+
+ if (c < 0)
+ {
+ /* tv = tv' + tu
+ *
+ * u = t0 tu + t1 (tv' + tu) = (t0 + t1) tu + t1 tv'
+ * v = s0 tu + s1 (tv' + tu) = (s0 + s1) tu + s1 tv' */
+
+ mpz_sub (tv, tv, tu);
+ mpz_add (t0, t0, t1);
+ mpz_add (s0, s0, s1);
+
+ shift = mpz_make_odd (tv);
+ mpz_mul_2exp (t1, t1, shift);
+ mpz_mul_2exp (s1, s1, shift);
+ }
+ else
+ {
+ mpz_sub (tu, tu, tv);
+ mpz_add (t1, t0, t1);
+ mpz_add (s1, s0, s1);
+
+ shift = mpz_make_odd (tu);
+ mpz_mul_2exp (t0, t0, shift);
+ mpz_mul_2exp (s0, s0, shift);
+ }
+ power += shift;
+ }
+ }
+
+ /* Now tv = odd part of gcd, and -s0 and t0 are corresponding
+ cofactors. */
+
+ mpz_mul_2exp (tv, tv, gz);
+ mpz_neg (s0, s0);
+
+ /* 2^p g = s0 u + t0 v. Eliminate one factor of two at a time. To
+ adjust cofactors, we need u / g and v / g */
+
+ mpz_divexact (s1, v, tv);
+ mpz_abs (s1, s1);
+ mpz_divexact (t1, u, tv);
+ mpz_abs (t1, t1);
+
+ while (power-- > 0)
+ {
+ /* s0 u + t0 v = (s0 - v/g) u - (t0 + u/g) v */
+ if (mpz_odd_p (s0) || mpz_odd_p (t0))
+ {
+ mpz_sub (s0, s0, s1);
+ mpz_add (t0, t0, t1);
+ }
+ assert (mpz_even_p (t0) && mpz_even_p (s0));
+ mpz_tdiv_q_2exp (s0, s0, 1);
+ mpz_tdiv_q_2exp (t0, t0, 1);
+ }
+
+ /* Arrange so that |s| < |u| / 2g */
+ mpz_add (s1, s0, s1);
+ if (mpz_cmpabs (s0, s1) > 0)
+ {
+ mpz_swap (s0, s1);
+ mpz_sub (t0, t0, t1);
+ }
+ if (u->_mp_size < 0)
+ mpz_neg (s0, s0);
+ if (v->_mp_size < 0)
+ mpz_neg (t0, t0);
+
+ mpz_swap (g, tv);
+ if (s)
+ mpz_swap (s, s0);
+ if (t)
+ mpz_swap (t, t0);
+
+ mpz_clear (tu);
+ mpz_clear (tv);
+ mpz_clear (s0);
+ mpz_clear (s1);
+ mpz_clear (t0);
+ mpz_clear (t1);
+}
+
+void
+mpz_lcm (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ mpz_t g;
+
+ if (u->_mp_size == 0 || v->_mp_size == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ mpz_init (g);
+
+ mpz_gcd (g, u, v);
+ mpz_divexact (g, u, g);
+ mpz_mul (r, g, v);
+
+ mpz_clear (g);
+ mpz_abs (r, r);
+}
+
+void
+mpz_lcm_ui (mpz_t r, const mpz_t u, unsigned long v)
+{
+ if (v == 0 || u->_mp_size == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ v /= mpz_gcd_ui (NULL, u, v);
+ mpz_mul_ui (r, u, v);
+
+ mpz_abs (r, r);
+}
+
+int
+mpz_invert (mpz_t r, const mpz_t u, const mpz_t m)
+{
+ mpz_t g, tr;
+ int invertible;
+
+ if (u->_mp_size == 0 || mpz_cmpabs_ui (m, 1) <= 0)
+ return 0;
+
+ mpz_init (g);
+ mpz_init (tr);
+
+ mpz_gcdext (g, tr, NULL, u, m);
+ invertible = (mpz_cmp_ui (g, 1) == 0);
+
+ if (invertible)
+ {
+ if (tr->_mp_size < 0)
+ {
+ if (m->_mp_size >= 0)
+ mpz_add (tr, tr, m);
+ else
+ mpz_sub (tr, tr, m);
+ }
+ mpz_swap (r, tr);
+ }
+
+ mpz_clear (g);
+ mpz_clear (tr);
+ return invertible;
+}
+
+
+/* Higher level operations (sqrt, pow and root) */
+
+void
+mpz_pow_ui (mpz_t r, const mpz_t b, unsigned long e)
+{
+ unsigned long bit;
+ mpz_t tr;
+ mpz_init_set_ui (tr, 1);
+
+ bit = GMP_ULONG_HIGHBIT;
+ do
+ {
+ mpz_mul (tr, tr, tr);
+ if (e & bit)
+ mpz_mul (tr, tr, b);
+ bit >>= 1;
+ }
+ while (bit > 0);
+
+ mpz_swap (r, tr);
+ mpz_clear (tr);
+}
+
+void
+mpz_ui_pow_ui (mpz_t r, unsigned long blimb, unsigned long e)
+{
+ mpz_t b;
+
+ mpz_init_set_ui (b, blimb);
+ mpz_pow_ui (r, b, e);
+ mpz_clear (b);
+}
+
+void
+mpz_powm (mpz_t r, const mpz_t b, const mpz_t e, const mpz_t m)
+{
+ mpz_t tr;
+ mpz_t base;
+ mp_size_t en, mn;
+ mp_srcptr mp;
+ struct gmp_div_inverse minv;
+ unsigned shift;
+ mp_ptr tp = NULL;
+
+ en = GMP_ABS (e->_mp_size);
+ mn = GMP_ABS (m->_mp_size);
+ if (mn == 0)
+ gmp_die ("mpz_powm: Zero modulo.");
+
+ if (en == 0)
+ {
+ mpz_set_ui (r, 1);
+ return;
+ }
+
+ mp = m->_mp_d;
+ mpn_div_qr_invert (&minv, mp, mn);
+ shift = minv.shift;
+
+ if (shift > 0)
+ {
+ /* To avoid shifts, we do all our reductions, except the final
+ one, using a *normalized* m. */
+ minv.shift = 0;
+
+ tp = gmp_alloc_limbs (mn);
+ gmp_assert_nocarry (mpn_lshift (tp, mp, mn, shift));
+ mp = tp;
+ }
+
+ mpz_init (base);
+
+ if (e->_mp_size < 0)
+ {
+ if (!mpz_invert (base, b, m))
+ gmp_die ("mpz_powm: Negative exponent and non-invertible base.");
+ }
+ else
+ {
+ mp_size_t bn;
+ mpz_abs (base, b);
+
+ bn = base->_mp_size;
+ if (bn >= mn)
+ {
+ mpn_div_qr_preinv (NULL, base->_mp_d, base->_mp_size, mp, mn, &minv);
+ bn = mn;
+ }
+
+ /* We have reduced the absolute value. Now take care of the
+ sign. Note that we get zero represented non-canonically as
+ m. */
+ if (b->_mp_size < 0)
+ {
+ mp_ptr bp = MPZ_REALLOC (base, mn);
+ gmp_assert_nocarry (mpn_sub (bp, mp, mn, bp, bn));
+ bn = mn;
+ }
+ base->_mp_size = mpn_normalized_size (base->_mp_d, bn);
+ }
+ mpz_init_set_ui (tr, 1);
+
+ while (--en >= 0)
+ {
+ mp_limb_t w = e->_mp_d[en];
+ mp_limb_t bit;
+
+ bit = GMP_LIMB_HIGHBIT;
+ do
+ {
+ mpz_mul (tr, tr, tr);
+ if (w & bit)
+ mpz_mul (tr, tr, base);
+ if (tr->_mp_size > mn)
+ {
+ mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv);
+ tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn);
+ }
+ bit >>= 1;
+ }
+ while (bit > 0);
+ }
+
+ /* Final reduction */
+ if (tr->_mp_size >= mn)
+ {
+ minv.shift = shift;
+ mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv);
+ tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn);
+ }
+ if (tp)
+ gmp_free_limbs (tp, mn);
+
+ mpz_swap (r, tr);
+ mpz_clear (tr);
+ mpz_clear (base);
+}
+
+void
+mpz_powm_ui (mpz_t r, const mpz_t b, unsigned long elimb, const mpz_t m)
+{
+ mpz_t e;
+
+ mpz_init_set_ui (e, elimb);
+ mpz_powm (r, b, e, m);
+ mpz_clear (e);
+}
+
+/* x=trunc(y^(1/z)), r=y-x^z */
+void
+mpz_rootrem (mpz_t x, mpz_t r, const mpz_t y, unsigned long z)
+{
+ int sgn;
+ mpz_t t, u;
+
+ sgn = y->_mp_size < 0;
+ if ((~z & sgn) != 0)
+ gmp_die ("mpz_rootrem: Negative argument, with even root.");
+ if (z == 0)
+ gmp_die ("mpz_rootrem: Zeroth root.");
+
+ if (mpz_cmpabs_ui (y, 1) <= 0) {
+ if (x)
+ mpz_set (x, y);
+ if (r)
+ r->_mp_size = 0;
+ return;
+ }
+
+ mpz_init (u);
+ mpz_init (t);
+ mpz_setbit (t, mpz_sizeinbase (y, 2) / z + 1);
+
+ if (z == 2) /* simplify sqrt loop: z-1 == 1 */
+ do {
+ mpz_swap (u, t); /* u = x */
+ mpz_tdiv_q (t, y, u); /* t = y/x */
+ mpz_add (t, t, u); /* t = y/x + x */
+ mpz_tdiv_q_2exp (t, t, 1); /* x'= (y/x + x)/2 */
+ } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */
+ else /* z != 2 */ {
+ mpz_t v;
+
+ mpz_init (v);
+ if (sgn)
+ mpz_neg (t, t);
+
+ do {
+ mpz_swap (u, t); /* u = x */
+ mpz_pow_ui (t, u, z - 1); /* t = x^(z-1) */
+ mpz_tdiv_q (t, y, t); /* t = y/x^(z-1) */
+ mpz_mul_ui (v, u, z - 1); /* v = x*(z-1) */
+ mpz_add (t, t, v); /* t = y/x^(z-1) + x*(z-1) */
+ mpz_tdiv_q_ui (t, t, z); /* x'=(y/x^(z-1) + x*(z-1))/z */
+ } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */
+
+ mpz_clear (v);
+ }
+
+ if (r) {
+ mpz_pow_ui (t, u, z);
+ mpz_sub (r, y, t);
+ }
+ if (x)
+ mpz_swap (x, u);
+ mpz_clear (u);
+ mpz_clear (t);
+}
+
+int
+mpz_root (mpz_t x, const mpz_t y, unsigned long z)
+{
+ int res;
+ mpz_t r;
+
+ mpz_init (r);
+ mpz_rootrem (x, r, y, z);
+ res = r->_mp_size == 0;
+ mpz_clear (r);
+
+ return res;
+}
+
+/* Compute s = floor(sqrt(u)) and r = u - s^2. Allows r == NULL */
+void
+mpz_sqrtrem (mpz_t s, mpz_t r, const mpz_t u)
+{
+ mpz_rootrem (s, r, u, 2);
+}
+
+void
+mpz_sqrt (mpz_t s, const mpz_t u)
+{
+ mpz_rootrem (s, NULL, u, 2);
+}
+
+int
+mpz_perfect_square_p (const mpz_t u)
+{
+ if (u->_mp_size <= 0)
+ return (u->_mp_size == 0);
+ else
+ return mpz_root (NULL, u, 2);
+}
+
+int
+mpn_perfect_square_p (mp_srcptr p, mp_size_t n)
+{
+ mpz_t t;
+
+ assert (n > 0);
+ assert (p [n-1] != 0);
+ return mpz_root (NULL, mpz_roinit_normal_n (t, p, n), 2);
+}
+
+mp_size_t
+mpn_sqrtrem (mp_ptr sp, mp_ptr rp, mp_srcptr p, mp_size_t n)
+{
+ mpz_t s, r, u;
+ mp_size_t res;
+
+ assert (n > 0);
+ assert (p [n-1] != 0);
+
+ mpz_init (r);
+ mpz_init (s);
+ mpz_rootrem (s, r, mpz_roinit_normal_n (u, p, n), 2);
+
+ assert (s->_mp_size == (n+1)/2);
+ mpn_copyd (sp, s->_mp_d, s->_mp_size);
+ mpz_clear (s);
+ res = r->_mp_size;
+ if (rp)
+ mpn_copyd (rp, r->_mp_d, res);
+ mpz_clear (r);
+ return res;
+}
+
+/* Combinatorics */
+
+void
+mpz_mfac_uiui (mpz_t x, unsigned long n, unsigned long m)
+{
+ mpz_set_ui (x, n + (n == 0));
+ if (m + 1 < 2) return;
+ while (n > m + 1)
+ mpz_mul_ui (x, x, n -= m);
+}
+
+void
+mpz_2fac_ui (mpz_t x, unsigned long n)
+{
+ mpz_mfac_uiui (x, n, 2);
+}
+
+void
+mpz_fac_ui (mpz_t x, unsigned long n)
+{
+ mpz_mfac_uiui (x, n, 1);
+}
+
+void
+mpz_bin_uiui (mpz_t r, unsigned long n, unsigned long k)
+{
+ mpz_t t;
+
+ mpz_set_ui (r, k <= n);
+
+ if (k > (n >> 1))
+ k = (k <= n) ? n - k : 0;
+
+ mpz_init (t);
+ mpz_fac_ui (t, k);
+
+ for (; k > 0; --k)
+ mpz_mul_ui (r, r, n--);
+
+ mpz_divexact (r, r, t);
+ mpz_clear (t);
+}
+
+
+/* Primality testing */
+
+/* Computes Kronecker (a/b) with odd b, a!=0 and GCD(a,b) = 1 */
+/* Adapted from JACOBI_BASE_METHOD==4 in mpn/generic/jacbase.c */
+static int
+gmp_jacobi_coprime (mp_limb_t a, mp_limb_t b)
+{
+ int c, bit = 0;
+
+ assert (b & 1);
+ assert (a != 0);
+ /* assert (mpn_gcd_11 (a, b) == 1); */
+
+ /* Below, we represent a and b shifted right so that the least
+ significant one bit is implicit. */
+ b >>= 1;
+
+ gmp_ctz(c, a);
+ a >>= 1;
+
+ for (;;)
+ {
+ a >>= c;
+ /* (2/b) = -1 if b = 3 or 5 mod 8 */
+ bit ^= c & (b ^ (b >> 1));
+ if (a < b)
+ {
+ if (a == 0)
+ return bit & 1 ? -1 : 1;
+ bit ^= a & b;
+ a = b - a;
+ b -= a;
+ }
+ else
+ {
+ a -= b;
+ assert (a != 0);
+ }
+
+ gmp_ctz(c, a);
+ ++c;
+ }
+}
+
+static void
+gmp_lucas_step_k_2k (mpz_t V, mpz_t Qk, const mpz_t n)
+{
+ mpz_mod (Qk, Qk, n);
+ /* V_{2k} <- V_k ^ 2 - 2Q^k */
+ mpz_mul (V, V, V);
+ mpz_submul_ui (V, Qk, 2);
+ mpz_tdiv_r (V, V, n);
+ /* Q^{2k} = (Q^k)^2 */
+ mpz_mul (Qk, Qk, Qk);
+}
+
+/* Computes V_k, Q^k (mod n) for the Lucas' sequence */
+/* with P=1, Q=Q; k = (n>>b0)|1. */
+/* Requires an odd n > 4; b0 > 0; -2*Q must not overflow a long */
+/* Returns (U_k == 0) and sets V=V_k and Qk=Q^k. */
+static int
+gmp_lucas_mod (mpz_t V, mpz_t Qk, long Q,
+ mp_bitcnt_t b0, const mpz_t n)
+{
+ mp_bitcnt_t bs;
+ mpz_t U;
+ int res;
+
+ assert (b0 > 0);
+ assert (Q <= - (LONG_MIN / 2));
+ assert (Q >= - (LONG_MAX / 2));
+ assert (mpz_cmp_ui (n, 4) > 0);
+ assert (mpz_odd_p (n));
+
+ mpz_init_set_ui (U, 1); /* U1 = 1 */
+ mpz_set_ui (V, 1); /* V1 = 1 */
+ mpz_set_si (Qk, Q);
+
+ for (bs = mpz_sizeinbase (n, 2) - 1; --bs >= b0;)
+ {
+ /* U_{2k} <- U_k * V_k */
+ mpz_mul (U, U, V);
+ /* V_{2k} <- V_k ^ 2 - 2Q^k */
+ /* Q^{2k} = (Q^k)^2 */
+ gmp_lucas_step_k_2k (V, Qk, n);
+
+ /* A step k->k+1 is performed if the bit in $n$ is 1 */
+ /* mpz_tstbit(n,bs) or the bit is 0 in $n$ but */
+ /* should be 1 in $n+1$ (bs == b0) */
+ if (b0 == bs || mpz_tstbit (n, bs))
+ {
+ /* Q^{k+1} <- Q^k * Q */
+ mpz_mul_si (Qk, Qk, Q);
+ /* U_{k+1} <- (U_k + V_k) / 2 */
+ mpz_swap (U, V); /* Keep in V the old value of U_k */
+ mpz_add (U, U, V);
+ /* We have to compute U/2, so we need an even value, */
+ /* equivalent (mod n) */
+ if (mpz_odd_p (U))
+ mpz_add (U, U, n);
+ mpz_tdiv_q_2exp (U, U, 1);
+ /* V_{k+1} <-(D*U_k + V_k) / 2 =
+ U_{k+1} + (D-1)/2*U_k = U_{k+1} - 2Q*U_k */
+ mpz_mul_si (V, V, -2*Q);
+ mpz_add (V, U, V);
+ mpz_tdiv_r (V, V, n);
+ }
+ mpz_tdiv_r (U, U, n);
+ }
+
+ res = U->_mp_size == 0;
+ mpz_clear (U);
+ return res;
+}
+
+/* Performs strong Lucas' test on x, with parameters suggested */
+/* for the BPSW test. Qk is only passed to recycle a variable. */
+/* Requires GCD (x,6) = 1.*/
+static int
+gmp_stronglucas (const mpz_t x, mpz_t Qk)
+{
+ mp_bitcnt_t b0;
+ mpz_t V, n;
+ mp_limb_t maxD, D; /* The absolute value is stored. */
+ long Q;
+ mp_limb_t tl;
+
+ /* Test on the absolute value. */
+ mpz_roinit_normal_n (n, x->_mp_d, GMP_ABS (x->_mp_size));
+
+ assert (mpz_odd_p (n));
+ /* assert (mpz_gcd_ui (NULL, n, 6) == 1); */
+ if (mpz_root (Qk, n, 2))
+ return 0; /* A square is composite. */
+
+ /* Check Ds up to square root (in case, n is prime)
+ or avoid overflows */
+ maxD = (Qk->_mp_size == 1) ? Qk->_mp_d [0] - 1 : GMP_LIMB_MAX;
+
+ D = 3;
+ /* Search a D such that (D/n) = -1 in the sequence 5,-7,9,-11,.. */
+ /* For those Ds we have (D/n) = (n/|D|) */
+ do
+ {
+ if (D >= maxD)
+ return 1 + (D != GMP_LIMB_MAX); /* (1 + ! ~ D) */
+ D += 2;
+ tl = mpz_tdiv_ui (n, D);
+ if (tl == 0)
+ return 0;
+ }
+ while (gmp_jacobi_coprime (tl, D) == 1);
+
+ mpz_init (V);
+
+ /* n-(D/n) = n+1 = d*2^{b0}, with d = (n>>b0) | 1 */
+ b0 = mpz_scan0 (n, 0);
+
+ /* D= P^2 - 4Q; P = 1; Q = (1-D)/4 */
+ Q = (D & 2) ? (long) (D >> 2) + 1 : -(long) (D >> 2);
+
+ if (! gmp_lucas_mod (V, Qk, Q, b0, n)) /* If Ud != 0 */
+ while (V->_mp_size != 0 && --b0 != 0) /* while Vk != 0 */
+ /* V <- V ^ 2 - 2Q^k */
+ /* Q^{2k} = (Q^k)^2 */
+ gmp_lucas_step_k_2k (V, Qk, n);
+
+ mpz_clear (V);
+ return (b0 != 0);
+}
+
+static int
+gmp_millerrabin (const mpz_t n, const mpz_t nm1, mpz_t y,
+ const mpz_t q, mp_bitcnt_t k)
+{
+ assert (k > 0);
+
+ /* Caller must initialize y to the base. */
+ mpz_powm (y, y, q, n);
+
+ if (mpz_cmp_ui (y, 1) == 0 || mpz_cmp (y, nm1) == 0)
+ return 1;
+
+ while (--k > 0)
+ {
+ mpz_powm_ui (y, y, 2, n);
+ if (mpz_cmp (y, nm1) == 0)
+ return 1;
+ /* y == 1 means that the previous y was a non-trivial square root
+ of 1 (mod n). y == 0 means that n is a power of the base.
+ In either case, n is not prime. */
+ if (mpz_cmp_ui (y, 1) <= 0)
+ return 0;
+ }
+ return 0;
+}
+
+/* This product is 0xc0cfd797, and fits in 32 bits. */
+#define GMP_PRIME_PRODUCT \
+ (3UL*5UL*7UL*11UL*13UL*17UL*19UL*23UL*29UL)
+
+/* Bit (p+1)/2 is set, for each odd prime <= 61 */
+#define GMP_PRIME_MASK 0xc96996dcUL
+
+int
+mpz_probab_prime_p (const mpz_t n, int reps)
+{
+ mpz_t nm1;
+ mpz_t q;
+ mpz_t y;
+ mp_bitcnt_t k;
+ int is_prime;
+ int j;
+
+ /* Note that we use the absolute value of n only, for compatibility
+ with the real GMP. */
+ if (mpz_even_p (n))
+ return (mpz_cmpabs_ui (n, 2) == 0) ? 2 : 0;
+
+ /* Above test excludes n == 0 */
+ assert (n->_mp_size != 0);
+
+ if (mpz_cmpabs_ui (n, 64) < 0)
+ return (GMP_PRIME_MASK >> (n->_mp_d[0] >> 1)) & 2;
+
+ if (mpz_gcd_ui (NULL, n, GMP_PRIME_PRODUCT) != 1)
+ return 0;
+
+ /* All prime factors are >= 31. */
+ if (mpz_cmpabs_ui (n, 31*31) < 0)
+ return 2;
+
+ mpz_init (nm1);
+ mpz_init (q);
+
+ /* Find q and k, where q is odd and n = 1 + 2**k * q. */
+ mpz_abs (nm1, n);
+ nm1->_mp_d[0] -= 1;
+ k = mpz_scan1 (nm1, 0);
+ mpz_tdiv_q_2exp (q, nm1, k);
+
+ /* BPSW test */
+ mpz_init_set_ui (y, 2);
+ is_prime = gmp_millerrabin (n, nm1, y, q, k) && gmp_stronglucas (n, y);
+ reps -= 24; /* skip the first 24 repetitions */
+
+ /* Use Miller-Rabin, with a deterministic sequence of bases, a[j] =
+ j^2 + j + 41 using Euler's polynomial. We potentially stop early,
+ if a[j] >= n - 1. Since n >= 31*31, this can happen only if reps >
+ 30 (a[30] == 971 > 31*31 == 961). */
+
+ for (j = 0; is_prime & (j < reps); j++)
+ {
+ mpz_set_ui (y, (unsigned long) j*j+j+41);
+ if (mpz_cmp (y, nm1) >= 0)
+ {
+ /* Don't try any further bases. This "early" break does not affect
+ the result for any reasonable reps value (<=5000 was tested) */
+ assert (j >= 30);
+ break;
+ }
+ is_prime = gmp_millerrabin (n, nm1, y, q, k);
+ }
+ mpz_clear (nm1);
+ mpz_clear (q);
+ mpz_clear (y);
+
+ return is_prime;
+}
+
+
+/* Logical operations and bit manipulation. */
+
+/* Numbers are treated as if represented in two's complement (and
+ infinitely sign extended). For a negative values we get the two's
+ complement from -x = ~x + 1, where ~ is bitwise complement.
+ Negation transforms
+
+ xxxx10...0
+
+ into
+
+ yyyy10...0
+
+ where yyyy is the bitwise complement of xxxx. So least significant
+ bits, up to and including the first one bit, are unchanged, and
+ the more significant bits are all complemented.
+
+ To change a bit from zero to one in a negative number, subtract the
+ corresponding power of two from the absolute value. This can never
+ underflow. To change a bit from one to zero, add the corresponding
+ power of two, and this might overflow. E.g., if x = -001111, the
+ two's complement is 110001. Clearing the least significant bit, we
+ get two's complement 110000, and -010000. */
+
+int
+mpz_tstbit (const mpz_t d, mp_bitcnt_t bit_index)
+{
+ mp_size_t limb_index;
+ unsigned shift;
+ mp_size_t ds;
+ mp_size_t dn;
+ mp_limb_t w;
+ int bit;
+
+ ds = d->_mp_size;
+ dn = GMP_ABS (ds);
+ limb_index = bit_index / GMP_LIMB_BITS;
+ if (limb_index >= dn)
+ return ds < 0;
+
+ shift = bit_index % GMP_LIMB_BITS;
+ w = d->_mp_d[limb_index];
+ bit = (w >> shift) & 1;
+
+ if (ds < 0)
+ {
+ /* d < 0. Check if any of the bits below is set: If so, our bit
+ must be complemented. */
+ if (shift > 0 && (mp_limb_t) (w << (GMP_LIMB_BITS - shift)) > 0)
+ return bit ^ 1;
+ while (--limb_index >= 0)
+ if (d->_mp_d[limb_index] > 0)
+ return bit ^ 1;
+ }
+ return bit;
+}
+
+static void
+mpz_abs_add_bit (mpz_t d, mp_bitcnt_t bit_index)
+{
+ mp_size_t dn, limb_index;
+ mp_limb_t bit;
+ mp_ptr dp;
+
+ dn = GMP_ABS (d->_mp_size);
+
+ limb_index = bit_index / GMP_LIMB_BITS;
+ bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS);
+
+ if (limb_index >= dn)
+ {
+ mp_size_t i;
+ /* The bit should be set outside of the end of the number.
+ We have to increase the size of the number. */
+ dp = MPZ_REALLOC (d, limb_index + 1);
+
+ dp[limb_index] = bit;
+ for (i = dn; i < limb_index; i++)
+ dp[i] = 0;
+ dn = limb_index + 1;
+ }
+ else
+ {
+ mp_limb_t cy;
+
+ dp = d->_mp_d;
+
+ cy = mpn_add_1 (dp + limb_index, dp + limb_index, dn - limb_index, bit);
+ if (cy > 0)
+ {
+ dp = MPZ_REALLOC (d, dn + 1);
+ dp[dn++] = cy;
+ }
+ }
+
+ d->_mp_size = (d->_mp_size < 0) ? - dn : dn;
+}
+
+static void
+mpz_abs_sub_bit (mpz_t d, mp_bitcnt_t bit_index)
+{
+ mp_size_t dn, limb_index;
+ mp_ptr dp;
+ mp_limb_t bit;
+
+ dn = GMP_ABS (d->_mp_size);
+ dp = d->_mp_d;
+
+ limb_index = bit_index / GMP_LIMB_BITS;
+ bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS);
+
+ assert (limb_index < dn);
+
+ gmp_assert_nocarry (mpn_sub_1 (dp + limb_index, dp + limb_index,
+ dn - limb_index, bit));
+ dn = mpn_normalized_size (dp, dn);
+ d->_mp_size = (d->_mp_size < 0) ? - dn : dn;
+}
+
+void
+mpz_setbit (mpz_t d, mp_bitcnt_t bit_index)
+{
+ if (!mpz_tstbit (d, bit_index))
+ {
+ if (d->_mp_size >= 0)
+ mpz_abs_add_bit (d, bit_index);
+ else
+ mpz_abs_sub_bit (d, bit_index);
+ }
+}
+
+void
+mpz_clrbit (mpz_t d, mp_bitcnt_t bit_index)
+{
+ if (mpz_tstbit (d, bit_index))
+ {
+ if (d->_mp_size >= 0)
+ mpz_abs_sub_bit (d, bit_index);
+ else
+ mpz_abs_add_bit (d, bit_index);
+ }
+}
+
+void
+mpz_combit (mpz_t d, mp_bitcnt_t bit_index)
+{
+ if (mpz_tstbit (d, bit_index) ^ (d->_mp_size < 0))
+ mpz_abs_sub_bit (d, bit_index);
+ else
+ mpz_abs_add_bit (d, bit_index);
+}
+
+void
+mpz_com (mpz_t r, const mpz_t u)
+{
+ mpz_add_ui (r, u, 1);
+ mpz_neg (r, r);
+}
+
+void
+mpz_and (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ mp_size_t un, vn, rn, i;
+ mp_ptr up, vp, rp;
+
+ mp_limb_t ux, vx, rx;
+ mp_limb_t uc, vc, rc;
+ mp_limb_t ul, vl, rl;
+
+ un = GMP_ABS (u->_mp_size);
+ vn = GMP_ABS (v->_mp_size);
+ if (un < vn)
+ {
+ MPZ_SRCPTR_SWAP (u, v);
+ MP_SIZE_T_SWAP (un, vn);
+ }
+ if (vn == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ uc = u->_mp_size < 0;
+ vc = v->_mp_size < 0;
+ rc = uc & vc;
+
+ ux = -uc;
+ vx = -vc;
+ rx = -rc;
+
+ /* If the smaller input is positive, higher limbs don't matter. */
+ rn = vx ? un : vn;
+
+ rp = MPZ_REALLOC (r, rn + (mp_size_t) rc);
+
+ up = u->_mp_d;
+ vp = v->_mp_d;
+
+ i = 0;
+ do
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ vl = (vp[i] ^ vx) + vc;
+ vc = vl < vc;
+
+ rl = ( (ul & vl) ^ rx) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ while (++i < vn);
+ assert (vc == 0);
+
+ for (; i < rn; i++)
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ rl = ( (ul & vx) ^ rx) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ if (rc)
+ rp[rn++] = rc;
+ else
+ rn = mpn_normalized_size (rp, rn);
+
+ r->_mp_size = rx ? -rn : rn;
+}
+
+void
+mpz_ior (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ mp_size_t un, vn, rn, i;
+ mp_ptr up, vp, rp;
+
+ mp_limb_t ux, vx, rx;
+ mp_limb_t uc, vc, rc;
+ mp_limb_t ul, vl, rl;
+
+ un = GMP_ABS (u->_mp_size);
+ vn = GMP_ABS (v->_mp_size);
+ if (un < vn)
+ {
+ MPZ_SRCPTR_SWAP (u, v);
+ MP_SIZE_T_SWAP (un, vn);
+ }
+ if (vn == 0)
+ {
+ mpz_set (r, u);
+ return;
+ }
+
+ uc = u->_mp_size < 0;
+ vc = v->_mp_size < 0;
+ rc = uc | vc;
+
+ ux = -uc;
+ vx = -vc;
+ rx = -rc;
+
+ /* If the smaller input is negative, by sign extension higher limbs
+ don't matter. */
+ rn = vx ? vn : un;
+
+ rp = MPZ_REALLOC (r, rn + (mp_size_t) rc);
+
+ up = u->_mp_d;
+ vp = v->_mp_d;
+
+ i = 0;
+ do
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ vl = (vp[i] ^ vx) + vc;
+ vc = vl < vc;
+
+ rl = ( (ul | vl) ^ rx) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ while (++i < vn);
+ assert (vc == 0);
+
+ for (; i < rn; i++)
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ rl = ( (ul | vx) ^ rx) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ if (rc)
+ rp[rn++] = rc;
+ else
+ rn = mpn_normalized_size (rp, rn);
+
+ r->_mp_size = rx ? -rn : rn;
+}
+
+void
+mpz_xor (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ mp_size_t un, vn, i;
+ mp_ptr up, vp, rp;
+
+ mp_limb_t ux, vx, rx;
+ mp_limb_t uc, vc, rc;
+ mp_limb_t ul, vl, rl;
+
+ un = GMP_ABS (u->_mp_size);
+ vn = GMP_ABS (v->_mp_size);
+ if (un < vn)
+ {
+ MPZ_SRCPTR_SWAP (u, v);
+ MP_SIZE_T_SWAP (un, vn);
+ }
+ if (vn == 0)
+ {
+ mpz_set (r, u);
+ return;
+ }
+
+ uc = u->_mp_size < 0;
+ vc = v->_mp_size < 0;
+ rc = uc ^ vc;
+
+ ux = -uc;
+ vx = -vc;
+ rx = -rc;
+
+ rp = MPZ_REALLOC (r, un + (mp_size_t) rc);
+
+ up = u->_mp_d;
+ vp = v->_mp_d;
+
+ i = 0;
+ do
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ vl = (vp[i] ^ vx) + vc;
+ vc = vl < vc;
+
+ rl = (ul ^ vl ^ rx) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ while (++i < vn);
+ assert (vc == 0);
+
+ for (; i < un; i++)
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ rl = (ul ^ ux) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ if (rc)
+ rp[un++] = rc;
+ else
+ un = mpn_normalized_size (rp, un);
+
+ r->_mp_size = rx ? -un : un;
+}
+
+static unsigned
+gmp_popcount_limb (mp_limb_t x)
+{
+ unsigned c;
+
+ /* Do 16 bits at a time, to avoid limb-sized constants. */
+ int LOCAL_SHIFT_BITS = 16;
+ for (c = 0; x > 0;)
+ {
+ unsigned w = x - ((x >> 1) & 0x5555);
+ w = ((w >> 2) & 0x3333) + (w & 0x3333);
+ w = (w >> 4) + w;
+ w = ((w >> 8) & 0x000f) + (w & 0x000f);
+ c += w;
+ if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS)
+ x >>= LOCAL_SHIFT_BITS;
+ else
+ x = 0;
+ }
+ return c;
+}
+
+mp_bitcnt_t
+mpn_popcount (mp_srcptr p, mp_size_t n)
+{
+ mp_size_t i;
+ mp_bitcnt_t c;
+
+ for (c = 0, i = 0; i < n; i++)
+ c += gmp_popcount_limb (p[i]);
+
+ return c;
+}
+
+mp_bitcnt_t
+mpz_popcount (const mpz_t u)
+{
+ mp_size_t un;
+
+ un = u->_mp_size;
+
+ if (un < 0)
+ return ~(mp_bitcnt_t) 0;
+
+ return mpn_popcount (u->_mp_d, un);
+}
+
+mp_bitcnt_t
+mpz_hamdist (const mpz_t u, const mpz_t v)
+{
+ mp_size_t un, vn, i;
+ mp_limb_t uc, vc, ul, vl, comp;
+ mp_srcptr up, vp;
+ mp_bitcnt_t c;
+
+ un = u->_mp_size;
+ vn = v->_mp_size;
+
+ if ( (un ^ vn) < 0)
+ return ~(mp_bitcnt_t) 0;
+
+ comp = - (uc = vc = (un < 0));
+ if (uc)
+ {
+ assert (vn < 0);
+ un = -un;
+ vn = -vn;
+ }
+
+ up = u->_mp_d;
+ vp = v->_mp_d;
+
+ if (un < vn)
+ MPN_SRCPTR_SWAP (up, un, vp, vn);
+
+ for (i = 0, c = 0; i < vn; i++)
+ {
+ ul = (up[i] ^ comp) + uc;
+ uc = ul < uc;
+
+ vl = (vp[i] ^ comp) + vc;
+ vc = vl < vc;
+
+ c += gmp_popcount_limb (ul ^ vl);
+ }
+ assert (vc == 0);
+
+ for (; i < un; i++)
+ {
+ ul = (up[i] ^ comp) + uc;
+ uc = ul < uc;
+
+ c += gmp_popcount_limb (ul ^ comp);
+ }
+
+ return c;
+}
+
+mp_bitcnt_t
+mpz_scan1 (const mpz_t u, mp_bitcnt_t starting_bit)
+{
+ mp_ptr up;
+ mp_size_t us, un, i;
+ mp_limb_t limb, ux;
+
+ us = u->_mp_size;
+ un = GMP_ABS (us);
+ i = starting_bit / GMP_LIMB_BITS;
+
+ /* Past the end there's no 1 bits for u>=0, or an immediate 1 bit
+ for u<0. Notice this test picks up any u==0 too. */
+ if (i >= un)
+ return (us >= 0 ? ~(mp_bitcnt_t) 0 : starting_bit);
+
+ up = u->_mp_d;
+ ux = 0;
+ limb = up[i];
+
+ if (starting_bit != 0)
+ {
+ if (us < 0)
+ {
+ ux = mpn_zero_p (up, i);
+ limb = ~ limb + ux;
+ ux = - (mp_limb_t) (limb >= ux);
+ }
+
+ /* Mask to 0 all bits before starting_bit, thus ignoring them. */
+ limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS);
+ }
+
+ return mpn_common_scan (limb, i, up, un, ux);
+}
+
+mp_bitcnt_t
+mpz_scan0 (const mpz_t u, mp_bitcnt_t starting_bit)
+{
+ mp_ptr up;
+ mp_size_t us, un, i;
+ mp_limb_t limb, ux;
+
+ us = u->_mp_size;
+ ux = - (mp_limb_t) (us >= 0);
+ un = GMP_ABS (us);
+ i = starting_bit / GMP_LIMB_BITS;
+
+ /* When past end, there's an immediate 0 bit for u>=0, or no 0 bits for
+ u<0. Notice this test picks up all cases of u==0 too. */
+ if (i >= un)
+ return (ux ? starting_bit : ~(mp_bitcnt_t) 0);
+
+ up = u->_mp_d;
+ limb = up[i] ^ ux;
+
+ if (ux == 0)
+ limb -= mpn_zero_p (up, i); /* limb = ~(~limb + zero_p) */
+
+ /* Mask all bits before starting_bit, thus ignoring them. */
+ limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS);
+
+ return mpn_common_scan (limb, i, up, un, ux);
+}
+
+
+/* MPZ base conversion. */
+
+size_t
+mpz_sizeinbase (const mpz_t u, int base)
+{
+ mp_size_t un, tn;
+ mp_srcptr up;
+ mp_ptr tp;
+ mp_bitcnt_t bits;
+ struct gmp_div_inverse bi;
+ size_t ndigits;
+
+ assert (base >= 2);
+ assert (base <= 62);
+
+ un = GMP_ABS (u->_mp_size);
+ if (un == 0)
+ return 1;
+
+ up = u->_mp_d;
+
+ bits = (un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]);
+ switch (base)
+ {
+ case 2:
+ return bits;
+ case 4:
+ return (bits + 1) / 2;
+ case 8:
+ return (bits + 2) / 3;
+ case 16:
+ return (bits + 3) / 4;
+ case 32:
+ return (bits + 4) / 5;
+ /* FIXME: Do something more clever for the common case of base
+ 10. */
+ }
+
+ tp = gmp_alloc_limbs (un);
+ mpn_copyi (tp, up, un);
+ mpn_div_qr_1_invert (&bi, base);
+
+ tn = un;
+ ndigits = 0;
+ do
+ {
+ ndigits++;
+ mpn_div_qr_1_preinv (tp, tp, tn, &bi);
+ tn -= (tp[tn-1] == 0);
+ }
+ while (tn > 0);
+
+ gmp_free_limbs (tp, un);
+ return ndigits;
+}
+
+char *
+mpz_get_str (char *sp, int base, const mpz_t u)
+{
+ unsigned bits;
+ const char *digits;
+ mp_size_t un;
+ size_t i, sn, osn;
+
+ digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ if (base > 1)
+ {
+ if (base <= 36)
+ digits = "0123456789abcdefghijklmnopqrstuvwxyz";
+ else if (base > 62)
+ return NULL;
+ }
+ else if (base >= -1)
+ base = 10;
+ else
+ {
+ base = -base;
+ if (base > 36)
+ return NULL;
+ }
+
+ sn = 1 + mpz_sizeinbase (u, base);
+ if (!sp)
+ {
+ osn = 1 + sn;
+ sp = (char *) gmp_alloc (osn);
+ }
+ else
+ osn = 0;
+ un = GMP_ABS (u->_mp_size);
+
+ if (un == 0)
+ {
+ sp[0] = '0';
+ sn = 1;
+ goto ret;
+ }
+
+ i = 0;
+
+ if (u->_mp_size < 0)
+ sp[i++] = '-';
+
+ bits = mpn_base_power_of_two_p (base);
+
+ if (bits)
+ /* Not modified in this case. */
+ sn = i + mpn_get_str_bits ((unsigned char *) sp + i, bits, u->_mp_d, un);
+ else
+ {
+ struct mpn_base_info info;
+ mp_ptr tp;
+
+ mpn_get_base_info (&info, base);
+ tp = gmp_alloc_limbs (un);
+ mpn_copyi (tp, u->_mp_d, un);
+
+ sn = i + mpn_get_str_other ((unsigned char *) sp + i, base, &info, tp, un);
+ gmp_free_limbs (tp, un);
+ }
+
+ for (; i < sn; i++)
+ sp[i] = digits[(unsigned char) sp[i]];
+
+ret:
+ sp[sn] = '\0';
+ if (osn && osn != sn + 1)
+ sp = gmp_realloc(sp, osn, sn + 1);
+ return sp;
+}
+
+int
+mpz_set_str (mpz_t r, const char *sp, int base)
+{
+ unsigned bits, value_of_a;
+ mp_size_t rn, alloc;
+ mp_ptr rp;
+ size_t dn, sn;
+ int sign;
+ unsigned char *dp;
+
+ assert (base == 0 || (base >= 2 && base <= 62));
+
+ while (isspace( (unsigned char) *sp))
+ sp++;
+
+ sign = (*sp == '-');
+ sp += sign;
+
+ if (base == 0)
+ {
+ if (sp[0] == '0')
+ {
+ if (sp[1] == 'x' || sp[1] == 'X')
+ {
+ base = 16;
+ sp += 2;
+ }
+ else if (sp[1] == 'b' || sp[1] == 'B')
+ {
+ base = 2;
+ sp += 2;
+ }
+ else
+ base = 8;
+ }
+ else
+ base = 10;
+ }
+
+ if (!*sp)
+ {
+ r->_mp_size = 0;
+ return -1;
+ }
+ sn = strlen(sp);
+ dp = (unsigned char *) gmp_alloc (sn);
+
+ value_of_a = (base > 36) ? 36 : 10;
+ for (dn = 0; *sp; sp++)
+ {
+ unsigned digit;
+
+ if (isspace ((unsigned char) *sp))
+ continue;
+ else if (*sp >= '0' && *sp <= '9')
+ digit = *sp - '0';
+ else if (*sp >= 'a' && *sp <= 'z')
+ digit = *sp - 'a' + value_of_a;
+ else if (*sp >= 'A' && *sp <= 'Z')
+ digit = *sp - 'A' + 10;
+ else
+ digit = base; /* fail */
+
+ if (digit >= (unsigned) base)
+ {
+ gmp_free (dp, sn);
+ r->_mp_size = 0;
+ return -1;
+ }
+
+ dp[dn++] = digit;
+ }
+
+ if (!dn)
+ {
+ gmp_free (dp, sn);
+ r->_mp_size = 0;
+ return -1;
+ }
+ bits = mpn_base_power_of_two_p (base);
+
+ if (bits > 0)
+ {
+ alloc = (dn * bits + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS;
+ rp = MPZ_REALLOC (r, alloc);
+ rn = mpn_set_str_bits (rp, dp, dn, bits);
+ }
+ else
+ {
+ struct mpn_base_info info;
+ mpn_get_base_info (&info, base);
+ alloc = (dn + info.exp - 1) / info.exp;
+ rp = MPZ_REALLOC (r, alloc);
+ rn = mpn_set_str_other (rp, dp, dn, base, &info);
+ /* Normalization, needed for all-zero input. */
+ assert (rn > 0);
+ rn -= rp[rn-1] == 0;
+ }
+ assert (rn <= alloc);
+ gmp_free (dp, sn);
+
+ r->_mp_size = sign ? - rn : rn;
+
+ return 0;
+}
+
+int
+mpz_init_set_str (mpz_t r, const char *sp, int base)
+{
+ mpz_init (r);
+ return mpz_set_str (r, sp, base);
+}
+
+size_t
+mpz_out_str (FILE *stream, int base, const mpz_t x)
+{
+ char *str;
+ size_t len, n;
+
+ str = mpz_get_str (NULL, base, x);
+ len = strlen (str);
+ n = fwrite (str, 1, len, stream);
+ gmp_free (str, len + 1);
+ return n;
+}
+
+
+static int
+gmp_detect_endian (void)
+{
+ static const int i = 2;
+ const unsigned char *p = (const unsigned char *) &i;
+ return 1 - *p;
+}
+
+/* Import and export. Does not support nails. */
+void
+mpz_import (mpz_t r, size_t count, int order, size_t size, int endian,
+ size_t nails, const void *src)
+{
+ const unsigned char *p;
+ ptrdiff_t word_step;
+ mp_ptr rp;
+ mp_size_t rn;
+
+ /* The current (partial) limb. */
+ mp_limb_t limb;
+ /* The number of bytes already copied to this limb (starting from
+ the low end). */
+ size_t bytes;
+ /* The index where the limb should be stored, when completed. */
+ mp_size_t i;
+
+ if (nails != 0)
+ gmp_die ("mpz_import: Nails not supported.");
+
+ assert (order == 1 || order == -1);
+ assert (endian >= -1 && endian <= 1);
+
+ if (endian == 0)
+ endian = gmp_detect_endian ();
+
+ p = (unsigned char *) src;
+
+ word_step = (order != endian) ? 2 * size : 0;
+
+ /* Process bytes from the least significant end, so point p at the
+ least significant word. */
+ if (order == 1)
+ {
+ p += size * (count - 1);
+ word_step = - word_step;
+ }
+
+ /* And at least significant byte of that word. */
+ if (endian == 1)
+ p += (size - 1);
+
+ rn = (size * count + sizeof(mp_limb_t) - 1) / sizeof(mp_limb_t);
+ rp = MPZ_REALLOC (r, rn);
+
+ for (limb = 0, bytes = 0, i = 0; count > 0; count--, p += word_step)
+ {
+ size_t j;
+ for (j = 0; j < size; j++, p -= (ptrdiff_t) endian)
+ {
+ limb |= (mp_limb_t) *p << (bytes++ * CHAR_BIT);
+ if (bytes == sizeof(mp_limb_t))
+ {
+ rp[i++] = limb;
+ bytes = 0;
+ limb = 0;
+ }
+ }
+ }
+ assert (i + (bytes > 0) == rn);
+ if (limb != 0)
+ rp[i++] = limb;
+ else
+ i = mpn_normalized_size (rp, i);
+
+ r->_mp_size = i;
+}
+
+void *
+mpz_export (void *r, size_t *countp, int order, size_t size, int endian,
+ size_t nails, const mpz_t u)
+{
+ size_t count;
+ mp_size_t un;
+
+ if (nails != 0)
+ gmp_die ("mpz_import: Nails not supported.");
+
+ assert (order == 1 || order == -1);
+ assert (endian >= -1 && endian <= 1);
+ assert (size > 0 || u->_mp_size == 0);
+
+ un = u->_mp_size;
+ count = 0;
+ if (un != 0)
+ {
+ size_t k;
+ unsigned char *p;
+ ptrdiff_t word_step;
+ /* The current (partial) limb. */
+ mp_limb_t limb;
+ /* The number of bytes left to do in this limb. */
+ size_t bytes;
+ /* The index where the limb was read. */
+ mp_size_t i;
+
+ un = GMP_ABS (un);
+
+ /* Count bytes in top limb. */
+ limb = u->_mp_d[un-1];
+ assert (limb != 0);
+
+ k = (GMP_LIMB_BITS <= CHAR_BIT);
+ if (!k)
+ {
+ do {
+ int LOCAL_CHAR_BIT = CHAR_BIT;
+ k++; limb >>= LOCAL_CHAR_BIT;
+ } while (limb != 0);
+ }
+ /* else limb = 0; */
+
+ count = (k + (un-1) * sizeof (mp_limb_t) + size - 1) / size;
+
+ if (!r)
+ r = gmp_alloc (count * size);
+
+ if (endian == 0)
+ endian = gmp_detect_endian ();
+
+ p = (unsigned char *) r;
+
+ word_step = (order != endian) ? 2 * size : 0;
+
+ /* Process bytes from the least significant end, so point p at the
+ least significant word. */
+ if (order == 1)
+ {
+ p += size * (count - 1);
+ word_step = - word_step;
+ }
+
+ /* And at least significant byte of that word. */
+ if (endian == 1)
+ p += (size - 1);
+
+ for (bytes = 0, i = 0, k = 0; k < count; k++, p += word_step)
+ {
+ size_t j;
+ for (j = 0; j < size; ++j, p -= (ptrdiff_t) endian)
+ {
+ if (sizeof (mp_limb_t) == 1)
+ {
+ if (i < un)
+ *p = u->_mp_d[i++];
+ else
+ *p = 0;
+ }
+ else
+ {
+ int LOCAL_CHAR_BIT = CHAR_BIT;
+ if (bytes == 0)
+ {
+ if (i < un)
+ limb = u->_mp_d[i++];
+ bytes = sizeof (mp_limb_t);
+ }
+ *p = limb;
+ limb >>= LOCAL_CHAR_BIT;
+ bytes--;
+ }
+ }
+ }
+ assert (i == un);
+ assert (k == count);
+ }
+
+ if (countp)
+ *countp = count;
+
+ return r;
+}
diff --git a/lib/mini-gmp.h b/lib/mini-gmp.h
new file mode 100644
index 0000000..ef64823
--- a/dev/null
+++ b/lib/mini-gmp.h
@@ -0,0 +1,308 @@
+/* mini-gmp, a minimalistic implementation of a GNU GMP subset.
+
+Copyright 2011-2015, 2017, 2019 Free Software Foundation, Inc.
+
+This file is part of the GNU MP Library.
+
+The GNU MP Library is free software; you can redistribute it and/or modify
+it under the terms of either:
+
+ * 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.
+
+or
+
+ * the GNU General Public License as published by the Free Software
+ Foundation; either version 2 of the License, or (at your option) any
+ later version.
+
+or both in parallel, as here.
+
+The GNU MP Library 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 copies of the GNU General Public License and the
+GNU Lesser General Public License along with the GNU MP Library. If not,
+see https://www.gnu.org/licenses/. */
+
+/* About mini-gmp: This is a minimal implementation of a subset of the
+ GMP interface. It is intended for inclusion into applications which
+ have modest bignums needs, as a fallback when the real GMP library
+ is not installed.
+
+ This file defines the public interface. */
+
+#ifndef __MINI_GMP_H__
+#define __MINI_GMP_H__
+
+/* For size_t */
+#include <stddef.h>
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+void mp_set_memory_functions (void *(*) (size_t),
+ void *(*) (void *, size_t, size_t),
+ void (*) (void *, size_t));
+
+void mp_get_memory_functions (void *(**) (size_t),
+ void *(**) (void *, size_t, size_t),
+ void (**) (void *, size_t));
+
+#ifndef MINI_GMP_LIMB_TYPE
+#define MINI_GMP_LIMB_TYPE long
+#endif
+
+typedef unsigned MINI_GMP_LIMB_TYPE mp_limb_t;
+typedef long mp_size_t;
+typedef unsigned long mp_bitcnt_t;
+
+typedef mp_limb_t *mp_ptr;
+typedef const mp_limb_t *mp_srcptr;
+
+typedef struct
+{
+ int _mp_alloc; /* Number of *limbs* allocated and pointed
+ to by the _mp_d field. */
+ int _mp_size; /* abs(_mp_size) is the number of limbs the
+ last field points to. If _mp_size is
+ negative this is a negative number. */
+ mp_limb_t *_mp_d; /* Pointer to the limbs. */
+} __mpz_struct;
+
+typedef __mpz_struct mpz_t[1];
+
+typedef __mpz_struct *mpz_ptr;
+typedef const __mpz_struct *mpz_srcptr;
+
+extern const int mp_bits_per_limb;
+
+void mpn_copyi (mp_ptr, mp_srcptr, mp_size_t);
+void mpn_copyd (mp_ptr, mp_srcptr, mp_size_t);
+void mpn_zero (mp_ptr, mp_size_t);
+
+int mpn_cmp (mp_srcptr, mp_srcptr, mp_size_t);
+int mpn_zero_p (mp_srcptr, mp_size_t);
+
+mp_limb_t mpn_add_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t);
+mp_limb_t mpn_add_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t);
+mp_limb_t mpn_add (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t);
+
+mp_limb_t mpn_sub_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t);
+mp_limb_t mpn_sub_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t);
+mp_limb_t mpn_sub (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t);
+
+mp_limb_t mpn_mul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t);
+mp_limb_t mpn_addmul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t);
+mp_limb_t mpn_submul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t);
+
+mp_limb_t mpn_mul (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t);
+void mpn_mul_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t);
+void mpn_sqr (mp_ptr, mp_srcptr, mp_size_t);
+int mpn_perfect_square_p (mp_srcptr, mp_size_t);
+mp_size_t mpn_sqrtrem (mp_ptr, mp_ptr, mp_srcptr, mp_size_t);
+
+mp_limb_t mpn_lshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int);
+mp_limb_t mpn_rshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int);
+
+mp_bitcnt_t mpn_scan0 (mp_srcptr, mp_bitcnt_t);
+mp_bitcnt_t mpn_scan1 (mp_srcptr, mp_bitcnt_t);
+
+void mpn_com (mp_ptr, mp_srcptr, mp_size_t);
+mp_limb_t mpn_neg (mp_ptr, mp_srcptr, mp_size_t);
+
+mp_bitcnt_t mpn_popcount (mp_srcptr, mp_size_t);
+
+mp_limb_t mpn_invert_3by2 (mp_limb_t, mp_limb_t);
+#define mpn_invert_limb(x) mpn_invert_3by2 ((x), 0)
+
+size_t mpn_get_str (unsigned char *, int, mp_ptr, mp_size_t);
+mp_size_t mpn_set_str (mp_ptr, const unsigned char *, size_t, int);
+
+void mpz_init (mpz_t);
+void mpz_init2 (mpz_t, mp_bitcnt_t);
+void mpz_clear (mpz_t);
+
+#define mpz_odd_p(z) (((z)->_mp_size != 0) & (int) (z)->_mp_d[0])
+#define mpz_even_p(z) (! mpz_odd_p (z))
+
+int mpz_sgn (const mpz_t);
+int mpz_cmp_si (const mpz_t, long);
+int mpz_cmp_ui (const mpz_t, unsigned long);
+int mpz_cmp (const mpz_t, const mpz_t);
+int mpz_cmpabs_ui (const mpz_t, unsigned long);
+int mpz_cmpabs (const mpz_t, const mpz_t);
+int mpz_cmp_d (const mpz_t, double);
+int mpz_cmpabs_d (const mpz_t, double);
+
+void mpz_abs (mpz_t, const mpz_t);
+void mpz_neg (mpz_t, const mpz_t);
+void mpz_swap (mpz_t, mpz_t);
+
+void mpz_add_ui (mpz_t, const mpz_t, unsigned long);
+void mpz_add (mpz_t, const mpz_t, const mpz_t);
+void mpz_sub_ui (mpz_t, const mpz_t, unsigned long);
+void mpz_ui_sub (mpz_t, unsigned long, const mpz_t);
+void mpz_sub (mpz_t, const mpz_t, const mpz_t);
+
+void mpz_mul_si (mpz_t, const mpz_t, long int);
+void mpz_mul_ui (mpz_t, const mpz_t, unsigned long int);
+void mpz_mul (mpz_t, const mpz_t, const mpz_t);
+void mpz_mul_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_addmul_ui (mpz_t, const mpz_t, unsigned long int);
+void mpz_addmul (mpz_t, const mpz_t, const mpz_t);
+void mpz_submul_ui (mpz_t, const mpz_t, unsigned long int);
+void mpz_submul (mpz_t, const mpz_t, const mpz_t);
+
+void mpz_cdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t);
+void mpz_fdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t);
+void mpz_tdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t);
+void mpz_cdiv_q (mpz_t, const mpz_t, const mpz_t);
+void mpz_fdiv_q (mpz_t, const mpz_t, const mpz_t);
+void mpz_tdiv_q (mpz_t, const mpz_t, const mpz_t);
+void mpz_cdiv_r (mpz_t, const mpz_t, const mpz_t);
+void mpz_fdiv_r (mpz_t, const mpz_t, const mpz_t);
+void mpz_tdiv_r (mpz_t, const mpz_t, const mpz_t);
+
+void mpz_cdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_fdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_tdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_cdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_fdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_tdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+
+void mpz_mod (mpz_t, const mpz_t, const mpz_t);
+
+void mpz_divexact (mpz_t, const mpz_t, const mpz_t);
+
+int mpz_divisible_p (const mpz_t, const mpz_t);
+int mpz_congruent_p (const mpz_t, const mpz_t, const mpz_t);
+
+unsigned long mpz_cdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_fdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_tdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_cdiv_q_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_fdiv_q_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_tdiv_q_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_cdiv_r_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_fdiv_r_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_tdiv_r_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_cdiv_ui (const mpz_t, unsigned long);
+unsigned long mpz_fdiv_ui (const mpz_t, unsigned long);
+unsigned long mpz_tdiv_ui (const mpz_t, unsigned long);
+
+unsigned long mpz_mod_ui (mpz_t, const mpz_t, unsigned long);
+
+void mpz_divexact_ui (mpz_t, const mpz_t, unsigned long);
+
+int mpz_divisible_ui_p (const mpz_t, unsigned long);
+
+unsigned long mpz_gcd_ui (mpz_t, const mpz_t, unsigned long);
+void mpz_gcd (mpz_t, const mpz_t, const mpz_t);
+void mpz_gcdext (mpz_t, mpz_t, mpz_t, const mpz_t, const mpz_t);
+void mpz_lcm_ui (mpz_t, const mpz_t, unsigned long);
+void mpz_lcm (mpz_t, const mpz_t, const mpz_t);
+int mpz_invert (mpz_t, const mpz_t, const mpz_t);
+
+void mpz_sqrtrem (mpz_t, mpz_t, const mpz_t);
+void mpz_sqrt (mpz_t, const mpz_t);
+int mpz_perfect_square_p (const mpz_t);
+
+void mpz_pow_ui (mpz_t, const mpz_t, unsigned long);
+void mpz_ui_pow_ui (mpz_t, unsigned long, unsigned long);
+void mpz_powm (mpz_t, const mpz_t, const mpz_t, const mpz_t);
+void mpz_powm_ui (mpz_t, const mpz_t, unsigned long, const mpz_t);
+
+void mpz_rootrem (mpz_t, mpz_t, const mpz_t, unsigned long);
+int mpz_root (mpz_t, const mpz_t, unsigned long);
+
+void mpz_fac_ui (mpz_t, unsigned long);
+void mpz_2fac_ui (mpz_t, unsigned long);
+void mpz_mfac_uiui (mpz_t, unsigned long, unsigned long);
+void mpz_bin_uiui (mpz_t, unsigned long, unsigned long);
+
+int mpz_probab_prime_p (const mpz_t, int);
+
+int mpz_tstbit (const mpz_t, mp_bitcnt_t);
+void mpz_setbit (mpz_t, mp_bitcnt_t);
+void mpz_clrbit (mpz_t, mp_bitcnt_t);
+void mpz_combit (mpz_t, mp_bitcnt_t);
+
+void mpz_com (mpz_t, const mpz_t);
+void mpz_and (mpz_t, const mpz_t, const mpz_t);
+void mpz_ior (mpz_t, const mpz_t, const mpz_t);
+void mpz_xor (mpz_t, const mpz_t, const mpz_t);
+
+mp_bitcnt_t mpz_popcount (const mpz_t);
+mp_bitcnt_t mpz_hamdist (const mpz_t, const mpz_t);
+mp_bitcnt_t mpz_scan0 (const mpz_t, mp_bitcnt_t);
+mp_bitcnt_t mpz_scan1 (const mpz_t, mp_bitcnt_t);
+
+int mpz_fits_slong_p (const mpz_t);
+int mpz_fits_ulong_p (const mpz_t);
+int mpz_fits_sint_p (const mpz_t);
+int mpz_fits_uint_p (const mpz_t);
+int mpz_fits_sshort_p (const mpz_t);
+int mpz_fits_ushort_p (const mpz_t);
+long int mpz_get_si (const mpz_t);
+unsigned long int mpz_get_ui (const mpz_t);
+double mpz_get_d (const mpz_t);
+size_t mpz_size (const mpz_t);
+mp_limb_t mpz_getlimbn (const mpz_t, mp_size_t);
+
+void mpz_realloc2 (mpz_t, mp_bitcnt_t);
+mp_srcptr mpz_limbs_read (mpz_srcptr);
+mp_ptr mpz_limbs_modify (mpz_t, mp_size_t);
+mp_ptr mpz_limbs_write (mpz_t, mp_size_t);
+void mpz_limbs_finish (mpz_t, mp_size_t);
+mpz_srcptr mpz_roinit_n (mpz_t, mp_srcptr, mp_size_t);
+
+#define MPZ_ROINIT_N(xp, xs) {{0, (xs),(xp) }}
+
+void mpz_set_si (mpz_t, signed long int);
+void mpz_set_ui (mpz_t, unsigned long int);
+void mpz_set (mpz_t, const mpz_t);
+void mpz_set_d (mpz_t, double);
+
+void mpz_init_set_si (mpz_t, signed long int);
+void mpz_init_set_ui (mpz_t, unsigned long int);
+void mpz_init_set (mpz_t, const mpz_t);
+void mpz_init_set_d (mpz_t, double);
+
+size_t mpz_sizeinbase (const mpz_t, int);
+char *mpz_get_str (char *, int, const mpz_t);
+int mpz_set_str (mpz_t, const char *, int);
+int mpz_init_set_str (mpz_t, const char *, int);
+
+/* This long list taken from gmp.h. */
+/* For reference, "defined(EOF)" cannot be used here. In g++ 2.95.4,
+ <iostream> defines EOF but not FILE. */
+#if defined (FILE) \
+ || defined (H_STDIO) \
+ || defined (_H_STDIO) /* AIX */ \
+ || defined (_STDIO_H) /* glibc, Sun, SCO */ \
+ || defined (_STDIO_H_) /* BSD, OSF */ \
+ || defined (__STDIO_H) /* Borland */ \
+ || defined (__STDIO_H__) /* IRIX */ \
+ || defined (_STDIO_INCLUDED) /* HPUX */ \
+ || defined (__dj_include_stdio_h_) /* DJGPP */ \
+ || defined (_FILE_DEFINED) /* Microsoft */ \
+ || defined (__STDIO__) /* Apple MPW MrC */ \
+ || defined (_MSL_STDIO_H) /* Metrowerks */ \
+ || defined (_STDIO_H_INCLUDED) /* QNX4 */ \
+ || defined (_ISO_STDIO_ISO_H) /* Sun C++ */ \
+ || defined (__STDIO_LOADED) /* VMS */
+size_t mpz_out_str (FILE *, int, const mpz_t);
+#endif
+
+void mpz_import (mpz_t, size_t, int, size_t, int, size_t, const void *);
+void *mpz_export (void *, size_t *, int, size_t, int, size_t, const mpz_t);
+
+#if defined (__cplusplus)
+}
+#endif
+#endif /* __MINI_GMP_H__ */
diff --git a/lib/mkancesdirs.c b/lib/mkancesdirs.c
index 14646f4..f638a6c 100644
--- a/lib/mkancesdirs.c
+++ b/lib/mkancesdirs.c
@@ -28,7 +28,7 @@
#include <errno.h>
#include <unistd.h>
-#include "dirname.h"
+#include "filename.h"
#include "savewd.h"
/* Ensure that the ancestor directories of FILE exist, using an
diff --git a/lib/mkancesdirs.h b/lib/mkancesdirs.h
index fdfd654..6a9d1e0 100644
--- a/lib/mkancesdirs.h
+++ b/lib/mkancesdirs.h
@@ -1,3 +1,21 @@
+/* Ensure the existence of the ancestor directories of a file.
+ Copyright (C) 2006-2020 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 Paul Eggert, 2006. */
+
#include <stddef.h>
#ifdef __cplusplus
diff --git a/lib/pthread-spin.c b/lib/pthread-spin.c
index 93e8b37..c131050 100644
--- a/lib/pthread-spin.c
+++ b/lib/pthread-spin.c
@@ -21,6 +21,8 @@
/* Specification. */
#include <pthread.h>
+#include <stdbool.h>
+
#if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
# include "windows-spin.h"
#endif
@@ -67,11 +69,60 @@ pthread_spin_destroy (pthread_spinlock_t *lock)
require to link with -latomic. */
# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
-/* Use GCC built-ins (available in GCC >= 4.7) that operate on the first byte
- of the lock.
+/* Use GCC built-ins (available in GCC >= 4.7) that operate on the first 32-bit
+ word of the lock.
Documentation:
<https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/_005f_005fatomic-Builtins.html> */
+# if 1
+/* An implementation that verifies the unlocks. */
+
+int
+pthread_spin_init (pthread_spinlock_t *lock,
+ int shared_across_processes _GL_UNUSED)
+{
+ __atomic_store_n ((unsigned int *) lock, 0, __ATOMIC_SEQ_CST);
+ return 0;
+}
+
+int
+pthread_spin_lock (pthread_spinlock_t *lock)
+{
+ /* Wait until *lock becomes 0, then replace it with 1. */
+ unsigned int zero;
+ while (!(zero = 0,
+ __atomic_compare_exchange_n ((unsigned int *) lock, &zero, 1, false,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)))
+ ;
+ return 0;
+}
+
+int
+pthread_spin_trylock (pthread_spinlock_t *lock)
+{
+ unsigned int zero;
+ if (!(zero = 0,
+ __atomic_compare_exchange_n ((unsigned int *) lock, &zero, 1, false,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)))
+ return EBUSY;
+ return 0;
+}
+
+int
+pthread_spin_unlock (pthread_spinlock_t *lock)
+{
+ /* If *lock is 1, then replace it with 0. */
+ unsigned int one = 1;
+ if (!__atomic_compare_exchange_n ((unsigned int *) lock, &one, 0, false,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+ abort ();
+ return 0;
+}
+
+# else
+/* An implementation that is a little bit more optimized, but does not verify
+ the unlocks. */
+
int
pthread_spin_init (pthread_spinlock_t *lock,
int shared_across_processes _GL_UNUSED)
@@ -103,6 +154,54 @@ pthread_spin_unlock (pthread_spinlock_t *lock)
return 0;
}
+# endif
+
+int
+pthread_spin_destroy (pthread_spinlock_t *lock)
+{
+ return 0;
+}
+
+# elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
+/* Use GCC built-ins (available in GCC >= 4.1).
+ Documentation:
+ <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html> */
+
+int
+pthread_spin_init (pthread_spinlock_t *lock,
+ int shared_across_processes _GL_UNUSED)
+{
+ * (volatile unsigned int *) lock = 0;
+ __sync_synchronize ();
+ return 0;
+}
+
+int
+pthread_spin_lock (pthread_spinlock_t *lock)
+{
+ /* Wait until *lock becomes 0, then replace it with 1. */
+ while (__sync_val_compare_and_swap ((unsigned int *) lock, 0, 1) != 0)
+ ;
+ return 0;
+}
+
+int
+pthread_spin_trylock (pthread_spinlock_t *lock)
+{
+ if (__sync_val_compare_and_swap ((unsigned int *) lock, 0, 1) != 0)
+ return EBUSY;
+ return 0;
+}
+
+int
+pthread_spin_unlock (pthread_spinlock_t *lock)
+{
+ /* If *lock is 1, then replace it with 0. */
+ if (__sync_val_compare_and_swap ((unsigned int *) lock, 1, 0) != 1)
+ abort ();
+ return 0;
+}
+
int
pthread_spin_destroy (pthread_spinlock_t *lock)
{
diff --git a/lib/scratch_buffer.h b/lib/scratch_buffer.h
index 4c0db3e..0b45f92 100644
--- a/lib/scratch_buffer.h
+++ b/lib/scratch_buffer.h
@@ -1,3 +1,21 @@
+/* Variable-sized buffer with on-stack default allocation.
+ Copyright (C) 2017-2020 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 Paul Eggert, 2017. */
+
#ifndef _GL_SCRATCH_BUFFER_H
#define _GL_SCRATCH_BUFFER_H
diff --git a/lib/se-context.in.h b/lib/se-context.in.h
index c3890a2..5306937 100644
--- a/lib/se-context.in.h
+++ b/lib/se-context.in.h
@@ -1,3 +1,21 @@
+/* SELinux-related headers.
+ Copyright (C) 2007-2020 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 Jim Meyering, 2007. */
+
#ifndef SELINUX_CONTEXT_H
# define SELINUX_CONTEXT_H
diff --git a/lib/stdopen.h b/lib/stdopen.h
index 27901d3..ccc62a0 100644
--- a/lib/stdopen.h
+++ b/lib/stdopen.h
@@ -1,3 +1,21 @@
+/* Arrange for stdin/stdout/stderr to be open.
+ Copyright (C) 2005-2020 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 Jim Meyering, 2005. */
+
#ifndef STDOPEN_H
# define STDOPEN_H 1
diff --git a/lib/string.in.h b/lib/string.in.h
index 77ccf94..c18efa7 100644
--- a/lib/string.in.h
+++ b/lib/string.in.h
@@ -134,11 +134,6 @@ _GL_FUNCDECL_RPL (memchr, void *, (void const *__s, int __c, size_t __n)
_GL_ARG_NONNULL ((1)));
_GL_CXXALIAS_RPL (memchr, void *, (void const *__s, int __c, size_t __n));
# else
-# if ! @HAVE_MEMCHR@
-_GL_FUNCDECL_SYS (memchr, void *, (void const *__s, int __c, size_t __n)
- _GL_ATTRIBUTE_PURE
- _GL_ARG_NONNULL ((1)));
-# endif
/* On some systems, this function is defined as an overloaded function:
extern "C" { const void * std::memchr (const void *, int, size_t); }
extern "C++" { void * std::memchr (void *, int, size_t); } */
diff --git a/lib/supersede.c b/lib/supersede.c
new file mode 100644
index 0000000..92317f2
--- a/dev/null
+++ b/lib/supersede.c
@@ -0,0 +1,469 @@
+/* Open a file, without destroying an old file with the same name.
+
+ Copyright (C) 2020 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, 2020. */
+
+#include <config.h>
+
+/* Specification. */
+#include "supersede.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#if defined _WIN32 && !defined __CYGWIN__
+/* A native Windows platform. */
+# define WIN32_LEAN_AND_MEAN /* avoid including junk */
+# include <windows.h>
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+#include "canonicalize.h"
+#include "clean-temp.h"
+#include "ignore-value.h"
+#include "stat-time.h"
+#include "utimens.h"
+#include "acl.h"
+
+#if defined _WIN32 && !defined __CYGWIN__
+/* Don't assume that UNICODE is not defined. */
+# undef MoveFileEx
+# define MoveFileEx MoveFileExA
+#endif
+
+static int
+create_temp_file (char *canon_filename, int flags, mode_t mode,
+ struct supersede_final_action *action)
+{
+ /* Use a temporary file always. */
+ size_t canon_filename_length = strlen (canon_filename);
+
+ /* The temporary file needs to be in the same directory, otherwise the
+ final rename may fail. */
+ char *temp_filename = (char *) malloc (canon_filename_length + 7 + 1);
+ memcpy (temp_filename, canon_filename, canon_filename_length);
+ memcpy (temp_filename + canon_filename_length, ".XXXXXX", 7 + 1);
+
+ int fd = gen_register_open_temp (temp_filename, 0, flags, mode);
+ if (fd < 0)
+ return -1;
+
+ action->final_rename_temp = temp_filename;
+ action->final_rename_dest = canon_filename;
+ return fd;
+}
+
+int
+open_supersede (const char *filename, int flags, mode_t mode,
+ bool supersede_if_exists, bool supersede_if_does_not_exist,
+ struct supersede_final_action *action)
+{
+ int fd;
+
+ if (supersede_if_exists)
+ {
+ if (supersede_if_does_not_exist)
+ {
+ struct stat statbuf;
+
+ if (stat (filename, &statbuf) >= 0
+ && ! S_ISREG (statbuf.st_mode)
+ /* The file exists and is possibly a character device, socket, or
+ something like that. */
+ && ((fd = open (filename, flags, mode)) >= 0
+ || errno != ENOENT))
+ {
+ if (fd >= 0)
+ {
+ action->final_rename_temp = NULL;
+ action->final_rename_dest = NULL;
+ }
+ }
+ else
+ {
+ /* The file does not exist or is a regular file.
+ Use a temporary file. */
+ char *canon_filename =
+ canonicalize_filename_mode (filename, CAN_ALL_BUT_LAST);
+ if (canon_filename == NULL)
+ fd = -1;
+ else
+ {
+ fd = create_temp_file (canon_filename, flags, mode, action);
+ if (fd < 0)
+ {
+ int saved_errno = errno;
+ free (canon_filename);
+ errno = saved_errno;
+ }
+ }
+ }
+ }
+ else
+ {
+ fd = open (filename, flags | O_CREAT | O_EXCL, mode);
+ if (fd >= 0)
+ {
+ /* The file did not exist. */
+ action->final_rename_temp = NULL;
+ action->final_rename_dest = NULL;
+ }
+ else
+ {
+ /* The file exists or is a symbolic link to a nonexistent
+ file. */
+ char *canon_filename =
+ canonicalize_filename_mode (filename, CAN_ALL_BUT_LAST);
+ if (canon_filename == NULL)
+ fd = -1;
+ else
+ {
+ fd = open (canon_filename, flags | O_CREAT | O_EXCL, mode);
+ if (fd >= 0)
+ {
+ /* It was a symbolic link to a nonexistent file. */
+ free (canon_filename);
+ action->final_rename_temp = NULL;
+ action->final_rename_dest = NULL;
+ }
+ else
+ {
+ /* The file exists. */
+ struct stat statbuf;
+
+ if (stat (canon_filename, &statbuf) >= 0
+ && S_ISREG (statbuf.st_mode))
+ {
+ /* It is a regular file. Use a temporary file. */
+ fd = create_temp_file (canon_filename, flags, mode,
+ action);
+ if (fd < 0)
+ {
+ int saved_errno = errno;
+ free (canon_filename);
+ errno = saved_errno;
+ }
+ }
+ else
+ {
+ /* It is possibly a character device, socket, or
+ something like that. */
+ fd = open (canon_filename, flags, mode);
+ if (fd >= 0)
+ {
+ free (canon_filename);
+ action->final_rename_temp = NULL;
+ action->final_rename_dest = NULL;
+ }
+ else
+ {
+ int saved_errno = errno;
+ free (canon_filename);
+ errno = saved_errno;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (supersede_if_does_not_exist)
+ {
+ fd = open (filename, flags, mode);
+ if (fd >= 0)
+ {
+ /* The file exists. */
+ action->final_rename_temp = NULL;
+ action->final_rename_dest = NULL;
+ }
+ else if (errno == ENOENT)
+ {
+ /* The file does not exist. Use a temporary file. */
+ char *canon_filename =
+ canonicalize_filename_mode (filename, CAN_ALL_BUT_LAST);
+ if (canon_filename == NULL)
+ fd = -1;
+ else
+ {
+ fd = create_temp_file (canon_filename, flags, mode, action);
+ if (fd < 0)
+ {
+ int saved_errno = errno;
+ free (canon_filename);
+ errno = saved_errno;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Never use a temporary file. */
+ fd = open (filename, flags | O_CREAT, mode);
+ action->final_rename_temp = NULL;
+ action->final_rename_dest = NULL;
+ }
+ }
+ return fd;
+}
+
+static int
+after_close_actions (int ret, const struct supersede_final_action *action)
+{
+ if (ret < 0)
+ {
+ /* There was an error writing. Erase the temporary file. */
+ if (action->final_rename_temp != NULL)
+ {
+ int saved_errno = errno;
+ ignore_value (unlink (action->final_rename_temp));
+ free (action->final_rename_temp);
+ free (action->final_rename_dest);
+ errno = saved_errno;
+ }
+ return ret;
+ }
+
+ if (action->final_rename_temp != NULL)
+ {
+ struct stat temp_statbuf;
+ struct stat dest_statbuf;
+
+ if (stat (action->final_rename_temp, &temp_statbuf) < 0)
+ {
+ /* We just finished writing the temporary file, but now cannot access
+ it. There's something wrong. */
+ int saved_errno = errno;
+ ignore_value (unlink (action->final_rename_temp));
+ free (action->final_rename_temp);
+ free (action->final_rename_dest);
+ errno = saved_errno;
+ return -1;
+ }
+
+ if (stat (action->final_rename_dest, &dest_statbuf) >= 0)
+ {
+ /* Copy the access time from the destination file to the temporary
+ file. */
+ {
+ struct timespec ts[2];
+
+ ts[0] = get_stat_atime (&dest_statbuf);
+ ts[1] = get_stat_mtime (&temp_statbuf);
+ ignore_value (utimens (action->final_rename_temp, ts));
+ }
+
+#if HAVE_CHOWN
+ /* Copy the owner and group from the destination file to the
+ temporary file. */
+ ignore_value (chown (action->final_rename_temp,
+ dest_statbuf.st_uid, dest_statbuf.st_gid));
+#endif
+
+ /* Copy the access permissions from the destination file to the
+ temporary file. */
+#if USE_ACL
+ switch (qcopy_acl (action->final_rename_dest, -1,
+ action->final_rename_temp, -1,
+ dest_statbuf.st_mode))
+ {
+ case -2:
+ /* Could not get the ACL of the destination file. */
+ case -1:
+ /* Could not set the ACL on the temporary file. */
+ ignore_value (unlink (action->final_rename_temp));
+ free (action->final_rename_temp);
+ free (action->final_rename_dest);
+ errno = EPERM;
+ return -1;
+ }
+#else
+ chmod (action->final_rename_temp, dest_statbuf.st_mode);
+#endif
+ }
+ else
+ /* No chmod needed, since the mode was already passed to
+ gen_register_open_temp. */
+ ;
+
+ /* Rename the temporary file to the destination file. */
+#if defined _WIN32 && !defined __CYGWIN__
+ /* A native Windows platform. */
+ /* ReplaceFile
+ <https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-replacefilea>
+ is atomic regarding the file's contents, says
+ https://stackoverflow.com/questions/167414/is-an-atomic-file-rename-with-overwrite-possible-on-windows>
+ But it fails with GetLastError () == ERROR_FILE_NOT_FOUND if
+ action->final_rename_dest does not exist. So better use
+ MoveFileEx
+ <https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-movefileexa>. */
+ if (!MoveFileEx (action->final_rename_temp, action->final_rename_dest,
+ MOVEFILE_REPLACE_EXISTING))
+ {
+ int saved_errno;
+ switch (GetLastError ())
+ {
+ case ERROR_INVALID_PARAMETER:
+ saved_errno = EINVAL; break;
+ default:
+ saved_errno = EIO; break;
+ }
+ ignore_value (unlink (action->final_rename_temp));
+ free (action->final_rename_temp);
+ free (action->final_rename_dest);
+ errno = saved_errno;
+ return -1;
+ }
+#else
+ if (rename (action->final_rename_temp, action->final_rename_dest) < 0)
+ {
+ int saved_errno = errno;
+ ignore_value (unlink (action->final_rename_temp));
+ free (action->final_rename_temp);
+ free (action->final_rename_dest);
+ errno = saved_errno;
+ return -1;
+ }
+#endif
+
+ unregister_temporary_file (action->final_rename_temp);
+
+ free (action->final_rename_temp);
+ free (action->final_rename_dest);
+ }
+
+ return ret;
+}
+
+int
+close_supersede (int fd, const struct supersede_final_action *action)
+{
+ if (fd < 0)
+ {
+ int saved_errno = errno;
+ free (action->final_rename_temp);
+ free (action->final_rename_dest);
+ errno = saved_errno;
+ return fd;
+ }
+
+ int ret;
+ if (action->final_rename_temp != NULL)
+ ret = close_temp (fd);
+ else
+ ret = close (fd);
+ return after_close_actions (ret, action);
+}
+
+FILE *
+fopen_supersede (const char *filename, const char *mode,
+ bool supersede_if_exists, bool supersede_if_does_not_exist,
+ struct supersede_final_action *action)
+{
+ /* Parse the mode. */
+ int open_direction = 0;
+ int open_flags = 0;
+ {
+ const char *p = mode;
+
+ for (; *p != '\0'; p++)
+ {
+ switch (*p)
+ {
+ case 'r':
+ open_direction = O_RDONLY;
+ continue;
+ case 'w':
+ open_direction = O_WRONLY;
+ open_flags |= /* not! O_CREAT | */ O_TRUNC;
+ continue;
+ case 'a':
+ open_direction = O_WRONLY;
+ open_flags |= /* not! O_CREAT | */ O_APPEND;
+ continue;
+ case 'b':
+ /* While it is non-standard, O_BINARY is guaranteed by
+ gnulib <fcntl.h>. */
+ open_flags |= O_BINARY;
+ continue;
+ case '+':
+ open_direction = O_RDWR;
+ continue;
+ case 'x':
+ /* not! open_flags |= O_EXCL; */
+ continue;
+ case 'e':
+ open_flags |= O_CLOEXEC;
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ mode_t open_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ int fd = open_supersede (filename, open_direction | open_flags, open_mode,
+ supersede_if_exists, supersede_if_does_not_exist,
+ action);
+ if (fd < 0)
+ return NULL;
+
+ FILE *stream = fdopen (fd, mode);
+ if (stream == NULL)
+ {
+ int saved_errno = errno;
+ close (fd);
+ close_supersede (-1, action);
+ errno = saved_errno;
+ }
+ return stream;
+}
+
+int
+fclose_supersede (FILE *stream, const struct supersede_final_action *action)
+{
+ if (stream == NULL)
+ return -1;
+ int ret;
+ if (action->final_rename_temp != NULL)
+ ret = fclose_temp (stream);
+ else
+ ret = fclose (stream);
+ return after_close_actions (ret, action);
+}
+
+#if GNULIB_FWRITEERROR
+int
+fwriteerror_supersede (FILE *stream, const struct supersede_final_action *action)
+{
+ if (stream == NULL)
+ return -1;
+ int ret;
+ if (action->final_rename_temp != NULL)
+ ret = fclose_temp (stream);
+ else
+ ret = fclose (stream);
+ return after_close_actions (ret, action);
+}
+#endif
diff --git a/lib/supersede.h b/lib/supersede.h
new file mode 100644
index 0000000..111d15b
--- a/dev/null
+++ b/lib/supersede.h
@@ -0,0 +1,157 @@
+/* Open a file, without destroying an old file with the same name.
+
+ Copyright (C) 2020 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, 2020. */
+
+#ifndef _GL_SUPERSEDE_H
+#define _GL_SUPERSEDE_H
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* When writing a file, for some usages it is important that at any moment,
+ a process that opens the file will see consistent data in the file. This
+ can be important in two situations:
+ * If supersede_if_exists == true, then when the file already existed,
+ it is important that a process that opens the file while the new file's
+ contents is being written sees consistent data - namely the old file's
+ data.
+ * If supersede_if_does_not_exist == true, then when the file did not exist,
+ it is important that a process that opens the file while the new file's
+ contents is being written sees no file (as opposed to a file with
+ truncated contents).
+
+ In both situations, the effect is implemented by creating a temporary file,
+ writing into that temporary file, and renaming the temporary file when the
+ temporary file's contents is complete.
+
+ Note that opening a file with superseding may fail when it would succeed
+ without superseding (for example, for a writable file in an unwritable
+ directory). And also the other way around: Opening a file with superseding
+ may succeed although it would fail without superseding (for example, for
+ an unwritable file in a writable directory). */
+
+/* This type holds everything that needs to needs to be remembered in order to
+ execute the final rename action. */
+struct supersede_final_action
+{
+ char *final_rename_temp;
+ char *final_rename_dest;
+};
+
+/* =================== open() and close() with supersede =================== */
+
+/* The typical code idiom is like this:
+
+ struct supersede_final_action action;
+ int fd = open_supersede (filename, O_RDWR, mode,
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ if (fd >= 0)
+ {
+ ... write the file's contents ...
+ if (successful)
+ {
+ if (close_supersede (fd, &action) < 0)
+ error (...);
+ }
+ else
+ {
+ // Abort the operation.
+ close (fd);
+ close_supersede (-1, &action);
+ }
+ }
+ */
+
+/* Opens a file (typically for writing) in superseding mode, depending on
+ supersede_if_exists and supersede_if_does_not_exist.
+ FLAGS should not contain O_CREAT nor O_EXCL.
+ MODE is used when the file does not yet exist. The umask of the process
+ is considered, like in open(), i.e. the effective mode is
+ (MODE & ~ getumask ()).
+ Upon success, it fills in ACTION and returns a file descriptor.
+ Upon failure, it returns -1 and sets errno. */
+extern int open_supersede (const char *filename, int flags, mode_t mode,
+ bool supersede_if_exists,
+ bool supersede_if_does_not_exist,
+ struct supersede_final_action *action);
+
+/* Closes a file and executes the final rename action.
+ FD must have been returned by open_supersede(), or -1 if you want to abort
+ the operation. */
+extern int close_supersede (int fd,
+ const struct supersede_final_action *action);
+
+/* ================== fopen() and fclose() with supersede ================== */
+
+/* The typical code idiom is like this:
+
+ struct supersede_final_action action;
+ FILE *stream =
+ fopen_supersede (filename, O_RDWR, mode,
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ if (stream != NULL)
+ {
+ ... write the file's contents ...
+ if (successful)
+ {
+ if (fclose_supersede (stream, &action) < 0)
+ error (...);
+ }
+ else
+ {
+ // Abort the operation.
+ fclose (stream);
+ fclose_supersede (NULL, &action);
+ }
+ }
+ */
+
+/* Opens a file (typically for writing) in superseding mode, depending on
+ supersede_if_exists and supersede_if_does_not_exist.
+ Upon success, it fills in ACTION and returns a file stream.
+ Upon failure, it returns NULL and sets errno. */
+extern FILE *fopen_supersede (const char *filename, const char *mode,
+ bool supersede_if_exists,
+ bool supersede_if_does_not_exist,
+ struct supersede_final_action *action);
+
+/* Closes a file stream and executes the final rename action.
+ STREAM must have been returned by fopen_supersede(), or NULL if you want to
+ abort the operation. */
+extern int fclose_supersede (FILE *stream,
+ const struct supersede_final_action *action);
+
+/* Closes a file stream, like with fwriteerror, and executes the final rename
+ action.
+ STREAM must have been returned by fopen_supersede(), or NULL if you want to
+ abort the operation. */
+extern int fwriteerror_supersede (FILE *stream,
+ const struct supersede_final_action *action);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GL_SUPERSEDE_H */
diff --git a/lib/sys_socket.in.h b/lib/sys_socket.in.h
index 948d689..577947d 100644
--- a/lib/sys_socket.in.h
+++ b/lib/sys_socket.in.h
@@ -195,12 +195,7 @@ struct sockaddr_storage
/* Include headers needed by the emulation code. */
# include <sys/types.h>
# include <io.h>
-
-# if !GNULIB_defined_socklen_t
-typedef int socklen_t;
-# define GNULIB_defined_socklen_t 1
-# endif
-
+/* If these headers don't define socklen_t, <config.h> does. */
# endif
/* Rudimentary 'struct msghdr'; this works as long as you don't try to
diff --git a/lib/sys_stat.in.h b/lib/sys_stat.in.h
index dc8881e..c1618a6 100644
--- a/lib/sys_stat.in.h
+++ b/lib/sys_stat.in.h
@@ -515,6 +515,23 @@ _GL_WARN_ON_USE (futimens, "futimens is not portable - "
#endif
+#if @GNULIB_GETUMASK@
+# if !@HAVE_GETUMASK@
+_GL_FUNCDECL_SYS (getumask, mode_t, (void));
+# endif
+_GL_CXXALIAS_SYS (getumask, mode_t, (void));
+# if @HAVE_GETUMASK@
+_GL_CXXALIASWARN (getumask);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef getumask
+# if HAVE_RAW_DECL_GETUMASK
+_GL_WARN_ON_USE (getumask, "getumask is not portable - "
+ "use gnulib module getumask for portability");
+# endif
+#endif
+
+
#if @GNULIB_LCHMOD@
/* Change the mode of FILENAME to MODE, without dereferencing it if FILENAME
denotes a symbolic link. */
diff --git a/lib/tmpfile.c b/lib/tmpfile.c
index 667c0f2..bdabe27 100644
--- a/lib/tmpfile.c
+++ b/lib/tmpfile.c
@@ -25,7 +25,7 @@
#include <stdbool.h>
#if defined _WIN32 && ! defined __CYGWIN__
-/* A native Windows platforms. */
+/* A native Windows platform. */
# include <fcntl.h>
# include <string.h>
@@ -50,7 +50,7 @@
used on native Windows and Android. */
#if defined _WIN32 && ! defined __CYGWIN__
-/* A native Windows platforms. */
+/* A native Windows platform. */
/* Don't assume that UNICODE is not defined. */
# undef OSVERSIONINFO
diff --git a/lib/tzset.c b/lib/tzset.c
index 24c972d..e3543ba 100644
--- a/lib/tzset.c
+++ b/lib/tzset.c
@@ -22,6 +22,9 @@
/* Specification. */
#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "localtime-buffer.h"
/* This is a wrapper for tzset, for systems on which tzset may clobber
diff --git a/lib/unictype/joininggroup_name.c b/lib/unictype/joininggroup_name.c
index dc683f8..b45ab5c 100644
--- a/lib/unictype/joininggroup_name.c
+++ b/lib/unictype/joininggroup_name.c
@@ -40,7 +40,7 @@ static const struct joining_group_stringpool_t joining_group_stringpool_contents
static const int joining_group_index[] =
{
-#define ELEM(tag,string) (int)(long)&((struct joining_group_stringpool_t *)0)->tag,
+#define ELEM(tag,string) (int)(size_t)&((struct joining_group_stringpool_t *)0)->tag,
#include "unictype/joininggroup_name.h"
#undef ELEM
};
diff --git a/lib/unistd.in.h b/lib/unistd.in.h
index 71904fe..3a49813 100644
--- a/lib/unistd.in.h
+++ b/lib/unistd.in.h
@@ -408,9 +408,6 @@ _GL_WARN_ON_USE (dup, "dup is unportable - "
_GL_FUNCDECL_RPL (dup2, int, (int oldfd, int newfd));
_GL_CXXALIAS_RPL (dup2, int, (int oldfd, int newfd));
# else
-# if !@HAVE_DUP2@
-_GL_FUNCDECL_SYS (dup2, int, (int oldfd, int newfd));
-# endif
_GL_CXXALIAS_SYS (dup2, int, (int oldfd, int newfd));
# endif
_GL_CXXALIASWARN (dup2);
diff --git a/lib/userspec.h b/lib/userspec.h
index 67af845..e20c390 100644
--- a/lib/userspec.h
+++ b/lib/userspec.h
@@ -1,3 +1,21 @@
+/* Parse a 'user:group' specifier (e.g. the first argument of chown utility).
+ Copyright (C) 2003-2020 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 Jim Meyering, 2003. */
+
#ifndef USERSPEC_H
# define USERSPEC_H 1
diff --git a/lib/windows-spin.c b/lib/windows-spin.c
index b90e7ae..4643ac1 100644
--- a/lib/windows-spin.c
+++ b/lib/windows-spin.c
@@ -27,6 +27,7 @@ void
glwthread_spin_init (glwthread_spinlock_t *lock)
{
lock->word = 0;
+ MemoryBarrier ();
}
int
diff --git a/lib/windows-thread.c b/lib/windows-thread.c
index 5b4edb0..16574e1 100644
--- a/lib/windows-thread.c
+++ b/lib/windows-thread.c
@@ -232,7 +232,7 @@ glwthread_thread_detach (glwthread_thread_t thread)
return 0;
}
-int
+void
glwthread_thread_exit (void *retval)
{
glwthread_thread_t thread = glwthread_thread_self ();
diff --git a/lib/windows-thread.h b/lib/windows-thread.h
index d026713..90da7bd 100644
--- a/lib/windows-thread.h
+++ b/lib/windows-thread.h
@@ -46,7 +46,7 @@ extern int glwthread_thread_create (glwthread_thread_t *threadp,
extern int glwthread_thread_join (glwthread_thread_t thread, void **retvalp);
extern int glwthread_thread_detach (glwthread_thread_t thread);
extern glwthread_thread_t glwthread_thread_self (void);
-extern int glwthread_thread_exit (void *retval);
+extern _Noreturn void glwthread_thread_exit (void *retval);
#ifdef __cplusplus
}
diff --git a/lib/xgetcwd.c b/lib/xgetcwd.c
index 62b8c8c..8fb9342 100644
--- a/lib/xgetcwd.c
+++ b/lib/xgetcwd.c
@@ -29,7 +29,7 @@
/* Return the current directory, newly allocated.
Upon an out-of-memory error, call xalloc_die.
- Upon any other type of error, return NULL. */
+ Upon any other type of error, return NULL with errno set. */
char *
xgetcwd (void)
diff --git a/m4/dirname.m4 b/m4/dirname.m4
deleted file mode 100644
index 9995ff3..0000000
--- a/m4/dirname.m4
+++ b/dev/null
@@ -1,19 +0,0 @@
-#serial 10 -*- autoconf -*-
-dnl Copyright (C) 2002-2006, 2009-2020 Free Software Foundation, Inc.
-dnl This file is free software; the Free Software Foundation
-dnl gives unlimited permission to copy and/or distribute it,
-dnl with or without modifications, as long as this notice is preserved.
-
-AC_DEFUN([gl_DIRNAME],
-[
- AC_REQUIRE([gl_DIRNAME_LGPL])
-])
-
-AC_DEFUN([gl_DIRNAME_LGPL],
-[
- dnl Prerequisites of lib/dirname.h.
- AC_REQUIRE([gl_DOUBLE_SLASH_ROOT])
-
- dnl No prerequisites of lib/basename-lgpl.c, lib/dirname-lgpl.c,
- dnl lib/stripslash.c.
-])
diff --git a/m4/dup2.m4 b/m4/dup2.m4
index 21b1ecc..462bfd0 100644
--- a/m4/dup2.m4
+++ b/m4/dup2.m4
@@ -1,4 +1,4 @@
-#serial 25
+#serial 26
dnl Copyright (C) 2002, 2005, 2007, 2009-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -8,107 +8,94 @@ AC_DEFUN([gl_FUNC_DUP2],
[
AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
AC_REQUIRE([AC_CANONICAL_HOST])
- m4_ifdef([gl_FUNC_DUP2_OBSOLETE], [
- AC_CHECK_FUNCS_ONCE([dup2])
- if test $ac_cv_func_dup2 = no; then
- HAVE_DUP2=0
- fi
- ], [
- AC_DEFINE([HAVE_DUP2], [1], [Define to 1 if you have the 'dup2' function.])
- ])
- if test $HAVE_DUP2 = 1; then
- AC_CACHE_CHECK([whether dup2 works], [gl_cv_func_dup2_works],
- [AC_RUN_IFELSE([
- AC_LANG_PROGRAM(
- [[#include <errno.h>
- #include <fcntl.h>
- #include <limits.h>
- #include <sys/resource.h>
- #include <unistd.h>
- #ifndef RLIM_SAVED_CUR
- # define RLIM_SAVED_CUR RLIM_INFINITY
- #endif
- #ifndef RLIM_SAVED_MAX
- # define RLIM_SAVED_MAX RLIM_INFINITY
- #endif
- ]],
- [[int result = 0;
- int bad_fd = INT_MAX;
- struct rlimit rlim;
- if (getrlimit (RLIMIT_NOFILE, &rlim) == 0
- && 0 <= rlim.rlim_cur && rlim.rlim_cur <= INT_MAX
- && rlim.rlim_cur != RLIM_INFINITY
- && rlim.rlim_cur != RLIM_SAVED_MAX
- && rlim.rlim_cur != RLIM_SAVED_CUR)
- bad_fd = rlim.rlim_cur;
- #ifdef FD_CLOEXEC
- if (fcntl (1, F_SETFD, FD_CLOEXEC) == -1)
- result |= 1;
- #endif
- if (dup2 (1, 1) != 1)
- result |= 2;
- #ifdef FD_CLOEXEC
- if (fcntl (1, F_GETFD) != FD_CLOEXEC)
- result |= 4;
- #endif
- close (0);
- if (dup2 (0, 0) != -1)
- result |= 8;
- /* Many gnulib modules require POSIX conformance of EBADF. */
- if (dup2 (2, bad_fd) == -1 && errno != EBADF)
- result |= 16;
- /* Flush out some cygwin core dumps. */
- if (dup2 (2, -1) != -1 || errno != EBADF)
- result |= 32;
- dup2 (2, 255);
- dup2 (2, 256);
- /* On OS/2 kLIBC, dup2() does not work on a directory fd. */
- {
- int fd = open (".", O_RDONLY);
- if (fd == -1)
- result |= 64;
- else if (dup2 (fd, fd + 1) == -1)
- result |= 128;
-
- close (fd);
- }
- return result;]])
- ],
- [gl_cv_func_dup2_works=yes], [gl_cv_func_dup2_works=no],
- [case "$host_os" in
- mingw*) # on this platform, dup2 always returns 0 for success
- gl_cv_func_dup2_works="guessing no" ;;
- cygwin*) # on cygwin 1.5.x, dup2(1,1) returns 0
- gl_cv_func_dup2_works="guessing no" ;;
- aix* | freebsd*)
- # on AIX 7.1 and FreeBSD 6.1, dup2 (1,toobig) gives EMFILE,
- # not EBADF.
- gl_cv_func_dup2_works="guessing no" ;;
- haiku*) # on Haiku alpha 2, dup2(1, 1) resets FD_CLOEXEC.
- gl_cv_func_dup2_works="guessing no" ;;
- *-android*) # implemented using dup3(), which fails if oldfd == newfd
- gl_cv_func_dup2_works="guessing no" ;;
- os2*) # on OS/2 kLIBC, dup2() does not work on a directory fd.
- gl_cv_func_dup2_works="guessing no" ;;
- *) gl_cv_func_dup2_works="guessing yes" ;;
- esac])
- ])
- case "$gl_cv_func_dup2_works" in
- *yes) ;;
- *)
- REPLACE_DUP2=1
- AC_CHECK_FUNCS([setdtablesize])
- ;;
- esac
- fi
+ AC_CACHE_CHECK([whether dup2 works], [gl_cv_func_dup2_works],
+ [AC_RUN_IFELSE([
+ AC_LANG_PROGRAM(
+ [[#include <errno.h>
+ #include <fcntl.h>
+ #include <limits.h>
+ #include <sys/resource.h>
+ #include <unistd.h>
+ #ifndef RLIM_SAVED_CUR
+ # define RLIM_SAVED_CUR RLIM_INFINITY
+ #endif
+ #ifndef RLIM_SAVED_MAX
+ # define RLIM_SAVED_MAX RLIM_INFINITY
+ #endif
+ ]],
+ [[int result = 0;
+ int bad_fd = INT_MAX;
+ struct rlimit rlim;
+ if (getrlimit (RLIMIT_NOFILE, &rlim) == 0
+ && 0 <= rlim.rlim_cur && rlim.rlim_cur <= INT_MAX
+ && rlim.rlim_cur != RLIM_INFINITY
+ && rlim.rlim_cur != RLIM_SAVED_MAX
+ && rlim.rlim_cur != RLIM_SAVED_CUR)
+ bad_fd = rlim.rlim_cur;
+ #ifdef FD_CLOEXEC
+ if (fcntl (1, F_SETFD, FD_CLOEXEC) == -1)
+ result |= 1;
+ #endif
+ if (dup2 (1, 1) != 1)
+ result |= 2;
+ #ifdef FD_CLOEXEC
+ if (fcntl (1, F_GETFD) != FD_CLOEXEC)
+ result |= 4;
+ #endif
+ close (0);
+ if (dup2 (0, 0) != -1)
+ result |= 8;
+ /* Many gnulib modules require POSIX conformance of EBADF. */
+ if (dup2 (2, bad_fd) == -1 && errno != EBADF)
+ result |= 16;
+ /* Flush out some cygwin core dumps. */
+ if (dup2 (2, -1) != -1 || errno != EBADF)
+ result |= 32;
+ dup2 (2, 255);
+ dup2 (2, 256);
+ /* On OS/2 kLIBC, dup2() does not work on a directory fd. */
+ {
+ int fd = open (".", O_RDONLY);
+ if (fd == -1)
+ result |= 64;
+ else if (dup2 (fd, fd + 1) == -1)
+ result |= 128;
+ close (fd);
+ }
+ return result;]])
+ ],
+ [gl_cv_func_dup2_works=yes], [gl_cv_func_dup2_works=no],
+ [case "$host_os" in
+ mingw*) # on this platform, dup2 always returns 0 for success
+ gl_cv_func_dup2_works="guessing no" ;;
+ cygwin*) # on cygwin 1.5.x, dup2(1,1) returns 0
+ gl_cv_func_dup2_works="guessing no" ;;
+ aix* | freebsd*)
+ # on AIX 7.1 and FreeBSD 6.1, dup2 (1,toobig) gives EMFILE,
+ # not EBADF.
+ gl_cv_func_dup2_works="guessing no" ;;
+ haiku*) # on Haiku alpha 2, dup2(1, 1) resets FD_CLOEXEC.
+ gl_cv_func_dup2_works="guessing no" ;;
+ *-android*) # implemented using dup3(), which fails if oldfd == newfd
+ gl_cv_func_dup2_works="guessing no" ;;
+ os2*) # on OS/2 kLIBC, dup2() does not work on a directory fd.
+ gl_cv_func_dup2_works="guessing no" ;;
+ *) gl_cv_func_dup2_works="guessing yes" ;;
+ esac])
+ ])
+ case "$gl_cv_func_dup2_works" in
+ *yes) ;;
+ *)
+ REPLACE_DUP2=1
+ AC_CHECK_FUNCS([setdtablesize])
+ ;;
+ esac
dnl Replace dup2() for supporting the gnulib-defined fchdir() function,
dnl to keep fchdir's bookkeeping up-to-date.
m4_ifdef([gl_FUNC_FCHDIR], [
gl_TEST_FCHDIR
if test $HAVE_FCHDIR = 0; then
- if test $HAVE_DUP2 = 1; then
- REPLACE_DUP2=1
- fi
+ REPLACE_DUP2=1
fi
])
])
diff --git a/m4/getrandom.m4 b/m4/getrandom.m4
index 37fb100..424c2fa 100644
--- a/m4/getrandom.m4
+++ b/m4/getrandom.m4
@@ -1,4 +1,4 @@
-# getrandom.m4 serial 5
+# getrandom.m4 serial 7
dnl Copyright 2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -36,6 +36,9 @@ AC_DEFUN([gl_FUNC_GETRANDOM],
case "$host_os" in
mingw*)
+ AC_CHECK_HEADERS([bcrypt.h], [], [],
+ [[#include <windows.h>
+ ]])
AC_CACHE_CHECK([whether the bcrypt library is guaranteed to be present],
[gl_cv_lib_assume_bcrypt],
[AC_COMPILE_IFELSE(
diff --git a/m4/getumask.m4 b/m4/getumask.m4
new file mode 100644
index 0000000..f8b526d
--- a/dev/null
+++ b/m4/getumask.m4
@@ -0,0 +1,24 @@
+# getumask.m4 serial 1
+dnl Copyright 2020 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_GETUMASK],
+[
+ AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
+
+ dnl Persuade glibc <sys/stat.h> to declare getumask().
+ AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+ AC_CHECK_FUNCS_ONCE([getumask])
+ if test $ac_cv_func_getumask = no; then
+ HAVE_GETUMASK=0
+ fi
+])
+
+# Prerequisites of lib/getumask.c.
+AC_DEFUN([gl_PREREQ_GETUMASK],
+[
+ :
+])
diff --git a/m4/lchmod.m4 b/m4/lchmod.m4
index b9e8a97..a86a304 100644
--- a/m4/lchmod.m4
+++ b/m4/lchmod.m4
@@ -1,4 +1,4 @@
-#serial 7
+#serial 8
dnl Copyright (C) 2005-2006, 2008-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
@@ -26,6 +26,5 @@ AC_DEFUN([gl_FUNC_LCHMOD],
# Prerequisites of lib/lchmod.c.
AC_DEFUN([gl_PREREQ_LCHMOD],
[
- AC_REQUIRE([AC_C_INLINE])
:
])
diff --git a/m4/libgmp.m4 b/m4/libgmp.m4
new file mode 100644
index 0000000..b569bb7
--- a/dev/null
+++ b/m4/libgmp.m4
@@ -0,0 +1,44 @@
+# Configure the GMP library or a replacement.
+
+dnl Copyright 2020 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_LIBGMP],
+[
+ AC_ARG_WITH([libgmp],
+ [AS_HELP_STRING([--without-libgmp],
+ [do not use the GNU Multiple Precision (GMP) library;
+ this is the default on systems lacking libgmp.])])
+
+ AC_CHECK_HEADERS_ONCE([gmp.h])
+ GMP_H=gmp.h
+ LIB_GMP=
+
+ case $with_libgmp in
+ no) ;;
+ yes) GMP_H= LIB_GMP=-lgmp;;
+ *) if test "$ac_cv_header_gmp_h" = yes; then
+ gl_saved_LIBS=$LIBS
+ AC_SEARCH_LIBS([__gmpz_roinit_n], [gmp])
+ LIBS=$gl_saved_LIBS
+ case $ac_cv_search___gmpz_roinit_n in
+ 'none needed')
+ GMP_H=;;
+ -*)
+ GMP_H= LIB_GMP=$ac_cv_search___gmpz_roinit_n;;
+ esac
+ fi;;
+ esac
+
+ if test -z "$GMP_H"; then
+ AC_DEFINE([HAVE_GMP], 1,
+ [Define to 1 if you have the GMP library instead of just the
+ mini-gmp replacement.])
+ fi
+
+ AC_SUBST([LIB_GMP])
+ AC_SUBST([GMP_H])
+ AM_CONDITIONAL([GL_GENERATE_GMP_H], [test -n "$GMP_H"])
+])
diff --git a/m4/manywarnings.m4 b/m4/manywarnings.m4
index 719bafb..d18da04 100644
--- a/m4/manywarnings.m4
+++ b/m4/manywarnings.m4
@@ -1,4 +1,4 @@
-# manywarnings.m4 serial 19
+# manywarnings.m4 serial 20
dnl Copyright (C) 2008-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@ AC_DEFUN([gl_MANYWARN_COMPLEMENT],
*" $gl_warn_item "*)
;;
*)
- gl_warn_set="$gl_warn_set $gl_warn_item"
+ gl_AS_VAR_APPEND([gl_warn_set], [" $gl_warn_item"])
;;
esac
done
@@ -49,12 +49,12 @@ m4_defun([gl_MANYWARN_ALL_GCC(C)],
AC_REQUIRE([AC_PROG_CC])
if test -n "$GCC"; then
- dnl Check if -W -Werror -Wno-missing-field-initializers is supported
+ dnl Check if -Wextra -Werror -Wno-missing-field-initializers is supported
dnl with the current $CC $CFLAGS $CPPFLAGS.
AC_CACHE_CHECK([whether -Wno-missing-field-initializers is supported],
[gl_cv_cc_nomfi_supported],
[gl_save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS -W -Werror -Wno-missing-field-initializers"
+ CFLAGS="$CFLAGS -Wextra -Werror -Wno-missing-field-initializers"
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[]], [[]])],
[gl_cv_cc_nomfi_supported=yes],
@@ -68,7 +68,7 @@ m4_defun([gl_MANYWARN_ALL_GCC(C)],
AC_CACHE_CHECK([whether -Wno-missing-field-initializers is needed],
[gl_cv_cc_nomfi_needed],
[gl_save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS -W -Werror"
+ CFLAGS="$CFLAGS -Wextra -Werror"
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[int f (void)
@@ -105,153 +105,41 @@ m4_defun([gl_MANYWARN_ALL_GCC(C)],
# To compare this list to your installed GCC's, run this Bash command:
#
# comm -3 \
- # <((sed -n 's/^ *\(-[^ 0-9][^ ]*\) .*/\1/p' manywarnings.m4; \
+ # <((sed -n 's/^ *\(-[^ 0-9][^ ]*\).*/\1/p' manywarnings.m4; \
# awk '/^[^#]/ {print $1}' ../build-aux/gcc-warning.spec) | sort) \
# <(LC_ALL=C gcc --help=warnings | sed -n 's/^ \(-[^ ]*\) .*/\1/p' | sort)
- gl_manywarn_set=
- for gl_manywarn_item in -fno-common \
- -W \
- -Wabsolute-value \
- -Waddress \
- -Waddress-of-packed-member \
- -Waggressive-loop-optimizations \
+ $1=
+ for gl_manywarn_item in -fanalyzer -fno-common \
-Wall \
- -Wanalyzer-double-fclose \
- -Wanalyzer-double-free \
- -Wanalyzer-exposure-through-output-file \
- -Wanalyzer-file-leak \
- -Wanalyzer-free-of-non-heap \
- -Wanalyzer-malloc-leak \
- -Wanalyzer-null-argument \
- -Wanalyzer-null-dereference \
- -Wanalyzer-possible-null-argument \
- -Wanalyzer-possible-null-dereference \
- -Wanalyzer-stale-setjmp-buffer \
- -Wanalyzer-tainted-array-index \
- -Wanalyzer-too-complex \
- -Wanalyzer-unsafe-call-within-signal-handler \
- -Wanalyzer-use-after-free \
- -Wanalyzer-use-of-pointer-in-stale-stack-frame \
-Warith-conversion \
- -Wattribute-warning \
- -Wattributes \
-Wbad-function-cast \
- -Wbool-compare \
- -Wbool-operation \
- -Wbuiltin-declaration-mismatch \
- -Wbuiltin-macro-redefined \
- -Wcannot-profile \
- -Wcast-align \
-Wcast-align=strict \
- -Wcast-function-type \
- -Wchar-subscripts \
- -Wclobbered \
- -Wcomment \
- -Wcomments \
- -Wcoverage-mismatch \
- -Wcpp \
- -Wdangling-else \
-Wdate-time \
- -Wdeprecated \
- -Wdeprecated-declarations \
- -Wdesignated-init \
-Wdisabled-optimization \
- -Wdiscarded-array-qualifiers \
- -Wdiscarded-qualifiers \
- -Wdiv-by-zero \
-Wdouble-promotion \
-Wduplicated-branches \
-Wduplicated-cond \
- -Wduplicate-decl-specifier \
- -Wempty-body \
- -Wendif-labels \
- -Wenum-compare \
- -Wenum-conversion \
- -Wexpansion-to-defined \
-Wextra \
- -Wformat-contains-nul \
- -Wformat-diag \
- -Wformat-extra-args \
- -Wformat-nonliteral \
- -Wformat-security \
-Wformat-signedness \
- -Wformat-y2k \
- -Wformat-zero-length \
- -Wframe-address \
- -Wfree-nonheap-object \
- -Whsa \
- -Wif-not-aligned \
- -Wignored-attributes \
- -Wignored-qualifiers \
- -Wimplicit \
- -Wimplicit-function-declaration \
- -Wimplicit-int \
- -Wincompatible-pointer-types \
-Winit-self \
-Winline \
- -Wint-conversion \
- -Wint-in-bool-context \
- -Wint-to-pointer-cast \
- -Winvalid-memory-model \
-Winvalid-pch \
- -Wlogical-not-parentheses \
-Wlogical-op \
- -Wmain \
- -Wmaybe-uninitialized \
- -Wmemset-elt-size \
- -Wmemset-transposed-args \
- -Wmisleading-indentation \
- -Wmissing-attributes \
- -Wmissing-braces \
-Wmissing-declarations \
- -Wmissing-field-initializers \
-Wmissing-include-dirs \
- -Wmissing-parameter-type \
- -Wmissing-profile \
-Wmissing-prototypes \
- -Wmultichar \
- -Wmultistatement-macros \
- -Wnarrowing \
-Wnested-externs \
- -Wnonnull \
- -Wnonnull-compare \
-Wnull-dereference \
- -Wodr \
- -Wold-style-declaration \
-Wold-style-definition \
-Wopenmp-simd \
- -Woverflow \
-Woverlength-strings \
- -Woverride-init \
-Wpacked \
- -Wpacked-bitfield-compat \
- -Wpacked-not-aligned \
- -Wparentheses \
-Wpointer-arith \
- -Wpointer-compare \
- -Wpointer-sign \
- -Wpointer-to-int-cast \
- -Wpragmas \
- -Wpsabi \
- -Wrestrict \
- -Wreturn-local-addr \
- -Wreturn-type \
- -Wscalar-storage-order \
- -Wsequence-point \
-Wshadow \
- -Wshift-count-negative \
- -Wshift-count-overflow \
- -Wshift-negative-value \
- -Wsizeof-array-argument \
- -Wsizeof-pointer-div \
- -Wsizeof-pointer-memaccess \
-Wstack-protector \
- -Wstrict-aliasing \
-Wstrict-overflow \
-Wstrict-prototypes \
- -Wstring-compare \
- -Wstringop-truncation \
-Wsuggest-attribute=cold \
-Wsuggest-attribute=const \
-Wsuggest-attribute=format \
@@ -260,95 +148,63 @@ m4_defun([gl_MANYWARN_ALL_GCC(C)],
-Wsuggest-attribute=pure \
-Wsuggest-final-methods \
-Wsuggest-final-types \
- -Wswitch \
- -Wswitch-bool \
- -Wswitch-outside-range \
- -Wswitch-unreachable \
-Wsync-nand \
-Wsystem-headers \
- -Wtautological-compare \
-Wtrampolines \
- -Wtrigraphs \
- -Wtype-limits \
-Wuninitialized \
-Wunknown-pragmas \
-Wunsafe-loop-optimizations \
- -Wunused \
- -Wunused-but-set-parameter \
- -Wunused-but-set-variable \
- -Wunused-function \
- -Wunused-label \
- -Wunused-local-typedefs \
-Wunused-macros \
- -Wunused-parameter \
- -Wunused-result \
- -Wunused-value \
- -Wunused-variable \
- -Wvarargs \
-Wvariadic-macros \
-Wvector-operation-performance \
-Wvla \
- -Wvolatile-register-var \
-Wwrite-strings \
- -Wzero-length-bounds \
\
; do
- gl_manywarn_set="$gl_manywarn_set $gl_manywarn_item"
+ gl_AS_VAR_APPEND([$1], [" $gl_manywarn_item"])
done
# gcc --help=warnings outputs an unusual form for these options; list
# them here so that the above 'comm' command doesn't report a false match.
- # Would prefer "min (PTRDIFF_MAX, SIZE_MAX)", but it must be a literal.
- # Also, AC_COMPUTE_INT requires it to fit in a long; it is 2**63 on
- # the only platforms where it does not fit in a long, so make that
- # a special case.
- AC_MSG_CHECKING([max safe object size])
- AC_COMPUTE_INT([gl_alloc_max],
- [LONG_MAX < (PTRDIFF_MAX < (size_t) -1 ? PTRDIFF_MAX : (size_t) -1)
- ? -1
- : PTRDIFF_MAX < (size_t) -1 ? (long) PTRDIFF_MAX : (long) (size_t) -1],
- [[#include <limits.h>
- #include <stddef.h>
- #include <stdint.h>
- ]],
- [gl_alloc_max=2147483647])
- case $gl_alloc_max in
- -1) gl_alloc_max=9223372036854775807;;
- esac
- AC_MSG_RESULT([$gl_alloc_max])
- gl_manywarn_set="$gl_manywarn_set -Walloc-size-larger-than=$gl_alloc_max"
- gl_manywarn_set="$gl_manywarn_set -Warray-bounds=2"
- gl_manywarn_set="$gl_manywarn_set -Wattribute-alias=2"
- gl_manywarn_set="$gl_manywarn_set -Wformat-overflow=2"
- gl_manywarn_set="$gl_manywarn_set -Wformat-truncation=2"
- gl_manywarn_set="$gl_manywarn_set -Wimplicit-fallthrough=5"
- gl_manywarn_set="$gl_manywarn_set -Wnormalized=nfc"
- gl_manywarn_set="$gl_manywarn_set -Wshift-overflow=2"
- gl_manywarn_set="$gl_manywarn_set -Wstringop-overflow=2"
- gl_manywarn_set="$gl_manywarn_set -Wunused-const-variable=2"
- gl_manywarn_set="$gl_manywarn_set -Wvla-larger-than=4031"
+ gl_AS_VAR_APPEND([$1], [' -Warray-bounds=2'])
+ gl_AS_VAR_APPEND([$1], [' -Wattribute-alias=2'])
+ gl_AS_VAR_APPEND([$1], [' -Wformat-overflow=2'])
+ gl_AS_VAR_APPEND([$1], [' -Wformat=2'])
+ gl_AS_VAR_APPEND([$1], [' -Wformat-truncation=2'])
+ gl_AS_VAR_APPEND([$1], [' -Wimplicit-fallthrough=5'])
+ gl_AS_VAR_APPEND([$1], [' -Wshift-overflow=2'])
+ gl_AS_VAR_APPEND([$1], [' -Wunused-const-variable=2'])
+ gl_AS_VAR_APPEND([$1], [' -Wvla-larger-than=4031'])
# These are needed for older GCC versions.
if test -n "$GCC"; then
case `($CC --version) 2>/dev/null` in
'gcc (GCC) '[[0-3]].* | \
'gcc (GCC) '4.[[0-7]].*)
- gl_manywarn_set="$gl_manywarn_set -fdiagnostics-show-option"
- gl_manywarn_set="$gl_manywarn_set -funit-at-a-time"
+ gl_AS_VAR_APPEND([$1], [' -fdiagnostics-show-option'])
+ gl_AS_VAR_APPEND([$1], [' -funit-at-a-time'])
;;
esac
fi
# Disable specific options as needed.
if test "$gl_cv_cc_nomfi_needed" = yes; then
- gl_manywarn_set="$gl_manywarn_set -Wno-missing-field-initializers"
+ gl_AS_VAR_APPEND([$1], [' -Wno-missing-field-initializers'])
fi
if test "$gl_cv_cc_uninitialized_supported" = no; then
- gl_manywarn_set="$gl_manywarn_set -Wno-uninitialized"
+ gl_AS_VAR_APPEND([$1], [' -Wno-uninitialized'])
fi
- $1=$gl_manywarn_set
+ # Some warnings have too many false alarms in GCC 10.1.
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93695
+ gl_AS_VAR_APPEND([$1], [' -Wno-analyzer-double-free'])
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94458
+ gl_AS_VAR_APPEND([$1], [' -Wno-analyzer-malloc-leak'])
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94851
+ gl_AS_VAR_APPEND([$1], [' -Wno-analyzer-null-dereference'])
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95758
+ gl_AS_VAR_APPEND([$1], [' -Wno-analyzer-use-after-free'])
AC_LANG_POP([C])
])
diff --git a/m4/memchr-obsolete.m4 b/m4/memchr-obsolete.m4
deleted file mode 100644
index 7305a77..0000000
--- a/m4/memchr-obsolete.m4
+++ b/dev/null
@@ -1,11 +0,0 @@
-# memchr-obsolete.m4 serial 1
-dnl Copyright (C) 2011-2020 Free Software Foundation, Inc.
-dnl This file is free software; the Free Software Foundation
-dnl gives unlimited permission to copy and/or distribute it,
-dnl with or without modifications, as long as this notice is preserved.
-
-AC_DEFUN([gl_FUNC_MEMCHR_OBSOLETE],
-[
- dnl The real code is in memchr.m4.
- :
-])
diff --git a/m4/memchr.m4 b/m4/memchr.m4
index 91eec88..81e8f98 100644
--- a/m4/memchr.m4
+++ b/m4/memchr.m4
@@ -1,4 +1,4 @@
-# memchr.m4 serial 15
+# memchr.m4 serial 16
dnl Copyright (C) 2002-2004, 2009-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -14,26 +14,17 @@ AC_DEFUN_ONCE([gl_FUNC_MEMCHR],
AC_CHECK_FUNCS_ONCE([mprotect])
AC_REQUIRE([gl_HEADER_STRING_H_DEFAULTS])
- m4_ifdef([gl_FUNC_MEMCHR_OBSOLETE], [
- dnl These days, we assume memchr is present. But if support for old
- dnl platforms is desired:
- AC_CHECK_FUNCS_ONCE([memchr])
- if test $ac_cv_func_memchr = no; then
- HAVE_MEMCHR=0
- fi
- ])
- if test $HAVE_MEMCHR = 1; then
- # Detect platform-specific bugs in some versions of glibc:
- # memchr should not dereference anything with length 0
- # https://bugzilla.redhat.com/show_bug.cgi?id=499689
- # memchr should not dereference overestimated length after a match
- # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=521737
- # https://sourceware.org/bugzilla/show_bug.cgi?id=10162
- # memchr should cast the second argument to 'unsigned char'.
- # This bug exists in Android 4.3.
- # Assume that memchr works on platforms that lack mprotect.
- AC_CACHE_CHECK([whether memchr works], [gl_cv_func_memchr_works],
- [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+ # Detect platform-specific bugs in some versions of glibc:
+ # memchr should not dereference anything with length 0
+ # https://bugzilla.redhat.com/show_bug.cgi?id=499689
+ # memchr should not dereference overestimated length after a match
+ # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=521737
+ # https://sourceware.org/bugzilla/show_bug.cgi?id=10162
+ # memchr should cast the second argument to 'unsigned char'.
+ # This bug exists in Android 4.3.
+ # Assume that memchr works on platforms that lack mprotect.
+ AC_CACHE_CHECK([whether memchr works], [gl_cv_func_memchr_works],
+ [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
#include <string.h>
#if HAVE_SYS_MMAN_H
# include <fcntl.h>
@@ -87,23 +78,22 @@ AC_DEFUN_ONCE([gl_FUNC_MEMCHR],
}
return result;
]])],
- [gl_cv_func_memchr_works=yes],
- [gl_cv_func_memchr_works=no],
- [case "$host_os" in
- # Guess no on Android.
- linux*-android*) gl_cv_func_memchr_works="guessing no" ;;
- # Guess yes on native Windows.
- mingw*) gl_cv_func_memchr_works="guessing yes" ;;
- # If we don't know, obey --enable-cross-guesses.
- *) gl_cv_func_memchr_works="$gl_cross_guess_normal" ;;
- esac
- ])
- ])
- case "$gl_cv_func_memchr_works" in
- *yes) ;;
- *) REPLACE_MEMCHR=1 ;;
- esac
- fi
+ [gl_cv_func_memchr_works=yes],
+ [gl_cv_func_memchr_works=no],
+ [case "$host_os" in
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_memchr_works="guessing no" ;;
+ # Guess yes on native Windows.
+ mingw*) gl_cv_func_memchr_works="guessing yes" ;;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_memchr_works="$gl_cross_guess_normal" ;;
+ esac
+ ])
+ ])
+ case "$gl_cv_func_memchr_works" in
+ *yes) ;;
+ *) REPLACE_MEMCHR=1 ;;
+ esac
])
# Prerequisites of lib/memchr.c.
diff --git a/m4/strcasestr.m4 b/m4/strcasestr.m4
index 4c2275f..ee22873 100644
--- a/m4/strcasestr.m4
+++ b/m4/strcasestr.m4
@@ -1,4 +1,4 @@
-# strcasestr.m4 serial 25
+# strcasestr.m4 serial 26
dnl Copyright (C) 2005, 2007-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -17,7 +17,7 @@ AC_DEFUN([gl_FUNC_STRCASESTR_SIMPLE],
if test $ac_cv_func_strcasestr = no; then
HAVE_STRCASESTR=0
else
- if test $HAVE_MEMCHR = 0 || test $REPLACE_MEMCHR = 1; then
+ if test $REPLACE_MEMCHR = 1; then
REPLACE_STRCASESTR=1
else
dnl Detect https://sourceware.org/bugzilla/show_bug.cgi?id=12092
diff --git a/m4/string_h.m4 b/m4/string_h.m4
index cc51337..516b346 100644
--- a/m4/string_h.m4
+++ b/m4/string_h.m4
@@ -5,7 +5,7 @@
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 23
+# serial 24
# Written by Paul Eggert.
@@ -87,7 +87,6 @@ AC_DEFUN([gl_HEADER_STRING_H_DEFAULTS],
HAVE_EXPLICIT_BZERO=1; AC_SUBST([HAVE_EXPLICIT_BZERO])
HAVE_FFSL=1; AC_SUBST([HAVE_FFSL])
HAVE_FFSLL=1; AC_SUBST([HAVE_FFSLL])
- HAVE_MEMCHR=1; AC_SUBST([HAVE_MEMCHR])
HAVE_DECL_MEMMEM=1; AC_SUBST([HAVE_DECL_MEMMEM])
HAVE_MEMPCPY=1; AC_SUBST([HAVE_MEMPCPY])
HAVE_DECL_MEMRCHR=1; AC_SUBST([HAVE_DECL_MEMRCHR])
diff --git a/m4/strstr.m4 b/m4/strstr.m4
index a2ec337..5291107 100644
--- a/m4/strstr.m4
+++ b/m4/strstr.m4
@@ -1,4 +1,4 @@
-# strstr.m4 serial 21
+# strstr.m4 serial 22
dnl Copyright (C) 2008-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -9,7 +9,7 @@ AC_DEFUN([gl_FUNC_STRSTR_SIMPLE],
[
AC_REQUIRE([gl_HEADER_STRING_H_DEFAULTS])
AC_REQUIRE([gl_FUNC_MEMCHR])
- if test $HAVE_MEMCHR = 0 || test $REPLACE_MEMCHR = 1; then
+ if test $REPLACE_MEMCHR = 1; then
REPLACE_STRSTR=1
else
dnl Detect https://sourceware.org/bugzilla/show_bug.cgi?id=12092
diff --git a/m4/dup2-obsolete.m4 b/m4/supersede.m4
index 5e72403..2d445cf 100644
--- a/m4/dup2-obsolete.m4
+++ b/m4/supersede.m4
@@ -1,11 +1,10 @@
-# dup2-obsolete.m4 serial 1
-dnl Copyright (C) 2011-2020 Free Software Foundation, Inc.
+# supersede.m4 serial 1
+dnl Copyright (C) 2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
-AC_DEFUN([gl_FUNC_DUP2_OBSOLETE],
+AC_DEFUN([gl_SUPERSEDE],
[
- dnl The real code is in dup2.m4.
- :
+ AC_CHECK_FUNCS([chown])
])
diff --git a/m4/sys_stat_h.m4 b/m4/sys_stat_h.m4
index 3efba5a..929144d 100644
--- a/m4/sys_stat_h.m4
+++ b/m4/sys_stat_h.m4
@@ -1,4 +1,4 @@
-# sys_stat_h.m4 serial 33 -*- Autoconf -*-
+# sys_stat_h.m4 serial 34 -*- Autoconf -*-
dnl Copyright (C) 2006-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -46,8 +46,8 @@ AC_DEFUN([gl_HEADER_SYS_STAT_H],
dnl Check for declarations of anything we want to poison if the
dnl corresponding gnulib module is not in use.
gl_WARN_ON_USE_PREPARE([[#include <sys/stat.h>
- ]], [fchmodat fstat fstatat futimens lchmod lstat mkdirat mkfifo mkfifoat
- mknod mknodat stat utimensat])
+ ]], [fchmodat fstat fstatat futimens getumask lchmod lstat
+ mkdirat mkfifo mkfifoat mknod mknodat stat utimensat])
AC_REQUIRE([AC_C_RESTRICT])
])
@@ -68,6 +68,7 @@ AC_DEFUN([gl_SYS_STAT_H_DEFAULTS],
GNULIB_FSTAT=0; AC_SUBST([GNULIB_FSTAT])
GNULIB_FSTATAT=0; AC_SUBST([GNULIB_FSTATAT])
GNULIB_FUTIMENS=0; AC_SUBST([GNULIB_FUTIMENS])
+ GNULIB_GETUMASK=0; AC_SUBST([GNULIB_GETUMASK])
GNULIB_LCHMOD=0; AC_SUBST([GNULIB_LCHMOD])
GNULIB_LSTAT=0; AC_SUBST([GNULIB_LSTAT])
GNULIB_MKDIRAT=0; AC_SUBST([GNULIB_MKDIRAT])
@@ -82,6 +83,7 @@ AC_DEFUN([gl_SYS_STAT_H_DEFAULTS],
HAVE_FCHMODAT=1; AC_SUBST([HAVE_FCHMODAT])
HAVE_FSTATAT=1; AC_SUBST([HAVE_FSTATAT])
HAVE_FUTIMENS=1; AC_SUBST([HAVE_FUTIMENS])
+ HAVE_GETUMASK=1; AC_SUBST([HAVE_GETUMASK])
HAVE_LCHMOD=1; AC_SUBST([HAVE_LCHMOD])
HAVE_LSTAT=1; AC_SUBST([HAVE_LSTAT])
HAVE_MKDIRAT=1; AC_SUBST([HAVE_MKDIRAT])
diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4
index dfa38f8..b4734da 100644
--- a/m4/unistd_h.m4
+++ b/m4/unistd_h.m4
@@ -1,4 +1,4 @@
-# unistd_h.m4 serial 80
+# unistd_h.m4 serial 81
dnl Copyright (C) 2006-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -120,7 +120,6 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS],
dnl Assume proper GNU behavior unless another module says otherwise.
HAVE_CHOWN=1; AC_SUBST([HAVE_CHOWN])
HAVE_COPY_FILE_RANGE=1; AC_SUBST([HAVE_COPY_FILE_RANGE])
- HAVE_DUP2=1; AC_SUBST([HAVE_DUP2])
HAVE_DUP3=1; AC_SUBST([HAVE_DUP3])
HAVE_EUIDACCESS=1; AC_SUBST([HAVE_EUIDACCESS])
HAVE_FACCESSAT=1; AC_SUBST([HAVE_FACCESSAT])
diff --git a/modules/argp b/modules/argp
index 125046a..50fde11 100644
--- a/modules/argp
+++ b/modules/argp
@@ -24,7 +24,7 @@ m4/argp.m4
Depends-on:
alloca
-dirname-lgpl
+basename-lgpl
extern-inline
getopt-gnu
strchrnul
diff --git a/modules/argv-iter-tests b/modules/argv-iter-tests
index f32587e..97cbe50 100644
--- a/modules/argv-iter-tests
+++ b/modules/argv-iter-tests
@@ -10,3 +10,4 @@ configure.ac:
Makefile.am:
TESTS += test-argv-iter
check_PROGRAMS += test-argv-iter
+test_argv_iter_LDADD = $(LDADD) $(LIB_GETRANDOM)
diff --git a/modules/asyncsafe-spin b/modules/asyncsafe-spin
new file mode 100644
index 0000000..fa27b08
--- a/dev/null
+++ b/modules/asyncsafe-spin
@@ -0,0 +1,28 @@
+Description:
+Spin locks for communication between threads and signal handlers.
+
+Files:
+lib/asyncsafe-spin.h
+lib/asyncsafe-spin.c
+
+Depends-on:
+signal-h
+stdbool
+sigprocmask
+windows-spin
+
+configure.ac:
+AC_REQUIRE([AC_C_INLINE])
+AC_CHECK_HEADERS_ONCE([pthread.h])
+
+Makefile.am:
+lib_SOURCES += asyncsafe-spin.c
+
+Include:
+"asyncsafe-spin.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
diff --git a/modules/asyncsafe-spin-tests b/modules/asyncsafe-spin-tests
new file mode 100644
index 0000000..301c2af
--- a/dev/null
+++ b/modules/asyncsafe-spin-tests
@@ -0,0 +1,20 @@
+Files:
+tests/test-asyncsafe-spin1.c
+tests/test-asyncsafe-spin2.c
+tests/atomic-int-gnulib.h
+m4/semaphore.m4
+
+Depends-on:
+thread
+lock
+yield
+
+configure.ac:
+AC_CHECK_HEADERS_ONCE([semaphore.h])
+AC_CHECK_DECLS_ONCE([alarm])
+AC_REQUIRE([gl_SEMAPHORE])
+
+Makefile.am:
+TESTS += test-asyncsafe-spin1 test-asyncsafe-spin2
+check_PROGRAMS += test-asyncsafe-spin1 test-asyncsafe-spin2
+test_asyncsafe_spin2_LDADD = $(LDADD) @LIBMULTITHREAD@ @YIELD_LIB@ @LIB_SEMAPHORE@
diff --git a/modules/at-internal b/modules/at-internal
index d58c9b0..2a37779 100644
--- a/modules/at-internal
+++ b/modules/at-internal
@@ -9,11 +9,12 @@ Depends-on:
errno
fcntl-h
intprops
-open
+open [case $host_os in mingw*) false;; *) :;; esac]
sys_stat
unistd
configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
Makefile.am:
lib_SOURCES += openat-priv.h openat-proc.c
diff --git a/modules/backup-rename b/modules/backup-rename
index 1e29b73..be12c90 100644
--- a/modules/backup-rename
+++ b/modules/backup-rename
@@ -11,10 +11,10 @@ m4/backupfile.m4
Depends-on:
argmatch
attribute
+basename-lgpl
closedir
d-ino
dirent-safer
-dirname-lgpl
fcntl
memcmp
opendirat
diff --git a/modules/backupfile b/modules/backupfile
index dc46d76..76f223a 100644
--- a/modules/backupfile
+++ b/modules/backupfile
@@ -11,10 +11,10 @@ m4/backupfile.m4
Depends-on:
argmatch
attribute
+basename-lgpl
closedir
d-ino
dirent-safer
-dirname-lgpl
fcntl
memcmp
opendirat
diff --git a/modules/basename-lgpl b/modules/basename-lgpl
new file mode 100644
index 0000000..f81898d
--- a/dev/null
+++ b/modules/basename-lgpl
@@ -0,0 +1,25 @@
+Description:
+Extract the last component (base name) of a file name.
+
+Files:
+lib/basename-lgpl.h
+lib/basename-lgpl.c
+
+Depends-on:
+double-slash-root
+filename
+stdbool
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += basename-lgpl.c
+
+Include:
+"basename-lgpl.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
diff --git a/modules/bitset b/modules/bitset
index 20c6806..21cde24 100644
--- a/modules/bitset
+++ b/modules/bitset
@@ -24,6 +24,9 @@ gettext-h
obstack
xalloc
+configure.ac:
+AC_REQUIRE([AC_C_INLINE])
+
Makefile.am:
lib_SOURCES += bitset.c bitset/array.c bitset/stats.c \
bitset/table.c bitset/list.c bitset/vector.c
diff --git a/modules/c-dtoastr b/modules/c-dtoastr
new file mode 100644
index 0000000..182cc7a
--- a/dev/null
+++ b/modules/c-dtoastr
@@ -0,0 +1,27 @@
+Description:
+Convert double to accurate string in C locale.
+
+Files:
+lib/ftoastr.h
+lib/ftoastr.c
+lib/c-dtoastr.c
+
+Depends-on:
+extensions
+intprops
+c-snprintf
+c-strtod
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += c-dtoastr.c
+
+Include:
+"ftoastr.h"
+
+License:
+GPL
+
+Maintainer:
+Marc Nieper-Wisskirchen
diff --git a/modules/c-dtoastr-tests b/modules/c-dtoastr-tests
new file mode 100644
index 0000000..687d766
--- a/dev/null
+++ b/modules/c-dtoastr-tests
@@ -0,0 +1,18 @@
+Files:
+tests/test-c-dtoastr.c
+tests/test-c-dtoastr.sh
+m4/locale-fr.m4
+tests/macros.h
+
+Depends-on:
+setlocale
+snprintf
+
+configure.ac:
+gt_LOCALE_FR
+
+Makefile.am:
+TESTS += test-c-dtoastr.sh
+TESTS_ENVIRONMENT += LOCALE_FR='@LOCALE_FR@'
+check_PROGRAMS += test-c-dtoastr
+test_c_dtoastr_LDADD = $(LDADD) $(LIB_SETLOCALE)
diff --git a/modules/c-ldtoastr b/modules/c-ldtoastr
new file mode 100644
index 0000000..e5b6fdf
--- a/dev/null
+++ b/modules/c-ldtoastr
@@ -0,0 +1,27 @@
+Description:
+Convert long double to accurate string in C locale.
+
+Files:
+lib/ftoastr.h
+lib/ftoastr.c
+lib/c-ldtoastr.c
+
+Depends-on:
+extensions
+intprops
+c-snprintf
+c-strtold
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += c-ldtoastr.c
+
+Include:
+"ftoastr.h"
+
+License:
+GPL
+
+Maintainer:
+Marc Nieper-Wisskirchen
diff --git a/modules/c-ldtoastr-tests b/modules/c-ldtoastr-tests
new file mode 100644
index 0000000..2d4c7f8
--- a/dev/null
+++ b/modules/c-ldtoastr-tests
@@ -0,0 +1,18 @@
+Files:
+tests/test-c-ldtoastr.c
+tests/test-c-ldtoastr.sh
+m4/locale-fr.m4
+tests/macros.h
+
+Depends-on:
+setlocale
+snprintf
+
+configure.ac:
+gt_LOCALE_FR
+
+Makefile.am:
+TESTS += test-c-ldtoastr.sh
+TESTS_ENVIRONMENT += LOCALE_FR='@LOCALE_FR@'
+check_PROGRAMS += test-c-ldtoastr
+test_c_ldtoastr_LDADD = $(LDADD) $(LIB_SETLOCALE)
diff --git a/modules/canonicalize b/modules/canonicalize
index 233fba9..16dfb69 100644
--- a/modules/canonicalize
+++ b/modules/canonicalize
@@ -5,15 +5,15 @@ Files:
lib/canonicalize.h
lib/canonicalize.c
m4/canonicalize.m4
-m4/double-slash-root.m4
Depends-on:
areadlink-with-size
+double-slash-root
errno
extensions
file-set
filename
-hash-triple
+hash-triple-simple
lstat
memmove
nocrash
diff --git a/modules/canonicalize-lgpl b/modules/canonicalize-lgpl
index 3526422..20ee790 100644
--- a/modules/canonicalize-lgpl
+++ b/modules/canonicalize-lgpl
@@ -10,15 +10,16 @@ Depends-on:
extensions
stdlib
nocrash
-alloca-opt [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
-errno [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
-filename [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
-lstat [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
-malloca [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
-memmove [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
-pathmax [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
-readlink [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
-sys_stat [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
+alloca-opt [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
+double-slash-root [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
+errno [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
+filename [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
+lstat [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
+malloca [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
+memmove [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
+pathmax [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
+readlink [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
+sys_stat [test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1]
configure.ac:
gl_CANONICALIZE_LGPL
diff --git a/modules/clean-temp b/modules/clean-temp
index d5c44ee..940869d 100644
--- a/modules/clean-temp
+++ b/modules/clean-temp
@@ -9,8 +9,10 @@ Depends-on:
stdbool
stdint
unistd
+lock
error
fatal-signal
+asyncsafe-spin
open
pathmax
tmpdir
@@ -19,6 +21,7 @@ rmdir
xalloc
xmalloca
linkedhash-list
+linked-list
xlist
gettext-h
@@ -31,6 +34,9 @@ lib_SOURCES += clean-temp.h clean-temp.c
Include:
"clean-temp.h"
+Link:
+$(LIB_GETRANDOM)
+
License:
GPL
diff --git a/modules/copy-file b/modules/copy-file
index cc4a3c7..293ad6a 100644
--- a/modules/copy-file
+++ b/modules/copy-file
@@ -7,7 +7,7 @@ lib/copy-file.c
m4/copy-file.m4
Depends-on:
-acl
+acl-permissions
binary-io
close
copy-file-range
@@ -17,6 +17,7 @@ full-write
gettext-h
ignore-value
open
+qcopy-acl
quote
safe-read
stat-time
diff --git a/modules/dfa b/modules/dfa
index ade4b5a..303957f 100644
--- a/modules/dfa
+++ b/modules/dfa
@@ -28,6 +28,7 @@ xalloc
xalloc-die
configure.ac:
+AC_REQUIRE([AC_C_INLINE])
Makefile.am:
lib_SOURCES += dfa.c localeinfo.c
diff --git a/modules/dfa-tests b/modules/dfa-tests
index 5855652..4c447c2 100644
--- a/modules/dfa-tests
+++ b/modules/dfa-tests
@@ -1,7 +1,7 @@
Files:
-tests/dfa-match.sh
-tests/dfa-match-aux.c
-tests/dfa-invalid-char-class.sh
+tests/test-dfa-match.sh
+tests/test-dfa-match-aux.c
+tests/test-dfa-invalid-char-class.sh
Depends-on:
getprogname
@@ -13,9 +13,9 @@ regex
configure.ac:
Makefile.am:
-TESTS += \
- dfa-invalid-char-class.sh \
- dfa-match.sh
+TESTS += \
+ test-dfa-invalid-char-class.sh \
+ test-dfa-match.sh
-check_PROGRAMS += dfa-match-aux
-dfa_match_aux_LDADD = $(LDADD) $(LIB_SETLOCALE) @LIBINTL@ $(LIB_MBRTOWC)
+check_PROGRAMS += test-dfa-match-aux
+test_dfa_match_aux_LDADD = $(LDADD) $(LIB_SETLOCALE) @LIBINTL@ $(LIB_MBRTOWC)
diff --git a/modules/dirname b/modules/dirname
index dce323a..e3ffbe0 100644
--- a/modules/dirname
+++ b/modules/dirname
@@ -12,7 +12,6 @@ xalloc
xstrndup
configure.ac:
-gl_DIRNAME
gl_MODULE_INDICATOR([dirname])
Makefile.am:
diff --git a/modules/dirname-lgpl b/modules/dirname-lgpl
index d6c3efd..8eb1dd7 100644
--- a/modules/dirname-lgpl
+++ b/modules/dirname-lgpl
@@ -4,21 +4,19 @@ Extract specific portions of filenames.
Files:
lib/dirname.h
lib/dirname-lgpl.c
-lib/basename-lgpl.c
lib/stripslash.c
-m4/dirname.m4
Depends-on:
+basename-lgpl
double-slash-root
filename
malloc-posix
stdbool
configure.ac:
-gl_DIRNAME_LGPL
Makefile.am:
-lib_SOURCES += dirname-lgpl.c basename-lgpl.c stripslash.c
+lib_SOURCES += dirname-lgpl.c stripslash.c
Include:
"dirname.h"
diff --git a/modules/dup2 b/modules/dup2
index 2ad65d1..70a28de 100644
--- a/modules/dup2
+++ b/modules/dup2
@@ -7,13 +7,12 @@ m4/dup2.m4
Depends-on:
unistd
-dup2-obsolete
-msvc-inval [test $HAVE_DUP2 = 0 || test $REPLACE_DUP2 = 1]
-msvc-nothrow [test $HAVE_DUP2 = 0 || test $REPLACE_DUP2 = 1]
+msvc-inval [test $REPLACE_DUP2 = 1]
+msvc-nothrow [test $REPLACE_DUP2 = 1]
configure.ac:
gl_FUNC_DUP2
-if test $HAVE_DUP2 = 0 || test $REPLACE_DUP2 = 1; then
+if test $REPLACE_DUP2 = 1; then
AC_LIBOBJ([dup2])
gl_PREREQ_DUP2
fi
diff --git a/modules/dup2-obsolete b/modules/dup2-obsolete
deleted file mode 100644
index 243bed1..0000000
--- a/modules/dup2-obsolete
+++ b/dev/null
@@ -1,28 +0,0 @@
-Description:
-dup2() function for old platforms.
-
-Status:
-obsolete
-
-Notice:
-This module is obsolete.
-
-Files:
-m4/dup2-obsolete.m4
-
-Depends-on:
-dup2
-
-configure.ac:
-gl_FUNC_DUP2_OBSOLETE
-
-Makefile.am:
-
-Include:
-<unistd.h>
-
-License:
-LGPLv2+
-
-Maintainer:
-all
diff --git a/modules/fatal-signal b/modules/fatal-signal
index 6683c79..de53de7 100644
--- a/modules/fatal-signal
+++ b/modules/fatal-signal
@@ -12,6 +12,7 @@ xalloc
stdbool
unistd
sigaction
+lock
sigprocmask
raise
diff --git a/modules/file-set b/modules/file-set
index 7895cda..532828c 100644
--- a/modules/file-set
+++ b/modules/file-set
@@ -7,7 +7,7 @@ lib/file-set.h
Depends-on:
hash
-hash-triple
+hash-triple-simple
stdbool
xalloc
xalloc-die
diff --git a/modules/filenamecat-lgpl b/modules/filenamecat-lgpl
index 09965a1..ffb94ab 100644
--- a/modules/filenamecat-lgpl
+++ b/modules/filenamecat-lgpl
@@ -8,7 +8,9 @@ m4/filenamecat.m4
Depends-on:
c99
-dirname-lgpl
+basename-lgpl
+filename
+malloc-posix
configure.ac:
gl_FILE_NAME_CONCAT_LGPL
diff --git a/modules/fopen b/modules/fopen
index 1bea865..425cb61 100644
--- a/modules/fopen
+++ b/modules/fopen
@@ -8,6 +8,7 @@ m4/fopen.m4
Depends-on:
stdio
largefile
+stdbool [test $REPLACE_FOPEN = 1]
unistd [test $REPLACE_FOPEN = 1]
fstat [test $REPLACE_FOPEN = 1]
diff --git a/modules/gen-uni-tables b/modules/gen-uni-tables
index e9655e0..b384471 100644
--- a/modules/gen-uni-tables
+++ b/modules/gen-uni-tables
@@ -13,6 +13,7 @@ strdup
strstr-simple
configure.ac:
+AC_REQUIRE([AC_C_INLINE])
Makefile.am:
diff --git a/modules/getloadavg b/modules/getloadavg
index a5a3c4e..21955b7 100644
--- a/modules/getloadavg
+++ b/modules/getloadavg
@@ -7,13 +7,13 @@ m4/getloadavg.m4
Depends-on:
extensions
-fopen-gnu
-intprops
-open
-stdbool
-stdlib
+intprops [test $HAVE_GETLOADAVG = 0]
+open [case $host_os in mingw*) false;; *) test $HAVE_GETLOADAVG = 0;; esac]
+stdbool [test $HAVE_GETLOADAVG = 0]
+stdlib [test $HAVE_GETLOADAVG = 0]
configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
gl_GETLOADAVG
if test $HAVE_GETLOADAVG = 0; then
AC_LIBOBJ([getloadavg])
diff --git a/modules/getprogname b/modules/getprogname
index e8038d8..7cbad31 100644
--- a/modules/getprogname
+++ b/modules/getprogname
@@ -7,7 +7,7 @@ lib/getprogname.c
m4/getprogname.m4
Depends-on:
-dirname-lgpl
+basename-lgpl
extensions
open
diff --git a/modules/getrandom b/modules/getrandom
index 76437eb..8add391 100644
--- a/modules/getrandom
+++ b/modules/getrandom
@@ -9,9 +9,10 @@ Depends-on:
sys_random
fcntl-h [test $HAVE_GETRANDOM = 0 || test $REPLACE_GETRANDOM = 1]
minmax [test $HAVE_GETRANDOM = 0 || test $REPLACE_GETRANDOM = 1]
-open [test $HAVE_GETRANDOM = 0 || test $REPLACE_GETRANDOM = 1]
+open [case $host_os in mingw*) false;; *) test $HAVE_GETRANDOM = 0 || test $REPLACE_GETRANDOM = 1;; esac]
configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
gl_FUNC_GETRANDOM
if test $HAVE_GETRANDOM = 0 || test $REPLACE_GETRANDOM = 1; then
AC_LIBOBJ([getrandom])
@@ -27,7 +28,7 @@ Link:
$(LIB_GETRANDOM)
License:
-LGPL
+LGPLv2+
Maintainer:
all
diff --git a/modules/getumask b/modules/getumask
new file mode 100644
index 0000000..9f19a0e
--- a/dev/null
+++ b/modules/getumask
@@ -0,0 +1,32 @@
+Description:
+getumask() function: retrieve the umask of the process (multithread-safe)
+
+Files:
+lib/getumask.c
+m4/getumask.m4
+
+Depends-on:
+sys_stat
+extensions
+unistd [test $HAVE_GETUMASK = 0]
+clean-temp [test $HAVE_GETUMASK = 0]
+tempname [test $HAVE_GETUMASK = 0]
+
+configure.ac:
+gl_FUNC_GETUMASK
+if test $HAVE_GETUMASK = 0; then
+ AC_LIBOBJ([getumask])
+ gl_PREREQ_GETUMASK
+fi
+gl_SYS_STAT_MODULE_INDICATOR([getumask])
+
+Makefile.am:
+
+Include:
+<sys/stat.h>
+
+License:
+GPL
+
+Maintainer:
+all
diff --git a/modules/getumask-tests b/modules/getumask-tests
new file mode 100644
index 0000000..2968f37
--- a/dev/null
+++ b/modules/getumask-tests
@@ -0,0 +1,13 @@
+Files:
+tests/test-getumask.c
+tests/signature.h
+tests/macros.h
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-getumask
+check_PROGRAMS += test-getumask
+test_getumask_LDADD = $(LDADD) $(LIB_GETRANDOM)
diff --git a/modules/hash-triple b/modules/hash-triple
index b746d47..b726192 100644
--- a/modules/hash-triple
+++ b/modules/hash-triple
@@ -3,10 +3,9 @@ Hash functions for file-related triples: name, device, inode.
Files:
lib/hash-triple.c
-lib/hash-triple.h
Depends-on:
-hash-pjw
+hash-triple-simple
same
same-inode
diff --git a/modules/hash-triple-simple b/modules/hash-triple-simple
new file mode 100644
index 0000000..1ef3209
--- a/dev/null
+++ b/modules/hash-triple-simple
@@ -0,0 +1,24 @@
+Description:
+Hash functions for file-related triples: name, device, inode.
+
+Files:
+lib/hash-triple-simple.c
+lib/hash-triple.h
+
+Depends-on:
+hash-pjw
+same-inode
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += hash-triple-simple.c
+
+Include:
+"hash-triple.h"
+
+License:
+GPL
+
+Maintainer:
+Jim Meyering
diff --git a/modules/javacomp b/modules/javacomp
index 0552797..5f8f60f 100644
--- a/modules/javacomp
+++ b/modules/javacomp
@@ -43,6 +43,7 @@ Include:
Link:
$(LIB_MBRTOWC)
+$(LIB_GETRANDOM)
License:
GPL
diff --git a/modules/libgmp b/modules/libgmp
new file mode 100644
index 0000000..b686717
--- a/dev/null
+++ b/modules/libgmp
@@ -0,0 +1,42 @@
+Description:
+GNU Multiple Precision Arithmetic library, or its mini-gmp substitute
+
+Files:
+lib/mini-gmp-gnulib.c
+lib/mini-gmp.c
+lib/mini-gmp.h
+m4/libgmp.m4
+
+Depends-on:
+
+configure.ac:
+gl_LIBGMP
+if test -n "$GMP_H"; then
+ AC_LIBOBJ([mini-gmp-gnulib])
+fi
+
+Makefile.am:
+BUILT_SOURCES += $(GMP_H)
+
+# Build gmp.h as a wrapper for mini-gmp.h when using mini-gmp.
+if GL_GENERATE_GMP_H
+gmp.h: $(top_builddir)/config.status
+ echo '#include "mini-gmp.h"' >$@-t
+ mv $@-t $@
+else
+gmp.h: $(top_builddir)/config.status
+ rm -f $@
+endif
+MOSTLYCLEANFILES += gmp.h gmp.h-t
+
+Include:
+<gmp.h>
+
+Link:
+$(LIB_GMP)
+
+License:
+LGPLv3+ or GPLv2+
+
+Maintainer:
+all
diff --git a/modules/libgmp-tests b/modules/libgmp-tests
new file mode 100644
index 0000000..50a1801
--- a/dev/null
+++ b/modules/libgmp-tests
@@ -0,0 +1,12 @@
+Files:
+tests/macros.h
+tests/test-libgmp.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-libgmp
+check_PROGRAMS += test-libgmp
+test_libgmp_LDADD = $(LDADD) @LIB_GMP@
diff --git a/modules/lock-tests b/modules/lock-tests
index 112fbf9..31d627d 100644
--- a/modules/lock-tests
+++ b/modules/lock-tests
@@ -2,6 +2,7 @@ Files:
tests/test-rwlock1.c
tests/test-lock.c
tests/test-once.c
+tests/atomic-int-gnulib.h
m4/semaphore.m4
Depends-on:
diff --git a/modules/memchr b/modules/memchr
index ed80e0c..3a413a2 100644
--- a/modules/memchr
+++ b/modules/memchr
@@ -10,11 +10,10 @@ m4/mmap-anon.m4
Depends-on:
extensions
string
-memchr-obsolete
configure.ac:
gl_FUNC_MEMCHR
-if test $HAVE_MEMCHR = 0 || test $REPLACE_MEMCHR = 1; then
+if test $REPLACE_MEMCHR = 1; then
AC_LIBOBJ([memchr])
gl_PREREQ_MEMCHR
fi
diff --git a/modules/memchr-obsolete b/modules/memchr-obsolete
deleted file mode 100644
index c1a6e43..0000000
--- a/modules/memchr-obsolete
+++ b/dev/null
@@ -1,28 +0,0 @@
-Description:
-memchr() function for old platforms.
-
-Status:
-obsolete
-
-Notice:
-This module is obsolete.
-
-Files:
-m4/memchr-obsolete.m4
-
-Depends-on:
-memchr
-
-configure.ac:
-gl_FUNC_MEMCHR_OBSOLETE
-
-Makefile.am:
-
-Include:
-<string.h>
-
-License:
-LGPLv2+
-
-Maintainer:
-all
diff --git a/modules/mkancesdirs b/modules/mkancesdirs
index 7ae1a48..c4cb3db 100644
--- a/modules/mkancesdirs
+++ b/modules/mkancesdirs
@@ -7,8 +7,8 @@ lib/mkancesdirs.h
m4/mkancesdirs.m4
Depends-on:
-dirname-lgpl
fcntl-h
+filename
savewd
stat-macros
sys_stat
diff --git a/modules/mkdtemp b/modules/mkdtemp
index 3bd83e2..13b2d5a 100644
--- a/modules/mkdtemp
+++ b/modules/mkdtemp
@@ -23,6 +23,9 @@ Makefile.am:
Include:
<stdlib.h>
+Link:
+$(LIB_GETRANDOM)
+
License:
LGPLv2+
diff --git a/modules/mkostemp b/modules/mkostemp
index afa49d6..460e842 100644
--- a/modules/mkostemp
+++ b/modules/mkostemp
@@ -26,6 +26,9 @@ Makefile.am:
Include:
<stdlib.h>
+Link:
+$(LIB_GETRANDOM)
+
License:
LGPLv2+
diff --git a/modules/mkostemps b/modules/mkostemps
index fb956f9..bb9fe71 100644
--- a/modules/mkostemps
+++ b/modules/mkostemps
@@ -25,6 +25,9 @@ Makefile.am:
Include:
<stdlib.h>
+Link:
+$(LIB_GETRANDOM)
+
License:
LGPLv2+
diff --git a/modules/mkstemp b/modules/mkstemp
index 780908f..d985715 100644
--- a/modules/mkstemp
+++ b/modules/mkstemp
@@ -24,6 +24,9 @@ Makefile.am:
Include:
<stdlib.h>
+Link:
+$(LIB_GETRANDOM)
+
License:
LGPLv2+
diff --git a/modules/mkstemps b/modules/mkstemps
index 61af8d5..1e5f097 100644
--- a/modules/mkstemps
+++ b/modules/mkstemps
@@ -24,6 +24,9 @@ Makefile.am:
Include:
<stdlib.h>
+Link:
+$(LIB_GETRANDOM)
+
License:
LGPLv2+
diff --git a/modules/mtx-tests b/modules/mtx-tests
index 7714ebc..5231062 100644
--- a/modules/mtx-tests
+++ b/modules/mtx-tests
@@ -1,6 +1,7 @@
Files:
tests/test-mtx.c
tests/test-call_once.c
+tests/atomic-int-isoc.h
tests/macros.h
Depends-on:
diff --git a/modules/pthread-mutex-tests b/modules/pthread-mutex-tests
index 12d386e..c419dcc 100644
--- a/modules/pthread-mutex-tests
+++ b/modules/pthread-mutex-tests
@@ -1,5 +1,6 @@
Files:
tests/test-pthread-mutex.c
+tests/atomic-int-posix.h
tests/macros.h
Depends-on:
diff --git a/modules/pthread-rwlock-tests b/modules/pthread-rwlock-tests
index 340ebf0..594f6b6 100644
--- a/modules/pthread-rwlock-tests
+++ b/modules/pthread-rwlock-tests
@@ -1,5 +1,6 @@
Files:
tests/test-pthread-rwlock.c
+tests/atomic-int-posix.h
tests/macros.h
Depends-on:
diff --git a/modules/pthread-spin b/modules/pthread-spin
index 53e0051..ce69944 100644
--- a/modules/pthread-spin
+++ b/modules/pthread-spin
@@ -7,6 +7,7 @@ m4/pthread-spin.m4
Depends-on:
pthread-h
+stdbool [test $HAVE_PTHREAD_SPIN_INIT = 0 || test $REPLACE_PTHREAD_SPIN_INIT = 1]
windows-spin [test $gl_threads_api = windows]
configure.ac:
diff --git a/modules/pthread-spin-tests b/modules/pthread-spin-tests
new file mode 100644
index 0000000..aafe8c7
--- a/dev/null
+++ b/modules/pthread-spin-tests
@@ -0,0 +1,20 @@
+Files:
+tests/test-pthread-spin.c
+tests/atomic-int-posix.h
+tests/macros.h
+m4/semaphore.m4
+
+Depends-on:
+pthread-thread
+pthread-mutex
+sched_yield
+
+configure.ac:
+AC_CHECK_HEADERS_ONCE([semaphore.h])
+AC_CHECK_DECLS_ONCE([alarm])
+AC_REQUIRE([gl_SEMAPHORE])
+
+Makefile.am:
+TESTS += test-pthread-spin
+check_PROGRAMS += test-pthread-spin
+test_pthread_spin_LDADD = $(LDADD) @LIBPMULTITHREAD@ @LIB_SCHED_YIELD@ @LIB_SEMAPHORE@
diff --git a/modules/stdlib-safer b/modules/stdlib-safer
index 8dbd682..80414b3 100644
--- a/modules/stdlib-safer
+++ b/modules/stdlib-safer
@@ -20,6 +20,9 @@ lib_SOURCES += mkstemp-safer.c
Include:
"stdlib-safer.h"
+Link:
+$(LIB_GETRANDOM)
+
License:
GPL
diff --git a/modules/string b/modules/string
index 67209d5..bc87bc8 100644
--- a/modules/string
+++ b/modules/string
@@ -73,7 +73,6 @@ string.h: string.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H
-e 's|@''HAVE_FFSL''@|$(HAVE_FFSL)|g' \
-e 's|@''HAVE_FFSLL''@|$(HAVE_FFSLL)|g' \
-e 's|@''HAVE_MBSLEN''@|$(HAVE_MBSLEN)|g' \
- -e 's|@''HAVE_MEMCHR''@|$(HAVE_MEMCHR)|g' \
-e 's|@''HAVE_DECL_MEMMEM''@|$(HAVE_DECL_MEMMEM)|g' \
-e 's|@''HAVE_MEMPCPY''@|$(HAVE_MEMPCPY)|g' \
-e 's|@''HAVE_DECL_MEMRCHR''@|$(HAVE_DECL_MEMRCHR)|g' \
diff --git a/modules/strnlen b/modules/strnlen
index c781671..ea5658a 100644
--- a/modules/strnlen
+++ b/modules/strnlen
@@ -8,7 +8,6 @@ m4/strnlen.m4
Depends-on:
string
extensions
-memchr-obsolete [test $HAVE_DECL_STRNLEN = 0 || test $REPLACE_STRNLEN = 1]
configure.ac:
gl_FUNC_STRNLEN
diff --git a/modules/supersede b/modules/supersede
new file mode 100644
index 0000000..cf6e196
--- a/dev/null
+++ b/modules/supersede
@@ -0,0 +1,41 @@
+Description:
+Open a file, without destroying an old file with the same name.
+
+Files:
+lib/supersede.h
+lib/supersede.c
+m4/supersede.m4
+
+Depends-on:
+fcntl-h
+sys_stat
+clean-temp
+tempname
+canonicalize
+open
+unlink
+ignore-value
+stat
+stat-time
+utimens
+acl-permissions
+qcopy-acl
+fdopen
+
+configure.ac:
+gl_SUPERSEDE
+
+Makefile.am:
+lib_SOURCES += supersede.c
+
+Include:
+"supersede.h"
+
+Link:
+$(LIB_GETRANDOM)
+
+License:
+GPL
+
+Maintainer:
+all
diff --git a/modules/supersede-tests b/modules/supersede-tests
new file mode 100644
index 0000000..811d8f6
--- a/dev/null
+++ b/modules/supersede-tests
@@ -0,0 +1,21 @@
+Files:
+tests/test-supersede.c
+tests/test-supersede-open.h
+tests/test-supersede-fopen.h
+tests/macros.h
+
+Depends-on:
+mkdtemp
+filenamecat
+write
+read-file
+unlink
+rmdir
+symlink
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-supersede
+check_PROGRAMS += test-supersede
+test_supersede_LDADD = $(LDADD) $(LIB_ACL) $(LIB_CLOCK_GETTIME) $(LIB_GETRANDOM)
diff --git a/modules/sys_stat b/modules/sys_stat
index 4783c7e..af276ab 100644
--- a/modules/sys_stat
+++ b/modules/sys_stat
@@ -38,6 +38,7 @@ sys/stat.h: sys_stat.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNU
-e 's/@''GNULIB_FSTAT''@/$(GNULIB_FSTAT)/g' \
-e 's/@''GNULIB_FSTATAT''@/$(GNULIB_FSTATAT)/g' \
-e 's/@''GNULIB_FUTIMENS''@/$(GNULIB_FUTIMENS)/g' \
+ -e 's/@''GNULIB_GETUMASK''@/$(GNULIB_GETUMASK)/g' \
-e 's/@''GNULIB_LCHMOD''@/$(GNULIB_LCHMOD)/g' \
-e 's/@''GNULIB_LSTAT''@/$(GNULIB_LSTAT)/g' \
-e 's/@''GNULIB_MKDIRAT''@/$(GNULIB_MKDIRAT)/g' \
@@ -51,6 +52,7 @@ sys/stat.h: sys_stat.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNU
-e 's|@''HAVE_FCHMODAT''@|$(HAVE_FCHMODAT)|g' \
-e 's|@''HAVE_FSTATAT''@|$(HAVE_FSTATAT)|g' \
-e 's|@''HAVE_FUTIMENS''@|$(HAVE_FUTIMENS)|g' \
+ -e 's|@''HAVE_GETUMASK''@|$(HAVE_GETUMASK)|g' \
-e 's|@''HAVE_LCHMOD''@|$(HAVE_LCHMOD)|g' \
-e 's|@''HAVE_LSTAT''@|$(HAVE_LSTAT)|g' \
-e 's|@''HAVE_MKDIRAT''@|$(HAVE_MKDIRAT)|g' \
diff --git a/modules/tempname b/modules/tempname
index 0f692d3..794a763 100644
--- a/modules/tempname
+++ b/modules/tempname
@@ -19,6 +19,7 @@ sys_stat
configure.ac:
gl_FUNC_GEN_TEMPNAME
+gl_MODULE_INDICATOR([tempname])
Makefile.am:
lib_SOURCES += tempname.c
@@ -26,6 +27,9 @@ lib_SOURCES += tempname.c
Include:
"tempname.h"
+Link:
+$(LIB_GETRANDOM)
+
License:
LGPLv2+
diff --git a/modules/tmpfile b/modules/tmpfile
index 884f284..20b88c8 100644
--- a/modules/tmpfile
+++ b/modules/tmpfile
@@ -26,6 +26,9 @@ Makefile.am:
Include:
<stdio.h>
+Link:
+$(LIB_GETRANDOM)
+
License:
LGPL
diff --git a/modules/tmpfile-safer b/modules/tmpfile-safer
index d63559e..02e49d4 100644
--- a/modules/tmpfile-safer
+++ b/modules/tmpfile-safer
@@ -20,6 +20,9 @@ lib_SOURCES += tmpfile-safer.c
Include:
"stdio-safer.h"
+Link:
+$(LIB_GETRANDOM)
+
License:
GPL
diff --git a/modules/unicodeio b/modules/unicodeio
index 8164ac4..51721db 100644
--- a/modules/unicodeio
+++ b/modules/unicodeio
@@ -24,6 +24,9 @@ lib_SOURCES += unicodeio.h unicodeio.c
Include:
"unicodeio.h"
+Link:
+$(LTLIBICONV) when linking with libtool, $(LIBICONV) otherwise
+
License:
GPL
diff --git a/modules/unicodeio-tests b/modules/unicodeio-tests
new file mode 100644
index 0000000..d771ff8
--- a/dev/null
+++ b/modules/unicodeio-tests
@@ -0,0 +1,21 @@
+Files:
+tests/test-unicodeio1.sh
+tests/test-unicodeio2.sh
+tests/test-unicodeio3.sh
+tests/test-unicodeio.c
+tests/macros.h
+m4/locale-fr.m4
+m4/locale-zh.m4
+m4/codeset.m4
+
+Depends-on:
+setlocale
+
+configure.ac:
+gt_LOCALE_FR_UTF8
+gt_LOCALE_ZH_CN
+
+Makefile.am:
+TESTS += test-unicodeio1.sh test-unicodeio2.sh test-unicodeio3.sh
+check_PROGRAMS += test-unicodeio
+test_unicodeio_LDADD = $(LDADD) $(LIB_SETLOCALE) $(LIBICONV)
diff --git a/modules/unistd b/modules/unistd
index 5550646..b14faaa 100644
--- a/modules/unistd
+++ b/modules/unistd
@@ -94,7 +94,6 @@ unistd.h: unistd.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H
< $(srcdir)/unistd.in.h | \
sed -e 's|@''HAVE_CHOWN''@|$(HAVE_CHOWN)|g' \
-e 's|@''HAVE_COPY_FILE_RANGE''@|$(HAVE_COPY_FILE_RANGE)|g' \
- -e 's|@''HAVE_DUP2''@|$(HAVE_DUP2)|g' \
-e 's|@''HAVE_DUP3''@|$(HAVE_DUP3)|g' \
-e 's|@''HAVE_EUIDACCESS''@|$(HAVE_EUIDACCESS)|g' \
-e 's|@''HAVE_FACCESSAT''@|$(HAVE_FACCESSAT)|g' \
diff --git a/tests/atomic-int-gnulib.h b/tests/atomic-int-gnulib.h
new file mode 100644
index 0000000..82e11a9
--- a/dev/null
+++ b/tests/atomic-int-gnulib.h
@@ -0,0 +1,173 @@
+/* Atomic integers. Useful for testing multithreaded locking primitives.
+ Copyright (C) 2005, 2008-2020 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/>. */
+
+
+/* Whether to use 'volatile' on some variables that communicate information
+ between threads. If set to 0, a semaphore or a lock is used to protect
+ these variables. If set to 1, 'volatile' is used; this is theoretically
+ equivalent but can lead to much slower execution (e.g. 30x slower total
+ run time on a 40-core machine), because 'volatile' does not imply any
+ synchronization/communication between different CPUs. */
+#define USE_VOLATILE 0
+
+#if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
+/* Whether to use a semaphore to communicate information between threads.
+ If set to 0, a lock is used. If set to 1, a semaphore is used.
+ Uncomment this to reduce the dependencies of this test. */
+# define USE_SEMAPHORE 1
+/* Mac OS X provides only named semaphores (sem_open); its facility for
+ unnamed semaphores (sem_init) does not work. */
+# if defined __APPLE__ && defined __MACH__
+# define USE_NAMED_SEMAPHORE 1
+# else
+# define USE_UNNAMED_SEMAPHORE 1
+# endif
+#endif
+
+
+#if USE_SEMAPHORE
+# include <errno.h>
+# include <fcntl.h>
+# include <semaphore.h>
+# include <unistd.h>
+#endif
+
+
+#if USE_VOLATILE
+struct atomic_int {
+ volatile int value;
+};
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+}
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+ return ai->value;
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+ ai->value = new_value;
+}
+#elif USE_SEMAPHORE
+/* This atomic_int implementation can only support the values 0 and 1.
+ It is initially 0 and can be set to 1 only once. */
+# if USE_UNNAMED_SEMAPHORE
+struct atomic_int {
+ sem_t semaphore;
+};
+#define atomic_int_semaphore(ai) (&(ai)->semaphore)
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+ sem_init (&ai->semaphore, 0, 0);
+}
+# endif
+# if USE_NAMED_SEMAPHORE
+struct atomic_int {
+ sem_t *semaphore;
+};
+#define atomic_int_semaphore(ai) ((ai)->semaphore)
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+ sem_t *s;
+ unsigned int count;
+ for (count = 0; ; count++)
+ {
+ char name[80];
+ /* Use getpid() in the name, so that different processes running at the
+ same time will not interfere. Use ai in the name, so that different
+ atomic_int in the same process will not interfere. Use a count in
+ the name, so that even in the (unlikely) case that a semaphore with
+ the specified name already exists, we can try a different name. */
+ sprintf (name, "test-lock-%lu-%p-%u",
+ (unsigned long) getpid (), ai, count);
+ s = sem_open (name, O_CREAT | O_EXCL, 0600, 0);
+ if (s == SEM_FAILED)
+ {
+ if (errno == EEXIST)
+ /* Retry with a different name. */
+ continue;
+ else
+ {
+ perror ("sem_open failed");
+ abort ();
+ }
+ }
+ else
+ {
+ /* Try not to leave a semaphore hanging around on the file system
+ eternally, if we can avoid it. */
+ sem_unlink (name);
+ break;
+ }
+ }
+ ai->semaphore = s;
+}
+# endif
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+ if (sem_trywait (atomic_int_semaphore (ai)) == 0)
+ {
+ if (sem_post (atomic_int_semaphore (ai)))
+ abort ();
+ return 1;
+ }
+ else if (errno == EAGAIN)
+ return 0;
+ else
+ abort ();
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+ if (new_value == 0)
+ /* It's already initialized with 0. */
+ return;
+ /* To set the value 1: */
+ if (sem_post (atomic_int_semaphore (ai)))
+ abort ();
+}
+#else
+struct atomic_int {
+ gl_lock_define (, lock)
+ int value;
+};
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+ gl_lock_init (ai->lock);
+}
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+ gl_lock_lock (ai->lock);
+ int ret = ai->value;
+ gl_lock_unlock (ai->lock);
+ return ret;
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+ gl_lock_lock (ai->lock);
+ ai->value = new_value;
+ gl_lock_unlock (ai->lock);
+}
+#endif
diff --git a/tests/atomic-int-isoc.h b/tests/atomic-int-isoc.h
new file mode 100644
index 0000000..c4e57e9
--- a/dev/null
+++ b/tests/atomic-int-isoc.h
@@ -0,0 +1,173 @@
+/* Atomic integers. Useful for testing multithreaded locking primitives.
+ Copyright (C) 2005, 2008-2020 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/>. */
+
+
+/* Whether to use 'volatile' on some variables that communicate information
+ between threads. If set to 0, a semaphore or a lock is used to protect
+ these variables. If set to 1, 'volatile' is used; this is theoretically
+ equivalent but can lead to much slower execution (e.g. 30x slower total
+ run time on a 40-core machine), because 'volatile' does not imply any
+ synchronization/communication between different CPUs. */
+#define USE_VOLATILE 0
+
+#if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
+/* Whether to use a semaphore to communicate information between threads.
+ If set to 0, a lock is used. If set to 1, a semaphore is used.
+ Uncomment this to reduce the dependencies of this test. */
+# define USE_SEMAPHORE 1
+/* Mac OS X provides only named semaphores (sem_open); its facility for
+ unnamed semaphores (sem_init) does not work. */
+# if defined __APPLE__ && defined __MACH__
+# define USE_NAMED_SEMAPHORE 1
+# else
+# define USE_UNNAMED_SEMAPHORE 1
+# endif
+#endif
+
+
+#if USE_SEMAPHORE
+# include <errno.h>
+# include <fcntl.h>
+# include <semaphore.h>
+# include <unistd.h>
+#endif
+
+
+#if USE_VOLATILE
+struct atomic_int {
+ volatile int value;
+};
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+}
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+ return ai->value;
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+ ai->value = new_value;
+}
+#elif USE_SEMAPHORE
+/* This atomic_int implementation can only support the values 0 and 1.
+ It is initially 0 and can be set to 1 only once. */
+# if USE_UNNAMED_SEMAPHORE
+struct atomic_int {
+ sem_t semaphore;
+};
+#define atomic_int_semaphore(ai) (&(ai)->semaphore)
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+ sem_init (&ai->semaphore, 0, 0);
+}
+# endif
+# if USE_NAMED_SEMAPHORE
+struct atomic_int {
+ sem_t *semaphore;
+};
+#define atomic_int_semaphore(ai) ((ai)->semaphore)
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+ sem_t *s;
+ unsigned int count;
+ for (count = 0; ; count++)
+ {
+ char name[80];
+ /* Use getpid() in the name, so that different processes running at the
+ same time will not interfere. Use ai in the name, so that different
+ atomic_int in the same process will not interfere. Use a count in
+ the name, so that even in the (unlikely) case that a semaphore with
+ the specified name already exists, we can try a different name. */
+ sprintf (name, "test-lock-%lu-%p-%u",
+ (unsigned long) getpid (), ai, count);
+ s = sem_open (name, O_CREAT | O_EXCL, 0600, 0);
+ if (s == SEM_FAILED)
+ {
+ if (errno == EEXIST)
+ /* Retry with a different name. */
+ continue;
+ else
+ {
+ perror ("sem_open failed");
+ abort ();
+ }
+ }
+ else
+ {
+ /* Try not to leave a semaphore hanging around on the file system
+ eternally, if we can avoid it. */
+ sem_unlink (name);
+ break;
+ }
+ }
+ ai->semaphore = s;
+}
+# endif
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+ if (sem_trywait (atomic_int_semaphore (ai)) == 0)
+ {
+ if (sem_post (atomic_int_semaphore (ai)))
+ abort ();
+ return 1;
+ }
+ else if (errno == EAGAIN)
+ return 0;
+ else
+ abort ();
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+ if (new_value == 0)
+ /* It's already initialized with 0. */
+ return;
+ /* To set the value 1: */
+ if (sem_post (atomic_int_semaphore (ai)))
+ abort ();
+}
+#else
+struct atomic_int {
+ mtx_t lock;
+ int value;
+};
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+ ASSERT (mtx_init (&ai->lock, mtx_plain) == thrd_success);
+}
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+ ASSERT (mtx_lock (&ai->lock) == thrd_success);
+ int ret = ai->value;
+ ASSERT (mtx_unlock (&ai->lock) == thrd_success);
+ return ret;
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+ ASSERT (mtx_lock (&ai->lock) == thrd_success);
+ ai->value = new_value;
+ ASSERT (mtx_unlock (&ai->lock) == thrd_success);
+}
+#endif
diff --git a/tests/atomic-int-posix.h b/tests/atomic-int-posix.h
new file mode 100644
index 0000000..dac797e
--- a/dev/null
+++ b/tests/atomic-int-posix.h
@@ -0,0 +1,178 @@
+/* Atomic integers. Useful for testing multithreaded locking primitives.
+ Copyright (C) 2005, 2008-2020 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/>. */
+
+
+/* Whether to use 'volatile' on some variables that communicate information
+ between threads. If set to 0, a semaphore or a lock is used to protect
+ these variables. If set to 1, 'volatile' is used; this is theoretically
+ equivalent but can lead to much slower execution (e.g. 30x slower total
+ run time on a 40-core machine), because 'volatile' does not imply any
+ synchronization/communication between different CPUs. */
+#define USE_VOLATILE 0
+
+#if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
+/* Whether to use a semaphore to communicate information between threads.
+ If set to 0, a lock is used. If set to 1, a semaphore is used.
+ Uncomment this to reduce the dependencies of this test. */
+# define USE_SEMAPHORE 1
+/* Mac OS X provides only named semaphores (sem_open); its facility for
+ unnamed semaphores (sem_init) does not work. */
+# if defined __APPLE__ && defined __MACH__
+# define USE_NAMED_SEMAPHORE 1
+# else
+# define USE_UNNAMED_SEMAPHORE 1
+# endif
+#endif
+
+
+#if USE_SEMAPHORE
+# include <errno.h>
+# include <fcntl.h>
+# include <semaphore.h>
+# include <unistd.h>
+#endif
+
+
+#if USE_VOLATILE
+struct atomic_int {
+ volatile int value;
+};
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+}
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+ return ai->value;
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+ ai->value = new_value;
+}
+#elif USE_SEMAPHORE
+/* This atomic_int implementation can only support the values 0 and 1.
+ It is initially 0 and can be set to 1 only once. */
+# if USE_UNNAMED_SEMAPHORE
+struct atomic_int {
+ sem_t semaphore;
+};
+#define atomic_int_semaphore(ai) (&(ai)->semaphore)
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+ sem_init (&ai->semaphore, 0, 0);
+}
+# endif
+# if USE_NAMED_SEMAPHORE
+struct atomic_int {
+ sem_t *semaphore;
+};
+#define atomic_int_semaphore(ai) ((ai)->semaphore)
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+ sem_t *s;
+ unsigned int count;
+ for (count = 0; ; count++)
+ {
+ char name[80];
+ /* Use getpid() in the name, so that different processes running at the
+ same time will not interfere. Use ai in the name, so that different
+ atomic_int in the same process will not interfere. Use a count in
+ the name, so that even in the (unlikely) case that a semaphore with
+ the specified name already exists, we can try a different name. */
+ sprintf (name, "test-lock-%lu-%p-%u",
+ (unsigned long) getpid (), ai, count);
+ s = sem_open (name, O_CREAT | O_EXCL, 0600, 0);
+ if (s == SEM_FAILED)
+ {
+ if (errno == EEXIST)
+ /* Retry with a different name. */
+ continue;
+ else
+ {
+ perror ("sem_open failed");
+ abort ();
+ }
+ }
+ else
+ {
+ /* Try not to leave a semaphore hanging around on the file system
+ eternally, if we can avoid it. */
+ sem_unlink (name);
+ break;
+ }
+ }
+ ai->semaphore = s;
+}
+# endif
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+ if (sem_trywait (atomic_int_semaphore (ai)) == 0)
+ {
+ if (sem_post (atomic_int_semaphore (ai)))
+ abort ();
+ return 1;
+ }
+ else if (errno == EAGAIN)
+ return 0;
+ else
+ abort ();
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+ if (new_value == 0)
+ /* It's already initialized with 0. */
+ return;
+ /* To set the value 1: */
+ if (sem_post (atomic_int_semaphore (ai)))
+ abort ();
+}
+#else
+struct atomic_int {
+ pthread_mutex_t lock;
+ int value;
+};
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+ pthread_mutexattr_t attr;
+
+ ASSERT (pthread_mutexattr_init (&attr) == 0);
+ ASSERT (pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL) == 0);
+ ASSERT (pthread_mutex_init (&ai->lock, &attr) == 0);
+ ASSERT (pthread_mutexattr_destroy (&attr) == 0);
+}
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+ ASSERT (pthread_mutex_lock (&ai->lock) == 0);
+ int ret = ai->value;
+ ASSERT (pthread_mutex_unlock (&ai->lock) == 0);
+ return ret;
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+ ASSERT (pthread_mutex_lock (&ai->lock) == 0);
+ ai->value = new_value;
+ ASSERT (pthread_mutex_unlock (&ai->lock) == 0);
+}
+#endif
diff --git a/tests/test-asyncsafe-spin1.c b/tests/test-asyncsafe-spin1.c
new file mode 100644
index 0000000..e345e17
--- a/dev/null
+++ b/tests/test-asyncsafe-spin1.c
@@ -0,0 +1,61 @@
+/* Test of spin locks for communication between threads and signal handlers.
+ Copyright (C) 2005, 2008-2020 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>, 2020. */
+
+#include <config.h>
+
+/* Specification. */
+#include "asyncsafe-spin.h"
+
+#include <signal.h>
+
+asyncsafe_spinlock_t global_spin_lock = ASYNCSAFE_SPIN_INIT;
+
+int
+main (void)
+{
+ sigset_t set;
+
+ sigemptyset (&set);
+ sigaddset (&set, SIGINT);
+
+ /* Check a spin-lock initialized through the constant initializer. */
+ {
+ sigset_t saved_set;
+ asyncsafe_spin_lock (&global_spin_lock, &set, &saved_set);
+ asyncsafe_spin_unlock (&global_spin_lock, &saved_set);
+ }
+
+ /* Check a spin-lock initialized through asyncsafe_spin_init. */
+ {
+ asyncsafe_spinlock_t local_spin_lock;
+ int i;
+
+ asyncsafe_spin_init (&local_spin_lock);
+
+ for (i = 0; i < 10; i++)
+ {
+ sigset_t saved_set;
+ asyncsafe_spin_lock (&local_spin_lock, &set, &saved_set);
+ asyncsafe_spin_unlock (&local_spin_lock, &saved_set);
+ }
+
+ asyncsafe_spin_destroy (&local_spin_lock);
+ }
+
+ return 0;
+}
diff --git a/tests/test-asyncsafe-spin2.c b/tests/test-asyncsafe-spin2.c
new file mode 100644
index 0000000..cb0364f
--- a/dev/null
+++ b/tests/test-asyncsafe-spin2.c
@@ -0,0 +1,245 @@
+/* Test of spin locks for communication between threads and signal handlers.
+ Copyright (C) 2005, 2008-2020 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>, 2005. */
+
+#include <config.h>
+
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
+
+/* Whether to enable locking.
+ Uncomment this to get a test program without locking, to verify that
+ it crashes. */
+#define ENABLE_LOCKING 1
+
+/* Whether to help the scheduler through explicit yield().
+ Uncomment this to see if the operating system has a fair scheduler. */
+#define EXPLICIT_YIELD 1
+
+/* Whether to print debugging messages. */
+#define ENABLE_DEBUGGING 0
+
+/* Number of simultaneous threads. */
+#define THREAD_COUNT 10
+
+/* Number of operations performed in each thread. */
+#if !(defined _WIN32 && ! defined __CYGWIN__) && HAVE_PTHREAD_H && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined __ibmxl__
+/* The GCC built-ins are known to work fine. */
+# define REPEAT_COUNT 5000
+#else
+/* This is quite high, because with a smaller count, say 50000, we often get
+ an "OK" result even with the racy implementation that we pick on Fedora 13
+ Linux/x86_64 (gcc 4.4). */
+# define REPEAT_COUNT 100000
+#endif
+
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "asyncsafe-spin.h"
+#if !ENABLE_LOCKING
+# define asyncsafe_spin_init(lock) (void)(lock)
+# define asyncsafe_spin_lock(lock, mask, saved_mask) \
+ ((void)(lock), (void)(mask), (void)(saved_mask))
+# define asyncsafe_spin_unlock(lock, saved_mask) \
+ ((void)(lock), (void)(saved_mask))
+# define asyncsafe_spin_destroy(lock) (void)(lock)
+#endif
+
+#include "glthread/lock.h"
+#include "glthread/thread.h"
+#include "glthread/yield.h"
+
+#if HAVE_DECL_ALARM
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+#include "atomic-int-gnulib.h"
+
+#if ENABLE_DEBUGGING
+# define dbgprintf printf
+#else
+# define dbgprintf if (0) printf
+#endif
+
+#if EXPLICIT_YIELD
+# define yield() gl_thread_yield ()
+#else
+# define yield()
+#endif
+
+static sigset_t signals_to_block;
+
+#define ACCOUNT_COUNT 4
+
+static int account[ACCOUNT_COUNT];
+
+static int
+random_account (void)
+{
+ return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
+}
+
+static void
+check_accounts (void)
+{
+ int i, sum;
+
+ sum = 0;
+ for (i = 0; i < ACCOUNT_COUNT; i++)
+ sum += account[i];
+ if (sum != ACCOUNT_COUNT * 1000)
+ abort ();
+}
+
+
+/* ------------------- Test use like normal locks ------------------- */
+
+/* Test normal locks by having several bank accounts and several threads
+ which shuffle around money between the accounts and another thread
+ checking that all the money is still there. */
+
+static asyncsafe_spinlock_t my_lock;
+
+static void *
+lock_mutator_thread (void *arg)
+{
+ int repeat;
+
+ for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
+ {
+ sigset_t saved_signals;
+ int i1, i2, value;
+
+ dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
+ asyncsafe_spin_lock (&my_lock, &signals_to_block, &saved_signals);
+ dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
+
+ i1 = random_account ();
+ i2 = random_account ();
+ value = ((unsigned int) rand () >> 3) % 10;
+ account[i1] += value;
+ account[i2] -= value;
+
+ dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
+ asyncsafe_spin_unlock (&my_lock, &saved_signals);
+ dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
+
+ dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
+ asyncsafe_spin_lock (&my_lock, &signals_to_block, &saved_signals);
+ check_accounts ();
+ asyncsafe_spin_unlock (&my_lock, &saved_signals);
+ dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
+
+ yield ();
+ }
+
+ dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
+ return NULL;
+}
+
+static struct atomic_int lock_checker_done;
+
+static void *
+lock_checker_thread (void *arg)
+{
+ while (get_atomic_int_value (&lock_checker_done) == 0)
+ {
+ sigset_t saved_signals;
+
+ dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
+ asyncsafe_spin_lock (&my_lock, &signals_to_block, &saved_signals);
+ check_accounts ();
+ asyncsafe_spin_unlock (&my_lock, &saved_signals);
+ dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
+
+ yield ();
+ }
+
+ dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
+ return NULL;
+}
+
+static void
+test_asyncsafe_spin (void)
+{
+ int i;
+ gl_thread_t checkerthread;
+ gl_thread_t threads[THREAD_COUNT];
+
+ /* Initialization. */
+ for (i = 0; i < ACCOUNT_COUNT; i++)
+ account[i] = 1000;
+ init_atomic_int (&lock_checker_done);
+ set_atomic_int_value (&lock_checker_done, 0);
+
+ /* Spawn the threads. */
+ checkerthread = gl_thread_create (lock_checker_thread, NULL);
+ for (i = 0; i < THREAD_COUNT; i++)
+ threads[i] = gl_thread_create (lock_mutator_thread, NULL);
+
+ /* Wait for the threads to terminate. */
+ for (i = 0; i < THREAD_COUNT; i++)
+ gl_thread_join (threads[i], NULL);
+ set_atomic_int_value (&lock_checker_done, 1);
+ gl_thread_join (checkerthread, NULL);
+ check_accounts ();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+int
+main ()
+{
+#if HAVE_DECL_ALARM
+ /* Declare failure if test takes too long, by using default abort
+ caused by SIGALRM. */
+ int alarm_value = 600;
+ signal (SIGALRM, SIG_DFL);
+ alarm (alarm_value);
+#endif
+
+ sigemptyset (&signals_to_block);
+ sigaddset (&signals_to_block, SIGINT);
+
+ asyncsafe_spin_init (&my_lock);
+
+ printf ("Starting test_asyncsafe_spin ..."); fflush (stdout);
+ test_asyncsafe_spin ();
+ printf (" OK\n"); fflush (stdout);
+
+ return 0;
+}
+
+#else
+
+/* No multithreading available. */
+
+#include <stdio.h>
+
+int
+main ()
+{
+ fputs ("Skipping test: multithreading not enabled\n", stderr);
+ return 77;
+}
+
+#endif
diff --git a/tests/test-c-dtoastr.c b/tests/test-c-dtoastr.c
new file mode 100644
index 0000000..373e7c4
--- a/dev/null
+++ b/tests/test-c-dtoastr.c
@@ -0,0 +1,58 @@
+/* Test of c_dtoastr() function.
+ Copyright (C) 2020 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/>. */
+
+#include <config.h>
+
+#include "ftoastr.h"
+
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "macros.h"
+
+int
+main (int argc, char *argv[])
+{
+ /* configure should already have checked that the locale is supported. */
+ if (setlocale (LC_ALL, "") == NULL)
+ return 1;
+
+ /* Test behaviour of snprintf() as a "control group".
+ (We should be running in a locale where ',' is the decimal point.) */
+ {
+ char s[16];
+
+ snprintf (s, sizeof s, "%#.0f", 1.0);
+ if (!strcmp (s, "1."))
+ {
+ /* Skip the test, since we're not in a useful locale for testing. */
+ return 77;
+ }
+ ASSERT (!strcmp (s, "1,"));
+ }
+
+ /* Test behaviour of c_dtoastr().
+ It should always use '.' as the decimal point. */
+ {
+ char buf[DBL_BUFSIZE_BOUND];
+
+ c_dtoastr (buf, sizeof buf, 0, 0, 0.1);
+ ASSERT (!strcmp (buf, "0.1"));
+ }
+
+ return 0;
+}
diff --git a/tests/test-c-dtoastr.sh b/tests/test-c-dtoastr.sh
new file mode 100644
index 0000000..ef0e459
--- a/dev/null
+++ b/tests/test-c-dtoastr.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# Test in an ISO-8859-1 or ISO-8859-15 locale.
+: ${LOCALE_FR=fr_FR}
+if test $LOCALE_FR = none; then
+ if test -f /usr/bin/localedef; then
+ echo "Skipping test: no traditional french locale is installed"
+ else
+ echo "Skipping test: no traditional french locale is supported"
+ fi
+ exit 77
+fi
+
+LC_ALL=$LOCALE_FR \
+${CHECKER} ./test-c-dtoastr${EXEEXT} 1
diff --git a/tests/test-c-ldtoastr.c b/tests/test-c-ldtoastr.c
new file mode 100644
index 0000000..140f0c6
--- a/dev/null
+++ b/tests/test-c-ldtoastr.c
@@ -0,0 +1,58 @@
+/* Test of c_ldtoastr() function.
+ Copyright (C) 2020 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/>. */
+
+#include <config.h>
+
+#include "ftoastr.h"
+
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "macros.h"
+
+int
+main (int argc, char *argv[])
+{
+ /* configure should already have checked that the locale is supported. */
+ if (setlocale (LC_ALL, "") == NULL)
+ return 1;
+
+ /* Test behaviour of snprintf() as a "control group".
+ (We should be running in a locale where ',' is the decimal point.) */
+ {
+ char s[16];
+
+ snprintf (s, sizeof s, "%#.0f", 1.0);
+ if (!strcmp (s, "1."))
+ {
+ /* Skip the test, since we're not in a useful locale for testing. */
+ return 77;
+ }
+ ASSERT (!strcmp (s, "1,"));
+ }
+
+ /* Test behaviour of c_ldtoastr().
+ It should always use '.' as the decimal point. */
+ {
+ char buf[DBL_BUFSIZE_BOUND];
+
+ c_ldtoastr (buf, sizeof buf, 0, 0, 0.1);
+ ASSERT (!strcmp (buf, "0.1"));
+ }
+
+ return 0;
+}
diff --git a/tests/test-c-ldtoastr.sh b/tests/test-c-ldtoastr.sh
new file mode 100644
index 0000000..ad7a32a
--- a/dev/null
+++ b/tests/test-c-ldtoastr.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# Test in an ISO-8859-1 or ISO-8859-15 locale.
+: ${LOCALE_FR=fr_FR}
+if test $LOCALE_FR = none; then
+ if test -f /usr/bin/localedef; then
+ echo "Skipping test: no traditional french locale is installed"
+ else
+ echo "Skipping test: no traditional french locale is supported"
+ fi
+ exit 77
+fi
+
+LC_ALL=$LOCALE_FR \
+${CHECKER} ./test-c-ldtoastr${EXEEXT} 1
diff --git a/tests/test-calloc-gnu.c b/tests/test-calloc-gnu.c
index c13ba47..2ae3925 100644
--- a/tests/test-calloc-gnu.c
+++ b/tests/test-calloc-gnu.c
@@ -36,20 +36,26 @@ int
main ()
{
/* Check that calloc (0, 0) is not a NULL pointer. */
- void *p = calloc (0, 0);
- if (p == NULL)
- return 1;
- free (p);
+ {
+ void * volatile p = calloc (0, 0);
+ if (p == NULL)
+ return 1;
+ free (p);
+ }
/* Check that calloc fails when requested to allocate a block of memory
larger than SIZE_MAX bytes.
- We use eight (), not 8, to avoid a compiler warning from GCC 7. */
- p = calloc ((size_t) -1 / 8 + 1, eight ());
- if (p != NULL)
- {
- free (p);
- return 1;
- }
+ We use eight (), not 8, to avoid a compiler warning from GCC 7.
+ 'volatile' is needed to defeat an incorrect optimization by clang 10,
+ see <https://bugs.llvm.org/show_bug.cgi?id=46055>. */
+ {
+ void * volatile p = calloc ((size_t) -1 / 8 + 1, eight ());
+ if (p != NULL)
+ {
+ free (p);
+ return 2;
+ }
+ }
return 0;
}
diff --git a/tests/dfa-invalid-char-class.sh b/tests/test-dfa-invalid-char-class.sh
index 8e8824d..094bab0 100755
--- a/tests/dfa-invalid-char-class.sh
+++ b/tests/test-dfa-invalid-char-class.sh
@@ -18,13 +18,13 @@
. "${srcdir=.}/init.sh"; path_prepend_ ../src
-# Add "." to PATH for the use of dfa-match-aux.
+# Add "." to PATH for the use of test-dfa-match-aux.
path_prepend_ .
fail=0
echo 'dfaerror: invalid character class' > exp
-LC_ALL=C ${CHECKER} dfa-match-aux '[[:foo:]]' a > out 2>&1
+LC_ALL=C ${CHECKER} test-dfa-match-aux '[[:foo:]]' a > out 2>&1
compare exp out || fail=1
Exit $fail
diff --git a/tests/dfa-match-aux.c b/tests/test-dfa-match-aux.c
index e0c5f3d..e0c5f3d 100644
--- a/tests/dfa-match-aux.c
+++ b/tests/test-dfa-match-aux.c
diff --git a/tests/dfa-match.sh b/tests/test-dfa-match.sh
index bc9e1dc..a7285ec 100755
--- a/tests/dfa-match.sh
+++ b/tests/test-dfa-match.sh
@@ -18,7 +18,7 @@
. "${srcdir=.}/init.sh"; path_prepend_ ../src
-# Add "." to PATH for the use of dfa-match-aux.
+# Add "." to PATH for the use of test-dfa-match-aux.
path_prepend_ .
if (type timeout) >/dev/null 2>&1; then
@@ -33,11 +33,11 @@ fi
fail=0
-${CHECKER} dfa-match-aux a ba 0 > out || fail=1
+${CHECKER} test-dfa-match-aux a ba 0 > out || fail=1
compare /dev/null out || fail=1
in=$(printf "bb\nbb")
-$timeout_10 ${CHECKER} dfa-match-aux a "$in" 1 > out || fail=1
+$timeout_10 ${CHECKER} test-dfa-match-aux a "$in" 1 > out || fail=1
compare /dev/null out || fail=1
Exit $fail
diff --git a/tests/test-getloadavg.c b/tests/test-getloadavg.c
index daf443b..b8782c2 100644
--- a/tests/test-getloadavg.c
+++ b/tests/test-getloadavg.c
@@ -70,7 +70,7 @@ main (int argc, char **argv)
if (loads > 1)
check_avg (5, avg[1], argc > 1);
if (loads > 2)
- check_avg (15, avg[1], argc > 1);
+ check_avg (15, avg[2], argc > 1);
if (loads > 0 && argc > 1)
putchar ('\n');
diff --git a/tests/test-getumask.c b/tests/test-getumask.c
new file mode 100644
index 0000000..b18afa2
--- a/dev/null
+++ b/tests/test-getumask.c
@@ -0,0 +1,52 @@
+/* Test of retrieving the umask of the process.
+ Copyright (C) 2020 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. */
+
+#include <config.h>
+
+#include <sys/stat.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (getumask, mode_t, (void));
+
+#include "macros.h"
+
+int
+main (void)
+{
+ int mask;
+
+ mask = getumask ();
+ ASSERT (mask >= 0);
+
+#if !ASSUME_UMASK_CONSTANT
+ /* These tests fail if the umask is required to not change. */
+
+ umask (002);
+
+ mask = getumask ();
+ ASSERT (mask == 002);
+
+ umask (077);
+
+ mask = getumask ();
+ ASSERT (mask == 077);
+
+#endif
+
+ return 0;
+}
diff --git a/tests/test-libgmp.c b/tests/test-libgmp.c
new file mode 100644
index 0000000..af8e4d7
--- a/dev/null
+++ b/tests/test-libgmp.c
@@ -0,0 +1,24 @@
+#include <config.h>
+
+#include <gmp.h>
+
+#include "macros.h"
+
+int
+main ()
+{
+ /* A simple sanity check that 2 + 2 = 4. */
+ static mp_limb_t const twobody[] = { 2 };
+ static mpz_t const two = MPZ_ROINIT_N ((mp_limb_t *) twobody, 1);
+ ASSERT (mpz_fits_slong_p (two));
+ ASSERT (mpz_get_si (two) == 2);
+
+ mpz_t four;
+ mpz_init (four);
+ mpz_add (four, two, two);
+ ASSERT (mpz_fits_slong_p (four));
+ ASSERT (mpz_get_si (four) == 4);
+ mpz_clear (four);
+
+ return 0;
+}
diff --git a/tests/test-lock.c b/tests/test-lock.c
index 7325c89..08419b4 100644
--- a/tests/test-lock.c
+++ b/tests/test-lock.c
@@ -50,28 +50,6 @@
Uncomment this to see if the operating system has a fair scheduler. */
#define EXPLICIT_YIELD 1
-/* Whether to use 'volatile' on some variables that communicate information
- between threads. If set to 0, a semaphore or a lock is used to protect
- these variables. If set to 1, 'volatile' is used; this is theoretically
- equivalent but can lead to much slower execution (e.g. 30x slower total
- run time on a 40-core machine), because 'volatile' does not imply any
- synchronization/communication between different CPUs. */
-#define USE_VOLATILE 0
-
-#if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
-/* Whether to use a semaphore to communicate information between threads.
- If set to 0, a lock is used. If set to 1, a semaphore is used.
- Uncomment this to reduce the dependencies of this test. */
-# define USE_SEMAPHORE 1
-/* Mac OS X provides only named semaphores (sem_open); its facility for
- unnamed semaphores (sem_init) does not work. */
-# if defined __APPLE__ && defined __MACH__
-# define USE_NAMED_SEMAPHORE 1
-# else
-# define USE_UNNAMED_SEMAPHORE 1
-# endif
-#endif
-
/* Whether to print debugging messages. */
#define ENABLE_DEBUGGING 0
@@ -113,18 +91,14 @@
#include "glthread/thread.h"
#include "glthread/yield.h"
-#if USE_SEMAPHORE
-# include <errno.h>
-# include <fcntl.h>
-# include <semaphore.h>
-# include <unistd.h>
-#endif
#if HAVE_DECL_ALARM
# include <signal.h>
# include <unistd.h>
#endif
+#include "atomic-int-gnulib.h"
+
#if ENABLE_DEBUGGING
# define dbgprintf printf
#else
@@ -137,132 +111,6 @@
# define yield()
#endif
-#if USE_VOLATILE
-struct atomic_int {
- volatile int value;
-};
-static void
-init_atomic_int (struct atomic_int *ai)
-{
-}
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- return ai->value;
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- ai->value = new_value;
-}
-#elif USE_SEMAPHORE
-/* This atomic_int implementation can only support the values 0 and 1.
- It is initially 0 and can be set to 1 only once. */
-# if USE_UNNAMED_SEMAPHORE
-struct atomic_int {
- sem_t semaphore;
-};
-#define atomic_int_semaphore(ai) (&(ai)->semaphore)
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- sem_init (&ai->semaphore, 0, 0);
-}
-# endif
-# if USE_NAMED_SEMAPHORE
-struct atomic_int {
- sem_t *semaphore;
-};
-#define atomic_int_semaphore(ai) ((ai)->semaphore)
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- sem_t *s;
- unsigned int count;
- for (count = 0; ; count++)
- {
- char name[80];
- /* Use getpid() in the name, so that different processes running at the
- same time will not interfere. Use ai in the name, so that different
- atomic_int in the same process will not interfere. Use a count in
- the name, so that even in the (unlikely) case that a semaphore with
- the specified name already exists, we can try a different name. */
- sprintf (name, "test-lock-%lu-%p-%u",
- (unsigned long) getpid (), ai, count);
- s = sem_open (name, O_CREAT | O_EXCL, 0600, 0);
- if (s == SEM_FAILED)
- {
- if (errno == EEXIST)
- /* Retry with a different name. */
- continue;
- else
- {
- perror ("sem_open failed");
- abort ();
- }
- }
- else
- {
- /* Try not to leave a semaphore hanging around on the file system
- eternally, if we can avoid it. */
- sem_unlink (name);
- break;
- }
- }
- ai->semaphore = s;
-}
-# endif
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- if (sem_trywait (atomic_int_semaphore (ai)) == 0)
- {
- if (sem_post (atomic_int_semaphore (ai)))
- abort ();
- return 1;
- }
- else if (errno == EAGAIN)
- return 0;
- else
- abort ();
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- if (new_value == 0)
- /* It's already initialized with 0. */
- return;
- /* To set the value 1: */
- if (sem_post (atomic_int_semaphore (ai)))
- abort ();
-}
-#else
-struct atomic_int {
- gl_lock_define (, lock)
- int value;
-};
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- gl_lock_init (ai->lock);
-}
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- gl_lock_lock (ai->lock);
- int ret = ai->value;
- gl_lock_unlock (ai->lock);
- return ret;
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- gl_lock_lock (ai->lock);
- ai->value = new_value;
- gl_lock_unlock (ai->lock);
-}
-#endif
-
#define ACCOUNT_COUNT 4
static int account[ACCOUNT_COUNT];
diff --git a/tests/test-memcasecmp.c b/tests/test-memcasecmp.c
index 00df2f7..33507dc 100644
--- a/tests/test-memcasecmp.c
+++ b/tests/test-memcasecmp.c
@@ -28,7 +28,10 @@ int
main (void)
{
/* Test equal / not equal distinction. */
- ASSERT (memcasecmp (zerosize_ptr (), zerosize_ptr (), 0) == 0);
+ void *page_boundary1 = zerosize_ptr ();
+ void *page_boundary2 = zerosize_ptr ();
+ if (page_boundary1 && page_boundary2)
+ ASSERT (memcasecmp (page_boundary1, page_boundary2, 0) == 0);
ASSERT (memcasecmp ("foo", "foobar", 2) == 0);
ASSERT (memcasecmp ("foo", "foobar", 3) == 0);
ASSERT (memcasecmp ("foo", "foobar", 4) != 0);
diff --git a/tests/test-memchr.c b/tests/test-memchr.c
index ab8d2c0..5ea4c03 100644
--- a/tests/test-memchr.c
+++ b/tests/test-memchr.c
@@ -49,7 +49,12 @@ main (void)
ASSERT (MEMCHR (input, 'a', n) == input);
ASSERT (MEMCHR (input, 'a', 0) == NULL);
- ASSERT (MEMCHR (zerosize_ptr (), 'a', 0) == NULL);
+
+ {
+ void *page_boundary = zerosize_ptr ();
+ if (page_boundary)
+ ASSERT (MEMCHR (page_boundary, 'a', 0) == NULL);
+ }
ASSERT (MEMCHR (input, 'b', n) == input + 1);
ASSERT (MEMCHR (input, 'c', n) == input + 2);
diff --git a/tests/test-memchr2.c b/tests/test-memchr2.c
index 47428f2..8d54789 100644
--- a/tests/test-memchr2.c
+++ b/tests/test-memchr2.c
@@ -48,7 +48,9 @@ main (void)
ASSERT (MEMCHR2 (input, 'b', 'a', n) == input);
ASSERT (MEMCHR2 (input, 'a', 'b', 0) == NULL);
- ASSERT (MEMCHR2 (zerosize_ptr (), 'a', 'b', 0) == NULL);
+ void *page_boundary = zerosize_ptr ();
+ if (page_boundary)
+ ASSERT (MEMCHR2 (page_boundary, 'a', 'b', 0) == NULL);
ASSERT (MEMCHR2 (input, 'b', 'd', n) == input + 1);
ASSERT (MEMCHR2 (input + 2, 'b', 'd', n - 2) == input + 1026);
diff --git a/tests/test-memcmp.c b/tests/test-memcmp.c
index eaaac45..5b262aa 100644
--- a/tests/test-memcmp.c
+++ b/tests/test-memcmp.c
@@ -31,7 +31,10 @@ main (void)
int (* volatile memcmp_ptr) (const void *, const void *, size_t) = memcmp;
/* Test equal / not equal distinction. */
- ASSERT (memcmp (zerosize_ptr (), zerosize_ptr (), 0) == 0);
+ void *page_boundary1 = zerosize_ptr ();
+ void *page_boundary2 = zerosize_ptr ();
+ if (page_boundary1 && page_boundary2)
+ ASSERT (memcmp (page_boundary1, page_boundary2, 0) == 0);
ASSERT (memcmp ("foo", "foobar", 2) == 0);
ASSERT (memcmp ("foo", "foobar", 3) == 0);
ASSERT (memcmp ("foo", "foobar", 4) != 0);
diff --git a/tests/test-memmem.c b/tests/test-memmem.c
index 1c1e0ad..132265f 100644
--- a/tests/test-memmem.c
+++ b/tests/test-memmem.c
@@ -73,16 +73,20 @@ main (int argc, char *argv[])
}
/* Check that length 0 does not dereference the pointer. */
- {
- const char *result = memmem (zerosize_ptr (), 0, "foo", 3);
- ASSERT (result == NULL);
- }
+ void *page_boundary = zerosize_ptr ();
+ if (page_boundary)
+ {
+ {
+ const char *result = memmem (page_boundary, 0, "foo", 3);
+ ASSERT (result == NULL);
+ }
- {
- const char input[] = "foo";
- const char *result = memmem (input, strlen (input), zerosize_ptr (), 0);
- ASSERT (result == input);
- }
+ {
+ const char input[] = "foo";
+ const char *result = memmem (input, strlen (input), page_boundary, 0);
+ ASSERT (result == input);
+ }
+ }
/* Check that a long periodic needle does not cause false positives. */
{
diff --git a/tests/test-memrchr.c b/tests/test-memrchr.c
index 001134b..235fb69 100644
--- a/tests/test-memrchr.c
+++ b/tests/test-memrchr.c
@@ -49,7 +49,9 @@ main (void)
ASSERT (MEMRCHR (input, 'a', n) == input + n - 1);
ASSERT (MEMRCHR (input, 'a', 0) == NULL);
- ASSERT (MEMRCHR (zerosize_ptr (), 'a', 0) == NULL);
+ void *page_boundary = zerosize_ptr ();
+ if (page_boundary)
+ ASSERT (MEMRCHR (page_boundary, 'a', 0) == NULL);
ASSERT (MEMRCHR (input, 'b', n) == input + n - 2);
ASSERT (MEMRCHR (input, 'c', n) == input + n - 3);
diff --git a/tests/test-mtx.c b/tests/test-mtx.c
index cbd6fd4..54c4187 100644
--- a/tests/test-mtx.c
+++ b/tests/test-mtx.c
@@ -34,28 +34,6 @@
Uncomment this to see if the operating system has a fair scheduler. */
#define EXPLICIT_YIELD 1
-/* Whether to use 'volatile' on some variables that communicate information
- between threads. If set to 0, a semaphore or a lock is used to protect
- these variables. If set to 1, 'volatile' is used; this is theoretically
- equivalent but can lead to much slower execution (e.g. 30x slower total
- run time on a 40-core machine), because 'volatile' does not imply any
- synchronization/communication between different CPUs. */
-#define USE_VOLATILE 0
-
-#if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
-/* Whether to use a semaphore to communicate information between threads.
- If set to 0, a lock is used. If set to 1, a semaphore is used.
- Uncomment this to reduce the dependencies of this test. */
-# define USE_SEMAPHORE 1
-/* Mac OS X provides only named semaphores (sem_open); its facility for
- unnamed semaphores (sem_init) does not work. */
-# if defined __APPLE__ && defined __MACH__
-# define USE_NAMED_SEMAPHORE 1
-# else
-# define USE_UNNAMED_SEMAPHORE 1
-# endif
-#endif
-
/* Whether to print debugging messages. */
#define ENABLE_DEBUGGING 0
@@ -75,19 +53,13 @@
#include "glthread/lock.h"
-#if USE_SEMAPHORE
-# include <errno.h>
-# include <fcntl.h>
-# include <semaphore.h>
-# include <unistd.h>
-#endif
-
#if HAVE_DECL_ALARM
# include <signal.h>
# include <unistd.h>
#endif
#include "macros.h"
+#include "atomic-int-isoc.h"
#if ENABLE_DEBUGGING
# define dbgprintf printf
@@ -101,132 +73,6 @@
# define yield()
#endif
-#if USE_VOLATILE
-struct atomic_int {
- volatile int value;
-};
-static void
-init_atomic_int (struct atomic_int *ai)
-{
-}
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- return ai->value;
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- ai->value = new_value;
-}
-#elif USE_SEMAPHORE
-/* This atomic_int implementation can only support the values 0 and 1.
- It is initially 0 and can be set to 1 only once. */
-# if USE_UNNAMED_SEMAPHORE
-struct atomic_int {
- sem_t semaphore;
-};
-#define atomic_int_semaphore(ai) (&(ai)->semaphore)
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- sem_init (&ai->semaphore, 0, 0);
-}
-# endif
-# if USE_NAMED_SEMAPHORE
-struct atomic_int {
- sem_t *semaphore;
-};
-#define atomic_int_semaphore(ai) ((ai)->semaphore)
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- sem_t *s;
- unsigned int count;
- for (count = 0; ; count++)
- {
- char name[80];
- /* Use getpid() in the name, so that different processes running at the
- same time will not interfere. Use ai in the name, so that different
- atomic_int in the same process will not interfere. Use a count in
- the name, so that even in the (unlikely) case that a semaphore with
- the specified name already exists, we can try a different name. */
- sprintf (name, "test-lock-%lu-%p-%u",
- (unsigned long) getpid (), ai, count);
- s = sem_open (name, O_CREAT | O_EXCL, 0600, 0);
- if (s == SEM_FAILED)
- {
- if (errno == EEXIST)
- /* Retry with a different name. */
- continue;
- else
- {
- perror ("sem_open failed");
- abort ();
- }
- }
- else
- {
- /* Try not to leave a semaphore hanging around on the file system
- eternally, if we can avoid it. */
- sem_unlink (name);
- break;
- }
- }
- ai->semaphore = s;
-}
-# endif
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- if (sem_trywait (atomic_int_semaphore (ai)) == 0)
- {
- if (sem_post (atomic_int_semaphore (ai)))
- abort ();
- return 1;
- }
- else if (errno == EAGAIN)
- return 0;
- else
- abort ();
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- if (new_value == 0)
- /* It's already initialized with 0. */
- return;
- /* To set the value 1: */
- if (sem_post (atomic_int_semaphore (ai)))
- abort ();
-}
-#else
-struct atomic_int {
- mtx_t lock;
- int value;
-};
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- ASSERT (mtx_init (&ai->lock, mtx_plain) == thrd_success);
-}
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- ASSERT (mtx_lock (&ai->lock) == thrd_success);
- int ret = ai->value;
- ASSERT (mtx_unlock (&ai->lock) == thrd_success);
- return ret;
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- ASSERT (mtx_lock (&ai->lock) == thrd_success);
- ai->value = new_value;
- ASSERT (mtx_unlock (&ai->lock) == thrd_success);
-}
-#endif
-
/* Returns a reference to the current thread as a pointer, for debugging. */
#if defined __MVS__
/* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
diff --git a/tests/test-pthread-mutex.c b/tests/test-pthread-mutex.c
index 55eeb88..b746e57 100644
--- a/tests/test-pthread-mutex.c
+++ b/tests/test-pthread-mutex.c
@@ -35,28 +35,6 @@
Uncomment this to see if the operating system has a fair scheduler. */
#define EXPLICIT_YIELD 1
-/* Whether to use 'volatile' on some variables that communicate information
- between threads. If set to 0, a semaphore or a lock is used to protect
- these variables. If set to 1, 'volatile' is used; this is theoretically
- equivalent but can lead to much slower execution (e.g. 30x slower total
- run time on a 40-core machine), because 'volatile' does not imply any
- synchronization/communication between different CPUs. */
-#define USE_VOLATILE 0
-
-#if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
-/* Whether to use a semaphore to communicate information between threads.
- If set to 0, a lock is used. If set to 1, a semaphore is used.
- Uncomment this to reduce the dependencies of this test. */
-# define USE_SEMAPHORE 1
-/* Mac OS X provides only named semaphores (sem_open); its facility for
- unnamed semaphores (sem_init) does not work. */
-# if defined __APPLE__ && defined __MACH__
-# define USE_NAMED_SEMAPHORE 1
-# else
-# define USE_UNNAMED_SEMAPHORE 1
-# endif
-#endif
-
/* Whether to print debugging messages. */
#define ENABLE_DEBUGGING 0
@@ -78,19 +56,13 @@
# include <sched.h>
#endif
-#if USE_SEMAPHORE
-# include <errno.h>
-# include <fcntl.h>
-# include <semaphore.h>
-# include <unistd.h>
-#endif
-
#if HAVE_DECL_ALARM
# include <signal.h>
# include <unistd.h>
#endif
#include "macros.h"
+#include "atomic-int-posix.h"
#if ENABLE_DEBUGGING
# define dbgprintf printf
@@ -104,137 +76,6 @@
# define yield()
#endif
-#if USE_VOLATILE
-struct atomic_int {
- volatile int value;
-};
-static void
-init_atomic_int (struct atomic_int *ai)
-{
-}
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- return ai->value;
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- ai->value = new_value;
-}
-#elif USE_SEMAPHORE
-/* This atomic_int implementation can only support the values 0 and 1.
- It is initially 0 and can be set to 1 only once. */
-# if USE_UNNAMED_SEMAPHORE
-struct atomic_int {
- sem_t semaphore;
-};
-#define atomic_int_semaphore(ai) (&(ai)->semaphore)
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- sem_init (&ai->semaphore, 0, 0);
-}
-# endif
-# if USE_NAMED_SEMAPHORE
-struct atomic_int {
- sem_t *semaphore;
-};
-#define atomic_int_semaphore(ai) ((ai)->semaphore)
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- sem_t *s;
- unsigned int count;
- for (count = 0; ; count++)
- {
- char name[80];
- /* Use getpid() in the name, so that different processes running at the
- same time will not interfere. Use ai in the name, so that different
- atomic_int in the same process will not interfere. Use a count in
- the name, so that even in the (unlikely) case that a semaphore with
- the specified name already exists, we can try a different name. */
- sprintf (name, "test-lock-%lu-%p-%u",
- (unsigned long) getpid (), ai, count);
- s = sem_open (name, O_CREAT | O_EXCL, 0600, 0);
- if (s == SEM_FAILED)
- {
- if (errno == EEXIST)
- /* Retry with a different name. */
- continue;
- else
- {
- perror ("sem_open failed");
- abort ();
- }
- }
- else
- {
- /* Try not to leave a semaphore hanging around on the file system
- eternally, if we can avoid it. */
- sem_unlink (name);
- break;
- }
- }
- ai->semaphore = s;
-}
-# endif
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- if (sem_trywait (atomic_int_semaphore (ai)) == 0)
- {
- if (sem_post (atomic_int_semaphore (ai)))
- abort ();
- return 1;
- }
- else if (errno == EAGAIN)
- return 0;
- else
- abort ();
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- if (new_value == 0)
- /* It's already initialized with 0. */
- return;
- /* To set the value 1: */
- if (sem_post (atomic_int_semaphore (ai)))
- abort ();
-}
-#else
-struct atomic_int {
- pthread_mutex_t lock;
- int value;
-};
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- pthread_mutexattr_t attr;
-
- ASSERT (pthread_mutexattr_init (&attr) == 0);
- ASSERT (pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL) == 0);
- ASSERT (pthread_mutex_init (&ai->lock, &attr) == 0);
- ASSERT (pthread_mutexattr_destroy (&attr) == 0);
-}
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- ASSERT (pthread_mutex_lock (&ai->lock) == 0);
- int ret = ai->value;
- ASSERT (pthread_mutex_unlock (&ai->lock) == 0);
- return ret;
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- ASSERT (pthread_mutex_lock (&ai->lock) == 0);
- ai->value = new_value;
- ASSERT (pthread_mutex_unlock (&ai->lock) == 0);
-}
-#endif
-
/* Returns a reference to the current thread as a pointer, for debugging. */
#if defined __MVS__
/* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
diff --git a/tests/test-pthread-rwlock.c b/tests/test-pthread-rwlock.c
index 9f15acb..7b8d013 100644
--- a/tests/test-pthread-rwlock.c
+++ b/tests/test-pthread-rwlock.c
@@ -29,28 +29,6 @@
Uncomment this to see if the operating system has a fair scheduler. */
#define EXPLICIT_YIELD 1
-/* Whether to use 'volatile' on some variables that communicate information
- between threads. If set to 0, a semaphore or a lock is used to protect
- these variables. If set to 1, 'volatile' is used; this is theoretically
- equivalent but can lead to much slower execution (e.g. 30x slower total
- run time on a 40-core machine), because 'volatile' does not imply any
- synchronization/communication between different CPUs. */
-#define USE_VOLATILE 0
-
-#if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
-/* Whether to use a semaphore to communicate information between threads.
- If set to 0, a lock is used. If set to 1, a semaphore is used.
- Uncomment this to reduce the dependencies of this test. */
-# define USE_SEMAPHORE 1
-/* Mac OS X provides only named semaphores (sem_open); its facility for
- unnamed semaphores (sem_init) does not work. */
-# if defined __APPLE__ && defined __MACH__
-# define USE_NAMED_SEMAPHORE 1
-# else
-# define USE_UNNAMED_SEMAPHORE 1
-# endif
-#endif
-
/* Whether to print debugging messages. */
#define ENABLE_DEBUGGING 0
@@ -72,19 +50,13 @@
# include <sched.h>
#endif
-#if USE_SEMAPHORE
-# include <errno.h>
-# include <fcntl.h>
-# include <semaphore.h>
-# include <unistd.h>
-#endif
-
#if HAVE_DECL_ALARM
# include <signal.h>
# include <unistd.h>
#endif
#include "macros.h"
+#include "atomic-int-posix.h"
#if ENABLE_DEBUGGING
# define dbgprintf printf
@@ -98,137 +70,6 @@
# define yield()
#endif
-#if USE_VOLATILE
-struct atomic_int {
- volatile int value;
-};
-static void
-init_atomic_int (struct atomic_int *ai)
-{
-}
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- return ai->value;
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- ai->value = new_value;
-}
-#elif USE_SEMAPHORE
-/* This atomic_int implementation can only support the values 0 and 1.
- It is initially 0 and can be set to 1 only once. */
-# if USE_UNNAMED_SEMAPHORE
-struct atomic_int {
- sem_t semaphore;
-};
-#define atomic_int_semaphore(ai) (&(ai)->semaphore)
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- sem_init (&ai->semaphore, 0, 0);
-}
-# endif
-# if USE_NAMED_SEMAPHORE
-struct atomic_int {
- sem_t *semaphore;
-};
-#define atomic_int_semaphore(ai) ((ai)->semaphore)
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- sem_t *s;
- unsigned int count;
- for (count = 0; ; count++)
- {
- char name[80];
- /* Use getpid() in the name, so that different processes running at the
- same time will not interfere. Use ai in the name, so that different
- atomic_int in the same process will not interfere. Use a count in
- the name, so that even in the (unlikely) case that a semaphore with
- the specified name already exists, we can try a different name. */
- sprintf (name, "test-lock-%lu-%p-%u",
- (unsigned long) getpid (), ai, count);
- s = sem_open (name, O_CREAT | O_EXCL, 0600, 0);
- if (s == SEM_FAILED)
- {
- if (errno == EEXIST)
- /* Retry with a different name. */
- continue;
- else
- {
- perror ("sem_open failed");
- abort ();
- }
- }
- else
- {
- /* Try not to leave a semaphore hanging around on the file system
- eternally, if we can avoid it. */
- sem_unlink (name);
- break;
- }
- }
- ai->semaphore = s;
-}
-# endif
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- if (sem_trywait (atomic_int_semaphore (ai)) == 0)
- {
- if (sem_post (atomic_int_semaphore (ai)))
- abort ();
- return 1;
- }
- else if (errno == EAGAIN)
- return 0;
- else
- abort ();
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- if (new_value == 0)
- /* It's already initialized with 0. */
- return;
- /* To set the value 1: */
- if (sem_post (atomic_int_semaphore (ai)))
- abort ();
-}
-#else
-struct atomic_int {
- pthread_mutex_t lock;
- int value;
-};
-static void
-init_atomic_int (struct atomic_int *ai)
-{
- pthread_mutexattr_t attr;
-
- ASSERT (pthread_mutexattr_init (&attr) == 0);
- ASSERT (pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL) == 0);
- ASSERT (pthread_mutex_init (&ai->lock, &attr) == 0);
- ASSERT (pthread_mutexattr_destroy (&attr) == 0);
-}
-static int
-get_atomic_int_value (struct atomic_int *ai)
-{
- ASSERT (pthread_mutex_lock (&ai->lock) == 0);
- int ret = ai->value;
- ASSERT (pthread_mutex_unlock (&ai->lock) == 0);
- return ret;
-}
-static void
-set_atomic_int_value (struct atomic_int *ai, int new_value)
-{
- ASSERT (pthread_mutex_lock (&ai->lock) == 0);
- ai->value = new_value;
- ASSERT (pthread_mutex_unlock (&ai->lock) == 0);
-}
-#endif
-
/* Returns a reference to the current thread as a pointer, for debugging. */
#if defined __MVS__
/* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
diff --git a/tests/test-pthread-spin.c b/tests/test-pthread-spin.c
new file mode 100644
index 0000000..aec8a96
--- a/dev/null
+++ b/tests/test-pthread-spin.c
@@ -0,0 +1,231 @@
+/* Test of POSIX spin locks.
+ Copyright (C) 2005, 2008-2020 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>, 2005. */
+
+#include <config.h>
+
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
+
+/* Whether to enable locking.
+ Uncomment this to get a test program without locking, to verify that
+ it crashes. */
+#define ENABLE_LOCKING 1
+
+/* Whether to help the scheduler through explicit sched_yield().
+ Uncomment this to see if the operating system has a fair scheduler. */
+#define EXPLICIT_YIELD 1
+
+/* Whether to print debugging messages. */
+#define ENABLE_DEBUGGING 0
+
+/* Number of simultaneous threads. */
+#define THREAD_COUNT 10
+
+/* Number of operations performed in each thread. */
+#define REPEAT_COUNT 50000
+
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if EXPLICIT_YIELD
+# include <sched.h>
+#endif
+
+#if HAVE_DECL_ALARM
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+#include "macros.h"
+#include "atomic-int-posix.h"
+
+#if ENABLE_DEBUGGING
+# define dbgprintf printf
+#else
+# define dbgprintf if (0) printf
+#endif
+
+#if EXPLICIT_YIELD
+# define yield() sched_yield ()
+#else
+# define yield()
+#endif
+
+/* Returns a reference to the current thread as a pointer, for debugging. */
+#if defined __MVS__
+ /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
+ The first three bytes of this field appear to uniquely identify a
+ pthread_t, though not necessarily representing a pointer. */
+# define pthread_self_pointer() (*((void **) pthread_self ().__))
+#else
+# define pthread_self_pointer() ((void *) (uintptr_t) pthread_self ())
+#endif
+
+#define ACCOUNT_COUNT 4
+
+static int account[ACCOUNT_COUNT];
+
+static int
+random_account (void)
+{
+ return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
+}
+
+static void
+check_accounts (void)
+{
+ int i, sum;
+
+ sum = 0;
+ for (i = 0; i < ACCOUNT_COUNT; i++)
+ sum += account[i];
+ if (sum != ACCOUNT_COUNT * 1000)
+ abort ();
+}
+
+
+/* ------------------- Test use like normal locks ------------------- */
+
+/* Test normal locks by having several bank accounts and several threads
+ which shuffle around money between the accounts and another thread
+ checking that all the money is still there. */
+
+static pthread_spinlock_t my_lock;
+
+static void *
+lock_mutator_thread (void *arg)
+{
+ int repeat;
+
+ for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
+ {
+ int i1, i2, value;
+
+ dbgprintf ("Mutator %p before lock\n", pthread_self_pointer ());
+ ASSERT (pthread_spin_lock (&my_lock) == 0);
+ dbgprintf ("Mutator %p after lock\n", pthread_self_pointer ());
+
+ i1 = random_account ();
+ i2 = random_account ();
+ value = ((unsigned int) rand () >> 3) % 10;
+ account[i1] += value;
+ account[i2] -= value;
+
+ dbgprintf ("Mutator %p before unlock\n", pthread_self_pointer ());
+ ASSERT (pthread_spin_unlock (&my_lock) == 0);
+ dbgprintf ("Mutator %p after unlock\n", pthread_self_pointer ());
+
+ dbgprintf ("Mutator %p before check lock\n", pthread_self_pointer ());
+ ASSERT (pthread_spin_lock (&my_lock) == 0);
+ check_accounts ();
+ ASSERT (pthread_spin_unlock (&my_lock) == 0);
+ dbgprintf ("Mutator %p after check unlock\n", pthread_self_pointer ());
+
+ yield ();
+ }
+
+ dbgprintf ("Mutator %p dying.\n", pthread_self_pointer ());
+ return NULL;
+}
+
+static struct atomic_int lock_checker_done;
+
+static void *
+lock_checker_thread (void *arg)
+{
+ while (get_atomic_int_value (&lock_checker_done) == 0)
+ {
+ dbgprintf ("Checker %p before check lock\n", pthread_self_pointer ());
+ ASSERT (pthread_spin_lock (&my_lock) == 0);
+ check_accounts ();
+ ASSERT (pthread_spin_unlock (&my_lock) == 0);
+ dbgprintf ("Checker %p after check unlock\n", pthread_self_pointer ());
+
+ yield ();
+ }
+
+ dbgprintf ("Checker %p dying.\n", pthread_self_pointer ());
+ return NULL;
+}
+
+static void
+test_pthread_spin (void)
+{
+ int i;
+ pthread_t checkerthread;
+ pthread_t threads[THREAD_COUNT];
+
+ /* Initialization. */
+ for (i = 0; i < ACCOUNT_COUNT; i++)
+ account[i] = 1000;
+ init_atomic_int (&lock_checker_done);
+ set_atomic_int_value (&lock_checker_done, 0);
+
+ /* Spawn the threads. */
+ ASSERT (pthread_create (&checkerthread, NULL, lock_checker_thread, NULL)
+ == 0);
+ for (i = 0; i < THREAD_COUNT; i++)
+ ASSERT (pthread_create (&threads[i], NULL, lock_mutator_thread, NULL) == 0);
+
+ /* Wait for the threads to terminate. */
+ for (i = 0; i < THREAD_COUNT; i++)
+ ASSERT (pthread_join (threads[i], NULL) == 0);
+ set_atomic_int_value (&lock_checker_done, 1);
+ ASSERT (pthread_join (checkerthread, NULL) == 0);
+ check_accounts ();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+int
+main ()
+{
+#if HAVE_DECL_ALARM
+ /* Declare failure if test takes too long, by using default abort
+ caused by SIGALRM. */
+ int alarm_value = 600;
+ signal (SIGALRM, SIG_DFL);
+ alarm (alarm_value);
+#endif
+
+ ASSERT (pthread_spin_init (&my_lock, 0) == 0);
+
+ printf ("Starting test_pthread_spin ..."); fflush (stdout);
+ test_pthread_spin ();
+ printf (" OK\n"); fflush (stdout);
+
+ return 0;
+}
+
+#else
+
+/* No multithreading available. */
+
+#include <stdio.h>
+
+int
+main ()
+{
+ fputs ("Skipping test: multithreading not enabled\n", stderr);
+ return 77;
+}
+
+#endif
diff --git a/tests/test-supersede-fopen.h b/tests/test-supersede-fopen.h
new file mode 100644
index 0000000..e230102
--- a/dev/null
+++ b/tests/test-supersede-fopen.h
@@ -0,0 +1,265 @@
+/* Tests for opening a file without destroying an old file with the same name.
+
+ Copyright (C) 2020 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, 2020. */
+
+static void
+test_fopen_supersede (bool supersede_if_exists, bool supersede_if_does_not_exist)
+{
+ char xtemplate[] = "gnulibtestXXXXXX";
+ char *dir = mkdtemp (xtemplate);
+ char *filename = file_name_concat (dir, "test.mo", NULL);
+ struct stat statbuf;
+
+ /* Test the case that the file does not yet exist. */
+ {
+ ASSERT (stat (filename, &statbuf) < 0);
+
+ struct supersede_final_action action;
+ FILE *fp =
+ fopen_supersede (filename, "wb",
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fp != NULL);
+ ASSERT (fwrite ("Hello world\n", 1, 12, fp) == 12 && fflush (fp) == 0);
+ if (supersede_if_does_not_exist)
+ ASSERT (stat (filename, &statbuf) < 0);
+ else
+ ASSERT (stat (filename, &statbuf) == 0);
+ ASSERT (fclose_supersede (fp, &action) == 0);
+
+ ASSERT (stat (filename, &statbuf) == 0);
+
+ size_t file_size;
+ char *file_contents = read_file (filename, RF_BINARY, &file_size);
+ ASSERT (file_size == 12);
+ ASSERT (memcmp (file_contents, "Hello world\n", 12) == 0);
+ }
+
+ /* Test the case that the file exists and is a regular file. */
+ {
+ ASSERT (stat (filename, &statbuf) == 0);
+ dev_t orig_dev = statbuf.st_dev;
+ ino_t orig_ino = statbuf.st_ino;
+
+ struct supersede_final_action action;
+ FILE *fp =
+ fopen_supersede (filename, "wb",
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fp != NULL);
+ ASSERT (fwrite ("Foobar\n", 1, 7, fp) == 7 && fflush (fp) == 0);
+ ASSERT (stat (filename, &statbuf) == 0);
+ {
+ size_t file_size;
+ char *file_contents = read_file (filename, RF_BINARY, &file_size);
+ if (supersede_if_exists)
+ {
+ ASSERT (file_size == 12);
+ ASSERT (memcmp (file_contents, "Hello world\n", 12) == 0);
+ }
+ else
+ {
+ ASSERT (file_size == 7);
+ ASSERT (memcmp (file_contents, "Foobar\n", 7) == 0);
+ }
+ }
+ ASSERT (fclose_supersede (fp, &action) == 0);
+
+ ASSERT (stat (filename, &statbuf) == 0);
+
+ size_t file_size;
+ char *file_contents = read_file (filename, RF_BINARY, &file_size);
+ ASSERT (file_size == 7);
+ ASSERT (memcmp (file_contents, "Foobar\n", 7) == 0);
+
+ if (supersede_if_exists)
+ {
+ /* Verify that the file now has a different inode number, on the same
+ device. */
+#if !(defined _WIN32 && !defined __CYGWIN__)
+ ASSERT (memcmp (&orig_dev, &statbuf.st_dev, sizeof (dev_t)) == 0);
+ ASSERT (memcmp (&orig_ino, &statbuf.st_ino, sizeof (ino_t)) != 0);
+#endif
+ }
+ }
+
+ /* Test the case that the file exists and is a character device. */
+ {
+ ASSERT (stat (DEV_NULL, &statbuf) == 0);
+
+ struct supersede_final_action action;
+ FILE *fp =
+ fopen_supersede (DEV_NULL, "wb",
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fp != NULL);
+ ASSERT (fwrite ("Foobar\n", 1, 7, fp) == 7 && fflush (fp) == 0);
+ ASSERT (stat (DEV_NULL, &statbuf) == 0);
+ ASSERT (fclose_supersede (fp, &action) == 0);
+
+ ASSERT (stat (DEV_NULL, &statbuf) == 0);
+ }
+
+ /* Test the case that the file is a symbolic link to an existing regular
+ file. */
+ {
+ const char *linkname = "link1";
+ unlink (linkname);
+ if (symlink (filename, linkname) >= 0)
+ {
+ ASSERT (stat (linkname, &statbuf) == 0);
+ dev_t orig_dev = statbuf.st_dev;
+ ino_t orig_ino = statbuf.st_ino;
+
+ struct supersede_final_action action;
+ FILE *fp =
+ fopen_supersede (linkname, "wb",
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fp != NULL);
+ ASSERT (fwrite ("New\n", 1, 4, fp) == 4 && fflush (fp) == 0);
+ ASSERT (stat (linkname, &statbuf) == 0);
+ {
+ size_t file_size;
+ char *file_contents = read_file (linkname, RF_BINARY, &file_size);
+ if (supersede_if_exists)
+ {
+ ASSERT (file_size == 7);
+ ASSERT (memcmp (file_contents, "Foobar\n", 7) == 0);
+ }
+ else
+ {
+ ASSERT (file_size == 4);
+ ASSERT (memcmp (file_contents, "New\n", 4) == 0);
+ }
+ }
+ ASSERT (fclose_supersede (fp, &action) == 0);
+
+ ASSERT (stat (linkname, &statbuf) == 0);
+
+ size_t file_size;
+ char *file_contents = read_file (linkname, RF_BINARY, &file_size);
+ ASSERT (file_size == 4);
+ ASSERT (memcmp (file_contents, "New\n", 4) == 0);
+
+ if (supersede_if_exists)
+ {
+ /* Verify that the file now has a different inode number, on the
+ same device. */
+#if !(defined _WIN32 && !defined __CYGWIN__)
+ ASSERT (memcmp (&orig_dev, &statbuf.st_dev, sizeof (dev_t)) == 0);
+ ASSERT (memcmp (&orig_ino, &statbuf.st_ino, sizeof (ino_t)) != 0);
+#endif
+ }
+
+ /* Clean up. */
+ unlink (linkname);
+ }
+ }
+
+ /* Test the case that the file is a symbolic link to an existing character
+ device. */
+ {
+ const char *linkname = "link2";
+ unlink (linkname);
+ if (symlink (DEV_NULL, linkname) >= 0)
+ {
+ ASSERT (stat (linkname, &statbuf) == 0);
+
+ struct supersede_final_action action;
+ FILE *fp =
+ fopen_supersede (linkname, "wb",
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fp != NULL);
+ ASSERT (fwrite ("New\n", 1, 4, fp) == 4 && fflush (fp) == 0);
+ ASSERT (stat (linkname, &statbuf) == 0);
+ ASSERT (fclose_supersede (fp, &action) == 0);
+
+ ASSERT (stat (linkname, &statbuf) == 0);
+
+ /* Clean up. */
+ unlink (linkname);
+ }
+ }
+
+ /* Clean up. */
+ unlink (filename);
+
+ /* Test the case that the file is a symbolic link to a nonexistent file in an
+ existing directory. */
+ {
+ const char *linkname = "link3";
+ unlink (linkname);
+ if (symlink (filename, linkname) >= 0)
+ {
+ ASSERT (stat (linkname, &statbuf) < 0);
+
+ struct supersede_final_action action;
+ FILE *fp =
+ fopen_supersede (linkname, "wb",
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fp != NULL);
+ ASSERT (fwrite ("Hello world\n", 1, 12, fp) == 12 && fflush (fp) == 0);
+ if (supersede_if_does_not_exist)
+ ASSERT (stat (linkname, &statbuf) < 0);
+ else
+ ASSERT (stat (linkname, &statbuf) == 0);
+ ASSERT (fclose_supersede (fp, &action) == 0);
+
+ ASSERT (stat (linkname, &statbuf) == 0);
+
+ size_t file_size;
+ char *file_contents = read_file (linkname, RF_BINARY, &file_size);
+ ASSERT (file_size == 12);
+ ASSERT (memcmp (file_contents, "Hello world\n", 12) == 0);
+
+ /* Clean up. */
+ unlink (linkname);
+ }
+ }
+
+ /* Test the case that the file is a symbolic link to a nonexistent file in a
+ nonexistent directory. */
+ {
+ const char *linkname = "link4";
+ unlink (linkname);
+ if (symlink ("/nonexistent/gnulibtest8237/24715863701440", linkname) >= 0)
+ {
+ ASSERT (stat (linkname, &statbuf) < 0);
+
+ struct supersede_final_action action;
+ FILE *fp =
+ fopen_supersede (linkname, "wb",
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fp == NULL);
+ ASSERT (errno == ENOENT);
+
+ ASSERT (stat (linkname, &statbuf) < 0);
+
+ /* Clean up. */
+ unlink (linkname);
+ }
+ }
+
+ /* Clean up. */
+ unlink (filename);
+ rmdir (dir);
+}
diff --git a/tests/test-supersede-open.h b/tests/test-supersede-open.h
new file mode 100644
index 0000000..f3b9b15
--- a/dev/null
+++ b/tests/test-supersede-open.h
@@ -0,0 +1,262 @@
+/* Tests for opening a file without destroying an old file with the same name.
+
+ Copyright (C) 2020 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, 2020. */
+
+static void
+test_open_supersede (bool supersede_if_exists, bool supersede_if_does_not_exist)
+{
+ char xtemplate[] = "gnulibtestXXXXXX";
+ char *dir = mkdtemp (xtemplate);
+ char *filename = file_name_concat (dir, "test.mo", NULL);
+ struct stat statbuf;
+
+ /* Test the case that the file does not yet exist. */
+ {
+ ASSERT (stat (filename, &statbuf) < 0);
+
+ struct supersede_final_action action;
+ int fd = open_supersede (filename, O_RDWR | O_TRUNC, 0666,
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fd >= 0);
+ ASSERT (write (fd, "Hello world\n", 12) == 12);
+ if (supersede_if_does_not_exist)
+ ASSERT (stat (filename, &statbuf) < 0);
+ else
+ ASSERT (stat (filename, &statbuf) == 0);
+ ASSERT (close_supersede (fd, &action) == 0);
+
+ ASSERT (stat (filename, &statbuf) == 0);
+
+ size_t file_size;
+ char *file_contents = read_file (filename, RF_BINARY, &file_size);
+ ASSERT (file_size == 12);
+ ASSERT (memcmp (file_contents, "Hello world\n", 12) == 0);
+ }
+
+ /* Test the case that the file exists and is a regular file. */
+ {
+ ASSERT (stat (filename, &statbuf) == 0);
+ dev_t orig_dev = statbuf.st_dev;
+ ino_t orig_ino = statbuf.st_ino;
+
+ struct supersede_final_action action;
+ int fd = open_supersede (filename, O_RDWR | O_TRUNC, 0666,
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fd >= 0);
+ ASSERT (write (fd, "Foobar\n", 7) == 7);
+ ASSERT (stat (filename, &statbuf) == 0);
+ {
+ size_t file_size;
+ char *file_contents = read_file (filename, RF_BINARY, &file_size);
+ if (supersede_if_exists)
+ {
+ ASSERT (file_size == 12);
+ ASSERT (memcmp (file_contents, "Hello world\n", 12) == 0);
+ }
+ else
+ {
+ ASSERT (file_size == 7);
+ ASSERT (memcmp (file_contents, "Foobar\n", 7) == 0);
+ }
+ }
+ ASSERT (close_supersede (fd, &action) == 0);
+
+ ASSERT (stat (filename, &statbuf) == 0);
+
+ size_t file_size;
+ char *file_contents = read_file (filename, RF_BINARY, &file_size);
+ ASSERT (file_size == 7);
+ ASSERT (memcmp (file_contents, "Foobar\n", 7) == 0);
+
+ if (supersede_if_exists)
+ {
+ /* Verify that the file now has a different inode number, on the same
+ device. */
+#if !(defined _WIN32 && !defined __CYGWIN__)
+ ASSERT (memcmp (&orig_dev, &statbuf.st_dev, sizeof (dev_t)) == 0);
+ ASSERT (memcmp (&orig_ino, &statbuf.st_ino, sizeof (ino_t)) != 0);
+#endif
+ }
+ }
+
+ /* Test the case that the file exists and is a character device. */
+ {
+ ASSERT (stat (DEV_NULL, &statbuf) == 0);
+
+ struct supersede_final_action action;
+ int fd = open_supersede (DEV_NULL, O_RDWR | O_TRUNC, 0666,
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fd >= 0);
+ ASSERT (write (fd, "Foobar\n", 7) == 7);
+ ASSERT (stat (DEV_NULL, &statbuf) == 0);
+ ASSERT (close_supersede (fd, &action) == 0);
+
+ ASSERT (stat (DEV_NULL, &statbuf) == 0);
+ }
+
+ /* Test the case that the file is a symbolic link to an existing regular
+ file. */
+ {
+ const char *linkname = "link1";
+ unlink (linkname);
+ if (symlink (filename, linkname) >= 0)
+ {
+ ASSERT (stat (linkname, &statbuf) == 0);
+ dev_t orig_dev = statbuf.st_dev;
+ ino_t orig_ino = statbuf.st_ino;
+
+ struct supersede_final_action action;
+ int fd =
+ open_supersede (linkname, O_RDWR | O_TRUNC, 0666,
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fd >= 0);
+ ASSERT (write (fd, "New\n", 4) == 4);
+ ASSERT (stat (linkname, &statbuf) == 0);
+ {
+ size_t file_size;
+ char *file_contents = read_file (linkname, RF_BINARY, &file_size);
+ if (supersede_if_exists)
+ {
+ ASSERT (file_size == 7);
+ ASSERT (memcmp (file_contents, "Foobar\n", 7) == 0);
+ }
+ else
+ {
+ ASSERT (file_size == 4);
+ ASSERT (memcmp (file_contents, "New\n", 4) == 0);
+ }
+ }
+ ASSERT (close_supersede (fd, &action) == 0);
+
+ ASSERT (stat (linkname, &statbuf) == 0);
+
+ size_t file_size;
+ char *file_contents = read_file (linkname, RF_BINARY, &file_size);
+ ASSERT (file_size == 4);
+ ASSERT (memcmp (file_contents, "New\n", 4) == 0);
+
+ if (supersede_if_exists)
+ {
+ /* Verify that the file now has a different inode number, on the
+ same device. */
+#if !(defined _WIN32 && !defined __CYGWIN__)
+ ASSERT (memcmp (&orig_dev, &statbuf.st_dev, sizeof (dev_t)) == 0);
+ ASSERT (memcmp (&orig_ino, &statbuf.st_ino, sizeof (ino_t)) != 0);
+#endif
+ }
+
+ /* Clean up. */
+ unlink (linkname);
+ }
+ }
+
+ /* Test the case that the file is a symbolic link to an existing character
+ device. */
+ {
+ const char *linkname = "link2";
+ unlink (linkname);
+ if (symlink (DEV_NULL, linkname) >= 0)
+ {
+ ASSERT (stat (linkname, &statbuf) == 0);
+
+ struct supersede_final_action action;
+ int fd =
+ open_supersede (linkname, O_RDWR | O_TRUNC, 0666,
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fd >= 0);
+ ASSERT (write (fd, "New\n", 4) == 4);
+ ASSERT (stat (linkname, &statbuf) == 0);
+ ASSERT (close_supersede (fd, &action) == 0);
+
+ ASSERT (stat (linkname, &statbuf) == 0);
+
+ /* Clean up. */
+ unlink (linkname);
+ }
+ }
+
+ /* Clean up. */
+ unlink (filename);
+
+ /* Test the case that the file is a symbolic link to a nonexistent file in an
+ existing directory. */
+ {
+ const char *linkname = "link3";
+ unlink (linkname);
+ if (symlink (filename, linkname) >= 0)
+ {
+ ASSERT (stat (linkname, &statbuf) < 0);
+
+ struct supersede_final_action action;
+ int fd =
+ open_supersede (linkname, O_RDWR | O_TRUNC, 0666,
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fd >= 0);
+ ASSERT (write (fd, "Hello world\n", 12) == 12);
+ if (supersede_if_does_not_exist)
+ ASSERT (stat (linkname, &statbuf) < 0);
+ else
+ ASSERT (stat (linkname, &statbuf) == 0);
+ ASSERT (close_supersede (fd, &action) == 0);
+
+ ASSERT (stat (linkname, &statbuf) == 0);
+
+ size_t file_size;
+ char *file_contents = read_file (linkname, RF_BINARY, &file_size);
+ ASSERT (file_size == 12);
+ ASSERT (memcmp (file_contents, "Hello world\n", 12) == 0);
+
+ /* Clean up. */
+ unlink (linkname);
+ }
+ }
+
+ /* Test the case that the file is a symbolic link to a nonexistent file in a
+ nonexistent directory. */
+ {
+ const char *linkname = "link4";
+ unlink (linkname);
+ if (symlink ("/nonexistent/gnulibtest8237/24715863701440", linkname) >= 0)
+ {
+ ASSERT (stat (linkname, &statbuf) < 0);
+
+ struct supersede_final_action action;
+ int fd =
+ open_supersede (linkname, O_RDWR | O_TRUNC, 0666,
+ supersede_if_exists, supersede_if_does_not_exist,
+ &action);
+ ASSERT (fd < 0);
+ ASSERT (errno == ENOENT);
+
+ ASSERT (stat (linkname, &statbuf) < 0);
+
+ /* Clean up. */
+ unlink (linkname);
+ }
+ }
+
+ /* Clean up. */
+ unlink (filename);
+ rmdir (dir);
+}
diff --git a/tests/test-supersede.c b/tests/test-supersede.c
new file mode 100644
index 0000000..843d891
--- a/dev/null
+++ b/tests/test-supersede.c
@@ -0,0 +1,63 @@
+/* Tests for opening a file without destroying an old file with the same name.
+
+ Copyright (C) 2020 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, 2020. */
+
+#include <config.h>
+
+/* Specification. */
+#include "supersede.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "filenamecat.h"
+#include "read-file.h"
+#include "macros.h"
+
+/* The name of the "always silent" device. */
+#if defined _WIN32 && ! defined __CYGWIN__
+/* Native Windows API. */
+# define DEV_NULL "NUL"
+#else
+/* Unix API. */
+# define DEV_NULL "/dev/null"
+#endif
+
+#include "test-supersede-open.h"
+#include "test-supersede-fopen.h"
+
+int
+main (void)
+{
+ test_open_supersede (false, false);
+ test_open_supersede (false, true);
+ test_open_supersede (true, false);
+ test_open_supersede (true, true);
+
+ test_fopen_supersede (false, false);
+ test_fopen_supersede (false, true);
+ test_fopen_supersede (true, false);
+ test_fopen_supersede (true, true);
+
+ return 0;
+}
diff --git a/tests/test-sys_stat-c++.cc b/tests/test-sys_stat-c++.cc
index 9b2e733..981199b 100644
--- a/tests/test-sys_stat-c++.cc
+++ b/tests/test-sys_stat-c++.cc
@@ -43,6 +43,10 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::futimens, int,
(int, struct timespec const[2]));
#endif
+#if GNULIB_TEST_GETUMASK
+SIGNATURE_CHECK (GNULIB_NAMESPACE::getumask, mode_t, (void));
+#endif
+
#if GNULIB_TEST_LCHMOD
SIGNATURE_CHECK (GNULIB_NAMESPACE::lchmod, int, (const char *, mode_t));
#endif
diff --git a/tests/test-unicodeio.c b/tests/test-unicodeio.c
new file mode 100644
index 0000000..be91a91
--- a/dev/null
+++ b/tests/test-unicodeio.c
@@ -0,0 +1,81 @@
+/* Tests for Unicode character output.
+
+ Copyright (C) 2020 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, 2020. */
+
+#include <config.h>
+
+/* Specification. */
+#include "unicodeio.h"
+
+#include <locale.h>
+#include <string.h>
+
+#include "macros.h"
+
+#define TEST_CODE 0x2022
+#define TEST_CODE_AS_UTF8 "\xe2\x80\xa2"
+#define TEST_CODE_AS_GB18030 "\x81\x36\xa6\x31"
+
+static char result[64];
+
+static long
+success_callback (const char *buf, size_t buflen, void *callback_arg)
+{
+ memcpy (result, buf, buflen);
+ result[buflen] = '\0';
+ return 42;
+}
+
+static long
+failure_callback (unsigned int code, const char *msg, void *callback_arg)
+{
+ ASSERT (code == TEST_CODE);
+ strcpy (result, ".");
+ return 55;
+}
+
+int
+main (int argc, char *argv[])
+{
+ /* configure should already have checked that the locale is supported. */
+ if (setlocale (LC_ALL, "") == NULL)
+ return 1;
+
+ switch (unicode_to_mb (TEST_CODE, success_callback, failure_callback, NULL))
+ {
+ case 42:
+ if (argc > 1)
+ switch (argv[1][0])
+ {
+ case '2':
+ ASSERT (strcmp (result, TEST_CODE_AS_UTF8) == 0);
+ break;
+ case '3':
+ ASSERT (strcmp (result, TEST_CODE_AS_GB18030) == 0);
+ break;
+ }
+ break;
+ case 55:
+ ASSERT (strcmp (result, ".") == 0);
+ break;
+ default:
+ ASSERT (0);
+ }
+
+ return 0;
+}
diff --git a/tests/test-unicodeio1.sh b/tests/test-unicodeio1.sh
new file mode 100755
index 0000000..5f5f1bd
--- a/dev/null
+++ b/tests/test-unicodeio1.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+# Test in the C locale.
+LC_ALL=C \
+${CHECKER} ./test-unicodeio${EXEEXT} 1
diff --git a/tests/test-unicodeio2.sh b/tests/test-unicodeio2.sh
new file mode 100755
index 0000000..f0ec8fb
--- a/dev/null
+++ b/tests/test-unicodeio2.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# Test whether a specific UTF-8 locale is installed.
+: ${LOCALE_FR_UTF8=fr_FR.UTF-8}
+if test $LOCALE_FR_UTF8 = none; then
+ if test -f /usr/bin/localedef; then
+ echo "Skipping test: no french Unicode locale is installed"
+ else
+ echo "Skipping test: no french Unicode locale is supported"
+ fi
+ exit 77
+fi
+
+LC_ALL=$LOCALE_FR_UTF8 \
+${CHECKER} ./test-unicodeio${EXEEXT} 2
diff --git a/tests/test-unicodeio3.sh b/tests/test-unicodeio3.sh
new file mode 100755
index 0000000..1544c96
--- a/dev/null
+++ b/tests/test-unicodeio3.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# Test whether a specific GB18030 locale is installed.
+: ${LOCALE_ZH_CN=zh_CN.GB18030}
+if test $LOCALE_ZH_CN = none; then
+ if test -f /usr/bin/localedef; then
+ echo "Skipping test: no transitional chinese locale is installed"
+ else
+ echo "Skipping test: no transitional chinese locale is supported"
+ fi
+ exit 77
+fi
+
+LC_ALL=$LOCALE_ZH_CN \
+${CHECKER} ./test-unicodeio${EXEEXT} 3
diff --git a/tests/unistr/test-chr.h b/tests/unistr/test-chr.h
index b67be48..f60610f 100644
--- a/tests/unistr/test-chr.h
+++ b/tests/unistr/test-chr.h
@@ -48,7 +48,9 @@ main (void)
ASSERT (U_CHR (input, length, 'a') == input);
ASSERT (U_CHR (input, 0, 'a') == NULL);
- ASSERT (U_CHR (zerosize_ptr (), 0, 'a') == NULL);
+ void *page_boundary = zerosize_ptr ();
+ if (page_boundary)
+ ASSERT (U_CHR (page_boundary, 0, 'a') == NULL);
ASSERT (U_CHR (input, length, 'b') == input + 1);
ASSERT (U_CHR (input, length, 'c') == input + 2);
diff --git a/tests/unistr/test-cmp.h b/tests/unistr/test-cmp.h
index 38a5223..207c59a 100644
--- a/tests/unistr/test-cmp.h
+++ b/tests/unistr/test-cmp.h
@@ -20,7 +20,10 @@ static void
test_cmp (void)
{
/* Test equal / not equal distinction. */
- ASSERT (U_CMP (zerosize_ptr (), zerosize_ptr (), 0) == 0);
+ void *page_boundary1 = zerosize_ptr ();
+ void *page_boundary2 = zerosize_ptr ();
+ if (page_boundary1 && page_boundary2)
+ ASSERT (U_CMP (page_boundary1, page_boundary2, 0) == 0);
{
static const UNIT input1[] = { 'f', 'o', 'o', 0 };
static const UNIT input2[] = { 'f', 'o', 'o', 'b', 'a', 'r', 0 };
diff --git a/top/maint.mk b/top/maint.mk
index 10a84c2..2a0d2b4 100644
--- a/top/maint.mk
+++ b/top/maint.mk
@@ -442,7 +442,7 @@ sc_prohibit_gnu_make_extensions:
$(SED) -ne '/Makefile/{s/\.in$$//;p;}' | \
while read m; do \
$(MAKE) -qp -f $$m .DUMMY-TARGET 2>/dev/null | \
- $(AWK) -v file=$$m -e '$($@_awk_)' || exit 1; \
+ $(AWK) -v file=$$m -e '$($@_awk_)' || exit 1; \
done; \
fi
@@ -915,7 +915,7 @@ sc_prohibit_always-defined_macros:
dummy /dev/null \
&& { printf '$(ME): define the above' \
' via some gnulib .h file\n' 1>&2; \
- exit 1; } \
+ exit 1; } \
|| :; \
fi
# ==================================================================

Return to:

Send suggestions and report system problems to the System administrator.