diff options
author | Eric Snow <ericsnowcurrently@gmail.com> | 2021-10-22 23:20:03 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-22 23:20:03 (GMT) |
commit | 17c61045c51512add61a9e75e9c7343cf4e4fb82 (patch) | |
tree | 59a98afdea508cacfb4dc4f10acf2f5b402ea0b2 /Python | |
parent | f30ad65dbf3c6b1b5eec14dc954d65ef32327857 (diff) | |
download | cpython-17c61045c51512add61a9e75e9c7343cf4e4fb82.zip cpython-17c61045c51512add61a9e75e9c7343cf4e4fb82.tar.gz cpython-17c61045c51512add61a9e75e9c7343cf4e4fb82.tar.bz2 |
bpo-45506: Normalize _PyPathConfig.stdlib_dir when calculated. (#29040)
The recently added PyConfig.stdlib_dir was being set with ".." entries. When __file__ was added for from modules this caused a problem on out-of-tree builds. This PR fixes that by normalizing "stdlib_dir" when it is calculated in getpath.c.
https://bugs.python.org/issue45506
Diffstat (limited to 'Python')
-rw-r--r-- | Python/fileutils.c | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/Python/fileutils.c b/Python/fileutils.c index 3d8f3a4..ac0046c 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2181,6 +2181,101 @@ _Py_find_basename(const wchar_t *filename) } +/* Remove navigation elements such as "." and "..". + + This is mostly a C implementation of posixpath.normpath(). + Return 0 on success. Return -1 if "orig" is too big for the buffer. */ +int +_Py_normalize_path(const wchar_t *path, wchar_t *buf, const size_t buf_len) +{ + assert(path && *path != L'\0'); + assert(*path == SEP); // an absolute path + if (wcslen(path) + 1 >= buf_len) { + return -1; + } + + int dots = -1; + int check_leading = 1; + const wchar_t *buf_start = buf; + wchar_t *buf_next = buf; + // The resulting filename will never be longer than path. + for (const wchar_t *remainder = path; *remainder != L'\0'; remainder++) { + wchar_t c = *remainder; + buf_next[0] = c; + buf_next++; + if (c == SEP) { + assert(dots <= 2); + if (dots == 2) { + // Turn "/x/y/../z" into "/x/z". + buf_next -= 4; // "/../" + assert(*buf_next == SEP); + // We cap it off at the root, so "/../spam" becomes "/spam". + if (buf_next == buf_start) { + buf_next++; + } + else { + // Move to the previous SEP in the buffer. + while (*(buf_next - 1) != SEP) { + assert(buf_next != buf_start); + buf_next--; + } + } + } + else if (dots == 1) { + // Turn "/./" into "/". + buf_next -= 2; // "./" + assert(*(buf_next - 1) == SEP); + } + else if (dots == 0) { + // Turn "//" into "/". + buf_next--; + assert(*(buf_next - 1) == SEP); + if (check_leading) { + if (buf_next - 1 == buf && *(remainder + 1) != SEP) { + // Leave a leading "//" alone, unless "///...". + buf_next++; + buf_start++; + } + check_leading = 0; + } + } + dots = 0; + } + else { + check_leading = 0; + if (dots >= 0) { + if (c == L'.' && dots < 2) { + dots++; + } + else { + dots = -1; + } + } + } + } + if (dots >= 0) { + // Strip any trailing dots and trailing slash. + buf_next -= dots + 1; // "/" or "/." or "/.." + assert(*buf_next == SEP); + if (buf_next == buf_start) { + // Leave the leading slash for root. + buf_next++; + } + else { + if (dots == 2) { + // Move to the previous SEP in the buffer. + do { + assert(buf_next != buf_start); + buf_next--; + } while (*(buf_next) != SEP); + } + } + } + *buf_next = L'\0'; + return 0; +} + + /* Get the current directory. buflen is the buffer size in wide characters including the null character. Decode the path from the locale encoding. |