gimp/app/config/gimpxmlparser.c

249 lines
6.2 KiB
C

/* The GIMP -- an image manipulation program
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* GimpXmlParser
* Copyright (C) 2003 Sven Neumann <sven@gimp.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <glib-object.h>
#include "config-types.h"
#include "gimpxmlparser.h"
struct _GimpXmlParser
{
GMarkupParseContext *context;
};
static gchar * parse_encoding (const gchar *text,
gint text_len);
/**
* gimp_xml_parser_new:
* @markup_parser: a #GMarkupParser
* @user_data: user data to pass to #GMarkupParser functions
*
* GimpXmlParser is a thin wrapper around GMarkupParser. This function
* creates one for you and sets up a GMarkupParseContext.
*
* Return value: a new #GimpXmlParser
**/
GimpXmlParser *
gimp_xml_parser_new (const GMarkupParser *markup_parser,
gpointer user_data)
{
GimpXmlParser *parser;
g_return_val_if_fail (markup_parser != NULL, NULL);
parser = g_new (GimpXmlParser, 1);
parser->context = g_markup_parse_context_new (markup_parser,
0, user_data, NULL);
return parser;
}
/**
* gimp_xml_parser_parse_file:
* @parser: a #GimpXmlParser
* @filename: name of a file to parse
* @error: return location for possible errors
*
* This function creates a GIOChannel for @filename and calls
* gimp_xml_parser_parse_io_channel() for you.
*
* Return value: %TRUE on success, %FALSE otherwise
**/
gboolean
gimp_xml_parser_parse_file (GimpXmlParser *parser,
const gchar *filename,
GError **error)
{
GIOChannel *io;
gboolean success;
g_return_val_if_fail (parser != NULL, FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
io = g_io_channel_new_file (filename, "r", error);
if (!io)
return FALSE;
success = gimp_xml_parser_parse_io_channel (parser, io, error);
g_io_channel_unref (io);
return success;
}
/**
* gimp_xml_parser_parse_io_channel:
* @parser: a #GimpXmlParser
* @io: a #GIOChannel
* @error: return location for possible errors
*
* Makes @parser read from the specified @io channel. This function
* returns when the GIOChannel becomes empty (end of file) or an
* error occurs, either reading from @io or parsing the read data.
*
* This function tries to determine the character encoding from the
* XML header and converts the content to UTF-8 for you.
*
* Return value: %TRUE on success, %FALSE otherwise
**/
gboolean
gimp_xml_parser_parse_io_channel (GimpXmlParser *parser,
GIOChannel *io,
GError **error)
{
GIOStatus status;
guchar buffer[8196];
gsize len = 0;
gsize bytes;
const gchar *io_encoding;
gchar *encoding = NULL;
g_return_val_if_fail (parser != NULL, FALSE);
g_return_val_if_fail (io != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
io_encoding = g_io_channel_get_encoding (io);
if (io_encoding && strcmp (io_encoding, "UTF-8"))
{
g_warning ("gimp_xml_parser_parse_io_channel():\n"
"The encoding has already been set on this GIOChannel!");
return FALSE;
}
/* try to determine the encoding */
while (len < sizeof (buffer) && !encoding)
{
status = g_io_channel_read_chars (io,
buffer + len, 1, &bytes, error);
len += bytes;
if (status == G_IO_STATUS_ERROR)
return FALSE;
if (status == G_IO_STATUS_EOF)
break;
encoding = parse_encoding (buffer, len);
}
if (encoding)
{
if (!g_io_channel_set_encoding (io, encoding, error))
return FALSE;
g_free (encoding);
}
while (TRUE)
{
if (!g_markup_parse_context_parse (parser->context, buffer, len, error))
return FALSE;
status = g_io_channel_read_chars (io,
buffer, sizeof (buffer), &len, error);
switch (status)
{
case G_IO_STATUS_ERROR:
return FALSE;
case G_IO_STATUS_EOF:
return g_markup_parse_context_end_parse (parser->context, error);
case G_IO_STATUS_NORMAL:
case G_IO_STATUS_AGAIN:
break;
}
}
}
/**
* gimp_xml_parser_free:
* @parser: a #GimpXmlParser
*
* Frees the resources allocated for @parser. You must not access
* @parser after calling this function.
**/
void
gimp_xml_parser_free (GimpXmlParser *parser)
{
g_return_if_fail (parser != NULL);
g_markup_parse_context_free (parser->context);
g_free (parser);
}
static gchar *
parse_encoding (const gchar *text,
gint text_len)
{
const gchar *start;
const gchar *end;
gint i;
g_return_val_if_fail (text, NULL);
if (text_len < 20)
return NULL;
start = g_strstr_len (text, text_len, "<?xml");
if (!start)
return NULL;
end = g_strstr_len (start, text_len - (start - text), "?>");
if (!end)
return NULL;
text_len = end - start;
if (text_len < 12)
return NULL;
start = g_strstr_len (start + 1, text_len - 1, "encoding=");
if (!start)
return NULL;
start += 9;
if (*start != '\"' && *start != '\'')
return NULL;
text_len = end - start;
if (text_len < 1)
return NULL;
for (i = 1; i < text_len; i++)
if (start[i] == start[0])
break;
if (i == text_len || i < 3)
return NULL;
return g_strndup (start + 1, i - 1);
}