From fb5a52884dd17d17bab47de7f53c17c05ced6e1b Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sat, 4 Jun 2011 17:30:12 +0200 Subject: Introduce conditional system This is very basic scripting support - simple value retrieval and block conditionals. Defines could be either static from theme setup or programatically added during page generation. --- src/block-parser.c | 133 ++++++++++++++++++++++++++++++++++++++++++-------- src/block-parser.h | 6 ++- src/gallery-utils.c | 19 ++++++++ src/gallery-utils.h | 5 ++ src/generators.c | 14 ++++-- src/replace-table.c | 46 ++++++++++++++--- src/replace-table.h | 6 +++ src/setup.c | 19 +++++++- src/setup.h | 1 + templates/default.xml | 8 +++ 10 files changed, 226 insertions(+), 31 deletions(-) diff --git a/src/block-parser.c b/src/block-parser.c index d4fcccb..cfe9047 100644 --- a/src/block-parser.c +++ b/src/block-parser.c @@ -23,7 +23,6 @@ #include #include -#include #include "block-parser.h" #include "gallery-utils.h" @@ -38,6 +37,8 @@ struct BlockParser { GHashTable *table; GQueue *active_tree; gchar *current_line; + GHashTable *conditionals; + guint ignore_level; }; typedef struct { @@ -45,6 +46,10 @@ typedef struct { gchar *data; gboolean used; gboolean finished; + + gboolean is_conditional; + gchar *conditional_key; + gboolean should_ignore; } BlockData; @@ -59,6 +64,7 @@ block_parser_destroy_notify (gpointer data) g_free (d->replace_key); g_free (d->data); + g_free (d->conditional_key); g_free (d); } @@ -84,6 +90,17 @@ block_parser_free (BlockParser *parser) g_free (parser); } +/* + * block_parser_set_conditionals: set a hash table to be used for conditionals lookup + * + */ +void +block_parser_set_conditionals (BlockParser *parser, GHashTable *conditionals) +{ + g_return_if_fail (parser != NULL); + + parser->conditionals = conditionals; +} /* * block_parser_register_key: tell parser to expect the key to catch @@ -103,10 +120,10 @@ block_parser_register_key (BlockParser *parser, const gchar *key, const gchar *r if (replace_key) data->replace_key = g_strdup_printf ("", replace_key); data->used = TRUE; /* buffer is empty, mask it for block_parser_has_unused_data() */ + data->is_conditional = FALSE; g_hash_table_replace (parser->table, g_strdup (key), data); } - /* * block_parser_get_data: return retrieved data or NULL if none read yet * returns newly allocated string, caller is responsible for freeing @@ -174,8 +191,18 @@ push_string (BlockParser *parser, const gchar *piece) { BlockData *data; gchar *s; + guint i; - data = g_queue_peek_head (parser->active_tree); + /* Test if the data should be ignored */ + if (parser->ignore_level > 0) + return; + + /* Not ignoring, find first non-conditional record */ + for (i = 0; (data = g_queue_peek_nth (parser->active_tree, i)) != NULL; i++) + if (! data->is_conditional) + break; + + /* Actually push the string */ if (data) { if (data->data) s = g_strdup_printf ("%s%s", data->data, piece); @@ -192,6 +219,25 @@ push_string (BlockParser *parser, const gchar *piece) } } +static gchar * +extract_arg (const gchar *str) +{ + const gchar *start; + const gchar *end; + + start = strstr (str, "("); + if (start == NULL) + return NULL; + start++; + + end = strstr (str, ")"); + if (end == NULL) + return NULL; + end++; + + return g_strndup (start, end - start - 1); +} + /* * block_parser_read_and_parse: reads input from the file and returns parsed line * - if there's a multiline block, string before the opening placeholder and @@ -210,8 +256,8 @@ block_parser_read_and_parse (BlockParser *parser, FILE *stream) gchar *token; gchar *start, *end; gchar *s; - GList *keys; - GList *l; + GHashTableIter iter; + gchar *key; BlockData *data; gboolean handled; @@ -224,8 +270,6 @@ block_parser_read_and_parse (BlockParser *parser, FILE *stream) } parser->current_line = g_strdup (""); - keys = g_hash_table_get_keys (parser->table); - /* find tokens */ b = buffer; while ((token = get_next_token (b, &start, &end, NULL))) { @@ -236,25 +280,77 @@ block_parser_read_and_parse (BlockParser *parser, FILE *stream) push_string (parser, s); g_free (s); + /* match conditionals */ + if (parser->conditionals && (g_str_has_prefix (token, "ifdef(") || g_str_has_prefix (token, "ifndef("))) { + data = g_new0 (BlockData, 1); + data->is_conditional = TRUE; + data->conditional_key = extract_arg (token); + data->should_ignore = ((g_str_has_prefix (token, "ifdef(") && g_hash_table_lookup (parser->conditionals, data->conditional_key) == NULL) || + (g_str_has_prefix (token, "ifndef(") && g_hash_table_lookup (parser->conditionals, data->conditional_key) != NULL)); + if (data->should_ignore && parser->ignore_level == 0) + parser->ignore_level = 1; + else + if (parser->ignore_level > 0) + parser->ignore_level++; + g_queue_push_head (parser->active_tree, data); + handled = TRUE; + } + + if (parser->conditionals && g_str_has_prefix (token, "endif(")) { + s = extract_arg (token); + data = g_queue_peek_head (parser->active_tree); + if (data == NULL || !data->is_conditional || strcmp (data->conditional_key, s) != 0) { + log_error ("block_parser_read_and_parse: something is wrong with the parser table: expected '%s' but got '%s'\n", s, data->conditional_key); + } + else { + g_queue_pop_head (parser->active_tree); + if (parser->ignore_level > 0) + parser->ignore_level--; + block_parser_destroy_notify (data); + handled = TRUE; + } + g_free (s); + } + + if (parser->conditionals && g_str_has_prefix (token, "else(")) { + s = extract_arg (token); + data = g_queue_peek_head (parser->active_tree); + if (data == NULL || !data->is_conditional || strcmp (data->conditional_key, s) != 0) { + log_error ("block_parser_read_and_parse: something is wrong with the parser table: expected '%s' but got '%s'\n", s, data->conditional_key); + } + else { + data->should_ignore = ! data->should_ignore; + /* if we're not ignoring, level is zero, let's start ignoring by setting to one */ + if (data->should_ignore && parser->ignore_level == 0) + parser->ignore_level = 1; + else + /* if we're ignoring but should not, let's go down to zero */ + if (! data->should_ignore && parser->ignore_level == 1) + parser->ignore_level = 0; + /* in all other cases (level > 1) we're ignoring already and we're not the topmost block, so let's keep being ignored */ + handled = TRUE; + } + g_free (s); + } + /* match known tags */ - for (l = keys; l != NULL; l = l->next) { + g_hash_table_iter_init (&iter, parser->table); + while (!handled && g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &data)) { /* test BEGIN_ tokens */ - s = g_strdup_printf ("BEGIN_%s", (gchar *) l->data); - if (strcmp (s, token) == 0) { - data = g_hash_table_lookup (parser->table, (gchar *) l->data); - if (data) { - g_queue_push_head (parser->active_tree, data); - handled = TRUE; - } + s = g_strdup_printf ("BEGIN_%s", key); + if (strcmp (s, token) == 0 && data) { + g_queue_push_head (parser->active_tree, data); + handled = TRUE; } g_free (s); + if (handled) + break; /* test END_ tokens */ - s = g_strdup_printf ("END_%s", (gchar *) l->data); + s = g_strdup_printf ("END_%s", key); if (strcmp (s, token) == 0) { - data = g_hash_table_lookup (parser->table, (gchar *) l->data); if (data == NULL || data != g_queue_peek_head (parser->active_tree)) { - log_error ("block_parser_read_and_parse: something is wrong with the parser table: expected '%s'\n", (gchar *) l->data); + log_error ("block_parser_read_and_parse: something is wrong with the parser table: expected '%s'\n", key); } else { g_queue_pop_head (parser->active_tree); @@ -282,7 +378,6 @@ block_parser_read_and_parse (BlockParser *parser, FILE *stream) /* push rest of the buffer till the end of the line */ push_string (parser, b); - g_list_free (keys); g_free (buffer); return parser->current_line; } diff --git a/src/block-parser.h b/src/block-parser.h index 21d0cfb..96c7fb8 100644 --- a/src/block-parser.h +++ b/src/block-parser.h @@ -31,7 +31,11 @@ BlockParser * block_parser_new (); void block_parser_free (BlockParser *parser); - +/* + * block_parser_set_conditionals: set a hash table to be used for conditionals lookup + * + */ +void block_parser_set_conditionals (BlockParser *parser, GHashTable *conditionals); /* * block_parser_register_key: tell parser to expect the key to catch diff --git a/src/gallery-utils.c b/src/gallery-utils.c index 494900c..3caff02 100644 --- a/src/gallery-utils.c +++ b/src/gallery-utils.c @@ -365,3 +365,22 @@ needs_update (const gchar *source, const gchar *dest) return (src_stat.st_mtime > dst_stat.st_mtime); } + +/* + * clone_string_hash_table: clones data from existing string hash table and returns newly-allocated copy + */ +GHashTable * +clone_string_hash_table (GHashTable *hash_table) +{ + GHashTable *dest; + GHashTableIter iter; + gchar *key, *value; + + dest = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + g_hash_table_iter_init (&iter, hash_table); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) + g_hash_table_insert (dest, g_strdup (key), g_strdup (value)); + + return dest; +} diff --git a/src/gallery-utils.h b/src/gallery-utils.h index 29c7d8f..4559fa4 100644 --- a/src/gallery-utils.h +++ b/src/gallery-utils.h @@ -78,6 +78,11 @@ void log_error (const gchar *format, ...) G_GNUC_PRINTF (1, 2); */ gboolean needs_update (const gchar *source, const gchar *dest); +/* + * clone_string_hash_table: clones data from existing string hash table and returns newly-allocated copy + */ +GHashTable * clone_string_hash_table (GHashTable *hash_table); + G_END_DECLS diff --git a/src/generators.c b/src/generators.c index 051ee28..4f0963d 100644 --- a/src/generators.c +++ b/src/generators.c @@ -280,6 +280,7 @@ write_html_album (TGallerySetup *setup, ReplaceTable *local_replace_table; BlockParser *block_parser; TImageSize *image_size, *thumb_image_size; + GHashTable *defines; fin = fopen (template_src, "r"); @@ -296,8 +297,10 @@ write_html_album (TGallerySetup *setup, res = TRUE; - global_replace_table = replace_table_new (); + global_replace_table = replace_table_new_from_defines (theme->defines); block_parser = block_parser_new (); + defines = clone_string_hash_table (theme->defines); + block_parser_set_conditionals (block_parser, defines); /* Get number of real pictures in the list */ real_total_items = 0; @@ -460,7 +463,7 @@ write_html_album (TGallerySetup *setup, } /* Generate images (preview, original, thumbnail) */ - local_replace_table = replace_table_new (); + local_replace_table = replace_table_new_from_defines (defines); s1 = NULL; switch (item->type) { @@ -546,6 +549,7 @@ write_html_album (TGallerySetup *setup, fclose (fout); fclose (fin); + g_hash_table_destroy (defines); replace_table_free (global_replace_table); block_parser_free (block_parser); return res; @@ -600,6 +604,7 @@ write_html_image (TGallerySetup *setup, ReplaceTable *replace_table; BlockParser *block_parser; TImageSize *image_size; + GHashTable *defines; fin = fopen (template_src, "r"); @@ -620,8 +625,10 @@ write_html_image (TGallerySetup *setup, preload_imgname = NULL; res = TRUE; - replace_table = replace_table_new (); + replace_table = replace_table_new_from_defines (theme->defines); block_parser = block_parser_new (); + defines = clone_string_hash_table (theme->defines); + block_parser_set_conditionals (block_parser, defines); /* Get our index in the album */ item_index = 0; @@ -927,6 +934,7 @@ write_html_image (TGallerySetup *setup, g_free (img_orig_dst_page); g_free (imgname); g_free (preload_imgname); + g_hash_table_destroy (defines); free_exif_data (exif); replace_table_free (replace_table); block_parser_free (block_parser); diff --git a/src/replace-table.c b/src/replace-table.c index 95d5e30..9cd02ea 100644 --- a/src/replace-table.c +++ b/src/replace-table.c @@ -37,6 +37,26 @@ replace_table_new () return (ReplaceTable *) g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } +/* + * replace_table_new_from_defines: creates new replace table object and fills it with defines from the hash table + * + */ +ReplaceTable * +replace_table_new_from_defines (GHashTable *defines) +{ + GHashTable *table; + GHashTableIter iter; + gchar *key, *value; + + table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + g_hash_table_iter_init (&iter, defines); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) + g_hash_table_insert (table, g_strdup_printf ("value(%s)", key), g_strdup (value)); + + return table; +} + void replace_table_free (ReplaceTable *table) { @@ -182,8 +202,9 @@ gchar * get_next_token (const gchar *s, gchar **start, gchar **end, gboolean *tag_parameter) { gchar *dollar; - gchar *end_brace; + gchar *end_paren; gchar *b; + int num_paren; *start = NULL; *end = NULL; @@ -193,10 +214,23 @@ get_next_token (const gchar *s, gchar **start, gchar **end, gboolean *tag_parame dollar = strstr (s, "$("); if (dollar == NULL) return NULL; - end_brace = strchr (dollar + 2, ')'); - if (end_brace == NULL) + end_paren = strchr (dollar + 2, ')'); + if (end_paren == NULL) return NULL; + /* Count opening parentheses and include nested parentheses in the token */ + num_paren = 0; + for (b = dollar; b < end_paren; b++) + if (*b == '(') + num_paren++; + while (num_paren > 1) { + end_paren = strchr (end_paren + 1, ')'); + if (end_paren == NULL) + return NULL; + num_paren--; + } + g_assert (num_paren <= 1); /* something really bad happened */ + /* Go back and try to find placeholder beginning */ for (b = dollar - 1; b >= s + 3; b--) { if (*b == '-' && *(b-1) == '-' && *(b-2) == '!' && *(b-3) == '<') { @@ -212,7 +246,7 @@ get_next_token (const gchar *s, gchar **start, gchar **end, gboolean *tag_parame *start = dollar; /* Go forth and try to find placeholder end */ - for (b = end_brace + 1; b <= end_brace + strlen (end_brace) - 3; b++) { + for (b = end_paren + 1; b <= end_paren + strlen (end_paren) - 3; b++) { if (*b == '-' && *(b+1) == '-' && *(b+2) == '>') { *end = b + 2; break; @@ -221,8 +255,8 @@ get_next_token (const gchar *s, gchar **start, gchar **end, gboolean *tag_parame break; } if (*end == NULL) - *end = end_brace; + *end = end_paren; - return g_strndup (dollar + 2, end_brace - dollar - 2); + return g_strndup (dollar + 2, end_paren - dollar - 2); } diff --git a/src/replace-table.h b/src/replace-table.h index 916af2b..799956d 100644 --- a/src/replace-table.h +++ b/src/replace-table.h @@ -28,6 +28,12 @@ typedef GHashTable ReplaceTable; ReplaceTable * replace_table_new (); +/* + * replace_table_new_from_defines: creates new replace table object and fills it with defines from the hash table + * + */ +ReplaceTable * replace_table_new_from_defines (GHashTable *defines); + void replace_table_free (ReplaceTable *table); diff --git a/src/setup.c b/src/setup.c index 83c5504..3a2a66d 100644 --- a/src/setup.c +++ b/src/setup.c @@ -143,11 +143,12 @@ parse_design_setup_xml (const gchar *filename) TXMLFile *xml; gchar *s; gchar *s2; + gchar *s3; TGalleryDesign *design; TImageSize *image_size; TGalleryDesignTheme *theme; - int count; - int i; + int count, c; + int i, j; xml = xml_parser_load (filename); if (xml == NULL) @@ -240,6 +241,19 @@ parse_design_setup_xml (const gchar *filename) g_free (s2); } g_free (s); + + theme->defines = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + s = g_strdup_printf ("/design_setup/theme[%d]/define", i + 1); + c = xml_file_node_get_children_count (xml, s); + g_free (s); + for (j = 0; j < c; j++) { + s = g_strdup_printf ("/design_setup/theme[%d]/define[%d]", i + 1, j + 1); + s2 = xml_file_get_node_attribute (xml, s, "name"); + s3 = xml_file_get_node_attribute (xml, s, "value"); + if (s2) + g_hash_table_replace (theme->defines, s2, s3 ? s3 : g_strdup ("")); + g_free (s); + } } xml_parser_free (xml); @@ -397,6 +411,7 @@ free_design_theme_data (TGalleryDesignTheme *theme) g_free (theme->picture_filename); g_free (theme->picture_image_size); g_strfreev (theme->supplemental_files); + g_hash_table_destroy (theme->defines); g_free (theme); } } diff --git a/src/setup.h b/src/setup.h index 69e92b6..a96a73f 100644 --- a/src/setup.h +++ b/src/setup.h @@ -109,6 +109,7 @@ typedef struct { gchar *picture_filename; gchar *picture_image_size; gchar **supplemental_files; + GHashTable *defines; } TGalleryDesignTheme; struct TGalleryDesign { diff --git a/templates/default.xml b/templates/default.xml index 67470c0..2a2fd8b 100644 --- a/templates/default.xml +++ b/templates/default.xml @@ -87,6 +87,14 @@ styles.css scripts-general.js ]]> + + + +