commit 3aa0f66199646daae7c99d0003ec82555b852338
parent ca5ed11b330ed22036c39a4ca2abd4f86438e4d0
Author: Paco Esteban <paco@e1e0.net>
Date: Thu, 18 Jun 2020 20:52:46 +0200
better error handling stolen from Stefan and Tracey
Diffstat:
M | Makefile | | | 2 | +- |
A | error.c | | | 69 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | helpers.c | | | 15 | ++++++++------- |
M | helpers.h | | | 3 | ++- |
M | ssnail.c | | | 215 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
A | ssnail_error.h | | | 41 | +++++++++++++++++++++++++++++++++++++++++ |
6 files changed, 253 insertions(+), 92 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,6 +1,6 @@
PROG = ssnail
-SRCS += ssnail.c helpers.c
+SRCS += ssnail.c helpers.c error.c
LDFLAGS += -L/usr/local/lib
LDADD += -llowdown -lm
diff --git a/error.c b/error.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018, 2019, 2020 Stefan Sperling <stsp@openbsd.org>
+ * Copyright (c) 2020 Tracey Emery <tracey@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/syslimits.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ssnail_error.h"
+
+#ifndef nitems
+#define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
+#endif
+
+const struct ssnail_error *
+ssnail_error_msg(int code, const char *msg)
+{
+ static struct ssnail_error error;
+ int i;
+
+ for (i = 0; i < nitems(ssnail_errors); i++) {
+ if (code == ssnail_errors[i].code) {
+ error.code = code;
+ error.msg = msg;
+ return &error;
+ }
+ }
+
+ abort();
+}
+
+const struct ssnail_error *
+ssnail_error_from_errno(const char *prefix)
+{
+ static struct ssnail_error error;
+ static char error_msg[PATH_MAX + 20];
+
+ snprintf(error_msg, sizeof(error_msg), "%s: %s", prefix,
+ strerror(errno));
+
+ error.code = SSNAIL_ERR_ERRNO;
+ error.msg = error_msg;
+ return &error;
+}
+
+void *
+ssnail_error_fprintf(const struct ssnail_error *error)
+{
+ /* not checking for fprintf errors */
+ fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
+
+ return NULL;
+}
diff --git a/helpers.c b/helpers.c
@@ -22,6 +22,7 @@
#include <stdlib.h>
#include <string.h>
+#include "ssnail_error.h"
#include "helpers.h"
char *
@@ -42,7 +43,7 @@ build_full_path(char *dir, char *file)
return fullpath;
}
-int
+const struct ssnail_error *
copy_file(const char *source_file, const char *dest_file, int force)
{
FILE *src, *dst;
@@ -51,12 +52,12 @@ copy_file(const char *source_file, const char *dest_file, int force)
long long src_mtime, dst_mtime;
if (stat(source_file, &st) == -1)
- return -1;
+ return ssnail_error_from_errno("cp stat src");
src_mtime = st.st_mtim.tv_sec;
if (stat(dest_file, &st) == -1) { /* dst file does not have to exist */
if (errno != ENOENT)
- return -1;
+ return ssnail_error_from_errno("cp stat dst");
dst_mtime = 0;
} else {
dst_mtime = st.st_mtim.tv_sec;
@@ -64,9 +65,9 @@ copy_file(const char *source_file, const char *dest_file, int force)
if ((src_mtime > dst_mtime) || force) {
if ((src = fopen(source_file, "rb")) == NULL)
- return -1;
+ return ssnail_error_from_errno("cp fopen src");
if ((dst = fopen(dest_file, "wb")) == NULL)
- return -1;
+ return ssnail_error_from_errno("cp fopen dst");
printf("copying %s --> %s\n", source_file, dest_file);
size_t rcnt, wcnt;
@@ -80,13 +81,13 @@ copy_file(const char *source_file, const char *dest_file, int force)
}
} while ((rcnt > 0) && (rcnt == wcnt));
if (wcnt)
- return -1;
+ return ssnail_error_msg(2, "cp");
fclose(src);
fclose(dst);
}
- return 0;
+ return NULL;
}
const char *
diff --git a/helpers.h b/helpers.h
@@ -20,7 +20,8 @@
#define HEADER "_header.html"
#define FOOTER "_footer.html"
-int copy_file(const char *, const char *, int);
+const struct ssnail_error *copy_file(const char *, const char *, int);
+
int load_from_file(char **, char *);
const char *get_filename_ext(const char *);
diff --git a/ssnail.c b/ssnail.c
@@ -30,6 +30,7 @@
#include <lowdown.h>
#include "helpers.h"
+#include "ssnail_error.h"
struct article {
size_t htmlz;
@@ -55,11 +56,12 @@ SLIST_HEAD(listhead, article) head;
__dead static void usage(void);
static void articleq_free(struct listhead *);
+static const struct ssnail_error *process_dir(char *, char *, int);
+static const struct ssnail_error *populate_article_entry(struct article *);
+static const struct ssnail_error *write_html(struct article *, char *, char *);
+static const struct ssnail_error *generate_index(struct listhead *, char *, char *, char *);
+
static int gen_html(struct article *);
-static int generate_index(struct listhead *, char *, char *, char *);
-static int populate_article_entry(struct article *);
-static int process_dir(char *, char *, int);
-static int write_html(struct article *, char *, char *);
__dead static void
usage(void)
@@ -72,19 +74,25 @@ usage(void)
int
main(int argc, char *argv[])
{
- DIR *dirp;
- struct dirent *dp;
- struct article *ap = NULL;
+ DIR *dirp;
+ struct dirent *dp;
+ struct article *ap = NULL;
+ const struct ssnail_error *error = NULL;
+
int ch, force = 0, index = 0, needs_index = 0;
char *srcdir = NULL, *dstdir = NULL, *fbuf = NULL;
char *header_tpl = NULL, *footer_tpl = NULL;
header_tpl = strdup(HEADER);
- if (header_tpl == NULL)
- return EXIT_FAILURE;
+ if (header_tpl == NULL) {
+ error = ssnail_error_from_errno("header_tpl def");
+ goto done;
+ }
footer_tpl = strdup(FOOTER);
- if (footer_tpl == NULL)
- return EXIT_FAILURE;
+ if (footer_tpl == NULL) {
+ error = ssnail_error_from_errno("footer_tpl def");
+ goto done;
+ }
while ((ch = getopt(argc, argv, "Ff:h:i")) != -1) {
switch (ch) {
@@ -94,14 +102,18 @@ main(int argc, char *argv[])
case 'f':
free(footer_tpl);
footer_tpl = strdup(optarg);
- if (footer_tpl == NULL)
- return EXIT_FAILURE;
+ if (footer_tpl == NULL) {
+ error = ssnail_error_from_errno("footer_tpl");
+ goto done;
+ }
break;
case 'h':
free(header_tpl);
header_tpl = strdup(optarg);
- if (header_tpl == NULL)
- return EXIT_FAILURE;
+ if (header_tpl == NULL) {
+ error = ssnail_error_from_errno("footer_tpl");
+ goto done;
+ }
break;
case 'i':
index = 1;
@@ -119,30 +131,38 @@ main(int argc, char *argv[])
usage();
srcdir = strdup(argv[0]);
- if (srcdir == NULL)
- return EXIT_FAILURE;
+ if (srcdir == NULL) {
+ error = ssnail_error_from_errno("srcdir arg");
+ goto done;
+ }
dstdir = strdup(argv[1]);
- if (dstdir == NULL)
- return EXIT_FAILURE;
+ if (dstdir == NULL) {
+ error = ssnail_error_from_errno("dstdir arg");
+ goto done;
+ }
- if (process_dir(srcdir, dstdir, force) == -1)
- err(1, "cannot process src dir");
+ if ((error = process_dir(srcdir, dstdir, force)) != NULL)
+ goto done;
printf("Generate html files ... \n");
SLIST_FOREACH(ap, &head, entries) {
if ((ap->src_mtime > ap->dst_mtime) || force) {
printf("... %s\n", ap->dst_path);
- if (write_html(ap, header_tpl, footer_tpl) == -1)
- err(3, "cannot write html file");
+ error = write_html(ap, header_tpl, footer_tpl);
+ if (error)
+ goto done;
needs_index = 1;
}
}
if (index && (needs_index || force)) {
printf("Generate html index ... \n");
- generate_index(&head, header_tpl, footer_tpl, dstdir);
+ error = generate_index(&head, header_tpl, footer_tpl, dstdir);
+ if (error)
+ goto done;
}
+done:
free(srcdir);
free(dstdir);
free(fbuf);
@@ -150,17 +170,21 @@ main(int argc, char *argv[])
free(footer_tpl);
articleq_free(&head);
+ if (error) {
+ ssnail_error_fprintf(error);
+ return error->code;
+ }
return EXIT_SUCCESS;
}
-static int
+static const struct ssnail_error *
generate_index(struct listhead *h, char *head_tpl,
char *foot_tpl, char *dst_dir)
{
+ const struct ssnail_error *error = NULL;
struct article *a;
char *header = NULL, *footer = NULL;
FILE *fout;
- int r = -1;
time_t now;
struct tm *timeInfo;
char mytime[12], *index_path = NULL;
@@ -169,20 +193,27 @@ generate_index(struct listhead *h, char *head_tpl,
timeInfo = localtime(&now);
strftime(mytime, sizeof(mytime), "%Y-%m-%d", timeInfo);
- if (load_from_file(&header, head_tpl) == 0)
+ if (load_from_file(&header, head_tpl) == 0) {
+ error = ssnail_error_msg(2, "load head_tpl");
goto out;
- /* XXX: 13. here is where you would null term *header = '\0' */
+ }
header = str_rep(header, "$title$", "index");
header = str_rep(header, "$author$", getlogin());
header = str_rep(header, "$date$", mytime);
- if (load_from_file(&footer, foot_tpl) == 0)
+ if (load_from_file(&footer, foot_tpl) == 0) {
+ error = ssnail_error_msg(2, "load foot_tpl");
goto out;
+ }
footer = str_rep(footer, "$title$", "index");
- if ((index_path = build_full_path(dst_dir, "index.html")) == NULL)
+ if ((index_path = build_full_path(dst_dir, "index.html")) == NULL) {
+ error = ssnail_error_msg(2, "build_path dst_dir");
goto out;
- if ((fout = fopen(index_path, "w")) == NULL)
+ }
+ if ((fout = fopen(index_path, "w")) == NULL) {
+ error = ssnail_error_msg(2, "fopen index");
goto out;
+ }
fwrite(header, 1, strlen(header), fout);
/* TODO: make this list reverse date ordered */
fprintf(fout, "<ul>\n");
@@ -197,13 +228,11 @@ generate_index(struct listhead *h, char *head_tpl,
fwrite(footer, 1, strlen(footer), fout);
fclose(fout);
- r = 0;
out:
free(index_path);
free(header);
free(footer);
- return r;
-
+ return error;
}
static void
@@ -229,7 +258,7 @@ gen_html(struct article *a)
struct lowdown_opts opts;
struct lowdown_metaq mq;
struct lowdown_meta *md;
- int r = 0;
+ int r = -1;
size_t orig_size = 0;
TAILQ_INIT(&mq); /* create the queue(linked list) of lowdown_meta */
@@ -259,9 +288,17 @@ gen_html(struct article *a)
/* set default values for metadata */
a->title = strdup("");
+ if (a->title == NULL)
+ goto out;
a->author = strdup("");
+ if (a->author == NULL)
+ goto out;
a->date = strdup("");
+ if (a->date == NULL)
+ goto out;
a->type = strdup("page");
+ if (a->type == NULL)
+ goto out;
/* collect metadata from markdown */
TAILQ_FOREACH(md, &mq, entries) {
@@ -279,7 +316,7 @@ gen_html(struct article *a)
}
}
- r = 1;
+ r = 0;
out:
lowdown_metaq_free(&mq);
@@ -287,138 +324,150 @@ out:
return r;
}
-static int
+static const struct ssnail_error *
write_html(struct article *ap, char *head_tpl, char *foot_tpl)
{
- char *header = NULL, *footer = NULL;
+ const struct ssnail_error *error = NULL;
FILE *fout;
- int r = -1;
-
-/*
- * XXX: 15. need error handling below. This is where the segfault is, because I
- * don't have the required article struct filled. So, without pouring over
- * everything you've written, I'm obviously missing something in a file
- * somewhere. :D
- */
+ char *header = NULL, *footer = NULL;
+
if (strlen(ap->title) == 0
|| strlen(ap->author) == 0
- || strlen(ap->date) == 0)
+ || strlen(ap->date) == 0) {
+ error = ssnail_error_msg(2, "metadata");
goto out;
+ }
- if (load_from_file(&header, head_tpl) == 0)
- return r;
+ if (load_from_file(&header, head_tpl) == 0) {
+ error = ssnail_error_msg(2, "load header");
+ goto out;
+ }
header = str_rep(header, "$title$", ap->title);
header = str_rep(header, "$author$", ap->author);
header = str_rep(header, "$date$", ap->date);
- if (load_from_file(&footer, foot_tpl) == 0)
+ if (load_from_file(&footer, foot_tpl) == 0) {
+ error = ssnail_error_msg(2, "load header");
goto out;
+ }
footer = str_rep(footer, "$title$", ap->title);
- if ((fout = fopen(ap->dst_path, "w")) == NULL)
+ if ((fout = fopen(ap->dst_path, "w")) == NULL) {
+ error = ssnail_error_from_errno("openw dst_path");
goto out;
+ }
fwrite(header, 1, strlen(header), fout);
fwrite(ap->html_content, 1, ap->htmlz, fout);
fwrite(footer, 1, strlen(footer), fout);
fclose(fout);
- r = 0;
out:
free(header);
free(footer);
- return r;
+ return error;
}
-static int
+static const struct ssnail_error *
populate_article_entry(struct article *ap)
{
- struct stat st;
- int retval = -1;
- int len = 0;
+ const struct ssnail_error *error = NULL;
+ struct stat st;
+ int len = 0;
- if (stat(ap->src_path, &st) == -1)
+ if (stat(ap->src_path, &st) == -1) {
+ error = ssnail_error_from_errno("src_path");
goto out;
+ }
ap->src_mtime = st.st_mtim.tv_sec;
if (stat(ap->dst_path, &st) == -1) { /* dst file does not have to exist */
- if (errno != ENOENT)
+ if (errno != ENOENT) {
+ error = ssnail_error_from_errno("dst_path");
goto out;
+ }
ap->dst_mtime = 0;
} else {
ap->dst_mtime = st.st_mtim.tv_sec;
}
- if ((ap->origz = load_from_file(&ap->orig_content, ap->src_path)) == 0)
+ if ((ap->origz = load_from_file(&ap->orig_content, ap->src_path)) == 0) {
+ error = ssnail_error_from_errno("load orig");
goto out;
+ }
- gen_html(ap);
- retval = 0;
+ if (gen_html(ap) != 0)
+ error = ssnail_error_msg(2, "gen_html");
out:
- return retval;
+ return error;
}
-static int
+static const struct ssnail_error *
process_dir(char *src, char *dst, int force)
{
- DIR *dirp;
- struct dirent *dp;
- struct stat st;
- struct article *a = NULL;
-
- int retval = -1;
+ DIR *dirp;
+ struct dirent *dp;
+ struct stat st;
+ struct article *a = NULL;
+ const struct ssnail_error *error = NULL;
char *src_path = NULL, *dst_path = NULL;
/* if dst folder does not exist, create it */
if (stat(dst, &st) == -1 && errno == ENOENT) {
if (mkdir(dst, 0755) == -1)
- return -1;
+ return ssnail_error_from_errno("mkdir dst");
}
if ((dirp = opendir(src)) == NULL)
- return -1;
+ return ssnail_error_from_errno("opendir src");
while ((dp = readdir(dirp)) != NULL) {
if (strcmp(dp->d_name, ".") == 0
|| strcmp(dp->d_name, "..") == 0)
continue;
- if ((src_path = build_full_path(src, dp->d_name)) == NULL)
+ if ((src_path = build_full_path(src, dp->d_name)) == NULL) {
+ error = ssnail_error_msg(2, "buildpath src");
goto out;
- if ((dst_path = build_full_path(dst, dp->d_name)) == NULL)
+ }
+ if ((dst_path = build_full_path(dst, dp->d_name)) == NULL) {
+ error = ssnail_error_msg(2, "buildpath src");
goto out;
+ }
if (dp->d_type == DT_REG) {
if (strcmp(get_filename_ext(src_path), "md") == 0) {
char *fbuf = NULL;
a = malloc(sizeof(struct article));
+ if (a == NULL) {
+ error = ssnail_error_from_errno("malloc a");
+ goto out;
+ }
strlcpy(a->src_path, src_path, PATH_MAX);
fbuf = str_rep(dst_path, ".md", ".html");
strlcpy(a->dst_path, fbuf, PATH_MAX);
free(fbuf);
- if((populate_article_entry(a) == -1))
+ error = populate_article_entry(a);
+ if (error)
goto out;
SLIST_INSERT_HEAD(&head, a, entries);
} else {
- if (copy_file(src_path, dst_path, force) == -1)
+ error = copy_file(src_path, dst_path, force);
+ if (error)
goto out;
}
}
- if (dp->d_type == DT_DIR) {
- if (process_dir(src_path, dst_path, force) == -1)
- goto out;
- }
+ if (dp->d_type == DT_DIR)
+ error = process_dir(src_path, dst_path, force);
}
- retval = 0;
-
out:
free(src_path);
free(dst_path);
src_path = NULL;
dst_path = NULL;
closedir(dirp);
- return retval;
+ return error;
}
diff --git a/ssnail_error.h b/ssnail_error.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018, 2019, 2020 Stefan Sperling <stsp@openbsd.org>
+ * Copyright (c) 2020 Tracey Emery <tracey@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SSNAIL_ERROR_H
+#define SSNAIL_ERROR_H
+
+/* Error codes */
+#define SSNAIL_ERR_OK 0
+#define SSNAIL_ERR_ERRNO 1
+#define SSNAIL_ERR_GENERIC 2
+
+static const struct ssnail_error {
+ int code;
+ const char *msg;
+} ssnail_errors[] = {
+ { SSNAIL_ERR_OK, "no error occured?!?" },
+ { SSNAIL_ERR_ERRNO, "see errno" },
+ { SSNAIL_ERR_GENERIC, "error" },
+};
+
+const struct ssnail_error *ssnail_error_msg(int, const char *);
+
+const struct ssnail_error *ssnail_error_from_errno(const char *);
+
+void *ssnail_error_fprintf(const struct ssnail_error *);
+
+#endif /* SSNAIL_ERROR_H */