For GNU gettext, I need the equivalent of module 'getcwd-lgpl' with a 'wchar_t *' result. Since both the implementation and the unit test are similar to 'getcwd-lgpl', it makes sense to keep them together, in gnulib, instead of having 'getcwd-lgpl' in gnulib and 'wgetcwd-lgpl' in gettext.
Done through these patches: 2023-09-30 Bruno Haible <br...@clisp.org> wgetcwd-lgpl: Add tests. * tests/test-wgetcwd-lgpl.c: New file, based on tests/test-getcwd-lgpl.c. * modules/wgetcwd-lgpl-tests: New file, based on modules/getcwd-lgpl-tests. wgetcwd-lgpl: New module. * lib/wchar.in.h (wgetcwd): New declaration. * lib/wgetcwd-lgpl.c: New file, based on lib/getcwd-lgpl.c. * m4/wchar_h.m4 (gl_WCHAR_H_REQUIRE_DEFAULTS): Initialize GNULIB_WGETCWD. * modules/wchar (Makefile.am): Substitute GNULIB_WGETCWD. * modules/wgetcwd-lgpl: New file. 2023-09-30 Bruno Haible <br...@clisp.org> getcwd-lgpl: Tweaks. * lib/unistd.in.h (getcwd): Mention the module 'getcwd-lgpl'. * lib/getcwd-lgpl.c (rpl_getcwd): Minimize scope of local variables. * tests/test-getcwd-lgpl.c (main): Use GNU coding style.
>From c3dda22534b5d575f167d601ed1d446744008506 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sat, 30 Sep 2023 16:15:44 +0200 Subject: [PATCH 1/3] getcwd-lgpl: Tweaks. * lib/unistd.in.h (getcwd): Mention the module 'getcwd-lgpl'. * lib/getcwd-lgpl.c (rpl_getcwd): Minimize scope of local variables. * tests/test-getcwd-lgpl.c (main): Use GNU coding style. --- ChangeLog | 7 +++++++ lib/getcwd-lgpl.c | 6 +++--- lib/unistd.in.h | 8 ++++---- tests/test-getcwd-lgpl.c | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3cb1e28640..f8082c30a9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2023-09-30 Bruno Haible <br...@clisp.org> + + getcwd-lgpl: Tweaks. + * lib/unistd.in.h (getcwd): Mention the module 'getcwd-lgpl'. + * lib/getcwd-lgpl.c (rpl_getcwd): Minimize scope of local variables. + * tests/test-getcwd-lgpl.c (main): Use GNU coding style. + 2023-09-29 Bruno Haible <br...@clisp.org> Allow different --libtool options from multiple gnulib-tool invocations. diff --git a/lib/getcwd-lgpl.c b/lib/getcwd-lgpl.c index 8a5bde9947..da478e42e4 100644 --- a/lib/getcwd-lgpl.c +++ b/lib/getcwd-lgpl.c @@ -45,12 +45,12 @@ typedef int dummy; char * rpl_getcwd (char *buf, size_t size) { - char *ptr; char *result; /* Handle single size operations. */ if (buf) { + /* Check SIZE argument. */ if (!size) { errno = EINVAL; @@ -79,7 +79,7 @@ rpl_getcwd (char *buf, size_t size) { char tmp[4032]; size = sizeof tmp; - ptr = getcwd (tmp, size); + char *ptr = getcwd (tmp, size); if (ptr) { result = strdup (ptr); @@ -95,7 +95,7 @@ rpl_getcwd (char *buf, size_t size) do { size <<= 1; - ptr = realloc (buf, size); + char *ptr = realloc (buf, size); if (ptr == NULL) { free (buf); diff --git a/lib/unistd.in.h b/lib/unistd.in.h index ac9f50eb3e..96453c90fd 100644 --- a/lib/unistd.in.h +++ b/lib/unistd.in.h @@ -1118,10 +1118,10 @@ _GL_WARN_ON_USE (ftruncate, "ftruncate is unportable - " or SIZE was too small. See the POSIX:2008 specification <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html>. - Additionally, the gnulib module 'getcwd' guarantees the following GNU - extension: 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. */ + Additionally, the gnulib module 'getcwd' or 'getcwd-lgpl' guarantees the + following GNU extension: 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. */ # if @REPLACE_GETCWD@ # if !(defined __cplusplus && defined GNULIB_NAMESPACE) # define getcwd rpl_getcwd diff --git a/tests/test-getcwd-lgpl.c b/tests/test-getcwd-lgpl.c index 821244f3de..26b249c767 100644 --- a/tests/test-getcwd-lgpl.c +++ b/tests/test-getcwd-lgpl.c @@ -92,7 +92,7 @@ main (int argc, char **argv) /* Validate a POSIX requirement on size. */ errno = 0; - ASSERT (getcwd(pwd2, 0) == NULL); + ASSERT (getcwd (pwd2, 0) == NULL); ASSERT (errno == EINVAL); free (pwd1); -- 2.34.1
>From bc73de46f7134509bff08ad65c0a5f1700d55c71 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sat, 30 Sep 2023 16:20:14 +0200 Subject: [PATCH 2/3] wgetcwd-lgpl: New module. * lib/wchar.in.h (wgetcwd): New declaration. * lib/wgetcwd-lgpl.c: New file, based on lib/getcwd-lgpl.c. * m4/wchar_h.m4 (gl_WCHAR_H_REQUIRE_DEFAULTS): Initialize GNULIB_WGETCWD. * modules/wchar (Makefile.am): Substitute GNULIB_WGETCWD. * modules/wgetcwd-lgpl: New file. --- ChangeLog | 10 ++++ lib/wchar.in.h | 18 ++++++ lib/wgetcwd-lgpl.c | 134 +++++++++++++++++++++++++++++++++++++++++++ m4/wchar_h.m4 | 3 +- modules/wchar | 1 + modules/wgetcwd-lgpl | 25 ++++++++ 6 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 lib/wgetcwd-lgpl.c create mode 100644 modules/wgetcwd-lgpl diff --git a/ChangeLog b/ChangeLog index f8082c30a9..cde3af6205 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2023-09-30 Bruno Haible <br...@clisp.org> + + wgetcwd-lgpl: New module. + * lib/wchar.in.h (wgetcwd): New declaration. + * lib/wgetcwd-lgpl.c: New file, based on lib/getcwd-lgpl.c. + * m4/wchar_h.m4 (gl_WCHAR_H_REQUIRE_DEFAULTS): Initialize + GNULIB_WGETCWD. + * modules/wchar (Makefile.am): Substitute GNULIB_WGETCWD. + * modules/wgetcwd-lgpl: New file. + 2023-09-30 Bruno Haible <br...@clisp.org> getcwd-lgpl: Tweaks. diff --git a/lib/wchar.in.h b/lib/wchar.in.h index f1bbff6b94..f114bce3f1 100644 --- a/lib/wchar.in.h +++ b/lib/wchar.in.h @@ -1684,6 +1684,24 @@ _GL_WARN_ON_USE (wcsftime, "wcsftime is unportable - " #endif +#if @GNULIB_WGETCWD@ && (defined _WIN32 && !defined __CYGWIN__) +/* Gets the name of the current working directory. + (a) If BUF is non-NULL, it is assumed to have room for SIZE wide characters. + This function stores the working directory (NUL-terminated) in BUF and + returns BUF. + (b) If BUF is NULL, an array is allocated with 'malloc'. The array is SIZE + wide characters long, unless SIZE == 0, in which case it is as big as + necessary. + If the directory couldn't be determined or SIZE was too small, this function + returns NULL and sets errno. For a directory of length LEN, SIZE should be + >= LEN + 3 in case (a) or >= LEN + 1 in case (b). + Possible errno values include: + - ERANGE if SIZE is too small. + - ENOMEM if the memory could no be allocated. */ +_GL_FUNCDECL_SYS (wgetcwd, wchar_t *, (wchar_t *buf, size_t size)); +#endif + + #endif /* _@GUARD_PREFIX@_WCHAR_H */ #endif /* _@GUARD_PREFIX@_WCHAR_H */ #endif diff --git a/lib/wgetcwd-lgpl.c b/lib/wgetcwd-lgpl.c new file mode 100644 index 0000000000..922e0fb810 --- /dev/null +++ b/lib/wgetcwd-lgpl.c @@ -0,0 +1,134 @@ +/* Copyright (C) 2011-2023 Free Software Foundation, Inc. + This file is part of gnulib. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +#include <config.h> + +/* Specification */ +#include <wchar.h> + +#include <errno.h> +#include <stdlib.h> + +#if defined _WIN32 && !defined __CYGWIN__ + +wchar_t * +wgetcwd (wchar_t *buf, size_t size) +{ + wchar_t *result; + + /* Uses _wgetcwd. + Documentation: + <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getcwd-wgetcwd> + Note that for a directory consisting of LEN wide characters, the SIZE + argument to _wgetcwd needs to be >= LEN + 3, not only >= LEN + 1, with + some versions of the Microsoft runtime libraries. */ + + /* Handle single size operations. */ + if (buf) + { + /* Check SIZE argument. */ + if (!size) + { + errno = EINVAL; + return NULL; + } + /* Invoke _wgetcwd as-is. In this case, the caller does not expect + an ENOMEM error; therefore don't use temporary memory. */ + return _wgetcwd (buf, size); + } + + if (size) + { + /* Allocate room for two more wide characters, so that directory names + of length <= SIZE - 1 can be returned. */ + buf = malloc ((size + 2) * sizeof (wchar_t)); + if (!buf) + { + errno = ENOMEM; + return NULL; + } + result = _wgetcwd (buf, size + 2); + if (!result) + { + free (buf); + return NULL; + } + if (wcslen (result) >= size) + { + free (buf); + errno = ERANGE; + return NULL; + } + /* Shrink result before returning it. */ + wchar_t *shrinked_result = realloc (result, size * sizeof (wchar_t)); + if (shrinked_result != NULL) + result = shrinked_result; + return result; + } + + /* Flexible sizing requested. Avoid over-allocation for the common + case of a name that fits within a 4k page, minus some space for + local variables, to be sure we don't skip over a guard page. */ + { + wchar_t tmp[4032 / sizeof (wchar_t)]; + size = sizeof tmp / sizeof (wchar_t); + wchar_t *ptr = _wgetcwd (tmp, size); + if (ptr) + { + result = _wcsdup (ptr); + if (!result) + errno = ENOMEM; + return result; + } + if (errno != ERANGE) + return NULL; + } + + /* My what a large directory name we have. */ + do + { + size <<= 1; + wchar_t *ptr = realloc (buf, size * sizeof (wchar_t)); + if (ptr == NULL) + { + free (buf); + errno = ENOMEM; + return NULL; + } + buf = ptr; + result = _wgetcwd (buf, size); + } + while (!result && errno == ERANGE); + + if (!result) + free (buf); + else + { + /* Here result == buf. */ + /* Shrink result before returning it. */ + size_t actual_size = wcslen (result) + 1; + if (actual_size < size) + { + wchar_t *shrinked_result = + realloc (result, actual_size * sizeof (wchar_t)); + if (shrinked_result != NULL) + result = shrinked_result; + } + } + return result; +} + +#endif diff --git a/m4/wchar_h.m4 b/m4/wchar_h.m4 index 31f5b0794d..8d62293646 100644 --- a/m4/wchar_h.m4 +++ b/m4/wchar_h.m4 @@ -7,7 +7,7 @@ dnl Written by Eric Blake. -# wchar_h.m4 serial 61 +# wchar_h.m4 serial 62 AC_DEFUN_ONCE([gl_WCHAR_H], [ @@ -186,6 +186,7 @@ AC_DEFUN([gl_WCHAR_H_REQUIRE_DEFAULTS] gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_WCSTOK]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_WCSWIDTH]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_WCSFTIME]) + gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_WGETCWD]) dnl Support Microsoft deprecated alias function names by default. gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_WCSDUP], [1]) ]) diff --git a/modules/wchar b/modules/wchar index ebdd0ece61..393b2b1837 100644 --- a/modules/wchar +++ b/modules/wchar @@ -82,6 +82,7 @@ wchar.h: wchar.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) -e 's/@''GNULIB_WCSTOK''@/$(GNULIB_WCSTOK)/g' \ -e 's/@''GNULIB_WCSWIDTH''@/$(GNULIB_WCSWIDTH)/g' \ -e 's/@''GNULIB_WCSFTIME''@/$(GNULIB_WCSFTIME)/g' \ + -e 's/@''GNULIB_WGETCWD''@/$(GNULIB_WGETCWD)/g' \ -e 's/@''GNULIB_MDA_WCSDUP''@/$(GNULIB_MDA_WCSDUP)/g' \ -e 's/@''GNULIB_FREE_POSIX''@/$(GNULIB_FREE_POSIX)/g' \ < $(srcdir)/wchar.in.h > $@-t1 diff --git a/modules/wgetcwd-lgpl b/modules/wgetcwd-lgpl new file mode 100644 index 0000000000..ed33406737 --- /dev/null +++ b/modules/wgetcwd-lgpl @@ -0,0 +1,25 @@ +Description: +Native Windows specific wgetcwd function. Like _wgetcwd, but also ensure +that wgetcwd(NULL, 0) returns a buffer allocated by the malloc() function. + +Files: +lib/wgetcwd-lgpl.c + +Depends-on: +wchar +free-posix + +configure.ac: +gl_WCHAR_MODULE_INDICATOR([wgetcwd]) + +Makefile.am: +lib_SOURCES += wgetcwd-lgpl.c + +Include: +<wchar.h> + +License: +LGPLv2+ + +Maintainer: +all -- 2.34.1
>From cbc02d24a5e064f4699939cbb15cce1a448d444a Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sat, 30 Sep 2023 16:23:28 +0200 Subject: [PATCH 3/3] wgetcwd-lgpl: Add tests. * tests/test-wgetcwd-lgpl.c: New file, based on tests/test-getcwd-lgpl.c. * modules/wgetcwd-lgpl-tests: New file, based on modules/getcwd-lgpl-tests. --- ChangeLog | 6 +++ modules/wgetcwd-lgpl-tests | 13 +++++ tests/test-wgetcwd-lgpl.c | 98 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 modules/wgetcwd-lgpl-tests create mode 100644 tests/test-wgetcwd-lgpl.c diff --git a/ChangeLog b/ChangeLog index cde3af6205..a8b06d31c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2023-09-30 Bruno Haible <br...@clisp.org> + wgetcwd-lgpl: Add tests. + * tests/test-wgetcwd-lgpl.c: New file, based on + tests/test-getcwd-lgpl.c. + * modules/wgetcwd-lgpl-tests: New file, based on + modules/getcwd-lgpl-tests. + wgetcwd-lgpl: New module. * lib/wchar.in.h (wgetcwd): New declaration. * lib/wgetcwd-lgpl.c: New file, based on lib/getcwd-lgpl.c. diff --git a/modules/wgetcwd-lgpl-tests b/modules/wgetcwd-lgpl-tests new file mode 100644 index 0000000000..4893da1db2 --- /dev/null +++ b/modules/wgetcwd-lgpl-tests @@ -0,0 +1,13 @@ +Files: +tests/test-wgetcwd-lgpl.c +tests/signature.h +tests/macros.h + +Depends-on: + +configure.ac: + +Makefile.am: +TESTS += test-wgetcwd-lgpl +check_PROGRAMS += test-wgetcwd-lgpl +test_wgetcwd_lgpl_LDADD = $(LDADD) $(LIBINTL) diff --git a/tests/test-wgetcwd-lgpl.c b/tests/test-wgetcwd-lgpl.c new file mode 100644 index 0000000000..382111a65e --- /dev/null +++ b/tests/test-wgetcwd-lgpl.c @@ -0,0 +1,98 @@ +/* Test of wgetcwd() function. + Copyright (C) 2009-2023 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 <wchar.h> + +#include "signature.h" +#if defined _WIN32 && !defined __CYGWIN__ +SIGNATURE_CHECK (wgetcwd, wchar_t *, (wchar_t *, size_t)); +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include "macros.h" + +int +main () +{ +#if defined _WIN32 && !defined __CYGWIN__ + wchar_t *pwd1; + wchar_t *pwd2; + + pwd1 = wgetcwd (NULL, 0); + ASSERT (pwd1 && *pwd1); + + /* Make sure the result is usable. */ + ASSERT (_wchdir (pwd1) == 0); + ASSERT (_wchdir (L".//./.") == 0); + + /* Make sure that result is normalized. */ + pwd2 = wgetcwd (NULL, 0); + ASSERT (pwd2); + ASSERT (wcscmp (pwd1, pwd2) == 0); + free (pwd2); + { + size_t len = wcslen (pwd1); + ssize_t i = len - 10; + if (i < 1) + i = 1; + pwd2 = wgetcwd (NULL, len + 1); + ASSERT (pwd2); + free (pwd2); + pwd2 = malloc ((len + 3) * sizeof (wchar_t)); + for ( ; i <= len; i++) + { + wchar_t *tmp; + errno = 0; + ASSERT (wgetcwd (pwd2, i) == NULL); + ASSERT (errno == ERANGE); + /* Allow either glibc or BSD behavior, since POSIX allows both. */ + errno = 0; + tmp = wgetcwd (NULL, i); + if (tmp) + { + ASSERT (wcscmp (pwd1, tmp) == 0); + free (tmp); + } + else + { + ASSERT (errno == ERANGE); + } + } + /* The SIZE argument here needs to be len + 3, not len + 1. */ + ASSERT (wgetcwd (pwd2, len + 3) == pwd2); + pwd2[len] = L'/'; + pwd2[len + 1] = L'\0'; + } + ASSERT (wcsstr (pwd2, L"/./") == NULL); + ASSERT (wcsstr (pwd2, L"/../") == NULL); + ASSERT (wcsstr (pwd2 + 1 + (pwd2[1] == L'/'), L"//") == NULL); + + /* Validate a POSIX requirement on size. */ + errno = 0; + ASSERT (wgetcwd (pwd2, 0) == NULL); + ASSERT (errno == EINVAL); + + free (pwd1); + free (pwd2); +#endif + + return 0; +} -- 2.34.1