--- src/http/modules/ngx_http_autoindex_module.c.820	2009-06-02 20:09:44.000000000 +0400
+++ src/http/modules/ngx_http_autoindex_module.c	2009-10-26 07:42:56.000000000 +0300
@@ -7,6 +7,7 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.h>
+#include <nginx.h>
 
 
 #if 0
@@ -39,12 +40,17 @@
     ngx_flag_t     enable;
     ngx_flag_t     localtime;
     ngx_flag_t     exact_size;
+    ngx_flag_t     noversion;
+
+    ngx_str_t      footer_text;
+    ngx_str_t      header_text;
+    ngx_str_t      readme_text;
 } ngx_http_autoindex_loc_conf_t;
 
 
 #define NGX_HTTP_AUTOINDEX_PREALLOCATE  50
 
-#define NGX_HTTP_AUTOINDEX_NAME_LEN     50
+#define NGX_HTTP_AUTOINDEX_NAME_LEN     75
 
 
 static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one,
@@ -80,6 +86,34 @@
       offsetof(ngx_http_autoindex_loc_conf_t, exact_size),
       NULL },
 
+    { ngx_string("autoindex_noversion"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_autoindex_loc_conf_t, noversion),
+      NULL },
+
+    { ngx_string("autoindex_footer_text"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_autoindex_loc_conf_t, footer_text),
+      NULL },
+
+    { ngx_string("autoindex_header_text"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_autoindex_loc_conf_t, header_text),
+      NULL },
+
+    { ngx_string("autoindex_readme_text"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_autoindex_loc_conf_t, readme_text),
+      NULL },
+
       ngx_null_command
 };
 
@@ -116,33 +150,151 @@
 
 
 static u_char title[] =
-"<html>" CRLF
+"<?xml version=\"1.0\" encoding=\"utf-8\"?>" CRLF
+"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">" CRLF
+"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">" CRLF
+"<head>" CRLF
 "<head><title>Index of "
 ;
 
 
 static u_char header[] =
-"</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
-"<h1>Index of "
+"</title>" CRLF
+"<style type=\"text/css\">" CRLF
+"a, a:active {text-decoration: none; color: blue;}" CRLF
+"a:visited {color: #48468F;}" CRLF
+"a:hover, a:focus {text-decoration: underline; color: red;}" CRLF
+"body {background-color: #F5F5F5;}" CRLF
+"h2 {margin-bottom: 12px;}" CRLF
+"table {margin-left: 12px;}" CRLF
+"th, td { font: 11px monospace; text-align: left;}" CRLF
+"th { font-weight: bold; padding-right: 14px; padding-bottom: 3px;}" CRLF
+"td {padding-right: 14px;}" CRLF
+"td.s, th.s {text-align: right;}" CRLF
+"div.list { background-color: white; border-top: 1px solid #646464; border-bottom: 1px solid #646464; padding-top: 10px; padding-bottom: 14px;}" CRLF
+"div.foot { font: 11px monospace; color: #787878; padding-top: 4px;}" CRLF
+"</style>" CRLF
+"</head>" CRLF
+"<body>" CRLF
+;
+
+
+static u_char header2[] =
+"<h2>Index of "
+;
+
+
+static u_char header3[] =
+"</h2>" CRLF
+"<div class=\"list\">" CRLF
+;
+
+static u_char header4[] =
+"<table summary=\"Directory Listing\" cellpadding=\"0\" cellspacing=\"0\">" CRLF
+"<thead><tr><th class=\"n\">Name</th><th class=\"m\">Last Modified</th><th class=\"s\">Size</th></tr></thead>" CRLF
+"<tbody>" CRLF
 ;
 
+
 static u_char tail[] =
+"</tbody>" CRLF
+"</table>" CRLF
+;
+
+
+static u_char tail2[] =
+"</div>" CRLF
+"<div class=\"foot\">"
+;
+
+
+static u_char tail3[] =
+"</div>" CRLF
 "</body>" CRLF
 "</html>" CRLF
 ;
 
 
+int LI_ltostr(char *buf, long val) {
+        char swap;
+        char *end;
+        int len = 1;
+
+        if (val < 0) {
+                len++;
+                *(buf++) = '-';
+                val = -val;
+        }
+
+        end = buf;
+        while (val > 9) {
+                *(end++) = '0' + (val % 10);
+                val = val / 10;
+        }
+        *(end) = '0' + val;
+        *(end + 1) = '\0';
+        len += end - buf;
+
+        while (buf < end) {
+                swap = *end;
+                *end = *buf;
+                *buf = swap;
+
+                buf++;
+                end--;
+        }
+
+        return len;
+}
+
+
+static int http_list_directory_sizefmt(char *buf, off_t size) {
+	const char unit[] = "KMGTPE";	/* Kilo, Mega, Tera, Peta, Exa */
+	const char *u = unit - 1;		/* u will always increment at least once */
+	int remain;
+	char *out = buf;
+
+	if (size < 100)
+		size += 99;
+	if (size < 100)
+		size = 0;
+
+	while (1) {
+		remain = (int) size & 1023;
+		size >>= 10;
+		u++;
+		if ((size & (~0 ^ 1023)) == 0)
+			break;
+	}
+
+	remain /= 100;
+	if (remain > 9)
+		remain = 9;
+	if (size > 999) {
+		size   = 0;
+		remain = 9;
+		u++;
+	}
+
+	out   += LI_ltostr(out, size);
+	out[0] = '.';
+	out[1] = remain + '0';
+	out[2] = *u;
+	out[3] = '\0';
+
+	return (out + 3 - buf);
+}
+
+
 static ngx_int_t
 ngx_http_autoindex_handler(ngx_http_request_t *r)
 {
-    u_char                         *last, *filename, scale;
-    off_t                           length;
+    u_char                         *last, *filename;
     size_t                          len, utf_len, allocated, root;
     ngx_tm_t                        tm;
     ngx_err_t                       err;
     ngx_buf_t                      *b;
-    ngx_int_t                       rc, size;
+    ngx_int_t                       rc;
     ngx_str_t                       path;
     ngx_dir_t                       dir;
     ngx_uint_t                      i, level, utf8;
@@ -366,23 +518,30 @@
     len = sizeof(title) - 1
           + r->uri.len
           + sizeof(header) - 1
+          + (alcf->header_text.len > 0 ? sizeof("<pre class=\"header\">") + alcf->header_text.len + sizeof("</pre>") - 2:0)
+          + sizeof(header2) - 1
           + r->uri.len
-          + sizeof("</h1>") - 1
-          + sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1
-          + sizeof("</pre><hr>") - 1
-          + sizeof(tail) - 1;
+          + sizeof(header3) - 1
+          + (alcf->readme_text.len > 0 ? sizeof("<pre class=\"readme\">") + alcf->readme_text.len + sizeof("</pre>") - 2:0)
+          + sizeof(header4) - 1
+          + sizeof("<tr><td class=\"n\"><a href=\"../\">Parent Directory/</a></td><td class=\"m\">&nbsp;</td><td class=\"s\">- &nbsp;</td></tr>" CRLF) - 1
+          + sizeof(tail) - 1
+          + (alcf->footer_text.len > 0 ? sizeof("<pre class=\"header\">") + alcf->footer_text.len + sizeof("</pre>") - 2:0)
+          + sizeof(tail2) - 1
+          + (alcf->noversion?sizeof(NGINX_VER):sizeof("nginx")) - 1
+          + sizeof(tail3) - 1;
 
     entry = entries.elts;
     for (i = 0; i < entries.nelts; i++) {
-        len += sizeof("<a href=\"") - 1
+        len += sizeof("<tr><td class=\"n\"><a href=\"") - 1
             + entry[i].name.len + entry[i].escape
             + 1                                          /* 1 is for "/" */
             + sizeof("\">") - 1
             + entry[i].name.len - entry[i].utf_len + entry[i].colon * 2
             + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof("&gt;") - 2
-            + sizeof("</a>") - 1
-            + sizeof(" 28-Sep-1970 12:00 ") - 1
-            + 20                                         /* the file size */
+            + sizeof("</a></td><td class=\"m\">&nbsp;</td><td class=\"s\">- &nbsp;</td></tr>") - 1
+            + sizeof(" 28-Sep-1970 12:00:00 ") - 1
+            + (alcf->exact_size?20:sizeof("999.9K"))     /* the file size */
             + 2;
     }
 
@@ -400,16 +559,30 @@
     b->last = ngx_cpymem(b->last, title, sizeof(title) - 1);
     b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
     b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);
+    if (alcf->header_text.len > 0) {
+        b->last = ngx_cpymem(b->last, "<pre class=\"header\">", sizeof("<pre class=\"header\">") - 1);
+        b->last = ngx_cpymem(b->last, alcf->header_text.data, alcf->header_text.len);
+        b->last = ngx_cpymem(b->last, "</pre>", sizeof("</pre>") - 1);
+    }
+    b->last = ngx_cpymem(b->last, header2, sizeof(header2) - 1);
+
     b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
-    b->last = ngx_cpymem(b->last, "</h1>", sizeof("</h1>") - 1);
+    b->last = ngx_cpymem(b->last, header3, sizeof(header3) - 1);
 
-    b->last = ngx_cpymem(b->last, "<hr><pre><a href=\"../\">../</a>" CRLF,
-                         sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1);
+    if (alcf->readme_text.len > 0) {
+//        b->last = ngx_cpymem(b->last, "<pre class=\"readme\">", sizeof("<pre class=\"readme\">") - 1);
+        b->last = ngx_cpymem(b->last, alcf->readme_text.data, alcf->readme_text.len);
+//        b->last = ngx_cpymem(b->last, "</pre>", sizeof("</pre>") - 1);
+    }
+
+    b->last = ngx_cpymem(b->last, header4, sizeof(header4) - 1);
+    b->last = ngx_cpymem(b->last, "<tr><td class=\"n\"><a href=\"../\">Parent Directory/</a></td><td class=\"m\">&nbsp;</td><td class=\"s\">- &nbsp;</td></tr>" CRLF,
+                         sizeof("<tr><td class=\"n\"><a href=\"../\">Parent Directory/</a></td><td class=\"m\">&nbsp;</td><td class=\"s\">- &nbsp;</td></tr>" CRLF) - 1);
 
     tp = ngx_timeofday();
 
     for (i = 0; i < entries.nelts; i++) {
-        b->last = ngx_cpymem(b->last, "<a href=\"", sizeof("<a href=\"") - 1);
+        b->last = ngx_cpymem(b->last, "<tr><td class=\"n\"><a href=\"", sizeof("<tr><td class=\"n\"><a href=\"") - 1);
 
         if (entry[i].colon) {
             *b->last++ = '.';
@@ -455,7 +628,7 @@
         }
 
         if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
-            b->last = ngx_cpymem(last, "..&gt;</a>", sizeof("..&gt;</a>") - 1);
+            b->last = ngx_cpymem(last, "..&gt;</a></td>", sizeof("..&gt;</a></td>") - 1);
 
         } else {
             if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
@@ -463,72 +636,36 @@
                 len++;
             }
 
-            b->last = ngx_cpymem(b->last, "</a>", sizeof("</a>") - 1);
-            ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len);
-            b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len;
+            b->last = ngx_cpymem(b->last, "</a></td>", sizeof("</a></td>") - 1);
         }
 
-        *b->last++ = ' ';
-
         ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);
 
-        b->last = ngx_sprintf(b->last, "%02d-%s-%d %02d:%02d ",
+        b->last = ngx_sprintf(b->last, "<td class=\"m\">%02d-%s-%d %02d:%02d:%02d</td>",
                               tm.ngx_tm_mday,
                               months[tm.ngx_tm_mon - 1],
                               tm.ngx_tm_year,
                               tm.ngx_tm_hour,
-                              tm.ngx_tm_min);
+                              tm.ngx_tm_min,
+                              tm.ngx_tm_sec);
 
         if (alcf->exact_size) {
             if (entry[i].dir) {
-                b->last = ngx_cpymem(b->last,  "                  -",
-                                     sizeof("                  -") - 1);
+                b->last = ngx_cpymem(b->last,  "<td class=\"s\">- &nbsp;</td></tr>",
+                                     sizeof("<td class=\"s\">- &nbsp;</td></tr>") - 1);
             } else {
-                b->last = ngx_sprintf(b->last, "%19O", entry[i].size);
+                b->last = ngx_sprintf(b->last, "<td class=\"s\">%O</td></tr>", entry[i].size);
             }
 
         } else {
             if (entry[i].dir) {
-                b->last = ngx_cpymem(b->last,  "      -",
-                                     sizeof("      -") - 1);
+                b->last = ngx_cpymem(b->last,  "<td class=\"s\">- &nbsp;</td></tr>",
+                                     sizeof("<td class=\"s\">- &nbsp;</td></tr>") - 1);
 
             } else {
-                length = entry[i].size;
-
-                if (length > 1024 * 1024 * 1024 - 1) {
-                    size = (ngx_int_t) (length / (1024 * 1024 * 1024));
-                    if ((length % (1024 * 1024 * 1024))
-                                                > (1024 * 1024 * 1024 / 2 - 1))
-                    {
-                        size++;
-                    }
-                    scale = 'G';
-
-                } else if (length > 1024 * 1024 - 1) {
-                    size = (ngx_int_t) (length / (1024 * 1024));
-                    if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {
-                        size++;
-                    }
-                    scale = 'M';
-
-                } else if (length > 9999) {
-                    size = (ngx_int_t) (length / 1024);
-                    if (length % 1024 > 511) {
-                        size++;
-                    }
-                    scale = 'K';
-
-                } else {
-                    size = (ngx_int_t) length;
-                    scale = '\0';
-                }
-
-                if (scale) {
-                    b->last = ngx_sprintf(b->last, "%6i%c", size, scale);
-
-                } else {
-                    b->last = ngx_sprintf(b->last, " %6i", size);
-                }
+                char sizebuf[sizeof("999.9K")];
+                http_list_directory_sizefmt(sizebuf, entry[i].size);
+                b->last = ngx_sprintf(b->last, "<td class=\"s\">%s</td></tr>", sizebuf);
             }
         }
 
@@ -538,9 +675,16 @@
 
     /* TODO: free temporary pool */
 
-    b->last = ngx_cpymem(b->last, "</pre><hr>", sizeof("</pre><hr>") - 1);
-
     b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);
+    if (alcf->footer_text.len > 0) {
+        b->last = ngx_cpymem(b->last, "<pre class=\"footer\">", sizeof("<pre class=\"footer\">") - 1);
+        b->last = ngx_cpymem(b->last, alcf->footer_text.data, alcf->footer_text.len);
+        b->last = ngx_cpymem(b->last, "</pre>", sizeof("</pre>") - 1);
+    }
+    b->last = ngx_cpymem(b->last, tail2, sizeof(tail2) - 1);
+    if (alcf->noversion) b->last = ngx_cpymem(b->last, "nginx", sizeof("nginx") - 1);
+    else b->last = ngx_cpymem(b->last, NGINX_VER, sizeof(NGINX_VER) - 1);
+    b->last = ngx_cpymem(b->last, tail3, sizeof(tail3) - 1);
 
     if (r == r->main) {
         b->last_buf = 1;
@@ -638,6 +782,7 @@
     conf->enable = NGX_CONF_UNSET;
     conf->localtime = NGX_CONF_UNSET;
     conf->exact_size = NGX_CONF_UNSET;
+    conf->noversion = NGX_CONF_UNSET;
 
     return conf;
 }
@@ -651,7 +796,11 @@
 
     ngx_conf_merge_value(conf->enable, prev->enable, 0);
     ngx_conf_merge_value(conf->localtime, prev->localtime, 0);
-    ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1);
+    ngx_conf_merge_value(conf->exact_size, prev->exact_size, 0);
+    ngx_conf_merge_value(conf->noversion, prev->noversion, 0);
+    ngx_conf_merge_str_value(conf->footer_text, prev->footer_text, "");
+    ngx_conf_merge_str_value(conf->header_text, prev->header_text, "");
+    ngx_conf_merge_str_value(conf->readme_text, prev->readme_text, "");
 
     return NGX_CONF_OK;
 }
