/* Cataract - Static web photo gallery generator * Copyright (C) 2008 Tomas Bzatek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "gallery-utils.h" #include "stats.h" #define STRING_ALLOC_SIZE 4096 /* * str_replace: replace substring 'search' with a 'replace' string * - multiple occurrences of the string are replaced * - reallocates the original string */ void str_replace (gchar **dst, const gchar *search, const gchar *replace) { gchar *d; gchar *src; gchar *found; int i; int alloc_size; int used; if (! dst || ! search || strlen (*dst) == 0 || strlen (search) == 0 || strstr (*dst, search) == NULL) return; /* let's suppose just one occurrence of replace, realloc if needed */ used = strlen (*dst) + 1; alloc_size = (((used + strlen (replace)) / STRING_ALLOC_SIZE) + 1) * STRING_ALLOC_SIZE; d = g_malloc (alloc_size); i = 0; src = *dst; while (strstr (src, search)) { found = strstr (src, search); used += strlen (replace) - strlen (search); if (used > alloc_size) { alloc_size = ((used / STRING_ALLOC_SIZE) + 1) * STRING_ALLOC_SIZE; d = g_realloc (d, alloc_size); } /* 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; /* return fixed string */ g_free (*dst); *dst = g_strdup (d); g_free (d); } /* * str_trim_inside: trims any leading, trailing and repeated whitespaces in the whole string */ #define IS_WHITESPACE(s) (s == ' ' || s == '\t') void str_trim_inside (gchar **s) { gchar *dst; gboolean was_whitespace; int pos; gchar *src; if (! s || strlen (*s) == 0 ) return; /* suppose the new string is not longer than the input string */ dst = g_malloc0 (strlen (*s) + 1); was_whitespace = TRUE; /* handles beginning of the string */ pos = 0; for (src = *s; *src != '\0'; src++) if (! IS_WHITESPACE (*src) || ! was_whitespace) { dst[pos++] = *src; was_whitespace = IS_WHITESPACE (*src); } if (IS_WHITESPACE (dst[pos - 1])) dst[pos - 1] = '\0'; /* return fixed string */ g_free (*s); *s = dst; } /* * copy_file: copy file from src to dst */ gboolean copy_file (const gchar *src, const gchar *dst) { #define BUFFER_SIZE 65536 FILE *fin; FILE *fout; void *buffer; size_t size_r; struct stat st; struct utimbuf ut; size_t written; fin = fopen (src, "r"); if (fin == NULL) { log_error ("copy_file: error reading file \"%s\": %s\n", src, strerror (errno)); return FALSE; } fout = fopen (dst, "w"); if (fout == NULL) { log_error ("copy_file: error writing to file \"%s\": %s\n", dst, strerror (errno)); fclose (fin); return FALSE; } buffer = g_malloc0 (BUFFER_SIZE); size_r = BUFFER_SIZE; while (! feof (fin) && size_r == BUFFER_SIZE) { size_r = fread (buffer, 1, BUFFER_SIZE, fin); written = fwrite (buffer, 1, size_r, fout); if (written < size_r) { log_error ("copy_file: error writing to file \"%s\": %s\n", dst, strerror (errno)); break; } } fclose (fout); fclose (fin); g_free (buffer); /* copy timestamps */ memset (&st, 0, sizeof (st)); if (stat (src, &st) == -1) { log_error ("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) log_error ("copy_file: cannot set timestamps on target file \"%s\": %s\n", dst, strerror (errno)); return TRUE; } /* * copy_file_dir: recursively copy file or directory from src to dst */ gboolean copy_file_dir (const gchar *src, const gchar *dst) { int n; struct dirent **namelist; char *new_src, *new_dst; if (g_file_test (src, G_FILE_TEST_IS_DIR)) { if (g_mkdir_with_parents (dst, DEFAULT_DATA_DIR_MODE)) { log_error ("error making target directory '%s': %s\n", dst, strerror (errno)); return FALSE; } n = scandir (src, &namelist, NULL, NULL); if (n < 0) { log_error ("error listing source directory '%s': %s\n", src, strerror (errno)); return FALSE; } while (n--) { if (g_strcmp0 (".", namelist[n]->d_name) != 0 && g_strcmp0 ("..", namelist[n]->d_name) != 0) { new_src = g_build_filename (src, namelist[n]->d_name, NULL); new_dst = g_build_filename (dst, namelist[n]->d_name, NULL); copy_file_dir (new_src, new_dst); g_free (new_src); g_free (new_dst); } free (namelist[n]); } free (namelist); return TRUE; } else return copy_file (src, dst); } /* * make_string: make string of 'substr' substrings * - returns newly allocated string */ gchar * make_string (const gchar *substr, int count) { int i; gchar *s; if (count < 0) count = 0; s = g_malloc0 (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 & * - returns newly allocated string */ void fix_entities (gchar **str) { gchar *d; gchar *src; gchar *found; gchar *scan; int i; int alloc_size; int used; if (! *str || strstr (*str, "&") == NULL) return; i = 0; src = *str; used = strlen (src) + 1; alloc_size = ((used / STRING_ALLOC_SIZE) + 1) * STRING_ALLOC_SIZE; d = g_malloc (alloc_size); 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 & */ used += 4; if (used > alloc_size) { alloc_size = ((used / STRING_ALLOC_SIZE) + 1) * STRING_ALLOC_SIZE; d = g_realloc (d, alloc_size); } 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; /* return fixed string */ g_free (*str); *str = g_strdup (d); g_free (d); } /* * 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 (gchar **str, const gchar *tag_begin, const gchar *tag_end) { gchar *src; gchar *found; gchar *found2; gchar *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 = g_malloc0 (strlen (src) - (found2 - found) + 1); memcpy (dest, src, found - src); memcpy (dest + (found - src), found2, strlen (found2) + 1); g_free (src); src = g_strdup (dest); g_free (dest); } else { /* break in case of malformed tag structure, avoid endless loop */ log_error ("remove_tags: malformed tag structure detected, strange things may happen\n"); break; } } *str = src; } /* * count_dir_levels: returns number of path elements */ int count_dir_levels (const gchar *path) { guint i; int level; if (path == NULL || strlen (path) == 0) return 0; level = 1; for (i = strlen (path) - 1; i > 0; i--) { if (G_IS_DIR_SEPARATOR (*(path + i)) && i > 0 && i < strlen (path)) level++; } return level; } /* * extract_file_ext: returns pointer to filename extension */ const gchar * extract_file_ext (const gchar *filename) { g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (strlen (filename) > 0, NULL); return strrchr (filename, '.') + 1; } /* * log_error: prints an error and increments stats */ /* this function can possibly store error in some list and print them all at the end again */ void log_error (const gchar *format, ...) { va_list args; va_start (args, format); vfprintf (stderr, format, args); va_end (args); stats_errors_inc (); } /* * needs_update: returns TRUE if the destionation file needs updating, also when missing */ gboolean needs_update (const gchar *source, const gchar *dest) { struct stat src_stat; struct stat dst_stat; memset (&src_stat, 0, sizeof (src_stat)); memset (&dst_stat, 0, sizeof (dst_stat)); /* if we can't stat the source file, return FALSE to prevent further errors during update */ if (stat (source, &src_stat) == -1) { log_error ("needs_update: cannot stat source file \"%s\": %s\n", source, strerror (errno)); return FALSE; } /* if we can't stat the destination file, we need update anyway */ if (stat (dest, &dst_stat) == -1) return TRUE; /* destination file size should not be zero */ if (dst_stat.st_size <= 0) return TRUE; return (src_stat.st_mtime > dst_stat.st_mtime); } /* * clone_string_hash_table: clones data from existing string hash table and returns newly-allocated copy */ GHashTable * clone_string_hash_table (GHashTable *hash_table) { GHashTable *dest; GHashTableIter iter; gchar *key, *value; dest = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); g_hash_table_iter_init (&iter, hash_table); while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) g_hash_table_insert (dest, g_strdup (key), g_strdup (value)); return dest; }