diff options
author | Eric Snow <ericsnowcurrently@gmail.com> | 2021-09-27 16:00:32 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-27 16:00:32 (GMT) |
commit | ae7839bbe817329dd015f9195da308a0f3fbd3e2 (patch) | |
tree | ba710c468adef4718e3d1ed9747d2acdc830216b /Python/fileutils.c | |
parent | e5f13ce5b48b551c09fdd0faeafa6ecf860de51c (diff) | |
download | cpython-ae7839bbe817329dd015f9195da308a0f3fbd3e2.zip cpython-ae7839bbe817329dd015f9195da308a0f3fbd3e2.tar.gz cpython-ae7839bbe817329dd015f9195da308a0f3fbd3e2.tar.bz2 |
bpo-45211: Move helpers from getpath.c to internal API. (gh-28550)
This accomplishes 2 things:
* consolidates some common code between getpath.c and getpathp.c
* makes the helpers available to code in other files
FWIW, the signature of the join_relfile() function (in fileutils.c) intentionally mirrors that of Windows' PathCchCombineEx().
Note that this change is mostly moving code around. No behavior is meant to change.
https://bugs.python.org/issue45211
Diffstat (limited to 'Python/fileutils.c')
-rw-r--r-- | Python/fileutils.c | 102 |
1 files changed, 98 insertions, 4 deletions
diff --git a/Python/fileutils.c b/Python/fileutils.c index 9e732dd..2492d05 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -7,6 +7,7 @@ #ifdef MS_WINDOWS # include <malloc.h> # include <windows.h> +# include <pathcch.h> // PathCchCombineEx extern int winerror_to_errno(int); #endif @@ -1205,6 +1206,31 @@ _Py_fstat(int fd, struct _Py_stat_struct *status) return 0; } +/* Like _Py_stat() but with a raw filename. */ +int +_Py_wstat(const wchar_t* path, struct stat *buf) +{ + int err; +#ifdef MS_WINDOWS + struct _stat wstatbuf; + err = _wstat(path, &wstatbuf); + if (!err) { + buf->st_mode = wstatbuf.st_mode; + } +#else + char *fname; + fname = _Py_EncodeLocaleRaw(path, NULL); + if (fname == NULL) { + errno = EINVAL; + return -1; + } + err = stat(fname, buf); + PyMem_RawFree(fname); +#endif + return err; +} + + /* Call _wstat() on Windows, or encode the path to the filesystem encoding and call stat() otherwise. Only fill st_mode attribute on Windows. @@ -1216,7 +1242,6 @@ _Py_stat(PyObject *path, struct stat *statbuf) { #ifdef MS_WINDOWS int err; - struct _stat wstatbuf; #if USE_UNICODE_WCHAR_CACHE const wchar_t *wpath = _PyUnicode_AsUnicode(path); @@ -1226,9 +1251,7 @@ _Py_stat(PyObject *path, struct stat *statbuf) if (wpath == NULL) return -2; - err = _wstat(wpath, &wstatbuf); - if (!err) - statbuf->st_mode = wstatbuf.st_mode; + err = _Py_wstat(wpath, statbuf); #if !USE_UNICODE_WCHAR_CACHE PyMem_Free(wpath); #endif /* USE_UNICODE_WCHAR_CACHE */ @@ -2072,6 +2095,77 @@ _Py_abspath(const wchar_t *path, wchar_t **abspath_p) } +// The caller must ensure "buffer" is big enough. +static int +join_relfile(wchar_t *buffer, size_t bufsize, + const wchar_t *dirname, const wchar_t *relfile) +{ +#ifdef MS_WINDOWS + if (FAILED(PathCchCombineEx(buffer, bufsize, dirname, relfile, 0))) { + return -1; + } +#else + assert(!_Py_isabs(relfile)); + size_t dirlen = wcslen(dirname); + size_t rellen = wcslen(relfile); + size_t maxlen = bufsize - 1; + if (maxlen > MAXPATHLEN || dirlen >= maxlen || rellen >= maxlen - dirlen) { + return -1; + } + if (dirlen == 0) { + // We do not add a leading separator. + wcscpy(buffer, relfile); + } + else { + if (dirname != buffer) { + wcscpy(buffer, dirname); + } + size_t relstart = dirlen; + if (dirlen > 1 && dirname[dirlen - 1] != SEP) { + buffer[dirlen] = SEP; + relstart += 1; + } + wcscpy(&buffer[relstart], relfile); + } +#endif + return 0; +} + +/* Join the two paths together, like os.path.join(). Return NULL + if memory could not be allocated. The caller is responsible + for calling PyMem_RawFree() on the result. */ +wchar_t * +_Py_join_relfile(const wchar_t *dirname, const wchar_t *relfile) +{ + assert(dirname != NULL && relfile != NULL); + assert(!_Py_isabs(relfile)); + size_t maxlen = wcslen(dirname) + 1 + wcslen(relfile); + size_t bufsize = maxlen + 1; + wchar_t *filename = PyMem_RawMalloc(bufsize * sizeof(wchar_t)); + if (filename == NULL) { + return NULL; + } + assert(wcslen(dirname) < MAXPATHLEN); + assert(wcslen(relfile) < MAXPATHLEN - wcslen(dirname)); + join_relfile(filename, bufsize, dirname, relfile); + return filename; +} + +/* Join the two paths together, like os.path.join(). + dirname: the target buffer with the dirname already in place, + including trailing NUL + relfile: this must be a relative path + bufsize: total allocated size of the buffer + Return -1 if anything is wrong with the path lengths. */ +int +_Py_add_relfile(wchar_t *dirname, const wchar_t *relfile, size_t bufsize) +{ + assert(dirname != NULL && relfile != NULL); + assert(bufsize > 0); + return join_relfile(dirname, bufsize, dirname, relfile); +} + + /* Get the current directory. buflen is the buffer size in wide characters including the null character. Decode the path from the locale encoding. |