summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org>2018-02-13 12:53:45 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2018-02-13 12:53:45 (GMT)
commit8225aeb234795b16827654c1c351544ec224d777 (patch) (side-by-side diff)
treeb2234583d4c67cd7f42699dd888b3587936d28ce
parentf4943a7aa280404648ff2862ef3c3f476b939194 (diff)
downloadfileserv-8225aeb234795b16827654c1c351544ec224d777.tar.gz
fileserv-8225aeb234795b16827654c1c351544ec224d777.tar.bz2
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.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--src/defidx.html14
-rw-r--r--src/fileserv.c78
-rw-r--r--src/fileserv.h21
-rw-r--r--src/idx.c231
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 %}
</th>
<th class="indexcolname">
- <a href="?C=N;O=D">Name</a>
+ <a href="?C=N&amp;O={% $(sortorder N) %}">Name</a>
</th>
<th class="indexcollastmod">
- <a href="?C=M;O=A">Last modified</a>
+ <a href="?C=M&amp;O={% $(sortorder M) %}">Last modified</a>
</th>
<th class="indexcolsize">
- <a href="?C=S;O=A">Size</a>
+ <a href="?C=S&amp;O={% $(sortorder S) %}">Size</a>
</th>
<th class="indexcoldesc">
- <a href="?C=D;O=A">Description</a>
+ <a href="?C=D&amp;O={% $(sortorder D) %}">Description</a>
</th>
</tr>
<tr class="indexbreakrow">
<th colspan="5"><hr></th>
</tr>
- <tr class="even">
+ {% if $(updir $URI) %}
+ <tr class="odd">
<td class="indexcolicon">
{% if $(icon back) %}
<img src="{% $(icon 'back') %}" alt="[PARENTDIR]">
@@ -42,12 +43,13 @@
{% endif %}
</td>
<td class="indexcolname">
- <a href="/">Parent Directory</a>
+ <a href="{% $(updir $URI) %}">Parent Directory</a>
</td>
<td class="indexcollastmod">&nbsp;</td>
<td class="indexcolsize"> - </td>
<td>&nbsp;</td>
</tr>
+ {% endif %}
{% loop %}
<tr class="{% $ROWCLASS %}">
<td class="indexcolicon">
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;

Return to:

Send suggestions and report system problems to the System administrator.