From e3a41da5a0a3d70ac53591f2b66144f2be2b3871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yaroslav=20de=20la=20Pe=C3=B1a=20Smirnov?= Date: Sun, 7 Nov 2021 02:02:45 +0300 Subject: Initial commit. Almost functional but still missing features and lacking testing. --- include/bstree.h | 68 ++++++++++++++++++++++++++++++++++++++++++ include/components.h | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/config.h | 43 +++++++++++++++++++++++++++ include/fs.h | 43 +++++++++++++++++++++++++++ include/log.h | 58 ++++++++++++++++++++++++++++++++++++ include/render.h | 38 ++++++++++++++++++++++++ include/site.h | 46 +++++++++++++++++++++++++++++ include/tests/tests.h | 45 ++++++++++++++++++++++++++++ 8 files changed, 423 insertions(+) create mode 100644 include/bstree.h create mode 100644 include/components.h create mode 100644 include/config.h create mode 100644 include/fs.h create mode 100644 include/log.h create mode 100644 include/render.h create mode 100644 include/site.h create mode 100644 include/tests/tests.h (limited to 'include') diff --git a/include/bstree.h b/include/bstree.h new file mode 100644 index 0000000..9577b73 --- /dev/null +++ b/include/bstree.h @@ -0,0 +1,68 @@ +/* + * Copyright 2021 Yaroslav de la Peña Smirnov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef BSTREE_H +#define BSTREE_H + +#include + +/* < 0 means a less than b; 0 means equal; > 0 means a more than b. */ +typedef int (*bst_cmp_fn)(const void *a, const void *b); +typedef void (*bst_free_fn)(void *data); + +struct bstree { + struct bstnode *root; + bst_cmp_fn cmp; + /* It can be NULL, in which case you will have to free the values manually */ + bst_free_fn free; +}; + +struct bstnode { + void *value; + struct bstnode *parent; + struct bstnode *left; + struct bstnode *right; +}; + +typedef bool (*bst_walk_cb)(struct bstnode *, void *data); + +struct bstree *bstree_new(bst_cmp_fn, bst_free_fn); + +struct bstnode *bstree_add(struct bstree *, void *val); + +/* Returns NULL if a node with the value wasn't found */ +struct bstnode *bstree_search(struct bstree *, void *val); + +struct bstnode *bstree_min(struct bstnode *); + +struct bstnode *bstree_max(struct bstnode *); + +struct bstnode *bstree_predecessor(struct bstnode *); + +struct bstnode *bstree_successor(struct bstnode *); + +bool bstree_inorder_walk(struct bstnode *, bst_walk_cb, void *data); + +void bstree_remove(struct bstree *, struct bstnode *); + +void bstree_destroy(struct bstree *); + +#endif diff --git a/include/components.h b/include/components.h new file mode 100644 index 0000000..ba5c866 --- /dev/null +++ b/include/components.h @@ -0,0 +1,82 @@ +#ifndef REVELA_COMPONENTS_H +#define REVELA_COMPONENTS_H + +#include +#include +#include + +#include "config.h" +#include "hashmap.h" + +#define PHOTO_THUMB_SUFFIX "_thumb" + +struct image { + /* The path to the source image file */ + char *source; + /* Points to the basename in source */ + const char *basename; + /* Points to the extension in source */ + const char *ext; + /* The "url" to the dir where index.html for this image will be located */ + char *url; + /* The "url" to the image file */ + char *url_image; + /* The "url" to the thumbnail image file */ + char *url_thumb; + /* Pointers to the relative urls in their respective urls */ + const char *dst; + const char *dst_image; + const char *dst_thumb; + /* The "raw" exif data extracted from the original file */ + ExifData *exif_data; + /* Last modified time of source file */ + struct timespec modtime; + /* The datetime this image was taken in human friendly form */ + char datestr[24]; + /* Same as date but in seconds for easier comparison. See image_set_date() */ + time_t tstamp; + struct album *album; + /* Hashmap with values to be passed to the image template */ + struct hashmap *map; + /* Hashmap with values to be passed to thumbs and previews vectors */ + struct hashmap *thumb; +}; + +struct album { + struct album_config *config; + /* The path to the source directory */ + char *source; + /* The full url that will be used in the template */ + char *url; + /* Pointer to the slug part in url that will be used for the new dir */ + const char *slug; + /* Pointer to the human readable date of the earliest image */ + const char *datestr; + /* The year of this album in human readable form; used to categorize */ + char year[8]; + /* The date of the album is the date of the earliest image */ + time_t tstamp; + struct bstree *images; + /* Hashmap with values to be passed to the template */ + struct hashmap *map; + /* Vector with hashmaps of images to be passed to the templates */ + struct vector *thumbs; + struct vector *previews; +}; + +struct image *image_new(char *src, const struct stat *, struct album *); + +int image_cmp(const void *a, const void *b); + +void image_destroy(void *data); + +struct album *album_new(struct album_config *, struct site_config *, + const char *src, const char *rsrc, const struct stat *); + +int album_cmp(const void *a, const void *b); + +void album_add_image(struct album *, struct image *); + +void album_destroy(void *data); + +#endif diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..398160b --- /dev/null +++ b/include/config.h @@ -0,0 +1,43 @@ +#ifndef REVELA_CONFIG_H +#define REVELA_CONFIG_H +#include +#include +#include + +#define SITE_CONF "site.ini" +#define ALBUM_CONF "album.ini" + +struct image_config { + bool strip; + uint8_t quality; + size_t max_width; + size_t max_height; + bool smart_resize; +}; + +struct site_config { + char *title; + char *base_url; + uint32_t max_previews; + struct image_config images; + struct image_config thumbnails; +}; + +struct album_config { + char *title; + char *desc; +}; + +bool site_config_read_ini(const char *wdir, struct site_config *); + +bool album_config_read_ini(const char *adir, struct album_config *); + +struct site_config *site_config_init(void); + +struct album_config *album_config_init(void); + +void site_config_destroy(struct site_config *config); + +void album_config_destroy(struct album_config *config); + +#endif diff --git a/include/fs.h b/include/fs.h new file mode 100644 index 0000000..015214a --- /dev/null +++ b/include/fs.h @@ -0,0 +1,43 @@ +#ifndef REVELA_FS_H +#define REVELA_FS_H + +#include +#include +#include + +/* + * Returns a pointer to where the basename of the file is inside path. + */ +const char *rbasename(const char *path); + +/* + * Makes a new directory if it doesn't exist. If there were errors returns + * false, otherwise returns true. + */ +bool nmkdir(const char *path, struct stat *dstat, bool dry); + +#define joinpathb(buf, a, b) sprintf(buf, "%s/%s", a, b) + +/* + * Joins two paths into one, e.g. /hello, word -> /hello/world + * This function allocates a new string and returns it. + */ +char *joinpath(const char *restrict a, const char *restrict b); + +bool isimage(const char *fname); + +/* + * Removes extension from file name "e.g. pepe.gif -> pepe. Returns a pointer to + * location of the extension in the string. + */ +const char *delext(const char *restrict basename, char *restrict dest, size_t n); + +/* + * -1 if error; 0 if the timestamps are different; 1 if they are equal. + */ +int file_is_uptodate(const char *path, const struct timespec *srcmtim); + +/* Sets access and modification times to the time passed */ +void setdatetime(const char *path, const struct timespec *mtim); + +#endif diff --git a/include/log.h b/include/log.h new file mode 100644 index 0000000..2be35ee --- /dev/null +++ b/include/log.h @@ -0,0 +1,58 @@ +#ifndef REVELA_LOG_H +#define REVELA_LOG_H + +#include +#include + +enum log_level { + LOG_SILENT, + LOG_FATAL, + LOG_ERROR, + LOG_INFO, + LOG_DETAIL, + LOG_DEBUG, +}; + +#ifdef DEBUG +static const char *log_level_names[] = { + "", + "FATAL", + "ERROR", + "INFO", + "DETAIL", + "DEBUG", +}; +#endif + +void log_set_verbosity(enum log_level); + +void log_printf(enum log_level, const char *restrict fmt, ...); + +#ifdef DEBUG +#define log_print(v, fmt, ...) \ + log_printf(v, "[%s] %s:%d " fmt, log_level_names[v], \ + __FILE__, __LINE__, ##__VA_ARGS__) +#else +#define log_print(v, fmt, ...) \ + log_printf(v, fmt, ##__VA_ARGS__) +#endif + +#ifdef DEBUG +#define log_printl(v, fmt, ...) \ + log_printf(v, "[%s] %s:%d " fmt "\n", log_level_names[v], \ + __FILE__, __LINE__, ##__VA_ARGS__) +#else +#define log_printl(v, fmt, ...) \ + log_printf(v, fmt "\n", ##__VA_ARGS__) +#endif + +#ifdef DEBUG +#define log_printl_errno(v, fmt, ...) \ + log_printf(v, "[%s] %s:%d " fmt ": %s\n", log_level_names[v], \ + __FILE__, __LINE__, ##__VA_ARGS__, strerror(errno)) +#else +#define log_printl_errno(v, fmt, ...) \ + log_printf(v, fmt ": %s\n", ##__VA_ARGS__, strerror(errno)) +#endif + +#endif diff --git a/include/render.h b/include/render.h new file mode 100644 index 0000000..faa6f73 --- /dev/null +++ b/include/render.h @@ -0,0 +1,38 @@ +#ifndef REVELA_RENDER_H +#define REVELA_RENDER_H + +#include + +#include "bstree.h" +#include "config.h" +#include "template.h" +#include "components.h" + +struct render { + /* Unja environment */ + struct env *env; + /* List of years with albums in each year */ + struct vector *years; + /* List of albums */ + struct vector *albums; + /* Hashmap used for all templates */ + struct hashmap *common_vars; + /* Modification time for the templates dir */ + struct timespec modtime; + bool dry_run; +}; + +bool render_make_index(struct render *, const char *path); + +bool render_make_album(struct render *r, const char *path, + const struct album *album); + +bool render_make_image(struct render *r, const char *path, + const struct image *image); + +bool render_init(struct render *, const char *path, struct site_config *, + struct bstree *albums); + +void render_deinit(struct render *); + +#endif diff --git a/include/site.h b/include/site.h new file mode 100644 index 0000000..678cd47 --- /dev/null +++ b/include/site.h @@ -0,0 +1,46 @@ +#ifndef REVELA_SITE_H +#define REVELA_SITE_H + +#include "config.h" +#include "bstree.h" +#include "render.h" +#include "components.h" + +#include + +#ifndef SITE_DEFAULT_RESOURCES +#define SITE_DEFAULT_RESOURCES "/usr/share/revela" +#endif + +#define CSSDIR "css" +#define STATICDIR "static" +#define CONTENTDIR "content" +#define TEMPLATESDIR "templates" +#define DEFAULTALBUM "unorganized" + +struct site { + struct site_config *config; + MagickWand *wand; + char *root_dir; + const char *output_dir; + char *content_dir; + /* + * Indicates how many characters after the full root dir path of the input + * directory the relative path of the albums + the content dir start. See + * site_init() + */ + size_t rel_content_dir; + struct bstree *albums; + struct render render; + bool dry_run; +}; + +bool site_build(struct site *); + +bool site_load(struct site *); + +bool site_init(struct site *); + +void site_deinit(struct site *); + +#endif diff --git a/include/tests/tests.h b/include/tests/tests.h new file mode 100644 index 0000000..8c89fcc --- /dev/null +++ b/include/tests/tests.h @@ -0,0 +1,45 @@ +#ifndef TESTS_H +#define TESTS_H +#include +#include + +#ifndef NOCOLOR +#define TBLD "\033[1m" +#define TRED "\033[31m" +#define TGRN "\033[32m" +#define TBLU "\033[34m" +#define TRST "\033[0m" +#else +#define TBLD "" +#define TRED "" +#define TGRN "" +#define TBLU "" +#define TRST "" +#endif + +#define RUN_TEST(test_func) \ + printf("%s:\t", #test_func); \ + fflush(stdout); \ + test_func(); \ + printf(TGRN "OK!\n" TRST) + +#define INIT_TESTS() \ + printf(TBLD "running %s tests\n" TRST, __FILE__) + +#define FAIL_TEST(reason) \ + printf(TBLD TRED "FAIL!\n" TRST); \ + printf("%s:%d: %s: ", __FILE__, __LINE__, __func__); \ + printf(reason); \ + abort() + +#define asserteq(a, b) \ + if (a != b) { \ + FAIL_TEST("assertion " TBLD TBLU #a " == " #b TRST " failed\n"); \ + } + +#define assertneq(a, b) \ + if (a == b) { \ + FAIL_TEST("assertion " TBLD TBLU #a " != " #b TRST " failed\n"); \ + } + +#endif -- cgit v1.2.3