2

I'm working in a package and want to create libraries, such as tikz and its usetikzlibrary.

However, as I use @ in private macro names, when a library uses another I get an error.

Here's my MWE (description below):

\documentclass[12pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[brazilian]{babel}

% hack to make filecontents overwrite
\makeatletter
\let\oldfilec@ntents\filec@ntents
\gdef\filec@ntents{\filec@ntents@overwrite\oldfilec@ntents}
\makeatother


\begin{filecontents}{libraryone.tex}
    \def\@libonevar{lib 1 variable with @}
    \def\libonefunction{\@libonevar}
    \def\libonevar{lib 1 variable without @}
    \def\liboneotherfunction{\libonevar}
\end{filecontents}

\begin{filecontents}{librarytwo.tex}
    \loadlibrary{libraryone}
    \def\@libtwovar{lib 2 variable}
    \def\libtwofunction{\libonefunction}
    \def\libtwootherfunction{\liboneotherfunction}
\end{filecontents}
    

\begin{filecontents}{mypackage.sty}
    \def\@pkgvar{pkg variable}
    \def\pkgfuncion{\@pkgvar}
    \newcommand{\loadlibrary}[1]{
        \input{#1}
}
\end{filecontents}

\usepackage{mypackage}

\begin{document}
From package: \pkgfuncion

\loadlibrary{libraryone}
From library one: \libonefunction\ and \liboneotherfunction

\loadlibrary{librarytwo}
From library one: \libonefunction\ and \liboneotherfunction % \libonefunction fails!

\end{document}

If I load libraryone, I'm able to use both \libonefunction and liboneotherfunction. However, if I load librarytwo, which loads libraryone, \liboneotherfunctions stops working with a message like "Use of @ doesn't match its definition. From library one: \libonefunction." \liboneotherfunction keeps working just fine.

If I remove all @ the problem goes away, for now, I really prefer to keep using @ for private stuff.

I read articles like When should I use \input vs. \include? but I found nothing about problems with macro names with @ in them.

Is there a way to avoid this problem?

EDIT

My problem remains even after @egreg's and @Ulrich Diez answers.

This is my current actual code (which uses TeX primitives since I thought \IfFileExists could be another problem):

% usedslibrary: load libraries
\newcommand{\ds@loadlibrary}[1]{%
    \ifcsname ds@library#1loaded\endcsname%
        %\PackageWarning{dsdraw}{Library '#1' already loaded.}%
    \else%
        \openin15={#1.code.tex}%
        \ifeof15%
            \PackageWarning{dsdraw}{Couldn't find any library named '#1'.}%
        \else%
            \closein15%
            \makeatletter%
            \input{#1.code.tex}%
            %\makeatother%
            \expandafter\def\csname ds@library#1loaded\endcsname{defined}%
        \fi%
    \fi%
}

2 Answers 2

3

Just nest the \input-command between \makeatletter..\makeatother:

\documentclass[12pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[brazilian]{babel}


\begin{filecontents*}[overwrite,nosearch]{libraryone.tex}
\def\@libonevar{lib 1 variable with @}%
\def\libonefunction{\@libonevar}%
\def\libonevar{lib 1 variable without @}%
\def\liboneotherfunction{\libonevar}%
\end{filecontents*}

\begin{filecontents*}[overwrite,nosearch]{librarytwo.tex}
\loadlibrary{libraryone}%
\def\@libtwovar{lib 2 variable}%
\def\libtwofunction{\libonefunction}%
\def\libtwootherfunction{\liboneotherfunction}%
\end{filecontents*}
    

\begin{filecontents*}[overwrite,nosearch]{mypackage.sty}
\def\@pkgvar{pkg variable}%
\def\pkgfuncion{\@pkgvar}%
\newcommand{\loadlibrary}[1]{%
   \makeatletter\input{#1}\makeatother %<-------
}%
\end{filecontents*}

\usepackage{mypackage}

\begin{document}
From package: \pkgfuncion

\loadlibrary{libraryone}
From library one: \libonefunction\ and \liboneotherfunction

\loadlibrary{librarytwo}
From library one: \libtwofunction\ and \libtwootherfunction

\end{document}

enter image description here

2

When TeX processes \usepackage{mypackage} it will, among other things, do \makeatletter and then \input{mypackage.sty}.

This does not happen with \input. Since \loadlibrary{libraryone} just does \input{libraryone}, TeX will interpret the first instruction

\def\@libonevar{lib 1 variable with @}

as the definition for the control symbol \@. This definition requires that \@ is followed by libonevar. Next \libonefunction is defined to expand to \@libonevar.

When you do also \loadlibrary{librarytwo}, \@ gets a different definition and now it requires that libtwovar follows it. When you now try \libonefunction, TeX substitutes it with \@libonevar and wants to expand \@, which is followed by the wrong tokens.

Solution: if your library files want to define command names with @ in them, do

\newcommand{\loadlibrary}[1]{%
  \makeatletter
  \input{#1}%
  \makeatother
}

By the way, the hack to make filecontents overwrite is no longer necessary; just call

\begin{filecontents}[overwrite]{<filename>}

Full example.

\documentclass[12pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[brazilian]{babel}

\begin{filecontents}[overwrite]{libraryone.tex}
\def\@libonevar{lib 1 variable with @}
\def\libonefunction{\@libonevar}
\def\libonevar{lib 1 variable without @}
\def\liboneotherfunction{\libonevar}
\end{filecontents}

\begin{filecontents}[overwrite]{librarytwo.tex}
\loadlibrary{libraryone}
\def\@libtwovar{lib 2 variable}
\def\libtwofunction{\libonefunction}
\def\libtwootherfunction{\liboneotherfunction}
\end{filecontents}
    

\begin{filecontents}[overwrite]{mypackage.sty}
\def\@pkgvar{pkg variable}
\def\pkgfuncion{\@pkgvar}
\newcommand{\loadlibrary}[1]{%
    \makeatletter
    \input{#1}%
    \makeatother
}
\end{filecontents}

\usepackage{mypackage}

\begin{document}
From package: \pkgfuncion

\loadlibrary{libraryone}
From library one: \libonefunction\ and \liboneotherfunction

\loadlibrary{librarytwo}
From library one: \libonefunction\ and \liboneotherfunction % \libonefunction fails!

\end{document}

enter image description here

4
  • For me it seems a little odd that \input keeps the package's intrinsic \makeatletter when \loadlibrary is called from .sty and forgets about it inside the library source... That makes sense though. Thanks for the overwrite option, since I learned only yesterday the package filecontents was deprecated and I didn't bother finding more about it.
    – Jander
    Commented Dec 18, 2021 at 12:54
  • Actually I had to remove the \makeatother that follows \input, since it was changing the @ catcode back and all private definitions after \loadlibrary went wrong. I side effect is that I guess it will left makeatletter active even for the main document. I will make a version to check where the library is been called from... maybe a starred or private version \@loadlibrary...
    – Jander
    Commented Dec 18, 2021 at 13:13
  • @Jander I believe you're using the wrong approach. You don't want that \makeatletter holds in the whole document.
    – egreg
    Commented Dec 18, 2021 at 13:18
  • I don't really want that, @egreg! :-) I edited the question with my actual current code. Maybe there's something there I'm missing. And, of course, I'll have to handle the \makeatother issue...
    – Jander
    Commented Dec 18, 2021 at 13:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .