summaryrefslogtreecommitdiff
path: root/src/block-parser.c
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@users.sourceforge.net>2009-04-04 20:09:08 +0200
committerTomas Bzatek <tbzatek@users.sourceforge.net>2009-04-04 20:09:08 +0200
commitc572b8e7a250ecfba7836c55bb5eda4c9473280a (patch)
tree56e513682ede75e15dba8049240ff535ef183540 /src/block-parser.c
parentcad89a1f9b9e36d7490c3f8cd1449082a1285050 (diff)
downloadcataract-c572b8e7a250ecfba7836c55bb5eda4c9473280a.tar.xz
New block parser
Diffstat (limited to 'src/block-parser.c')
-rw-r--r--src/block-parser.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/src/block-parser.c b/src/block-parser.c
new file mode 100644
index 0000000..713f963
--- /dev/null
+++ b/src/block-parser.c
@@ -0,0 +1,318 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2009 Tomas Bzatek <tbzatek@users.sourceforge.net>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "block-parser.h"
+#include "gallery-utils.h"
+
+
+
+#define BUFFER_SIZE 65536 /* lines cannot be longer than this */
+
+
+struct BlockParser {
+ GHashTable *table;
+ GQueue *active_tree;
+ char *current_line;
+};
+
+typedef struct {
+ char *replace_key;
+ char *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 "<!-- $(replace_key) -->"
+ *
+ */
+void
+block_parser_register_key (BlockParser *parser, const char *key, const char *replace_key)
+{
+ BlockData *data;
+
+ g_return_if_fail (parser != NULL);
+
+ data = g_new0 (BlockData, 1);
+ if (replace_key)
+ data->replace_key = g_strdup_printf ("<!-- $(%s) -->", 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
+ *
+ */
+char *
+block_parser_get_data (BlockParser *parser, const char *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 char *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;
+}
+
+
+
+
+/* -------------------------------------------------------------------------------------------------------- */
+
+/*
+ * get_next_token: retrieves first token (<!-- $(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'
+ *
+ */
+static char *
+get_next_token (const char *s, char **start, char **end)
+{
+ char *dollar;
+ char *end_brace;
+ char *b;
+
+ *start = NULL;
+ *end = NULL;
+
+ dollar = strstr (s, "$(");
+ if (dollar == NULL)
+ return NULL;
+ end_brace = strchr (dollar + 2, ')');
+ if (end_brace == NULL)
+ return NULL;
+
+ /* Go back and try to find placeholder beginning */
+ for (b = dollar; b >= s + 3; b--) {
+ if (*b == '-' && *(b-1) == '-' && *(b-2) == '!' && *(b-3) == '<') {
+ *start = b - 3;
+ break;
+ }
+ }
+ if (*start == NULL)
+ *start = dollar;
+
+ /* Go forth and try to find placeholder end */
+ for (b = end_brace; b <= end_brace + strlen (end_brace) - 3; b++) {
+ if (*b == '-' && *(b+1) == '-' && *(b+2) == '>') {
+ *end = b + 2;
+ break;
+ }
+ }
+ if (*end == NULL)
+ *end = end_brace;
+
+ return g_strndup (dollar + 2, end_brace - dollar - 2);
+}
+
+
+static void
+push_string (BlockParser *parser, const char *piece)
+{
+ BlockData *data;
+ char *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
+ *
+ */
+char *
+block_parser_read_and_parse (BlockParser *parser, FILE *stream)
+{
+ char *buffer;
+ char *b;
+ char *token;
+ char *start, *end;
+ char *s;
+ GList *keys;
+ GList *l;
+ BlockData *data;
+ gboolean handled;
+
+ g_return_val_if_fail (parser != NULL, NULL);
+
+ buffer = malloc (BUFFER_SIZE);
+ memset (buffer, 0, 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))) {
+ 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", (char *) l->data);
+ if (strcmp (s, token) == 0) {
+ data = g_hash_table_lookup (parser->table, (char *) 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", (char *) l->data);
+ if (strcmp (s, token) == 0) {
+ data = g_hash_table_lookup (parser->table, (char *) l->data);
+ if (data == NULL || data != g_queue_peek_head (parser->active_tree)) {
+ fprintf (stderr, "block_parser_read_and_parse: something is wrong with the parser table: expected '%s'\n", (char *) 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);
+ free (buffer);
+ return parser->current_line;
+}