[SYMBOL_LINES] Support for line numbers on Symbols
[gnome.gobject-introspection] / 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_ident (PyGISourceSymbol *self,
123                   void            *context)
124 {
125   
126   if (!self->symbol->ident)
127     {
128       Py_INCREF(Py_None);
129       return Py_None;
130     }
131     
132   return PyString_FromString (self->symbol->ident);
133 }
134
135 static PyObject *
136 symbol_get_base_type (PyGISourceSymbol *self,
137                       void             *context)
138 {
139   return pygi_source_type_new (self->symbol->base_type);
140 }
141
142 static PyObject *
143 symbol_get_const_int (PyGISourceSymbol *self,
144                       void             *context)
145 {
146   if (!self->symbol->const_int_set)
147     {
148       Py_INCREF(Py_None);
149       return Py_None;
150     }
151   return PyInt_FromLong (self->symbol->const_int);
152 }
153
154 static PyObject *
155 symbol_get_const_double (PyGISourceSymbol *self,
156                          void             *context)
157 {
158   if (!self->symbol->const_double_set)
159     {
160       Py_INCREF(Py_None);
161       return Py_None;
162     }
163   return PyFloat_FromDouble (self->symbol->const_double);
164 }
165
166 static PyObject *
167 symbol_get_const_string (PyGISourceSymbol *self,
168                          void             *context)
169 {
170   if (!self->symbol->const_string)
171     {
172       Py_INCREF(Py_None);
173       return Py_None;
174     }
175     
176   return PyString_FromString (self->symbol->const_string);
177 }
178
179 static PyObject *
180 symbol_get_source_filename (PyGISourceSymbol *self,
181                             void             *context)
182 {
183   if (!self->symbol->source_filename)
184     {
185       Py_INCREF(Py_None);
186       return Py_None;
187     }
188
189   return PyString_FromString (self->symbol->source_filename);
190 }
191
192 static const PyGetSetDef _PyGISourceSymbol_getsets[] = {
193   /* int ref_count; */
194   { "type", (getter)symbol_get_type, NULL, NULL},
195   /* int id; */
196   { "ident", (getter)symbol_get_ident, NULL, NULL},
197   { "base_type", (getter)symbol_get_base_type, NULL, NULL},
198   /* gboolean const_int_set; */
199   { "const_int", (getter)symbol_get_const_int, NULL, NULL},
200   /* gboolean const_double_set; */
201   { "const_double", (getter)symbol_get_const_double, NULL, NULL},
202   { "const_string", (getter)symbol_get_const_string, NULL, NULL},
203   { "source_filename", (getter)symbol_get_source_filename, NULL, NULL},
204   { "line", (getter)symbol_get_line, NULL, NULL},
205   { 0 }
206 };
207
208
209
210 /* Type */
211
212 static PyObject *
213 pygi_source_type_new (GISourceType *type)
214 {
215   PyGISourceType *self;
216   
217   if (type == NULL)
218     {
219       Py_INCREF (Py_None);
220       return Py_None;
221     }
222   
223   self = (PyGISourceType *)PyObject_New (PyGISourceType,
224                                          &PyGISourceType_Type);
225   self->type = type;
226   return (PyObject*)self;
227 }
228
229 static PyObject *
230 type_get_type (PyGISourceType *self,
231                void           *context)
232 {
233   return PyInt_FromLong (self->type->type);
234 }
235
236 static PyObject *
237 type_get_storage_class_specifier (PyGISourceType *self,
238                                   void           *context)
239 {
240   return PyInt_FromLong (self->type->storage_class_specifier);
241 }
242
243 static PyObject *
244 type_get_type_qualifier (PyGISourceType *self,
245                          void           *context)
246 {
247   return PyInt_FromLong (self->type->type_qualifier);
248 }
249
250 static PyObject *
251 type_get_function_specifier (PyGISourceType *self,
252                              void           *context)
253 {
254   return PyInt_FromLong (self->type->function_specifier);
255 }
256
257 static PyObject *
258 type_get_name (PyGISourceType *self,
259                void           *context)
260 {
261   if (!self->type->name)
262     {
263       Py_INCREF (Py_None);
264       return Py_None;
265     }
266     
267   return PyString_FromString (self->type->name);
268 }
269
270 static PyObject *
271 type_get_base_type (PyGISourceType *self,
272                     void           *context)
273 {
274   return pygi_source_type_new (self->type->base_type);
275 }
276
277 static PyObject *
278 type_get_child_list (PyGISourceType *self,
279                      void           *context)
280 {
281   GList *l;
282   PyObject *list;
283   int i = 0;
284
285   if (!self->type)
286     return Py_BuildValue("[]");
287   
288   list = PyList_New (g_list_length (self->type->child_list));
289   
290   for (l = self->type->child_list; l; l = l->next)
291     {
292       PyObject *item = pygi_source_symbol_new (l->data);
293       PyList_SetItem (list, i++, item);
294       Py_INCREF (item);
295     }
296
297   Py_INCREF (list);
298   return list;
299 }
300
301 static PyObject *
302 type_get_is_bitfield (PyGISourceType *self,
303                              void           *context)
304 {
305   return PyInt_FromLong (self->type->is_bitfield);
306 }
307
308 static const PyGetSetDef _PyGISourceType_getsets[] = {
309   { "type", (getter)type_get_type, NULL, NULL},
310   { "storage_class_specifier", (getter)type_get_storage_class_specifier, NULL, NULL},
311   { "type_qualifier", (getter)type_get_type_qualifier, NULL, NULL},
312   { "function_specifier", (getter)type_get_function_specifier, NULL, NULL},
313   { "name", (getter)type_get_name, NULL, NULL},
314   { "base_type", (getter)type_get_base_type, NULL, NULL},
315   { "child_list", (getter)type_get_child_list, NULL, NULL},
316   { "is_bitfield", (getter)type_get_is_bitfield, NULL, NULL},
317   { 0 }
318 };
319
320
321
322 /* Scanner */
323
324 static int
325 pygi_source_scanner_init (PyGISourceScanner *self,
326                           PyObject          *args,
327                           PyObject          *kwargs)
328 {
329   if (!PyArg_ParseTuple (args, ":SourceScanner.__init__"))
330     return -1;
331
332   self->scanner = gi_source_scanner_new ();
333
334   return 0;
335 }
336
337 static PyObject *
338 pygi_source_scanner_append_filename (PyGISourceScanner *self,
339                                      PyObject          *args)
340 {
341   char *filename;
342
343   if (!PyArg_ParseTuple (args, "s:SourceScanner.append_filename", &filename))
344     return NULL;
345
346   self->scanner->filenames = g_list_append (self->scanner->filenames,
347                                             g_realpath (filename));
348   
349   Py_INCREF (Py_None);
350   return Py_None;
351 }
352
353 static PyObject *
354 pygi_source_scanner_parse_macros (PyGISourceScanner *self,
355                                   PyObject          *args)
356 {
357   GList *filenames;
358   int i;
359   PyObject *list;
360
361   list = PyTuple_GET_ITEM (args, 0);
362
363   if (!PyList_Check (list))
364     {
365       PyErr_SetString (PyExc_RuntimeError, "parse macro takes a list of filenames");
366       return NULL;
367     }
368
369   filenames = NULL;
370   for (i = 0; i < PyList_Size (list); ++i)
371     {
372       PyObject *obj;
373       char *filename;
374
375       obj = PyList_GetItem (list, i);
376       filename = PyString_AsString (obj);
377
378       filenames = g_list_append (filenames, filename);
379     }
380
381   gi_source_scanner_parse_macros (self->scanner, filenames);
382   g_list_free (filenames);
383
384   Py_INCREF (Py_None);
385   return Py_None;
386 }
387
388 static PyObject *
389 pygi_source_scanner_parse_file (PyGISourceScanner *self,
390                                 PyObject          *args)
391 {
392   int fd;
393   FILE *fp;
394   
395   if (!PyArg_ParseTuple (args, "i:SourceScanner.parse_file", &fd))
396     return NULL;
397
398 #ifdef _WIN32
399   /* The file descriptor passed to us is from the C library Python
400    * uses. That is msvcr71.dll at least for Python 2.5. This code, at
401    * least if compiled with mingw, uses msvcrt.dll, so we cannot use
402    * the file descriptor directly. So perform appropriate magic.
403    */
404   {
405     HMODULE msvcr71;
406     int (*p__get_osfhandle) (int);
407     HANDLE handle;
408
409     msvcr71 = GetModuleHandle ("msvcr71.dll");
410     if (!msvcr71)
411       {
412         g_print ("No msvcr71.dll loaded.\n");
413         return NULL;
414       }
415
416     p__get_osfhandle = GetProcAddress (msvcr71, "_get_osfhandle");
417     if (!p__get_osfhandle)
418       {
419         g_print ("No _get_osfhandle found in msvcr71.dll.\n");
420         return NULL;
421       }
422
423     handle = p__get_osfhandle (fd);
424     if (!p__get_osfhandle)
425       {
426         g_print ("Could not get OS handle from msvcr71 fd.\n");
427         return NULL;
428       }
429     
430     fd = _open_osfhandle (handle, _O_RDONLY);
431     if (fd == -1)
432       {
433         g_print ("Could not open C fd from OS handle.\n");
434         return NULL;
435       }
436   }
437 #endif
438
439   fp = fdopen (fd, "r");
440   if (!fp)
441     {
442       PyErr_SetFromErrno (PyExc_OSError);
443       return NULL;
444     }
445
446   if (!gi_source_scanner_parse_file (self->scanner, fp))
447     {
448       g_print ("Something went wrong during parsing.\n");
449       return NULL;
450     }
451
452   Py_INCREF (Py_None);
453   return Py_None;
454 }
455
456 static PyObject *
457 pygi_source_scanner_lex_filename (PyGISourceScanner *self,
458                                   PyObject          *args)
459 {
460   char *filename;
461   
462   if (!PyArg_ParseTuple (args, "s:SourceScanner.lex_filename", &filename))
463     return NULL;
464
465   self->scanner->current_filename = g_strdup (filename);
466   if (!gi_source_scanner_lex_filename (self->scanner, filename))
467     {
468       g_print ("Something went wrong during lexing.\n");
469       return NULL;
470     }
471   self->scanner->filenames =
472     g_list_append (self->scanner->filenames, g_strdup (filename));
473
474   Py_INCREF (Py_None);
475   return Py_None;
476 }
477
478 static PyObject *
479 pygi_source_scanner_set_macro_scan (PyGISourceScanner *self,
480                                     PyObject          *args)
481 {
482   int macro_scan;
483   
484   if (!PyArg_ParseTuple (args, "b:SourceScanner.set_macro_scan", &macro_scan))
485     return NULL;
486
487   gi_source_scanner_set_macro_scan (self->scanner, macro_scan);
488
489   Py_INCREF (Py_None);
490   return Py_None;
491 }
492
493 static PyObject *
494 pygi_source_scanner_get_symbols (PyGISourceScanner *self)
495 {
496   GSList *l, *symbols;
497   PyObject *list;
498   int i = 0;
499   
500   symbols = gi_source_scanner_get_symbols (self->scanner);
501   list = PyList_New (g_slist_length (symbols));
502   
503   for (l = symbols; l; l = l->next)
504     {
505       PyObject *item = pygi_source_symbol_new (l->data);
506       PyList_SetItem (list, i++, item);
507       Py_INCREF (item);
508     }
509
510   Py_INCREF (list);
511   return list;
512 }
513
514 static PyObject *
515 pygi_source_scanner_get_comments (PyGISourceScanner *self)
516 {
517   GSList *l, *comments;
518   PyObject *list;
519   int i = 0;
520   
521   comments = gi_source_scanner_get_comments (self->scanner);
522   list = PyList_New (g_slist_length (comments));
523   
524   for (l = comments; l; l = l->next)
525     {
526       PyObject *item = PyString_FromString (l->data);
527       PyList_SetItem (list, i++, item);
528       Py_INCREF (item);
529     }
530
531   Py_INCREF (list);
532   return list;
533 }
534
535 static const PyMethodDef _PyGISourceScanner_methods[] = {
536   { "get_comments", (PyCFunction) pygi_source_scanner_get_comments, METH_NOARGS },
537   { "get_symbols", (PyCFunction) pygi_source_scanner_get_symbols, METH_NOARGS },
538   { "append_filename", (PyCFunction) pygi_source_scanner_append_filename, METH_VARARGS },
539   { "parse_file", (PyCFunction) pygi_source_scanner_parse_file, METH_VARARGS },
540   { "parse_macros", (PyCFunction) pygi_source_scanner_parse_macros, METH_VARARGS },
541   { "lex_filename", (PyCFunction) pygi_source_scanner_lex_filename, METH_VARARGS },
542   { "set_macro_scan", (PyCFunction) pygi_source_scanner_set_macro_scan, METH_VARARGS },
543   { NULL, NULL, 0 }
544 };
545
546
547 static int calc_attrs_length(PyObject *attributes, int indent,
548                              int self_indent)
549 {
550   int attr_length = 0;
551   int i;
552   
553   if (indent == -1)
554     return -1;
555
556   for (i = 0; i < PyList_Size (attributes); ++i)
557     {
558       PyObject *tuple;
559       char *attr, *value;
560       char *escaped;
561       
562       tuple = PyList_GetItem (attributes, i);
563       if (PyTuple_GetItem(tuple, 1) == Py_None)
564         continue;
565
566       if (!PyArg_ParseTuple(tuple, "ss", &attr, &value))
567         return -1;
568       
569       escaped = g_markup_escape_text (value, -1);
570       attr_length += 2 + strlen(attr) + strlen(escaped) + 2;
571       g_free(escaped);
572     }
573
574   return attr_length + indent + self_indent;
575 }
576
577 /* Hall of shame, wasted time debugging the code below
578  * 20min - Johan 2009-02-19
579  */
580 static PyObject *
581 pygi_collect_attributes (PyObject *self,
582                          PyObject *args)
583 {
584   char *tag_name;
585   PyObject *attributes;
586   int indent, indent_len, i, j, self_indent;
587   char *indent_char;
588   gboolean first;
589   GString *attr_value;
590   int len;
591   
592   if (!PyArg_ParseTuple(args, "sO!isi",
593                         &tag_name, &PyList_Type, &attributes,
594                         &self_indent, &indent_char,
595                         &indent))
596     return NULL;
597
598   if (attributes == Py_None || !PyList_Size(attributes))
599     return PyString_FromString("");
600
601   len = calc_attrs_length(attributes, indent, self_indent);
602   if (len < 0)
603     return NULL;
604   if (len > 79)
605     indent_len = self_indent + strlen(tag_name) + 1;
606   else
607     indent_len = 0;
608
609   first = TRUE;
610   attr_value = g_string_new ("");
611   
612   for (i = 0; i < PyList_Size (attributes); ++i)
613     {
614       PyObject *tuple;
615       char *attr, *value, *escaped;
616       
617       tuple = PyList_GetItem (attributes, i);
618       
619       if (!PyTuple_Check (tuple)) 
620         {
621           PyErr_SetString(PyExc_TypeError,
622                           "attribute item must be a tuple");
623           return NULL;
624         }
625       
626       if (!PyTuple_Size (tuple) == 2)
627         {
628           PyErr_SetString(PyExc_IndexError,
629                           "attribute item must be a tuple of length 2");
630           return NULL;
631         }
632       
633       if (PyTuple_GetItem(tuple, 1) == Py_None)
634         continue;
635
636       /* this leaks, but we exit after, so */
637       if (!PyArg_ParseTuple(tuple, "ss", &attr, &value))
638         return NULL;
639
640       if (indent_len && !first)
641         {
642           g_string_append_c (attr_value, '\n');
643           for (j = 0; j < indent_len; j++)
644             g_string_append_c (attr_value, ' ');
645         }
646       g_string_append_c (attr_value, ' ');
647       g_string_append (attr_value, attr);
648       g_string_append_c (attr_value, '=');
649       g_string_append_c (attr_value, '\"');
650       escaped = g_markup_escape_text (value, -1);
651       g_string_append (attr_value, escaped);
652       g_string_append_c (attr_value, '\"');
653       if (first)
654         first = FALSE;
655   }
656
657   return PyString_FromString (g_string_free (attr_value, FALSE));
658 }
659
660 /* Module */
661
662 static const PyMethodDef pyscanner_functions[] = {
663   { "collect_attributes",
664     (PyCFunction) pygi_collect_attributes, METH_VARARGS },
665   { NULL, NULL, 0, NULL }
666 };
667
668 DL_EXPORT(void)
669 init_giscanner(void)
670 {
671     PyObject *m, *d;
672
673     m = Py_InitModule ("giscanner._giscanner",
674                        (PyMethodDef*)pyscanner_functions);
675     d = PyModule_GetDict (m);
676
677     PyGISourceScanner_Type.tp_init = (initproc)pygi_source_scanner_init;
678     PyGISourceScanner_Type.tp_methods = (PyMethodDef*)_PyGISourceScanner_methods;
679     REGISTER_TYPE (d, "SourceScanner", PyGISourceScanner_Type);
680
681     PyGISourceSymbol_Type.tp_getset = (PyGetSetDef*)_PyGISourceSymbol_getsets;
682     REGISTER_TYPE (d, "SourceSymbol", PyGISourceSymbol_Type);
683
684     PyGISourceType_Type.tp_getset = (PyGetSetDef*)_PyGISourceType_getsets;
685     REGISTER_TYPE (d, "SourceType", PyGISourceType_Type);
686 }