grim/gplate

Properly generate the documentation (at least: 100% symbol coverage).

I hate to flatten history like this, as this represented by 37 commits
in my local repository. However, I could _not_ get the result to
merge for some reason that I didn't have the time to figure out, so I
flattened it down to a single patch against upstream, and fixed the
resulting diff, and this is that commit.

In a nutshell, this commit:

- gets rid of the SGML template files, as the content that they had
was moved to source code files so that the documentation is closer
to the code, and also because the SGML templates were blocking some
already existing doc-comments in the source code.

- Gets rid of the gplate-sections.txt file, as gtk-doc builds it for
us.

- Gets rid of the gplate.types file. It is generated from scanning
the source code, and adjusted by sed to eliminate things that
gtk-doc won't be able to runtime introspect since they're not
currently "there".

- Hooks the doc/gplates directory into the build system (just
barely). The real work is not even done by CMake, as I couldn't
figure out how to get it to do what I wanted, which leads to...

- The documentation is _actually_ built by doc/gplate/build-docs.sh,
which contains hardcoded values that I could not figure out how to
get CMake to expose certain things. This means that the build
system doesn't actually know jack about the documentation (and this
also means that the documentation won't be installed as part of the
"make install" target, either). (BUT, the documentation is
available and BUILDS!)

While this commit makes things better than they were before the
commit, I would not call this commit overly polished. I fully expect
the shell script to be replaced by something done in/with/via CMake
directly, I just don't know how to do it that way.
/*
* GPlate - GObject based templating library
* Copyright (C) 2007-2012 Gary Kramlich <grim@reaperworld.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gplate/gplate-template.h>
#include <gplate/gplate-collection.h>
#include <gplate/gplate-errors.h>
#include <gplate/gplate-library.h>
#include <gplate/gplate-util.h>
#include <gplate/variables/gplate-dictionary-variable.h>
#include <stdio.h>
#include <string.h>
#define GPLATE_TEMPLATE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), GPLATE_TYPE_TEMPLATE, GPlateTemplatePrivate))
/******************************************************************************
* Enums
*****************************************************************************/
enum {
PROP_ZERO,
PROP_WD,
PROP_LAST
};
/******************************************************************************
* Structs
*****************************************************************************/
typedef struct {
GPlateVariable *vars;
gchar *wd;
GList *tokens;
GList *current_token;
} GPlateTemplatePrivate;
typedef struct {
GType type;
gchar *contents;
} GPlateTemplateTagData;
/******************************************************************************
* Globals
*****************************************************************************/
static GObjectClass *parent_class = NULL;
/******************************************************************************
* Collection Stuff
*****************************************************************************/
static GPlateVariable *
gplate_template_find_variable(const GPlateCollection *collection,
const gchar *name)
{
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(collection);
return gplate_collection_find_variable(GPLATE_COLLECTION(priv->vars),
name);
}
static gboolean
gplate_template_add_variable_with_name(GPlateCollection *collection,
const gchar *name,
GPlateVariable *variable)
{
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(collection);
GPlateCollection *c2 = NULL;
c2 = GPLATE_COLLECTION(priv->vars);
return gplate_collection_add_variable_with_name(c2, name, variable);
}
static gboolean
gplate_template_remove_variable(GPlateCollection *collection,
GPlateVariable *variable)
{
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(collection);
return gplate_collection_remove_variable(GPLATE_COLLECTION(priv->vars),
variable);
}
static void
gplate_template_collection_init(GPlateCollectionIface *iface) {
iface->find_variable = gplate_template_find_variable;
iface->add_variable_with_name = gplate_template_add_variable_with_name;
iface->remove_variable = gplate_template_remove_variable;
}
/******************************************************************************
* Template Stuff
*****************************************************************************/
static void
gplate_template_update_pattern_helper(GType tag, const gchar *prefix,
const gchar *suffix, gpointer d)
{
GString *str = (GString *)d;
const gchar *sep = "";
/* don't add fall through tags to the regex */
if(!prefix || !suffix)
return;
if(str->len > 1)
sep = "|";
g_string_append_printf(str, "%s%s.*?%s", sep, prefix, suffix);
}
static gchar *
gplate_template_update_pattern(GPlateTemplate *tplate) {
GString *str = g_string_new("(");
gchar *ret = NULL;
gplate_library_tags_foreach(gplate_template_update_pattern_helper, str);
g_string_append_printf(str, "%s", ")");
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
static void
gplate_template_find_tag_helper(GType tag, const gchar *prefix,
const gchar *suffix, gpointer d)
{
GPlateTemplateTagData *data = (GPlateTemplateTagData *)d;
gchar *pattern = NULL;
/* if our tag info doesn't have a prefix or a suffix, we drop out. */
if(!prefix || !suffix)
return;
/* create our pattern string */
pattern = g_strdup_printf("%s.*?%s", prefix, suffix);
/* look for a match:
*
* G_REGEX_DOTALL is REQUIRED so that newlines get counted.
*
* ie: {{\nfoo\n}} would be treated as a fall through if not for it.
*/
if(g_regex_match_simple(pattern, data->contents, G_REGEX_DOTALL, 0))
data->type = tag;
/* cleanup */
g_free(pattern);
}
static GType
gplate_template_find_tag(GPlateTemplate *tplate, gchar *contents) {
GPlateTemplateTagData d;
d.contents = contents;
d.type = G_TYPE_INVALID;
gplate_library_tags_foreach(gplate_template_find_tag_helper, &d);
if(d.type == G_TYPE_INVALID)
d.type = gplate_library_get_default_tag();
return d.type;
}
static GList *
gplate_template_real_tokenize(GPlateTemplate *tplate, const gchar *tplate_string,
GError **error)
{
GList *ret = NULL;
GRegex *regex1 = NULL;
gchar **matches = NULL;
gchar *pattern = NULL;
gint i = 0;
pattern = gplate_template_update_pattern(tplate);
regex1 = g_regex_new(pattern, G_REGEX_OPTIMIZE, 0, error);
if(!regex1) {
g_free(pattern);
return NULL;
}
g_free(pattern);
matches = g_regex_split(regex1, tplate_string, 0);
for(i = 0; matches[i]; i++) {
GPlateTag *tag = NULL;
GType type = G_TYPE_INVALID;
gchar *prefix = NULL, *suffix = NULL;
if(g_utf8_strlen(matches[i], -1) <= 0)
continue;
type = gplate_template_find_tag(tplate, matches[i]);
if(!gplate_library_lookup_tag(type, &prefix, &suffix, error))
continue;
if(!prefix && !suffix) {
/* it's a fall through tag */
tag = g_object_new(type,
"contents", matches[i],
NULL);
} else {
/* it's a normal tag, strip it's prefix and suffix */
GRegex *regex2 = NULL;
GMatchInfo *match = NULL;
gchar *pattern = NULL, *contents = NULL;
pattern = g_strdup_printf("%s(.*?)%s", prefix, suffix);
regex2 = g_regex_new(pattern,
G_REGEX_OPTIMIZE | G_REGEX_DOTALL,
0, error);
g_free(pattern);
if(!regex2) {
GList *l = NULL;
for(l = ret; l; l = l->next)
g_object_unref(G_OBJECT(l->data));
g_list_free(ret);
return NULL;
}
/* get our match info */
if(!g_regex_match(regex2, matches[i], 0, &match)) {
GList *l = NULL;
g_match_info_free(match);
for(l = ret; l; l = l->next)
g_object_unref(G_OBJECT(l->data));
g_list_free(ret);
return NULL;
}
contents = g_match_info_fetch(match, 1);
g_strstrip(contents);
g_match_info_free(match);
tag = g_object_new(type,
"contents", contents,
"template", tplate,
NULL);
g_free(contents);
}
ret = g_list_prepend(ret, tag);
}
g_strfreev(matches);
g_regex_unref(regex1);
ret = g_list_reverse(ret);
return ret;
}
static GPlateTag *
gplate_template_real_first_tag(GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = priv->tokens;
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_last_tag(GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = g_list_last(priv->tokens);
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_current_tag(GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_next_tag(GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = g_list_next(priv->current_token);
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_previous_tag(GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = g_list_previous(priv->current_token);
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_nth_tag(GPlateTemplate *tplate, guint nth) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = g_list_nth(priv->current_token, nth);
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_nth_previous_tag(GPlateTemplate *tplate, guint nth) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = g_list_nth_prev(priv->current_token, nth);
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static void
gplate_template_real_insert_tags(GPlateTemplate *tplate, GList *tags) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
/* check if our current node is valid */
if(!priv->current_token) {
/* check if we have a list of tokens */
if(!priv->tokens) {
/* no existing tokens, set it to tags and exit */
priv->tokens = tags;
} else {
/* we have tokens, but we've read the last one, move back to the
* last one.
*/
priv->tokens = g_list_concat(priv->tokens, tags);
}
} else {
GList *tmp = NULL;
/* we have a valid node selected, save it's next node */
tmp = priv->current_token->next;
/* now mangle current and the first node in tags to shove tags in */
priv->current_token->next = tags;
tags->prev = priv->current_token;
/* if the original next node is valid, we need to put them back into
* place
*/
if(tmp) {
GList *last = g_list_last(tags);
last->next = tmp;
tmp->prev = last;
}
}
}
/******************************************************************************
* Object Stuff
*****************************************************************************/
static void
gplate_template_get_property(GObject *obj, guint param_id, GValue *value,
GParamSpec *pspec)
{
GPlateTemplate *tplate = GPLATE_TEMPLATE(obj);
switch(param_id) {
case PROP_WD:
g_value_set_string(value,
gplate_template_get_working_directory(tplate));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
gplate_template_set_property(GObject *obj, guint param_id, const GValue *value,
GParamSpec *pspec)
{
GPlateTemplate *tplate = GPLATE_TEMPLATE(obj);
switch(param_id) {
case PROP_WD:
gplate_template_set_working_directory(tplate,
g_value_get_string(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
gplate_template_finalize(GObject *obj) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(obj);
GList *l = NULL;
g_object_unref(priv->vars);
for(l = priv->tokens; l; l = l->next)
g_object_unref(G_OBJECT(l->data));
g_list_free(priv->tokens);
G_OBJECT_CLASS(parent_class)->finalize(obj);
}
static void
gplate_template_class_init(GPlateTemplateClass *klass) {
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
GParamSpec *pspec = NULL;
parent_class = g_type_class_peek_parent(klass);
g_type_class_add_private(klass, sizeof(GPlateTemplatePrivate));
obj_class->get_property = gplate_template_get_property;
obj_class->set_property = gplate_template_set_property;
obj_class->finalize = gplate_template_finalize;
klass->tokenize = gplate_template_real_tokenize;
klass->first_tag = gplate_template_real_first_tag;
klass->last_tag = gplate_template_real_last_tag;
klass->current_tag = gplate_template_real_current_tag;
klass->next_tag = gplate_template_real_next_tag;
klass->previous_tag = gplate_template_real_previous_tag;
klass->nth_tag = gplate_template_real_nth_tag;
klass->nth_previous_tag = gplate_template_real_nth_previous_tag;
klass->insert_tags = gplate_template_real_insert_tags;
pspec = g_param_spec_string("working-directory", "working directory",
"The working directory for the template",
NULL, G_PARAM_READWRITE);
g_object_class_install_property(obj_class, PROP_WD, pspec);
}
static void
gplate_template_init(GTypeInstance *self, gpointer klass) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(self);
priv->vars = gplate_dictionary_variable_new("dict");
}
/******************************************************************************
* Render helpers
*****************************************************************************/
static inline GPlateFunction *
gplate_template_render_find_function(const gchar *name, GType tag,
gboolean *deffunc, GError **error)
{
GPlateFunction *ret = NULL;
GType function = G_TYPE_INVALID;
gboolean def = FALSE;
function = gplate_library_lookup_function_for_tag(name, tag, NULL);
if(function == G_TYPE_INVALID) {
function = gplate_library_get_default_function_for_tag(tag, NULL);
def = TRUE;
}
if(function != G_TYPE_INVALID) {
ret = g_object_new(function, NULL);
if(deffunc)
*deffunc = def;
}
return ret;
}
/******************************************************************************
* GPlateTemplate API
*****************************************************************************/
GType
gplate_template_get_type(void) {
static GType type = 0;
if(type == 0) {
static const GTypeInfo info = {
sizeof(GPlateTemplateClass),
NULL,
NULL,
(GClassInitFunc)gplate_template_class_init,
NULL,
NULL,
sizeof(GPlateTemplate),
0,
gplate_template_init,
};
static const GInterfaceInfo collection_info = {
(GInterfaceInitFunc)gplate_template_collection_init,
NULL,
NULL,
};
type = g_type_register_static(G_TYPE_OBJECT,
"GPlateTemplate",
&info, 0);
g_type_add_interface_static(type, GPLATE_TYPE_COLLECTION,
&collection_info);
}
return type;
}
/**
* gplate_template_new:
*
* Creates a new #GPlateTemplate instance.
*
* Return Value: The new #GPlateTemplate instance.
*/
GPlateTemplate *
gplate_template_new(void) {
return g_object_new(GPLATE_TYPE_TEMPLATE, NULL);
}
/**
* gplate_template_tokenize:
* @tplate: The #GPlateTemplate.
* @tplate_string: The string to be translated.
* @error: The return address for any errors.
*
* Tokenizes @tplate_string into a #GList of #GPlateTag's to be later passed to
* #gplate_template_render.
*
* Normally this is called by #gplate_template_render directly, but there are a
* few corner cases where you may want to call this directly.
*
* Return Value: A #GList of #GPlateTags.
*/
GList *
gplate_template_tokenize(GPlateTemplate *tplate, const gchar *tplate_string,
GError **error)
{
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
g_return_val_if_fail(tplate_string, NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->tokenize)
return klass->tokenize(tplate, tplate_string, error);
return NULL;
}
/**
* gplate_template_render:
* @tplate: The #GPlateTemplate.
* @tplate_string: The template string to render.
* @error: Return address for errors.
*
* Renders @tplate_string using @tplate.
*
* Return Value: The rendered output.
*/
gchar *
gplate_template_render(GPlateTemplate *tplate, const gchar *tplate_string,
GError **error)
{
GPlateTemplatePrivate *priv = NULL;
GPlateTag *tag = NULL;
GString *str = NULL;
gchar *ret = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
g_return_val_if_fail(tplate_string, NULL);
priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
priv->tokens = gplate_template_tokenize(tplate, tplate_string, error);
if(!priv->tokens)
return g_strdup("");
/* for whatever reason, using g_string_new_len here doesn't work */
str = g_string_new("");
for(tag = gplate_template_first_tag(tplate);
tag;
tag = gplate_template_next_tag(tplate))
{
gchar *output = gplate_template_render_tag(tplate, tag);
g_string_append_printf(str, "%s", output);
g_free(output);
}
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
/**
* gplate_template_render_file:
* @tplate: The #GPlateTemplate.
* @filename: The file name to render.
* @error: Return address for errors.
*
* Renders @filename using #GPlateTemplate @tplate.
*
* Return Value: The rendered output.
*/
gchar *
gplate_template_render_file(GPlateTemplate *tplate, const gchar *filename,
GError **error)
{
gchar *contents = NULL, *ret = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
g_return_val_if_fail(filename, NULL);
if(!gplate_template_get_working_directory(tplate)) {
gchar *dirname = g_path_get_dirname(filename);
gplate_template_set_working_directory(tplate, dirname);
g_free(dirname);
}
if(!gplate_util_read_file(filename, &contents, NULL, error))
return FALSE;
ret = gplate_template_render(tplate, contents, error);
g_free(contents);
return ret;
}
/**
* gplate_template_render_until:
* @tplate: The #GPlateTemplate
* @ntags: A return address for the number of tags that were rendered
* @...: A NULL terminated list of #GType, content pairs.
*
* Renders the tags in a template from the current point until it a tag is
* found that is the correct type and has the same contents as one of the
* #GType, content pairs.
*
* Returns: The rendered output.
*/
gchar *
gplate_template_render_until(GPlateTemplate *tplate, guint *ntags, ...) {
GPlateTag *tag = NULL;
GList *l = NULL;
GString *str = NULL;
GQueue *queue = NULL;
GType type = G_TYPE_INVALID;
gchar *ret = NULL;
guint count = 0;
gboolean backup = FALSE;
va_list args;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
str = g_string_new("");
queue = g_queue_new();
/* store all of the content/tag pairs */
va_start(args, ntags);
while((type = va_arg(args, gsize)) != G_TYPE_INVALID) {
GPlateTemplateTagData *td = NULL;
gchar *contents = NULL;
td = g_new(GPlateTemplateTagData, 1);
contents = va_arg(args, gchar *);
td->contents = contents ? g_strdup(contents) : NULL;
td->type = type;
g_queue_push_tail(queue, td);
}
va_end(args);
/* now iterate the tags */
while((tag = gplate_template_next_tag(tplate))) {
GPlateTemplateTagData ctd;
gboolean stop = FALSE;
ctd.contents = gplate_tag_get_contents(tag);
ctd.type = G_OBJECT_TYPE(tag);
/* run through our list of TagData's to stop on */
for(l = queue->head; l; l = l->next) {
GPlateTemplateTagData *td = (GPlateTemplateTagData *)l->data;
if(ctd.type == td->type &&
g_utf8_collate(ctd.contents, td->contents) == 0)
{
stop = TRUE;
backup = TRUE;
}
}
g_free(ctd.contents);
if(stop) {
count++;
break;
}
g_string_append_printf(str, "%s",
gplate_template_render_tag(tplate, tag));
count++;
}
/* now clean everything up... */
for(l = queue->head; l; l = l->next) {
GPlateTemplateTagData *td = (GPlateTemplateTagData *)l->data;
g_free(td->contents);
g_free(td);
}
g_queue_free(queue);
ret = str->str;
g_string_free(str, FALSE);
/* if backup is set, we back up to the tag which caused us to stop. In
* other words, the tag stack will currently be at the tag after the one
* we stopped at, but we want to be on the one that we stopped on.
*/
if(backup) {
gplate_template_previous_tag(tplate);
count--;
}
if(ntags)
*ntags = count;
return ret;
}
/**
* gplate_template_jump_to:
* @tplate: The #GPlateTemplate
* @ntags: A return address for the number of tags that were rendered
* @...: A NULL terminated list of #GType, content pairs.
*
* Skips all tags, until one is found that is the correct type and has the same
* contents as one of the #GType, content pairs.
*
* The current tag will be the one jumped to.
*/
void
gplate_template_jump_to(GPlateTemplate *tplate, guint *ntags, ...) {
GPlateTag *tag = NULL;
GList *l = NULL;
GQueue *queue = NULL;
GType type = G_TYPE_INVALID;
gboolean backup = FALSE;
guint count = 0;
va_list args;
g_return_if_fail(GPLATE_IS_TEMPLATE(tplate));
queue = g_queue_new();
/* store all of the content/tag pairs */
va_start(args, ntags);
while((type = va_arg(args, gsize)) != G_TYPE_INVALID) {
GPlateTemplateTagData *td = NULL;
const gchar *contents = NULL;
td = g_new(GPlateTemplateTagData, 1);
contents = va_arg(args, gchar *);
td->contents = contents ? g_strdup(contents) : NULL;
td->type = type;
g_queue_push_tail(queue, td);
}
va_end(args);
/* iterate the tags */
while((tag = gplate_template_next_tag(tplate))) {
GPlateTemplateTagData ctd;
gboolean stop = FALSE;
ctd.contents = gplate_tag_get_contents(tag);
ctd.type = G_OBJECT_TYPE(tag);
for(l = queue->head; l; l = l->next) {
GPlateTemplateTagData *td = (GPlateTemplateTagData *)l->data;
if(ctd.type == td->type &&
g_utf8_collate(ctd.contents, td->contents) == 0)
{
stop = TRUE;
backup = TRUE;
}
}
g_free(ctd.contents);
if(stop) {
count++;
break;
}
count++;
}
/* cleanup */
for(l = queue->head; l; l = l->next) {
GPlateTemplateTagData *td = l->data;
g_free(td->contents);
g_free(td);
}
g_queue_free(queue);
if(backup) {
gplate_template_previous_tag(tplate);
count--;
}
if(ntags)
*ntags = count;
}
/**
* gplate_template_first_tag:
* @tplate: The #GPlateTemplate.
*
* Moves the tag stack to the first tag and returns it.
*
* Return Value: The first tag or NULL.
*/
GPlateTag *
gplate_template_first_tag(GPlateTemplate *tplate) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->first_tag)
return klass->first_tag(tplate);
return NULL;
}
/**
* gplate_template_last_tag:
* @tplate: The #GPlateTemplate.
*
* Moves the tag stack to the last tag and returns it.
*
* Return Value: The last tag or NULL.
*/
GPlateTag *
gplate_template_last_tag(GPlateTemplate *tplate) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->last_tag)
return klass->last_tag(tplate);
return NULL;
}
/**
* gplate_template_current_tag:
* @tplate: The #GPlateTemplate.
*
* Returns the current tag.
*
* Return Value: The current tag or NULL.
*/
GPlateTag *
gplate_template_current_tag(GPlateTemplate *tplate) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->current_tag)
return klass->current_tag(tplate);
return NULL;
}
/**
* gplate_template_next_tag:
* @tplate: The #GPlateTemplate.
*
* Moves the tag stack forward one tag and returns it.
*
* Return Value: The next tag or NULL.
*/
GPlateTag *
gplate_template_next_tag(GPlateTemplate *tplate) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->next_tag)
return klass->next_tag(tplate);
return NULL;
}
/**
* gplate_template_previous_tag:
* @tplate: The #GPlateTemplate.
*
* Moves the tag stack back one tag and returns it.
*
* Return Value: The previous tag or NULL.
*/
GPlateTag *
gplate_template_previous_tag(GPlateTemplate *tplate) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->previous_tag)
return klass->previous_tag(tplate);
return NULL;
}
/**
* gplate_template_nth_tag:
* @tplate: The #GPlateTemplate.
* @nth: The number of tags to move forwards.
*
* Moves the tag stack forward @nth tags and returns that tag.
*
* Return Value: The @nth tag or NULL.
*/
GPlateTag *
gplate_template_nth_tag(GPlateTemplate *tplate, guint nth) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->nth_tag)
return klass->nth_tag(tplate, nth);
return NULL;
}
/**
* gplate_template_nth_previous_tag:
* @tplate: The #GPlateTemplate.
* @nth: The number of tags to move backwards.
*
* Moves the tag stack back @nth tags and returns that tag.
*
* Return Value: The @nth previous tag or NULL.
*/
GPlateTag *
gplate_template_nth_previous_tag(GPlateTemplate *tplate, guint nth) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->nth_previous_tag)
return klass->nth_previous_tag(tplate, nth);
return NULL;
}
/**
* gplate_template_insert_tags:
* @tplate: The #GPlateTemplate.
* @tags: A #GList of #GPlateTag's to insert.
*
* Inserts @tags after the current tag in @tplate's stack.
*/
void
gplate_template_insert_tags(GPlateTemplate *tplate, GList *tags) {
GPlateTemplateClass *klass = NULL;
g_return_if_fail(GPLATE_IS_TEMPLATE(tplate));
g_return_if_fail(tags);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->insert_tags)
klass->insert_tags(tplate, tags);
}
static gchar *
gplate_template_render_token(GPlateTemplate *tplate, const gchar *contents,
GType tag, GError **error)
{
GPlateFunction *function = NULL;
GType deftag = G_TYPE_INVALID;
gboolean deffunc = FALSE;
gchar *ret = NULL;
deftag = gplate_library_get_default_tag();
if(tag == deftag) {
GType f = gplate_library_get_default_function_for_tag(tag, NULL);
function = g_object_new(f, NULL);
if(!function)
return g_strdup("");
ret = gplate_function_evaluate(function, tplate, contents);
} else {
gchar *name = NULL, *leftovers = NULL;
name = gplate_util_get_first_word(contents, &leftovers);
if(!name)
return g_strdup("");
function =
gplate_template_render_find_function(name, tag, &deffunc, error);
if(function) {
if(deffunc)
ret = gplate_function_evaluate(function, tplate, contents);
else
ret = gplate_function_evaluate(function, tplate, leftovers);
g_object_unref(G_OBJECT(function));
}
g_free(name);
g_free(leftovers);
}
return (ret) ? ret : g_strdup("");
}
/**
* gplate_template_render_tag:
* @tplate: The #GPlateTemplate.
* @tag: The #GPlateTag.
*
* Renders @tag using @tplate as it's context.
*
* Return Value: The rendered output of @tag.
*/
gchar *
gplate_template_render_tag(GPlateTemplate *tplate, const GPlateTag *tag) {
GType ttype = G_TYPE_INVALID;
gchar *contents = NULL, *ret = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
g_return_val_if_fail(GPLATE_IS_TAG(tag), NULL);
ttype = G_OBJECT_TYPE(tag);
contents = gplate_tag_get_contents(tag);
ret = gplate_template_render_token(tplate, contents, ttype, NULL);
g_free(contents);
return ret;
}
/**
* gplate_template_set_working_directory:
* @tplate: The #GPlateTemplate.
* @wd: The working directory to use for @tplate.
*
* Sets the working directory for @tplate to @wd.
*
* The working directory is used for functions that need paths. For example
* #GPlateIncludeFunction.
*
* If this is not set, and #gplate_template_render_file is called, the working
* directory will be set to that parent directory of the file.
*
* The working directory can be unset by supplying NULL for @wd.
*/
void
gplate_template_set_working_directory(GPlateTemplate *tplate, const gchar *wd) {
GPlateTemplatePrivate *priv = NULL;
g_return_if_fail(GPLATE_IS_TEMPLATE(tplate));
priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
g_free(priv->wd);
priv->wd = (wd) ? g_strdup(wd) : NULL;
g_object_notify(G_OBJECT(tplate), "working-directory");
}
/**
* gplate_template_get_working_directory:
* @tplate: The #GPlateTemplate.
*
* Gets the current working directory from @tplate.
*
* See #gplate_template_set_working_directory for more information.
*
* Return Value: The current working directory for @tplate or NULL.
*/
const gchar *
gplate_template_get_working_directory(const GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
return priv->wd;
}