Update: this thing is patched (by making \noexpand
no-operation on \endwrite
) in new versions of (PDF)TeX, but another segmentation fault is possible. Detail in https://topanswers.xyz/tex?q=5286#a5394.
To be fair, maybe Bruno Le Floch (member of the LaTeX3 team, probably also the one wrote that paragraph in interface3.pdf) discovered this one before me.
error message - Shortest code that raises a SIGSEGV - Code Golf Stack Exchange
To be precise, correcting a comment below: both \show ⟨\endwrite⟩
and \meaning ⟨\endwrite⟩
will not segmentation fault in Knuth TeX -- the former shows
> \endwrite=\outer macro:
.
but it does in PDFTeX. Only \let ⟨some assignable thing⟩ ⟨\endwrite⟩
(or executing ⟨\endwrite⟩
directly) segmentation fault Knuth TeX.
No idea why nobody have posted an answer for the \endwrite
token, but here it is.
%! TEX program = pdflatex
\documentclass{article}
\begin{document}
\ExplSyntaxOn
% starting state of the input stream:
% \weird } \endwrite ...
\def \weird { \expandafter \weirda \expandafter { \iffalse } \fi }
% → \weirda { } \endwrite ...
\def \weirda #1 { \expandafter \weirdb \noexpand }
% \endwrite cannot be grabbed as #2 so we \noexpand it
% → \weirdb <safe \endwrite token>
\def \weirdb #1 { \iffalse { \fi } #1 \edef \mycontainendwrite { \noexpand #1 } }
% after it's grabbed once it becomes "unsafe" again
% → } \endwrite \edef \mycontainendwrite { \noexpand \endwrite } ...
% we need to restore the `} \endwrite` tokens otherwise it's an error
\setbox0=\vbox{
\write 16{\weird}
}
\tex_shipout:D\box0
\nonstopmode
\show \mycontainendwrite
\errorstopmode
\expandafter \let \expandafter \myendwrite \mycontainendwrite
\nonstopmode
\show \myendwrite % no output in LuaLaTeX, segmentation fault in pdflatex and XeLaTeX and latex
\errorstopmode
\ExplSyntaxOff
\end{document}
Basically if you do \write <some number> {<some token list>}
, the token list is stored unexpanded in the "whatsit", and when the box is shipped out e.g. with \shipout \box 0 \somemoretokens ...
, something similar to \immediate \write <the number> { <the token list> } \endwrite \somemoretokens ...
is executed.
(*) As pointed out below, \immediate\write
does this as well. Last time I tried it doesn't happen -- probably some mistake on my end.
To store the endwrite somewhere, inject it after the \write
(because the content inside is purely expandable, so you can't store it).
Interestingly it creates a segmentation fault. (I wonder if it's exploitable.) it's just a null pointer dereference, on normal environments it's guaranteed to SIGSEGV, so much less "interesting" than the month value arbitrary-location memory read fixed recently.
The output on LuaLaTeX is
> \mycontainendwrite=macro:
->\endwrite .
l.37 \show \mycontainendwrite
> \myendwrite=\outer macro:
.
l.43 \show \myendwrite
and on the rest is
> \mycontainendwrite=macro:
->\endwrite .
l.37 \show \mycontainendwrite
Segmentation fault (core dumped)
(the segmentation fault is caused by the \let
, not the \show
.)
Side note for the remaining tokens. You can read texdoc tex
and tweak around a bit to figure them out.
First trigger an error that causes the token to be generated. Then "guess" what the resulting following tokens in the input stream will be.
For example, for the \inaccessible
token which can be accessed by executing \def
followed by a non-control-sequence.
$ pdflatex
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022/Arch Linux) (preloaded format=pdflatex)
restricted \write18 enabled.
**\def a
entering extended mode
LaTeX2e patch level 1
L3 programming layer
! Missing control sequence inserted.
\inaccessible
\def a
? I\a{}\show
> \inaccessible=undefined.
\inaccessible
\def a
? I\show
> the letter a.
a
\def a
?
The underlined parts are typed text. The rest are output from pdflatex.
As you can deduce, when \def a
is executed, TeX prints an error message, drops the user to the command prompt,
then continue as-if \def
is just executed and \inaccessible a
are following in the input stream.
If you type I\a{}\show
here, the content to be executed becomes \def\a{}\show\inaccessible a
as you can expect.
This token has .tok
value 0x20010001
(or 536936449
) in lualatex, but I don't think there's any way to create a token given its tok value anyway.
Alternatively (for academical purpose) it's also possible to use the error hook in Lua to intentionally trigger the error to create the inaccessible token, but if you do this 100 times in the same paragraph TeX will stop.
\directlua{
luatexbase.add_to_callback("show_error_hook",
function()
luatexbase.remove_from_callback("show_error_hook", "get inaccessible token")
inaccessible_token=token.get_next()
token.put_next(inaccessible_token)
end,
"get inaccessible token"
)
}
\ExplSyntaxOn
\batchmode
\def a{}
\errorstopmode
Try it online! (link to some online code execution service for a demo and see the result.)
Additional note.
Point 4. The \endtemplate
token
If you do the following (similar to the trick to get \endwrite
token, but don't need the brace hack)
\def\weird{\expandafter \weirda\noexpand}
\def\weirda #1{\global\edef\container{\noexpand #1}#1}
{\setbox0=\vbox{\halign{#\cr\noexpand\weird\cr}}}
you can obtain the outer endtemplate token itself, instead of just its meaning. It has the property
- string representation is also
\endtemplate
, just like the inner token
- meaning is
\outer endtemplate:
, or [unknown command code! (151, 4)]:
on LuaTeX. (another bug...?)
- is outer.
- is expandable.
- if
\noexpand
is applied on it, its meaning becomes \relax
, like a normal expandable token.
If it's expanded once, it becomes the inner token which has the property
- string representation is
\endtemplate
, as mentioned above
- meaning is
end of alignment template
- is not outer.
- is not expandable.
Point 3. notexpanded
As mentioned in the book, its meaning is not equal to the meaning of the normal \relax
. It may be noticeable that its meaning can be assigned to another token, the new token would be unexpandable.
Also, this meaning is also obtained when \noexpand
is applied on an undefined token.
When \noexpand
is applied on a non-expandable token (including the frozen relax), it's not a no-op, but the effect is only shown if you either
- redefine it to be something else, then the meaning of the token on the input stream becomes
\relax
- apply
\show
, then \notexpanded
is printed on the console. (so \expandafter\show \noexpand\let
shows \notexpanded: let
even though \let
is not expandable)
Demo.
% ======== assign meaning \a = \noexpand of y
\def\y {}
\expandafter \let \expandafter \a \noexpand \y
% ======== assign meaning \b = \noexpand of z
\expandafter \let \expandafter \b \noexpand \z
% ======== assign meaning \c = \noexpand of \let
\expandafter \let \expandafter \c \noexpand \let
% ======== \a is different from \relax
\ifx\a\relax \errmessage{this cannot happen} \fi
% ======== \a expanded once is different from \relax
\expandafter\ifx\a\relax \errmessage{this cannot happen} \fi
% ======== \a is different from \y
\ifx\a\y \errmessage{this cannot happen} \fi
% ======== \a expanded once remains the same i.e. it's not expandable, unlike the notexpanded token
\expandafter\ifx\a\a \else \errmessage{this cannot happen} \fi
% ======== \a and \b is the same
\ifx\a\b \else \errmessage{this cannot happen} \fi
% ======== \c meaning is \let
\ifx\c\let \else \errmessage{this cannot happen} \fi
\bye