1  /*
2  ** Convert objects from Python to CoreFoundation and vice-versa.
3  */
4  
5  #include <CoreServices/CoreServices.h>
6  
7  #include "Python.h"
8  #include "pymactoolbox.h"
9  #include "pycfbridge.h"
10  
11  
12  /* ---------------------------------------- */
13  /* CoreFoundation objects to Python objects */
14  /* ---------------------------------------- */
15  
16  PyObject *
PyCF_CF2Python(CFTypeRef src)17  PyCF_CF2Python(CFTypeRef src) {
18      CFTypeID typeid;
19  
20      if( src == NULL ) {
21          Py_INCREF(Py_None);
22          return Py_None;
23      }
24      typeid = CFGetTypeID(src);
25      if (typeid == CFArrayGetTypeID())
26          return PyCF_CF2Python_sequence((CFArrayRef)src);
27      if (typeid == CFDictionaryGetTypeID())
28          return PyCF_CF2Python_mapping((CFDictionaryRef)src);
29      return PyCF_CF2Python_simple(src);
30  }
31  
32  PyObject *
PyCF_CF2Python_sequence(CFArrayRef src)33  PyCF_CF2Python_sequence(CFArrayRef src) {
34      int size = CFArrayGetCount(src);
35      PyObject *rv;
36      CFTypeRef item_cf;
37      PyObject *item_py = NULL;
38      int i;
39  
40      if ( (rv=PyList_New(size)) == NULL )
41          return NULL;
42      for(i=0; i<size; i++) {
43          item_cf = CFArrayGetValueAtIndex(src, i);
44          if (item_cf == NULL ) goto err;
45          item_py = PyCF_CF2Python(item_cf);
46          if (item_py == NULL ) goto err;
47          if (PyList_SetItem(rv, i, item_py) < 0) goto err;
48          item_py = NULL;
49      }
50      return rv;
51  err:
52      Py_XDECREF(item_py);
53      Py_DECREF(rv);
54      return NULL;
55  }
56  
57  PyObject *
PyCF_CF2Python_mapping(CFTypeRef src)58  PyCF_CF2Python_mapping(CFTypeRef src) {
59      int size = CFDictionaryGetCount(src);
60      PyObject *rv = NULL;
61      CFTypeRef *allkeys = NULL, *allvalues = NULL;
62      CFTypeRef key_cf, value_cf;
63      PyObject *key_py = NULL, *value_py = NULL;
64      int i;
65  
66      allkeys = malloc(size*sizeof(CFTypeRef *));
67      if (allkeys == NULL) {
68          PyErr_NoMemory();
69          goto err;
70      }
71      allvalues = malloc(size*sizeof(CFTypeRef *));
72      if (allvalues == NULL) {
73          PyErr_NoMemory();
74          goto err;
75      }
76      if ( (rv=PyDict_New()) == NULL ) goto err;
77      CFDictionaryGetKeysAndValues(src, allkeys, allvalues);
78      for(i=0; i<size; i++) {
79          key_cf = allkeys[i];
80          value_cf = allvalues[i];
81          key_py = PyCF_CF2Python(key_cf);
82          if (key_py == NULL ) goto err;
83          value_py = PyCF_CF2Python(value_cf);
84          if (value_py == NULL ) goto err;
85          if (PyDict_SetItem(rv, key_py, value_py) < 0) goto err;
86          key_py = NULL;
87          value_py = NULL;
88      }
89      return rv;
90  err:
91      Py_XDECREF(key_py);
92      Py_XDECREF(value_py);
93      Py_XDECREF(rv);
94      free(allkeys);
95      free(allvalues);
96      return NULL;
97  }
98  
99  PyObject *
PyCF_CF2Python_simple(CFTypeRef src)100  PyCF_CF2Python_simple(CFTypeRef src) {
101      CFTypeID typeid;
102  
103      typeid = CFGetTypeID(src);
104      if (typeid == CFStringGetTypeID())
105          return PyCF_CF2Python_string((CFStringRef)src);
106      if (typeid == CFBooleanGetTypeID())
107          return PyBool_FromLong((long)CFBooleanGetValue(src));
108      if (typeid == CFNumberGetTypeID()) {
109          if (CFNumberIsFloatType(src)) {
110              double d;
111              CFNumberGetValue(src, kCFNumberDoubleType, &d);
112              return PyFloat_FromDouble(d);
113          } else {
114              long l;
115              if (!CFNumberGetValue(src, kCFNumberLongType, &l))
116                  /* XXXX Out of range! */;
117              return PyInt_FromLong(l);
118          }
119      }
120      /* XXXX Should return as CFTypeRef, really... */
121      PyMac_Error(resNotFound);
122      return NULL;
123  }
124  
125  /* Unsure - Return unicode or 8 bit strings? */
126  PyObject *
PyCF_CF2Python_string(CFStringRef src)127  PyCF_CF2Python_string(CFStringRef src) {
128      int size = CFStringGetLength(src)+1;
129      Py_UNICODE *data = malloc(size*sizeof(Py_UNICODE));
130      CFRange range;
131      PyObject *rv;
132  
133      range.location = 0;
134      range.length = size;
135      if( data == NULL ) return PyErr_NoMemory();
136      CFStringGetCharacters(src, range, data);
137      rv = (PyObject *)PyUnicode_FromUnicode(data, size-1);
138      free(data);
139      return rv;
140  }
141  
142  /* ---------------------------------------- */
143  /* Python objects to CoreFoundation objects */
144  /* ---------------------------------------- */
145  
146  int
PyCF_Python2CF(PyObject * src,CFTypeRef * dst)147  PyCF_Python2CF(PyObject *src, CFTypeRef *dst) {
148  
149      if (PyString_Check(src) || PyUnicode_Check(src))
150          return PyCF_Python2CF_simple(src, dst);
151      if (PySequence_Check(src))
152          return PyCF_Python2CF_sequence(src, (CFArrayRef *)dst);
153      if (PyMapping_Check(src))
154          return PyCF_Python2CF_mapping(src, (CFDictionaryRef *)dst);
155      return PyCF_Python2CF_simple(src, dst);
156  }
157  
158  int
PyCF_Python2CF_sequence(PyObject * src,CFArrayRef * dst)159  PyCF_Python2CF_sequence(PyObject *src, CFArrayRef *dst) {
160      CFMutableArrayRef rv = NULL;
161      CFTypeRef item_cf = NULL;
162      PyObject *item_py = NULL;
163      int size, i;
164  
165      if( !PySequence_Check(src) ) {
166          PyErr_Format(PyExc_TypeError,
167              "Cannot convert %.500s objects to CFArray",
168              src->ob_type->tp_name);
169          return 0;
170      }
171      size = PySequence_Size(src);
172      rv = CFArrayCreateMutable((CFAllocatorRef)NULL, size, &kCFTypeArrayCallBacks);
173      if (rv == NULL) {
174          PyMac_Error(resNotFound);
175          goto err;
176      }
177  
178      for( i=0; i<size; i++) {
179          item_py = PySequence_GetItem(src, i);
180          if (item_py == NULL) goto err;
181          if ( !PyCF_Python2CF(item_py, &item_cf)) goto err;
182          Py_DECREF(item_py);
183          CFArraySetValueAtIndex(rv, i, item_cf);
184          CFRelease(item_cf);
185          item_cf = NULL;
186      }
187      *dst = rv;
188      return 1;
189  err:
190      Py_XDECREF(item_py);
191      if (rv) CFRelease(rv);
192      if (item_cf) CFRelease(item_cf);
193      return 0;
194  }
195  
196  int
PyCF_Python2CF_mapping(PyObject * src,CFDictionaryRef * dst)197  PyCF_Python2CF_mapping(PyObject *src, CFDictionaryRef *dst) {
198      CFMutableDictionaryRef rv = NULL;
199      PyObject *aslist = NULL;
200      CFTypeRef key_cf = NULL, value_cf = NULL;
201      PyObject *item_py = NULL, *key_py = NULL, *value_py = NULL;
202      int size, i;
203  
204      if( !PyMapping_Check(src) ) {
205          PyErr_Format(PyExc_TypeError,
206              "Cannot convert %.500s objects to CFDictionary",
207              src->ob_type->tp_name);
208          return 0;
209      }
210      size = PyMapping_Size(src);
211      rv = CFDictionaryCreateMutable((CFAllocatorRef)NULL, size,
212                                      &kCFTypeDictionaryKeyCallBacks,
213                                      &kCFTypeDictionaryValueCallBacks);
214      if (rv == NULL) {
215          PyMac_Error(resNotFound);
216          goto err;
217      }
218      if ( (aslist = PyMapping_Items(src)) == NULL ) goto err;
219  
220      for( i=0; i<size; i++) {
221          item_py = PySequence_GetItem(aslist, i);
222          if (item_py == NULL) goto err;
223          if (!PyArg_ParseTuple(item_py, "OO", &key_py, &value_py)) goto err;
224          if ( !PyCF_Python2CF(key_py, &key_cf) ) goto err;
225          if ( !PyCF_Python2CF(value_py, &value_cf) ) goto err;
226          CFDictionaryAddValue(rv, key_cf, value_cf);
227          CFRelease(key_cf);
228          key_cf = NULL;
229          CFRelease(value_cf);
230          value_cf = NULL;
231      }
232      *dst = rv;
233      return 1;
234  err:
235      Py_XDECREF(item_py);
236      Py_XDECREF(aslist);
237      if (rv) CFRelease(rv);
238      if (key_cf) CFRelease(key_cf);
239      if (value_cf) CFRelease(value_cf);
240      return 0;
241  }
242  
243  int
PyCF_Python2CF_simple(PyObject * src,CFTypeRef * dst)244  PyCF_Python2CF_simple(PyObject *src, CFTypeRef *dst) {
245  
246  #if 0
247      if (PyObject_HasAttrString(src, "CFType")) {
248          *dst = PyObject_CallMethod(src, "CFType", "");
249          return (*dst != NULL);
250      }
251  #endif
252      if (PyString_Check(src) || PyUnicode_Check(src))
253          return PyCF_Python2CF_string(src, (CFStringRef *)dst);
254      if (PyBool_Check(src)) {
255          if (src == Py_True)
256              *dst = kCFBooleanTrue;
257          else
258              *dst = kCFBooleanFalse;
259          return 1;
260      }
261      if (PyInt_Check(src)) {
262          long v = PyInt_AsLong(src);
263          *dst = CFNumberCreate(NULL, kCFNumberLongType, &v);
264          return 1;
265      }
266      if (PyFloat_Check(src)) {
267          double d = PyFloat_AsDouble(src);
268          *dst = CFNumberCreate(NULL, kCFNumberDoubleType, &d);
269          return 1;
270      }
271  
272      PyErr_Format(PyExc_TypeError,
273                "Cannot convert %.500s objects to CFType",
274                                   src->ob_type->tp_name);
275      return 0;
276  }
277  
278  int
PyCF_Python2CF_string(PyObject * src,CFStringRef * dst)279  PyCF_Python2CF_string(PyObject *src, CFStringRef *dst) {
280      char *chars;
281      CFIndex size;
282      UniChar *unichars;
283  
284      if (PyString_Check(src)) {
285          if (!PyArg_Parse(src, "es", "ascii", &chars))
286              return 0; /* This error is more descriptive than the general one below */
287          *dst = CFStringCreateWithCString((CFAllocatorRef)NULL, chars, kCFStringEncodingASCII);
288          PyMem_Free(chars);
289          return 1;
290      }
291      if (PyUnicode_Check(src)) {
292          /* We use the CF types here, if Python was configured differently that will give an error */
293          size = PyUnicode_GetSize(src);
294          if ((unichars = PyUnicode_AsUnicode(src)) == NULL ) goto err;
295          *dst = CFStringCreateWithCharacters((CFAllocatorRef)NULL, unichars, size);
296          return 1;
297      }
298  err:
299      PyErr_Format(PyExc_TypeError,
300                "Cannot convert %.500s objects to CFString",
301                                   src->ob_type->tp_name);
302      return 0;
303  }
304