summaryrefslogtreecommitdiff
path: root/macros/text1/texix/texix.web
diff options
context:
space:
mode:
Diffstat (limited to 'macros/text1/texix/texix.web')
-rw-r--r--macros/text1/texix/texix.web1577
1 files changed, 1577 insertions, 0 deletions
diff --git a/macros/text1/texix/texix.web b/macros/text1/texix/texix.web
new file mode 100644
index 0000000000..bbf577633e
--- /dev/null
+++ b/macros/text1/texix/texix.web
@@ -0,0 +1,1577 @@
+% Version 1.3 makes it easier to create other changes files (6/89)
+\font\twelvept=cmbx12
+\font\tentex=cmr10
+\def\topofcontents{\null\vfill\eject
+ \def\titlepage{T}
+ \centerline{{\twelvept The \TeX IX Index Program}}
+ \centerline{{\twelvept For IBM VM/CMS Pascal/VS}}
+ \vskip15pt
+ \centerline{Version 1.3, June 1989}
+ \hbox{\vbox{\hsize\the\hsize This work is
+ protected as an unpublished work under U.S. copyright laws.
+ Copyright $\copyright$ 1988 by WSUCSC. All rights Reserved.}}
+ \vskip18pt
+ \hbox{\vbox{\hsize\the\hsize This software is furnished under a
+ license for
+ use only on a single computer system and may be copied only
+ with the inclusion of the above copyright notice.
+ This software, or any other copies
+ thereof, may not be provided or otherwise made available to any
+ other person except for use on such system and to one who agrees to
+ these license terms. Title to and ownership of the software shall
+ at all times remain in WSUCSC.}}
+ \vfill}
+\let\tentex=\tt
+\def\_{\leavevmode \kern.06em \vbox{\hrule width.3em}}%
+%
+\def\}{\ifmmode \rbrace
+ \else $\rbrace$\fi}%
+%
+\def\{{\ifmmode \lbrace
+ \else $\lbrace$\fi}%
+%
+\def\us#1{$\underline{\smash{\hbox{#1}}}$}%
+\def\lin#1{\par
+ \leftskip 0pt
+ \advance \leftskip by #1
+ }%
+\def\vs#1{\vskip #1\relax}
+\catcode`*=11
+% labeled definition macro
+%
+\newdimen\la*w \la*w=1in % Label width
+\newtoks\la*g \la*g={1em} % Label gutter
+\newtoks\la*s \la*s={12pt} % Skip before label
+\newtoks\la*f \la*f={\rm} % Label font
+\newbox\tempbox
+%
+\long\def\label#1{\par\vskip\the\la*s
+ \setbox\tempbox=\vtop{\hsize=\la*w
+ \leftskip=0pt
+ \rightskip=0pt plus2em
+ \tolerance=1600
+ \noindent
+ \the\la*f
+ #1}%
+ \hangindent=\la*w
+ \advance \hangindent by \the\la*g
+ \hangafter=1
+ \noindent
+ \setbox0=\hbox{\rlap{\box\tempbox}}\ht0=0pt\dp0=0pt\box0%
+ \hskip\la*w
+ \hskip\the\la*g
+ \ignorespaces}
+\let\la=\label
+\catcode`*=12
+\let\nin=\noindent
+\def\lbr{\null\hfil\break}
+\def\PASCAL{{\sc PASCAL/VS}}
+\def\hs#1{\hskip#1}
+\def\text#1{{\it \TeX T#1}}
+\def\9{\it}
+\def\bs{$\backslash$}
+% Version 1.0; Dean Guenther; 4/1/86
+%
+@* Introduction.
+This procedure was created to give \text1\ the ability to produce a sorted
+index in the same one pass, instead of creating the index, sorting it, and
+printing the index out in three separate steps.
+
+To accomplish this feat, a new control sequence, \bs{}sortindex, was added to
+the basic \TeX\ program which gives the \text1\ user up to three sorted
+index files per run. The format of this new control sequence is
+\par{\tt\indent\bs{}sortindex\it n}
+\par
+\nin where {\it n} is the index file number: 1, 2, or 3.
+
+@ The @^TEXIX@>
+program is written entirely in WEB, except for an external
+procedure, {\it plsort}, which is written in @^PL/1@> PL/1.
+This was necessary since
+\PASCAL\ cannot call
+@:plsort@>
+Syncsort @^SYNCSORT@>
+to do an internal sort, and PL/1 can.
+
+@
+Also, it should be pointed out early that I made use of \PASCAL'
+nifty string handling capability, in particular, the |string|,
+|index| and |substr| functions. If needed, these should not be too
+difficult to translate into another \PASCAL\
+compiler (famous last words).
+
+@p
+segment texix;
+procedure texix(ix:char); external;
+procedure texix;
+const @<Global Constants@>
+type @<Global Types@>
+var @! m,k,i,j :integer;@/
+@<Global Variables@>
+
+@ The file {\it ix\_file}
+is the file opened for output in the \text1\ session
+itself. It will be closed in TEXIX, opened for input, closed again, and
+finally opened for output where the final sorted index file will be
+written to, complete with all \text1\ markup necessary for printing
+the index.
+
+@<Global Var...@>=
+sysprint:text;
+@!ix_file :text;
+
+@ The {\it sort\_file} is used for the internal sort only. The \text1\ user
+never has access to it.
+
+@<Global Var...@>=
+@!sort_file :file of sort_type;
+
+@* Macros and definitions.
+Here are some macros and definitions used throughout.
+
+@d incr(#) == # := # + 1
+@d decr(#) == # := # - 1
+
+@d othercases == otherwise
+@d endcases == end;
+
+@d dosubstr == substr
+@d doindex == index
+
+@f othercases == else
+@f endcases == end
+
+@d getout == return
+@d messages == sysprint
+
+@d max_field = 300
+@d max_levels = 3
+@d max_lrecl = 2048
+
+@d remove_characters(#) == if length(in_record) > # then
+in_record := dosubstr(in_record,1 + #)
+else in_record := ''
+
+@d do_nothing == begin end
+
+@d do_sort_as == begin remove_characters(6);
+i := doindex(in_record,'{$}');
+write_sort_chars(i-1);
+remove_characters(3);
+end
+
+@d check_case == begin
+if not respect_case then for j:= 1 to
+sort_part[i].field_lngth do sort_part[i].field_level[j] :=
+upper_case(sort_part[i].field_level[j]); end
+
+@d string_type(#) == string(#)
+
+@d ccat_temp == @t\hs{2em}@>@=||@>@t\hs{2em}@>
+
+
+@d add_comma(#) == # := ccat(#,',')
+@d add_cmma_blnk(#) == # := ccat(#,', ')
+
+@d do_when_bold == begin
+if length(bold_string) > 0 then add_comma(bold_string);
+bold_string:=
+ccat(ccat(ccat(bold_string,'{\bd '),curr_str_page),'}');
+@.\bs{}bd@>
+end
+
+@d do_when_underscore == begin
+if length(us_string) > 0 then add_comma(us_string);
+us_string:=
+ccat(ccat(ccat(us_string,'\us{'),curr_str_page),'}');
+@.\bs{}us@>
+end
+
+@d do_when_italic == begin
+if length(rm_string) > 0 then add_comma(rm_string);
+rm_string:=
+ccat(ccat(ccat(rm_string,'{\it '),curr_str_page),'}');
+@.\bs{}it@>
+end
+
+@d do_when_roman == begin
+if length(rm_string) > 0 then add_comma(rm_string);
+rm_string:=ccat(rm_string,curr_str_page);
+end
+
+@d do_write(#)==write(#)
+
+@d do_lnwrite(#)==writeln(#)
+
+@ This is arbitrary, but
+there should never be more than 8 indicies.
+
+@d s_file_number == '9'
+
+@ This is used to print the subentry1 header if there are no page
+numbers under its subentry and there are under subentry2.
+
+@d id2_missing_check==
+curr_level:=2;
+if sort_record.sort_part[3].field_lngth=0
+then do_nothing else begin
+id2_is_missing:=true;
+write_header(sort_record.print[2].field_lngth,
+sort_record.print[2].field_level);
+id2_is_missing:=false;
+curr_level:=3;end
+
+@ This is used to print the primary header if there are no page
+numbers under the primary header, and there are page numbers under
+either subentry1 or subentry2.
+
+@d id1_missing_check==if sort_record.sort_part[2].field_lngth = 0
+then begin curr_level:=1;
+if sort_record.sort_part[3].field_lngth = 0
+then do_nothing else begin
+id1_is_missing:=true;
+write_header(sort_record.print[1].field_lngth,
+sort_record.print[1].field_level);
+id1_is_missing:=false;
+curr_level:=3;end;end
+else begin curr_level:=1;
+id1_is_missing:=true;
+write_header(sort_record.print[1].field_lngth,
+sort_record.print[1].field_level);
+id1_is_missing:=false;
+id2_missing_check;
+end
+
+@ This is a little macro used to access {\it write\_header}.
+
+@d write_prev_header==
+begin case curr_level of
+1: write_header(prev_ln1,prev_pn1);
+2: write_header(prev_ln2,prev_pn2);
+3: write_header(prev_ln3,prev_pn3);
+othercases do_nothing
+endcases
+end;
+
+@ The function ccat will concatenate two strings together.
+
+@^System dependent code@>
+@p function ccat(x,y:string_pass):string_pass;
+begin
+ ccat := x ccat_temp y;
+end;
+
+@* Opening and Closing the files.
+This is all very dependent on \PASCAL.
+
+@ The {\it reset\_file} procedure is used to open all files.
+One time this is done when first entering TEXIX.
+Note that the file name is ``{\tt IX\it n}'' where ``{\it n}'' is
+1--3. This can easily be extended to 4--8 by chaning the \bs{}index
+markup in TEXT1@@.
+The other time the open is done is when opening the sort file, after it
+has already been sorted.
+This is system dependent. The following will work for \PASCAL.
+
+@^System dependent code@>
+@p procedure reset_file(file_number:char);
+begin
+if file_number = '9' then reset(sort_file,
+'NAME=TEXT1$$.OUTSORT.A,LRECL=1857,RECFM=V')
+else reset(ix_file,
+ccat(ccat('NAME=TEXT1$$.IX',str(file_number)),'.A'));
+end;
+
+@
+The {\it sort\_file} is opened for output to write the sort records to.
+The {\it ix\_file} is opened for output after the sort records have
+been sorted and processed again.
+This is system dependent. The following will work for \PASCAL.
+
+@^System dependent code@>
+@p procedure file_rewrite(file_number:char);
+begin
+if file_number = '9'
+then
+rewrite(sort_file,'NAME=TEXT1$$.INSORT.A,LRECL=1857,RECFM=V')
+else
+rewrite(ix_file,
+ccat(ccat('NAME=TEXT1$$.IX',str(file_number)),
+'.A,LRECL=2048,RECFM=V'));
+end;
+
+@* The Sort Record Description. The record type called
+{\it sort\_type} is the record written to the {\it sort\_file}.
+If consists of the following:
+\la{\bf Bytes}
+\la{1}
+A one byte {\it record\_type} (0={\bf bold page number};
+2= roman page number (the default); 4=\us{underscored} \us{page}
+\us{number};
+6={\it italic page number}; 9=blind entry).
+\la{2--5}
+The integer page number. If the page
+number is in the preface part of the document, then the page number will
+be negative.
+\la{6--9}
+Used in sorting decending. That means that -1 will be at the top.
+If the page number is plus to begin with, then this is set to zero
+for sorting, so it will be at the bottom of the list.
+\la{10-29} This is the same as page number, unless the page number is
+negative, in which case this field is a roman numeral.
+\la{30--33\lbr34--333\lbr334--337\lbr338--637\lbr638--641\lbr642--941}
+The sort field. There are three arrays in this field. Each array
+consists of a 4 byte length, followed by the {\it field\_array} which is
+the length of the sort entry or subentry.
+It is these fields which will get sorted by @^Syncsort@> Syncsort.
+If there was a {\it sort\_as}
+used for a sort entry or subentry, that value passed in the {\it sort\_as}
+is placed here.
+\vs{24pt}
+\la{942--945\lbr946--1245\lbr1246--1249\lbr
+1250--1549\lbr1550--1553\lbr1554--1853}
+The print field.
+The description is the same as the sort field.
+This is the way the index entry will print, but not necessarily how
+it will sort.
+\vs{96pt}
+
+@ @<Global Constants@>=
+@!max_pn_alpha=20;
+@!max_pnum=9;
+
+@ @<Global Types@>=
+@!string_pass=string_type(max_lrecl);
+@!pass_pn_alpha=string_type(max_pn_alpha);
+@!pn_type=packed array[1..max_pnum] of char;
+@!pn_alpha_type=packed array[1..max_pn_alpha] of char;
+@!field_array = packed array[1..max_field] of char;
+@!field_type = packed record@|
+@!field_lngth :integer;
+@!field_level :field_array;
+end;
+@!sort_type = packed record@/
+@!record_type :char;
+@!page_number :integer;
+@!abs_page_number :integer;
+@!page_string :pn_alpha_type;
+@!sort_part :packed array[1..3] of field_type;
+@!print :packed array[1..3] of field_type;
+end;
+
+@* Subroutines. For many of the following subroutines, the following
+global variable, {\it current\_level} is needed to indicate what
+index level we are presently processing.
+0 = the primary index; 1 = subentry 1, and
+2 = subentry 2.
+{\it on\_a\_roll} is `true' if we have a series of consecutive page
+numbers going. `false' otherwise.
+In other places, {\it print\_style}=1 for the paragraph style; it is
+2 for the dash style; and it is 3 for the indent style.
+
+@<Global Var...@>=
+@!curr_level :integer;
+@!on_a_roll:boolean;
+
+@ The {\it write\_header} procedure will write out the primary and
+subentry level titles from {\it sort\_record.print}.
+
+@p procedure write_header(print_length:integer;print_field:field_array);
+var i:integer;
+begin
+case curr_level of
+1: begin
+ do_write(ix_file,'\goodbreak\hp ');
+@.\bs{}leavevmode@>
+@.\bs{}goodbreak@>
+@.\bs{}hp@>
+ for i:=1 to print_length do do_write(ix_file,print_field[i]);
+ case print_style of
+ '1':begin
+ do_write(ix_file,', ');
+ end;
+ '2','3':begin
+ if id1_is_missing or dot_leadering
+ then do_lnwrite(ix_file,' ')
+ else do_lnwrite(ix_file,', ');
+ end;
+ othercases do_nothing
+ endcases; end;
+2: begin case print_style of
+ '1':do_nothing;
+ '2':begin
+ do_write(ix_file,'\indentsubentry',ix,'1---');
+@.\bs{}indentsubentry@>
+ end;
+ '3':begin
+ do_write(ix_file,'\indentsubentry',ix,'1');
+ end;
+ othercases do_nothing
+ endcases;
+ for i:=1 to print_length do do_write(ix_file,print_field[i]);
+ if (print_style <> '1') or id2_is_missing or dot_leadering
+ then do_lnwrite(ix_file,' ')
+ else do_lnwrite(ix_file,', ');
+ end;
+3: begin case print_style of
+ '1':do_nothing;
+ '2':begin
+ do_write(ix_file,'\indentsubentry',ix,'2---');
+ end;
+ '3':begin
+ do_write(ix_file,'\indentsubentry',ix,'2');
+ end;
+ othercases do_nothing
+ endcases;
+ for i:=1 to print_length do do_write(ix_file,print_field[i]);
+ if dot_leadering
+ then do_lnwrite(ix_file,' ')
+ else do_lnwrite(ix_file,', ');
+ end;
+othercases do_nothing
+endcases
+end;
+
+@ The {\it numeric} function will take a packed array of length
+max\_pnum and convert
+that array (which is really the page number) into an integer.
+
+@p function numeric(simple_array:pn_type):integer;
+var @!i,j_mult,pn:integer;
+begin pn := 0;
+i := max_pnum;j_mult:=1;
+repeat
+if i = 1 then if simple_array[1] = '-'
+then pn := -1 * pn
+else pn := pn + ((ord(simple_array[1])-ord('0')) * j_mult)
+else pn := pn + ((ord(simple_array[i])-ord('0')) * j_mult);
+j_mult := j_mult * 10;
+decr(i);
+until i < 1;
+numeric:=pn;
+end;
+
+@ The {\it get\_numeric}
+function will take a packed array of length max\_pn\_alpha and convert
+that array (which is really the page number) into an integer.
+
+@p function get_numeric(x_string:string_type(max_pn_alpha)):integer;
+var @!i:integer;
+begin
+readstr(x_string,i);
+get_numeric:=i;
+end;
+
+@ The {\it strvalue} function takes an integer and converts it into
+a string.
+
+@^System dependent code@>
+@p procedure strvalue(x:integer; var results:pass_pn_alpha);
+var
+temp:string_type(max_pn_alpha);
+begin
+writestr(temp,x);
+results:=ltrim(temp);
+end;
+
+@ The {\it strconv} function takes an array and converts it into
+a string.
+
+@p function strconv(x:pn_alpha_type):string_type(max_pn_alpha);
+var i:integer;
+temp:string_type(max_pn_alpha);
+begin
+temp:='';i := 1;
+with sort_record do begin
+repeat
+if x[i] <> ' ' then
+temp := ccat(temp,str(x[i]));
+incr(i);
+until (i > max_pn_alpha) or (x[i] = ' ');
+end;
+strconv:=temp;
+end;
+
+@ The procedure {\it write\_print\_chars} will write the number of
+characters indicated
+in the parm field to the appropriate print field. There are three levels of
+print fields. Level 0 is the primary index, level 1 is the subentry 1 index,
+and level 2 is the subentry 2 index. After writing the number of characters
+to the print field, that number of characters is removed from the input record.
+You might notice that this procedure is very similar to the
+write\_sort\_chars
+procedure. The only difference in the two is that this procedure deals
+with how the index is
+to be printed after sorting. The former deals with how the
+index is to be sorted.
+Also, this procedure is called by {\it write\_sort\_chars}.
+
+@^System dependent code@>
+@p procedure write_print_chars(number_of_characters:integer);
+var m:integer;
+begin with sort_record.print[curr_level+1] do
+for m := 1 to number_of_characters do begin
+incr(field_lngth);
+field_level[field_lngth] := in_record[m];
+end;
+remove_characters(number_of_characters);
+end;
+
+@ The procedure {\it write\_sort\_chars}
+will write the number of characters indicated
+in the parm field to the appropriate sort field. There are three levels of
+sort fields. Level 0 is the primary index, level 1 is the subentry 1 index,
+and level 2 is the subentry 2 index. After writing the characters to
+the sort fields, they will be removed from the input string.
+
+@^System dependent code@>
+@p procedure write_sort_chars(number_of_characters:integer);
+var m:integer;
+begin with sort_record.sort_part[curr_level+1] do
+for m := 1 to number_of_characters do begin
+incr(field_lngth);
+field_level[field_lngth] := in_record[m];
+end;
+remove_characters(number_of_characters);
+end;
+
+@
+This procedure processes subentries within the \bs{}index command. For example,
+the phrase ``Mt. St. Helens'' would be processed here for the entry created
+by the markup:
+\vs{12pt}
+{\lin{.5in}
+{\tt\bs{}index\{volcanos\bs{}subentry1\{Mt. St. Helens\}\}}\par}
+\vs{12pt}
+\nin Note
+that there are two levels of subentries, \bs{}subentry1 and \bs{}subentry2.
+You can
+use \bs{}sortas within a \bs{}subentry. But you cannot use \bs{}subentry2 within
+\bs{}subentry1 and vise versa. If you think it should be permitted, don't.
+Also, \bs{}blindentry is not permitted within \bs{}subentry.
+(I can't be flexible {\bf everywhere}.)
+
+@ First save the current level, then set the current level to the
+subentry level. Process until the subentry is completely digested.
+
+@^System dependent code@>
+@p procedure process_subentry(@!entry_level:integer);
+var temp_level :integer;
+digest :boolean;
+begin remove_characters(1);{Throw away the `1' or `2'}@/
+temp_level := curr_level;
+curr_level := entry_level;
+digest := true;
+repeat
+@<Digest Subentry@>
+until not digest;
+curr_level := temp_level;
+end;
+
+@ If the next character is a dollar sign, then we {\bf might} have a \bs{}sortas
+If the next character is a left curly brace, then check to see if we've
+reached the end of this subentry. If the character is not `\$' or `\{',
+then write the character to the print file.
+
+@<Digest Subentry@>=
+if in_record[1] = '$'
+then @<Check for Sortas@>
+else if in_record[1] = '{'
+then @<Check for Subentry End@>
+else write_print_chars(1);
+
+@ If the next two characters are `\$\}' then we are are the end of
+the subentry.
+
+@<Check for Subentry End@>=
+begin
+if (in_record[2] = '$') and (in_record[3] = '}')
+then begin
+digest := false;
+remove_characters(3);
+end
+else write_print_chars(1);
+end
+
+@ If the next three are `\{\$\}' then we {\it might} have a sortas.
+and check the next two
+characters to see if they
+are `{\tt sa}'. If so, we {\bf do} have a sortas.
+Otherwise write those 6 characters out. In any case, if we don't have
+a `\{\$\}' to begin with, then write the first character out
+(which was a
+`\$' if you remember from earlier.)
+
+@<Check for Sortas@>=
+if (in_record[2] = '{') and
+(in_record[3] = '$') and (in_record[4] = '}')
+then begin
+if (in_record[5] = 's') and (in_record[6] = 'a')
+then do_sort_as
+else write_print_chars(6);
+end
+else write_print_chars(1)
+
+@
+The {\it equal\_arrays}
+function returns a true if the two arrays being passed are
+identical. False if not.
+
+@p function equal_arrays(fieldy:field_array;fieldz:field_array):boolean;
+var i :integer;
+still_checking :boolean;
+begin i := 1;
+still_checking := true;
+repeat
+if fieldy[i] <> fieldz[i] {if arrays not equal}
+then still_checking := false
+else incr(i);
+until (i>max_field) or not still_checking;
+equal_arrays := still_checking;
+end;
+
+@ The {\it plsort} procedure is written in PL/1 to call the CMS sort program
+Syncsort. This was necessary since you cannot call Syncsort from PASCAL/VS.
+The fields to be sorted are defined internally in the {\it plsort}
+program. This should be the same as the three sort fields in {\it sort\_type}.
+@:sort_type@>
+@:plsort@> @^PL/1@>
+@^Syncsort@>
+
+@p procedure plsort(var sort_rc:integer); fortran;
+
+@ This function converts to all uppercase.
+Notice that this is an EBCIDIC conversion, not an ASCII conversion
+to uppercase.
+
+@p function upper_case(x:char):char;
+var temp:char;
+begin
+ if (ord(x)>=129)and(ord(x)<=169) then
+ temp:=chr(ord(x)+64)
+ else temp:=x;
+upper_case:=temp;
+end;
+
+
+@* Read Each Index Entry.
+Each line in the {\it ix\_file} is a separate index entry generated with the
+\bs{\it index markup}. This markup has the format
+\par{\tt\bs{}index\it n\tt\{\it entry\rm/\it markup\tt\}}
+\par
+\nin where ``{\it entry}''
+is the textual material of the primary index. The
+``{\it markup}'' may be one of the following submarkup
+which may {\bf only}
+appear within the \bs{\it index markup}. Never outside of it.
+\item{1.} {\tt\bs{}subentry{\it n\/}}: where {\it n} is ``1'' or ``2'',
+denoting the subentry level.
+You would use this submarkup in the following fashion:
+\par{\lin{+.5in}\tt\bs{}index1\{\rm entry
+\bs\rm subentry1\tt\{\it submarkup\tt\}\}\par}
+\indent where {\it submarkup} here can only be \bs{\it sortas}.
+\item{2.}{\tt\bs{}sortas}: This markup is used to indicate text for either the
+primary sort level, or one of the two subentry sort levels that is to be
+sorted, but not printed in the resulting index. For example, someone may
+want ``10 Downing Street''
+to sort as ``ten Downing Street''. This would be accomplished by entering
+``{\tt\bs{}index1\{10 Downing Street\bs{}sortas\{ten Downing Street\}\}}''.
+You can also use \bs{\it sortas} within the \bs{\it subentry} submarkup.
+\item{3.}
+{\tt\bs{}itpn}: This markup indicates this index number
+is to be printed in italics in the sorted index.
+To use it, enter:
+\par{\lin{+.5in}\tt\bs{}index1\{\rm entry \tt
+\bs{}itpn\}\par}
+\item{4.} {\tt\bs{}bdpn}: This markup indicates this
+index number is to be
+printed in bold in the sorted index.
+To use it, enter:
+\par{\lin{+.5in}\tt\bs{}index1\{\rm entry \tt
+\bs{}bdpn\}\par}
+\item{5.} {\tt\bs{}uspn}: This markup indicates
+this index number is to
+be underscored in the sorted index.
+To use it, enter:
+\par{\lin{+.5in}\tt\bs{}index1\{\rm entry \tt
+\bs{}uspn\}\par}
+\item{6.} {\tt\bs{}blindentry}: This markup indicates this index is to be
+a blind entry or ``cross reference''. It may appear inside of a
+standard entry or subentry. There is no page number associated with
+this submarkup.
+\par
+
+@ @<Global Var...@>=
+@!respect_case :boolean;
+@!dot_leadering :boolean;
+@!print_style:char;
+@!sort_record :sort_type;
+@!in_record :string_type(max_lrecl);
+@!temp_strvalue:pass_pn_alpha;
+
+@^System dependent code@>
+@ This procedure will read the {\it ix\_file} in until all index requests
+have been read.
+
+@p procedure read_all_entries;
+var @!i,j,k:integer;
+begin
+@<Get Default Index Values@>
+while not eof(ix_file) do begin
+@<Initialize Sort Records@>@/
+@<Read Next Record@>@/
+@<Move Sort Type to Sort Record@>@/
+@<Move Page Number to Sort Record@>@/
+@<Digest the Input Record@>@/
+@<Fill in Sort Fields@>@/
+@<Write Sort Record@>@/
+end;end;
+
+@ The first index default variable passed is ``case=". A ``yes" value will
+respect the case in an index sort field. ``no" (the default) will convert
+everything to uppercase before sorting.
+
+The second index default ``style=" will be one of ``1", ``2'', or ``3''
+depending on whether the style is ``paragraph'', ``dash'', or ``indented''
+(which is the default).
+
+The third default read in here is the ``leadering=". A ``yes'' here will give
+dot leadering. Guess what ``no'' does.
+Dot leadering in the paragraph style ({\it print\_style}=1). If that
+has been requested, change {\it dot\_leadering} to false.
+
+@<Get Default Index Values@>=
+readln(ix_file,in_record);
+if dosubstr(in_record,doindex(in_record,'=')+1) = 'no'
+then respect_case := false
+else respect_case := true;
+readln(ix_file,in_record);@/
+print_style:=in_record[doindex(in_record,'=')+1];@/
+readln(ix_file,in_record);@/
+if dosubstr(in_record,doindex(in_record,'=')+1) = 'no'
+then dot_leadering := false
+else dot_leadering := true;@/
+if dot_leadering and (print_style = '1')
+then begin writeln(messages,
+'<TEXT1> Error! Dot leadering not permitted with the paragraph style.');
+writeln(messages,' Dot leadering will be disabled.');
+dot_leadering := false;
+end;
+
+@ The {\it all\_blanks} variable is filled with all blanks. Other arrays
+of the same dimension of {\it all\_blanks} can be set to blank themselves
+by saying $array\leftarrow all\_blanks$.
+
+@<Global Var...@>=
+@!all_blanks:field_array;
+
+@ @<Initialize Sort Records@>=
+with sort_record do
+for i := 1 to max_levels do begin
+sort_part[i].field_level:= all_blanks;
+sort_part[i].field_lngth := 0;
+print[i].field_level:= all_blanks;
+print[i].field_lngth := 0;
+end;
+curr_level := 0;
+
+@ Read the next index request to be processed.
+Get rid of trailing blanks.
+
+@<Read Next Record@>=
+readln(ix_file,in_record);
+in_record:=trim(in_record);
+
+@ The {\it sort\_type} is always in column 2. Column 1, 3 and 4 will always
+be curly braces. (`\{{\it s}\}\{' where ``{\it s}'' is the sort type.)
+After moving the {\it sort\_type} to the sort record, then
+delete the {\it sort\_type} and the three curly braces.
+
+@<Move Sort Type to Sort Record@>=
+sort_record.record_type := in_record[2];
+remove_characters(4);
+
+@ The end of the page number is the next right curly brace (`\}').
+Move all of the digits from the input record to the sort record,
+starting with the last digit and going forward. If a minus sign (`-')
+is in
+the page number (preface material), then put a minus sign in
+byte one of the sort record's page number.
+
+@<Global Var...@>=
+@!in_page_number:pn_type;
+
+@ Find and move the page number to the sort record. If it is negative,
+then its a roman numeral. In that case, move the page number to the
+{\it abs\_page\_number} to be sorted descending, as negative numbers
+should be. If its not negative, then move zero to {\it abs\_page\_number}
+so it will move to the bottom of that sort field.
+After it is moved, delete the appropriate number of characters to
+finish digesting the entry.
+
+@<Move Page Number to Sort Record@>=
+k := doindex(in_record,'}'); {find the end of the page number}
+i := k - 1;
+for j := 1 to max_pnum do in_page_number[j] := '0';
+j := max_pnum;
+repeat
+if in_record[i] = '-'
+then begin
+in_page_number[1] := '-';
+i := 0;
+end
+else begin
+in_page_number[j] := in_record[i];
+decr(i);
+decr(j);
+end;
+until i <= 0;
+remove_characters(k+1);@/
+sort_record.page_number:=numeric(in_page_number);@/
+if sort_record.page_number < 0
+then sort_record.abs_page_number:=abs(sort_record.page_number)
+else sort_record.abs_page_number:=0;
+k := doindex(in_record,'}'); {find the end of the page number string}
+for i := 1 to max_pn_alpha do sort_record.page_string[i] := ' ';
+for i := 1 to k-1 do
+sort_record.page_string[i] := in_record[i];
+remove_characters(k);
+
+@^System dependent code@>
+@ Process the rest of the input record. Each time you see a dollar sign,
+there could be a submarkup coming, so examine the next three characters.
+
+@<Digest the Input Record@>=
+repeat
+if (in_record[1] = '$') and (length(in_record) >= 4)
+then @<Examine Next 3 Chars@>
+else write_print_chars(1);
+until length(in_record) < 1;
+
+@ Once the index request has been digested, each sort field is checked
+to see if it is blank. If so, then {\it sort\_as} was not used, so copy the
+{\it print} field to the {\it sort} field.
+
+@<Fill in Sort Fields@>=
+with sort_record do begin
+for i := 1 to max_levels do
+if sort_part[i].field_lngth=0
+then begin
+sort_part[i].field_level := print[i].field_level;
+sort_part[i].field_lngth := print[i].field_lngth;
+check_case; end
+else check_case;
+end;
+
+@^System dependent code@>
+@ Ok, the record has been processed, so write it to the output file.
+
+@<Write Sort Record@>=
+sort_file@@:=sort_record;
+put(sort_file);
+
+@ Ok, a dollar sign signals the beginning of some sort of action code if the
+next three characters are `\{\$\}'. So lets look at them and see.
+
+@<Examine Next 3 Chars@>=
+begin if (in_record[2] = '{') and
+(in_record[3] = '$') and (in_record[4] = '}')
+then @<Examine Next 2 Chars@>
+else write_print_chars(1);{Well then, the dollar sign must be part of the index}
+end
+
+@ If a {\it blindentry} (`{\tt be}') or {\it subentry} (`{\tt se}'),
+then perform the appropriate sections.
+
+@<Examine Next 2 Chars@>=
+begin if (in_record[5] = 'b') and (in_record[6] = 'e')
+then @<Do Blind Entry@>
+else if (in_record[5] = 's') and (in_record[6] = 'e')
+then @<Do Sub Entries@>
+else if (in_record[5] = 's') and (in_record[6] = 'a')
+then do_sort_as
+else write_print_chars(6);{Might as well write all 6 characters examined}
+end
+
+@ Everything up to the next `\{\$\}' is the blind entry.
+The page number for a blind entry will always be 999999999.
+
+@<Do Blind Entry@>=
+begin remove_characters(6);{Throw away the `\$\{\$\}be'}@/
+i := doindex(in_record,'{$}');
+curr_level:=2;
+write_print_chars(i-1);
+curr_level:=0;
+remove_characters(3);{Throw away the `\{\$\}'}
+sort_record.page_number := 999999999;
+end
+
+@ Process the subentry, depending on whether or not it is
+{\it subentry}1 or {\it subentry}2. If not, ignore the whole thing.
+
+@<Do Sub Entries@>=
+begin remove_characters(6); {Throw away the `\$\{\$\}se'}
+if in_record[1] = '1'
+then process_subentry(1)
+else if in_record[1] = '2'
+then process_subentry(2)
+else write_print_chars(1);{Perhaps someone entered \bs{}subentry3??}
+end
+
+@* Write Formated Index. This section of code takes the sorted index
+entries, merges them together, and writes them back out to the index file
+to be read in by \bs{}printindex in the user's program. This section consists
+of three procedures for accomplishing this feat: {\it add\_page\_number},
+{\it digest\_the\_line}, and {\it read\_sorted\_records}.
+{\it numeric} is a function to convert the character string page number into
+an integer.
+
+@^System dependent code@>
+@ The procedure {\it add\_page\_number} is used to add the page number of the
+current sorted record to the {\it string\_bold} if a
+\bs{\it bdpn}, or to
+the {\it string\_underscore} if a \bs{\it uspn},
+or otherwise to
+the {\it str\_build}.
+
+@p procedure add_page_number;
+var i:integer;
+
+@ @<Global Var...@>=
+@!prev_page_number:integer;
+@!prev_pg_string:string_type(max_pn_alpha);
+@!prev_record_type:char;
+
+@ First check to see if we have a new page number. If so, then add the page
+number to the string. Otherwise, only add the page number if the sort type
+is new and is not equal to ``6'' (italic).
+
+@p
+begin
+with sort_record do if prev_page_number = page_number
+then if (prev_record_type = record_type) and (record_type <> '9')
+ then getout
+else if record_type = '6' then getout;
+@<Add Page Number to the String@>
+end;
+
+@ {\it str\_build} is used to accumulate all of the page numbers.
+{\it str\_blind\_entry} is used to accumulate the blind entry information
+(typically there should be no more than one, but allowance is made for more.)
+
+@<Global Var...@>=
+@!str_build:string_type(max_lrecl);
+@!str_blind_entry:string_type(max_lrecl);
+{come on, who's going to have one that long?}
+
+@ This module will take the page number of the current sorted index
+record and add it to the build string, unless the record type is
+9, which is a blind entry, in which case it is added to the blind entry
+string. Note that if the record type is 0 (bold), 4 (underscore) or
+6 (italic), then the page number is prefaced with ``B'', ``U'' or
+``I'' respectively. This identifies that the page number is to be
+emphasized when the build string is processed through the
+{\it digest\_the\_line} procedure.
+
+@<Add Page Number to the String@>=
+if sort_record.record_type = '9' then @<Do Sorted Blind Entry@>
+else begin @<Do Sorted Non Blind Entry@>
+end;
+prev_page_number := sort_record.page_number;@/
+prev_pg_string := strconv(sort_record.page_string);@/
+prev_record_type := sort_record.record_type;
+
+@ @<Do Sorted Blind Entry@>=
+with sort_record.print[3] do begin
+if length(str_blind_entry) > 0 then add_cmma_blnk(str_blind_entry);
+for i := 1 to field_lngth do
+str_blind_entry := ccat(str_blind_entry,str(field_level[i]));
+end
+
+@ If the {\it page\_string[1]} is less than zero, its alphabetic and
+roman numeral processing is necessary.
+
+@<Do Sorted Non Blind Entry@>=
+with sort_record do begin
+if ord(page_string[1]) < ord('0')
+then @<Add Roman Page Number@>
+else @<Add Arabic Page Number@>;
+end;
+
+@ @<Add Roman Page Number@>=
+begin
+if length(str_build) >= 1
+then str_build:=ccat(',',str_build);
+strvalue(page_number,temp_strvalue);
+str_build := ccat(ccat(ccat(strconv(page_string),
+','),temp_strvalue),str_build);
+if record_type = '0'
+then str_build := ccat('B',str_build)
+else if record_type = '4'
+then str_build := ccat('U',str_build)
+else if record_type = '6'
+then str_build := ccat('I',str_build)
+end
+
+@ @<Add Arabic Page Number@>=
+begin
+if length(str_build) >= 1
+then add_comma(str_build);
+if record_type = '0'
+then str_build := ccat(str_build,'B')
+else if record_type = '4'
+then str_build := ccat(str_build,'U')
+else if record_type = '6'
+then str_build := ccat(str_build,'I');
+str_build := ccat(str_build,strconv(page_string));
+end
+
+@ {\it output\_string} is used to collect all of the page numbers
+before writing them back out to the index file;
+{\it bold\_string} collects the bold page numbers;
+{\it us\_string} collects the underscored page numbers;
+{\it rm\_string} collects everything not bold or underscored.
+As you might expect, {\it italic\_last\_page} is true when the last
+page number of a `roll' is to be italic.
+
+@<Global Var...@>=
+@!output_string:string_type(max_lrecl);
+@!bold_string:string_type(max_lrecl);
+@!us_string:string_type(max_lrecl);
+@!rm_string:string_type(max_lrecl);
+@!italic_last_page:boolean;
+
+@^System dependent code@>
+@ {\it finish\_the\_process} is a procedure that is used to
+first check to see if we are {\it on\_a\_roll}, which means we are formatting
+something like pages 1-3.
+After doing that, if there were any bold page numbers, they are concatenated
+to the front of the {\it output\_string}; underscored page numbers (if any)
+are concatenated to the end of the {\it output\_string}.
+
+@p procedure finish_the_process;
+begin
+@<Concatenate Bold Pages to Output@>;@/
+@<Concatenate Roman Pages to Output@>;@/
+@<Concatenate Underscored Pages to Output@>;
+end;
+
+@ If there were any bold page numbers, then add them to the {\it
+output\_string}.
+
+@<Concatenate Bold...@>=
+if length(bold_string) > 0 then begin
+if length(output_string) > 0 then add_cmma_blnk(output_string);
+output_string := ccat(output_string,bold_string);
+end
+
+@ If we were on a roll (a consecutive series of page numbers) then finish
+the roll. Then if there were any roman or italic page numbers, add them to
+the {\it output\_string}.
+
+@<Concatenate Roman...@>=
+if on_a_roll then if italic_last_page
+then rm_string :=
+ccat(ccat(ccat(rm_string,'{\it '),trim(prev_pg_string)),'}')
+else rm_string := ccat(rm_string,prev_pg_string);
+@.\bs{}it@>
+if length(rm_string) > 0 then begin if length(output_string) > 0
+then add_cmma_blnk(output_string);
+output_string := ccat(output_string,rm_string);
+end
+
+@ If there were any underscored page numbers, add them to the {\it
+output\_string} here.
+
+@<Concatenate Under...@>=
+if length(us_string) > 0 then begin if length(output_string) > 0
+then add_cmma_blnk(output_string);
+output_string := ccat(output_string,us_string);
+end
+
+@ The procedure {\it start\_digesting} will initialize a few variables,
+then add the {\it current\_page} to the appropriate list.
+
+@p procedure start_digesting;
+begin @<Initialize Some Global Variables@>;@/
+prev_pg_string := curr_str_page;@/
+@<Do Appropriate Highlighting@>;
+end;
+
+@ @<Initialize Some Global Variables@>=
+italic_last_page := false;
+bold_string := ''; us_string :=''; rm_string:='';
+starting_to_process:=false;
+on_a_roll:=false
+
+@ @<Get the Current Print Type of this Page Number@>=
+print_type:= curr_str_page[1];
+if (print_type='B') or (print_type='I') or (print_type='U')
+then curr_str_page:=dosubstr(curr_str_page,2)
+else print_type := ' '
+
+@ @<Do Appropriate Highlighting@>=
+if print_type = 'B' then do_when_bold
+else if print_type = 'I' then do_when_italic
+else if print_type = 'U' then do_when_underscore
+else do_when_roman
+
+@^System dependent code@>
+@ The procedure {\it digest\_the\_line} will take the full {\it str\_build}
+and add the dashes when there is a run of page numbers (i.e. ``{\tt 1,2,3}'' bec
+``1-3''); add the bold page numbers in front of the string (i.e.
+``{\tt 1,2,B3,3,4}''
+prints as ``{\bf 3},1-4''. Note that page 3 had to appear twice
+``{\tt B3,3}''. If it had only appeard as a bold page number and not the
+default, ``{\tt 1,2,B3,4}'', you would get ``1-2,{\bf 3},4'');
+add the underscored
+page numbers to the end of the string (similar to bold page numbers,
+``1,2,U3,3,4'' prints as ``1-4,\us{3}''); and print in italics when
+necessary.
+
+@p procedure digest_the_line;
+var
+i:integer;
+@!temp_roman:string_type(max_pn_alpha);
+@!no_page_numbers:boolean;
+begin
+@<Process Each Page Number@>;@/
+@<Write the Line Out@>;
+end;
+
+@ @<Global Var...@>=
+@!starting_to_process :boolean;
+@!print_type:char;
+@!curr_str_page :string_type(max_pn_alpha);
+@!curr_num_page :integer;
+
+@ Initialize the variables, then read each page number until the whole
+string is digested, then finish off the page number string before returning.
+If {\it str\_build} is null, then we are doing a blind entry with no
+page numbers associated. In that case, initialize the highlighting
+strings.
+@<Process Each Page Number@>=
+output_string := '';
+starting_to_process := true;
+no_page_numbers:=false;
+if length(str_build) > 0
+then repeat @<Digesting Each Page Number@>
+ until length(str_build) < 1
+else if starting_to_process then begin
+@<Initialize Some Global Variables@>;
+no_page_numbers:=true;
+end;
+finish_the_process;
+
+@ First get the next page number. Then if we are starting, initialize
+everything. If we have already started, then check to see if we have a
+consecutive page number sequence (i.e. pages 1,2,3,4 etc.) which is called
+a `roll'.
+@<Digesting Each Page Number@>=
+@<Get Next Page Number@>;@/
+if starting_to_process
+then start_digesting
+else @<Check for a Roll@>;
+prev_page_number:=curr_num_page;
+
+@ @<Get Next Page Number@>=
+i := doindex(str_build,',');
+if i < 1 then begin curr_str_page:=str_build; str_build:='';end
+else begin
+curr_str_page:=dosubstr(str_build,1,i-1);
+str_build := dosubstr(str_build,i+1);
+end;
+@<Get the Current Print Type of this Page Number@>;@/
+if ord(curr_str_page[1]) < ord('0') {Then its alphabetic}
+then @<Get Numeric for Roman Page Number@>
+else curr_num_page:=get_numeric(curr_str_page);
+
+@ @<Get Numeric for Roman Page Number@>=
+begin
+i := doindex(str_build,',');
+if i < 1 then begin temp_roman:=str_build; str_build:='';end
+else begin
+temp_roman:=dosubstr(str_build,1,i-1);
+str_build := dosubstr(str_build,i+1);
+end;
+curr_num_page:=get_numeric(temp_roman);
+end
+
+@ If we are on a roll, then the previous page number will be one less than
+the current page number. If this is not the case, then {\it
+finish\_the\_process}
+and {\it start\_digesting} all over again.
+
+@<Check for a Roll@>=
+if ((prev_page_number = curr_num_page) and (print_type <> ' ')) or
+ ((prev_page_number>=0) and (prev_page_number = curr_num_page - 1)) or
+ ((prev_page_number<0) and (prev_page_number = curr_num_page + 1))
+then begin
+@<Got a Roll Going@>
+prev_pg_string:=curr_str_page;
+end
+else begin finish_the_process; start_digesting; end
+
+@ Ok, so we're on a roll, right? If the {\it print\_type} is ``B'' (bold) or
+``U'' (underscored) then do those and return. Otherwise, check to se if we
+were previously {\it on\_a\_roll}. If we weren't, then add the hyphen to the
+starting page number. In either case, move the {\it current\_page} to the
+{\it prev\_pg\_string}. Lastly, if the page number is italic, then set
+{\it italic\_last\_page} to true, in case it ends up being the last page in
+the string.
+
+@<Got a Roll Going@>=
+if print_type = 'B' then do_when_bold
+else if print_type = 'U' then do_when_underscore
+else begin if not on_a_roll then begin on_a_roll := true;
+rm_string:=ccat(rm_string,'--'); end;
+if print_type = 'I' then italic_last_page := true
+else italic_last_page := false;
+end;
+
+@ Before the page numbers are actually written, we need to write out
+the ID depending on which level we are formatting. Level 1 is the
+primary level, level 2 is the subentry1, and level 3 is subentry2.
+Then, we need to do a couple of things depending on whether there is
+dot leadering going on or not.
+Finally, write the output string to the formatted index file.
+
+@<Write the Line Out@>=
+write_prev_header;
+if dot_leadering then begin @<Do Dot Leadering@> end
+else begin @<Do not do Dot Leadering@> end;
+do_lnwrite(ix_file,output_string);
+
+@ If there is dot leadering, then add the blind entry first, before adding
+the leadering.
+
+@<Do Dot Leadering@>=
+if length(str_blind_entry) > 0
+then begin
+if length(output_string) > 0 then add_cmma_blnk(output_string);
+output_string := ccat(output_string,str_blind_entry);
+end;
+output_string := ccat(ccat('\leader{}',output_string),'\par');
+@.\bs{}leader@>
+@.\bs{}par@>
+
+@ @<Global Var...@>=
+@!new_level_1:boolean;
+
+@ If there is not dot leadering, then the blind entry goes at the end of the
+page, except for
+the paragraph style ({\it print\_style}=1). Also, if the paragraph style,
+then do not add the {\tt \bs{}par} at the end. Instead, add a comma
+if there is a new level 1 entry.
+
+@<Do not do Dot Leadering@>=
+if (print_style <> '1') and (length(str_blind_entry) > 0)@/
+then begin
+if length(output_string) > 0 then add_cmma_blnk(output_string);
+output_string := ccat(output_string,str_blind_entry);
+end;
+if print_style = '1'
+then if new_level_1 and ((length(str_blind_entry)=0) or no_page_numbers)
+then do_nothing
+else add_cmma_blnk(output_string)
+else output_string := ccat(output_string,'\par');
+@.\bs{}par@>
+
+@ The {\it build\_sorted\_index} procedure is the driving force behind
+formatting the index. The basic process is to read all of the sorted records in
+and create a record called {\it str\_build}. There will be one of these for
+each primary and subentry level index. After doing the {\it str\_build},
+this record is then read through again to add the {\it\TeX T1} markup
+necessary to
+print out the sorted index line.
+
+@p procedure build_sorted_index;
+var @!first_time_through:boolean;
+
+@ @<Global Var...@>=
+@!prev_sr1:field_array;
+@!prev_sr2:field_array;
+@!prev_sr3:field_array;
+@!prev_pn1:field_array;
+@!prev_ln1:integer;{the length of pn1}
+@!prev_pn2:field_array;
+@!prev_ln2:integer;{the length of pn2}
+@!prev_pn3:field_array;
+@!prev_ln3:integer;{the length of pn3}
+@!id1_is_missing:boolean;
+@!id2_is_missing:boolean;
+
+@ The first thing we need to do is read the next sorted record. If this is the
+first time ever through this procedure, then do some quick initializing. Then,
+once we come to a new ID, we should write out the previous index page numbers,
+etc. If the ID has not changed from the previous record, then add the page
+number given here on the sorted record to the {\it str\_build}.
+
+@p begin first_time_through:=true;
+while not eof(sort_file) do begin
+sort_record:=sort_file@@;
+get(sort_file);
+if first_time_through then begin @<Do First Time Inits@> end;
+if not equal_arrays(prev_sr1,sort_record.sort_part[1].field_level)
+then begin
+@<Start a New Level 1@> end
+else if not equal_arrays(prev_sr2,sort_record.sort_part[2].field_level)
+then begin
+@<Start a New Level 2@> end
+else if not equal_arrays(prev_sr3,sort_record.sort_part[3].field_level)
+and (sort_record.record_type <> '9')
+then begin
+@<Start a New Level 3@> end
+else add_page_number;
+end;
+@<Digest and Print Level 1@>
+end;
+
+@ First write out the {\it \bs{}everyindex} record at the beginning
+of the index file.
+
+@<Do First Time Inits@>=
+do_lnwrite(ix_file,'\everyindex{',ix,'}');
+@.\bs{}everyindex@>
+
+@ These initializations only get done at the beginning of the
+first sort record.
+
+@<Do First Time Inits@>=
+first_time_through:=false;
+id1_is_missing:=false;
+id2_is_missing:=false;
+new_level_1:=false;
+with sort_record do begin
+prev_sr1:=sort_part[1].field_level;
+prev_sr2:=sort_part[2].field_level;
+prev_sr3:=sort_part[3].field_level;
+prev_pn1:=print[1].field_level;
+prev_ln1:=print[1].field_lngth;
+prev_pn2:=print[2].field_level;
+prev_ln2:=print[2].field_lngth;
+prev_pn3:=print[3].field_level;
+prev_ln3:=print[3].field_lngth;
+end;
+prev_page_number:=0;
+str_blind_entry:='';@/
+str_build:='';@/
+
+@ If this is not a blind entry, then check to see if sort records for
+the first level id are missing. If they are, then the id name itself
+will be printed in {\it id1\_missing\_check}.
+
+@<Do First Time Inits@>=
+if sort_record.record_type <> '9'
+then id1_missing_check;
+
+@ Only alpha (a--z and A--Z) and numeric (0--9) characters get an
+{\it \bs{}everyletterbreak}. Punctuation, for example, will not get
+an {\it \bs{}everyletterbreak}.
+
+@<Do First Time Inits@>=
+with sort_record.sort_part[1] do begin
+if ((field_level[1]>='a') and (field_level[1]<='i')) or @/
+((field_level[1]>='j') and (field_level[1]<='r')) or @/
+((field_level[1]>='s') and (field_level[1]<='z')) or @/
+((field_level[1]>='A') and (field_level[1]<='I')) or @/
+((field_level[1]>='J') and (field_level[1]<='R')) or @/
+((field_level[1]>='S') and (field_level[1]<='Z')) or @/
+((field_level[1]>='0') and (field_level[1]<='9'))
+ then
+do_lnwrite(ix_file,'\everyletterbreak{',ix,'}{',field_level[1],'}')
+ else
+do_lnwrite(ix_file,'\everyletterbreak{',ix,'}{}');
+end
+@.\bs{}everyletterbreak@>
+
+@
+First we need to {\it digest\_the\_line} which is the current index's
+entry. {\it digest\_the\_line} will write out the formatted index unless
+we are in the paragraph
+style of formatted index
+({\it print\_style}=1). If we are in the paragraph style,
+then we need to write out any {\it str\_blind\_entry} pending.
+
+@<Digest and Print Level 1@>=
+new_level_1:=true;
+digest_the_line;
+if print_style = '1' then
+do_lnwrite(ix_file,str_blind_entry,'\par');
+new_level_1:=false;
+@.\bs{}par@>
+
+@ We have just found the start of a new primary index level.
+First we need to process the previous index's
+entry.
+If there is a blind entry, and it is style `1', then add it now.
+Next, if we are at a letter break (i.e., going from the sorted ``A''
+primary index letters to the ``B''s) then write the ``{\tt
+\bs{}everyletterbreak}'' to the formatted index file.
+
+@<Start a New Level 1@>=
+@<Digest and Print Level 1@>
+with sort_record.sort_part[1] do begin
+if (field_level[1] <> prev_sr1[1])
+then if
+(((field_level[1]>='a') and (field_level[1]<='i')) or @/
+((field_level[1]>='j') and (field_level[1]<='r')) or @/
+((field_level[1]>='s') and (field_level[1]<='z')) or @/
+((field_level[1]>='A') and (field_level[1]<='I')) or @/
+((field_level[1]>='J') and (field_level[1]<='R')) or @/
+((field_level[1]>='S') and (field_level[1]<='Z')) or @/
+((field_level[1]>='0') and (field_level[1]<='9'))) @/
+then do_lnwrite(ix_file,'\everyletterbreak{',ix,'}{',field_level[1],'}')
+ else
+do_lnwrite(ix_file,'\everyletterbreak{',ix,'}{}');
+end;
+@.\bs{}everyletterbreak@>
+
+@ The next thing to be done when starting a new primary index is to
+check the subentries. If they exist, then you can assume that this
+primary level index has no page numbers associated with it. The macro
+{\it id1\_missing\_check} accomplishes this.
+
+@<Start a New Level 1@>=
+curr_level:=1;@/
+if sort_record.record_type <> '9'
+then id1_missing_check;
+
+@ The complicated stuff is done. So now we can actually do some simple
+initializations to get this new level 1 (primary level) sort rolling.
+
+@<Start a New Level 1@>=
+str_build:='';
+str_blind_entry:='';
+with sort_record do begin
+prev_sr1:=sort_part[1].field_level;
+prev_sr2:=sort_part[2].field_level;
+prev_sr3:=sort_part[3].field_level;
+prev_pn1:=print[1].field_level;
+prev_ln1:=print[1].field_lngth;
+prev_pn2:=print[2].field_level;
+prev_ln2:=print[2].field_lngth;
+prev_pn3:=print[3].field_level;
+prev_ln3:=print[3].field_lngth;
+prev_page_number:=0;
+end;
+add_page_number;
+
+@ We have just found the start of a new subentry1 index level.
+These modules are similar to {\it Start a New Level 1}, but not as
+detailed since it is dealing with fewer levels.
+First we need to {\it digest\_the\_line} which is the previous index's
+entry. {\it digest\_the\_line} will write out the formatted index unless
+we are in the paragraph
+style of formatted index
+({\it print\_style}=1).
+
+@<Start a New Level 2@>=
+digest_the_line;
+
+@ The next thing to be done when starting a new subentry1 index is to
+check the subentry2. If it exists, then you can assume that this
+subentry index has no page numbers associated with it.
+This is done in {\it id2\_missing\_check}.
+
+@<Start a New Level 2@>=
+id2_missing_check;
+
+@ The complicated stuff is done. So now we can actually do some simple
+initializations to get this new level 2 (subentry2 level) sort rolling.
+
+@<Start a New Level 2@>=
+str_build:='';
+if print_style <> '1' then str_blind_entry:='';
+with sort_record do begin
+prev_sr2:=sort_part[2].field_level;
+prev_sr3:=sort_part[3].field_level;
+prev_pn2:=print[2].field_level;
+prev_ln2:=print[2].field_lngth;
+prev_pn3:=print[3].field_level;
+prev_ln3:=print[3].field_lngth;
+prev_page_number:=0;
+end;
+add_page_number;
+
+@ We have just found the start of a new subentry2 index level.
+These modules are similar to {\it Start a New Level 2}, but not as
+detailed since it is dealing with the lowest level.
+First we need to {\it digest\_the\_line} which is the previous index's
+entry. {\it digest\_the\_line} will write out the formatted index unless
+we are in the paragraph
+style of formatted index
+({\it print\_style}=1).
+
+@<Start a New Level 3@>=
+digest_the_line;
+curr_level:=3;
+
+@ Do some
+initializations to get this new level 3 (subentry2) sort rolling.
+
+@<Start a New Level 3@>=
+str_build:='';
+if print_style <> '1' then str_blind_entry:='';
+with sort_record do begin
+prev_sr3:=sort_part[3].field_level;
+prev_pn3:=print[3].field_level;
+prev_ln3:=print[3].field_lngth;
+prev_page_number:=0;
+end;
+add_page_number;
+
+@* Main Program.
+Ok, here is the main program. First we initialize (all\_blanks); then
+set the ix\_file for input and the sort\_file for output; read all of the
+entries, processing each one; close the files; sort; read the sorted file
+in and build the entries, writing them back to the ix\_file to be read in
+by the index markup. WHEW!!
+
+@p
+begin @<Initialize Main@>@/
+termout(messages);
+reset_file(ix);@/
+file_rewrite(s_file_number); {Should always be file 9}@/
+read_all_entries;@/
+close(ix_file);@/
+close(sort_file);@/
+@<Sort the Index@>@/
+reset_file(s_file_number);@/
+file_rewrite(ix);@/
+build_sorted_index;@/
+end;
+
+@ @<Initialize Main@>=
+for i := 1 to max_field do all_blanks[i] := ' ';
+
+@ As mentioned earlier, {\it plsort} is an external PL/1 subroutine
+@^PL/1@>
+@^Syncsort@>
+which is used to call Syncsort to sort the file.
+The sort fields are as follows:
+\halign{\hskip3em\hfill#\hfill&&\hskip3em\hfill#\hfill\cr
+\bf Starting Column&\bf Length&\bf Field Description&\bf Order\cr
+38&300&character&ascending\cr
+342&300&character&ascending\cr
+646&300&character&ascending\cr
+10&4&binary&descending\cr
+6&4&binary&ascending\cr}
+
+@<Global Var...@>=
+sort_rc:integer;
+
+@ @<Sort the Index@>=
+plsort(sort_rc);
+if sort_rc = 0
+ then writeln(messages,'Index Successfully Completed')
+ else writeln(messages,'Index Failed');
+
+@* Index.
+All modules in which an identifier is
+used are listed with that identifier, except that reserved words are
+indexed only when they appear in format definitions, and the appearances
+of identifiers in module names are not indexed. Underlined entries
+correspond to where the identifier was declared. Error messages, control
+sequences put into the output, and a few
+other things like ``Syncsort'' are indexed here too.