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 am confused as to why the MWE below produces any black text. As the code is below, the output is:

enter image description here

However, if you comment out the \IfStrEq line then it works fine and the black text goes away?

\documentclass{article}
\usepackage{xcolor}
\usepackage{xstring}
\usepackage{xparse}

\NewDocumentCommand{\GetTexFilePath}{m m m}{%
    \IfStrEq{#1}{SpecialValue}{}{}% this should do nothing
    %
    ../../../#1/#2/#3%
}%

\begin{document}
\def\FileWithPath{\GetTexFilePath{01}{02}{z}}%
\IfFileExists{\FileWithPath}{%
    \textcolor{red}{File \FileWithPath\ found.}%
}{%
    \textcolor{red}{File \FileWithPath\ not found.}%
}%
\end{document}
share|improve this question
    
Very odd. I tried several things. (1) \edef\FileWithPath.. same problem. (2) \def\FileWithPath{...} problem gone. (3) Using \FileWithPath works as expected. The only thing that goes wrong is using \FileWithPath and \IfFileExists in combination. This may also be related to xparse, which is why I added it to the list of keywords. –  Marc van Dongen Jan 30 '12 at 7:22
    
@MarcvanDongen file names simply have to expand to characters. It is an expansion problem. –  Bruno Le Floch Jan 30 '12 at 8:04
add comment

1 Answer

up vote 18 down vote accepted

Your problem boils down to the fact that \IfStrEq is not expandable, but \IfFileExist tries to expand it nevertheless. Essentially, \IfFileExist does the following: (#1 is your file, here \IfStrEq...)

\openin\@inputcheck#1\relax % open the file
\ifeof\@inputcheck          % did the file exist?
  File exists.
\else
  \@iffileonpath {#1}       % search in \input@path
    {File exists.}{File doesn't exist.}
\fi

The TeX primitive \openin takes the stream number \@inputcheck as its first argument, then expands as much as it can, grabbing characters for a file name until encountering a space (which it removes), or a non-expandable command. If the file name #1 is made of characters, or expands to it, then the file name that \openin finds stops at the \relax, and \IfFileExists searches for the correct file. In your case, though, \FileWithPath expands from the left as much as TeX can, until reaching the unexpandable command \let. At this stage, no character has been encountered (as expected: nothing is typeset before \IfStrEq actually starts doing its tests), so \openin finds an empty file name.

But the whole \FileWithPath is still there, just expanded a little. As you expect, \IfStrEq typesets nothing, but the rest of the path is typeset. Then comes the \ifeof test: since your TeX distribution contains a file .tex (you can find its path by trying \input\relax), the empty \openin has opened that file in the stream \@inputcheck, thus the \ifeof test is true. The branch File found of the conditional is taken, making us think that everything has gone well.

So... how to fix that? Use an expandable string comparison, for instance. The package expl3 (from the l3kernel bundle), which you are loading anyways through xparse, provides \str_if_eq:xxTF and \str_if_eq:nnTF which should suit your needs (the first expands its arguments as in an \edef before comparing them character by character; the second doesn't expand). To use such a "code" command, we need to go to the "Expl Syntax".

\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}

\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\GetTexFilePath}{m m m}
  {
    \str_if_eq:xxTF{#1}{SpecialValue}{}{}
    ../../../#1/#2/#3
  }
\ExplSyntaxOff

\begin{document}
\def\FileWithPath{\GetTexFilePath{01}{02}{z}}%
\IfFileExists{\FileWithPath}{%
    \textcolor{red}{File \FileWithPath\ found.}%
}{%
    \textcolor{red}{File \FileWithPath\ not found.}%
}%
\end{document}

[Edited to replace \NewDocumentCommand by \NewExpandableDocumentCommand: even though the former happens to work in this situation, in general, it defines a non-expandable command, and that will most often not expand, getting you back to the expansion problems described in the first part of this answer.] [Re-edited: Peter Grill mentions that \NewExpandableDocumentCommand does not exist.]

Alternatively, you could use the slightly weird primitive \pdfstrcmp (or \strcmp in XeTeX, or, in LuaTeX, some command provided by Heiko?), which takes two arguments (expanding them as an \edef), and expands to -1, 0, or 1 depending on the lexicographical ordering of the strings, 0 if they're equal.

EDIT: Given your further question, I think a simpler approach is to "compute" the full paths when defining \FileWithPath instead of defining \FileWithPath to compute the paths. To do that, replace \def\FileWithPath{...} by a call to an assignment function \defTeXFilePath which performs some checks (expandable or not), and then decides what to store in \FileWithPath. Here I went for \tl_if_eq:nnTF instead of \str_if_eq:xxTF. Differences: "tl" instead of "str" means that we are comparing tokens, with their character code and category code, instead of simple strings (I think of that as colour versus black and white); "n" instead of "x" means that we perform no expansion. Feel free to change that to suit your needs.

\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\defTeXFilePath}{m m m m}
  {
    \tl_if_eq:nnTF{#2}{SpecialValue}
      { \tl_set:Nn #1 { ../../../#2/#3/#4 } }
      { \tl_set:Nn #1 { ../../../#2/xxx#3/#4 } }
  }
\ExplSyntaxOff

\begin{document}
\defTeXFilePath{\FileWithPath}{01}{02}{z}%
%\show\FileWithPath % see that \FileWithPath contains no macro call.
\IfFileExists{\FileWithPath}{%
    \textcolor{red}{File \FileWithPath\ found.}%
}{%
    \textcolor{red}{File \FileWithPath\ not found.}%
}%
\end{document}
share|improve this answer
    
I have resisted using the expl3 syntax until now as it seems quote foreign to me. But in trying to adapt this solution, I assumed that that what goes in the trailing braces would after {SpecialValue} would be {<true>}{<false>}. So, I changed that line to \str_if_eq:xxTF{#1}{SpecialValue}{\def\SomeMacroName{foo}}{\def\SomeMacroName{b‌​ar}} and problem is back. I want to use SomeMacroName to adjust the file name. Clearly there is something about this syntax that I am not understanding. I had removed it from the original MWE as it was not needed to reproduce the problem. –  Peter Grill Jan 30 '12 at 18:02
    
Again, \def is not expandable. You should use \str_if_eq:xxTF{#1}{SpecialValue}{<file name for the special value>}{<normal file name>}. You can't make any assignment within the file name. –  Bruno Le Floch Jan 30 '12 at 19:06
    
I think you meant \DeclareExpandableDocumentCommand instead of \NewExpandableDocumentCommand, although I think the \NewExpandableDocumentCommand is more consistent. I'd change it, but not sure if this is due to a recent update to xparse or not. Also follow up question: Convert string to file name and use in \href –  Peter Grill Mar 16 '12 at 22:10
    
@PeterGrill I don't see what's wrong with \NewExpandableDocumentCommand. It should work fine, unless you've already defined that command elsewhere somehow. In that case, of course, you need \DeclareExpandableDocumentCommand. –  Bruno Le Floch Mar 17 '12 at 1:41
    
I seem am getting a Undefined control sequence. l.6 \NewExpandableDocumentCommand{\GetTexFilePath when I run your first MWE. But works fine if I replace it with \DeclareExpandableDocumentCommand. –  Peter Grill Mar 17 '12 at 3:17
show 1 more comment

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.