* INDEX.SNO
*
* Formats a mail digest file for printing.  Does pagination, produces a
* Table of Contents and an Index.  The Index is based on entries in the
* message headers of the form
*
* Keywords: xxxx, xxxx, xxxx, "xxx, xxxx", xxxx
* Cross-Ref: string
*
* A list of zero or more keywords may be included, separated by commas.
* If the keyword itself is to contain a comma, it is enclosed in doublequotes.
* The cross ref string is entered literally in the index.  Examples:
*
* Keywords: Blort
* Keywords: Foo, Bar, This and That, "Law, Murphy's", Baz
* Cross-Ref: Murphy's Law, see Law, Murphy's
*
* Any particular Cross-Reference need be entered in the file only once.
* The index is sorted in ASCII order, so alphabetic case matters.
*
* The table of contents depends on the subject of each digest containing
* the words "Info-Kermit Digest Vx #y", where x & y are the vol/issue number.
* The end of a digest is detected by "End of InfoxxxxxDigest" where a
* digest message header would start.
*
* F. da Cruz, C. Gianone, CUCCA, May 1986
*
* Modified May 88 to work with VAX spitbol
* Modified Nov 88 to work with UNIX MM mail format
* Modified Oct 91 to allow XREF as synonym for CROSS-REF

	&TRIM = 1
	PAGEWID = 79
	PAGELEN = 58

* Patterns

	NULL =
	&ALPHABET POS(13) (LEN(1) . CR)
	&ALPHABET POS(10) (LEN(1) . LF)
	CRLF = CR LF
	&ALPHABET POS(34) (LEN(1) . DQ)
	BLANK = ' '
	&ALPHABET POS(9) (LEN(1) . HT)
	WS = BLANK HT
	&ALPHABET POS(12) (LEN(1) . FF)
	LOWER = 'abcdefghijklmnopqrstuvwxyz'
	UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
	DIGITS = '0123456789'

	KEYS = 'Keywords' | 'Keyword' | 'KEYWORDS' | 'KEYWORD'
+		| 'keywords' | 'keyword'
	REFS = 'Cross-Ref' | 'Cross-Reference' | 'CROSS-REF'
+		| 'XREF' | 'Xref' | 'xref'
	KEYW = POS(0) KEYS ':' BREAK(WS) REM . KEY
	XREF = POS(0) REFS ':' BREAK(WS) REM . SEE

	SUBJ = POS(0) 'SUBJECT:' BREAK(WS) REM . TITLE
	ISUB = 'INFO' LEN(1) 'KERMIT' LEN(1) 'DIGEST V'
+		SPAN(DIGITS) . VOLUME SPAN(BLANK) '#' SPAN(DIGITS) . ISSUE
	ASUB = POS(0) 'subject:' | 'SUBJECT:' | 'Subject:' SPAN(WS) REM . SUB

* Table, array declarations

	IXTB = TABLE()
	CONTENTS = ARRAY(500)
	INDEX = ARRAY('1000,2')	
	II = 0
	CC = 0

* Newpage function definition.  Starts a new page, puts heading on top
* composed of current digest title and page number.  Set PAGE = -1 to
* disable page number.  Reverses page headings.

	DEFINE('NEWPAGE()')				:(NEWPAGX)
NEWPAGE	OUTPUT = FF
	GE(PAGE,0)					:S(NEWP2)
	OUTPUT = TITLE
	OUTPUT = 					:(NEWP4)
NEWP2	PAGE = PAGE + 1
	X = 'Page ' PAGE
	EQ(REMDR(PAGE,2),1)				:S(NEWP3)
	OUTPUT = X LPAD(TITLE,PAGEWID - SIZE(X))	:(NEWP4)
NEWP3	OUTPUT = TITLE LPAD(X,PAGEWID - SIZE(TITLE))
NEWP4	OUTPUT =
	LC = 3						:(RETURN)
NEWPAGX

* Function to output a line.  If the line is longer than the page
* width, it is broken at the nearest blank, and two (or more) lines are
* output.  If the bottom of the page is reached, a new page is started.

	DEFINE('OUTLIN(S)')				:(OUTLINX)
OUTLIN							:(OUTLIN2)
OUTLOOP	IDENT(S,NULL)					:S(RETURN)
OUTLIN2	P = PAGEWID
	P = GE(P,SIZE(S)) SIZE(S)			:S(OUTGB2)

OUTFB	S POS(P) BLANK					:S(OUTGB)
	P = GT(P,0) P - 1				:S(OUTFB)

OUTGB	P = EQ(P,0) PAGEWID
	P = GE(P,SIZE(S)) SIZE(S)

OUTGB2	S LEN(P) . S1 = NULL
	OUTPUT = S1
	LC = LT(LC,PAGELEN) LC + 1			:S(OUTLOOP)
	NEWPAGE()					:(OUTLOOP)
OUTMORE
							:(OUTLIN)
OUTLINX				

* Program start

START	PAGE = 0
	LC = 0

* Here to initialize a new digest issue

INITISS	TITLE = NULL
	VOLUME = 0
	ISSUE = 0
	
* New issue, discard mail header.

BIGHDR	LINE = INPUT					:F(DONE)
	IDENT(LINE,NULL)				:S(BODY)
	TEMP = REPLACE(LINE,LOWER,UPPER)
	TEMP SUBJ
	TITLE POS(0) SPAN(WS) = NULL			:(BIGHDR)

* Body of message, starts with "table of contents" (list of subjects)

BODY	TITLE ISUB					:F(INITISS)
	NEWPAGE()
	TERMINAL = '** V' VOLUME ' #' ISSUE
	X = 'Volume ' VOLUME ', Number ' ISSUE
	CC = CC + 1
	CONTENTS<CC> = X LPAD(PAGE,PAGEWID - SIZE(X))

TOC	LINE = INPUT					:F(DONE)
	OUTLIN(LINE)
TOC2	LINE POS(0) '---'				:F(TOC)

* Here for digest messages.

MSG0	LINE = INPUT					:F(DONE)
	TEMP = REPLACE(LINE,LOWER,UPPER)
	OUTLIN(LINE)
	IDENT(LINE,NULL)				:S(MSG0)

* Look for message header with keywords, or else end of digest.

MSGS	TEMP POS(0) 'END' LEN(1) 'OF' LEN(1) 'INFO' ARB 'DIGEST'
+							:F(MSGHDX)

* Here for end of digest.  Gobble up junk until next digest.
	
EOD	LINE = INPUT					:F(DONE)
	LINE POS(0) 'From '				:F(EOD)S(INITISS)

* New message in current digest.  Read headers, looking for keywords.

MSGHDX	LINE = INPUT					:F(DONE)
	OUTLIN(LINE)
	IDENT(LINE,NULL)				:S(MSGBODY)
	TEMP = REPLACE(LINE,LOWER,UPPER)
	LINE ASUB					:F(MSGHDY)
	TEMP POS(0) 'RE:'				:S(MSGHDY)
	TEMP 'NEW ' | 'ANNOUNC' | 'AVAILAB' | 'RELEAS' | 'PROPOS' | 'UPDATE'
+							:F(MSGHDY)
	CC = CC + 1
	CONTENTS<CC> = '  ' SUB LPAD(PAGE,PAGEWID - SIZE(SUB) - 2)
							:(MSGS)
MSGHDY	LINE KEYW					:S(KEYWORD)
	LINE XREF					:F(MSGS)

* Got a cross reference to enter.

	SEE POS(0) SPAN(WS) = NULL
	KW = SEE
	SW = SEE
	REPLACE(SW,LOWER,UPPER)

REFADD	T = IXTB<SW>
	X = BLANK
	DIFFER(T,NULL)					:S(KEYSCAN)
	II = LE(II,1000) II + 1				:S(KEYADD2)
	TERMINAL = "** Index overflow - Fatal!"		:(END)

* Got keyword header, enter into index.

KEYWORD	KEY = KEY ','

* Get next keyword from list.

KEYSCAN	KEY POS(0) SPAN(WS) = NULL
	IDENT(KEY,NULL)					:S(MSGS)
	KEY POS(0) (DQ (BREAK(DQ) . KW) DQ ',') = NULL 	:S(KEYINDX)
	KEY (BREAK(',') . KW ',') = NULL		:F(MSGS)

* Make an all uppercase sort key from the keyword, and a trial page ref.

KEYINDX	KW = TRIM(KW)
	SW = REPLACE(KW,LOWER,UPPER)
	X = ', ' PAGE

* See if this is a new item.  If so, create a new array entry.

KEYADD	T = IXTB<SW>
	DIFFER(T,NULL)					:S(KEYADD4)
	II = LE(II,1000) II + 1				:S(KEYADD2)
	TERMINAL = "** Index overflow - Fatal!"		:(END)

* Adding a new one

KEYADD2	IXTB<SW> = II	
	INDEX<II,1> = KW
	INDEX<II,2> = X					:(KEYSCAN)

* See if the page reference should be added, and in which form.

KEYADD4
	INDEX<T,2> (BLANK | '-') PAGE RPOS(0)		:S(KEYSCAN)
	INDEX<T,2> LEN(1) . PUNC SPAN(DIGITS) . LAST RPOS(0) :F(KEYADD5)
	EQ(PAGE,LAST + 1)				:F(KEYADD5)
	X = DIFFER(PUNC,'-') '-' PAGE			:S(KEYADD5)
	INDEX<T,2> LAST RPOS(0) = PAGE			:(KEYSCAN)
KEYADD5	INDEX<T,2> = INDEX<T,2> X 			:(KEYSCAN)

* Now read and output the message body.

MSGBODY	LINE = INPUT					:F(DONE)
	OUTLIN(LINE)
	LINE POS(0) '---'				:S(MSG0)
							:(MSGBODY)
* End of input file.  Now output the index.

DONE
***	TERMINAL = '** Input complete, sorting Index...'
	A = SORT(IXTB)					:S(IDXOUT)
	TERMINAL = '** Error converting index table'	:(TOCOUT)

IDXOUT	EQ(REMDR(PAGE,2),1) NEWPAGE()
	TITLE = 'Index'
	NEWPAGE()
	I = 1

IDXLOOP	J = A<I,2>
	K = INDEX<J,1> INDEX<J,2>
	OUTLIN(K)
	I = LT(I,II) I + 1				:S(IDXLOOP)

* And put the table of contents on the end.

TOCOUT	EQ(REMDR(PAGE,2),1) NEWPAGE()
	PAGE = -1
	TITLE = 'Table of Contents'
	NEWPAGE()
	I = 0
TOCLOOP	OUTLIN(CONTENTS<I>)
	I = LE(I,CC) I + 1				:S(TOCLOOP)

END
