exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / wgetcwd-lgpl.c
blob16611776f31745a51f4cdacdc49cdf3a43dfb1b7
1 /* Copyright (C) 2011-2024 Free Software Foundation, Inc.
2 This file is part of gnulib.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 #include <config.h>
19 /* Specification */
20 #include <wchar.h>
22 #include <errno.h>
23 #include <stdlib.h>
25 #if defined _WIN32 && !defined __CYGWIN__
27 wchar_t *
28 wgetcwd (wchar_t *buf, size_t size)
30 wchar_t *result;
32 /* Uses _wgetcwd.
33 Documentation:
34 <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getcwd-wgetcwd>
35 Note that for a directory consisting of LEN wide characters, the SIZE
36 argument to _wgetcwd needs to be >= LEN + 3, not only >= LEN + 1, with
37 some versions of the Microsoft runtime libraries. */
39 /* Handle single size operations. */
40 if (buf)
42 /* Check SIZE argument. */
43 if (!size)
45 errno = EINVAL;
46 return NULL;
48 /* Invoke _wgetcwd as-is. In this case, the caller does not expect
49 an ENOMEM error; therefore don't use temporary memory. */
50 return _wgetcwd (buf, size);
53 if (size)
55 /* Allocate room for two more wide characters, so that directory names
56 of length <= SIZE - 1 can be returned. */
57 buf = malloc ((size + 2) * sizeof (wchar_t));
58 if (!buf)
60 errno = ENOMEM;
61 return NULL;
63 result = _wgetcwd (buf, size + 2);
64 if (!result)
66 free (buf);
67 return NULL;
69 if (wcslen (result) >= size)
71 free (buf);
72 errno = ERANGE;
73 return NULL;
75 /* Shrink result before returning it. */
76 wchar_t *shrinked_result = realloc (result, size * sizeof (wchar_t));
77 if (shrinked_result != NULL)
78 result = shrinked_result;
79 return result;
82 /* Flexible sizing requested. Avoid over-allocation for the common
83 case of a name that fits within a 4k page, minus some space for
84 local variables, to be sure we don't skip over a guard page. */
86 wchar_t tmp[4032 / sizeof (wchar_t)];
87 size = sizeof tmp / sizeof (wchar_t);
88 wchar_t *ptr = _wgetcwd (tmp, size);
89 if (ptr)
91 result = _wcsdup (ptr);
92 if (!result)
93 errno = ENOMEM;
94 return result;
96 if (errno != ERANGE)
97 return NULL;
100 /* My what a large directory name we have. */
103 size <<= 1;
104 wchar_t *ptr = realloc (buf, size * sizeof (wchar_t));
105 if (ptr == NULL)
107 free (buf);
108 errno = ENOMEM;
109 return NULL;
111 buf = ptr;
112 result = _wgetcwd (buf, size);
114 while (!result && errno == ERANGE);
116 if (!result)
117 free (buf);
118 else
120 /* Here result == buf. */
121 /* Shrink result before returning it. */
122 size_t actual_size = wcslen (result) + 1;
123 if (actual_size < size)
125 wchar_t *shrinked_result =
126 realloc (result, actual_size * sizeof (wchar_t));
127 if (shrinked_result != NULL)
128 result = shrinked_result;
131 return result;
134 #endif