/*
** C version of chronolog script.  Much faster, takes 1 second vs
** the 55 seconds of the script.
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <string.h>
#include <libgen.h>
#include "search.h"

static FILE *outfile; // Stupid twalk needs globals...
static int   year, years;

struct node {
    time_t date;   // Primary sort key.
    int    year;   // Payload, for HTML year indexing.
    char  *string; // Payload, also secondary sort key.
} *root, *rroot;   // Both forward-sorted and reverse-sorted trees.

/*
** Make the year-index table entries at the front of the file.
*/
static void
indextable(const struct node **nodep, VISIT value, int level)
{
    if (value == postorder || value == leaf) {
	if (year != (*nodep)->year) {
	    year = (*nodep)->year;
	    fprintf(outfile, "<TD><A HREF=#%d>%d</A>\n", year, year);
	    if (++years >= 10) {
		fprintf(outfile, "<TR>\n");
		years = 0;
	    }
	}
    }
}

/*
** Make the main table.
*/
static void
nodedump(const struct node **nodep, VISIT value, int level)
{
    if (value == postorder || value == leaf) {
	if (year != (*nodep)->year) { // New annual section header?
	    year = (*nodep)->year;
	    fprintf(outfile,
		    "<TR><TD><HR></TD><TD><HR></TD></TR>\n"
		    "<TR><TD><U>Dated Entry</U><TD><U>From file</U>\n"
		    "<TR><TD><B>%d</B><A NAME=%d></A>\n", year, year);
	}
	fprintf(outfile, "%s", (*nodep)->string);
    }
}

/*
** Make an output file.
*/
static void
dumptree(const char *progname, char *reverse)
{
    fprintf(outfile,
	    "<html>\n"
	    "<head>\n"
	    "<!-- Created automatically by the %s program! -->\n"
	    "<title>%sChronological Log Index</title>\n"
	    "</head>\n"
	    "<body>\n"
	    "<H1>%sChronological Log Index</H1>\n"
	    "<TABLE><TR>\n", progname, reverse, reverse);
    year = years = 0;
    twalk(*reverse ? rroot : root, (__action_fn_t) indextable);
    fprintf(outfile,
	    "</TABLE><P>\n"
	    "<TABLE>\n");
    year = 0;
    twalk(*reverse ? rroot : root, (__action_fn_t) nodedump);
    fprintf(outfile,
	    "</TABLE>\n"
	    "<P>\n"
	    "<A HREF=index.html>Return to Site Home</A>\n"
	    "</body>\n"
	    "</html>\n");
}

/*
** Key comparitor for sorted tree.
*/
static int
ncompare(struct node *n1, struct node *n2)
{
    if (n1->date == n2->date)
	return strcmp(n1->string, n2->string);
    return n1->date - n2->date;
}

/*
** Key comparitor for reverse-sorted tree.
*/
static int
rncompare(struct node *n1, struct node *n2)
{
    if (n1->date == n2->date)
	return strcmp(n2->string, n1->string);
    return n2->date - n1->date;
}

/*
** Put a node in both (forward and reverse sorted) trees.
*/
static void
addnode(char *name, time_t date, int year)
{
    struct node *nstore;
    int          keep;

    nstore = malloc(sizeof *nstore);
    nstore->date = date;
    nstore->year = year;
    nstore->string = strdup(name);
    keep = 0;
    if (!tfind(nstore, &root, (__compar_fn_t)ncompare)) {
	tsearch(nstore, &root, (__compar_fn_t)ncompare);
	keep = 1;
    }
    if (!tfind(nstore, &rroot, (__compar_fn_t)rncompare)) {
	tsearch(nstore, &rroot, (__compar_fn_t)rncompare);
	keep = 1;
    }
    if (!keep) {
	free(nstore->string);
	free(nstore);
    }
}

/*
** Process a given HTML file.
*/
static void
processfile(const char *filename)
{
    FILE        *fp;
    char         buf[1024], buf2[1024], *cp, *cp2;
    const  char *fn;
    struct tm    date;
    time_t       time;

    if (fp = fopen(filename, "r")) {
	if ((fn = strchr(filename, '/')))
	    fn++;
	else
	    fn = filename;
	while (fgets(buf, sizeof buf, fp)) {
	    if (!strncmp(buf, "<H3>", 4)) {
		if ((cp = strchr(&buf[4], '<'))) {
		    *cp = 0;
		    if ((cp = strchr(++cp, '='))) {
			cp++;
			if ((cp2 = strchr(cp, '>')))
			    *cp2 = 0;
		    }
		}
		if (cp && cp2) {
		    snprintf(buf2, sizeof buf2, "<TR><TD><A HREF=%s#%s>%s</A> "
			     "<TD>(<A HREF=%s>%s</A>)\n", fn, cp, &buf[4],
			     fn, fn);
		    memset(&date, 0, sizeof date); // Clear unused fields.
		    strptime(cp, "%d%b%Y", &date);
		    time = mktime(&date);
		    if (time > 0)
			addnode(buf2, time, date.tm_year + 1900);
		}
	    }
	}
	fclose(fp);
    }
}

/*
** Process every html file in a directory.  Recurse down subdirectories.
** Don't follow symlinks, where here lead to other data stores.
*/
static void
processdir(const char *pathname, const char *dirname)
{
    DIR           *dir;
    struct dirent *dire;
    char           path[MAXNAMLEN+3];
    struct stat    st;
    int            ii;

    snprintf(path, sizeof path, "%s/%s", pathname, dirname);
    if (dir = opendir(path)) {
	while (dire = readdir(dir)) {
	    if (dire->d_name[0] == '.')
		continue;
	    snprintf(path, sizeof path, "%s/%s", pathname, dire->d_name);
	    if (!lstat(path, &st)) { /* Don't follow symlinks. (PDF) */
		if ((st.st_mode & S_IFMT) == S_IFREG) {
		    ii = strlen(dire->d_name);
		    if (ii > 5 && !strcmp(&dire->d_name[ii - 5], ".html")) {
			processfile(path);
		    }
		} else if ((st.st_mode & S_IFMT) == S_IFDIR) {
		    processdir(path, ".");
		}
	    }
	}
	closedir(dir);
    }
}

/*
** Main program.  Builds HTML index files for the logs found in the
** HTML files in the directory tree, sorted forward- and
** reverse-chronologically.
**
** Usage: chronolog [<outfile> [<reverse-oufile>]]
*/
int
main(int argc, char *argv[])
{
    char *outfname;
    char *routfname;

    outfile = stdout;
    outfname = "chronolog.html";
    routfname = 0;
    switch (argc) {
	case 3:
	    routfname = argv[2];
	case 2:
	    outfname = argv[1];
	    break;

	case 1:
	    break;

	default:
	    fprintf(stderr, "Usage: %s [<outfile> [routfile]]\n", argv[0]);
	    exit(1);
	    break;
    }
    processdir(".", ".");

    if (outfname) {
	outfile = fopen(outfname, "w");
	if (!outfile) {
	    fprintf(stderr, "Can't open %s for writing\n", outfname);
	    exit(1);
	}
    }

    dumptree(basename(argv[0]), "");
    fclose(outfile);

    if (routfname) {
 	outfile = fopen(routfname, "w");
	if (!outfile) {
	    fprintf(stderr, "Can't open %s for writing\n", routfname);
	    exit(1);
	}
	dumptree(basename(argv[0]), "Reverse-");
	fclose(outfile);
    }

    exit(0);
}
