|
16 | 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
17 | 17 | │ PERFORMANCE OF THIS SOFTWARE. │
|
18 | 18 | ╚─────────────────────────────────────────────────────────────────────────────*/
|
19 |
| -#include "libc/assert.h" |
20 | 19 | #include "libc/calls/calls.h"
|
21 |
| -#include "libc/calls/state.internal.h" |
| 20 | +#include "libc/calls/struct/metastat.internal.h" |
22 | 21 | #include "libc/calls/syscall-nt.internal.h"
|
23 | 22 | #include "libc/calls/syscall-sysv.internal.h"
|
24 | 23 | #include "libc/dce.h"
|
| 24 | +#include "libc/errno.h" |
25 | 25 | #include "libc/intrin/strace.internal.h"
|
26 |
| -#include "libc/intrin/weaken.h" |
27 | 26 | #include "libc/limits.h"
|
28 |
| -#include "libc/log/backtrace.internal.h" |
29 |
| -#include "libc/mem/mem.h" |
| 27 | +#include "libc/nt/files.h" |
| 28 | +#include "libc/stdio/sysparam.h" |
30 | 29 | #include "libc/str/str.h"
|
| 30 | +#include "libc/sysv/consts/at.h" |
| 31 | +#include "libc/sysv/consts/o.h" |
31 | 32 | #include "libc/sysv/errfuns.h"
|
32 | 33 |
|
33 |
| -/** |
34 |
| - * Returns current working directory, e.g. |
35 |
| - * |
36 |
| - * const char *dirname = gc(getcwd(0,0)); // if malloc is linked |
37 |
| - * const char *dirname = getcwd(alloca(PATH_MAX),PATH_MAX); |
38 |
| - * |
39 |
| - * @param buf is where UTF-8 NUL-terminated path string gets written, |
40 |
| - * which may be NULL to ask this function to malloc a buffer |
41 |
| - * @param size is number of bytes available in buf, e.g. PATH_MAX+1, |
42 |
| - * which may be 0 if buf is NULL |
43 |
| - * @return buf containing system-normative path or NULL w/ errno |
44 |
| - * @error ERANGE, EINVAL, ENOMEM |
45 |
| - */ |
46 |
| -char *getcwd(char *buf, size_t size) { |
47 |
| - char *p, *r; |
48 |
| - if (buf) { |
49 |
| - p = buf; |
50 |
| - if (!size) { |
51 |
| - einval(); |
52 |
| - STRACE("getcwd(%p, %'zu) %m", buf, size); |
53 |
| - return 0; |
54 |
| - } |
55 |
| - } else if (_weaken(malloc)) { |
56 |
| - unassert(!__vforked); |
57 |
| - if (!size) size = PATH_MAX; |
58 |
| - if (!(p = _weaken(malloc)(size))) { |
59 |
| - STRACE("getcwd(%p, %'zu) %m", buf, size); |
60 |
| - return 0; |
| 34 | +#define XNU_F_GETPATH 50 |
| 35 | +#define XNU_MAXPATHLEN 1024 |
| 36 | + |
| 37 | +static int sys_getcwd_xnu(char *res, size_t size) { |
| 38 | + int fd, len, rc = -1; |
| 39 | + union metastat st[2]; |
| 40 | + char buf[XNU_MAXPATHLEN]; |
| 41 | + if ((fd = __sys_openat_nc(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY, 0)) != -1) { |
| 42 | + if (__sys_fstat(fd, &st[0]) != -1) { |
| 43 | + if (st[0].xnu.st_dev && st[0].xnu.st_ino) { |
| 44 | + if (__sys_fcntl(fd, XNU_F_GETPATH, (uintptr_t)buf) != -1) { |
| 45 | + if (__sys_fstatat(AT_FDCWD, buf, &st[1], 0) != -1) { |
| 46 | + if (st[0].xnu.st_dev == st[1].xnu.st_dev && |
| 47 | + st[0].xnu.st_ino == st[1].xnu.st_ino) { |
| 48 | + if ((len = strlen(buf)) < size) { |
| 49 | + memcpy(res, buf, (rc = len + 1)); |
| 50 | + } else { |
| 51 | + erange(); |
| 52 | + } |
| 53 | + } else { |
| 54 | + einval(); |
| 55 | + } |
| 56 | + } |
| 57 | + } |
| 58 | + } else { |
| 59 | + einval(); |
| 60 | + } |
61 | 61 | }
|
| 62 | + sys_close(fd); |
| 63 | + } |
| 64 | + return rc; |
| 65 | +} |
| 66 | + |
| 67 | +static int sys_getcwd_metal(char *buf, size_t size) { |
| 68 | + if (size >= 5) { |
| 69 | + strcpy(buf, "/zip"); |
| 70 | + return 5; |
62 | 71 | } else {
|
63 |
| - einval(); |
64 |
| - STRACE("getcwd() needs buf≠0 or __static_yoink(\"malloc\")"); |
65 |
| - return 0; |
| 72 | + return erange(); |
66 | 73 | }
|
67 |
| - *p = '\0'; |
68 |
| - if (!IsWindows()) { |
69 |
| - if (IsMetal()) { |
70 |
| - r = size >= 5 ? strcpy(p, "/zip") : 0; |
71 |
| - } else if (IsXnu()) { |
72 |
| - r = sys_getcwd_xnu(p, size); |
73 |
| - } else if (sys_getcwd(p, size) != (void *)-1) { |
74 |
| - r = p; |
75 |
| - } else { |
76 |
| - r = 0; |
| 74 | +} |
| 75 | + |
| 76 | +static inline int IsAlpha(int c) { |
| 77 | + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); |
| 78 | +} |
| 79 | + |
| 80 | +static dontinline textwindows int sys_getcwd_nt(char *buf, size_t size) { |
| 81 | + |
| 82 | + // get current directory from the system |
| 83 | + char16_t p16[PATH_MAX]; |
| 84 | + uint32_t n = GetCurrentDirectory(PATH_MAX, p16); |
| 85 | + if (!n) return eacces(); // system call failed |
| 86 | + if (n >= PATH_MAX) return erange(); // not enough room?!? |
| 87 | + |
| 88 | + // convert utf-16 to utf-8 |
| 89 | + // we can't modify `buf` until we're certain of success |
| 90 | + char p8[PATH_MAX], *p = p8; |
| 91 | + n = tprecode16to8(p, PATH_MAX, p16).ax; |
| 92 | + if (n >= PATH_MAX) return erange(); // utf-8 explosion |
| 93 | + |
| 94 | + // turn \\?\c:\... into c:\... |
| 95 | + if (p[0] == '\\' && // |
| 96 | + p[1] == '\\' && // |
| 97 | + p[2] == '?' && // |
| 98 | + p[3] == '\\' && // |
| 99 | + IsAlpha(p[4]) && // |
| 100 | + p[5] == ':' && // |
| 101 | + p[6] == '\\') { |
| 102 | + p += 4; |
| 103 | + n -= 4; |
| 104 | + } |
| 105 | + |
| 106 | + // turn c:\... into \c\... |
| 107 | + if (IsAlpha(p[0]) && // |
| 108 | + p[1] == ':' && // |
| 109 | + p[2] == '\\') { |
| 110 | + p[1] = p[0]; |
| 111 | + p[0] = '\\'; |
| 112 | + } |
| 113 | + |
| 114 | + // we now know the final length |
| 115 | + // check if the user supplied a buffer large enough |
| 116 | + if (n >= size) { |
| 117 | + return erange(); |
| 118 | + } |
| 119 | + |
| 120 | + // copy bytes converting backslashes to slash |
| 121 | + for (int i = 0; i < n; ++i) { |
| 122 | + int c = p[i]; |
| 123 | + if (c == '\\') { |
| 124 | + c = '/'; |
77 | 125 | }
|
78 |
| - } else { |
79 |
| - r = sys_getcwd_nt(p, size); |
| 126 | + buf[i] = c; |
80 | 127 | }
|
81 |
| - if (!buf) { |
82 |
| - if (!r) { |
83 |
| - if (_weaken(free)) { |
84 |
| - _weaken(free)(p); |
85 |
| - } |
| 128 | + |
| 129 | + // return number of bytes including nul |
| 130 | + buf[n++] = 0; |
| 131 | + return n; |
| 132 | +} |
| 133 | + |
| 134 | +/** |
| 135 | + * Returns current working directory. |
| 136 | + * |
| 137 | + * Cosmo provides this function to address the shortcomings of getcwd(). |
| 138 | + * First, this function doesn't link malloc(). Secondly, this offers the |
| 139 | + * Linux and NetBSD's getcwd() API across platforms since it's the best. |
| 140 | + * |
| 141 | + * @param buf receives utf-8 path and isn't modified on error |
| 142 | + * @param size is byte capacity of `buf` |
| 143 | + * @return bytes copied including nul on success, or -1 w/ errno |
| 144 | + * @raise EACCES if the current directory path couldn't be accessed |
| 145 | + * @raise ERANGE if `size` wasn't big enough for path and nul byte |
| 146 | + * @raise EFAULT if `buf` points to invalid memory |
| 147 | + */ |
| 148 | +int __getcwd(char *buf, size_t size) { |
| 149 | + int rc; |
| 150 | + if (IsLinux() || IsNetbsd()) { |
| 151 | + rc = sys_getcwd(buf, size); |
| 152 | + } else if (IsXnu()) { |
| 153 | + rc = sys_getcwd_xnu(buf, size); |
| 154 | + } else if (IsWindows()) { |
| 155 | + rc = sys_getcwd_nt(buf, size); |
| 156 | + } else if (IsFreebsd() || IsOpenbsd()) { |
| 157 | + if (sys_getcwd(buf, size) != -1) { |
| 158 | + rc = strlen(buf) + 1; |
| 159 | + } else if (SupportsFreebsd() && (errno == ENOMEM || errno == EINVAL)) { |
| 160 | + rc = erange(); |
86 | 161 | } else {
|
87 |
| - if (_weaken(realloc)) { |
88 |
| - if ((p = _weaken(realloc)(r, strlen(r) + 1))) { |
89 |
| - r = p; |
90 |
| - } |
91 |
| - } |
| 162 | + rc = -1; |
92 | 163 | }
|
| 164 | + } else { |
| 165 | + rc = sys_getcwd_metal(buf, size); |
93 | 166 | }
|
94 |
| - STRACE("getcwd(%p, %'zu) → %#s", buf, size, r); |
95 |
| - return r; |
| 167 | + STRACE("__getcwd([%#hhs], %'zu) → %d% m", rc != -1 ? buf : "n/a", size, rc); |
| 168 | + return rc; |
96 | 169 | }
|
0 commit comments