/* 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 #include "block-parser.h" #include "gallery-utils.h" #include "replace-table.h" #define BUFFER_SIZE 65536 /* line cannot be longer than this */ struct BlockParser { GHashTable *table; GQueue *active_tree; gchar *current_line; }; typedef struct { gchar *replace_key; gchar *data; gboolean used; gboolean finished; } BlockData; static void block_parser_destroy_notify (gpointer data) { BlockData *d = data; if (data == NULL) return; g_free (d->replace_key); g_free (d->data); g_free (d); } BlockParser * block_parser_new () { BlockParser *parser; parser = g_new0 (BlockParser, 1); parser->table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, block_parser_destroy_notify); parser->active_tree = g_queue_new (); return parser; } void block_parser_free (BlockParser *parser) { g_return_if_fail (parser != NULL); g_hash_table_destroy (parser->table); g_queue_free (parser->active_tree); g_free (parser); } /* * block_parser_register_key: tell parser to expect the key to catch * key: part of the BEGIN_xxx placeholder, matched BEGIN_xxx and END_xxx as one block * replace_key: placeholder to replace finished block with, for later use in replace_table, * placeholder will be surrounded by "" * */ void block_parser_register_key (BlockParser *parser, const gchar *key, const gchar *replace_key) { BlockData *data; g_return_if_fail (parser != NULL); data = g_new0 (BlockData, 1); 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() */ 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 * */ gchar * block_parser_get_data (BlockParser *parser, const gchar *key) { BlockData *data = NULL; g_return_val_if_fail (parser != NULL, NULL); data = g_hash_table_lookup (parser->table, key); if (data != NULL) { data->used = TRUE; return g_strdup (data->data); } return NULL; } /* * block_parser_has_unused_data: indicates whether the data have already been read and used (by calling block_parser_get_data) * */ gboolean block_parser_has_unused_data (BlockParser *parser, const gchar *key) { BlockData *data = NULL; g_return_val_if_fail (parser != NULL, FALSE); data = g_hash_table_lookup (parser->table, key); if (data != NULL) { return (data->used == FALSE) && (data->finished == TRUE); } return FALSE; } /* -------------------------------------------------------------------------------------------------------- */ static void push_string (BlockParser *parser, const gchar *piece) { BlockData *data; gchar *s; data = g_queue_peek_head (parser->active_tree); if (data) { if (data->data) s = g_strdup_printf ("%s%s", data->data, piece); else s = g_strdup (piece); g_free (data->data); data->data = s; data->used = FALSE; } else { s = g_strdup_printf ("%s%s", parser->current_line, piece); g_free (parser->current_line); parser->current_line = s; } } /* * 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 * string after closing placeholder are returned on one line, * separated by "replace_key" placeholder as specified in registered blocks * - only the outmost "replace_key" placeholder will be used, * no matter how many nested blocks were inside * - returns newly allocated string, caller is responsible for freeing * */ gchar * block_parser_read_and_parse (BlockParser *parser, FILE *stream) { gchar *buffer; gchar *b; gchar *token; gchar *start, *end; gchar *s; GList *keys; GList *l; BlockData *data; gboolean handled; g_return_val_if_fail (parser != NULL, NULL); buffer = g_malloc0 (BUFFER_SIZE); parser->current_line = g_strdup (""); if (! fgets (buffer, BUFFER_SIZE, stream) || strlen (buffer) == 0) return NULL; keys = g_hash_table_get_keys (parser->table); /* find tokens */ b = buffer; while ((token = get_next_token (b, &start, &end, NULL))) { handled = FALSE; /* push the string before the token */ s = g_strndup (b, start - b); push_string (parser, s); g_free (s); /* match known tags */ for (l = keys; l != NULL; l = l->next) { /* 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; } } g_free (s); /* test END_ tokens */ s = g_strdup_printf ("END_%s", (gchar *) l->data); 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); } else { g_queue_pop_head (parser->active_tree); /* push the replacement placeholder */ if (data->replace_key) push_string (parser, data->replace_key); data->finished = TRUE; handled = TRUE; } } g_free (s); } /* push the tag if not matched above */ if (! handled) { s = g_strndup (start, end - start + 1); push_string (parser, s); g_free (s); } g_free (token); b = end + 1; } /* 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; }