% \iffalse meta-comment
% An Infrastructure for Semantic Macros and Module Scoping
% Copyright (c) 2019 Michael Kohlhase, all rights reserved
%                this file is released under the
%                LaTeX Project Public License (LPPL)
% 
% The original of this file is in the public repository at 
% http://github.com/sLaTeX/sTeX/
%
% TODO update copyright  
%
%<*driver>
\providecommand\bibfolder{../../lib/bib}
\input{../../doc/stex-docheader}

\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{ \sTeX-MathHub
% 	\thanks{Version {\fileversion} (last revised {\filedate})} 
% }
%
% \author{Michael Kohlhase, Dennis Müller\\
% 	FAU Erlangen-Nürnberg\\
% 	\url{http://kwarc.info/}
% }
%
% \maketitle
%
%\ifinfulldoc\else
% This is the documentation for the \pkg{stex-mathhub} package.
% For a more high-level introduction, 
%  see \href{\basedocurl/manual.pdf}{the \sTeX Manual} or the
% \href{\basedocurl/stex.pdf}{full \sTeX documentation}.
% \fi
%
%
% \begin{documentation}\label{pkg:mathhub:doc}
%
% This sub package provides code for handling \sTeX archives,
% files, file paths and related methods.
%
% \ifinfulldoc\else
% \begin{sfragment}{Manual}\input{../../doc/packages/stex-mathhub}\end{sfragment}
% \fi
%
% \section{Macros and Environments}\label{pkg:mathhub:doc:macros}
%
% \begin{function}{\stex_kpsewhich:n}
% |\stex_kpsewhich:n| executes kpsewhich and stores the return
% in\\ |\l_stex_kpsewhich_return_str|. This does not require
% shell escaping.
% \end{function}
%
% \subsection{Files, Paths, URIs}
%
% \begin{function}{\stex_path_from_string:Nn}
%
%   \begin{syntax} \cs{stex_path_from_string:Nn} \meta{path-variable} \Arg{string} \end{syntax}
%   turns the \meta{string} into a path by splitting it at |/|-characters
%   and stores the result in \meta{path-variable}. Also applies
%   \cs{stex_path_canonicalize:N}.
% \end{function}
%
% \begin{function}{\stex_path_to_string:NN, \stex_path_to_string:N}
%   The inverse; turns a path into a string and stores it in the second
% argument variable, or leaves it in the input stream.
% \end{function}
%
% \begin{function}{\stex_path_canonicalize:N}
%   Canonicalizes the path provided; in particular, resolves |.| and |..|
%   path segments.
% \end{function}
%
% \begin{function}[pTF]{\stex_path_if_absolute:N}
%   Checks whether the path provided is \emph{absolute}, i.e. starts
%   with an empty segment
% \end{function}
%
% \begin{variable}{\c_stex_pwd_seq, \c_stex_pwd_str, \c_stex_mainfile_seq, \c_stex_mainfile_str}
%   Store the current working directory as path-sequence and string,
%   respectively, and the (heuristically guessed) full path to the
%   main file, based on the PWD and |\jobname|.
% \end{variable}
%
% \begin{variable}{\g_stex_currentfile_seq}
%   The file being currently processed (respecting |\input| etc.)
% \end{variable}
%
% \begin{function}{\stex_filestack_push:n,\stex_filestack_pop:}
% Push and pop (repsectively) a file path to the file stack,
% to keep track of the current file. Are called in hooks |file/before|
% and |file/after|, respectively.
% \end{function}
%
%  \subsection{MathHub Archives}
%
% \begin{variable}{\mathhub, \c_stex_mathhub_seq, \c_stex_mathhub_str}
% We determine the path to the local MathHub folder via one of
% three means, in order of precedence:
% \begin{enumerate}
%   \item The |mathhub| package option, or
%   \item the |\mathhub|-macro, if it has been defined before
%     the |\usepackage{stex}|-statement, or
%   \item the |MATHHUB| system variable.
% \end{enumerate}
% In all three cases, \cs{c_stex_mathhub_seq} and
% \cs{c_stex_mathhub_str} are set accordingly.
% \end{variable}
%
% \begin{variable}{\l_stex_current_repository_prop}
%   Always points to the \emph{current} MathHub repository (if
%   we currently are in one). Has the following fields corresponding
%   to the entries in the |MANIFEST.MF|-file:
%   \begin{itemize}
%     \item[|id|:] The name of the archive, including its group (e.g. |smglom/calculus|),
%     \item[|ns|:] The content namespace (for modules and symbols),
%     \item[|narr|:] the narration namespace (for document references),
%     \item[|docurl|:] The URL that is used as a basis for \emph{external references},
%     \item[|deps|:] All archives that this archive depends on (currently not in use).
%   \end{itemize}
% \end{variable}
%
% \begin{function}{\stex_set_current_repository:n}
%   Sets the current repository to the one with the provided ID.
%   calls \cs{__stex_mathhub_do_manifest:n}, so works whether this
%   repository's |MANIFEST.MF|-file has already been read or not.
% \end{function}
%
% \begin{function}{\stex_require_repository:n}
%   Calls \cs{__stex_mathhub_do_manifest:n} iff the corresponding
%   archive property list does not already exist, and
%   adds a corresponding definition to the |.sms|-file.
% \end{function}
%
% \begin{function}{\stex_in_repository:nn}
%   \begin{syntax}\cs{stex_in_repository:nn}\Arg{repository-name}\Arg{code}\end{syntax}
% Change the current repository to \Arg{repository-name} (or not, if \Arg{repository-name} is
% empty), and passes its ID on to \Arg{code} as |#1|. Switches back
% to the previous repository after executing \Arg{code}.
% \end{function}
%
%  \subsection{Using Content in Archives}
%
% \begin{function}[EXP]{\mhpath}
%   \begin{syntax}\cs{mhpath}\Arg{archive-ID}\Arg{filename}\end{syntax}
% Expands to the full path of file \meta{filename} in repository \meta{archive-ID}.
% Does not check whether the file or the repository exist. 
% \end{function}
%
% \begin{function}{\inputref,\mhinput}
%   \begin{syntax}\cs{inputref}|[|\meta{archive-ID}|]|\Arg{filename}\end{syntax}
% Both \cs{input} the file \meta{filename} in archive \meta{archive-ID} (relative
% to the |source|-subdirectory). \cs{mhinput} does so directly.
% \cs{inputref} does so within an |\begingroup|...|\endgroup|-block,
% and skips it in |html|-mode, inserting a \emph{reference} to the
% file instead.
%
%   Both also set |\ifinputref| to true.
% \end{function}
%
% \begin{function}{\addmhbibresource}
%   \begin{syntax}\cs{inputref}|[|\meta{archive-ID}|]|\Arg{filename}\end{syntax}
% Adds a |.bib|-file \meta{filename} in archive \meta{archive-ID} (relative
% to the top-directory of the archive!).
% \end{function}
%
% \begin{function}{\libinput}
%   \begin{syntax} \cs{libinput}\Arg{filename} \end{syntax}
%   Inputs \meta{filename}|.tex| from the |lib| folders in the
%   current archive and the |meta-inf|-archive of the current archive group(s)
%   (if existent) in descending order. Throws an error if no file by that name exists in
%   any of the relevant |lib|-folders.
% \end{function}
%
% \begin{function}{\libusepackage}
%   \begin{syntax} \cs{libusepackage}[\meta{args}]\Arg{filename} \end{syntax}
%   Like \cs{libinput}, but looks for |.sty|-files and calls
%   |\usepackage[\meta{args}]\Arg{filename}| instead of \cs{input}.
%
%   Throws an error, if none or more than one suitable package file is found.
% \end{function}
%
% \begin{function}{\mhgraphics,\cmhgraphics}
%   \emph{If} the \pkg{graphicx} package is loaded, these
%   macros are defined at |\begin{document}|.
%
%   \cs{mhgraphics} takes the same arguments as \cs{includegraphics},
%   with the additional optional key |mhrepos|. It then resolves
%   the file path in |\mhgraphics[mhrepos=Foo/Bar]{foo/bar.png}|
%   relative to the |source|-folder of the |Foo/Bar|-archive.
%
%   \cs{cmhgraphics} additional wraps the image in a |center|-environment.
% \end{function}
%
% \begin{function}{\lstinputmhlisting,\clstinputmhlisting}
%   Like \cs{mhgraphics}, but only defined if the \pkg{listings}-package
%   is loaded, and with \cs{lstinputlisting} instead of \cs{includegraphics}.
% \end{function}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\sTeX-MathHub Implementation}\label{pkg:mathhub:doc:impl}
%
%    \begin{macrocode}
%<*package>

%%%%%%%%%%%%%   mathhub.dtx   %%%%%%%%%%%%%

%<@@=stex_path>
%    \end{macrocode}
%
% Warnings and error messages
%
%    \begin{macrocode}
\msg_new:nnn{stex}{error/norepository}{
  No~archive~#1~found~in~#2
}
\msg_new:nnn{stex}{error/notinarchive}{
  Not~currently~in~an~archive,~but~\detokenize{#1}~
  needs~one!
}
\msg_new:nnn{stex}{error/nofile}{
  \detokenize{#1}~could~not~find~file~#2
}
\msg_new:nnn{stex}{error/twofiles}{
  \detokenize{#1}~found~two~candidates~for~#2
}
%    \end{macrocode}
%
% \subsubsection{Generic Path Handling}
%
% We treat paths as \LaTeX3-sequences (of the individual
% path segments, i.e. separated by a /-character) unix-style;
% i.e. a path is absolute if the sequence starts with an empty 
% entry.
%
% \begin{macro}{\stex_path_from_string:Nn}
%    \begin{macrocode}
\cs_new_protected:Nn \stex_path_from_string:Nn {
  \str_set:Nx \l_tmpa_str { #2 }
  \str_if_empty:NTF \l_tmpa_str {
    \seq_clear:N #1
  }{
    \exp_args:NNNo \seq_set_split:Nnn #1 / { \l_tmpa_str }    
    \sys_if_platform_windows:T{
      \seq_clear:N \l_tmpa_tl
      \seq_map_inline:Nn #1 {
        \seq_set_split:Nnn \l_tmpb_tl \c_backslash_str { ##1 }
        \seq_concat:NNN \l_tmpa_tl \l_tmpa_tl \l_tmpb_tl
      }
      \seq_set_eq:NN #1 \l_tmpa_tl
    }
    \stex_path_canonicalize:N #1
  }
}

%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\stex_path_to_string:NN,\stex_path_to_string:N}
%    \begin{macrocode}
\cs_new_protected:Nn \stex_path_to_string:NN {
  \exp_args:NNe \str_set:Nn #2 { \seq_use:Nn #1 / }
}

\cs_new:Nn \stex_path_to_string:N {
  \seq_use:Nn #1 /
}
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\c_@@_dot_str,\c_@@_up_str}
%
% |.| and |..|, respectively.
%
%    \begin{macrocode}
\str_const:Nn \c_@@_dot_str {.}
\str_const:Nn \c_@@_up_str {..}
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\stex_path_canonicalize:N}
%
%  Canonicalizes the path provided; in particular, resolves |.| and |..|
%  path segments.
%
%    \begin{macrocode}
\cs_new_protected:Nn \stex_path_canonicalize:N {
  \seq_if_empty:NF #1 {
    \seq_clear:N \l_tmpa_seq
    \seq_get_left:NN #1 \l_tmpa_tl
    \str_if_empty:NT \l_tmpa_tl {
      \seq_put_right:Nn \l_tmpa_seq {}
    }
    \seq_map_inline:Nn #1 {
      \str_set:Nn \l_tmpa_tl { ##1 }
      \str_if_eq:NNF \l_tmpa_tl \c_@@_dot_str {
        \str_if_eq:NNTF \l_tmpa_tl \c_@@_up_str {
          \seq_if_empty:NTF \l_tmpa_seq {
            \exp_args:NNo \seq_put_right:Nn \l_tmpa_seq {
              \c_@@_up_str
            }
          }{
            \seq_get_right:NN \l_tmpa_seq \l_tmpa_tl
            \str_if_eq:NNTF \l_tmpa_tl \c_@@_up_str {
              \exp_args:NNo \seq_put_right:Nn \l_tmpa_seq {
                \c_@@_up_str
              }
            }{
              \seq_pop_right:NN \l_tmpa_seq \l_tmpb_tl
            }
          }
        }{
          \str_if_empty:NF \l_tmpa_tl {
            \exp_args:NNo \seq_put_right:Nn \l_tmpa_seq { \l_tmpa_tl }
          }
        }
      }
    }
    \seq_gset_eq:NN #1 \l_tmpa_seq
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF]{\stex_path_if_absolute:N}
%    \begin{macrocode}
\prg_new_conditional:Nnn \stex_path_if_absolute:N {p, T, F, TF} {
  \seq_if_empty:NTF #1 {
    \prg_return_false:
  }{
    \seq_get_left:NN #1 \l_tmpa_tl
    \sys_if_platform_windows:TF{
      \str_if_in:NnTF \l_tmpa_tl {:}{
        \prg_return_true:
      }{
        \prg_return_false:
      }
    }{
      \str_if_empty:NTF \l_tmpa_tl {
        \prg_return_true:
      }{
        \prg_return_false:
      }
    }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{PWD and kpsewhich}
%
% \begin{macro}{\stex_kpsewhich:n}
%    \begin{macrocode}
\str_new:N\l_stex_kpsewhich_return_str
\cs_new_protected:Nn \stex_kpsewhich:n {
  \sys_get_shell:nnN { kpsewhich ~ #1 } { } \l_tmpa_tl
  \exp_args:NNo\str_set:Nn\l_stex_kpsewhich_return_str{\l_tmpa_tl}
  \tl_trim_spaces:N \l_stex_kpsewhich_return_str
}
%    \end{macrocode}
% \end{macro}
%
% We determine the PWD
%
% \begin{variable}{\c_stex_pwd_seq,\c_stex_pwd_str}
%    \begin{macrocode}
\sys_if_platform_windows:TF{
  \begingroup\escapechar=-1\catcode`\\=12
  \exp_args:Nx\stex_kpsewhich:n{-expand-var~\c_percent_str CD\c_percent_str}
  \exp_args:NNx\str_replace_all:Nnn\l_stex_kpsewhich_return_str{\c_backslash_str}/
  \exp_args:Nnx\use:nn{\endgroup}{\str_set:Nn\exp_not:N\l_stex_kpsewhich_return_str{\l_stex_kpsewhich_return_str}}
}{
  \stex_kpsewhich:n{-var-value~PWD}
}

\stex_path_from_string:Nn\c_stex_pwd_seq\l_stex_kpsewhich_return_str
\stex_path_to_string:NN\c_stex_pwd_seq\c_stex_pwd_str
\stex_debug:nn {mathhub} {PWD:~\str_use:N\c_stex_pwd_str}
%    \end{macrocode}
% \end{variable}
%
% \subsubsection{File Hooks and Tracking}
%    \begin{macrocode}
%<@@=stex_files>
%    \end{macrocode}
%
% We introduce hooks for file inputs that keep track of the
% absolute paths of files used. This will be useful to keep track
% of modules, their archives, namespaces etc.
%
% Note that the absolute paths are only accurate in |\input|-statements
% for paths relative to the PWD, so they shouldn't be relied upon
% in any other setting than for \sTeX-purposes.
%
% \begin{variable}{\g_@@_stack}
%
% keeps track of file changes
%
%    \begin{macrocode}
\seq_gclear_new:N\g_@@_stack
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\c_stex_mainfile_seq, \c_stex_mainfile_str}
%    \begin{macrocode}
\str_set:Nx \c_stex_mainfile_str {\c_stex_pwd_str/\jobname.tex}
\stex_path_from_string:Nn \c_stex_mainfile_seq 
  \c_stex_mainfile_str
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_stex_currentfile_seq}
%    \begin{macrocode}
\seq_gclear_new:N\g_stex_currentfile_seq
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\stex_filestack_push:n}
%    \begin{macrocode}
\cs_new_protected:Nn \stex_filestack_push:n {
  \stex_path_from_string:Nn\g_stex_currentfile_seq{#1}
  \stex_path_if_absolute:NF\g_stex_currentfile_seq{
    \stex_path_from_string:Nn\g_stex_currentfile_seq{
      \c_stex_pwd_str/#1
    }
  }
  \seq_gset_eq:NN\g_stex_currentfile_seq\g_stex_currentfile_seq
  \exp_args:NNo\seq_gpush:Nn\g_@@_stack\g_stex_currentfile_seq
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\stex_filestack_pop:}
%    \begin{macrocode}
\cs_new_protected:Nn \stex_filestack_pop: {
  \seq_if_empty:NF\g_@@_stack{
    \seq_gpop:NN\g_@@_stack\l_tmpa_seq
  }
  \seq_if_empty:NTF\g_@@_stack{
    \seq_gset_eq:NN\g_stex_currentfile_seq\c_stex_mainfile_seq
  }{
    \seq_get:NN\g_@@_stack\l_tmpa_seq
    \seq_gset_eq:NN\g_stex_currentfile_seq\l_tmpa_seq
  }
}
%    \end{macrocode}
% \end{macro}
%
% Hooks for the current file:
%
%    \begin{macrocode}
\AddToHook{file/before}{
  \stex_filestack_push:n{\CurrentFilePath/\CurrentFile}
}
\AddToHook{file/after}{
  \stex_filestack_pop:
}
%    \end{macrocode}
%
% \subsection{MathHub Repositories}
%    \begin{macrocode}
%<@@=stex_mathhub>
%    \end{macrocode}
%
% \begin{variable}{\mathhub, \c_stex_mathhub_seq, \c_stex_mathhub_str}
% The path to the mathhub directory. If the \cs{mathhub}-macro is not set,
% we query |kpsewhich| for the |MATHHUB| system variable.
%    \begin{macrocode}
\str_if_empty:NTF\mathhub{
  \sys_if_platform_windows:TF{
    \begingroup\escapechar=-1\catcode`\\=12
    \exp_args:Nx\stex_kpsewhich:n{-expand-var~\c_percent_str MATHHUB\c_percent_str}
    \exp_args:NNx\str_replace_all:Nnn\l_stex_kpsewhich_return_str{\c_backslash_str}/
    \exp_args:Nnx\use:nn{\endgroup}{\str_set:Nn\exp_not:N\l_stex_kpsewhich_return_str{\l_stex_kpsewhich_return_str}}
  }{
    \stex_kpsewhich:n{-var-value~MATHHUB}
  }
  \str_set_eq:NN\c_stex_mathhub_str\l_stex_kpsewhich_return_str
  
  \str_if_empty:NTF\c_stex_mathhub_str{
    \msg_warning:nn{stex}{warning/nomathhub}
  }{
    \stex_debug:nn{mathhub}{MathHub:~\str_use:N\c_stex_mathhub_str}
    \exp_args:NNo \stex_path_from_string:Nn\c_stex_mathhub_seq\c_stex_mathhub_str
  }
}{
  \stex_path_from_string:Nn \c_stex_mathhub_seq \mathhub
  \stex_path_if_absolute:NF \c_stex_mathhub_seq {
    \exp_args:NNx \stex_path_from_string:Nn \c_stex_mathhub_seq {
      \c_stex_pwd_str/\mathhub
    }
  }
  \stex_path_to_string:NN\c_stex_mathhub_seq\c_stex_mathhub_str
  \stex_debug:nn{mathhub} {MathHub:~\str_use:N\c_stex_mathhub_str}
}
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\_@@_do_manifest:n}
% Checks whether the manifest for archive |#1| already exists, and
% if not, finds and parses the corresponding manifest file
%    \begin{macrocode}
\cs_new_protected:Nn \_@@_do_manifest:n {
  \prop_if_exist:cF {c_stex_mathhub_#1_manifest_prop} {
    \str_set:Nx \l_tmpa_str { #1 }
    \prop_new:c { c_stex_mathhub_#1_manifest_prop }
    \seq_set_split:NnV \l_tmpa_seq / \l_tmpa_str
    \seq_concat:NNN \l_tmpa_seq \c_stex_mathhub_seq \l_tmpa_seq
    \_@@_find_manifest:N \l_tmpa_seq
    \seq_if_empty:NTF \l_@@_manifest_file_seq {
      \msg_error:nnxx{stex}{error/norepository}{#1}{
        \stex_path_to_string:N \c_stex_mathhub_str
      }
    } {
      \exp_args:No \_@@_parse_manifest:n { \l_tmpa_str }
    }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\l_@@_manifest_file_seq}
%    \begin{macrocode}
\seq_new:N\l_@@_manifest_file_seq
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\_@@_find_manifest:N}
%
% Attempts to find the |MANIFEST.MF| in some file path and
% stores its path in \cs{l_@@_manifest_file_seq}:
%
%    \begin{macrocode}
\cs_new_protected:Nn \_@@_find_manifest:N {
  \seq_set_eq:NN\l_tmpa_seq #1
  \bool_set_true:N\l_tmpa_bool
  \bool_while_do:Nn \l_tmpa_bool {
    \seq_if_empty:NTF \l_tmpa_seq {
      \bool_set_false:N\l_tmpa_bool
    }{
      \file_if_exist:nTF{
        \stex_path_to_string:N\l_tmpa_seq/MANIFEST.MF
      }{
        \seq_put_right:Nn\l_tmpa_seq{MANIFEST.MF}
        \bool_set_false:N\l_tmpa_bool
      }{
        \file_if_exist:nTF{
          \stex_path_to_string:N\l_tmpa_seq/META-INF/MANIFEST.MF
        }{
          \seq_put_right:Nn\l_tmpa_seq{META-INF}
          \seq_put_right:Nn\l_tmpa_seq{MANIFEST.MF}
          \bool_set_false:N\l_tmpa_bool
        }{
          \file_if_exist:nTF{
            \stex_path_to_string:N\l_tmpa_seq/meta-inf/MANIFEST.MF
          }{
            \seq_put_right:Nn\l_tmpa_seq{meta-inf}
            \seq_put_right:Nn\l_tmpa_seq{MANIFEST.MF}
            \bool_set_false:N\l_tmpa_bool
          }{
            \seq_pop_right:NN\l_tmpa_seq\l_tmpa_tl
          }
        }
      }
    }
  }
  \seq_set_eq:NN\l_@@_manifest_file_seq\l_tmpa_seq
}
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\c_@@_manifest_ior}
%
%   File variable used for |MANIFEST|-files
%
%    \begin{macrocode}
\ior_new:N \c_@@_manifest_ior
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\_@@_parse_manifest:n}
%
% Stores the entries in manifest file in the
% corresponding property list:
%
%    \begin{macrocode}
\cs_new_protected:Nn \_@@_parse_manifest:n {
  \seq_set_eq:NN \l_tmpa_seq \l_@@_manifest_file_seq
  \ior_open:Nn \c_@@_manifest_ior {\stex_path_to_string:N \l_tmpa_seq}
  \ior_map_inline:Nn \c_@@_manifest_ior {
    \str_set:Nn \l_tmpa_str {##1}
    \exp_args:NNoo \seq_set_split:Nnn 
        \l_tmpb_seq \c_colon_str \l_tmpa_str
    \seq_pop_left:NNTF \l_tmpb_seq \l_tmpa_tl {
      \exp_args:NNe \str_set:Nn \l_tmpb_tl { 
        \exp_args:NNo \seq_use:Nn \l_tmpb_seq \c_colon_str 
      }
      \exp_args:No \str_case:nnTF \l_tmpa_tl {
        {id} {
          \prop_gput:cno { c_stex_mathhub_#1_manifest_prop } 
            { id } \l_tmpb_tl
        }
        {narration-base} {
          \prop_gput:cno { c_stex_mathhub_#1_manifest_prop } 
            { narr } \l_tmpb_tl
        }
        {url-base} {
          \prop_gput:cno { c_stex_mathhub_#1_manifest_prop } 
            { docurl } \l_tmpb_tl
        }
        {source-base} {
          \prop_gput:cno { c_stex_mathhub_#1_manifest_prop } 
            { ns } \l_tmpb_tl
        }
        {ns} {
          \prop_gput:cno { c_stex_mathhub_#1_manifest_prop } 
            { ns } \l_tmpb_tl
        }
        {dependencies} {
          \prop_gput:cno { c_stex_mathhub_#1_manifest_prop } 
            { deps } \l_tmpb_tl
        }
      }{}{}
    }{}
  }
  \ior_close:N \c_@@_manifest_ior
}
%    \end{macrocode}
% \end{macro}
% 
%
% \begin{macro}{\stex_set_current_repository:n}
%    \begin{macrocode}
\cs_new_protected:Nn \stex_set_current_repository:n {
  \stex_require_repository:n { #1 }
  \prop_set_eq:Nc \l_stex_current_repository_prop { 
    c_stex_mathhub_#1_manifest_prop 
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\stex_require_repository:n}
%    \begin{macrocode}
\cs_new_protected:Nn \stex_require_repository:n {
  \prop_if_exist:cF { c_stex_mathhub_#1_manifest_prop } {
    \stex_debug:nn{mathhub}{Opening~archive:~#1}
    \_@@_do_manifest:n { #1 }
  }
}
%    \end{macrocode}
% \end{macro}
%
%\begin{variable}{\l_stex_current_repository_prop}
%
% Current MathHub repository
%
%    \begin{macrocode}
%\prop_new:N \l_stex_current_repository_prop

\_@@_find_manifest:N \c_stex_pwd_seq
\seq_if_empty:NTF \l_@@_manifest_file_seq {
  \stex_debug:nn{mathhub}{Not~currently~in~a~MathHub~repository}
} {
  \_@@_parse_manifest:n { main }
  \prop_get:NnN \c_stex_mathhub_main_manifest_prop {id} 
    \l_tmpa_str
  \prop_set_eq:cN { c_stex_mathhub_\l_tmpa_str _manifest_prop }
    \c_stex_mathhub_main_manifest_prop
  \exp_args:Nx \stex_set_current_repository:n { \l_tmpa_str }
  \stex_debug:nn{mathhub}{Current~repository:~
    \prop_item:Nn \l_stex_current_repository_prop {id}
  }
}
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\stex_in_repository:nn}
% Executes the code in the second argument in the context
% of the repository whose ID is provided as the first argument.
%    \begin{macrocode}
\cs_new_protected:Nn \stex_in_repository:nn {
  \str_set:Nx \l_tmpa_str { #1 }
  \cs_set:Npn \l_tmpa_cs ##1 { #2 }
  \str_if_empty:NTF \l_tmpa_str {
    \prop_if_exist:NTF \l_stex_current_repository_prop {
      \stex_debug:nn{mathhub}{do~in~current~repository:~\prop_item:Nn \l_stex_current_repository_prop { id }}
      \exp_args:Ne \l_tmpa_cs{
        \prop_item:Nn \l_stex_current_repository_prop { id }
      }
    }{
      \l_tmpa_cs{}
    }
  }{
    \stex_debug:nn{mathhub}{in~repository:~\l_tmpa_str}
    \stex_require_repository:n \l_tmpa_str
    \str_set:Nx \l_tmpa_str { #1 }
    \exp_args:Nne \use:nn {
      \stex_set_current_repository:n \l_tmpa_str
      \exp_args:Nx \l_tmpa_cs{\l_tmpa_str}
    }{
      \stex_debug:nn{mathhub}{switching~back~to:~
        \prop_if_exist:NTF \l_stex_current_repository_prop {
          \prop_item:Nn \l_stex_current_repository_prop { id }:~
          \meaning\l_stex_current_repository_prop
        }{
          no~repository
        }
      }
      \prop_if_exist:NTF \l_stex_current_repository_prop {
       \stex_set_current_repository:n {
        \prop_item:Nn \l_stex_current_repository_prop { id }
       }
      }{
        \let\exp_not:N\l_stex_current_repository_prop\exp_not:N\undefined
      }
    }
  }
}
%    \end{macrocode}
% \end{macro}
%
%  \subsection{Using Content in Archives}
%
% \begin{macro}{\mhpath}
%    \begin{macrocode}
\def \mhpath #1 #2 {
  \exp_args:Ne \tl_if_empty:nTF{#1}{
    \c_stex_mathhub_str / 
      \prop_item:Nn \l_stex_current_repository_prop { id }
      / source / #2
  }{
    \c_stex_mathhub_str / #1 / source / #2
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\inputref,\mhinput}
%    \begin{macrocode}
\newif \ifinputref \inputreffalse

\cs_new_protected:Nn \_@@_mhinput:nn {
  \stex_in_repository:nn {#1} {
    \ifinputref
      \input{ \c_stex_mathhub_str / ##1 / source / #2 }
    \else
      \inputreftrue
      \input{ \c_stex_mathhub_str / ##1 / source / #2 }
      \inputreffalse
    \fi
  }
}
\NewDocumentCommand \mhinput { O{} m}{
  \stex_mhinput:nn{ #1 }{ #2 }
}

\cs_new_protected:Nn \_@@_inputref:nn {
  \stex_in_repository:nn {#1} {
    \bool_lazy_any:nTF {
      {\rustex_if_p:} 
      {\latexml_if_p:}
    } {
      \str_clear:N \l_tmpa_str
      \prop_get:NnNF \l_stex_current_repository_prop { narr } \l_tmpa_str {
        \prop_get:NnNF \l_stex_current_repository_prop { ns } \l_tmpa_str {}
      }
      \stex_annotate_invisible:nnn{inputref}{
        \l_tmpa_str / #2
      }{}
    }{
      \begingroup
        \inputreftrue
        \input{ \c_stex_mathhub_str / ##1 / source / #2 }
      \endgroup
    }
  }
}
\NewDocumentCommand \inputref { O{} m}{
  \_@@_inputref:nn{ #1 }{ #2 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\addmhbibresource}
%    \begin{macrocode}
\cs_new_protected:Nn \_@@_mhbibresource:nn {
  \stex_in_repository:nn {#1} {
    \addbibresource{ \c_stex_mathhub_str / ##1 / #2 }
  }
}
\newcommand\addmhbibresource[2][]{
  \_@@_mhbibresource:nn{ #1 }{ #2 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\libinput}
%    \begin{macrocode}
\cs_new_protected:Npn \libinput #1 {
  \prop_if_exist:NF \l_stex_current_repository_prop {
    \msg_error:nnn{stex}{error/notinarchive}\libinput
  } 
  \prop_get:NnNF \l_stex_current_repository_prop {id} \l_tmpa_str {
    \msg_error:nnn{stex}{error/notinarchive}\libinput
  }
  \seq_clear:N \l_@@_libinput_files_seq
  \seq_set_eq:NN \l_tmpa_seq \c_stex_mathhub_seq
  \seq_set_split:NnV \l_tmpb_seq / \l_tmpa_str

  \bool_while_do:nn { ! \seq_if_empty_p:N \l_tmpb_seq }{
    \str_set:Nx \l_tmpa_str {\stex_path_to_string:N \l_tmpa_seq / meta-inf / lib / #1.tex}
    \IfFileExists{ \l_tmpa_str }{
      \seq_put_right:No \l_@@_libinput_files_seq \l_tmpa_str
    }{}
    \seq_pop_left:NN \l_tmpb_seq \l_tmpa_str
    \seq_put_right:No \l_tmpa_seq \l_tmpa_str
  }

  \str_set:Nx \l_tmpa_str {\stex_path_to_string:N \l_tmpa_seq / lib / #1.tex}
  \IfFileExists{ \l_tmpa_str }{
    \seq_put_right:No \l_@@_libinput_files_seq \l_tmpa_str
  }{}

  \seq_if_empty:NTF \l_@@_libinput_files_seq {
    \msg_error:nnxx{stex}{error/nofile}{\exp_not:N\libinput}{#1.tex}
  }{
    \seq_map_inline:Nn \l_@@_libinput_files_seq {
      \input{ ##1 }
    }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\libusepackage}
%    \begin{macrocode}
\NewDocumentCommand \libusepackage {O{} m} {
  \prop_if_exist:NF \l_stex_current_repository_prop {
    \msg_error:nnn{stex}{error/notinarchive}\libusepackage
  } 
  \prop_get:NnNF \l_stex_current_repository_prop {id} \l_tmpa_str {
    \msg_error:nnn{stex}{error/notinarchive}\libusepackage
  }
  \seq_clear:N \l_@@_libinput_files_seq
  \seq_set_eq:NN \l_tmpa_seq \c_stex_mathhub_seq
  \seq_set_split:NnV \l_tmpb_seq / \l_tmpa_str

  \bool_while_do:nn { ! \seq_if_empty_p:N \l_tmpb_seq }{
    \str_set:Nx \l_tmpa_str {\stex_path_to_string:N \l_tmpa_seq / meta-inf / lib / #2}
    \IfFileExists{ \l_tmpa_str.sty }{
      \seq_put_right:No \l_@@_libinput_files_seq \l_tmpa_str
    }{}
    \seq_pop_left:NN \l_tmpb_seq \l_tmpa_str
    \seq_put_right:No \l_tmpa_seq \l_tmpa_str
  }

  \str_set:Nx \l_tmpa_str {\stex_path_to_string:N \l_tmpa_seq / lib / #2}
  \IfFileExists{ \l_tmpa_str.sty }{
    \seq_put_right:No \l_@@_libinput_files_seq \l_tmpa_str
  }{}

  \seq_if_empty:NTF \l_@@_libinput_files_seq {
    \msg_error:nnxx{stex}{error/nofile}{\exp_not:N\libusepackage}{#2.sty}
  }{
    \int_compare:nNnTF {\seq_count:N \l_@@_libinput_files_seq} = 1 {
      \seq_map_inline:Nn \l_@@_libinput_files_seq {
        \usepackage[#1]{ ##1 }
      }
    }{
      \msg_error:nnxx{stex}{error/twofiles}{\exp_not:N\libusepackage}{#2.sty}
    }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\mhgraphics,\cmhgraphics}
%    \begin{macrocode}

\AddToHook{begindocument}{
	\ltx@ifpackageloaded{graphicx}{
    \define@key{Gin}{mhrepos}{\def\Gin@mhrepos{#1}}
    \newcommand\mhgraphics[2][]{%
      \def\Gin@mhrepos{}\setkeys{Gin}{#1}%
      \includegraphics[#1]{\mhpath\Gin@mhrepos{#2}}}
    \newcommand\cmhgraphics[2][]{\begin{center}\mhgraphics[#1]{#2}\end{center}}    
  }{}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\lstinputmhlisting,\clstinputmhlisting}
%    \begin{macrocode}
	\ltx@ifpackageloaded{listings}{
    \define@key{lst}{mhrepos}{\def\lst@mhrepos{#1}}
    \newcommand\lstinputmhlisting[2][]{%
      \def\lst@mhrepos{}\setkeys{lst}{#1}%
      \lstinputlisting[#1]{\mhpath\lst@mhrepos{#2}}}
    \newcommand\clstinputmhlisting[2][]{\begin{center}\lstinputmhlisting[#1]{#2}\end{center}}
  }{}
}

%</package>
%    \end{macrocode}
% \end{macro}
%
% \end{implementation}
%
% \PrintIndex
