summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2018-02-16 12:34:53 +0200
committerSergey Poznyakoff <gray@gnu.org>2018-02-16 12:34:53 +0200
commitc93a6c17a2af32dd15c09adb926088d353ff800d (patch)
tree3d99f16caeb62c1a7b9d238031247e01f5a35f6e
parentbbb854527f0b841ffedbc2ba769ad495cd13a34d (diff)
downloadfileserv-c93a6c17a2af32dd15c09adb926088d353ff800d.tar.gz
fileserv-c93a6c17a2af32dd15c09adb926088d353ff800d.tar.bz2
Cleanup
Move the directory listing code into a separate module. Improve error reporting in directory index code. Don't bail out on (most of the) out of memory errors. * src/dirls.c: New file. * src/dirls.h: New file. * src/fileserv.c (errno_to_http_code): Increase resolution. * src/fileserv.h (parse_template_string) (parse_template_file): Change return value.
-rw-r--r--src/Makefile.am2
-rw-r--r--src/config.c5
-rw-r--r--src/dirls.c247
-rw-r--r--src/dirls.h17
-rw-r--r--src/fileserv.c24
-rw-r--r--src/fileserv.h4
-rw-r--r--src/idx.c669
7 files changed, 598 insertions, 370 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index b793023..d812da5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,10 +1,10 @@
bin_PROGRAMS=fileserv
fileserv_SOURCES=fileserv.c runas.c fileserv.h logger.c pidfile.c\
wordsplit.c wordsplit.h catfile.c config.c idx.c defidx.h\
- mem.c remoteip.c icon.c
+ mem.c remoteip.c icon.c dirls.c dirls.h
BUILT_SOURCES=defidx.h
if FSRV_WRAP
fileserv_SOURCES += wrapacl.c
endif
dist_man_MANS=fileserv.8
LDADD = ../mimetypes/libmimetypes.a
diff --git a/src/config.c b/src/config.c
index a7561e7..adcc677 100644
--- a/src/config.c
+++ b/src/config.c
@@ -314,13 +314,16 @@ set_index_template(size_t argc, char **argv, CONFIG *conf,
char const *file, int line)
{
if (argc != 2) {
error("%s:%d: expected exactly one argument", file, line);
return 1;
}
- parse_template_file(argv[1]);
+ if (parse_template_file(argv[1])) {
+ error("%s:%d: invalid template", file, line);
+ return 1;
+ }
return 0;
}
static int
set_index_css(size_t argc, char **argv, CONFIG *conf,
char const *file, int line)
diff --git a/src/dirls.c b/src/dirls.c
new file mode 100644
index 0000000..62ce71c
--- /dev/null
+++ b/src/dirls.c
@@ -0,0 +1,247 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/queue.h>
+#include <string.h>
+#include <unistd.h>
+#include "fileserv.h"
+#include "dirls.h"
+#include "mimetypes.h"
+
+void
+dirls_init(DIRLS *lst)
+{
+ lst->count = 0;
+ STAILQ_INIT(&lst->list);
+}
+
+void
+dirls_free(DIRLS *lst)
+{
+ DIRLSENT *ent;
+
+ while ((ent = STAILQ_FIRST(&lst->list))) {
+ STAILQ_REMOVE_HEAD(&lst->list, next);
+ free(ent);
+ }
+ lst->count = 0;
+}
+
+static void
+dirls_append(DIRLS *lst, DIRLSENT *ent)
+{
+ STAILQ_INSERT_TAIL(&lst->list, ent, next);
+ lst->count++;
+}
+
+int
+dirls_scan(DIRLS *dirls, char const *path, CONFIG const *conf)
+{
+ DIR *dir;
+ struct dirent *ent;
+ DIRLSENT *dirlsent;
+ int err = 0;
+
+ dir = opendir(path);
+ if (!dir) {
+ err = errno;
+ error("can't open directory %s: %s",
+ path, strerror(err));
+ return err;
+ }
+ dirls_init(dirls);
+ while (err == 0 && (ent = readdir(dir))) {
+ size_t len;
+ char *name;
+ struct stat st;
+
+ if (filename_is_hidden(ent->d_name, conf))
+ continue;
+
+ name = catfile(path, ent->d_name);
+ if (!name) {
+ err = errno;
+ break;
+ }
+
+ if (stat(name, &st)) {
+ error("can't stat %s: %s", name, strerror(errno));
+ free(name);
+ continue;
+ }
+
+ if (conf->list_unreadable == 0) {
+ if (access(name, R_OK)) {
+ free(name);
+ continue;
+ }
+ }
+
+ len = strlen(ent->d_name);
+ dirlsent = malloc(sizeof(*dirlsent)
+ + len
+ + (S_ISDIR(st.st_mode) ? 1 : 0)
+ + 1);
+ if (!dirlsent)
+ err = errno;
+ else {
+ dirlsent->name = (char*)(dirlsent + 1);
+ memcpy(dirlsent->name, ent->d_name, len);
+ if (S_ISDIR(st.st_mode))
+ dirlsent->name[len++] = '/';
+ dirlsent->name[len] = 0;
+
+ dirlsent->st = st;
+ dirlsent->type = get_file_type(name);
+ dirls_append(dirls, dirlsent);
+ }
+ free(name);
+ }
+ closedir(dir);
+
+ if (err)
+ dirls_free(dirls);
+
+ return err;
+}
+
+static void
+dirls_qsort(DIRLS *list, int cmp (DIRLSENT const *, DIRLSENT const *, void *),
+ void *data)
+{
+ if (list->count < 2)
+ return;
+ else if (list->count == 2) {
+ DIRLSENT *a, *b;
+ a = STAILQ_FIRST(&list->list);
+ b = STAILQ_NEXT(a, next);
+ if (cmp(a, b, data) > 0) {
+ STAILQ_REMOVE_HEAD(&list->list, next);
+ STAILQ_INSERT_TAIL(&list->list, a, next);
+ }
+ } else {
+ DIRLSENT *cur, *middle;
+ DIRLS high, low;
+ int rc;
+
+ cur = middle = STAILQ_FIRST(&list->list);
+ do {
+ cur = STAILQ_NEXT(cur, next);
+ if (!cur)
+ return;
+ } while ((rc = cmp(middle, cur, data)) == 0);
+
+ if (rc > 0)
+ middle = cur;
+
+ dirls_init(&high);
+ dirls_init(&low);
+
+ while ((cur = STAILQ_FIRST(&list->list))) {
+ STAILQ_REMOVE_HEAD(&list->list, next);
+ if (cmp(middle, cur, data) < 0)
+ dirls_append(&high, cur);
+ else
+ dirls_append(&low, cur);
+ }
+
+ /* Sort both lists */
+ dirls_qsort(&low, cmp, data);
+ dirls_qsort(&high, cmp, data);
+
+ /* Join lists in order and return */
+ STAILQ_CONCAT(&low.list, &high.list);
+ list->list = low.list;
+ list->count = low.count + high.count;
+ }
+}
+
+static char ordargs[] = "AD";
+static char colargs[] = "NMSD";
+
+INDEX_SORT_COL
+index_sort_col_from_arg(int c)
+{
+ char const *p;
+ if ((p = strchr(colargs, c)))
+ return p - colargs;
+ return ISC_NAME;
+}
+
+int
+index_sort_col_to_arg(INDEX_SORT_COL c)
+{
+ return colargs[c];
+}
+
+INDEX_SORT_ORD
+index_sort_ord_from_arg(int c)
+{
+ char const *p;
+ if ((p = strchr(ordargs, c)))
+ return p - ordargs;
+ return ISO_ASC;
+}
+
+int
+index_sort_ord_to_arg(INDEX_SORT_ORD c)
+{
+ return ordargs[c];
+}
+
+static inline int
+comp_result(int val, void *data)
+{
+ if (*(INDEX_SORT_ORD*)data == ISO_DESC)
+ val = -val;
+ return val;
+}
+
+static int
+comp_name(DIRLSENT const *a, DIRLSENT const *b, void *data)
+{
+ return comp_result(strcmp(a->name, b->name), data);
+}
+
+static int
+comp_time(DIRLSENT const *a, DIRLSENT const *b, void *data)
+{
+ int r;
+ if (a->st.st_mtime < b->st.st_mtime)
+ r = -1;
+ else if (a->st.st_mtime > b->st.st_mtime)
+ r = 1;
+ else
+ r = 0;
+ return comp_result(r, data);
+}
+
+static int
+comp_size(DIRLSENT const *a, DIRLSENT const *b, void *data)
+{
+ int r;
+ if (a->st.st_size < b->st.st_size)
+ r = -1;
+ else if (a->st.st_size > b->st.st_size)
+ r = 1;
+ else
+ r = 0;
+ return comp_result(r, data);
+}
+
+static int (*comp[])(DIRLSENT const *, DIRLSENT const *, void *) = {
+ comp_name,
+ comp_time,
+ comp_size,
+ comp_name
+};
+
+void
+dirls_sort(DIRLS *dirls, INDEX_SORT_COL col, INDEX_SORT_ORD ord)
+{
+ dirls_qsort(dirls, comp[col], &ord);
+}
+
diff --git a/src/dirls.h b/src/dirls.h
new file mode 100644
index 0000000..0067bff
--- /dev/null
+++ b/src/dirls.h
@@ -0,0 +1,17 @@
+typedef struct dirlsent {
+ char *name;
+ struct stat st;
+ char const *type;
+ STAILQ_ENTRY(dirlsent) next;
+} DIRLSENT;
+
+typedef struct dirls {
+ size_t count;
+ STAILQ_HEAD(, dirlsent) list;
+} DIRLS;
+
+void dirls_init(DIRLS *dirls);
+void dirls_free(DIRLS *dirls);
+int dirls_scan(DIRLS *dirls, char const *path, CONFIG const *conf);
+void dirls_sort(DIRLS *dirls, INDEX_SORT_COL col, INDEX_SORT_ORD ord);
+
diff --git a/src/fileserv.c b/src/fileserv.c
index 98c0c33..ae1599d 100644
--- a/src/fileserv.c
+++ b/src/fileserv.c
@@ -61,12 +61,27 @@ fileserv_panic(void *cls, const char *file, unsigned int line,
error("%s:%d: MHD PANIC: %s", file, line, reason);
else
error("%s:%d: MHD PANIC", file, line);
abort();
}
+static inline int
+errno_to_http_code(int ec)
+{
+ switch (ec) {
+ case EINVAL:
+ case ENOSYS:
+ case ENOMEM:
+ return MHD_HTTP_INTERNAL_SERVER_ERROR;
+ case ENOENT:
+ return MHD_HTTP_NOT_FOUND;
+ default:
+ return MHD_HTTP_FORBIDDEN;
+ }
+}
+
void
usage(void)
{
printf("usage: %s [OPTIONS] [HOST:]URL:DIR...\n", progname);
printf("OPTIONS are:\n\n");
printf(" -c FILE read configuration from FILE\n");
@@ -242,19 +257,12 @@ static char *
cfname(char const *fname)
{
/* FIXME */
return realpath(fname, NULL);
}
-
-static inline int
-errno_to_http_code(int ec)
-{
- return ec == ENOENT ? MHD_HTTP_NOT_FOUND : MHD_HTTP_FORBIDDEN;
-}
-
static int
find_index_file(char const *dir, CONFIG *conf, char **index_file,
struct stat *pst)
{
char *cf;
@@ -434,13 +442,13 @@ get_file_resp(struct MHD_Connection *conn, char const *url, FILE_RESP *resp)
res = directory_index(fd, resp->conf, url,
resp->file_name,
get_sort_col(conn),
get_sort_ord(conn));
if (res) {
close(fd);
- return MHD_HTTP_FORBIDDEN;
+ return errno_to_http_code(res);
}
fstat(fd, &resp->st);
resp->fd = fd;
resp->type = "text/html";
return MHD_HTTP_OK;
} else
diff --git a/src/fileserv.h b/src/fileserv.h
index d22d927..cf72b39 100644
--- a/src/fileserv.h
+++ b/src/fileserv.h
@@ -119,14 +119,14 @@ INDEX_SORT_ORD index_sort_ord_from_arg(int c);
int index_sort_ord_to_arg(INDEX_SORT_ORD c);
int filename_is_special(char const *name);
int filename_is_hidden(char const *name, CONFIG const *conf);
int filename_is_valid(char const *name);
-void parse_template_string(char const *str);
-void parse_template_file(char const *file_name);
+int parse_template_string(char const *str);
+int parse_template_file(char const *file_name);
void trusted_ip_add(char const *s);
struct MHD_Connection;
char *get_remote_ip(struct MHD_Connection *conn);
typedef struct icon {
diff --git a/src/idx.c b/src/idx.c
index be5b87c..1db1370 100644
--- a/src/idx.c
+++ b/src/idx.c
@@ -2,116 +2,270 @@
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <sys/queue.h>
-#include <sys/types.h>
-#include <dirent.h>
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include "fileserv.h"
-#include "mimetypes.h"
+#include "dirls.h"
#include "wordsplit.h"
-enum index_node_type {
+enum tmpl_node_type {
NODE_TEXT,
NODE_LOOP,
NODE_COND,
NODE_EXPR
};
-typedef STAILQ_HEAD(index_node_list, index_node) INDEX_NODE_LIST;
+typedef STAILQ_HEAD(tmpl_node_list, tmpl_node) TMPL_NODE_LIST;
-struct index_cond {
+struct tmpl_cond {
char *expr;
- INDEX_NODE_LIST branch[2];
+ TMPL_NODE_LIST branch[2];
};
-struct index_node {
+struct tmpl_node {
int type;
- STAILQ_ENTRY(index_node) next;
+ STAILQ_ENTRY(tmpl_node) next;
union {
char *text;
- struct index_node_list loop;
- struct index_cond cond;
+ struct tmpl_node_list loop;
+ struct tmpl_cond cond;
} v;
};
-typedef struct index_node INDEX_NODE;
+typedef struct tmpl_node TMPL_NODE;
struct condition {
- INDEX_NODE *node;
+ TMPL_NODE *node;
int brn;
STAILQ_ENTRY(condition) next;
};
typedef STAILQ_HEAD(, condition) COND_LIST;
+struct eval_env;
+
+/* Type-agnostic node functions */
+
+struct node_class {
+ void (*node_init)(TMPL_NODE *);
+ void (*node_free)(TMPL_NODE *);
+ int (*node_eval)(TMPL_NODE *, struct eval_env *);
+};
+
+static int node_text_eval(TMPL_NODE *, struct eval_env *);
+static int node_loop_eval(TMPL_NODE *, struct eval_env *);
+static int node_cond_eval(TMPL_NODE *, struct eval_env *);
+static int node_expr_eval(TMPL_NODE *, struct eval_env *);
+
+static void node_loop_init(TMPL_NODE *);
+static void node_loop_free(TMPL_NODE *);
+
+static void node_cond_init(TMPL_NODE *);
+static void node_cond_free(TMPL_NODE *);
+
+static struct node_class node_class[] = {
+ [NODE_TEXT] = { NULL, NULL, node_text_eval },
+ [NODE_LOOP] = { node_loop_init, node_loop_free, node_loop_eval },
+ [NODE_COND] = { node_cond_init, node_cond_free, node_cond_eval },
+ [NODE_EXPR] = { NULL, NULL, node_expr_eval }
+};
+
+#define ASSERT_NODE_TYPE(n) \
+ assert((n)->type >= 0 && (n)->type <= sizeof(node_class)/sizeof(node_class[0]))
+
+static void
+node_init(TMPL_NODE *node)
+{
+ ASSERT_NODE_TYPE(node);
+ if (node_class[node->type].node_init)
+ return node_class[node->type].node_init(node);
+}
+
+static void
+node_free(TMPL_NODE *node)
+{
+ ASSERT_NODE_TYPE(node);
+ if (node_class[node->type].node_free)
+ node_class[node->type].node_free(node);
+ free(node);
+}
+
+static int
+node_eval(TMPL_NODE *node, struct eval_env *env)
+{
+ ASSERT_NODE_TYPE(node);
+ assert(node_class[node->type].node_eval);
+ return node_class[node->type].node_eval(node, env);
+}
+
+
+/* Node list functions */
+
+static void
+node_list_free(TMPL_NODE_LIST *nlist)
+{
+ TMPL_NODE *node;
+
+ while ((node = STAILQ_FIRST(nlist))) {
+ STAILQ_REMOVE_HEAD(nlist, next);
+ node_free(node);
+ }
+}
+
+static int
+node_list_eval(TMPL_NODE_LIST *nlist, struct eval_env *env)
+{
+ TMPL_NODE *node;
+ int rc = 0;
+
+ STAILQ_FOREACH(node, nlist, next) {
+ rc = node_eval(node, env);
+ if (rc)
+ break;
+ }
+ return rc;
+}
+
+/* Node-specific initialization and deallocation functions */
+static void
+node_loop_init(TMPL_NODE *node)
+{
+ STAILQ_INIT(&node->v.loop);
+}
+
+static void
+node_loop_free(TMPL_NODE *node)
+{
+ node_list_free(&node->v.loop);
+}
+
+static void
+node_cond_init(TMPL_NODE *node)
+{
+ STAILQ_INIT(&node->v.cond.branch[0]);
+ STAILQ_INIT(&node->v.cond.branch[1]);
+}
+
+static void
+node_cond_free(TMPL_NODE *node)
+{
+ node_list_free(&node->v.cond.branch[0]);
+ node_list_free(&node->v.cond.branch[1]);
+}
+
+/* Parser error codes */
+enum {
+ TMPL_NOERR,
+ TMPL_ERR_LOOP_NESTING,
+ TMPL_ERR_ENDLOOP_WITHOUT_LOOP,
+ TMPL_ERR_ENDIF_WITHOUT_IF,
+ TMPL_ERR_ELSE_WITHOUT_IF,
+ TMPL_ERR_DUP_ELSE,
+ TMPL_ERR_ESCAPE_NOT_CLOSED,
+ TMPL_ERR_LOOP_NOT_CLOSED,
+ TMPL_ERR_COND_NOT_CLOSED,
+ TMPL_ERR_NOMEM
+};
+
+/* Corresponding textual descriptions */
+static char const *tmpl_err_str[] = {
+ "no error",
+ "attempt to nest loops",
+ "endloop without loop",
+ "endif without if",
+ "else without if",
+ "duplicate else",
+ "escape not closed",
+ "loop not closed",
+ "conditional not closed",
+ "not enough memory"
+};
+
+/* Template parser state */
struct template_state {
int escape:1;
int loop:1;
COND_LIST cond;
char const *start;
char const *cur;
+ int error;
};
-static void parse_template(INDEX_NODE_LIST *nodes, struct template_state *st);
+/* Allocate and initialize single node */
+static inline TMPL_NODE *
+node_alloc(struct template_state *st, int type, size_t extra)
+{
+ TMPL_NODE *np = calloc(1, sizeof(*np) + extra);
+ if (np)
+ np->type = type;
+ else
+ st->error = TMPL_ERR_NOMEM;
+ node_init(np);
+ return np;
+}
+
+/* Template parser */
+static void parse_template(TMPL_NODE_LIST *nodes, struct template_state *st);
-static INDEX_NODE *
-new_text_node(int type, char const *start, size_t len)
+static TMPL_NODE *
+new_text_node(struct template_state *st, int type, size_t len)
{
- INDEX_NODE *np = xmalloc(sizeof(*np));
- np->type = type;
- np->v.text = xmalloc(len + 1);
- memcpy(np->v.text, start, len);
- np->v.text[len] = 0;
+ TMPL_NODE *np = node_alloc(st, type, len + 1);
+ if (np) {
+ np->v.text = (char*)(np + 1);
+ memcpy(np->v.text, st->start, len);
+ np->v.text[len] = 0;
+ }
return np;
}
-static INDEX_NODE *
+static TMPL_NODE *
next_text_node(struct template_state *st)
{
st->start = st->cur;
while (*st->cur) {
if (*st->cur == '{' && st->cur[1] == '%') {
st->escape = 1;
break;
}
st->cur++;
}
- return new_text_node(NODE_TEXT, st->start, st->cur - st->start);
+ return new_text_node(st, NODE_TEXT, st->cur - st->start);
}
-static INDEX_NODE *
+static TMPL_NODE *
parse_cond(struct template_state *st, int brn, size_t off, size_t len)
{
struct condition cond;
- INDEX_NODE *np;
+ TMPL_NODE *np;
while (off < len && isspace(st->start[off]))
off++;
-
- np = xmalloc(sizeof(*np));
- np->type = NODE_COND;
len -= off;
- np->v.cond.expr = xmalloc(len + 1);
- memcpy(np->v.cond.expr, st->start + off, len);
- np->v.cond.expr[len] = 0;
- cond.node = np;
- cond.brn = brn;
- STAILQ_INSERT_HEAD(&st->cond, &cond, next);
- STAILQ_INIT(&np->v.cond.branch[!cond.brn]);
- parse_template(&np->v.cond.branch[cond.brn], st);
-
+ np = node_alloc(st, NODE_COND, len + 1);
+ if (np) {
+ np->v.cond.expr = (char*)(np + 1);
+ memcpy(np->v.cond.expr, st->start + off, len);
+ np->v.cond.expr[len] = 0;
+
+ cond.node = np;
+ cond.brn = brn;
+ STAILQ_INSERT_HEAD(&st->cond, &cond, next);
+ STAILQ_INIT(&np->v.cond.branch[!cond.brn]);
+ parse_template(&np->v.cond.branch[cond.brn], st);
+ }
return np;
}
-static INDEX_NODE *
+static TMPL_NODE *
next_escape_node(struct template_state *st)
{
size_t len;
st->start = st->cur + 2;
while (*st->cur) {
@@ -121,38 +275,43 @@ next_escape_node(struct template_state *st)
}
st->cur++;
}
if (st->escape) {
st->escape = 0;
- return new_text_node(NODE_TEXT, st->start,
- st->cur - st->start);
+ return new_text_node(st, NODE_TEXT, st->cur - st->start);
}
while (st->start < st->cur && isspace(st->start[0]))
st->start++;
len = st->cur - st->start;
while (len > 0 && isspace(st->start[len-1]))
len--;
st->cur += 2;
if (memcmp(st->start, "loop", len) == 0) {
- INDEX_NODE *np;
+ TMPL_NODE *np;
- assert(st->loop == 0);
+ if (st->loop) {
+ st->error = TMPL_ERR_LOOP_NESTING;
+ return NULL;
+ }
st->loop = 1;
- np = xmalloc(sizeof(*np));
- np->type = NODE_LOOP;
- parse_template(&np->v.loop, st);
+ np = node_alloc(st, NODE_LOOP, 0);
+ if (np)
+ parse_template(&np->v.loop, st);
return np;
}
if (memcmp(st->start, "endloop", len) == 0) {
- assert(st->loop);
+ if (!st->loop) {
+ st->error = TMPL_ERR_ENDLOOP_WITHOUT_LOOP;
+ return NULL;
+ }
st->loop = 0;
return NULL;
}
if (len > 2
&& (memcmp(st->start, "if", 2) == 0 && isspace(st->start[2])))
@@ -160,281 +319,147 @@ next_escape_node(struct template_state *st)
if (len > 6
&& (memcmp(st->start, "unless", 6) == 0 && isspace(st->start[6])))
return parse_cond(st, 0, 6, len);
if (memcmp(st->start, "endif", len) == 0) {
- STAILQ_REMOVE_HEAD(&st->cond, next);
+ if (STAILQ_FIRST(&st->cond))
+ STAILQ_REMOVE_HEAD(&st->cond, next);
+ else
+ st->error = TMPL_ERR_ENDIF_WITHOUT_IF;
return NULL;
}
if (memcmp(st->start, "else", len) == 0) {
- INDEX_NODE *np;
+ TMPL_NODE *np;
struct condition *cond = STAILQ_FIRST(&st->cond);
- assert(cond);
+ if (!cond) {
+ st->error = TMPL_ERR_ELSE_WITHOUT_IF;
+ return NULL;
+ }
np = cond->node;
- assert(STAILQ_FIRST(&np->v.cond.branch[!cond->brn]) == NULL);
+ if (STAILQ_FIRST(&np->v.cond.branch[!cond->brn])) {
+ st->error = TMPL_ERR_DUP_ELSE;
+ return NULL;
+ }
parse_template(&np->v.cond.branch[!cond->brn], st);
return NULL;
}
- return new_text_node(NODE_EXPR, st->start, len);
+ return new_text_node(st, NODE_EXPR, len);
}
-static INDEX_NODE *
+static TMPL_NODE *
next_node(struct template_state *st)
{
+ if (st->error)
+ return NULL;
if (*st->cur == 0)
return NULL;
return (st->escape ? next_escape_node : next_text_node)(st);
}
static void
-parse_template(INDEX_NODE_LIST *nodes, struct template_state *st)
+parse_template(TMPL_NODE_LIST *nodes, struct template_state *st)
{
- INDEX_NODE *np;
+ TMPL_NODE *np;
STAILQ_INIT(nodes);
while ((np = next_node(st)))
STAILQ_INSERT_TAIL(nodes, np, next);
}
-INDEX_NODE_LIST node_list = STAILQ_HEAD_INITIALIZER(node_list);
+TMPL_NODE_LIST node_list = STAILQ_HEAD_INITIALIZER(node_list);
-void
+int
parse_template_string(char const *str)
{
struct template_state state;
state.escape = 0;
state.loop = 0;
STAILQ_INIT(&state.cond);
state.start = str;
state.cur = str;
+ state.error = TMPL_NOERR;
parse_template(&node_list, &state);
- assert(state.escape == 0);
- assert(state.loop == 0);
- assert(STAILQ_FIRST(&state.cond) == NULL);
+ if (state.error != TMPL_NOERR) {
+ error("error parsing template near character %d: %s",
+ state.start - str,
+ tmpl_err_str[state.error]);
+ node_list_free(&node_list);
+ return -1;
+ }
+
+ if (state.escape)
+ state.error = TMPL_ERR_ESCAPE_NOT_CLOSED;
+ else if (state.loop)
+ state.error = TMPL_ERR_LOOP_NOT_CLOSED;
+ else if (STAILQ_FIRST(&state.cond))
+ state.error = TMPL_ERR_COND_NOT_CLOSED;
+
+ if (state.error != TMPL_NOERR) {
+ error("error parsing template (at end): %s",
+ tmpl_err_str[state.error]);
+ node_list_free(&node_list);
+ return -1;
+ }
+
+ return 0;
}
-void
+int
parse_template_file(char const *file_name)
{
struct stat st;
char *buf;
FILE *fp;
ssize_t n;
+ int rc;
if (stat(file_name, &st)) {
error("can't stat %s: %s", file_name, strerror(errno));
- return;
+ return -1;
}
fp = fopen(file_name, "r");
if (!fp) {
error("can't open %s: %s", file_name, strerror(errno));
- return;
+ return -1;
+ }
+
+ buf = malloc(st.st_size + 1);
+ if (!buf) {
+ error("can't open %s: %s", file_name, strerror(errno));
+ fclose(fp);
+ return -1;
}
- buf = xmalloc(st.st_size + 1);
n = fread(buf, st.st_size, 1, fp);
if (n == 1) {
buf[st.st_size] = 0;
- parse_template_string(buf);
- } else
+ rc = parse_template_string(buf);
+ } else {
error("error reading from %s: %s", file_name, strerror(errno));
+ rc = 1;
+ }
fclose(fp);
free(buf);
+ return rc;
}
-typedef struct idxent {
- char *name;
- struct stat st;
- char const *type;
- STAILQ_ENTRY(idxent) next;
-} IDXENT;
-typedef struct idxlist {
- size_t count;
- STAILQ_HEAD(, idxent) list;
-} IDXLIST;
-
-static void
-idxlist_init(IDXLIST *lst)
-{
- lst->count = 0;
- STAILQ_INIT(&lst->list);
-}
-
-static void
-idxlist_free(IDXLIST *lst)
-{
- IDXENT *ent;
-
- while ((ent = STAILQ_FIRST(&lst->list))) {
- STAILQ_REMOVE_HEAD(&lst->list, next);
- free(ent->name);
- free(ent);
- }
- lst->count = 0;
-}
-
-static void
-idxlist_append(IDXLIST *lst, IDXENT *ent)
-{
- STAILQ_INSERT_TAIL(&lst->list, ent, next);
- lst->count++;
-}
-
-static int
-idxlist_scan(IDXLIST *idx_list, char const *path, CONFIG const *conf)
-{
- DIR *dir;
- struct dirent *ent;
- IDXENT *idxent;
-
- dir = opendir(path);
- if (!dir) {
- int ec = errno;
- error("can't open directory %s: %s",
- path, strerror(ec));
- return ec;
- }
- idxlist_init(idx_list);
- while ((ent = readdir(dir))) {
- char *name;
- struct stat st;
-
- if (filename_is_hidden(ent->d_name, conf))
- continue;
-
- name = catfile(path, ent->d_name);
-
- if (stat(name, &st)) {
- error("can't stat %s: %s", name, strerror(errno));
- free(name);
- continue;
- }
-
- if (conf->list_unreadable == 0) {
- if (access(name, R_OK)) {
- free(name);
- continue;
- }
- }
-
- idxent = xmalloc(sizeof(*idxent));
-
- if (S_ISDIR(st.st_mode)) {
- size_t len = strlen(ent->d_name);
- idxent->name = xmalloc(len + 2);
- memcpy(idxent->name, ent->d_name, len);
- idxent->name[len++] = '/';
- idxent->name[len] = 0;
- } else
- idxent->name = xstrdup(ent->d_name);
- idxent->st = st;
- idxent->type = get_file_type(name);
- idxlist_append(idx_list, idxent);
- free(name);
- }
- closedir(dir);
-
- return 0;
-}
-
-static void
-idxlist_qsort(IDXLIST *list, int cmp (IDXENT const *, IDXENT const *, void *),
- void *data)
-{
- if (list->count < 2)
- return;
- else if (list->count == 2) {
- IDXENT *a, *b;
- a = STAILQ_FIRST(&list->list);
- b = STAILQ_NEXT(a, next);
- if (cmp(a, b, data) > 0) {
- STAILQ_REMOVE_HEAD(&list->list, next);
- STAILQ_INSERT_TAIL(&list->list, a, next);
- }
- } else {
- IDXENT *cur, *middle;
- IDXLIST high, low;
- int rc;
-
- cur = middle = STAILQ_FIRST(&list->list);
- do {
- cur = STAILQ_NEXT(cur, next);
- if (!cur)
- return;
- } while ((rc = cmp(middle, cur, data)) == 0);
-
- if (rc > 0)
- middle = cur;
-
- idxlist_init(&high);
- idxlist_init(&low);
-
- while ((cur = STAILQ_FIRST(&list->list))) {
- STAILQ_REMOVE_HEAD(&list->list, next);
- if (cmp(middle, cur, data) < 0)
- idxlist_append(&high, cur);
- else
- idxlist_append(&low, cur);
- }
-
- /* Sort both lists */
- idxlist_qsort(&low, cmp, data);
- idxlist_qsort(&high, cmp, data);
-
- /* Join lists in order and return */
- STAILQ_CONCAT(&low.list, &high.list);
- list->list = low.list;
- list->count = low.count + high.count;
- }
-}
-
-static char ordargs[] = "AD";
-static char colargs[] = "NMSD";
-
-INDEX_SORT_COL
-index_sort_col_from_arg(int c)
-{
- char const *p;
- if ((p = strchr(colargs, c)))
- return p - colargs;
- return ISC_NAME;
-}
-
-int
-index_sort_col_to_arg(INDEX_SORT_COL c)
-{
- return colargs[c];
-}
-
-INDEX_SORT_ORD
-index_sort_ord_from_arg(int c)
-{
- char const *p;
- if ((p = strchr(ordargs, c)))
- return p - ordargs;
- return ISO_ASC;
-}
-
-int
-index_sort_ord_to_arg(INDEX_SORT_ORD c)
-{
- return ordargs[c];
-}
+/* Directory tmpl scanner */
-typedef struct {
+/* Run-time template evaluator */
+typedef struct eval_env {
int fd;
CONFIG const *conf;
- IDXENT *ent;
+ DIRLSENT *ent;
int n;
struct wordsplit ws;
int wsflags;
INDEX_SORT_COL col;
INDEX_SORT_ORD ord;
ICON const *icon;
@@ -448,111 +473,92 @@ eval_expand(char const *str, EVAL_ENV *env)
return -1;
env->wsflags |= WRDSF_REUSE;
return 0;
}
static int
-node_text_eval(INDEX_NODE *node, EVAL_ENV *env)
+node_text_eval(TMPL_NODE *node, EVAL_ENV *env)
{
size_t len = strlen(node->v.text);
ssize_t n = write(env->fd, node->v.text, len);
if (n != len) {
if (n == -1)
- error("index write error: %s", strerror(errno));
+ error("tmpl write error: %s", strerror(errno));
else
- error("index write error: %s", "disk full?");
+ error("tmpl write error: %s", "disk full?");
return -1;
}
return 0;
}
static int
-node_expr_eval(INDEX_NODE *node, EVAL_ENV *env)
+node_expr_eval(TMPL_NODE *node, EVAL_ENV *env)
{
size_t len;
ssize_t n;
if (eval_expand(node->v.text, env))
return -1;
len = strlen(env->expansion);
n = write(env->fd, env->expansion, len);
if (n != len) {
if (n == -1)
- error("index write error: %s", strerror(errno));
+ error("tmpl write error: %s", strerror(errno));
else
- error("index write error: %s", "disk full?");
+ error("tmpl write error: %s", "disk full?");
return -1;
}
return 0;
}
-static int node_list_eval(INDEX_NODE_LIST *nodes, EVAL_ENV *env);
-
static int
-node_cond_eval(INDEX_NODE *node, EVAL_ENV *env)
+node_cond_eval(TMPL_NODE *node, EVAL_ENV *env)
{
if (eval_expand(node->v.cond.expr, env))
return -1;
return node_list_eval(&node->v.cond.branch[env->expansion[0] != 0],
env);
}
static int
-node_loop_eval(INDEX_NODE *node, EVAL_ENV *env)
+node_loop_eval(TMPL_NODE *node, EVAL_ENV *env)
{
while (env->ent) {
if (node_list_eval(&node->v.loop, env))
return -1;
env->ent = STAILQ_NEXT(env->ent, next);
env->n++;
}
return 0;
}
-
-static int (*eval[])(INDEX_NODE *node, EVAL_ENV *env) = {
- [NODE_TEXT] = node_text_eval,
- [NODE_LOOP] = node_loop_eval,
- [NODE_COND] = node_cond_eval,
- [NODE_EXPR] = node_expr_eval
-};
-
-static int
-node_list_eval(INDEX_NODE_LIST *nodes, EVAL_ENV *env)
-{
- INDEX_NODE *node;
-
- STAILQ_FOREACH(node, nodes, next) {
- assert(node->type >= 0
- && node->type < sizeof(eval)/sizeof(eval[0])
- && eval[node->type]);
- if (eval[node->type](node, env))
- return -1;
- }
- return 0;
-}
static inline int
streq(const char *v, const char *s, size_t l)
{
return strncmp(v, s, l) == 0 && v[l] == 0;
}
+static inline int
+retstr(char **ret, char const *str)
+{
+ *ret = strdup(str);
+ return *ret ? WRDSE_OK : WRDSE_NOSPACE;
+}
+
static int
exp_rowclass(char **ret, EVAL_ENV *env)
{
- *ret = xstrdup(env->n % 2 ? "odd" : "even");
- return WRDSE_OK;
+ return retstr(ret, env->n % 2 ? "odd" : "even");
}
static int
exp_filename(char **ret, EVAL_ENV *env)
{
- if (env->ent) {
- *ret = xstrdup(env->ent->name);
- return WRDSE_OK;
- } else
+ if (env->ent)
+ return retstr(ret, env->ent->name);
+ else
return WRDSE_UNDEF;
}
static int
exp_filesize(char **ret, EVAL_ENV *env)
{
@@ -566,50 +572,47 @@ exp_filesize(char **ret, EVAL_ENV *env)
if (s < 1024)
break;
s /= 1024;