Last active
February 20, 2025 23:55
-
-
Save jeremyd2019/95c2cfd7eef2ed29a339860896deddec to your computer and use it in GitHub Desktop.
python ctypes interface to cygwin apis
This file contains hidden or 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", "cygwin_max_pid"] | |
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 | |
_CW_MAX_CYGWIN_PID = 61 | |
class DosDriveMappings: | |
def __enter__(self): | |
self._ddm = _cygwin_internal(_CW_ALLOC_DRIVE_MAP) | |
return self | |
def __exit__(self, *exc_details): | |
_cygwin_internal(_CW_FREE_DRIVE_MAP, self._ddm) | |
del self._ddm | |
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 | |
def cygwin_max_pid(): | |
return _cygwin_internal(_CW_MAX_CYGWIN_PID) | |
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 < cygwin_max_pid()) | |
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)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment