Skip to content

Commit b300847

Browse files
mingwandroidAlexpuxlazka0xb8naveen521kk
committed
mingw: prefer unix sep if MSYSTEM environment variable
Co-authored-by: Алексей <[email protected]> Co-authored-by: Christoph Reiter <[email protected]> Co-authored-by: cat <[email protected]> Co-authored-by: Naveen M K <[email protected]>
1 parent eb1c2b5 commit b300847

File tree

6 files changed

+201
-48
lines changed

6 files changed

+201
-48
lines changed

Include/pylifecycle.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ PyAPI_FUNC(int) Py_IsInitialized(void);
2121
PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void);
2222
PyAPI_FUNC(void) Py_EndInterpreter(PyThreadState *);
2323

24+
PyAPI_FUNC(wchar_t) Py_GetSepW(const wchar_t *);
25+
PyAPI_FUNC(char) Py_GetSepA(const char *);
26+
27+
PyAPI_FUNC(void) Py_NormalizeSepsW(wchar_t *);
28+
PyAPI_FUNC(void) Py_NormalizeSepsA(char *);
29+
2430

2531
/* Py_PyAtExit is for the atexit module, Py_AtExit is for low-level
2632
* exit functions.

Lib/ntpath.py

Lines changed: 56 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
curdir = '.'
1212
pardir = '..'
1313
extsep = '.'
14-
sep = '\\'
1514
pathsep = ';'
16-
altsep = '/'
1715
defpath = '.;C:\\bin'
1816
devnull = 'nul'
1917

@@ -23,6 +21,14 @@
2321
import genericpath
2422
from genericpath import *
2523

24+
if sys.platform == "win32" and "MSYSTEM" in os.environ:
25+
sep = '/'
26+
altsep = '\\'
27+
else:
28+
sep = '\\'
29+
altsep = '/'
30+
bsep = str.encode(sep)
31+
baltsep = str.encode(altsep)
2632

2733
__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
2834
"basename","dirname","commonprefix","getsize","getmtime",
@@ -34,9 +40,33 @@
3440

3541
def _get_bothseps(path):
3642
if isinstance(path, bytes):
37-
return b'\\/'
43+
return bsep+baltsep
44+
else:
45+
return sep+altsep
46+
47+
def _get_sep(path):
48+
if isinstance(path, bytes):
49+
return bsep
3850
else:
39-
return '\\/'
51+
return sep
52+
53+
def _get_altsep(path):
54+
if isinstance(path, bytes):
55+
return baltsep
56+
else:
57+
return altsep
58+
59+
def _get_colon(path):
60+
if isinstance(path, bytes):
61+
return b':'
62+
else:
63+
return ':'
64+
65+
def _get_unc_prefix(path):
66+
if isinstance(path, bytes):
67+
return b'\\\\?\\UNC\\'
68+
else:
69+
return '\\\\?\\UNC\\'
4070

4171
# Normalize the case of a pathname and map slashes to backslashes.
4272
# Other normalizations (such as optimizing '../' away) are not done
@@ -58,14 +88,14 @@ def normcase(s):
5888
return s
5989
if isinstance(s, bytes):
6090
encoding = sys.getfilesystemencoding()
61-
s = s.decode(encoding, 'surrogateescape').replace('/', '\\')
91+
s = s.decode(encoding, 'surrogateescape').replace(altsep, sep)
6292
s = _LCMapStringEx(_LOCALE_NAME_INVARIANT,
6393
_LCMAP_LOWERCASE, s)
6494
return s.encode(encoding, 'surrogateescape')
6595
else:
6696
return _LCMapStringEx(_LOCALE_NAME_INVARIANT,
6797
_LCMAP_LOWERCASE,
68-
s.replace('/', '\\'))
98+
s.replace(altsep, sep))
6999
except ImportError:
70100
def normcase(s):
71101
"""Normalize case of pathname.
@@ -74,8 +104,8 @@ def normcase(s):
74104
"""
75105
s = os.fspath(s)
76106
if isinstance(s, bytes):
77-
return os.fsencode(os.fsdecode(s).replace('/', '\\').lower())
78-
return s.replace('/', '\\').lower()
107+
return os.fsencode(os.fsdecode(s).replace(altsep, sep).lower())
108+
return s.replace(altsep, sep).lower()
79109

80110

81111
# Return whether a path is absolute.
@@ -87,14 +117,9 @@ def normcase(s):
87117
def isabs(s):
88118
"""Test whether a path is absolute"""
89119
s = os.fspath(s)
90-
if isinstance(s, bytes):
91-
sep = b'\\'
92-
altsep = b'/'
93-
colon_sep = b':\\'
94-
else:
95-
sep = '\\'
96-
altsep = '/'
97-
colon_sep = ':\\'
120+
sep = _get_sep(s)
121+
altsep = _get_altsep(s)
122+
colon_sep = _get_colon(s) + sep
98123
s = s[:3].replace(altsep, sep)
99124
# Absolute: UNC, device, and paths with a drive and root.
100125
# LEGACY BUG: isabs("/x") should be false since the path has no drive.
@@ -106,14 +131,9 @@ def isabs(s):
106131
# Join two (or more) paths.
107132
def join(path, *paths):
108133
path = os.fspath(path)
109-
if isinstance(path, bytes):
110-
sep = b'\\'
111-
seps = b'\\/'
112-
colon = b':'
113-
else:
114-
sep = '\\'
115-
seps = '\\/'
116-
colon = ':'
134+
sep = _get_sep(path)
135+
seps = _get_bothseps(path)
136+
colon = _get_colon(path)
117137
try:
118138
if not paths:
119139
path[:0] + sep #23780: Ensure compatible data type even if p is null.
@@ -172,16 +192,10 @@ def splitdrive(p):
172192
"""
173193
p = os.fspath(p)
174194
if len(p) >= 2:
175-
if isinstance(p, bytes):
176-
sep = b'\\'
177-
altsep = b'/'
178-
colon = b':'
179-
unc_prefix = b'\\\\?\\UNC\\'
180-
else:
181-
sep = '\\'
182-
altsep = '/'
183-
colon = ':'
184-
unc_prefix = '\\\\?\\UNC\\'
195+
sep = _get_sep(p)
196+
altsep = _get_altsep(p)
197+
colon = _get_colon(p)
198+
unc_prefix = _get_unc_prefix(p)
185199
normp = p.replace(altsep, sep)
186200
if normp[0:2] == sep * 2:
187201
# UNC drives, e.g. \\server\share or \\?\UNC\server\share
@@ -231,9 +245,9 @@ def split(p):
231245
def splitext(p):
232246
p = os.fspath(p)
233247
if isinstance(p, bytes):
234-
return genericpath._splitext(p, b'\\', b'/', b'.')
248+
return genericpath._splitext(p, bsep, baltsep, b'.')
235249
else:
236-
return genericpath._splitext(p, '\\', '/', '.')
250+
return genericpath._splitext(p, sep, altsep, '.')
237251
splitext.__doc__ = genericpath._splitext.__doc__
238252

239253

@@ -496,14 +510,12 @@ def expandvars(path):
496510
def normpath(path):
497511
"""Normalize path, eliminating double slashes, etc."""
498512
path = os.fspath(path)
513+
sep = _get_sep(path)
514+
altsep = _get_altsep(path)
499515
if isinstance(path, bytes):
500-
sep = b'\\'
501-
altsep = b'/'
502516
curdir = b'.'
503517
pardir = b'..'
504518
else:
505-
sep = '\\'
506-
altsep = '/'
507519
curdir = '.'
508520
pardir = '..'
509521
path = path.replace(altsep, sep)
@@ -731,6 +743,7 @@ def realpath(path, *, strict=False):
731743
# strip the prefix anyway.
732744
if ex.winerror == initial_winerror:
733745
path = spath
746+
path = normpath(path)
734747
return path
735748

736749

@@ -741,12 +754,11 @@ def realpath(path, *, strict=False):
741754
def relpath(path, start=None):
742755
"""Return a relative version of a path"""
743756
path = os.fspath(path)
757+
sep = _get_sep(path)
744758
if isinstance(path, bytes):
745-
sep = b'\\'
746759
curdir = b'.'
747760
pardir = b'..'
748761
else:
749-
sep = '\\'
750762
curdir = '.'
751763
pardir = '..'
752764

@@ -801,13 +813,11 @@ def commonpath(paths):
801813
raise ValueError('commonpath() arg is an empty sequence')
802814

803815
paths = tuple(map(os.fspath, paths))
816+
sep = _get_sep(paths[0])
817+
altsep = _get_altsep(paths[0])
804818
if isinstance(paths[0], bytes):
805-
sep = b'\\'
806-
altsep = b'/'
807819
curdir = b'.'
808820
else:
809-
sep = '\\'
810-
altsep = '/'
811821
curdir = '.'
812822

813823
try:

Modules/posixmodule.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3835,6 +3835,7 @@ posix_getcwd(int use_bytes)
38353835
return PyErr_SetFromWindowsErr(0);
38363836
}
38373837

3838+
Py_NormalizeSepsW(wbuf2);
38383839
PyObject *resobj = PyUnicode_FromWideChar(wbuf2, len);
38393840
if (wbuf2 != wbuf) {
38403841
PyMem_RawFree(wbuf2);
@@ -4439,6 +4440,7 @@ os__getfinalpathname_impl(PyObject *module, path_t *path)
44394440
target_path = tmp;
44404441
}
44414442

4443+
Py_NormalizeSepsW(target_path);
44424444
result = PyUnicode_FromWideChar(target_path, result_length);
44434445
if (result && path->narrow) {
44444446
Py_SETREF(result, PyUnicode_EncodeFSDefault(result));

Python/initconfig.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ static const char usage_envvars[] =
176176
"PYTHONVERBOSE : trace import statements (-v)\n"
177177
"PYTHONWARNINGS=arg : warning control (-W arg)\n";
178178

179-
#if defined(MS_WINDOWS)
179+
#if defined(_MSC_VER)
180180
# define PYTHONHOMEHELP "<prefix>\\python{major}{minor}"
181181
#else
182182
# define PYTHONHOMEHELP "<prefix>/lib/pythonX.X"

Python/pathconfig.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,140 @@
1818
extern "C" {
1919
#endif
2020

21+
#ifdef __MINGW32__
22+
#define wcstok wcstok_s
23+
#include <windows.h>
24+
#endif
25+
26+
static int
27+
Py_StartsWithA(const char * str, const char * prefix)
28+
{
29+
while(*prefix)
30+
{
31+
if(*prefix++ != *str++)
32+
return 0;
33+
}
34+
35+
return 1;
36+
}
37+
38+
static int
39+
Py_StartsWithW(const wchar_t * str, const wchar_t * prefix)
40+
{
41+
while(*prefix)
42+
{
43+
if(*prefix++ != *str++)
44+
return 0;
45+
}
46+
47+
return 1;
48+
}
49+
50+
char
51+
Py_GetSepA(const char *name)
52+
{
53+
char* msystem = (char*)2; /* So that non Windows use / as sep */
54+
static char sep = '\0';
55+
#ifdef _WIN32
56+
/* https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247%28v=vs.85%29.aspx
57+
* The "\\?\" prefix .. indicate that the path should be passed to the system with minimal
58+
* modification, which means that you cannot use forward slashes to represent path separators
59+
*/
60+
if (name != NULL && Py_StartsWithA(name, "\\\\?\\") != 0)
61+
{
62+
return '\\';
63+
}
64+
#endif
65+
if (sep != '\0')
66+
return sep;
67+
#if defined(__MINGW32__)
68+
msystem = Py_GETENV("MSYSTEM");
69+
#endif
70+
if (msystem != NULL)
71+
sep = '/';
72+
else
73+
sep = '\\';
74+
return sep;
75+
}
76+
77+
static char
78+
Py_GetAltSepA(const char *name)
79+
{
80+
char sep = Py_GetSepA(name);
81+
if (sep == '/')
82+
return '\\';
83+
return '/';
84+
}
85+
86+
void
87+
Py_NormalizeSepsA(char *name)
88+
{
89+
assert(name != NULL);
90+
char sep = Py_GetSepA(name);
91+
char altsep = Py_GetAltSepA(name);
92+
char* seps;
93+
if (name[0] != '\0' && name[1] == ':') {
94+
name[0] = toupper(name[0]);
95+
}
96+
seps = strchr(name, altsep);
97+
while(seps) {
98+
*seps = sep;
99+
seps = strchr(seps, altsep);
100+
}
101+
}
102+
103+
wchar_t
104+
Py_GetSepW(const wchar_t *name)
105+
{
106+
char* msystem = (char*)2; /* So that non Windows use / as sep */
107+
static wchar_t sep = L'\0';
108+
#ifdef _WIN32
109+
/* https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247%28v=vs.85%29.aspx
110+
* The "\\?\" prefix .. indicate that the path should be passed to the system with minimal
111+
* modification, which means that you cannot use forward slashes to represent path separators
112+
*/
113+
if (name != NULL && Py_StartsWithW(name, L"\\\\?\\") != 0)
114+
{
115+
return L'\\';
116+
}
117+
#endif
118+
if (sep != L'\0')
119+
return sep;
120+
#if defined(__MINGW32__)
121+
msystem = Py_GETENV("MSYSTEM");
122+
#endif
123+
if (msystem != NULL)
124+
sep = L'/';
125+
else
126+
sep = L'\\';
127+
return sep;
128+
}
129+
130+
static wchar_t
131+
Py_GetAltSepW(const wchar_t *name)
132+
{
133+
char sep = Py_GetSepW(name);
134+
if (sep == L'/')
135+
return L'\\';
136+
return L'/';
137+
}
138+
139+
void
140+
Py_NormalizeSepsW(wchar_t *name)
141+
{
142+
assert(name != NULL);
143+
wchar_t sep = Py_GetSepW(name);
144+
wchar_t altsep = Py_GetAltSepW(name);
145+
wchar_t* seps;
146+
if (name[0] != L'\0' && name[1] == L':') {
147+
name[0] = towupper(name[0]);
148+
}
149+
seps = wcschr(name, altsep);
150+
while(seps) {
151+
*seps = sep;
152+
seps = wcschr(seps, altsep);
153+
}
154+
}
21155

22156
/* External interface */
23157

@@ -317,6 +451,7 @@ _Py_SetProgramFullPath(const wchar_t *program_full_path)
317451
if (has_value && _Py_path_config.program_full_path == NULL) {
318452
path_out_of_memory(__func__);
319453
}
454+
Py_NormalizeSepsW(_Py_path_config.program_name);
320455
}
321456

322457

0 commit comments

Comments
 (0)