% \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> \def\libfolder#1{../../lib/#1} \input{../../doc/stex-docheader} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \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} % \changes{3.1.0}{2022/03/09}{Fixed a bug with \textbackslash inputref outside of archives} % % 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 % four 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, or % \item a path specified in |~/.stex/mathhub.path|. % \end{enumerate} % In all four 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 { \stex_debug:nn{files}{#2} \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 } \stex_debug:nn{files}{Yields: \stex_path_to_string: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 {\begingroup \catcode`\ =12 \sys_get_shell:nnN { kpsewhich ~ #1 } { } \l_tmpa_tl \tl_gset_eq:NN \l_tmpa_tl \l_tmpa_tl \endgroup \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 \stex_get_document_uri: } % \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 } \stex_get_document_uri: } % \end{macrocode} % \end{macro} % % Hooks for the current file: % % \begin{macrocode} \AddToHook{file/before}{ \tl_if_empty:NTF\CurrentFilePath{ \stex_filestack_push:n{\CurrentFile} }{ \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\str_if_eq:onT\l_stex_kpsewhich_return_str{\c_percent_str MATHHUB\c_percent_str}{\str_clear:N\l_stex_kpsewhich_return_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:NT \c_stex_mathhub_str { \sys_if_platform_windows:TF{ \begingroup\escapechar=-1\catcode`\\=12 \exp_args:Nx\stex_kpsewhich:n{-var-value~HOME} \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~HOME} } \ior_open:NnT \g_tmpa_ior{\l_stex_kpsewhich_return_str / .stex / mathhub.path}{ \begingroup\escapechar=-1\catcode`\\=12 \ior_str_get:NN \g_tmpa_ior \l_tmpa_str \sys_if_platform_windows:T{ \exp_args:NNx\str_replace_all:Nnn\l_tmpa_str{\c_backslash_str}/ } \str_gset_eq:NN \c_stex_mathhub_str\l_tmpa_str \endgroup \ior_close:N \g_tmpa_ior } } \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 } \input{Fatal~Error!} } { \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 \stex_persist:x { \prop_set_from_keyval:cn{ c_stex_mathhub_#1_manifest_prop }{ \exp_after:wN \prop_to_keyval:N \csname c_stex_mathhub_#1_manifest_prop\endcsname } } } % \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 \bool_if:NF \c_stex_persist_mode_bool { \_@@_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}{ \_@@_mhinput:nn{ #1 }{ #2 } } \cs_new_protected:Nn \_@@_inputref:nn { \stex_in_repository:nn {#1} { \stex_html_backend:TF { \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 {} } \tl_if_empty:nTF{ ##1 }{ \IfFileExists{#2}{ \stex_annotate_invisible:nnn{inputref}{ \l_tmpa_str / #2 }{} }{ \input{#2} } }{ \IfFileExists{ \c_stex_mathhub_str / ##1 / source / #2 }{ \stex_annotate_invisible:nnn{inputref}{ \l_tmpa_str / #2 }{} }{ \input{ \c_stex_mathhub_str / ##1 / source / #2 } } } }{ \begingroup \inputreftrue \tl_if_empty:nTF{ ##1 }{ \input{#2} }{ \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}} \providecommand\mhgraphics[2][]{% \def\Gin@mhrepos{}\setkeys{Gin}{#1}% \includegraphics[#1]{\mhpath\Gin@mhrepos{#2}}} \providecommand\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}} }{} } % % \end{macrocode} % \end{macro} % % \end{implementation} % \ifinfulldoc\else\printbibliography\fi % % \PrintIndex