%%% File: indexing.tex
%%% A part of mfpic 0.6b 2003/01/02
%%%
% This file is cobbled together from parts of eplain.tex.
% It is sufficient to get one index stream and one index command,
% \idx (which is equivalent to eplain's \idxmarked). Usage:
% \idx\mac{term} writes "\mac{term}" both to the index (alphabetized by
% "term") and in place.
% It differs from \idxmarked in that \mac{term} occurs last, so \mac can
% pick up additional (optional) arguments.
\catcode`\@=11
\def\@gobble#1{}%
\def\@gobblemeaning#1:->{}%
\def\sanitize{\expandafter\@gobblemeaning\meaning}%
\def\@futurenonspacelet#1{\def\cs{#1}%
   \afterassignment\@stepone\let\@nexttoken=
}%
\begingroup % The grouping here avoids stepping on an outside use of `\\'.
\def\\{\global\let\@stoken= }%
\\ % now \@stoken is a space token (\\ is a control symbol, so that
\endgroup
\def\@stepone{\expandafter\futurelet\cs\@steptwo}%
\def\@steptwo{\expandafter\ifx\cs\@stoken\let\@@next=\@stepthree
   \else\let\@@next=\@nexttoken\fi \@@next}%
\def\@stepthree{\afterassignment\@stepone\let\@@next= }%
\def\@getoptionalarg#1{%
   \let\@optionaltemp = #1%
   \let\@optionalnext = \relax
   \@futurenonspacelet\@optionalnext\@bracketcheck
}%
\def\@bracketcheck{%
   \ifx [\@optionalnext
      \expandafter\@@getoptionalarg
   \else
      \let\@optionalarg = \empty
      \expandafter\@optionaltemp
   \fi
}%
\def\@@getoptionalarg[#1]{%
   \def\@optionalarg{#1}%
   \@optionaltemp
}%
\def\idx{\@idxgetrange\@idx}%
\let\indexfilebasename = \jobname
\newwrite\@indexfile
\newif\if@indexfileopened
\def\@idxwrite#1#2{%
  \if@indexfileopened\else
    \expandafter\immediate\openout\@indexfile =
      \indexfilebasename.idx
    \expandafter\global\@indexfileopenedtrue
  \fi
  \def\temp{#1}%
  \edef\@wr{%
    \expandafter\write\@indexfile{%
      \string\indexentry
      {\sanitize\temp}%
      {\noexpand#2}%
    }%
  }%
  \@wr
  \ifindexproofing \insert\@indexproof{\indexproofterm{#1}}\fi
}%
\newif\ifindexproofing
\newinsert\@indexproof
\dimen\@indexproof = \maxdimen                  % No limit on number of terms.
\count\@indexproof = 0  \skip\@indexproof = 0pt % They take up no space.
\font\indexprooffont = cmtt8
\def\indexproofterm#1{\hbox{\strut \indexprooffont #1}}%
\let\@plainmakeheadline = \makeheadline
\def\makeheadline{%
  \indexproofunbox
  \@plainmakeheadline
}%
\def\indexsetmargins{%
  \ifx\undefined\outsidemargin
    \dimen@ = 1truein
    \advance\dimen@ by \hoffset
    \edef\outsidemargin{\the\dimen@}%
    \let\insidemargin = \outsidemargin
  \fi
}%
\def\indexproofunbox{%
  \ifvoid\@indexproof\else
    \indexsetmargins
    \rlap{%
      \kern\hsize
      \ifodd\pageno \kern\outsidemargin \else \kern\insidemargin \fi
      \vbox to 0pt{\unvbox\@indexproof\vss}%
    }%
  \fi
}%
\def\idxrangebeginword{begin}%
\def\idxbeginrangemark{(}% the corresponding magic char to go in the idx file
\def\idxrangeendword{end}%
\def\idxendrangemark{)}%
\def\idxseecmdword{see}%
\def\idxseealsocmdword{seealso}%
\newif\if@idxsee
\let\@idxseenterm = \relax
\def\idxpagemarkupcmdword{pagemarkup}%
\let\@idxpagemarkup = \relax
\def\@idxgetrange#1{%
  \let\@idxrangestr = \empty
  \let\@afteridxgetrange = #1%
  \@getoptionalarg\@finidxgetopt
}%
\def\@finidxgetopt{%
  \ifx\@optionalarg\empty\else
    \expandafter\@idxcheckpagemarkup\@optionalarg=,%
    \ifx\@optionalarg\idxrangebeginword
      \def\@idxrangestr{\idxencapoperator\idxbeginrangemark}%
    \else
      \ifx\@optionalarg\idxrangeendword
        \def\@idxrangestr{\idxencapoperator\idxendrangemark}%
      \else
        \ifx\@optionalarg\idxseecmdword
          \def\@idxpagemarkup{indexsee}%
          \@idxseetrue
        \else
          \ifx\@optionalarg\idxseealsocmdword
            \def\@idxpagemarkup{indexseealso}%
            \@idxseetrue
          \else
             \ifx\@idxpagemarkup\relax
               \errmessage{Unrecognized index option `\@optionalarg'}%
             \fi
          \fi
        \fi
      \fi
    \fi
  \fi
  \@afteridxgetrange
}%
\def\@idxcheckpagemarkup#1=#2,{%
  \def\temp{#1}%
  \ifx\temp\idxpagemarkupcmdword
    \if ,#2, % If #2 is empty, complain.
      \errmessage{Missing markup command to `pagemarkup'}%
    \else
      \def\temp##1={##1}%
      \edef\@idxpagemarkup{\temp\string#2}%
    \fi
  \fi
}%
\def\idxsubentryseparator{!}%
\def\idxencapoperator{|}%
\def\idxmaxpagenum{99999}%
\newtoks\@idxmaintoks
\newtoks\@idxsubtoks
\def\@idxtokscollect{%
  \edef\temp{\the\@idxsubtoks}%
  \edef\@indexentry{%
    \the\@idxmaintoks
    \ifx\temp\empty\else \idxsubentryseparator\the\@idxsubtoks \fi
    \@idxrangestr
  }%
  \if@idxsee
    \@idxseefalse % Reset so the next term won't be a `see'.
    \edef\temp{\noexpand\@finidxtokscollect{\idxmaxpagenum}}%
  \else
    \def\temp{\@finfinidxtokscollect\folio}%
  \fi
  \temp
}%
\def\@finidxtokscollect#1#2{%
  \def\@idxseenterm{#2}%
  \@finfinidxtokscollect{#1}%
}%
\def\@finfinidxtokscollect#1{%
  \ifx\@idxpagemarkup\relax \else
    \toks@ = \expandafter{\@indexentry}%
    \edef\@indexentry{\the\toks@ \idxencapoperator \@idxpagemarkup}%
    \let\@idxpagemarkup = \relax
  \fi
  \ifx\@idxseenterm\relax \else
    \toks@ = \expandafter{\@indexentry}%
    \edef\@indexentry{\the\toks@{\sanitize\@idxseenterm}}%
    \let\@idxseenterm = \relax
  \fi
  \expandafter\@idxwrite\expandafter{\@indexentry}{#1}%
}%
\def\@idxcollect#1#2{%
  \@idxmaintoks = {#1}%
  \@idxsubtoks = {#2}%
  \@idxtokscollect
}%
\def\idxsortkeysep{@}% This `@' is catcode 11, but it doesn't matter.
\def\@idxconstructmarked#1#2#3{%
  \toks@ = {#2}% The control sequence.
  \toks2 = {#3}% The term.
  \edef\temp{\the\toks2 \idxsortkeysep \the\toks@{\the\toks2}}%
  #1 = \expandafter{\temp}%
}%
\def\@idx#1#2{%
  \@idxconstructmarked\@idxmaintoks{#1}{#2}%
  \@idxsubtoks = {}%
  \@idxtokscollect
  #1{#2}% Produce \CS{TERM} as output. %DHL: do last instead of first
}%
\let\indexfonts = \relax
\def\readindexfile#1{%
  \edef\@idxprefix{#1}%
  \testfileexistence[\indexfilebasename]{ind}%
  \iffileexists \begingroup
    \ifx\begin\undefined
      \def\begin##1{\@beginindex}%
      \let\end = \@gobble
    \fi
    \input \indexfilebasename.ind
    \singlecolumn
  \endgroup
  \else
    \message{No index file \indexfilebasename.ind.}%
  \fi
}%
\newif\iffileexists
\def\testfileexistence{\@getoptionalarg\@finishtestfileexistence}%
\def\@finishtestfileexistence#1{%
   \begingroup
      \def\extension{#1}%
      \immediate\openin0 =
         \ifx\@optionalarg\empty\jobname\else\@optionalarg\fi
         \ifx\extension\empty \else .#1\fi
         \space
      \ifeof 0
         \global\fileexistsfalse
      \else
         \global\fileexiststrue
      \fi
      \immediate\closein0
   \endgroup
}%
\def\@beginindex{%
  \let\item = \@indexitem
  \let\subitem = \@indexsubitem
  \let\subsubitem = \@indexsubsubitem
  \indexfonts
  \doublecolumns
  \parindent = 0pt
}%
\let\indexspace = \bigbreak
\let\afterindexterm = \quad
\newcount\aboveindexitempenalty
\aboveindexitempenalty=-100
\newskip\aboveindexitemskipamount  \aboveindexitemskipamount = 0pt plus2pt
\def\aboveindexitemskip{\vskip\aboveindexitemskipamount}%
\def\@indexitem{\begingroup
  \@indexitemsetup
  \leftskip = 0pt
  \penalty\aboveindexitempenalty % Encourage page breaks before items.
  \aboveindexitemskip
  \def\par{\endgraf\endgroup\nobreak}%
}%
\def\@indexsubitem{%
  \@indexitemsetup
  \leftskip = 1em
}%
\def\@indexsubsubitem{%
  \@indexitemsetup
  \leftskip = 2em
}%
\def\@indexitemsetup{%
  \par
  \hangindent = 1em
  \raggedright
  \hyphenpenalty = 10000
}%
\def\seevariant{\it}%
\def\indexseeword{See}%
\def\indexsee#1#2{{\seevariant \indexseeword\ }#1}%
\def\indexseealsowords{see also}%
\def\indexseealso#1#2{{\seevariant \indexseealsowords\ }#1}%
%
\newskip\abovecolumnskip \abovecolumnskip = \bigskipamount
\newskip\belowcolumnskip \belowcolumnskip = \bigskipamount
\newdimen\gutter \gutter = 2pc
\newbox\@partialpage
\newdimen\@columnhsize
\newdimen\@normalhsize
\newdimen\@normalvsize  % The original (before multi-columns) \vsize.
\newtoks\previousoutput
\def\quadcolumns{\@columns4}%
\def\triplecolumns{\@columns3}%
\def\doublecolumns{\@columns2}%
\def\begincolumns#1{\ifcase#1\relax \or \singlecolumn \or \@columns2 \or
                            \@columns3 \or \@columns4 \else \relax \fi}%
\let\endcolumns = \singlecolumn
\let\@ndcolumns = \relax
\chardef\@numcolumns = 1
\def\@columns#1{%
  \@ndcolumns
  \let\@ndcolumns = \@endcolumns
  \chardef\@numcolumns = #1
  \par                     % Shouldn't start in horizontal mode.
  \previousoutput = \expandafter{\the\output}%
  \@columnhsize = \hsize
  \count@ = \@numcolumns
  \advance\count@ by -1
  \advance\@columnhsize by -\count@\gutter
  \divide\@columnhsize by \@numcolumns
  \output = {\global\setbox\@partialpage =
    \vbox{\unvbox255\vskip\abovecolumnskip}%
  }%
  \pagegoal = \pagetotal
  \eject
  \output = {\@columnoutput}%
  \@normalhsize = \hsize
  \@normalvsize = \vsize
  \hsize = \@columnhsize
  \advance\vsize by -\ht\@partialpage
  \advance\vsize by -\ht\footins
  \ifvoid\footins\else \advance\vsize by -\skip\footins \fi
  \multiply\count\footins by \@numcolumns
  \advance\vsize by -\ht\topins
  \ifvoid\topins\else \advance\vsize by -\skip\topins \fi
  \multiply\count\topins by \@numcolumns
  \global\vsize = \@numcolumns\vsize
}%
\def\gutterbox{\vbox to \dimen0{\vfil\hbox{\hfil}\vfil}}%
\newif\if@forceextraline\@forceextralinefalse
\def\@columnsplit{%
  \splittopskip = \topskip
  \splitmaxdepth = \baselineskip
  \dimen@ = \ht255
    \divide\dimen@ by \@numcolumns
 \if@forceextraline
   \advance\dimen@ by \baselineskip
 \fi
 \begingroup
    \vbadness = 10000
    \global\setbox1 = \vsplit255 to \dimen@  \global\wd1 = \hsize
    \global\setbox3 = \vsplit255 to \dimen@  \global\wd3 = \hsize
    \ifnum\@numcolumns > 2
      \global\setbox5 = \vsplit255 to \dimen@ \global\wd5 = \hsize
    \fi
    \ifnum\@numcolumns > 3
      \global\setbox7 = \vsplit255 to \dimen@ \global\wd7 = \hsize
    \fi
  \endgroup
  \if@forceextraline                         % If this is the first time
  \else                                      % through, save the single
    \setbox\@forcelinebox=\copy\@partialpage % column material. 
  \fi
  \setbox0 = \box255
  \global\setbox255 = \vbox{%
    \unvbox\@partialpage
    \ifcase\@numcolumns \relax\or\relax
      \or \hbox to \@normalhsize{\box1\hfil\gutterbox\hfil\box3}%
      \or \hbox to \@normalhsize{\box1\hfil\gutterbox\hfil\box3%
                                      \hfil\gutterbox\hfil\box5}%
      \or \hbox to \@normalhsize{\box1\hfil\gutterbox\hfil\box3%
                                      \hfil\gutterbox\hfil\box5%
                                      \hfil\gutterbox\hfil\box7}%
    \fi
  }%
  \setbox\@partialpage = \box0
}%
\def\@columnoutput{%
  \@columnsplit
  \@recoverclubpenalty
  \hsize = \@normalhsize % Local to \output's group.
  \vsize = \@normalvsize
  \the\previousoutput
  \unvbox\@partialpage
  \penalty\outputpenalty
  \global\vsize = \@numcolumns\@normalvsize
}%
\def\singlecolumn{%
  \@ndcolumns
  \chardef\@numcolumns = 1
  \vskip\belowcolumnskip
  \nointerlineskip
}%
\newbox\@forcelinebox
\def\@endcolumns{%
  \global\let\@ndcolumns = \relax
  \par % Shouldn't start in horizontal mode.
  \global\output = {\global\setbox1 = \box255}%
  \pagegoal = \pagetotal
  \eject                     % Exercise the page builder, i.e., \output.
  \setbox2 = \box1           % Save material in box2 in case of overflow.
  \global\setbox255 = \copy2 % Retrieve what the fake \output set.
  \@columnsplit
  \ifvoid\@partialpage
  \else % There is some left-over. 
    \setbox0=\box\@partialpage % Merely to void \@partialpage
    \global\setbox255 = \box2  % Retrieve what the fake \output set.
    \@forceextralinetrue       % Add \forcelinebox to \box255 to save single
    \@columnsplit              % column material. 
    \global\setbox255 = \vbox{\box\@forcelinebox\box255}%
  \fi
  \global\vsize = \@normalvsize
  \global\hsize = \@normalhsize
  \global\output = \expandafter{\the\previousoutput}%
  \ifvoid\topins\else\topinsert\unvbox\topins\endinsert\fi
  \unvbox255
}%
\def\@saveclubpenalty{% save the current value of \clubpenalty
  \edef\@recoverclubpenalty{%
     \global\clubpenalty=\the\clubpenalty\relax%
     \global\let\noexpand\@recoverclubpenalty\relax
  }% the \noexpand handles infinite self-reference
}%
\let\@recoverclubpenalty\relax
\newdimen\temp@dimen
\def\columnfill{%
 \par
 \dimen@=\pagetotal   % The height of the text set so far.
 \temp@dimen = \vsize % = \@numcolumns * columnheight
 \divide\temp@dimen by \@numcolumns % find out column height
 \loop
   \ifdim \dimen@ > \temp@dimen
     \advance \dimen@ by -\temp@dimen
     \advance \dimen@ by \topskip % fudge factor
 \repeat
 \advance \temp@dimen by -\dimen@
 \advance \temp@dimen by -\prevdepth
 \@saveclubpenalty
 \clubpenalty=10000\relax
 \hrule height\temp@dimen width0pt depth0pt\relax
 \nointerlineskip
 \par
 \nointerlineskip
 \penalty0\vfil % To allow a page break here.
 \relax
}%

\catcode`\@=12
