/*
 * index_html.c
 * 3/22/91
 * Usage:  index [ infile [ outfile ] ]
 * Reads raw document index from infile or standard input,
 * writes [nt]roff output to outfile or standard output.
 * Each input line should have the format "%<key>[^<subkey>]=<ref>".
 * Comments on routines input(), output(), parse() give more detail
 * about input and output format.
 *
 * 01/16/94 - modified to handle input generated by tmac.cohhtml, and
 * to compile properly under Coherent 386. (fwb)
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>

/* External. */
extern char *mktemp();

/* Manifest constants. */
#define	VERSION	"HTML"			/* version number		*/
#define	MAXLINE	2000			/* maximum input line length	*/
#define	SEP	'\001'			/* field separator		*/
#define	CARAT	'\002'			/* replacement for '^' for compare */
#define	IXTMP	"/tmp/ixXXXXXX"		/* tmpname prototype		*/
#define	USAGE	"Usage:\tindex [ -v ] [ infile [ outfile ] ]\n"

/* Forward. */
void	fatal();
void	filclose();
FILE	*filopen();
void	flush();
void	input();
void	output();
void	parse();
void	usage();

/* Output strings. */
#define	INDENT	"   "			/* indent for secondary key */
#define INXHEAD	"<PRE>\n"		/* at head of output file */
#define INXFOOT	"</PRE>\n"		/* at foot of output file */

/* Globals. */
char	key[MAXLINE];			/* Index key, cooked.		*/
char	keyraw[MAXLINE];		/* Index key, raw.		*/
char	keyref[MAXLINE];		/* Index reference.		*/
char	keysec[MAXLINE];		/* Index secondary key.		*/
char	line[MAXLINE];			/* Input line buffer.		*/
int	ninput;				/* Number of index entries.	*/
int	nlines;				/* Number of input lines.	*/
int	noutput;			/* Number of output lines.	*/
char	tmp1[sizeof(IXTMP)+1];		/* Temporary filename.		*/
char	tmp2[sizeof(IXTMP)+1];		/* Temporary filename.		*/
int	vflag;				/* Verbose.			*/

/* debugging macro */
#define DEBUG fprintf(stderr, "Line %d\n", __LINE__); fflush(stderr)

main(argc, argv) int argc; char *argv[];
{
	register char *s;

	/* Construct temporary filenames. */
	strcpy (tmp1, IXTMP);
	if ((s = mktemp(tmp1)) == NULL)
		fatal("cannot make temporary file name");

	strcpy (tmp2, IXTMP);
	if ((s = mktemp(tmp1)) == NULL)
		fatal("cannot make temporary file name");

	/* Read input file, write sortable lines to tmp1. */
	input((argc > 1) ? argv[1] : NULL);

	/* Sort tmp1 to tmp2. */
	sprintf(line, "sort %s >%s", tmp1, tmp2);

	if (system(line) != 0)
		fatal("command \"%s\" failed", line);

	/* Read sorted tmp2, write output file. */
	output();

	/* Done, remove tmps and exit. */
	unlink(tmp1);
	unlink(tmp2);

	exit(0);
}

/*
 * Print error message and exit.
 */
/* VARARGS */
void
fatal(s) char *s;
{
	fprintf(stderr, "index: %r\n", &s);
	if (tmp1[0] != '\0')
		unlink(tmp1);
	if (tmp2[0] != '\0')
		unlink(tmp2);
	exit(1);
}

/*
 * Close a file.
 */
void
filclose(fp, msg) FILE *fp; char *msg;
{
	if (fclose(fp) == EOF)
		fatal("cannot close %s file", msg);
}

/*
 * Open a file.
 */
FILE *
filopen(name, mode) char *name, *mode;
{
	register FILE *fp;

	if ((fp = fopen(name, mode)) == NULL)
		fatal("cannot open file \"%s\"", name);
	return fp;
}

/*
 * Flush out an index entry.
 */
void
flush(ofp, first, last) FILE *ofp; int first, last;
{
	if (first == last)
		fprintf(ofp, "%d", last);
	else
		fprintf(ofp, "%d-%d", first, last);
}

/*
 * Read input file.
 * Lines not beginning with '%' are ignored.
 * Lines beginning with '%' are parsed by parse().
 * The result is sorted by key first (case- and font-insensitive),
 * secondarily by keyraw (to restore case and font information),
 * and thirdly by keyref.
 */
void
input(file) char *file;
{
	register FILE *ifp, *ofp;

	ifp = (file == NULL) ? stdin : filopen(file, "r");
	ofp = filopen(tmp1, "w");

	while (fgets(line, MAXLINE-1, ifp) != NULL) {
		nlines++;			/* Bump line count. */
		if (line[0] != '%')
			continue;		/* Ignore non-index lines. */
		ninput++;			/* Bump entry count. */
		parse();			/* Parse the line. */
		if (*keyraw == '\0') {
			fprintf(stderr, "index: Warning: line %d ignored (empty key)\n", nlines);
			continue;
		}
		fprintf(ofp, "%s%c%s%c%s\n", key, SEP, keyraw, SEP, keyref);
	}

	if (file != NULL)
		filclose(ifp, "input");
	filclose(ofp, "output");
}

/*
 * Write output file.
 * The output consists of:
 *	1)  The last line of the input file(line number info);
 *	2)  The index header INXHEAD;
 *	3)  The sorted index; and
 *	4)  The index footer INXFOOT.
 */
#define	S_CLEAN	0			/* no references yet		*/
#define	S_NUM	1			/* numeric references buffered	*/
#define	S_ALPHA	2			/* in non-numeric references	*/
void
output()
{
	register FILE *ifp, *ofp;
	register char *s, *cooked, *raw, *ref, *sec;
	int first, last, this, state;
	char letter, output_file[15];

	ifp = filopen(tmp2, "r");

	strcpy (output_file, "index._.html");
	ofp = filopen(output_file, "w");
	letter = '_';

	fprintf(ofp, "%s", INXHEAD);
	key[0] = keysec[0] = '\0';

	while (fgets(line, MAXLINE-1, ifp) != NULL) {
		/* Break input line into key, raw, ref, sec. */
		cooked = line;
		line[strlen(line)-1] = '\0';		/* zap trailing \n */

		if ((s = strchr(cooked, SEP)) == NULL)
			fatal("no first SEP, line=%s", line);
		else
			raw = s + 1;
		if ((s = strchr(raw, SEP)) == NULL)
			fatal("no second SEP, line=%s", line);
		else
			ref = s + 1;
		*(raw-1) = *(ref-1) = '\0';
		if ((sec = strchr(raw, CARAT)) != NULL)
			*sec++ = '\0';

		/* Output line information, buffering as needed. */
		if (strcmp(key, raw) != 0
		 || (sec != NULL && strcmp(keysec, sec) != 0)) {
			fputc('\n', ofp);		/* terminate line */

			++noutput;
			if (strcmp(key, raw) != 0) {
				/* New key. */
				strcpy(key, raw);

				/* put each letter of the alphabet into
				   its own output file; bundle all the
				   entries that begin with a punctuation mark
				   into one output file */
				if (*key != letter && *key >= 'a') {
					/* switch letters of the alphabet */
					fclose(output_file);
					letter = *key;
					sprintf(output_file, "index.%c.html", letter);
					ofp = filopen(output_file, "w");
				}

				if (*key == '.' || *key == '\'')
					fprintf(ofp, "\\&");
				fprintf(ofp, "%s", key);
/* change from steve's code:  eliminate solitary tab on key line that
   has no page number on it. */
				if (isdigit(*ref))
					fputc((sec == NULL) ? '\t' : '\n', ofp);
				
			}
			if (sec != NULL) {
				/* Secondary key. */
				strcpy(keysec, sec);
				fprintf(ofp, ", %s\n", sec);
			} else {
				fprintf(ofp, "\n");
				keysec[0] = '\0';
			}
		}

		/* Put out or buffer reference. */
		fprintf(ofp, "%s%s\n", INDENT, ref);
	}
	if (state == S_NUM)
		flush(ofp, first, last);
	fprintf(ofp, "%s", INXFOOT);

	filclose(ifp, "temporary");
}

/*
 * Parse input line from line[] into keyraw, key, keyref.
 * The input line must have the format "%<key>[^<subkey>]=<ref>".
 * keyraw is "<key>[^<subkey>]" portion, which may contain font changes.
 * key is the "cooked" version of keyraw used as primary sort key,
 * with case and font information mapped out (so "a", "Ab" and \fBabc\fR"
 * sort in that order) and non-ASCII characters stripped.
 * '^' in keyraw not followed by another '^' indicates a secondary index
 * entry and is replaced here by CARAT; '^^' in keyraw is replaced by '^'.
 * key is keyraw, "cooked" as follows:
 * keyref is the reference; numeric <ref>s are 0-padded to four digits
 * in keyref so that they sort correctly.
 */
void
parse()
{
	register char *cp;
	register char c;

	/* Zap trailing newline. */
	if ((cp = strchr(line, '\n')) != NULL)
		*cp = '\0';

	/* Sanity check. */
	if (strchr(line, SEP) != NULL)
		fatal("SEP in line %d: \"%s\"", nlines, line);
	else if (strchr(line, CARAT) != NULL)
		fatal("CARAT in line %d: \"%s\"", nlines, line);

	/* Strip unwanted characters. */
	for (cp = &line[1]; c = *cp; cp++) {
		if (!isascii(c) || iscntrl(c)) {
			fprintf(stderr, "index: Warning: line %d: nonASCII character 0x%02x removed\n", nlines, c & 0xFF);
			memcpy(cp, cp+1, strlen(cp));
			--cp;
		}
	}

	/* Replace "^^" with "^", "^" with CARAT. */
	for (cp = &line[1]; c = *cp; cp++)
		if (c == '^') {
			if (*(cp+1) == '^')	/* Replace '^^' with '^'. */
				memcpy(cp, cp+1, strlen(cp));
			else
				*cp = CARAT;	/* Replace '^' with CARAT. */
		}

	/* Set keyraw[], keyref[]. */
	if ((cp = strrchr(line, '@')) == NULL)
		fatal("missing '@' in line %d: \"%s\"", nlines, line);

	*cp++ = '\0';				/* Truncate raw key at '='. */
	strcpy(keyraw, &line[1]);		/* Raw key without '%'. */
	if (isdigit(*cp))
		sprintf(keyref, "%04d", atoi(cp)); /* Prepend leading 0s for sort */
	else
		strcpy(keyref, cp);		/* Nonnumeric key reference. */

	/* Cook keyraw[] to key[]. */
	strcpy(key, keyraw);
	for (cp = key; c = *cp; cp++) {
		if (isupper(c))
			*cp = tolower(c);	/* Convert UPPER to lower. */
		else if (c == '\\' && *(cp+1) == 'f') {
			memcpy(cp, cp+3, strlen(cp)-2);	/* Remove font escapes. */
			--cp;
		}
	}
}

/*
 * Print usage message and exit.
 */
void
usage()
{
	fprintf(stderr, USAGE);
	exit(1);
}

/* end of index.c */
