% GB/T 7714 BibTeX Style % https://github.com/zepinglee/gbt7714-bibtex-style % % Copyright (C) 2016-2026 by Zeping Lee % % 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 2008 or later. % % \begin{macrocode} INTEGERS { control.convert.punct control.max.bib.names control.min.bib.names control.max.cite.names control.min.cite.names control.uppercase.family control.initialize.with.hyphen control.check.pinyin control.initialize.pinyin control.cite.final.and control.bib.final.and control.space.before.et.al control.year.before.title control.sentence.case.title control.sentence.case.journal control.sentence.case.booktitle control.link.title control.article.title control.patent.country control.entry.type.id control.space.before.type.id control.entry.medium.id control.slash control.in control.emph.booktitle control.emph.journal control.short.journal control.journal.dots control.link.journal control.bold.volume control.unknown.publisher control.space.before.pages control.page.ranges control.urldate control.url control.doi control.cstr control.eprint control.note control.end.dot control.capitalize.subtitle control.warn.empty.field %<*authoryear> lang.zh.order lang.ja.order lang.en.order lang.ru.order lang.other.order % } STRINGS { control.bib.punct control.cite.lang control.name.year.delim control.page.range.delim } % \end{macrocode} % % 下面每个变量若被设为 |#1| 则启用该项,若被设为 |#0| 则不启用。 % 默认的值是严格遵循国标的配置。 % \begin{macrocode} FUNCTION {load.config} { % \end{macrocode} % % TODO: v3.0.0 将 2015、2005 版默认值改为 GB。 % \begin{macrocode} %<*!(2015|2005|halfwidth|thu|ucas|ustc)> "GB" 'control.bib.punct := % GB / half / bylanguage % %<*2015|2005|halfwidth|thu|ucas|ustc> "half" 'control.bib.punct := % GB / half / bylanguage % % \end{macrocode} % % 控制是否转换标题内部的标点。 % \begin{macrocode} #1 'control.convert.punct := % \end{macrocode} % % 姓名的数量超过 |control.max.bib.names| 时,只著录前 |control.min.bib.names| 个, % 其后加“,等”或与之相应的词。 % \begin{macrocode} #3 'control.max.bib.names := #3 'control.min.bib.names := %<*!ucas> #1 'control.max.cite.names := #1 'control.min.cite.names := % %<*ucas> #2 'control.max.cite.names := #1 'control.min.cite.names := % % \end{macrocode} % % \begin{macrocode} #0 'control.bib.final.and := #1 'control.cite.final.and := % \end{macrocode} % % \begin{macrocode} #1 'control.space.before.et.al := % \end{macrocode} % % 英文姓名转为全大写: % \begin{macrocode} %<*!(2005|2015)|thu|ustc> #0 'control.uppercase.family := % %<*(2005|2015)&!(thu|ustc)> #1 'control.uppercase.family := % % \end{macrocode} % % 名字缩写时是否保留连字符: % \begin{macrocode} %<*!(2005|2015)> #1 'control.initialize.with.hyphen := % %<*(2005|2015)> #0 'control.initialize.with.hyphen := % % \end{macrocode} % % 检查姓名是否为拼音: % \begin{macrocode} #1 'control.check.pinyin := % \end{macrocode} % % 控制用汉语拼音书写的人名是否缩写。 % \begin{macrocode} %<*!(2005|2015)> #0 'control.initialize.pinyin := % %<*2005|2015> #1 'control.initialize.pinyin := % % \end{macrocode} % % 使用 TeX 宏输出“和”、“等” % \begin{macrocode} %<*!ucas> "byentry" 'control.cite.lang := % byentry / chinese / english / macro % %<*ucas> "macro" 'control.cite.lang := % byentry / chinese / english / macro % % \end{macrocode} % % 将年份置于著者后面(著者-出版年制默认) % \begin{macrocode} %<*numeric|ucas> #0 'control.year.before.title := % %<*authoryear&!ucas> #1 'control.year.before.title := % % \end{macrocode} % % 采用著者-出版年制时,作者姓名与年份之间使用句点连接: % \begin{macrocode} %<*numeric> "period" 'control.name.year.delim := % comma / period % %<*authoryear> %<*!2005> "comma" 'control.name.year.delim := % comma / period % %<*2005> "period" 'control.name.year.delim := % comma / period % % % \end{macrocode} % % 英文标题转为 sentence case (句首字母大写,其余小写): % \begin{macrocode} #1 'control.sentence.case.title := #1 'control.sentence.case.booktitle := %<*!(2005|2015)|thu|ustc> #0 'control.sentence.case.journal := % % TODO: v3.0.0 set this to #1 after implementation of GB/T 7714-2025 %<*(2005|2015)&!(thu|ustc)> #0 'control.sentence.case.journal := % % \end{macrocode} % % Sentence case 的标题在冒号后是否大写: % \begin{macrocode} #0 'control.capitalize.subtitle := % \end{macrocode} % % 在标题添加超链接: % \begin{macrocode} #0 'control.link.title := % \end{macrocode} % % 期刊是否含标题(参考 \pkg{bilatex-phys} 的选项),此选项也适用于 @incollection, % @inproceedings, @patent 的标题,它们的处理方式相同。 % \begin{macrocode} #1 'control.article.title := % \end{macrocode} % % 专利题名是否含专利国别 % \begin{macrocode} %<*!(2005|thu)> #0 'control.patent.country := % %<*(2005|thu)> #1 'control.patent.country := % % \end{macrocode} % % 控制是否著录类型标识(比如“[M/OL],含文献载体标识): % \begin{macrocode} #1 'control.entry.type.id := % \end{macrocode} % % 题名与文献类型标识的分隔符。 % \begin{macrocode} #0 'control.space.before.type.id := % \end{macrocode} % % 是否显示文献载体标识(比如“/OL“): % \begin{macrocode} #1 'control.entry.medium.id := % \end{macrocode} % % 使用“//”表示析出文献 % \begin{macrocode} #1 'control.slash := % \end{macrocode} % % 控制是否使用”In:”和“见”。 % \begin{macrocode} #0 'control.in := % \end{macrocode} % % 书名使用斜体: % \begin{macrocode} %<*!italic-book-title> #0 'control.emph.booktitle := % %<*italic-book-title> #1 'control.emph.booktitle := % % \end{macrocode} % % 期刊名使用斜体: % \begin{macrocode} #0 'control.emph.journal := % \end{macrocode} % % 期刊名使用缩写: % \begin{macrocode} #0 'control.short.journal := % \end{macrocode} % % 刊名缩写是否去除缩写点: % \begin{macrocode} #0 'control.journal.dots := % \end{macrocode} % % 在期刊题名添加超链接: % \begin{macrocode} #0 'control.link.journal := % \end{macrocode} % % 期刊的卷使用粗体: % \begin{macrocode} #0 'control.bold.volume := % \end{macrocode} % % 无出版地或出版者时,著录“出版地不详”,“出版者不详”,“S.l.” 或 “s.n.”: % \begin{macrocode} #0 'control.unknown.publisher := % \end{macrocode} % % 页码与前面的冒号之间是否有空格: % \begin{macrocode} #1 'control.space.before.pages := % \end{macrocode} % % 页码是否只含起始页: % \begin{macrocode} #1 'control.page.ranges := % \end{macrocode} % % 起止页码中的连接号: % \begin{macrocode} "-" 'control.page.range.delim := % \end{macrocode} % % 是否著录非电子文献的引用日期: % \begin{macrocode} #1 'control.urldate := % \end{macrocode} % % 是否著录 URL: % \begin{macrocode} %<*!(ustc)> #1 'control.url := % %<*ustc> #0 'control.url := % % \end{macrocode} % % 是否著录 DOI: % \begin{macrocode} %<*!(2005|ustc)> #1 'control.doi := % %<*2005|ustc> #0 'control.doi := % % \end{macrocode} % % 是否著录 CSTR: % \begin{macrocode} %<*!(2005|2015)> #1 'control.cstr := % %<*2005|2015> #0 'control.cstr := % % \end{macrocode} % % 是否著录预印本的标识符 eprint(“arXiv:1112.6136”的编号部分。 % \begin{macrocode} #0 'control.eprint := % \end{macrocode} % % 在每一条文献最后输出注释(note)的内容: % \begin{macrocode} #0 'control.note := % \end{macrocode} % % 结尾加句点 % \begin{macrocode} #1 'control.end.dot := % \end{macrocode} % % 是否警告缺失的字段。 % \begin{macrocode} #1 'control.warn.empty.field := % \end{macrocode} % % 参考文献表按照“著者-出版年”组织时,各个文种的顺序: % \begin{macrocode} %<*authoryear> #1 'lang.zh.order := #2 'lang.ja.order := #3 'lang.en.order := #4 'lang.ru.order := #5 'lang.other.order := % } % \end{macrocode} % % % \subsection{The ENTRY declaration} % % Like Scribe's (according to pages 231-2 of the April '84 edition), % but no fullauthor or editors fields because BibTeX does name handling. % The annote field is commented out here because this family doesn't % include an annotated bibliography style. And in addition to the fields % listed here, BibTeX has a built-in crossref field, explained later. % \begin{macrocode} ENTRY { address archiveprefix author bookauthor booksubtitle booktitle booktitleaddon country % 专利国别 cstr date dimensions doi edition editor eid entrymediumid entrysubtype entrytypeid eventtitle eprint eprinttype holder howpublished institution journal journalsubtitle journaltitle journaltitleaddon key langid language location mainsubtitle maintitle maintitleaddon mark medium nationality % 专利国别,兼容 IEEEtran.bst note number organization pages publisher scale school series shortjournal subtitle title titleaddon translator url urldate version volume year CTL_bib_punct CTL_convert_punct CTL_max_bib_names CTL_min_bib_names CTL_max_cite_names CTL_min_cite_names CTL_uppercase_family CTL_initialize_with_hyphen CTL_check_pinyin CTL_initialize_pinyin CTL_bib_final_and CTL_cite_final_and CTL_cite_lang CTL_space_before_et_al CTL_year_before_title CTL_name_year_delim CTL_sentence_case CTL_sentence_case_title CTL_sentence_case_booktitle CTL_sentence_case_journal CTL_capitalize_subtitle CTL_link_title CTL_article_title CTL_patent_country CTL_entry_type_id CTL_space_before_type_id CTL_entry_medium_id CTL_slash CTL_in CTL_emph_booktitle CTL_emph_journal CTL_short_journal CTL_journal_dots CTL_link_journal CTL_bold_volume CTL_unknown_publisher CTL_space_before_pages CTL_page_ranges CTL_page_range_delim CTL_urldate CTL_url CTL_doi CTL_eprint CTL_note CTL_end_dot CTL_warn_empty_field } { entry.lang is.lang.cjk entry.numbered require.url } % \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.type.id entry.eprint entry.url } % \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. % % \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.punct } INTEGERS { lang.zh lang.ja lang.ko lang.en lang.ru lang.other } INTEGERS { charptr len } STRINGS { pinyin.table } FUNCTION {init.state.consts} { #0 'before.all := #1 'mid.sentence := #2 'after.sentence := #3 'after.block := #4 'after.punct := #3 'lang.zh := #4 'lang.ja := #5 'lang.ko := #1 'lang.en := #2 'lang.ru := #0 'lang.other := " a ai an ang ao " "ba bai ban bang bao bei ben beng bi bian biao bie bin bing bo bu " * "ca cai can cang cao ce cen ceng cha chai chan chang chao che chen cheng chi chong chou chu chuai chuan chuang chui chun chuo ci cong cou cu cuan cui cun cuo " * "da dai dan dang dao de dei deng di dia dian diao die ding diu dong dou du duan dui dun duo " * "e ei en eng er " * "fa fan fang fei fen feng fo fou fu " * "ga gai gan gang gao ge gei gen geng gong gou gu gua guai guan guang gui gun guo " * "ha hai han hang hao he hei hen heng hong hou hu hua huai huan huang hui hun huo " * "ji jia jian jiang jiao jie jin jing jiong jiu ju juan jue jun " * "ka kai kan kang kao ke ken keng kong kou ku kua kuai kuan kuang kui kun kuo " * "la lai lan lang lao le lei leng li lia lian liang liao lie lin ling liu long lou lu luan lun luo lü lüe lyu lyue " * "ma mai man mang mao me mei men meng mi mian miao mie min ming miu mo mou mu " * "na nai nan nang nao ne nei nen neng ni nian niang niao nie nin ning niu nong nu nuan nuo nü nüe nyu nyue " * "o ou " * "pa pai pan pang pao pei pen peng pi pian piao pie pin ping po pou pu " * "qi qia qian qiang qiao qie qin qing qiong qiu qu quan que qun " * "ran rang rao re ren reng ri rong rou ru ruan rui run ruo " * "sa sai san sang sao se sen seng sha shai shan shang shao she shei shen sheng shi shou shu shua shuai shuan shuang shui shun shuo si song sou su suan sui sun suo " * "ta tai tan tang tao te teng ti tian tiao tie ting tong tou tu tuan tui tun tuo " * "wa wai wan wang wei wen weng wo wu " * "xi xia xian xiang xiao xie xin xing xiong xiu xu xuan xue xun " * "ya yan yang yao ye yi yin ying yong you yu yuan yue yun " * "za zai zan zang zao ze zei zen zeng zha zhai zhan zhang zhao zhe zhei zhen zheng zhi zhong zhou zhu zhua zhuai zhuan zhuang zhui zhun zhuo zi zong zou zu zuan zui zun zuo " * 'pinyin.table := %<*deprecated> %<*numeric> "The style name 'gbt7714-numerical' is deprecated. Use 'gbt7714-numeric' instead." warning$ % %<*authoryear> "The style name 'gbt7714-author-year' is deprecated. Use 'gbt7714-authoryear' instead." warning$ % % } % \end{macrocode} % % 下面是一些常量的定义 % \begin{macrocode} FUNCTION {bbl.anonymous} { entry.lang lang.zh = { "佚名" } { "Anon" } if$ } FUNCTION {bbl.no.date} { entry.lang lang.zh = { "无日期" } { "n.d." } if$ } FUNCTION {space.precedes.et.al} { is.lang.cjk { control.space.before.et.al { "\ " } { "" } if$ } { " " } if$ } FUNCTION {bbl.and} { entry.lang lang.zh = { "和" } { entry.lang lang.ja = { "と" } { entry.lang lang.ru = { "и" } { "and" } if$ } if$ } if$ } FUNCTION {bbl.et.al} { entry.lang lang.zh = { "等" } { entry.lang lang.ja = { "ほか" } { entry.lang lang.ru = { "и~др." } { "et~al." } if$ } if$ } if$ } FUNCTION {cite.and} { control.cite.lang "byentry" = 'bbl.and { control.cite.lang "macro" = { "{\biband}" } { control.cite.lang "chinese" = { "和" } { "and" } if$ } if$ } if$ } FUNCTION {cite.et.al} { control.cite.lang "byentry" = { bbl.et.al } { control.cite.lang "macro" = { "{\bibetal}" } { control.cite.lang "chinese" = { "等" } { "et~al." } if$ } if$ } if$ } FUNCTION {bbl.in} { entry.lang lang.zh = { "见" } { "In" } if$ } FUNCTION {bbl.translator} { entry.lang lang.zh = { "译" } { "trans." } if$ } %<*!2005> FUNCTION {bbl.wide.space} { "\quad " } % %<*2005> FUNCTION {bbl.wide.space} { "\ " } % FUNCTION {bbl.edition} { entry.lang lang.zh = { "版" } { entry.lang lang.ja = { "版" } { entry.lang lang.ru = { "изд." } { "ed." } if$ } if$ } if$ } FUNCTION {bbl.double.slash} { "//\allowbreak" } FUNCTION {bbl.sine.loco} { entry.lang lang.zh = { "出版地不详" } { "S.l." } if$ } FUNCTION {bbl.sine.nomine} { entry.lang lang.zh = { "出版者不详" } { "s.n." } if$ } FUNCTION {bbl.sine.loco.sine.nomine} { entry.lang lang.zh = { "出版地不详: 出版者不详" } { "S.l.: s.n." } if$ } FUNCTION {default.self.tokens} { ":,-'–—?.!" } FUNCTION {latin.upper} { "ÀÁÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮİIĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŸŹŻŽ" } FUNCTION {latin.lower} { "àáãäåæçèéêëìíîïðñòóôõöøùúûüýþÿāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįiıĵķĺļľŀłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷÿźżž" } FUNCTION {range.delimiters} { "-–—~" } % \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$ } FUNCTION {string.length} { #0 swap$ { duplicate$ "" = not } { swap$ #1 + swap$ #2 global.max$ substring$ } while$ pop$ } STRINGS { x y } STRINGS { replace find text } % The "X Y contains" function checks if string Y is contained in X. % Although we only need to check if Y is at the beginning of X, % it makes no practical difference, so just use it as-is. FUNCTION {contains} { 'find := 'text := find string.length 'len := text string.length len - #1 + 'charptr := { charptr #0 > text charptr len substring$ find = not and } { charptr #1 - 'charptr := } while$ charptr #0 > } % \end{macrocode} % % the variables s and t are temporary string holders % \begin{macrocode} STRINGS { s t } FUNCTION {add.period} { "bylanguage" control.bib.punct = { is.lang.cjk { "." * } 'add.period$ if$ } 'add.period$ if$ } FUNCTION {after.period.space} { is.lang.cjk "bylanguage" control.bib.punct = and { "" } { " " } if$ } FUNCTION {add.comma} { "bylanguage" control.bib.punct = is.lang.cjk and "GB" control.bib.punct = or { "," * } { ", " * } if$ } FUNCTION {add.colon} { "bylanguage" control.bib.punct = is.lang.cjk and "GB" control.bib.punct = or { ":" * } { ": " * } if$ } FUNCTION {add.colon.by.language} { is.lang.cjk { ":" * } { ": " * } if$ } FUNCTION {bbl.pages.colon} { "bylanguage" control.bib.punct = is.lang.cjk and "GB" control.bib.punct = or { ":" } { control.space.before.pages { ": " } { ":\allowbreak " } if$ } if$ } FUNCTION {make.parentheses} { "bylanguage" control.bib.punct = is.lang.cjk and "GB" control.bib.punct = or { "(" swap$ * ")" * } { "(" swap$ * ")" * } if$ } FUNCTION {make.brackets} { "[" swap$ * "]" * } INTEGERS { find_length } FUNCTION { find.replace } { 'replace := 'find := 'text := find string.length 'find_length := "" { text empty$ not } { text #1 find_length substring$ find = { replace * text #1 find_length + global.max$ substring$ 'text := } { text #1 #1 substring$ * text #2 global.max$ substring$ 'text := } if$ } while$ } FUNCTION {convert.fullwidth.punctuations} { ", " "," find.replace ": " ":" find.replace "; " ";" find.replace "! " "!" find.replace "!" "!" find.replace "? " "?" find.replace "?" "?" find.replace " (" "(" find.replace "(" "(" find.replace ") " ")" find.replace ")" ")" find.replace } FUNCTION {convert.halfwidth.punctuations} { "." ". " find.replace "," ", " find.replace ":" ": " find.replace ";" "; " find.replace "!" "! " find.replace "?" "? " find.replace "(" " (" find.replace ")." ")." find.replace ")," ")," find.replace "):" "):" find.replace ");" ");" find.replace ")}" ")}" find.replace ")" ") " find.replace % Strip leading and trailing spaces duplicate$ #1 #1 substring$ " " = { #2 global.max$ substring$ } 'skip$ if$ duplicate$ #-1 #1 substring$ " " = { duplicate$ string.length #1 - #1 swap$ substring$ } 'skip$ if$ } FUNCTION {convert.punctuations} { "GB" control.bib.punct = { "." ". " find.replace convert.fullwidth.punctuations } { "half" control.bib.punct = { convert.halfwidth.punctuations } { "bylanguage" control.bib.punct = { is.lang.cjk { ". " "." find.replace convert.fullwidth.punctuations } { convert.halfwidth.punctuations } if$ } 'skip$ if$ } if$ } if$ } FUNCTION {format.punctuations} { control.convert.punct 'convert.punctuations 'skip$ if$ is.lang.cjk { "---" "—" find.replace "--" "—" find.replace } { "“" "``" find.replace "”" "''" find.replace "‘" "`" find.replace "’" "'" find.replace "—" "---" find.replace "–" "--" find.replace } if$ } STRINGS { output.punct } % \end{macrocode} % % This is similar to biblatex's \setunit. % \begin{macrocode} FUNCTION {set.punct} { output.state mid.sentence = output.state after.punct = or { 'output.punct := after.punct 'output.state := } 'pop$ if$ } FUNCTION {output.nonnull} { 's := output.state mid.sentence = { add.comma write$ } { output.state after.block = { add.period write$ newline$ "\newblock " write$ } { output.state before.all = 'write$ { output.state after.punct = { output.punct * write$ } { add.period after.period.space * write$ } if$ } if$ } if$ mid.sentence 'output.state := } if$ s } FUNCTION {output} { duplicate$ empty$ 'pop$ 'output.nonnull if$ } FUNCTION {output.check} { 't := duplicate$ empty$ { pop$ control.warn.empty.field { "empty " t * " in " * cite$ * warning$ } 'skip$ if$ } 'output.nonnull if$ } % \end{macrocode} % % This function finishes all entries. % % \begin{macrocode} FUNCTION {fin.entry} { control.end.dot 'add.period 'skip$ if$ write$ newline$ } FUNCTION {new.block} { output.state before.all = 'skip$ { after.block 'output.state := } if$ } FUNCTION {new.sentence} { output.state after.block = 'skip$ { output.state before.all = 'skip$ { after.sentence 'output.state := } if$ } if$ } FUNCTION {new.slash} { output.state before.all = 'skip$ { control.slash { bbl.double.slash * write$ newline$ "" before.all 'output.state := } { new.block } if$ } 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} % % In order to support UTF-8 encoding, we need some auxiliary functions. Below % are a series of such functions. We try to make functions loosely-coupled as % much as possible. Where the use of variables is inevitable in functions, we % generally assume it is the caller's responsibility to save and restore those % variables. Exceptions are made for some unary functions, where it is % convenient for the callee to do so. % \begin{macrocode} INTEGERS { a b } % \end{macrocode} % % Function |is.int.in.range| takes a codepoint and two integers and check if the % codepoint is between these two integers (inclusive). % \begin{macrocode} % codepoint: int, a: int, b: int -> bool % variable used: b FUNCTION {is.int.in.range} { 'b := #1 + b > { #1 - b < } { pop$ #0 } if$ } % \end{macrocode} % % Function |mult.power2| takes two integers and returns \(2^nm\). % \begin{macrocode} % m: int, n: int -> int FUNCTION {mult.power2} { { duplicate$ #0 > } { swap$ duplicate$ + swap$ #1 - } while$ pop$ } % \end{macrocode} % % Function |find.match.brace| takes two strings, the first of which is assumed % to be |"{"|, and find the matching brace in the second string. It returns a % token (or subtoken) and the rest of the string after the matching brace. When % braces are unmatched, it issues a warning and complete the brace % automatically, following the convention of the original \BibTeX{}. % \begin{macrocode} % "{", str -> subtoken: str, rest: str % variables used: s, t FUNCTION {find.match.brace} { 's := 't := #1 { duplicate$ #0 > s empty$ not and } { s #1 #1 substring$ "{" = { #1 + } { s #1 #1 substring$ "}" = { #1 - } 'skip$ if$ } if$ t s #1 #1 substring$ * 't := s #2 global.max$ substring$ 's := } while$ duplicate$ #0 > { "Unbalanced brace(s): one or more closing braces are missing" warning$ { duplicate$ #0 > } { t "}" * 't := #1 - } while$ } 'skip$ if$ pop$ t s } % \end{macrocode} % % Function |split.first.char.from.str| takes a UTF-8 string and return % the first UTF-8 character and the rest of the string in reverse order. % \begin{macrocode} % str -> str, char FUNCTION {split.first.char.from.str} { duplicate$ "" = { "split.first.char.from.str: Trying to split an empty string!" warning$ "" } { duplicate$ #1 #1 substring$ chr.to.int$ #128 < { duplicate$ #1 #1 substring$ swap$ #2 global.max$ substring$ swap$ } { duplicate$ #1 #1 substring$ chr.to.int$ #224 < { duplicate$ #1 #2 substring$ swap$ #3 global.max$ substring$ swap$ } { duplicate$ #1 #1 substring$ chr.to.int$ #240 < { duplicate$ #1 #3 substring$ swap$ #4 global.max$ substring$ swap$ } { duplicate$ #1 #4 substring$ swap$ #5 global.max$ substring$ swap$ } if$ } if$ } if$ } if$ } % \end{macrocode} % % Function |get.first.char.from.str| takes a UTF-8 string and return the % first UTF-8 character. % \begin{macrocode} % str -> char FUNCTION {get.first.char.from.str} { split.first.char.from.str swap$ pop$ } % \end{macrocode} % % Function |split.first.tex.char.from.str| is like % |split.first.char.from.str|. It takes a UTF-8 string and return the % first UTF-8 character or first \TeX group and the rest of string in % reverse order. % \begin{macrocode} % str -> rest: str, texchar FUNCTION {split.first.tex.char.from.str} { duplicate$ #1 #1 substring$ "{" = { split.first.char.from.str swap$ find.match.brace swap$ } 'split.first.char.from.str if$ } % \end{macrocode} % % Function |char.to.unicode| takes a UTF-8 character and returns its % codepoint in Unicode. It issues a warning and returns \(-1\) if the % presumed character is an empty string. For other invalid input, the % behavior is undefined. % \begin{macrocode} % char -> int FUNCTION {char.to.unicode} { duplicate$ #4 #1 substring$ "" = { duplicate$ #3 #1 substring$ "" = { duplicate$ #2 #1 substring$ "" = { duplicate$ "" = { "Empty string is not a char!" warning$ pop$ #-1 } { #1 #1 substring$ chr.to.int$ } if$ } { duplicate$ #2 #1 substring$ chr.to.int$ #128 - swap$ #1 #1 substring$ chr.to.int$ #192 - #6 mult.power2 + } if$ } { duplicate$ #3 #1 substring$ chr.to.int$ #128 - swap$ duplicate$ #2 #1 substring$ chr.to.int$ #128 - swap$ #1 #1 substring$ chr.to.int$ #224 - #6 mult.power2 + #6 mult.power2 + } if$ } { duplicate$ #4 #1 substring$ chr.to.int$ #128 - swap$ duplicate$ #3 #1 substring$ chr.to.int$ #128 - swap$ duplicate$ #2 #1 substring$ chr.to.int$ #128 - swap$ #1 #1 substring$ chr.to.int$ #240 - #6 mult.power2 + #6 mult.power2 + #6 mult.power2 + } if$ } % \end{macrocode} % % Function |is.char.in.str| takes a string and a UTF-8 character. It % checks whether the character is in the string. It issues a warning % and returns \(0\) if the presumed character is an empty string. It % also returns \(0\) if the string itself is empty. For other input, % the behavior is undefined. % \begin{macrocode} % str, char -> bool % variable used: t FUNCTION {is.char.in.str} { 't := t "" = { "is.char.in.str: Empty string is not a char!" warning$ } 'skip$ if$ #0 swap$ { duplicate$ "" = not } { split.first.char.from.str t = { pop$ pop$ #1 "" } 'skip$ if$ } while$ pop$ } % \end{macrocode} % % Function |is.upper.ascii| takes a UTF-8 character and checks whether % it is an uppercase ASCII letter. % \begin{macrocode} % char -> bool % variable used: b FUNCTION {is.upper.ascii} { char.to.unicode #65 swap$ #90 swap$ is.int.in.range } % \end{macrocode} % % Function |is.upper| takes a UTF-8 character and checks whether it is % uppercase in the range from |U+0000| to |U+017F|. % \begin{macrocode} % char -> bool % variable used: b FUNCTION {is.upper} { duplicate$ is.upper.ascii { pop$ #1 } { latin.upper swap$ is.char.in.str } if$ } % \end{macrocode} % % Function |is.lower.ascii| takes a UTF-8 character and checks whether % it is a lowercase ASCII letter. % \begin{macrocode} % char -> bool % variable used: b FUNCTION {is.lower.ascii} { char.to.unicode #97 swap$ #122 swap$ is.int.in.range } % \end{macrocode} % % Function |is.upper| takes a UTF-8 character and checks whether it is % lowercase in the range from |U+0000| to |U+017F|. % \begin{macrocode} % char -> bool % variable used: b FUNCTION {is.lower} { duplicate$ is.lower.ascii { pop$ #1 } { latin.lower swap$ is.char.in.str } if$ } % \end{macrocode} % % Function |is.printable.ascii| takes a UTF-8 character and checks % whether it is a printable ASCII character. % \begin{macrocode} % char -> bool % variable used: b FUNCTION {is.printable.ascii} { char.to.unicode #32 swap$ #126 swap$ is.int.in.range } % \end{macrocode} % % Function |is.letter.ascii| takes a UTF-8 character and checks % whether it is an ASCII letter. % \begin{macrocode} % char -> bool % variable used: b FUNCTION {is.letter.ascii} { duplicate$ is.upper.ascii swap$ is.lower.ascii or } % \end{macrocode} % % Function |is.symbol.ascii| takes a UTF-8 character and checks whether % it is a printable ASCII character but not an ASCII letter. % \begin{macrocode} % char -> bool % variable used: b FUNCTION {is.symbol.ascii} { duplicate$ is.printable.ascii swap$ is.letter.ascii not and } % \end{macrocode} % % Function |is.all.lower| takes a string and checks whether every % character in it is lowercase in the range from |U+0000| to |U+017F|. % \begin{macrocode} % str -> bool % variable used: b % return true if str is empty FUNCTION {is.all.lower} { #1 swap$ { duplicate$ "" = not } { split.first.char.from.str is.lower 'skip$ { pop$ pop$ #0 "" } if$ } while$ pop$ } % str -> bool % variable used: b FUNCTION {is.tex.str.in.title.case} { duplicate$ "" = { pop$ #0 } { split.first.tex.char.from.str purify$ duplicate$ "" = { pop$ pop$ #0 } { split.first.char.from.str is.upper { duplicate$ "" = % single-letter word { pop$ pop$ #1 } { duplicate$ is.all.lower { empty$ { duplicate$ "" = { pop$ #0 } 'is.all.lower if$ } 'is.all.lower if$ } { pop$ pop$ #0 } if$ } if$ } { pop$ pop$ #0} if$ } if$ } if$ } % char, int -> bool % variables used: t, b FUNCTION {is.in.inter.token.chars} { duplicate$ #0 = { pop$ " " = } { #1 = { " " range.delimiters * swap$ is.char.in.str } 'is.letter.ascii if$ } if$ } % str, int -> intertoken: str, rest: str % variable used: t, b FUNCTION {skip.inter.token.chars.by} { 'b := 't := "" t { duplicate$ "" = not } { split.first.char.from.str duplicate$ b is.in.inter.token.chars { swap$ 't := * t } { swap$ * 't := "" } if$ } while$ pop$ t } % str -> intertoken: str, rest: str % variable used: t, b FUNCTION {skip.inter.token.chars} { #0 skip.inter.token.chars.by } % str -> intertoken: str, rest: str % variable used: t, b FUNCTION {skip.inter.token.command} { duplicate$ "" = { "" } { duplicate$ #1 #1 substring$ is.symbol.ascii { split.first.char.from.str swap$ } { #2 skip.inter.token.chars.by } if$ } if$ } % cmdstr -> cmdstr FUNCTION {is.special.char.command} { #2 global.max$ substring$ skip.inter.token.command empty$ 'skip$ { "is.special.char.command: cmdstr has extra components!" warning$ } if$ duplicate$ duplicate$ duplicate$ duplicate$ duplicate$ duplicate$ "oOlLij" swap$ is.char.in.str swap$ "oe" = or swap$ "OE" = or swap$ "ae" = or swap$ "AE" = or swap$ "aa" = or swap$ "AA" = or } % str, str, char -> char % variable used: t FUNCTION {map.char} { 't := split.first.char.from.str { swap$ duplicate$ "" = not } { swap$ t = { pop$ "" t } { swap$ split.first.char.from.str pop$ swap$ split.first.char.from.str } if$ } while$ pop$ t = 'get.first.char.from.str { pop$ t } if$ } % char -> char % variables used: t, b FUNCTION {to.lower} { duplicate$ is.upper.ascii { chr.to.int$ #32 + int.to.chr$ } { latin.lower swap$ latin.upper swap$ map.char } if$ } % char -> char % variables used: t, b FUNCTION {to.upper} { duplicate$ is.lower.ascii { chr.to.int$ #32 - int.to.chr$ } { latin.upper swap$ latin.lower swap$ map.char } if$ } % str -> str % variables used: t, b FUNCTION {all.to.lower} { "" swap$ { duplicate$ empty$ not } { split.first.char.from.str to.lower swap$ 't := * t } while$ * } % texchar -> texchar % variables used: t, b FUNCTION {command.to.lower} { duplicate$ "" = { "command.to.lower: Empty string is not a texchar!" warning$ } { duplicate$ #1 #1 substring$ #92 int.to.chr$ = { duplicate$ is.special.char.command 'all.to.lower 'skip$ if$ } 'to.lower if$ } if$ } % texchar -> texchar % variables used: t, b FUNCTION {tex.to.lower} { duplicate$ #1 #2 substring$ "{" #92 int.to.chr$ * = { "" swap$ { duplicate$ "" = not } { split.first.char.from.str duplicate$ #92 int.to.chr$ = { swap$ skip.inter.token.command 't := * t swap$ command.to.lower } 'to.lower if$ swap$ 't := * t } while$ pop$ } { duplicate$ #1 #1 substring$ "{" = { split.first.char.from.str swap$ find.match.brace pop$ } 'command.to.lower if$ } if$ } % str -> str % variables used: t, b FUNCTION {all.to.upper} { "" swap$ { duplicate$ empty$ not } { split.first.char.from.str to.upper swap$ 't := * t } while$ * } % texchar -> texchar % variables used: t, b FUNCTION {command.to.upper} { duplicate$ "" = { "command.to.lower: Empty string is not a texchar!" warning$ } { duplicate$ #1 #1 substring$ #92 int.to.chr$ = { duplicate$ is.special.char.command 'all.to.upper 'skip$ if$ } 'to.upper if$ } if$ } % texchar -> texchar % variables used: t, b FUNCTION {tex.to.upper} { duplicate$ #1 #2 substring$ "{" #92 int.to.chr$ * = { "" swap$ { duplicate$ "" = not } { split.first.char.from.str duplicate$ #92 int.to.chr$ = { swap$ skip.inter.token.command 't := * t swap$ command.to.upper } 'to.upper if$ swap$ 't := * t } while$ pop$ } { duplicate$ #1 #1 substring$ "{" = { split.first.char.from.str swap$ find.match.brace pop$ } 'command.to.upper if$ } if$ } % texstr -> texstr % variable used: t, b FUNCTION {lower.token.if.in.title.case} { duplicate$ is.tex.str.in.title.case { split.first.tex.char.from.str tex.to.lower swap$ * } 'skip$ if$ } % int -> str FUNCTION {self.tokens} { #0 = 'default.self.tokens 'range.delimiters if$ } % str, int -> token: str, rest: str % variables used: s, t, b FUNCTION {tokenize.by} { 'b := 's := s "" = { "" "" } { s split.first.char.from.str duplicate$ b self.tokens swap$ is.char.in.str 'swap$ { duplicate$ #92 int.to.chr$ = { swap$ skip.inter.token.command 's := * s } { pop$ pop$ "" s { duplicate$ "" = not } { split.first.char.from.str duplicate$ "\ " b self.tokens * swap$ is.char.in.str { pop$ pop$ "" } { duplicate$ "{" = { swap$ find.match.brace } 'swap$ if$ 's := * s } if$ } while$ pop$ s } if$ } if$ } if$ } % str -> str % variables used: s, t, b FUNCTION {tokenize} { #0 tokenize.by } % str -> str % variables used: s, t, b FUNCTION {smart.sentence.case} { tokenize 's := { s "" = not } { s skip.inter.token.chars 's := * s tokenize swap$ duplicate$ ":" = { swap$ 's := * s skip.inter.token.chars 's := * s tokenize swap$ control.capitalize.subtitle 'skip$ { lower.token.if.in.title.case } if$ } 'lower.token.if.in.title.case if$ swap$ 's := * } while$ } % str -> str % variables used: s, t, b FUNCTION {smart.upper.case} { s swap$ t swap$ "" swap$ { duplicate$ "" = not } { tokenize swap$ duplicate$ #1 #1 substring$ #92 int.to.chr$ = 'command.to.upper { "" swap$ { duplicate$ "" = not } { split.first.tex.char.from.str tex.to.upper swap$ 't := * t } while$ pop$ } if$ swap$ 't := * t skip.inter.token.chars 't := * t } while$ pop$ swap$ 't := swap$ 's := } % \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 emphasized, % if that is non-empty, otherwise it returns the null string. Italic % corrections aren't used, so this function should be used when punctuation % will follow the result. % % \begin{pseudocode} % emphasize(s) == % BEGIN % if empty$(s) then return "" % else return "{\em " * s * "}" % \end{pseudocode} % % 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$ } FUNCTION {emphasize.book.title} { control.emph.booktitle is.lang.cjk not and 'emphasize 'skip$ if$ } % \end{macrocode} % % \subsubsection{Detect Language} % \begin{macrocode} INTEGERS { codepoint tmp.lang } FUNCTION {get.codepoint.lang} { 'codepoint := codepoint #128 < { codepoint #64 > codepoint #91 < and codepoint #96 > codepoint #123 < and or { lang.en } { lang.other } if$ } % \end{macrocode} % 俄文西里尔字母:U+0400 到 U+052F,1024--1328。 % \begin{macrocode} { codepoint #1023 > codepoint #1328 < and { lang.ru } % \end{macrocode} % CJK Unified Ideographs: U+4E00--U+9FFF; 19968--40959. % \begin{macrocode} { codepoint #19967 > codepoint #40960 < and { lang.zh } % \end{macrocode} % CJK Unified Ideographs Extension A: U+3400--U+4DBF; 13312--19903. % \begin{macrocode} { codepoint #13311 > codepoint #19904 < and { lang.zh } % \end{macrocode} % 日语假名:U+3040--U+30FF, 12352--12543. % \begin{macrocode} { codepoint #12351 > codepoint #12544 < and { lang.ja } % \end{macrocode} % 韩语谚文音节:U+AC00--U+D7AF, 44032--55215. % \begin{macrocode} { codepoint #44031 > codepoint #55216 < and { lang.ko } { lang.other } if$ } if$ } if$ } if$ } if$ } if$ } FUNCTION {get.str.lang} { lang.other 'tmp.lang := { duplicate$ empty$ not } { split.first.char.from.str char.to.unicode get.codepoint.lang duplicate$ tmp.lang > { 'tmp.lang := } 'pop$ if$ } while$ duplicate$ empty$ { pop$ } { "non-empty string " quote$ swap$ * quote$ " left" warning$ } if$ tmp.lang } FUNCTION {check.entry.lang} { title field.or.null duplicate$ empty$ { author field.or.null * } 'skip$ if$ duplicate$ empty$ { journal field.or.null * } 'skip$ if$ duplicate$ empty$ { journaltitle field.or.null * } 'skip$ if$ duplicate$ empty$ { booktitle field.or.null * } 'skip$ if$ duplicate$ empty$ { address field.or.null * } 'skip$ if$ duplicate$ empty$ { location field.or.null * } 'skip$ if$ duplicate$ empty$ { publisher field.or.null * } 'skip$ if$ get.str.lang } FUNCTION {set.entry.lang} { langid empty$ { language empty$ { "" } { language } if$ } { langid } if$ "l" change.case$ 's := s empty$ { check.entry.lang } { s "english" = s "american" = or s "british" = or { lang.en } { s "chinese" = { lang.zh } { s "japanese" = { lang.ja } { s "korean" = { lang.ko } { s "russian" = { lang.ru } { lang.other } if$ } if$ } if$ } if$ } if$ } if$ 'entry.lang := #0 'is.lang.cjk := entry.lang lang.zh = entry.lang lang.ja = or entry.lang lang.ko = or { #1 'is.lang.cjk := } 'skip$ if$ } FUNCTION {set.entry.numbered} { type$ "archive" = type$ "letter" = or type$ "legislation" = or type$ "patent" = or type$ "regulation" = or type$ "report" = or %<*2005|2015> type$ "standard" = or % type$ "techreport" = or { #1 'entry.numbered := } { #0 'entry.numbered := } if$ } % \end{macrocode} % % \subsubsection{Format names} % % 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. % % \begin{pseudocode} % VAR: nameptr, namesleft, numnames: INTEGER % pseudoVAR: nameresult: STRING (it's what's accumulated on the stack) % % 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. % \begin{macrocode} INTEGERS { nameptr namesleft numnames name.lang } FUNCTION {is.lowercase.word} { #1 { #0 > } { duplicate$ #1 #1 substring$ chr.to.int$ duplicate$ #96 > swap$ #123 < and { #2 global.max$ substring$ duplicate$ empty$ { #0 } { #1 } if$ } { #0 } if$ } while$ empty$ } % Assume already changed to lowercase FUNCTION {is.single.pinyin} { " " swap$ * " " * pinyin.table swap$ contains } % Assume already changed to lowercase FUNCTION {is.single.pinyin.word} { duplicate$ is.lowercase.word 'is.single.pinyin { pop$ #0 } if$ } % Check if the word is pinyin of one or two characters. % Assume already changed to lowercase. % This function is extremely slow, so it should be used only when necessary. FUNCTION {is.double.pinyin} { duplicate$ string.length 'a := #1 { a #0 > and } { duplicate$ #1 a substring$ is.single.pinyin { duplicate$ a #1 + global.max$ substring$ duplicate$ empty$ { pop$ #0 } { is.single.pinyin { #0 } { a #1 - 'a := #1 } if$ } if$ } { a #1 - 'a := #1 } if$ } while$ 't := t #1 #1 substring$ t a #1 + #1 substring$ duplicate$ empty$ 'pop$ { " " swap$ * * } if$ 't := a #0 > } FUNCTION {is.double.pinyin.word} { duplicate$ is.lowercase.word 'is.double.pinyin { pop$ #0 } if$ } FUNCTION {extract.before} { 'find := 'text := find string.length 'find_length := "" { text empty$ not } { text #1 find_length substring$ find = { "" 'text := } { text #1 #1 substring$ * text #2 global.max$ substring$ 'text := } if$ } while$ } FUNCTION {extract.after} { 'find := 'text := find string.length 'find_length := "" { text empty$ not } { text #1 find_length substring$ find = { text find_length #1 + global.max$ substring$ * "" 'text := } { text #2 global.max$ substring$ 'text := } if$ } while$ } FUNCTION {format.western.given.name} { s nameptr "{ f.}" format.name$ control.initialize.with.hyphen 'skip$ { duplicate$ "-" contains { "-" " " find.replace "u" change.case$ } 'skip$ if$ } if$ s nameptr "{, jj}" format.name$ * "." "" find.replace } FUNCTION {is.family.name.pinyin} { s nameptr "{ll}" format.name$ duplicate$ empty$ { pop$ #0 } { "l" change.case$ duplicate$ "-" contains { duplicate$ "-" extract.before is.single.pinyin.word { "-" extract.after is.single.pinyin.word } { pop$ #0 } if$ } { % 这个函数实在太慢了 % is.double.pinyin is.single.pinyin.word } if$ } if$ } FUNCTION {is.given.name.pinyin} { s nameptr "{ff}" format.name$ duplicate$ empty$ { pop$ #0 } { "l" change.case$ "’" "'" find.replace duplicate$ "'" contains { duplicate$ "'" extract.before duplicate$ #1 #1 substring$ 't := is.single.pinyin.word { "'" extract.after duplicate$ #1 #1 substring$ t " " * swap$ * 't := is.single.pinyin.word } { pop$ #0 } if$ } 'is.double.pinyin.word if$ } if$ } FUNCTION {is.name.pinyin} { s nameptr "{vv}{jj}" format.name$ empty$ { is.family.name.pinyin 'is.given.name.pinyin { #0 } if$ } { #0 } if$ } FUNCTION {format.latin.name} { s nameptr "{vv~}{ll}" format.name$ control.uppercase.family 'smart.upper.case 'skip$ if$ control.check.pinyin { is.name.pinyin { control.initialize.pinyin { " " t "u" change.case$ * } { s nameptr "{ ff}" format.name$ } if$ } { format.western.given.name } if$ } { format.western.given.name } if$ * } FUNCTION {format.name} { s nameptr "{vv~}{ll}{,~ff}{, jj}" format.name$ get.str.lang duplicate$ duplicate$ lang.zh = swap$ lang.ja = or { pop$ s nameptr "{vv~}{ll}" format.name$ s nameptr "{ff}{, jj}" format.name$ duplicate$ get.str.lang duplicate$ lang.zh = swap$ lang.ja = or { * } { pop$ } if$ } { lang.ru = { s nameptr "{vv~}{ll}" format.name$ s nameptr "{ff}{, jj}" format.name$ "." "" find.replace duplicate$ empty$ 'pop$ { " " swap$ * * } if$ } { format.latin.name } if$ } if$ } FUNCTION {inter.word.space} { is.lang.cjk { "" } { " " } if$ } FUNCTION {format.names} { 's := #1 'nameptr := s num.names$ 'numnames := numnames 'namesleft := { namesleft #0 > } { format.name 't := nameptr #1 > { numnames control.max.bib.names > nameptr control.min.bib.names > and { "others" 't := #1 'namesleft := } 'skip$ if$ namesleft #1 > { add.comma t * } { t "l" change.case$ "others" = { nameptr #2 > { add.comma } { inter.word.space * } if$ bbl.et.al * } { control.bib.final.and { is.lang.cjk not nameptr #2 > and { add.comma } { inter.word.space * } if$ bbl.and * inter.word.space * } { add.comma } if$ t * } if$ } if$ } 't if$ nameptr #1 + 'nameptr := namesleft #1 - 'namesleft := } while$ format.punctuations } FUNCTION {warn.empty.field} { control.warn.empty.field { "empty " swap$ * " in " * cite$ * warning$ } 'pop$ if$ } FUNCTION {format.anonymous} { %<*authoryear> bbl.anonymous % %<*numeric> "" % } FUNCTION {format.authors} { author empty$ { "author" warn.empty.field format.anonymous } { author format.names } if$ } FUNCTION {format.editors} { editor empty$ { "editor" warn.empty.field format.anonymous } { editor format.names } if$ } FUNCTION {format.authors.editors} { author empty$ { editor empty$ { "author and editor" warn.empty.field format.anonymous } { editor format.names } if$ } { author format.names } if$ } FUNCTION {format.translators} { translator empty$ { "" } { translator format.names add.comma bbl.translator * } if$ } FUNCTION {format.lab.name} { s nameptr "{vv~}{ll}{,~ff}{, jj}" format.name$ get.str.lang duplicate$ lang.zh = swap$ lang.ja = or { s nameptr "{vv~}{ll}" format.name$ s nameptr "{ff}{, jj}" format.name$ duplicate$ get.str.lang duplicate$ lang.zh = swap$ lang.ja = or { * } 'pop$ if$ } { s nameptr "{vv~}{ll}" format.name$ } if$ } FUNCTION {format.full.names} { 's := #1 'nameptr := s num.names$ 'numnames := numnames 'namesleft := { namesleft #0 > } { format.lab.name 't := nameptr #1 > { namesleft #1 > { add.comma t * } { t "l" change.case$ "others" = { nameptr #2 > { add.comma } { inter.word.space * } if$ cite.et.al * } { control.cite.final.and { is.lang.cjk not nameptr #2 > and { add.comma } { inter.word.space * } if$ cite.and * inter.word.space * } { add.comma } if$ t * } if$ } if$ } 't if$ nameptr #1 + 'nameptr := namesleft #1 - 'namesleft := } while$ format.punctuations } 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 {holder.author.full} { holder empty$ { author empty$ { "" } { author format.full.names } if$ } { holder format.full.names } if$ } FUNCTION {make.full.names} { type$ "book" = type$ "inbook" = booktitle empty$ not and or 'author.editor.full { type$ "collection" = type$ "proceedings" = or 'editor.full { type$ "patent" = 'holder.author.full 'author.full if$ } if$ } if$ } FUNCTION {output.bibitem} { newline$ "\bibitem[" write$ label ")" * make.full.names duplicate$ short.list = { pop$ } { duplicate$ "]" contains { "{" swap$ * "}" * } 'skip$ if$ * } if$ "]{" * write$ cite$ write$ "}" write$ newline$ "" before.all 'output.state := } % \end{macrocode} % % \subsubsection{Format title} % % 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 capitalized; % for some styles, however, we leave it as it is in the database. % \begin{macrocode} FUNCTION {format.title.subtitle.titleaddon} { title empty$ { "" } { title subtitle empty$ 'skip$ { add.colon.by.language subtitle * } if$ titleaddon empty$ 'skip$ { add.colon.by.language titleaddon * } if$ } if$ } % 只有英文转为 sentence case, % 其他语言通常不使用 title case,所以应按照 sentence case 保存, % 不需要转为 sentence case。 FUNCTION {change.sentence.case} { entry.lang lang.en = 'smart.sentence.case 'skip$ if$ } FUNCTION {add.link} { url empty$ not { "\href{" url * "}{" * swap$ * "}" * } { doi empty$ not { "\href{https://doi.org/" doi * "}{" * swap$ * "}" * } 'skip$ if$ } if$ } FUNCTION {format.tr.number} { "" number empty$ 'skip$ { type$ "patent" = control.patent.country and { country empty$ not { country * } { nationality empty$ not { nationality * } { address empty$ not { address * } { location empty$ not { location * } { "" } { entry.lang lang.zh = { "中国" * } 'skip$ if$ } if$ } if$ } if$ } if$ } 'skip$ if$ duplicate$ empty$ 'skip$ 'add.comma if$ number duplicate$ get.str.lang lang.zh = 'format.punctuations 'skip$ if$ * } if$ } FUNCTION {entry.has.url.or.doi} { control.url require.url or entry.url empty$ not and doi empty$ not cstr empty$ not or control.doi control.url or require.url or and or } FUNCTION {format.entry.type.id} { control.entry.type.id { entry.type.id control.entry.medium.id { entrymediumid empty$ { medium empty$ { entry.has.url.or.doi type$ "online" = or type$ "webpage" = or { "/OL" * } 'skip$ if$ } { "The field 'medium' for entry medium ID is deprecated. Use 'entrymediumid' instead." warning$ "/" * medium * } if$ } { "/" * entrymediumid * } if$ } 'skip$ if$ make.brackets control.space.before.type.id { " " } { "\allowbreak" } if$ swap$ * } { "" } if$ } FUNCTION {format.title} { title empty$ { "" } { format.title.subtitle.titleaddon control.sentence.case.title 'change.sentence.case 'skip$ if$ format.punctuations control.link.title 'add.link 'skip$ if$ entry.numbered number empty$ not and { add.colon format.tr.number * } 'skip$ if$ format.entry.type.id * } if$ } % \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 ". 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 ". % We capitalize 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} { #1 { #0 > } { duplicate$ #1 #1 substring$ chr.to.int$ duplicate$ #47 > swap$ #58 < and { #2 global.max$ substring$ duplicate$ empty$ { #0 } { #1 } if$ } { #0 } if$ } while$ empty$ } FUNCTION {format.bvolume} { volume empty$ { "" } { volume is.number { entry.lang lang.zh = { "第" volume tie.or.space.connect " 卷" * } { entry.lang lang.ko = { "제" volume tie.or.space.connect " 권" * } { "v." volume * } if$ } if$ } { volume } if$ } if$ } FUNCTION {format.maintitle} { maintitle empty$ { "" } { mainsubtitle empty$ 'skip$ { add.colon.by.language mainsubtitle * } if$ maintitleaddon empty$ 'skip$ { add.colon.by.language maintitleaddon * } if$ } if$ } FUNCTION {format.maintitle.volume} { maintitle empty$ { "" } { maintitle volume empty$ 'skip$ { add.colon.by.language format.bvolume * } if$ } if$ } FUNCTION {format.title.volume} { title empty$ { "" } { format.title.subtitle.titleaddon volume empty$ 'skip$ { add.colon.by.language format.bvolume * } if$ } if$ } FUNCTION {format.series.volume} { series empty$ { "" } { series volume empty$ 'skip$ { add.colon.by.language format.bvolume * } if$ } if$ } FUNCTION {format.btitle} { maintitle empty$ { series empty$ { format.title.volume } { volume empty$ { format.title.volume } { format.series.volume entry.numbered 'skip$ { "volume and number" number either.or.check } if$ bbl.wide.space * format.title.subtitle.titleaddon * } if$ } if$ } { volume empty$ { format.maintitle add.colon.by.language format.title.subtitle.titleaddon * } { format.maintitle.volume bbl.wide.space * format.title.subtitle.titleaddon * } if$ } if$ duplicate$ empty$ 'skip$ { control.sentence.case.title 'change.sentence.case 'skip$ if$ format.punctuations emphasize.book.title control.link.title 'add.link 'skip$ if$ entry.numbered number empty$ not and { add.colon format.tr.number * } 'skip$ if$ type$ "map" = scale empty$ not and { add.period duplicate$ #-1 #1 substring$ "." = { " " * } 'skip$ if$ scale * } 'skip$ if$ format.entry.type.id * } if$ } % \end{macrocode} % % \begin{macrocode} FUNCTION {format.booktitle} { booktitle empty$ { "" } { booktitle control.sentence.case.booktitle 'change.sentence.case 'skip$ if$ booksubtitle empty$ 'skip$ { add.colon booksubtitle control.sentence.case.booktitle { "l" change.case$ } 'skip$ if$ * } if$ emphasize.book.title booktitleaddon empty$ 'skip$ { add.colon booktitleaddon control.sentence.case.booktitle { "l" change.case$ } 'skip$ if$ * } if$ format.punctuations } if$ } FUNCTION {format.series.volume.booktitle} { series empty$ { maintitle empty$ { format.booktitle volume empty$ 'skip$ { add.colon format.bvolume * } if$ } { format.maintitle.volume bbl.wide.space * format.booktitle * } if$ } { volume empty$ { format.booktitle } { format.series.volume bbl.wide.space * format.booktitle * } if$ } if$ } FUNCTION {format.in.ed.booktitle} { booktitle empty$ { "" } { control.in { bbl.in add.colon } { "" } if$ editor empty$ 'skip$ { format.editors * output new.block "" } if$ format.series.volume.booktitle * } if$ } FUNCTION {remove.dots} { 's := "" { s empty$ not } { s #1 #1 substring$ duplicate$ "." = 'pop$ { * } if$ s #2 global.max$ substring$ 's := } while$ } FUNCTION {format.journal.title} { journal empty$ { journaltitle empty$ { "" } { journaltitle } if$ } { journal } if$ journalsubtitle empty$ 'skip$ { duplicate$ empty$ 'skip$ 'add.colon.by.language if$ journalsubtitle * } if$ journaltitleaddon empty$ 'skip$ { duplicate$ empty$ 'skip$ 'add.colon.by.language if$ journaltitleaddon * } if$ control.sentence.case.journal 'change.sentence.case 'skip$ if$ } FUNCTION {format.short.journal} { shortjournal empty$ { format.journal.title duplicate$ field.or.null " " contains % The journal title is not a single word is.lang.cjk not and { "shortjournal" warn.empty.field } 'skip$ if$ } { shortjournal control.journal.dots 'skip$ 'remove.dots if$ } if$ } FUNCTION {get.journal.title} { control.short.journal 'format.short.journal { format.journal.title duplicate$ empty$ { shortjournal empty$ { "journal" warn.empty.field } { pop$ format.short.journal } if$ } { shortjournal empty$ control.journal.dots not and 'remove.dots { duplicate$ shortjournal = control.journal.dots not and 'remove.dots 'skip$ if$ } if$ } if$ } if$ } FUNCTION {check.arxiv.preprint} { "l" change.case$ duplicate$ #1 #5 substring$ "arxiv" = { 'x := "arxiv:" 'y := y text.length$ 'len := x text.length$ len - #1 + 'charptr := { charptr #0 > x charptr len substring$ y = not and } { charptr #1 - 'charptr := } while$ charptr #0 > { x charptr #6 + global.max$ substring$ 'x := x string.length #1 + 'len := #1 'charptr := { charptr len < x charptr #1 substring$ " " = not and x charptr #1 substring$ "[" = not and } { charptr #1 + 'charptr := } while$ x #1 charptr substring$ duplicate$ empty$ 'pop$ { "\url{https://arxiv.org/abs/" swap$ * "}" * 'entry.url := } if$ } 'skip$ if$ #1 } { pop$ #0 } if$ } FUNCTION {format.journal} { get.journal.title duplicate$ empty$ 'skip$ { format.punctuations control.emph.journal is.lang.cjk not and 'emphasize 'skip$ if$ } if$ } % \end{macrocode} % % \subsubsection{Format entry type mark} % % \begin{macrocode} FUNCTION {set.entry.type.id} { entry.type.id empty$ { entrytypeid empty$ { entrysubtype empty$ { mark empty$ { 'entry.type.id := } { pop$ "The field 'mark' for entry type ID is deprecated. Use 'entrytypeid' instead." warning$ mark 'entry.type.id := } if$ } { entrysubtype "newspaper" = { pop$ "N" 'entry.type.id := } { entrysubtype "inproceedings" = { pop$ "C" 'entry.type.id := } { entrysubtype "report" = { pop$ "R" 'entry.type.id := } { entrysubtype "techreport" = { pop$ "R" 'entry.type.id := } { entrysubtype "standard" = { pop$ "S" 'entry.type.id := } { entrysubtype "patent" = { pop$ "P" 'entry.type.id := } { entrysubtype "dataset" = { pop$ "DS" 'entry.type.id := } { 'entry.type.id := } if$ } if$ } if$ } if$ } if$ } if$ } if$ } if$ } { pop$ entrytypeid 'entry.type.id := } if$ } 'pop$ if$ } % \end{macrocode} % % \subsubsection{Format edition} % % 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} { is.lang.cjk 'skip$ { duplicate$ #-2 #1 substring$ "1" = { "th" * } { duplicate$ #-1 #1 substring$ "1" = { "st" * } { duplicate$ #-1 #1 substring$ "2" = { "nd" * } { duplicate$ #-1 #1 substring$ "3" = { "rd" * } { "th" * } if$ } if$ } if$ } if$ } if$ } % If the string on the top of the stack begins with a number, % (e.g., 11th) then replace the string with the leading number % it contains. Otherwise retain the string as-is. s holds the % extracted number, t holds the part of the string that remains % to be scanned. FUNCTION {extract.num} { duplicate$ 't := "" 's := { t empty$ not } { t #1 #1 substring$ t #2 global.max$ substring$ 't := duplicate$ is.digit { s swap$ * 's := } { pop$ "" 't := } if$ } while$ s empty$ 'skip$ { pop$ s } if$ } % Converts the word number string on the top of the stack to % Arabic string form. Will be successful up to "tenth". FUNCTION {word.to.num} { duplicate$ "l" change.case$ 's := s "first" = { pop$ "1" } 'skip$ if$ s "second" = { pop$ "2" } 'skip$ if$ s "third" = { pop$ "3" } 'skip$ if$ s "fourth" = { pop$ "4" } 'skip$ if$ s "fifth" = { pop$ "5" } 'skip$ if$ s "sixth" = { pop$ "6" } 'skip$ if$ s "seventh" = { pop$ "7" } 'skip$ if$ s "eighth" = { pop$ "8" } 'skip$ if$ s "ninth" = { pop$ "9" } 'skip$ if$ s "tenth" = { pop$ "10" } 'skip$ if$ s "revised edition" = { pop$ "Rev. ed." } 'skip$ if$ s "revised ed." = { pop$ "Rev. ed." } 'skip$ if$ s "revised" = { pop$ "Rev. ed." } 'skip$ if$ s "rev." = { pop$ "Rev. ed." } 'skip$ if$ s "修订版" = { pop$ "修订版" } 'skip$ if$ s "修订" = { pop$ "修订版" } 'skip$ if$ } FUNCTION {format.edition} { edition empty$ { "" } { edition duplicate$ #1 #1 substring$ is.digit 'extract.num 'word.to.num if$ duplicate$ "1" = { pop$ "" } { duplicate$ #1 #1 substring$ is.digit { num.to.ordinal " " * bbl.edition * } 'skip$ if$ } if$ } if$ } FUNCTION {format.version} { version empty$ { "" } { version #1 #1 substring$ duplicate$ "V" = { pop$ version } { "v" = { "V" version #2 global.max$ substring$ * } { "V" version * } if$ } if$ } if$ } % \end{macrocode} % % \subsubsection{Format publishing items} % % 出版地址和出版社会有 “[S.l.: s.n.]” 的情况,所以必须一起处理。 % \begin{macrocode} FUNCTION {format.publisher} { publisher empty$ { institution empty$ { school empty$ { "" } { school } if$ } { institution } if$ } { publisher } if$ format.punctuations } FUNCTION {format.address.publisher} { address empty$ { location empty$ { "" } { location } if$ } { address } if$ format.punctuations duplicate$ empty$ not { format.publisher empty$ not { add.colon format.publisher * } { entry.has.url.or.doi not control.unknown.publisher and { add.colon bbl.sine.nomine make.brackets * } 'skip$ if$ } if$ } { pop$ entry.has.url.or.doi not control.unknown.publisher and { format.publisher empty$ not { bbl.sine.loco make.brackets add.colon format.publisher * } { bbl.sine.loco.sine.nomine make.brackets } if$ } { format.publisher empty$ not { format.publisher } { "" } if$ } if$ } if$ } FUNCTION {format.eventtitle} { eventtitle empty$ { "" } { eventtitle format.punctuations } if$ } % \end{macrocode} % % \subsubsection{Format date} % % 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. % % 期刊需要著录起止范围,其中年份使用“/”分隔,卷和期使用“--”分隔。 % 版本 v2.0.2 前的年份也使用“--”分隔,仅提供兼容性,不再推荐。 % \begin{macrocode} FUNCTION {normalize.year.dash} { %<*!(2005|2015)> "bylanguage" control.bib.punct = is.lang.cjk and "GB" control.bib.punct = or { "---" "—" find.replace "--" "—" find.replace "–" "—" find.replace "-" "—" find.replace } { "---" "-" find.replace "--" "-" find.replace "–" "-" find.replace "—" "-" find.replace } if$ % %<*2005|2015> "---" "-" find.replace "--" "-" find.replace "–" "-" find.replace "—" "-" find.replace % } FUNCTION {format.date} { year empty$ { date empty$ { entry.has.url.or.doi { "" } { "year" warn.empty.field urldate empty$ { "" } { urldate "-" extract.before extra.label * make.brackets } if$ } if$ } { date "/" extract.before "-" extract.before date "/" contains { "-" normalize.year.dash * date "/" extract.after "-" extract.before * } 'skip$ if$ extra.label * } if$ } { year normalize.year.dash extra.label * } if$ %<*authoryear> control.year.before.title { pop$ "" } 'skip$ if$ % } % \end{macrocode} % % 专利和报纸都是使用完整日期 % \begin{macrocode} FUNCTION {format.full.date} { date empty$ { year } { control.year.before.title { date } { date #1 #4 substring$ extra.label * date #5 global.max$ substring$ * } if$ } if$ } % \end{macrocode} % % 正文中的引用标注的出版年,若为空则显示“无日期”或“n.d.”,需要带消歧的小写字后缀。 % \begin{macrocode} FUNCTION {format.lab.date} { year empty$ { date empty$ { urldate empty$ { bbl.no.date extra.label empty$ 'skip$ { "-" * extra.label *} if$ } { urldate "-" extract.before extra.label * make.brackets } if$ } { date "/" extract.before "-" extract.before date "/" contains { "-" normalize.year.dash * date "/" extract.after "-" extract.before * } 'skip$ if$ extra.label * } if$ } { year normalize.year.dash extra.label * } if$ } % \end{macrocode} % % 著者-出版年制的出版年应置于题名前、著者姓名后。 % 需要根据以下选项控制格式: % - \opt{control.year.before.title} 出版年是否置于题名前; % - \opt{control.name.year.delim} 著者姓名与出版年之间的分隔符; % \begin{macrocode} %<*authoryear> FUNCTION {format.date.before.title} { control.year.before.title { "period" control.name.year.delim = 'new.sentence 'skip$ if$ format.lab.date } { "" } if$ } % % \end{macrocode} % % 7.6 创建/发布或修改日期、引用日期 % \begin{macrocode} FUNCTION {format.creation.modification.date} { date empty$ { %<*2025> year empty$ { "" } { year} if$ % %<*!2025> "" % } { date } if$ duplicate$ empty$ 'skip$ { make.parentheses "\allowbreak " swap$ * } if$ } % \end{macrocode} % % 2025 版的引用日期对于以下文献类型是必备:网站、网页、数据集、预印本类型, % 其余类型不需要著录。 % 2005、2015 版只有电子资源著录 URL/DOI 时才著录引用日期。 % \begin{macrocode} FUNCTION {format.urldate} { %<*2025> control.urldate urldate empty$ not and % %<*!2025> entry.has.url.or.doi control.urldate urldate empty$ not and and % { "\allowbreak" urldate make.brackets * } { "" } if$ } % \end{macrocode} % % \subsubsection{Format pages} % % 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} % str -> str % variable used: s, t, b FUNCTION {normalize.page.range} { "" swap$ { duplicate$ empty$ not } { #1 skip.inter.token.chars.by 't := empty$ { "" } 'control.page.range.delim if$ * t #1 tokenize.by 't := * t } while$ pop$ } % \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 control.page.ranges 'normalize.page.range { #1 tokenize.by pop$ } if$ } if$ } FUNCTION {format.dimensions} { dimensions empty$ { "" } { dimensions "x" "$\times$" find.replace "×" "$\times$" find.replace } if$ } % \end{macrocode} % % The |format.vol.num.pages| function is for the volume, number, and page range % of a journal article. We use the format: vol(number):pages, with some % variations for empty fields. This doesn't begin a sentence. % % 报纸在卷号缺失时,期号与前面的日期直接相连,所以必须拆开输出。 % \begin{macrocode} FUNCTION {format.journal.volume} { volume empty$ { "" } { control.bold.volume { "\textbf{" volume * "}" * } { volume } if$ } if$ } FUNCTION {format.journal.number} { number empty$ { "" } { "\allowbreak " number make.parentheses * } if$ } FUNCTION {format.journal.pages} { pages empty$ { eid empty$ { "" } { eid } if$ } { format.pages } if$ } FUNCTION {format.newspaper.pages} { pages empty$ { "" } { "\allowbreak " format.pages make.parentheses * } if$ } % \end{macrocode} % % \subsubsection{Format url and doi} % % 传统的 \BibTeX{} 习惯使用 howpublished 著录 url,这里提供支持。 % \begin{macrocode} FUNCTION {check.electronic} { entry.url empty$ { url empty$ { howpublished field.or.null #1 #5 substring$ "\url{" = { howpublished 'entry.url := } { note field.or.null #1 #5 substring$ "\url{" = { note "}" extract.before "}" * 'entry.url := } 'skip$ if$ } if$ } { "\url{" url * "}" * 'entry.url := } if$ } 'skip$ if$ } FUNCTION {format.url} { control.url require.url or { entry.url empty$ { control.doi not require.url or { cstr empty$ { doi empty$ 'skip$ { "\url{https://doi.org/" doi * "}" * 'entry.url := } if$ } { "\url{https://cstr.cn/" cstr * "}" * 'entry.url := } if$ } 'skip$ if$ } 'skip$ if$ entry.url empty$ { "" } { new.block entry.url } if$ } { "" } if$ } % \end{macrocode} % % 需要检测 DOI 是否已经包含在 URL 中。 % \begin{macrocode} FUNCTION {either.or} { swap$ duplicate$ empty$ { pop$ duplicate$ empty$ { pop$ "" } 'skip$ if$ } { swap$ pop$ } if$ } FUNCTION {format.doi} { control.doi { control.cstr not cstr empty$ or { doi empty$ { "" } { control.url require.url or entry.url doi contains and { "" } { new.block "DOI:\doi{" doi * "}" * } if$ } if$ } { control.url require.url or entry.url cstr contains and { "" } { new.block "CSTR:\cstr{" cstr * "}" * } if$ } if$ } { "" } if$ } FUNCTION {format.eprint} { archiveprefix eprinttype either.or duplicate$ empty$ { pop$ journal journaltitle either.or shortjournal either.or "l" change.case$ #1 #5 substring$ "arxiv" = { "arXiv" } { "" } if$ } { duplicate$ "arxiv" = { pop$ "arXiv" } { duplicate$ "pubmed" = { pop$ "PubMed" } 'skip$ if$ } if$ } if$ entry.url empty$ { duplicate$ "l" change.case$ "arxiv" = entry.eprint empty$ not and { "\url{https://arxiv.org/abs/" entry.eprint * "}" * 'entry.url := } 'skip$ if$ } 'skip$ if$ control.eprint eprint empty$ not and { duplicate$ empty$ 'skip$ { ":" * } if$ eprint * } 'skip$ if$ } FUNCTION {format.note} { note empty$ not control.note and { note } { "" } 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$ } FUNCTION {link.open} { control.link.journal { url empty$ { doi empty$ { "" } { "\href{https://doi.org/" doi * "}{" * } if$ } { "\href{" url * "}{" * } if$ duplicate$ empty$ { pop$ } { "" set.punct output before.all 'output.state := } if$ } 'skip$ if$ } FUNCTION {link.close} { control.link.journal { url empty$ doi empty$ and 'skip$ { "" set.punct "}" output before.all 'output.state := } if$ } 'skip$ if$ } % \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 % % The other entry functions are all quite similar, so no "comment version" % will be given for them. % \begin{macrocode} FUNCTION {article.journal} { "J" set.entry.type.id volume empty$ number empty$ and entry.url empty$ not and date field.or.null text.length$ #9 > and entry.type.id "J" = and { #1 'require.url := } 'skip$ if$ output.bibitem format.authors output %<*authoryear> format.date.before.title output % control.article.title { new.block format.title "title" output.check } 'skip$ if$ new.block format.translators output new.block link.open format.journal "journal" output.check entry.type.id "N" = { format.full.date "date" output.check "" set.punct format.newspaper.pages output } { % \end{macrocode} % % 卷次和期号均为空时视为在线出版,使用完整日期。 % \begin{macrocode} %<*2025> volume empty$ number empty$ and date field.or.null text.length$ #9 > and { format.full.date "date" output.check } { format.date "year" output.check } if$ % %<*!2025> format.date output % format.journal.volume output "" set.punct format.journal.number output bbl.pages.colon set.punct format.journal.pages output } if$ link.close %<*!2025> "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } FUNCTION {archive} { "A" set.entry.type.id output.bibitem format.authors output %<*authoryear> format.date.before.title output % new.block format.btitle "title" output.check % new.block % format.translators output new.block format.address.publisher output %<*2025> format.full.date "year" output.check bbl.pages.colon set.punct format.pages output % %<*!2025> format.date output bbl.pages.colon set.punct format.pages output "" set.punct format.creation.modification.date output "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } % \end{macrocode} % % % 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{macrocode} FUNCTION {book} { "M" set.entry.type.id output.bibitem format.authors.editors output %<*authoryear> format.date.before.title output % new.block format.btitle "title" output.check new.block format.translators output new.sentence format.edition output new.block format.address.publisher output format.date output bbl.pages.colon set.punct format.pages output %<*!2025> "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } FUNCTION {letter} { archive } FUNCTION {dataset} { "DS" set.entry.type.id output.bibitem format.authors output %<*authoryear> format.date.before.title output % new.block format.btitle "title" output.check new.block format.version output new.block publisher "publisher" output.check "" set.punct format.creation.modification.date output "" set.punct %<*2025> format.urldate "urldate" output.check % %<*!2025> format.urldate output % format.url output format.doi output new.block format.note output fin.entry } FUNCTION {database} { "DB" set.entry.type.id dataset } % \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 % % 原生 BibTeX 的数据模型中 \texttt{@inbook} 不含 \texttt{booktitle} , % 按照“专著”处理。而 biblatex 的 \texttt{@inbook} 跟 \texttt{incollection} 一样。 % 按照“专著的析出文献”处理。 % \begin{macrocode} FUNCTION {inbook} { "M" set.entry.type.id booktitle empty$ 'book { output.bibitem format.authors output %<*authoryear> format.date.before.title output % control.article.title { new.block format.title "title" output.check } 'skip$ if$ new.block format.translators output new.slash booktitle empty$ { "" } { control.in { bbl.in add.colon } { "" } if$ bookauthor empty$ { editor empty$ 'skip$ { format.editors * output new.block "" } if$ } { bookauthor format.names * output new.block "" } if$ format.series.volume.booktitle * } if$ "booktitle" output.check new.block format.edition output new.block format.address.publisher output format.date output bbl.pages.colon set.punct format.pages output %<*!2025> "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } if$ } % \end{macrocode} % % \subsubsection{图书中的析出文献} % % 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} { "M" set.entry.type.id output.bibitem format.authors output %<*authoryear> format.date.before.title output % control.article.title { new.block format.title "title" output.check } 'skip$ if$ new.block format.translators output new.slash format.in.ed.booktitle "booktitle" output.check new.block format.edition output new.block format.address.publisher output format.date output bbl.pages.colon set.punct format.pages output %<*!2025> "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } % \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 % % 以图书中的析出文献的形式出版的会议论文,按照 @incollection 处理,使用“//”符号。 % 未正式出版的会议论文使用下的格式,不使用“//”符号。 % \begin{macrocode} FUNCTION {inproceedings} { "C" set.entry.type.id output.bibitem format.authors output %<*authoryear> format.date.before.title output % control.article.title { new.block format.title "title" output.check } 'skip$ if$ new.slash booktitle empty$ { format.eventtitle "eventtitle" output.check } { format.in.ed.booktitle "booktitle" output.check new.block format.edition output new.block format.address.publisher output } if$ format.date output bbl.pages.colon set.punct format.pages output %<*!2025> "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } % \end{macrocode} % % The conference function is included for Scribe compatibility. % \begin{macrocode} FUNCTION {conference} { inproceedings } % \end{macrocode} % % \subsubsection{专利文献} % % number 域也可以用来表示专利号。 % \begin{macrocode} FUNCTION {patent} { "P" set.entry.type.id output.bibitem holder empty$ { format.authors "holder and author" output.check } { holder format.names output.nonnull } if$ %<*authoryear> format.date.before.title output % control.article.title { new.block format.title "title" output.check } 'skip$ if$ new.block format.full.date "year" output.check %<*2025> bbl.pages.colon set.punct format.pages output % %<*!2025> "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } % \end{macrocode} % % \subsubsection{连续出版物} % % \begin{macrocode} FUNCTION {format.periodical.volume} { volume number either.or duplicate$ empty$ { pop$ "" } { format.punctuations normalize.year.dash } if$ } FUNCTION {periodical} { "J" set.entry.type.id output.bibitem format.editors output %<*authoryear> format.date.before.title output % new.block format.title "title" output.check new.block format.periodical.volume output new.block format.address.publisher output format.date output %<*2005|2015> "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } % \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.entry.type.id output.bibitem format.editors output %<*authoryear> format.date.before.title output % new.block format.btitle "title" output.check new.block format.translators output new.sentence format.edition output new.block format.address.publisher output format.date output bbl.pages.colon set.punct format.pages output %<*!2025> "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } % \end{macrocode} % % \subsubsection{预印本} % % \begin{macrocode} FUNCTION {preprint} { #1 'require.url := %<*2025> "PP" set.entry.type.id % %<*2015> "A" set.entry.type.id % %<*!(2015|2025)> "Z" set.entry.type.id % output.bibitem format.authors output %<*authoryear> format.date.before.title output % control.article.title { new.block format.title "title" output.check } 'skip$ if$ new.block format.version output new.block format.eprint output "" set.punct format.creation.modification.date output "" set.punct format.urldate "urldate" output.check format.url "url" output.check format.doi output new.block format.note output fin.entry } % \end{macrocode} % % A phdthesis is like a mastersthesis. % % Required: author, title, school, year % % Optional: type, address, month, note % \begin{macrocode} FUNCTION {thesis} { "D" set.entry.type.id output.bibitem format.authors output %<*authoryear> format.date.before.title output % new.block format.btitle "title" output.check new.block format.address.publisher output format.date output bbl.pages.colon set.punct format.pages output %<*!2025> "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } FUNCTION {phdthesis} { thesis } % \end{macrocode} % % A mastersthesis is a Master's thesis. % % Required: author, title, school, year % % Optional: type, address, month, note % \begin{macrocode} FUNCTION {mastersthesis} { thesis } % \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.entry.type.id output.bibitem format.authors output %<*authoryear> format.date.before.title output % new.block format.btitle "title" output.check % new.block % format.translators output new.block %<*2025> institution empty$ publisher empty$ and { format.full.date "year" output.check bbl.pages.colon set.punct format.pages output } { format.address.publisher output format.date output bbl.pages.colon set.punct format.pages output } if$ % %<*!2025> institution empty$ pages empty$ and control.url and entry.url empty$ not and 'skip$ { format.address.publisher output format.date output bbl.pages.colon set.punct format.pages output } if$ "" set.punct format.creation.modification.date output "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } FUNCTION {report} { techreport } % \end{macrocode} % % A manual is technical documentation. % % Required: title % % Optional: author, organization, address, edition, month, year, note % \begin{macrocode} FUNCTION {manual} { techreport } % \end{macrocode} % % \subsubsection{电子资源} % \begin{macrocode} FUNCTION {webpage} { #1 'require.url := output.bibitem format.authors output %<*authoryear> format.date.before.title output % new.block "EB" set.entry.type.id format.btitle "title" output.check new.block publisher empty$ 'skip$ { format.address.publisher output format.date output bbl.pages.colon set.punct format.pages output "" set.punct } if$ format.creation.modification.date output "" set.punct format.urldate "urldate" output.check format.url "url" output.check format.doi output new.block format.note output fin.entry } % \end{macrocode} % % \subsubsection{其他文献类型} % % 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} { eprint empty$ not archiveprefix empty$ not or eprinttype empty$ not or 'preprint { entry.url empty$ { %<*!2005> "Z" set.entry.type.id % %<*2005> "M" set.entry.type.id % book } 'webpage if$ } if$ empty.misc.check } FUNCTION {article} { entrysubtype field.or.null "l" change.case$ "newspaper" = { "N" set.entry.type.id article.journal } { journal journaltitle either.or shortjournal either.or duplicate$ empty$ { pop$ archiveprefix eprinttype either.or eprint either.or empty$ 'article.journal 'preprint if$ } { check.arxiv.preprint 'preprint 'article.journal if$ } if$ } if$ } % \end{macrocode} % % A booklet is a bound thing without a publisher or sponsoring institution. % % Required: title % % Optional: author, howpublished, address, month, year, note % \begin{macrocode} FUNCTION {booklet} { book } FUNCTION {collection} { "G" set.entry.type.id book } FUNCTION {image} { misc } FUNCTION {legislation} { archive } FUNCTION {map} { "CM" set.entry.type.id booktitle empty$ not { incollection } { output.bibitem format.authors output %<*authoryear> format.date.before.title output % new.block format.btitle "title" output.check new.block format.edition output new.block format.address.publisher output format.date output bbl.pages.colon set.punct format.pages output %<*!(2005|2015)> new.block format.dimensions output % %<*2005|2015> "" set.punct format.creation.modification.date output "" set.punct format.urldate output % format.url output format.doi output new.block format.note output fin.entry } if$ } FUNCTION {newspaper} { "N" set.entry.type.id article.journal } FUNCTION {online} { eprint empty$ archiveprefix empty$ and eprinttype empty$ and { webpage } { preprint } if$ } FUNCTION {software} { "CP" set.entry.type.id webpage } FUNCTION {standard} { "S" set.entry.type.id %<*!(2005|2015)> output.bibitem %<*authoryear> format.authors.editors output format.date.before.title output new.block % number field.or.null output is.lang.cjk { "\quad " set.punct } { " " set.punct } if$ format.btitle "title" output.check new.block format.url output new.block format.doi output new.block format.note output fin.entry % %<*2005|2015> booktitle empty$ 'book 'incollection if$ % } % \end{macrocode} % % An unpublished is something that hasn't been published. % % Required: author, title, note % % Optional: month, year % \begin{macrocode} FUNCTION {unpublished} { misc } FUNCTION {video} { misc } % \end{macrocode} % % We use entry type `misc' for an unknown type; BibTeX gives a warning. % \begin{macrocode} FUNCTION {default.type} { misc } % \end{macrocode} % % The following is taken from ttb.pdf. % \begin{macrocode} FUNCTION {mult} { 'a := %% we store the first value 'b := %% we store the second value b #0 < %% We remember the sign of b, and {#-1 #0 b - 'b :=} %% then consider its absolute value. {#1} %% if$ %% #0 %% Put 0 on the stack. {b #0 >} %% While b is strictly positive, { %% we add a to the value on the stack a + %% and decrement b. b #1 - 'b := %% } %% while$ %% swap$ %% Last, we take the opposite 'skip$ %% if b was negative. {#0 swap$ -} %% if$ %% } FUNCTION {chr.to.value} %% The ASCII code of a character { chr.to.int$ #48 - %% ASCII value of "0" -> 48 duplicate$ duplicate$ %% "1" -> 49 #0 < swap$ #9 > or %% ... { %% "9" -> 57 #48 + int.to.chr$ " is not a number..." * warning$ %% Return 0 if it is not a number pop$ #0 %% } {} if$ } FUNCTION {str.to.int.aux} %% The auxiliary function { { duplicate$ empty$ not } %% While the string is not empty { %% consider its first char swap$ #10 mult 'a := %% and ``add'' it at the end of duplicate$ #1 #1 substring$ %% the result. chr.to.value a + swap$ #2 global.max$ substring$ } while$ pop$ } FUNCTION {str.to.int} { %% Handling negative values duplicate$ #1 #1 substring$ "-" = {#1 swap$ #2 global.max$ substring$} {#0 swap$} if$ %% Initialization, and then #0 swap$ str.to.int.aux %% call to the aux. function swap$ {#0 swap$ -} %% And handle the sign. {} if$ } FUNCTION {boolean.to.int} { "l" change.case$ duplicate$ "true" = { pop$ #1 } { duplicate$ "false" = { pop$ #0 } { "unknown boolean " quote$ * swap$ * quote$ * " in " * cite$ * warning$ #0 } if$ } if$ } FUNCTION {GBT7714BSTCTL} { CTL_bib_punct empty$ 'skip$ { CTL_bib_punct 'control.bib.punct := } if$ CTL_convert_punct empty$ 'skip$ { CTL_convert_punct boolean.to.int 'control.convert.punct := } if$ CTL_max_bib_names empty$ 'skip$ { CTL_max_bib_names str.to.int 'control.max.bib.names := } if$ CTL_min_bib_names empty$ 'skip$ { CTL_min_bib_names str.to.int 'control.min.bib.names := } if$ CTL_max_cite_names empty$ 'skip$ { CTL_max_cite_names str.to.int 'control.max.cite.names := } if$ CTL_min_cite_names empty$ 'skip$ { CTL_min_cite_names str.to.int 'control.min.cite.names := } if$ CTL_uppercase_family empty$ 'skip$ { CTL_uppercase_family boolean.to.int 'control.uppercase.family := } if$ CTL_initialize_with_hyphen empty$ 'skip$ { CTL_initialize_with_hyphen boolean.to.int 'control.initialize.with.hyphen := } if$ CTL_check_pinyin empty$ 'skip$ { CTL_check_pinyin boolean.to.int 'control.check.pinyin := } if$ CTL_initialize_pinyin empty$ 'skip$ { CTL_initialize_pinyin boolean.to.int 'control.initialize.pinyin := } if$ CTL_bib_final_and empty$ 'skip$ { CTL_bib_final_and boolean.to.int 'control.bib.final.and := } if$ CTL_cite_final_and empty$ 'skip$ { CTL_cite_final_and boolean.to.int 'control.cite.final.and := } if$ CTL_cite_lang empty$ 'skip$ { CTL_cite_lang 'control.cite.lang := } if$ CTL_space_before_et_al empty$ 'skip$ { CTL_space_before_et_al boolean.to.int 'control.space.before.et.al := } if$ CTL_year_before_title empty$ 'skip$ { CTL_year_before_title boolean.to.int 'control.year.before.title := } if$ CTL_name_year_delim empty$ 'skip$ { CTL_name_year_delim 'control.name.year.delim := } if$ CTL_sentence_case empty$ 'skip$ { CTL_sentence_case boolean.to.int 'control.sentence.case.title := CTL_sentence_case boolean.to.int 'control.sentence.case.booktitle := %<*2005|2015> CTL_sentence_case boolean.to.int 'control.sentence.case.journal := % } if$ CTL_sentence_case_title empty$ 'skip$ { CTL_sentence_case_title boolean.to.int 'control.sentence.case.title := } if$ CTL_sentence_case_booktitle empty$ 'skip$ { CTL_sentence_case_booktitle boolean.to.int 'control.sentence.case.booktitle := } if$ CTL_sentence_case_journal empty$ 'skip$ { CTL_sentence_case_journal boolean.to.int 'control.sentence.case.journal := } if$ CTL_capitalize_subtitle empty$ 'skip$ { CTL_capitalize_subtitle boolean.to.int 'control.capitalize.subtitle := } if$ CTL_link_title empty$ 'skip$ { CTL_link_title boolean.to.int 'control.link.title := } if$ CTL_article_title empty$ 'skip$ { CTL_article_title boolean.to.int 'control.article.title := } if$ CTL_patent_country empty$ 'skip$ { CTL_patent_country boolean.to.int 'control.patent.country := } if$ CTL_entry_type_id empty$ 'skip$ { CTL_entry_type_id boolean.to.int 'control.entry.type.id := } if$ CTL_space_before_type_id empty$ 'skip$ { CTL_space_before_type_id boolean.to.int 'control.space.before.type.id := } if$ CTL_entry_medium_id empty$ 'skip$ { CTL_entry_medium_id boolean.to.int 'control.entry.medium.id := } if$ CTL_slash empty$ 'skip$ { CTL_slash boolean.to.int 'control.slash := } if$ CTL_in empty$ 'skip$ { CTL_in boolean.to.int 'control.in := } if$ CTL_emph_booktitle empty$ 'skip$ { CTL_emph_booktitle boolean.to.int 'control.emph.booktitle := } if$ CTL_emph_journal empty$ 'skip$ { CTL_emph_journal boolean.to.int 'control.emph.journal := } if$ CTL_short_journal empty$ 'skip$ { CTL_short_journal boolean.to.int 'control.short.journal := } if$ CTL_journal_dots empty$ 'skip$ { CTL_journal_dots boolean.to.int 'control.journal.dots := } if$ CTL_link_journal empty$ 'skip$ { CTL_link_journal boolean.to.int 'control.link.journal := } if$ CTL_bold_volume empty$ 'skip$ { CTL_bold_volume boolean.to.int 'control.bold.volume := } if$ CTL_unknown_publisher empty$ 'skip$ { CTL_unknown_publisher boolean.to.int 'control.unknown.publisher := } if$ CTL_space_before_pages empty$ 'skip$ { CTL_space_before_pages boolean.to.int 'control.space.before.pages := } if$ CTL_page_ranges empty$ 'skip$ { CTL_page_ranges boolean.to.int 'control.page.ranges := } if$ CTL_page_range_delim empty$ 'skip$ { CTL_page_range_delim 'control.page.range.delim := } if$ CTL_urldate empty$ 'skip$ { CTL_urldate boolean.to.int 'control.urldate := } if$ CTL_url empty$ 'skip$ { CTL_url boolean.to.int 'control.url := } if$ CTL_doi empty$ 'skip$ { CTL_doi boolean.to.int 'control.doi := } if$ CTL_eprint empty$ 'skip$ { CTL_eprint boolean.to.int 'control.eprint := } if$ CTL_note empty$ 'skip$ { CTL_note boolean.to.int 'control.note := } if$ CTL_end_dot empty$ 'skip$ { CTL_end_dot boolean.to.int 'control.end.dot := } if$ CTL_warn_empty_field empty$ 'skip$ { CTL_warn_empty_field boolean.to.int 'control.warn.empty.field := } if$ } FUNCTION {control.pass} { type$ "gbt7714bstctl" = 'GBT7714BSTCTL 'skip$ if$ } FUNCTION {control.check} { % TODO } % \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 \cs{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{Format labels} % % 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$ } % \end{macrocode} % % The |format.lab.names| function makes a short label by using the initials of % the von and Last parts of the names (but if there are more than four names, % (i.e., people) it truncates after three and adds a superscripted "+"; % it also adds such a "+" if the last of multiple authors is "others"). % If there is only one name, and its von and Last parts combined have just % a single name-token ("Knuth" has a single token, "Brinch Hansen" has two), % we take the first three letters of the last name. The boolean % et.al.char.used tells whether we've used a superscripted "+", so that we % know whether to include a LaTeX macro for it. % % \begin{pseudocode} % format.lab.names(s) == % BEGIN % numnames := num.names$(s) % if numnames > 1 then % if numnames > 4 then % namesleft := 3 % else % namesleft := numnames % nameptr := 1 % nameresult := "" % while namesleft > 0 % do % if (name_ptr = numnames) and % format.name$(s, nameptr, "{ff }{vv }{ll}{ jj}") = "others" % then nameresult := nameresult * "{\etalchar{+}}" % et.al.char.used := true % else nameresult := nameresult * % format.name$(s, nameptr, "{v{}}{l{}}") % nameptr := nameptr + 1 % namesleft := namesleft - 1 % od % if numnames > 4 then % nameresult := nameresult * "{\etalchar{+}}" % et.al.char.used := true % else % t := format.name$(s, 1, "{v{}}{l{}}") % if text.length$(t) < 2 then % there's just one name-token % nameresult := text.prefix$(format.name$(s,1,"{ll}"),3) % else % nameresult := t % fi % fi % return nameresult % END % \end{pseudocode} % % Exactly what fields we look at in constructing the primary part of the label % depends on the entry type; this selectivity (as opposed to, say, always % looking at author, then editor, then key) helps ensure that "ignored" fields, % as described in the LaTeX book, really are ignored. Note that MISC is part % of the deepest `else' clause in the nested part of calc.label; thus, any % unrecognized entry type in the database is handled correctly. % % There is one auxiliary function for each of the four different sequences of % fields we use. The first of these functions looks at the author field, and % then, if necessary, the key field. The other three functions, which might % look at two fields and the key field, are similar, except that the key field % takes precedence over the organization field (for labels---not for sorting). % % The calc.label function calculates the preliminary label of an entry, which % is formed by taking three letters of information from the author or editor or % key or organization field (depending on the entry type and on what's empty, % but ignoring a leading "The " in the organization), and appending the last % two characters (digits) of the year. It is an error if the appropriate fields % among author, editor, organization, and key are missing, and we use % the first three letters of the |cite$| in desperation when this happens. % The resulting label has the year part, but not the name part, |purify$|ed % (|purify$|ing the year allows some sorting shenanigans by the user). % % This function also calculates the version of the label to be used in sorting. % % The final label may need a trailing 'a', 'b', etc., to distinguish it from % otherwise identical labels, but we can't calculated those "extra.label"s % until after sorting. % % \begin{pseudocode} % calc.label == % BEGIN % if type$ = "book" or "inbook" then % author.editor.key.label % else if type$ = "proceedings" then % editor.key.organization.label % else if type$ = "manual" then % author.key.organization.label % else % author.key.label % fi fi fi % label := label * substring$(purify$(field.or.null(year)), -1, 2) % % assuming we will also sort, we calculate a sort.label % sort.label := sortify(label), but use the last four, not two, digits % END % \end{pseudocode} % \begin{macrocode} FUNCTION {format.lab.names} { 's := #1 'nameptr := s num.names$ 'numnames := numnames 'namesleft := { namesleft #0 > } { format.lab.name 't := nameptr #1 > { numnames control.max.cite.names > nameptr control.min.cite.names > and { "others" 't := #1 'namesleft := } 'skip$ if$ namesleft #1 > { add.comma t * } { t "others" = { nameptr #2 > { add.comma } { space.precedes.et.al * } if$ cite.et.al * } { control.cite.final.and { is.lang.cjk not nameptr #2 > and { add.comma } { inter.word.space * } if$ cite.and * inter.word.space * } { add.comma } if$ t * } if$ } if$ } 't if$ nameptr #1 + 'nameptr := namesleft #1 - 'namesleft := } while$ format.punctuations } FUNCTION {author.key.label} { author empty$ { bbl.anonymous } { author format.lab.names } if$ } FUNCTION {author.editor.key.label} { author empty$ { editor empty$ { bbl.anonymous } { editor format.lab.names } if$ } { author format.lab.names } if$ } FUNCTION {author.key.organization.label} { author empty$ { organization empty$ { bbl.anonymous } { "The " #4 organization chop.word #3 text.prefix$ } if$ } { author format.lab.names } if$ } FUNCTION {editor.key.organization.label} { editor empty$ { organization empty$ { bbl.anonymous } { "The " #4 organization chop.word #3 text.prefix$ } if$ } { editor format.lab.names } if$ } FUNCTION {holder.author.label} { holder empty$ { author empty$ { bbl.anonymous } { author format.lab.names } if$ } { holder format.lab.names } if$ } FUNCTION {calc.short.authors} { type$ "book" = type$ "inbook" = booktitle empty$ not and or 'author.editor.key.label { type$ "collection" = type$ "proceedings" = or type$ "periodical" = or { editor empty$ not 'editor.key.organization.label 'author.key.organization.label if$ } { type$ "patent" = 'holder.author.label 'author.key.label if$ } if$ } if$ 'short.list := } % \end{macrocode} % % 如果 label 中有中括号“[”,分别用大括号保护起来,防止 \cs{bibitem} 处理出错。 % 另外为了兼容 \pkg{bibunits},“name(year)fullname” 的每一项都要分别保护起来, % 参考 \href{https://github.com/tuna/thuthesis/issues/630}{tuna/thuthesis/\#630}。 % \begin{macrocode} FUNCTION {calc.label} { calc.short.authors short.list duplicate$ "]" contains { "{" swap$ * "}" * } 'skip$ if$ "(" * format.lab.date duplicate$ "]" contains { "{" swap$ * "}" * } 'skip$ if$ * 'label := } % \end{macrocode} % % % \subsection{Sorting} % % 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 = { lang.zh.order } { entry.lang lang.ja = { lang.ja.order } { entry.lang lang.en = { lang.en.order } { entry.lang lang.ru = { lang.ru.order } { lang.other.order } if$ } if$ } if$ } if$ #64 + int.to.chr$ } 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} { entry.lang lang.zh = { "yi4 ming2" } { "anon" } if$ } FUNCTION {warn.empty.key} { is.lang.cjk { "key" warn.empty.field } 'skip$ if$ } FUNCTION {author.sort} { key empty$ { warn.empty.key author empty$ { anonymous.sort } { author sort.format.names } if$ } { key } if$ } FUNCTION {author.editor.sort} { key empty$ { warn.empty.key author empty$ { editor empty$ { anonymous.sort } { editor sort.format.names } if$ } { author sort.format.names } if$ } { key } if$ } FUNCTION {author.organization.sort} { key empty$ { warn.empty.key author empty$ { organization empty$ { anonymous.sort } { "The " #4 organization chop.word sortify } if$ } { author sort.format.names } if$ } { key } if$ } FUNCTION {editor.organization.sort} { key empty$ { warn.empty.key editor empty$ { organization empty$ { anonymous.sort } { "The " #4 organization chop.word sortify } if$ } { editor sort.format.names } if$ } { key } if$ } % % \end{macrocode} % % 顺序编码制的排序要简单得多 % \begin{macrocode} %<*numeric> INTEGERS { seq.num } FUNCTION {init.seq} { #0 'seq.num :=} FUNCTION {int.to.fix} { "000000000" swap$ int.to.str$ * #-1 #10 substring$ } % % \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} { type$ "gbt7714bstctl" = { %<*authoryear> "_" " " % %<*numeric> "_ " seq.num int.to.fix % } { set.entry.lang set.entry.numbered "" 'entry.url := #0 'require.url := check.electronic calc.label label sortify " " * %<*authoryear> sort.language.label " " * type$ "book" = type$ "inbook" = booktitle empty$ not and 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$ % %<*numeric> seq.num #1 + 'seq.num := seq.num int.to.fix % } if$ 'sort.label := sort.label * #1 entry.max$ substring$ 'sort.key$ := } % \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 last.extra.label } INTEGERS { longest.label.width number.label } FUNCTION {initialize.longest.label} { "" 'longest.label := #0 int.to.chr$ 'last.label := "" 'next.extra := #0 'longest.label.width := #0 'number.label := "" 'last.extra.label := } FUNCTION {forward.pass} { type$ "gbt7714bstctl" = 'skip$ { %<*authoryear> last.label label = { "" 'extra.label := last.extra.label text.length$ 'charptr := { last.extra.label charptr #1 substring$ "z" = charptr #0 > and } { "a" extra.label * 'extra.label := charptr #1 - 'charptr := } while$ charptr #0 > { last.extra.label charptr #1 substring$ chr.to.int$ #1 + int.to.chr$ extra.label * 'extra.label := last.extra.label #1 charptr #1 - substring$ extra.label * 'extra.label := } { "a" extra.label * 'extra.label := } if$ extra.label 'last.extra.label := } { "a" 'last.extra.label := "" 'extra.label := label 'last.label := } if$ % number.label #1 + 'number.label := } if$ } FUNCTION {reverse.pass} { type$ "gbt7714bstctl" = 'skip$ { %<*authoryear> 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 := } if$ } FUNCTION {bib.sort.order} { sort.label 'sort.key$ := } % \end{macrocode} % % % \subsection{Write bbl file} % % 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$ control.cite.lang "macro" = { "\providecommand{\biband}{和}" write$ newline$ "\providecommand{\bibetal}{等}" write$ newline$ } 'skip$ if$ "\providecommand{\natexlab}[1]{#1}" write$ newline$ "\providecommand{\url}[1]{#1}" write$ newline$ "\expandafter\ifx\csname urlstyle\endcsname\relax\else" write$ newline$ " \urlstyle{same}\fi" write$ newline$ control.doi control.eprint or { "\expandafter\ifx\csname href\endcsname\relax" write$ newline$ control.doi { " \def\doi{\begingroup \urlstyle{same}\Url}" write$ newline$ control.cstr { " \def\cstr{\begingroup \urlstyle{same}\Url}" write$ newline$ } 'skip$ if$ } 'skip$ if$ control.eprint { " \def\eprint#1#2{#2}" write$ newline$ } 'skip$ if$ "\else" write$ newline$ control.doi { " \def\doi#1{\href{https://doi.org/#1}{\nolinkurl{#1}}}" write$ newline$ control.cstr { " \def\cstr#1{\href{https://cstr.cn/#1}{\nolinkurl{#1}}}" write$ newline$ } 'skip$ if$ } 'skip$ if$ control.eprint { " \let\eprint\href" write$ newline$ } 'skip$ if$ "\fi" write$ newline$ } 'skip$ if$ } % \end{macrocode} % % Finally, we finish up by writing the `|\end{thebibliography}|' command. % \begin{macrocode} FUNCTION {end.bib} { newline$ "\end{thebibliography}" write$ newline$ } % \end{macrocode} % % % \subsection{Main execution} % % Now we read in the .BIB entries. % \begin{macrocode} READ EXECUTE {init.state.consts} EXECUTE {load.config} ITERATE {control.pass} EXECUTE {control.check} %<*numeric> EXECUTE {init.seq} % ITERATE {presort} % \end{macrocode} % % And now we can sort % \begin{macrocode} SORT EXECUTE {initialize.longest.label} ITERATE {forward.pass} REVERSE {reverse.pass} ITERATE {bib.sort.order} SORT EXECUTE {begin.bib} % \end{macrocode} % % Now we produce the output for all the entries % \begin{macrocode} ITERATE {call.type$} EXECUTE {end.bib} % \end{macrocode}