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