grim/gplate
Clone
Summary
Browse
Changes
Graph
some more testing migration stuff.
2012-09-04, Gary Kramlich
7f8e0e4f250f
some more testing migration stuff.
Variables aren't done yet
/*
* 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_gtype
(
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
* @Varargs: 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
* @Varargs: 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
;
}