1/* 2 * Special wrappers for pointer values 3 * 4 * Pointer arguments to methods can be split into 3 classes: 5 * - Pass-by-reference arguments (in, out, inout). 6 * This is supported by using the modifiers _C_IN, _C_OUT and _C_INOUT 7 * - Pointers to buffers 8 * This requires special support, you can't detect the size of the buffer 9 * from the signature 10 * - Opaque values 11 * Types like FSRef are for all intents and purposes opaque values, 12 * that happen to be represented using pointers (to structs). The functions 13 * in this file allow extension modules to register functions to convert these 14 * values to and from their Python representation. 15 * 16 * NOTE: 17 * - The pythonify and depythonify functions have the same interface as 18 * the *_New and *_Convert functions in MacPython (pymactoolbox.h), this 19 * makes it easier to interface with that packages. 20 */ 21#include "pyobjc.h" 22 23 24#import <CoreFoundation/CoreFoundation.h> 25 26struct wrapper { 27 const char* signature; 28 int offset; 29 PyObject* (*pythonify)(void*); 30 int (*depythonify)(PyObject*, void*); 31}; 32 33/* Using an array is pretty lame, this needs to be replaced by a more 34 * efficient datastructure. However: As long as their is only a limited 35 * number of custom wrappers this should not be a problem. 36 */ 37static struct wrapper* items = 0; 38static Py_ssize_t item_count = 0; 39 40/* 41 * If signature is a pointer to a structure return the index of the character 42 * just beyond the end of the struct name. This information is needed because 43 * @encode(struct foo*) can return two different strings: 44 * 1) ^{foo} if the compiler has not yet seen a full definition of struct foo 45 * 2) ^{foo=...} if the compiler has not yet seen a full definition of 46 * the struct 47 * We want to treat those two pointer as the same type, therefore we need to 48 * ignore everything beyond the end of the struct name. 49 */ 50static int find_end_of_structname(const char* signature) { 51 if (signature[1] == _C_CONST && signature[2] == _C_STRUCT_B) { 52 char* end1; 53 char* end2; 54 55 end1 = strchr(signature, _C_STRUCT_E); 56 end2 = strchr(signature, '='); 57 58 if (end2 == NULL) { 59 return end1 - signature; 60 } else { 61 return end2 - signature; 62 } 63 64 } else if (signature[1] == _C_STRUCT_B) { 65 char* end1; 66 char* end2; 67 68 end1 = strchr(signature, _C_STRUCT_E); 69 end2 = strchr(signature, '='); 70 71 if (end2 == NULL) { 72 return end1 - signature; 73 } else { 74 return end2 - signature; 75 } 76 } 77 return strlen(signature); 78} 79 80static struct wrapper* 81FindWrapper(const char* signature) 82{ 83 Py_ssize_t i; 84 Py_ssize_t len; 85 86 len = strlen(signature); 87 88 for (i = 0; i < item_count; i++) { 89 if (strncmp(signature, items[i].signature, items[i].offset) == 0) { 90 /* See comment just above find_end_of_structname */ 91 if (signature[1] == _C_CONST && signature[2] == _C_STRUCT_B) { 92 char ch = signature[items[i].offset]; 93 if (ch == '=' || ch == _C_STRUCT_E) { 94 return items + i; 95 } 96 97 } else if (signature[1] == _C_STRUCT_B) { 98 char ch = signature[items[i].offset]; 99 if (ch == '=' || ch == _C_STRUCT_E) { 100 return items + i; 101 } 102 103 } else { 104 if (signature[items[i].offset] == '\0') { 105 return items + i; 106 } 107 } 108 } 109 } 110 return NULL; 111} 112 113static PyObject* 114ID_to_py(void* idValue) 115{ 116 return pythonify_c_value(@encode(id), &idValue); 117} 118 119static int 120py_to_ID(PyObject* obj, void* output) 121{ 122 return depythonify_c_value(@encode(id), obj, output); 123} 124 125int PyObjCPointerWrapper_RegisterID(const char *signature) { 126 return PyObjCPointerWrapper_Register(signature, 127 (PyObjCPointerWrapper_ToPythonFunc)&ID_to_py, 128 (PyObjCPointerWrapper_FromPythonFunc)&py_to_ID); 129} 130 131int 132PyObjCPointerWrapper_Register( 133 const char* signature, 134 PyObjCPointerWrapper_ToPythonFunc pythonify, 135 PyObjCPointerWrapper_FromPythonFunc depythonify 136 137 ) 138{ 139 struct wrapper* value; 140 141 /* 142 * Check if we already have a wrapper, if so replace that. 143 * This makes it possible to replace a default wrapper by something 144 * better. 145 */ 146 if (signature == NULL) { 147 return -1; 148 } 149 value = FindWrapper(signature); 150 if (value != NULL) { 151 value->pythonify = pythonify; 152 value->depythonify = depythonify; 153 return 0; 154 } 155 156 if (items == NULL) { 157 items = PyMem_Malloc(sizeof(struct wrapper)); 158 if (items == NULL) { 159 PyErr_NoMemory(); 160 return -1; 161 } 162 item_count = 1; 163 } else { 164 struct wrapper* tmp; 165 166 tmp = PyMem_Realloc( 167 items, sizeof(struct wrapper) * (item_count+1)); 168 if (tmp == NULL) { 169 PyErr_NoMemory(); 170 return -1; 171 } 172 items = tmp; 173 item_count ++; 174 } 175 176 value = items + (item_count-1); 177 178 value->signature = PyObjCUtil_Strdup(signature); 179 if (value->signature == NULL) { 180 PyErr_NoMemory(); 181 item_count --; 182 return -1; 183 } 184 185 value->offset = find_end_of_structname(value->signature); 186 187 value->pythonify = pythonify; 188 value->depythonify = depythonify; 189 190 return 0; 191} 192 193 194 195PyObject* 196PyObjCPointerWrapper_ToPython(const char* type, void* datum) 197{ 198 struct wrapper* item; 199 PyObject* result; 200 201 item = FindWrapper(type); 202 if (item == NULL) { 203 return NULL; 204 } 205 206 if (item->pythonify == ID_to_py) { 207 result = PyObjC_FindPythonProxy(*(id*)datum); 208 if (result != NULL) { 209 return result; 210 211 } else if (*(void**)datum == kCFAllocatorUseContext) { 212 /* kCFAllocatorUseContext is a bit too magic for its 213 * own good. 214 * 215 * Note that this is a crude hack, but as long as this 216 * is the only such object I don't think its worthwhile 217 * to add generic support for this. 218 */ 219 result = PyObjCCF_NewSpecial2( 220 CFAllocatorGetTypeID(), *(void**)datum); 221 222 PyObjC_RegisterPythonProxy(*(id*)datum, result); 223 return result; 224 } 225 } 226 227 result = item->pythonify(*(void**)datum); 228 return result; 229} 230 231 232int 233PyObjCPointerWrapper_FromPython( 234 const char* type, PyObject* value, void* datum) 235{ 236 struct wrapper* item; 237 int r; 238 239 if (value == PyObjC_NULL) { 240 *(void**)datum = NULL; 241 return 0; 242 } 243 244 item = FindWrapper(type); 245 if (item == NULL) { 246 return -1; 247 } 248 249 r = item->depythonify(value, datum); 250 if (r == 0) { 251 return 0; 252 } else { 253 return -1; 254 } 255} 256 257int PyObjCPointerWrapper_HaveWrapper(const char* type) 258{ 259 return (FindWrapper(type) != NULL); 260} 261 262 263static PyObject* 264PyObjectPtr_New(void *obj) 265{ 266 return (PyObject*)obj; 267} 268 269static int 270PyObjectPtr_Convert(PyObject* obj, void* pObj) 271{ 272 *(void**)pObj = (void *)obj; 273 return 0; 274} 275 276#if PY_MAJOR_VERSION == 2 277 278static int dontClose(FILE* fp __attribute__((__unused__))) 279{ 280 return 0; 281} 282static PyObject* 283FILE_New(void *obj) 284{ 285 FILE* fp = (FILE*)obj; 286 char* mode = "r"; 287 288#if defined(__SRW) 289 /* This is a hack, but allows us to pass the right file mode into 290 * Python. 291 */ 292 if (fp->_flags & __SWR) { 293 mode = "w"; 294 } else if (fp->_flags & __SRW) { 295 mode = "w+"; 296 } 297 298#endif 299 return PyFile_FromFile(fp, "<objc-file>", mode, dontClose); 300} 301 302static int 303FILE_Convert(PyObject* obj, void* pObj) 304{ 305 *(FILE**)pObj = PyFile_AsFile(obj); 306 if (*(FILE**)pObj == NULL) { 307 return 1; 308 } 309 310 return 0; 311} 312 313#endif 314 315/* 316 * Generic CF type support 317 */ 318static PyObject* 319CF_to_py(void* cfValue) 320{ 321 return PyObjC_IDToCFType((id)cfValue); 322} 323 324static int 325py_to_CF(PyObject* obj, void* output) 326{ 327 id tmp = PyObjC_CFTypeToID(obj); 328 if (tmp == NULL && PyErr_Occurred()) { 329 return -1; 330 } 331 *(void**)output = tmp; 332 return 0; 333} 334 335int PyObjCPointerWrapper_RegisterCF(const char *signature) { 336 return PyObjCPointerWrapper_Register(signature, 337 (PyObjCPointerWrapper_ToPythonFunc)&CF_to_py, 338 (PyObjCPointerWrapper_FromPythonFunc)&py_to_CF); 339} 340 341 342int 343PyObjCPointerWrapper_Init(void) 344{ 345 int r = 0; 346 347 r = PyObjCPointerWrapper_RegisterCF(@encode(CFURLRef)); 348 if (r == -1) return -1; 349 350 r = PyObjCPointerWrapper_RegisterCF(@encode(CFSetRef)); 351 if (r == -1) return -1; 352 353#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 354 r = PyObjCPointerWrapper_RegisterCF(@encode(CFNetServiceRef)); 355 if (r == -1) return -1; 356#endif 357 358 r = PyObjCPointerWrapper_RegisterCF(@encode(CFReadStreamRef)); 359 if (r == -1) return -1; 360 361 r = PyObjCPointerWrapper_RegisterCF(@encode(CFRunLoopRef)); 362 if (r == -1) return -1; 363 364 r = PyObjCPointerWrapper_Register(@encode(PyObject*), 365 PyObjectPtr_New, PyObjectPtr_Convert); 366 if (r == -1) return -1; 367 368#if PY_MAJOR_VERSION == 2 369 r = PyObjCPointerWrapper_Register(@encode(FILE*), 370 FILE_New, FILE_Convert); 371 if (r == -1) return -1; 372#endif 373 374 return 0; 375} 376