1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
|
%%% ====================================================================
%%% @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}
%</driver>
% \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]
%</2e>
% \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}
%</209>
% \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
|