% \iffalse meta-comment
%
% Copyright (C) 2016-2018 by Zeping Lee <zepinglee AT gmail.com>
%
% This file may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% of this license or (at your option) any later version.
% The latest version of this license is in
%    https://www.latex-project.org/lppl.txt
% and version 1.3c or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
%<*internal>
\iffalse
\fi
\begingroup
    \def\nameoflatex{LaTeX2e}
\expandafter\endgroup\ifx\nameoflatex\fmtname\else
\csname fi\endcsname
%</internal>
%<*install>
\input docstrip.tex
\preamble

Copyright (C) 2016-\the\year by Zeping Lee <zepinglee AT gmail.com>

This file may be distributed and/or modified under the
conditions of the LaTeX Project Public License, either version 1.3c
of this license or (at your option) any later version.
The latest version of this license is in
   https://www.latex-project.org/lppl.txt
and version 1.3c or later is part of all distributions of LaTeX
version 2005/12/01 or later.

\endpreamble
\keepsilent
\askforoverwritefalse
\nopostamble
\generate{
  \file{\jobname.sty}{\from{\jobname.dtx}{package}}
  \file{\jobname-plain.bst}{\from{\jobname.dtx}{authoryear}}
  \file{\jobname-unsrt.bst}{\from{\jobname.dtx}{numerical}}
}
\endbatchfile
%</install>
%<*internal>
\fi
%</internal>
%<*driver>
\ProvidesFile{\jobname.dtx}
%</driver>
%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%<package>\ProvidesPackage{gbt7714}
%<*package>
  [2018/05/12 v1.0.7 GB/T 7714-2015 BibTeX Style]
%</package>
%
%<*driver>
\documentclass[a4paper]{ltxdoc}
\usepackage[paper=a4paper,margin=1in]{geometry}
\usepackage{hypdoc}
\hypersetup{allcolors=blue}
\usepackage[UTF8]{ctex}
\IfFileExists{/System/Library/Fonts/Times.ttc}{%
  \setmainfont{Times}
  \setsansfont[Scale=MatchLowercase]{Helvetica}
  \setmonofont[Scale=MatchLowercase]{Menlo}
}{\relax}
\usepackage{booktabs}
\usepackage{listings}
\lstnewenvironment{latex}{%
  \lstset{%
    basicstyle = \small\ttfamily,
    language = [LaTeX]TeX,
    gobble = 2,
    frame = single,}}{}
\lstnewenvironment{pseudocode}{%
  \lstset{%
    basicstyle=\ttfamily,
    language=bash,
    gobble=2,
    frame=single,}}{}
\DeclareRobustCommand\file{\nolinkurl}
\DeclareRobustCommand\env{\texttt}
\DeclareRobustCommand\pkg{\textsf}
\DeclareRobustCommand\cls{\textsf}
\DeclareRobustCommand\opt{\texttt}
\renewcommand\glossaryname{版本历史}
\GlossaryPrologue{\section*{\glossaryname}}
\renewcommand\indexname{命令索引}
\makeatletter
\renewcommand*{\changes@}[3]{%
  \protected@edef\@tempa{%
    \noexpand\glossary{#1 (#2)\levelchar
      \ifx\saved@macroname\@empty
        \space\actualchar\generalname
      \else
        \expandafter\@gobble\saved@macroname
        \actualchar
        \string\verb\quotechar*\verbatimchar\saved@macroname\verbatimchar
      \fi
      :\levelchar #3}}%
  \@tempa
  \endgroup
  \@esphack
}
\makeatother
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\OnlyDescription

\begin{document}
  \DocInput{\jobname.dtx}
  \PrintChanges
  \PrintIndex
\end{document}
%</driver>
% \fi
%
% \CheckSum{0}
%
% \CharacterTable
%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%   Digits        \0\1\2\3\4\5\6\7\8\9
%   Exclamation   \!     Double quote  \"     Hash (number) \#
%   Dollar        \$     Percent       \%     Ampersand     \&
%   Acute accent  \'     Left paren    \(     Right paren   \)
%   Asterisk      \*     Plus          \+     Comma         \,
%   Minus         \-     Point         \.     Solidus       \/
%   Colon         \:     Semicolon     \;     Less than     \<
%   Equals        \=     Greater than  \>     Question mark \?
%   Commercial at \@     Left bracket  \[     Backslash     \\
%   Right bracket \]     Circumflex    \^     Underscore    \_
%   Grave accent  \`     Left brace    \{     Vertical bar  \|
%   Right brace   \}     Tilde         \~}
%
%
%
% \GetFileInfo{\jobname.dtx}
%
% \title{GB/T 7714-2015 \BibTeX{} style}
% \author{Zeping Lee\thanks{zepinglee AT gmail.com}}
% \date{\filedate\qquad\fileversion}
% \maketitle
%
% \changes{v1.0}{2018/01/01}{Initial release.}
% \changes{v1.0.1}{2018/03/09}{著者出版年制的文献引用不再排序}
% \changes{v1.0.2}{2017/03/16}{正确识别姓名中的“others”}
% \changes{v1.0.3}{2018/03/29}{顺序编码制连续两个文献引用之间使用连接号}
% \changes{v1.0.4}{2018/04/12}{页码的连接号由 en dash 改为 hyphen}
% \changes{v1.0.5}{2018/04/18}{允许著录多个 DOI}
% \changes{v1.0.6}{2018/05/10}{不再处理中文标题的英文单词的大小写}
% \changes{v1.0.6}{2018/05/10}{文献列表的数字标签左对齐}
% \changes{v1.0.7}{2018/05/12}{修正了检测 Unicode 语言}
%
%
% \section{简介}
%
% 《GB/T 7714-2015 信息与文献\ 参考文献著录规则》（以下简称《规则》）是我国关于
% 参考文献的推荐标准。
% 本宏包是《规则》的 \BibTeX{} 实现，具有以下特性：
% \begin{itemize}
%   \item 兼容 \pkg{natbib}
%   \item 支持了顺序编码制和著者-出版年制两种风格
%   \item 自动识别语言并进行相应处理
% \end{itemize}
%
%
% \section{使用方法}
%
% 首先应在导言区调用宏包 \pkg{gbt7714} ，可以选择的参数如
% 表~\ref{tab:options}。
%
% \begin{table}[htbp]
%   \centering
%   \begin{tabular}{lll}
%     \toprule
%       参数                 & 参考文献列表 & 引用标注    \\
%     \midrule
%       \opt{super} （默认） & 顺序编码     & 角标数字    \\
%       \opt{numbers}        & 顺序编码     & 数字        \\
%       \opt{authoryear}     & 著者-出版年  & 著者-出版年 \\
%     \bottomrule
%   \end{tabular}
%   \caption{参考文献风格的参数}
%   \label{tab:options}
% \end{table}
%
% 举个例子：
% \begin{latex}
% \usepackage[authoryear]{gbt7714}
% \end{latex}
% 然后\emph{不再}需要调用 \cs{bibliographystyle} 命令设置参考文献列表风格。
%
% 使用时需要注意以下两点：
% \begin{itemize}
%   \item 使用的 bib 数据库应使用 UTF-8 编码。
%   \item 使用著者-出版年制参考文献表时，中文文献需要在 key 域填写作者姓名
%         的拼音，才能使得文献列表按照拼音排序。
% \end{itemize}
%
%
% \section{文献类型}
%
% 《规则》中列举了 16 种参考文献类型，
% 表~\ref{tab:entry-types} 列举了 \file{bib} 数据库中对应的文献类型。
% 这些都尽可能地兼容 \BibTeX{} 的标准类型，但是新增了若干文献类型（带星号的）。
%
% \begin{table}[htbp]
%   \centering
%   \begin{tabular}{lcl}
%     \toprule
%     文献类型         & 标识代码 & Entry Type \\
%     \midrule
%     普通图书         & M        & book 或 inbook \\
%     图书的析出文献   & M        & incollection \\
%     会议录           & C        & proceedings \\
%     会议录的析出文献 & C        & inproceedings 或 conference \\
%     汇编             & G        & collection* \\
%     报纸             & N        & newspaper* \\
%     期刊的析出文献   & J        & article \\
%     学位论文         & D        & mastersthesis 或 phdthesis \\
%     报告             & R        & techreport \\
%     标准             & S        & standard* \\
%     专利             & P        & patent* \\
%     数据库           & DB       & database* \\
%     计算机程序       & CP       & software* \\
%     电子公告         & EB       & online* \\
%     档案             & A        & archive* \\
%     舆图             & CM       & map* \\
%     数据集           & DS       & dataset* \\
%     其他             & Z        & misc \\
%     \bottomrule
%   \end{tabular}
%   \caption{全部文献类型，其中带星号的不是 \BibTeX{} 标准类型}
%   \label{tab:entry-types}
% \end{table}
%
% 注意，不支持的 \BibTeX{} 标准类型有 booklet, manual, unpublished。
%
%
% \section{著录项目}
%
% 表~\ref{tab:fields} 列出了本宏包支持的全部著录项目。
%
% \begin{table}[htbp]
%   \centering
%   \begin{tabular}{ll}
%     \toprule
%     著录项目（域）         & Field \\
%     \midrule
%     主要责任者             & author \\
%     题名                   & title \\
%     文献类型标识           & mark* \\
%     载体类型标识           & medium* \\
%     译者                   & translator* \\
%     编辑                   & editor \\
%     组织（用于会议）       & organization \\
%     图书题名               & booktitle \\
%     系列                   & series \\
%     期刊题名               & journal \\
%     版本                   & edition \\
%     出版地                 & address \\
%     出版者                 & publisher \\
%     学校（用于phdthesis）  & school \\
%     机构（用于techreport） & institution \\
%     出版年                 & year \\
%     卷                     & volume \\
%     期（或者专利号）       & number \\
%     引文页码               & pages \\
%     更新或修改日期         & date* \\
%     引用日期               & urldate* \\
%     获取和访问路径         & url \\
%     数字对象唯一标识符     & doi \\
%     语言                   & language* \\
%     拼音（用于排序）       & key \\
%     \bottomrule
%   \end{tabular}
%   \caption{全部著录项目，其中带星号的不是 \BibTeX{}/natbib 的标准著录项目。
%     不支持的 \BibTeX{} 标准著录项目有 annote, chapter, crossref,
%     howpublished, month, note, type。}
%   \label{tab:fields}
% \end{table}
%
% 由于《规则》中要求的著录项目比 \BibTeX{} 支持的要多，必须新增一些著录项目，
% 这些新增的类型在设计时参考了 BibLaTeX，如 date 和 urldate。
%
% 本宏包默认情况下可以自动识别文献语言，并自动处理文献类型和载体类型标识，
% 但是在少数情况下需要用户手动指定，如：
% \begin{latex}
% @article{citekey,
%   language = {japanese},
%   mark     = {M},
%   medium   = {CD},
%   ...
% \end{latex}
% 可选的语言有 english, chinese, japanese, russian，
% 目前对日语和俄语的支持较为有限。
%
%
% \section{参考文献列表的排序}
%
% 在使用著者-出版年制时，需要将中文参考文献按照拼音或者笔画排序，然而 Unicode
% 中汉字是根据康熙字典排序的，而且由于 \BibTeX{} 功能的局限性，我们无法根据著
% 者姓名读取拼音，所以只能手动处理：在 key 域手动添加著者姓名拼音，
% 如：
% \begin{latex}
% @book{capital,
%   author = {马克思 and 恩格斯},
%   key    = {ma3 ke4 si1   en1 ge2 si1},
%   ...
% \end{latex}
%
%
%
% \StopEventually{}
%
% \section{Package}
%
% 下面声明和处理宏包的选项，有 \opt{authoryear} 和 \opt{numbers}。
%    \begin{macrocode}
%<*package>
\newif\if@gbt@numerical
\newif\if@gbt@super
\DeclareOption{super}{\@gbt@numericaltrue\@gbt@supertrue}
\DeclareOption{numbers}{\@gbt@numericaltrue\@gbt@superfalse}
\DeclareOption{authoryear}{\@gbt@numericalfalse}
\DeclareOption*{\PassOptionsToPackage{\CurrentOption}{natbib}}
\ExecuteOptions{super}
\ProcessOptions\relax
%    \end{macrocode}
%
% 只在顺序编码时使用 \opt{sort\&compress}。
%    \begin{macrocode}
\if@gbt@numerical
  \PassOptionsToPackage{sort&compress}{natbib}
\fi
\RequirePackage{natbib}
%    \end{macrocode}
%
% 定义接口切换引用文献的标注法，可用 \cs{citestyle} 调用 \opt{numerical}
% 或 \opt{authoryear}，参见 \pkg{natbib}。
%    \begin{macrocode}
\newcommand\bibstyle@super{\bibpunct{[}{]}{,}{s}{,}{\textsuperscript{,}}}
\newcommand\bibstyle@numbers{\bibpunct{[}{]}{,}{n}{,}{,}}
\newcommand\bibstyle@authoryear{\bibpunct{(}{)}{;}{a}{,}{,}}
%    \end{macrocode}
%
% \begin{macro}{\gbtbibstyle}
% 定义接口切换参考文献表的风格，可选 \opt{authoryear} 和 \opt{numerical}，
% 这个仅用于\pkg{chapterbib}。
%    \begin{macrocode}
\def\tmp@numerical{numerical}
\def\tmp@authoryear{authoryear}
\newcommand\gbtbibstyle[1]{%
  \def\tmp@gbt{#1}%
  \ifx\tmp@gbt\tmp@numerical%
    \bibliographystyle{gbt7714-unsrt}%
  \else%
    \ifx\tmp@gbt\tmp@authoryear%
      \bibliographystyle{gbt7714-plain}%
    \else%
      \PackageError{gbt7714}{Unknown argument #1.}
      {It should be `numerical' or `authoryear'.}
    \fi%
  \fi%
}
%    \end{macrocode}
% \end{macro}
%
% 处理宏包选项。
%    \begin{macrocode}
\if@gbt@numerical
  \if@gbt@super
    \citestyle{super}%
    \gbtbibstyle{numerical}%
  \else
    \citestyle{numbers}
    \gbtbibstyle{numerical}%
  \fi
\else
  \citestyle{authoryear}
  \gbtbibstyle{authoryear}%
\fi
%    \end{macrocode}
%
% 下面修改 \pkg{natbib} 的引用格式，主要是将页码写在上标位置。
% Numerical 模式的 \cs{citet} 的页码：
%    \begin{macrocode}
\RequirePackage{etoolbox}
\patchcmd{\NAT@citexnum}{%
  \@ifnum{\NAT@ctype=\z@}{%
    \if*#2*\else\NAT@cmt#2\fi
  }{}%
  \NAT@mbox{\NAT@@close}%
}{%
  \NAT@mbox{\NAT@@close}%
  \@ifnum{\NAT@ctype=\z@}{%
    \if*#2*\else\textsuperscript{#2}\fi
  }{}%
}{}{}
%    \end{macrocode}
%
% Numerical 模式的 \cs{citep} 的页码：
%    \begin{macrocode}
\renewcommand\NAT@citesuper[3]{\ifNAT@swa
  \if*#2*\else#2\NAT@spacechar\fi
\unskip\kern\p@\textsuperscript{\NAT@@open#1\NAT@@close\if*#3*\else#3\fi}%
   \else #1\fi\endgroup}
%    \end{macrocode}
%
% Author-year 模式的 \cs{citet} 的页码：
%    \begin{macrocode}
\patchcmd{\NAT@citex}{%
  \if*#2*\else\NAT@cmt#2\fi
  \if\relax\NAT@date\relax\else\NAT@@close\fi
}{%
  \if\relax\NAT@date\relax\else\NAT@@close\fi
  \if*#2*\else\textsuperscript{#2}\fi
}{}{}
%    \end{macrocode}
%
% Author-year 模式的 \cs{citep} 的页码：
%    \begin{macrocode}
\renewcommand\NAT@cite%
    [3]{\ifNAT@swa\NAT@@open\if*#2*\else#2\NAT@spacechar\fi
        #1\NAT@@close\if*#3*\else\textsuperscript{#3}\fi\else#1\fi\endgroup}
%    \end{macrocode}
%
% 在顺序编码制下，\pkg{natbib} 只有在三个以上连续文献引用才会使用连接号，
% 这里修改为允许两个引用使用连接号。
%    \begin{macrocode}
\patchcmd{\NAT@citexnum}{%
  \ifx\NAT@last@yr\relax
    \def@NAT@last@yr{\@citea}%
  \else
    \def@NAT@last@yr{--\NAT@penalty}%
  \fi
}{%
  \def@NAT@last@yr{-\NAT@penalty}%
}{}{}
%    \end{macrocode}
%
% 参考文献列表的标签左对齐
%    \begin{macrocode}
\renewcommand\@biblabel[1]{[#1]\hfill}
%    \end{macrocode}
%
% 需要 \pkg{url} 宏包显示 URL。
%    \begin{macrocode}
\RequirePackage{url}
%    \end{macrocode}
%
% 增加 URL 可断行的位置。
%    \begin{macrocode}
\g@addto@macro\UrlBreaks{%
  \do\a\do\b\do\c\do\d\do\e\do\f\do\g\do\h\do\i\do\j%
  \do\k\do\l\do\m\do\n\do\o\do\p\do\q\do\r\do\s\do\t%
  \do\u\do\v\do\w\do\x\do\y\do\z%
  \do\A\do\B\do\C\do\D\do\E\do\F\do\G\do\H\do\I\do\J%
  \do\K\do\L\do\M\do\N\do\O\do\P\do\Q\do\R\do\S\do\T%
  \do\U\do\V\do\W\do\X\do\Y\do\Z%
  \do\1\do\2\do\3\do\4\do\5\do\6\do\7\do\8\do\9\do\0%
}
%</package>
%    \end{macrocode}
%
%
% \section{BibTeX style implementation}
%
% \subsection{Fields and entry strings}
%
%    \begin{macrocode}
%<*authoryear|numerical>
ENTRY
  { address
    author
    booktitle
    date
    doi
    edition
    editor
    institution
    journal
    key
    language
    mark
    medium
    number
    organization
    pages
    publisher
    school
    series
    title
    translator
    url
    urldate
    volume
    year
  }
  { entry.lang }
%    \end{macrocode}
%
% These string entry variables are used to form the citation label.
% In a storage pinch, sort.label can be easily computed on the fly.
%
%    \begin{macrocode}
  { label extra.label sort.label short.list entry.mark }

%    \end{macrocode}
%
% \subsection{Entry functions}
% Each entry function starts by calling output.bibitem, to write the
% |\bibitem| and its arguments to the .BBL file.  Then the various fields
% are formatted and printed by output or output.check.  Those functions
% handle the writing of separators (commas, periods, |\newblock|'s),
% taking care not to do so when they are passed a null string.
% Finally, fin.entry is called to add the final period and finish the
% entry.
%
% A bibliographic reference is formatted into a number of `blocks':
% in the open format, a block begins on a new line and subsequent
% lines of the block are indented.  A block may contain more than
% one sentence (well, not a grammatical sentence, but something to
% be ended with a sentence ending period).  The entry functions should
% call new.block whenever a block other than the first is about to be
% started.  They should call new.sentence whenever a new sentence is
% to be started.  The output functions will ensure that if two
% new.sentence's occur without any non-null string being output between
% them then there won't be two periods output.  Similarly for two
% successive new.block's.
%
% The output routines don't write their argument immediately.
% Instead, by convention, that argument is saved on the stack to be
% output next time (when we'll know what separator needs to come
% after it).  Meanwhile, the output routine has to pop the pending
% output off the stack, append any needed separator, and write it.
%
% To tell which separator is needed, we maintain an output.state.
% It will be one of these values:
%       before.all              just after the |\bibitem|
%       mid.sentence            in the middle of a sentence: comma needed
%                                       if more sentence is output
%       after.sentence          just after a sentence: period needed
%       after.block             just after a block (and sentence):
%                                       period and |\newblock| needed.
% Note: These styles don't use after.sentence
%
% VAR: output.state : INTEGER           -- state variable for output
%
% The output.nonnull function saves its argument (assumed to be nonnull)
% on the stack, and writes the old saved value followed by any needed
% separator.  The ordering of the tests is decreasing frequency of
% occurrence.
%
% 由于专著中的析出文献需要用到很特殊的“//”，所以我又加了一个 after.slash。
% 其他需要在特定符号后面输出，所以写了一个 output.after。
%
% \begin{pseudocode}
% output.nonnull(s) ==
%  BEGIN
%       s := argument on stack
%       if output.state = mid.sentence then
%           write$(pop() * ", ")
%                 -- "pop" isn't a function: just use stack top
%       else
%           if output.state = after.block then
%               write$(add.period$(pop()))
%               newline$
%               write$("\newblock ")
%           else
%               if output.state = before.all then
%                   write$(pop())
%               else        -- output.state should be after.sentence
%                   write$(add.period$(pop()) * " ")
%               fi
%           fi
%           output.state := mid.sentence
%       fi
%       push s on stack
%  END
% \end{pseudocode}
%
% The output function calls output.nonnull if its argument is non-empty;
% its argument may be a missing field (thus, not necessarily a string)
%
% \begin{pseudocode}
% output(s) ==
%  BEGIN
%       if not empty$(s) then output.nonnull(s)
%       fi
%  END
% \end{pseudocode}
%
% The output.check function is the same as the output function except that, if
% necessary, output.check warns the user that the t field shouldn't be empty
% (this is because it probably won't be a good reference without the field;
% the entry functions try to make the formatting look reasonable even when
% such fields are empty).
%
% \begin{pseudocode}
% output.check(s,t) ==
%  BEGIN
%       if empty$(s) then
%           warning$("empty " * t * " in " * cite$)
%       else output.nonnull(s)
%       fi
%  END
% \end{pseudocode}
%
% The output.bibitem function writes the |\bibitem| for the current entry
% (the label should already have been set up), and sets up the separator
% state for the output functions.  And, it leaves a string on the stack
% as per the output convention.
%
% \begin{pseudocode}
% output.bibitem ==
%  BEGIN
%       newline$
%       write$("\bibitem[")     % for alphabetic labels,
%       write$(label)           % these three lines
%       write$("]{")            % are used
%       write$("\bibitem{")             % this line for numeric labels
%       write$(cite$)
%       write$("}")
%       push "" on stack
%       output.state := before.all
%  END
% \end{pseudocode}
%
% The fin.entry function finishes off an entry by adding a period to the
% string remaining on the stack.  If the state is still before.all
% then nothing was produced for this entry, so the result will look bad,
% but the user deserves it. (We don't omit the whole entry because the
% entry was cited, and a bibitem is needed to define the citation label.)
%
% \begin{pseudocode}
% fin.entry ==
%  BEGIN
%       write$(add.period$(pop()))
%       newline$
%  END
% \end{pseudocode}
%
% The new.block function prepares for a new block to be output, and
% new.sentence prepares for a new sentence.
%
% \begin{pseudocode}
% new.block ==
%  BEGIN
%       if output.state <> before.all then
%           output.state := after.block
%       fi
%  END
% \end{pseudocode}
%
% \begin{pseudocode}
% new.sentence ==
%  BEGIN
%       if output.state <> after.block then
%           if output.state <> before.all then
%               output.state :=  after.sentence
%           fi
%       fi
%  END
% \end{pseudocode}
%
%    \begin{macrocode}
INTEGERS { output.state before.all mid.sentence after.sentence after.block after.slash }

INTEGERS { lang.zh lang.ja lang.en lang.ru lang.other }

INTEGERS { charptr len }

FUNCTION {init.state.consts}
{ #0 'before.all :=
  #1 'mid.sentence :=
  #2 'after.sentence :=
  #3 'after.block :=
  #4 'after.slash :=
  #3 'lang.zh :=
  #4 'lang.ja :=
  #1 'lang.en :=
  #2 'lang.ru :=
  #0 'lang.other :=
}

%    \end{macrocode}
%
% the variables s and t are temporary string holders
%
%    \begin{macrocode}
STRINGS { s t }

FUNCTION {debug}
{ 's :=
  duplicate$
  "DEBUG: " s * " -> `" *
  swap$ * "'" *
  top$
}

FUNCTION {debug.int}
{ 's :=
  duplicate$ int.to.str$
  "DEBUG: " s * " == " *
  swap$ *
  top$
}

FUNCTION {punct.colon}
{ ": "
}

FUNCTION {punct.slash}
{ "//\allowbreak{}"
}

FUNCTION {punct.space}
{ " "
}

FUNCTION {output.nonnull}
{ 's :=
  output.state mid.sentence =
    { ", " * write$ }
    { output.state after.block =
        { add.period$ write$
          newline$
          "\newblock " write$
        }
        { output.state before.all =
            'write$
            { output.state after.slash =
                { punct.slash * write$ }
                { add.period$ " " * write$ }
              if$
            }
          if$
        }
      if$
      mid.sentence 'output.state :=
    }
  if$
  s
}

FUNCTION {output}
{ duplicate$ empty$
    'pop$
    'output.nonnull
  if$
}

FUNCTION {output.after}
{ 't :=
  duplicate$ empty$
    'pop$
    { 's :=
      output.state mid.sentence =
        { t * write$ }
        { output.state after.block =
            { add.period$ write$
              newline$
              "\newblock " write$
            }
            { output.state before.all =
                'write$
                { output.state after.slash =
                    { punct.slash * write$ }
                    { add.period$ " " * write$ }
                  if$
                }
              if$
            }
          if$
          mid.sentence 'output.state :=
        }
      if$
      s
    }
  if$
}

FUNCTION {output.check}
{ 't :=
  duplicate$ empty$
    { pop$ "empty " t * " in " * cite$ * warning$ }
    'output.nonnull
  if$
}

%    \end{macrocode}
%
% This function finishes all entries.
%
%    \begin{macrocode}
FUNCTION {fin.entry}
{ add.period$
  write$
  newline$
}

FUNCTION {new.block}
{ output.state before.all =
    'skip$
    { output.state after.slash =
        'skip$
        { after.block 'output.state := }
      if$
    }
  if$
}

FUNCTION {new.sentence}
{ output.state after.block =
    'skip$
    { output.state before.all =
        'skip$
        { output.state after.slash =
            'skip$
            { after.sentence 'output.state := }
          if$
        }
      if$
    }
  if$
}

FUNCTION {new.slash}
{ output.state before.all =
    'skip$
    { after.slash 'output.state := }
  if$
}

%    \end{macrocode}
%
% These three functions pop one or two (integer) arguments from the stack
% and push a single one, either 0 or 1.
% The |'skip$| in the `and' and `or' functions are used because
% the corresponding |if$| would be idempotent
%
%    \begin{macrocode}
FUNCTION {not}
{   { #0 }
    { #1 }
  if$
}

FUNCTION {and}
{   'skip$
    { pop$ #0 }
  if$
}

FUNCTION {or}
{   { pop$ #1 }
    'skip$
  if$
}

%    \end{macrocode}
%
% Sometimes we begin a new block only if the block will be big enough.  The
% new.block.checka function issues a new.block if its argument is nonempty;
% new.block.checkb does the same if either of its TWO arguments is nonempty.
%
%    \begin{macrocode}
FUNCTION {new.block.checka}
{ empty$
    'skip$
    'new.block
  if$
}

FUNCTION {new.block.checkb}
{ empty$
  swap$ empty$
  and
    'skip$
    'new.block
  if$
}

%    \end{macrocode}
%
% The new.sentence.check functions are analogous.
%
%    \begin{macrocode}
FUNCTION {new.sentence.checka}
{ empty$
    'skip$
    'new.sentence
  if$
}

FUNCTION {new.sentence.checkb}
{ empty$
  swap$ empty$
  and
    'skip$
    'new.sentence
  if$
}

%    \end{macrocode}
%
% \subsection{Formatting chunks}
% Here are some functions for formatting chunks of an entry.
% By convention they either produce a string that can be followed by
% a comma or period (using |add.period$|, so it is OK to end in a period),
% or they produce the null string.
%
% A useful utility is the field.or.null function, which checks if the
% argument is the result of pushing a `missing' field (one for which no
% assignment was made when the current entry was read in from the database)
% or the result of pushing a string having no non-white-space characters.
% It returns the null string if so, otherwise it returns the field string.
% Its main (but not only) purpose is to guarantee that what's left on the
% stack is a string rather than a missing field.
%
% \begin{pseudocode}
% field.or.null(s) ==
%  BEGIN
%       if empty$(s) then return ""
%       else return s
%  END
% \end{pseudocode}
%
% Another helper function is emphasize, which returns the argument emphazised,
% if that is non-empty, otherwise it returns the null string.  Italic
% corrections aren't used, so this function should be used when punctation
% will follow the result.
%
% \begin{pseudocode}
% emphasize(s) ==
%  BEGIN
%       if empty$(s) then return ""
%       else return "{\em " * s * "}"
% \begin{pseudocode}
%
% The format.names function formats the argument (which should be in
% BibTeX name format) into "First Von Last, Junior", separated by commas
% and with an "and" before the last (but ending with "et~al." if the last
% of multiple authors is "others").  This function's argument should always
% contain at least one name.
%
% VAR: nameptr, namesleft, numnames: INTEGER
% pseudoVAR: nameresult: STRING         (it's what's accumulated on the stack)
%
% \begin{pseudocode}
% format.names(s) ==
%  BEGIN
%       nameptr := 1
%       numnames := num.names$(s)
%       namesleft := numnames
%       while namesleft > 0
%         do
%                               % for full names:
%           t := format.name$(s, nameptr, "{ff~}{vv~}{ll}{, jj}")
%                               % for abbreviated first names:
%           t := format.name$(s, nameptr, "{f.~}{vv~}{ll}{, jj}")
%           if nameptr > 1 then
%               if namesleft > 1 then nameresult := nameresult * ", " * t
%               else if numnames > 2
%                      then nameresult := nameresult * ","
%                    fi
%                    if t = "others"
%                      then nameresult := nameresult * " et~al."
%                      else nameresult := nameresult * " and " * t
%                    fi
%               fi
%           else nameresult := t
%           fi
%           nameptr := nameptr + 1
%           namesleft := namesleft - 1
%         od
%       return nameresult
%  END
% \end{pseudocode}
%
% The format.authors function returns the result of format.names(author)
% if the author is present, or else it returns the null string
%
% \begin{pseudocode}
% format.authors ==
%  BEGIN
%       if empty$(author) then return ""
%       else return format.names(author)
%       fi
%  END
% \end{pseudocode}
%
% Format.editors is like format.authors, but it uses the editor field,
% and appends ", editor" or ", editors"
%
% \begin{pseudocode}
% format.editors ==
%  BEGIN
%       if empty$(editor) then return ""
%       else
%           if num.names$(editor) > 1 then
%               return format.names(editor) * ", editors"
%           else
%               return format.names(editor) * ", editor"
%           fi
%       fi
%  END
% \end{pseudocode}
%
% Other formatting functions are similar, so no "comment version" will be
% given for them.
%
% The `pop\$' in this function gets rid of the duplicate `empty' value and
% the `skip\$' returns the duplicate field value
%
%    \begin{macrocode}
FUNCTION {field.or.null}
{ duplicate$ empty$
    { pop$ "" }
    'skip$
  if$
}

FUNCTION {emphasize}
{ duplicate$ empty$
    { pop$ "" }
    { "\emph{" swap$ * "}" * }
  if$
}

INTEGERS { byte second.byte }

INTEGERS { char.lang tmp.lang }

STRINGS { tmp.str }

FUNCTION {get.str.lang}
{ 'tmp.str :=
  lang.other 'tmp.lang :=
  #1 'charptr :=
  tmp.str text.length$ #1 + 'len :=
    { charptr len < }
    { tmp.str charptr #1 substring$ chr.to.int$ 'byte :=
      byte #128 <
        { charptr #1 + 'charptr :=
          byte #64 > byte #91 < and byte #96 > byte #123 < and or
            { lang.en 'char.lang := }
            { lang.other 'char.lang := }
          if$
        }
        { tmp.str charptr #1 + #1 substring$ chr.to.int$ 'second.byte :=
          byte #224 <
%    \end{macrocode}
% 俄文西里尔字母：U+0400 到 U+052F，对应 UTF-8 从 D0 80 到 D4 AF。
%    \begin{macrocode}
            { charptr #2 + 'charptr :=
              byte #207 > byte #212 < and
              byte #212 = second.byte #176 < and or
                { lang.ru 'char.lang := }
                { lang.other 'char.lang := }
              if$
            }
            { byte #240 <
%    \end{macrocode}
% CJK Unified Ideographs: U+4E00--U+9FFF; UTF-8: E4 B8 80--E9 BF BF.
%    \begin{macrocode}
                { charptr #3 + 'charptr :=
                  byte #227 > byte #234 < and
                    { lang.zh 'char.lang := }
%    \end{macrocode}
% CJK Unified Ideographs Extension A: U+3400--U+4DBF; UTF-8: E3 90 80--E4 B6 BF.
%    \begin{macrocode}
                    { byte #227 =
                        { second.byte #143 >
                            { lang.zh 'char.lang := }
%    \end{macrocode}
% 日语假名：U+3040--U+30FF, UTF-8: E3 81 80--E3 83 BF.
%    \begin{macrocode}
                            { second.byte #128 > second.byte #132 < and
                                { lang.ja 'char.lang := }
                                { lang.other 'char.lang := }
                              if$
                            }
                          if$
                        }
%    \end{macrocode}
% CJK Compatibility Ideographs: U+F900--U+FAFF, UTF-8: EF A4 80--EF AB BF.
%    \begin{macrocode}
                        { byte #239 =
                          second.byte #163 > second.byte #172 < and and
                            { lang.zh 'char.lang := }
                            { lang.other 'char.lang := }
                          if$
                        }
                      if$
                    }
                  if$
                }
%    \end{macrocode}
% CJK Unified Ideographs Extension B--F: U+20000--U+2EBEF,
% UTF-8: F0 A0 80 80--F0 AE AF AF.
% CJK Compatibility Ideographs Supplement: U+2F800--U+2FA1F,
% UTF-8: F0 AF A0 80--F0 AF A8 9F.
%    \begin{macrocode}
                { charptr #4 + 'charptr :=
                  byte #240 = second.byte #159 > and
                    { lang.zh 'char.lang := }
                    { lang.other 'char.lang := }
                  if$
                }
              if$
            }
          if$
        }
      if$
      char.lang tmp.lang >
        { char.lang 'tmp.lang := }
        'skip$
      if$
    }
  while$
  tmp.lang
}

FUNCTION {is.in.chinese}
{ entry.lang lang.zh =
}

FUNCTION {format.et.al}
{ is.in.chinese
    { "等"}
    { "et~al." }
  if$
}

FUNCTION {format.anonymous}
{ is.in.chinese
    { "佚名" }
    { "Anon" }
  if$
}

INTEGERS { nameptr namesleft numnames name.lang }

FUNCTION {format.names}
{ 's :=
  #1 'nameptr :=
  s num.names$ 'numnames :=
  numnames 'namesleft :=
    { namesleft #0 > }
    { s nameptr "{vv~}{ll}{, jj}{, ff}" format.name$ 't :=
      nameptr #4 =
        { format.et.al
          #1 'namesleft :=
        }
        { t "others" =
            { format.et.al }
            { t get.str.lang 'name.lang :=
              name.lang lang.en =
                { t #1 "{vv~}{ll}{ f{~}}" format.name$ "u" change.case$
                  t #1 "{, jj}" format.name$ *
                }
                { t #1 "{ll}{ff}" format.name$ }
              if$
            }
          if$
        }
      if$
      nameptr #1 >
        { ", " swap$ * * }
        'skip$
      if$
      nameptr #1 + 'nameptr :=
      namesleft #1 - 'namesleft :=
    }
  while$
}

FUNCTION {format.key}
{ empty$
    { key field.or.null }
    { "" }
  if$
}

FUNCTION {format.authors}
{ author empty$
%<*authoryear>
    { format.anonymous }
%</authoryear>
%<*numerical>
    { "" }
%</numerical>
    { author format.names }
  if$
}

FUNCTION {format.editors}
{ editor empty$
    { "" }
    { editor format.names }
  if$
}

FUNCTION {format.translators}
{ translator empty$
    { "" }
    { translator format.names
      is.in.chinese
        { translator num.names$ #3 >
            { "译" * }
            { ", 译" * }
          if$
        }
        'skip$
      if$
    }
  if$
}

FUNCTION {format.url}
{ url empty$
    { "" }
    { new.block "\url{" url * "}" * }
  if$
}

%    \end{macrocode}
%
% 需要检测 DOI 是否已经包含在 URL 中。
%
%    \begin{macrocode}
FUNCTION {is.doi.in.url}
{ 's :=
  s empty$
    { #1 }
    { url empty$
        { #0 }
        { s text.length$ 'len :=
          url text.length$ 'charptr :=
            { url charptr len substring$ s = not
              charptr #0 >
              and
            }
            { charptr #1 - 'charptr := }
          while$
          charptr
        }
      if$
    }
  if$
}

FUNCTION {format.doi}
{ ""
  doi empty$
    'skip$
    { "" 's :=
      doi 't :=
      #0 'numnames :=
        { t empty$ not}
        { t #1 #1 substring$ 'tmp.str :=
          tmp.str "," = tmp.str " " = or t #2 #1 substring$ empty$ or
            { t #2 #1 substring$ empty$
                { s tmp.str * 's := }
                'skip$
              if$
              s empty$ s is.doi.in.url or
                'skip$
                { numnames #1 + 'numnames :=
                  numnames #1 >
                    { ", " * }
                    { "DOI: " * }
                  if$
                  "\doi{" s * "}" * *
                }
              if$
              "" 's :=
            }
            { s tmp.str * 's := }
          if$
          t #2 global.max$ substring$ 't :=
        }
      while$
      's :=
      s empty$ not
        { new.block s }
        { "" }
      if$
    }
  if$
}

%    \end{macrocode}
%
% The format.title function is used for non-book-like titles.
% For most styles we convert to lowercase (except for the very first letter,
% and except for the first one after a colon (followed by whitespace)),
% and hope the user has brace-surrounded words that need to stay capitilized;
% for some styles, however, we leave it as it is in the database.
%
%    \begin{macrocode}
FUNCTION {format.title}
{ title empty$
    { "" }
    { title
      entry.lang lang.en =
        { "t" change.case$ }
        'skip$
      if$
    }
  if$
}

FUNCTION {set.mark}
{ entry.mark empty$ not
    'pop$
    { mark empty$ not
        { pop$ mark 'entry.mark := }
        { 'entry.mark := }
      if$
      medium empty$ not
        { entry.mark "/" * medium * 'entry.mark := }
        { url empty$ not
            { entry.mark "/OL" * 'entry.mark := }
            'skip$
          if$
        }
      if$
    }
  if$
}

FUNCTION {format.mark}
{ "\allowbreak[" entry.mark * "]" * }

FUNCTION {format.full.names}
{'s :=
  #1 'nameptr :=
  s num.names$ 'numnames :=
  numnames 'namesleft :=
    { namesleft #0 > }
    { s nameptr "{vv~}{ll}{, jj}{, ff}" format.name$ 't :=
      t get.str.lang 'name.lang :=
      name.lang lang.en =
        { t #1 "{vv~}{ll}" format.name$ 't := }
        { t #1 "{ll}{ff}" format.name$ 't := }
      if$
      nameptr #1 >
        {
          namesleft #1 >
            { ", " * t * }
            {
              numnames #2 >
                { "," * }
                'skip$
              if$
              t "others" =
                { " et~al." * }
                { " and " * t * }
              if$
            }
          if$
        }
        't
      if$
      nameptr #1 + 'nameptr :=
      namesleft #1 - 'namesleft :=
    }
  while$
}

FUNCTION {author.editor.full}
{ author empty$
    { editor empty$
        { "" }
        { editor format.full.names }
      if$
    }
    { author format.full.names }
  if$
}

FUNCTION {author.full}
{ author empty$
    { "" }
    { author format.full.names }
  if$
}

FUNCTION {editor.full}
{ editor empty$
    { "" }
    { editor format.full.names }
  if$
}

FUNCTION {make.full.names}
{ type$ "book" =
  type$ "inbook" =
  or
    'author.editor.full
    { type$ "collection" =
      type$ "proceedings" =
      or
        'editor.full
        'author.full
      if$
    }
  if$
}

FUNCTION {output.bibitem}
{ newline$
  "\bibitem[" write$
  label write$
  ")" make.full.names duplicate$ short.list =
     { pop$ }
     { * }
   if$
  "]{" * write$
  cite$ write$
  "}" write$
  newline$
  ""
  before.all 'output.state :=
}

%    \end{macrocode}
%
% By default, BibTeX sets the global integer variable |global.max$| to the BibTeX
% constant |glob_str_size|, the maximum length of a global string variable.
% Analogously, BibTeX sets the global integer variable |entry.max$| to
% |ent_str_size|, the maximum length of an entry string variable.
% The style designer may change these if necessary (but this is unlikely)
%
% The n.dashify function makes each single |`-'| in a string a double |`--'|
% if it's not already
%
% \begin{pseudocode}
% pseudoVAR: pageresult: STRING         (it's what's accumulated on the stack)
%
% n.dashify(s) ==
%  BEGIN
%       t := s
%       pageresult := ""
%       while (not empty$(t))
%         do
%           if (first character of t = "-")
%             then
%               if (next character isn't)
%                 then
%                   pageresult := pageresult * "--"
%                   t := t with the "-" removed
%                 else
%                   while (first character of t = "-")
%                     do
%                       pageresult := pageresult * "-"
%                       t := t with the "-" removed
%                     od
%               fi
%             else
%               pageresult := pageresult * the first character
%               t := t with the first character removed
%           fi
%         od
%       return pageresult
%  END
% \end{pseudocode}
%
% 《规则》里页码范围的连接号使用 hyphen，需要将 dash 转为 hyphen。
%
%    \begin{macrocode}
FUNCTION {hyphenate}
{ 't :=
  ""
    { t empty$ not }
    { t #1 #1 substring$ "-" =
        { "-" *
            { t #1 #1 substring$ "-" = }
            { t #2 global.max$ substring$ 't := }
          while$
        }
        { t #1 #1 substring$ *
          t #2 global.max$ substring$ 't :=
        }
      if$
    }
  while$
}

%    \end{macrocode}
%
% The format.date function is for the month and year, but we give a warning if
% there's an empty year but the month is there, and we return the empty string
% if they're both empty.
%
% Newspaer 和 paptent 要显示完整的日期，同时不再显示修改日期。
% 但是在 author-year 模式下，需要单独设置 format.year。
%
%    \begin{macrocode}
FUNCTION {extract.before.dash}
{ duplicate$ empty$
    { pop$ "" }
    { 's :=
      #1 'charptr :=
      s text.length$ #1 + 'len :=
        { charptr len <
          s charptr #1 substring$ "-" = not
          and
        }
        { charptr #1 + 'charptr := }
      while$
      s #1 charptr #1 - substring$
    }
  if$
}

FUNCTION {extract.after.dash}
{ duplicate$ empty$
    { pop$ "" }
    { 's :=
      #1 'charptr :=
      s text.length$ #1 + 'len :=
        { charptr len <
          s charptr #1 substring$ "-" = not
          and
        }
        { charptr #1 + 'charptr := }
      while$
        { charptr len <
          s charptr #1 substring$ "-" =
          and
        }
        { charptr #1 + 'charptr := }
      while$
      s charptr global.max$ substring$
    }
  if$
}

FUNCTION {contains.dash}
{ duplicate$ empty$
    { pop$ #0 }
    { 's :=
        { s empty$ not
          s #1 #1 substring$ "-" = not
          and
        }
        { s #2 global.max$ substring$ 's := }
      while$
      s empty$ not
    }
  if$
}

FUNCTION {format.year}
{ year empty$ not
    { year extract.before.dash }
    { date empty$ not
        { date extract.before.dash }
        { "empty year in " cite$ * warning$
          ""
        }
      if$
    }
  if$
  extra.label *
}

FUNCTION {format.date}
{ type$ "patent" = type$ "newspaper" = or
  date empty$ not and
    { date }
    { year }
  if$
}

FUNCTION {format.editdate}
{ date empty$ not
  type$ "newspaper" = not and
  url empty$ not doi empty$ not or
  and
    { "\allowbreak(" date * ")" * }
    { "" }
  if$
}

%    \end{macrocode}
%
% 《著录规则》中的“引用日期”都是与 URL 同时出现的，所以其实为 urldate，这个虽然
% 不是 \BibTeX{} 标准的域，但是实际中很常见。
%
%    \begin{macrocode}
FUNCTION {format.urldate}
{ urldate empty$ not
    { "\allowbreak[" urldate * "]" * }
    { "" }
  if$
}

%    \end{macrocode}
%
% The format.btitle is for formatting the title field when it is a book-like
% entry---the style used here keeps it in uppers-and-lowers and emphasizes it.
%
%    \begin{macrocode}
FUNCTION {format.btitle}
{ title emphasize
}

%    \end{macrocode}
%
% For several functions we'll need to connect two strings with a
% tie (~) if the second one isn't very long (fewer than 3 characters).
% The tie.or.space.connect function does that.  It concatenates the two
% strings on top of the stack, along with either a tie or space between
% them, and puts this concatenation back onto the stack:
%
% \begin{pseudocode}
% tie.or.space.connect(str1,str2) ==
%    BEGIN
%       if text.length$(str2) < 3
%         then return the concatenation of str1, "~", and str2
%         else return the concatenation of str1, " ", and str2
%    END
% \end{pseudocode}
%
%    \begin{macrocode}
FUNCTION {tie.or.space.connect}
{ duplicate$ text.length$ #3 <
    { "~" }
    { " " }
  if$
  swap$ * *
}

%    \end{macrocode}
%
% The either.or.check function complains if both fields or an either-or pair
% are nonempty.
%
% \begin{pseudocode}
% either.or.check(t,s) ==
%  BEGIN
%       if empty$(s) then
%           warning$(can't use both " * t * " fields in " * cite$)
%       fi
%  END
% \end{pseudocode}
%
%    \begin{macrocode}
FUNCTION {either.or.check}
{ empty$
    'pop$
    { "can't use both " swap$ * " fields in " * cite$ * warning$ }
  if$
}

%    \end{macrocode}
%
% The format.bvolume function is for formatting the volume and perhaps
% series name of a multivolume work.  If both a volume and a series field
% are there, we assume the series field is the title of the whole multivolume
% work (the title field should be the title of the thing being referred to),
% and we add an "of <series>".  This function is called in mid-sentence.
%
% The format.number.series function is for formatting the series name
% and perhaps number of a work in a series.  This function is similar to
% format.bvolume, although for this one the series must exist (and the
% volume must not exist).  If the number field is empty we output either
% the series field unchanged if it exists or else the null string.
% If both the number and series fields are there we assume the series field
% gives the name of the whole series (the title field should be the title
% of the work being one referred to), and we add an "in <series>".
% We capitilize Number when this function is used at the beginning of a block.
%
%    \begin{macrocode}
FUNCTION {is.digit}
{ duplicate$ empty$
    { pop$ #0 }
    { chr.to.int$
      duplicate$ "0" chr.to.int$ <
      { pop$ #0 }
      { "9" chr.to.int$ >
          { #0 }
          { #1 }
        if$
      }
    if$
    }
  if$
}

FUNCTION {is.number}
{ 's :=
  s empty$
    { #0 }
    { s text.length$ 'charptr :=
        { charptr #0 >
          s charptr #1 substring$ is.digit
          and
        }
        { charptr #1 - 'charptr := }
      while$
      charptr not
    }
  if$
}

FUNCTION {format.volume}
{ volume empty$
    { "" }
    { volume is.number
        { is.in.chinese
            { "第 " volume * " 卷" * }
            { "volume" volume tie.or.space.connect }
          if$
        }
        { volume }
      if$
    }
  if$
}

FUNCTION {format.number}
{ number empty$
    { "" }
    { number is.number
        { is.in.chinese
            { "第 " number * " 册" * }
            { "number" number tie.or.space.connect }
          if$
        }
        { number }
      if$
    }
  if$
}

FUNCTION {format.volume.number}
{ volume empty$ not
    { format.volume }
    { format.number }
  if$
}

FUNCTION {format.series.vol.num.title}
{ format.volume.number 's :=
  series empty$ not
    { series ": " *
      s empty$ not
        { s * "\quad " * }
        'skip$
      if$
      title *
    }
    { title
      s empty$ not
        { ": " * s * }
        'skip$
      if$
    }
  if$
  entry.lang lang.en =
    { "t" change.case$ }
    'skip$
  if$
}

FUNCTION {format.series.vol.num.booktitle}
{ format.volume.number 's :=
  series empty$ not
    { series ": " *
      s empty$ not
        { s * "\quad " * }
        'skip$
      if$
      booktitle *
    }
    { booktitle
      s empty$ not
        { ": " * s * }
        'skip$
      if$
    }
  if$
}

%    \end{macrocode}
%
% The format.edition function appends " edition" to the edition, if present.
% We lowercase the edition (it should be something like "Third"), because
% this doesn't start a sentence.
%
%    \begin{macrocode}
FUNCTION {num.to.ordinal}
{ duplicate$ text.length$ 'charptr :=
  duplicate$ charptr #1 substring$ 's :=
  s "1" =
    { "st" * }
    { s "2" =
        { "nd" * }
        { s "3" =
            { "rd" * }
            { "th" * }
          if$
        }
      if$
    }
  if$
}

FUNCTION {format.edition}
{ edition empty$
    { "" }
    { edition is.number
        { is.in.chinese
            { edition " 版" * }
            { edition num.to.ordinal " ed." * }
          if$
        }
        { entry.lang lang.en =
            { edition "t" change.case$ 's :=
              s "Revised" = s "Revised edition" = or
                { "Rev. ed." }
                { s " ed." *}
              if$
            }
            { edition }
          if$
        }
      if$
    }
  if$
}

%    \end{macrocode}
%
% 出版地址和出版社会有 “[S.l.: s.n.]” 的情况，所以必须一起处理。
%
%    \begin{macrocode}
FUNCTION {format.sine.loco}
{ is.in.chinese
    { "[出版地不详]" }
    { "[S.l.]" }
  if$
}

FUNCTION {format.sine.nomine}
{ is.in.chinese
    { "[出版者不详]" }
    { "[s.n.]" }
  if$
}

FUNCTION {format.publisher}
{ publisher empty$ not
    { publisher }
    { school empty$ not
        { school }
        { organization empty$ not
            { organization }
            { institution empty$ not
                { institution }
                { "" }
              if$
            }
          if$
        }
      if$
    }
  if$
}

FUNCTION {format.sine.loco.sine.nomine}
{ is.in.chinese
    { "[出版地不详: 出版者不详]" }
    { "[S.l.: s.n.]" }
  if$
}

FUNCTION {format.address.publisher}
{ address empty$ not
    { address
      format.publisher empty$ not
        { ": " * format.publisher * }
        { url empty$ doi empty$ and
            { ": " * format.sine.nomine * }
            'skip$
          if$
        }
      if$
    }
    { url empty$ doi empty$ and
        { format.publisher empty$ not
            { format.sine.loco ": " * format.publisher * }
            { format.sine.loco.sine.nomine }
          if$
        }
        { format.publisher empty$ not
            { format.publisher }
            { "" }
          if$
        }
      if$
    }
  if$
}

%    \end{macrocode}
%
% This function doesn't begin a sentence so "pages" isn't capitalized.
% Other functions that use this should keep that in mind.
%
%    \begin{macrocode}
FUNCTION {format.pages}
{ pages empty$
    { "" }
    { pages hyphenate }
  if$
}

FUNCTION {format.journal.volume}
{ volume }

FUNCTION {format.journal.number}
{ number empty$ not
    { "\penalty0 (" number * ")" * }
    { "" }
  if$
}

%    \end{macrocode}
%
% 连续出版物的年卷期有起止范围，需要特殊处理
%    \begin{macrocode}
FUNCTION {format.periodical.year.volume.number}
{ year empty$ not
    { year extract.before.dash }
    { "No year in periodical " cite$ * warning$ }
  if$
  volume empty$ not
    { ", " * volume extract.before.dash * }
    'skip$
  if$
  number empty$ not
    { "\penalty0 (" * number extract.before.dash * ")" * }
    'skip$
  if$
  year contains.dash
    { "--" *
      year extract.after.dash empty$
      volume extract.after.dash empty$ and
      number extract.after.dash empty$ and not
        { year extract.after.dash empty$ not
            { year extract.after.dash * }
            { year extract.before.dash * }
          if$
          volume empty$ not
            { ", " * volume extract.after.dash * }
            'skip$
          if$
          number empty$ not
            { "\penalty0 (" * number extract.after.dash * ")" * }
            'skip$
          if$
        }
        'skip$
      if$
    }
    'skip$
  if$
}

%    \end{macrocode}
%
% The format.in.ed.booktitle function is used for starting out a sentence
% that begins "In <booktitle>", putting an editor before the title if one
% exists.
%
%    \begin{macrocode}
FUNCTION {format.in.ed.booktitle}
{ booktitle empty$
    { "" }
    { editor empty$
        { "In " booktitle emphasize * }
        { "In " format.editors * ", " * booktitle emphasize * }
      if$
    }
  if$
}

%    \end{macrocode}
%
% The function empty.misc.check complains if all six fields are empty, and
% if there's been no sorting or alphabetic-label complaint.
%
%    \begin{macrocode}
FUNCTION {empty.misc.check}
{ author empty$ title empty$
  year empty$
  and and
  key empty$ not and
    { "all relevant fields are empty in " cite$ * warning$ }
    'skip$
  if$
}

%    \end{macrocode}
%
% Now come the cross-referencing functions (these are invoked because
% one entry in the database file(s) cross-references another, by giving
% the other entry's database key in a `crossref' field).  This feature
% allows one or more titled things that are part of a larger titled
% thing to cross-reference the larger thing.  These styles allow for
% five posibilities: (1) an ARTICLE may cross-reference an ARTICLE;
% (2) a BOOK, (3) INBOOK, or (4) INCOLLECTION may cross-reference a BOOK;
% or (5) an INPROCEEDINGS may cross-reference a PROCEEDINGS.
% Each of these is explained in more detail later.
%
% An ARTICLE entry type may cross reference another ARTICLE (this is
% intended for when an entire journal is devoted to a single topic---
% but since there is no JOURNAL entry type, the journal, too, should be
% classified as an ARTICLE but without the author and title fields).
% This will result in two warning messages for the journal's entry
% if it's included in the reference list, but such is life.
%
% format.article.crossref ==
%  BEGIN
%       if empty$(key) then
%           if empty$(journal) then
%               warning$("need key or journal for " * cite$ *
%                                               " to crossref " * crossref)
%               return(" \cite{" * crossref * "}")
%           else
%               return("In " * emphazise.correct (journal) *
%                                               " \cite{" * crossref * "}")
%               fi
%       else
%           return("In " * key * " \cite{" * crossref * "}")
%       fi
%  END
%
% The other cross-referencing functions are similar, so no "comment version"
% will be given for them.
%
%    \begin{macrocode}
FUNCTION {format.article.crossref}
{ key empty$
    { journal empty$
        { "need key or journal for " cite$ * " to crossref " * crossref *
          warning$
          ""
        }
        { "In \emph{" journal * "}" * }
      if$
    }
    { "In " }
  if$
  " \citet{" * crossref * "}" *
}

%    \end{macrocode}
%
% A BOOK (or INBOOK) entry type (assumed to be for a single volume in a
% multivolume work) may cross reference another BOOK (the entire multivolume).
% Usually there will be an editor, in which case we use that to construct the
% cross reference; otherwise we use a nonempty key field or else the series
% field (since the series gives the title of the multivolume work).
%
%    \begin{macrocode}
FUNCTION {format.book.crossref}
{ volume empty$
    { "empty volume in " cite$ * "'s crossref of " * crossref * warning$
      "In "
    }
    { "Volume" volume tie.or.space.connect
      " of " *
    }
  if$
  editor empty$
  editor field.or.null author field.or.null =
  or
    { key empty$
        { series empty$
            { "need editor, key, or series for " cite$ * " to crossref " *
              crossref * warning$
              "" *
            }
            { "\emph{" * series * "}" * }
          if$
        }
        'skip$
      if$
    }
    'skip$
  if$
  " \citet{" * crossref * "}" *
}

%    \end{macrocode}
%
% An INCOLLECTION entry type may cross reference a BOOK (assumed to be the
% collection), or an INPROCEEDINGS may cross reference a PROCEEDINGS.
% Often there will be an editor, in which case we use that to construct
% the cross reference; otherwise we use a nonempty key field or else
% the booktitle field (which gives the cross-referenced work's title).
%
%    \begin{macrocode}
FUNCTION {format.incoll.inproc.crossref}
{ editor empty$
  editor field.or.null author field.or.null =
  or
    { key empty$
        { booktitle empty$
            { "need editor, key, or booktitle for " cite$ * " to crossref " *
              crossref * warning$
              ""
            }
            { "In \emph{" booktitle * "}" * }
          if$
        }
        { "In " }
      if$
    }
    { "In " }
  if$
  " \citet{" * crossref * "}" *
}

%    \end{macrocode}
%
% \subsection{Functions for all entry types}
%
% Now we define the type functions for all entry types that may appear
% in the .BIB file---e.g., functions like `article' and `book'.  These
% are the routines that actually generate the .BBL-file output for
% the entry.  These must all precede the READ command.  In addition, the
% style designer should have a function `default.type' for unknown types.
% Note: The fields (within each list) are listed in order of appearance,
% except as described for an `inbook' or a `proceedings'.
%
% The article function is for an article in a journal.  An article may
% CROSSREF another article.
%       Required fields: author, title, journal, year
%       Optional fields: volume, number, pages, month, note
%
% \begin{pseudocode}
% article ==
%  BEGIN
%       output.bibitem
%       output.check(format.authors,"author")
%       new.block
%       output.check(format.title,"title")
%       new.block
%       if missing$(crossref) then
%           output.check(emphasize(journal),"journal")
%           output(format.vol.num.pages)
%           output.check(format.date,"year")
%       else
%           output.nonnull(format.article.crossref)
%           output(format.pages)
%       fi
%       new.block
%       output(note)
%       fin.entry
%  END
% \end{pseudocode}
%
% The book function is for a whole book.  A book may CROSSREF another book.
%       Required fields: author or editor, title, publisher, year
%       Optional fields: volume or number, series, address, edition, month,
%                       note
%
% \begin{pseudocode}
% book ==
%  BEGIN
%       if empty$(author) then output.check(format.editors,"author and editor")
%       else    output.check(format.authors,"author")
%               if missing$(crossref) then
%                   either.or.check("author and editor",editor)
%               fi
%       fi
%       new.block
%       output.check(format.btitle,"title")
%       if missing$(crossref) then
%           output(format.bvolume)
%           new.block
%           output(format.number.series)
%           new.sentence
%           output.check(publisher,"publisher")
%           output(address)
%       else
%           new.block
%           output.nonnull(format.book.crossref)
%       fi
%       output(format.edition)
%       output.check(format.date,"year")
%       new.block
%       output(note)
%       fin.entry
%  END
% \end{pseudocode}
%
% The other entry functions are all quite similar, so no "comment version"
% will be given for them.
%
% \subsection{专著}
%
%    \begin{macrocode}
FUNCTION {monograph}
{ output.bibitem
  author empty$ not
    { format.authors }
    { editor empty$ not
        { format.editors }
%<*authoryear>
        { format.anonymous }
%</authoryear>
%<*numerical>
        { "" }
%</numerical>
      if$
    }
  if$
  output
%<*authoryear>
  format.year "year" output.check
%</authoryear>
  new.block
  format.series.vol.num.title "title" output.check
  "M" set.mark
  format.mark "" output.after
  new.block
  format.translators output
  new.sentence
  format.edition output
  new.block
  format.address.publisher output
%<*numerical>
  format.year "year" output.check
%</numerical>
  format.pages punct.colon output.after
  format.urldate "" output.after
  format.url output
  format.doi output
  fin.entry
}

%    \end{macrocode}
%
% \subsection{专著中的析出文献}
%
% An incollection is like inbook, but where there is a separate title
% for the referenced thing (and perhaps an editor for the whole).
% An incollection may CROSSREF a book.
%       Required: author, title, booktitle, publisher, year
%       Optional: editor, volume or number, series, type, chapter, pages,
%                       address, edition, month, note
%
%    \begin{macrocode}
FUNCTION {incollection}
{ output.bibitem
  format.authors "author" output.check
  author format.key output
%<*authoryear>
  format.year "year" output.check
%</authoryear>
  new.block
  format.title "title" output.check
  "M" set.mark
  format.mark "" output.after
  new.block
  format.translators output
  new.slash
  format.editors output
  new.block
  format.series.vol.num.booktitle "booktitle" output.check
  new.block
  format.edition output
  new.block
  format.address.publisher output
%<*numerical>
  format.year "year" output.check
%</numerical>
  format.pages punct.colon output.after
  format.urldate "" output.after
  format.url output
  format.doi output
  fin.entry
}

%    \end{macrocode}
%
% \subsection{连续出版物}
%
%    \begin{macrocode}
FUNCTION {periodical}
{ output.bibitem
  format.authors "author" output.check
  author format.key output
%<*authoryear>
  format.year "year" output.check
%</authoryear>
  new.block
  format.title "title" output.check
  "J" set.mark
  format.mark "" output.after
  new.block
  format.periodical.year.volume.number output
  new.block
  format.address.publisher output
%<*numerical>
  format.date "year" output.check
%</numerical>
  format.urldate "" output.after
  format.url output
  format.doi output
  fin.entry
}

%    \end{macrocode}
%
% \subsection{连续出版物中的析出文献}
%
%    \begin{macrocode}
FUNCTION {article.in.journal}
{ output.bibitem
  format.authors "author" output.check
  author format.key output
%<*authoryear>
  format.year "year" output.check
%</authoryear>
  new.block
  format.title "title" output.check
  "J" set.mark
  format.mark "" output.after
  new.block
  journal "journal" output.check
%<*numerical>
  format.date "year" output.check
%</numerical>
  volume output
  format.journal.number "" output.after
  format.pages punct.colon output.after
  format.editdate "" output.after
  format.urldate "" output.after
  format.url output
  format.doi output
  fin.entry
}

%    \end{macrocode}
%
% \subsection{专利文献}
%
% number 域也可以用来表示专利号。
%
%    \begin{macrocode}
FUNCTION {patent}
{ output.bibitem
  format.authors output
  author format.key output
%<*authoryear>
  format.year "year" output.check
%</authoryear>
  new.block
  format.title
  number empty$ not
    { punct.colon * number * }
    'skip$
  if$
  "title" output.check
  "P" set.mark
  format.mark "" output.after
  new.block
  format.date "year" output.check
  format.urldate "" output.after
  format.url output
  format.doi output
  fin.entry
}

%    \end{macrocode}
%
% \subsection{电子资源}
%
%    \begin{macrocode}
FUNCTION {online}
{ output.bibitem
  format.authors output
  author format.key output
%<*authoryear>
  format.year "year" output.check
%</authoryear>
  new.block
  format.series.vol.num.title "title" output.check
  "EB" set.mark
  format.mark "" output.after
  new.block
  format.address.publisher output
%<*numerical>
  date empty$
    { format.date output }
    'skip$
  if$
%</numerical>
  format.pages punct.colon output.after
  format.editdate "" output.after
  format.urldate "" output.after
  format.url output
  format.doi output
  fin.entry
}

%    \end{macrocode}
%
% A misc is something that doesn't fit elsewhere.
%       Required: at least one of the `optional' fields
%       Optional: author, title, howpublished, month, year, note
%
% Misc 用来自动判断类型。
%    \begin{macrocode}
FUNCTION {misc}
{ journal empty$ not
    'article.in.journal
    { booktitle empty$ not
        'incollection
        { url empty$ not
            'online
            { "Z" set.mark
              monograph
            }
          if$
        }
      if$
    }
  if$
  empty.misc.check
}

FUNCTION {archive}
{ "A" set.mark
  misc
}

FUNCTION {article} { misc }

FUNCTION {book} { monograph }

FUNCTION {collection}
{ "G" set.mark
  monograph
}

FUNCTION {database}
{ "DB" set.mark
  misc
}

FUNCTION {dataset}
{ "DS" set.mark
  misc
}

%    \end{macrocode}
%
% An inbook is a piece of a book: either a chapter and/or a page range.
% It may CROSSREF a book.  If there's no volume field, the type field
% will come before number and series.
%
%       Required: author or editor, title, chapter and/or pages, publisher,year
%
%       Optional: volume or number, series, type, address, edition, month, note
%
% inbook 类是不含 booktitle 域的，所以不应该适用于“专著中的析出文献”，而应该是专
% 著，即 book 类。
%
%    \begin{macrocode}
FUNCTION {inbook} { book }

%    \end{macrocode}
%
% An inproceedings is an article in a conference proceedings, and it may
% CROSSREF a proceedings.  If there's no address field, the month (\& year)
% will appear just before note.
%       Required: author, title, booktitle, year
%       Optional: editor, volume or number, series, pages, address, month,
%                       organization, publisher, note
%
%    \begin{macrocode}
FUNCTION {inproceedings}
{ "C" set.mark
  incollection
}

%    \end{macrocode}
%
% The conference function is included for Scribe compatibility.
%
%    \begin{macrocode}
FUNCTION {conference} { inproceedings }

FUNCTION {map}
{ "CM" set.mark
  misc
}

%    \end{macrocode}
%
% A mastersthesis is a Master's thesis.
%       Required: author, title, school, year
%       Optional: type, address, month, note
%
%    \begin{macrocode}
FUNCTION {mastersthesis}
{ "D" set.mark
  monograph
}

FUNCTION {newspaper}
{ "N" set.mark
  article.in.journal
}

%    \end{macrocode}
%
% A phdthesis is like a mastersthesis.
%       Required: author, title, school, year
%       Optional: type, address, month, note
%
%    \begin{macrocode}
FUNCTION {phdthesis} { mastersthesis }

%    \end{macrocode}
%
% A proceedings is a conference proceedings.
% If there is an organization but no editor field, the organization will
% appear as the first optional field (we try to make the first block nonempty);
% if there's no address field, the month (\& year) will appear just before note.
%       Required: title, year
%       Optional: editor, volume or number, series, address, month,
%                       organization, publisher, note
%
%    \begin{macrocode}
FUNCTION {proceedings}
{ "C" set.mark
  monograph
}

FUNCTION {software}
{ "CP" set.mark
  misc
}

FUNCTION {standard}
{ "S" set.mark
  misc
}

%    \end{macrocode}
%
% A techreport is a technical report.
%       Required: author, title, institution, year
%       Optional: type, number, address, month, note
%
%    \begin{macrocode}
FUNCTION {techreport}
{ "R" set.mark
  misc
}

%    \end{macrocode}
%
% We use entry type `misc' for an unknown type; BibTeX gives a warning.
%
%    \begin{macrocode}
FUNCTION {default.type} { misc }

%    \end{macrocode}
%
% \subsection{Common macros}
%
% Here are macros for common things that may vary from style to style.
% Users are encouraged to use these macros.
%
% Months are either written out in full or abbreviated
%
%    \begin{macrocode}
MACRO {jan} {"January"}

MACRO {feb} {"February"}

MACRO {mar} {"March"}

MACRO {apr} {"April"}

MACRO {may} {"May"}

MACRO {jun} {"June"}

MACRO {jul} {"July"}

MACRO {aug} {"August"}

MACRO {sep} {"September"}

MACRO {oct} {"October"}

MACRO {nov} {"November"}

MACRO {dec} {"December"}

%    \end{macrocode}
%
% Journals are either written out in full or abbreviated;
% the abbreviations are like those found in ACM publications.
%
% To get a completely different set of abbreviations, it may be best to make
% a separate .bib file with nothing but those abbreviations; users could then
% include that file name as the first argument to the \bibliography command
%
%    \begin{macrocode}
MACRO {acmcs} {"ACM Computing Surveys"}

MACRO {acta} {"Acta Informatica"}

MACRO {cacm} {"Communications of the ACM"}

MACRO {ibmjrd} {"IBM Journal of Research and Development"}

MACRO {ibmsj} {"IBM Systems Journal"}

MACRO {ieeese} {"IEEE Transactions on Software Engineering"}

MACRO {ieeetc} {"IEEE Transactions on Computers"}

MACRO {ieeetcad}
 {"IEEE Transactions on Computer-Aided Design of Integrated Circuits"}

MACRO {ipl} {"Information Processing Letters"}

MACRO {jacm} {"Journal of the ACM"}

MACRO {jcss} {"Journal of Computer and System Sciences"}

MACRO {scp} {"Science of Computer Programming"}

MACRO {sicomp} {"SIAM Journal on Computing"}

MACRO {tocs} {"ACM Transactions on Computer Systems"}

MACRO {tods} {"ACM Transactions on Database Systems"}

MACRO {tog} {"ACM Transactions on Graphics"}

MACRO {toms} {"ACM Transactions on Mathematical Software"}

MACRO {toois} {"ACM Transactions on Office Information Systems"}

MACRO {toplas} {"ACM Transactions on Programming Languages and Systems"}

MACRO {tcs} {"Theoretical Computer Science"}

%    \end{macrocode}
%
% \subsection{Read bib entries and execute}
%
% Now we read in the .BIB entries.
%
%    \begin{macrocode}
READ

EXECUTE {init.state.consts}

FUNCTION {set.entry.lang}
{ language empty$
    { author field.or.null title field.or.null * get.str.lang }
    { language "english" = language "american" = or language "british" = or
        { lang.en }
        { language "chinese" =
            { lang.zh }
            { language "japanese" =
                { lang.ja }
                { language "russian" =
                    { lang.ru }
                    { lang.other }
                  if$
                }
              if$
            }
          if$
        }
      if$
    }
  if$
  'entry.lang :=
}

%    \end{macrocode}
%
% The sortify function converts to lower case after |purify$|ing; it's
% used in sorting and in computing alphabetic labels after sorting
%
% The chop.word(w,len,s) function returns either s or, if the first len
% letters of s equals w (this comparison is done in the third line of the
% function's definition), it returns that part of s after w.
%
%    \begin{macrocode}
FUNCTION {sortify}
{ purify$
  "l" change.case$
}

%    \end{macrocode}
%
% We need the chop.word stuff for the dubious unsorted-list-with-labels case.
%
%    \begin{macrocode}
FUNCTION {chop.word}
{ 's :=
  'len :=
  s #1 len substring$ =
    { s len #1 + global.max$ substring$ }
    's
  if$
}

FUNCTION {format.lab.names}
{ 's :=
  s #1 "{vv~}{ll}{, jj}{, ff}" format.name$ 't :=
  t get.str.lang 'name.lang :=
  name.lang lang.en =
    { t #1 "{vv~}{ll}" format.name$}
    { t #1 "{ll}{ff}" format.name$}
  if$
  s num.names$ #1 >
    { "\ " * format.et.al * }
    'skip$
  if$
}

FUNCTION {author.key.label}
{ author empty$
    { key empty$
        { cite$ #1 #3 substring$ }
        'key
      if$
    }
    { author format.lab.names }
  if$
}

FUNCTION {author.editor.key.label}
{ author empty$
    { editor empty$
        { key empty$
            { cite$ #1 #3 substring$ }
            'key
          if$
        }
        { editor format.lab.names }
      if$
    }
    { author format.lab.names }
  if$
}

FUNCTION {author.key.organization.label}
{ author empty$
    { key empty$
        { organization empty$
            { cite$ #1 #3 substring$ }
            { "The " #4 organization chop.word #3 text.prefix$ }
          if$
        }
        'key
      if$
    }
    { author format.lab.names }
  if$
}

FUNCTION {editor.key.organization.label}
{ editor empty$
    { key empty$
        { organization empty$
            { cite$ #1 #3 substring$ }
            { "The " #4 organization chop.word #3 text.prefix$ }
          if$
        }
        'key
      if$
    }
    { editor format.lab.names }
  if$
}

FUNCTION {calc.short.authors}
{ type$ "book" =
  type$ "inbook" =
  or
    'author.editor.key.label
    { type$ "collection" =
      type$ "proceedings" =
      or
        { editor empty$ not
            'editor.key.organization.label
            'author.key.organization.label
          if$
        }
        'author.key.label
      if$
    }
  if$
  'short.list :=
}

FUNCTION {calc.label}
{ calc.short.authors
  short.list
  "("
  *
  format.year duplicate$ empty$
  short.list key field.or.null = or
     { pop$ "" }
     'skip$
  if$
  *
  'label :=
}

%    \end{macrocode}
%
% When sorting, we compute the sortkey by executing "presort" on each entry.
% The presort key contains a number of "sortify"ed strings, concatenated
% with multiple blanks between them.  This makes things like "brinch  per"
% come before "brinch hansen  per".
%
% The fields used here are: the sort.label for alphabetic labels (as set by
% calc.label), followed by the author names (or editor names or organization
% (with a leading "The " removed) or key field, depending on entry type and on
% what's empty), followed by year, followed by the first bit of the title
% (chopping off a leading "The ", "A ", or "An ").
% Names are formatted: Von Last First Junior.
% The names within a part will be separated by a single blank
% (such as "brinch hansen"), two will separate the name parts themselves
% (except the von and last), three will separate the names,
% four will separate the names from year (and from label, if alphabetic),
% and four will separate year from title.
%
% The sort.format.names function takes an argument that should be in
% BibTeX name format, and returns a string containing "   "-separated
% names in the format described above.  The function is almost the same
% as format.names.
%
%    \begin{macrocode}
%<*authoryear>
FUNCTION {sort.language.label}
{ entry.lang lang.zh =
    { "a zh " }
    { entry.lang lang.ja =
        { "b ja " }
        { entry.lang lang.en =
            { "c en " }
            { entry.lang lang.ru =
                { "d ru " }
                { "e other " }
              if$
            }
          if$
        }
      if$
    }
  if$
}

FUNCTION {sort.format.names}
{ 's :=
  #1 'nameptr :=
  ""
  s num.names$ 'numnames :=
  numnames 'namesleft :=
    { namesleft #0 > }
    {
      s nameptr "{vv{ } }{ll{ }}{  ff{ }}{  jj{ }}" format.name$ 't :=
      nameptr #1 >
        {
          "   "  *
          namesleft #1 = t "others" = and
            { "zzzzz" * }
            { numnames #2 > nameptr #2 = and
                { "zz" * year field.or.null * "   " * }
                'skip$
              if$
              t sortify *
            }
          if$
        }
        { t sortify * }
      if$
      nameptr #1 + 'nameptr :=
      namesleft #1 - 'namesleft :=
    }
  while$
}

%    \end{macrocode}
%
% The sort.format.title function returns the argument,
% but first any leading "A "'s, "An "'s, or "The "'s are removed.
% The chop.word function uses s, so we need another string variable, t
%
%    \begin{macrocode}
FUNCTION {sort.format.title}
{ 't :=
  "A " #2
    "An " #3
      "The " #4 t chop.word
    chop.word
  chop.word
  sortify
  #1 global.max$ substring$
}

%    \end{macrocode}
%
% The auxiliary functions here, for the presort function, are analogous to
% the ones for calc.label; the same comments apply, except that the
% organization field takes precedence here over the key field.  For sorting
% purposes, we still remove a leading "The " from the organization field.
%
%    \begin{macrocode}
FUNCTION {anonymous.sort}
{ is.in.chinese
    { "yi4 ming2" }
    { "anon" }
  if$
}

FUNCTION {author.sort}
{ key empty$
    { author empty$
        { anonymous.sort }
        { author sort.format.names }
      if$
    }
    { key sortify }
  if$
}

FUNCTION {author.editor.sort}
{ key empty$
    { author empty$
        { editor empty$
            { anonymous.sort }
            { editor sort.format.names }
          if$
        }
        { author sort.format.names }
      if$
    }
    { key sortify }
  if$
}

FUNCTION {author.organization.sort}
{ key empty$
    { author empty$
        { organization empty$
            { anonymous.sort }
            { "The " #4 organization chop.word sortify }
          if$
        }
        { author sort.format.names }
      if$
    }
    { key sortify }
  if$
}

FUNCTION {editor.organization.sort}
{ key empty$
    { editor empty$
        { organization empty$
            { anonymous.sort }
            { "The " #4 organization chop.word sortify }
          if$
        }
        { editor sort.format.names }
      if$
    }
    { key sortify }
  if$
}

%</authoryear>
%<*numerical>
INTEGERS { seq.num }

FUNCTION {init.seq}
{ #0 'seq.num :=}

EXECUTE {init.seq}

FUNCTION {int.to.fix}
{ "000000000" swap$ int.to.str$ *
  #-1 #10 substring$
}

%</numerical>
%    \end{macrocode}
%
% There is a limit, entry.max$, on the length of an entry string variable
% (which is what its sort.key$ is), so we take at most that many characters
% of the constructed key, and hope there aren't many references that match
% to that many characters!
%
%    \begin{macrocode}
FUNCTION {presort}
{ set.entry.lang
  calc.label
  label sortify
  "    "
  *
%<*authoryear>
  sort.language.label
  type$ "book" =
  type$ "inbook" =
  or
    'author.editor.sort
    { type$ "collection" =
      type$ "proceedings" =
      or
        'editor.organization.sort
        'author.sort
      if$
    }
  if$
  *
  "    "
  *
  year field.or.null sortify
  *
  "    "
  *
  cite$
  *
  #1 entry.max$ substring$
%</authoryear>
%<*numerical>
  seq.num #1 + 'seq.num :=
  seq.num  int.to.fix
%</numerical>
  'sort.label :=
  sort.label *
  #1 entry.max$ substring$
  'sort.key$ :=
}

ITERATE {presort}

%    \end{macrocode}
%
% And now we can sort
%
%    \begin{macrocode}
SORT

%    \end{macrocode}
%
% Now comes the final computation for alphabetic labels, putting in the 'a's
% and 'b's and so forth if required.  This involves two passes: a forward
% pass to put in the 'b's, 'c's and so on, and a backwards pass
% to put in the 'a's (we don't want to put in 'a's unless we know there
% are 'b's).
% We have to keep track of the longest (in |width$| terms) label, for use
% by the "thebibliography" environment.
%
% \begin{pseudocode}
% VAR: longest.label, last.sort.label, next.extra: string
%      longest.label.width, last.extra.num: integer
%
% initialize.longest.label ==
%  BEGIN
%       longest.label := ""
%       last.sort.label := int.to.chr$(0)
%       next.extra := ""
%       longest.label.width := 0
%       last.extra.num := 0
%  END
%
% forward.pass ==
%  BEGIN
%       if last.sort.label = sort.label then
%           last.extra.num := last.extra.num + 1
%           extra.label := int.to.chr$(last.extra.num)
%       else
%           last.extra.num := chr.to.int$("a")
%           extra.label := ""
%           last.sort.label := sort.label
%       fi
%  END
%
% reverse.pass ==
%  BEGIN
%       if next.extra = "b" then
%           extra.label := "a"
%       fi
%       label := label * extra.label
%       if width$(label) > longest.label.width then
%           longest.label := label
%           longest.label.width := width$(label)
%       fi
%       next.extra := extra.label
%  END
% \end{pseudocode}
%
%    \begin{macrocode}
STRINGS { longest.label last.label next.extra }

INTEGERS { longest.label.width last.extra.num number.label }

FUNCTION {initialize.longest.label}
{ "" 'longest.label :=
  #0 int.to.chr$ 'last.label :=
  "" 'next.extra :=
  #0 'longest.label.width :=
  #0 'last.extra.num :=
  #0 'number.label :=
}

FUNCTION {forward.pass}
{ last.label label =
    { last.extra.num #1 + 'last.extra.num :=
      last.extra.num int.to.chr$ 'extra.label :=
    }
    { "a" chr.to.int$ 'last.extra.num :=
      "" 'extra.label :=
      label 'last.label :=
    }
  if$
  number.label #1 + 'number.label :=
}

FUNCTION {reverse.pass}
{ next.extra "b" =
    { "a" 'extra.label := }
    'skip$
  if$
  extra.label 'next.extra :=
  extra.label
  duplicate$ empty$
    'skip$
    { "{\natexlab{" swap$ * "}}" * }
  if$
  'extra.label :=
  label extra.label * 'label :=
}

EXECUTE {initialize.longest.label}

ITERATE {forward.pass}

REVERSE {reverse.pass}

FUNCTION {bib.sort.order}
{ sort.label  'sort.key$ :=
}

ITERATE {bib.sort.order}

SORT

%    \end{macrocode}
%
% Now we're ready to start writing the .BBL file.
% We begin, if necessary, with a LaTeX macro for unnamed names in an alphabetic
% label; next comes stuff from the `preamble' command in the database files.
% Then we give an incantation containing the command
%     |\begin{thebibliography}{...}|
% where the `...' is the longest label.
%
% We also call init.state.consts, for use by the output routines.
%
%    \begin{macrocode}
FUNCTION {begin.bib}
{   preamble$ empty$
    'skip$
    { preamble$ write$ newline$ }
  if$
  "\begin{thebibliography}{" number.label int.to.str$ * "}" *
  write$ newline$
  "\providecommand{\natexlab}[1]{#1}"
  write$ newline$
  "\providecommand{\url}[1]{#1}"
  write$ newline$
  "\providecommand{\href}[2]{\url{#2}}"
  write$ newline$
  "\providecommand{\doi}[1]{\href{https://doi.org/#1}{#1}}"
  write$ newline$
  "\expandafter\ifx\csname urlstyle\endcsname\relax\relax\else"
  write$ newline$
  "  \urlstyle{same}\fi"
  write$ newline$
}

EXECUTE {begin.bib}

%    \end{macrocode}
%
% Now we produce the output for all the entries
%
%    \begin{macrocode}
ITERATE {call.type$}

%    \end{macrocode}
%
% Finally, we finish up by writing the `|\end{thebibliography}|' command.
%
%    \begin{macrocode}
FUNCTION {end.bib}
{ newline$
  "\end{thebibliography}" write$ newline$
}

EXECUTE {end.bib}
%</authoryear|numerical>
%    \end{macrocode}
%
% \Finale
\endinput
