summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2018-02-13 14:53:45 +0200
committerSergey Poznyakoff <gray@gnu.org>2018-02-13 14:53:45 +0200
commit8225aeb234795b16827654c1c351544ec224d777 (patch)
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.
-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
@@ -15,42 +15,44 @@
<img src="{% $(icon blank) %}" alt="[ICO]">
{% else %}
[ICO]
{% 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]">
{% else %}
[PARENTDIR]
{% 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">
{% if $(icon $FILETYPE) %}
<img src="{% $(icon $FILETYPE) %}" alt="[{% $(alt $FILETYPE) %}]">
{% else %}
diff --git a/src/fileserv.c b/src/fileserv.c
index 9fe6772..a1f6ea3 100644
--- a/src/fileserv.c
+++ b/src/fileserv.c
@@ -230,13 +230,14 @@ urimap_find_dir(char const *host, char const *filename)
TAILQ_FOREACH(map, &map_head, next) {
if (map->host && strcasecmp(map->host, host))
continue;
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;
}
void
@@ -366,30 +367,68 @@ make_location(char const *proto, char const *host, char const *url)
strcat(buf, "/");
strcat(buf, url);
strcat(buf, "/");
}
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);
map = urimap_find(host, url);
if (!map)
return MHD_HTTP_NOT_FOUND;
resp->file_name = catfile_n(map->dir, map->dir_len, url + map->uri_len);
if (lstat(resp->file_name, &resp->st))
return errno_to_http_code(errno);
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;
if (url[strlen(url) - 1] != '/') {
resp->location = make_location("http",
host, url);
@@ -402,49 +441,39 @@ get_file_resp(char const *host, char const *url, FILE_RESP *resp)
res = find_index_file(resp->file_name, resp->conf, &cf,
&resp->st);
if (res == MHD_HTTP_OK) {
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));
return MHD_HTTP_INTERNAL_SERVER_ERROR;
}
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;
}
fstat(fd, &resp->st);
resp->fd = fd;
resp->type = "text/html";
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;
}
resp->fd = open(resp->file_name, O_RDONLY);
if (resp->fd == -1) {
@@ -842,15 +871,12 @@ fileserv_handler(void *cls,
const char *url, const char *method,
const char *version,
const char *upload_data, size_t *upload_data_size,
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;
int status;
if (strcmp(method, MHD_HTTP_METHOD_GET) &&
@@ -860,13 +886,13 @@ fileserv_handler(void *cls,
if (&aptr != *con_cls) {
*con_cls = &aptr;
return MHD_YES;
}
*con_cls = NULL;
- status = get_file_resp(host, url, &resp);
+ status = get_file_resp(conn, url, &resp);
switch (status) {
case MHD_HTTP_OK:
break;
case MHD_HTTP_MOVED_PERMANENTLY:
case MHD_HTTP_FOUND:
ret = http_redirect(conn, method, url, status,
diff --git a/src/fileserv.h b/src/fileserv.h
index 816864d..2ef8cab 100644
--- a/src/fileserv.h
+++ b/src/fileserv.h
@@ -66,6 +66,27 @@ DIRCONFIG *dirconfig(char const *path, size_t prefix_len);
void dirconfig_parse(const char *file, DIRCONFIG *conf);
DIRCONFIG *dirconfig_init(void);
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
@@ -253,41 +253,59 @@ parse_template_file(char const *file_name)
typedef struct idxent {
char *name;
struct stat st;
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;
IDXENT *idxent;
dir = opendir(path);
if (!dir) {
int ec = errno;
error("can't open directory %s: %s",
path, strerror(ec));
return ec;
}
- STAILQ_INIT(idx_list);
+ idxlist_init(idx_list);
while ((ent = readdir(dir))) {
char *name;
struct stat st;
if (ent->d_name[0] == '.'
&& (ent->d_name[1] == 0
@@ -307,31 +325,124 @@ idxlist_scan(IDXLIST *idx_list, char *path, DIRCONFIG *conf)
free(name);
continue;
}
}
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;
static int
eval_expand(char const *str, EVAL_ENV *env)
{
@@ -531,13 +642,49 @@ static int
cmd_icon(char **ret, char **argv, EVAL_ENV *env)
{
/* FIXME */
*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)
{
EVAL_ENV *env = clos;
@@ -545,21 +692,79 @@ template_command(char **ret, const char *cmd, size_t len, char **argv,
return cmd_icon(ret, argv, clos);
}
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;
}
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;
EVAL_ENV env;
char const *varenv[5];
@@ -568,17 +773,21 @@ directory_index(int fd, DIRCONFIG *conf, char *uri, char *path)
assert(!STAILQ_EMPTY(&node_list));
}
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;
varenv[2] = "INDEXCSS";
varenv[3] = index_css;
varenv[4] = NULL;

Return to:

Send suggestions and report system problems to the System administrator.