Skip to content

Instantly share code, notes, and snippets.

@jeremyd2019
Created June 3, 2024 23:07
Show Gist options
  • Save jeremyd2019/95c2cfd7eef2ed29a339860896deddec to your computer and use it in GitHub Desktop.
Save jeremyd2019/95c2cfd7eef2ed29a339860896deddec to your computer and use it in GitHub Desktop.
python ctypes interface to cygwin apis
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