1"""create and manipulate C data types in Python"""
2
3import os as _os, sys as _sys
4
5__version__ = "1.1.0"
6
7from _ctypes import Union, Structure, Array
8from _ctypes import _Pointer
9from _ctypes import CFuncPtr as _CFuncPtr
10from _ctypes import __version__ as _ctypes_version
11from _ctypes import RTLD_LOCAL, RTLD_GLOBAL
12from _ctypes import ArgumentError
13
14from struct import calcsize as _calcsize
15
16if __version__ != _ctypes_version:
17    raise Exception("Version number mismatch", __version__, _ctypes_version)
18
19if _os.name == "nt":
20    from _ctypes import FormatError
21
22DEFAULT_MODE = RTLD_LOCAL
23if _os.name == "posix" and _sys.platform == "darwin":
24    # On OS X 10.3, we use RTLD_GLOBAL as default mode
25    # because RTLD_LOCAL does not work at least on some
26    # libraries.  OS X 10.3 is Darwin 7, so we check for
27    # that.
28
29    if int(_os.uname().release.split('.')[0]) < 8:
30        DEFAULT_MODE = RTLD_GLOBAL
31
32from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \
33     FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \
34     FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
35     FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR
36
37# WINOLEAPI -> HRESULT
38# WINOLEAPI_(type)
39#
40# STDMETHODCALLTYPE
41#
42# STDMETHOD(name)
43# STDMETHOD_(type, name)
44#
45# STDAPICALLTYPE
46
47def create_string_buffer(init, size=None):
48    """create_string_buffer(aBytes) -> character array
49    create_string_buffer(anInteger) -> character array
50    create_string_buffer(aBytes, anInteger) -> character array
51    """
52    if isinstance(init, bytes):
53        if size is None:
54            size = len(init)+1
55        _sys.audit("ctypes.create_string_buffer", init, size)
56        buftype = c_char * size
57        buf = buftype()
58        buf.value = init
59        return buf
60    elif isinstance(init, int):
61        _sys.audit("ctypes.create_string_buffer", None, init)
62        buftype = c_char * init
63        buf = buftype()
64        return buf
65    raise TypeError(init)
66
67def c_buffer(init, size=None):
68##    "deprecated, use create_string_buffer instead"
69##    import warnings
70##    warnings.warn("c_buffer is deprecated, use create_string_buffer instead",
71##                  DeprecationWarning, stacklevel=2)
72    return create_string_buffer(init, size)
73
74_c_functype_cache = {}
75def CFUNCTYPE(restype, *argtypes, **kw):
76    """CFUNCTYPE(restype, *argtypes,
77                 use_errno=False, use_last_error=False) -> function prototype.
78
79    restype: the result type
80    argtypes: a sequence specifying the argument types
81
82    The function prototype can be called in different ways to create a
83    callable object:
84
85    prototype(integer address) -> foreign function
86    prototype(callable) -> create and return a C callable function from callable
87    prototype(integer index, method name[, paramflags]) -> foreign function calling a COM method
88    prototype((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal
89    prototype((function name, dll object)[, paramflags]) -> foreign function exported by name
90    """
91    flags = _FUNCFLAG_CDECL
92    if kw.pop("use_errno", False):
93        flags |= _FUNCFLAG_USE_ERRNO
94    if kw.pop("use_last_error", False):
95        flags |= _FUNCFLAG_USE_LASTERROR
96    if kw:
97        raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
98    try:
99        return _c_functype_cache[(restype, argtypes, flags)]
100    except KeyError:
101        class CFunctionType(_CFuncPtr):
102            _argtypes_ = argtypes
103            _restype_ = restype
104            _flags_ = flags
105        _c_functype_cache[(restype, argtypes, flags)] = CFunctionType
106        return CFunctionType
107
108if _os.name == "nt":
109    from _ctypes import LoadLibrary as _dlopen
110    from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL
111
112    _win_functype_cache = {}
113    def WINFUNCTYPE(restype, *argtypes, **kw):
114        # docstring set later (very similar to CFUNCTYPE.__doc__)
115        flags = _FUNCFLAG_STDCALL
116        if kw.pop("use_errno", False):
117            flags |= _FUNCFLAG_USE_ERRNO
118        if kw.pop("use_last_error", False):
119            flags |= _FUNCFLAG_USE_LASTERROR
120        if kw:
121            raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
122        try:
123            return _win_functype_cache[(restype, argtypes, flags)]
124        except KeyError:
125            class WinFunctionType(_CFuncPtr):
126                _argtypes_ = argtypes
127                _restype_ = restype
128                _flags_ = flags
129            _win_functype_cache[(restype, argtypes, flags)] = WinFunctionType
130            return WinFunctionType
131    if WINFUNCTYPE.__doc__:
132        WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE")
133
134elif _os.name == "posix":
135    from _ctypes import dlopen as _dlopen
136
137from _ctypes import sizeof, byref, addressof, alignment, resize
138from _ctypes import get_errno, set_errno
139from _ctypes import _SimpleCData
140
141def _check_size(typ, typecode=None):
142    # Check if sizeof(ctypes_type) against struct.calcsize.  This
143    # should protect somewhat against a misconfigured libffi.
144    from struct import calcsize
145    if typecode is None:
146        # Most _type_ codes are the same as used in struct
147        typecode = typ._type_
148    actual, required = sizeof(typ), calcsize(typecode)
149    if actual != required:
150        raise SystemError("sizeof(%s) wrong: %d instead of %d" % \
151                          (typ, actual, required))
152
153class py_object(_SimpleCData):
154    _type_ = "O"
155    def __repr__(self):
156        try:
157            return super().__repr__()
158        except ValueError:
159            return "%s(<NULL>)" % type(self).__name__
160_check_size(py_object, "P")
161
162class c_short(_SimpleCData):
163    _type_ = "h"
164_check_size(c_short)
165
166class c_ushort(_SimpleCData):
167    _type_ = "H"
168_check_size(c_ushort)
169
170class c_long(_SimpleCData):
171    _type_ = "l"
172_check_size(c_long)
173
174class c_ulong(_SimpleCData):
175    _type_ = "L"
176_check_size(c_ulong)
177
178if _calcsize("i") == _calcsize("l"):
179    # if int and long have the same size, make c_int an alias for c_long
180    c_int = c_long
181    c_uint = c_ulong
182else:
183    class c_int(_SimpleCData):
184        _type_ = "i"
185    _check_size(c_int)
186
187    class c_uint(_SimpleCData):
188        _type_ = "I"
189    _check_size(c_uint)
190
191class c_float(_SimpleCData):
192    _type_ = "f"
193_check_size(c_float)
194
195class c_double(_SimpleCData):
196    _type_ = "d"
197_check_size(c_double)
198
199class c_longdouble(_SimpleCData):
200    _type_ = "g"
201if sizeof(c_longdouble) == sizeof(c_double):
202    c_longdouble = c_double
203
204if _calcsize("l") == _calcsize("q"):
205    # if long and long long have the same size, make c_longlong an alias for c_long
206    c_longlong = c_long
207    c_ulonglong = c_ulong
208else:
209    class c_longlong(_SimpleCData):
210        _type_ = "q"
211    _check_size(c_longlong)
212
213    class c_ulonglong(_SimpleCData):
214        _type_ = "Q"
215    ##    def from_param(cls, val):
216    ##        return ('d', float(val), val)
217    ##    from_param = classmethod(from_param)
218    _check_size(c_ulonglong)
219
220class c_ubyte(_SimpleCData):
221    _type_ = "B"
222c_ubyte.__ctype_le__ = c_ubyte.__ctype_be__ = c_ubyte
223# backward compatibility:
224##c_uchar = c_ubyte
225_check_size(c_ubyte)
226
227class c_byte(_SimpleCData):
228    _type_ = "b"
229c_byte.__ctype_le__ = c_byte.__ctype_be__ = c_byte
230_check_size(c_byte)
231
232class c_char(_SimpleCData):
233    _type_ = "c"
234c_char.__ctype_le__ = c_char.__ctype_be__ = c_char
235_check_size(c_char)
236
237class c_char_p(_SimpleCData):
238    _type_ = "z"
239    def __repr__(self):
240        return "%s(%s)" % (self.__class__.__name__, c_void_p.from_buffer(self).value)
241_check_size(c_char_p, "P")
242
243class c_void_p(_SimpleCData):
244    _type_ = "P"
245c_voidp = c_void_p # backwards compatibility (to a bug)
246_check_size(c_void_p)
247
248class c_bool(_SimpleCData):
249    _type_ = "?"
250
251from _ctypes import POINTER, pointer, _pointer_type_cache
252
253class c_wchar_p(_SimpleCData):
254    _type_ = "Z"
255    def __repr__(self):
256        return "%s(%s)" % (self.__class__.__name__, c_void_p.from_buffer(self).value)
257
258class c_wchar(_SimpleCData):
259    _type_ = "u"
260
261def _reset_cache():
262    _pointer_type_cache.clear()
263    _c_functype_cache.clear()
264    if _os.name == "nt":
265        _win_functype_cache.clear()
266    # _SimpleCData.c_wchar_p_from_param
267    POINTER(c_wchar).from_param = c_wchar_p.from_param
268    # _SimpleCData.c_char_p_from_param
269    POINTER(c_char).from_param = c_char_p.from_param
270    _pointer_type_cache[None] = c_void_p
271
272def create_unicode_buffer(init, size=None):
273    """create_unicode_buffer(aString) -> character array
274    create_unicode_buffer(anInteger) -> character array
275    create_unicode_buffer(aString, anInteger) -> character array
276    """
277    if isinstance(init, str):
278        if size is None:
279            if sizeof(c_wchar) == 2:
280                # UTF-16 requires a surrogate pair (2 wchar_t) for non-BMP
281                # characters (outside [U+0000; U+FFFF] range). +1 for trailing
282                # NUL character.
283                size = sum(2 if ord(c) > 0xFFFF else 1 for c in init) + 1
284            else:
285                # 32-bit wchar_t (1 wchar_t per Unicode character). +1 for
286                # trailing NUL character.
287                size = len(init) + 1
288        _sys.audit("ctypes.create_unicode_buffer", init, size)
289        buftype = c_wchar * size
290        buf = buftype()
291        buf.value = init
292        return buf
293    elif isinstance(init, int):
294        _sys.audit("ctypes.create_unicode_buffer", None, init)
295        buftype = c_wchar * init
296        buf = buftype()
297        return buf
298    raise TypeError(init)
299
300
301# XXX Deprecated
302def SetPointerType(pointer, cls):
303    if _pointer_type_cache.get(cls, None) is not None:
304        raise RuntimeError("This type already exists in the cache")
305    if id(pointer) not in _pointer_type_cache:
306        raise RuntimeError("What's this???")
307    pointer.set_type(cls)
308    _pointer_type_cache[cls] = pointer
309    del _pointer_type_cache[id(pointer)]
310
311# XXX Deprecated
312def ARRAY(typ, len):
313    return typ * len
314
315################################################################
316
317
318class CDLL(object):
319    """An instance of this class represents a loaded dll/shared
320    library, exporting functions using the standard C calling
321    convention (named 'cdecl' on Windows).
322
323    The exported functions can be accessed as attributes, or by
324    indexing with the function name.  Examples:
325
326    <obj>.qsort -> callable object
327    <obj>['qsort'] -> callable object
328
329    Calling the functions releases the Python GIL during the call and
330    reacquires it afterwards.
331    """
332    _func_flags_ = _FUNCFLAG_CDECL
333    _func_restype_ = c_int
334    # default values for repr
335    _name = '<uninitialized>'
336    _handle = 0
337    _FuncPtr = None
338
339    def __init__(self, name, mode=DEFAULT_MODE, handle=None,
340                 use_errno=False,
341                 use_last_error=False,
342                 winmode=None):
343        self._name = name
344        flags = self._func_flags_
345        if use_errno:
346            flags |= _FUNCFLAG_USE_ERRNO
347        if use_last_error:
348            flags |= _FUNCFLAG_USE_LASTERROR
349        if _sys.platform.startswith("aix"):
350            """When the name contains ".a(" and ends with ")",
351               e.g., "libFOO.a(libFOO.so)" - this is taken to be an
352               archive(member) syntax for dlopen(), and the mode is adjusted.
353               Otherwise, name is presented to dlopen() as a file argument.
354            """
355            if name and name.endswith(")") and ".a(" in name:
356                mode |= ( _os.RTLD_MEMBER | _os.RTLD_NOW )
357        if _os.name == "nt":
358            if winmode is not None:
359                mode = winmode
360            else:
361                import nt
362                mode = nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
363                if '/' in name or '\\' in name:
364                    self._name = nt._getfullpathname(self._name)
365                    mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
366
367        class _FuncPtr(_CFuncPtr):
368            _flags_ = flags
369            _restype_ = self._func_restype_
370        self._FuncPtr = _FuncPtr
371
372        if handle is None:
373            self._handle = _dlopen(self._name, mode)
374        else:
375            self._handle = handle
376
377    def __repr__(self):
378        return "<%s '%s', handle %x at %#x>" % \
379               (self.__class__.__name__, self._name,
380                (self._handle & (_sys.maxsize*2 + 1)),
381                id(self) & (_sys.maxsize*2 + 1))
382
383    def __getattr__(self, name):
384        if name.startswith('__') and name.endswith('__'):
385            raise AttributeError(name)
386        func = self.__getitem__(name)
387        setattr(self, name, func)
388        return func
389
390    def __getitem__(self, name_or_ordinal):
391        func = self._FuncPtr((name_or_ordinal, self))
392        if not isinstance(name_or_ordinal, int):
393            func.__name__ = name_or_ordinal
394        return func
395
396class PyDLL(CDLL):
397    """This class represents the Python library itself.  It allows
398    accessing Python API functions.  The GIL is not released, and
399    Python exceptions are handled correctly.
400    """
401    _func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
402
403if _os.name == "nt":
404
405    class WinDLL(CDLL):
406        """This class represents a dll exporting functions using the
407        Windows stdcall calling convention.
408        """
409        _func_flags_ = _FUNCFLAG_STDCALL
410
411    # XXX Hm, what about HRESULT as normal parameter?
412    # Mustn't it derive from c_long then?
413    from _ctypes import _check_HRESULT, _SimpleCData
414    class HRESULT(_SimpleCData):
415        _type_ = "l"
416        # _check_retval_ is called with the function's result when it
417        # is used as restype.  It checks for the FAILED bit, and
418        # raises an OSError if it is set.
419        #
420        # The _check_retval_ method is implemented in C, so that the
421        # method definition itself is not included in the traceback
422        # when it raises an error - that is what we want (and Python
423        # doesn't have a way to raise an exception in the caller's
424        # frame).
425        _check_retval_ = _check_HRESULT
426
427    class OleDLL(CDLL):
428        """This class represents a dll exporting functions using the
429        Windows stdcall calling convention, and returning HRESULT.
430        HRESULT error values are automatically raised as OSError
431        exceptions.
432        """
433        _func_flags_ = _FUNCFLAG_STDCALL
434        _func_restype_ = HRESULT
435
436class LibraryLoader(object):
437    def __init__(self, dlltype):
438        self._dlltype = dlltype
439
440    def __getattr__(self, name):
441        if name[0] == '_':
442            raise AttributeError(name)
443        dll = self._dlltype(name)
444        setattr(self, name, dll)
445        return dll
446
447    def __getitem__(self, name):
448        return getattr(self, name)
449
450    def LoadLibrary(self, name):
451        return self._dlltype(name)
452
453cdll = LibraryLoader(CDLL)
454pydll = LibraryLoader(PyDLL)
455
456if _os.name == "nt":
457    pythonapi = PyDLL("python dll", None, _sys.dllhandle)
458elif _sys.platform == "cygwin":
459    pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2])
460else:
461    pythonapi = PyDLL(None)
462
463
464if _os.name == "nt":
465    windll = LibraryLoader(WinDLL)
466    oledll = LibraryLoader(OleDLL)
467
468    GetLastError = windll.kernel32.GetLastError
469    from _ctypes import get_last_error, set_last_error
470
471    def WinError(code=None, descr=None):
472        if code is None:
473            code = GetLastError()
474        if descr is None:
475            descr = FormatError(code).strip()
476        return OSError(None, descr, None, code)
477
478if sizeof(c_uint) == sizeof(c_void_p):
479    c_size_t = c_uint
480    c_ssize_t = c_int
481elif sizeof(c_ulong) == sizeof(c_void_p):
482    c_size_t = c_ulong
483    c_ssize_t = c_long
484elif sizeof(c_ulonglong) == sizeof(c_void_p):
485    c_size_t = c_ulonglong
486    c_ssize_t = c_longlong
487
488# functions
489
490from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr
491
492## void *memmove(void *, const void *, size_t);
493memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)
494
495## void *memset(void *, int, size_t)
496memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)
497
498def PYFUNCTYPE(restype, *argtypes):
499    class CFunctionType(_CFuncPtr):
500        _argtypes_ = argtypes
501        _restype_ = restype
502        _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
503    return CFunctionType
504
505_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
506def cast(obj, typ):
507    return _cast(obj, obj, typ)
508
509_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
510def string_at(ptr, size=-1):
511    """string_at(addr[, size]) -> string
512
513    Return the string at addr."""
514    return _string_at(ptr, size)
515
516try:
517    from _ctypes import _wstring_at_addr
518except ImportError:
519    pass
520else:
521    _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr)
522    def wstring_at(ptr, size=-1):
523        """wstring_at(addr[, size]) -> string
524
525        Return the string at addr."""
526        return _wstring_at(ptr, size)
527
528
529if _os.name == "nt": # COM stuff
530    def DllGetClassObject(rclsid, riid, ppv):
531        try:
532            ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*'])
533        except ImportError:
534            return -2147221231 # CLASS_E_CLASSNOTAVAILABLE
535        else:
536            return ccom.DllGetClassObject(rclsid, riid, ppv)
537
538    def DllCanUnloadNow():
539        try:
540            ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*'])
541        except ImportError:
542            return 0 # S_OK
543        return ccom.DllCanUnloadNow()
544
545from ctypes._endian import BigEndianStructure, LittleEndianStructure
546
547# Fill in specifically-sized types
548c_int8 = c_byte
549c_uint8 = c_ubyte
550for kind in [c_short, c_int, c_long, c_longlong]:
551    if sizeof(kind) == 2: c_int16 = kind
552    elif sizeof(kind) == 4: c_int32 = kind
553    elif sizeof(kind) == 8: c_int64 = kind
554for kind in [c_ushort, c_uint, c_ulong, c_ulonglong]:
555    if sizeof(kind) == 2: c_uint16 = kind
556    elif sizeof(kind) == 4: c_uint32 = kind
557    elif sizeof(kind) == 8: c_uint64 = kind
558del(kind)
559
560_reset_cache()
561