From b6ec120435a070a28eafe0b3e686ef2ac9b0f042 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Thu, 22 Feb 2024 20:26:02 -0500 Subject: [PATCH 1/7] DX_RUN_LOG: Work around DJGPP subshell bug. On DJGPP bash, running set -x in a subshell also affects the parent shell environment. Inadvertently turning on traces for the whole rest of the configure run is not desirable, so work around the problem by manually turning the traces back off when this happens. --- m4/base.m4 | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/m4/base.m4 b/m4/base.m4 index c14ba68..4dc0d14 100644 --- a/m4/base.m4 +++ b/m4/base.m4 @@ -1,10 +1,10 @@ -dnl Copyright © 2012, 2014, 2021, 2023 Nick Bowler -dnl -dnl Basic macros for dxcommon. -dnl -dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2. -dnl This is free software: you are free to do what the fuck you want to. -dnl There is NO WARRANTY, to the extent permitted by law. +# Copyright © 2012, 2014, 2021, 2023-2024 Nick Bowler +# +# Basic macros for dxcommon. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. AC_PREREQ([2.64]) @@ -68,14 +68,20 @@ dnl dnl This macro expands to the dxcommon base directory, as a quoted string. AC_DEFUN([DX_BASEDIR], [m4_ignore(DX_INIT())m4_defn([_DX_BASEDIR])]) -dnl DX_RUN_LOG(command) -dnl -dnl Run a command, logging all of the command, its output, and overall -dnl exit status to config.log. The expansion of this macro is a single -dnl complex shell command suitable for use in shell conditionals. -AC_DEFUN([DX_RUN_LOG], [{ (set -x; $1;) >&AS_MESSAGE_LOG_FD 2>&1 - dx_status=$?; AS_ECHO(["\$? = $dx_status"]) >&AS_MESSAGE_LOG_FD 2>&1 - test $dx_status = 0; }]) +# DX_RUN_LOG(command) +# +# Run a command, logging all of the command, its output, and overall +# exit status to config.log. The expansion of this macro is a single +# complex shell command suitable for use in shell conditionals. +AC_DEFUN([DX_RUN_LOG], +[{ _dx_shopt=$- +( set -x +$1 +) >&AS_MESSAGE_LOG_FD 2>&1 +_dx_status=$? +test x"$-" = x"$_dx_shopt" || set +x # work around DJGPP subshell bug +AS_ECHO(["\$? = $_dx_status"]) >&AS_MESSAGE_LOG_FD +test $_dx_status = 0; } 2>/dev/null]) # DX_PATCH_MACRO([macro-name], [regexp], [replacement]) # -- 2.43.2 From e2f2206552cd2153641c17908d938930fb15d3d4 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Wed, 28 Feb 2024 23:08:12 -0500 Subject: [PATCH 2/7] Add helper macro for libtool patches. Monkey patch libtool to workaround a problem discovered when using the VAX C compiler. It seems that read-only static data initialized with function pointers can lead to trouble with this compiler. It is obviously not the only compiler with a problem like this, since libtool already similar exceptions to avoid "const" on other systems, we just need to add another case. --- m4/lt-patches.m4 | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 m4/lt-patches.m4 diff --git a/m4/lt-patches.m4 b/m4/lt-patches.m4 new file mode 100644 index 0000000..735633f --- /dev/null +++ b/m4/lt-patches.m4 @@ -0,0 +1,36 @@ +# Copyright © 2024 Nick Bowler +# +# Monkey patches for libtool. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +# DX_PATCH_LIBTOOL +# +# Apply all the patches described below. Should be expanded before LT_INIT. +AC_DEFUN([DX_PATCH_LIBTOOL], +[AC_BEFORE([$0], [LT_INIT])dnl +m4_foreach_w([patch], m4_defn([_DX_LT_PATCHES]), + [m4_indir([_DX_LT_PATCH(]m4_defn([patch])[)])])]) + +m4_define([_DX_LT_PATCHES]) +m4_define([_DX_LT_PATCH], + [m4_define([_DX_LT_PATCH($1)], [$2])m4_define([_DX_LT_PATCHES], + m4_defn([_DX_LT_PATCHES])[ $1])]) + +# Avoid "const" on VAX C for libtool's dynamic symbol bits, like with OSF and +# Windows platforms, as there seem to be some problems with relocations of +# function pointer values in readonly memory. +# +# We patch it in ltmain.sh (via config.status) and in configure directly. +# This code is also duplicated in libltdl so further fixes might be needed +# for packages using bindled libltdl (not done here... yet?). +_DX_LT_PATCH([ltmain-sh-vax-c], + [DX_PATCH_MACRO([_LT_CONFIG], [^\( *\)[$]SED '[$]q' "[$]ltmain"], + [\1# Use this opportunity also to work around a VAX C bug in upstream libtool +\1$SED 's/^# *elif.*__osf__$/& || (defined vaxc \\&\\& defined VAXC)/' "$ltmain"])]) + +_DX_LT_PATCH([libtool-m4-vax-c], + [DX_PATCH_MACRO([_LT_CMD_GLOBAL_SYMBOLS], [^# *elif.*__osf__$], + [\& || (defined vaxc && defined VAXC)])]) -- 2.43.2 From f5df895ced92ac539cafd825efee98835d8bf0d3 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Wed, 3 Apr 2024 23:57:32 -0400 Subject: [PATCH 3/7] Avoid _Alignas/_Alignof redefinition on FreeBSD. It seems that FreeBSD provides _Alignof and _Alignas macros in various standard header files, including . Since the configure probes don't include any headers, these are missed, resulting in conflicting macro definitions. To fix this, all we really need to do is include some header file in the test program, so these system versions are visible when testing for _Alignas and _Alignof support. --- m4/align.m4 | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/m4/align.m4 b/m4/align.m4 index 03e18a8..945beff 100644 --- a/m4/align.m4 +++ b/m4/align.m4 @@ -8,10 +8,14 @@ # # Probe whether the C compiler understands _Alignof(T). # +# Some implementations (e.g., FreeBSD 9) define a usable _Alignof macro +# in (and other headers), so we can use that if available. +# # If not supported, _Alignof(T) is defined as a function-like macro which # uses offsetof to guess the alignment of T, which must be a type such that -# T x; is a valid declaration of x as an object of type T. It is therefore -# necessary that the caller includes before using _Alignof. +# T x; is a valid declaration of x as an object of type T. +# +# For these reasons, callers should #include before using _Alignof. # # We skip the test if Autoconf has previously determined that the C compiler # supports C11 or a newer standard, since the C11 test program checks this. @@ -22,7 +26,8 @@ AC_DEFUN([DX_C_ALIGNOF], [AS_CASE([${ac_cv_prog_cc_c11-no}/${ac_prog_cc_stdc-no}], [no/no|*/c89|*/c99], [AC_CACHE_CHECK([if $CC supports _Alignof], [dx_cv_have_alignof], - [AC_COMPUTE_INT([_dx_tmp], [_Alignof(char)], [@&t@], [_dx_tmp=0]) + [AC_COMPUTE_INT([_dx_tmp], [_Alignof(char)], [#include +], [_dx_tmp=0]) AS_CASE([$_dx_tmp], [1], [dx_cv_have_alignof=yes], [dx_cv_have_alignof=no])])], [dx_cv_have_alignof=yes]) AS_CASE([$dx_cv_have_alignof], [no], [AC_DEFINE([_Alignof(T)], @@ -33,6 +38,10 @@ AS_CASE([$dx_cv_have_alignof], [no], [AC_DEFINE([_Alignof(T)], # # Probe whether the C compiler understands _Alignas(X). # +# Some implementations (e.g., FreeBSD 9) define a usable _Alignas macro +# in (and other headers), so we can use that if available. For +# this reason, callers should #include before using _Alignas. +# # If not supported, but the compiler supports the GNU __attribute__ or # Microsoft __declspec methods to set alignment, _Alignas(X) is defined # as a function-like macro which expands to such syntax. These only @@ -55,7 +64,8 @@ in '_Alignas(X)' '__attribute__((__aligned__(X)))' '__declspec(align(X))' do AC_COMPUTE_INT([_dx_tmp], [sizeof (struct { char a; char ALIGNAS_TEST(4) b; }) >= 8], - [#define ALIGNAS_TEST(X) $_dx_alignas + [#include +#define ALIGNAS_TEST(X) $_dx_alignas ],[_dx_tmp=0]) AS_CASE([$_dx_tmp/$_dx_alignas], [1/_Alignas*], [dx_cv_have_alignas=yes; break], -- 2.43.2 From 419ef0274998c19b8c46b0f511bf44f49a514670 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Wed, 10 Apr 2024 20:57:36 -0400 Subject: [PATCH 4/7] DX_C_ALIGNAS: Work around bash-5 parsing bug. Apparently bash-5 and later versions fail to parse for loops with a newline before the 'in' keyword, if that loop itself is in a case. statement. Previous versions of bash have no such problem, nor does any other shell I've tried, but the workaround is simple enough. --- m4/align.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/m4/align.m4 b/m4/align.m4 index 945beff..a69e640 100644 --- a/m4/align.m4 +++ b/m4/align.m4 @@ -59,7 +59,7 @@ AC_DEFUN([DX_C_ALIGNAS], [no/no|*/c89|*/c99], [AC_CACHE_CHECK([if $CC supports _Alignas], [dx_cv_have_alignas], [dx_cv_have_alignas=no -for _dx_alignas +for _dx_alignas dnl Eat newline to work around bash-5 parsing bug. in '_Alignas(X)' '__attribute__((__aligned__(X)))' '__declspec(align(X))' do AC_COMPUTE_INT([_dx_tmp], -- 2.43.2 From 9efff8c7a48509fabab57631f5c64c669183dee0 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Sun, 12 May 2024 14:16:40 -0400 Subject: [PATCH 5/7] Unset enable_silent_rules in testsuite. The Automake silent-rules feature allows the user to specify the default silent-rules behaviour at configure time via an environment variable. This changes the output of make rules, leading to failed tests if the testsuite is run with this set in the user's environment. --- testsuite.at | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/testsuite.at b/testsuite.at index 5ca87bf..66b7869 100644 --- a/testsuite.at +++ b/testsuite.at @@ -1,7 +1,6 @@ -AT_COPYRIGHT([dnl -Copyright © 2015, 2019-2023 Nick Bowler -License WTFPL2: Do What The Fuck You Want To Public License, version 2. -This is free software: you are free to do what the fuck you want to. +AT_COPYRIGHT([Copyright (C) 2015, 2019-2024 Nick Bowler +License GPLv3+: GNU General Public License version 3 or any later version. +This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.]) m4_include([snippet/at-compat.at]) @@ -12,6 +11,10 @@ AT_INIT AT_COLOR_TESTS m4_divert_push([PREPARE_TESTS])dnl +# Avoid inheriting the user's preference for Automake silent rules from the +# environment, as this changes make output leading to failed tests. +AS_UNSET([enable_silent_rules]) + # Reduce influence from the toplevel "make" invocation on test cases. AS_UNSET([MAKEFLAGS]) AS_UNSET([MAKELEVEL]) -- 2.43.2 From 27f6a9c3d4663286f1bec753fbd035ab1a6eb5de Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Sun, 12 May 2024 14:05:47 -0400 Subject: [PATCH 6/7] Import getline helper from cdecl99. This is pretty generally useful. The common function is altered from the original version to allow the caller to manage the error conditions, so not completely a drop in replacement but pretty close. --- Makefile.am | 15 +++--- m4/getline.m4 | 31 ++++++++++++ src/getline.h | 121 ++++++++++++++++++++++++++++++++++++++++++++ t/.gitignore | 1 + t/getline.c | 123 +++++++++++++++++++++++++++++++++++++++++++++ t/libdummy.c | 2 +- tests/functions.at | 11 ++-- tests/libs.at | 77 ++++++++++++++++++++++------ 8 files changed, 354 insertions(+), 27 deletions(-) create mode 100644 m4/getline.m4 create mode 100644 src/getline.h create mode 100644 t/getline.c diff --git a/Makefile.am b/Makefile.am index be73056..377ca02 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ -# Copyright © 2015, 2019, 2021-2023 Nick Bowler +# Copyright © 2015, 2019, 2021-2024 Nick Bowler # -# License WTFPL2: Do What The Fuck You Want To Public License, version 2. -# This is free software: you are free to do what the fuck you want to. +# License GPLv3+: GNU General Public License version 3 or any later version. +# This is free software: you are free to change and redistribute it. # There is NO WARRANTY, to the extent permitted by law. ACLOCAL_AMFLAGS = -I m4 @@ -11,9 +11,9 @@ AM_CPPFLAGS = -I$(top_srcdir)/src $(STUB_INCLUDES) EXTRA_DIST = scripts/fix-gnulib.pl scripts/fix-ltdl.pl \ scripts/bake-config.awk scripts/gen-options.awk \ scripts/gen-strtab.awk scripts/gen-tree.awk scripts/join.awk \ - scripts/pe-subsys.awk src/copysym.h src/help.h src/pack.h \ - src/tap.h t/tapcheck.sh t/getopt/getopt.h t/nls/gettext.h \ - t/nls/mbswidth.h tests/data/gnulib.mk + scripts/pe-subsys.awk src/copysym.h src/getline.h src/help.h \ + src/pack.h src/tap.h t/tapcheck.sh t/getopt/getopt.h \ + t/nls/gettext.h t/nls/mbswidth.h tests/data/gnulib.mk check_LIBRARIES = t/libdummy.a t/libempty.a @@ -60,6 +60,9 @@ libnlscopysym_a_SOURCES = src/copysym.c libnlscopysym_a_CPPFLAGS = $(AM_CPPFLAGS) -DENABLE_NLS libnlscopysym_a_SHORTNAME = nls +check_PROGRAMS += t/getline +t_getline_SOURCES = t/getline.c src/tap.c + DISTCLEANFILES = SUFFIXES = diff --git a/m4/getline.m4 b/m4/getline.m4 new file mode 100644 index 0000000..8356a62 --- /dev/null +++ b/m4/getline.m4 @@ -0,0 +1,31 @@ +# Copyright © 2024 Nick Bowler +# +# 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 . + +# DX_CHECK_GETLINE +# +# Check whether or not the getline function is available. If it is, the macro +# HAVE_GETLINE is defined to 1 and the cache variable dx_cv_have_getline is set +# to "yes". Otherwise, dx_cv_have_getline is set to "no". +AC_DEFUN([DX_CHECK_GETLINE], +[AC_CACHE_CHECK([for getline], [dx_cv_have_getline], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +], [ssize_t (*x)() = getline; +char *p = 0; +size_t n = 0; +return getline(&p, &n, stdin); +])], [dx_cv_have_getline=yes], [dx_cv_have_getline=no])]) +AS_CASE([$dx_cv_have_getline], [yes], + [AC_DEFINE([HAVE_GETLINE], [1], + [Define to 1 if the getline function is available.])])]) diff --git a/src/getline.h b/src/getline.h new file mode 100644 index 0000000..b0f4999 --- /dev/null +++ b/src/getline.h @@ -0,0 +1,121 @@ +/* + * Copyright © 2024 Nick Bowler + * + * getline-like function which removes trailing newline (if any). + * + * If HAVE_GETLINE is not defined (or defined to 0) then a standard C + * implementation is used. Othewrise, the POSIX getline function + * is called. + * + * 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 . + */ +#ifndef DX_GETLINE_H_ +#define DX_GETLINE_H_ + +#include +#include +#include +#include + +/* + * Size of the initial buffer allocated internally by the fallback + * implementation when *linebuf is NULL. + */ +#ifndef DX_GETLINE_INITIAL_ALLOC +# define DX_GETLINE_INITIAL_ALLOC 75 +#endif + +enum { + DX_GETLINE_OK = 1, + DX_GETLINE_EOF = 0, + DX_GETLINE_ERROR = -1, + DX_GETLINE_ENOMEM = -2 +}; + +/* + * Wrapper around getline with standard C fallback. + * + * Note that for portability to some getline implementations (e.g., FreeBSD) + * both *linebuf and *n should be set to zero on the initial call. + * + * If pre-allocating a buffer, ensure that its size is more than 1 byte, + * otherwise AIX 7.2 getline fails to work correctly. + * + * Returns 1 (DX_GETLINE_OK) if a line was read or 0 (DX_GETLINE_EOF) if + * no line could be read because the end of file was reached. + * + * On failure, returns a negative value. If the C library input call failed + * then the return value is DX_GETLINE_ERROR and the reason for the failure + * should be available in errno. + * + * For the standard C fallback only, a return value of DX_GETLINE_ENOMEM + * indicates that the buffer allocation could not be expanded to fit the + * input line. + */ +static inline int dx_getline(char **linebuf, size_t *n, FILE *f) +{ +#if HAVE_GETLINE + ssize_t rc; + + if ((rc = getline(linebuf, n, f)) < 0) { + if (ferror(f)) + return DX_GETLINE_ERROR; + return DX_GETLINE_EOF; + } + + if (rc-- && (*linebuf)[rc] == '\n') + (*linebuf)[rc] = '\0'; + + return DX_GETLINE_OK; +#else + char *work = *linebuf; + size_t pos = 0; + size_t sz; + + if (!work) { + sz = DX_GETLINE_INITIAL_ALLOC; + goto initial_alloc; + } + + for (sz = *n;;) { + if (!fgets(&work[pos], sz - pos, f)) { + if (ferror(f)) + return DX_GETLINE_ERROR; + + return pos ? DX_GETLINE_OK : DX_GETLINE_ERROR; + } + + pos += strlen(&work[pos]); + if (work[pos-1] == '\n') { + work[pos-1] = '\0'; + return DX_GETLINE_OK; + } + + if (sz > INT_MAX/2 || sz > ((size_t)-1)/4) + break; + + sz = ((sz*4) + 2) / 3; +initial_alloc: + work = realloc(work, sz); + if (!work) + break; + *linebuf = work; + *n = sz; + } + + return DX_GETLINE_ENOMEM; +#endif +} + +#endif diff --git a/t/.gitignore b/t/.gitignore index 39c659a..862ed4f 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -1,4 +1,5 @@ /copysym +/getline /helpdesc /helpopt /helpopt2 diff --git a/t/getline.c b/t/getline.c new file mode 100644 index 0000000..912e571 --- /dev/null +++ b/t/getline.c @@ -0,0 +1,123 @@ +/* + * Copyright © 2024 Nick Bowler + * + * 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 . + */ + +void tap_diag(const char *fmt, ...); + +#undef HAVE_GETLINE +#define DX_GETLINE_INITIAL_ALLOC 2 +#include "getline.h" +#include "tap.h" + +#include + +#define LONGLINE "this line is pretty long and may require scaling the buffer multiple times" + +static const char testdata[] = + "\n" + LONGLINE "\n" + LONGLINE "\n" + LONGLINE "\n" + "hellorld\n"; + +int main(void) +{ + char *line = NULL; + size_t long_size; + size_t n = 0; + FILE *f; + + if (!(f = tmpfile())) + tap_bail_out("tmpfile failed: %s\n", strerror(errno)); + + if (fwrite(testdata, sizeof testdata-1, 1, f) != 1) + tap_bail_out("fwrite failed: %s\n", strerror(errno)); + + if (fflush(f) != 0) + tap_bail_out("fflush failed: %s\n", strerror(errno)); + + if (fseek(f, 0, SEEK_SET) != 0) + tap_bail_out("fseek failed: %s\n", strerror(errno)); + + /* First line: empty */ + tap_result(dx_getline(&line, &n, f) != 0, "dx_getline"); + if (!tap_result(n == 2, "alloc size")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %lu", (unsigned long)n); + tap_diag(" Expected: 2"); + } + if (!tap_result(line[0] == 0, "returned string")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: \"%.*s\"", (int)n, line); + tap_diag(" Expected: \"\""); + } + + /* Next line: long line (buffer needs expanding) */ + tap_result(dx_getline(&line, &n, f) != 0, "dx_getline"); + if (!tap_result(n > sizeof LONGLINE, "alloc size")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %lu", (unsigned long)n); + tap_diag(" Expected: >%u", (unsigned)(sizeof LONGLINE)); + } + if (!tap_result(!strcmp(line, LONGLINE), "returned string")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: \"%.*s\"", (int)n, line); + tap_diag(" Expected: \"" LONGLINE "\""); + } + + /* Next line: long line (buffer does not need expanding) */ + long_size = n; + tap_result(dx_getline(&line, &n, f) != 0, "dx_getline"); + if (!tap_result(n == long_size, "alloc size")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %lu", (unsigned long)n); + tap_diag(" Expected: %lu", (unsigned long)long_size); + } + if (!tap_result(!strcmp(line, LONGLINE), "returned string")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: \"%.*s\"", (int)n, line); + tap_diag(" Expected: \"" LONGLINE "\""); + } + + /* Next line: long line (new buffer allocation) */ + free(line); line = NULL; n = 0; + tap_result(dx_getline(&line, &n, f) != 0, "dx_getline"); + if (!tap_result(n == long_size, "alloc size")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %lu", (unsigned long)n); + tap_diag(" Expected: %lu", (unsigned long)long_size); + } + if (!tap_result(!strcmp(line, LONGLINE), "returned string")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: \"%.*s\"", (int)n, line); + tap_diag(" Expected: \"" LONGLINE "\""); + } + + /* Next line: short line */ + tap_result(dx_getline(&line, &n, f) != 0, "dx_getline"); + if (!tap_result(n == long_size, "alloc size")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %lu", (unsigned long)n); + tap_diag(" Expected: %lu", (unsigned long)long_size); + } + if (!tap_result(!strcmp(line, "hellorld"), "returned string")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: \"%.*s\"", (int)n, line); + tap_diag(" Expected: \"hellorld\""); + } + + tap_done(); +} diff --git a/t/libdummy.c b/t/libdummy.c index 1bfa48a..e51fbc7 100644 --- a/t/libdummy.c +++ b/t/libdummy.c @@ -15,4 +15,4 @@ struct MEVENT; int getmouse_nc_(struct MEVENT *mev) { return 0; } int getmouse_bogus_(void *p) { return 0; } -void dx_closelog(void) { } +void dx_link_stub(void) { } diff --git a/tests/functions.at b/tests/functions.at index 01a96a7..bede78e 100644 --- a/tests/functions.at +++ b/tests/functions.at @@ -1,8 +1,8 @@ -dnl Copyright © 2015, 2021-2024 Nick Bowler -dnl -dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2. -dnl This is free software: you are free to do what the fuck you want to. -dnl There is NO WARRANTY, to the extent permitted by law. +# Copyright © 2015, 2021-2024 Nick Bowler +# +# License GPLv3+: GNU General Public License version 3 or any later version. +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. AT_BANNER([Binary packing functions]) @@ -175,3 +175,4 @@ AT_CLEANUP AT_BANNER([Miscellaneous functions]) TEST_TAP_SIMPLE([copyright_symbol], [copysym], [], []) +TEST_TAP_SIMPLE([do_getline], [getline], [], [getline]) diff --git a/tests/libs.at b/tests/libs.at index f7cf534..8a7be8d 100644 --- a/tests/libs.at +++ b/tests/libs.at @@ -1,17 +1,17 @@ -dnl Copyright © 2019-2020, 2022-2023 Nick Bowler -dnl -dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2. -dnl This is free software: you are free to do what the fuck you want to. -dnl There is NO WARRANTY, to the extent permitted by law. +# Copyright © 2019-2020, 2022-2024 Nick Bowler +# +# License GPLv3+: GNU General Public License version 3 or any later version. +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. AT_BANNER([Library tests]) -dnl TEST_DUMMY_PKGCONFIG([cflags], [libs]) -dnl -dnl Create a hack pkg-config script in the current working directory which -dnl responds to --cflags and --libs with the provided values. The macro -dnl arguments should each be a single shell word, suitable for the right -dnl hand side of a shell assignment. +# TEST_DUMMY_PKGCONFIG([cflags], [libs]) +# +# Create a hack pkg-config script in the current working directory which +# responds to --cflags and --libs with the provided values. The macro +# arguments should each be a single shell word, suitable for the right +# hand side of a shell assignment. m4_define([TEST_DUMMY_PKGCONFIG], [[cat >pkg-config < Date: Sat, 18 May 2024 16:14:36 -0400 Subject: [PATCH 7/7] dx_getline: Fix EOF handling in standard C fallback. The return value of the function at end of input was not tested so of course it does not work: in the standard C fallback, a mistake was introduced during the import and it returns DX_GETLINE_ERROR instead of the expected DX_GETLINE_EOF. Easy enough to fix and update the test program to check this. --- src/getline.h | 2 +- t/getline.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/getline.h b/src/getline.h index b0f4999..f659c60 100644 --- a/src/getline.h +++ b/src/getline.h @@ -93,7 +93,7 @@ static inline int dx_getline(char **linebuf, size_t *n, FILE *f) if (ferror(f)) return DX_GETLINE_ERROR; - return pos ? DX_GETLINE_OK : DX_GETLINE_ERROR; + return pos ? DX_GETLINE_OK : DX_GETLINE_EOF; } pos += strlen(&work[pos]); diff --git a/t/getline.c b/t/getline.c index 912e571..f7024ea 100644 --- a/t/getline.c +++ b/t/getline.c @@ -39,6 +39,7 @@ int main(void) size_t long_size; size_t n = 0; FILE *f; + int rc; if (!(f = tmpfile())) tap_bail_out("tmpfile failed: %s\n", strerror(errno)); @@ -53,7 +54,12 @@ int main(void) tap_bail_out("fseek failed: %s\n", strerror(errno)); /* First line: empty */ - tap_result(dx_getline(&line, &n, f) != 0, "dx_getline"); + rc = dx_getline(&line, &n, f); + if (!tap_result(rc == DX_GETLINE_OK, "dx_getline")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %d", rc); + tap_diag(" Expected: %d (DX_GETLINE_OK)", DX_GETLINE_OK); + } if (!tap_result(n == 2, "alloc size")) { tap_diag("Failed, unexpected result"); tap_diag(" Received: %lu", (unsigned long)n); @@ -66,7 +72,12 @@ int main(void) } /* Next line: long line (buffer needs expanding) */ - tap_result(dx_getline(&line, &n, f) != 0, "dx_getline"); + rc = dx_getline(&line, &n, f); + if (!tap_result(rc == DX_GETLINE_OK, "dx_getline")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %d", rc); + tap_diag(" Expected: %d (DX_GETLINE_OK)", DX_GETLINE_OK); + } if (!tap_result(n > sizeof LONGLINE, "alloc size")) { tap_diag("Failed, unexpected result"); tap_diag(" Received: %lu", (unsigned long)n); @@ -80,7 +91,12 @@ int main(void) /* Next line: long line (buffer does not need expanding) */ long_size = n; - tap_result(dx_getline(&line, &n, f) != 0, "dx_getline"); + rc = dx_getline(&line, &n, f); + if (!tap_result(rc == DX_GETLINE_OK, "dx_getline")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %d", rc); + tap_diag(" Expected: %d (DX_GETLINE_OK)", DX_GETLINE_OK); + } if (!tap_result(n == long_size, "alloc size")) { tap_diag("Failed, unexpected result"); tap_diag(" Received: %lu", (unsigned long)n); @@ -94,7 +110,12 @@ int main(void) /* Next line: long line (new buffer allocation) */ free(line); line = NULL; n = 0; - tap_result(dx_getline(&line, &n, f) != 0, "dx_getline"); + rc = dx_getline(&line, &n, f); + if (!tap_result(rc == DX_GETLINE_OK, "dx_getline")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %d", rc); + tap_diag(" Expected: %d (DX_GETLINE_OK)", DX_GETLINE_OK); + } if (!tap_result(n == long_size, "alloc size")) { tap_diag("Failed, unexpected result"); tap_diag(" Received: %lu", (unsigned long)n); @@ -107,7 +128,12 @@ int main(void) } /* Next line: short line */ - tap_result(dx_getline(&line, &n, f) != 0, "dx_getline"); + rc = dx_getline(&line, &n, f); + if (!tap_result(rc == DX_GETLINE_OK, "dx_getline")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %d", rc); + tap_diag(" Expected: %d (DX_GETLINE_OK)", DX_GETLINE_OK); + } if (!tap_result(n == long_size, "alloc size")) { tap_diag("Failed, unexpected result"); tap_diag(" Received: %lu", (unsigned long)n); @@ -119,5 +145,18 @@ int main(void) tap_diag(" Expected: \"hellorld\""); } + /* Next line: end of file */ + rc = dx_getline(&line, &n, f); + if (!tap_result(rc == DX_GETLINE_EOF, "dx_getline (end of file)")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %d", rc); + tap_diag(" Expected: %d (DX_GETLINE_EOF)", DX_GETLINE_EOF); + } + if (!tap_result(n == long_size, "alloc size")) { + tap_diag("Failed, unexpected result"); + tap_diag(" Received: %lu", (unsigned long)n); + tap_diag(" Expected: %lu", (unsigned long)long_size); + } + tap_done(); } -- 2.43.2