% \iffalse meta-comment % % File: ctex-auxpkg.dtx % ----------------------------------------------------------------------- % Copyright (C) 2003--2026 % CTEX.ORG and any individual authors listed elsewhere in this file. % ----------------------------------------------------------------------- % This work 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 * % * % http://www.latex-project.org/lppl.txt * % * % and version 1.3c or later is part of all distributions of LaTeX * % version 2008 or later. * % * % This work has the LPPL maintenance status `maintained'. * % ----------------------------------------------------------------------- % This work consists of the files ctex.dtx, * % ctex-kernel.dtx, * % ctex-engine.dtx, * % ctex-scheme.dtx, * % ctex-auxpkg.dtx, * % ctex-fontset.dtx, * % ctexpunct.spa, * % ctxdoc.cls, * % ctxdocstrip.tex, * % ctex-zhconv.lua, * % ctex-zhconv-index.lua, * % ctex-zhconv-make.lua, * % the derived files ctex.ins, * % ctex.sty, * % ctexsize.sty, * % ctexheading.sty, * % ctexart.cls, * % ctexbook.cls, * % ctexrep.cls, * % ctexbeamer.cls, * % ctexcap.sty, * % ctexhook.sty, * % ctexpatch.sty, * % ctex-c5size.clo, * % ctex-cs4size.clo, * % ctex-heading-article.def, * % ctex-heading-book.def, * % ctex-heading-report.def, * % ctex-heading-beamer.def, * % ctexbackend.cfg, * % ctex-engine-pdftex.def, * % ctex-engine-xetex.def, * % ctex-engine-luatex.def, * % ctex-engine-aptex.def, * % ctex-engine-uptex.def, * % ctex-name-utf8.cfg, * % ctex-name-gbk.cfg, * % ctex.cfg, * % ctexopts.cfg, * % ctex-scheme-plain.def, * % ctex-scheme-plain-article.def, * % ctex-scheme-plain-book.def, * % ctex-scheme-plain-report.def, * % ctex-scheme-plain-beamer.def, * % ctex-scheme-chinese.def, * % ctex-scheme-chinese-article.def, * % ctex-scheme-chinese-book.def, * % ctex-scheme-chinese-report.def, * % ctex-scheme-chinese-beamer.def, * % ctex-fontset-adobe.def, * % ctex-fontset-fandol.def, * % ctex-fontset-founder.def, * % ctex-fontset-hanyi.def, * % ctex-fontset-mac.def, * % ctex-fontset-macnew.def, * % ctex-fontset-macold.def, * % ctex-fontset-ubuntu.def, * % ctex-fontset-windows.def, * % c19rm.fd, * % c19sf.fd, * % c19tt.fd, * % c70rm.fd, * % c70sf.fd, * % c70tt.fd, * % jy2zhrm.fd, * % jy2zhsf.fd, * % jy2zhtt.fd, * % jt2zhrm.fd, * % jt2zhsf.fd, * % jt2zhtt.fd, * % ctexspa.def, * % translator-theorem-dictionary-ChineseUTF8.dict, * % translator-theorem-dictionary-ChineseGBK.dict, * % ctex-spa-make.tex, * % ctex-spa-macro.tex, * % ctex-zhmap-adobe.tex, * % ctex-zhmap-fandol.tex, * % ctex-zhmap-founder.tex, * % ctex-zhmap-hanyi.tex, * % ctex-zhmap-mac.tex, * % ctex-zhmap-ubuntu.tex, * % ctex-zhmap-windows.tex, * % the documentation files ctex.pdf, * % and README.md. * % ----------------------------------------------------------------------- % * % Any modification of this file should ensure that the copyright and * % license information is placed in the derived files. * % * % ----------------------------------------------------------------------- % %<*!driver> %\NeedsTeXFormat{LaTeX2e}[2026/06/01] %\RequirePackage{expl3} %<+!driver>\GetIdInfo $Id: ctex-auxpkg.dtx 2.6.2 2026-07-04 20:30:27 +0800 6357331c Liam $ % {Chinese adapter in LaTeX (CTEX)} %\ProvidesExplPackage{ctexcap} % {Document and package hooks (CTEX)} %\ProvidesExplPackage{ctexhook} % {Patching commands (CTEX)} %\ProvidesExplPackage{ctexpatch} % {Chinese translation for theorem name (CTEX)} %\ProvidesDictionary{translator-theorem-dictionary} % {\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} % {ChineseGBK}% % {ChineseUTF8}% % [\ExplFileDate\space v\ExplFileVersion\space \ExplFileDescription] % % % \fi % % \begin{implementation} % % \paragraph*{\fbox{\file{\jobname-auxpkg.dtx}}} % % \begin{macrocode} %<@@=ctex> % \end{macrocode} % % \subsection{\pkg{translator} 宏包的中文字典} % % \begin{macrocode} %<*dict> % \end{macrocode} % % \changes{v2.4}{2016/02/19}{提供 \pkg{translator} 宏包的中文定理名称翻译。} % % 包括 \pkg{ChineseGBK} 和 \pkg{ChineseUTF8} 两种形式,目前只翻译 \pkg{beamer} % 宏包需要的定理环境名称。 % % \begin{macrocode} %<*theorem> \providetranslation{Comments}{评论} \providetranslation{comments}{评论} \providetranslation{Comment}{评论} \providetranslation{comment}{评论} \providetranslation{Corollaries}{推论} \providetranslation{corollaries}{推论} \providetranslation{Corollary}{推论} \providetranslation{corollary}{推论} \providetranslation{Definitions}{定义} \providetranslation{definitions}{定义} \providetranslation{Definition}{定义} \providetranslation{definition}{定义} \providetranslation{Examples}{例} \providetranslation{examples}{例} \providetranslation{Example}{例} \providetranslation{example}{例} \providetranslation{Exercises}{练习} \providetranslation{exercises}{练习} \providetranslation{Exercise}{练习} \providetranslation{exercise}{练习} \providetranslation{Facts}{事实} \providetranslation{facts}{事实} \providetranslation{Fact}{事实} \providetranslation{fact}{事实} \providetranslation{Key Lemmas}{关键引理} \providetranslation{key lemmas}{关键引理} \providetranslation{Key Lemma}{关键引理} \providetranslation{key lemma}{关键引理} \providetranslation{Key Observations}{关键观察} \providetranslation{key observations}{关键观察} \providetranslation{Key Observation}{关键观察} \providetranslation{key observation}{关键观察} \providetranslation{Lemmas}{引理} \providetranslation{lemmas}{引理} \providetranslation{Lemma}{引理} \providetranslation{lemma}{引理} \providetranslation{Main Theorems}{主要定理} \providetranslation{main theorems}{主要定理} \providetranslation{Main Theorem}{主要定理} \providetranslation{main theorem}{主要定理} \providetranslation{Observations}{观察} \providetranslation{observations}{观察} \providetranslation{Observation}{观察} \providetranslation{observation}{观察} \providetranslation{Problems}{问题} \providetranslation{problems}{问题} \providetranslation{Problem}{问题} \providetranslation{problem}{问题} \providetranslation{Proofs}{证明} \providetranslation{proofs}{证明} \providetranslation{Proof}{证明} \providetranslation{proof}{证明} \providetranslation{Proof Sketch}{证明提要} \providetranslation{Proof sketch}{证明提要} \providetranslation{proof sketch}{证明提要} \providetranslation{Proof Sketches}{证明提要} \providetranslation{Proof sketches}{证明提要} \providetranslation{proof sketches}{证明提要} \providetranslation{Sketch of Proof}{证明提要} \providetranslation{Sketch of Proofs}{证明提要} \providetranslation{Sketch of proof}{证明提要} \providetranslation{Sketch of proofs}{证明提要} \providetranslation{sketch of proof}{证明提要} \providetranslation{sketch of proofs}{证明提要} \providetranslation{Propositions}{命题} \providetranslation{propositions}{命题} \providetranslation{Proposition}{命题} \providetranslation{proposition}{命题} \providetranslation{Remarks}{注} \providetranslation{remarks}{注} \providetranslation{Remark}{注} \providetranslation{remark}{注} \providetranslation{Solutions}{解} \providetranslation{solutions}{解} \providetranslation{Solution}{解} \providetranslation{solution}{解} \providetranslation{Theorems}{定理} \providetranslation{theorems}{定理} \providetranslation{Theorem}{定理} \providetranslation{theorem}{定理} % % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\pkg{ctexcap} 宏包} % % \begin{macrocode} %<*ctexcap> % \end{macrocode} % % \pkg{ctexcap} 是过时宏包。 % \begin{macrocode} \clist_new:N \l_@@_ctexcap_options_clist \clist_set:Ne \l_@@_ctexcap_options_clist { \exp_not:v { opt@ \@currname . \@currext } , heading } \msg_new:nnn { ctexcap } { deprecated } { Package~`ctexcap'~is~deprecated.\\ Please~use~package~`ctex'~with~option~`#1'~instead: \\\\ \iow_indent:n { \token_to_str:N \usepackage [#1] \{ ctex \} } \\ } \msg_warning:nne { ctexcap } { deprecated } { \clist_use:Nn \l_@@_ctexcap_options_clist { , ~ } } % \end{macrocode} % % \pkg{ctexcap} 是默认打开 \opt{heading} 选项的 \pkg{ctex}。 % \begin{macrocode} \PassOptionsToPackage { heading = true } { ctexcap } \RequirePackageWithOptions { ctex } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\pkg{ctexhook} 宏包} % % \changes{v2.2}{2015/06/26}{将文档开头和宏包末尾钩子提取到 \pkg{ctexhook} 宏包中。} % \changes{v2.5}{2020/04/21}{增加宏包开头钩子。} % \changes{v2.5.4}{2020/08/02}{兼容 \LaTeX \ 2020/10/01 的钩子机制。} % % \begin{macrocode} %<*ctexhook> % \end{macrocode} % % \begin{macro}[int]{\ctex_if_format_at_least:nTF} % 与 \tn{IfFormatAtLeastTF} 同义。 % \begin{macrocode} \cs_new:Npn \ctex_if_format_at_least:nTF { \@ifl@t@r \fmtversion } % \end{macrocode} % \end{macro} % % \changes{v2.5.7}{2021/06/12}{重新应用 \pkg{l3cctab}。} % % \begin{macro}[int]{\ctex_file_input:n,\ctex_push_file:,\ctex_pop_file:} % 输入文件,关闭 \LaTeXiii 语法环境,并设置 |@| 为字母类,利用 \pkg{l3cctab} 实现。 % 我们使用 \cs{file_input:n} 而不是 \LaTeXe \ 的 \tn{input} 或者 \tn{InputIfFileExists} % 载入文件,因此 \LaTeXe \ 的文件钩子都\emph{无效}。 % \begin{macrocode} \cs_new_protected:Npn \ctex_file_input:n #1 { \ctex_push_file: \file_input:n {#1} \ctex_pop_file: } \bool_if_exist:NTF \l__kernel_expl_bool { \cs_new_protected:Npn \ctex_push_file: { \seq_gpush:Nx \g_@@_expl_status_seq { \bool_if:NTF \l__kernel_expl_bool { 1 } { 0 } } \bool_set_false:N \l__kernel_expl_bool \cctab_begin:N \c_@@_package_cctab } \cs_new_protected:Npn \ctex_pop_file: { \cctab_end: \seq_gpop:NN \g_@@_expl_status_seq \l_@@_expl_status_tl \int_if_odd:nTF { \l_@@_expl_status_tl } { \bool_set_true:N \l__kernel_expl_bool } { \bool_set_false:N \l__kernel_expl_bool } } \tl_new:N \l_@@_expl_status_tl \seq_new:N \g_@@_expl_status_seq } { \cs_new_protected:Npn \ctex_push_file: { \cctab_begin:N \c_@@_package_cctab } \cs_new_protected:Npn \ctex_pop_file: { \cctab_end: } } \cctab_const:Nn \c_@@_package_cctab { \cctab_select:N \c_document_cctab \char_set_catcode_letter:n { 64 } } % \end{macrocode} % \end{macro} % % \changes{v2.5.7}{2021/06/09}{使用 \tn{disable@package@load} 禁止宏包载入。} % % \begin{macro}[int]{\ctex_disable_package:n} % 禁止宏包载入。采用 \LaTeX \ 2020-10-01 提供的 \tn{disable@package@load} 实现, % 否则采用传统方式:预定义 |\ver@|\meta{package}|.sty| 标识符。 % \begin{macrocode} \cs_new_protected:Npn \ctex_disable_package:n #1 { \@ifpackageloaded {#1} { \msg_error:nnee } { \@@_disable_package_aux:nnnn } { ctexhook } { disable-package } {#1} { \@currname } } \cs_new_protected:Npn \@@_disable_package_aux:nnnn #1#2#3#4 { \cs_if_exist:NTF \disable@package@load { \exp_args:Nne \disable@package@load {#3} { \msg_warning:nnnn {#1} {#2} {#3} {#4} } } { \tl_const:cn { ver@ #3 . \@pkgextension } { 9999/99/99 } } } \msg_new:nnn { ctexhook } { disable-package } { Package~`#1'~can~not~be~loaded~with~`#2'. } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_replace_package:nn} % 替换宏包。采用 \LaTeX \ 2020-10-01 提供的 \tn{declare@file@substitution} 实现, % 否则给出无效警告。 % \begin{macrocode} \ctex_if_format_at_least:nTF { 2020/10/01 } { \cs_new_protected:Npn \ctex_replace_package:nn #1#2 { \declare@file@substitution { #1 . \@pkgextension } { #2 . \@pkgextension } } } { \cs_new_protected:Npn \ctex_replace_package:nn { \msg_warning:nnnn { ctexhook } { replace-package-invalid } } \msg_new:nnn { ctexhook } { replace-package-invalid } { \token_to_str:N \ctex_replace_package:nn \{#1\}\{#2\}~is~invalid~ before~LaTeX~2020-10-01. } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_at_begin_package:nn} % 如果宏包已经被载入,则钩子无效,给出警告。 % \begin{macrocode} \cs_new_protected:Npn \ctex_at_begin_package:nn #1 { \@ifpackageloaded {#1} { \@@_package_loaded_warning:nn {#1} } { \ctex_gadd_package_hook:nnn { before } {#1} } } \cs_new_protected:Npn \@@_package_loaded_warning:nn #1#2 { \msg_warning:nne { ctexhook } { invalid-hook } {#1} } \msg_new:nnn { ctexhook } { invalid-hook } { Package~`#1'~is~loaded. \\ \token_to_str:N \ctex_at_begin_package:nn \{#1\}\{...\}~is~invalid. } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_at_end_package:nn} % 与 \pkg{filehook} 的 \tn{AtEndOfPackageFile*} 类似,如果原来没有在载入宏包则 % 在宏包末尾执行语句,否则立即执行。 % \begin{macrocode} \cs_new_protected:Npn \ctex_at_end_package:nn #1 { \@ifpackageloaded {#1} { \use:n } { \ctex_gadd_package_hook:nnn { after } {#1} } } % \end{macrocode} % \end{macro} % % \changes{v2.5.6}{2021/02/16}{使用正确的导言区末尾钩子。} % \changes{v2.5.8}{2021/11/18}{兼容 \LaTeX \ 2021/11/15。} % % \LaTeX \ 2020/10/01 开始提供常用钩子管理机制。在新机制下,我们只需要做简单的包装。 % \begin{macrocode} \ctex_if_format_at_least:nTF { 2020/10/01 } { \cs_new_protected:Npx \ctex_gadd_ltxhook:nn #1 { \hook_gput_code:nnn {#1} { \c_novalue_tl } } \cs_new_protected:Npn \ctex_at_end_preamble:n { \ctex_gadd_ltxhook:nn { begindocument/before } } \cs_new_protected:Npn \ctex_after_end_preamble:n { \ctex_gadd_ltxhook:nn { begindocument/end } } \cs_new_protected:Npx \ctex_gadd_package_hook:nnn #1#2 { \ctex_if_format_at_least:nTF { 2021/11/15 } { \ctex_gadd_ltxhook:nn { package/#2/#1 } } { \ctex_gadd_ltxhook:nn { package/#1/#2 } } } \file_input_stop: } { } % \end{macrocode} % % 对于 \LaTeX \ 2020/10/01 之前的版本,需要自行补丁。 % % \begin{macro}[int]{\ctex_at_end_preamble:n,\ctex_after_end_preamble:n} % 实现 \pkg{etoolbox} 宏包的 \tn{AtEndPreamble} 和 \tn{AfterEndPreamble}。 % \begin{macrocode} \cs_new_protected:Npn \ctex_at_end_preamble:n { \tl_gput_right:Nn \g_@@_end_preamble_hook_tl } \cs_new_protected:Npn \ctex_after_end_preamble:n { \tl_gput_right:Nn \g_@@_after_end_preamble_hook_tl } \cs_new_protected:Npn \CTEX@document@left@hook { \group_end: \g_@@_end_preamble_hook_tl \group_begin: } \cs_new_protected:Npn \CTEX@document@right@hook { \scan_stop: \g_@@_after_end_preamble_hook_tl \tex_ignorespaces:D } \cs_set_nopar:Npx \document { \CTEX@document@left@hook \exp_not:o { \document } \CTEX@document@right@hook } \tl_new:N \g_@@_end_preamble_hook_tl \tl_new:N \g_@@_after_end_preamble_hook_tl % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_gadd_package_hook:nnn, % \ctex_gadd_hook:Nn, \ctex_gadd_hook:cn} % 给钩子附加内容。 % \begin{macrocode} \cs_new_protected:Npn \ctex_gadd_package_hook:nnn #1#2 { \ctex_gadd_hook:cn { g_@@_at_ #1 _ #2 _hook_tl } } \cs_new_protected:Npn \ctex_gadd_hook:Nn #1 { \tl_if_exist:NF #1 { \tl_new:N #1 } \tl_gput_right:Nn #1 } \cs_generate_variant:Nn \ctex_gadd_hook:Nn { c } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_use_package_hook:nn} % 宏包钩子,只执行一次,用后清除。 % \begin{macrocode} \cs_new_protected:Npn \ctex_use_package_hook:nn #1#2 { \group_begin: \exp_args:NNc \group_end: \@@_use_package_hook_aux:N { g_@@_at_ #1 _ #2 _hook_tl } } \cs_new_protected:Npn \@@_use_package_hook_aux:N #1 { \cs_if_exist_use:NT #1 { \cs_undefine:N #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@reset@ptions,\CTEX@reset@ptions@hook} % \tn{@pushfilename} 内部的 \tn{@currname} 和 \tn{@currext} 保存的是 % 前一个宏包的状态,不能使用。需要对其后的 \tn{@reset@ptions} 做补丁来实现 % \cs{ctex_at_begin_package:nn} 的功能。 % \begin{macrocode} \tl_put_right:Nn \@reset@ptions { \CTEX@reset@ptions@hook } \cs_new_protected:Npn \CTEX@reset@ptions@hook { \cs_if_eq:NNT \@currext \@pkgextension { \ctex_use_package_hook:nn { before } { \@currname } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@popfilename,\CTEX@popfilename@hook} % 对 \tn{@popfilename} 做补丁来实现 \cs{ctex_at_end_package:nn} 的功能。 % \begin{macrocode} \tl_put_left:Nn \@popfilename { \CTEX@popfilename@hook } \cs_new_protected:Npn \CTEX@popfilename@hook { \cs_if_eq:NNT \@currext \@pkgextension { \ctex_use_package_hook:nn { after } { \@currname } } } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\pkg{ctexpatch} 宏包} % % \changes{v2.2}{2015/06/23}{新增子宏包 \pkg{ctexpatch} 实现给宏打补丁功能。} % % \begin{macrocode} %<*ctexpatch> % \end{macrocode} % % \begin{macro}[int]{\ctex_patch_cmd_once:NnnnTF} % 只进行第一次匹配进行替换。参数 |#2| 是宏重建时的 \tn{catcode} 设置。 % \begin{macrocode} \cs_new_protected:Npn \ctex_patch_cmd_once:NnnnTF #1#2 { \ctex_patch_boot:NNnnTF \@@_patch_cmd:Nnnnnw #1 { once } {#2} { \use_i:nn } { \use_ii:nn } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_patch_cmd_all:NnnnTF} % 替换所有匹配到的文本。 % \begin{macrocode} \cs_new_protected:Npn \ctex_patch_cmd_all:NnnnTF #1#2 { \ctex_patch_boot:NNnnTF \@@_patch_cmd:Nnnnnw #1 { all } {#2} { \use_i:nn } { \use_ii:nn } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_patch_cmd:Nnn} % 快捷方式,在补丁的时候关闭 \LaTeXiii{} 语法和设置 |@| 为字母类,补丁失败时给出警告。 % \begin{macrocode} \cs_new_protected:Npn \ctex_patch_cmd:Nnn #1 { \ctex_patch_boot:NNnnTF \@@_patch_cmd:Nnnnnw #1 { once } { \ExplSyntaxOff \char_set_catcode_letter:n { 64 } } { } { \ctex_patch_failure:N #1 } } \cs_new_protected:Npn \ctex_patch_failure:N #1 { \msg_warning:nne { ctexpatch } { patch-failure } { \token_to_str:N #1 } } \msg_new:nnn { ctexpatch } { patch-failure } { Oops!~Command~`#1'~is~NOT~patchable.\\ } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_preto_cmd:NnnTF} % 在宏的原本定义前面增加钩子。 % \begin{macrocode} \cs_new_protected:Npn \ctex_preto_cmd:NnnTF #1#2 { \ctex_patch_boot:NNnnTF \@@_hookto_cmd:Nnnnw #1 { left } {#2} { \use_i:nn } { \use_ii:nn } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_appto_cmd:NnnTF} % 在宏的原本定义后面追加钩子。 % \begin{macrocode} \cs_new_protected:Npn \ctex_appto_cmd:NnnTF #1#2 { \ctex_patch_boot:NNnnTF \@@_hookto_cmd:Nnnnw #1 { right } {#2} { \use_i:nn } { \use_ii:nn } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_patch_boot:NNnnTF} % 参数记号 |#| 作为宏的参数被读入时,总是会双写,会影响随后的字符串替换。需要先 % 将它转换为普通符号。 % \begin{macrocode} \cs_new_protected:Npn \ctex_patch_boot:NNnnTF #1#2#3#4#5#6 { \cs_set_protected:Npx \@@_patch_true:w { \exp_not:n {#5} } \cs_set_protected:Npx \@@_patch_false:w { \exp_not:n {#6} } \group_begin: \char_set_catcode_other:n { 35 } \ctex_parse_name:NN #1 #2 {#3} {#4} } \cs_new_eq:NN \@@_patch_true:w \use_i:nn \cs_new_eq:NN \@@_patch_false:w \use_ii:nn % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\ctex_parse_name:NN} % \changes{v2.4}{2016/04/11}{修复宏名解析错误。} % 用 \tn{DeclareRobustCommand} 定义的宏或者由 \tn{newcommand} 或 \tn{newrobustcmd} % 定义的带一个可选参数的宏第一次展开的结果都不是其实际定义,实际定义被保存在另外的 % 宏中。由这些命令定义的宏的第一次展开结果可以有下面的形式(细节可查阅 \pkg{xpatch} % 的文档): % \begin{verbatim}[numbers=left,gobble=4] % \protect␣\xaa␣␣ % \DeclareRobustCommand\xaa[1]{...} % \protect␣\xab␣␣ % \DeclareRobustCommand\xab[1][]{...} % \@protected@testopt␣\xac␣\\xac␣{} % \newcommand\xac[1][]{...} % \@testopt␣\\xad␣{} % \newrobustcmd\xad[1][]{...} % \x@protect␣\1\protect␣\1␣␣ % \DeclareRobustCommand\1[1]{...} % \x@protect␣\2\protect␣\2␣␣ % \DeclareRobustCommand\2[1][]{...} % \@protected@testopt␣\3\\3␣{} % \newcommand\3[1][]{...} % \@testopt␣\\4␣{} % \newrobustcmd\4[1][]{...} % \end{verbatim} % \pkg{ctexpatch} 的主要原理是先对宏的 \tn{meaning} 作字符串替换,然后再用 % \tn{scantokens} 来重建它。我们希望对宏的实际定义打补丁,为此需要先得到 % 对应的名字。\pkg{letltxmacro}、\pkg{show2e} 和 \pkg{xpatch} 宏包中都有 % 类似的工作。 % \begin{macrocode} \cs_new_protected:Npn \ctex_parse_name:NN #1#2 { \ctex_parse_name:NNe #1#2 { \cs_to_str:N #2 } } \group_begin: \cs_set_protected:Npn \@@_tmp:w #1#2#3 { \cs_new_protected:Npn \ctex_parse_name:NNn ##1##2##3 { \bool_lazy_or:nnTF { \cs_if_exist_p:c { ##3 ~ } } { \cs_if_exist_p:c { #1##3 } } { \group_begin: \use:e { \group_end: \@@_parse_name:nNNNnN { \cs_replacement_spec:N ##2 } \exp_not:N ##2 \exp_not:c { ##3 ~ } \exp_not:c { #1##3 } } { ##3 } ##1 } { ##1##2 } } \cs_new_protected:Npn \@@_parse_name:nNNNnN ##1##2##3##4##5##6 { \exp_args:Nc ##6 { \str_case:nnTF {##1} { { \protect ##3 } { } { \x@protect ##2 \protect ##3 } { } } { \str_if_eq:eeTF { \exp_not:n { #1@protected@ ##3 #1##3 } } { \exp_last_unbraced:Ne \@@_parse_name:w { \cs_replacement_spec:N ##3 } #3 ~ #2 \q_stop } { #1##5 ~ } { ##5 ~ } } { \str_case:onTF { \@@_parse_name:w ##1 #3 ~ #2 \q_stop } { { #1@protected@ ##2 ##4 } { } { #1@ ##4 } { } } { #1##5 } {##5} } } } \cs_new:Npn \@@_parse_name:w ##1 #3 ~ ##2 #2 ##3 \q_stop { ##1##2 } } \use:e { \@@_tmp:w { \c_backslash_str } { \c_left_brace_str } { \tl_to_str:n { testopt } } } \group_end: \cs_generate_variant:Nn \ctex_parse_name:NNn { NNe } % \end{macrocode} % \end{macro} % % \begin{variable}{\l_@@_prefix_str,\l_@@_parameter_str,\l_@@_replacement_str} % 分别保存宏的 \tn{meaning} 中的前缀、参数文本和替换文本。 % \begin{macrocode} \str_new:N \l_@@_prefix_str \str_new:N \l_@@_parameter_str \str_new:N \l_@@_replacement_str % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\ctex_get_macro_meaning:NTF} % \begin{macro}{\@@_get_macro_meaning:w} % 解构待补丁宏的 \tn{meaning}。若命令不是宏,则走向 |false| 分支。 % \begin{macrocode} \group_begin: \cs_set_protected:Npn \@@_tmp:w #1 { \prg_new_protected_conditional:Npnn \ctex_get_macro_meaning:N ##1 { TF } { \exp_after:wN \@@_get_macro_meaning:w \token_to_meaning:N ##1 \q_mark #1 -> \q_mark \q_stop } \cs_new_protected:Npn \@@_get_macro_meaning:w ##1 #1 ##2 -> ##3 \q_mark ##4 \q_stop { \tl_if_empty:nTF { ##4 } { \prg_return_false: } { \str_set:Nn \l_@@_prefix_str { ##1 } \str_set:Nn \l_@@_parameter_str { ##2 } \str_set:Nn \l_@@_replacement_str { ##3 } \prg_return_true: } } } \exp_args:No \@@_tmp:w { \tl_to_str:n { macro: } } \group_end: % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int]{\ctex_if_rescanable:NnTF} % 检查宏是否可以重建。 % \begin{macrocode} \cs_new_protected:Npn \ctex_if_rescanable:NnTF #1#2#3#4 { \ctex_get_macro_meaning:NTF #1 { \@@_patch_rebuild:Nn \@@_rebuild_cmd:w {#2} \cs_if_eq:NNTF #1 \@@_rebuild_cmd:w {#3} {#4} } {#4} } \cs_new_eq:NN \@@_rebuild_cmd:w \prg_do_nothing: % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_patch_rebuild:Nn} % 使用 \cs{tl_rescan:nn} 来重新记号化 \tn{meaning} 字符串。 % \begin{macrocode} \cs_new_protected:Npn \@@_patch_rebuild:Nn #1#2 { \@@_patch_rescan:NNn \l_@@_prefix_tl \l_@@_prefix_str {#2} \@@_patch_rescan:NNn \l_@@_parameter_tl \l_@@_parameter_str {#2} \@@_patch_rescan:NNn \l_@@_replacement_tl \l_@@_replacement_str {#2} \use:e { \exp_not:o { \l_@@_prefix_tl } \tex_def:D \exp_not:N #1 \exp_not:o { \l_@@_parameter_tl } { \exp_not:o { \l_@@_replacement_tl } } } } \cs_new_protected:Npn \@@_patch_rescan:NNn #1#2#3 { \str_if_empty:NTF #2 { \tl_clear:N #1 } { \tl_set_rescan:Nno #1 {#3} {#2} } } \tl_new:N \l_@@_prefix_tl \tl_new:N \l_@@_parameter_tl \tl_new:N \l_@@_replacement_tl % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_patch_cmd:Nnnnnw} % 对宏的替换文本进行字符串替换,然后重建。 % \begin{macrocode} \cs_new_protected:Npn \@@_patch_cmd:Nnnnnw #1#2#3#4#5 { \group_end: \ctex_if_rescanable:NnTF #1 {#3} { \use:e { \@@_patch_replace:nnnTF {#2} { \tl_to_str:n {#4} } { \tl_to_str:n {#5} } } { \@@_patch_rebuild:Nn #1 {#3} \@@_patch_true:w } { \@@_patch_false:w } } { \@@_patch_false:w } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_patch_replace:nnnTF} % 替换前先检查原文本是否存在。 % \begin{macrocode} \cs_new_protected:Npn \@@_patch_replace:nnnTF #1#2#3#4 { \tl_if_in:NnTF \l_@@_replacement_str {#2} { \use:c { tl_replace_ #1 :Nnn } \l_@@_replacement_str {#2} {#3} #4 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_hookto_cmd:Nnnnw} % 在宏的前/后附加钩子。 % \begin{macrocode} \cs_new_protected:Npn \@@_hookto_cmd:Nnnnw #1#2#3#4 { \group_end: \ctex_get_macro_meaning:NTF #1 { \str_if_empty:NTF \l_@@_parameter_str { \@@_hookto_cmd_parameterless:Nnnnw } { \@@_hookto_cmd_parameter:Nnnnw } #1 {#2} {#3} {#4} } { \@@_patch_false:w } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_hookto_cmd_parameterless:Nnnnw} % 如果宏没有参数,可以直接进行附加操作。注意保持宏的前缀。 % \begin{macrocode} \cs_new_protected:Npn \@@_hookto_cmd_parameterless:Nnnnw #1#2#3#4 { \str_if_empty:NF \l_@@_prefix_str { \tl_rescan:no {#3} { \l_@@_prefix_str } } \tex_edef:D #1 { \use:c { @@_ #2 _hook_aux:nn } { \exp_not:o {#1} } { \exp_not:n {#4} } } \@@_patch_true:w } \cs_generate_variant:Nn \tl_rescan:nn { no } \cs_new:Npn \@@_left_hook_aux:nn #1#2 { #2#1 } \cs_new_eq:NN \@@_right_hook_aux:nn \use:nn % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_hookto_cmd_parameter:Nnnnw} % 如果宏有参数,需要在字符串中进行附加,然后再重建。 % \begin{macrocode} \cs_new_protected:Npn \@@_hookto_cmd_parameter:Nnnnw #1#2#3#4 { \@@_patch_rebuild:Nn \@@_rebuild_cmd:w {#3} \cs_if_eq:NNTF #1 \@@_rebuild_cmd:w { \use:c { str_put_ #2 :Nn } \l_@@_replacement_str {#4} \@@_patch_rebuild:Nn #1 {#3} \@@_patch_true:w } { \@@_patch_false:w } } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} %<@@=> % \end{macrocode} % % \end{implementation}