summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am41
-rw-r--r--src/cgg-config.h30
-rwxr-xr-xsrc/cgg-dirgen54
-rw-r--r--src/cgg.c155
-rw-r--r--src/gallery-utils.c262
-rw-r--r--src/gallery-utils.h53
-rw-r--r--src/generators-replace-table.c174
-rw-r--r--src/generators-replace-table.h63
-rw-r--r--src/generators.c1037
-rw-r--r--src/generators.h64
-rw-r--r--src/items.c320
-rw-r--r--src/items.h96
-rw-r--r--src/jpeg-utils.cpp429
-rw-r--r--src/jpeg-utils.h94
-rw-r--r--src/setup.c268
-rw-r--r--src/setup.h92
-rw-r--r--src/xml-parser.c261
-rw-r--r--src/xml-parser.h67
18 files changed, 3560 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..443d2f1
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,41 @@
+NULL =
+
+INCLUDES = \
+ -DDATADIR=\"$(datadir)\" \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED_CFLAGS) \
+ $(LIBS_CFLAGS) \
+ -DG_LOG_DOMAIN=\"cgg\"
+
+bin_PROGRAMS = \
+ cgg \
+ $(NULL)
+
+bin_SCRIPTS = \
+ cgg-dirgen \
+ $(NULL)
+
+cgg_SOURCES = \
+ cgg.c \
+ cgg-config.h \
+ gallery-utils.c \
+ gallery-utils.h \
+ generators.c \
+ generators.h \
+ generators-replace-table.c \
+ generators-replace-table.h \
+ items.c \
+ items.h \
+ jpeg-utils.cpp \
+ jpeg-utils.h \
+ setup.c \
+ setup.h \
+ xml-parser.c \
+ xml-parser.h \
+ $(NULL)
+
+cgg_LDADD = $(LIBS_LIBS)
+
+EXTRA_DIST = cgg-dirgen
diff --git a/src/cgg-config.h b/src/cgg-config.h
new file mode 100644
index 0000000..7a447d7
--- /dev/null
+++ b/src/cgg-config.h
@@ -0,0 +1,30 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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.
+ */
+
+
+
+/* Directory names of smaller images */
+#define THUMBNAIL_DIR "_thumb"
+#define IMG_BIG_DIR "_big"
+#define IMG_ORIG_DIR "_orig"
+
+#define SETUP_XML "setup.xml"
+
+// #define APP_VERSION "0.99.0"
+// #define APP_BUILD_DATE "2008-07-27"
+
diff --git a/src/cgg-dirgen b/src/cgg-dirgen
new file mode 100755
index 0000000..44dd256
--- /dev/null
+++ b/src/cgg-dirgen
@@ -0,0 +1,54 @@
+#!/bin/bash
+## Cataract Gallery Generator - a simple static web photo gallery
+## cgg-dirgen - Directory index.xml generator
+## Copyright (C) 2008 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.
+##
+
+## Optional arguments (must be in order, for now):
+## -d Use preview pictures from the "preview" folder
+## -o Do not include original image (removes link to original size)
+## -t Generate thumbnails from the "thumbnails" folder
+
+
+cat << XML_HEADER_STOP
+<?xml version="1.0" encoding="utf-8"?>
+<gallery type="album">
+ <general>
+ <ID>Album ID</ID>
+ <title>Album Title</title>
+ <description><![CDATA[Album description<br/>
+ ]]></description>
+ </general>
+
+ <items>
+XML_HEADER_STOP
+
+for i in `find -L . -maxdepth 1 -type f -iname '*.jpg' -or -iname '*.jpeg' -or -iname '*.gif' -or -iname '*.png' | sort`; do
+ INCL="";
+ INCL2="";
+ INCL3="";
+ if [ "$1" = "-d" ]; then INCL=" preview=\"preview/`echo $i | cut -b 3-`\""; fi
+ if [ "$2" = "-o" ]; then INCL2=" <nofullsize />\n"; fi
+ if [ "$3" = "-t" ]; then INCL3=" thumbnail=\"thumbnails/`echo $i | cut -b 3-`\""; fi
+ echo -e " <item src=\"`echo $i | cut -b 3-`\"${INCL}${INCL3}>\n${INCL2} <title> </title>\n <title_description> </title_description>\n </item>\n";
+done
+
+cat << XML_FOOTER_STOP
+ </items>
+</gallery>
+XML_FOOTER_STOP
+
diff --git a/src/cgg.c b/src/cgg.c
new file mode 100644
index 0000000..042e114
--- /dev/null
+++ b/src/cgg.c
@@ -0,0 +1,155 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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 <stdint.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <libxml/xmlmemory.h>
+
+#include <config.h>
+
+#include "cgg-config.h"
+#include "setup.h"
+#include "generators.h"
+
+
+
+
+
+
+/*
+ * parse_cmd: parse commandline and fill global variable parameters
+ */
+gboolean
+parse_cmd (int argc, char* argv[], char **source_dir, char **dst_dir, gboolean *verbose)
+{
+ static gboolean _verbose = FALSE;
+ static gchar *_source_dir = NULL;
+ static gchar *_dst_dir = NULL;
+
+ static GOptionEntry entries[] =
+ {
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &_verbose, "Be verbose", NULL },
+ { "source", 's', 0, G_OPTION_ARG_STRING, &_source_dir, "Specifies path to source structure", NULL },
+ { "output", 'o', 0, G_OPTION_ARG_STRING, &_dst_dir, "Generate files to the specified directory instead of current", NULL },
+ { NULL }
+ };
+
+ GError *error = NULL;
+ GOptionContext *context;
+ char *s1;
+
+ g_set_prgname ("cgg");
+
+ context = g_option_context_new ("- web gallery generator");
+ s1 = g_strdup_printf ("cgg v%s [%s] Copyright (c) %s Tomas Bzatek", VERSION, APP_BUILD_DATE, APP_COPYRIGHT_DATE);
+ g_option_context_set_summary (context, s1);
+ g_free (s1);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ if (argc == 1) {
+ s1 = g_option_context_get_help (context, TRUE, NULL);
+ g_print ("%s", s1);
+ g_free (s1);
+ g_option_context_free (context);
+ return FALSE;
+ }
+
+ if (! g_option_context_parse (context, &argc, &argv, &error)) {
+ g_print ("option parsing failed: %s\n", error->message);
+ s1 = g_option_context_get_help (context, TRUE, NULL);
+ g_print ("%s", s1);
+ g_free (s1);
+ g_option_context_free (context);
+ return FALSE;
+ }
+ g_option_context_free (context);
+
+ *source_dir = _source_dir;
+ *dst_dir = _dst_dir;
+ *verbose = _verbose;
+
+ return TRUE;
+}
+
+
+
+int
+main(int argc, char* argv[])
+{
+ char *source_dir;
+ char *dst_dir;
+ gboolean verbose;
+ TGallerySetup *setup;
+
+ /*
+ * this initialize the library and check potential ABI mismatches
+ * between the version it was compiled for and the actual shared
+ * library used.
+ */
+ LIBXML_TEST_VERSION;
+
+ source_dir = NULL;
+ dst_dir = NULL;
+ setup = malloc(sizeof(TGallerySetup));
+
+
+ /* Parse commandline */
+ if (! parse_cmd (argc, argv, &source_dir, &dst_dir, &verbose))
+ return -1;
+
+ if ((! source_dir) || (access (source_dir, R_OK))) {
+ fprintf (stderr, "error: source directory must be specified and pointing to valid structure\n");
+ return -4;
+ }
+ if (! dst_dir) {
+ fprintf (stderr, "error: target directory must be specified\n");
+ return -5;
+ }
+
+ /* Read gallery settings */
+ if (! find_setup_xml (setup)) {
+ fprintf (stderr, "error: could not parse gallery settings file\n");
+ return -2;
+ }
+
+ setup->real_templates_dir = find_templates_directory (setup);
+ if (setup->real_templates_dir == NULL) {
+ fprintf (stderr, "error: could not determine templates directory\n");
+ return -3;
+ }
+
+
+ /* Start building the gallery tree */
+ setup->verbose = verbose;
+ build_tree (setup, source_dir, dst_dir, NULL);
+
+
+ /* Cleanup function for the XML library. */
+ xmlCleanupParser();
+
+ free (source_dir);
+ free (dst_dir);
+ free_setup_data (setup);
+
+ return (0);
+}
diff --git a/src/gallery-utils.c b/src/gallery-utils.c
new file mode 100644
index 0000000..6378ae5
--- /dev/null
+++ b/src/gallery-utils.c
@@ -0,0 +1,262 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include "gallery-utils.h"
+
+
+/*
+ * str_replace: replace substring 'search' with a 'replace' string
+ * - multiple occurrences of the string are replaced
+ * - reallocates the original string
+ */
+void
+str_replace (char **dst, const char *search, const char *replace)
+{
+ #define REPLACE_MAX_LENGTH 32768
+ static char d[REPLACE_MAX_LENGTH];
+ char *src;
+ char *found;
+ int i;
+
+ /* TODO: add range checking */
+
+ if (strstr (*dst, search) == NULL || strlen (*dst) == 0 || strlen (search) == 0)
+ return;
+
+ i = 0;
+ src = *dst;
+ while (strstr (src, search)) {
+ found = strstr (src, search);
+
+ /* copy the data between search string */
+ if (found > src) {
+ memcpy (&d[i], src, found - src);
+ i += found - src;
+ }
+
+ /* copy replace string instead */
+ if (strlen (replace) > 0) {
+ memcpy (&d[i], replace, strlen (replace));
+ i += strlen (replace);
+ }
+ src = found + strlen (search);
+ }
+
+ /* copy the rest */
+ if (src) {
+ memcpy (&d[i], src, strlen (src));
+ i += strlen (src);
+ }
+ d[i] = 0x0;
+
+
+#ifdef __DEBUG_ALL__
+ printf ("str_replace('%s', '%s') = '%s' --> '%s'\n", search, replace, *dst, &d[0]);
+#endif
+
+ /* return fixed string */
+ free (*dst);
+ *dst = g_strdup (&d[0]);
+}
+
+
+/*
+ * copy_file: copy file from src to dst
+ */
+gboolean
+copy_file (const char *src, const char *dst)
+{
+ #define BUFFER_SIZE 65536
+
+ FILE *fin;
+ FILE *fout;
+ void *buffer;
+ int size_r;
+ struct stat st;
+ struct utimbuf ut;
+
+ fin = fopen (src, "r");
+ if (fin == NULL) {
+ fprintf (stderr, "copy_file: error reading file \"%s\": %s\n", src, strerror (errno));
+ return FALSE;
+ }
+
+ fout = fopen (dst, "w");
+ if (fout == NULL) {
+ fprintf (stderr, "copy_file: error writing to file \"%s\": %s\n", dst, strerror (errno));
+ fclose (fin);
+ return FALSE;
+ }
+
+ buffer = malloc (BUFFER_SIZE);
+ memset (buffer, 0, BUFFER_SIZE);
+ size_r = BUFFER_SIZE;
+
+ while ((! feof (fin)) && (size_r == BUFFER_SIZE)) {
+ size_r = fread (buffer, 1, BUFFER_SIZE, fin);
+ fwrite (buffer, 1, size_r, fout);
+ }
+
+ fclose (fout);
+ fclose (fin);
+ free (buffer);
+
+ /* copy timestamps */
+ memset (&st, 0, sizeof (st));
+ if (stat (src, &st) == -1) {
+ fprintf (stderr, "copy_file: cannot stat source file \"%s\": %s\n", src, strerror (errno));
+ return TRUE;
+ }
+
+ memset (&ut, 0, sizeof (ut));
+ ut.actime = st.st_atime;
+ ut.modtime = st.st_mtime;
+ if (utime (dst, &ut) == -1)
+ fprintf (stderr, "copy_file: cannot set timestamps on target file \"%s\": %s\n", dst, strerror (errno));
+
+ return TRUE;
+}
+
+
+/*
+ * make_string: make string of 'substr' substrings
+ * - returns newly allocated string
+ */
+char *
+make_string (const char* substr, const int count)
+{
+ int i;
+ char *s;
+
+ s = malloc (strlen (substr) * count + 1);
+ for (i = 0; i < count; i++)
+ memcpy (s + (strlen (substr) * i), substr, strlen (substr));
+ s[strlen (substr) * count] = 0;
+ return s;
+}
+
+
+/*
+ * fix_entities: replace all invalid & entities with &amp;
+ * - returns newly allocated string
+ */
+void
+fix_entities (char **str)
+{
+ static char d[REPLACE_MAX_LENGTH];
+ char *src;
+ char *found;
+ char *scan;
+ int i;
+
+ /* TODO: add range checking */
+
+ if (! *str || strstr (*str, "&") == NULL)
+ return;
+
+ i = 0;
+ src = *str;
+ while (strstr (src, "&")) {
+ found = strstr (src, "&");
+
+ /* copy the data between search string */
+ memcpy (&d[i], src, found - src + 1);
+ i += found - src + 1;
+
+ /* scroll to next whitespace */
+ scan = found + 1;
+ while (scan && (
+ (*scan >= 0x41 && *scan <= 0x5a) || (*scan >= 0x61 && *scan <= 0x7a) || /* A-Z, a-z */
+ (*scan >= 0x30 && *scan <= 0x39) || (*scan == 0x23) /* 0-9, # */
+ ))
+ scan++;
+
+ if (scan && (*scan == 0x3b)) {
+ /* this is semicolon, correctly closed entity */
+ /* -- ignore */
+ }
+ else {
+ /* replace with &amp; */
+ memcpy (&d[i], "amp;", 4);
+ i += 4;
+ }
+ src = found + 1;
+ }
+
+ /* copy the rest */
+ if (src) {
+ memcpy (&d[i], src, strlen (src));
+ i += strlen (src);
+ }
+ d[i] = 0x0;
+
+
+#ifdef __DEBUG_ALL__
+ printf ("fix_entities: '%s' --> '%s'\n", *str, &d[0]);
+#endif
+
+ /* return fixed string */
+ free (*str);
+ *str = g_strdup (&d[0]);
+}
+
+
+/*
+ * remove_tags: remove all occurences of tags beginning with tag_begin and ending with tag_end
+ * - e.g. remove_tags (&x, "<!--", "-->") will remove all comments
+ * - returns newly allocated string
+ */
+void
+remove_tags (char **str, const char *tag_begin, const char *tag_end)
+{
+ char *src;
+ char *found;
+ char *found2;
+ char *dest;
+
+ if (! *str || ! tag_begin || ! tag_end || strlen (*str) == 0 || strlen (tag_begin) == 0 || strlen (tag_end) == 0)
+ return;
+
+ src = *str;
+
+ while ((found = strstr (src, tag_begin)) != NULL) {
+ found2 = strstr (found, tag_end);
+ if (found2) {
+ found2 += strlen (tag_end);
+ dest = malloc (strlen (src) - (found2 - found) + 1);
+ memcpy (dest, src, found - src);
+ memcpy (dest + (found - src), found2, strlen (found2) + 1);
+#ifdef __DEBUG_ALL__
+ printf ("found = %ld, found2 = %ld, strlen = %d, res = %d\n", (long int)found, (long int)found2, strlen (src), strlen (src) - (found2 - found) + 1);
+ printf ("orig = %s, new = %s, strlen = %d\n", src, dest, strlen (dest));
+#endif
+ g_free (src);
+ src = g_strdup (dest);
+ free (dest);
+ }
+ }
+ *str = src;
+}
diff --git a/src/gallery-utils.h b/src/gallery-utils.h
new file mode 100644
index 0000000..53c82c0
--- /dev/null
+++ b/src/gallery-utils.h
@@ -0,0 +1,53 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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 <glib.h>
+
+#define IS_DIR_SEP(ch) ((ch) == '/' || (ch) == '~')
+#define IS_EQUAL_SIGN(ch) ((ch) == '=')
+
+
+/*
+ * str_replace: replace substring 'search' with a 'replace' string
+ * - multiple occurences of the string are replaced
+ * - reallocates the original string
+ */
+void str_replace (char **dst, const char *search, const char *replace);
+
+/*
+ * copy_file: copy file from src to dst
+ */
+gboolean copy_file (const char *src, const char *dst);
+
+/*
+ * make_string: make string of 'substr' substrings
+ * - returns newly allocated string
+ */
+char *make_string (const char* substr, const int count);
+
+/*
+ * fix_entities: replace all invalid & entities with &amp;
+ * - returns newly allocated string
+ */
+void fix_entities (char **str);
+
+/*
+ * remove_tags: remove all occurences of tags beginning with tag_begin and ending with tag_end
+ * - e.g. remove_tags (&x, "<!--", "-->") will remove all comments
+ * - returns newly allocated string
+ */
+void remove_tags (char **str, const char *tag_begin, const char *tag_end);
diff --git a/src/generators-replace-table.c b/src/generators-replace-table.c
new file mode 100644
index 0000000..23771fc
--- /dev/null
+++ b/src/generators-replace-table.c
@@ -0,0 +1,174 @@
+/* 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 "generators-replace-table.h"
+#include "gallery-utils.h"
+
+
+
+
+#if 0
+static void
+replace_table_destroy_notify (gpointer data)
+{
+ g_print ("replace_table_destroy_notify: %s\n", (gchar *) data);
+
+ if ((gchar *) data)
+ g_free ((gchar *) data);
+}
+#endif
+
+ReplaceTable *
+replace_table_new ()
+{
+ return (ReplaceTable *) g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+void
+replace_table_free (ReplaceTable *table)
+{
+ g_return_if_fail (table != NULL);
+
+ g_hash_table_destroy (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, 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, 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, g_strdup (tag), s);
+ va_end (args);
+ }
+}
+
+
+
+static void
+replace_table_process_value (gpointer key, gpointer value, gpointer user_data)
+{
+ gchar **buffer = user_data;
+ gchar *tag;
+ gchar *tag_value;
+
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (value != NULL);
+ g_return_if_fail (user_data != NULL);
+
+ /* replace <!-- $(xxx) --> tags */
+ tag = g_strdup_printf ("<!-- $(%s) -->", (gchar *) key);
+ tag_value = g_strdup ((gchar *) value);
+ adjust_tags_normal (&tag_value);
+ while (strstr (*buffer, tag)) {
+ str_replace (buffer, tag, tag_value);
+ }
+ g_free (tag_value);
+ g_free (tag);
+
+ /* replace $(xxx) tags */
+ tag = g_strdup_printf ("$(%s)", (gchar *) key);
+ tag_value = g_strdup ((gchar *) value);
+ adjust_tags_parameter (&tag_value);
+ while (strstr (*buffer, tag)) {
+ str_replace (buffer, tag, tag_value);
+ }
+ g_free (tag_value);
+ g_free (tag);
+}
+
+/*
+ * 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)
+{
+ g_return_if_fail (table != NULL);
+
+ g_hash_table_foreach (table, replace_table_process_value, buffer);
+
+/*
+ gchar *val = g_hash_table_lookup (table, "TAG");
+ g_print ("replace_table_process: val = '%s'\n", val);
+*/
+}
+
+
+/*
+ * 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 (char **str)
+{
+ fix_entities (str);
+}
+
+void
+adjust_tags_parameter (char **str)
+{
+ /* TODO: replace line endings with single space? */
+ remove_tags (str, "<!--", "-->"); /* comments */
+ remove_tags (str, "<", ">"); /* tags */
+ fix_entities (str); /* entities */
+ str_replace (str, "\"", "&quot;"); /* " */
+ str_replace (str, "'", "&apos;"); /* ' */
+}
diff --git a/src/generators-replace-table.h b/src/generators-replace-table.h
new file mode 100644
index 0000000..d72d150
--- /dev/null
+++ b/src/generators-replace-table.h
@@ -0,0 +1,63 @@
+/* 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 <glib.h>
+
+
+typedef GHashTable ReplaceTable;
+
+/*
+ * write_html_album: process album and index template files
+ *
+ * template_src = template file of the album/index
+ * dst = save generated file as
+ * items = array of items in the album/index
+ *
+ */
+ReplaceTable *replace_table_new ();
+
+void replace_table_free (ReplaceTable *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);
+void replace_table_add_key_int (ReplaceTable *table, const gchar *tag, gint value);
+void replace_table_add_key_printf (ReplaceTable *table, const gchar *tag, const gchar *format, ...) G_GNUC_PRINTF (3, 4);
+
+/*
+ * 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);
+
+
+/*
+ * 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 (char **str);
+void adjust_tags_parameter (char **str);
+
diff --git a/src/generators.c b/src/generators.c
new file mode 100644
index 0000000..8a2053a
--- /dev/null
+++ b/src/generators.c
@@ -0,0 +1,1037 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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 <config.h>
+
+#include "cgg-config.h"
+#include "setup.h"
+#include "items.h"
+#include "jpeg-utils.h"
+#include "gallery-utils.h"
+#include "generators-replace-table.h"
+
+
+/*
+ * generate_image: auxiliary function for write_html_album
+ * - img_src and thumb should be freed afterwards
+ */
+void
+generate_image (TGallerySetup *setup,
+ TAlbum *items,
+ TIndexItem *item,
+ unsigned int item_index,
+ const char *dst,
+ unsigned long *img_w, unsigned long *img_h,
+ char **img_src, char **thumb)
+{
+ unsigned long new_w, new_h;
+ unsigned long thumb_w, thumb_h;
+ char *thumb_dst;
+ char *big_dst;
+ char *big_src;
+ char *orig_dst;
+ char *img_src_full;
+ char *thumb_src_full;
+ char *s1;
+ int bigq;
+
+
+ *img_src = NULL;
+ *thumb = NULL;
+ *img_w = 0;
+ *img_h = 0;
+
+ if (items->type == GALLERY_TYPE_INDEX) {
+ if (item->thumbnail == NULL || strlen (item->thumbnail) == 0)
+ return;
+ img_src_full = g_strconcat (items->base_dir, "/", item->thumbnail, NULL);
+ thumb_src_full = g_strconcat (items->base_dir, "/", item->thumbnail, NULL);
+ *img_src = g_path_get_basename (item->thumbnail);
+ s1 = g_path_get_basename (item->thumbnail);
+ *thumb = g_strdup_printf ("thn_%.3d_%s", item_index, s1);
+ g_free (s1);
+ }
+ else
+ if (items->type == GALLERY_TYPE_ALBUM) {
+ *img_src = (item->path == NULL && item->preview) ? item->preview : item->path;
+ *thumb = (item->thumbnail) ? item->thumbnail : *img_src;
+ img_src_full = g_strconcat (items->base_dir, "/", *img_src, NULL);
+ thumb_src_full = g_strconcat (items->base_dir, "/", *thumb, NULL);
+ *img_src = g_path_get_basename (*img_src);
+ s1 = g_path_get_basename (*thumb);
+ *thumb = g_strdup_printf ("thn_%.3d_%s", item_index, s1);
+ g_free (s1);
+ }
+
+ get_image_sizes (img_src_full, img_w, img_h);
+
+ if ((img_w > 0) && (img_h > 0)) {
+ new_w = *img_w;
+ new_h = *img_h;
+
+ /* Generate thumbnail */
+ get_image_sizes (thumb_src_full, &thumb_w, &thumb_h);
+ s1 = g_path_get_dirname (dst);
+ thumb_dst = g_strconcat (s1, "/", THUMBNAIL_DIR, "/", *thumb, NULL);
+ g_free (s1);
+ if (setup->verbose) printf (" Generating thumbnail of '%s' ...", *thumb);
+
+ if ((thumb_w > 0) && (thumb_h > 0)) {
+ if ((thumb_w / thumb_h) >= 1)
+ calculate_sizes (setup->thumbnail_landscape_width, setup->thumbnail_landscape_height, &thumb_w, &thumb_h);
+ else
+ calculate_sizes (setup->thumbnail_portrait_width, setup->thumbnail_portrait_height, &thumb_w, &thumb_h);
+ if (! resize_image (thumb_src_full, thumb_dst, thumb_w, thumb_h, setup->thumbnail_quality, TRUE))
+ fprintf (stderr, "write_html_index: error resizing thumbnail %s\n", thumb_src_full);
+ else
+ if (setup->verbose) printf (" done.\n");
+ g_free (thumb_dst);
+ } else
+ printf (" failed!\n");
+
+
+ /* Generate/copy preview and original image */
+ if (items->type == GALLERY_TYPE_ALBUM)
+ {
+ s1 = g_path_get_dirname (dst);
+ big_dst = g_strconcat (s1, "/", IMG_BIG_DIR, "/", *img_src, NULL);
+ g_free (s1);
+
+ if (item->preview == NULL) {
+ /* No preview image supplied, generate it from original */
+ bigq = setup->preview_quality;
+ if ((items->quality > 0) && (items->quality <= 100))
+ bigq = items->quality;
+ if ((item->quality > 0) && (item->quality <= 100))
+ bigq = item->quality;
+ new_w = *img_w;
+ new_h = *img_h;
+
+ if (setup->verbose) printf (" Generating preview of '%s' ...", *img_src);
+ if ((item->width > 0) && (item->height > 0)) {
+ calculate_sizes (item->width, item->height, &new_w, &new_h);
+ } else {
+ if ((*img_w / *img_h) >= 1)
+ {
+ if ((items->landscape_width > 0) && (items->landscape_height > 0))
+ calculate_sizes (items->landscape_width, items->landscape_height, &new_w, &new_h);
+ else
+ calculate_sizes (setup->preview_landscape_width, setup->preview_landscape_height, &new_w, &new_h);
+ }
+ else
+ {
+ if ((items->portrait_width > 0) && (items->portrait_height > 0))
+ calculate_sizes (items->portrait_width, items->portrait_height, &new_w, &new_h);
+ else
+ calculate_sizes (setup->preview_portrait_width, setup->preview_portrait_height, &new_w, &new_h);
+ }
+ }
+
+ if (! resize_image (img_src_full, big_dst, new_w, new_h, bigq, FALSE))
+ fprintf (stderr, "write_html_index: error resizing big image %s\n", img_src_full);
+ else
+ if (setup->verbose) printf (" done.\n");
+ }
+ else
+ {
+ /* Copy the preview (big) image provided */
+ big_src = g_strconcat (items->base_dir, "/", item->preview, NULL);
+ if (setup->verbose) printf (" Copying preview image '%s' ...", *img_src);
+ if (! copy_file (big_src, big_dst))
+ fprintf (stderr, "write_html_index: error copying preview image %s\n", big_src);
+ else
+ if (setup->verbose) printf (" done.\n");
+ g_free (big_src);
+ }
+ modify_exif (big_dst, setup->erase_exif_thumbnail, setup->add_copyright);
+ g_free (big_dst);
+
+ if (item->force_fullsize || (items->fullsize && ! item->force_nofullsize) ||
+ (! item->force_nofullsize && ! items->nofullsize && ! setup->nofullsize))
+ {
+ s1 = g_path_get_dirname(dst);
+ orig_dst = g_strconcat (s1, "/", IMG_ORIG_DIR, "/", *img_src, NULL);
+ g_free (s1);
+ if (setup->verbose) printf (" Copying original image '%s' ...", *img_src);
+ if (! copy_file(img_src_full, orig_dst))
+ fprintf (stderr, "write_html_index: error copying original image %s\n", img_src_full);
+ else
+ if (setup->verbose) printf(" done.\n");
+ modify_exif (orig_dst, setup->erase_exif_thumbnail, setup->add_copyright);
+ g_free (orig_dst);
+ }
+ }
+ }
+ g_free (img_src_full);
+ g_free (thumb_src_full);
+}
+
+
+
+/*
+ * write_html_album: process album and index template files
+ *
+ * template_src = template file of the album/index
+ * dst = save generated file as
+ * items = array of items in the album/index
+ *
+ */
+gboolean
+write_html_album (TGallerySetup *setup,
+ const char *template_src,
+ const char *dst,
+ TAlbum *items)
+{
+ #define BUFFER_SIZE 65536
+ FILE *fin;
+ FILE *fout;
+ char *buffer;
+ char *buf_img_list_landscape;
+ char *buf_img_list_portrait;
+ char *buf_img_separator;
+ char *buf_go_up_string;
+ gboolean in_img_list;
+ gboolean in_img_list_landscape;
+ gboolean in_img_list_portrait;
+ gboolean in_img_separator;
+ gboolean in_go_up_string;
+ char *b;
+ char *s1, *s2, *s3, *s4, *s5;
+ TAlbum *parent;
+ TIndexItem *item;
+ TIndexItem *tmp_item;
+ int level, old_parent_item_index;
+ gboolean res, bb;
+ int i;
+ unsigned long img_w, img_h;
+ char *img_src;
+ char *thumb;
+ unsigned int real_total_items;
+ ReplaceTable *global_replace_table;
+ ReplaceTable *local_replace_table;
+
+
+ fin = fopen (template_src, "r");
+ if (fin == NULL) {
+ fprintf (stderr, "write_html_index: error reading file \"%s\": %s\n", template_src, strerror (errno));
+ return FALSE;
+ }
+ fout = fopen (dst, "w");
+ if (fout == NULL) {
+ fprintf (stderr, "write_html_index: error writing to file \"%s\": %s\n", dst, strerror (errno));
+ fclose (fin);
+ return FALSE;
+ }
+
+ buffer = malloc (BUFFER_SIZE);
+ buf_img_list_landscape = malloc (BUFFER_SIZE);
+ buf_img_list_portrait = malloc (BUFFER_SIZE);
+ buf_img_separator = malloc (BUFFER_SIZE);
+ buf_go_up_string = malloc (BUFFER_SIZE);
+ in_img_list = FALSE;
+ in_img_list_landscape = FALSE;
+ in_img_list_portrait = FALSE;
+ in_img_separator = FALSE;
+ in_go_up_string = FALSE;
+ res = TRUE;
+
+ global_replace_table = replace_table_new ();
+
+ /* Get number of real pictures in the list */
+ real_total_items = 0;
+ for (i = 0; i < items->items->len; i++) {
+ tmp_item = g_ptr_array_index (items->items, i);
+ if (tmp_item->type == INDEX_ITEM_TYPE_PICTURE)
+ real_total_items++;
+ }
+ replace_table_add_key_int (global_replace_table, "TOTAL_ITEMS", real_total_items);
+
+ /* Page title */
+ if (items->parent_index == NULL || setup->site_title == NULL)
+ s1 = g_strdup (setup->site_title ? setup->site_title : items->ID);
+ else
+ s1 = g_strdup_printf ("%s | %s", items->title, setup->site_title);
+ replace_table_add_key (global_replace_table, "PAGE_TITLE", s1);
+ g_free (s1);
+
+ /* Simple placeholders */
+ replace_table_add_key (global_replace_table, "ID", items->ID);
+ replace_table_add_key (global_replace_table, "TITLE", items->title);
+ replace_table_add_key (global_replace_table, "DESCRIPTION", items->desc);
+ replace_table_add_key (global_replace_table, "FOOTNOTE", items->footnote);
+ replace_table_add_key (global_replace_table, "FOOTER", setup->footer);
+ s1 = setup->use_inpage_links ? g_strdup_printf ("../index.html#i%d", items->parent_item_index + 1) : g_strdup ("../index.html");
+ replace_table_add_key (global_replace_table, "GO_UP_LINK", s1);
+ g_free (s1);
+
+ /* Navigation bar */
+ s1 = g_strdup (items->ID);
+ old_parent_item_index = items->parent_item_index + 1;
+ parent = items->parent_index;
+ level = 1;
+ while (parent) {
+ s3 = make_string ("../", level);
+ s4 = g_strdup (parent->ID);
+ s5 = setup->use_inpage_links ? g_strdup_printf ("#i%d", old_parent_item_index) : g_strdup ("");
+ s2 = g_strdup_printf ("<a href=\"%sindex.html%s\">%s</a> &gt; %s", s3, s5, s4, s1);
+ free (s3);
+ g_free (s1);
+ g_free (s4);
+ g_free (s5);
+ s1 = s2;
+ old_parent_item_index = parent->parent_item_index + 1;
+ parent = parent->parent_index;
+ level++;
+ }
+ replace_table_add_key (global_replace_table, "NAV_BAR", s1);
+ g_free (s1);
+
+ /* META tags */
+ s1 = g_strdup_printf ("\t<meta name=\"generator\" content=\"Cataract Gallery Generator v%s\" />\n", VERSION);
+ if (setup->meta_author || items->meta_author) {
+ s3 = g_strdup (items->meta_author ? items->meta_author : setup->meta_author);
+ adjust_tags_parameter (&s3);
+ s2 = g_strdup_printf ("%s\t<meta name=\"author\" content=\"%s\" />\n", s1, s3);
+ g_free (s3);
+ g_free (s1);
+ s1 = s2;
+ }
+ if (setup->meta_description || items->meta_description) {
+ s3 = g_strdup (items->meta_description ? items->meta_description : setup->meta_description);
+ adjust_tags_parameter (&s3);
+ s2 = g_strdup_printf ("%s\t<meta name=\"description\" content=\"%s\" />\n", s1, s3);
+ g_free (s3);
+ g_free (s1);
+ s1 = s2;
+ }
+ if (setup->meta_keywords || items->meta_keywords) {
+ s3 = g_strdup (items->meta_keywords ? items->meta_keywords : setup->meta_keywords);
+ adjust_tags_parameter (&s3);
+ s2 = g_strdup_printf ("%s\t<meta name=\"keywords\" content=\"%s\" />\n", s1, s3);
+ g_free (s3);
+ g_free (s1);
+ s1 = s2;
+ }
+ replace_table_add_key (global_replace_table, "CGG_META_TAGS", s1);
+ g_free (s1);
+
+
+ /* Read through the template and replace placeholders with real data */
+ while (! feof (fin))
+ {
+ memset (buffer, 0, BUFFER_SIZE);
+ if (! fgets (buffer, BUFFER_SIZE, fin))
+ break;
+
+ /* Block placeholders */
+ if (in_img_list && (strstr (buffer, "<!-- $(BEGIN_IMG_LIST_LANDSCAPE) -->"))) {
+ in_img_list_landscape = TRUE;
+ continue;
+ }
+ if (in_img_list && (strstr (buffer, "<!-- $(END_IMG_LIST_LANDSCAPE) -->"))) {
+ in_img_list_landscape = FALSE;
+ continue;
+ }
+ if (in_img_list && (strstr (buffer, "<!-- $(BEGIN_IMG_LIST_PORTRAIT) -->"))) {
+ in_img_list_portrait = TRUE;
+ continue;
+ }
+ if (in_img_list && (strstr (buffer, "<!-- $(END_IMG_LIST_PORTRAIT) -->"))) {
+ in_img_list_portrait = FALSE;
+ continue;
+ }
+ if (in_img_list && (strstr (buffer, "<!-- $(BEGIN_LIST_SEPARATOR) -->"))) {
+ in_img_separator = TRUE;
+ continue;
+ }
+ if (in_img_list && (strstr (buffer, "<!-- $(END_LIST_SEPARATOR) -->"))) {
+ in_img_separator = FALSE;
+ continue;
+ }
+ if (in_img_list && in_img_list_landscape) {
+ buf_img_list_landscape = strncat (buf_img_list_landscape, buffer, BUFFER_SIZE - strlen (buf_img_list_landscape) - 2);
+ continue;
+ }
+ if (in_img_list && in_img_list_portrait) {
+ buf_img_list_portrait = strncat (buf_img_list_portrait, buffer, BUFFER_SIZE - strlen (buf_img_list_portrait) - 2);
+ continue;
+ }
+ if (in_img_list && in_img_separator) {
+ buf_img_separator = strncat (buf_img_separator, buffer, BUFFER_SIZE - strlen (buf_img_separator) - 2);
+ continue;
+ }
+
+ if (strstr (buffer, "<!-- $(BEGIN_GO_UP) -->")) {
+ memset (buf_go_up_string, 0, BUFFER_SIZE);
+ in_go_up_string = TRUE;
+ continue;
+ }
+ if (in_go_up_string && strstr (buffer, "<!-- $(END_GO_UP) -->")) {
+ in_go_up_string = FALSE;
+ if (! items->parent_index)
+ continue;
+ }
+ if (in_go_up_string) {
+ buf_go_up_string = strncat (buf_go_up_string, buffer, BUFFER_SIZE - strlen (buf_go_up_string) - 2);
+ continue;
+ }
+
+ /* Image list, nested placeholders */
+ if (strstr (buffer, "<!-- $(BEGIN_IMG_LIST) -->")) {
+ memset (buf_img_list_landscape, 0, BUFFER_SIZE);
+ memset (buf_img_list_portrait, 0, BUFFER_SIZE);
+ memset (buf_img_separator, 0, BUFFER_SIZE);
+ in_img_list = TRUE;
+ in_img_list_landscape = FALSE;
+ in_img_list_portrait = FALSE;
+ in_img_separator = FALSE;
+ continue;
+ }
+ if (in_img_list && (strstr (buffer, "<!-- $(END_IMG_LIST) -->"))) {
+ in_img_list = FALSE;
+ in_img_list_landscape = FALSE;
+ in_img_list_portrait = FALSE;
+
+ /* Now we have all block placeholders read, generate the items: */
+ for (i = 0; i < items->items->len; i++)
+ {
+ item = g_ptr_array_index (items->items, i);
+ if (item == NULL) {
+ fprintf (stderr, "write_html_index: error getting item %d\n", i);
+ continue;
+ }
+
+ /* Generate the images (preview, original, thumbnail) */
+ local_replace_table = replace_table_new ();
+ img_w = 0;
+ img_h = 0;
+ s1 = NULL;
+
+ switch (item->type) {
+ case INDEX_ITEM_TYPE_PICTURE:
+ img_src = NULL;
+ thumb = NULL;
+ generate_image (setup, items, item, i, dst, &img_w, &img_h, &img_src, &thumb);
+ /* Skip HTML code generation if it's a hidden item */
+ if (! item->hidden) {
+ if (img_w == 0 || img_h == 0 || (img_w / img_h) >= 1)
+ s1 = strdup (buf_img_list_landscape);
+ else
+ s1 = strdup (buf_img_list_portrait);
+
+ replace_table_add_key_printf (local_replace_table, "ALBUM_SUBPATH", "%s/index.html", item->path);
+ replace_table_add_key_printf (local_replace_table, "IMG_SUBPAGE", "%s.html", img_src);
+ replace_table_add_key (local_replace_table, "IMG_TITLE", item->title);
+ replace_table_add_key (local_replace_table, "IMG_DESCRIPTION", item->title_description);
+ replace_table_add_key_printf (local_replace_table, "IMG_LIST_ID", "i%d", i + 1);
+ replace_table_add_key_printf (local_replace_table, "IMG_THUMBNAIL", "%s/%s", THUMBNAIL_DIR, thumb);
+ replace_table_add_key (local_replace_table, "IMG_FILENAME", img_src);
+ if (items->type == GALLERY_TYPE_INDEX) {
+ s3 = g_strconcat (items->base_dir, "/", item->path, "/index.xml", NULL);
+ replace_table_add_key_int (local_replace_table, "ALBUM_NUM_ITEMS", get_album_objects_count (s3));
+ g_free (s3);
+ }
+ }
+ if (img_src)
+ g_free (img_src);
+ if (thumb)
+ g_free (thumb);
+ break;
+
+ case INDEX_ITEM_TYPE_SEPARATOR:
+ s1 = strdup (buf_img_separator);
+ replace_table_add_key (local_replace_table, "LIST_SEPARATOR_TITLE", item->title);
+ break;
+ }
+
+ bb = TRUE;
+ if (s1) {
+ replace_table_process (&s1, local_replace_table);
+ bb = fputs (s1, fout);
+ free (s1);
+ }
+ replace_table_free (local_replace_table);
+ if (! bb) {
+ fprintf (stderr, "write_html_index: error writing to file \"%s\": %s\n", dst, strerror (errno));
+ res = FALSE;
+ break;
+ }
+ }
+ continue; /* no need to write anything */
+ }
+
+ /* Select apropriate line buffer */
+ if (strstr (buffer, "<!-- $(END_GO_UP) -->") && items->parent_index) {
+ b = strdup (buf_go_up_string);
+ } else
+ b = strdup (buffer);
+
+ /* Replace all known tags */
+ replace_table_process (&b, global_replace_table);
+
+ /* Write to file */
+ bb = fputs (b, fout);
+ free (b);
+ if (! bb) {
+ fprintf (stderr, "write_html_index: error writing to file \"%s\": %s\n", dst, strerror (errno));
+ res = FALSE;
+ break;
+ }
+ }
+
+ fclose (fout);
+ fclose (fin);
+ replace_table_free (global_replace_table);
+ free (buffer);
+ free (buf_img_list_landscape);
+ free (buf_img_list_portrait);
+ free (buf_img_separator);
+ free (buf_go_up_string);
+ return res;
+}
+
+
+/*
+ * write_html_image: process single image template file
+ *
+ * template_src = template file of the album/index
+ * original_img = source image file (original full-size) to get EXIF data from
+ * dst = save generated file as
+ * item = data for the current item
+ * parent_items = array of items in the album, to determine our position and make links to previous/next image
+ *
+ */
+gboolean
+write_html_image (TGallerySetup *setup,
+ const char *template_src,
+ const char *original_img,
+ const char *dst,
+ TIndexItem *item,
+ TAlbum *parent_items)
+{
+ #define BUFFER_SIZE 65536
+ FILE *fin;
+ FILE *fout;
+ char *buffer;
+ char *big_dst;
+ char *orig_dst;
+ char *buf_img_fullsize_link;
+ gboolean in_img_fullsize_link;
+ TExifData *exif;
+ unsigned long img_big_w, img_big_h, img_orig_w, img_orig_h;
+ unsigned int item_index, real_item_index, real_total_items;
+ TIndexItem *previous_item;
+ TIndexItem *next_item;
+ TIndexItem *tmp_item;
+ TAlbum *parent;
+ int i;
+ char *s1, *s2, *s3, *s4, *s5;
+ char *imgname, *preload_imgname;
+ char *title, *title_desc;
+ char *b;
+ gboolean res, bb;
+ int level, old_parent_item_index;
+ gboolean override_title_meta;
+ gboolean image_fullsize;
+ ReplaceTable *replace_table;
+
+
+ fin = fopen (template_src, "r");
+ if (fin == NULL) {
+ fprintf (stderr, "write_html_image: error reading file \"%s\": %s\n", template_src, strerror (errno));
+ return FALSE;
+ }
+ fout = fopen (dst, "w");
+ if (fout == NULL) {
+ fprintf (stderr, "write_html_image: error writing to file \"%s\": %s\n", dst, strerror (errno));
+ fclose (fin);
+ return FALSE;
+ }
+
+ buffer = malloc (BUFFER_SIZE);
+ preload_imgname = NULL;
+
+ replace_table = replace_table_new ();
+
+ /* Get our index in the album */
+ item_index = 0;
+ real_item_index = 0;
+ real_total_items = 0;
+ for (i = 0; i < parent_items->items->len; i++) {
+ tmp_item = g_ptr_array_index (parent_items->items, i);
+ if (tmp_item->type == INDEX_ITEM_TYPE_PICTURE) {
+ if (! item_index) real_item_index++;
+ real_total_items++;
+ }
+ if (tmp_item == item)
+ item_index = i + 1;
+ }
+
+ /* Get previous and next items */
+ previous_item = NULL;
+ next_item = NULL;
+ for (i = item_index - 2; i >= 0 && (previous_item == NULL || previous_item->type != INDEX_ITEM_TYPE_PICTURE); i--)
+ previous_item = g_ptr_array_index (parent_items->items, i);
+ if (previous_item && previous_item->type != INDEX_ITEM_TYPE_PICTURE)
+ previous_item = NULL;
+ for (i = item_index; item_index < parent_items->items->len && (next_item == NULL || next_item->type != INDEX_ITEM_TYPE_PICTURE); i++)
+ next_item = g_ptr_array_index (parent_items->items, i);
+ if (next_item && next_item->type != INDEX_ITEM_TYPE_PICTURE)
+ next_item = NULL;
+
+ /* Paths setup */
+ imgname = g_path_get_basename ((item->path == NULL && item->preview) ? item->preview : item->path);
+ if (next_item && setup->preload) {
+ s1 = g_path_get_basename ((next_item->path == NULL && next_item->preview) ? next_item->preview : next_item->path);
+ preload_imgname = g_strconcat (IMG_BIG_DIR, "/", s1, NULL);
+ g_free (s1);
+ }
+ s1 = g_path_get_dirname (dst);
+ big_dst = g_strconcat (s1, "/", IMG_BIG_DIR, "/", imgname, NULL);
+ orig_dst = g_strconcat (s1, "/", IMG_ORIG_DIR, "/", imgname, NULL);
+ g_free (s1);
+ buf_img_fullsize_link = malloc (BUFFER_SIZE);
+ memset (buf_img_fullsize_link, 0, BUFFER_SIZE);
+ in_img_fullsize_link = FALSE;
+ res = TRUE;
+
+ /* Get EXIF data from the original image */
+ if (get_exif (original_img, &exif))
+ fprintf (stderr, "write_html_image: error getting exif data from file \"%s\"\n", orig_dst);
+
+ /* Retrieve image sizes of preview and original image */
+ get_image_sizes (big_dst, &img_big_w, &img_big_h);
+ image_fullsize = item->force_fullsize || (parent_items->fullsize && ! item->force_nofullsize) ||
+ (! item->force_nofullsize && ! parent_items->nofullsize && ! setup->nofullsize);
+ if (image_fullsize)
+ get_image_sizes (orig_dst, &img_orig_w, &img_orig_h);
+
+ /* Get title and description from IPTC/EXIF/JPEG if not defined */
+ title = g_strdup (item->title);
+ title_desc = g_strdup (item->title_description);
+ if (setup->use_iptc_exif && title == NULL && title_desc == NULL) {
+ if (exif->iptc_caption)
+ title = g_strdup (exif->iptc_caption);
+ if (exif->jpeg_comment) {
+ if (! title)
+ title = g_strdup (exif->jpeg_comment);
+ else
+ title_desc = g_strdup (exif->jpeg_comment);
+ }
+ if (exif->exif_imgdesc) {
+ if (! title)
+ title = g_strdup (exif->exif_imgdesc);
+/* if (! title_desc) -- disabled
+ title_desc = g_strdup (exif->exif_imgdesc); */
+ }
+ if (exif->exif_usercomment) {
+ if (! title)
+ title = g_strdup (exif->exif_usercomment);
+ if (! title_desc)
+ title_desc = g_strdup (exif->exif_usercomment);
+ }
+ /* Convert line breaks to be visible in the HTML code */
+ if (title) {
+ str_replace (&title, "\r\n", "<br />");
+ str_replace (&title, "\n", "<br />");
+ }
+ if (title_desc) {
+ str_replace (&title_desc, "\r\n", "<br />");
+ str_replace (&title_desc, "\n", "<br />");
+ }
+ }
+ if (title) title = g_strstrip (title);
+ if (title_desc) title_desc = g_strstrip (title_desc);
+
+ /* Page title */
+ s1 = (title && strlen (title) > 0) ? g_strdup_printf("%s | ", title) : NULL;
+ s2 = g_strdup_printf ("%s [%d/%d]", parent_items->title ? parent_items->title : parent_items->ID, real_item_index, real_total_items);
+ s3 = setup->site_title ? g_strdup_printf(" | %s", setup->site_title) : NULL;
+ s4 = g_strconcat (s1 ? s1 : "", s2, s3 ? s3 : "", NULL);
+ replace_table_add_key (replace_table, "PAGE_TITLE", s4);
+ if (s1) g_free (s1);
+ if (s2) g_free (s2);
+ if (s3) g_free (s3);
+ g_free (s4);
+
+ /* Simple placeholders */
+ replace_table_add_key (replace_table, "FILE_NAME", imgname);
+ replace_table_add_key (replace_table, "TITLE", title);
+ replace_table_add_key (replace_table, "DESCRIPTION", title_desc);
+ replace_table_add_key (replace_table, "FOOTER", setup->footer);
+ replace_table_add_key_int (replace_table, "TOTAL_ITEMS", real_total_items);
+ replace_table_add_key_int (replace_table, "FILE_NO", real_item_index);
+ replace_table_add_key_int (replace_table, "IMG_SIZE_BIG_W", img_big_w);
+ replace_table_add_key_int (replace_table, "IMG_SIZE_BIG_H", img_big_h);
+ replace_table_add_key_int (replace_table, "IMG_SIZE_ORIG_W", img_orig_w);
+ replace_table_add_key_int (replace_table, "IMG_SIZE_ORIG_H", img_orig_h);
+ replace_table_add_key_printf (replace_table, "IMG_SRC_BIG", "%s/%s", IMG_BIG_DIR, imgname);
+ replace_table_add_key_printf (replace_table, "IMG_SRC_FULL", "%s/%s", IMG_ORIG_DIR, imgname);
+ replace_table_add_key (replace_table, "IMG_SRC_PRELOAD", preload_imgname ? preload_imgname : "");
+
+ /* Navigation bar */
+ s1 = g_strdup (imgname);
+ parent = parent_items;
+ level = 0;
+ while (parent) {
+ s3 = make_string ("../", level);
+ s4 = g_strdup (parent->ID);
+ s5 = setup->use_inpage_links ? g_strdup_printf ("#i%d", parent == parent_items ? item_index : old_parent_item_index) : g_strdup ("");
+ s2 = g_strdup_printf ("<a href=\"%sindex.html%s\">%s</a> &gt; %s", s3, s5, s4, s1);
+ free (s3);
+ g_free (s1);
+ g_free (s4);
+ g_free (s5);
+ s1 = s2;
+ old_parent_item_index = parent->parent_item_index + 1;
+ parent = parent->parent_index;
+ level++;
+ }
+ replace_table_add_key (replace_table, "NAV_BAR", s1);
+ g_free (s1);
+
+ /* EXIF values */
+ replace_table_add_key (replace_table, "EXIF_ISO", exif->iso ? exif->iso : "??");
+ replace_table_add_key (replace_table, "EXIF_TIME", exif->exposure ? exif->exposure : "??");
+ replace_table_add_key (replace_table, "EXIF_APERTURE", exif->aperture ? exif->aperture : "??");
+ replace_table_add_key (replace_table, "EXIF_FOCAL_LENGTH", exif->focal_length ? exif->focal_length : "??");
+ replace_table_add_key (replace_table, "EXIF_FLASH", exif->flash ? exif->flash : "??");
+ replace_table_add_key (replace_table, "EXIF_DATE", exif->datetime ? exif->datetime : "??");
+ replace_table_add_key (replace_table, "EXIF_CAMERA_MODEL", exif->camera_model ? exif->camera_model : "??");
+ s1 = g_strdup_printf ("(%s)", exif->focal_length_35mm);
+ replace_table_add_key (replace_table, "EXIF_FOCAL_35", exif->focal_length_35mm ? s1 : "");
+ g_free (s1);
+
+ /* Border style */
+ s1 = item->border_style;
+ if (s1 == NULL)
+ s1 = parent_items->border_style;
+ if (s1 == NULL)
+ s1 = setup->border_style;
+ if (s1 == NULL)
+ s1 = "border_single";
+ replace_table_add_key (replace_table, "IMG_BORDER_STYLE", s1);
+
+ /* Next/Previous links */
+ if (next_item) {
+ s2 = g_path_get_basename ((next_item->path == NULL && next_item->preview) ? next_item->preview : next_item->path);
+ replace_table_add_key_printf (replace_table, "LINK_NEXT", "%s.html", s2);
+ g_free (s2);
+ }
+ else
+ replace_table_add_key (replace_table, "LINK_NEXT", "index.html");
+ if (previous_item) {
+ s2 = g_path_get_basename ((previous_item->path == NULL && previous_item->preview) ? previous_item->preview : previous_item->path);
+ replace_table_add_key_printf (replace_table, "LINK_PREV", "%s.html", s2);
+ g_free (s2);
+ }
+ else
+ replace_table_add_key (replace_table, "LINK_PREV", "index.html");
+
+ /* META tags */
+ override_title_meta = setup->use_title_as_meta && title && (strlen (title) > 0);
+ s1 = g_strdup_printf ("\t<meta name=\"generator\" content=\"Cataract Gallery Generator v%s\" />\n", VERSION);
+ if (setup->meta_author || parent_items->meta_author) {
+ s3 = g_strdup (parent_items->meta_author ? parent_items->meta_author : setup->meta_author);
+ adjust_tags_parameter (&s3);
+ s2 = g_strdup_printf ("%s\t<meta name=\"author\" content=\"%s\" />\n", s1, s3);
+ g_free (s3);
+ g_free (s1);
+ s1 = s2;
+ }
+ if (setup->meta_description || parent_items->meta_description || override_title_meta) {
+ s3 = g_strdup (override_title_meta ? title : (parent_items->meta_description ? parent_items->meta_description : setup->meta_description));
+ adjust_tags_parameter (&s3);
+ s2 = g_strdup_printf ("%s\t<meta name=\"description\" content=\"%s\" />\n", s1, s3);
+ g_free (s3);
+ g_free (s1);
+ s1 = s2;
+ }
+ if ((setup->meta_keywords || parent_items->meta_keywords) && (! override_title_meta)) {
+ s3 = g_strdup (parent_items->meta_keywords ? parent_items->meta_keywords : setup->meta_keywords);
+ adjust_tags_parameter (&s3);
+ s2 = g_strdup_printf ("%s\t<meta name=\"keywords\" content=\"%s\" />\n", s1, s3);
+ g_free (s3);
+ g_free (s1);
+ s1 = s2;
+ }
+ replace_table_add_key (replace_table, "CGG_META_TAGS", s1);
+ g_free (s1);
+
+
+ /* Read through the template and replace placeholders with real data */
+ while (! feof (fin)) {
+ memset (buffer, 0, BUFFER_SIZE);
+ if (! fgets (buffer, BUFFER_SIZE, fin))
+ break;
+
+ /* Block placeholders */
+ if (strstr (buffer, "<!-- $(BEGIN_IMG_FULLSIZE_LINK) -->")) {
+ in_img_fullsize_link = TRUE;
+ continue;
+ }
+ if (strstr (buffer, "<!-- $(END_IMG_FULLSIZE_LINK) -->")) {
+ in_img_fullsize_link = FALSE;
+ if (! image_fullsize) /* write down the block later in this cycle */
+ continue;
+ }
+ if (in_img_fullsize_link) {
+ buf_img_fullsize_link = strncat (buf_img_fullsize_link, buffer, BUFFER_SIZE - strlen (buf_img_fullsize_link) - 2);
+ continue;
+ }
+
+ /* Select apropriate line buffer */
+ if (strstr (buffer, "<!-- $(END_IMG_FULLSIZE_LINK) -->") && image_fullsize) {
+ b = strdup (buf_img_fullsize_link);
+ } else
+ b = strdup (buffer);
+
+ /* Replace all known tags */
+ replace_table_process (&b, replace_table);
+
+ /* Write to file */
+ bb = fputs (b, fout);
+ free (b);
+ if (! bb) {
+ fprintf (stderr, "write_html_image: error writing to file \"%s\": %s\n", dst, strerror (errno));
+ res = FALSE;
+ break;
+ }
+ }
+
+ fclose (fout);
+ fclose (fin);
+ if (title) g_free (title);
+ if (title_desc) g_free (title_desc);
+ free (buffer);
+ free (big_dst);
+ free (orig_dst);
+ free (buf_img_fullsize_link);
+ g_free (imgname);
+ if (preload_imgname)
+ g_free (preload_imgname);
+ free_exif_data (exif);
+ replace_table_free (replace_table);
+ return res;
+}
+
+
+/*
+ * build_tree: generate complete gallery tree based on source xml files
+ *
+ * src_tree = source directory of the items
+ * dst_dir = destination of the generated items
+ * parent_index = parent album to determine our descent in the tree
+ *
+ */
+gboolean
+build_tree (TGallerySetup *setup,
+ const char *src_tree,
+ const char *dst_dir,
+ TAlbum *parent_index,
+ int parent_item_index)
+{
+ char *idx_file;
+ TAlbum *items;
+ TIndexItem *item;
+ char *s1, *s2, *s3;
+ char *thumb_dir;
+ char *img_big_dir;
+ char *img_orig_dir;
+ char *template;
+ char *imgname;
+ gboolean res;
+ int i;
+
+ printf ("*** Entering directory '%s'\n", src_tree);
+ #ifdef __DEBUG_ALL__
+ printf ("setup->real_templates_dir = %s\n", setup->real_templates_dir);
+ #endif
+
+ /* Check access permissions */
+ if (access (src_tree, R_OK)) {
+ fprintf (stderr, "error accessing source directory: %s\n", strerror (errno));
+ return FALSE;
+ }
+ if (access (dst_dir, R_OK | W_OK | X_OK)) {
+ if (mkdir (dst_dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
+ fprintf (stderr, "error creating destination directory: %s\n", strerror (errno));
+ return FALSE;
+ }
+ }
+
+ /* Check the index file */
+ idx_file = g_strconcat (src_tree, "/index.xml", NULL);
+ if (access (idx_file, R_OK)) {
+ fprintf (stderr, "error accessing index file '%s': %s\n", idx_file, strerror (errno));
+ g_free (idx_file);
+ return FALSE;
+ }
+
+ /* Read the index file and fill items array */
+ items = malloc (sizeof (TAlbum));
+ memset (items, 0, sizeof (TAlbum));
+ if (! parse_album_xml (idx_file, items)) {
+ fprintf (stderr, "error reading index file '%s'\n", idx_file);
+ g_free (idx_file);
+ free_album_data (items);
+ return FALSE;
+ }
+ g_free (idx_file);
+ items->parent_index = parent_index;
+ items->parent_item_index = parent_item_index;
+
+
+ /* Copy support files */
+ if (setup->verbose) printf ("Writing '%s' ...", setup->styles);
+ s1 = g_strconcat (setup->real_templates_dir, "/", setup->styles, NULL);
+ s2 = g_strconcat (dst_dir, "/", setup->styles, NULL);
+ copy_file (s1, s2);
+ g_free (s1);
+ g_free (s2);
+ if (setup->verbose) printf (" done.\n");
+
+ if (setup->verbose) printf ("Writing '%s' ...", setup->scripts);
+ s1 = g_strconcat (setup->real_templates_dir, "/", setup->scripts, NULL);
+ s2 = g_strconcat (dst_dir, "/", setup->scripts, NULL);
+ copy_file (s1, s2);
+ g_free (s1);
+ g_free (s2);
+ if (setup->verbose) printf (" done.\n");
+
+
+ /* Prepare target thumbnail directory */
+ thumb_dir = g_strconcat (dst_dir, "/", THUMBNAIL_DIR, NULL);
+ if (access (thumb_dir, R_OK | W_OK | X_OK))
+ if (mkdir (thumb_dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
+ fprintf (stderr, "error making target thumbnail directory: %s\n", strerror (errno));
+ g_free (thumb_dir);
+ free_album_data (items);
+ return FALSE;
+ }
+ g_free (thumb_dir);
+
+ /* Prepare target preview and orig directories */
+ if (items->type == GALLERY_TYPE_ALBUM)
+ {
+ res = TRUE;
+ img_big_dir = g_strconcat (dst_dir, "/", IMG_BIG_DIR, NULL);
+ img_orig_dir = g_strconcat (dst_dir, "/", IMG_ORIG_DIR, NULL);
+ if (access (img_big_dir, R_OK | W_OK | X_OK))
+ if (mkdir (img_big_dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
+ fprintf (stderr, "error making target preview directory: %s\n", strerror (errno));
+ res = FALSE;
+ }
+ if (access (img_orig_dir, R_OK | W_OK | X_OK))
+ if (mkdir (img_orig_dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
+ fprintf (stderr, "error making target full size directory: %s\n", strerror (errno));
+ res = FALSE;
+ }
+ g_free (img_big_dir);
+ g_free (img_orig_dir);
+ if (! res) {
+ free_album_data (items);
+ return FALSE;
+ }
+ }
+
+
+ /* Start generating items */
+ if (items->type == GALLERY_TYPE_INDEX)
+ template = g_strconcat ("/", setup->template_index, NULL);
+ else
+ if (items->type == GALLERY_TYPE_ALBUM)
+ template = g_strconcat ("/", setup->template_album, NULL);
+
+ if (setup->verbose) printf ("Writing 'index.html' ...\n");
+ s1 = g_strconcat (setup->real_templates_dir, template, NULL);
+ s2 = g_strconcat (dst_dir, "/index.html", NULL);
+ res = write_html_album (setup, s1, s2, items);
+ g_free (s1);
+ g_free (s2);
+ g_free (template);
+ if (! res) {
+ fprintf (stderr, "error generating target index file\n");
+ free_album_data (items);
+ return FALSE;
+ }
+ if (setup->verbose) printf (" done.\n");
+
+
+ /* Recurse to sub-albums (in case of album index) */
+ if (items->type == GALLERY_TYPE_INDEX)
+ {
+ if (items->items->len > 0) {
+ for (i = 0; i < items->items->len; i++) {
+ item = g_ptr_array_index (items->items, i);
+ if (item == NULL) {
+ fprintf (stderr, "build_tree: error getting item %d\n", i);
+ continue;
+ }
+ if (item->type == INDEX_ITEM_TYPE_PICTURE) {
+ s1 = g_strconcat (src_tree, "/", item->path, "/", NULL);
+ s2 = g_strconcat (dst_dir, "/", item->path, "/", NULL);
+ build_tree (setup, s1, s2, items, i);
+ g_free (s1);
+ g_free (s2);
+ }
+ }
+ }
+ }
+
+ /* Generate separate image pages (in case of album) */
+ if (items->type == GALLERY_TYPE_ALBUM)
+ {
+ if (items->items->len > 0) {
+ for (i = 0; i < items->items->len; i++) {
+ item = g_ptr_array_index (items->items, i);
+ if (item == NULL) {
+ fprintf (stderr, "build_tree: error getting item %d\n", i);
+ continue;
+ }
+ if (item->type == INDEX_ITEM_TYPE_PICTURE) {
+ imgname = g_path_get_basename ((item->path == NULL && item->preview) ? item->preview : item->path);
+ if (setup->verbose) printf ("Writing '%s.html' ...", imgname);
+ s1 = g_strconcat (setup->real_templates_dir, "/", setup->template_photo, NULL);
+ s2 = g_strconcat (items->base_dir, "/", (item->path == NULL && item->preview) ? item->preview : item->path, NULL);
+ s3 = g_strconcat (dst_dir, "/", imgname, ".html", NULL);
+ res = write_html_image (setup, s1, s2, s3, item, items);
+ g_free (s1);
+ g_free (s2);
+ g_free (s3);
+ g_free (imgname);
+ if (! res ) continue;
+ if (setup->verbose) printf (" done.\n");
+ }
+ }
+ }
+ }
+
+ printf ("*** Leaving directory '%s'\n", src_tree);
+ free_album_data (items);
+
+ return TRUE;
+}
diff --git a/src/generators.h b/src/generators.h
new file mode 100644
index 0000000..c87e8a3
--- /dev/null
+++ b/src/generators.h
@@ -0,0 +1,64 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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 "setup.h"
+#include "items.h"
+
+
+/*
+ * write_html_album: process album and index template files
+ *
+ * template_src = template file of the album/index
+ * dst = save generated file as
+ * items = array of items in the album/index
+ *
+ */
+gboolean write_html_album (TGallerySetup *setup,
+ const char *template_src,
+ const char *dst,
+ TAlbum *items);
+
+/*
+ * write_html_image: process single image template file
+ *
+ * template_src = template file of the album/index
+ * original_img = source image file (original full-size) to get EXIF data from
+ * dst = save generated file as
+ * item = data for the current item
+ * parent_items = array of items in the album, to determine our position and make links to previous/next image
+ *
+ */
+gboolean write_html_image (TGallerySetup *setup,
+ const char *template_src,
+ const char *original_img,
+ const char *dst,
+ TIndexItem *item,
+ TAlbum *parent_items);
+
+/*
+ * build_tree: generate complete gallery tree based on source xml files
+ *
+ * src_tree = source directory of the items
+ * dst_dir = destination of the generated items
+ * parent_index = parent album to determine our descent in the tree
+ *
+ */
+gboolean build_tree (TGallerySetup *setup,
+ const char *src_tree,
+ const char *dst_dir,
+ TAlbum *parent_index);
+
diff --git a/src/items.c b/src/items.c
new file mode 100644
index 0000000..986fd6c
--- /dev/null
+++ b/src/items.c
@@ -0,0 +1,320 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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 <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include "items.h"
+#include "xml-parser.h"
+
+
+
+/*
+ * parse_album_xml: XML parser for gallery index.xml files
+ */
+gboolean
+parse_album_xml (const char *filename, TAlbum *index)
+{
+ TXMLFile *xml;
+ char *gallery_type;
+ int count;
+ int i;
+ char *s, *s2;
+ char *node_name;
+ TIndexItem *item;
+
+ xml = xml_parser_load (filename);
+ if (xml == NULL)
+ return FALSE;
+
+ /* Initialize data struct */
+ if (index == NULL)
+ index = malloc (sizeof (TAlbum));
+ memset (index, 0, sizeof (TAlbum));
+
+ index->base_dir = g_path_get_dirname (filename);
+
+ /* Retrieve gallery type */
+ gallery_type = xml_file_get_node_attribute (xml, "/gallery", "type");
+ #ifdef __DEBUG_ALL__
+ printf("gallery_type = %s\n", gallery_type);
+ #endif
+ if (strcmp (gallery_type, "index") == 0)
+ index->type = GALLERY_TYPE_INDEX;
+ else
+ if (strcmp (gallery_type, "album") == 0)
+ index->type = GALLERY_TYPE_ALBUM;
+ else {
+ printf ("Invalid gallery type (%s)\n", gallery_type);
+ free (index);
+ return FALSE;
+ }
+
+ /* Section General */
+ index->ID = xml_file_get_node_value (xml, "/gallery/general/ID/text()");
+ index->title = xml_file_get_node_value (xml, "/gallery/general/title/text()");
+ index->desc = xml_file_get_node_value (xml, "/gallery/general/description/text()");
+ index->footnote = xml_file_get_node_value (xml, "/gallery/general/footnote/text()");
+
+ index->quality = xml_file_get_node_attribute_long (xml, "/gallery/general/images", "quality", -1);
+ index->landscape_width = xml_file_get_node_attribute_long (xml, "/gallery/general/images", "landscape_w", 0);
+ index->landscape_height = xml_file_get_node_attribute_long (xml, "/gallery/general/images", "landscape_h", 0);
+ index->portrait_width = xml_file_get_node_attribute_long (xml, "/gallery/general/images", "portrait_w", 0);
+ index->portrait_height = xml_file_get_node_attribute_long (xml, "/gallery/general/images", "portrait_h", 0);
+
+ index->border_style = xml_file_get_node_attribute (xml, "/gallery/general/border", "style");
+ index->meta_author = xml_file_get_node_value (xml, "/gallery/general/meta/author/text()");
+ index->meta_description = xml_file_get_node_value (xml, "/gallery/general/meta/description/text()");
+ index->meta_keywords = xml_file_get_node_value (xml, "/gallery/general/meta/keywords/text()");
+
+ index->nofullsize = xml_file_get_node_present (xml, "/gallery/general/nofullsize");
+ index->fullsize = xml_file_get_node_present (xml, "/gallery/general/fullsize");
+
+ /* Section Items */
+ count = xml_file_node_get_children_count (xml, "/gallery/items/*");
+ #ifdef __DEBUG_ALL__
+ printf("parse_album_xml: items count = %d\n", count);
+ #endif
+ index->items = g_ptr_array_new();
+
+ for (i = 0; i < count; i++)
+ {
+ item = malloc (sizeof (TIndexItem));
+ memset (item, 0, sizeof (TIndexItem));
+
+ s = g_strdup_printf ("/gallery/items/*[%d]", i + 1);
+ node_name = xml_file_get_node_name (xml, s);
+ if (! node_name) continue;
+ #ifdef __DEBUG_ALL__
+ printf("parse_album_xml: item[%d] = '%s'\n", i + 1, node_name);
+ #endif
+
+ if (strcmp (node_name, "item") == 0) {
+ item->type = INDEX_ITEM_TYPE_PICTURE;
+ if (index->type == GALLERY_TYPE_INDEX)
+ item->path = xml_file_get_node_attribute (xml, s, "path");
+ else
+ item->path = xml_file_get_node_attribute (xml, s, "src");
+ item->preview = xml_file_get_node_attribute (xml, s, "preview");
+ item->quality = xml_file_get_node_attribute_long (xml, s, "quality", -1);
+ item->width = xml_file_get_node_attribute_long (xml, s, "width", 0);
+ item->height = xml_file_get_node_attribute_long (xml, s, "height", 0);
+ item->border_style = xml_file_get_node_attribute (xml, s, "border");
+ if (index->type == GALLERY_TYPE_ALBUM)
+ item->thumbnail = xml_file_get_node_attribute (xml, s, "thumbnail");
+ g_free (s);
+
+ s = g_strdup_printf ("/gallery/items/*[%d]/title/text()", i + 1);
+ item->title = xml_file_get_node_value (xml, s);
+ g_free (s);
+
+ s = g_strdup_printf ("/gallery/items/*[%d]/title_description/text()", i + 1);
+ item->title_description = xml_file_get_node_value (xml, s);
+ g_free (s);
+
+ if (index->type == GALLERY_TYPE_INDEX) {
+ s = g_strdup_printf ("/gallery/items/*[%d]/thumbnail", i + 1);
+ item->thumbnail = xml_file_get_node_attribute (xml, s, "src");
+ g_free (s);
+ }
+
+ s = g_strdup_printf ("/gallery/items/*[%d]/nofullsize", i + 1);
+ item->force_nofullsize = (xml_file_get_node_present (xml, s) || item->path == NULL);
+ g_free (s);
+
+ s = g_strdup_printf ("/gallery/items/*[%d]/fullsize", i + 1);
+ item->force_fullsize = xml_file_get_node_present (xml, s);
+ g_free (s);
+
+ s = g_strdup_printf ("/gallery/items/*[%d]/hidden", i + 1);
+ item->hidden = (xml_file_get_node_present (xml, s));
+ g_free (s);
+
+ /* Retrieve title and description from linked album if not defined here */
+ if (index->type == GALLERY_TYPE_INDEX &&
+ item->title == NULL && item->title_description == NULL) {
+ s = g_strconcat (index->base_dir, "/", item->path, "/index.xml", NULL);
+ get_album_titles (s, &item->title, &item->title_description, NULL);
+ g_free (s);
+ }
+
+ /* Retrieve thumbnail from linked album if not defined here */
+ if (index->type == GALLERY_TYPE_INDEX && item->thumbnail == NULL) {
+ s = g_strconcat (index->base_dir, "/", item->path, "/index.xml", NULL);
+ s2 = NULL;
+ get_album_titles (s, NULL, NULL, &s2);
+ if (s2) {
+ item->thumbnail = g_strconcat (item->path, "/", s2, NULL);
+ g_free (s2);
+ }
+ g_free (s);
+ }
+
+ if (item->path || item->preview)
+ {
+ g_ptr_array_add (index->items, item);
+ }
+ else
+ {
+ fprintf (stderr, "%s: No image src specified, skipping!\n", filename);
+ free (item);
+ }
+ }
+ else
+ if (strcmp (node_name, "separator") == 0) {
+ item->type = INDEX_ITEM_TYPE_SEPARATOR;
+
+ s = g_strdup_printf ("/gallery/items/*[%d]/text()", i + 1);
+ item->title = xml_file_get_node_value (xml, s);
+ g_free (s);
+
+ g_ptr_array_add (index->items, item);
+ }
+ else {
+ /* Free the item if nobody cares */
+ free (item);
+ }
+ free (node_name);
+ }
+
+ xml_parser_close (xml);
+
+ /* Print the items */
+ #ifdef __DEBUG_ALL__
+ printf ("ID = '%s'\ntitle = '%s'\ndescription = '%s'\n", index->ID, index->title, index->desc);
+ for (i = 0; i < index->items->len; i++) {
+ TIndexItem *item = g_ptr_array_index (index->items, i);
+ printf ("item %d: path = '%s', title = '%s', title_description = '%s', thumbnail = '%s'\n",
+ i, item->path, item->title, item->title_description, item->thumbnail);
+ }
+ #endif
+ return TRUE;
+}
+
+
+/*
+ * free_album_data: free allocated album data
+ */
+void
+free_album_data (TAlbum *album)
+{
+ if (album) {
+ if (album->ID)
+ free (album->ID);
+ if (album->title)
+ free (album->title);
+ if (album->desc)
+ free (album->desc);
+ if (album->footnote)
+ free (album->footnote);
+ if (album->base_dir)
+ free (album->base_dir);
+ if (album->border_style)
+ free (album->border_style);
+ if (album->meta_author)
+ free (album->meta_author);
+ if (album->meta_description)
+ free (album->meta_description);
+ if (album->meta_keywords)
+ free (album->meta_keywords);
+
+ if (album->items) {
+ if (album->items->len > 0) {
+ TIndexItem *item;
+ int i;
+
+ for (i = 0; i < album->items->len; i++) {
+ item = g_ptr_array_index (album->items, i);
+ if (item != NULL) {
+ if (item->path)
+ free (item->path);
+ if (item->title)
+ free (item->title);
+ if (item->title_description)
+ free (item->title_description);
+ if (item->thumbnail)
+ free (item->thumbnail);
+ if (item->preview)
+ free (item->preview);
+ if (item->border_style)
+ free (item->border_style);
+ free (item);
+ }
+ }
+ }
+ g_ptr_array_free (album->items, TRUE);
+ }
+ free (album);
+ album = NULL;
+ }
+}
+
+
+/*
+ * get_gallery_objects_count: retrieve number of items in specified album
+ */
+int
+get_album_objects_count (const char *filename)
+{
+ TXMLFile *xml;
+ int count;
+
+ xml = xml_parser_load (filename);
+ if (xml == NULL)
+ return 0;
+
+ count = xml_file_node_get_children_count (xml, "/gallery/items/item");
+ xml_parser_close (xml);
+
+ #ifdef __DEBUG_ALL__
+ printf ("get_objects_count(%s) = %d\n", filename, count);
+ #endif
+ return count;
+}
+
+/*
+ * get_album_titles: retrieve title, description and first thumbnail from specified album
+ */
+void
+get_album_titles (const char *filename, char **title, char **description, char **thumbnail)
+{
+ TXMLFile *xml;
+
+ xml = xml_parser_load (filename);
+ if (xml == NULL)
+ return;
+
+ if (title)
+ *title = xml_file_get_node_value (xml, "/gallery/general/title/text()");
+ if (description)
+ *description = xml_file_get_node_value (xml, "/gallery/general/description/text()");
+ if (thumbnail) {
+ *thumbnail = xml_file_get_node_attribute (xml, "/gallery/items/item[1]/thumbnail", "src");
+ if (! *thumbnail)
+ *thumbnail = xml_file_get_node_attribute (xml, "/gallery/items/item[1]", "src");
+ if (! *thumbnail)
+ *thumbnail = xml_file_get_node_attribute (xml, "/gallery/items/item[1]", "preview");
+ }
+
+ xml_parser_close (xml);
+}
diff --git a/src/items.h b/src/items.h
new file mode 100644
index 0000000..9fd6822
--- /dev/null
+++ b/src/items.h
@@ -0,0 +1,96 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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.
+ */
+#ifndef __ITEMS_H__
+#define __ITEMS_H__
+
+
+#include <glib.h>
+
+
+typedef enum {
+ GALLERY_TYPE_INDEX = 1 << 0,
+ GALLERY_TYPE_ALBUM = 1 << 1
+} TGalleryType;
+
+typedef enum {
+ INDEX_ITEM_TYPE_PICTURE = 1 << 0,
+ INDEX_ITEM_TYPE_SEPARATOR = 1 << 1
+} TIndexItemType;
+
+typedef struct {
+ TGalleryType type;
+ char *ID;
+ char *title;
+ char *desc;
+ char *footnote;
+ GPtrArray *items;
+ char *base_dir;
+ void *parent_index; /* pointer to the parent TAlbum structure */
+ int parent_item_index; /* item index in the parent album */
+ int quality;
+ unsigned long landscape_width;
+ unsigned long landscape_height;
+ unsigned long portrait_width;
+ unsigned long portrait_height;
+ char *border_style;
+ char *meta_author;
+ char *meta_description;
+ char *meta_keywords;
+ gboolean nofullsize;
+ gboolean fullsize;
+} TAlbum;
+
+typedef struct {
+ char *path;
+ char *title;
+ char *title_description;
+ char *thumbnail;
+ char *preview;
+ int quality;
+ unsigned long width;
+ unsigned long height;
+ gboolean force_nofullsize;
+ gboolean force_fullsize;
+ char *border_style;
+ TIndexItemType type;
+ gboolean hidden;
+} TIndexItem;
+
+
+
+/*
+ * parse_album_xml: XML parser for gallery index.xml files
+ */
+gboolean parse_album_xml (const char *filename, TAlbum *index);
+
+/*
+ * free_album_data: free allocated album data
+ */
+void free_album_data (TAlbum *index);
+
+/*
+ * get_album_objects_count: retrieve number of items in specified album
+ */
+int get_album_objects_count (const char *filename);
+
+/*
+ * get_album_titles: retrieve title, description and first thumbnail from specified album
+ */
+void get_album_titles (const char *filename, char **title, char **description, char **thumbnail);
+
+#endif /* __ITEMS_H__ */
diff --git a/src/jpeg-utils.cpp b/src/jpeg-utils.cpp
new file mode 100644
index 0000000..501f7ea
--- /dev/null
+++ b/src/jpeg-utils.cpp
@@ -0,0 +1,429 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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 <exiv2/image.hpp>
+#include <exiv2/exif.hpp>
+
+#include <wand/magick-wand.h>
+
+#include "jpeg-utils.h"
+
+
+
+/*
+ * get_exif: retrieve EXIF informations from a JPEG image
+ */
+int
+get_exif (const char *filename, TExifData **exif_data)
+{
+ TExifData *data;
+
+ data = (TExifData*) malloc (sizeof (TExifData));
+ memset (data, 0, sizeof (TExifData));
+ *exif_data = data;
+
+ try
+ {
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filename);
+ g_assert (image.get() != 0);
+ image->readMetadata();
+
+ Exiv2::ExifData &exifData = image->exifData();
+ if (! exifData.empty()) {
+ /* EXIF::Aperture */
+ try {
+ float val = exifData["Exif.Photo.FNumber"].toFloat();
+ if (val >= 0)
+ data->aperture = g_strdup_printf ("f/%.1f", val);
+ } catch (...) { }
+
+ /* EXIF::Camera model */
+ try {
+ const char *val = exifData["Exif.Image.Model"].toString().c_str();
+ if (val && strlen(val) > 0)
+ data->camera_model = strdup (val);
+ } catch (...) { }
+
+ /* EXIF::DateTime */
+ try {
+ const char *val = NULL;
+ try {
+ val = exifData["Exif.Photo.DateTimeOriginal"].toString().c_str();
+ } catch (...) { }
+ if ((! val) || (strlen(val) == 0))
+ try {
+ val = exifData["Exif.Image.DateTime"].toString().c_str();
+ } catch (...) { }
+
+ if (val && strlen(val) > 0) {
+ static struct tm tt;
+ static char conv[1024];
+
+ if (sscanf (val, "%d:%d:%d %d:%d:%d", &tt.tm_year, &tt.tm_mon, &tt.tm_mday, &tt.tm_hour, &tt.tm_min, &tt.tm_sec) == 6)
+ {
+ tt.tm_year -= 1900;
+ tt.tm_mon--;
+ if (strftime (&conv[0], sizeof(conv), "%c", &tt))
+ data->datetime = strdup (&conv[0]);
+ }
+ }
+ } catch (...) { }
+
+ /* EXIF::Shutter speed */
+ try {
+ float val = exifData["Exif.Photo.ExposureTime"].toFloat();
+ if (val > 0) {
+ if (val < 0.5)
+ data->exposure = g_strdup_printf ("1/%.0f s", 1/val);
+ else
+ data->exposure = g_strdup_printf ("%.1f s", val);
+ }
+ } catch (...) { }
+
+ /* EXIF::Flash */
+ try {
+ long int val = exifData["Exif.Photo.Flash"].toLong();
+ if ((val > 0) && ((val & 1) == 1))
+ data->flash = strdup ((char *) "Flash fired");
+ else
+ data->flash = strdup ((char *) "--");
+ } catch (...) { }
+
+ /* EXIF::Focal length */
+ try {
+ float val = exifData["Exif.Photo.FocalLength"].toFloat();
+ if (val >= 0)
+ data->focal_length = g_strdup_printf ("%.0f mm", val);
+ } catch (...) { }
+
+ /* EXIF::ISO */
+ try {
+ long int val = exifData["Exif.Photo.ISOSpeedRatings"].toLong();
+ if (val > 0)
+ data->iso = g_strdup_printf ("%ld", val);
+ } catch (...) { }
+
+ /* EXIF::Software */
+ try {
+ data->exif_software = strdup (exifData["Exif.Image.Software"].toString().c_str());
+ } catch (...) { }
+
+ /* EXIF::Image description */
+ try {
+ data->exif_imgdesc = strdup (exifData["Exif.Image.ImageDescription"].toString().c_str());
+ } catch (...) { }
+
+ /* EXIF::Artist */
+ try {
+ data->exif_artist = strdup (exifData["Exif.Image.Artist"].toString().c_str());
+ } catch (...) { }
+
+ /* EXIF::Copyright */
+ try {
+ data->exif_copyright = strdup (exifData["Exif.Image.Copyright"].toString().c_str());
+ } catch (...) { }
+
+ /* EXIF::User comment */
+ try {
+ data->exif_usercomment = strdup (exifData["Exif.Photo.UserComment"].toString().c_str());
+ } catch (...) { }
+ }
+
+ Exiv2::IptcData &iptcData = image->iptcData();
+ if (! iptcData.empty()) {
+ /* IPTC::Object name */
+ try {
+ data->iptc_objectname = strdup (iptcData["Iptc.Application2.ObjectName"].toString().c_str());
+ } catch (...) { }
+
+ /* IPTC::Copyright */
+ try {
+ data->iptc_copyright = strdup (iptcData["Iptc.Application2.Copyright"].toString().c_str());
+ } catch (...) { }
+
+ /* IPTC::Credit */
+ try {
+ data->iptc_credit = strdup (iptcData["Iptc.Application2.Credit"].toString().c_str());
+ } catch (...) { }
+
+ /* IPTC::Caption */
+ try {
+ data->iptc_caption = strdup (iptcData["Iptc.Application2.Caption"].toString().c_str());
+ } catch (...) { }
+
+ /* IPTC::Author */
+ try {
+ data->iptc_author = strdup (iptcData["Iptc.Application2.Byline"].toString().c_str());
+ } catch (...) { }
+ }
+
+ /* JPEG::Comment */
+ try {
+ data->jpeg_comment = strdup (image->comment().c_str());
+ } catch (...) { }
+ }
+ catch (Exiv2::AnyError& e)
+ {
+ fprintf (stderr, "get_exif: Caught Exiv2 exception: '%s'\n", e.what());
+ return -1;
+ }
+
+#ifdef __DEBUG_ALL__
+ printf("EXIF_TAG_DATE_TIME = '%s'\n", data->datetime);
+ printf("EXIF_TAG_MODEL = '%s'\n", data->camera_model);
+ printf("EXIF_TAG_ISO_SPEED_RATINGS = '%s'\n", data->iso);
+ printf("EXIF_TAG_FOCAL_LENGTH = '%s'\n", data->focal_length);
+ printf("EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM = '%s'\n", data->focal_length_35mm);
+ printf("EXIF_TAG_FNUMBER = '%s'\n", data->aperture);
+ printf("EXIF_TAG_EXPOSURE_TIME = '%s'\n", data->exposure);
+ printf("EXIF_TAG_FLASH = '%s'\n", data->flash);
+ printf("Exif.Image.Software = '%s'\n", data->exif_software);
+ printf("Exif.Image.ImageDescription = '%s'\n", data->exif_imgdesc);
+ printf("Exif.Image.Artist = '%s'\n", data->exif_artist);
+ printf("Exif.Image.Copyright = '%s'\n", data->exif_copyright);
+ printf("Exif.Photo.UserComment = '%s'\n", data->exif_usercomment);
+ printf("Iptc.Application2.ObjectName = '%s'\n", data->iptc_objectname);
+ printf("Iptc.Application2.Copyright = '%s'\n", data->iptc_copyright);
+ printf("Iptc.Application2.Credit = '%s'\n", data->iptc_credit);
+ printf("Iptc.Application2.Caption = '%s'\n", data->iptc_caption);
+ printf("Iptc.Application2.Byline = '%s'\n", data->iptc_author);
+ printf("JPEG comment = '%s'\n", data->jpeg_comment);
+#endif
+
+ return 0;
+}
+
+/*
+ * free_exif_struct: free allocated structure
+ */
+void
+free_exif_data (TExifData *data)
+{
+ if (data) {
+ if (data->aperture)
+ free (data->aperture);
+ if (data->camera_model)
+ free (data->camera_model);
+ if (data->datetime)
+ free (data->datetime);
+ if (data->exposure)
+ free (data->exposure);
+ if (data->flash)
+ free (data->flash);
+ if (data->focal_length)
+ free (data->focal_length);
+ if (data->focal_length_35mm)
+ free (data->focal_length_35mm);
+ if (data->iso)
+ free (data->iso);
+
+ if (data->exif_software)
+ free (data->exif_software);
+ if (data->exif_imgdesc)
+ free (data->exif_imgdesc);
+ if (data->exif_artist)
+ free (data->exif_artist);
+ if (data->exif_copyright)
+ free (data->exif_copyright);
+ if (data->exif_usercomment)
+ free (data->exif_usercomment);
+
+ if (data->iptc_objectname)
+ free (data->iptc_objectname);
+ if (data->iptc_copyright)
+ free (data->iptc_copyright);
+ if (data->iptc_credit)
+ free (data->iptc_credit);
+ if (data->iptc_caption)
+ free (data->iptc_caption);
+ if (data->iptc_author)
+ free (data->iptc_author);
+
+ if (data->jpeg_comment)
+ free (data->jpeg_comment);
+
+ free (data);
+ data = NULL;
+ }
+}
+
+
+/*
+ * resize_image: resize image pointed by src and save result to dst
+ */
+gboolean
+resize_image (const char *src, const char *dst,
+ int size_x, int size_y,
+ int quality,
+ gboolean thumbnail)
+{
+ #define ThrowWandException(wand) \
+ { \
+ char *description; \
+ ExceptionType severity; \
+ \
+ description = MagickGetException (wand, &severity); \
+ (void) fprintf (stderr, "Error converting image: %s %s %ld %s\n", GetMagickModule(), description); \
+ description = (char*) MagickRelinquishMemory (description); \
+ return FALSE; \
+ }
+
+ MagickBooleanType status;
+ MagickWand *magick_wand;
+
+ /* Read an image. */
+ MagickWandGenesis();
+ magick_wand = NewMagickWand();
+ status = MagickReadImage (magick_wand, src);
+ if (status == MagickFalse)
+ ThrowWandException (magick_wand);
+ if (thumbnail)
+ MagickThumbnailImage (magick_wand, size_x, size_y);
+ else
+ MagickResizeImage (magick_wand, size_x, size_y, LanczosFilter, 1.0);
+ MagickSetImageCompressionQuality (magick_wand, quality);
+
+ /* Write the image and destroy it. */
+ status = MagickWriteImage (magick_wand, dst);
+ if (status == MagickFalse)
+ ThrowWandException (magick_wand);
+ magick_wand = DestroyMagickWand (magick_wand);
+ MagickWandTerminus();
+
+ return TRUE;
+}
+
+
+/*
+ * get_image_sizes: retrieve image dimensions
+ */
+void
+get_image_sizes (const char *img,
+ unsigned long *width, unsigned long *height)
+{
+ #define xThrowWandException(wand) \
+ { \
+ char *description; \
+ ExceptionType severity; \
+ \
+ description = MagickGetException (wand, &severity); \
+ (void) fprintf (stderr, "Error reading image info: %s %s %ld %s\n", GetMagickModule(), description); \
+ description = (char*) MagickRelinquishMemory(description); \
+ return; \
+ }
+
+ MagickBooleanType status;
+ MagickWand *magick_wand;
+
+ *width = -1;
+ *height = -1;
+
+ /* Read an image. */
+ MagickWandGenesis();
+ magick_wand = NewMagickWand();
+ status = MagickPingImage (magick_wand, img);
+ if (status == MagickFalse)
+ xThrowWandException (magick_wand);
+ *width = MagickGetImageWidth (magick_wand);
+ *height = MagickGetImageHeight (magick_wand);
+
+ magick_wand = DestroyMagickWand (magick_wand);
+ MagickWandTerminus();
+}
+
+
+/*
+ * calculate_sizes: calculate maximal image sizes within specified limits keeping aspect ratio
+ */
+void
+calculate_sizes (const unsigned long max_width, const unsigned long max_height,
+ unsigned long *width, unsigned long *height)
+{
+ if ((max_width > *width) && (max_height > *height))
+ return;
+
+ double max_ratio = (double) max_width / (double) max_height;
+ double real_ratio = (double) *width / (double) *height;
+
+ if ((*width > *height) && (max_ratio <= real_ratio))
+ {
+ *height = (unsigned long) (max_width / real_ratio);
+ *width = max_width;
+ }
+ else
+ {
+ *width = (unsigned long) (max_height * real_ratio);
+ *height = max_height;
+ }
+}
+
+
+/*
+ * modify_exif: - strip thumbnail stored in EXIF table
+ * - add copyright to Exif::Image::Copyright and Iptc::Application2::Copyright
+ */
+void
+modify_exif (const char *filename, gboolean strip_thumbnail, const char *add_copyright)
+{
+ bool was_modified = false;
+
+ if ((! strip_thumbnail) && (add_copyright == NULL))
+ return;
+
+ try
+ {
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filename);
+ g_assert (image.get() != 0);
+
+ image->readMetadata();
+ Exiv2::ExifData &exifData = image->exifData();
+ if (add_copyright) {
+ exifData["Exif.Image.Copyright"] = add_copyright;
+ image->iptcData()["Iptc.Application2.Copyright"] = add_copyright;
+ was_modified = true;
+ }
+
+ if (strip_thumbnail && (! exifData.empty())) {
+#ifdef HAVE_EXIFTHUMB
+ Exiv2::ExifThumb exifThumb(image->exifData());
+ std::string thumbExt = exifThumb.extension();
+#else
+ std::string thumbExt = exifData.thumbnailExtension();
+#endif
+
+ if (! thumbExt.empty()) {
+#ifdef HAVE_EXIFTHUMB
+ exifThumb.erase();
+#else
+ exifData.eraseThumbnail();
+#endif
+ was_modified = true;
+ }
+ }
+
+ if (was_modified)
+ image->writeMetadata();
+ }
+ catch (Exiv2::AnyError& e)
+ {
+ fprintf (stderr, "modify_exif: Caught Exiv2 exception: '%s'\n", e.what());
+ }
+}
diff --git a/src/jpeg-utils.h b/src/jpeg-utils.h
new file mode 100644
index 0000000..9b8919f
--- /dev/null
+++ b/src/jpeg-utils.h
@@ -0,0 +1,94 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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.
+ */
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#include <glib.h>
+
+
+/* TODO: we want to have numerical values here at some point in the future */
+typedef struct {
+ char *datetime;
+ char *camera_model;
+ char *iso;
+ char *focal_length;
+ char *focal_length_35mm;
+ char *aperture;
+ char *exposure;
+ char *flash;
+
+ char *exif_software;
+ char *exif_imgdesc;
+ char *exif_artist;
+ char *exif_copyright;
+ char *exif_usercomment;
+
+ char *iptc_objectname;
+ char *iptc_copyright;
+ char *iptc_credit;
+ char *iptc_caption;
+ char *iptc_author;
+
+ char *jpeg_comment;
+} TExifData;
+
+
+/*
+ * get_exif: retrieve EXIF info from a JPEG image
+ */
+int get_exif (const char *filename, TExifData **exif_data);
+
+/*
+ * free_exif_struct: free allocated structure
+ */
+void free_exif_data (TExifData *data);
+
+
+/*
+ * resize_image: resize image pointed by src and save result to dst
+ * - setting thumbnail flag will remove all profiles and optimize for size
+ */
+gboolean resize_image (const char *src, const char *dst,
+ int size_x, int size_y,
+ int quality,
+ gboolean thumbnail);
+
+/*
+ * get_image_sizes: retrieve image dimensions
+ */
+void get_image_sizes (const char *img,
+ unsigned long *width, unsigned long *height);
+
+/*
+ * calculate_sizes: calculate maximal image sizes within specified limits keeping aspect ratio
+ */
+void calculate_sizes (const unsigned long max_width, const unsigned long max_height,
+ unsigned long *width, unsigned long *height);
+
+/*
+ * modify_exif: - strip thumbnail stored in EXIF table
+ * - add copyright to Exif::Image::Copyright and Iptc::Application2::Copyright
+ */
+void modify_exif (const char *filename, gboolean strip_thumbnail, const char *add_copyright);
+
+
+#ifdef __cplusplus
+ }
+#endif
diff --git a/src/setup.c b/src/setup.c
new file mode 100644
index 0000000..aafd218
--- /dev/null
+++ b/src/setup.c
@@ -0,0 +1,268 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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 <unistd.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <glib.h>
+
+#include <config.h>
+
+#include "cgg-config.h"
+#include "gallery-utils.h"
+#include "setup.h"
+#include "xml-parser.h"
+
+
+
+/*
+ * find_setup_xml: try to find setup.xml in standard paths
+ */
+gboolean
+find_setup_xml (TGallerySetup *setup)
+{
+ #define BUFFER_SIZE 65536
+
+ char *pth;
+ char *cwd;
+ gboolean b;
+
+ cwd = malloc (BUFFER_SIZE);
+ cwd = getcwd (cwd, BUFFER_SIZE);
+ pth = g_strconcat (cwd, "/", SETUP_XML, NULL);
+ free (cwd);
+
+ b = parse_setup_xml (pth, setup);
+ g_free (pth);
+ if (b) return TRUE;
+
+ pth = g_strconcat (getenv ("HOME"), "/.cgg/", SETUP_XML, NULL);
+ b = parse_setup_xml (pth, setup);
+ g_free (pth);
+ if (b) return TRUE;
+
+ pth = g_strconcat("/usr/share/cgg/", SETUP_XML, NULL);
+ b = parse_setup_xml (pth, setup);
+ g_free (pth);
+ if (b) return TRUE;
+
+ pth = g_strconcat("/usr/local/share/cgg/", SETUP_XML, NULL);
+ b = parse_setup_xml (pth, setup);
+ g_free (pth);
+ return b;
+}
+
+
+/*
+ * parse_setup_xml: XML parser for setup.xml file
+ */
+gboolean
+parse_setup_xml (const char *filename, TGallerySetup *setup)
+{
+ TXMLFile *xml;
+ char *s;
+
+ xml = xml_parser_load (filename);
+ if (xml == NULL)
+ return FALSE;
+
+ /* initialize data struct */
+ if (setup == NULL)
+ return FALSE;
+ memset (setup, 0, sizeof (TGallerySetup));
+ setup->setup_xml_path = strdup (filename);
+
+ setup->templates_path = xml_file_get_node_value (xml, "/gallery_setup/templates/path/text()");
+ setup->template_index = xml_file_get_node_value (xml, "/gallery_setup/templates/index/text()");
+ setup->template_album = xml_file_get_node_value (xml, "/gallery_setup/templates/album/text()");
+ setup->template_photo = xml_file_get_node_value (xml, "/gallery_setup/templates/photo/text()");
+ setup->styles = xml_file_get_node_value (xml, "/gallery_setup/templates/styles/text()");
+ setup->scripts = xml_file_get_node_value (xml, "/gallery_setup/templates/scripts/text()");
+ setup->thumbnail_landscape_width = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/thumbnail", "landscape_w", 0);
+ setup->thumbnail_landscape_height = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/thumbnail", "landscape_h", 0);
+ setup->thumbnail_portrait_width = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/thumbnail", "portrait_w", 0);
+ setup->thumbnail_portrait_height = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/thumbnail", "portrait_h", 0);
+ setup->thumbnail_quality = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/thumbnail", "quality", -1);
+ setup->preview_landscape_width = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/preview", "landscape_w", 0);
+ setup->preview_landscape_height = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/preview", "landscape_h", 0);
+ setup->preview_portrait_width = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/preview", "portrait_w", 0);
+ setup->preview_portrait_height = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/preview", "portrait_h", 0);
+ setup->preview_quality = xml_file_get_node_attribute_long (xml, "/gallery_setup/images/preview", "quality", -1);
+ setup->footer = xml_file_get_node_value (xml, "/gallery_setup/footer/text()");
+ setup->border_style = xml_file_get_node_attribute (xml, "/gallery_setup/images/border", "style");
+ setup->meta_author = xml_file_get_node_value (xml, "/gallery_setup/meta/author/text()");
+ setup->meta_description = xml_file_get_node_value (xml, "/gallery_setup/meta/description/text()");
+ setup->meta_keywords = xml_file_get_node_value (xml, "/gallery_setup/meta/keywords/text()");
+
+ s = xml_file_get_node_attribute (xml, "/gallery_setup/images/preload", "value");
+ setup->preload = s ? strcasecmp (s, "yes") == 0 : TRUE; /* default to TRUE */
+ if (s) g_free (s);
+ s = xml_file_get_node_attribute (xml, "/gallery_setup/images/use_iptc_exif", "value");
+ setup->use_iptc_exif = s ? strcasecmp (s, "yes") == 0 : FALSE; /* default to FALSE */
+ if (s) g_free (s);
+ s = xml_file_get_node_attribute (xml, "/gallery_setup/images/erase_embed_thumbnail", "value");
+ setup->erase_exif_thumbnail = s ? strcasecmp (s, "yes") == 0 : FALSE; /* default to FALSE */
+ if (s) g_free (s);
+ s = xml_file_get_node_attribute (xml, "/gallery_setup/meta/use_title_as_meta", "value");
+ setup->use_title_as_meta = s ? strcasecmp (s, "yes") == 0 : TRUE; /* default to TRUE */
+ if (s) g_free (s);
+
+ setup->site_title = xml_file_get_node_attribute (xml, "/gallery_setup/meta/site", "title");
+ setup->add_copyright = xml_file_get_node_value (xml, "/gallery_setup/meta/add_copyright/text()");
+ s = xml_file_get_node_attribute (xml, "/gallery_setup/navigation/use_inpage_links", "value");
+ setup->use_inpage_links = s ? strcasecmp (s, "yes") == 0 : TRUE; /* default to TRUE */
+ if (s) g_free (s);
+
+ setup->nofullsize = xml_file_get_node_present (xml, "/gallery_setup/images/nofullsize");
+
+ xml_parser_close (xml);
+
+ #ifdef __DEBUG_ALL__
+ printf("setup: templates_path = '%s'\n", setup->templates_path);
+ printf("setup: template_index = '%s'\n", setup->template_index);
+ printf("setup: template_album = '%s'\n", setup->template_album);
+ printf("setup: template_photo = '%s'\n", setup->template_photo);
+ printf("setup: styles = '%s'\n", setup->styles);
+ printf("setup: scripts = '%s'\n", setup->scripts);
+ printf("setup: thumbnail_quality = %d\n", setup->thumbnail_quality);
+ printf("setup: thumbnail_landscape_width = %ld\n", setup->thumbnail_landscape_width);
+ printf("setup: thumbnail_landscape_height = %ld\n", setup->thumbnail_landscape_height);
+ printf("setup: thumbnail_portrait_width = %ld\n", setup->thumbnail_portrait_width);
+ printf("setup: thumbnail_portrait_height = %ld\n", setup->thumbnail_portrait_height);
+ printf("setup: preview_quality = %d\n", setup->preview_quality);
+ printf("setup: preview_landscape_width = %ld\n", setup->preview_landscape_width);
+ printf("setup: preview_landscape_height = %ld\n", setup->preview_landscape_height);
+ printf("setup: preview_portrait_width = %ld\n", setup->preview_portrait_width);
+ printf("setup: preview_portrait_height = %ld\n", setup->preview_portrait_height);
+ printf("setup: footer = '%s'\n", setup->footer);
+ printf("setup: border_style = '%s'\n", setup->border_style);
+ printf("setup: meta_author = '%s'\n", setup->meta_author);
+ printf("setup: meta_description = '%s'\n", setup->meta_description);
+ printf("setup: meta_keywords = '%s'\n", setup->meta_keywords);
+ printf("setup: preload = %d\n", setup->preload);
+ printf("setup: use_iptc_exif = %d\n", setup->use_iptc_exif);
+ printf("setup: erase_exif_thumbnail = %d\n", setup->erase_exif_thumbnail);
+ #endif
+
+ return TRUE;
+}
+
+
+
+int
+test_tmpl_access (const char *dir, const char *path)
+{
+ char *s;
+ int b;
+
+ s = g_strconcat (dir, "/", path, NULL);
+ b = access (s, R_OK);
+ g_free (s);
+ return b;
+}
+
+int
+test_tmpl_files (const char *dir, TGallerySetup *setup)
+{
+ return test_tmpl_access (dir, setup->template_album) | test_tmpl_access (dir, setup->template_photo) |
+ test_tmpl_access (dir, setup->template_index) | test_tmpl_access (dir, setup->styles) |
+ test_tmpl_access (dir, setup->scripts);
+}
+
+/*
+ * find_templates_directory: absolute/relative path checks, trying to find templates directory
+ * - returned string should be freed
+ */
+char *
+find_templates_directory (TGallerySetup *setup)
+{
+ char *base_dir;
+ char *pth;
+
+ if (IS_DIR_SEP (*setup->templates_path))
+ {
+ #ifdef __DEBUG_ALL__
+ printf("Warning: using absolute paths to templates\n");
+ #endif
+
+ if (! test_tmpl_files (setup->templates_path, setup))
+ return strdup (setup->templates_path);
+ }
+ else
+ {
+ base_dir = g_path_get_dirname (setup->setup_xml_path);
+ pth = g_strconcat (base_dir, "/", setup->templates_path, NULL);
+ g_free (base_dir);
+
+ #ifdef __DEBUG_ALL__
+ printf("Warning: using relative paths to templates\n");
+ #endif
+
+ if (! test_tmpl_files (pth, setup))
+ return pth;
+ }
+
+ fprintf (stderr, "Couldn't find proper templates directory (tried '%s')\n", setup->templates_path);
+ return NULL;
+}
+
+
+/*
+ * free_setup_data: free allocated setup data
+ */
+void
+free_setup_data (TGallerySetup *setup)
+{
+ if (setup) {
+ if (setup->real_templates_dir)
+ free (setup->real_templates_dir);
+ if (setup->setup_xml_path)
+ free (setup->setup_xml_path);
+ if (setup->templates_path)
+ free (setup->templates_path);
+ if (setup->template_index)
+ free (setup->template_index);
+ if (setup->template_album)
+ free (setup->template_album);
+ if (setup->template_photo)
+ free (setup->template_photo);
+ if (setup->styles)
+ free (setup->styles);
+ if (setup->scripts)
+ free (setup->scripts);
+ if (setup->footer)
+ free (setup->footer);
+ if (setup->meta_author)
+ free (setup->meta_author);
+ if (setup->border_style)
+ free (setup->border_style);
+ if (setup->site_title)
+ free (setup->site_title);
+ if (setup->add_copyright)
+ free (setup->add_copyright);
+ free (setup);
+ setup = NULL;
+ }
+}
diff --git a/src/setup.h b/src/setup.h
new file mode 100644
index 0000000..eea4b17
--- /dev/null
+++ b/src/setup.h
@@ -0,0 +1,92 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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.
+ */
+#ifndef __SETUP_H__
+#define __SETUP_H__
+
+
+#include <glib.h>
+
+
+typedef struct {
+ gboolean verbose;
+ char *real_templates_dir;
+
+ char *setup_xml_path;
+
+ char *templates_path;
+ char *template_index;
+ char *template_album;
+ char *template_photo;
+ char *styles;
+ char *scripts;
+
+ char *footer;
+ char *meta_author;
+ char *meta_description;
+ char *meta_keywords;
+ gboolean use_title_as_meta;
+
+ int thumbnail_quality;
+ unsigned long thumbnail_landscape_width;
+ unsigned long thumbnail_landscape_height;
+ unsigned long thumbnail_portrait_width;
+ unsigned long thumbnail_portrait_height;
+
+ int preview_quality;
+ unsigned long preview_landscape_width;
+ unsigned long preview_landscape_height;
+ unsigned long preview_portrait_width;
+ unsigned long preview_portrait_height;
+
+ char *border_style;
+ gboolean nofullsize;
+
+ gboolean preload;
+ gboolean use_iptc_exif;
+ gboolean erase_exif_thumbnail;
+
+ char *site_title;
+ char *add_copyright;
+ gboolean use_inpage_links;
+} TGallerySetup;
+
+
+
+/*
+ * find_setup_xml: try to find setup.xml in standard paths
+ */
+gboolean find_setup_xml (TGallerySetup *setup);
+
+/*
+ * parse_setup_xml: XML parser for setup.xml file
+ */
+gboolean parse_setup_xml (const char *filename, TGallerySetup *setup);
+
+/*
+ * free_setup_data: free allocated setup data
+ */
+void free_setup_data (TGallerySetup *setup);
+
+/*
+ * find_templates_directory: absolute/relative path checks, trying to find templates directory
+ * - returned string should be freed
+ */
+char *find_templates_directory (TGallerySetup *setup);
+
+
+#endif /* __SETUP_H__ */
diff --git a/src/xml-parser.c b/src/xml-parser.c
new file mode 100644
index 0000000..eb6c86f
--- /dev/null
+++ b/src/xml-parser.c
@@ -0,0 +1,261 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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 <unistd.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <glib.h>
+
+#include "xml-parser.h"
+
+
+
+/*
+ * xml_parser_load: initialize and load the XML document
+ */
+TXMLFile *
+xml_parser_load (const char *filename)
+{
+ TXMLFile *file;
+
+ file = malloc (sizeof (TXMLFile));
+ memset (file, 0, sizeof (TXMLFile));
+
+ /* Load XML document */
+ file->doc = xmlParseFile (filename);
+ if (file->doc == NULL) {
+ fprintf (stderr, "Error: unable to parse file \"%s\"\n", filename);
+ free (file);
+ return NULL;
+ }
+
+ /* Create xpath evaluation context */
+ file->xpathCtx = xmlXPathNewContext (file->doc);
+ if (file->xpathCtx == NULL) {
+ fprintf (stderr, "Error: unable to create new XPath context\n");
+ xmlFreeDoc (file->doc);
+ free (file);
+ return FALSE;
+ }
+
+ return file;
+}
+
+
+/*
+ * xml_parser_close: close the XML document parser
+ */
+void
+xml_parser_close (TXMLFile *file)
+{
+ if (file)
+ {
+ xmlXPathFreeContext (file->xpathCtx);
+ xmlFreeDoc (file->doc);
+ free (file);
+ file = NULL;
+ }
+}
+
+/*
+ * xml_file_get_node_name: retrieve name of the XPath node
+ */
+char *
+xml_file_get_node_name (TXMLFile *file, const char *x_path)
+{
+ xmlXPathObjectPtr xpathObj;
+ xmlNodePtr cur;
+ char *attrv;
+
+ if ((! file) || (! x_path))
+ return NULL;
+
+ /* Evaluate xpath expression */
+ xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx);
+ if (xpathObj == NULL) {
+ fprintf (stderr, "Error: unable to evaluate xpath expression \"%s\"\n", x_path);
+ return NULL;
+ }
+
+ attrv = NULL;
+ if ((xpathObj->nodesetval) && (xpathObj->nodesetval->nodeNr > 0)) {
+ cur = xpathObj->nodesetval->nodeTab[0];
+ if (cur->name)
+ attrv = strdup ((const char *) cur->name);
+ }
+
+ xmlXPathFreeObject (xpathObj);
+ return attrv;
+}
+
+
+/*
+ * xml_file_get_node_value: retrieve string value from XPath node
+ * - multiple matched nodes will be concatenated into one string
+ * - otherwise please use [0], [1] etc. quantificators
+ */
+char *
+xml_file_get_node_value (TXMLFile *file, const char *x_path)
+{
+ xmlXPathObjectPtr xpathObj;
+ xmlNodePtr cur;
+ char *val, *valx;
+ int i;
+
+ if ((! file) || (! x_path))
+ return NULL;
+
+ /* Evaluate xpath expression */
+ xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx);
+ if (xpathObj == NULL) {
+ fprintf (stderr, "Error: unable to evaluate xpath expression \"%s\"\n", x_path);
+ return NULL;
+ }
+
+ val = NULL;
+ if ((xpathObj->nodesetval) && (xpathObj->nodesetval->nodeNr > 0)) {
+ #ifdef __DEBUG_ALL__
+ printf("Result (%d nodes):\n", xpathObj->nodesetval->nodeNr);
+ #endif
+
+ for (i = 0; i < xpathObj->nodesetval->nodeNr; i++)
+ {
+ cur = xpathObj->nodesetval->nodeTab[i];
+ #ifdef __DEBUG_ALL__
+ printf(" XPATH matched: element node \"%s[%d]\", value = '%s'\n", cur->name, i, cur->content);
+ #endif
+
+ if (cur->content)
+ {
+ if (val == NULL)
+ {
+ val = g_strdup ((char *) cur->content);
+ }
+ else
+ {
+ valx = g_strconcat (val, (char *) cur->content, NULL);
+ g_free (val);
+ val = valx;
+ }
+ }
+ }
+ }
+
+ xmlXPathFreeObject (xpathObj);
+ return val;
+}
+
+
+/*
+ * xml_file_get_node_attribute: retrieve attribute value from XPath node
+ */
+char *
+xml_file_get_node_attribute (TXMLFile *file, const char *x_path, const char *attr)
+{
+ xmlXPathObjectPtr xpathObj;
+ xmlNodePtr cur;
+ xmlChar *attrvx;
+ char *attrv;
+
+ if ((! file) || (! x_path))
+ return NULL;
+
+ /* Evaluate xpath expression */
+ xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx);
+ if (xpathObj == NULL) {
+ fprintf (stderr, "Error: unable to evaluate xpath expression \"%s\"\n", x_path);
+ return NULL;
+ }
+
+ attrv = NULL;
+ if ((xpathObj->nodesetval) && (xpathObj->nodesetval->nodeNr > 0)) {
+ cur = xpathObj->nodesetval->nodeTab[0];
+ attrvx = xmlGetProp (cur, (const xmlChar *) attr);
+ if (attrvx) {
+ attrv = strdup ((char*) attrvx);
+ xmlFree (attrvx);
+ }
+
+ #ifdef __DEBUG_ALL__
+ printf("Result (%d nodes):\n", xpathObj->nodesetval->nodeNr);
+ printf(" XPATH matched: element node \"%s\", value = '%s', property value = '%s'\n", cur->name, cur->content, attrv);
+ #endif
+ }
+
+ xmlXPathFreeObject (xpathObj);
+ return attrv;
+}
+
+long
+xml_file_get_node_attribute_long (TXMLFile *file, const char *x_path, const char *attr, const int _default)
+{
+ char *s;
+ long int i;
+
+ s = xml_file_get_node_attribute (file, x_path, attr);
+ if (s == NULL)
+ return _default;
+
+ i = atol (s);
+ free (s);
+ return i;
+}
+
+/*
+ * xml_file_get_node_present: existency test of the XPath node
+ */
+gboolean
+xml_file_get_node_present (TXMLFile *file, const char *x_path)
+{
+ return xml_file_node_get_children_count (file, x_path) > 0;
+}
+
+
+/*
+ * xml_file_node_get_children_count: retrieve number of children items of the specified XPath node
+ */
+int
+xml_file_node_get_children_count (TXMLFile *file, const char *x_path)
+{
+ xmlXPathObjectPtr xpathObj;
+ int count;
+
+ if ((! file) || (! x_path))
+ return 0;
+
+ /* Evaluate xpath expression */
+ xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx);
+ if (xpathObj == NULL) {
+ fprintf (stderr, "Error: unable to evaluate xpath expression \"%s\"\n", x_path);
+ return 0;
+ }
+
+ count = 0;
+ if (xpathObj->nodesetval)
+ count = xpathObj->nodesetval->nodeNr;
+
+ xmlXPathFreeObject (xpathObj);
+ return count;
+}
diff --git a/src/xml-parser.h b/src/xml-parser.h
new file mode 100644
index 0000000..02be628
--- /dev/null
+++ b/src/xml-parser.h
@@ -0,0 +1,67 @@
+/* Cataract - Static web photo gallery generator
+ * Copyright (C) 2008 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 <glib.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/xpath.h>
+
+
+typedef struct {
+ xmlDocPtr doc;
+ xmlXPathContextPtr xpathCtx;
+} TXMLFile;
+
+
+
+/*
+ * xml_parser_load: initialize and load the XML document
+ */
+TXMLFile * xml_parser_load (const char *filename);
+
+/*
+ * xml_parser_close: close the XML document parser
+ */
+void xml_parser_close (TXMLFile *file);
+
+/*
+ * xml_file_get_node_name: retrieve name of the XPath node
+ */
+char * xml_file_get_node_name (TXMLFile *file, const char *x_path);
+
+/*
+ * xml_file_get_node_value: retrieve string value from XPath node
+ * - multiple matched nodes will be concatenated into one string
+ * - otherwise please use [0], [1] etc. quantificators
+ */
+char * xml_file_get_node_value (TXMLFile *file, const char *x_path);
+
+/*
+ * xml_file_get_node_attribute: retrieve attribute value from XPath node
+ */
+char * xml_file_get_node_attribute (TXMLFile *file, const char *x_path, const char *attr);
+long xml_file_get_node_attribute_long (TXMLFile *file, const char *x_path, const char *attr, const int _default);
+
+/*
+ * xml_file_get_node_present: existency test of the XPath node
+ */
+gboolean xml_file_get_node_present (TXMLFile *file, const char *x_path);
+
+/*
+ * xml_file_node_get_children_count: retrieve number of children items of the specified XPath node
+ */
+int xml_file_node_get_children_count (TXMLFile *file, const char *x_path);