1. The Whole Damn Thing
  2. Introduction
  3. Apache configuration
  4. Web calendars
  5. Avoid NIH: use cal to generate months
  6. Generated templates
  7. File listing
  8. Security notes
  9. Script: autoindex
  10. Script: dirindex
  11. The final display
  12. Stylesheet: 2col.css
  13. Installation
  14. Feedback

1. The Whole Damn Thing

article.tgz holds this article plus all the scripts, templates, CSS stuff, etc.

2. Introduction

I've been using Firefox as a file-browser for years. The default file display provided by Apache is OK, but it's not great.

I tried some tweaks but they weren't much of an improvement, and I had a specific itch to scratch: rapidly searching or reading dated files like firewall- or system-logs.

For example, I rotate my system logs at midnight by creating hard-links from /var/log files to files under a dated directory. On 15 Apr, the tree looked like this:

FILETREE                    HARDLINKED TO
/var
+--log
|   +--2026
|   |   +--0415
|   |   |   +--cron         /var/log/cron
|   |   |   +--daemon       /var/log/daemon
|   |   |   +--debug.log    ...
|   |   |   +--local0log
|   |   |   +--local1log
|   |   |   +--local2log
|   |   |   +--local3log
|   |   |   +--local4log
|   |   |   +--local5log
|   |   |   +--local6log
|   |   |   +--local7log
|   |   |   +--maillog
|   |   |   +--ntplog
|   |   |   +--secure
|   |   |   +--syslog       /var/log/syslog

I want a calendar on the right half of the screen. Clicking 4/15/2026 gives me something like this on the left half, where the filenames are URLs pointing to the associated files:

561422 15-Apr-2026 23:59:57 cron
   101 15-Apr-2026 00:01:00 daemon
    37 15-Apr-2026 00:00:01 debug.log
    37 15-Apr-2026 00:00:01 local0log
    37 15-Apr-2026 00:00:01 local1log
    37 15-Apr-2026 00:00:01 local2log
    37 15-Apr-2026 00:00:01 local3log
    37 15-Apr-2026 00:00:01 local4log
    37 15-Apr-2026 00:00:01 local5log
285736 15-Apr-2026 23:36:18 local6log
   194 15-Apr-2026 00:02:00 local7log
  4663 15-Apr-2026 05:38:37 maillog
   140 15-Apr-2026 06:22:28 ntplog
    37 15-Apr-2026 00:00:01 secure
 41485 15-Apr-2026 23:59:57 syslog

Permissions, etc. are dealt with elsewhere.

3. Apache configuration

My httpd.conf:

# DirectoryIndex: sets the file that Apache will serve if a directory
# is requested.  Basics first...
<IfModule dir_module>
    DirectoryIndex index.htm index.html index.xml index.txt
</IfModule>

# ...then append index.php to DirectoryIndex...
<IfDefine php_module.c>
    <FilesMatch \.php$>
        SetHandler application/x-httpd-php
    </FilesMatch>

    AddType application/x-httpd-php .php
    AddType application/x-httpd-php-source .phps
    <IfModule dir_module>
        DirectoryIndex index.php
    </IfModule>
</IfDefine>

# ...and finally append autoindex -- must come last.
# Sat, 14 Nov 2020 03:53:51 -0500
#   Disable if you want to autogenerate fancy indexes.

<IfModule dir_module>
    DirectoryIndex /cgi-bin/autoindex
</IfModule>

4. Web calendars

Dealing with dates is a PITA, but web access takes it to an entirely new level:

5. Avoid NIH: use cal to generate months

cal does everything I need for a readable month. All I had to do was add URLs for each day. cal output for Oct 2015 looks like this:

    October 2015
 Su Mo Tu We Th Fr Sa
              1  2  3
  4  5  6  7  8  9 10
 11 12 13 14 15 16 17
 18 19 20 21 22 23 24
 25 26 27 28 29 30 31

5.1. Script: mon2pre

mon2pre reads cal output and writes a table.

cal 10 2015 | mon2pre | tidy > sample-oct.htm

The tidied-up HTML looks like this. I frequently use days as folders, so the CSS "class=e" is for an empty day, and "class=m" is for a date that should be marked as (say) containing files.

<!DOCTYPE html>
<!-- Generator: mon2pre -->
<html>
  <head>
    <title>October 2015</title>
    <style>
    a         { text-decoration: none; }
    a:link    { color: blue; }
    a:visited { color: red; }
    a:hover   { background-color: black; color: white; }
    </style>
  </head>
  <body>
    <pre>
          Oct
 Su Mo Tu We Th Fr Sa
            <a class="e"
     href="TOP/2015/1001">  1</a><a class="e"
     href="TOP/2015/1002">  2</a><a class="e"
     href="TOP/2015/1003">  3</a>
<a class="e"
     href="TOP/2015/1004">  4</a><a class="e"
     href="TOP/2015/1005">  5</a><a class="e"
     href="TOP/2015/1006">  6</a><a class="e"
     href="TOP/2015/1007">  7</a><a class="e"
     href="TOP/2015/1008">  8</a><a class="e"
     href="TOP/2015/1009">  9</a><a class="e"
     href="TOP/2015/1010"> 10</a>
<a class="e"
     href="TOP/2015/1011"> 11</a><a class="e"
     href="TOP/2015/1012"> 12</a><a class="e"
     href="TOP/2015/1013"> 13</a><a class="e"
     href="TOP/2015/1014"> 14</a><a class="e"
     href="TOP/2015/1015"> 15</a><a class="e"
     href="TOP/2015/1016"> 16</a><a class="e"
     href="TOP/2015/1017"> 17</a>
<a class="e"
     href="TOP/2015/1018"> 18</a><a class="e"
     href="TOP/2015/1019"> 19</a><a class="e"
     href="TOP/2015/1020"> 20</a><a class="e"
     href="TOP/2015/1021"> 21</a><a class="e"
     href="TOP/2015/1022"> 22</a><a class="e"
     href="TOP/2015/1023"> 23</a><a class="e"
     href="TOP/2015/1024"> 24</a>
<a class="e"
     href="TOP/2015/1025"> 25</a><a class="e"
     href="TOP/2015/1026"> 26</a><a class="e"
     href="TOP/2015/1027"> 27</a><a class="e"
     href="TOP/2015/1028"> 28</a><a class="e"
     href="TOP/2015/1029"> 29</a><a class="e"
     href="TOP/2015/1030"> 30</a><a class="e"
     href="TOP/2015/1031"> 31</a>

</pre>
  </body>
</html>

oct.htm has the HTML, and oct.jpg has a screenshot. The script has an option -b (body only) in case I want to use the output as part of a larger page that already has CSS and a container for multiple months.

It's intended to be a template; replace "TOP" with the top-level directory you want to display.

5.2. Script: mkmonths

mkmonths runs mon2pre for several years. I stopped at 2038 since that's going to be our next Unix clean-up-the-code year:

#!/bin/ksh
# generate plain/html calendars

export PATH=/usr/local/bin:/bin:/usr/bin

for year in $(seq 1990 2038); do
    for mon in $(seq -w 1 12); do
        echo ${year}-${mon}
        cal $mon $year | ./mon2pre -b > templates/${year}/${mon}.tpl
    done
done

6. Generated templates

Unless someone redoes the Gregorian calendar, these templates should do the trick. The templates directory for 2025 looks like this:

autoindex
+--templates
|   +--2025
|   |   +--01.tpl
|   |   +--02.tpl
|   |   +--03.tpl
|   |   +--04.tpl
|   |   +--05.tpl
|   |   +--06.tpl
|   |   +--07.tpl
|   |   +--08.tpl
|   |   +--09.tpl
|   |   +--10.tpl
|   |   +--11.tpl
|   |   +--12.tpl
|   +--2025.htm
|   +--2025.tpl

The January 2025 template (01.tpl) is the calendar body contained within the <pre> tags:

          Jan
 Su Mo Tu We Th Fr Sa
         <a class="e"
     href="TOP/2025/0101">  1</a><a class="e"
     href="TOP/2025/0102">  2</a><a class="e"
     href="TOP/2025/0103">  3</a><a class="e"
     href="TOP/2025/0104">  4</a>
<a class="e"
     href="TOP/2025/0105">  5</a><a class="e"
     href="TOP/2025/0106">  6</a><a class="e"
     href="TOP/2025/0107">  7</a><a class="e"
     href="TOP/2025/0108">  8</a><a class="e"
     href="TOP/2025/0109">  9</a><a class="e"
     href="TOP/2025/0110"> 10</a><a class="e"
     href="TOP/2025/0111"> 11</a>
<a class="e"
     href="TOP/2025/0112"> 12</a><a class="e"
     href="TOP/2025/0113"> 13</a><a class="e"
     href="TOP/2025/0114"> 14</a><a class="e"
     href="TOP/2025/0115"> 15</a><a class="e"
     href="TOP/2025/0116"> 16</a><a class="e"
     href="TOP/2025/0117"> 17</a><a class="e"
     href="TOP/2025/0118"> 18</a>
<a class="e"
     href="TOP/2025/0119"> 19</a><a class="e"
     href="TOP/2025/0120"> 20</a><a class="e"
     href="TOP/2025/0121"> 21</a><a class="e"
     href="TOP/2025/0122"> 22</a><a class="e"
     href="TOP/2025/0123"> 23</a><a class="e"
     href="TOP/2025/0124"> 24</a><a class="e"
     href="TOP/2025/0125"> 25</a>
<a class="e"
     href="TOP/2025/0126"> 26</a><a class="e"
     href="TOP/2025/0127"> 27</a><a class="e"
     href="TOP/2025/0128"> 28</a><a class="e"
     href="TOP/2025/0129"> 29</a><a class="e"
     href="TOP/2025/0130"> 30</a><a class="e"
     href="TOP/2025/0131"> 31</a>

I needed a quick way to include month tables in a certain order, and a shell script plus M4 is about as easy as it gets. The 2025.tpl file holds M4 commands to create a 4-row/3-col calendar for 2025, with TOP replaced by /home/notebook:

define(`TOP',`/home/notebook')dnl
sinclude(`header')dnl
<table><thead><tr><th></th><th>2025</th><th></th></tr></thead><tbody>
<!-- row 1/4 -->
<tr>
<td valign="top"><pre class="calendar">sinclude(`2025/01.tpl')</pre></td>
<td valign="top"><pre class="calendar">sinclude(`2025/02.tpl')</pre></td>
<td valign="top"><pre class="calendar">sinclude(`2025/03.tpl')</pre></td>
</tr>
<!-- row 2/4 -->
<tr>
<td valign="top"><pre class="calendar">sinclude(`2025/04.tpl')</pre></td>
<td valign="top"><pre class="calendar">sinclude(`2025/05.tpl')</pre></td>
<td valign="top"><pre class="calendar">sinclude(`2025/06.tpl')</pre></td>
</tr>
<!-- row 3/4 -->
<tr>
<td valign="top"><pre class="calendar">sinclude(`2025/07.tpl')</pre></td>
<td valign="top"><pre class="calendar">sinclude(`2025/08.tpl')</pre></td>
<td valign="top"><pre class="calendar">sinclude(`2025/09.tpl')</pre></td>
</tr>
<!-- row 4/4 -->
<tr>
<td valign="top"><pre class="calendar">sinclude(`2025/10.tpl')</pre></td>
<td valign="top"><pre class="calendar">sinclude(`2025/11.tpl')</pre></td>
<td valign="top"><pre class="calendar">sinclude(`2025/12.tpl')</pre></td>
</tr>
</tbody></table>
sinclude(`footer')dnl

The header and footer include files hold everything else needed to create a page table.

This covers the right side of the screen, leaving the left side free for displaying directories and files.

7. File listing

I wanted something like a basic Apache file list with directories first, some whitespace, and then plain files (ignoring dotfiles and other cruft). It should include filetype icons plus basic metadata like filesize and modification time.

A CGI script can handle this without excessive overhead, since I'm using pre-generated date templates for everything except the file list. I have old machines and I'm not patient; when my response time gets annoying, I'll trade up to whatever the cool kids are using.

One caveat: NO Java or Javascript. I consider those tools of Satan.

8. Security notes

9. Script: autoindex

This is a top-level driver that finds the type and location of the directory we're displaying. It's different for (say) a syslog or firewall-log directory vs. my home directory. It looks for a file called .autoindex holding one line -- the script that does the real work of listing files on one side and a calendar on the other.

10. Script: dirindex

This is where the real work happens. It's a perl CGI script which uses the DOCUMENT_ROOT and REQUEST_URI environment variables to figure out what directory you want to display and where it actually resides on the filesystem. Here are the relevant environment variables when viewing the URL /home/notebook/2026/0417/:

 DOCUMENT_ROOT = /doc/html/htdocs
REQUEST_METHOD = GET
REQUEST_SCHEME = http
   REQUEST_URI = /home/notebook/2026/0417/

My DOCUMENT_ROOT has a symbolic link to my home directory, so I can append REQUEST_URI to DOCUMENT_ROOT and get a legitimate top-level directory for the directory I want to display. The file $HOME/notebook/2026/0417/hiding-datacenter-emissions will be shown on the page as /home/notebook/2026/0417/hiding-datacenter-emissions.

Basic idea:

cd to today's directory from $REQUEST_URI and open it
Find any subdirectories
Find any other files that aren't directories

The screen looks like this, with icons shown in square brackets:

Directories:

  [DIR]        18  2026-04-17 22:13:02 Maildir
  [DIR]         6  2026-04-17 08:10:10 movies
  [DIR]         9  2026-04-17 08:03:03 rss
  [DIR]        26  2026-04-18 00:15:06 stats
  [DIR]        65  2026-04-20 08:05:28 townhall

Files:

  [TXT]    316709  2026-04-17 08:05:28 aier.xml
  [TXT]      4770  2026-04-17 00:20:00 appointments.txt
  [TXT]     13800  2026-04-17 23:59:00 browser-history.furbag
  [TXT]    141977  2026-04-17 08:05:26 fifth-domain.xml
  [TXT]      7304  2026-04-17 03:57:20 hiding-datacenter-emissions
  [TXT]    176832  2026-04-17 08:05:27 nextgov.xml
  [TXT]    283340  2026-04-17 08:05:28 quillette.xml
  [TXT]      1940  2026-04-17 02:44:17 reddit-bash-scripting
  [TXT]     30424  2026-04-17 08:05:30 risks.xml

Here's a screenshot.

You can see a list of directories at the top of the calendar display on the right. I didn't see a good reason to regenerate something on the fly that rarely if ever changes, so dirindex looks for those directories in the file $HOME/notebook/.dirlist:

<a href="/home/notebook/1990/0101">1990</a>
<a href="/home/notebook/1991/0101">1991</a>
...
<a href="/home/notebook/2009/0101">2009</a>
<a href="/home/notebook/2010/0101">2010</a><br>
<a href="/home/notebook/2011/0101">2011</a>
... 
<a href="/home/notebook/2025/0101">2025</a>
<a href="/home/notebook/2026/0101">2026</a>

I've included a reasonable set of icons in the ficons directory (expected to be under DOCUMENT_ROOT) which are assigned to files based on the extension:

'.[1-9]' => 'file-man.png',
'.aiff'  => 'file-sound.png',
'.doc'   => 'x-office-document.png',
'.exe'   => 'application-x-executable.png',
...
'.wav'   => 'file-sound.png',
'.wmv'   => 'video-x-generic.png',
'.xhtml' => 'text-html.png',

11. The final display

I generate as much content as possible in advance, so the CGI script runs quickly. Yearly calendars are copied from the templates directory to my notebook directory:

+--home
|   +--vogelke
|   |   +--notebook
|   |   |   +--2000
|   |   |   |   +--.calendar     <==  copy of templates/2000.htm
|   |   |   +--2001
|   |   |   |   +--.calendar     <==  copy of templates/2001.htm
...
|   |   |   +--2025
|   |   |   |   +--.calendar     <==  copy of templates/2025.htm
|   |   |   +--2026
|   |   |   |   +--.calendar     <==  copy of templates/2026.htm
...

Here's a complete sample page showing the calendar for 4 May 2026.

12. Stylesheet: 2col.css

I derived this from an article by Matthew James Taylor: https://matthewjamestaylor.com/2-column-layouts

His articles are excellent if you want responsive multi-column pages that work.

13. Installation

  1. Any files or directories you want to display must be readable by your webserver. This can be risky, which is why I copy things like /var/log or firewall-log files elsewhere for display.

  2. Tell your webserver to use /cgi-bin/autoindex as the default for generating a directory index. The Apache configuration section shows how to do that for Apache.

  3. Put dirindex and autoindex scripts in your cgi-bin directory.

  4. Copy templates for each year (YYYY.htm) wherever you can find them. Those templates contain hard-wired paths (in my case, /home/notebook/2026 for this year) that match aliases provided by Apache, like /home pointing to my home directory.

  5. Copy the stylesheet (2col.css) wherever your CSS files live. The dirindex script assumes they live in a /style directory under your document root.

  6. Scripts will look for yearly calendars named .calendar under the YYYY directories.

  7. I included two additional scripts: slindex is for displaying copies of syslog files (/var/log) since I rotate them daily, and mfindex displays information about files that have been modified or added on a given day.

14. Feedback

Feel free to send comments.


Generated from article.t2t by txt2tags
$Revision: 1.8 $
$Date: 2026-05-05 01:16:21-04 $
$UUID: 1b845f31-a658-4a5d-a871-9aa9573b52cd $