%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Module: ZzTeX Indexing Facilities, version 2 % % Synopsis: This file contains the facilities for generating index % parameter files, raw entry files, and for setting the indexes. % % Author: Paul C. Anagnostopoulos % Created: 13 June 2013 % % Copyright 1989--2020 by Paul C. Anagnostopoulos % under The MIT License (opensource.org/licenses/MIT) % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Define Index Locators % ------ ----- -------- % This list contains the definition of each locator. New ones must be % added to the end, and any program looking at the definitions must assume % that duplicates may exist and that the last one prevails. \def \zixloclist {} \def \definelocator #1#2#3{% {type}{attributes}{[format-args]} {\setflag \pg = \false \setflag \rng = \false \setflag \txt = \false \def \page {\setflag \pg = \true}% \def \nopage {\setflag \pg = \false}% \def \range {\setflag \pg = \true \setflag \rng = \true}% \def \norange {\setflag \rng = \false}% \def \text {\setflag \txt = \true}% \def \notext {\setflag \txt = \false}% #2% {\def \attr {#2}% \edef \znext {% \noexpand\append{{#1}{\expandafter\zdefof \meaning\attr\zmark}}}% \znext{\zixloclist}}% \if \andp{\pg}{\notp{\txt}}% \withname\gdef{\x#1}##1{\zixindex{#1}{S}{##1}{}}% \if \rng \withname\gdef{\x#1begin}##1{\zixindex{#1}{B}{##1}{}}% \withname\gdef{\x#1end}##1{\zixindex{#1}{E}{##1}{}}% \fi \else\if \txt \withname\gdef{\x#1}##1##2{\zixindex{#1}{S}{##1}{##2}}% \if \rng \withname\gdef{\x#1begin}##1##2{\zixindex{#1}{B}{##1}{##2}}% \withname\gdef{\x#1end}##1##2{\zixindex{#1}{E}{##1}{##2}}% \fi \else \error{locattr}{Invalid index locator attributes}% \fi\fi \if \notp{\emptyargp{#3}}% \definelocatorformat{#1}#3\relax \fi}} % Define Locator Formats % ------ ------- ------- % This list contains the definition of each locator format. It works like % the locator list above. \def \zixformatlist {} \def \definelocatorformat #1#2#3#4{% {name}{rank.order}{level-1-items}{level-2-items} {\def \lvli {#3}% \def \lvlii {#4}% \edef \znext {% \noexpand\append{{#1}{#2}% {\expandafter\zdefof \meaning\lvli\zmark}% {\expandafter\zdefof \meaning\lvlii\zmark}}}% \znext{\zixformatlist}}} % Builtin Locator Types % ------- ------- ----- % These locators are built-in for ease of use. Ranks 0, 1, and 5 and higher % are reserved for the user. Ranks 10 and higher result in deferred % locators. \definelocator{page}{\page\range} {{2.1}{prefix1:, |tmplt:?P|rtmplt:?P--?Q|sep:, }{}} \definelocator{figure}{\page\range} {{2.3}{prefix1:, |tmplt:?P\emph{f}|rtmplt:?P--?Q\emph{f}|sep:, }{}} \definelocator{note}{\page} {{2.5}{prefix1:, |tmplt:?P\emph{n}|sep:, }{}} \definelocator{table}{\page\range} {{2.7}{prefix1:, |tmplt:?P\emph{t}|rtmplt:?P--?Q\emph{t}|sep:, }{}} \definelocator{see}{\nopage\text} {{3}{prefix1:. \emph{See} |tmplt:?T|sep:; }{}} \definelocator{seealso}{\nopage\text} {{4}{prefix1:. \emph{See also} |tmplt:?T|sep:; }{}} % Generate Indexes % -------- ------- \def \zixnamelist {} \setflag \zindexing = \false \def \generateindex #1{% {name} \append{#1}{\zixnamelist}% \global\setflag \zindexing = \true} % Index File Handling % ----- ---- -------- %\definewrite{\zixrootfile} %\definewrite{\zixdivfile} \setflag \zixdivopen = \false \def \zindexinit {% \if \zindexing \maplist{\zixwritezzp{##1}}{\zixnamelist}% \immediate\openout \zixrootfile = \jobname.zzi\relax \immediate\write \zixrootfile {\zcomment Raw index entry file for book `\jobname' root.}% \fi} \def \zindexfinal {% \if \zindexing \immediate\write \zixrootfile {\zcomment [end]}% \immediate\closeout \zixrootfile \fi} \def \zindexdivinit #1{% {division} \if \zindexing \immediate\openout \zixdivfile = #1.zzi\relax \immediate\write \zixdivfile {\zcomment Raw index entry file for book `\jobname', division `#1'.}% \global\setflag \zixdivopen = \true \fi} \def \zindexdivfinal {% \if \zixdivopen \immediate\write \zixdivfile {\zcomment [end]}% \immediate\closeout \zixdivfile \global\setflag \zixdivopen = \false \fi} \def \zixwritezzp #1{% {name} \begingroup \processdesign{\index}{#1}% \immediate\openout \zwritea = #1.zzp\relax \immediate\write \zwritea {root \jobname;}% \immediate\write \zwritea {index #1;}% \immediate\write \zwritea {divisions \the\divisions;}% \maplist{\immediate\write \zwritea {define-locator ##1;}}{\zixloclist}% \maplist{\immediate\write \zwritea {define-format ##1;}}{\zixformatlist}% \immediate\write \zwritea {locators \the\locators;}% \immediate\write \zwritea {\the\indexparams}% \immediate\closeout \zwritea \endgroup} % Write Raw Index Entry % ----- --- ----- ----- % This macro is called from the indexing macros defined by % \definelocator. \def \zixindex #1#2#3#4{% {locator}{page-code}{headings}{[text]} \zixindexb {#1}{#2}{#4}#3|||\zmark} \catcode"B8 = \catother \def \zixindexb #1#2#3#4|#5|#6|#7\zmark{% {locator}{page-code}{text} % head1|head2|head3|...\zmark \if \zindexing \zbeginhidewrite \def \zhdx {#4}% \def \zhdy {#5}% \def \zhdz {#6}% \def \ztxt {#3}% \edef \znext {#1^^b8% #2^^b8% \noexpand\folio^^b8% \expandafter\zdefof \meaning\zhdx\zmark^^b8% \expandafter\zdefof \meaning\zhdy\zmark^^b8% \expandafter\zdefof \meaning\zhdz\zmark^^b8% \expandafter\zdefof \meaning\ztxt\zmark}% \write \if \zixdivopen \zixdivfile \else \zixrootfile \fi \expandafter{\znext}% \if \vmodep \repeatpenalty \fi % In case glue follows write. \zendhidewrite \fi} \catcode"B8 = \catinvalid % Discussion of Index Formatting % ---------- -- ----- ---------- % An index is formatted inside an index block, which is more or less % like a text block. Within the index block are occurences of the % \indexentry macro, one for each entry. In addition, the % \alphabreak macro can be used to produce breaks preceding the 'A' % entries, 'B' entries, etc. % % In an index block, parts 4 and 5 of the mark are used to remember % index headings so they can be carried forward to subsequent columns % or pages. The mark is used as follows: % % Part Subpart Usage % 4 1 Boolean, true if in the midst of a first-level entry. % 4 2 The first-level entry heading. % 5 1 Boolean, true if in the midst of a second-level entry. % 5 2 The second-level entry heading. % Index Block % ----- ----- \defineblock{\index}{\endindex}{\false}{redefine} %~block index Type % \def \alphaformat ##1{...} % Alphabetic break formatter. % \bodyfont = {\size\style} % Body font. % \clubpenalty = penalty % Club penalty is often different. % \columngutter = glue % Column gutter. % \divisions = {...} % Which divisions to include. % \leftindent = glue % Left indentation. % \locators = {...} % Which locators to include. % \indexparams = {...} % Parameters for index generator. % \parhang = glue % Paragraph hang. % \parindent = dimen % Paragraph indent. % \parrag = dimen % Paragraph raggedness. % \parskip = glue % Paragraph skip. % \position = {...} % Position and number of columns. % \rightindent = glue % Right indentation. %~end %\definetoks{\locators} %\definetoks{\indexparams} %\definecount{\previndexlevel}{0} %\definecount{\zixcols}{0} \def \index #1{% {type} \blockcantbein{\index}{\index}% \beginblockscope{index}% \global\increment \indexdepth \clubpenalty = \breakallowed %~default soft \parskip = 0pt %~default soft \processdesign{\index}{#1}% \global\increment \indexnumber \def \zixname {#1}% \zixparsepos \indexformat} \def \indexformat {% \the\bodyfont \alterindentation{\leftindent}{\rightindent}% \setparrag{\parrag}% \setpagecolumns{\zixpageuse}{\the\zixcols}{\columngutter}% \everycolumn = {\zixcarryover}% \begingroup \let \zfullcolumnbreak = \fullcolumnbreak \let \fullcolumnbreak = \zixcolumnbreak \let \shortcolumnbreak = \zixcolumnbreak \global\previndexlevel = 0\relax} \def \endindex {% \endgraf % End last entry. \vbox{}% % Force start of column if not already. \endgroup \zclearindexmarks \everycolumn = {}% \setpagecolumns{\usetextmeasure}{1}{0pt}% \global\decrement \indexdepth \endblockscope{index}} \def \zixparsepos {% \zixcols = 1 \def \zixpageuse {\usetextmeasure}% \setflag \zixrunin = \false \let \oneup = \relax \let \outline = \relax \def \runin {\setflag \zixrunin = \true}% \def \threeup {\zixcols = 3}% \def \twoup {\zixcols = 2}% \let \usetextmeasure = \relax \let \usetextwidth = \relax \def \usetypewidth {\def \zixpageuse {\usetypewidth}}% \the\position} % Format Index Entries % ------ ----- ------- \setflag \zixbreak = \false % This macro formats the main portion of an index entry. \def \indexentry #1#2#3{% level{heading}{locators} \ifcase #1% \or % Level 1 heading: \endgraf \zclearindexmarks \zixmaybeforcebreak \hangindent = \parhang \hangafter = 1 {\name{\index\zixname entryiformat}{#2\setmarkinfo{4}{\true}{#2}}{#3}}% \or % Level 2 heading: \if \notp{\zixrunin}% \endgraf \if \eqlp{\previndexlevel}{1}\vpenalty{\breaknever}\fi \fi \setmarkinfo{5}{\false}{}% \zixmaybeforcebreak \hangindent = \parhang \hangafter = 1 {\name{\index\zixname entryiiformat}{#2\setmarkinfo{5}{\true}{#2}}{#3}}% \or % Level 3 heading: \if \notp{\zixrunin}\endgraf \fi \zixmaybeforcebreak \hangindent = \parhang \hangafter = 1 {\name{\index\zixname entryiiiformat}{#2}{#3}}% \fi \global\previndexlevel = #1\relax \ignorespaces} % This macro format any deferred locators. \def \indexdeferred #1#2{% level{locators} \ifcase #1% \or % Level 1 deferred locators: \if \notp{\zixrunin}\endgraf \fi \setmarkinfo{5}{\false}{}% \zixmaybeforcebreak \hangindent = \parhang \hangafter = 1 {\name{\index\zixname deferrediformat}{#2}}% \or % Level 2 deferred locators: \if \notp{\zixrunin}\endgraf \fi \zixmaybeforcebreak \hangindent = \parhang \hangafter = 1 {\name{\index\zixname deferrediiformat}{#2}}% \fi \ignorespaces} \def \alphabreak #1{% {text} \endgraf \zclearindexmarks \zixmaybeforcebreak \alphaformat{#1}% \endgraf} \def \zclearindexmarks {% \setmarkinfo{4}{\false}{}% \setmarkinfo{5}{\false}{}} \def \zixcolumnbreak {% \setflag \zixbreak = \true} \def \zixmaybeforcebreak {% \if \zixbreak \zfullcolumnbreak \setflag \zixbreak = \false \fi} % This macro is invoked at the beginning of each column to perform % any first- and second-level entry carryovers. It only includes % carryovers on the first column of versos. \def \zixcarryover {% \if \andp{\evenpagep}{\firstcolumnp}% {\setflag \znext = {\markinfo{\bottommark}{4}{1}}% \if \znext \hangindent = \parhang \hangafter = 1 \name{\index\zixname carryoveriformat}{\markinfo{\bottommark}{4}{2}}% \setflag \znext = {\markinfo{\bottommark}{5}{1}}% \if \znext \hangindent = \parhang \hangafter = 1 \name{\index\zixname carryoveriiformat}{\markinfo{\bottommark}{5}{2}}% \fi \endgraf \tdimena = \zoutputcoldepth % Compensate for depth of \advance \tdimena by -\prevdepth % previous column v. depth \kern \tdimena % of carryover line(s). \fi}% \fi} % Built-in Entry Formats % -------- ----- ------- % These entry formats and carryovers are for a typical outliine style index. \def \indexoutlineentryiformat #1#2{% \noindent #1#2\par} \def \indexoutlineentryiiformat #1#2{% \indent #1#2\par} \def \indexoutlineentryiiiformat #1#2{% \indent\indent #1#2\par} \def \indexoutlinedeferrediformat #1{% \indent #1\par} \def \indexoutlinedeferrediiformat #1{% \indent\indent #1\par} \def \indexoutlinecarryoveriformat #1{% \noindent #1 \tie{\emph{(continued)}}\par} \def \indexoutlinecarryoveriiformat #1{% \indent #1 \tie{\emph{(continued)}}\par} % These entry formats and carryovers are for a typical run-in style index. \setflag \zixplain = \false \def \indexruninentryiformat #1#2{% \noindent #1#2% \global\setflag \zixplain = {\emptyargp{#2}}% \if \zixplain :\space \fi} \def \indexruninentryiiformat #1#2{% \if \notp{\zixplain};\space \fi \global\setflag \zixplain = \false #1#2} \def \indexruninentryiiiformat #1#2{% \error{runiniii}{It makes no sense to have level-3 entries in a run-in index}} \def \indexrunindeferrediformat #1{% . #1} \def \indexrunincarryoveriformat #1{% \noindent #1 \tie{\emph{(continued)}}\par} \def \indexrunincarryoveriiformat #1{% \relax}