Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I have a shell script that produces a large list of macros in an external file in the form:

\expandafter\newcommand\csname XX Name ABC\endcsname{some text ABC 1}
\expandafter\newcommand\csname XX Name XYX\endcsname{some text XYX a}
\expandafter\newcommand\csname XX Name XYZ\endcsname{some text XYZ}
...

The name of the macro is based on files names in various directories. As the files are in different directories they can have the same name in which case TeX has a problem and stops at the first macro that was a duplicate.

I'd like to detect duplicates and provide a complete list of those duplicates by redefining \newcommand. However, my attempt has a few problems:

  1. It doesn't work - everything is reported as a duplicate.
  2. The text of the definition of the \newcommand is printed in the doc. Would prefer this not to be there as it is of no value. I can live with this text in the output, just don't understand how it is getting there.

Code:

\documentclass{article}
\usepackage{etoolbox}

%\usepackage{filecontents}% <--- Commented so that foo.tex is not accidentally overwritten.
\begin{filecontents*}{foo.tex}
    \expandafter\newcommand\csname XX Name ABC\endcsname{some text ABC 1}
    \expandafter\newcommand\csname XX Name XYX\endcsname{some text XYX a}
    \expandafter\newcommand\csname XX Name XYZ\endcsname{some text XYZ}
    \expandafter\newcommand\csname XX Name ABC\endcsname{some text ABC 2}
    \expandafter\newcommand\csname XX Name XYX\endcsname{some text XYX b}
\end{filecontents*}

\newcounter{ErrorCount}

\let\OldNewcommand\newcommand

\begin{document}
    \renewcommand*{\newcommand}[2][]{%
        \ifdefined#2
            \stepcounter{ErrorCount}%
            \typeout{**** Error: "#1" is a duplicate.}%
            \par Error: ``#1" is a duplicate
        \else
            \OldNewcommand[#1]{#2}%
        \fi
%       \ifcsname{#2}{%
%           \stepcounter{ErrorCount}%
%           \typeout{**** Error: "#1" is a duplicate.}%
%           \par Error: ``#1" is a duplicate
%       }{%
%           \OldNewcommand[#1]{#2}%
%       }%
    }%
    % -------------
    \input{foo.tex}
    \let\newcommand\OldNewcommand
    % -------------
    \ifnum\arabic{ErrorCount}=0
        \typeout{**** Error: 0.}%
        \par All good. No errors.
    \else
        \typeout{**** Error: Duplicates found.}%
        \par Error: \arabic{ErrorCount} duplicates found.
    \fi
\end{document} 
share|improve this question
    
the original definition of newcommand has the mandatory comamnd name argument first, with the optional number and defaults as #2 and #3, you can not define that structure with \renewcommand in your new definition the command name is #2 as in my deleted answer but I suspect yo udon't want to change the argument order –  David Carlisle May 14 at 19:16

3 Answers 3

up vote 9 down vote accepted

latex is already doing the check, so just change the error to a warning and increment:

\documentclass{article}


%\usepackage{filecontents}% <--- Commented so that foo.tex is not accidentally overwritten.
\begin{filecontents*}{foo.tex}
    \expandafter\newcommand\csname XX Name ABC\endcsname{some text ABC 1}
    \expandafter\newcommand\csname XX Name XYX\endcsname{some text XYX a}
    \expandafter\newcommand\csname XX Name XYZ\endcsname{some text XYZ}
    \expandafter\newcommand\csname XX Name ABC\endcsname{some text ABC 2}
    \expandafter\newcommand\csname XX Name XYX\endcsname{some text XYX b}
\end{filecontents*}

\makeatletter
\gdef\@notdefinable{%
  \stepcounter{ErrorCount}%
  \@latex@warning@no@line{%
   Command \@backslashchar\reserved@a\space
   already defined.\MessageBreak
   Or name \@backslashchar\@qend... illegal,
   see p.192 of the manual}}

%\makeatother
\newcounter{ErrorCount}

\begin{document}
    % -------------
    \input{foo.tex}
    \let\newcommand\OldNewcommand
    % -------------
    \ifnum\value{ErrorCount}=0
        \typeout{**** Error: 0.}%
        \par All good. No errors.
    \else
        \typeout{**** Error: Duplicates found.}%
        \par Error: \theErrorCount{} duplicates found.
    \fi


\end{document} 
share|improve this answer
    
Works great. To get the output in the PDF I changed the last line to see p.192 of the manual} \par\@backslashchar\reserved@a"\space already defined}. Out of curiosity, where does the left double quite in the PDF output (with the above change) come from? –  Peter Grill May 14 at 20:08
    
@PeterGrill "\space would typeset as a left double quote followed by a space, just don't use it? –  David Carlisle May 15 at 11:15

I used \@namedef instead of \expandafter\csname.... Makes life easier. You can also set \let\namedef\@namedef if you do want the at symbol:

\documentclass{article}

\usepackage{filecontents}
\begin{filecontents*}{foo.tex}
    \@namedef{XX Name ABC}{some text ABC 1}
    \@namedef{XX Name XYX}{some text XYX a}
    \@namedef{XX Name XYZ}{some text XYZ}
    \@namedef{XX Name ABC}{some text ABC 2}
    \@namedef{XX Name XYX}{some text XYX b}
\end{filecontents*}

\newcounter{ErrorCount}
\makeatletter
\let\Old@namedef\@namedef
\def\@namedef#1#2{%
        \if\@nameuse{#1}\relax
          \Old@namedef{#1}{#2}
        \else
          \stepcounter{ErrorCount}%
            \typeout{**** Error: "#1" is a duplicate.}%
            \par Error: ``#1" is a duplicate
        \fi
}
\makeatother
\begin{document}
\makeatletter
\input{foo.tex}
\makeatother
    \ifnum\arabic{ErrorCount}=0
        \typeout{**** Error: 0.}%
        \par All good. No errors.
    \else
        \typeout{**** Error: Duplicates found.}%
        \par Error: \arabic{ErrorCount} duplicates found.
    \fi
\end{document} 

enter image description here

share|improve this answer
1  
\if\@nameuse{#1}\relax: let's start the macro with two equal tokens, ...; better something like \if\relax\@nameuse{#1}, but I prefer that the macro is not expanded: \expandafter\ifx\csname #1\endcsname\relax –  Heiko Oberdiek May 14 at 19:27

Well you can borrow some code from the kernel, where the main idea is to capture the command name as a string.

Sample output

\documentclass{article}
\usepackage{etoolbox}

\usepackage{filecontents}
\begin{filecontents*}{foo.tex}
    \expandafter\newcommand\csname XX Name ABC\endcsname{some text ABC 1}
    \expandafter\newcommand\csname XX Name XYX\endcsname{some text XYX a}
    \expandafter\newcommand\csname XX Name XYZ\endcsname{some text XYZ}
    \expandafter\newcommand\csname XX Name ABC\endcsname{some text ABC 2}
    \expandafter\newcommand\csname XX Name XYX\endcsname{some text XYX b}
\end{filecontents*}

\newcounter{ErrorCount}

\let\OldNewcommand\newcommand

\begin{document}
\makeatletter
\renewcommand*{\newcommand}[2]{%
  \begingroup \escapechar\m@ne\xdef\@mytempa{{\string#1}}\endgroup
  \expandafter\@ifundefined\@mytempa
  {OK \OldNewcommand{#1}{#2}%
  }{\stepcounter{ErrorCount}%
    \typeout{**** Error: "\string#1" is a duplicate.}%
    \par Error: ``\string#1" is a duplicate
  }%
}%
\makeatother
    % -------------
    \input{foo.tex}
    \let\newcommand\OldNewcommand
    % -------------
    \ifnum\arabic{ErrorCount}=0
        \typeout{**** Error: 0.}%
        \par All good. No errors.
    \else
        \typeout{**** Error: Duplicates found.}%
        \par Error: \arabic{ErrorCount} duplicates found.
    \fi
\end{document} 

I am not sure what your optional arguments were meant to be doing.

share|improve this answer
    
The optional arguments were just to be consistent with the actual possibilities of \newcommand. I don't use optional parameters in this particular case. –  Peter Grill May 14 at 19:38

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.