16

I use the following code to check if a package is loaded:

\RequirePackage{ltxcmds}
\newcommand{\IfPackageLoaded}[2]{\ltx@ifpackageloaded{#1}{#2}{}}

However, sometimes I want to check if more than one package is loaded. Therefore I would like to generalise the command, such that more than one package can be added in a list separated with commas:

\IfPackagesLoaded{xcolor, colortbl, hyperref}{
...
}%

What would the typical implementation of the processing of such a list look like?

user202729
  • 7,143

3 Answers3

9

As Andrey stated you can use the LaTeX internal macro \@for to go through a comma separated list. Another useful macro is \zap@space, which is normally used to remove spaces from a package option list.

I would define a \next macro which is usually true, i.e. expands to the first of the next two arguments but is that to expand to the second instead if any not-loaded package is encountered. I then redefine an internal macro (inside a group) to skip all other packages. The package-loaded-test done by \lxt@ifpackageloaded (ltxcmds package) and \@ifpackageloaded (LaTeX core, but only-in-preamble) is to check if the macro \ver@<package>.sty is define, which holds the version of the package (but can be empty). I added this code here directly to not be dependent to a package just for this small code.

\documentclass{article}

\usepackage{fixltx2e}
\usepackage{doc}

\makeatletter
\newcommand{\IfPackagesLoaded}[1]{%
    \begingroup
    \let\next\@firstoftwo
    \@for\@tempa:=#1\do{%
        \expandafter\@IfPackagesLoaded\expandafter{\@tempa}%
    }%
    \expandafter\endgroup
    \next
}
\def\@IfPackagesLoaded#1{%
    \edef\@tempa{\zap@space#1 \@empty}% remove any spaces
    \expandafter\ifx\csname ver@\@tempa.sty\endcsname\relax% package not loaded?
        \let\next\@secondoftwo
        \let\@IfPackagesLoaded\@gobble% No need for further checks
    \fi
}
\makeatother

\begin{document}

\IfPackagesLoaded{fixltx2e,doc}{\typeout{yes}}{\typeout{no}}
\IfPackagesLoaded{ fixltx2e , doc }{\typeout{yes}}{\typeout{no}}
\IfPackagesLoaded{hyperref,fixltx2e}{\typeout{yes}}{\typeout{no}}

\end{document}
Martin Scharrer
  • 262,582
  • can you comment, in the best case line after line, what this code does? I understand that the for does an iteration, and in each @IfPackageLoaded is called with @tempa. But what is the meaning of next, what do you do with \@firstoftwo and \@secondoftwo and why can you rename \@IfPackagesLoaded within the command itself? I would assume that it is not available in the second iteration in that case. So basically I do not understand most of this code... – Matthias Pospiech Nov 20 '11 at 18:52
8

You can use the LaTeX kernel's \@for macro, which processes a comma-separated list, in conjunction with an if switch. The \IfPackagesLoaded macro initially sets the switch to true, then goes through the list and sets the switch to false if a package is missing.

\documentclass{article}

\usepackage{ltxcmds}

\usepackage{fixltx2e}
\usepackage{doc}

\makeatletter
\newif\ifallloaded
\newcommand{\IfPackagesLoaded}[3]{%
  \allloadedtrue
  \@for\@tempa:=#1\do{%
    \ltx@ifpackageloaded{\@tempa}{}{\allloadedfalse}}%
  \ifallloaded #2\else #3\fi}
\makeatother

\begin{document}

\IfPackagesLoaded{fixltx2e,doc}{yes}{no}
\IfPackagesLoaded{hyperref,fixltx2e}{yes}{no}

\end{document}
Andrey Vihrov
  • 22,325
7

As others have remarked, the kernel provides \@ifpackageloaded and ltxcmds also \ltx@ifpackageloaded that can be used in the document.

\usepackage{ltxcmds,etoolbox}

\makeatletter
\newcommand{\IfPackagesLoaded}[1]{%
  \@tempswatrue
  \def\do##1{\edef\@tempa{\zap@space##1 \@empty}%
    \ltx@ifpackageloaded{\@tempa}{}{\@tempswafalse}}%
  \docsvlist{#1}%
  \if@tempswa\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi}
\makeatother

Processing the list element through \zap@space ensures that no spaces around the package name remain also if the call is

\IfPackagesLoaded{ abc , xyz }

The conditional \if@tempswa is predefined by LaTeX and is used as a "scratch conditional"; it should be relied on only on immediate application, as other macros might change its truth value. The idea is that the conditional is set to true at the beginning and each "not already loaded" package in the list sets it to false. Therefore it will be still true at the end only if all packages in the list are already loaded.

The final line uses this fact and uses the code in the second or third arguments to \IfPackagesLoaded. Actually they aren't really arguments to \IfPackagesLoaded, but from the user's point of view it makes no difference. I might have defined \IfPackagesLoaded with three arguments by using as the last instruction

\if@tempswa#2\else#3\fi

but the way chosen is better as it leaves no trailing \else or \fi when the code in the second and third argument is executed.

StrongBad
  • 20,495
egreg
  • 1,121,712
  • As I understand \docsvlist processed for each index in the csv list the \do command. But what are these predefined if sequences \@tempswatrue and what does the last line with \if@tempswa – Matthias Pospiech Nov 20 '11 at 18:56
  • @MatthiasPospiech See edited message with explanations; you can use the idea of \zap@space also in Andrey's code, by putting \edef\@tempa{\xp\xp\xp\zap@space\xp\@tempa\space\@empty} just after the opening brace following \do; here \xp stands for \expandafter. – egreg Nov 20 '11 at 20:25
  • Actually I would have expected that there are 2 or three arguments such that there is an if and an else branch. You say that you get around this by using \if@tempswa\expandafter@firstoftwo\else\expandafter@secondoftwo\fi` but now I completely fail to understand how many argument the command has? – Matthias Pospiech Nov 20 '11 at 20:30
  • The command has one argument, but the call must be \IfPackagesLoaded{list}{true}{false}; actually {true} and {false} will be absorbed as arguments either by \@firstoftwo or \@secondoftwo, depending on the test. It's very common in LaTeX; for example \section is defined as having no argument as you can easily check doing \show\section. It's not relevant for the user, though, who can think to \section as taking an optional and a mandatory argument. You can see here for a discussion about \@firstoftwo and \@secondoftwo. – egreg Nov 20 '11 at 20:36