diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2018-02-16 12:34:53 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2018-02-16 12:34:53 +0200 |
commit | c93a6c17a2af32dd15c09adb926088d353ff800d (patch) | |
tree | 3d99f16caeb62c1a7b9d238031247e01f5a35f6e | |
parent | bbb854527f0b841ffedbc2ba769ad495cd13a34d (diff) | |
download | fileserv-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.am | 2 | ||||
-rw-r--r-- | src/config.c | 5 | ||||
-rw-r--r-- | src/dirls.c | 247 | ||||
-rw-r--r-- | src/dirls.h | 17 | ||||
-rw-r--r-- | src/fileserv.c | 24 | ||||
-rw-r--r-- | src/fileserv.h | 4 | ||||
-rw-r--r-- | src/idx.c | 669 |
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 { @@ -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; |