Why can I say \input something but not \centerline something?
\centerline
is a macro, defined in plain.tex
with \def\centerline#1{\line{\hss#1\hss}}
, so it takes an argument. As usual, an argument will be a single token (S
, for example, is a single token), unless this token is a {
, in which case a {
...}
-balanced list of tokens will be taken as the argument.
In Plain TeX \input
is a primitive, so its rules are different. The primitive will look for character tokens and will stop at the first space token[1]. In LaTeX, \input
is a macro, but if the next token is not a {
, then it behaves as the primitive input. If the next token is a {
, then \input
behaves as a macro and grabs the file name as argument.
You can kind-of emulate that with \centerline
by making it grab the rest of the line of input:
\def\centerline{%
\begingroup
\catcode`\^^M=13
\centerlineaux}
\begingroup
\catcode`\^^M=13
\gdef\centerlineaux#1^^M{\endgroup\line{\hss#1\hss}}
\endgroup
\centerline This entire line is centered.
But this is not.
\bye
1 There are different implementations of \input
: Knuth's \input
will scan character tokens until a non-character token or a space is found. If that token was a space, it is discarded. The characters found are taken as the file name.
The Web2C implementation (TeXLive and MiKTeX use that) of \input
allows spaces in file names as long as the space occurrs between two "
.
Finally, in all TeX engines from TeXLive 2020 onwards (before that only in LuaTeX), if the next token after the primitive \input
is a {
, the file name is grabbed between braces (\input{story}
will input story.tex
). Since this is what Knuth calls a "filesystem-dependent change", this applies to tex
as well.
\input
is actually a primitive, and they are defined as part of the TeX core, not as a separate macro that is built on other macros. This is probably best explained if you look at the definition of \input
and friends (within latex.ltx
):
\ifx\@@input\@undefined\let\@@input\input\fi
% <some other definitions>
\def\input{\@ifnextchar\bgroup\@iinput\@@input}
% <some other definitions>
\def\@iinput#1{%
\InputIfFileExists{#1}{}%
{\filename@parse\@curr@file
\edef\reserved@a{\noexpand\@missingfileerror
{\filename@area\filename@base}%
{\ifx\filename@ext\relax tex\else\filename@ext\fi}}%
\reserved@a}}
Note that the sequence of commands stores \input
- the TeX primitive definition - inside \@@input
at the start of loading the LaTeX kernel. Then it (re)defines \input
to be conditional via \@ifnextchar
. This conditional checks if you supplied an opening curly brace {
(or \bgroup
). If so, it calls \@iinput
. This would be the case when you used
\input{story}
The definition of \@iinput
implies it takes a single, mandatory argument, like any other macro you use. This makes sense from your original use case. However, why does \input story
work the same way? For that, we follow the false branch in the \@ifnextchar
conditional. That is, \@@input
.
Since \@@input
is an exact copy of the \input
primitive which, reads a filename as the string of non-empty characters following \input
. Reference to this process is given in scan_file_name
within tex.web
.