3

I'm new to LaTeX, and here's what I'm trying to accomplish:

  • I have Markdown and LaTeX files in my working directory, say markdown_1.md and latex_1.tex.
  • In my main LaTeX file, main.tex, I have defined a new command \inject which is supposed to check if the argument is a LaTeX or Markdown file. If the file is Markdown, it should execute \input{.cache/markdown_1.tex} and \input{latex_1.tex}.

Why .cache/markdown.tex? Because I have a \write18 command in the file which converts Markdown to Latex and outputs it there.

Here's what I've attempted so far, by researching various StackExchange threads online:

\usepackage{etoolbox}
\makeatletter
\newcommand{\inject}[1]{
    \def\textendash{-}%
    \filename@parse{#1}%
    \edef\filename@base{\detokenize\expandafter{\filename@base}}%
    \def\file{\ifstrequal{\filename@ext}{md}{.cache/\filename@base.tex}{#1}}%
    \input{\file}%
}
\makeatother

I call it as:

\inject{markdown_1.md}
\inject{latex_1.tex}

The issue with this is that the PDF isn't getting generated when I use latexmk. I've passed -shell-escape to pdfLatex.

How can I make this work out?

  • You should take a look to rmarkdown and bookdown before continuing with this approach. – Fran Jul 19 at 7:14
  • Shouldn't the conditional work the other way around? If the extension is .tex, input the file, otherwise input the file in the cache. – egreg Jul 19 at 10:21
  • @egreg Yeah, you got it right. It was a typo and I've corrected it. – Utkarsh Verma Jul 20 at 0:50
2

The problem is that \ifstrequal does no expansion to its arguments. This might be solved by forcing expansion, but let me present a different solution based on expl3 and its richer supply of functions and tests.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\inject}{m}
 {
  \utkarsh_inject:n { #1 }
 }

\tl_new:N \l__utkarsh_inject_dir_tl
\tl_new:N \l__utkarsh_inject_name_tl
\tl_new:N \l__utkarsh_inject_ext_tl

\cs_new_protected:Nn \utkarsh_inject:n
 {
  \file_parse_full_name:nNNN
    { #1 } % the argument
    \l__utkarsh_inject_dir_tl  % stores the directory
    \l__utkarsh_inject_name_tl % stores the file name
    \l__utkarsh_inject_ext_tl  % stores the extension
  \tl_if_empty:NTF \l__utkarsh_inject_ext_tl
   {% no extension
    \file_input:n { #1 }
   }
   {% there is an extension
    \str_if_eq:eeTF { \tl_to_str:N \l__utkarsh_inject_ext_tl } { .tex }
     {% it is .tex
      \file_input:n { #1 }
     }
     {% it is not .tex
      \file_input:n { .cache/\l__utkarsh_inject_name_tl.tex }
     }
   }
 }
\ExplSyntaxOff

\begin{document}

\inject{test}

\inject{test.tex}

\inject{testmd.md}

\end{document}

I created a file test.tex in the work directory and a file testmd.tex in the .cache subdirectory thereof.

enter image description here

If you insist on etoolbox, you need to reverse the test and to expand \filename@ext

\documentclass{article}

\usepackage{etoolbox}
\makeatletter
\newcommand{\inject}[1]{%
  \filename@parse{#1}%
  \edef\filename@base{\detokenize\expandafter{\filename@base}}%
  \expandafter\ifstrequal\expandafter{\filename@ext}{tex}
    {\def\file@to@input{#1}}% the extension is .tex
    {\edef\file@to@input{.cache/\filename@base.tex}}% the extension is not .tex
  \input{\file@to@input}%
}
\makeatother

\begin{document}

\inject{test.tex}

\inject{testmd.md}

\end{document}

The extension has to be specified, in this case; it would be possible to add a test for empty extension as well.

enter image description here

| improve this answer | |
  • Thanks for your answer. I went with the etoolbox approach since it seemed much simpler to me. – Utkarsh Verma Jul 20 at 1:41
5

The manual of the etoolbox package says:

\ifstrequal{⟨string⟩}{⟨string⟩}{⟨true⟩}{⟨false⟩}

Compares two strings and executes ⟨true⟩ if they are equal, and ⟨false⟩ otherwise. The strings are not expanded in the test and the comparison is category code agnostic. Control sequence tokens in any of the ⟨string⟩ arguments will be detokenized and treated as strings. This command is robust.

"Control sequence tokens in any of the ⟨string⟩ arguments will be detokenized and treated as strings" implies that \ifstrequal will not expand/carry out expandable control-sequences (like \filename@ext) but will turn them into sequences of explicit character-tokens of category code 12(other) (respective 10(space) in the case of the character in question being a space character (code point number 32 in TeX's internal character encoding scheme which either is ASCII with traditional TeX engines or is unicode/utf-8 with TeX-engines based on XeTeX or LuaTeX)).

"This command is robust" means \ifstrequal is defined in terms of \protected. This implies that

  • \ifstrequal cannot be component of a filename provided to an \input-command.
  • \ifstrequal won't be expanded by \edef.

The following three pieces of code form preliminary answers to help you understand "expansion". The explanation of why they are only preliminary comes afterwards.

In your scenario you may need to add a bit of expansion-control manually:

\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand{\inject}[1]{%
    % I don't know why you redefine \textendash, so let's do it within a local scope/group:
    % \detokenize is applied in order to turn active characters into harmless characters
    % of category code 12(other).
    \begingroup
    \def\textendash{-}%
    \expandafter\filename@parse\expandafter{\detokenize{#1}}%
    \expandafter\ifstrequal\expandafter{\filename@ext}{tex}{%
       \expandafter\endgroup
       \expandafter\def\expandafter\file\expandafter{\detokenize{#1}}%
    }{%
       \edef\file{\detokenize{.cache/}\detokenize\expandafter{\filename@base}\detokenize{.tex}}%
       \expandafter\endgroup
       \expandafter\def\expandafter\file\expandafter{\file}%
    }%
    \input{\file}%
}
\makeatother

\begin{document}

\inject{markdown_1.md}

\inject{latex_1.tex}

\end{document}

Using \romannumeral0-expansion you can probably do without defining a temporary macro \file:

\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand\exchange[2]{#2#1}%
\newcommand{\inject}[1]{%
    \begingroup
    % \detokenize is applied in order to turn active characters into harmless characters
    % of category code 12(other) and space-characters of whatsoever category code into
    % characters of category code 10(space).
    \expandafter\filename@parse\expandafter{\detokenize{#1}}%
    \expandafter\ifstrequal\expandafter{\filename@ext}{tex}{%
       \expandafter\endgroup\expandafter\input\expandafter{\detokenize{#1}}%
    }{%
       \expandafter\endgroup\expandafter\input\expandafter{%
         \romannumeral0%
         \expandafter\exchange\expandafter{%
           \detokenize{.tex}%
         }{%
           \expandafter\exchange\expandafter{\detokenize\expandafter{\filename@base}}{%
             \expandafter\exchange\expandafter{\detokenize{.cache/}}{ }%
           }%
         }%
       }%
    }%
}
\makeatother

\begin{document}

\inject{markdown_1.md}

\inject{latex_1.tex}

\end{document}

In case you use an engine where the \expanded-primitive is available you can probably do:

\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand{\inject}[1]{%
    \begingroup
    % \detokenize is applied in order to turn active characters into harmless characters
    % of category code 12(other) and space-characters of whatsoever category code into
    % characters of category code 10(space).
    \expandafter\filename@parse\expandafter{\detokenize{#1}}%
    \expandafter\ifstrequal\expandafter{\filename@ext}{tex}{%
       \expandafter\endgroup\expandafter\input\expandafter{\detokenize{#1}}%
    }{%
       \expanded{%
         \noexpand\endgroup\noexpand\input{%
           \detokenize{.cache/}%
           \detokenize\expandafter{\filename@base}%
           \detokenize{.tex}%
         }%
       }%
    }%
}%
\makeatother

\begin{document}

\inject{markdown_1.md}

\inject{latex_1.tex}

\end{document}

Here comes the explanation why these three snippets form preliminary answers only:

Both in the example provided by you in your question and in these three snippets \detokenize is used.

\detokenize in any case

  • appends a space character behind a character sequence that comes from detokenizing a control word token=a control sequence token whose name consists either of more than one character or of a single characters whose category code is 11(letter).
  • produces space tokens of category code 10(space) when delivering spaces/when detokenizing explicit character tokens of character code 32.
  • doubles hash-characters/explicit character tokens of category code 6(parameter) before turning them into characters of category code 12(other). E.g., with the example provided by you in your question hashes present in the filename will be doubled within \filename@base.

The issue of avoiding the doubling of hashes is addressed in the question Prevent hash doubling with \message and in the answers thereof.

The issue of avoiding the appending of spaces behind control word tokens is addressed in one of my answers thereof.

Spaces are also a problem with filenames. As there is also the TeX-primitive-syntax \input file.tex (without curly braces) where the space token (character code 32, category code 10(space)) serves for separating the filename from other things, it might be a good idea to turn spaces that belong to filenames into tokens of category code 12(other).

Yet another problem might be filenames with unbalanced curly braces and/or percent-characters.

Besides this you may wish to wrap the entire filepath in question in quotes (") in order to make sure that your computer-platform handles filenames/filepaths with spaces correctly. (In case you work on a computer platform where quotes are allowed as parts of filenames/directory names, you may need to do something else.)

In case you want to use \inject on document-level only, never "from within other macros"/"from within macro-arguments"/"from within token-registers", I suggest the following route:

Use the xparse-package and have \inject fetch the filename-argument as one of xparse's v-argument-types. This will prevent hash-doubling. Also this way control word tokens whereafter a space would be appended by \detokenize do not come into being at all. Then apply \detokenize for turning active characters (which might be there due to the inputenc-package) into characters of category code 12. Then apply a routine for replacing space-tokens of category code 10(space) as delivered by \scantokens by space characters of category code 12(other):

\documentclass{article}
\usepackage{xparse}
\usepackage{etoolbox}
\makeatletter

\newcommand\exchange[2]{#2#1}%

%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Replace explicit catcode-10-spaces by explicit catcode-12-spaces in an 
%% argument where the control-word-token \relax does not occur and where curly
%% braces are of category code 12(other).
%% All this is the case with tokens delivered by `\detokenize` or `\string`.
%%
%% Apply as 
%%  \romannumeral0\@UDSpaceReplace<token-list where to replace space-tokens><space-token>\relax{}%
%%
\begingroup
\newcommand\@UDSpaceReplace[1]{%
  \endgroup
  \long\def\@UDSpaceReplace##1 ##2\relax##3{%
    %% ##1 - Tokens before the first space-token.
    %% ##2 - Tokens between the first space-token and the end
    %%       of the token-list where spaces are to be
    %%       replaced. (The end is marked by \relax as
    %%       \relax doesn't occur in the token-list.)
    %% ##3 - Stuff where Spaces are replaced so far.
    %% #1  - Space of category code 12.
    \UD@CheckWhetherNull{##2}%
    { ##3##1}{\@UDSpaceReplace##2\relax{##3##1#1}}%
  }%
}%
\catcode`\ =12\relax
\@UDSpaceReplace{ }%
%%-----------------------------------------------------------------------------
%% The \inject-command.
%% Don't apply from inside other macro definitions.
%% Don't apply from inside a macro-argument.
%% Don't apply from inside a token-register.
%% Only apply on document-level.
%%.............................................................................
\NewDocumentCommand{\inject}{}{%
  \begingroup
  \catcode`\^^I=12\relax
  \injectwithchangedcatcoderegime
}%

\NewDocumentCommand{\injectwithchangedcatcoderegime}{+v}{%
  \expandafter\filename@parse\expandafter{%
    \romannumeral0\expandafter\@UDSpaceReplace\detokenize{#1} \relax{}%
  }%
  \expandafter\ifstrequal\expandafter{\filename@ext}{tex}{%
    \expandafter\endgroup\expandafter\input\expandafter{%
      \romannumeral0\expandafter\@UDSpaceReplace\detokenize{"#1"} \relax{}%
    }%
  }{%
    \expandafter\endgroup\expandafter\input\expandafter{%
      \romannumeral0%
      \expandafter\exchange\expandafter{%
        \detokenize{.tex"}%
      }{%
        \expandafter\exchange\expandafter{%
          \romannumeral0\expandafter\@UDSpaceReplace\detokenize\expandafter{\filename@base} \relax{}%
        }{%
          \ifx\filename@area\@empty\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
          {%
            \expandafter\exchange\expandafter{%
              \detokenize{".cache/}%
            }{ }%
          }{%
            \expandafter\exchange\expandafter{%
              \detokenize{cache/}%
            }{%
              \expandafter\exchange\expandafter{%
                \romannumeral0\expandafter\@UDSpaceReplace\detokenize\expandafter{\expandafter"\filename@area} \relax{}%
              }{ }%
            }%
          }%
        }%
      }%
    }%
  }%
}%
\makeatother

\begin{document}

\inject{markdown_1.md}

\inject{latex_1.tex}

\inject{W e i  r d{#1fi}le.tex}

\inject{W e i  r d{#1fi}le.md}

\inject|W e i  / r d}#1fi{le.md|

\end{document}

In case you do wish to use \inject "from within other macros"/"from within macro-arguments"/"from within token-registers" also, whereby things might be tokenized under normal catcode-régime instead of verbatim-catcode-régime and then be "passed on" to \inject, I suggest using a routine which applies \string to every token of the filepath-argument and in the result replaces spaces of category code 10(space) by spaces of category code 12(other). \string—unlike \detokenize—does not double hashes and does not append spaces behind character-sequences that come from control-word-tokens. However this leaves dealing with filenames/filepaths containing

  • unbalanced curly braces and/or
  • percent-characters/comment-characters and/or
  • several consecutive space characters and/or
  • backslashes that lead to tokenizing things as control-sequence-tokens, probably control-word-tokens whereafter space-characters in the .tex-input-file don't get tokenized,

to you:

%% Copyright (C) 2019, 2020 by Ulrich Diez (eu_angelion@web.de)
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public Licence (LPPL), either
%% version 1.3 of this license or (at your option) any later
%% version. (The latest version of this license is in:
%% http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 1999/12/01 or later.)
%% The author of this work is Ulrich Diez.
%% This work has the LPPL maintenance status 'not maintained'.
%% Usage of any/every component of this work is at your own risk.
%% There is no warranty - neither for probably included
%% documentation nor for any other part/component of this work.
%% If something breaks, you usually may keep the pieces.

%%\errorcontextlines=10000

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo,
%%    \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%%    \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%%    \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
  {\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has leading
%%                        catcode-1-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has no leading
%%                        catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is a
%%                               space-token>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is not
%%                               a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
  \romannumeral0\UD@CheckWhetherNull{#1}%
  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\UD@@CheckWhetherLeadingSpace.#1 }{}}%
}%
\@ifdefinable\UD@@CheckWhetherLeadingSpace{%
  \long\def\UD@@CheckWhetherLeadingSpace#1 {%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
    {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
    {\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
     \expandafter\expandafter\expandafter}\expandafter\expandafter
     \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {AB}
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
  \romannumeral0%
  \UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  { #1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------    
%% In case an argument's first token is an opening brace, stringify that and
%% add another opening brace before that and remove everything behind the 
%% matching closing brace:
%% \UD@StringifyOpeningBrace{{Foo}bar} yields {{Foo}  whereby the second
%% opening brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyOpeningBrace[1]{%
  \romannumeral0%
  \expandafter\UD@ExtractFirstArgLoop\expandafter{%
    \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
    \expandafter\expandafter
    \expandafter            {%
    \expandafter\UD@firstoftwo
    \expandafter{%
    \expandafter}%
    \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
    \expandafter\string
    \expandafter}%
    \string#1%
  \UD@SelDOm}%
}%
%%-----------------------------------------------------------------------------    
%% In case an argument's first token is an opening brace, remove everything till 
%% finding the corresponding closing brace. Then stringify that closing brace:
%% \UD@StringifyClosingBrace{{Foo}bar} yields: {}bar} whereby the first closing
%% brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyClosingBrace[1]{%
   \romannumeral0\expandafter\expandafter\expandafter
                 \UD@StringifyClosingBraceloop
                 \UD@ExtractFirstArg{#1}{#1}%
}%
\newcommand\UD@CheckWhetherStringifiedOpenBraceIsSpace[1]{%
%% This can happen when character 32 (space) has catcode 1...
  \expandafter\UD@CheckWhetherLeadingSpace\expandafter{%
    \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
    \expandafter\UD@secondoftwo
    \expandafter{%
    \expandafter}%
    \expandafter{%
    \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
    \expandafter\UD@firstoftwo
    \expandafter{%
    \expandafter}%
    \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
    \expandafter\string
    \expandafter}%
    \string#1%
  }%
}%
\newcommand\UD@TerminateStringifyClosingBraceloop[2]{%
  \UD@Exchange{ }{\expandafter\expandafter\expandafter}%
  \expandafter\expandafter
  \expandafter{%
  \expandafter\string      
  \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
  \expandafter#1%
  \string#2%
  }%
}%
\newcommand\UD@StringifyClosingBraceloopRemoveElement[4]{%
  \expandafter\UD@PassFirstToSecond\expandafter{\expandafter
  {\romannumeral0\expandafter\UD@secondoftwo\string}{}%
    \UD@CheckWhetherStringifiedOpenBraceIsSpace{#4}{%
      \UD@Exchange{\UD@removespace}%
    }{%
      \UD@Exchange{\UD@firstoftwo\expandafter{\expandafter}}%
    }{%
      \UD@Exchange{ }{\expandafter\expandafter\expandafter}%
      \expandafter#1%
      \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
      \expandafter
    }%
    \string#4%
  }{\expandafter\UD@StringifyClosingBraceloop\expandafter{#2#3}}%
}%
\newcommand\UD@StringifyClosingBraceloop[2]{%
  \UD@CheckWhetherNull{#1}{%
    \UD@CheckWhetherStringifiedOpenBraceIsSpace{#2}{%
      \UD@TerminateStringifyClosingBraceloop{\UD@removespace}%
    }{%
      \UD@TerminateStringifyClosingBraceloop{\UD@firstoftwo\expandafter{\expandafter}}%
    }%
    {#2}%
  }{%
    \UD@CheckWhetherLeadingSpace{#1}{%
      \UD@StringifyClosingBraceloopRemoveElement
      {\UD@removespace}{\UD@removespace}%
    }{%
      \UD@StringifyClosingBraceloopRemoveElement
      {\UD@firstoftwo\expandafter{\expandafter}}{\UD@firstoftwo{}}%
    }%
    {#1}{#2}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Stringify each token:
%%
%% \romannumeral0\StringifyLoop{}{<token 1><token 2>...<token n>}
%%
%% yields:  <stringification of token 1>%
%%          <stringification of token 2>%
%%          ...
%%          <stringification of token n>%
%%
%% whereby "stringification of token" means the result of applying \string
%% to the token in question.
%%.............................................................................
\newcommand\StringifyLoop[2]{%
  \UD@CheckWhetherNull{#2}{ #1}{%
    \UD@CheckWhetherBrace{#2}{%
      \expandafter\expandafter\expandafter\UD@Exchange
      \expandafter\expandafter\expandafter{%
        \UD@StringifyClosingBrace{#2}%
      }{%
        \expandafter\StringifyLoop\expandafter{%
          \romannumeral0%
          \expandafter\expandafter\expandafter\UD@Exchange
          \expandafter\expandafter\expandafter{\UD@StringifyOpeningBrace{#2}}{\StringifyLoop{#1}}%
        }%
      }%
    }{%
      \UD@CheckWhetherLeadingSpace{#2}{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@removespace#2}{%
          \StringifyLoop{#1 }%
        }%
      }{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#2}{%
          \expandafter\StringifyLoop\expandafter{%
            \romannumeral0%
            \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\UD@Exchange
            \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{%
              \expandafter\expandafter\expandafter\string
              \expandafter\UD@Exchange
              \romannumeral0\UD@ExtractFirstArgLoop{#2\UD@SelDOm}{}%
            }%
            { #1}%
          }%
        }%
      }%
    }%
  }%
}%
%%-----------------------------------------------------------------------------
%% Replace explicit catcode-10-spaces by explicit catcode-12-spaces in an 
%% argument where the control-word-token \relax does not occur and where curly
%% braces are of category code 12(other).
%% All this is the case with tokens delivered by `\detokenize` or `\string`
%%
%% Apply as 
%%  \romannumeral0\@UDSpaceReplace<token-list where to replace space-tokens><space-token>\relax{}%
%%
\begingroup
\newcommand\@UDSpaceReplace[1]{%
  \endgroup
  \long\def\@UDSpaceReplace##1 ##2\relax##3{%
    %% ##1 - Tokens before the first space-token.
    %% ##2 - Tokens between the first space-token and the end
    %%       of the token-list where spaces are to be
    %%       replaced. (The end is marked by \relax as
    %%       \relax doesn't occur in the token-list.)
    %% ##3 - Stuff where Spaces are replaced so far.
    %% #1  - Space of category code 12.
    \UD@CheckWhetherNull{##2}%
    { ##3##1}{\@UDSpaceReplace##2\relax{##3##1#1}}%
  }%
}%
\catcode`\ =12\relax
\@UDSpaceReplace{ }%
%%-----------------------------------------------------------------------------
%% The \inject-command.
%%.............................................................................
\newcommand\inject[1]{%
  \begingroup
  \expandafter\filename@parse\expandafter{%
    \romannumeral0\expandafter\@UDSpaceReplace\romannumeral0\StringifyLoop{}{#1} \relax{}%
  }%
  \expandafter\ifstrequal\expandafter{\filename@ext}{tex}{%
    \expandafter\endgroup\expandafter\input\expandafter{%
      \romannumeral0\expandafter\@UDSpaceReplace\romannumeral0\StringifyLoop{}{"#1"} \relax{}%
    }%
  }{%
    \expandafter\endgroup\expandafter\input\expandafter{%
      \romannumeral0%
      \expandafter\UD@Exchange\expandafter{%
        \romannumeral0\StringifyLoop{}{.tex"}%
      }{%
        \expandafter\UD@Exchange\expandafter{%
          \romannumeral0%
          \expandafter\@UDSpaceReplace
          \romannumeral0%
          \expandafter\StringifyLoop\expandafter{\expandafter}\expandafter{\filename@base} \relax{}%
        }{%
          \ifx\filename@area\@empty\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
          {%
            \expandafter\UD@Exchange\expandafter{%
              \romannumeral0\StringifyLoop{}{".cache/}%
            }{ }%
          }{%
            \expandafter\UD@Exchange\expandafter{%
              \romannumeral0\StringifyLoop{}{cache/}%
            }{%
              \expandafter\UD@Exchange\expandafter{%
                \romannumeral0%
                \expandafter\@UDSpaceReplace
                \romannumeral0%
                \expandafter\StringifyLoop\expandafter{\expandafter}%
                \expandafter{\expandafter"\filename@area} \relax{}%
              }{ }%
            }%
          }%
        }%
      }%
    }%
  }%
}%

\makeatother

\begin{document}

\inject{markdown_1.md}

\inject{latex_1.tex}

\inject{W e i  r d{#1fi}le.tex}

\inject{W e i  r d{#1fi}le.md}

\begingroup

\catcode`\[=1

\inject{W e i  r d \LaTeX [#{1fi}}le.tex}

\inject{W e i  r_ §& d \LaTeX [#{1fi}}le.md}

\inject{W e i  r_ /§& d \LaTeX [#{1fi}}le.md}

\endgroup

\end{document}

With all these examples I get onto the terminal and into the .log-file error-messages about files not being found .

These error-messages, however, contain the strings that would be used as filepaths if the files existed and therefore were found.

BTW: The value of \escapechar also affects \string and \detokenize.

| improve this answer | |
  • 1
    +1: Impressive! – Dr. Manuel Kuehner Jul 19 at 21:00
  • 1
    @Dr.ManuelKuehner Thanks. Instead of "impressive" I myself might probably have used "weird" referring to my pieces of code, but let us not forget that these words are not mutually exclusive. ;-) – Ulrich Diez Jul 19 at 21:18
  • Thanks for your deep explanations! I really needed them as a beginner. – Utkarsh Verma Jul 20 at 1:34
0

Your criteria seem to be to a) use LaTeX (i.e. limit/avoid TeX primitives) and b) input a markdown pre-processed header before certain LaTeX files. If so, this will do what you want.

\documentclass{article}

\usepackage{etoolbox}

\newcommand*{\inject}[2][]{
    \ifstrempty{#1}
        {\input{#2}}
        {%
        \input{.cache/#1}
        \input{#2}}
    }

\begin{document}

% put the file without markdown pre-process header
\inject{mytexfile}

% put the file with markdown pre-process header
\inject[mymdhder]{mytexfile}

\end{document}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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