# Is it possible to abort loading a package if it's too old?

I'm working on a package that has to work on both TeXlive 2009 (Ubuntu, so updating TeXlive is a nonstarter) and later versions, and I'd like to be able to use fontspec with LuaLaTeX if we're on TeXlive 2010 or later, but \RequirePackage{fontspec}[2008/08/10] (Ubuntu's TeXlive 2009 ships with a fontspec.sty from 2008/08/09) still tries to load fontspec.sty and the date argument only serves to make LaTeX issue a warning that it's too old - after barfing out errors because fontspec.sty has \RequireXeTeX in it.

Is there a way to abort loading a package if it's too old, or do I have to do something borderline insane involving (temporarily) redefining \ProvidesPackage. I have the following example cooked up, but I'd love to replace it with something saner:

\listfiles
\documentclass{article}
%\usepackage{trace}
\def\XID#1{\begingroup\ifdefined #1\aftergroup#1\fi\endgroup}
\XID\traceon
\let\OPP\ProvidesPackage
\def\ProvidesPackage#1[#2/#3]{%
\let\ProvidesPackage\OPP
\let\OPP\undefined
\begingroup
\ifnum #2 < 2009 % if fontspec is older than 2009, abort
\aftergroup\endinput
\XID\traceoff
\fi
\endgroup
\XID\traceoff
\ProvidesPackage#1[#2/#3]}

\usepackage{fontspec}
\ifdefined\setmainfont
\message{^^J*** \string\setmainfont\space defined! ***^^J}
\else
\message{^^J*** \string\setmainfont\space not defined! ***^^J}
\fi
\begin{document}
\end{document}


EDIT: After some fairly intense hacking, I came up with this shorter and saner replacement:

\documentclass{minimal}
\makeatletter
\def\IfNewer#1#2#3#4{% we chain into #5 if true and #6 if false using \@{first,second}oftwo.
\begingroup %that we might forget the craziness that is to follow...
\def\NeedsTeXFormat##1{\@ifnextchar[\relax\relax}
% This is rather brittle and should probably use \@ifnextchar[
\def\ProvidesPackage##1[##2/##3/##4 ##5]{%
\newif\if@goahead % defaults to false, obviously.
% Ugly test, but it works
\ifnum ##2 < #2 % Year too old
\else\ifnum ##2 = #2\ifnum ##3 < #3 % Year OK, month too old
\else\ifnum ##2 = #2\ifnum ##3 = #3\ifnum ##4 < #4 % Year & month OK, day too old
\else
\fi\fi\fi\fi\fi\fi
\endinput}
\input #1.sty % use TeX's \input to avoid #1.sty being in \listfiles
\endgroup % \if@goahead and our redifinitions disappear in a puff of logic.
\expandafter\@firstoftwo
\else
\endgroup
\expandafter\@secondoftwo
\fi}
\makeatother
{\message{^^J*** New enough! ***^^J}}
{\message{^^J*** Too old! ***^^J}}
\begin{document}
\end{document}

• What happens if the user said \let\setmainfont\relax, then obviously your test fails. – IRAN Nov 6 '11 at 6:28
• After looking at it, redefining \ProvidesPackage seems to be the only way to do it. My first idea was to hack the warning into a \endinput, but it is only executed after the file was full loaded as well. – Martin Scharrer Nov 6 '11 at 7:58
• Related Question: Dual compatibility of TeX sources. – Peter Grill Nov 15 '12 at 20:15

After looking through the related LaTeX core code I don't see any other method than redefining \ProvidesPackage. It is the earliest place where the version is known. However, I would reuse the existing macros as much as possible to keep it compatible.

The following code reads the two arguments of, feeds them to the original and then checks the package version as normal, aborting the loading if required. Note that the package version number still gets defined (globally!) and can be used afterwards as well. You should set it to \relax as shown to mark the package as not loaded.

\documentclass{article}

\makeatletter

\let\origProvidesPackage\ProvidesPackage
\def\ProvidesPackage#1{%
\@testopt{\@ProvidesPackage{#1}}{}%
}
\def\@ProvidesPackage#1[#2]{%
\let\ProvidesPackage\origProvidesPackage
\ifx\\#2\\
\ProvidesPackage{#1}%
\else
\ProvidesPackage{#1}[#2]%
\fi
\@ifpackagelater{fontspec}{2008/08/10}{%
}{%
\endinput
}%
}

\usepackage{fontspec}
\@ifpackagelater{fontspec}{2008/08/10}{%
\expandafter\let\csname ver@fontspec.sty\endcsname\relax%   Mark package as not loaded; this must be after \usepackage because of internal LaTeX core code.
}{}%

\makeatother

\begin{document}

\end{document}

• What does \ifx\\#2\\  do? I thought the syntax was \ifx\cs@ne\cstw@ to compare two control sequences. – kahen Nov 6 '11 at 10:34
• @kahen: That's a common coding template to test if #2 is empty, if so the expression stands for \ifx\\\, i.e. it is tested if \ is identical to itself, i.e. is always true, otherwise (if #2 doesn't start with \ is it false and everything until the \else (or \fi if there is no \else) is ignored anyway. – Martin Scharrer Nov 6 '11 at 10:41
• \tracingall \ifx\\ \@empty \\ \fi shows that it takes the false branch. Clearly I don't understand what's going on here. Wouldn't it be simpler to just \ifx#2\@empty? – kahen Nov 6 '11 at 10:53
• No, it doesn't test if #2 is a macro which is empty, but if #2 does expand to nothing, then \ifx\\#2\\ is equal to \ifx\\\\ (true), not \ifx\\\@empty\\ (false). Because this is actually an often ask question I posted it here: tex.stackexchange.com/questions/33823/what-does-ifx-1-stand-for – Martin Scharrer Nov 6 '11 at 11:43
• This makes me sad: it's apparently impossible to undefine \ver@\@currname.sty while in the middle of executing \RequirePackage. It causes stuff to break all over the place. – kahen Nov 10 '11 at 10:17

Sorry, I did not understand the question - Werner explained it me.

Ok, after you loaded package, you can check its version, for example

\@ifpackagelater{amsmath}{1999/12/20}{\typeout{OK}}{\typeout{BAD}}


See this example:

\documentclass{article}
\RequirePackage{amsmath}
\makeatletter
\makeatother

\begin{document}
Text
\end{document}


The problem here is that you need first to load the package, and only then its version becomes known. Which is ok if you want to kill the compilation for older distributions, but might pose problems otherwise.

In the comments I suggested to write whether the package is ok to a special file, and on the next run to check it before loading the package. Probably first one needs to redefine \RequreXeTeX to make the first run end.

• But isn't this exactly what the OP mentioned didn't work? If not, please elaborate on why this works. Perhaps it is OS-dependent? – Werner Nov 6 '11 at 5:19
• Thanks, I did not understand the question. The OP propably needed \@ifpackagelater{PACKAGE}{DATE}{yes branch}{no branch} – Boris Nov 6 '11 at 5:36
• So you have to succesfully \usepackage{fontspec} before you can test whether it was OK?! That doesn't help since lualatex <some file using fontspec> fails on TeXlive 2009 (it runs smack into a \RequireXeTeX right after \ProvidesPackage) but succeeds on TeXlive 2010. Seems there is no way around redefining \ProvidesPackage then – kahen Nov 6 '11 at 5:52
• The problem is, tex needs to read the file to get its version. Here is a hack for you: write the version of the package to the special file, and on the next run read it from this file rather than from the package. – Boris Nov 6 '11 at 6:01