[SYMBOL_LINES] Support for line numbers on Symbols
[gnome.gobject-introspection] / giscanner / giscannermodule.c
index b9245f2..2d2e073 100644 (file)
@@ -1,11 +1,11 @@
 /* GObject introspection: scanner
  *
- * Copyright (C) 2008
+ * Copyright (C) 2008  Johan Dahlin <johan@gnome.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * version 2 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
-#include "sourcescanner.h"
 #include <Python.h>
+#include "sourcescanner.h"
+#include "grealpath.h"
+
+#ifdef _WIN32
+#include <fcntl.h>
+#include <io.h>
+#define WIN32_LEAN_AND_MEAN
+#define STRICT
+#include <windows.h>
+#endif
+
+DL_EXPORT(void) init_giscanner(void);
 
 #define NEW_CLASS(ctype, name, cname)        \
-static PyMethodDef _Py##cname##_methods[];    \
+static const PyMethodDef _Py##cname##_methods[];    \
 PyTypeObject Py##cname##_Type = {             \
     PyObject_HEAD_INIT(NULL)                  \
     0,                                       \
@@ -57,6 +68,8 @@ typedef struct {
   GISourceType *type;
 } PyGISourceType;
 
+static PyObject * pygi_source_type_new (GISourceType *type);
+
 typedef struct {
   PyObject_HEAD
   GISourceSymbol *symbol;
@@ -67,42 +80,128 @@ typedef struct {
   GISourceScanner *scanner;
 } PyGISourceScanner;
 
-NEW_CLASS(PyGISourceSymbol, "SourceSymbol", GISourceSymbol);
-NEW_CLASS(PyGISourceType, "SourceType", GISourceType);
-NEW_CLASS(PyGISourceScanner, "SourceScanner", GISourceScanner);
+NEW_CLASS (PyGISourceSymbol, "SourceSymbol", GISourceSymbol);
+NEW_CLASS (PyGISourceType, "SourceType", GISourceType);
+NEW_CLASS (PyGISourceScanner, "SourceScanner", GISourceScanner);
 
 
 /* Symbol */
 
 static PyObject *
-symbol_get_type(PyGISourceSymbol *self,
-               void             *context)
+pygi_source_symbol_new (GISourceSymbol *symbol)
+{
+  PyGISourceSymbol *self;
+  
+  if (symbol == NULL)
+    {
+      Py_INCREF (Py_None);
+      return Py_None;
+    }
+    
+  self = (PyGISourceSymbol *)PyObject_New (PyGISourceSymbol,
+                                          &PyGISourceSymbol_Type);
+  self->symbol = symbol;
+  return (PyObject*)self;
+}
+
+static PyObject *
+symbol_get_type (PyGISourceSymbol *self,
+                void             *context)
+{
+  return PyInt_FromLong (self->symbol->type);
+}
+
+static PyObject *
+symbol_get_line (PyGISourceSymbol *self,
+                void             *context)
 {
-  return PyInt_FromLong(self->symbol->type);
+  return PyInt_FromLong (self->symbol->line);
 }
 
 static PyObject *
-symbol_get_ident(PyGISourceSymbol *self,
+symbol_get_ident (PyGISourceSymbol *self,
                  void            *context)
 {
-  return PyString_FromString(self->symbol->ident);
+  
+  if (!self->symbol->ident)
+    {
+      Py_INCREF(Py_None);
+      return Py_None;
+    }
+    
+  return PyString_FromString (self->symbol->ident);
+}
+
+static PyObject *
+symbol_get_base_type (PyGISourceSymbol *self,
+                     void             *context)
+{
+  return pygi_source_type_new (self->symbol->base_type);
+}
+
+static PyObject *
+symbol_get_const_int (PyGISourceSymbol *self,
+                     void             *context)
+{
+  if (!self->symbol->const_int_set)
+    {
+      Py_INCREF(Py_None);
+      return Py_None;
+    }
+  return PyInt_FromLong (self->symbol->const_int);
+}
+
+static PyObject *
+symbol_get_const_double (PyGISourceSymbol *self,
+                         void             *context)
+{
+  if (!self->symbol->const_double_set)
+    {
+      Py_INCREF(Py_None);
+      return Py_None;
+    }
+  return PyFloat_FromDouble (self->symbol->const_double);
+}
+
+static PyObject *
+symbol_get_const_string (PyGISourceSymbol *self,
+                        void             *context)
+{
+  if (!self->symbol->const_string)
+    {
+      Py_INCREF(Py_None);
+      return Py_None;
+    }
+    
+  return PyString_FromString (self->symbol->const_string);
 }
 
 static PyObject *
-symbol_get_base_type(PyGISourceSymbol *self,
-                    void             *context)
+symbol_get_source_filename (PyGISourceSymbol *self,
+                            void             *context)
 {
-  PyGISourceType *item;
-  item = (PyGISourceType *)PyObject_GC_New(PyGISourceType,
-                                          &PyGISourceType_Type);
-  item->type = self->symbol->base_type;
-  return (PyObject*)item;
+  if (!self->symbol->source_filename)
+    {
+      Py_INCREF(Py_None);
+      return Py_None;
+    }
+
+  return PyString_FromString (self->symbol->source_filename);
 }
 
-static PyGetSetDef _PyGISourceSymbol_getsets[] = {
+static const PyGetSetDef _PyGISourceSymbol_getsets[] = {
+  /* int ref_count; */
   { "type", (getter)symbol_get_type, NULL, NULL},
+  /* int id; */
   { "ident", (getter)symbol_get_ident, NULL, NULL},
   { "base_type", (getter)symbol_get_base_type, NULL, NULL},
+  /* gboolean const_int_set; */
+  { "const_int", (getter)symbol_get_const_int, NULL, NULL},
+  /* gboolean const_double_set; */
+  { "const_double", (getter)symbol_get_const_double, NULL, NULL},
+  { "const_string", (getter)symbol_get_const_string, NULL, NULL},
+  { "source_filename", (getter)symbol_get_source_filename, NULL, NULL},
+  { "line", (getter)symbol_get_line, NULL, NULL},
   { 0 }
 };
 
@@ -111,28 +210,110 @@ static PyGetSetDef _PyGISourceSymbol_getsets[] = {
 /* Type */
 
 static PyObject *
-type_get_type(PyGISourceType *self,
-             void           *context)
+pygi_source_type_new (GISourceType *type)
+{
+  PyGISourceType *self;
+  
+  if (type == NULL)
+    {
+      Py_INCREF (Py_None);
+      return Py_None;
+    }
+  
+  self = (PyGISourceType *)PyObject_New (PyGISourceType,
+                                        &PyGISourceType_Type);
+  self->type = type;
+  return (PyObject*)self;
+}
+
+static PyObject *
+type_get_type (PyGISourceType *self,
+              void           *context)
+{
+  return PyInt_FromLong (self->type->type);
+}
+
+static PyObject *
+type_get_storage_class_specifier (PyGISourceType *self,
+                                 void           *context)
 {
-  return PyInt_FromLong(self->type->type);
+  return PyInt_FromLong (self->type->storage_class_specifier);
 }
 
 static PyObject *
-type_get_name(PyGISourceType *self,
-             void           *context)
+type_get_type_qualifier (PyGISourceType *self,
+                        void           *context)
+{
+  return PyInt_FromLong (self->type->type_qualifier);
+}
+
+static PyObject *
+type_get_function_specifier (PyGISourceType *self,
+                            void           *context)
+{
+  return PyInt_FromLong (self->type->function_specifier);
+}
+
+static PyObject *
+type_get_name (PyGISourceType *self,
+              void           *context)
 {
   if (!self->type->name)
     {
-      Py_INCREF(Py_None);
+      Py_INCREF (Py_None);
       return Py_None;
     }
     
-  return PyString_FromString(self->type->name);
+  return PyString_FromString (self->type->name);
 }
 
-static PyGetSetDef _PyGISourceType_getsets[] = {
+static PyObject *
+type_get_base_type (PyGISourceType *self,
+                   void           *context)
+{
+  return pygi_source_type_new (self->type->base_type);
+}
+
+static PyObject *
+type_get_child_list (PyGISourceType *self,
+                    void           *context)
+{
+  GList *l;
+  PyObject *list;
+  int i = 0;
+
+  if (!self->type)
+    return Py_BuildValue("[]");
+  
+  list = PyList_New (g_list_length (self->type->child_list));
+  
+  for (l = self->type->child_list; l; l = l->next)
+    {
+      PyObject *item = pygi_source_symbol_new (l->data);
+      PyList_SetItem (list, i++, item);
+      Py_INCREF (item);
+    }
+
+  Py_INCREF (list);
+  return list;
+}
+
+static PyObject *
+type_get_is_bitfield (PyGISourceType *self,
+                            void           *context)
+{
+  return PyInt_FromLong (self->type->is_bitfield);
+}
+
+static const PyGetSetDef _PyGISourceType_getsets[] = {
   { "type", (getter)type_get_type, NULL, NULL},
+  { "storage_class_specifier", (getter)type_get_storage_class_specifier, NULL, NULL},
+  { "type_qualifier", (getter)type_get_type_qualifier, NULL, NULL},
+  { "function_specifier", (getter)type_get_function_specifier, NULL, NULL},
   { "name", (getter)type_get_name, NULL, NULL},
+  { "base_type", (getter)type_get_base_type, NULL, NULL},
+  { "child_list", (getter)type_get_child_list, NULL, NULL},
+  { "is_bitfield", (getter)type_get_is_bitfield, NULL, NULL},
   { 0 }
 };
 
@@ -153,34 +334,142 @@ pygi_source_scanner_init (PyGISourceScanner *self,
   return 0;
 }
 
+static PyObject *
+pygi_source_scanner_append_filename (PyGISourceScanner *self,
+                                    PyObject          *args)
+{
+  char *filename;
+
+  if (!PyArg_ParseTuple (args, "s:SourceScanner.append_filename", &filename))
+    return NULL;
+
+  self->scanner->filenames = g_list_append (self->scanner->filenames,
+                                           g_realpath (filename));
+  
+  Py_INCREF (Py_None);
+  return Py_None;
+}
+
+static PyObject *
+pygi_source_scanner_parse_macros (PyGISourceScanner *self,
+                                  PyObject          *args)
+{
+  GList *filenames;
+  int i;
+  PyObject *list;
+
+  list = PyTuple_GET_ITEM (args, 0);
+
+  if (!PyList_Check (list))
+    {
+      PyErr_SetString (PyExc_RuntimeError, "parse macro takes a list of filenames");
+      return NULL;
+    }
+
+  filenames = NULL;
+  for (i = 0; i < PyList_Size (list); ++i)
+    {
+      PyObject *obj;
+      char *filename;
+
+      obj = PyList_GetItem (list, i);
+      filename = PyString_AsString (obj);
+
+      filenames = g_list_append (filenames, filename);
+    }
+
+  gi_source_scanner_parse_macros (self->scanner, filenames);
+  g_list_free (filenames);
+
+  Py_INCREF (Py_None);
+  return Py_None;
+}
+
 static PyObject *
 pygi_source_scanner_parse_file (PyGISourceScanner *self,
                                PyObject          *args)
 {
-  char *filename;
+  int fd;
   FILE *fp;
   
-  if (!PyArg_ParseTuple (args, "s:SourceScanner.__init__", &filename))
+  if (!PyArg_ParseTuple (args, "i:SourceScanner.parse_file", &fd))
     return NULL;
 
-  fp = fopen (filename, "r");
+#ifdef _WIN32
+  /* The file descriptor passed to us is from the C library Python
+   * uses. That is msvcr71.dll at least for Python 2.5. This code, at
+   * least if compiled with mingw, uses msvcrt.dll, so we cannot use
+   * the file descriptor directly. So perform appropriate magic.
+   */
+  {
+    HMODULE msvcr71;
+    int (*p__get_osfhandle) (int);
+    HANDLE handle;
+
+    msvcr71 = GetModuleHandle ("msvcr71.dll");
+    if (!msvcr71)
+      {
+       g_print ("No msvcr71.dll loaded.\n");
+       return NULL;
+      }
+
+    p__get_osfhandle = GetProcAddress (msvcr71, "_get_osfhandle");
+    if (!p__get_osfhandle)
+      {
+       g_print ("No _get_osfhandle found in msvcr71.dll.\n");
+       return NULL;
+      }
+
+    handle = p__get_osfhandle (fd);
+    if (!p__get_osfhandle)
+      {
+       g_print ("Could not get OS handle from msvcr71 fd.\n");
+       return NULL;
+      }
+    
+    fd = _open_osfhandle (handle, _O_RDONLY);
+    if (fd == -1)
+      {
+       g_print ("Could not open C fd from OS handle.\n");
+       return NULL;
+      }
+  }
+#endif
+
+  fp = fdopen (fd, "r");
   if (!fp)
     {
-      PyErr_SetFromErrnoWithFilename (PyExc_OSError, filename);
+      PyErr_SetFromErrno (PyExc_OSError);
       return NULL;
     }
 
-  self->scanner->filenames =
-    g_list_append (self->scanner->filenames, g_strdup (filename));
-  self->scanner->current_filename = self->scanner->filenames->data;
-  
   if (!gi_source_scanner_parse_file (self->scanner, fp))
     {
-      g_print ("Something went wrong..\n");
+      g_print ("Something went wrong during parsing.\n");
       return NULL;
     }
 
-  fclose (fp);
+  Py_INCREF (Py_None);
+  return Py_None;
+}
+
+static PyObject *
+pygi_source_scanner_lex_filename (PyGISourceScanner *self,
+                                 PyObject          *args)
+{
+  char *filename;
+  
+  if (!PyArg_ParseTuple (args, "s:SourceScanner.lex_filename", &filename))
+    return NULL;
+
+  self->scanner->current_filename = g_strdup (filename);
+  if (!gi_source_scanner_lex_filename (self->scanner, filename))
+    {
+      g_print ("Something went wrong during lexing.\n");
+      return NULL;
+    }
+  self->scanner->filenames =
+    g_list_append (self->scanner->filenames, g_strdup (filename));
 
   Py_INCREF (Py_None);
   return Py_None;
@@ -213,27 +502,166 @@ pygi_source_scanner_get_symbols (PyGISourceScanner *self)
   
   for (l = symbols; l; l = l->next)
     {
-      PyGISourceSymbol *item;
-      item = (PyGISourceSymbol *)PyObject_GC_New(PyGISourceSymbol,
-                                                &PyGISourceSymbol_Type);
-      item->symbol = l->data;
-      PyList_SetItem(list, i++, (PyObject*)item);
+      PyObject *item = pygi_source_symbol_new (l->data);
+      PyList_SetItem (list, i++, item);
+      Py_INCREF (item);
     }
+
+  Py_INCREF (list);
+  return list;
+}
+
+static PyObject *
+pygi_source_scanner_get_comments (PyGISourceScanner *self)
+{
+  GSList *l, *comments;
+  PyObject *list;
+  int i = 0;
+  
+  comments = gi_source_scanner_get_comments (self->scanner);
+  list = PyList_New (g_slist_length (comments));
   
+  for (l = comments; l; l = l->next)
+    {
+      PyObject *item = PyString_FromString (l->data);
+      PyList_SetItem (list, i++, item);
+      Py_INCREF (item);
+    }
+
+  Py_INCREF (list);
   return list;
 }
 
-static PyMethodDef _PyGISourceScanner_methods[] = {
+static const PyMethodDef _PyGISourceScanner_methods[] = {
+  { "get_comments", (PyCFunction) pygi_source_scanner_get_comments, METH_NOARGS },
   { "get_symbols", (PyCFunction) pygi_source_scanner_get_symbols, METH_NOARGS },
+  { "append_filename", (PyCFunction) pygi_source_scanner_append_filename, METH_VARARGS },
   { "parse_file", (PyCFunction) pygi_source_scanner_parse_file, METH_VARARGS },
+  { "parse_macros", (PyCFunction) pygi_source_scanner_parse_macros, METH_VARARGS },
+  { "lex_filename", (PyCFunction) pygi_source_scanner_lex_filename, METH_VARARGS },
   { "set_macro_scan", (PyCFunction) pygi_source_scanner_set_macro_scan, METH_VARARGS },
   { NULL, NULL, 0 }
 };
 
 
+static int calc_attrs_length(PyObject *attributes, int indent,
+                            int self_indent)
+{
+  int attr_length = 0;
+  int i;
+  
+  if (indent == -1)
+    return -1;
+
+  for (i = 0; i < PyList_Size (attributes); ++i)
+    {
+      PyObject *tuple;
+      char *attr, *value;
+      char *escaped;
+      
+      tuple = PyList_GetItem (attributes, i);
+      if (PyTuple_GetItem(tuple, 1) == Py_None)
+       continue;
+
+      if (!PyArg_ParseTuple(tuple, "ss", &attr, &value))
+        return -1;
+      
+      escaped = g_markup_escape_text (value, -1);
+      attr_length += 2 + strlen(attr) + strlen(escaped) + 2;
+      g_free(escaped);
+    }
+
+  return attr_length + indent + self_indent;
+}
+
+/* Hall of shame, wasted time debugging the code below
+ * 20min - Johan 2009-02-19
+ */
+static PyObject *
+pygi_collect_attributes (PyObject *self,
+                        PyObject *args)
+{
+  char *tag_name;
+  PyObject *attributes;
+  int indent, indent_len, i, j, self_indent;
+  char *indent_char;
+  gboolean first;
+  GString *attr_value;
+  int len;
+  
+  if (!PyArg_ParseTuple(args, "sO!isi",
+                       &tag_name, &PyList_Type, &attributes,
+                       &self_indent, &indent_char,
+                       &indent))
+    return NULL;
+
+  if (attributes == Py_None || !PyList_Size(attributes))
+    return PyString_FromString("");
+
+  len = calc_attrs_length(attributes, indent, self_indent);
+  if (len < 0)
+    return NULL;
+  if (len > 79)
+    indent_len = self_indent + strlen(tag_name) + 1;
+  else
+    indent_len = 0;
+
+  first = TRUE;
+  attr_value = g_string_new ("");
+  
+  for (i = 0; i < PyList_Size (attributes); ++i)
+    {
+      PyObject *tuple;
+      char *attr, *value, *escaped;
+      
+      tuple = PyList_GetItem (attributes, i);
+      
+      if (!PyTuple_Check (tuple)) 
+        {
+          PyErr_SetString(PyExc_TypeError,
+                          "attribute item must be a tuple");
+          return NULL;
+        }
+      
+      if (!PyTuple_Size (tuple) == 2)
+        {
+          PyErr_SetString(PyExc_IndexError,
+                          "attribute item must be a tuple of length 2");
+          return NULL;
+        }
+      
+      if (PyTuple_GetItem(tuple, 1) == Py_None)
+       continue;
+
+      /* this leaks, but we exit after, so */
+      if (!PyArg_ParseTuple(tuple, "ss", &attr, &value))
+        return NULL;
+
+      if (indent_len && !first)
+       {
+         g_string_append_c (attr_value, '\n');
+         for (j = 0; j < indent_len; j++)
+           g_string_append_c (attr_value, ' ');
+       }
+      g_string_append_c (attr_value, ' ');
+      g_string_append (attr_value, attr);
+      g_string_append_c (attr_value, '=');
+      g_string_append_c (attr_value, '\"');
+      escaped = g_markup_escape_text (value, -1);
+      g_string_append (attr_value, escaped);
+      g_string_append_c (attr_value, '\"');
+      if (first)
+       first = FALSE;
+  }
+
+  return PyString_FromString (g_string_free (attr_value, FALSE));
+}
+
 /* Module */
 
-PyMethodDef pyscanner_functions[] = {
+static const PyMethodDef pyscanner_functions[] = {
+  { "collect_attributes",
+    (PyCFunction) pygi_collect_attributes, METH_VARARGS },
   { NULL, NULL, 0, NULL }
 };
 
@@ -242,16 +670,17 @@ init_giscanner(void)
 {
     PyObject *m, *d;
 
-    m = Py_InitModule ("giscanner._giscanner", pyscanner_functions);
+    m = Py_InitModule ("giscanner._giscanner",
+                      (PyMethodDef*)pyscanner_functions);
     d = PyModule_GetDict (m);
 
     PyGISourceScanner_Type.tp_init = (initproc)pygi_source_scanner_init;
-    PyGISourceScanner_Type.tp_methods = _PyGISourceScanner_methods;
+    PyGISourceScanner_Type.tp_methods = (PyMethodDef*)_PyGISourceScanner_methods;
     REGISTER_TYPE (d, "SourceScanner", PyGISourceScanner_Type);
 
-    PyGISourceSymbol_Type.tp_getset = _PyGISourceSymbol_getsets;
+    PyGISourceSymbol_Type.tp_getset = (PyGetSetDef*)_PyGISourceSymbol_getsets;
     REGISTER_TYPE (d, "SourceSymbol", PyGISourceSymbol_Type);
 
-    PyGISourceType_Type.tp_getset = _PyGISourceType_getsets;
+    PyGISourceType_Type.tp_getset = (PyGetSetDef*)_PyGISourceType_getsets;
     REGISTER_TYPE (d, "SourceType", PyGISourceType_Type);
 }