Index: src/browse.c
===================================================================
--- src/browse.c
+++ src/browse.c
@@ -93,10 +93,66 @@
     zSep = "/";
     while( zPath[j]=='/' ){ j++; }
   }
 }
 
+void get_filemeta(const char* zFN, char** zAuthor, char** zComment, char** mtime)
+{
+    Stmt q;
+    int fnid;
+    int mid;
+    int ztime;
+
+    int min   = 60;
+    int hour  = min * 60;
+    int day   = hour * 24;
+    int week  = day * 7;
+    int month = week * 4;
+    int year  = month * 12;
+
+    db_prepare(&q, "SELECT fnid FROM filename WHERE name=:x");
+    db_bind_text(&q, ":x", zFN);
+    db_step(&q);
+
+    fnid = db_column_int(&q, 0);
+    db_finalize(&q);
+
+    db_prepare(&q, "SELECT max(mid) FROM mlink WHERE fnid=:x");
+    db_bind_int(&q, ":x", fnid);
+    db_step(&q);
+
+    mid = db_column_int(&q, 0);
+    db_finalize(&q);
+
+    db_prepare(&q, "SELECT (julianday('now') - julianday(mtime)) * 86400, user, comment FROM event WHERE objid=:x");
+    db_bind_int(&q, ":x", mid);
+    db_step(&q);
+
+    ztime       = db_column_int(&q, 0);
+    (*zAuthor)  = db_column_text(&q, 1);
+    (*zComment) = db_column_text(&q, 2);
+
+    if (ztime == 0){
+        mtime[0] = 0;
+        return;
+    }
+
+    if (ztime - year >= 0)
+        sprintf(*mtime, "%i years", ztime/year);
+    else if (ztime - month >= 0)
+        sprintf(*mtime, "%i months", ztime/month);
+    else if (ztime - week >= 0)
+        sprintf(*mtime, "%i weeks", ztime/week);
+    else if (ztime - day >= 0)
+        sprintf(*mtime, "%i days", ztime/day);
+    else if (ztime - hour >= 0)
+        sprintf(*mtime, "%i hours", ztime/hour);
+    else if (ztime - min >= 0)
+        sprintf(*mtime, "%i minutes", ztime/min);
+    else
+        sprintf(*mtime, "%i seconds", ztime);
+}
 
 /*
 ** WEBPAGE: dir
 **
 ** Query parameters:
@@ -105,13 +161,10 @@
 **    ci=LABEL         Show only files in this check-in.  Optional.
 */
 void page_dir(void){
   const char *zD = P("name");
   int nD = zD ? strlen(zD)+1 : 0;
-  int mxLen;
-  int nCol, nRow;
-  int cnt, i;
   char *zPrefix;
   Stmt q;
   const char *zCI = P("ci");
   int rid = 0;
   char *zUuid = 0;
@@ -235,40 +288,41 @@
     db_multi_exec(
       "INSERT OR IGNORE INTO localfiles"
       " SELECT pathelement(name,0), NULL FROM filename"
     );
   }
-
-  /* Generate a multi-column table listing the contents of zD[]
-  ** directory.
-  */
-  mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
-  cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/");
-  nCol = 4;
-  nRow = (cnt+nCol-1)/nCol;
   db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
-  @ <table class="browser"><tr><td class="browser"><ul class="browser">
-  i = 0;
-  while( db_step(&q)==SQLITE_ROW ){
-    const char *zFN;
-    if( i==nRow ){
-      @ </ul></td><td class="browser"><ul class="browser">
-      i = 0;
-    }
-    i++;
-    zFN = db_column_text(&q, 0);
-    if( zFN[0]=='/' ){
-      zFN++;
-      @ <li><a href="%s(zSubdirLink)%T(zFN)">%h(zFN)/</a></li>
-    }else if( zCI ){
-      const char *zUuid = db_column_text(&q, 1);
-      @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a></li>
-    }else{
-      @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
-      @     </a></li>
-    }
+  @ <table class="report browse" border="1"><tr><th>File</th><th>Age</th><th>Author</th><th>Last comment</th></tr>
+
+  while (db_step(&q) == SQLITE_ROW) {
+   const char *zFN;
+   char *zAuthor  = NULL;
+   char *zComment = NULL;
+
+   zFN = db_column_text(&q, 0);
+   char *zFNPath  = malloc(strlen(zPrefix) + strlen(zFN) + 2);
+   char *zMtime   = malloc(32);
+
+   strcpy(zFNPath, zPrefix);
+   strcat(zFNPath, zFN);
+
+   get_filemeta(zFNPath, &zAuthor, &zComment, &zMtime);
+
+   if (zFN[0] == '/') {
+       zFN++;
+       @ <tr><td><a href="%s(zSubdirLink)%T(zFN)">%h(zFN)/</a></td><td nowrap>%s(zMtime)</td><td>%s(zAuthor)</td><td>%s(zComment)</td></tr>
+   }
+   else if (zCI) {
+       const char *zUuid = db_column_text(&q, 1);
+       @ <tr><td><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a></td><td nowrap>%s(zMtime)</td><td>%s(zAuthor)</td><td>%s(zComment)</td></tr>
+   }
+   else
+       @ <tr><td><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)</a></td><td nowrap>%s(zMtime)</td><td>%s(zAuthor)</td><td>%s(zComment)</td></tr>
+
+   free(zFNPath);
+   free(zMtime);
   }
   db_finalize(&q);
   manifest_destroy(pM);
-  @ </ul></td></tr></table>
+  @ </table>
   style_footer();
 }


