(root)/
glib-2.79.0/
girepository/
gdump.c
       1  /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
       2   * GObject introspection: Dump introspection data
       3   *
       4   * Copyright (C) 2008 Colin Walters <walters@verbum.org>
       5   *
       6   * SPDX-License-Identifier: LGPL-2.1-or-later
       7   *
       8   * This library is free software; you can redistribute it and/or
       9   * modify it under the terms of the GNU Lesser General Public
      10   * License as published by the Free Software Foundation; either
      11   * version 2 of the License, or (at your option) any later version.
      12   *
      13   * This library is distributed in the hope that it will be useful,
      14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16   * Lesser General Public License for more details.
      17   *
      18   * You should have received a copy of the GNU Lesser General Public
      19   * License along with this library; if not, write to the
      20   * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      21   * Boston, MA 02111-1307, USA.
      22   */
      23  
      24  /* This file is both compiled into libgirepository.so, and installed
      25   * on the filesystem.  But for the dumper, we want to avoid linking
      26   * to libgirepository; see
      27   * https://bugzilla.gnome.org/show_bug.cgi?id=630342
      28   */
      29  #ifdef GI_COMPILATION
      30  #include "config.h"
      31  #include "girepository.h"
      32  #endif
      33  
      34  #include <glib.h>
      35  #include <glib-object.h>
      36  #include <gmodule.h>
      37  
      38  #include <stdlib.h>
      39  #include <stdio.h>
      40  #include <string.h>
      41  
      42  /* Analogue of g_output_stream_write_all(). */
      43  static gboolean
      44  write_all (FILE          *out,
      45             const void    *buffer,
      46             gsize          count,
      47             gsize         *bytes_written,
      48             GError       **error)
      49  {
      50    size_t ret;
      51  
      52    ret = fwrite (buffer, 1, count, out);
      53  
      54    if (bytes_written != NULL)
      55      *bytes_written = ret;
      56  
      57    if (ret < count)
      58      {
      59        g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
      60                     "Failed to write to file");
      61        return FALSE;
      62      }
      63  
      64    return TRUE;
      65  }
      66  
      67  /* Analogue of g_data_input_stream_read_line(). */
      68  static char *
      69  read_line (FILE   *input,
      70             size_t *len_out)
      71  {
      72    GByteArray *buffer = g_byte_array_new ();
      73    const guint8 nul = '\0';
      74  
      75    while (TRUE)
      76      {
      77        size_t ret;
      78        guint8 byte;
      79  
      80        ret = fread (&byte, 1, 1, input);
      81        if (ret == 0)
      82          break;
      83  
      84        if (byte == '\n')
      85          break;
      86  
      87        g_byte_array_append (buffer, &byte, 1);
      88      }
      89  
      90    g_byte_array_append (buffer, &nul, 1);
      91  
      92    if (len_out != NULL)
      93      *len_out = buffer->len - 1;  /* don’t include terminating nul */
      94  
      95    return (char *) g_byte_array_free (buffer, FALSE);
      96  }
      97  
      98  static void
      99  escaped_printf (FILE *out, const char *fmt, ...) G_GNUC_PRINTF (2, 3);
     100  
     101  static void
     102  escaped_printf (FILE *out, const char *fmt, ...)
     103  {
     104    char *str;
     105    va_list args;
     106    gsize written;
     107    GError *error = NULL;
     108  
     109    va_start (args, fmt);
     110  
     111    str = g_markup_vprintf_escaped (fmt, args);
     112    if (!write_all (out, str, strlen (str), &written, &error))
     113      {
     114        g_critical ("failed to write to iochannel: %s", error->message);
     115        g_clear_error (&error);
     116      }
     117    g_free (str);
     118  
     119    va_end (args);
     120  }
     121  
     122  static void
     123  goutput_write (FILE *out, const char *str)
     124  {
     125    gsize written;
     126    GError *error = NULL;
     127    if (!write_all (out, str, strlen (str), &written, &error))
     128      {
     129        g_critical ("failed to write to iochannel: %s", error->message);
     130        g_clear_error (&error);
     131      }
     132  }
     133  
     134  typedef GType (*GetTypeFunc)(void);
     135  typedef GQuark (*ErrorQuarkFunc)(void);
     136  
     137  static GType
     138  invoke_get_type (GModule *self, const char *symbol, GError **error)
     139  {
     140    GetTypeFunc sym;
     141    GType ret;
     142  
     143    if (!g_module_symbol (self, symbol, (void**)&sym))
     144      {
     145        g_set_error (error,
     146  		   G_FILE_ERROR,
     147  		   G_FILE_ERROR_FAILED,
     148  		   "Failed to find symbol '%s'", symbol);
     149        return G_TYPE_INVALID;
     150      }
     151  
     152    ret = sym ();
     153    if (ret == G_TYPE_INVALID)
     154      {
     155        g_set_error (error,
     156  		   G_FILE_ERROR,
     157  		   G_FILE_ERROR_FAILED,
     158  		   "Function '%s' returned G_TYPE_INVALID", symbol);
     159      }
     160    return ret;
     161  }
     162  
     163  static GQuark
     164  invoke_error_quark (GModule *self, const char *symbol, GError **error)
     165  {
     166    ErrorQuarkFunc sym;
     167  
     168    if (!g_module_symbol (self, symbol, (void**)&sym))
     169      {
     170        g_set_error (error,
     171  		   G_FILE_ERROR,
     172  		   G_FILE_ERROR_FAILED,
     173  		   "Failed to find symbol '%s'", symbol);
     174        return G_TYPE_INVALID;
     175      }
     176  
     177    return sym ();
     178  }
     179  
     180  static char *
     181  value_transform_to_string (const GValue *value)
     182  {
     183    GValue tmp = G_VALUE_INIT;
     184    char *s = NULL;
     185  
     186    g_value_init (&tmp, G_TYPE_STRING);
     187  
     188    if (g_value_transform (value, &tmp))
     189      {
     190        const char *str = g_value_get_string (&tmp);
     191  
     192        if (str != NULL)
     193          s = g_strescape (str, NULL);
     194      }
     195  
     196    g_value_unset (&tmp);
     197  
     198    return s;
     199  }
     200  
     201  /* A simpler version of g_strdup_value_contents(), but with stable
     202   * output and less complex semantics
     203   */
     204  static char *
     205  value_to_string (const GValue *value)
     206  {
     207    if (value == NULL)
     208      return NULL;
     209  
     210    if (G_VALUE_HOLDS_STRING (value))
     211      {
     212        const char *s = g_value_get_string (value);
     213  
     214        if (s == NULL)
     215          return g_strdup ("NULL");
     216  
     217        return g_strescape (s, NULL);
     218      }
     219    else
     220      {
     221        GType value_type = G_VALUE_TYPE (value);
     222  
     223        switch (G_TYPE_FUNDAMENTAL (value_type))
     224          {
     225          case G_TYPE_BOXED:
     226            if (g_value_get_boxed (value) == NULL)
     227              return NULL;
     228            else
     229              return value_transform_to_string (value);
     230            break;
     231  
     232          case G_TYPE_OBJECT:
     233            if (g_value_get_object (value) == NULL)
     234              return NULL;
     235            else
     236              return value_transform_to_string (value);
     237            break;
     238  
     239          case G_TYPE_POINTER:
     240            return NULL;
     241  
     242          default:
     243            return value_transform_to_string (value);
     244          }
     245      }
     246  
     247    return NULL;
     248  }
     249  
     250  static void
     251  dump_properties (GType type, FILE *out)
     252  {
     253    guint i;
     254    guint n_properties = 0;
     255    GParamSpec **props;
     256  
     257    if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT)
     258      {
     259        GObjectClass *klass;
     260        klass = g_type_class_ref (type);
     261        props = g_object_class_list_properties (klass, &n_properties);
     262      }
     263    else
     264      {
     265        void *klass;
     266        klass = g_type_default_interface_ref (type);
     267        props = g_object_interface_list_properties (klass, &n_properties);
     268      }
     269  
     270    for (i = 0; i < n_properties; i++)
     271      {
     272        GParamSpec *prop;
     273  
     274        prop = props[i];
     275        if (prop->owner_type != type)
     276  	continue;
     277  
     278        const GValue *v = g_param_spec_get_default_value (prop);
     279        char *default_value = value_to_string (v);
     280  
     281        if (v != NULL && default_value != NULL)
     282          {
     283            escaped_printf (out, "    <property name=\"%s\" type=\"%s\" flags=\"%d\" default-value=\"%s\"/>\n",
     284                            prop->name,
     285                            g_type_name (prop->value_type),
     286                            prop->flags,
     287                            default_value);
     288          }
     289        else
     290          {
     291            escaped_printf (out, "    <property name=\"%s\" type=\"%s\" flags=\"%d\"/>\n",
     292                            prop->name,
     293                            g_type_name (prop->value_type),
     294                            prop->flags);
     295          }
     296  
     297        g_free (default_value);
     298      }
     299  
     300    g_free (props);
     301  }
     302  
     303  static void
     304  dump_signals (GType type, FILE *out)
     305  {
     306    guint i;
     307    guint n_sigs;
     308    guint *sig_ids;
     309  
     310    sig_ids = g_signal_list_ids (type, &n_sigs);
     311    for (i = 0; i < n_sigs; i++)
     312      {
     313        guint sigid;
     314        GSignalQuery query;
     315        guint j;
     316  
     317        sigid = sig_ids[i];
     318        g_signal_query (sigid, &query);
     319  
     320        escaped_printf (out, "    <signal name=\"%s\" return=\"%s\"",
     321  		      query.signal_name, g_type_name (query.return_type));
     322  
     323        if (query.signal_flags & G_SIGNAL_RUN_FIRST)
     324          escaped_printf (out, " when=\"first\"");
     325        else if (query.signal_flags & G_SIGNAL_RUN_LAST)
     326          escaped_printf (out, " when=\"last\"");
     327        else if (query.signal_flags & G_SIGNAL_RUN_CLEANUP)
     328          escaped_printf (out, " when=\"cleanup\"");
     329        else if (query.signal_flags & G_SIGNAL_MUST_COLLECT)
     330          escaped_printf (out, " when=\"must-collect\"");
     331        if (query.signal_flags & G_SIGNAL_NO_RECURSE)
     332          escaped_printf (out, " no-recurse=\"1\"");
     333  
     334        if (query.signal_flags & G_SIGNAL_DETAILED)
     335          escaped_printf (out, " detailed=\"1\"");
     336  
     337        if (query.signal_flags & G_SIGNAL_ACTION)
     338          escaped_printf (out, " action=\"1\"");
     339  
     340        if (query.signal_flags & G_SIGNAL_NO_HOOKS)
     341          escaped_printf (out, " no-hooks=\"1\"");
     342  
     343        goutput_write (out, ">\n");
     344  
     345        for (j = 0; j < query.n_params; j++)
     346  	{
     347  	  escaped_printf (out, "      <param type=\"%s\"/>\n",
     348  			  g_type_name (query.param_types[j]));
     349  	}
     350        goutput_write (out, "    </signal>\n");
     351      }
     352    g_free (sig_ids);
     353  }
     354  
     355  static void
     356  dump_object_type (GType type, const char *symbol, FILE *out)
     357  {
     358    guint n_interfaces;
     359    guint i;
     360    GType *interfaces;
     361  
     362    escaped_printf (out, "  <class name=\"%s\" get-type=\"%s\"",
     363  		  g_type_name (type), symbol);
     364    if (type != G_TYPE_OBJECT)
     365      {
     366        GString *parent_str;
     367        GType parent;
     368        gboolean first = TRUE;
     369  
     370        parent = g_type_parent (type);
     371        parent_str = g_string_new ("");
     372        while (parent != G_TYPE_INVALID)
     373          {
     374            if (first)
     375              first = FALSE;
     376            else
     377              g_string_append_c (parent_str, ',');
     378            g_string_append (parent_str, g_type_name (parent));
     379            parent = g_type_parent (parent);
     380          }
     381  
     382        escaped_printf (out, " parents=\"%s\"", parent_str->str);
     383  
     384        g_string_free (parent_str, TRUE);
     385      }
     386  
     387    if (G_TYPE_IS_ABSTRACT (type))
     388      escaped_printf (out, " abstract=\"1\"");
     389  
     390    if (G_TYPE_IS_FINAL (type))
     391      escaped_printf (out, " final=\"1\"");
     392  
     393    goutput_write (out, ">\n");
     394  
     395    interfaces = g_type_interfaces (type, &n_interfaces);
     396    for (i = 0; i < n_interfaces; i++)
     397      {
     398        GType itype = interfaces[i];
     399        escaped_printf (out, "    <implements name=\"%s\"/>\n",
     400  		      g_type_name (itype));
     401      }
     402    g_free (interfaces);
     403  
     404    dump_properties (type, out);
     405    dump_signals (type, out);
     406    goutput_write (out, "  </class>\n");
     407  }
     408  
     409  static void
     410  dump_interface_type (GType type, const char *symbol, FILE *out)
     411  {
     412    guint n_interfaces;
     413    guint i;
     414    GType *interfaces;
     415  
     416    escaped_printf (out, "  <interface name=\"%s\" get-type=\"%s\">\n",
     417  		  g_type_name (type), symbol);
     418  
     419    interfaces = g_type_interface_prerequisites (type, &n_interfaces);
     420    for (i = 0; i < n_interfaces; i++)
     421      {
     422        GType itype = interfaces[i];
     423        if (itype == G_TYPE_OBJECT)
     424  	{
     425  	  /* Treat this as implicit for now; in theory GInterfaces are
     426  	   * supported on things like GstMiniObject, but right now
     427  	   * the introspection system only supports GObject.
     428  	   * http://bugzilla.gnome.org/show_bug.cgi?id=559706
     429  	   */
     430  	  continue;
     431  	}
     432        escaped_printf (out, "    <prerequisite name=\"%s\"/>\n",
     433  		      g_type_name (itype));
     434      }
     435    g_free (interfaces);
     436  
     437    dump_properties (type, out);
     438    dump_signals (type, out);
     439    goutput_write (out, "  </interface>\n");
     440  }
     441  
     442  static void
     443  dump_boxed_type (GType type, const char *symbol, FILE *out)
     444  {
     445    escaped_printf (out, "  <boxed name=\"%s\" get-type=\"%s\"/>\n",
     446  		  g_type_name (type), symbol);
     447  }
     448  
     449  static void
     450  dump_flags_type (GType type, const char *symbol, FILE *out)
     451  {
     452    guint i;
     453    GFlagsClass *klass;
     454  
     455    klass = g_type_class_ref (type);
     456    escaped_printf (out, "  <flags name=\"%s\" get-type=\"%s\">\n",
     457  		  g_type_name (type), symbol);
     458  
     459    for (i = 0; i < klass->n_values; i++)
     460      {
     461        GFlagsValue *value = &(klass->values[i]);
     462  
     463        escaped_printf (out, "    <member name=\"%s\" nick=\"%s\" value=\"%u\"/>\n",
     464  		      value->value_name, value->value_nick, value->value);
     465      }
     466    goutput_write (out, "  </flags>\n");
     467  }
     468  
     469  static void
     470  dump_enum_type (GType type, const char *symbol, FILE *out)
     471  {
     472    guint i;
     473    GEnumClass *klass;
     474  
     475    klass = g_type_class_ref (type);
     476    escaped_printf (out, "  <enum name=\"%s\" get-type=\"%s\">\n",
     477  		  g_type_name (type), symbol);
     478  
     479    for (i = 0; i < klass->n_values; i++)
     480      {
     481        GEnumValue *value = &(klass->values[i]);
     482  
     483        escaped_printf (out, "    <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
     484  		      value->value_name, value->value_nick, value->value);
     485      }
     486    goutput_write (out, "  </enum>");
     487  }
     488  
     489  static void
     490  dump_fundamental_type (GType type, const char *symbol, FILE *out)
     491  {
     492    guint n_interfaces;
     493    guint i;
     494    GType *interfaces;
     495    GString *parent_str;
     496    GType parent;
     497    gboolean first = TRUE;
     498  
     499  
     500    escaped_printf (out, "  <fundamental name=\"%s\" get-type=\"%s\"",
     501  		  g_type_name (type), symbol);
     502  
     503    if (G_TYPE_IS_ABSTRACT (type))
     504      escaped_printf (out, " abstract=\"1\"");
     505  
     506    if (G_TYPE_IS_FINAL (type))
     507      escaped_printf (out, " final=\"1\"");
     508  
     509    if (G_TYPE_IS_INSTANTIATABLE (type))
     510      escaped_printf (out, " instantiatable=\"1\"");
     511  
     512    parent = g_type_parent (type);
     513    parent_str = g_string_new ("");
     514    while (parent != G_TYPE_INVALID)
     515      {
     516        if (first)
     517          first = FALSE;
     518        else
     519          g_string_append_c (parent_str, ',');
     520        if (!g_type_name (parent))
     521          break;
     522        g_string_append (parent_str, g_type_name (parent));
     523        parent = g_type_parent (parent);
     524      }
     525  
     526    if (parent_str->len > 0)
     527      escaped_printf (out, " parents=\"%s\"", parent_str->str);
     528    g_string_free (parent_str, TRUE);
     529  
     530    goutput_write (out, ">\n");
     531  
     532    interfaces = g_type_interfaces (type, &n_interfaces);
     533    for (i = 0; i < n_interfaces; i++)
     534      {
     535        GType itype = interfaces[i];
     536        escaped_printf (out, "    <implements name=\"%s\"/>\n",
     537  		      g_type_name (itype));
     538      }
     539    g_free (interfaces);
     540    goutput_write (out, "  </fundamental>\n");
     541  }
     542  
     543  static void
     544  dump_type (GType type, const char *symbol, FILE *out)
     545  {
     546    switch (g_type_fundamental (type))
     547      {
     548      case G_TYPE_OBJECT:
     549        dump_object_type (type, symbol, out);
     550        break;
     551      case G_TYPE_INTERFACE:
     552        dump_interface_type (type, symbol, out);
     553        break;
     554      case G_TYPE_BOXED:
     555        dump_boxed_type (type, symbol, out);
     556        break;
     557      case G_TYPE_FLAGS:
     558        dump_flags_type (type, symbol, out);
     559        break;
     560      case G_TYPE_ENUM:
     561        dump_enum_type (type, symbol, out);
     562        break;
     563      case G_TYPE_POINTER:
     564        /* GValue, etc.  Just skip them. */
     565        break;
     566      default:
     567        dump_fundamental_type (type, symbol, out);
     568        break;
     569      }
     570  }
     571  
     572  static void
     573  dump_error_quark (GQuark quark, const char *symbol, FILE *out)
     574  {
     575    escaped_printf (out, "  <error-quark function=\"%s\" domain=\"%s\"/>\n",
     576  		  symbol, g_quark_to_string (quark));
     577  }
     578  
     579  /**
     580   * gi_repository_dump:
     581   * @input_filename: (type filename): Input filename (for example `input.txt`)
     582   * @output_filename: (type filename): Output filename (for example `output.xml`)
     583   * @error: a %GError
     584   *
     585   * Dump the introspection data from the types specified in @input_filename to
     586   * @output_filename.
     587   *
     588   * The input file should be a
     589   * UTF-8 Unix-line-ending text file, with each line containing either
     590   * `get-type:` followed by the name of a [type@GObject.Type] `_get_type`
     591   * function, or `error-quark:` followed by the name of an error quark function.
     592   * No extra whitespace is allowed.
     593   *
     594   * This function will overwrite the contents of the output file.
     595   *
     596   * Returns: true on success, false on error
     597   * Since: 2.80
     598   */
     599  #ifndef GI_COMPILATION
     600  static gboolean
     601  dump_irepository (const char  *input_filename,
     602                    const char  *output_filename,
     603                    GError     **error) G_GNUC_UNUSED;
     604  static gboolean
     605  dump_irepository (const char  *input_filename,
     606                    const char  *output_filename,
     607                    GError     **error)
     608  #else
     609  gboolean
     610  gi_repository_dump (const char  *input_filename,
     611                      const char  *output_filename,
     612                      GError     **error)
     613  #endif
     614  {
     615    GHashTable *output_types;
     616    FILE *input;
     617    FILE *output;
     618    GModule *self;
     619    gboolean caught_error = FALSE;
     620  
     621    self = g_module_open (NULL, 0);
     622    if (!self)
     623      {
     624        g_set_error (error,
     625  		   G_FILE_ERROR,
     626  		   G_FILE_ERROR_FAILED,
     627  		   "failed to open self: %s",
     628  		   g_module_error ());
     629        return FALSE;
     630      }
     631  
     632    input = fopen (input_filename, "rb");
     633    if (input == NULL)
     634      {
     635        int saved_errno = errno;
     636        g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
     637                     "Failed to open ‘%s’: %s", input_filename, g_strerror (saved_errno));
     638  
     639        g_module_close (self);
     640  
     641        return FALSE;
     642      }
     643  
     644    output = fopen (output_filename, "wb");
     645    if (output == NULL)
     646      {
     647        int saved_errno = errno;
     648        g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
     649                     "Failed to open ‘%s’: %s", output_filename, g_strerror (saved_errno));
     650  
     651        fclose (input);
     652        g_module_close (self);
     653  
     654        return FALSE;
     655      }
     656  
     657    goutput_write (output, "<?xml version=\"1.0\"?>\n");
     658    goutput_write (output, "<dump>\n");
     659  
     660    output_types = g_hash_table_new (NULL, NULL);
     661  
     662    while (TRUE)
     663      {
     664        gsize len;
     665        char *line = read_line (input, &len);
     666        const char *function;
     667  
     668        if (line == NULL || *line == '\0')
     669          {
     670            g_free (line);
     671            break;
     672          }
     673  
     674        g_strchomp (line);
     675  
     676        if (strncmp (line, "get-type:", strlen ("get-type:")) == 0)
     677          {
     678            GType type;
     679  
     680            function = line + strlen ("get-type:");
     681  
     682            type = invoke_get_type (self, function, error);
     683  
     684            if (type == G_TYPE_INVALID)
     685              {
     686                g_printerr ("Invalid GType function: '%s'\n", function);
     687                caught_error = TRUE;
     688                g_free (line);
     689                break;
     690              }
     691  
     692            if (g_hash_table_lookup (output_types, (gpointer) type))
     693              goto next;
     694            g_hash_table_insert (output_types, (gpointer) type, (gpointer) type);
     695  
     696            dump_type (type, function, output);
     697          }
     698        else if (strncmp (line, "error-quark:", strlen ("error-quark:")) == 0)
     699          {
     700            GQuark quark;
     701            function = line + strlen ("error-quark:");
     702            quark = invoke_error_quark (self, function, error);
     703  
     704            if (quark == 0)
     705              {
     706                g_printerr ("Invalid error quark function: '%s'\n", function);
     707                caught_error = TRUE;
     708                g_free (line);
     709                break;
     710              }
     711  
     712            dump_error_quark (quark, function, output);
     713          }
     714  
     715  
     716      next:
     717        g_free (line);
     718      }
     719  
     720    g_hash_table_destroy (output_types);
     721  
     722    goutput_write (output, "</dump>\n");
     723  
     724    {
     725      /* Avoid overwriting an earlier set error */
     726      if (fclose (input) != 0 && !caught_error)
     727        {
     728          int saved_errno = errno;
     729  
     730          g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
     731                       "Error closing input file ‘%s’: %s", input_filename,
     732                       g_strerror (saved_errno));
     733          caught_error = TRUE;
     734        }
     735  
     736      if (fclose (output) != 0 && !caught_error)
     737        {
     738          int saved_errno = errno;
     739  
     740          g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
     741                       "Error closing output file ‘%s’: %s", output_filename,
     742                       g_strerror (saved_errno));
     743          caught_error = TRUE;
     744        }
     745    }
     746  
     747    return !caught_error;
     748  }