Home | History | Annotate | Download | only in bus
      1 /* -*- mode: C; c-file-style: "gnu" -*- */
      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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     21  *
     22  */
     23 
     24 #include "config-parser.h"
     25 #include <dbus/dbus-internals.h>
     26 #include <expat.h>
     27 
     28 static XML_Memory_Handling_Suite memsuite =
     29 {
     30   dbus_malloc,
     31   dbus_realloc,
     32   dbus_free
     33 };
     34 
     35 typedef struct
     36 {
     37   BusConfigParser *parser;
     38   const char *filename;
     39   DBusString content;
     40   DBusError *error;
     41   dbus_bool_t failed;
     42 } ExpatParseContext;
     43 
     44 static dbus_bool_t
     45 process_content (ExpatParseContext *context)
     46 {
     47   if (context->failed)
     48     return FALSE;
     49 
     50   if (_dbus_string_get_length (&context->content) > 0)
     51     {
     52       if (!bus_config_parser_content (context->parser,
     53                                       &context->content,
     54                                       context->error))
     55         {
     56           context->failed = TRUE;
     57           return FALSE;
     58         }
     59       _dbus_string_set_length (&context->content, 0);
     60     }
     61 
     62   return TRUE;
     63 }
     64 
     65 static void
     66 expat_StartElementHandler (void            *userData,
     67                            const XML_Char  *name,
     68                            const XML_Char **atts)
     69 {
     70   ExpatParseContext *context = userData;
     71   int i;
     72   char **names;
     73   char **values;
     74 
     75   /* Expat seems to suck and can't abort the parse if we
     76    * throw an error. Expat 2.0 is supposed to fix this.
     77    */
     78   if (context->failed)
     79     return;
     80 
     81   if (!process_content (context))
     82     return;
     83 
     84   /* "atts" is key, value, key, value, NULL */
     85   for (i = 0; atts[i] != NULL; ++i)
     86     ; /* nothing */
     87 
     88   _dbus_assert (i % 2 == 0);
     89   names = dbus_new0 (char *, i / 2 + 1);
     90   values = dbus_new0 (char *, i / 2 + 1);
     91 
     92   if (names == NULL || values == NULL)
     93     {
     94       dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL);
     95       context->failed = TRUE;
     96       dbus_free (names);
     97       dbus_free (values);
     98       return;
     99     }
    100 
    101   i = 0;
    102   while (atts[i] != NULL)
    103     {
    104       _dbus_assert (i % 2 == 0);
    105       names [i / 2] = (char*) atts[i];
    106       values[i / 2] = (char*) atts[i+1];
    107 
    108       i += 2;
    109     }
    110 
    111   if (!bus_config_parser_start_element (context->parser,
    112                                         name,
    113                                         (const char **) names,
    114                                         (const char **) values,
    115                                         context->error))
    116     {
    117       dbus_free (names);
    118       dbus_free (values);
    119       context->failed = TRUE;
    120       return;
    121     }
    122 
    123   dbus_free (names);
    124   dbus_free (values);
    125 }
    126 
    127 static void
    128 expat_EndElementHandler (void           *userData,
    129                          const XML_Char *name)
    130 {
    131   ExpatParseContext *context = userData;
    132 
    133   if (!process_content (context))
    134     return;
    135 
    136   if (!bus_config_parser_end_element (context->parser,
    137                                       name,
    138                                       context->error))
    139     {
    140       context->failed = TRUE;
    141       return;
    142     }
    143 }
    144 
    145 /* s is not 0 terminated. */
    146 static void
    147 expat_CharacterDataHandler (void           *userData,
    148                             const XML_Char *s,
    149                             int             len)
    150 {
    151   ExpatParseContext *context = userData;
    152   if (context->failed)
    153     return;
    154 
    155   if (!_dbus_string_append_len (&context->content,
    156                                 s, len))
    157     {
    158       dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL);
    159       context->failed = TRUE;
    160       return;
    161     }
    162 }
    163 
    164 
    165 BusConfigParser*
    166 bus_config_load (const DBusString      *file,
    167                  dbus_bool_t            is_toplevel,
    168                  const BusConfigParser *parent,
    169                  DBusError             *error)
    170 {
    171   XML_Parser expat;
    172   const char *filename;
    173   BusConfigParser *parser;
    174   ExpatParseContext context;
    175   DBusString dirname;
    176 
    177   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    178 
    179   parser = NULL;
    180   expat = NULL;
    181   context.error = error;
    182   context.failed = FALSE;
    183 
    184   filename = _dbus_string_get_const_data (file);
    185 
    186   if (!_dbus_string_init (&context.content))
    187     {
    188       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    189       return NULL;
    190     }
    191 
    192   if (!_dbus_string_init (&dirname))
    193     {
    194       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    195       _dbus_string_free (&context.content);
    196       return NULL;
    197     }
    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