% \iffalse meta-comment % %% File: l3sys-shell.dtx % % Copyright (C) 2018-2020 The LaTeX3 Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % http://www.latex-project.org/lppl.txt % % This file is part of the "l3experimental bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/latex3/latex3 % % for those people who are interested. % %<*driver|package> \RequirePackage{expl3} % %<*driver> \documentclass[full]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3sys-shell} package\\ System shell functions^^A % } % % \author{^^A % The \LaTeX3 Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2020-01-12} % % \maketitle % % \begin{documentation} % % \begin{function}[added = 2018-07-28]{\sys_shell_cp:nn} % \begin{syntax} % \cs{sys_shell_cp:nn} \Arg{source} \Arg{dest} % \end{syntax} % Copies the files specified in the \meta{source} (which may include % wildcards) to the \meta{dest}. The file paths should be specified using % |/| as a path separator. Copying is \emph{not} recursive: only files at % the path level given are copied. If unrestricted shell escape is not % enabled, no action is attempted. % \end{function} % % \begin{function}[added = 2018-07-27]{\sys_shell_mkdir:n} % \begin{syntax} % \cs{sys_shell_mkdir:n} \Arg{directory} % \end{syntax} % Creates the \meta{directory}, which should should be specified using |/| as % a path separator. If unrestricted shell escape is not enabled, no action is % attempted. % \end{function} % % \begin{function}[added = 2018-07-28]{\sys_shell_mv:nn} % \begin{syntax} % \cs{sys_shell_mv:nn} \Arg{old} \Arg{new} % \end{syntax} % Moves the files from the \meta{old} to \meta{new} names/locations: the % \meta{old} names may include wildcards. In both arguments, |/| should be % used as the path separator. If unrestricted shell escape is not enabled, no % action is attempted. % \end{function} % % \begin{function}[added = 2018-07-27]{\sys_shell_rm:n} % \begin{syntax} % \cs{sys_shell_rm:n} \Arg{files} % \end{syntax} % Removes the \meta{files} (which may include wildcards). The file path should % be specified using |/| as a path separator. If unrestricted shell escape is % not enabled, no action is attempted. % \end{function} % % \begin{function}[added = 2018-07-27]{\sys_shell_rmdir:n} % \begin{syntax} % \cs{sys_shell_rmdir:n} \Arg{directory} % \end{syntax} % Removes the \meta{directory}, which should should be specified using |/| as % a path separator. If unrestricted shell escape is not enabled, no action is % attempted. % \end{function} % % \begin{function}[added = 2018-07-28]{\sys_get_shell_pwd:N} % \begin{syntax} % \cs{sys_get_shell_pwd:N} \meta{str var} % \end{syntax} % Sets the \meta{str var} to the present working directory (the output of % |pwd| on Unix or |cd| on Windows). Note that on Windows this will contain % string backslash chars (|\|). If unrestricted shell escape is not % enabled, no action is taken. % \end{function} % % \begin{function}[added = 2018-07-28]{\sys_shell_split_ls:nN} % \begin{syntax} % \cs{sys_shell_split_ls:N} \Arg{glob} \meta{seq} % \end{syntax} % Sets the \meta{seq var} to contain one entry per directory listing % (equivalent to |ls -1| or |dir /b|) as specified by the \meta{glob}: the % entries are strings. If unrestricted shell is not enabled, no action is % taken. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3sys-shell} implementation} % % \begin{macrocode} %<*initex|package> % \end{macrocode} % % \begin{macrocode} %<@@=sys> % \end{macrocode} % % \begin{macrocode} %<*package> \ProvidesExplPackage{l3sys-shell}{2020-01-12}{} {L3 Experimental system shell functions} % % \end{macrocode} % % \begin{macro}[EXP]{\@@_path_to_win:n} % \begin{macro}[EXP]{\@@_path_to_win:w} % \begin{macro}[EXP]{\@@_path_to_win:N} % A simple expandable search-and-replace for providing Windows-style paths. % \begin{macrocode} \cs_new:Npn \@@_path_to_win:n #1 { \exp_after:wN \@@_path_to_win:w \tl_to_str:n {#1} ~ \q_stop } \cs_new:Npn \@@_path_to_win:w #1 ~ #2 \q_stop { \@@_path_to_win:N #1 \q_nil \tl_if_empty:nF {#2} { \c_space_tl \@@_path_to_win:w #2 \q_stop } } \cs_new:Npn \@@_path_to_win:N #1 { \quark_if_nil:NF #1 { \token_if_eq_meaning:NNTF #1 / { \c_backslash_str } {#1} \@@_path_to_win:N } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\sys_shell_cp:nn} % Simple Unix-like file copying: at some stage we may need a directory-only % version as Windows and Unix have different requirements here. % \begin{macrocode} \cs_new_protected:Npx \sys_shell_cp:nn #1#2 { \sys_if_shell_unrestricted:T { \sys_shell_now:x { \sys_if_platform_unix:T { cp~-f~ \exp_not:N \tl_to_str:n {#1} ~ \exp_not:N \tl_to_str:n {#2} } \sys_if_platform_windows:T { copy~/y~ \exp_not:N \@@_path_to_win:n {#1} ~ \exp_not:N \@@_path_to_win:n {#2} } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sys_shell_mkdir:n} % Windows (with the extensions) will automatically make directory trees but % issues a warning if the directory already exists: avoid by including a % test. % \begin{macrocode} \cs_new_protected:Npx \sys_shell_mkdir:n #1 { \sys_if_shell_unrestricted:T { \sys_shell_now:x { \sys_if_platform_unix:T { mkdir~-p~ \exp_not:N \tl_to_str:n {#1} } \sys_if_platform_windows:T { if~not~exist~ \exp_not:N \@@_path_to_win:n { #1 / nul } ~ mkdir~ \exp_not:N \@@_path_to_win:n {#1} } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sys_shell_mv:nn} % On Windows we do not have a single |mv| operation, so copy-and-delete % instead. % \begin{macrocode} \cs_new_protected:Npx \sys_shell_mv:nn #1#2 { \sys_if_shell_unrestricted:T { \sys_shell_now:x { \sys_if_platform_unix:T { mv~ \exp_not:N \tl_to_str:n {#1} ~ \exp_not:N \tl_to_str:n {#2} } \sys_if_platform_windows:T { copy~/y~ \exp_not:N \@@_path_to_win:n {#1} ~ \exp_not:N \@@_path_to_win:n {#2} \token_to_str:N & \token_to_str:N & del~/f~/q~\exp_not:N \@@_path_to_win:n {#1} } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sys_shell_rm:n} % Deletion: obviously a big health warning here! % \begin{macrocode} \cs_new_protected:Npx \sys_shell_rm:n #1 { \sys_if_shell_unrestricted:T { \sys_shell_now:x { \sys_if_platform_unix:T { rm~-f~ \exp_not:N \tl_to_str:n {#1} } \sys_if_platform_windows:T { del~/f~/q~ \exp_not:N \@@_path_to_win:n {#1} } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sys_shell_rmdir:n} % When removing a directory, we create it first as that avoids errors in % the Windows case. % \begin{macrocode} \cs_new_protected:Npx \sys_shell_rmdir:n #1 { \sys_if_shell_unrestricted:T { \sys_shell_mkdir:n {#1} \sys_shell_now:x { \sys_if_platform_unix:T { rm~-rf~ \exp_not:N \tl_to_str:n {#1} } \sys_if_platform_windows:T { rmdir~/s~/q~ \exp_not:N \@@_path_to_win:n {#1} } } } } % \end{macrocode} % \end{macro} % % \begin{variable}{\l_@@_tmp_tl} % Scratch space. % \begin{macrocode} \tl_new:N \l_@@_tmp_tl % \end{macrocode} % \end{variable} % % \begin{macro}{\sys_get_shell_pwd:N} % Getting the path is easy: the main work is avoiding loosing any % information. (This information can be obtained using the recorder file: % that does not require shell escape. However, it's hard to see how it might % be useful without the other file functions.) % \begin{macrocode} \cs_new_protected:Npx \sys_get_shell_pwd:N #1 { \sys_if_shell_unrestricted:T { \exp_not:N \sys_get_shell:nnN { \sys_if_platform_unix:T { pwd } \sys_if_platform_windows:T { cd } } { \char_set_catcode_other:N \exp_not:N \\ \char_set_catcode_other:N \exp_not:N \# \char_set_catcode_other:N \exp_not:N \~ \char_set_catcode_other:N \exp_not:N \% \char_set_catcode_space:N \exp_not:N \ % \tex_endlinechar:D -1 \scan_stop: } \exp_not:N \l_@@_tmp_tl \str_set:NV #1 \exp_not:N \l_@@_tmp_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sys_shell_split_ls:nN} % Getting a one-per-line listing is easy enough. We need to set % \cs{ExplSyntaxOff} as that deals with the end-of-line character. After that, % just a case of tidying up. The listing always ends in |^^M| so there is an % extra entry to trim. % \begin{macrocode} \cs_new_protected:Npx \sys_shell_split_ls:nN #1#2 { \sys_if_shell_unrestricted:T { \exp_not:N \sys_get_shell:nnN { \sys_if_platform_unix:T { ls~-1~ #1 } \sys_if_platform_windows:T { dir~/b~ #1 } } { \ExplSyntaxOff \char_set_catcode_other:N \exp_not:N \\ \char_set_catcode_other:N \exp_not:N \# \char_set_catcode_other:N \exp_not:N \~ \char_set_catcode_other:N \exp_not:N \% \char_set_catcode_other:n { 13 } } \exp_not:N \l_@@_tmp_tl \str_set:NV \exp_not:N \l_@@_tmp_tl \exp_not:N \l_@@_tmp_tl \seq_set_split:NnV #2 { \char_generate:nn { `\^^M } { 12 } } \exp_not:N \l_@@_tmp_tl \seq_pop_right:NN #2 \exp_not:N \l_@@_tmp_tl } } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex