summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2021-10-22 23:20:03 (GMT)
committerGitHub <noreply@github.com>2021-10-22 23:20:03 (GMT)
commit17c61045c51512add61a9e75e9c7343cf4e4fb82 (patch)
tree59a98afdea508cacfb4dc4f10acf2f5b402ea0b2 /Python
parentf30ad65dbf3c6b1b5eec14dc954d65ef32327857 (diff)
downloadcpython-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.c95
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.