\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{terms}[2005/10/18 LaTeX package aiding the creation of indices]
%
% LaTeX package "terms" for short declaration of terms in a text, which then can be
% collected more easily in an index or may be emphasized in the running text.
% Beisdes the package provides support for similiar treatment of symbols, which is mainly
% aimed at a glossary of symbols or list of symbols at the end of a document.
%
% Author: Thomas Eckert, eckert(@)mathematik.uni-marburg.de
%
% Version 5: 1. Implementing a glossary of symbols mechanism with \Symbol and
%   \symbol commands like the \Term and \term commands.
%   2. Besides the "!"-syntax is treated as the "+*/"-syntax.
%   3. Added \Headword mechanism for wildcards in subentries replacing the headword.
%
%
% Wishlist and ideas
%
%  1. What about the idea in version 2 of saving the index entry for a term that it can
%     be reused by \term? Also labeling for complicated expressions? This is now imple-
%     mented in the \Symbol command.
%  2. Perhaps automatically apply "+*/"-syntax for composed expressions (multiple words or
%     at least words with "-" inside? Which order then, i.e. "+" or "/"?
%  3. English glossary? Or at least international expression in index behind the main
%     entry? The last is only possible with topic "1." here?
%  4. Use standard LateX documentation (dtx-mechanism) for development of this package!
%  5. Perhaps link a symbol to a term and list all terms's occurences also for the symbol.
%     Is it too dangerous? If so, at least give the option for this mechanism?
%  6. The eqref meachnism of package `nomencl' is undermined (to admit multiple entries).
%     If it should be necessary, perhaps shift it behind the `|' sign in the index syntax.
%     Seems to be a bad solution in the nomencl-package anyway?
%  7. With the "~" perhaps enhance syntax for double "*/" to end the main entry and
%     continue the side entry (e.g. "white *honey* milk" for "white ~ milk" under "honey").
%  8. Similarly, admit multiple "+" signs for multiple additional entries?
%
%
% Known bugs
%
%  1. \Term{symmetric *matrix} (and similarly with "/") leads to an index entry with
%     misplaced space: under "matrix" there is a line "symmetric , 11" if occurence is on
%     page 11 for example. There is space too much in front of the comma.
%     Workaround: Maybe simply make the index processor not print a comma - as makeindex
%     does by default. Then the two spaces become one space and all is fine.
%  2. If there is a \term{x} and a \Term{x} on the same page, then the second is omitted
%     in the index and not the first one, which though is less important. Use with care.
%  3. The mechanism for follow-up \symbol of course doesn't work, if the \symbol command
%     comes earlier than the defining \Symbol call. Perhaps save all these calls in a list
%     and perform writing to the job.nlo only at the end?
%  4. The sorting isn't good: T, t, T_i are sorted in exactly this order -- independent of
%     \NonLetterSub. But often or always  one would prefer T, T_i, t or even the t in the
%     beginning.
%
%
% Usage
%
% In a LateX document the preamble should contain
%     \usepackage[noindex,symbols,myindex]{terms}
% where [noindex,symbols,myindex] are optional, if one doesn't like terms being collected in
% an index or wants a list of symbols or these indices somewhere else then at the end of the
% document. In the latter case use \printindex (from package makeidx loaded by this package
% unless option "noindex" is given) and \printnomenclature (for list of symbols whence
% enabled and then taken from package nomencl required by this package).
%
% For the documentation of the provided commands see the corresponding section below.
%
% The main thing in using the "terms" package is to call makeindex for the job's idx-file
% after first processing the tex-file and then rerun tex on the file to include the newly
% generated ind-file. This is done via the \printindex command, best positioned at the end
% of the document (standard unless option "myindex" is set). (See the documentation of the
% makeindex program or makeidx.sty.) The same holds for a second index, a list of symbols,
% which can be created by the option `symbols'. In this case you have to run makeindex (or
% an analogous index processor) also as
%     makeindex -s nomencl.ist -o job.nls job.nlo
% whence the nomenclature package `nomencl' is available with its style file `nomencl.ist'.
% Here `job' has to be replaced by the name of your tex file.
%
% To avoid the described bug "1." you should try to instruct your index processor (by some
% style file or ini-file) to format page numbers either as right aligned or simply to omit
% the comma. For the standard index processor "makeindex" this is done by adding the
% following three lines to a (local) style file (e.g. "terms.ist", see also next paragraph):
%     delim_0 " "
%     delim_1 " "
%     delim_2 " "
% Then makeindex is to be called as "makeindex -s terms.ist job.idx".
%
% Besides it is recommended to make single subitems appearing in the same line as their
% top items (but only if it is really "single", which means that the top item does not
% ocurr on it's own somewhere, i.e. has no page specification). For makeindex again, this
% is done with the two lines
%     item_x2 ", "
%     item_x1 ", "
% in the style file. Such a file "terms.ist" comes with this LaTeX package "terms.sty",
% including also a redefined preamble to get an entry for the index in the table of contents:
%     preamble "\\begin{theindex}\n\\addcontentsline{toc}{section}{Index}\n"
%
% A little memo: Try to replace the "!"-syntax in ":entry"-arguments of a \Term command (see
% below). Often the "*"-form is possible and also better processed (e.g. for the linked for-
% matting by means of \@Main). And observe that for the moment the "!" syntax is not at all
% processed in the main argument of \Term and therefore will be printed in the text itself.
%
%
%
% Package Options and Dependencies
%
\newif\ifindex\indextrue
\newif\ifsymbols\symbolsfalse
\newif\ifstandard\standardtrue
%
\DeclareOption{noindex}{\indexfalse}
\DeclareOption{symbols}{\symbolstrue}
\DeclareOption{myindex}{\standardfalse}
\ProcessOptions
%
\ifsymbols%
  \RequirePackage[refpage,noprefix]{nomencl}[2005/03/31]% At least version 4.1!
  % Some redefinition for the package following its sample04.cfg (expansion) or what is
  % described under section "I want it expanded!" in its documentation.
  \def\makenomenclature{%
    \newwrite\@nomenclaturefile
    \immediate\openout\@nomenclaturefile=\jobname\@outputfileextension
    \def\@nomenclature{%
      \@ifnextchar[%
        {\@@@@nomenclature}{\@@@@nomenclature[\nomprefix]}}%
    \typeout{Writing nomenclature file \jobname\@outputfileextension}%
    \let\makenomenclature\@empty}
  \def\@@@@nomenclature[#1]#2#3{%
  \protected@write\@nomenclaturefile{}%
    {\string\nomenclatureentry{#1@[{#2}]% Deleted "#2" between "#1" and "@"!
      #3% Deleted "\begingroup" in front (see \nompageref below) and the \nomeqref stuff behind.
      |nompageref{\@ThisType}}{\thepage}}}% Inserted "{\@ThisType}" and the following!
  \PackageInfo{terms}{Disabled the `eqref' mechanism of package `nomencl' %
    (for multiple page references)}%
  \def\@ThisType{main}% In case \nomenclature is still called manually.
  \def\nompageref#1#2{\if@printpageref\pagedeclaration{\csname#1\endcsname{#2}}\fi\nomentryend}%
    % Deleted "\endgroup" (see above) and added a new parameter #1 \for `\main'
  % And some adjustments
  \def\nomname{List of Symbols}%
  \def\nompreamble{\labelsep.25in}%
  \nomitemsep-.5\parsep%
  \def\nomlabel#1{\hfil#1}% For left aligned symbols in the list.
  \def\pagedeclaration#1{\nobreakspace#1}% No comma. It should be placed in nomencl.ist anyway.
  % Now, initialize a glossary of symbols
  \makenomenclature%
  \ifstandard\AtEndDocument{\printnomenclature[.8in]}\fi%
  % At last define a macro for the creation of a single entry:
  \def\@HandleMainSymbolEntry{%
    \def\@ThisType{main}%
    % Extract from \@SymbolCode a \csname'ble string \@SymbolEntryName:
    \let\@SymbolEntryName\@SymbolCode\@onelevel@sanitize\@SymbolEntryName%
    % Now, prepare the \@SymbolCode: convert to string, but replace "!" and "|":
    \let\@@SymbolCode\@SymbolCode% This will be the new and `cleaned' one.
    \@onelevel@sanitize\@@SymbolCode%
    \@ReplaceEach\!\@In\@@SymbolCode\@By\Backstep% Necessary at least for the following.
    \@ReplaceEach!\@In\@@SymbolCode\@By\ExclamationMark%
    \@ReplaceEach\|\@In\@@SymbolCode\@By\Vert% Again respect order for these two!
    \@ReplaceEach|\@In\@@SymbolCode\@By\vert%
    \if@Found @\@In\@@SymbolCode{\PackageWarning{terms}%
      {@-signs in symbol code `\@@SymbolCode' cannot be processed.}}
    % Finally save the call for the entry in the symbol list for \symbol and execute it:
    \expandafter\expandafter\expandafter\xdef\expandafter%
      \csname\@SymbolEntryName-@@Symbol\endcsname{%
      \noexpand\nomenclature[\@SymbolEntry]{$\@@SymbolCode$}{\@Description}%
    }%
    \expandafter\csname\@SymbolEntryName-@@Symbol\endcsname%
  }%
\else%
  \let\@HandleMainSymbolEntry\relax%
\fi%
%
\ifindex%
  \RequirePackage{makeidx}%
  \makeindex%
  \ifstandard\AtEndDocument{\printindex}\fi%
  \def\@Index#1{\index{#1|\@Type}}% Use \@Type from \@Term for consequent formatting.
\else%
  \def\@Index#1{}%
\fi%
%
%
%
% Provided commands
%
\newcommand\Term[2][]{\@Term{#2}{main}{#1}}
\newcommand\term[2][]{\@Term{#2}{side}{#1}}
% Use as \Term[international]{term} and compare documentation of \@Term for the
% special syntax of <term>. (Here parameter <international> is optional.)
\newcommand\HandleInternational[1]{ (\textit{#1})\@Index{#1 (\@Main)}}
% Compare \@Term's documentation below.
%
\let\textmain\emph% In \@Term{term}{#2}{...} the formatting of <term> is done via
\let\textside\relax% "\csname text#2\endcsname" resulting in \textmain for #2 as "main".
%
\let\entrymain\textbf% Also, parameter <type> of \@Term is transferred to \index{...|type},
\let\entryside\textrm% aliased \@Index{...}, for formatted page numbers (makeidx package).
%
\let\linked\textit% In the index a link to a main entry is marked \linked. This takes one
% argument for only partial expressions. But ignoring this, \linked may also be an \uparrow.
% Observe: Directly after "!" or at the very beginning \linked{X} must be preceeded by "X@"
% for proper ordering (compare the internal command "\@Convert#1!#2!#3~" below).
%
\def\Headword{\ensuremath{\sim}}% The wildcard for the headword in a subentry.
\let\HeadwordFirst\Headword% Wild card for a missing headword in front of all stuff.
%
\newcommand\Symbol[1]{%
  % Use as \Symbol{code:entry:description} where <entry> is optional and, if there is
  % no colon given at all, <description> is taken from last \Term's <entry>.
  %
  % At first extract all the components from the one-parameter-syntax.
  \@DecomposeSymbol#1:::~%
  % Now print out the extracted symbol, which has been extracted into \@SymbolCode.
  \@SymbolCode%
  % If a list of symbols is enabled (package option) the following creates the entry.
  \@HandleMainSymbolEntry%
}
\let\symbolLaTeX\symbol% Unfortunately LaTeX has already defined a command \symbol (for
  % accessing symbols in a font). But after saving this, we may overwrite it:
\PackageInfo{terms}{Redefining macro \protect\symbol\space and saved LaTeX's\MessageBreak%
  (only compatibility reasoned) one as macro \protect\symbolLaTeX}
\renewcommand\symbol[2][]{%
  % This is for follow-up entries. Use as \symbol[entry]{symbol} where <symbol> is
  % printed and taken as <entry> if this optional argument is omitted. Now, <entry>
  % specifies the related entry, i.e. the related \Symbol's <entry> parameter.
  #2%
  \ifsymbols%
    \def\@ThisType{side}%
    \if\blank{#1}\def\@SymbolEntryName{#2}\else\def\@SymbolEntryName{#1}\fi%
    \@onelevel@sanitize\@SymbolEntryName%
    \expandafter\@ifundefined\expandafter{\@SymbolEntryName-@@Symbol}{% then
      \PackageWarning{terms}%
        {Symbol "\@SymbolEntryName" unknown. No follow-up entry is created.}%
    }{% else
      \expandafter\csname\@SymbolEntryName-@@Symbol\endcsname%
    }% fi
  \fi%
}
\newtoks\NonLetterSub
\NonLetterSub={ }% This defines what is substituted for non-letters for sorting of
  % symbols. To preserve the original sequence from the first letter onwards:
  % \let\NonLetterSub\relax
  % But be careful: All the \index-related symbols @, !, and | will cause problems then.
%
% Implementation
%
% The main macro \@Term
%
% Use as \@Term{term}{type}{international}, where <term> may be of the syntax
%     term = text[:entry][;additional]
% with <entry> set to <text> if omitted. Then an <entry> of the form
%     entry = head@tail, @={+,*,/}
% will be interpreted as the following index entries (more or less):
%     +: \@Index{<head><tail>}\@Index{<tail>!\linked{<head>}}
%     *: \@Index{<tail>!<head>}
%     /: \@Index{<tail>!<head>}\@Index{<head>\linked{<tail>}}.
% Thus "+" leads to an additional entry, "*" to an entry under a part of the term which
% is not the beginning, and "/" is the combination of both. The main part ist saved in
% a new variable <main>. Additional entries are marked by italics with the meaning of
% "see mainly under this topic" for possible further occurences of the term.
%
% More exactly each \@Index call is a usual \index completed by a "|<type>" at it's end
% to get proper formatting. Here the parameter <type> is expected to be "main" or
% "side", generated by \Term or \term respectively.
%
% A non-empty <additional> leads to an additional entry with a link formatting as
% described above. Here a linked main entry can be referred to by a "**"-syntax:
% A "*"-bracketed phrase is set \linked. If the phrase itself is empty, i.e. there is a
% double "**", then it will be replaced by the actual \@Main entry.
%
% After the index entries are performed the term's text is printed, cleared of the
% syntax elements "+*/".
%
% Finally, the <international> parameter is meant to be an expression of the term in a
% second language and is typeset as "\HandleInternational{...}" if not empty. Redefine
% this command for appropriate behavior and add some language glossary for example.
%
\newcommand\@Term[3]{%
  \@DecomposeTerm#1::~% This extracts \@Text, \@Entry, and \@Additional from #1.
  \gdef\@Type{#2}% Export #2 to be used in \@Index for formatting the index entry.
  \if\blank{\@Entry}\else\expandafter\@SplitPlus\@Entry++~\fi% Create index entries and
  \ifindex%
    \if\blank{\@Additional}\else\expandafter\@AddIndex\@Additional***~\fi% ";"-additionals.
  \fi%
  \csname text#2\endcsname{\@Text}% Print the text formatted by \textmain or \textside.
  \if\blank{#3}\else\HandleInternational{#3}\fi% Handle the <international>-argument #3.
}
%
% Macros for decomposing \@Term's parameter <term> into \@Text, \@Entry, and \@Additional
\def\@DecomposeTerm#1:#2:#3~{%
  \if\blank{#2}\@SplitEntries#1;;~\gdef\@Text{\@Main}% (\let\linked\relax here?)
  \else\@SplitEntries#2;;~\gdef\@Text{#1}\fi%
}
\def\@SplitEntries#1;#2;#3~{\gdef\@Entry{#1}\gdef\@Additional{#2}}
%
% Macros creating \@Main and the index entries
\def\@SplitPlus#1+#2+#3~{\if\blank{#3}\@SplitStar#1**~\else%
  \gdef\@Main{#1#2}\@Index{#1#2}\@Index{#2!#1@\linked{#1}\Headword}\fi}
\def\@SplitStar#1*#2*#3~{\if\blank{#3}\@SplitSlash#1//~\else%
  \gdef\@Main{#1\linked{#2}}\@Index{#2!#1\Headword}\fi}
\def\@SplitSlash#1/#2/#3~{\if\blank{#3}\@SplitExclm#1!!~\else%
  \gdef\@Main{#1\linked{#2}}\@Index{#2!#1\Headword}\@Index{#1\linked{#2}}\fi}
\def\@SplitExclm#1!#2!#3~{\if\blank{#3}\gdef\@Main{#1}\@Index{#1}\else%
  \gdef\@Main{#1#2}\@Index{#1!#2@\HeadwordFirst#2}\fi}%
%
% Macros for additional entries from a (";"-given) argument containing "*"-syntax
\def\@AddIndex#1*#2*#3*#4~{%
  \if\blank{#4}% no star given
    \ifindex\index{#1|see{\@Main}}\fi%
  \else% at least one star given
    \if\blank{#2}% A double "**" refers to the actual main entry.
      \@Index{\@Convert#1!!~\linked{\@Main}#3}%
    \else% A "*"-bracketed phrase is set \linked.
      \@Index{\@Convert#1!!~\linked{#2}#3}%
    \fi%
  \fi%
}
\def\@Convert#1!#2!#3~{%
  \if\blank{#3}\if\blank{#1}\@Main @\fi% The argument was completely empty.
  \else#1!\if\blank{#2}\@Main @\else#2\fi\fi% The argument finished with a "!".
}
%
%
% Macros for the \Symbol commands
%
% Macros for decomposing \Symbol's parameter into \@SymbolCode, \@SymbolEntry, and \@Description
\def\@DecomposeSymbol#1:#2:#3:#4~{%
  \gdef\@SymbolCode{#1}%
  % Try to sort automatically:
  \if\blank{#3}\ExtractLetters{#1}{\@SymbolEntry}\else\xdef\@SymbolEntry{#2}\fi%
  % If description is omitted take it from last \@Term command's \@Main (entry text):
  \if\blank{#2}\let\@Description\@Main\else\gdef\@Description{#2}\fi%
  \@onelevel@sanitize\@Description% for proper writing into file
}
% To extract letters from a string #1 into a sequence saved in #2 use the following macro.
% But be careful: #1 is taken as a \string (token by token); in particular it is not expanded.
% Thus, \ExtractLetters{\a}{\b} results in a string value of "\a" for \b whatever \a has been.
\def\ExtractLetters#1#2{%
  \@ExtractedLetters{}%
  \Call@ExtractLetters#1~!~%
  \xdef#2{\the\@ExtractedLetters}%
}
\newtoks\@ExtractedLetters
\def\Call@ExtractLetters#1#2~!~{\expandafter\@ExtractLetters\string#1#2~!~}
  % This removes curly brackets around #1, which wouldn't be processed correctly by the
  % \string command in the call of the actual extracting procedure.
\def\@ExtractLetters#1#2~!~{%
  % Only letters have nontrivial \lccode (lowercase code). Normally this could be read off
  % more directly from \catcode 11 (category code `letter'); but here the obligatory \string
  % command in the call of this macro levels each \catcode down to 12 (`other').
  \ifnum\lccode`#1=0\if\blank{\the\@ExtractedLetters}\else\@ifundefined{NonLetterSub}{%
    \@ExtractedLetters\expandafter{\the\@ExtractedLetters#1}%
  }{%
    \@ExtractedLetters\expandafter{\the\@ExtractedLetters\the\NonLetterSub}%
  }\fi\else%
    \@ExtractedLetters\expandafter{\the\@ExtractedLetters#1}%
  \fi%
  \if\blank{#2}\else\Call@ExtractLetters#2~!~\fi%
}
% The following macro replaces all occurences of #1 by #3 in the string with name #2. Thus,
% #2 is expanded, whereas #1 and #3 are taken directly but being then converted to strings
% internally (for proper comparisons). Observe that in order to work, #2 really has to be a
% string. It can be produced by \@onelevel@sanitize#2, if #2 is a macro already.
% The number of replacings is saved in counter @Recplacings.
\def\@ReplaceEach#1\@In#2\@By#3{%
  % We want to get #1 as a string and therefore call a help macro \@@ReplaceEach doing the
  % actual work and getting a "stringed" parameter #1.
  \def\@@Pattern{#1}\@onelevel@sanitize\@@Pattern%
  \expandafter\@@ReplaceEach\@@Pattern\@In#2\@By{#3}%
}
\def\@@ReplaceEach#1\@In#2\@By#3{%
  \def\@@@Sub{#3}\@onelevel@sanitize\@@@Sub% Turns #3 into the string \@@@Sub.
  \def\@@@Cut##1#1\*@@@Cut{##1}% Cuts off last #1.
  \def\@@@Replace##1#1##2\*@@@Replace{##1% This is okay.
    \if\blank{##2}\else% Something left to proceed. But ##2 has a #1 too much at its end.
      \@@@Sub\expandafter\@@@Replace\@@@Cut##2\*@@@Cut#1\*@@@Replace\fi%
  }% Now perform this and save back to #2. The \expandafter is to get the meaning of #2.
  \xdef#2{\expandafter\@@@Replace#2#1\*@@@Replace}%
}
% The following macro tests if #1 is found in #2, where #2 is assumed to expand
% to a string and #1 is taken as a string directly.
\def\if@Found#1\@In#2#3{%
  \def\@Pattern{#1}\@onelevel@sanitize\@Pattern%
  \expandafter\@if@Found\@Pattern\@In{#2}{#3}%
}
\def\@if@Found#1\@In#2#3{%
  \def\@Pattern{#1}\@onelevel@sanitize\@Pattern%
  \def\if@@Found##1#1##2\*if@@Found{\if\blank{##2}\else#3\fi}%
  \expandafter\if@@Found#2#1\*if@@Found%
}
% The following two definitions are used as replacements in the \Symbol command.
\let\Backstep\!%
\let\ExclamationMark!%
%
%
% General aiding macros
%
% Macros for (hyperlinked) proper page formatting in the index entries
\def\hyperpage#1{#1}
% Because of the this definition the package "hyperref" has to be loaded after "terms" that
% it may overwrite this empty definition. But it is necessary for the following two commands
% used in \@Index as for example \index{...|main}.
\def\main#1{\entrymain{\hyperpage{#1}}}
\def\side#1{\entryside{\hyperpage{#1}}}
%
% A test for empty arguments: \if\blank{...}
%   (from www.ctan.org/tex-archive/info/aro-bend/answer.002)
\long\def\blank#1{\expandafter\bl@nk#1@@..\bl@nk}%
\long\def\bl@nk#1#2@#3#4\bl@nk{#3#4}
%
\endinput
