Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert POSIX->WIN path, in Cygwin Python, w/o calling cygpath

Tags:

python

cygwin

I use a Python script, running in a Cygwin build of Python, to create commands issued to native Windows utilities (not Cygwin-aware). This requires conversion of path parameters from POSIX to WIN form before issuing the command.

Calling the cygpath utility is the nicest way to do this, since it uses Cygwin to do what it's there to do, but it's also a little horrifying (and slow).

I'm already running a Cygwin build of Python - so the code to do the conversion is present. It seems like there should be a Cygwin/Python specific extension that gives me a hook to this capability, directly in Python, without having to fire up a whole new process.

like image 296
jrm03063 Avatar asked Jun 04 '12 15:06

jrm03063


People also ask

What is Cygpath?

The cygpath program is a utility that converts Windows native filenames to Cygwin POSIX-style pathnames and vice versa. It can be used when a Cygwin program needs to pass a file name to a native Windows program, or expects to get a file name from a native Windows program.

Where is Cygdrive on Windows?

Using Cygwin As noted, Cygwin provides a Unix-like environment under Windows. The installation directory (by default, c:\cygwin) is the root of the Unix-like file system, which contains bin, etc, home, tmp, and usr directories as would be found on a GNU/Linux or other Unix system.

Where are Cygwin files stored in Windows?

In a new installation of Cygwin, your home directory will be in C:/cygwin/home/<user>/ , and can be accessed by the usual ~ shortcut. Although this works just fine, it's often useful to use the utilities provided by Cygwin in your local Windows user area C:/Users/<user> .


1 Answers

This is possible by calling the Cygwin API using ctypes. The below code works for me–I am using 64-bit cygwin DLL version 2.5.2 on Windows 2012, and this works on the Cygwin versions of both Python 2.7.10 and Python 3.4.3.

Basically we call cygwin_create_path from cygwin1.dll to perform the path conversion. That function allocates a memory buffer (using malloc) containing the converted path. So then we need to use free from cygwin1.dll to release the buffer it allocated.

Note that xunicode below is a poor man's alternative to six (a Python 2/3 compatibility library); if you need to support both Python 2 and 3, six is the much better answer, but I wanted my example to be free of dependencies on any non-bundled modules which is why I did it this way.

from ctypes import cdll, c_void_p, c_int32, cast, c_char_p, c_wchar_p
from sys import version_info

xunicode = str if version_info[0] > 2 else eval("unicode")

# If running under Cygwin Python, just use DLL name
# If running under non-Cygwin Windows Python, use full path to cygwin1.dll
# Note Python and cygwin1.dll must match bitness (i.e. 32-bit Python must
# use 32-bit cygwin1.dll, 64-bit Python must use 64-bit cygwin1.dll.)
cygwin = cdll.LoadLibrary("cygwin1.dll")
cygwin_create_path = cygwin.cygwin_create_path
cygwin_create_path.restype = c_void_p
cygwin_create_path.argtypes = [c_int32, c_void_p]

# Initialise the cygwin DLL. This step should only be done if using
# non-Cygwin Python. If you are using Cygwin Python don't do this because
# it has already been done for you.
cygwin_dll_init = cygwin.cygwin_dll_init
cygwin_dll_init.restype = None
cygwin_dll_init.argtypes = []
cygwin_dll_init()

free = cygwin.free
free.restype = None
free.argtypes = [c_void_p]

CCP_POSIX_TO_WIN_A = 0
CCP_POSIX_TO_WIN_W = 1
CCP_WIN_A_TO_POSIX = 2
CCP_WIN_W_TO_POSIX = 3

def win2posix(path):
    """Convert a Windows path to a Cygwin path"""
    result = cygwin_create_path(CCP_WIN_W_TO_POSIX,xunicode(path))
    if result is None:
        raise Exception("cygwin_create_path failed")
    value = cast(result,c_char_p).value
    free(result)
    return value

def posix2win(path):
    """Convert a Cygwin path to a Windows path"""
    result = cygwin_create_path(CCP_POSIX_TO_WIN_W,str(path))
    if result is None:
        raise Exception("cygwin_create_path failed")
    value = cast(result,c_wchar_p).value
    free(result)
    return value

# Example, convert LOCALAPPDATA to cygwin path and back
from os import environ
localAppData = environ["LOCALAPPDATA"]
print("Original Win32 path: %s" % localAppData)
localAppData = win2posix(localAppData)
print("As a POSIX path: %s" % localAppData)
localAppData = posix2win(localAppData)
print("Back to a Windows path: %s" % localAppData)
like image 163
Simon Kissane Avatar answered Oct 14 '22 08:10

Simon Kissane