/* Cataract - Static web photo gallery generator * Copyright (C) 2009 Tomas Bzatek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include "replace-table.h" #include "gallery-utils.h" struct ReplaceTable { GHashTable *table; GHashTable *defines; }; ReplaceTable * replace_table_new () { ReplaceTable *table; table = g_new0 (ReplaceTable, 1); table->table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); return table; } void replace_table_free (ReplaceTable *table) { g_return_if_fail (table != NULL); g_hash_table_destroy (table->table); g_free (table); } /* * replace_table_add_key: add tag/value pair to replace * * tag, value will be referenced inside * */ void replace_table_add_key (ReplaceTable *table, const gchar *tag, const gchar *value) { g_return_if_fail (table != NULL); if (tag != NULL && value != NULL) g_hash_table_replace (table->table, g_strdup (tag), g_strdup (value)); } void replace_table_add_key_int (ReplaceTable *table, const gchar *tag, gint value) { g_return_if_fail (table != NULL); if (tag != NULL) g_hash_table_replace (table->table, g_strdup (tag), g_strdup_printf ("%d", value)); } void replace_table_add_key_printf (ReplaceTable *table, const gchar *tag, const gchar *format, ...) { va_list args; gchar *s = NULL; g_return_if_fail (table != NULL); if (tag != NULL && format != NULL) { va_start (args, format); g_vasprintf (&s, format, args); g_hash_table_replace (table->table, g_strdup (tag), s); va_end (args); } } /* * replace_table_set_defines: set a hash table to be used for defines lookup * */ void replace_table_set_defines (ReplaceTable *table, GHashTable *defines) { g_return_if_fail (table != NULL); table->defines = defines; } /* * replace_table_process: process buffer and replace all tags filled in the replace table * * - reallocates source buffer * */ void replace_table_process (gchar **buffer, ReplaceTable *table) { gchar *token; gchar *start, *end; gchar *b; gchar *replace_value; gchar *s; GString *dst; gboolean tag_parameter; gboolean handled; g_return_if_fail (table != NULL); g_return_if_fail (buffer != NULL); if (*buffer == NULL || strlen (*buffer) < 1) return; dst = g_string_new (NULL); b = *buffer; while ((token = get_next_token (b, &start, &end, &tag_parameter))) { handled = FALSE; /* push the string before the token */ g_string_append_len (dst, b, start - b); replace_value = NULL; /* match defines */ if (table->defines && g_str_has_prefix (token, "value(")) { s = extract_token_arg (token); replace_value = g_strdup (g_hash_table_lookup (table->defines, s)); /* fall back to empty string if not defined */ if (replace_value == NULL) replace_value = g_strdup (""); g_free (s); } /* lookup in the replace table */ if (! replace_value) replace_value = g_strdup (g_hash_table_lookup (table->table, token)); /* process the data */ if (replace_value) { if (! tag_parameter) adjust_tags_normal (&replace_value); else adjust_tags_parameter (&replace_value); g_string_append (dst, replace_value); g_free (replace_value); handled = TRUE; } /* push the tag if not matched above */ if (! handled) g_string_append_len (dst, start, end - start + 1); g_free (token); b = end + 1; } /* push rest of the buffer till the end of the line */ g_string_append (dst, b); g_free (*buffer); *buffer = g_string_free (dst, FALSE); } /* * adjust_tags_normal: adjust string for normal HTML use * adjust_tags_parameter: adjust string for use as tag parameter value * - both funtions return newly allocated string */ void adjust_tags_normal (gchar **str) { fix_entities (str); } void adjust_tags_parameter (gchar **str) { /* TODO: replace line endings with a single space? */ remove_tags (str, ""); /* comments */ remove_tags (str, "<", ">"); /* tags */ fix_entities (str); /* entities */ str_replace (str, "\"", """); /* " */ str_replace (str, "'", "'"); /* ' */ } /* -------------------------------------------------------------------------------------------------------- */ /* * get_next_token: retrieves first token ( or $(TOKEN)) in the string * - returns newly allocated token name, caller is responsible for freeing * - start and end are positions of token in the source string 's' * - tag_parameter indicates where the token stands - as a tag itself ( ) * or as a parameter value of some tag ( $(TOKEN) ) * */ gchar * get_next_token (const gchar *s, gchar **start, gchar **end, gboolean *tag_parameter) { gchar *dollar; gchar *end_paren; gchar *b; int num_paren; *start = NULL; *end = NULL; if (tag_parameter) *tag_parameter = TRUE; dollar = strstr (s, "$("); if (dollar == NULL) return 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) == '<') { *start = b - 3; if (tag_parameter) *tag_parameter = FALSE; break; } if (*b != ' ') break; } if (*start == NULL) *start = dollar; /* Go forth and try to find placeholder end */ for (b = end_paren + 1; b <= end_paren + strlen (end_paren) - 3; b++) { if (*b == '-' && *(b+1) == '-' && *(b+2) == '>') { *end = b + 2; break; } if (*b != ' ') break; } if (*end == NULL) *end = end_paren; return g_strndup (dollar + 2, end_paren - dollar - 2); } /* * extract_token_arg: retrieves token argument "xxx(value)" * - returns newly allocated string, caller is responsible for freeing * */ gchar * extract_token_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); }