Created
June 3, 2024 23:07
-
-
Save jeremyd2019/95c2cfd7eef2ed29a339860896deddec to your computer and use it in GitHub Desktop.
python ctypes interface to cygwin apis
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import contextlib | |
import ctypes | |
import errno | |
import os | |
__all__ = ["CCP_POSIX_TO_WIN_A", "CCP_POSIX_TO_WIN_W", | |
"CCP_WIN_A_TO_POSIX", "CCP_WIN_W_TO_POSIX", | |
"CCP_ABSOLUTE", "CCP_RELATIVE", "CCP_PROC_CYGDRIVE", | |
"cygwin_conv_path", "cygwin_conv_path_list", | |
"cygwin_winpid_to_pid", "cygwin_pid_to_winpid", | |
"DosDriveMappings", "Mounts"] | |
try: | |
cygwin = ctypes.CDLL("msys-2.0.dll", use_errno=True) | |
except (FileNotFoundError, OSError): | |
cygwin = ctypes.CDLL("cygwin1.dll", use_errno=True) | |
_cygwin_conv_patha_proto = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_uint, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_size_t, use_errno=True) | |
_cygwin_conv_pathw_proto = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_uint, ctypes.c_wchar_p, ctypes.c_void_p, ctypes.c_size_t, use_errno=True) | |
_cygwin_conv_patha = _cygwin_conv_patha_proto(("cygwin_conv_path", cygwin)) | |
_cygwin_conv_pathw = _cygwin_conv_pathw_proto(("cygwin_conv_path", cygwin)) | |
_cygwin_conv_path_lista = _cygwin_conv_patha_proto(("cygwin_conv_path_list", cygwin)) | |
_cygwin_conv_path_listw = _cygwin_conv_pathw_proto(("cygwin_conv_path_list", cygwin)) | |
CCP_POSIX_TO_WIN_A = 0 | |
CCP_POSIX_TO_WIN_W = 1 | |
CCP_WIN_A_TO_POSIX = 2 | |
CCP_WIN_W_TO_POSIX = 3 | |
CCP_ABSOLUTE = 0 | |
CCP_RELATIVE = 0x100 | |
CCP_PROC_CYGDRIVE = 0x200 | |
def _cygwin_conv_path(what, from_, ccpfunc): | |
buf = ctypes.create_string_buffer(520 if what == CCP_POSIX_TO_WIN_W else 260) | |
while ccpfunc(what, from_, buf, ctypes.sizeof(buf)) < 0: | |
eno = ctypes.get_errno() | |
if eno == errno.ENOSPC: | |
cyglen = ccpfunc(what, from_, buf, 0) | |
if (cyglen > ctypes.sizeof(buf)): | |
buf = ctypes.create_string_buffer(cyglen) | |
continue | |
raise OSError(eno, os.strerror(eno)) | |
if what == CCP_POSIX_TO_WIN_W: | |
return ctypes.wstring_at(buf) | |
return buf.value | |
def cygwin_conv_path(what, from_): | |
if what == CCP_WIN_W_TO_POSIX: | |
ccpfunc = _cygwin_conv_pathw | |
else: | |
ccpfunc = _cygwin_conv_patha | |
return _cygwin_conv_path(what, from_, ccpfunc) | |
def cygwin_conv_path_list(what, from_): | |
if what == CCP_WIN_W_TO_POSIX: | |
ccpfunc = _cygwin_conv_path_listw | |
else: | |
ccpfunc = _cygwin_conv_path_lista | |
return _cygwin_conv_path(what, from_, ccpfunc) | |
_cygwin_internal = cygwin.cygwin_internal | |
_cygwin_internal.argtypes = ctypes.c_int, # ... | |
_cygwin_internal.restype = ctypes.c_void_p | |
_CW_PID_TO_WINPID = 18 | |
_CW_ALLOC_DRIVE_MAP = 45 | |
_CW_MAP_DRIVE_MAP = 46 | |
_CW_FREE_DRIVE_MAP = 47 | |
class DosDriveMappings: | |
class _mapping(ctypes.Structure): | |
pass | |
_mapping._fields_ = [("next", ctypes.POINTER(_mapping)), | |
("doslen", ctypes.c_size_t), | |
("ntlen", ctypes.c_size_t), | |
("dospath", ctypes.c_wchar_p), | |
("ntdevpath", ctypes.c_wchar_p)] | |
def __enter__(self): | |
self._ddm = ctypes.cast(_cygwin_internal(_CW_ALLOC_DRIVE_MAP), ctypes.POINTER(ctypes.POINTER(self._mapping))) | |
return self | |
def __exit__(self, *exc_details): | |
_cygwin_internal(_CW_FREE_DRIVE_MAP, self._ddm) | |
del self._ddm | |
def __iter__(self): | |
p = self._ddm.contents | |
while p: | |
yield p.contents.dospath, p.contents.ntdevpath | |
p = p.contents.next | |
def map(self, path): | |
buf = ctypes.create_unicode_buffer(path, 260) | |
return ctypes.wstring_at(_cygwin_internal(_CW_MAP_DRIVE_MAP, self._ddm, buf)) | |
_cygwin_winpid_to_pid = cygwin.cygwin_winpid_to_pid | |
_cygwin_winpid_to_pid.argtypes = ctypes.c_int, | |
_cygwin_winpid_to_pid.restype = ctypes.c_int | |
def cygwin_winpid_to_pid(winpid): | |
return _cygwin_winpid_to_pid(winpid) | |
def cygwin_pid_to_winpid(pid): | |
return _cygwin_internal(_CW_PID_TO_WINPID, ctypes.c_int(pid)) or -1 | |
class Mounts: | |
class _mntent(ctypes.Structure): | |
_fields_ = [("mnt_fsname", ctypes.c_char_p), | |
("mnt_dir", ctypes.c_char_p), | |
("mnt_type", ctypes.c_char_p), | |
("mnt_opts", ctypes.c_char_p), | |
("mnt_freq", ctypes.c_int), | |
("mnt_passno", ctypes.c_int)] | |
_setmntent = cygwin.setmntent | |
_setmntent.argtypes = ctypes.c_char_p, ctypes.c_char_p | |
_setmntent.restype = ctypes.c_void_p | |
_getmntent = cygwin.getmntent | |
_getmntent.argtypes = ctypes.c_void_p, | |
_getmntent.restype = ctypes.POINTER(_mntent) | |
_endmntent = cygwin.endmntent | |
_endmntent.argtypes = ctypes.c_void_p, | |
_endmntent.restype = ctypes.c_int | |
def __enter__(self): | |
# cygwin doesn't use the args | |
self._filep = self._setmntent(b"", b"") | |
return self | |
def __exit__(self, *exc_details): | |
self._endmntent(self._filep) | |
del self._filep | |
def __iter__(self): | |
while (mntent := self._getmntent(self._filep)): | |
yield mntent.contents | |
if __name__ == "__main__": | |
# tests | |
import sys | |
pid = os.getpid() | |
winpid = cygwin_pid_to_winpid(pid) | |
assert(winpid > 0) | |
cygpid = cygwin_winpid_to_pid(winpid) | |
assert(cygpid > 0 and cygpid < 65536) | |
assert(cygpid == pid) | |
assert(cygwin_pid_to_winpid(123456) == -1) | |
assert(cygwin_winpid_to_pid(123456) == -1) | |
print(cygwin_conv_path(CCP_POSIX_TO_WIN_W, os.fsencode(sys.executable))) | |
print(os.fsdecode(cygwin_conv_path(CCP_WIN_W_TO_POSIX, r"C:\Windows\System32"))) | |
print(cygwin_conv_path_list(CCP_POSIX_TO_WIN_W, os.fsencode(os.pathsep.join(sys.path)))) | |
with Mounts() as mounts: | |
for mntent in mounts: | |
print(os.fsdecode(mntent.mnt_fsname), os.fsdecode(mntent.mnt_dir)) | |
with DosDriveMappings() as ddm: | |
for dospath, ntdevpath in ddm: | |
print(dospath, ntdevpath) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment