Home | History | Annotate | Download | only in bus
      1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
      2 /* config-loader-expat.c  expat XML loader
      3  *
      4  * Copyright (C) 2003 Red Hat, Inc.
      5  *
      6  * Licensed under the Academic Free License version 2.1
      7  *
      8  * This program is free software; you can redistribute it and/or modify
      9  * it under the terms of the GNU General Public License as published by
     10  * the Free Software Foundation; either version 2 of the License, or
     11  * (at your option) any later version.
     12  *
     13  * This program 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
     16  * GNU General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program; if not, write to the Free Software
     20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #include <config.h>
     25 #include "config-parser.h"
     26 #include <dbus/dbus-internals.h>
     27 #include <expat.h>
     28 
     29 static XML_Memory_Handling_Suite memsuite;
     30 
     31 typedef struct
     32 {
     33   BusConfigParser *parser;
     34   const char *filename;
     35   DBusString content;
     36   DBusError *error;
     37   dbus_bool_t failed;
     38 } ExpatParseContext;
     39 
     40 static dbus_bool_t
     41 process_content (ExpatParseContext *context)
     42 {
     43   if (context->failed)
     44     return FALSE;
     45 
     46   if (_dbus_string_get_length (&context->content) > 0)
     47     {
     48       if (!bus_config_parser_content (context->parser,
     49                                       &context->content,
     50                                       context->error))
     51         {
     52           context->failed = TRUE;
     53           return FALSE;
     54         }
     55       _dbus_string_set_length (&context->content, 0);
     56     }
     57 
     58   return TRUE;
     59 }
     60 
     61 static void
     62 expat_StartElementHandler (void            *userData,
     63                            const XML_Char  *name,
     64                            const XML_Char **atts)
     65 {
     66   ExpatParseContext *context = userData;
     67   int i;
     68   char **names;
     69   char **values;
     70 
     71   /* Expat seems to suck and can't abort the parse if we
     72    * throw an error. Expat 2.0 is supposed to fix this.
     73    */
     74   if (context->failed)
     75     return;
     76 
     77   if (!process_content (context))
     78     return;
     79 
     80   /* "atts" is key, value, key, value, NULL */
     81   for (i = 0; atts[i] != NULL; ++i)
     82     ; /* nothing */
     83 
     84   _dbus_assert (i % 2 == 0);
     85   names = dbus_new0 (char *, i / 2 + 1);
     86   values = dbus_new0 (char *, i / 2 + 1);
     87 
     88   if (names == NULL || values == NULL)
     89     {
     90       dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL);
     91       context->failed = TRUE;
     92       dbus_free (names);
     93       dbus_free (values);
     94       return;
     95     }
     96 
     97   i = 0;
     98   while (atts[i] != NULL)
     99     {
    100       _dbus_assert (i % 2 == 0);
    101       names [i / 2] = (char*) atts[i];
    102       values[i / 2] = (char*) atts[i+1];
    103 
    104       i += 2;
    105     }
    106 
    107   if (!bus_config_parser_start_element (context->parser,
    108                                         name,
    109                                         (const char **) names,
    110                                         (const char **) values,
    111                                         context->error))
    112     {
    113       dbus_free (names);
    114       dbus_free (values);
    115       context->failed = TRUE;
    116       return;
    117     }
    118 
    119   dbus_free (names);
    120   dbus_free (values);
    121 }
    122 
    123 static void
    124 expat_EndElementHandler (void           *userData,
    125                          const XML_Char *name)
    126 {
    127   ExpatParseContext *context = userData;
    128 
    129   if (!process_content (context))
    130     return;
    131 
    132   if (!bus_config_parser_end_element (context->parser,
    133                                       name,
    134                                       context->error))
    135     {
    136       context->failed = TRUE;
    137       return;
    138     }
    139 }
    140 
    141 /* s is not 0 terminated. */
    142 static void
    143 expat_CharacterDataHandler (void           *userData,
    144                             const XML_Char *s,
    145                             int             len)
    146 {
    147   ExpatParseContext *context = userData;
    148   if (context->failed)
    149     return;
    150 
    151   if (!_dbus_string_append_len (&context->content,
    152                                 s, len))
    153     {
    154       dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL);
    155       context->failed = TRUE;
    156       return;
    157     }
    158 }
    159 
    160 
    161 BusConfigParser*
    162 bus_config_load (const DBusString      *file,
    163                  dbus_bool_t            is_toplevel,
    164                  const BusConfigParser *parent,
    165                  DBusError             *error)
    166 {
    167   XML_Parser expat;
    168   const char *filename;
    169   BusConfigParser *parser;
    170   ExpatParseContext context;
    171   DBusString dirname;
    172 
    173   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    174 
    175   parser = NULL;
    176   expat = NULL;
    177   context.error = error;
    178   context.failed = FALSE;
    179 
    180   filename = _dbus_string_get_const_data (file);
    181 
    182   if (!_dbus_string_init (&context.content))
    183     {
    184       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    185       return NULL;
    186     }
    187 
    188   if (!_dbus_string_init (&dirname))
    189     {
    190       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    191       _dbus_string_free (&context.content);
    192       return NULL;
    193     }
    194 
    195   memsuite.malloc_fcn = dbus_malloc;
    196   memsuite.realloc_fcn = dbus_realloc;
    197   memsuite.free_fcn = dbus_free;
    198 
    199   expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL);
    200   if (expat == NULL)
    201     {
    202       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    203       goto failed;
    204     }
    205 
    206   if (!_dbus_string_get_dirname (file, &dirname))
    207     {
    208       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    209       goto failed;
    210     }
    211 
    212   parser = bus_config_parser_new (&dirname, is_toplevel, parent);
    213   if (parser == NULL)
    214     {
    215       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    216       goto failed;
    217     }
    218   context.parser = parser;
    219 
    220   XML_SetUserData (expat, &context);
    221   XML_SetElementHandler (expat,
    222                          expat_StartElementHandler,
    223                          expat_EndElementHandler);
    224   XML_SetCharacterDataHandler (expat,
    225                                expat_CharacterDataHandler);
    226 
    227   {
    228     DBusString data;
    229     const char *data_str;
    230 
    231     if (!_dbus_string_init (&data))
    232       {
    233         dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    234         goto failed;
    235       }
    236 
    237     if (!_dbus_file_get_contents (&data, file, error))
    238       {
    239         _dbus_string_free (&data);
    240         goto failed;
    241       }
    242 
    243     data_str = _dbus_string_get_const_data (&data);
    244 
    245     if (!XML_Parse (expat, data_str, _dbus_string_get_length (&data), TRUE))
    246       {
    247         if (context.error != NULL &&
    248             !dbus_error_is_set (context.error))
    249           {
    250             enum XML_Error e;
    251 
    252             e = XML_GetErrorCode (expat);
    253             if (e == XML_ERROR_NO_MEMORY)
    254               dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    255             else
    256               dbus_set_error (error, DBUS_ERROR_FAILED,
    257                               "Error in file %s, line %d, column %d: %s\n",
    258                               filename,
    259                               XML_GetCurrentLineNumber (expat),
    260                               XML_GetCurrentColumnNumber (expat),
    261                               XML_ErrorString (e));
    262           }
    263 
    264         _dbus_string_free (&data);
    265         goto failed;
    266       }
    267 
    268     _dbus_string_free (&data);
    269 
    270     if (context.failed)
    271       goto failed;
    272   }
    273 
    274   if (!bus_config_parser_finished (parser, error))
    275     goto failed;
    276 
    277   _dbus_string_free (&dirname);
    278   _dbus_string_free (&context.content);
    279   XML_ParserFree (expat);
    280 
    281   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    282   return parser;
    283 
    284  failed:
    285   _DBUS_ASSERT_ERROR_IS_SET (error);
    286 
    287   _dbus_string_free (&dirname);
    288   _dbus_string_free (&context.content);
    289   if (expat)
    290     XML_ParserFree (expat);
    291   if (parser)
    292     bus_config_parser_unref (parser);
    293   return NULL;
    294 }
    295