%%% ==================================================================== %%% @LaTeX-style-file{ %%% filename = "grabhedr.dtx", %%% version = "1.99a", %%% date = "2013/01/24", %%% author = "Michael Downes", %%% copyright = "This file is part of the dialogl package, released %%% under the LPPL; see dialogl.ins for details." %%% keywords = "TeX, file header, %%% supported = "no", %%% abstract = "This file defines a macro \inputfwh %%% to be used instead of \input, to allow TeX to grab %%% information from standardized file headers in the form %%% proposed by Nelson Beebe during his term as president of the %%% TeX Users Group. Of which all this here is an example.", %%% } %%% ==================================================================== % % \iffalse %<*driver> \input{dia-driv.tex} % % \fi % % \section{Introduction} % This file defines a function \cw{inputfwh} to be used instead of % \cw{input}, to allow \tex/ to grab information from standardized % file headers in the form proposed by Nelson Beebe during his term % as president of the \tex/ Users Group. Usage: %\begin{usage} %\inputfwh{file.nam} %\end{usage} % Functions \cw{localcatcodes} and \cw{restorecatcodes} for % managing catcode changes are also defined herein, as well as a % handful of utility functions, many of them borrowed from % \fn{latex.tex}: \cw{@empty}, \cw{@gobble}, \cw{@gobbletwo}, % \cw{@car}, \cw{@@input}, \cw{toks@}, \cw{afterfi}, % \cw{fileversiondate}, \cw{trap.input}. % % The use of \cw{inputfwh}, \cw{fileversiondate}, and % \cw{trap.input} as illustrated in \fn{dialog.sty} is cumbersome % klugery that in fact would better be handled by appropriate % functionality built into the format file. But none of the major % formats have anything along these lines yet. (It would also help % if \tex/ made the current input file name accessible, like % \cw{inputlineno}.) % % \StopEventually{} % % \section{Implementation} % Standard package identification: % \begin{macrocode} %<*2e> \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{grabhedr}[1994/11/08 v0.9j] % % \end{macrocode} % % By enclosing this entire file in a group, saving and restoring % catcodes `by hand' is rendered unnecessary. This is perhaps the % best way to locally change catcodes, better than the % \cw{localcatcodes} function defined below. But it tends to be % inconvenient for the \tex/ programmer: every time you add % something you have to remember to make it global; if you're like % me, you end up making every change twice, with an abortive test % run of \tex/ in between, in which you discover that a certain % control sequence is undefined because you didn't assign it % globally. (Using \cw{globaldefs} = 1 is dangerous in my experience; % you have to take care not to accidentally change any variables that % you don't want to be changed globally.) % \begin{macrocode} \begingroup % \end{macrocode} % Inside this group, enforce normal catcodes. All definitions must % be global in order to persist beyond the \cw{endgroup}. % \begin{macrocode} \catcode96 12 % left quote \catcode`\= 12 \catcode`\{=1 \catcode`\}=2 \catcode`\#=6 \catcode`\$=3 \catcode`\~=13 \catcode`\^=7 \catcode`\_=8 \catcode`\^^M=5 \catcode`\"=12 % \end{macrocode} % Make \qc{\@} a letter for use in `private' control sequences. % \begin{macrocode} \catcode`\@=11 % \end{macrocode} % % \section{Preliminaries} % For \cw{@empty}, \cw{@gobble}, \ldots{} we use the \latex/ names so % that if \fn{grabhedr.sty} is used with \latex/ we won't waste hash % table and string pool space. % % \begin{macro}{\@empty} % Empty macro, for \cw{ifx} tests or initialization of variables. % \begin{macrocode} \gdef\@empty{} % \end{macrocode} % \end{macro} % % \begin{macro}{\@gobble} % \begin{macro}{\@gobbletwo} % \begin{macro}{\@gobblethree} % Functions for gobbling unwanted tokens. % \begin{macrocode} \long\gdef\@gobble#1{} \long\gdef\@gobbletwo#1#2{} \long\gdef\@gobblethree#1#2#3{} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@car} % The function \cw{@car}, though not really needed by % \fn{grabhedr.sty}, is needed by the principal customers of % \fn{grabhedr.sty} (e.g., \fn{dialog.sty}). % \begin{macrocode} \long\gdef\@car#1#2\@nil{#1} % \end{macrocode} % \end{macro} % % \begin{macro}{\@@input} % To define \cw{@@input} as in \latex/ we want to let it equal to % the primitive \cw{input}. But if a \latex/ format is being used % we don't want to execute that assignment because by now % \cw{input} has changed its meaning. And if some other format is % being used it behooves us to check, before defining \cw{@@input}, % whether \cw{input} still has its primitive meaning. Otherwise % there's a good chance \cw{inputfwh} will fail to work properly. % \begin{macrocode} \ifx\UndEFiNed\@@input % LaTeX not loaded. % \end{macrocode} % This code shows a fairly easy way to check whether the meaning of a % primitive control sequence is still the original meaning. % \begin{macrocode} \edef\0{\meaning\input}\edef\1{\string\input}% \ifx\0\1% \global\let\@@input\input \else \errhelp{% Grabhedr.sty needs to know the name of the \input primitive in order to define \inputfwh properly. Consult a TeXnician for help.} \errmessage{% Non-primitive \noexpand\input detected}% \fi \fi % \end{macrocode} % \end{macro} % % \begin{macro}{\toks@} % Scratch token register. % \begin{macrocode} \global\toksdef\toks@=0 % \end{macrocode} % \end{macro} % % \begin{macro}{\afterfi} % Sonja Maus's function for throwing code over the \cw{fi} (``An % Expansion Power Lemma'', \TUB{} vol 12 no 2 June 1991). (Except % that she called this function \cw{beforefi}.) % % \begin{macrocode} \long\gdef\afterfi#1\fi{\fi#1} % \end{macrocode} % \end{macro} % % \begin{macro}{\nx@} % We will be using \cw{noexpand} a lot; this abbreviation improves % the readability of the code. % \begin{macrocode} \global\let\nx@\noexpand % \end{macrocode} % \end{macro} % % \begin{macro}{\xp@} % Another convenient abbreviation. % \begin{macrocode} \global\let\xp@\expandafter % \end{macrocode} % \end{macro} % % \section{Reading standard file headers} % The function \cw{inputfwh} (`input file with header') inputs % the given file, checking first to see if it starts with a % standardized file header; if so, the filename, version and date % are scanned for and stored in a control sequence. % % For maximum robustness, we strive to rely on the fewest possible % assumptions about what the file that is about to be input might % contain. % % Assumption 1: Percent character \qc{\%} has category 14. I.e., if % the first line of the file to be input starts with \qc{\%}, it is % OK to throw away that line. % % \begin{macro}{\@percentchar} % \begin{macrocode} \begingroup \lccode`\.=`\%% \lowercase{\gdef\@percentchar{.}}% \endgroup % \end{macrocode} % \end{macro} % % \begin{macro}{\fileversiondate} % The function \cw{fileversiondate} is not only a useful support % function for \cw{inputfwh}, it can also be used by itself at the % beginning of a file to set file name, version, and date correctly % even if the file is input by some means other than % \cw{inputfwh}\Dash assuming that the arguments of the % \cw{fileversiondate} command are kept properly up to date. % \begin{macrocode} \gdef\fileversiondate#1#2#3{% \xp@\xdef\csname#1\endcsname{#2 (#3)}% \def\filename{#1}\def\fileversion{#2}% \def\filedate{#3}% \message{#1 \csname#1\endcsname}% } % \end{macrocode} % And now apply \cw{fileversiondate} to this file. % \begin{macrocode} %<*209> \fileversiondate{grabhedr.sty}{0.9j}{1994/11/08} % % \end{macrocode} % \end{macro} % % \begin{macro}{\@filehdrstart} % \fn{filehdr.el} by default adds a string of equal signs (with an % initial comment prefix) at the very top of a file header. This % string must be scanned away first before we can start looking for % the real information of the file header. % \begin{macrocode} \xdef\@filehdrstart{% \@percentchar\@percentchar\@percentchar\space ==================================% ==================================} % \end{macrocode} % \end{macro} % % \begin{macro}{\@scanfileheader} % The purpose of this function is just to scan up to the opening % brace that marks the beginning of the file header body. % Everything before that is ignored, not needed for our present % purposes. % \begin{macrocode} \gdef\@scanfileheader#1@#2#{\@xscanfileheader} % \end{macrocode} % \end{macro} % % \begin{macro}{\@xscanfileheader} % Throw in some dummy values of version and date at the end so that % all we require from a file header is that the filename field must % be present. % \begin{macrocode} \long\gdef\@xscanfileheader#1{% \@yscanfileheader#1{} version = "??", date = "??",\@yscanfileheader} % \end{macrocode} % \end{macro} % % \begin{macro}{\@yscanfileheader} % This function assumes that filename, version, and date of a file % are listed in that order (but not necessarily adjacent). It's % possible for the version and date to be missing, or out of order, % but the corresponding \tex/ variables \cw{fileversion} and % \cw{filedate} will not get set properly unless the order is: % filename, [\ldots,] version, [\ldots,] date. Trying to handle % different orderings would be desirable but I haven't yet been % struck by a suitable flash of insight on how to do it without % grubby, time-consuming picking apart of the entire file header. % \begin{macrocode} \long\gdef\@yscanfileheader #1 filename = "#2",#3 version = "#4",% #5 date = "#6",#7\@yscanfileheader{% \endgroup \csname fileversiondate\endcsname{#2}{#4}{#6}% } % \end{macrocode} % \end{macro} % % \begin{macro}{\@readfirstheaderline} % This function has to look at the first line of the file to see if % it has the expected form for the first line of a file header. % \begin{macrocode} \begingroup \lccode`\$=`\^^M \lowercase{\gdef\@readfirstheaderline#1$}{% \toks@{#1}% \edef\@tempa{\@percentchar\the\toks@}% \ifx\@tempa\@filehdrstart \endgroup \begingroup \catcode`\%=9 \catcode`\^^M=5 \catcode`\@=11 % \end{macrocode} % Double quote and equals sign need to be category 12 in order for % the parameter matching of \cw{@xscanfileheader} to work, and % space needs its normal catcode of 10. % \begin{macrocode} \catcode`\ =10 \catcode`\==12 \catcode`\"=12 \xp@\@scanfileheader \else \message{(* Missing file header? *)}% \afterfi\endgroup \fi} \endgroup % \end{macrocode} % \end{macro} % % \begin{macro}{\@xinputfwh} % An auxiliary function. % \begin{macrocode} \gdef\@xinputfwh{% \ifx\next\@readfirstheaderline % \end{macrocode} % Sanitize a few characters. Otherwise an unmatched brace or other % special character might cause a problem in the process of reading % the first line as a macro argument. % \begin{macrocode} \catcode`\%=12 \catcode`\{=12 \catcode`\}=12 \catcode`\\=12 \catcode`\^^L=12 \catcode`\^=12 % Unique terminator token for the first line. \catcode`\^^M=3\relax \else \endgroup\fi } % \end{macrocode} % \end{macro} % % \begin{macro}{\@inputfwh} % Auxiliary function, carries out the necessary \cw{futurelet}. % \begin{macrocode} \gdef\@inputfwh{\futurelet\next\@xinputfwh} % \end{macrocode} % \end{macro} % % \begin{macro}{\inputfwh} % Strategy for (almost) bulletproof reading of the first line of % the input file is like this: Give the percent sign a special % catcode, then use \cw{futurelet} to freeze the catcode of the % first token in the input file. If the first token is {\em not\/} % a percent character, then fine, just close the group wherein the % percent character had its special catcode, and proceed with % normal input; the first token will have its proper catcode % because we did not change anything except the percent character. % Otherwise, we still proceed with `normal' input execution, but by % making \qc{\%} active and defining it suitably, we can carry out % further tests to see if the first file line has the expected form % (three percent signs plus lots of equal signs). % \begin{macrocode} \gdef\inputfwh#1{% \begingroup\catcode`\%=\active \endlinechar`\^^M\relax \lccode`\~=`\%\relax \lowercase{\let~}\@readfirstheaderline \xp@\@inputfwh\@@input #1\relax } % \end{macrocode} % \end{macro} % % \section{Managing catcode changes} % % A survey of other methods for saving and restoring catcodes would % be more work than I have time for at the moment. The method % given here is the best one I know (other methods use up one extra % control sequence name per file, or don't robustly % handle multiple levels of file nesting). % % \begin{macro}{\localcatcodes} % The \cw{localcatcodes} function changes catcodes according to the % character/catcode pairs given in its argument, saving the % previous catcode values of those characters on a stack so that % they can be retrieved later with \cw{restorecatcodes}. Example: %\begin{usage} %\localcatcodes{\@{11}\"\active} %\end{usage} % to change the catcode of \cs{\@} to 11 (letter) and the catcode % of \qc{\"} to 13 (active). In \plaintex/ you'd better be careful % to use \qc{\+} instead of \cs{\+} in the argument of % \cw{localcatcodes} because of the outerness of \cs{\+}. % % This function works by using token registers 0 and 4 % to accumulate catcode assignment statements: in \cw{toks0} we put % the statements necessary to restore catcodes to their previous % values, while in \cw{toks 4} we put the statements necessary to % set catcodes to their new values. % \begin{macrocode} \gdef\localcatcodes#1{% \ifx\@empty\@catcodestack \gdef\@catcodestack{{}}% \fi \def\do##1##2{% \ifnum##2>\z@ \catcode\number`##1 \space \number\catcode`##1\relax \expandafter\do\fi}% \xdef\@catcodestack{{\do#1\relax\m@ne}% \@catcodestack}% \def\do##1##2{\catcode`##1 ##2\relax\do}% \do#1\ {\catcode32\let\do}% } % \end{macrocode} % \end{macro} % % \begin{macro}{\@catcodestack} % Init the stack with an empty element; otherwise popping the % next-to-last element would wrongly remove braces from the last % element. But as a matter of fact we could just as well initialize % \cw{@catcodestack} to empty because \cw{localcatcodes} is careful % to add an empty final element if necessary. % \begin{macrocode} \gdef\@catcodestack{{}} % \end{macrocode} % \end{macro} % % \begin{macro}{\restorecatcodes} % The function \cw{restorecatcodes} has to pop the stack and % execute the popped code. % \begin{macrocode} \gdef\restorecatcodes{% \begingroup \ifx\@empty\@catcodestack \errmessage{Can't pop catcodes; \nx@\@catcodestack = empty}% \endgroup \else \def\do##1##2\do{% \gdef\@catcodestack{##2}% % \end{macrocode} % Notice the placement of \arg{1} after the \cw{endgroup}, so that % the catcode assignments are local assignments. % \begin{macrocode} \endgroup##1}% \xp@\do\@catcodestack\do \fi } % \end{macrocode} % \end{macro} % % \section{Trapping redundant input statements} % The utility \fn{listout.tex} calls \fn{menus.sty}, which calls % \fn{dialog.sty}, and all three of these files start by loading % \fn{grabhedr.sty} in order to take advantage of its functions % \cw{fileversiondate}, \cw{localcatcodes}, and \cw{inputfwh}. But % consequently, when \fn{listout.tex} is used there will be two % redundant attempts to load \fn{grabhedr.sty}. The straightforward % way to avoid the redundant input attempts would be to surround % them with an \cw{ifx} test: % %^^V \ifx\undefined\fileversiondate %^^V \input grabhedr.sty \relax %^^V \fileversiondate{foo.bar}{0.9e}{10-Jun-1993} %^^V \fi % % This method has a few drawbacks, however: (1)~the conditional % remains open throughout the processing of everything in % \fn{grabhedr.sty} and the \cw{fileversiondate} statement, which % makes any \cw{else} or \cw{fi} mismatch problems harder to debug; % (2)~if \cw{undefined} becomes accidentally defined the \cw{ifx} % test will fail; (3)~choosing the right control sequence to test % against \cw{undefined} requires a little care. % % In a situation where we know that the file to be input has had % \cw{fileversiondate} applied to it, if it was already input, then % we have a failsafe control sequence that we can test to find out % whether the file has already been input\Dash the name % of the file. Assuming a standard form for the input statement % (one that will work with either plain \tex/ or \latex/, and makes % as few assumptions as possible), we can write a function that % will trap input statements and execute them only if the given % file has not yet been loaded: % %^^V \csname trap.input\endcsname %^^V \input grabhedr.sty \relax %^^V \fileversiondate{foo.bar}{1.2}{1993-Jun-07} % % \begin{macro}{\trap.input} % The function \cw{trap.input} scans for an input statement in % canonical form and executes it if and only if the file has not yet % been input (more precisely, if the control sequence consisting of % the file name is undefined, which means that it has not had % \cw{fileversiondate} applied to it). The canonical form % that I consider to be the best is \cw{input} \meta{full file % name}{\tt\char32 }\cw{relax}. Having the \cw{relax} means that the % input statement will not try to expand beyond the end of the line % if \cw{endlinechar} is catcoded to 9 (ignore), as is done rather % frequently now by progressive \tex/ programmers. The \cw{relax} % would ordinarily render the space after the file name unnecessary, % but I prefer leaving the space in to avoid interfering with % redefinitions of \cw{input} to take a space-delimited argument % that are occasionally done to achieve other special effects (see, % for example, ``Organizing a large collection of stylefiles'', by % Angelika Binding, {\sl Cahiers GUTenberg}, num\'ero 10--11, % septembre 1991, p.~175.) \latex/'s argument form % \cw{input}\verb"{...}" cannot, unfortunately, be the % canonical form if \plaintex/ compatibility is required. % \begin{macrocode} \expandafter\gdef\csname trap.input\endcsname \input#1 \relax{% \expandafter\ifx\csname#1\endcsname\relax \afterfi\inputfwh{#1}\relax \fi} % \end{macrocode} % \end{macro} % % End the group that encloses this entire file, and then call % \cw{endinput}. % \begin{macrocode} \endgroup \endinput % \end{macrocode} % % \CheckSum{294} % \Finale