From 8225aeb234795b16827654c1c351544ec224d777 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Tue, 13 Feb 2018 14:53:45 +0200 Subject: Sort produced directory listings. * src/defidx.html: Make query arguments configurable. * src/fileserv.c (urimap_find_dir): Match exact directory name, as well as the one followed by sud-directory components. (get_file_resp): Check for symlinks first. Pass sorting arguments to directory_index. * src/fileserv.h (INDEX_SORT_COL, INDEX_SORT_ORD): New datatypes. (directory_index) (index_sort_col_from_arg) (index_sort_col_to_arg) (index_sort_ord_from_arg) (index_sort_ord_to_arg): New protos. * src/idx.c (IDXLIST): Add count member. (idxlist_init,idxlist_append): New functions. (idxlist_scan): Append '/' to the names of subdirectories. (idxlist_qsort): New function. (index_sort_col_from_arg) (index_sort_col_to_arg) (index_sort_ord_from_arg) (index_sort_ord_to_arg): New functions. (EVAL_ENV): Add sorting order and column members (ord, col). (template_command): New commands: updir, sortorder. (directory_index): Sort the listing. --- src/defidx.html | 14 ++-- src/fileserv.c | 78 ++++++++++++------- src/fileserv.h | 21 ++++++ src/idx.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 301 insertions(+), 43 deletions(-) diff --git a/src/defidx.html b/src/defidx.html index 83935bd..fa3cc66 100644 --- a/src/defidx.html +++ b/src/defidx.html @@ -18,22 +18,23 @@ {% endif %} - Name + Name - Last modified + Last modified - Size + Size - Description + Description
- + {% if $(updir $URI) %} + {% if $(icon back) %} [PARENTDIR] @@ -42,12 +43,13 @@ {% endif %} - Parent Directory + Parent Directory   -   + {% endif %} {% loop %} diff --git a/src/fileserv.c b/src/fileserv.c index 9fe6772..a1f6ea3 100644 --- a/src/fileserv.c +++ b/src/fileserv.c @@ -233,7 +233,8 @@ urimap_find_dir(char const *host, char const *filename) if (map->dir_len > len) continue; if (memcmp(filename, map->dir, map->dir_len) == 0 - && filename[map->dir_len] == '/') + && (filename[map->dir_len] == 0 + || filename[map->dir_len] == '/')) return map; } return NULL; @@ -369,12 +370,33 @@ make_location(char const *proto, char const *host, char const *url) } return buf; } - + +static INDEX_SORT_COL +get_sort_col(struct MHD_Connection *conn) +{ + char const *s = MHD_lookup_connection_value(conn, + MHD_GET_ARGUMENT_KIND, + "C"); + return s ? index_sort_col_from_arg(s[0]) : ISC_NAME; +} + +static INDEX_SORT_COL +get_sort_ord(struct MHD_Connection *conn) +{ + char const *s = MHD_lookup_connection_value(conn, + MHD_GET_ARGUMENT_KIND, + "O"); + return s ? index_sort_ord_from_arg(s[0]) : ISO_ASC; +} + int -get_file_resp(char const *host, char const *url, FILE_RESP *resp) +get_file_resp(struct MHD_Connection *conn, char const *url, FILE_RESP *resp) { struct urimap const *map; char *cf; + char const *host = MHD_lookup_connection_value(conn, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_HOST); file_resp_init(resp); @@ -387,6 +409,23 @@ get_file_resp(char const *host, char const *url, FILE_RESP *resp) resp->conf = dirconfig(resp->file_name, map->dir_len); + if (S_ISLNK(resp->st.st_mode)) { + if (!resp->conf->follow) + return MHD_HTTP_FORBIDDEN; + + cf = cfname(resp->file_name); + if (!cf) { + return MHD_HTTP_NOT_FOUND; + } else if (urimap_find_dir(host, cf)) { + free(resp->file_name); + resp->file_name = cf; + stat(cf, &resp->st); + } else { + free(cf); + return MHD_HTTP_NOT_FOUND; + } + } + if (S_ISDIR(resp->st.st_mode)) { int res; @@ -405,8 +444,11 @@ get_file_resp(char const *host, char const *url, FILE_RESP *resp) free(resp->file_name); resp->file_name = cf; } else if (resp->conf->listing) { - char *template = catfile(tmpdir, "idxXXXXXX"); - int fd = mkstemp(template); + char *template; + int fd; + + template = catfile(tmpdir, "idxXXXXXX"); + fd = mkstemp(template); if (fd == -1) { error("can't create temporary file name: %s", strerror(errno)); @@ -414,8 +456,11 @@ get_file_resp(char const *host, char const *url, FILE_RESP *resp) } unlink(template); free(template); + res = directory_index(fd, resp->conf, url, - resp->file_name); + resp->file_name, + get_sort_col(conn), + get_sort_ord(conn)); if (res) { close(fd); return MHD_HTTP_FORBIDDEN; @@ -426,22 +471,6 @@ get_file_resp(char const *host, char const *url, FILE_RESP *resp) return MHD_HTTP_OK; } else return res; - } - - if (S_ISLNK(resp->st.st_mode)) { - if (!resp->conf->follow) - return MHD_HTTP_FORBIDDEN; - - cf = cfname(resp->file_name); - if (!cf) { - return MHD_HTTP_NOT_FOUND; - } else if (urimap_find_dir(host, cf)) { - free(resp->file_name); - resp->file_name = cf; - } else { - free(cf); - return MHD_HTTP_NOT_FOUND; - } } else if (!S_ISREG(resp->st.st_mode)) { return MHD_HTTP_FORBIDDEN; } @@ -845,9 +874,6 @@ fileserv_handler(void *cls, void **con_cls) { static int aptr; - char const *host = MHD_lookup_connection_value(conn, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_HOST); FILE_RESP resp; struct MHD_Response *response; int ret; @@ -863,7 +889,7 @@ fileserv_handler(void *cls, } *con_cls = NULL; - status = get_file_resp(host, url, &resp); + status = get_file_resp(conn, url, &resp); switch (status) { case MHD_HTTP_OK: break; diff --git a/src/fileserv.h b/src/fileserv.h index 816864d..2ef8cab 100644 --- a/src/fileserv.h +++ b/src/fileserv.h @@ -69,3 +69,24 @@ void dirconfig_free(DIRCONFIG *conf); char *catfile_n(char const *dir, size_t len, char const *file); char *catfile(char const *dir, char const *file); + +typedef enum { + ISC_NAME, + ISC_TIME, + ISC_SIZE, + ISC_DESC +} INDEX_SORT_COL; + +typedef enum { + ISO_ASC, + ISO_DESC +} INDEX_SORT_ORD; + +int directory_index(int fd, + DIRCONFIG const *conf, char const *uri, char const *path, + INDEX_SORT_COL col, INDEX_SORT_ORD ord); + +INDEX_SORT_COL index_sort_col_from_arg(int c); +int index_sort_col_to_arg(INDEX_SORT_COL c); +INDEX_SORT_ORD index_sort_ord_from_arg(int c); +int index_sort_ord_to_arg(INDEX_SORT_ORD c); diff --git a/src/idx.c b/src/idx.c index 623acbb..9398aa4 100644 --- a/src/idx.c +++ b/src/idx.c @@ -256,22 +256,40 @@ typedef struct idxent { char const *type; STAILQ_ENTRY(idxent) next; } IDXENT; -typedef STAILQ_HEAD(, idxent) IDXLIST; +typedef struct idxlist { + size_t count; + STAILQ_HEAD(, idxent) list; +} IDXLIST; + +void +idxlist_init(IDXLIST *lst) +{ + lst->count = 0; + STAILQ_INIT(&lst->list); +} void idxlist_free(IDXLIST *lst) { IDXENT *ent; - while ((ent = STAILQ_FIRST(lst))) { - STAILQ_REMOVE_HEAD(lst, next); + while ((ent = STAILQ_FIRST(&lst->list))) { + STAILQ_REMOVE_HEAD(&lst->list, next); free(ent->name); free(ent); } + lst->count = 0; +} + +void +idxlist_append(IDXLIST *lst, IDXENT *ent) +{ + STAILQ_INSERT_TAIL(&lst->list, ent, next); + lst->count++; } static int -idxlist_scan(IDXLIST *idx_list, char *path, DIRCONFIG *conf) +idxlist_scan(IDXLIST *idx_list, char const *path, DIRCONFIG const *conf) { DIR *dir; struct dirent *ent; @@ -284,7 +302,7 @@ idxlist_scan(IDXLIST *idx_list, char *path, DIRCONFIG *conf) path, strerror(ec)); return ec; } - STAILQ_INIT(idx_list); + idxlist_init(idx_list); while ((ent = readdir(dir))) { char *name; struct stat st; @@ -310,25 +328,118 @@ idxlist_scan(IDXLIST *idx_list, char *path, DIRCONFIG *conf) } idxent = xmalloc(sizeof(*idxent)); - idxent->name = xstrdup(ent->d_name); + + 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); - STAILQ_INSERT_TAIL(idx_list, idxent, next); + idxlist_append(idx_list, idxent); free(name); } closedir(dir); - /* FIXME: sort list */ 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]; +} + typedef struct { int fd; - DIRCONFIG *conf; + DIRCONFIG const *conf; IDXENT *ent; int n; struct wordsplit ws; int wsflags; + INDEX_SORT_COL col; + INDEX_SORT_ORD ord; #define expansion ws.ws_wordv[0] } EVAL_ENV; @@ -534,7 +645,43 @@ cmd_icon(char **ret, char **argv, EVAL_ENV *env) *ret = strdup(""); return *ret ? WRDSE_OK : WRDSE_NOSPACE; } + +static int +cmd_updir(char **ret, char **argv, EVAL_ENV *env) +{ + char *dir = argv[1]; + size_t len; + if (!dir) + dir = "/"; + len = strlen(dir); + if (dir[len-1] == '/') + len--; + while (len > 0 && dir[len-1] != '/') + len--; + *ret = malloc(len + 1); + if (!*ret) + return WRDSE_NOSPACE; + memcpy(*ret, dir, len); + (*ret)[len] = 0; + return WRDSE_OK; +} + +static int +cmd_sortorder(char **ret, char **argv, EVAL_ENV *env) +{ + *ret = malloc(2); + if (!*ret) + return WRDSE_NOSPACE; + (*ret)[0] = + index_sort_ord_to_arg( + (argv[1] && index_sort_col_to_arg(env->col) == argv[1][0]) + ? !env->ord + : ISO_ASC); + (*ret)[1] = 0; + return WRDSE_OK; +} + static int template_command(char **ret, const char *cmd, size_t len, char **argv, void *clos) @@ -548,6 +695,15 @@ template_command(char **ret, const char *cmd, size_t len, char **argv, if (strcmp(argv[0], "alt") == 0) { return cmd_alt(ret, argv, clos); } + + if (strcmp(argv[0], "updir") == 0) { + return cmd_updir(ret, argv, clos); + } + + if (strcmp(argv[0], "sortorder") == 0) { + return cmd_sortorder(ret, argv, clos); + } + return WRDSE_UNDEF; } @@ -555,8 +711,57 @@ static char defidx[] = #include "defidx.h" ; +static inline int +comp_result(int val, void *data) +{ + if (*(INDEX_SORT_ORD*)data == ISO_DESC) + val = -val; + return val; +} + +static int +comp_name(IDXENT const *a, IDXENT const *b, void *data) +{ + return comp_result(strcmp(a->name, b->name), data); +} + +static int +comp_time(IDXENT const *a, IDXENT 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(IDXENT const *a, IDXENT 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[])(IDXENT const *, IDXENT const *, void *) = { + comp_name, + comp_time, + comp_size, + comp_name +}; + int -directory_index(int fd, DIRCONFIG *conf, char *uri, char *path) +directory_index(int fd, DIRCONFIG const *conf, + char const *uri, char const *path, + INDEX_SORT_COL col, INDEX_SORT_ORD ord) { int rc; IDXLIST idxlist; @@ -571,11 +776,15 @@ directory_index(int fd, DIRCONFIG *conf, char *uri, char *path) rc = idxlist_scan(&idxlist, path, conf); if (rc) return rc; + /* FIXME: sort list */ + idxlist_qsort(&idxlist, comp[col], &ord); env.fd = fd; env.conf = conf; - env.ent = STAILQ_FIRST(&idxlist); + env.ent = STAILQ_FIRST(&idxlist.list); env.n = 0; + env.col = col; + env.ord = ord; varenv[0] = "URI"; varenv[1] = uri; -- cgit v1.2.1