# HG changeset patch # User Olaf Wintermann # Date 1748607116 -7200 # Node ID dc7cfde0f3bc0a8b810975a7644d9d1505fc28c7 # Parent 2c316612f6483c418d1655e0fce7d3321246ce1a implement new directory listing, that outputs size/lastmodified and has a sorted output diff -r 2c316612f648 -r dc7cfde0f3bc src/server/safs/service.c --- a/src/server/safs/service.c Fri May 30 12:18:15 2025 +0200 +++ b/src/server/safs/service.c Fri May 30 14:11:56 2025 +0200 @@ -40,6 +40,7 @@ #include "../util/strbuf.h" #include +#include #include #include @@ -715,14 +716,114 @@ return http_handle_websocket(sn, rq, &ws); } + +// TODO: maybe move these util functions to another file or library +static char* util_size_str2(const CxAllocator *a, WSBool iscollection, uint64_t contentlength, uint64_t dimension, int precision) { + char *str = cxMalloc(a, 16); + uint64_t size = contentlength; + + if(iscollection) { + str[0] = '\0'; // currently no information for collections + } else if(dimension < 0x400) { + snprintf(str, 16, "%" PRIu64 " bytes", size); + } else if(dimension < 0x100000) { + float s = (float)size/0x400; + int diff = (s*100 - (int)s*100); + if(diff > 90) { + diff = 0; + s += 0.10f; + } + if(dimension < 0x2800 && diff != 0) { + // size < 10 KiB + snprintf(str, 16, "%.*f KiB", precision, s); + } else { + snprintf(str, 16, "%.0f KiB", s); + } + } else if(dimension < 0x40000000) { + float s = (float)size/0x100000; + int diff = (s*100 - (int)s*100); + if(diff > 90) { + diff = 0; + s += 0.10f; + } + if(dimension < 0xa00000 && diff != 0) { + // size < 10 MiB + snprintf(str, 16, "%.*f MiB", precision, s); + } else { + size /= 0x100000; + snprintf(str, 16, "%.0f MiB", s); + } + } else if(dimension < 0x1000000000ULL) { + float s = (float)size/0x40000000; + int diff = (s*100 - (int)s*100); + if(diff > 90) { + diff = 0; + s += 0.10f; + } + if(dimension < 0x280000000 && diff != 0) { + // size < 10 GiB + snprintf(str, 16, "%.*f GiB", precision, s); + } else { + size /= 0x40000000; + snprintf(str, 16, "%.0f GiB", s); + } + } else { + size /= 1024; + float s = (float)size/0x40000000; + int diff = (s*100 - (int)s*100); + if(diff > 90) { + diff = 0; + s += 0.10f; + } + if(dimension < 0x280000000 && diff != 0) { + // size < 10 TiB + snprintf(str, 16, "%.*f TiB", precision, s); + } else { + size /= 0x40000000; + snprintf(str, 16, "%.0f TiB", s); + } + } + return str; +} + +static char* util_size_str(const CxAllocator *a, WSBool iscollection, uint64_t contentlength) { + return util_size_str2(a, iscollection, contentlength, contentlength, 1); +} + +static char* util_date_str(const CxAllocator *a, time_t tm) { + struct tm t; + struct tm n; + time_t now = time(NULL); +#ifdef _WIN32 + memcpy(&t, localtime(&tm), sizeof(struct tm)); + memcpy(&n, localtime(&now), sizeof(struct tm)); +#else + localtime_r(&tm, &t); + localtime_r(&now, &n); +#endif /* _WIN32 */ + char *str = cxMalloc(a, 16); + if(t.tm_year == n.tm_year) { + strftime(str, 16, "%b %d %H:%M", &t); + } else { + strftime(str, 16, "%b %d %Y", &t); + } + return str; +} + +static int cmp_file_type_name(IndexEntry *a, IndexEntry *b) { + if(a->isdir != b->isdir) { + return a->isdir ? -1 : 1; + } + return strcmp(a->name, b->name); +} + int service_index(pblock *pb, Session *sn, Request *rq) { //printf("service_index\n"); + const CxAllocator *a = pool_allocator(sn->pool); char *path = pblock_findkeyval(pb_key_path, rq->vars); char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); - cxstring r_uri = cx_str(uri); - // open the file VFSContext *vfs = vfs_request_context(sn, rq); VFS_DIR dir = vfs_opendir(vfs, path); @@ -733,41 +834,102 @@ sbuf_t *out = sbuf_new(1024); // output buffer // write html header - sbuf_puts(out, "\n\nIndex of "); + sbuf_puts(out, "<!DOCTYPE html>\n<html>\n<head>\n<title>Index of "); sbuf_puts(out, uri); - sbuf_puts(out, "\n\n

Index of "); + sbuf_puts(out, "\n"); + sbuf_puts(out, "\n"); + sbuf_puts(out, "\n

Index of "); sbuf_puts(out, uri); sbuf_puts(out, "


\n\n"); - - // list directory - VFS_ENTRY f; - while(vfs_readdir(dir, &f)) { - cxstring filename = cx_str(f.name); - - sbuf_puts(out, ""); - sbuf_append(out, filename); - sbuf_puts(out, "
\n"); + + CxList *files = cxLinkedListCreate(a, (cx_compare_func)cmp_file_type_name, sizeof(IndexEntry)); + if(!files) { + return REQ_ABORTED; } - sbuf_puts(out, "\n\n\n"); + // read directory at store entries in the files list + int ret = REQ_PROCEED; + VFS_ENTRY f; + while(vfs_readdir_stat(dir, &f)) { + IndexEntry entry; + entry.name = pool_strdup(sn->pool, f.name); + if(!entry.name) { + ret = REQ_ABORTED; + break; + } + if(f.stat_errno == 0) { + entry.isdir = S_ISDIR(f.stat.st_mode); + entry.size = (size_t)f.stat.st_size; + entry.size_str = util_size_str(a, entry.isdir, entry.size); + entry.lastmodified = util_date_str(a, f.stat.st_mtime); + } else { + entry.isdir = 0; + entry.lastmodified = NULL; + entry.size_str = NULL; + entry.size = 0; + } + + if(cxListAdd(files, &entry)) { + ret = REQ_ABORTED; + break; + } + } + + // generate html output + sbuf_puts(out, "\n\n"); + cxListSort(files); + CxIterator i = cxListIterator(files); + cx_foreach(IndexEntry *, entry, i) { + sbuf_puts(out, "\n"); + + sbuf_puts(out, ""); + + sbuf_puts(out, ""); + + sbuf_puts(out, ""); + + sbuf_puts(out, ""); + + sbuf_puts(out, "\n"); + } + + sbuf_puts(out, "
TypeNameSizeLast Modified
"); + sbuf_puts(out, entry->isdir ? "[DIR]" : "[FILE]"); + sbuf_puts(out, ""); + sbuf_puts(out, "name); + sbuf_puts(out, "\">"); + sbuf_puts(out, entry->name); + sbuf_puts(out, ""); + sbuf_puts(out, ""); + if(entry->size_str) { + sbuf_puts(out, entry->size_str); + } + sbuf_puts(out, ""); + if(entry->size_str) { + sbuf_puts(out, entry->lastmodified); + } + sbuf_puts(out, "
\n\n\n"); // send stuff to client - pblock_removekey(pb_key_content_type, rq->srvhdrs); - pblock_kvinsert(pb_key_content_type, "text/html", 9, rq->srvhdrs); - pblock_nninsert("content-length", out->length, rq->srvhdrs); - protocol_status(sn, rq, 200, NULL); - http_start_response(sn, rq); + if(ret == REQ_PROCEED) { + pblock_removekey(pb_key_content_type, rq->srvhdrs); + pblock_kvinsert(pb_key_content_type, "text/html; charset=utf-8", 24, rq->srvhdrs); + pblock_nninsert("content-length", out->length, rq->srvhdrs); + protocol_status(sn, rq, 200, NULL); + http_start_response(sn, rq); - net_write(sn->csd, out->ptr, out->length); + net_write(sn->csd, out->ptr, out->length); + } // close vfs_closedir(dir); sbuf_free(out); - return REQ_PROCEED; + return ret; } int send_options(pblock *pb, Session *sn, Request *rq) { diff -r 2c316612f648 -r dc7cfde0f3bc src/server/safs/service.h --- a/src/server/safs/service.h Fri May 30 12:18:15 2025 +0200 +++ b/src/server/safs/service.h Fri May 30 14:11:56 2025 +0200 @@ -67,6 +67,14 @@ WSBool write_inprogress; WSBool error; } AsyncSendRange; + +typedef struct IndexEntry { + char *name; + char *size_str; + char *lastmodified; + size_t size; + WSBool isdir; +} IndexEntry; int send_file(pblock *pb, Session *sn, Request *rq);