summaryrefslogtreecommitdiff
path: root/biblio/bibtex
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2023-01-10 03:01:21 +0000
committerNorbert Preining <norbert@preining.info>2023-01-10 03:01:21 +0000
commitdecba0e4470fd7bfe288f98ee709553fc13014b8 (patch)
tree781f266fc727864e1c9bb62f07c1069dc926fbec /biblio/bibtex
parent61f8490e549cb7af263b9d903bc20d61ea37a2d7 (diff)
CTAN sync 202301100301
Diffstat (limited to 'biblio/bibtex')
-rw-r--r--biblio/bibtex/utils/bibcop/DEPENDS.txt2
-rw-r--r--biblio/bibtex/utils/bibcop/LICENSE.txt21
-rw-r--r--biblio/bibtex/utils/bibcop/README.md64
-rw-r--r--biblio/bibtex/utils/bibcop/bibcop-logo.pdfbin0 -> 5100 bytes
-rw-r--r--biblio/bibtex/utils/bibcop/bibcop.130
-rw-r--r--biblio/bibtex/utils/bibcop/bibcop.dtx471
-rw-r--r--biblio/bibtex/utils/bibcop/bibcop.ins55
-rw-r--r--biblio/bibtex/utils/bibcop/bibcop.pdfbin0 -> 342958 bytes
-rwxr-xr-xbiblio/bibtex/utils/bibcop/bibcop.pl635
9 files changed, 1278 insertions, 0 deletions
diff --git a/biblio/bibtex/utils/bibcop/DEPENDS.txt b/biblio/bibtex/utils/bibcop/DEPENDS.txt
new file mode 100644
index 0000000000..32acfffc04
--- /dev/null
+++ b/biblio/bibtex/utils/bibcop/DEPENDS.txt
@@ -0,0 +1,2 @@
+hard iexec
+hard pgfopts
diff --git a/biblio/bibtex/utils/bibcop/LICENSE.txt b/biblio/bibtex/utils/bibcop/LICENSE.txt
new file mode 100644
index 0000000000..4351be233c
--- /dev/null
+++ b/biblio/bibtex/utils/bibcop/LICENSE.txt
@@ -0,0 +1,21 @@
+(The MIT License)
+
+Copyright (c) 2022-2023 Yegor Bugayenko
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the 'Software'), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/biblio/bibtex/utils/bibcop/README.md b/biblio/bibtex/utils/bibcop/README.md
new file mode 100644
index 0000000000..471c4dcdcb
--- /dev/null
+++ b/biblio/bibtex/utils/bibcop/README.md
@@ -0,0 +1,64 @@
+<img src="https://raw.githubusercontent.com/yegor256/bibcop/master/bibcop-logo.svg" height="92px"/>
+
+[![l3build](https://github.com/yegor256/bibcop/actions/workflows/l3build.yml/badge.svg)](https://github.com/yegor256/bibcop/actions/workflows/l3build.yml)
+[![CTAN](https://img.shields.io/ctan/v/bibcop)](https://ctan.org/pkg/bibcop)
+[![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/yegor256/bibcop/blob/master/LICENSE.txt)
+
+This LaTeX package checks the quality of your `.bib` file and
+emits warning message if any issues are found. You may also like
+[biblint](https://github.com/Kingsford-Group/biblint) and
+[biblatex-check](https://github.com/pezmc/biblatex-check) tools —
+they do _almost_ the same but from the command line.
+
+First, [install it](https://en.wikibooks.org/wiki/LaTeX/Installing_Extra_Packages)
+from [CTAN](https://ctan.org/pkg/bibcop)
+and then use in the preamble (if you use BibTeX, for example):
+
+```tex
+\documentclass{article}
+\usepackage{bibcop}
+\begin{document}
+\bibliographystyle{plain}
+\bibliography{main}
+\end{document}
+```
+
+Otherwise, you can download [`bibcop.sty`](https://raw.githubusercontent.com/yegor256/bibcop/gh-pages/bibcop/bibcop.sty) and add to your project (together with [`bibcop.pl`](https://raw.githubusercontent.com/yegor256/bibcop/master/bibcop.pl)!).
+
+You can also download [`bibcop.pl`](https://raw.githubusercontent.com/yegor256/bibcop/master/bibcop.pl)
+and use it as a command line tool
+to check your `.bib` files and to auto-fix them
+(you should have [Perl](https://www.perl.org) installed):
+
+```
+$ perl bibcop.pl --fix main.bib > fixed.bib
+```
+
+This command will read the `main.bib` file and create `fixed.bib`, which
+will have the fixed and properly formatted content (well, to some extent).
+
+If you install the package using [`tlmgr`](https://www.tug.org/texlive/tlmgr.html),
+you should be able to use `bibcop` directly, without the necessity to mention Perl:
+
+```
+$ tlgmr install bibcop
+$ bibcop --help
+```
+
+## How to Contribute
+
+If you want to contribute yourself, make a fork, then create a branch,
+then run `l3build ctan` in the root directory.
+It should compile everything without errors. If not, submit an issue and wait.
+Otherwise, make your changes and then run `l3build ctan` again. If the build is
+still clean, submit a pull request.
+
+If you want to add a new check, add it as a Perl subroutine to the `bibcop.pl` file.
+Don't forget to add a test to one of the test files that stay in the `perl-tests/` directory.
+When ready, run this, in order to check that all tests pass:
+
+```bash
+$ perl tests.pl
+```
+
+You should see the `GREAT!` message. \ No newline at end of file
diff --git a/biblio/bibtex/utils/bibcop/bibcop-logo.pdf b/biblio/bibtex/utils/bibcop/bibcop-logo.pdf
new file mode 100644
index 0000000000..497d52dc0b
--- /dev/null
+++ b/biblio/bibtex/utils/bibcop/bibcop-logo.pdf
Binary files differ
diff --git a/biblio/bibtex/utils/bibcop/bibcop.1 b/biblio/bibtex/utils/bibcop/bibcop.1
new file mode 100644
index 0000000000..4e45ba6c02
--- /dev/null
+++ b/biblio/bibtex/utils/bibcop/bibcop.1
@@ -0,0 +1,30 @@
+.TH bibcop 1 "2023-01-08"
+.SH NAME
+bibcop \- Style Checker and Fixer of BibTeX Files (.bib)
+.SH SYNOPSIS
+bibcop [<options>] <.bib file path>
+.SH DESCRIPTION
+The bibcop tool is a Perl script for checking the quality of BibTeX bibliograph
+files (.bib) and automatically fixing found inconsistencies. It is not only
+a command line tool, but a LaTeX package, which can check the quality
+of a .bib file during the rendering of a LaTeX document. All found
+issues are reported as LaTeX warnings.
+.SH OPTIONS
+Various options apply:
+.IP --version
+Print the version of the tool
+.IP --help
+Print the introduction screen
+.IP --fix
+Autho-fix all issues and print a new content to the console
+.IP --latex
+Print everything in LaTeX format
+.IP --verbose
+Print debugging information too
+.SH AUTHOR
+Yegor Bugayenko (yegor256@gmail.com)
+.SH BUGS
+Please log issues on the GitHub homepage:
+https://github.com/yegor256/bibcop/issues.
+.SH SEE ALSO
+See bibcop.pdf for more details.
diff --git a/biblio/bibtex/utils/bibcop/bibcop.dtx b/biblio/bibtex/utils/bibcop/bibcop.dtx
new file mode 100644
index 0000000000..d084f00961
--- /dev/null
+++ b/biblio/bibtex/utils/bibcop/bibcop.dtx
@@ -0,0 +1,471 @@
+% \iffalse meta-comment
+% (The MIT License)
+%
+% Copyright (c) 2022-2023 Yegor Bugayenko
+%
+% Permission is hereby granted, free of charge, to any person obtaining a copy
+% of this software and associated documentation files (the 'Software'), to deal
+% in the Software without restriction, including without limitation the rights
+% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+% copies of the Software, and to permit persons to whom the Software is
+% furnished to do so, subject to the following conditions:
+%
+% The above copyright notice and this permission notice shall be included in all
+% copies or substantial portions of the Software.
+%
+% THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+% SOFTWARE.
+% \fi
+
+% \CheckSum{0}
+%
+% \CharacterTable
+% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
+% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
+% Digits \0\1\2\3\4\5\6\7\8\9
+% Exclamation \! Double quote \" Hash (number) \#
+% Dollar \$ Percent \% Ampersand \&
+% Acute accent \' Left paren \( Right paren \)
+% Asterisk \* Plus \+ Comma \,
+% Minus \- Point \. Solidus \/
+% Colon \: Semicolon \; Less than \<
+% Equals \= Greater than \> Question mark \?
+% Commercial at \@ Left bracket \[ Backslash \\
+% Right bracket \] Circumflex \^ Underscore \_
+% Grave accent \` Left brace \{ Vertical bar \|
+% Right brace \} Tilde \~}
+
+% \GetFileInfo{bibcop.dtx}
+% \DoNotIndex{\endgroup,\begingroup,\let,\else,\s,\n,\r,\\,\1,\fi}
+
+% \iffalse
+%<*driver>
+\ProvidesFile{bibcop.dtx}
+%</driver>
+%<package>\NeedsTeXFormat{LaTeX2e}
+%<package>\ProvidesPackage{bibcop}
+%<*package>
+[2023-01-08 0.0.6 Style Checker of Bibliography Files]
+%</package>
+%<*driver>
+\documentclass{ltxdoc}
+\usepackage[T1]{fontenc}
+\usepackage[maxnames=1,minnames=1,maxbibnames=1,natbib=true,citestyle=authoryear,bibstyle=authoryear,doi=false,url=false,isbn=false,isbn=false]{biblatex}
+\addbibresource{bibcop.bib}
+\usepackage[tt=false, type1=true]{libertine}
+\usepackage{microtype}
+\AddToHook{env/verbatim/begin}{\microtypesetup{protrusion=false}}
+\usepackage{href-ul}
+\usepackage{xcolor}
+\usepackage{graphicx}
+\PageIndex
+\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+ \DocInput{bibcop.dtx}
+ \PrintChanges
+ \PrintIndex
+\end{document}
+%</driver>
+% \fi
+
+% \title{\includegraphics[height=1in]{bibcop-logo.pdf} \\ |bibcop|: \LaTeX{} Package \\ for Style Checking of |.bib| Files\thanks{The sources are in GitHub at \href{https://github.com/yegor256/bibcop}{yegor256/bibcop}}}
+% \author{Yegor Bugayenko \\ \texttt{yegor256@gmail.com}}
+% \date{\filedate, \fileversion}
+%
+% \maketitle
+%
+% \textbf{\color{red}NB!}
+% You must run \TeX{} processor with |--shell-escape| option
+% and you must have \href{https://www.perl.org}{Perl} installed.
+% This package doesn't work on Windows.
+
+% \section{Introduction}
+%
+% This package scans a |.bib| file for style errors and emits
+% warning messages if any issues are found (the package must be included before
+% all other bibliography related packages):
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+\documentclass{article}
+\usepackage{bibcop}
+\begin{document}
+\bibliographystyle{plain}
+\bibliography{main}
+\end{document}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+% Some warnings may be printed in the \TeX{} log.
+% Once the issues in the |main.bib| file are fixed, the warnings disappear.
+
+% Bibcop doesn't pay much attention the formatting of the |.bib| file. It doesn't emit
+% warnings if a comma is missed after the last tag in a \BibTeX{} entry or the tags
+% and their equation symbols are not aligned. Instead, Bibcop is focused on the content
+% of the tags and their possible inconsistencies.
+
+% If the |.sty| file is used directly (without installing it into the \TeX{} tree),
+% the |bibcop.pl| file must also be placed next to it --- it is the Perl script that does all the work
+% of checking your |.bib| files. The |.sty| is just a simple wrapper around it.
+
+% The |\usepackage{bibcop}| must stay right after |\usepackage{biblatex}|
+% (if Bib\LaTeX{} is used), otherwise
+% there won't be any warnings from |bibcop|.
+
+% \section{Package Options}
+
+% It's possible to configure the behavior of the package with the help of a few package options:
+
+% \DescribeMacro{verbose}
+% The |verbose| package option prints all debugging messages to the \TeX{} log:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+\usepackage[verbose]{bibcop}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{script}
+% The package depends on the |bibcop.pl| file, which is a Perl script that does all the work. This file is supposed to be located either in the current directory or in the |texmf-dist/scripts/bibcop/| directory. Using the |script| option the location of the script may be explicitly defined (it is not recommended to use this option unless there is a special demand for it):
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+\usepackage[script=my-perl-script.pl]{bibcop}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \section{The Rules}
+
+% This is a more or less complete list of rules enforced on a |.bib| file:
+
+% \DescribeMacro{types}
+% Only |@article|, |@book|, |@inproceedings|, and |@misc| types of \BibTeX{} entries are allowed.
+% Everything else, like |@manual|, |@phdthesis|, and many others are simply prohibited.
+% The mentioned four should be enough for everything.
+
+% \DescribeMacro{tags}
+% There are pretty limited lists of allowed tags for each type of \BibTeX{} entry. The tags
+% that are not in the list are prohibited to use.
+
+% \DescribeMacro{doi}
+% Every \BibTeX{} entry must have the |doi| tag, which is a unique
+% \href{https://www.doi.org}{Digital Object Identifier}
+% of the material that you reference. It seems to be a good practice, in order to avoid ambiguity,
+% to always mention the DOI.
+% It also recommended to use the \href{https://ctan.org/pkg/iexec}{doi} package,
+% in order to make all ``|doi|'' fields turned into hyper links.
+
+% \DescribeMacro{caps}
+% In the |title|, |booktitle|, and |journal| tags, all major words must be capitalized,
+% as it is \href{https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case}{recommended} by APA:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+title = {A Preliminary Architecture for a Basic Data-Flow Processor}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+% Here, the leading ``|A|'' is capital because it opens the title.
+% The word ``|for|'' and the article ``|a|'' are minor words, that's why they are in lower case.
+% Both parts of the composite word ``|Data-Flow|'' are capitalized.
+% Sometimes this rule may need to be violated, when there is custom capitalization, as it is done
+% by the author of the paper. In order to do this, the words with custom capitalization
+% must be wrapped in curled brackets, for example:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+title = {Structured Programming {with} Go {To} Statements}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{author}
+% The |author| must contain a list of authors separated by ``|and|''. Each author
+% must have two parts separated by a comma. The first part is the last name of the author,
+% the second part is a list of their first names separated by a space, for example:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+author = {Knuth, Donald E. and Duane, Bibby}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+% When the list of authors is too long, it's possible to say ``|and others|'':
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+author = {Knuth, Donald E. and others}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+% When first names are shortened to a single letter, it has to have a tailing dot.
+% A specially pronounced author may be wrapped it in curled brackets, to make Bibcop ignore it:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+author = {{Some author} and {I}}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{shorts}
+% It is not allowed to shorten any words aside from the |author| tag, for example this is illegal:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+journal = {J. Log. Comput.}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+% This must be replaced with the following:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+journal = {Journal of Logic and Computation}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{brackets}
+% The |title|, |booktitle|, and |journal| must be wrapped in double brackets, for example:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+title = {{A Survey of Symbolic Execution Techniques}}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+% This is necessary in order to prevent down-casing of capitalized words, which is done by some
+% bibliography styles.
+
+% \DescribeMacro{year}
+% It is not allowed to mention the year inside the title of a conference or a journal, for example,
+% this would be illegal:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+booktitle = {{1994 IEEE International Conference on Computer Languages}},
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+% The year should only be mentioned in the |year| tag, nowhere else.
+% In the |year| tag only numbers are allowed:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+year = {1994},
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{month}
+% The |month| may contain only a number:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+month = {12},
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{volume}
+% The |volume| may contain only a number:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+volume = {32},
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{number}
+% The |number| may contain only a number:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+number = {132},
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{pages}
+% The |pages| may contain either a number or two numbers separated by a double dash:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+pages = {145--163},
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{proceedings}
+% The |booktitle| in the |@inproceedings| entry must always start with ``|Proceedings| |of| |the|'',
+% as in this example:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+booktitle = {{Proceedings of the International
+ Conference on Computer Languages}},
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{arXiv}
+% If the |archivePrefix| is present, the |eprint| and the |primaryClass| must also be present and must adhere to the formatting principles of \href{https://arxiv.org/help/arxiv_identifier}{arXiv identifiers}:
+%\iffalse
+%<*verb>
+%\fi
+\begin{verbatim}
+@misc{bugayenko2021,
+ archivePrefix = {arXiv},
+ eprint = {2111.13384},
+ primaryClass = {cs.PL},
+}
+\end{verbatim}
+%\iffalse
+%</verb>
+%\fi
+
+% \DescribeMacro{typography}
+% All tags in each \BibTeX{} entry are checked for obeying the basic typography rules:
+% \begin{itemize}
+% \item No spaces are allowed in front of a comma, a semi-colon, a colon, a dot, a question mark, and an exclamation mark;
+% \item A text may not end with a dot, a comma, a semi-colon, a colon, or a dash;
+% \item A triple dash must be surrounded by spaces.
+% \end{itemize}
+
+% \StopEventually{}
+
+% \section{Implementation}
+% \changes{0.0.1}{2022/12/11}{First draft.}
+% \changes{0.0.2}{2022/12/12}{Documentation extended, more rules added.}
+% \changes{0.0.4}{2022/12/14}{Extra checks for the typography, together with more extensive Perl testing.}
+% \changes{0.0.4}{2022/12/14}{The \texttt{--verbose} option introduced, to enable debugging information only on demand.}
+% \changes{0.0.4}{2022/12/15}{Package options introduced, the \texttt{verbose} option enables detailed logging inside the \TeX{} log.}
+% \changes{0.0.6}{2023/01/08}{The \texttt{script} package option introduced, to enable explicit configuration of the location of the \texttt{bibcop.pl} Perl script.}
+
+% First, we include a few packages.
+% We need \href{https://ctan.org/pkg/iexec}{iexec} for executing Perl scripts:
+% \begin{macrocode}
+\RequirePackage{iexec}
+% \end{macrocode}
+
+% Then, we process package options:
+% \begin{macrocode}
+\RequirePackage{pgfopts}
+\pgfkeys{
+ /bibcop/.cd,
+ verbose/.store in=\bibcop@verbose,
+ script/.store in = \bibcop@script,
+}
+\ProcessPgfPackageOptions{/bibcop}
+% \end{macrocode}
+
+% \begin{macro}{bibcop.pl}
+% Then, we find the Perl script:
+% \begin{macrocode}
+\makeatletter
+\ifdefined\bibcop@script\else
+ \IfFileExists{bibcop.pl}
+ {\gdef\bibcop@script{perl ./bibcop.pl}}
+ {\gdef\bibcop@script{bibcop}}
+\fi
+\message{bibcop: The Perl script is at '\bibcop@script'^^J}%
+\makeatother
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\bibliography}
+% Then, we re-define the |\bibliography| command:
+% \begin{macrocode}
+\makeatletter
+\ifdefined\bibliography
+ \let\bibcop@oldbibliography\bibliography
+ \renewcommand\bibliography[1]{%
+ \iexec{\bibcop@script\space
+ \ifdefined\bibcop@verbose--verbose\fi\space
+ --latex '#1.bib'}%
+ \message{bibcop: style checking finished^^J}%
+ \bibcop@oldbibliography{#1}%
+ }
+\fi
+\makeatother
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\addbibresource}
+% Then, we re-define the |\addbibresource| command:
+% \begin{macrocode}
+\makeatletter
+\ifdefined\addbibresource
+ \let\bibcop@oldaddbibresource\addbibresource
+ \renewcommand\addbibresource[1]{%
+ \iexec{\bibcop@script\space
+ \ifdefined\bibcop@verbose--verbose\fi\space
+ --latex '#1'}%
+ \message{bibcop: style checking finished^^J}%
+ \bibcop@oldaddbibresource{#1}%
+ }
+\fi
+\makeatother
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macrocode}
+\endinput
+% \end{macrocode}
+
+% \Finale
+
+% \clearpage
+% \printbibliography
+% \clearpage
+
+% \PrintChanges
+% \clearpage
+% \PrintIndex
diff --git a/biblio/bibtex/utils/bibcop/bibcop.ins b/biblio/bibtex/utils/bibcop/bibcop.ins
new file mode 100644
index 0000000000..d0ea50f43c
--- /dev/null
+++ b/biblio/bibtex/utils/bibcop/bibcop.ins
@@ -0,0 +1,55 @@
+%% (The MIT License)
+%%
+%% Copyright (c) 2022-2023 Yegor Bugayenko
+%%
+%% Permission is hereby granted, free of charge, to any person obtaining a copy
+%% of this software and associated documentation files (the 'Software'), to deal
+%% in the Software without restriction, including without limitation the rights
+%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the Software is
+%% furnished to do so, subject to the following conditions:
+%%
+%% The above copyright notice and this permission notice shall be included in all
+%% copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+%% SOFTWARE.
+
+\input docstrip.tex
+\keepsilent
+\usedir{macros/latex/contrib/bibcop}
+\preamble
+(The MIT License)
+
+Copyright (c) 2022-2023 Yegor Bugayenko
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the 'Software'), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+\endpreamble
+\generate{\file{bibcop.sty}{\from{bibcop.dtx}{package}}}
+\obeyspaces
+\Msg{To finish the installation you have to move the ".sty"}
+\Msg{file into a directory searched by TeX. To produce the}
+\Msg{documentation run the file ".dtx" through LaTeX. If any}
+\Msg{questions, submit a new GitHub issue.}
+\endbatchfile
diff --git a/biblio/bibtex/utils/bibcop/bibcop.pdf b/biblio/bibtex/utils/bibcop/bibcop.pdf
new file mode 100644
index 0000000000..1971661357
--- /dev/null
+++ b/biblio/bibtex/utils/bibcop/bibcop.pdf
Binary files differ
diff --git a/biblio/bibtex/utils/bibcop/bibcop.pl b/biblio/bibtex/utils/bibcop/bibcop.pl
new file mode 100755
index 0000000000..3deda06e45
--- /dev/null
+++ b/biblio/bibtex/utils/bibcop/bibcop.pl
@@ -0,0 +1,635 @@
+#!/usr/bin/perl
+# (The MIT License)
+#
+# Copyright (c) 2022-2023 Yegor Bugayenko
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the 'Software'), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+package bibcop;
+
+use warnings;
+use strict;
+
+# Hash of incoming command line arguments.
+my %args = map { $_ => 1 } @ARGV;
+
+# If you want to add an extra check, just create a new procedure
+# named as "check_*".
+
+# Only these tags are allowed and only these types of entries.
+my %blessed = (
+ 'article' => ['doi', 'year', 'title', 'author', 'journal', 'volume', 'number', 'publisher?', 'pages?'],
+ 'inproceedings' => ['doi', 'booktitle', 'title', 'author', 'year', 'pages?', 'organization?', 'volume?'],
+ 'book' => ['doi', 'title', 'author', 'year', 'publisher'],
+ 'misc' => ['title', 'author', 'year', 'eprint?', 'archiveprefix?', 'primaryclass?', 'publisher?', 'organization?', 'doi?', 'url?'],
+);
+
+# Check the presence of mandatory tags.
+sub check_mandatory_tags {
+ my (%entry) = @_;
+ my $type = $entry{':type'};
+ my $mandatory = $blessed{$type};
+ foreach my $tag (@$mandatory) {
+ if ($tag =~ /^.*\?$/) {
+ next;
+ }
+ if (not(exists $entry{$tag})) {
+ my $listed = listed_tags(%entry);
+ return "A mandatory '$tag' tag for '\@$type' is missing among $listed"
+ }
+ }
+ if (exists $blessed{$type}) {
+ my %required = map { $_ => 1 } @$mandatory;
+ foreach my $tag (keys %entry) {
+ if ($tag =~ /^:/) {
+ next;
+ }
+ if (not(exists $required{$tag}) && not(exists $required{$tag . '?'})) {
+ return "The '$tag' tag is not suitable for '$type', use only these: (@$mandatory)"
+ }
+ }
+ }
+}
+
+# Check that all major words are capitalized.
+sub check_capitalization {
+ my (%entry) = @_;
+ my %tags = map { $_ => 1 } qw/title booktitle journal publisher organization/;
+ my %minors = map { $_ => 1 } qw/in of at to by the a an and or as if up via yet nor but off on for into/;
+ foreach my $tag (keys %entry) {
+ if (not exists $tags{$tag}) {
+ next;
+ }
+ my $value = $entry{$tag};
+ my @words = only_words($value);
+ my $pos = 0;
+ foreach my $word (@words) {
+ if (not $word =~ /^[A-Za-z]/) {
+ next;
+ }
+ $pos = $pos + 1;
+ if (exists $minors{$word}) {
+ next;
+ }
+ if (exists $minors{lc($word)} and $pos gt 1) {
+ return "All minor words in the '$tag' must be lower-cased, while '$word' (no.$pos) is not"
+ }
+ if ($word =~ /^[a-z].*/) {
+ return "All major words in the '$tag' must be capitalized, while '$word' (no.$pos) is not"
+ }
+ }
+ }
+}
+
+# Check that the 'author' is formatted correctly.
+sub check_author {
+ my (%entry) = @_;
+ if (exists $entry{'author'} and not $entry{'author'} =~ /^\{.+\}$/) {
+ my $author = clean_tex($entry{'author'});
+ if (not $author =~ /^[A-Z][^ ]+(,( [A-Z][^ ]+)+)?( and [A-Z][^ ]+(,( [A-Z][^ ]+)+)?)*( and others)?$/) {
+ return "The format of the 'author' is wrong, use something like 'Knuth, Donald E. and Duane, Bibby'"
+ }
+ if ($author =~ /.*[A-Z]([ ,]|$).*/) {
+ return "A shortened name must have a tailing dot, as in 'Knuth, Donald E.'"
+ }
+ }
+}
+
+# Check that titles don't have shortened words with a tailing dot.
+sub check_shortenings {
+ my (%entry) = @_;
+ my %tags = map { $_ => 1 } qw/title booktitle journal/;
+ foreach my $tag (keys %entry) {
+ if (not exists $tags{$tag}) {
+ next;
+ }
+ my $value = $entry{$tag};
+ my @words = only_words($value);
+ foreach my $word (@words) {
+ if (not $word =~ /^[A-Za-z]/) {
+ next;
+ }
+ if ($word =~ /^.*\.$/) {
+ return "Do not shorten the words in the '$tag', such as '$word'"
+ }
+ }
+ }
+}
+
+# Check the right format of the 'title' and 'booktitle.'
+sub check_titles {
+ my (%entry) = @_;
+ my @tags = qw/title booktitle/;
+ foreach my $tag (@tags) {
+ if (not exists($entry{$tag})) {
+ next;
+ }
+ my $title = $entry{$tag};
+ if (not $title =~ /^\{.+\}$/) {
+ return "The '$tag' must be wrapped in double curled brackets"
+ }
+ }
+}
+
+# Check the right format of the tags for arXiv.
+# See https://arxiv.org/help/arxiv_identifier
+sub check_arXiv {
+ my (%entry) = @_;
+ if (exists($entry{'archiveprefix'})) {
+ if (not exists $entry{'eprint'}) {
+ return "The 'eprint' is mandatory when 'archiveprefix' is there"
+ }
+ if (not $entry{'eprint'} =~ /^[0-9]{4}\.[0-9]{4,5}(v[0-9]+)?$/) {
+ return "The 'eprint' must have two integers separated by a dot"
+ }
+ my $eprint = $entry{'eprint'};
+ my ($head, $tail) = split(/\./, $eprint);
+ my $year = substr($head, 0, 2);
+ my $month = substr($head, 2);
+ if ($month > 12) {
+ return "The month '$month' of the 'eprint' is wrong, it can't be bigger than 12"
+ }
+ if (not exists $entry{'primaryclass'}) {
+ return "The 'primaryclass' is mandatory when 'archiveprefix' is there"
+ }
+ if (not $entry{'primaryclass'} =~ /^[a-z]{2,}\.[A-Z]{2}$/) {
+ return "The 'primaryclass' must have two parts, like 'cs.PL'"
+ }
+ }
+}
+
+# Check that organization is not mentioned in the booktitle.
+sub check_org_in_booktitle {
+ my (%entry) = @_;
+ my @orgs = ( 'ACM', 'IEEE' );
+ if (exists($entry{'booktitle'})) {
+ my $title = $entry{'booktitle'};
+ foreach my $o (@orgs) {
+ if ($title =~ /^.*\Q$o\E.*$/) {
+ return "The '$o' organization must not be mentioned in the booktitle, use 'publisher' tag instead"
+ }
+ }
+ if ($title =~ /^.*(ACM|IEEE).*$/) {
+ return "Don't mention the"
+ }
+ }
+}
+
+# Check that no values have tailing dots.
+# Check that there are no spaces before commans.
+sub check_typography {
+ my (%entry) = @_;
+ my %symbols = (
+ '.' => 'dot',
+ ',' => 'comma',
+ ';' => 'semi-colon',
+ ':' => 'colon',
+ '!' => 'exclamation mark',
+ '?' => 'question mark',
+ '-' => 'dash',
+ '--' => 'double dash',
+ '---' => 'triple dash',
+ );
+ my @spaces_around = ( '---' );
+ my @no_spaces_around = ( '--', '-' );
+ my @no_space_before = ( '.', ',', ';', ':', '?', '!' );
+ my @bad_tails = ( '.', ',', ';', ':', '-' );
+ foreach my $tag (keys %entry) {
+ if ($tag =~ /^:.*/) {
+ next;
+ }
+ my $value = $entry{$tag};
+ foreach my $s (@bad_tails) {
+ if ($s eq '.' and $tag eq 'author') {
+ next;
+ }
+ if ($value =~ /^.*\Q$s\E$/) {
+ return "The '$tag' must not end with a $symbols{$s}"
+ }
+ }
+ foreach my $s (@no_space_before) {
+ if ($value =~ /^.*\s\Q$s\E.*$/) {
+ return "In the '$tag', do not put a space before a $symbols{$s}"
+ }
+ }
+ foreach my $s (@spaces_around) {
+ if ($value =~ /^.*[^\s]\Q$s\E.*$/ or $value =~ /^.*\Q$s\E[^\s].*$/) {
+ return "In the '$tag', put spaces around a $symbols{$s}"
+ }
+ }
+ foreach my $s (@no_spaces_around) {
+ if ($value =~ /^.*\s\Q$s\E\s.*$/) {
+ return "In the '$tag', don't put spaces around a $symbols{$s}"
+ }
+ }
+ }
+}
+
+# Check the year is not mentioned in titles.
+sub check_year_in_titles {
+ my (%entry) = @_;
+ my @tags = qw/title booktitle journal/;
+ foreach my $tag (@tags) {
+ if (not exists($entry{$tag})) {
+ next;
+ }
+ my @words = only_words($entry{$tag});
+ foreach my $word (@words) {
+ if ($word =~ /^[1-9][0-9]{3}$/) {
+ return "The '$tag' must not contain the year $word, it is enough to have the 'year' tag"
+ }
+ }
+ }
+}
+
+# Check the right format of the 'booktitle' in the 'inproceedings' entry.
+sub check_booktile_of_inproceedings {
+ my (%entry) = @_;
+ my $tag = 'inproceedings';
+ if ($entry{':type'} eq $tag) {
+ if (exists $entry{'booktitle'}) {
+ my @words = only_words($entry{'booktitle'});
+ if (lc($words[0]) ne 'proceedings' or lc($words[1]) ne 'of' or lc($words[2]) ne 'the') {
+ return "The '$tag' must start with 'Proceedings of the ...'"
+ }
+ }
+ }
+}
+
+# Check the right format of the 'doi.'
+sub check_doi {
+ my (%entry) = @_;
+ if (exists $entry{'doi'}) {
+ my $doi = $entry{'doi'};
+ if (not $doi =~ /^[0-9a-zA-Z.]+\/[0-9a-zA-Z._\-)(]+$/) {
+ return "The format of the 'doi' is wrong"
+ }
+ }
+}
+
+# Check the right format of the 'year.'
+sub check_year {
+ my (%entry) = @_;
+ if (exists $entry{'year'}) {
+ my $year = $entry{'year'};
+ if (not $year =~ /^[0-9]{3,4}$/) {
+ return "The format of the 'year' is wrong"
+ }
+ }
+}
+
+# Check the right format of the 'month.'
+sub check_month {
+ my (%entry) = @_;
+ if (exists $entry{'month'}) {
+ my $month = $entry{'month'};
+ if (not $month =~ /^[1-9]|10|11|12$/) {
+ return "The format of the 'month' is wrong"
+ }
+ }
+}
+
+# Check the right format of the 'volume.'
+sub check_volume {
+ my (%entry) = @_;
+ if (exists $entry{'volume'}) {
+ my $volume = $entry{'volume'};
+ if (not $volume =~ /^[1-9][0-9]*$/) {
+ return "The format of the 'volume' is wrong"
+ }
+ }
+}
+
+# Check the right format of the 'number.'
+sub check_number {
+ my (%entry) = @_;
+ if (exists $entry{'number'}) {
+ my $number = $entry{'number'};
+ if (not $number =~ /^[1-9][0-9]*$/) {
+ return "The format of the 'number' is wrong"
+ }
+ }
+}
+
+# Check the right format of the 'pages.'
+sub check_pages {
+ my (%entry) = @_;
+ if (exists $entry{'pages'}) {
+ my $pages = $entry{'pages'};
+ if (not $pages =~ /^([1-9][0-9]*--[1-9][0-9]*|[1-9][0-9]*)$/) {
+ return "The format of the 'pages' is wrong"
+ }
+ my @parts = split(/--/, $pages);
+ if (@parts+0 eq 2) {
+ if ($parts[0] eq $parts[1]) {
+ return "The 'pages' mentions the same page twice, just use it once"
+ }
+ if ($parts[0] gt $parts[1]) {
+ return "The 'pages' are in the wrong order"
+ }
+ }
+ }
+}
+
+# Check unescaped symbols.
+sub check_unescaped_symbols {
+ my (%entry) = @_;
+ my @symbols = ( '&' );
+ foreach my $tag (keys %entry) {
+ if ($tag =~ /^:.*/) {
+ next;
+ }
+ my $value = $entry{$tag};
+ foreach my $s (@symbols) {
+ if ($value =~ /^(.*[^\\{]|)\Q$s\E.*$/) {
+ return "The '$tag' contains a dangerous unescaped symbol (wrap it in curled brackets if you really need it)"
+ }
+ }
+ }
+}
+
+# Check one entry.
+sub process_entry {
+ my (%entry) = @_;
+ my @checks;
+ foreach my $entry (keys %bibcop::) {
+ if ($entry =~ /^check_/) {
+ push(@checks, $entry);
+ }
+ }
+ my @sorted = sort @checks;
+ my @errors;
+ foreach my $check (@sorted) {
+ no strict 'refs';
+ my $err = $check->(%entry);
+ if ($err ne '') {
+ push(@errors, $err);
+ }
+ }
+ return @errors;
+}
+
+# Parse the incoming .bib file and return an array
+# of hash-maps, where each one is a bibentry.
+sub entries {
+ my ($bib) = @_;
+ my @entries;
+ my $s = 'top';
+ my %entry;
+ my $acc = '';
+ my $tag = '';
+ my $lineno = 0;
+ my $nest = 0;
+ my $escape = 0;
+ for my $pos (0..length($bib)-1) {
+ my $char = substr($bib, $pos, 1);
+ if ($char eq ' ') {
+ # ignore the white space
+ } elsif ($char eq "\n") {
+ # ignore the EOL
+ $lineno = $lineno + 1;
+ } elsif ($s eq 'top') {
+ if ($char eq '@') {
+ %entry = ();
+ $s = 'start';
+ $acc = '';
+ } else {
+ warning("Each BibTeX entry must start with '\@', what is '$char'?");
+ last;
+ }
+ } elsif ($char =~ /[a-z]/ and $s eq 'start') {
+ # @article
+ } elsif ($char eq '{' and $s eq 'start') {
+ $entry{':type'} = substr($acc, 1);
+ $acc = '';
+ $s = 'body';
+ } elsif ($char =~ /[a-zA-Z]/ and $s eq 'body') {
+ $acc = '';
+ $s = 'tag';
+ } elsif ($char =~ /[a-zA-Z0-9_]/ and $s eq 'tag') {
+ # reading the tag
+ } elsif ($char =~ /[a-zA-Z0-9]/ and $s eq 'value') {
+ # reading the value without quotes or brackets
+ } elsif ($char eq ',' and $s eq 'tag') {
+ $entry{':name'} = $acc;
+ $s = 'body';
+ } elsif ($char eq '=' and $s eq 'tag') {
+ my $t = lc($acc);
+ if (exists $entry{$t}) {
+ warning("The tag '$t' is seen more than once");
+ }
+ $tag = $t;
+ $s = 'value';
+ $acc = '';
+ } elsif ($char eq ',' and $s eq 'value') {
+ if (not exists $entry{$tag}) {
+ my $tex = substr($acc, 1);
+ $tex =~ s/\s//g;
+ $entry{$tag} = $tex;
+ }
+ $s = 'body';
+ } elsif ($char eq '}' and $s eq 'body') {
+ push(@entries, { %entry });
+ $s = 'top';
+ } elsif ($char eq '}' and $s eq 'value') {
+ if (not exists $entry{$tag}) {
+ my $tex = substr($acc, 1);
+ $tex =~ s/\s//g;
+ $entry{$tag} = $tex;
+ }
+ push(@entries, { %entry });
+ $s = 'top';
+ } elsif ($char eq '}' and $s eq 'tag') {
+ $entry{':name'} = $acc;
+ push(@entries, { %entry });
+ $s = 'top';
+ } elsif ($char eq '"' and $s eq 'value') {
+ $s = 'quote';
+ $acc = '';
+ } elsif ($char eq '"' and $s eq 'quote') {
+ $entry{$tag} = substr($acc, 1);
+ $s = 'value';
+ } elsif ($s eq 'quote') {
+ # nothing
+ } elsif ($char eq '{' and $s eq 'value') {
+ $nest = 1;
+ $s = 'brackets';
+ $acc = '';
+ } elsif ($s eq 'brackets') {
+ if ($char eq '\\') {
+ $escape = 1;
+ } elsif ($char eq '{' and $escape ne 1) {
+ $nest = $nest + 1;
+ } elsif ($char eq '}' and $escape ne 1) {
+ $nest = $nest - 1;
+ if ($nest eq 0) {
+ $entry{$tag} = substr($acc, 1);
+ $s = 'value';
+ }
+ }
+ $escape = 0;
+ } else {
+ warning("It is impossible to parse the .bib file, because I do not know what to do with '$char' at line #$lineno (s=$s)");
+ last;
+ }
+ if ($char eq ' ' and not($s =~ /quote|brackets/)) {
+ next;
+ }
+ $acc = $acc . $char;
+ }
+ return @entries;
+}
+
+# Takes the text and returns only list of words seen there.
+sub only_words {
+ my ($tex) = @_;
+ return split(/[ \-]/, clean_tex($tex));
+}
+
+# Take a TeX string and return a cleaner one, without redundant spaces, brackets, etc.
+sub clean_tex {
+ my ($tex) = @_;
+ $tex =~ s/\s+/ /g;
+ $tex =~ s/^\s+//g;
+ $tex =~ s/\s+$//g;
+ while ($tex =~ s/^\{(.+)\}$/$1/g) {};
+ return $tex;
+}
+
+# Take a bibentry and print all its tags as a comma-separated string.
+sub listed_tags {
+ my (%entry) = @_;
+ my @list;
+ foreach my $tag (keys %entry) {
+ if ($tag =~ /^:.*/) {
+ next;
+ }
+ push(@list, $tag);
+ }
+ my @sorted = sort @list;
+ return '(' . join(', ', @sorted) . ')';
+}
+
+# Print ERROR message to the console and die.
+sub error {
+ my ($txt) = @_;
+ if (exists $args{'--latex'}) {
+ print "\\PackageError{bibcop}{$txt}{}\n";
+ exit 0;
+ } else {
+ print STDERR $txt . "\n";
+ exit 1;
+ }
+}
+
+# Print DEBUG message to the console.
+sub debug {
+ my ($txt) = @_;
+ if (exists $args{'--verbose'}) {
+ if (exists $args{'--latex'}) {
+ print "\\message{bibcop: $txt^^J}\n";
+ } else {
+ print $txt . "\n";
+ }
+ }
+}
+
+# Print INFO message to the console.
+sub info {
+ my ($txt) = @_;
+ if (exists $args{'--latex'}) {
+ print '% ';
+ }
+ print $txt . "\n";
+}
+
+# Print INFO message to the console.
+sub warning {
+ my ($txt) = @_;
+ if (exists $args{'--latex'}) {
+ print "\\PackageWarningNoLine{bibcop}{$txt}\n";
+ } else {
+ print $txt . "\n";
+ }
+}
+
+if (@ARGV+0 eq 0 or exists $args{'--help'} or exists $args{'-?'}) {
+ info("Bibcop is a Style Checker of BibTeX Files\n\n" .
+ "Usage:\n" .
+ " bibcop [<options>] <.bib file path>\n\n" .
+ "Options:\n" .
+ " -v, --version Print the current version of the tool and exit\n" .
+ " -?, --help Print this help screen\n" .
+ " --fix Fix the errors and print a new version of the .bib file to the console\n" .
+ " --verbose Print supplementary debugging information\n" .
+ " --latex Report errors in LaTeX format using \\PackageWarningNoLine command\n\n" .
+ "If any issues, report to GitHub: https://github.com/yegor256/bibcop");
+} elsif (exists $args{'--version'} or exists $args{'-v'}) {
+ info('0.0.6');
+} else {
+ my ($file) = grep { not($_ =~ /^--.*$/) } @ARGV;
+ if (not $file) {
+ error('File name must be specified');
+ }
+ open(my $fh, '<', $file) or error('Cannot open file: ' . $file);
+ my $bib; { local $/; $bib = <$fh>; }
+ my @entries = entries($bib);
+ if (exists $args{'--fix'}) {
+ for my $i (0..(@entries+0 - 1)) {
+ my %entry = %{ $entries[$i] };
+ my $type = $entry{':type'};
+ if (not exists $blessed{$type}) {
+ error("I don't know what to do with \@$type type of BibTeX entry");
+ }
+ my $tags = $blessed{$entry{':type'}};
+ my %allowed = map { $_ => 1 } @$tags;
+ my @lines;
+ foreach my $tag (keys %entry) {
+ if ($tag =~ /^:/) {
+ next;
+ }
+ if (not exists $allowed{$tag} and not exists $allowed{$tag . '?'}) {
+ next;
+ }
+ my $value = clean_tex($entry{$tag});
+ if ($tag =~ /title|booktitle|journal/) {
+ $value = '{' . $value . '}';
+ }
+ push(@lines, " $tag = {$value},");
+ }
+ info("\@$type\{$entry{':name'},");
+ my @sorted = sort @lines;
+ foreach my $line (@sorted) {
+ info($line);
+ }
+ info("}\n");
+ }
+ } else {
+ debug((@entries+0) . ' entries found in ' . $file);
+ for my $i (0..(@entries+0 - 1)) {
+ my %entry = %{ $entries[$i] };
+ debug("Checking $entry{':name'} (no.$i)...");
+ foreach my $err (process_entry(%entry)) {
+ warning("$err, in the '$entry{':name'}' entry");
+ }
+ }
+ }
+}
+
+1;