[sourcescanner] Plug a couple of python leaks
[platform/upstream/gobject-introspection.git] / giscanner / giscannermodule.c
1 /* GObject introspection: scanner
2  *
3  * Copyright (C) 2008  Johan Dahlin <johan@gnome.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25 #include <Python.h>
26 #include "sourcescanner.h"
27 #include "grealpath.h"
28
29 #ifdef _WIN32
30 #include <fcntl.h>
31 #include <io.h>
32 #define WIN32_LEAN_AND_MEAN
33 #define STRICT
34 #include <windows.h>
35 #endif
36
37 DL_EXPORT(void) init_giscanner(void);
38
39 #define NEW_CLASS(ctype, name, cname)         \
40 static const PyMethodDef _Py##cname##_methods[];    \
41 PyTypeObject Py##cname##_Type = {             \
42     PyObject_HEAD_INIT(NULL)                  \
43     0,                                        \
44     "scanner." name,                          \
45     sizeof(ctype),                    \
46     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,             \
47     0, 0, 0, 0, 0, 0,                         \
48     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, \
49     NULL, 0, 0, 0,                            \
50     0,        \
51     0, 0,                                     \
52     0,                                        \
53     0, 0, NULL, NULL, 0, 0,                   \
54     0             \
55 }
56
57 #define REGISTER_TYPE(d, name, type)          \
58     type.ob_type = &PyType_Type;              \
59     type.tp_alloc = PyType_GenericAlloc;      \
60     type.tp_new = PyType_GenericNew;          \
61     if (PyType_Ready (&type))                 \
62         return;                               \
63     PyDict_SetItemString (d, name, (PyObject *)&type); \
64     Py_INCREF (&type);
65
66 typedef struct {
67   PyObject_HEAD
68   GISourceType *type;
69 } PyGISourceType;
70
71 static PyObject * pygi_source_type_new (GISourceType *type);
72
73 typedef struct {
74   PyObject_HEAD
75   GISourceSymbol *symbol;
76 } PyGISourceSymbol;
77
78 typedef struct {
79   PyObject_HEAD
80   GISourceScanner *scanner;
81 } PyGISourceScanner;
82
83 NEW_CLASS (PyGISourceSymbol, "SourceSymbol", GISourceSymbol);
84 NEW_CLASS (PyGISourceType, "SourceType", GISourceType);
85 NEW_CLASS (PyGISourceScanner, "SourceScanner", GISourceScanner);
86
87
88 /* Symbol */
89
90 static PyObject *
91 pygi_source_symbol_new (GISourceSymbol *symbol)
92 {
93   PyGISourceSymbol *self;
94   
95   if (symbol == NULL)
96     {
97       Py_INCREF (Py_None);
98       return Py_None;
99     }
100     
101   self = (PyGISourceSymbol *)PyObject_New (PyGISourceSymbol,
102                                            &PyGISourceSymbol_Type);
103   self->symbol = symbol;
104   return (PyObject*)self;
105 }
106
107 static PyObject *
108 symbol_get_type (PyGISourceSymbol *self,
109                  void             *context)
110 {
111   return PyInt_FromLong (self->symbol->type);
112 }
113
114 static PyObject *
115 symbol_get_line (PyGISourceSymbol *self,
116                  void             *context)
117 {
118   return PyInt_FromLong (self->symbol->line);
119 }
120
121 static PyObject *
122 symbol_get_private (PyGISourceSymbol *self,
123                     void             *context)
124 {
125   return PyBool_FromLong (self->symbol->private);
126 }
127
128 static PyObject *
129 symbol_get_ident (PyGISourceSymbol *self,
130                   void            *context)
131 {
132   
133   if (!self->symbol->ident)
134     {
135       Py_INCREF(Py_None);
136       return Py_None;
137     }
138     
139   return PyString_FromString (self->symbol->ident);
140 }
141
142 static PyObject *
143 symbol_get_base_type (PyGISourceSymbol *self,
144                       void             *context)
145 {
146   return pygi_source_type_new (self->symbol->base_type);
147 }
148
149 static PyObject *
150 symbol_get_const_int (PyGISourceSymbol *self,
151                       void             *context)
152 {
153   if (!self->symbol->const_int_set)
154     {
155       Py_INCREF(Py_None);
156       return Py_None;
157     }
158   return PyInt_FromLong (self->symbol->const_int);
159 }
160
161 static PyObject *
162 symbol_get_const_double (PyGISourceSymbol *self,
163                          void             *context)
164 {
165   if (!self->symbol->const_double_set)
166     {
167       Py_INCREF(Py_None);
168       return Py_None;
169     }
170   return PyFloat_FromDouble (self->symbol->const_double);
171 }
172
173 static PyObject *
174 symbol_get_const_string (PyGISourceSymbol *self,
175                          void             *context)
176 {
177   if (!self->symbol->const_string)
178     {
179       Py_INCREF(Py_None);
180       return Py_None;
181     }
182     
183   return PyString_FromString (self->symbol->const_string);
184 }
185
186 static PyObject *
187 symbol_get_source_filename (PyGISourceSymbol *self,
188                             void             *context)
189 {
190   if (!self->symbol->source_filename)
191     {
192       Py_INCREF(Py_None);
193       return Py_None;
194     }
195
196   return PyString_FromString (self->symbol->source_filename);
197 }
198
199 static const PyGetSetDef _PyGISourceSymbol_getsets[] = {
200   /* int ref_count; */
201   { "type", (getter)symbol_get_type, NULL, NULL},
202   /* int id; */
203   { "ident", (getter)symbol_get_ident, NULL, NULL},
204   { "base_type", (getter)symbol_get_base_type, NULL, NULL},
205   /* gboolean const_int_set; */
206   { "const_int", (getter)symbol_get_const_int, NULL, NULL},
207   /* gboolean const_double_set; */
208   { "const_double", (getter)symbol_get_const_double, NULL, NULL},
209   { "const_string", (getter)symbol_get_const_string, NULL, NULL},
210   { "source_filename", (getter)symbol_get_source_filename, NULL, NULL},
211   { "line", (getter)symbol_get_line, NULL, NULL},
212   { "private", (getter)symbol_get_private, NULL, NULL},
213   { 0 }
214 };
215
216
217
218 /* Type */
219
220 static PyObject *
221 pygi_source_type_new (GISourceType *type)
222 {
223   PyGISourceType *self;
224   
225   if (type == NULL)
226     {
227       Py_INCREF (Py_None);
228       return Py_None;
229     }
230   
231   self = (PyGISourceType *)PyObject_New (PyGISourceType,
232                                          &PyGISourceType_Type);
233   self->type = type;
234   return (PyObject*)self;
235 }
236
237 static PyObject *
238 type_get_type (PyGISourceType *self,
239                void           *context)
240 {
241   return PyInt_FromLong (self->type->type);
242 }
243
244 static PyObject *
245 type_get_storage_class_specifier (PyGISourceType *self,
246                                   void           *context)
247 {
248   return PyInt_FromLong (self->type->storage_class_specifier);
249 }
250
251 static PyObject *
252 type_get_type_qualifier (PyGISourceType *self,
253                          void           *context)
254 {
255   return PyInt_FromLong (self->type->type_qualifier);
256 }
257
258 static PyObject *
259 type_get_function_specifier (PyGISourceType *self,
260                              void           *context)
261 {
262   return PyInt_FromLong (self->type->function_specifier);
263 }
264
265 static PyObject *
266 type_get_name (PyGISourceType *self,
267                void           *context)
268 {
269   if (!self->type->name)
270     {
271       Py_INCREF (Py_None);
272       return Py_None;
273     }
274     
275   return PyString_FromString (self->type->name);
276 }
277
278 static PyObject *
279 type_get_base_type (PyGISourceType *self,
280                     void           *context)
281 {
282   return pygi_source_type_new (self->type->base_type);
283 }
284
285 static PyObject *
286 type_get_child_list (PyGISourceType *self,
287                      void           *context)
288 {
289   GList *l;
290   PyObject *list;
291   int i = 0;
292
293   if (!self->type)
294     return Py_BuildValue("[]");
295   
296   list = PyList_New (g_list_length (self->type->child_list));
297   
298   for (l = self->type->child_list; l; l = l->next)
299     {
300       PyObject *item = pygi_source_symbol_new (l->data);
301       PyList_SetItem (list, i++, item);
302     }
303
304   Py_INCREF (list);
305   return list;
306 }
307
308 static PyObject *
309 type_get_is_bitfield (PyGISourceType *self,
310                              void           *context)
311 {
312   return PyInt_FromLong (self->type->is_bitfield);
313 }
314
315 static const PyGetSetDef _PyGISourceType_getsets[] = {
316   { "type", (getter)type_get_type, NULL, NULL},
317   { "storage_class_specifier", (getter)type_get_storage_class_specifier, NULL, NULL},
318   { "type_qualifier", (getter)type_get_type_qualifier, NULL, NULL},
319   { "function_specifier", (getter)type_get_function_specifier, NULL, NULL},
320   { "name", (getter)type_get_name, NULL, NULL},
321   { "base_type", (getter)type_get_base_type, NULL, NULL},
322   { "child_list", (getter)type_get_child_list, NULL, NULL},
323   { "is_bitfield", (getter)type_get_is_bitfield, NULL, NULL},
324   { 0 }
325 };
326
327
328
329 /* Scanner */
330
331 static int
332 pygi_source_scanner_init (PyGISourceScanner *self,
333                           PyObject          *args,
334                           PyObject          *kwargs)
335 {
336   if (!PyArg_ParseTuple (args, ":SourceScanner.__init__"))
337     return -1;
338
339   self->scanner = gi_source_scanner_new ();
340
341   return 0;
342 }
343
344 static PyObject *
345 pygi_source_scanner_append_filename (PyGISourceScanner *self,
346                                      PyObject          *args)
347 {
348   char *filename;
349
350   if (!PyArg_ParseTuple (args, "s:SourceScanner.append_filename", &filename))
351     return NULL;
352
353   self->scanner->filenames = g_list_append (self->scanner->filenames,
354                                             g_realpath (filename));
355   
356   Py_INCREF (Py_None);
357   return Py_None;
358 }
359
360 static PyObject *
361 pygi_source_scanner_parse_macros (PyGISourceScanner *self,
362                                   PyObject          *args)
363 {
364   GList *filenames;
365   int i;
366   PyObject *list;
367
368   list = PyTuple_GET_ITEM (args, 0);
369
370   if (!PyList_Check (list))
371     {
372       PyErr_SetString (PyExc_RuntimeError, "parse macro takes a list of filenames");
373       return NULL;
374     }
375
376   filenames = NULL;
377   for (i = 0; i < PyList_Size (list); ++i)
378     {
379       PyObject *obj;
380       char *filename;
381
382       obj = PyList_GetItem (list, i);
383       filename = PyString_AsString (obj);
384
385       filenames = g_list_append (filenames, filename);
386     }
387
388   gi_source_scanner_parse_macros (self->scanner, filenames);
389   g_list_free (filenames);
390
391   Py_INCREF (Py_None);
392   return Py_None;
393 }
394
395 static PyObject *
396 pygi_source_scanner_parse_file (PyGISourceScanner *self,
397                                 PyObject          *args)
398 {
399   int fd;
400   FILE *fp;
401   
402   if (!PyArg_ParseTuple (args, "i:SourceScanner.parse_file", &fd))
403     return NULL;
404
405 #ifdef _WIN32
406   /* The file descriptor passed to us is from the C library Python
407    * uses. That is msvcr71.dll at least for Python 2.5. This code, at
408    * least if compiled with mingw, uses msvcrt.dll, so we cannot use
409    * the file descriptor directly. So perform appropriate magic.
410    */
411   {
412     HMODULE msvcr71;
413     int (*p__get_osfhandle) (int);
414     HANDLE handle;
415
416     msvcr71 = GetModuleHandle ("msvcr71.dll");
417     if (!msvcr71)
418       {
419         g_print ("No msvcr71.dll loaded.\n");
420         return NULL;
421       }
422
423     p__get_osfhandle = GetProcAddress (msvcr71, "_get_osfhandle");
424     if (!p__get_osfhandle)
425       {
426         g_print ("No _get_osfhandle found in msvcr71.dll.\n");
427         return NULL;
428       }
429
430     handle = p__get_osfhandle (fd);
431     if (!p__get_osfhandle)
432       {
433         g_print ("Could not get OS handle from msvcr71 fd.\n");
434         return NULL;
435       }
436     
437     fd = _open_osfhandle (handle, _O_RDONLY);
438     if (fd == -1)
439       {
440         g_print ("Could not open C fd from OS handle.\n");
441         return NULL;
442       }
443   }
444 #endif
445
446   fp = fdopen (fd, "r");
447   if (!fp)
448     {
449       PyErr_SetFromErrno (PyExc_OSError);
450       return NULL;
451     }
452
453   if (!gi_source_scanner_parse_file (self->scanner, fp))
454     {
455       g_print ("Something went wrong during parsing.\n");
456       return NULL;
457     }
458
459   Py_INCREF (Py_None);
460   return Py_None;
461 }
462
463 static PyObject *
464 pygi_source_scanner_lex_filename (PyGISourceScanner *self,
465                                   PyObject          *args)
466 {
467   char *filename;
468   
469   if (!PyArg_ParseTuple (args, "s:SourceScanner.lex_filename", &filename))
470     return NULL;
471
472   self->scanner->current_filename = g_strdup (filename);
473   if (!gi_source_scanner_lex_filename (self->scanner, filename))
474     {
475       g_print ("Something went wrong during lexing.\n");
476       return NULL;
477     }
478   self->scanner->filenames =
479     g_list_append (self->scanner->filenames, g_strdup (filename));
480
481   Py_INCREF (Py_None);
482   return Py_None;
483 }
484
485 static PyObject *
486 pygi_source_scanner_set_macro_scan (PyGISourceScanner *self,
487                                     PyObject          *args)
488 {
489   int macro_scan;
490   
491   if (!PyArg_ParseTuple (args, "b:SourceScanner.set_macro_scan", &macro_scan))
492     return NULL;
493
494   gi_source_scanner_set_macro_scan (self->scanner, macro_scan);
495
496   Py_INCREF (Py_None);
497   return Py_None;
498 }
499
500 static PyObject *
501 pygi_source_scanner_get_symbols (PyGISourceScanner *self)
502 {
503   GSList *l, *symbols;
504   PyObject *list;
505   int i = 0;
506   
507   symbols = gi_source_scanner_get_symbols (self->scanner);
508   list = PyList_New (g_slist_length (symbols));
509   
510   for (l = symbols; l; l = l->next)
511     {
512       PyObject *item = pygi_source_symbol_new (l->data);
513       PyList_SetItem (list, i++, item);
514     }
515
516   Py_INCREF (list);
517   return list;
518 }
519
520 static PyObject *
521 pygi_source_scanner_get_comments (PyGISourceScanner *self)
522 {
523   GSList *l, *comments;
524   PyObject *list;
525   int i = 0;
526
527   comments = gi_source_scanner_get_comments (self->scanner);
528   list = PyList_New (g_slist_length (comments));
529
530   for (l = comments; l; l = l->next)
531     {
532       GISourceComment *comment = l->data;
533       PyObject *item = Py_BuildValue ("(ssi)", comment->comment,
534                                       comment->filename,
535                                       comment->line);
536       PyList_SetItem (list, i++, item);
537     }
538
539   Py_INCREF (list);
540   return list;
541 }
542
543 static const PyMethodDef _PyGISourceScanner_methods[] = {
544   { "get_comments", (PyCFunction) pygi_source_scanner_get_comments, METH_NOARGS },
545   { "get_symbols", (PyCFunction) pygi_source_scanner_get_symbols, METH_NOARGS },
546   { "append_filename", (PyCFunction) pygi_source_scanner_append_filename, METH_VARARGS },
547   { "parse_file", (PyCFunction) pygi_source_scanner_parse_file, METH_VARARGS },
548   { "parse_macros", (PyCFunction) pygi_source_scanner_parse_macros, METH_VARARGS },
549   { "lex_filename", (PyCFunction) pygi_source_scanner_lex_filename, METH_VARARGS },
550   { "set_macro_scan", (PyCFunction) pygi_source_scanner_set_macro_scan, METH_VARARGS },
551   { NULL, NULL, 0 }
552 };
553
554
555 static int calc_attrs_length(PyObject *attributes, int indent,
556                              int self_indent)
557 {
558   int attr_length = 0;
559   int i;
560   
561   if (indent == -1)
562     return -1;
563
564   for (i = 0; i < PyList_Size (attributes); ++i)
565     {
566       PyObject *tuple;
567       char *attr, *value;
568       char *escaped;
569       
570       tuple = PyList_GetItem (attributes, i);
571       if (PyTuple_GetItem(tuple, 1) == Py_None)
572         continue;
573
574       if (!PyArg_ParseTuple(tuple, "ss", &attr, &value))
575         return -1;
576       
577       escaped = g_markup_escape_text (value, -1);
578       attr_length += 2 + strlen(attr) + strlen(escaped) + 2;
579       g_free(escaped);
580     }
581
582   return attr_length + indent + self_indent;
583 }
584
585 /* Hall of shame, wasted time debugging the code below
586  * 20min - Johan 2009-02-19
587  */
588 static PyObject *
589 pygi_collect_attributes (PyObject *self,
590                          PyObject *args)
591 {
592   char *tag_name;
593   PyObject *attributes;
594   int indent, indent_len, i, j, self_indent;
595   char *indent_char;
596   gboolean first;
597   GString *attr_value;
598   int len;
599   
600   if (!PyArg_ParseTuple(args, "sO!isi",
601                         &tag_name, &PyList_Type, &attributes,
602                         &self_indent, &indent_char,
603                         &indent))
604     return NULL;
605
606   if (attributes == Py_None || !PyList_Size(attributes))
607     return PyString_FromString("");
608
609   len = calc_attrs_length(attributes, indent, self_indent);
610   if (len < 0)
611     return NULL;
612   if (len > 79)
613     indent_len = self_indent + strlen(tag_name) + 1;
614   else
615     indent_len = 0;
616
617   first = TRUE;
618   attr_value = g_string_new ("");
619   
620   for (i = 0; i < PyList_Size (attributes); ++i)
621     {
622       PyObject *tuple;
623       char *attr, *value, *escaped;
624       
625       tuple = PyList_GetItem (attributes, i);
626       
627       if (!PyTuple_Check (tuple)) 
628         {
629           PyErr_SetString(PyExc_TypeError,
630                           "attribute item must be a tuple");
631           return NULL;
632         }
633       
634       if (!PyTuple_Size (tuple) == 2)
635         {
636           PyErr_SetString(PyExc_IndexError,
637                           "attribute item must be a tuple of length 2");
638           return NULL;
639         }
640       
641       if (PyTuple_GetItem(tuple, 1) == Py_None)
642         continue;
643
644       /* this leaks, but we exit after, so */
645       if (!PyArg_ParseTuple(tuple, "ss", &attr, &value))
646         return NULL;
647
648       if (indent_len && !first)
649         {
650           g_string_append_c (attr_value, '\n');
651           for (j = 0; j < indent_len; j++)
652             g_string_append_c (attr_value, ' ');
653         }
654       g_string_append_c (attr_value, ' ');
655       g_string_append (attr_value, attr);
656       g_string_append_c (attr_value, '=');
657       g_string_append_c (attr_value, '\"');
658       escaped = g_markup_escape_text (value, -1);
659       g_string_append (attr_value, escaped);
660       g_string_append_c (attr_value, '\"');
661       if (first)
662         first = FALSE;
663   }
664
665   return PyString_FromString (g_string_free (attr_value, FALSE));
666 }
667
668 /* Module */
669
670 static const PyMethodDef pyscanner_functions[] = {
671   { "collect_attributes",
672     (PyCFunction) pygi_collect_attributes, METH_VARARGS },
673   { NULL, NULL, 0, NULL }
674 };
675
676 DL_EXPORT(void)
677 init_giscanner(void)
678 {
679     PyObject *m, *d;
680
681     m = Py_InitModule ("giscanner._giscanner",
682                        (PyMethodDef*)pyscanner_functions);
683     d = PyModule_GetDict (m);
684
685     PyGISourceScanner_Type.tp_init = (initproc)pygi_source_scanner_init;
686     PyGISourceScanner_Type.tp_methods = (PyMethodDef*)_PyGISourceScanner_methods;
687     REGISTER_TYPE (d, "SourceScanner", PyGISourceScanner_Type);
688
689     PyGISourceSymbol_Type.tp_getset = (PyGetSetDef*)_PyGISourceSymbol_getsets;
690     REGISTER_TYPE (d, "SourceSymbol", PyGISourceSymbol_Type);
691
692     PyGISourceType_Type.tp_getset = (PyGetSetDef*)_PyGISourceType_getsets;
693     REGISTER_TYPE (d, "SourceType", PyGISourceType_Type);
694 }