|
Packit |
116408 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
|
|
Packit |
116408 |
/*
|
|
Packit |
116408 |
* Copyright (C) 2002-2003 Mikael Hallendal <micke@imendio.com>
|
|
Packit |
116408 |
* Copyright (C) 2002-2003 CodeFactory AB
|
|
Packit |
116408 |
* Copyright (C) 2005,2008 Imendio AB
|
|
Packit |
116408 |
* Copyright (C) 2017 Sébastien Wilmet <swilmet@gnome.org>
|
|
Packit |
116408 |
*
|
|
Packit |
116408 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
116408 |
* modify it under the terms of the GNU General Public License as
|
|
Packit |
116408 |
* published by the Free Software Foundation; either version 2 of the
|
|
Packit |
116408 |
* License, or (at your option) any later version.
|
|
Packit |
116408 |
*
|
|
Packit |
116408 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
116408 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
116408 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
116408 |
* General Public License for more details.
|
|
Packit |
116408 |
*
|
|
Packit |
116408 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
116408 |
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
116408 |
*/
|
|
Packit |
116408 |
|
|
Packit |
116408 |
#include "config.h"
|
|
Packit |
116408 |
#include "dh-parser.h"
|
|
Packit |
116408 |
#include <string.h>
|
|
Packit |
116408 |
#include "dh-error.h"
|
|
Packit |
116408 |
#include "dh-link.h"
|
|
Packit |
116408 |
#include "dh-util.h"
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* Possible things to do for the version 3 of the Devhelp index file format (if
|
|
Packit |
116408 |
* one day there is a strong desire to create a new version):
|
|
Packit |
116408 |
* - Replace <functions> element by <keywords>.
|
|
Packit |
116408 |
* - Maybe have an up-to-date URI for the NAMESPACE.
|
|
Packit |
116408 |
* - Rename <book> attribute 'name' to 'id', because "book name" can be confused
|
|
Packit |
116408 |
* with the book title or other 'name' attributes (for <sub> and <keyword>,
|
|
Packit |
116408 |
* the 'name' attribute has a different meaning). With "book ID" there is no
|
|
Packit |
116408 |
* ambiguity.
|
|
Packit |
116408 |
* - Maybe rename <book> attribute 'title' to 'name', to be consistent with
|
|
Packit |
116408 |
* the <sub> and <keyword> elements. dh_link_get_name() would also have a
|
|
Packit |
116408 |
* clearer meaning for book top-level links. But "book title" has the
|
|
Packit |
116408 |
* advantage that there is no ambiguity.
|
|
Packit |
116408 |
*/
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* It's the xmlns attribute. It is currently (well, in 2015 at least) used on
|
|
Packit |
116408 |
* developer.gnome.org to look for <keyword> elements attached to that
|
|
Packit |
116408 |
* namespace.
|
|
Packit |
116408 |
*
|
|
Packit |
116408 |
* devhelp.net was initially the Devhelp website, but it is now no longer the
|
|
Packit |
116408 |
* case. But it is not a problem, a namespace qualifies a node name, it doesn't
|
|
Packit |
116408 |
* have to be a real site. And it is now too late to change it, at least for the
|
|
Packit |
116408 |
* format version 2.
|
|
Packit |
116408 |
*
|
|
Packit |
116408 |
* See:
|
|
Packit |
116408 |
* https://bugzilla.gnome.org/show_bug.cgi?id=566447
|
|
Packit |
116408 |
* https://bugzilla.gnome.org/show_bug.cgi?id=749591#c1
|
|
Packit |
116408 |
*/
|
|
Packit |
116408 |
#define NAMESPACE "http://www.devhelp.net/book"
|
|
Packit |
116408 |
|
|
Packit |
116408 |
#define BYTES_PER_READ 4096
|
|
Packit |
116408 |
|
|
Packit |
116408 |
typedef enum {
|
|
Packit |
116408 |
FORMAT_VERSION_1,
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* The main change is that version 2 uses <keyword> instead of
|
|
Packit |
116408 |
* <function>.
|
|
Packit |
116408 |
*/
|
|
Packit |
116408 |
FORMAT_VERSION_2
|
|
Packit |
116408 |
} FormatVersion;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
typedef struct {
|
|
Packit |
116408 |
GMarkupParser *markup_parser;
|
|
Packit |
116408 |
GMarkupParseContext *context;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
GFile *index_file;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
gchar *book_title;
|
|
Packit |
116408 |
gchar *book_id;
|
|
Packit |
116408 |
gchar *book_language;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* List of all DhLink* */
|
|
Packit |
116408 |
GList *all_links;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* Tree of DhLink* (not including keywords).
|
|
Packit |
116408 |
* The top node of the book.
|
|
Packit |
116408 |
*/
|
|
Packit |
116408 |
GNode *book_node;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* Current sub section node */
|
|
Packit |
116408 |
GNode *parent_node;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
FormatVersion version;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
guint parsing_chapters : 1;
|
|
Packit |
116408 |
guint parsing_keywords : 1;
|
|
Packit |
116408 |
} DhParser;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
static void
|
|
Packit |
116408 |
dh_parser_free (DhParser *parser)
|
|
Packit |
116408 |
{
|
|
Packit |
116408 |
g_markup_parse_context_free (parser->context);
|
|
Packit |
116408 |
g_free (parser->markup_parser);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_clear_object (&parser->index_file);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_free (parser->book_title);
|
|
Packit |
116408 |
g_free (parser->book_id);
|
|
Packit |
116408 |
g_free (parser->book_language);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_list_free_full (parser->all_links, (GDestroyNotify)dh_link_unref);
|
|
Packit |
116408 |
_dh_util_free_book_tree (parser->book_node);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_free (parser);
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
static void
|
|
Packit |
116408 |
replace_newlines_by_spaces (gchar *str)
|
|
Packit |
116408 |
{
|
|
Packit |
116408 |
gint i;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (str == NULL)
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
for (i = 0; str[i] != '\0'; i++) {
|
|
Packit |
116408 |
if (str[i] == '\n' || str[i] == '\r')
|
|
Packit |
116408 |
str[i] = ' ';
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
static void
|
|
Packit |
116408 |
parser_start_node_book (DhParser *parser,
|
|
Packit |
116408 |
GMarkupParseContext *context,
|
|
Packit |
116408 |
const gchar *node_name,
|
|
Packit |
116408 |
const gchar **attribute_names,
|
|
Packit |
116408 |
const gchar **attribute_values,
|
|
Packit |
116408 |
GError **error)
|
|
Packit |
116408 |
{
|
|
Packit |
116408 |
gint line;
|
|
Packit |
116408 |
gint col;
|
|
Packit |
116408 |
gint attr_num;
|
|
Packit |
116408 |
gchar *base = NULL;
|
|
Packit |
116408 |
const gchar *name = NULL;
|
|
Packit |
116408 |
const gchar *title = NULL;
|
|
Packit |
116408 |
const gchar *uri = NULL;
|
|
Packit |
116408 |
const gchar *language = NULL;
|
|
Packit |
116408 |
DhLink *link;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (g_ascii_strcasecmp (node_name, "book") != 0) {
|
|
Packit |
116408 |
g_markup_parse_context_get_position (context, &line, &col);
|
|
Packit |
116408 |
g_set_error (error,
|
|
Packit |
116408 |
DH_ERROR,
|
|
Packit |
116408 |
DH_ERROR_MALFORMED_BOOK,
|
|
Packit |
116408 |
"Expected <book> element, got <%s> at line %d, column %d.",
|
|
Packit |
116408 |
node_name, line, col);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
for (attr_num = 0; attribute_names[attr_num] != NULL; attr_num++) {
|
|
Packit |
116408 |
if (g_ascii_strcasecmp (attribute_names[attr_num], "xmlns") == 0) {
|
|
Packit |
116408 |
const gchar *xmlns;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
xmlns = attribute_values[attr_num];
|
|
Packit |
116408 |
if (g_ascii_strcasecmp (xmlns, NAMESPACE) != 0) {
|
|
Packit |
116408 |
g_markup_parse_context_get_position (context, &line, &col);
|
|
Packit |
116408 |
g_set_error (error,
|
|
Packit |
116408 |
DH_ERROR,
|
|
Packit |
116408 |
DH_ERROR_MALFORMED_BOOK,
|
|
Packit |
116408 |
"Expected xmlns value “" NAMESPACE "”, "
|
|
Packit |
116408 |
"got “%s” at line %d, column %d.",
|
|
Packit |
116408 |
xmlns, line, col);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
} else if (g_ascii_strcasecmp (attribute_names[attr_num], "name") == 0) {
|
|
Packit |
116408 |
name = attribute_values[attr_num];
|
|
Packit |
116408 |
} else if (g_ascii_strcasecmp (attribute_names[attr_num], "title") == 0) {
|
|
Packit |
116408 |
title = attribute_values[attr_num];
|
|
Packit |
116408 |
} else if (g_ascii_strcasecmp (attribute_names[attr_num], "base") == 0) {
|
|
Packit |
116408 |
/* Dup this one */
|
|
Packit |
116408 |
base = g_strdup (attribute_values[attr_num]);
|
|
Packit |
116408 |
} else if (g_ascii_strcasecmp (attribute_names[attr_num], "link") == 0) {
|
|
Packit |
116408 |
uri = attribute_values[attr_num];
|
|
Packit |
116408 |
} else if (g_ascii_strcasecmp (attribute_names[attr_num], "language") == 0) {
|
|
Packit |
116408 |
language = attribute_values[attr_num];
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (name == NULL || title == NULL || uri == NULL) {
|
|
Packit |
116408 |
g_markup_parse_context_get_position (context, &line, &col);
|
|
Packit |
116408 |
g_set_error (error,
|
|
Packit |
116408 |
DH_ERROR,
|
|
Packit |
116408 |
DH_ERROR_MALFORMED_BOOK,
|
|
Packit |
116408 |
"“title”, “name” and “link” attributes are required "
|
|
Packit |
116408 |
"inside the <book> element at line %d, column %d.",
|
|
Packit |
116408 |
line, col);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* Store book metadata */
|
|
Packit |
116408 |
g_free (parser->book_title);
|
|
Packit |
116408 |
parser->book_title = g_strdup (title);
|
|
Packit |
116408 |
replace_newlines_by_spaces (parser->book_title);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_free (parser->book_id);
|
|
Packit |
116408 |
parser->book_id = g_strdup (name);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_free (parser->book_language);
|
|
Packit |
116408 |
parser->book_language = g_strdup (language);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (base == NULL) {
|
|
Packit |
116408 |
GFile *directory;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
directory = g_file_get_parent (parser->index_file);
|
|
Packit |
116408 |
base = g_file_get_path (directory);
|
|
Packit |
116408 |
g_object_unref (directory);
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
link = dh_link_new_book (base,
|
|
Packit |
116408 |
parser->book_id,
|
|
Packit |
116408 |
parser->book_title,
|
|
Packit |
116408 |
uri);
|
|
Packit |
116408 |
g_free (base);
|
|
Packit |
116408 |
parser->all_links = g_list_prepend (parser->all_links, link);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_assert (parser->book_node == NULL);
|
|
Packit |
116408 |
g_assert (parser->parent_node == NULL);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
parser->book_node = g_node_new (dh_link_ref (link));
|
|
Packit |
116408 |
parser->parent_node = parser->book_node;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
static void
|
|
Packit |
116408 |
parser_start_node_chapter (DhParser *parser,
|
|
Packit |
116408 |
GMarkupParseContext *context,
|
|
Packit |
116408 |
const gchar *node_name,
|
|
Packit |
116408 |
const gchar **attribute_names,
|
|
Packit |
116408 |
const gchar **attribute_values,
|
|
Packit |
116408 |
GError **error)
|
|
Packit |
116408 |
{
|
|
Packit |
116408 |
gint line;
|
|
Packit |
116408 |
gint col;
|
|
Packit |
116408 |
gint attr_num;
|
|
Packit |
116408 |
const gchar *name = NULL;
|
|
Packit |
116408 |
const gchar *uri = NULL;
|
|
Packit |
116408 |
DhLink *link;
|
|
Packit |
116408 |
GNode *node;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (g_ascii_strcasecmp (node_name, "sub") != 0) {
|
|
Packit |
116408 |
g_markup_parse_context_get_position (context, &line, &col);
|
|
Packit |
116408 |
g_set_error (error,
|
|
Packit |
116408 |
DH_ERROR,
|
|
Packit |
116408 |
DH_ERROR_MALFORMED_BOOK,
|
|
Packit |
116408 |
"Expected <sub> element, got <%s> at line %d, column %d.",
|
|
Packit |
116408 |
node_name, line, col);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
for (attr_num = 0; attribute_names[attr_num] != NULL; attr_num++) {
|
|
Packit |
116408 |
if (g_ascii_strcasecmp (attribute_names[attr_num], "name") == 0)
|
|
Packit |
116408 |
name = attribute_values[attr_num];
|
|
Packit |
116408 |
else if (g_ascii_strcasecmp (attribute_names[attr_num], "link") == 0)
|
|
Packit |
116408 |
uri = attribute_values[attr_num];
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (name == NULL || uri == NULL) {
|
|
Packit |
116408 |
g_markup_parse_context_get_position (context, &line, &col);
|
|
Packit |
116408 |
g_set_error (error,
|
|
Packit |
116408 |
DH_ERROR,
|
|
Packit |
116408 |
DH_ERROR_MALFORMED_BOOK,
|
|
Packit |
116408 |
"“name” and “link” elements are required inside "
|
|
Packit |
116408 |
"the <sub> element at line %d, column %d.",
|
|
Packit |
116408 |
line, col);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_assert (parser->book_node != NULL);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
link = dh_link_new (DH_LINK_TYPE_PAGE,
|
|
Packit |
116408 |
parser->book_node->data,
|
|
Packit |
116408 |
name,
|
|
Packit |
116408 |
uri);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
parser->all_links = g_list_prepend (parser->all_links, link);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_assert (parser->parent_node != NULL);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
node = g_node_new (dh_link_ref (link));
|
|
Packit |
116408 |
g_node_prepend (parser->parent_node, node);
|
|
Packit |
116408 |
parser->parent_node = node;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
static void
|
|
Packit |
116408 |
parser_start_node_keyword (DhParser *parser,
|
|
Packit |
116408 |
GMarkupParseContext *context,
|
|
Packit |
116408 |
const gchar *node_name,
|
|
Packit |
116408 |
const gchar **attribute_names,
|
|
Packit |
116408 |
const gchar **attribute_values,
|
|
Packit |
116408 |
GError **error)
|
|
Packit |
116408 |
{
|
|
Packit |
116408 |
gint line;
|
|
Packit |
116408 |
gint col;
|
|
Packit |
116408 |
gint attr_num;
|
|
Packit |
116408 |
const gchar *type = NULL;
|
|
Packit |
116408 |
const gchar *name = NULL;
|
|
Packit |
116408 |
const gchar *uri = NULL;
|
|
Packit |
116408 |
const gchar *deprecated = NULL;
|
|
Packit |
116408 |
DhLinkType link_type;
|
|
Packit |
116408 |
DhLink *link;
|
|
Packit |
116408 |
gchar *name_to_free = NULL;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (parser->version == FORMAT_VERSION_2 &&
|
|
Packit |
116408 |
g_ascii_strcasecmp (node_name, "keyword") != 0) {
|
|
Packit |
116408 |
g_markup_parse_context_get_position (context, &line, &col);
|
|
Packit |
116408 |
g_set_error (error,
|
|
Packit |
116408 |
DH_ERROR,
|
|
Packit |
116408 |
DH_ERROR_MALFORMED_BOOK,
|
|
Packit |
116408 |
"Expected <keyword> element, got <%s> at line %d, column %d.",
|
|
Packit |
116408 |
node_name, line, col);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
} else if (parser->version == FORMAT_VERSION_1 &&
|
|
Packit |
116408 |
g_ascii_strcasecmp (node_name, "function") != 0) {
|
|
Packit |
116408 |
g_markup_parse_context_get_position (context, &line, &col);
|
|
Packit |
116408 |
g_set_error (error,
|
|
Packit |
116408 |
DH_ERROR,
|
|
Packit |
116408 |
DH_ERROR_MALFORMED_BOOK,
|
|
Packit |
116408 |
"Expected <function> element, got <%s> at line %d, column %d.",
|
|
Packit |
116408 |
node_name, line, col);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
for (attr_num = 0; attribute_names[attr_num] != NULL; attr_num++) {
|
|
Packit |
116408 |
if (g_ascii_strcasecmp (attribute_names[attr_num], "type") == 0)
|
|
Packit |
116408 |
type = attribute_values[attr_num];
|
|
Packit |
116408 |
else if (g_ascii_strcasecmp (attribute_names[attr_num], "name") == 0)
|
|
Packit |
116408 |
name = attribute_values[attr_num];
|
|
Packit |
116408 |
else if (g_ascii_strcasecmp (attribute_names[attr_num], "link") == 0)
|
|
Packit |
116408 |
uri = attribute_values[attr_num];
|
|
Packit |
116408 |
else if (g_ascii_strcasecmp (attribute_names[attr_num], "deprecated") == 0)
|
|
Packit |
116408 |
deprecated = attribute_values[attr_num];
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (name == NULL || uri == NULL) {
|
|
Packit |
116408 |
g_markup_parse_context_get_position (context, &line, &col);
|
|
Packit |
116408 |
g_set_error (error,
|
|
Packit |
116408 |
DH_ERROR,
|
|
Packit |
116408 |
DH_ERROR_MALFORMED_BOOK,
|
|
Packit |
116408 |
"“name” and “link” attributes are required inside "
|
|
Packit |
116408 |
"the <%s> element at line %d, column %d.",
|
|
Packit |
116408 |
parser->version == FORMAT_VERSION_2 ? "keyword" : "function",
|
|
Packit |
116408 |
line, col);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (parser->version == FORMAT_VERSION_2 && type == NULL) {
|
|
Packit |
116408 |
g_markup_parse_context_get_position (context, &line, &col);
|
|
Packit |
116408 |
g_set_error (error,
|
|
Packit |
116408 |
DH_ERROR,
|
|
Packit |
116408 |
DH_ERROR_MALFORMED_BOOK,
|
|
Packit |
116408 |
"“type” attribute is required inside the "
|
|
Packit |
116408 |
"<keyword> element at line %d, column %d.",
|
|
Packit |
116408 |
line, col);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (parser->version == FORMAT_VERSION_2) {
|
|
Packit |
116408 |
if (g_str_equal (type, "function"))
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_FUNCTION;
|
|
Packit |
116408 |
else if (g_str_equal (type, "struct"))
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_STRUCT;
|
|
Packit |
116408 |
else if (g_str_equal (type, "macro"))
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_MACRO;
|
|
Packit |
116408 |
else if (g_str_equal (type, "enum"))
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_ENUM;
|
|
Packit |
116408 |
else if (g_str_equal (type, "typedef"))
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_TYPEDEF;
|
|
Packit |
116408 |
else if (g_str_equal (type, "property"))
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_PROPERTY;
|
|
Packit |
116408 |
else if (g_str_equal (type, "signal"))
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_SIGNAL;
|
|
Packit |
116408 |
else
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_KEYWORD;
|
|
Packit |
116408 |
} else {
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_KEYWORD;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* Strip out trailing "() (handling variants with space or non-breaking
|
|
Packit |
116408 |
* space before the parentheses).
|
|
Packit |
116408 |
*
|
|
Packit |
116408 |
* FIXME: gtk-doc still adds those parentheses. I thought that the code
|
|
Packit |
116408 |
* was needed to support the format version 1. Maybe gtk-doc should no
|
|
Packit |
116408 |
* longer add those trailing parentheses, since with the format version
|
|
Packit |
116408 |
* 2 we already know the link type.
|
|
Packit |
116408 |
*/
|
|
Packit |
116408 |
if (g_str_has_suffix (name, "\xc2\xa0()")) {
|
|
Packit |
116408 |
name_to_free = g_strndup (name, strlen (name) - 4);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (link_type == DH_LINK_TYPE_KEYWORD)
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_FUNCTION;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
name = name_to_free;
|
|
Packit |
116408 |
} else if (g_str_has_suffix (name, " ()")) {
|
|
Packit |
116408 |
name_to_free = g_strndup (name, strlen (name) - 3);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (link_type == DH_LINK_TYPE_KEYWORD)
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_FUNCTION;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
name = name_to_free;
|
|
Packit |
116408 |
} else if (g_str_has_suffix (name, "()")) {
|
|
Packit |
116408 |
name_to_free = g_strndup (name, strlen (name) - 2);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* With old devhelp format, take a guess that this is a
|
|
Packit |
116408 |
* macro.
|
|
Packit |
116408 |
*/
|
|
Packit |
116408 |
if (link_type == DH_LINK_TYPE_KEYWORD)
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_MACRO;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
name = name_to_free;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* Strip out prefixing "struct", "union", "enum", to make searching
|
|
Packit |
116408 |
* easier. Also fix up the link type (only applies for old devhelp
|
|
Packit |
116408 |
* format).
|
|
Packit |
116408 |
*/
|
|
Packit |
116408 |
if (g_str_has_prefix (name, "struct ")) {
|
|
Packit |
116408 |
name = name + 7;
|
|
Packit |
116408 |
if (link_type == DH_LINK_TYPE_KEYWORD)
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_STRUCT;
|
|
Packit |
116408 |
} else if (g_str_has_prefix (name, "union ")) {
|
|
Packit |
116408 |
name = name + 6;
|
|
Packit |
116408 |
if (link_type == DH_LINK_TYPE_KEYWORD)
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_STRUCT;
|
|
Packit |
116408 |
} else if (g_str_has_prefix (name, "enum ")) {
|
|
Packit |
116408 |
name = name + 5;
|
|
Packit |
116408 |
if (link_type == DH_LINK_TYPE_KEYWORD)
|
|
Packit |
116408 |
link_type = DH_LINK_TYPE_ENUM;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_assert (parser->book_node != NULL);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
link = dh_link_new (link_type,
|
|
Packit |
116408 |
parser->book_node->data,
|
|
Packit |
116408 |
name,
|
|
Packit |
116408 |
uri);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_free (name_to_free);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (deprecated != NULL)
|
|
Packit |
116408 |
dh_link_set_flags (link, dh_link_get_flags (link) | DH_LINK_FLAGS_DEPRECATED);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
parser->all_links = g_list_prepend (parser->all_links, link);
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
static void
|
|
Packit |
116408 |
parser_start_node_cb (GMarkupParseContext *context,
|
|
Packit |
116408 |
const gchar *node_name,
|
|
Packit |
116408 |
const gchar **attribute_names,
|
|
Packit |
116408 |
const gchar **attribute_values,
|
|
Packit |
116408 |
gpointer user_data,
|
|
Packit |
116408 |
GError **error)
|
|
Packit |
116408 |
{
|
|
Packit |
116408 |
DhParser *parser = user_data;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (parser->book_node == NULL) {
|
|
Packit |
116408 |
parser_start_node_book (parser,
|
|
Packit |
116408 |
context,
|
|
Packit |
116408 |
node_name,
|
|
Packit |
116408 |
attribute_names,
|
|
Packit |
116408 |
attribute_values,
|
|
Packit |
116408 |
error);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (parser->parsing_chapters) {
|
|
Packit |
116408 |
parser_start_node_chapter (parser,
|
|
Packit |
116408 |
context,
|
|
Packit |
116408 |
node_name,
|
|
Packit |
116408 |
attribute_names,
|
|
Packit |
116408 |
attribute_values,
|
|
Packit |
116408 |
error);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
} else if (parser->parsing_keywords) {
|
|
Packit |
116408 |
parser_start_node_keyword (parser,
|
|
Packit |
116408 |
context,
|
|
Packit |
116408 |
node_name,
|
|
Packit |
116408 |
attribute_names,
|
|
Packit |
116408 |
attribute_values,
|
|
Packit |
116408 |
error);
|
|
Packit |
116408 |
return;
|
|
Packit |
116408 |
} else if (g_ascii_strcasecmp (node_name, "chapters") == 0) {
|
|
Packit |
116408 |
parser->parsing_chapters = TRUE;
|
|
Packit |
116408 |
} else if (g_ascii_strcasecmp (node_name, "functions") == 0) {
|
|
Packit |
116408 |
parser->parsing_keywords = TRUE;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
static void
|
|
Packit |
116408 |
parser_end_node_cb (GMarkupParseContext *context,
|
|
Packit |
116408 |
const gchar *node_name,
|
|
Packit |
116408 |
gpointer user_data,
|
|
Packit |
116408 |
GError **error)
|
|
Packit |
116408 |
{
|
|
Packit |
116408 |
DhParser *parser = user_data;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (parser->parsing_keywords) {
|
|
Packit |
116408 |
if (g_ascii_strcasecmp (node_name, "functions") == 0)
|
|
Packit |
116408 |
parser->parsing_keywords = FALSE;
|
|
Packit |
116408 |
} else if (parser->parsing_chapters) {
|
|
Packit |
116408 |
g_assert (parser->parent_node != NULL);
|
|
Packit |
116408 |
g_node_reverse_children (parser->parent_node);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (g_ascii_strcasecmp (node_name, "sub") == 0) {
|
|
Packit |
116408 |
/* Move up in the tree */
|
|
Packit |
116408 |
parser->parent_node = parser->parent_node->parent;
|
|
Packit |
116408 |
g_assert (parser->parent_node != NULL);
|
|
Packit |
116408 |
} else if (g_ascii_strcasecmp (node_name, "chapters") == 0) {
|
|
Packit |
116408 |
parser->parsing_chapters = FALSE;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* All <sub> elements should be closed, we should have
|
|
Packit |
116408 |
* come back to the top node (corresponding to the
|
|
Packit |
116408 |
* <book> element).
|
|
Packit |
116408 |
*
|
|
Packit |
116408 |
* It could be a g_assert(), normally GMarkupParser
|
|
Packit |
116408 |
* already catches malformed XML files (if a <sub>
|
|
Packit |
116408 |
* element is not correctly closed). But just in case
|
|
Packit |
116408 |
* GMarkupParser is not smart enough, it's safer to have
|
|
Packit |
116408 |
* a g_return_if_fail() to avoid a crash.
|
|
Packit |
116408 |
*/
|
|
Packit |
116408 |
g_return_if_fail (parser->parent_node == parser->book_node);
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
gboolean
|
|
Packit |
116408 |
dh_parser_read_file (GFile *index_file,
|
|
Packit |
116408 |
gchar **book_title,
|
|
Packit |
116408 |
gchar **book_id,
|
|
Packit |
116408 |
gchar **book_language,
|
|
Packit |
116408 |
GNode **book_tree,
|
|
Packit |
116408 |
GList **all_links,
|
|
Packit |
116408 |
GError **error)
|
|
Packit |
116408 |
{
|
|
Packit |
116408 |
DhParser *parser;
|
|
Packit |
116408 |
gchar *index_file_uri;
|
|
Packit |
116408 |
gboolean gz;
|
|
Packit |
116408 |
GFileInputStream *file_input_stream = NULL;
|
|
Packit |
116408 |
GInputStream *input_stream = NULL;
|
|
Packit |
116408 |
gboolean ok = TRUE;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
g_return_val_if_fail (G_IS_FILE (index_file), FALSE);
|
|
Packit |
116408 |
g_return_val_if_fail (book_title != NULL && *book_title == NULL, FALSE);
|
|
Packit |
116408 |
g_return_val_if_fail (book_id != NULL && *book_id == NULL, FALSE);
|
|
Packit |
116408 |
g_return_val_if_fail (book_language != NULL && *book_language == NULL, FALSE);
|
|
Packit |
116408 |
g_return_val_if_fail (book_tree != NULL && *book_tree == NULL, FALSE);
|
|
Packit |
116408 |
g_return_val_if_fail (all_links != NULL && *all_links == NULL, FALSE);
|
|
Packit |
116408 |
g_return_val_if_fail (error != NULL && *error == NULL, FALSE);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
parser = g_new0 (DhParser, 1);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
index_file_uri = g_file_get_uri (index_file);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (g_str_has_suffix (index_file_uri, ".devhelp2")) {
|
|
Packit |
116408 |
parser->version = FORMAT_VERSION_2;
|
|
Packit |
116408 |
gz = FALSE;
|
|
Packit |
116408 |
} else if (g_str_has_suffix (index_file_uri, ".devhelp")) {
|
|
Packit |
116408 |
parser->version = FORMAT_VERSION_1;
|
|
Packit |
116408 |
gz = FALSE;
|
|
Packit |
116408 |
} else if (g_str_has_suffix (index_file_uri, ".devhelp2.gz")) {
|
|
Packit |
116408 |
parser->version = FORMAT_VERSION_2;
|
|
Packit |
116408 |
gz = TRUE;
|
|
Packit |
116408 |
} else {
|
|
Packit |
116408 |
parser->version = FORMAT_VERSION_1;
|
|
Packit |
116408 |
gz = TRUE;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
parser->markup_parser = g_new0 (GMarkupParser, 1);
|
|
Packit |
116408 |
parser->markup_parser->start_element = parser_start_node_cb;
|
|
Packit |
116408 |
parser->markup_parser->end_element = parser_end_node_cb;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
parser->context = g_markup_parse_context_new (parser->markup_parser, 0, parser, NULL);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
parser->index_file = g_object_ref (index_file);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
file_input_stream = g_file_read (index_file, NULL, error);
|
|
Packit |
116408 |
if (file_input_stream == NULL) {
|
|
Packit |
116408 |
ok = FALSE;
|
|
Packit |
116408 |
goto exit;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* At this point we know that the file exists, the G_IO_ERROR_NOT_FOUND
|
|
Packit |
116408 |
* has been catched earlier. So print warning.
|
|
Packit |
116408 |
*/
|
|
Packit |
116408 |
if (parser->version == FORMAT_VERSION_1)
|
|
Packit |
116408 |
g_warning ("The file '%s' uses the Devhelp index file format version 1, "
|
|
Packit |
116408 |
"which is deprecated. A future version of Devhelp may remove "
|
|
Packit |
116408 |
"the support for the format version 1. The index file should "
|
|
Packit |
116408 |
"be ported to the Devhelp index file format version 2.",
|
|
Packit |
116408 |
index_file_uri);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (gz) {
|
|
Packit |
116408 |
GZlibDecompressor *zlib_decompressor;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
zlib_decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
|
|
Packit |
116408 |
input_stream = g_converter_input_stream_new (G_INPUT_STREAM (file_input_stream),
|
|
Packit |
116408 |
G_CONVERTER (zlib_decompressor));
|
|
Packit |
116408 |
g_object_unref (zlib_decompressor);
|
|
Packit |
116408 |
} else {
|
|
Packit |
116408 |
input_stream = G_INPUT_STREAM (g_object_ref (file_input_stream));
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
while (TRUE) {
|
|
Packit |
116408 |
gchar buffer[BYTES_PER_READ];
|
|
Packit |
116408 |
gssize bytes_read;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
bytes_read = g_input_stream_read (input_stream,
|
|
Packit |
116408 |
buffer,
|
|
Packit |
116408 |
BYTES_PER_READ,
|
|
Packit |
116408 |
NULL,
|
|
Packit |
116408 |
error);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (bytes_read > 0) {
|
|
Packit |
116408 |
if (!g_markup_parse_context_parse (parser->context,
|
|
Packit |
116408 |
buffer,
|
|
Packit |
116408 |
bytes_read,
|
|
Packit |
116408 |
error)) {
|
|
Packit |
116408 |
ok = FALSE;
|
|
Packit |
116408 |
goto exit;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
} else if (bytes_read == 0) {
|
|
Packit |
116408 |
/* End of file */
|
|
Packit |
116408 |
break;
|
|
Packit |
116408 |
} else {
|
|
Packit |
116408 |
ok = FALSE;
|
|
Packit |
116408 |
goto exit;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
if (!g_markup_parse_context_end_parse (parser->context, error)) {
|
|
Packit |
116408 |
ok = FALSE;
|
|
Packit |
116408 |
goto exit;
|
|
Packit |
116408 |
}
|
|
Packit |
116408 |
|
|
Packit |
116408 |
/* Index file successfully read. Set out parameters. */
|
|
Packit |
116408 |
|
|
Packit |
116408 |
*book_title = parser->book_title;
|
|
Packit |
116408 |
parser->book_title = NULL;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
*book_id = parser->book_id;
|
|
Packit |
116408 |
parser->book_id = NULL;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
*book_language = parser->book_language;
|
|
Packit |
116408 |
parser->book_language = NULL;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
*book_tree = parser->book_node;
|
|
Packit |
116408 |
parser->book_node = NULL;
|
|
Packit |
116408 |
parser->parent_node = NULL;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
*all_links = parser->all_links;
|
|
Packit |
116408 |
parser->all_links = NULL;
|
|
Packit |
116408 |
|
|
Packit |
116408 |
exit:
|
|
Packit |
116408 |
g_free (index_file_uri);
|
|
Packit |
116408 |
g_clear_object (&file_input_stream);
|
|
Packit |
116408 |
g_clear_object (&input_stream);
|
|
Packit |
116408 |
dh_parser_free (parser);
|
|
Packit |
116408 |
|
|
Packit |
116408 |
return ok;
|
|
Packit |
116408 |
}
|