summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/mdwtools/at.dtx
blob: ea72376c88adf2c667b1d6ce09237a099e44f370 (plain)
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
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
% \begin{meta-comment}
%
% $Id: at.dtx,v 1.3 1996/11/19 20:46:55 mdw Exp $
%
% Allow @-commands
%
% (c) 1995 Mark Wooding
%
%----- Revision history -----------------------------------------------------
%
% $Log: at.dtx,v $
% Revision 1.3  1996/11/19 20:46:55  mdw
% Entered into RCS
%
%
% \end{meta-comment}
%
% \begin{meta-comment} <general public licence>
%%
%% at package -- support for `@' commands'
%% Copyright (c) 1996 Mark Wooding
%%
%% This program is free software; you can redistribute it and/or modify
%% it under the terms of the GNU General Public License as published by
%% the Free Software Foundation; either version 2 of the License, or
%% (at your option) any later version.
%%
%% This program is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%% GNU General Public License for more details.
%%
%% You should have received a copy of the GNU General Public License
%% along with this program; if not, write to the Free Software
%% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
%%
% \end{meta-comment}
%
% \begin{meta-comment} <Package preamble>
%<+package>\NeedsTeXFormat{LaTeX2e}
%<+package>\ProvidesPackage{at}
%<+package>                [1996/05/02 1.3 @-command support (MDW)]
% \end{meta-comment}
%
% \CheckSum{355}
%% \CharacterTable
%%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%%   Digits        \0\1\2\3\4\5\6\7\8\9
%%   Exclamation   \!     Double quote  \"     Hash (number) \#
%%   Dollar        \$     Percent       \%     Ampersand     \&
%%   Acute accent  \'     Left paren    \(     Right paren   \)
%%   Asterisk      \*     Plus          \+     Comma         \,
%%   Minus         \-     Point         \.     Solidus       \/
%%   Colon         \:     Semicolon     \;     Less than     \<
%%   Equals        \=     Greater than  \>     Question mark \?
%%   Commercial at \@     Left bracket  \[     Backslash     \\
%%   Right bracket \]     Circumflex    \^     Underscore    \_
%%   Grave accent  \`     Left brace    \{     Vertical bar  \|
%%   Right brace   \}     Tilde         \~}
%%
%
% \begin{meta-comment} <driver>
%
%<*driver>
\input{mdwtools}
\describespackage{at}
\aton
\atlet p=\package
\atdef at{\package{at}}
\atdef={\mbox{-}}
\atdef-{@@@=}
\atlet.=\syntax
\mdwdoc
%</driver>
%
% \end{meta-comment}
%
% \section{User guide}
%
% The @at\ package is an attempt to remove a lot of tedious typing that
% ends up in \LaTeX\ documents, by expanding the number of short command
% names available.  The new command names begin with the `|@|' character,
% rather than the conventional `|\|', so you can tell them apart.
%
% The package provides some general commands for defining @-commands, and
% then uses them to define some fairly simple ones which will be useful to
% most people.
%
% The rules for @-command names aren't terribly complex:
% \begin{itemize}
% \item If the first character of the name is a letter, then the command name
%       consists of all characters up to, but not including, the first
%       nonletter.  Spaces following the command name are ignored.
% \item If the first character of the name is a backslash, then the @-command
%       name consists of the control sequence introduced by the backslash.
% \item Otherwise, the command name consists only of that first character.
%       Spaces following the name are not ignored, unless that character
%       was itself a space character.
% \end{itemize}
%
% Usually, digits are not considered to be letters.  However, the
% \package{at} package will consider digits to be letters if you give it the
% \textsf{digits} option in the |\usepackage| command.  (Note that this
% only affects the \package{at} package; it won't change the characters
% allowed in normal command names.)
%
% \DescribeMacro{\atallowdigits}
% \DescribeMacro{\atdisallowdigits}
% You can enable and disable digits being considered as letters dynamically.
% The |\atallowdigits| command allows digits to be used as letters;
% |\atdisallowdigits| prevents this.  Both declarations follow \LaTeX's
% usual scoping rules.  Both of these commands have corresponding
% environments with the same names (without the leading `|\|', obviously).
%
% \subsection{Defining @-commands}
%
% \DescribeMacro{\newatcommand}
% \DescribeMacro{\renewatcommand}
% The |\newatcommand| command will define a new @-command using a syntax
% similar to |\newcommand|.  For example, you could define
% \begin{listing}
%\newatcommand c[1]{\chapter{#1}}
% \end{listing}
% to make @.{"@c{"<name>"}"} equivalent to @.{"\\chapter{"<name>"}"}.
%
% A |\renewatcommand| is also provided to redefine existing commands, should
% the need arise.
%
% \DescribeMacro{\atdef}
% For \TeX\ hackers, the |\atdef| command defines @-commands using a syntax
% similar to \TeX's built-in |\def|.  
%
% As an example, the following command makes @.{"@/"<text>"/"} write its
% argument \<text> in italics:
% \begin{listing}
%\atdef/#1/{\textit{#1}}
% \end{listing}
% The real implementation of the |@/|\dots|/| command is a bit more
% complex, and is given in the next section.
%
% You can use all of \TeX's features for defining the syntax of your
% command.  (See chapter~20 of @/The \TeX book/ for more details.)
%
% \DescribeMacro{\atlet}
% Since |\atdef| is provided to behave similarly to |\def|, @at\ provides
% |\atlet| which works similarly to |\let|.  For example you can say
% \begin{listing}
%\atlet!=\index
% \end{listing}
% to allow the short |@!| to behave exactly like |\index|.
%
% Note that all commands defined using these commands are robust even if you
% use fragile commands in their definitions.  Unless you start doing very
% strange things, @-commands never need |\protect|ing.
%
% \subsection{Predefined @-commands}
%
% A small number of hopefully useful commands are provided by default.
% These are described in the table below:
%
% \bigskip \begin{center} \begin{tabular}{lp{3in}}                    \hline
% \bf Command        & \bf Meaning                                 \\ \hline
% @.{"@@"}           & Typesets an `@@' character.                 \\
% @.{"@/"<text>"/"}  & In text (LR or paragraph) mode, typesets its
%                      argument emphasised.  In maths mode, it
%                      always chooses italics.                     \\
% @.{"@*"<text>"*"}  & Typesets its argument \<text> in bold.      \\
% @.{"@i{"<text>"}"} & Equivalent to `@.{"\\index{"<text>"}"}'.    \\
% @.{"@I{"<text>"}"} & As for |@i|, but also writes its argument
%                      to the document.                            \\ \hline
% \end{tabular} \end{center} \bigskip
%
% Package writers should not rely on any predefined @-commands -- they're
% provided for users, and users should be able to redefine them without
% fear of messing anything up.  (This includes the `standard' commands
% provided by the @at\ package, by the way.  They're provided in the vague
% hope that they might be useful, and as examples.)
%
% \implementation
%
% \section{Implementation}
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
% \subsection{Options handling}
%
% We need a switch to say whether digits should be allowed.  Since this
% is a user thing, I'll avoid |\newif| and just define the thing by hand.
%
%    \begin{macrocode}
\def\atallowdigits{\let\ifat@digits\iftrue}
\def\atdisallowdigits{\let\ifat@digits\iffalse}
%    \end{macrocode}
%
% Now define the options.
%
%    \begin{macrocode}
\DeclareOption{digits}{\atallowdigits}
\DeclareOption{nodigits}{\atdisallowdigits}
\ExecuteOptions{nodigits}
\ProcessOptions
%    \end{macrocode}
%
% \subsection{How the commands work}
%
% Obviously we make the `@@' character active.  It inspects the next
% character (or argument, actually -- it can be enclosed in braces for
% longer commands, although this is a bit futile), and builds the command
% name from that.
%
% The |\at| command is equivalent to the active `@@' character always.
%
%
% \subsection{Converting command names}
%
% We need to be able to read an @-command name, and convert it to a normal
% \TeX\ control sequence.  First, we declare some control sequences for
% braces, which we need later.
%
%    \begin{macrocode}
\begingroup
\catcode`\<1
\catcode`\>2
\catcode`\{12
\catcode`\}12
\gdef\at@lb<{>
\gdef\at@rb<}>
\gdef\at@spc< >
\endgroup
%    \end{macrocode}
%
% I'll set up some helper routines now, to help me read the command
% names.  The way this works is that we |\futurelet| the token into
% |\@let@token|.  These routines will then sort out what to do next.
%
% \begin{macro}{\at@test}
%
% Given an |\if|\dots\ test, does its first or second argument.
%
%    \begin{macrocode}
\def\at@test#1\then{%
  #1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi%
}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\at@ifcat}
%
% Checks the category code of the current character.  If it matches the
% argument, it does its second argument, otherwise it does the third.
%
%    \begin{macrocode}
\def\at@ifcat#1{\at@test\ifcat#1\noexpand\@let@token\then}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\at@ifletter}
%
% This routine tests the token to see if it's a letter, and if so adds
% it to the token list and does the first argument; otherwise it does the
% second argument.  It accepts digits as letters if the switch is turned
% on.
%
% There's some fun later, so I'll describe this slowly.  First, we compare
% the category code to a letter, and if we have a match, we know we're done;
% we need to pick up the letter as an argument.  If the catcode is `other',
% we must compare with numbers to see if it's in range.
%
%    \begin{macrocode}
\def\at@ifletter#1#2{%
  \at@ifcat x%
    {\at@ifletter@ii{#1}}%
    {\at@ifcat 0%
      {\at@ifletter@i{#1}{#2}}%
      {#2}%
    }% 
}
%    \end{macrocode}
%
% Right.  It's `other' (so it's safe to handle as a macro argument) and we
% need to know if it's a digit.  This is a little tricky: I use |\if| to
% compare two characters.  The first character is~`1' or~`0' depending on the
% `digit' switch; the second is~`1' or~`x' depending on whether it's actually
% a digit.  They'll only match if everything's worked out OK.
%
%    \begin{macrocode}
\def\at@ifletter@i#1#2#3{%
  \at@test\if%
    \ifat@digits1\else0\fi%
    \ifnum`#3<`0x\else\ifnum`#3>`9x\else1\fi\fi%
  \then%
    {\at@ifletter@ii{#1}{#3}}%
    {#2#3}%
}
%    \end{macrocode}
%
% Right; we have the character, so add it to the list and carry on.
%
%    \begin{macrocode}
\def\at@ifletter@ii#1#2{\toks@\expandafter{\the\toks@#2}#1}
%    \end{macrocode}
%
% \end{macro}
%
% Now we define the command name reading routines.  We have @/almost/ the
% same behaviour as \TeX, although we can't support `|%|' characters for
% reasons to do with \TeX's tokenising algorithm.
%
% \begin{macro}{\at@read@name}
%
% The routine which actually reads the command name works as follows:
% \begin{enumerate}
% \item Have a peek at the next character.  If it's a left or right brace,
%       then use the appropriate character.
% \item If the character is not a letter, just use the character (or whole
%       control sequence.
% \item Finally, if it's a letter, keep reading letters until we find one
%       that wasn't.
% \end{enumerate}
%
% First, we do some setting up and read the first character
%
%    \begin{macrocode}
\def\at@read@name#1{%
  \let\at@next=#1%
  \toks@{}%
  \futurelet\@let@token\at@rn@i%
}
%    \end{macrocode}
%
% Next, sort out what to do, based on the category code.
%
%    \begin{macrocode}
\def\at@rn@i{%
  \def\@tempa{\afterassignment\at@rn@iv\let\@let@token= }%
  \at@ifletter%
    {\futurelet\@let@token\at@rn@iii}%
    {\at@ifcat\bgroup%
      {\toks@\expandafter{\at@lb}\@tempa}%
      {\at@ifcat\egroup%
        {\toks@\expandafter{\at@rb}\@tempa}%
        {\at@ifcat\at@spc%
          {\toks@{ }\@tempa}%
          {\at@rn@ii}%
        }%
      }%
    }%
}
%    \end{macrocode}
%
% Most types of tokens can be fiddled using |\string|.
%
%    \begin{macrocode}
\def\at@rn@ii#1{%
  \toks@\expandafter{\string#1}%
  \at@rn@iv%
}
%    \end{macrocode}
%
% We've found a letter, so we should check for another one.
%
%    \begin{macrocode}
\def\at@rn@iii{%
  \at@ifletter%
    {\futurelet\@let@token\at@rn@iii}%
    {\@ifnextchar.\at@rn@iv\at@rn@iv}%
}
%    \end{macrocode}
%
% Finally, we need to pass the real string, as an argument, to the
% macro.  We make |\@let@token| relax, since it might be something which will
% upset \TeX\ later, e.g., a |#| character.
%
%    \begin{macrocode}
\def\at@rn@iv{%
  \let\@let@token\relax%
  \expandafter\at@next\csname at.\the\toks@\endcsname%
}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\at@cmdname}
%
% Given a control sequence, work out which @-command it came from.
%
%    \begin{macrocode}
\def\at@cmdname#1{\expandafter\at@cmdname@i\string#1\@@foo}
%    \end{macrocode}
%
% Now extract the trailing bits.
%
%    \begin{macrocode}
\def\at@cmdname@i#1.#2\@@foo{#2}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\at@decode}
%
% The |\at@decode| macro takes an extracted @-command name, and tries to
% execute the correct control sequence derived from it.
%
%    \begin{macrocode}
\def\at@decode#1{%
  \at@test\ifx#1\relax\then{%
    \PackageError{at}{Unknown @-command `@\at@cmdname#1'}{%
      The @-command you typed wasn't recognised, so I've ignored it.
    }%
  }{%
    #1%
  }%
}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\@at}
%
% We'd like a measure of compatibility with @p{amsmath}.  The @-commands
% provided by @p{amsmath} work only in maths mode, so this gives us a way of
% distinguishing.  If the control sequence |\Iat| is defined, and we're in
% maths mode, we'll call that instead of doing our own thing.
%
%    \begin{macrocode}
\def\@at{%
  \def\@tempa{\at@read@name\at@decode}%
  \ifmmode\ifx\Iat\not@@defined\else%
    \let\@tempa\Iat%
  \fi\fi%
  \@tempa%
}
%    \end{macrocode}
%
% \end{macro}
%
%
% \subsection{Defining new commands}
%
% \begin{macro}{\at@buildcmd}
%
% First, we define a command to build these other commands:
%
%    \begin{macrocode}
\def\at@buildcmd#1#2{%
  \expandafter\def\csname\expandafter
    \@gobble\string#1@decode\endcsname##1{#2##1}%
  \edef#1{%
    \noexpand\at@read@name%
    \expandafter\noexpand%
      \csname\expandafter\@gobble\string#1@decode\endcsname%
  }%
}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\newatcommand}
% \begin{macro}{\renewatcommand}
% \begin{macro}{\provideatcommand}
% \begin{macro}{\atdef}
% \begin{macro}{\atshow}
%
% Now we define the various operations on @-commands.
%
%    \begin{macrocode}
\at@buildcmd\newatcommand\newcommand
\at@buildcmd\renewatcommand\renewcommand
\at@buildcmd\provideatcommand\providecommand
\at@buildcmd\atdef\def
\at@buildcmd\atshow\show
%    \end{macrocode}
%
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\atlet}
%
% |\atlet| is rather harder than the others, because we want to allow people
% to say things like @.{"\\atlet"<name>"=@"<name>}.  The following hacking
% does the trick.  I'm trying very hard to duplicate |\let|'s behaviour with
% respect to space tokens here, to avoid any surprises, although there
% probably will be some differences.  In particular, |\afterassignment|
% won't work in any sensible way.
%
% First, we read the name of the @-command we're defining.  We also open
% a group, to stop messing other people up, and make `@@' into an `other'
% token, so that it doesn't irritatingly look like its meaning as a control
% sequence.
%
%    \begin{macrocode}
\def\atlet{%
  \begingroup%
  \@makeother\@%
  \at@read@name\atlet@i%
}
%    \end{macrocode}
%
% Put the name into a scratch macro for later use.  Now see if there's an
% equals sign up ahead.  If not, this will gobble any spaces in between the
% @-command name and the argument.
%
%    \begin{macrocode}
\def\atlet@i#1{%
  \def\at@temp{#1}%
  \@ifnextchar=\atlet@ii{\atlet@ii=}%
}
%    \end{macrocode}
%
% Now we gobble the equals sign (whatever catcode it is), and peek at the
% next token up ahead using |\let| with no following space.
%
%    \begin{macrocode}
\def\atlet@ii#1{\afterassignment\atlet@iii\global\let\at@gnext=}
%    \end{macrocode}
%
% The control sequence |\at@gnext| is now |\let| to be whatever we want the
% @-command to be, unless it's picked up an `@@' sign.  If it has, we've
% eaten the |@| token, so just read the name and pass it on.  Otherwise,
% we can |\let| the @-command directly to |\at@gnext|.  There's some
% nastiness here to make |\the\toks@| expand before we close the group and
% restore its previous definition.
%
%    \begin{macrocode}
\def\atlet@iii{%
  \if @\noexpand\at@gnext%
    \expandafter\at@read@name\expandafter\atlet@iv%
  \else%
    \expandafter\endgroup%
    \expandafter\let\at@temp= \at@gnext%
  \fi%
}
%    \end{macrocode}
%
% We've read the source @-command name, so just copy the definitions over.
%
%    \begin{macrocode}
\def\atlet@iv#1{%
  \expandafter\endgroup%
  \expandafter\let\at@temp=#1%
}
%    \end{macrocode}
%
% \end{macro}
%
%
% \subsection{Robustness of @-commands}
%
% We want all @-commands to be robust.  We could leave them all being
% fragile, although making robust @-commands would then be almost impossible.
% There are two problems which we must face:
%
% \begin{itemize}
%
% \item The `|\@at|' command which scans the @-command name is (very)
%       fragile.  I could have used |\DeclareRobustCommand| for it (and in
%       fact I did in an earlier version), but that doesn't help the other
%       problem at all.
%
% \item The `name' of the @-command may contain active characters or control
%       sequences, which will be expanded at the wrong time unless we do
%       something about it now.
%
% \end{itemize}
%
% We must also be careful not to introduce extra space characters into any
% files written, because spaces are significant in @-commands.  Finally,
% we have a minor problem in that most auxiliary files are read in with
% the `@@' character set to be a letter.
%
% \begin{macro}{\at}
%
% Following the example of \LaTeX's `short' command handling, we'll define
% |\at| to decide what to do depending on what |\protect| looks like.  If
% we're typesetting, we just call |\@at| (above) and expect it to cope.
% Otherwise we call |\at@protect|, which scoops up the |\fi| and the |\@at|,
% and inserts other magic.
%
%    \begin{macrocode}
\def\at{\ifx\protect\@typeset@protect\else\at@protect\fi\@at}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\at@protect}
%
% Since we gobbled the |\fi| from the above, we must put that back.  We then 
% need to do things which are more complicated.  If |\protect| is behaving 
% like |\string|, then we do one sort of protection.  Otherwise, we assume
% that |\protect| is being like |\noexpand|.
%
%    \begin{macrocode}
\def\at@protect\fi#1{%
  \fi%
  \ifx\protect\string%
    \expandafter\at@protect@string%
  \else%
    \expandafter\at@protect@noexpand%
  \fi%
}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\at@protect@string}
%
% When |\protect| is |\string|, we don't need to be able to recover the
% original text particularly accurately -- it's for the user to look at.
% Therefore, we just output a $|@|_{11}$ and use |\string| on the next
% token.  This must be sufficient, since we only allow multi-token command
% names if the first token is a letter (code~11).
%
%    \begin{macrocode}
\def\at@protect@string{@\string}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\at@protect@noexpand}
%
% This is a little more complex, since we're still expecting to be executed
% properly at some stage.  However, there's a cheeky dodge we can employ
% since the |\at| command is thoroughly robustified (or at least it will be
% by the time we've finished this).  All |\@unexpandable@protect| does
% is confer repeated robustness on a fragile command.  Since our command
% is robust, we don't need this and we can get away with just using a
% single |\noexpand|, both for the |\@at@| command and the following token
% (which we must robustify, because no-one else can do it for us -- if
% anyone tries, they end up using the |@\protect| command which is rather
% embarassing).
%
% I'll give the definition, and then examine how this expands in various
% cases.
%
%    \begin{macrocode}
\def\at@protect@noexpand{\noexpand\@at@ @\noexpand}
\def\@at@#1{\at}
%    \end{macrocode}
%
% A few points, before we go into the main examination of the protection.
% I've inserted a $|@|_{11}$ token, which is gobbled by |\@at@| when the
% thing is finally expanded fully.  This prevents following space tokens
% in an |\input| file from being swallowed because they follow a control
% sequence.  (I can't use the normal $|@|_{13}$ token, because when files
% like the |.aux| file are read in, |@| is given code~11 by
% |\makeatletter|.)
%
% \setbox0\hbox{|@at@|}
% Now for a description of why this works.  When |\at| is expanded, it works
% out that |\protect| is either |\noexpand| or |\@unexpandable@protect|, and
% becomes |\at@protect@noexpand|.  Because of the |\noexpand| tokens, this
% stops being expanded once it reaches $\fbox{\box0}\,|@|_{11}\,x$ (where
% $x$ is the token immediately following the $|@|_{13}$ character).  If this
% is expanded again, for example in another |\edef|, or in a |\write| or a
% |\mark|, the |\@at@| wakes up, gobbles the following |@| (whatever catcode
% it is -- there may be intervening |\write| and |\input| commands) and
% becomes |\at|, and the whole thing can start over again.
%
% \end{macro}
%
%
% \subsection{Enabling and disabling @-commands}
%
% \begin{macro}{\aton}
%
% We define the |\aton| command to enable all of our magic.  We store
% the old catcode in the |\atoff| command, make `@@' active, and make it
% do the stuff.
%
%    \begin{macrocode}
\def\aton{%
  \ifnum\catcode`\@=\active\else%
    \edef\atoff{\catcode`\noexpand\@\the\catcode`\@}%
    \catcode`\@\active%
    \lccode`\~`\@%
    \lowercase{\let~\at}%
  \fi%
}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\atoff}
%
% The |\atoff| command makes `@@' do the stuff it's meant to.  We remember
% the old catcode and revert to it.  This is largely unnecessary.
%
%    \begin{macrocode}
\def\atoff{\catcode`\@12}
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}{\makeatother}
%
% Now we make our active `@@' the default outside of package files.
%
%    \begin{macrocode}
\let\makeatother\aton
%    \end{macrocode}
%
% \end{macro}
%
% And we must make sure that the user can use all of our nice commands.
% Once the document starts, we allow @-commands.
%
%    \begin{macrocode}
\AtBeginDocument{\aton}
%    \end{macrocode}
%
% \begin{macro}{\dospecials}
% \begin{macro}{\@sanitize}
%
% We must add the `@@' character to the various specials lists.
%
%    \begin{macrocode}
\expandafter\def\expandafter\dospecials\expandafter{\dospecials\do\@}
\expandafter\def\expandafter\@sanitize\expandafter{%
  \@sanitize\@makeother\@}
%    \end{macrocode}
%
% \end{macro}
% \end{macro}
%
% \subsection{Default @-commands}
%
% We define some trivial examples to get the user going.
%
%    \begin{macrocode}
\expandafter\chardef\csname at.@\endcsname=`\@
\atdef*#1*{\ifmmode\mathbf{#1}\else\textbf{#1}\fi}
\atdef/#1/{\ifmmode\mathit{#1}\else\emph{#1}\fi}
\atlet i=\index
\atdef I#1{#1\index{#1}}
%</package>
%    \end{macrocode}
%
% \hfill Mark Wooding, \today
%
% \Finale
%
\endinput