diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index ea22912..ffbf5dc 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -25,6 +25,7 @@ return array(
'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
'Symfony\\Component\\ErrorHandler\\' => array($vendorDir . '/symfony/error-handler'),
'Stevenmaguire\\OAuth2\\Client\\' => array($vendorDir . '/stevenmaguire/oauth2-bitbucket/src'),
+ 'RenanBr\\BibTexParser\\' => array($vendorDir . '/renanbr/bibtex-parser/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index ff243b9..0951bcd 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -51,6 +51,10 @@ class ComposerStaticInitc94a8368bcea9853dd31683be0b15c06
'Symfony\\Component\\ErrorHandler\\' => 31,
'Stevenmaguire\\OAuth2\\Client\\' => 28,
),
+ 'R' =>
+ array (
+ 'RenanBr\\BibTexParser\\' => 21,
+ ),
'P' =>
array (
'Psr\\Log\\' => 8,
@@ -159,6 +163,10 @@ class ComposerStaticInitc94a8368bcea9853dd31683be0b15c06
array (
0 => __DIR__ . '/..' . '/stevenmaguire/oauth2-bitbucket/src',
),
+ 'RenanBr\\BibTexParser\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/renanbr/bibtex-parser/src',
+ ),
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 4107283..6f21f40 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -1402,6 +1402,71 @@
},
"install-path": "../ralouphie/getallheaders"
},
+ {
+ "name": "renanbr/bibtex-parser",
+ "version": "2.2.0",
+ "version_normalized": "2.2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/renanbr/bibtex-parser.git",
+ "reference": "d02d2426822235f5179ecdf635ba710c9d6d2ddd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/renanbr/bibtex-parser/zipball/d02d2426822235f5179ecdf635ba710c9d6d2ddd",
+ "reference": "d02d2426822235f5179ecdf635ba710c9d6d2ddd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=5.7",
+ "ryakad/pandoc-php": "^1.0"
+ },
+ "suggest": {
+ "ryakad/pandoc-php": "Needed to support LaTeX decoder in class RenanBr\\BibTexParser\\Processor\\LatexToUnicodeProcessor",
+ "ueberdosis/pandoc": "Alternate Pandoc PHP package which (if available) will be preferred over ryakad/pandoc-php"
+ },
+ "time": "2023-08-25T11:21:46+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "RenanBr\\BibTexParser\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Renan de Lima Barbosa",
+ "email": "renandelima@gmail.com"
+ }
+ ],
+ "description": "BibTex Parser provides an API to read .bib files programmatically",
+ "keywords": [
+ "Bibliography",
+ "bib",
+ "bibtex",
+ "citation",
+ "cite",
+ "latex",
+ "parser"
+ ],
+ "support": {
+ "issues": "https://github.com/renanbr/bibtex-parser/issues",
+ "source": "https://github.com/renanbr/bibtex-parser/tree/2.2.0"
+ },
+ "install-path": "../renanbr/bibtex-parser"
+ },
{
"name": "stevenmaguire/oauth2-bitbucket",
"version": "3.0.0",
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index 16bfab1..0bfc856 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -220,6 +220,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'renanbr/bibtex-parser' => array(
+ 'pretty_version' => '2.2.0',
+ 'version' => '2.2.0.0',
+ 'reference' => 'd02d2426822235f5179ecdf635ba710c9d6d2ddd',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../renanbr/bibtex-parser',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'stevenmaguire/oauth2-bitbucket' => array(
'pretty_version' => '3.0.0',
'version' => '3.0.0.0',
diff --git a/vendor/renanbr/bibtex-parser/LICENSE b/vendor/renanbr/bibtex-parser/LICENSE
new file mode 100644
index 0000000..05cbcd3
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/LICENSE
@@ -0,0 +1,18 @@
+Copyright (c) 2017 Renan de Lima Barbosa
+
+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/vendor/renanbr/bibtex-parser/Makefile b/vendor/renanbr/bibtex-parser/Makefile
new file mode 100644
index 0000000..4094b83
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/Makefile
@@ -0,0 +1,74 @@
+ifeq ($(shell type podman > /dev/null 2>&1; echo $$?), 0)
+ ENGINE ?= podman
+else ifeq ($(shell type docker > /dev/null 2>&1; echo $$?), 0)
+ ENGINE ?= docker
+endif
+
+PHP_VERSION ?= 7.4
+
+IMAGE_BASE = jakzal/phpqa:php$(PHP_VERSION)
+IMAGE = renanbr/bibtex-parser:php$(PHP_VERSION)
+LABEL = maintainer=renanbr-bibtex-parser
+
+RUN = $(ENGINE) run --init -it --rm -v "$(CURDIR):/project" -w /project
+
+.DEFAULT_GOAL := help
+
+help: ## Display this message help
+ @make -v | head -n 1
+ @awk '\
+ BEGIN {\
+ FS = ":.*##";\
+ printf "\n\033[33mUsage:\033[0m\n [PHP_VERSION=major.minor] make [target]\n\n\033[33mAvailable targets:\033[0m\n" \
+ } /^[a-zA-Z0-9_-]+:.*?##/ { \
+ printf " \033[32m%-18s\033[0m %s\n", $$1, $$2 \
+ } /^##/ { \
+ printf "\033[33m %s\033[0m\n", substr($$0, 4) \
+ }' $(MAKEFILE_LIST)
+.PHONY: help
+
+## Checks
+
+check: static-analysis cs-check test ## Run all checks
+
+static-analysis: vendor ## Run static analysis
+ $(RUN) $(IMAGE) phpstan analyse --verbose
+.PHONY: static-analysis
+
+cs-check: check-engine ## Check for coding standards violations
+ mkdir -p var
+ $(RUN) $(IMAGE_BASE) php-cs-fixer fix --dry-run --verbose
+.PHONY: cs-check
+
+test: vendor ## Run tests
+ $(RUN) $(IMAGE) php -d pcov.enabled=1 ./vendor/bin/phpunit --testdox --coverage-text --verbose
+.PHONY: test
+
+## Fixers
+
+cs-fix: check-engine ## Fix coding standards
+ mkdir -p var
+ $(RUN) $(IMAGE_BASE) php-cs-fixer fix
+.PHONY: cs-fix
+
+## Misc
+
+clean: check-engine ## Clean up workspace
+ $(RUN) $(IMAGE_BASE) rm -rf composer.lock var/ vendor/
+ $(ENGINE) image rm --force $$($(ENGINE) images --filter "label=$(LABEL)" --quiet) 2>&1 | true
+.PHONY: clean
+
+vendor: build-image
+ $(RUN) $(IMAGE) composer install -vvv
+
+## Container engine
+
+check-engine:
+ifeq ($(ENGINE),)
+ $(error "Container engine not found. Did you install podman or docker?")
+endif
+.PHONY: check-engine
+
+build-image: check-engine
+ $(ENGINE) build --tag $(IMAGE) --build-arg FROM=$(IMAGE_BASE) --label $(LABEL) .docker/
+.PHONY: build-image
diff --git a/vendor/renanbr/bibtex-parser/README.md b/vendor/renanbr/bibtex-parser/README.md
new file mode 100644
index 0000000..0a3860e
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/README.md
@@ -0,0 +1,569 @@
+
PHP BibTeX Parser 2.x
+
+ This is a
+ BibTeX
+ parser written in
+ PHP.
+
+
+
+
+
+
+
+
+
+
+![Tests](https://github.com/renanbr/bibtex-parser/workflows/Tests/badge.svg)
+[![codecov](https://codecov.io/gh/renanbr/bibtex-parser/branch/master/graph/badge.svg)](https://codecov.io/gh/renanbr/bibtex-parser)
+![Static Analysis](https://github.com/renanbr/bibtex-parser/workflows/Static%20Analysis/badge.svg)
+![Coding Standards](https://github.com/renanbr/bibtex-parser/workflows/Coding%20Standards/badge.svg)
+
+You are browsing the documentation of **BibTeX Parser 2.x**, the latest version.
+
+## Table of contents
+
+* [Installing](#installing)
+* [Usage](#usage)
+* [Vocabulary](#vocabulary)
+* [Processors](#processors)
+ * [Tag name case](#tag-name-case)
+ * [Authors and editors](#authors-and-editors)
+ * [Keywords](#keywords)
+ * [Date](#date)
+ * [Fill missing tag](#fill-missing-tag)
+ * [Trim tags](#trim-tags)
+ * [Determine URL from the DOI](#determine-url-from-the-doi)
+ * [LaTeX to unicode](#latex-to-unicode)
+ * [Custom](#custom)
+* [Handling errors](#handling-errors)
+* [Advanced usage](#advanced-usage)
+
+## Installing
+
+```bash
+composer require renanbr/bibtex-parser
+```
+
+## Usage
+
+```php
+use RenanBr\BibTexParser\Listener;
+use RenanBr\BibTexParser\Parser;
+use RenanBr\BibTexParser\Processor;
+
+require 'vendor/autoload.php';
+
+$bibtex = <<addProcessor(new Processor\TagNameCaseProcessor(CASE_LOWER));
+// $listener->addProcessor(new Processor\NamesProcessor());
+// $listener->addProcessor(new Processor\KeywordsProcessor());
+// $listener->addProcessor(new Processor\DateProcessor());
+// $listener->addProcessor(new Processor\FillMissingProcessor([/* ... */]));
+// $listener->addProcessor(new Processor\TrimProcessor());
+// $listener->addProcessor(new Processor\UrlFromDoiProcessor());
+// $listener->addProcessor(new Processor\LatexToUnicodeProcessor());
+// ... you can append as many Processors as you want
+
+// Create a Parser and attach the listener
+$parser = new Parser();
+$parser->addListener($listener);
+
+// Parse the content, then read processed data from the Listener
+$parser->parseString($bibtex); // or parseFile('/path/to/file.bib')
+$entries = $listener->export();
+
+print_r($entries);
+```
+
+This will output:
+
+```
+Array
+(
+ [0] => Array
+ (
+ [_type] => article
+ [citation-key] => einstein1916relativity
+ [title] => Relativity: The Special and General Theory
+ [author] => Einstein, Albert
+ [year] => 1916
+ )
+)
+```
+
+## Vocabulary
+
+[BibTeX] is all about "entry", "tag's name" and "tag's content".
+
+> A [BibTeX] **entry** consists of the type (the word after @), a citation-key and a number of tags which define various characteristics of the specific [BibTeX] entry.
+> (...) A [BibTeX] **tag** is specified by its **name** followed by an equals sign, and the **content**.
+
+Source: http://www.bibtex.org/Format/
+
+Note:
+This library considers "type" and "citation-key" as tags.
+This behavior can be changed [implementing your own Listener](#advanced-usage).
+
+## Processors
+
+`Processor` is a [callable] that receives an entry as argument and returns a modified entry.
+
+This library contains three main parts:
+
+- `Parser` class, responsible for detecting units inside a [BibTeX] input;
+- `Listener` class, responsible for gathering units and transforming them into a list of entries;
+- `Processor` classes, responsible for manipulating entries.
+
+Despite you can't configure the `Parser`, you can append as many `Processor` as you want to the `Listener` through `Listener::addProcessor()` before exporting the contents.
+Be aware that `Listener` provides, by default, these features:
+
+- Found entries are reachable through `Listener::export()` method;
+- [Tag content concatenation](http://www.bibtex.org/Format/);
+ - e.g. `hello # " world"` tag's content will generate `hello world` [string]
+- [Tag content abbreviation handling](http://www.bibtex.org/Format/);
+ - e.g. `@string{foo="bar"} @misc{bar=foo}` will make `$entries[1]['bar']` assume `bar` as value
+- Publication's type exposed as `_type` tag;
+- Citation key exposed as `citation-key` tag;
+- Original entry text exposed as `_original` tag.
+
+This project ships some useful processors.
+
+### Tag name case
+
+In [BibTeX] the tag's names aren't case-sensitive.
+This library exposes entries as [array], in which keys are case-sensitive.
+To avoid this misunderstanding, you can force the tags' name character case using `TagNameCaseProcessor`.
+
+Usage
+
+```php
+use RenanBr\BibTexParser\Processor\TagNameCaseProcessor;
+
+$listener->addProcessor(new TagNameCaseProcessor(CASE_UPPER)); // or CASE_LOWER
+```
+
+```bib
+@article{
+ title={BibTeX rocks}
+}
+```
+
+```
+Array
+(
+ [0] => Array
+ (
+ [TYPE] => article
+ [TITLE] => BibTeX rocks
+ )
+)
+```
+
+
+
+### Authors and editors
+
+[BibTeX] recognizes four parts of an author's name: First Von Last Jr.
+If you would like to parse the `author` and `editor` tags included in your entries, you can use the `NamesProcessor` class.
+
+Usage
+
+```php
+use RenanBr\BibTexParser\Processor\NamesProcessor;
+
+$listener->addProcessor(new NamesProcessor());
+```
+
+```bib
+@article{
+ title={Relativity: The Special and General Theory},
+ author={Einstein, Albert}
+}
+```
+
+```
+Array
+(
+ [0] => Array
+ (
+ [type] => article
+ [title] => Relativity: The Special and General Theory
+ [author] => Array
+ (
+ [0] => Array
+ (
+ [first] => Albert
+ [von] =>
+ [last] => Einstein
+ [jr] =>
+ )
+ )
+ )
+)
+```
+
+
+
+### Keywords
+
+The `keywords` tag contains a list of expressions represented as [string], you might want to read them as an [array] instead.
+
+Usage
+
+```php
+use RenanBr\BibTexParser\Processor\KeywordsProcessor;
+
+$listener->addProcessor(new KeywordsProcessor());
+```
+
+```bib
+@misc{
+ title={The End of Theory: The Data Deluge Makes the Scientific Method Obsolete},
+ keywords={big data, data deluge, scientific method}
+}
+```
+
+```
+Array
+(
+ [0] => Array
+ (
+ [type] => misc
+ [title] => The End of Theory: The Data Deluge Makes the Scientific Method Obsolete
+ [keywords] => Array
+ (
+ [0] => big data
+ [1] => data deluge
+ [2] => scientific method
+ )
+ )
+)
+```
+
+
+
+### Date
+
+It adds a new tag `_date` as [DateTimeImmutable].
+This processor adds the new tag **if and only if** this the tags `month` and `year` are fulfilled.
+
+Usage
+
+```php
+use RenanBr\BibTexParser\Processor\DateProcessor;
+
+$listener->addProcessor(new DateProcessor());
+```
+
+```bib
+@misc{
+ month="1~oct",
+ year=2000
+}
+```
+
+```
+Array
+(
+ [0] => Array
+ (
+ [type] => misc
+ [month] => 1~oct
+ [year] => 2000
+ [_date] => DateTimeImmutable Object
+ (
+ [date] => 2000-10-01 00:00:00.000000
+ [timezone_type] => 3
+ [timezone] => UTC
+ )
+ )
+)
+```
+
+
+
+### Fill missing tag
+
+It puts a default value to some missing field.
+
+Usage
+
+```php
+use RenanBr\BibTexParser\Processor\FillMissingProcessor;
+
+$listener->addProcessor(new FillMissingProcessor([
+ 'title' => 'This entry has no title',
+ 'year' => 1970,
+]));
+```
+
+```bib
+@misc{
+}
+
+@misc{
+ title="I do exist"
+}
+```
+
+```
+Array
+(
+ [0] => Array
+ (
+ [type] => misc
+ [title] => This entry has no title
+ [year] => 1970
+ )
+ [1] => Array
+ (
+ [type] => misc
+ [title] => I do exist
+ [year] => 1970
+ )
+)
+```
+
+
+
+### Trim tags
+
+Apply [trim()] to all tags.
+
+Usage
+
+```php
+use RenanBr\BibTexParser\Processor\TrimProcessor;
+
+$listener->addProcessor(new TrimProcessor());
+```
+
+```bib
+@misc{
+ title=" too much space "
+}
+```
+
+```
+Array
+(
+ [0] => Array
+ (
+ [type] => misc
+ [title] => too much space
+ )
+
+)
+```
+
+
+
+### Determine URL from the DOI
+
+Sets `url` tag with [DOI] if `doi` tag is present and `url` tag is missing.
+
+Usage
+
+```php
+use RenanBr\BibTexParser\Processor\UrlFromDoiProcessor;
+
+$listener->addProcessor(new UrlFromDoiProcessor());
+```
+
+```bib
+@misc{
+ doi="qwerty"
+}
+
+@misc{
+ doi="azerty",
+ url="http://example.org"
+}
+```
+
+```
+Array
+(
+ [0] => Array
+ (
+ [type] => misc
+ [doi] => qwerty
+ [url] => https://doi.org/qwerty
+ )
+
+ [1] => Array
+ (
+ [type] => misc
+ [doi] => azerty
+ [url] => http://example.org
+ )
+)
+```
+
+
+
+### LaTeX to unicode
+
+[BibTeX] files store [LaTeX] contents.
+You might want to read them as unicode instead.
+The `LatexToUnicodeProcessor` class solves this problem, but before adding the processor to the listener you must:
+
+- [install Pandoc](http://pandoc.org/installing.html) in your system; and
+- add [ryakad/pandoc-php](https://github.com/ryakad/pandoc-php) or [ueberdosis/pandoc](https://github.com/ueberdosis/pandoc) as a dependency of your project.
+
+Usage
+
+```php
+use RenanBr\BibTexParser\Processor\LatexToUnicodeProcessor;
+
+$listener->addProcessor(new LatexToUnicodeProcessor());
+```
+
+```bib
+@article{
+ title={Caf\\'{e}s and bars}
+}
+```
+
+```
+Array
+(
+ [0] => Array
+ (
+ [type] => article
+ [title] => Cafés and bars
+ )
+)
+```
+
+
+
+Note: Order matters, add this processor as the last.
+
+### Custom
+
+The `Listener::addProcessor()` method expects a [callable] as argument.
+In the example shown below, we append the text `with laser` to the `title` tags for all entries.
+
+Usage
+
+```php
+$listener->addProcessor(static function (array $entry) {
+ $entry['title'] .= ' with laser';
+ return $entry;
+});
+```
+
+```
+@article{
+ title={BibTeX rocks}
+}
+```
+
+```
+Array
+(
+ [0] => Array
+ (
+ [type] => article
+ [title] => BibTeX rocks with laser
+ )
+)
+```
+
+
+
+## Handling errors
+
+This library throws two types of exception: `ParserException` and `ProcessorException`.
+The first one may happen during the data extraction.
+When it occurs it probably means the parsed BibTeX isn't valid.
+The second exception may happen during the data processing.
+When it occurs it means the listener's processors can't handle properly the data found.
+Both implement `ExceptionInterface`.
+
+```php
+use RenanBr\BibTexParser\Exception\ExceptionInterface;
+use RenanBr\BibTexParser\Exception\ParserException;
+use RenanBr\BibTexParser\Exception\ProcessorException;
+
+try {
+ // ... parser and listener configuration
+
+ $parser->parseFile('/path/to/file.bib');
+ $entries = $listener->export();
+} catch (ParserException $exception) {
+ // The BibTeX isn't valid
+} catch (ProcessorException $exception) {
+ // Listener's processors aren't able to handle data found
+} catch (ExceptionInterface $exception) {
+ // Alternatively, you can use this exception to catch all of them at once
+}
+```
+
+## Advanced usage
+
+The core of this library contains these main classes:
+
+- `RenanBr\BibTexParser\Parser` responsible for detecting units inside a [BibTeX] input;
+- `RenanBr\BibTexParser\ListenerInterface` responsible for treating units found.
+
+You can attach listeners to the parser through `Parser::addListener()`.
+The parser is able to detect [BibTeX] units, such as "type", "tag's name", "tag's content".
+As the parser finds a unit, it triggers the listeners attached to it.
+
+You can code your own listener! All you have to do is handle units.
+
+```php
+namespace RenanBr\BibTexParser;
+
+interface ListenerInterface
+{
+ /**
+ * Called when an unit is found.
+ *
+ * @param string $text The original content of the unit found.
+ * Escape character will not be sent.
+ * @param string $type The type of unit found.
+ * It can assume one of Parser's constant value.
+ * @param array $context Contains details of the unit found.
+ */
+ public function bibTexUnitFound($text, $type, array $context);
+}
+```
+
+`$type` may assume one of these values:
+
+- `Parser::TYPE`
+- `Parser::CITATION_KEY`
+- `Parser::TAG_NAME`
+- `Parser::RAW_TAG_CONTENT`
+- `Parser::BRACED_TAG_CONTENT`
+- `Parser::QUOTED_TAG_CONTENT`
+- `Parser::ENTRY`
+
+`$context` is an [array] with these keys:
+
+- `offset` contains the `$text`'s beginning position.
+ It may be useful, for example, to [seek on a file pointer](https://php.net/fseek);
+- `length` contains the original `$text`'s length.
+ It may differ from [string] length sent to the listener because may there are escaped characters.
+
+[BibTeX]: https://tug.org/bibtex/
+[DOI]: https://www.doi.org/
+[DateTimeImmutable]: https://www.php.net/manual/class.datetimeimmutable.php
+[LaTeX]: https://www.latex-project.org/
+[array]: https://php.net/manual/language.types.array.php
+[callable]: https://php.net/manual/en/language.types.callable.php
+[string]: https://php.net/manual/language.types.string.php
+[trim()]: https://www.php.net/trim
diff --git a/vendor/renanbr/bibtex-parser/composer.json b/vendor/renanbr/bibtex-parser/composer.json
new file mode 100644
index 0000000..2bbbabc
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/composer.json
@@ -0,0 +1,50 @@
+{
+ "name": "renanbr/bibtex-parser",
+ "type": "library",
+ "description": "BibTex Parser provides an API to read .bib files programmatically",
+ "keywords": [
+ "bib",
+ "bibtex",
+ "latex",
+ "parser",
+ "bibliography",
+ "citation",
+ "cite"
+ ],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Renan de Lima Barbosa",
+ "email": "renandelima@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=5.7",
+ "ryakad/pandoc-php": "^1.0"
+ },
+ "suggest": {
+ "ryakad/pandoc-php": "Needed to support LaTeX decoder in class RenanBr\\BibTexParser\\Processor\\LatexToUnicodeProcessor",
+ "ueberdosis/pandoc": "Alternate Pandoc PHP package which (if available) will be preferred over ryakad/pandoc-php"
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "RenanBr\\BibTexParser\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "RenanBr\\BibTexParser\\Test\\": "tests/"
+ }
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Exception/ExceptionInterface.php b/vendor/renanbr/bibtex-parser/src/Exception/ExceptionInterface.php
new file mode 100644
index 0000000..4cecdcb
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Exception/ExceptionInterface.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Exception;
+
+/**
+ * Interface for package exceptions.
+ */
+interface ExceptionInterface
+{
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Exception/ParserException.php b/vendor/renanbr/bibtex-parser/src/Exception/ParserException.php
new file mode 100644
index 0000000..f2d8803
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Exception/ParserException.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Exception;
+
+use Exception;
+
+class ParserException extends Exception implements ExceptionInterface
+{
+ /**
+ * @param string $character
+ * @param int $line
+ * @param int $column
+ */
+ public static function unexpectedCharacter($character, $line, $column)
+ {
+ // Avoid var_export() weird treatment for \0
+ $character = "\0" === $character ? "'\\0'" : var_export($character, true);
+
+ return new self(sprintf(
+ 'Unexpected character %s at line %d column %d',
+ $character,
+ $line,
+ $column
+ ));
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Exception/ProcessorException.php b/vendor/renanbr/bibtex-parser/src/Exception/ProcessorException.php
new file mode 100644
index 0000000..a453f39
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Exception/ProcessorException.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Exception;
+
+use Exception;
+
+class ProcessorException extends Exception implements ExceptionInterface
+{
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Listener.php b/vendor/renanbr/bibtex-parser/src/Listener.php
new file mode 100644
index 0000000..91be372
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Listener.php
@@ -0,0 +1,109 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser;
+
+class Listener implements ListenerInterface
+{
+ /** @var array */
+ private $entries = [];
+
+ /**
+ * Current tag name.
+ *
+ * Indicates where to save contents when triggered by the parser.
+ *
+ * @var string
+ */
+ private $currentTagName;
+
+ /** @var array */
+ private $processors = [];
+
+ /** @var array */
+ private $processed = [];
+
+ /**
+ * @return array all entries found during parsing process
+ */
+ public function export()
+ {
+ $offset = \count($this->processed);
+ $missing = \array_slice($this->entries, $offset);
+ foreach ($this->processors as $processor) {
+ $missing = array_filter(array_map($processor, $missing));
+ }
+ $this->processed = array_merge($this->processed, $missing);
+
+ return $this->processed;
+ }
+
+ /**
+ * @param callable $processor Function to be applied to every BibTeX entry.
+ * The processor given must return the modified entry.
+ * Processors will be applied in the same order in which they were added.
+ * The suggested signature is:
+ * function (array $entry): array
+ */
+ public function addProcessor(callable $processor)
+ {
+ $this->processors[] = $processor;
+ }
+
+ public function bibTexUnitFound($text, $type, array $context)
+ {
+ switch ($type) {
+ case Parser::TYPE:
+ // Starts a new entry
+ $this->entries[] = [
+ '_type' => $text,
+ 'type' => $text, // compatibility
+ ];
+ break;
+
+ case Parser::CITATION_KEY:
+ $index = \count($this->entries) - 1;
+ $this->entries[$index]['citation-key'] = $text;
+ break;
+
+ case Parser::TAG_NAME:
+ // Saves tag into the current entry
+ $index = \count($this->entries) - 1;
+ $this->currentTagName = $text;
+ $this->entries[$index][$this->currentTagName] = null;
+ break;
+
+ case Parser::RAW_TAG_CONTENT:
+ // Searches for an abbreviation
+ foreach ($this->entries as $entry) {
+ if ('string' === $entry['type'] && \array_key_exists($text, $entry)) {
+ $text = $entry[$text];
+ break;
+ }
+ }
+ // no break
+
+ case Parser::BRACED_TAG_CONTENT:
+ case Parser::QUOTED_TAG_CONTENT:
+ // Appends content into the current tag
+ if (null !== $text) {
+ $index = \count($this->entries) - 1;
+ $this->entries[$index][$this->currentTagName] .= $text;
+ }
+ break;
+
+ case Parser::ENTRY:
+ $index = \count($this->entries) - 1;
+ $this->entries[$index]['_original'] = $text;
+ break;
+ }
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/ListenerInterface.php b/vendor/renanbr/bibtex-parser/src/ListenerInterface.php
new file mode 100644
index 0000000..b5f4f85
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/ListenerInterface.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser;
+
+interface ListenerInterface
+{
+ /**
+ * Called when an unit is found.
+ *
+ * @param string $text The original content of the unit found.
+ * Escape character will not be sent.
+ * @param string $type The type of unit found.
+ * It can assume one of Parser's constant value.
+ * @param array $context contains details of the unit found
+ */
+ public function bibTexUnitFound($text, $type, array $context);
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Parser.php b/vendor/renanbr/bibtex-parser/src/Parser.php
new file mode 100644
index 0000000..1c0cfa0
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Parser.php
@@ -0,0 +1,566 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser;
+
+use ErrorException;
+use RenanBr\BibTexParser\Exception\ParserException;
+
+class Parser
+{
+ const TYPE = 'type';
+ const CITATION_KEY = 'citation_key';
+ const TAG_NAME = 'tag_name';
+ const RAW_TAG_CONTENT = 'raw_tag_content';
+ const BRACED_TAG_CONTENT = 'braced_tag_content';
+ const QUOTED_TAG_CONTENT = 'quoted_tag_content';
+ const ENTRY = 'entry';
+
+ const NONE = 'none';
+ const COMMENT = 'comment';
+ const FIRST_TAG_NAME = 'first_tag_name';
+ const POST_TYPE = 'post_type';
+ const POST_TAG_NAME = 'post_tag_name';
+ const PRE_TAG_CONTENT = 'pre_tag_content';
+
+ /** @var string */
+ private $state;
+
+ /** @var string */
+ private $buffer;
+
+ /** @var int|null */
+ private $bufferOffset;
+
+ /** @var array|null */
+ private $firstTagSnapshot;
+
+ /** @var string|null */
+ private $originalEntryBuffer;
+
+ /** @var int|null */
+ private $originalEntryOffset;
+
+ /** @var bool */
+ private $skipOriginalEntryReading;
+
+ /** @var int */
+ private $line;
+
+ /** @var int */
+ private $column;
+
+ /** @var int */
+ private $offset;
+
+ /** @var bool */
+ private $isTagContentEscaped;
+
+ /** @var bool */
+ private $mayConcatenateTagContent;
+
+ /** @var string|null */
+ private $tagContentDelimiter;
+
+ /** @var int */
+ private $braceLevel;
+
+ /** @var ListenerInterface[] */
+ private $listeners = [];
+
+ public function addListener(ListenerInterface $listener)
+ {
+ $this->listeners[] = $listener;
+ }
+
+ /**
+ * @param string $file
+ *
+ * @throws ParserException if $file given is not a valid BibTeX
+ * @throws ErrorException if $file given is not readable
+ */
+ public function parseFile($file)
+ {
+ $handle = @fopen($file, 'r');
+ if (!$handle) {
+ throw new ErrorException(sprintf('Unable to open %s', $file));
+ }
+ try {
+ $this->reset();
+ while (!feof($handle)) {
+ $buffer = fread($handle, 128);
+ $this->parse($buffer);
+ }
+ $this->throwExceptionIfReadingEntry("\0");
+ } finally {
+ fclose($handle);
+ }
+ }
+
+ /**
+ * @param string $string
+ *
+ * @throws ParserException if $string given is not a valid BibTeX
+ */
+ public function parseString($string)
+ {
+ $this->reset();
+ $this->parse($string);
+ $this->throwExceptionIfReadingEntry("\0");
+ }
+
+ /**
+ * @param string $text
+ */
+ private function parse($text)
+ {
+ $length = mb_strlen($text);
+ for ($position = 0; $position < $length; ++$position) {
+ $char = mb_substr($text, $position, 1);
+ $this->read($char);
+ if ("\n" === $char) {
+ ++$this->line;
+ $this->column = 1;
+ } else {
+ ++$this->column;
+ }
+ ++$this->offset;
+ }
+ }
+
+ private function reset()
+ {
+ $this->state = self::NONE;
+ $this->buffer = '';
+ $this->firstTagSnapshot = null;
+ $this->originalEntryBuffer = null;
+ $this->originalEntryOffset = null;
+ $this->skipOriginalEntryReading = false;
+ $this->line = 1;
+ $this->column = 1;
+ $this->offset = 0;
+ $this->mayConcatenateTagContent = false;
+ $this->isTagContentEscaped = false;
+ $this->tagContentDelimiter = null;
+ $this->braceLevel = 0;
+ }
+
+ // ----- Readers -----------------------------------------------------------
+
+ /**
+ * @param string $char
+ */
+ private function read($char)
+ {
+ $previousState = $this->state;
+
+ switch ($this->state) {
+ case self::NONE:
+ $this->readNone($char);
+ break;
+ case self::COMMENT:
+ $this->readComment($char);
+ break;
+ case self::TYPE:
+ $this->readType($char);
+ break;
+ case self::POST_TYPE:
+ $this->readPostType($char);
+ break;
+ case self::FIRST_TAG_NAME:
+ case self::TAG_NAME:
+ $this->readTagName($char);
+ break;
+ case self::POST_TAG_NAME:
+ $this->readPostTagName($char);
+ break;
+ case self::PRE_TAG_CONTENT:
+ $this->readPreTagContent($char);
+ break;
+ case self::RAW_TAG_CONTENT:
+ $this->readRawTagContent($char);
+ break;
+ case self::QUOTED_TAG_CONTENT:
+ case self::BRACED_TAG_CONTENT:
+ $this->readDelimitedTagContent($char);
+ break;
+ }
+
+ $this->readOriginalEntry($char, $previousState);
+ }
+
+ /**
+ * @param string $char
+ */
+ private function readNone($char)
+ {
+ if ('@' === $char) {
+ $this->state = self::TYPE;
+ } elseif (!$this->isWhitespace($char)) {
+ $this->state = self::COMMENT;
+ }
+ }
+
+ /**
+ * @param string $char
+ */
+ private function readComment($char)
+ {
+ if ($this->isWhitespace($char)) {
+ $this->state = self::NONE;
+ }
+ }
+
+ /**
+ * @param string $char
+ */
+ private function readType($char)
+ {
+ if (preg_match('/^[a-zA-Z]$/', $char)) {
+ $this->appendToBuffer($char);
+ } else {
+ $this->throwExceptionIfBufferIsEmpty($char);
+
+ // Skips @comment type
+ if ('comment' === mb_strtolower($this->buffer)) {
+ $this->skipOriginalEntryReading = true;
+ $this->buffer = '';
+ $this->bufferOffset = null;
+ $this->state = self::COMMENT;
+ $this->readComment($char);
+
+ return;
+ }
+
+ $this->triggerListenersWithCurrentBuffer();
+
+ // once $char isn't a valid character
+ // it must be interpreted as POST_TYPE
+ $this->state = self::POST_TYPE;
+ $this->readPostType($char);
+ }
+ }
+
+ /**
+ * @param string $char
+ */
+ private function readPostType($char)
+ {
+ if ('{' === $char) {
+ $this->state = self::FIRST_TAG_NAME;
+ } elseif (!$this->isWhitespace($char)) {
+ throw ParserException::unexpectedCharacter($char, $this->line, $this->column);
+ }
+ }
+
+ /**
+ * @param string $char
+ */
+ private function readTagName($char)
+ {
+ if (preg_match('/^[a-zA-Z0-9_\+:\-\.\/\x{00C0}-\x{01FF}]$/u', $char)) {
+ $this->appendToBuffer($char);
+ } elseif ($this->isWhitespace($char) && empty($this->buffer)) {
+ // Skips because we didn't start reading
+ } elseif ('}' === $char && empty($this->buffer)) {
+ // No tag name found, $char is just closing current entry
+ $this->state = self::NONE;
+ } else {
+ $this->throwExceptionIfBufferIsEmpty($char);
+
+ if (self::FIRST_TAG_NAME === $this->state) {
+ // Takes a snapshot of current state to be triggered later as
+ // tag name or citation key, see readPostTagName()
+ $this->firstTagSnapshot = $this->takeBufferSnapshot();
+ } else {
+ // Current buffer is a simple tag name
+ $this->triggerListenersWithCurrentBuffer();
+ }
+
+ // Once $char isn't a valid tag name character, it must be
+ // interpreted as post tag name
+ $this->state = self::POST_TAG_NAME;
+ $this->readPostTagName($char);
+ }
+ }
+
+ /**
+ * @param string $char
+ */
+ private function readPostTagName($char)
+ {
+ if ('=' === $char) {
+ // First tag name isn't a citation key, because it has content
+ $this->triggerListenersWithFirstTagSnapshotAs(self::TAG_NAME);
+ $this->state = self::PRE_TAG_CONTENT;
+ } elseif ('}' === $char) {
+ // First tag name is a citation key, because $char closes entry and
+ // lets first tag without value
+ $this->triggerListenersWithFirstTagSnapshotAs(self::CITATION_KEY);
+ $this->state = self::NONE;
+ } elseif (',' === $char) {
+ // First tag name is a citation key, because $char moves to the next
+ // tag and lets first tag without value
+ $this->triggerListenersWithFirstTagSnapshotAs(self::CITATION_KEY);
+ $this->state = self::TAG_NAME;
+ } elseif (!$this->isWhitespace($char)) {
+ throw ParserException::unexpectedCharacter($char, $this->line, $this->column);
+ }
+ }
+
+ /**
+ * @param string $char
+ */
+ private function readPreTagContent($char)
+ {
+ if (preg_match('/^[a-zA-Z0-9]$/', $char)) {
+ // When concatenation is available it means there is already a
+ // defined value, and parser expect a concatenator, a tag separator
+ // or an entry closing char as next $char
+ $this->throwExceptionAccordingToConcatenationAvailability($char, true);
+ $this->state = self::RAW_TAG_CONTENT;
+ $this->readRawTagContent($char);
+ } elseif ('"' === $char) {
+ // The exception is here for the same reason of the first case
+ $this->throwExceptionAccordingToConcatenationAvailability($char, true);
+ $this->tagContentDelimiter = '"';
+ $this->state = self::QUOTED_TAG_CONTENT;
+ } elseif ('{' === $char) {
+ // The exception is here for the same reason of the first case
+ $this->throwExceptionAccordingToConcatenationAvailability($char, true);
+ $this->tagContentDelimiter = '}';
+ $this->state = self::BRACED_TAG_CONTENT;
+ } elseif ('#' === $char) {
+ $this->throwExceptionAccordingToConcatenationAvailability($char, false);
+ $this->mayConcatenateTagContent = false;
+ } elseif (',' === $char) {
+ $this->throwExceptionAccordingToConcatenationAvailability($char, false);
+ $this->mayConcatenateTagContent = false;
+ $this->state = self::TAG_NAME;
+ } elseif ('}' === $char) {
+ $this->throwExceptionAccordingToConcatenationAvailability($char, false);
+ $this->mayConcatenateTagContent = false;
+ $this->state = self::NONE;
+ } elseif (!$this->isWhitespace($char)) {
+ throw ParserException::unexpectedCharacter($char, $this->line, $this->column);
+ }
+ }
+
+ /**
+ * @param string $char
+ */
+ private function readRawTagContent($char)
+ {
+ if (preg_match('/^[a-zA-Z0-9_\+:\-\.\/]$/', $char)) {
+ $this->appendToBuffer($char);
+ } else {
+ $this->throwExceptionIfBufferIsEmpty($char);
+ $this->triggerListenersWithCurrentBuffer();
+
+ // once $char isn't a valid character
+ // it must be interpreted as TAG_CONTENT
+ $this->mayConcatenateTagContent = true;
+ $this->state = self::PRE_TAG_CONTENT;
+ $this->readPreTagContent($char);
+ }
+ }
+
+ /**
+ * @param string $char
+ */
+ private function readDelimitedTagContent($char)
+ {
+ if ($this->isTagContentEscaped) {
+ $this->isTagContentEscaped = false;
+ if ($this->tagContentDelimiter !== $char && '\\' !== $char && '%' !== $char) {
+ $this->appendToBuffer('\\');
+ }
+ $this->appendToBuffer($char);
+ } elseif ('}' === $this->tagContentDelimiter && '{' === $char) {
+ ++$this->braceLevel;
+ $this->appendToBuffer($char);
+ } elseif ($this->tagContentDelimiter === $char) {
+ if (0 === $this->braceLevel) {
+ $this->triggerListenersWithCurrentBuffer();
+ $this->mayConcatenateTagContent = true;
+ $this->state = self::PRE_TAG_CONTENT;
+ } else {
+ --$this->braceLevel;
+ $this->appendToBuffer($char);
+ }
+ } elseif ('\\' === $char) {
+ $this->isTagContentEscaped = true;
+ } else {
+ $this->appendToBuffer($char);
+ }
+ }
+
+ /**
+ * @param string $char
+ * @param string $previousState
+ */
+ private function readOriginalEntry($char, $previousState)
+ {
+ if ($this->skipOriginalEntryReading) {
+ $this->originalEntryBuffer = '';
+ $this->originalEntryOffset = null;
+ $this->skipOriginalEntryReading = false;
+
+ return;
+ }
+
+ // Checks whether we are reading an entry character or not
+ $isPreviousStateEntry = $this->isEntryState($previousState);
+ $isCurrentStateEntry = $this->isEntryState($this->state);
+ $isEntry = $isPreviousStateEntry || $isCurrentStateEntry;
+ if (!$isEntry) {
+ return;
+ }
+
+ // Appends $char to the original entry buffer
+ if (empty($this->originalEntryBuffer)) {
+ $this->originalEntryOffset = $this->offset;
+ }
+ $this->originalEntryBuffer .= $char;
+
+ // Sends original entry to the listeners when $char closes an entry
+ $isClosingEntry = $isPreviousStateEntry && !$isCurrentStateEntry;
+ if ($isClosingEntry) {
+ $this->triggerListeners($this->originalEntryBuffer, self::ENTRY, [
+ 'offset' => $this->originalEntryOffset,
+ 'length' => $this->offset - $this->originalEntryOffset + 1,
+ ]);
+ $this->originalEntryBuffer = '';
+ $this->originalEntryOffset = null;
+ }
+ }
+
+ // ----- Listener triggers -------------------------------------------------
+
+ /**
+ * @param string $text
+ * @param string $type
+ */
+ private function triggerListeners($text, $type, array $context)
+ {
+ foreach ($this->listeners as $listener) {
+ $listener->bibTexUnitFound($text, $type, $context);
+ }
+ }
+
+ private function triggerListenersWithCurrentBuffer()
+ {
+ $snapshot = $this->takeBufferSnapshot();
+ $text = $snapshot['text'];
+ $context = $snapshot['context'];
+ $this->triggerListeners($text, $this->state, $context);
+ }
+
+ /**
+ * @param string $type
+ */
+ private function triggerListenersWithFirstTagSnapshotAs($type)
+ {
+ if (empty($this->firstTagSnapshot)) {
+ return;
+ }
+ $text = $this->firstTagSnapshot['text'];
+ $context = $this->firstTagSnapshot['context'];
+ $this->firstTagSnapshot = null;
+ $this->triggerListeners($text, $type, $context);
+ }
+
+ // ----- Buffer tools ------------------------------------------------------
+
+ /**
+ * @param string $char
+ */
+ private function appendToBuffer($char)
+ {
+ if (empty($this->buffer)) {
+ $this->bufferOffset = $this->offset;
+ }
+ $this->buffer .= $char;
+ }
+
+ /**
+ * @return array
+ */
+ private function takeBufferSnapshot()
+ {
+ $snapshot = [
+ 'text' => $this->buffer,
+ 'context' => [
+ 'offset' => $this->bufferOffset,
+ 'length' => $this->offset - $this->bufferOffset,
+ ],
+ ];
+ $this->bufferOffset = null;
+ $this->buffer = '';
+
+ return $snapshot;
+ }
+
+ // ----- Exception throwers ------------------------------------------------
+
+ /**
+ * @param string $char
+ * @param bool $availability
+ */
+ private function throwExceptionAccordingToConcatenationAvailability($char, $availability)
+ {
+ if ($availability === $this->mayConcatenateTagContent) {
+ throw ParserException::unexpectedCharacter($char, $this->line, $this->column);
+ }
+ }
+
+ /**
+ * @param string $char
+ */
+ private function throwExceptionIfBufferIsEmpty($char)
+ {
+ if (empty($this->buffer)) {
+ throw ParserException::unexpectedCharacter($char, $this->line, $this->column);
+ }
+ }
+
+ /**
+ * @param string $char
+ */
+ private function throwExceptionIfReadingEntry($char)
+ {
+ if ($this->isEntryState($this->state)) {
+ throw ParserException::unexpectedCharacter($char, $this->line, $this->column);
+ }
+ }
+
+ // ----- Auxiliaries -------------------------------------------------------
+
+ /**
+ * @param string $state
+ *
+ * @return bool
+ */
+ private function isEntryState($state)
+ {
+ return self::NONE !== $state && self::COMMENT !== $state;
+ }
+
+ /**
+ * @param string $char
+ *
+ * @return bool
+ */
+ private function isWhitespace($char)
+ {
+ return ' ' === $char || "\t" === $char || "\n" === $char || "\r" === $char;
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Processor/DateProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/DateProcessor.php
new file mode 100644
index 0000000..e07ee10
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Processor/DateProcessor.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Processor;
+
+use DateTimeImmutable;
+
+class DateProcessor
+{
+ use TagSearchTrait;
+ const TAG_NAME = '_date';
+
+ /**
+ * @var string
+ */
+ private $tagName;
+
+ /**
+ * @param string $tagName
+ */
+ public function __construct($tagName = null)
+ {
+ $this->tagName = $tagName ?: self::TAG_NAME;
+ }
+
+ /**
+ * @return array
+ */
+ public function __invoke(array $entry)
+ {
+ $yearTag = $this->tagSearch('year', array_keys($entry));
+ $monthTag = $this->tagSearch('month', array_keys($entry));
+ if (null !== $yearTag && null !== $monthTag) {
+ $year = (int) $entry[$yearTag];
+ $monthArray = explode('~', $entry[$monthTag]);
+ if (2 === \count($monthArray)) {
+ list($day, $month) = $monthArray;
+ $day = (int) $day;
+ $dateMonthNumber = date_parse($month);
+ $month = $dateMonthNumber['month'] ?: null;
+ if (checkdate($month, $day, $year)) {
+ $timestamp = mktime(0, 0, 0, $month, $day, $year);
+ $entry[$this->tagName] = new DateTimeImmutable(date('Y-m-d', $timestamp), new \DateTimeZone('UTC'));
+ }
+ }
+ }
+
+ return $entry;
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Processor/FillMissingProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/FillMissingProcessor.php
new file mode 100644
index 0000000..aefbad9
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Processor/FillMissingProcessor.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Processor;
+
+class FillMissingProcessor
+{
+ use TagSearchTrait;
+
+ /**
+ * @var array
+ */
+ protected $missingFields;
+
+ public function __construct(array $missingFields)
+ {
+ $this->missingFields = $missingFields;
+ }
+
+ public function __invoke(array $entry)
+ {
+ $tags = array_keys($entry);
+
+ foreach ($this->missingFields as $tag => $value) {
+ if (!$this->tagSearch($tag, $tags)) {
+ $entry[$tag] = $value;
+ }
+ }
+
+ return $entry;
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Processor/KeywordsProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/KeywordsProcessor.php
new file mode 100644
index 0000000..1f19b6b
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Processor/KeywordsProcessor.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Processor;
+
+/**
+ * Splits tags contents as array.
+ */
+class KeywordsProcessor
+{
+ use TagCoverageTrait;
+
+ public function __construct()
+ {
+ $this->setTagCoverage(['keywords']);
+ }
+
+ /**
+ * @return array
+ */
+ public function __invoke(array $entry)
+ {
+ $covered = $this->getCoveredTags(array_keys($entry));
+ foreach ($covered as $tag) {
+ $entry[$tag] = preg_split('/, |; /', $entry[$tag]);
+ }
+
+ return $entry;
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Processor/LatexToUnicodeProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/LatexToUnicodeProcessor.php
new file mode 100644
index 0000000..f5eec61
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Processor/LatexToUnicodeProcessor.php
@@ -0,0 +1,104 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Processor;
+
+use Composer\InstalledVersions;
+use Exception;
+use Pandoc\Pandoc;
+use RenanBr\BibTexParser\Exception\ProcessorException;
+use RuntimeException;
+
+/**
+ * Translates LaTeX texts to unicode.
+ */
+class LatexToUnicodeProcessor
+{
+ use TagCoverageTrait;
+
+ /** @var (callable(string): string)|null */
+ private $converter;
+
+ /**
+ * @return array
+ */
+ public function __invoke(array $entry)
+ {
+ $covered = $this->getCoveredTags(array_keys($entry));
+ foreach ($covered as $tag) {
+ // Translate string
+ if (\is_string($entry[$tag])) {
+ $entry[$tag] = $this->decode($entry[$tag]);
+ continue;
+ }
+
+ // Translate array
+ if (\is_array($entry[$tag])) {
+ array_walk_recursive($entry[$tag], function (&$text) {
+ if (\is_string($text)) {
+ $text = $this->decode($text);
+ }
+ });
+ }
+ }
+
+ return $entry;
+ }
+
+ /**
+ * @param mixed $text
+ *
+ * @return string
+ */
+ private function decode($text)
+ {
+ try {
+ return \call_user_func($this->getConverter(), $text);
+ } catch (Exception $exception) {
+ throw new ProcessorException(sprintf('Error while processing LaTeX to Unicode: %s', $exception->getMessage()), 0, $exception);
+ }
+ }
+
+ /**
+ * @return (callable(string): string)
+ */
+ private function getConverter()
+ {
+ if ($this->converter) {
+ return $this->converter;
+ }
+
+ if (InstalledVersions::isInstalled('ueberdosis/pandoc')) {
+ $pandoc = new Pandoc();
+
+ return $this->converter = static function ($text) use ($pandoc) {
+ // @phpstan-ignore-next-line
+ return mb_substr($pandoc->input($text)->execute([
+ '--from', 'latex',
+ '--to', 'plain',
+ '--wrap', 'none',
+ ]), 0, -1);
+ };
+ } elseif (InstalledVersions::isInstalled('ryakad/pandoc-php')) {
+ $pandoc = new Pandoc();
+
+ return $this->converter = static function ($text) use ($pandoc) {
+ return $pandoc->runWith($text, [
+ 'from' => 'latex',
+ 'to' => 'plain',
+ 'wrap' => 'none',
+ ]);
+ };
+ }
+
+ throw new RuntimeException('Pandoc wrapper not installed. Try running "composer require ueberdosis/pandoc"');
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Processor/NamesProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/NamesProcessor.php
new file mode 100644
index 0000000..2d832eb
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Processor/NamesProcessor.php
@@ -0,0 +1,243 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Processor;
+
+use RenanBr\BibTexParser\Exception\ProcessorException;
+
+/**
+ * Splits names in four parts: First Von Last Jr.
+ *
+ * This class includes source code adapted from the Structures_BibTex package,
+ * (c) Elmar Pitschke , included here under PHP license:
+ * http://www.php.net/license/3_0.txt
+ *
+ * @author Andre Chalom
+ *
+ * @see https://github.com/pear/Structures_BibTex
+ */
+class NamesProcessor
+{
+ use TagCoverageTrait;
+
+ public function __construct()
+ {
+ $this->setTagCoverage(['author', 'editor']);
+ }
+
+ /**
+ * @return array
+ */
+ public function __invoke(array $entry)
+ {
+ $covered = $this->getCoveredTags(array_keys($entry));
+ foreach ($covered as $tag) {
+ $entry[$tag] = $this->extractAuthors($entry[$tag]);
+ }
+
+ return $entry;
+ }
+
+ /**
+ * Extracting the authors.
+ *
+ * @param string $entry The entry with the authors
+ *
+ * @return array the extracted authors
+ *
+ * @author Elmar Pitschke
+ */
+ private function extractAuthors($entry)
+ {
+ // Sanitizes the entry to remove unwanted whitespace
+ $entry = trim(preg_replace('/\s+/', ' ', $entry));
+
+ $authorarray = [];
+ $authorarray = explode(' and ', $entry);
+ for ($i = 0; $i < \count($authorarray); ++$i) {
+ $author = trim($authorarray[$i]);
+ /*The first version of how an author could be written (First von Last)
+ has no commas in it*/
+ $first = '';
+ $von = '';
+ $last = '';
+ $jr = '';
+ if (false === mb_strpos($author, ',')) {
+ $tmparray = [];
+ $tmparray = preg_split('/[\s\~]/', $author);
+ $size = \count($tmparray);
+ if (1 === $size) { //There is only a last
+ $last = $tmparray[0];
+ } elseif (2 === $size) { //There is a first and a last
+ $first = $tmparray[0];
+ $last = $tmparray[1];
+ } else {
+ $invon = false;
+ $inlast = false;
+ for ($j = 0; $j < ($size - 1); ++$j) {
+ if ($inlast) {
+ $last .= ' '.$tmparray[$j];
+ } elseif ($invon) {
+ try {
+ $case = $this->determineCase($tmparray[$j]);
+
+ if ((0 === $case) || (-1 === $case)) { //Change from von to last
+ //You only change when there is no more lower case there
+ $islast = true;
+ for ($k = ($j + 1); $k < ($size - 1); ++$k) {
+ try {
+ $futurecase = $this->determineCase($tmparray[$k]);
+ if (0 === $futurecase) {
+ $islast = false;
+ }
+ } catch (ProcessorException $sbe) {
+ // Ignore
+ }
+ }
+ if ($islast) {
+ $inlast = true;
+ if (-1 === $case) { //Caseless belongs to the last
+ $last .= ' '.$tmparray[$j];
+ } else {
+ $von .= ' '.$tmparray[$j];
+ }
+ } else {
+ $von .= ' '.$tmparray[$j];
+ }
+ } else {
+ $von .= ' '.$tmparray[$j];
+ }
+ } catch (ProcessorException $sbe) {
+ // Ignore
+ }
+ } else {
+ try {
+ $case = $this->determineCase($tmparray[$j]);
+ if (0 === $case) { //Change from first to von
+ $invon = true;
+ $von .= ' '.$tmparray[$j];
+ } else {
+ $first .= ' '.$tmparray[$j];
+ }
+ } catch (ProcessorException $sbe) {
+ // Ignore
+ }
+ }
+ }
+ //The last entry is always the last!
+ $last .= ' '.$tmparray[$size - 1];
+ }
+ } else { //Version 2 and 3
+ $tmparray = [];
+ $tmparray = explode(',', $author);
+ //The first entry must contain von and last
+ $vonlastarray = [];
+ $vonlastarray = explode(' ', $tmparray[0]);
+ $size = \count($vonlastarray);
+ if (1 === $size) { //Only one entry->got to be the last
+ $last = $vonlastarray[0];
+ } else {
+ $inlast = false;
+ for ($j = 0; $j < ($size - 1); ++$j) {
+ if ($inlast) {
+ $last .= ' '.$vonlastarray[$j];
+ } else {
+ if (0 !== ($this->determineCase($vonlastarray[$j]))) { //Change from von to last
+ $islast = true;
+ for ($k = ($j + 1); $k < ($size - 1); ++$k) {
+ try {
+ $case = $this->determineCase($vonlastarray[$k]);
+ if (0 === $case) {
+ $islast = false;
+ }
+ } catch (ProcessorException $sbe) {
+ // Ignore
+ }
+ }
+ if ($islast) {
+ $inlast = true;
+ $last .= ' '.$vonlastarray[$j];
+ } else {
+ $von .= ' '.$vonlastarray[$j];
+ }
+ } else {
+ $von .= ' '.$vonlastarray[$j];
+ }
+ }
+ }
+ $last .= ' '.$vonlastarray[$size - 1];
+ }
+ //Now we check if it is version three (three entries in the array (two commas)
+ if (3 === \count($tmparray)) {
+ $jr = $tmparray[1];
+ }
+ //Everything in the last entry is first
+ $first = $tmparray[\count($tmparray) - 1];
+ }
+ $authorarray[$i] = ['first' => trim($first), 'von' => trim($von), 'last' => trim($last), 'jr' => trim($jr)];
+ }
+
+ return $authorarray;
+ }
+
+ /**
+ * Case Determination according to the needs of BibTex.
+ *
+ * To parse the Author(s) correctly a determination is needed
+ * to get the Case of a word. There are three possible values:
+ * - Upper Case (return value 1)
+ * - Lower Case (return value 0)
+ * - Caseless (return value -1)
+ *
+ * @param string $word
+ *
+ * @throws ProcessorException
+ *
+ * @return int The Case
+ *
+ * @author Elmar Pitschke
+ */
+ private function determineCase($word)
+ {
+ $ret = -1;
+ $trimmedword = trim($word);
+ /*We need this variable. Without the next of would not work
+ (trim changes the variable automatically to a string!)*/
+ if (\is_string($word) && (mb_strlen($trimmedword) > 0)) {
+ $i = 0;
+ $found = false;
+ $openbrace = 0;
+ while (!$found && ($i <= mb_strlen($word))) {
+ $letter = mb_substr($trimmedword, $i, 1);
+ $ord = \ord($letter);
+ if (123 === $ord) { //Open brace
+ ++$openbrace;
+ }
+ if (125 === $ord) { //Closing brace
+ --$openbrace;
+ }
+ if (($ord >= 65) && ($ord <= 90) && (0 === $openbrace)) { //The first character is uppercase
+ $ret = 1;
+ $found = true;
+ } elseif (($ord >= 97) && ($ord <= 122) && (0 === $openbrace)) { //The first character is lowercase
+ $ret = 0;
+ $found = true;
+ } else { //Not yet found
+ ++$i;
+ }
+ }
+ } else {
+ throw new ProcessorException('Could not determine case on word: '.$word);
+ }
+
+ return $ret;
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Processor/TagCoverageTrait.php b/vendor/renanbr/bibtex-parser/src/Processor/TagCoverageTrait.php
new file mode 100644
index 0000000..6bbea3d
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Processor/TagCoverageTrait.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Processor;
+
+trait TagCoverageTrait
+{
+ use TagSearchTrait;
+
+ /** @var array */
+ private $tagCoverageList = [
+ '_original',
+ '_type',
+ ];
+
+ /** @var string */
+ private $tagCoverageStrategy = 'blacklist';
+
+ /**
+ * @param array $tags List of tags to be covered
+ * @param string $strategy Can assume "whitelist" (default) or "blacklist"
+ */
+ public function setTagCoverage($tags, $strategy = null)
+ {
+ $this->tagCoverageList = $tags;
+ $this->tagCoverageStrategy = $strategy ?: 'whitelist';
+ }
+
+ /**
+ * Calculates which tags are covered.
+ *
+ * The search performed internally is case-insensitive.
+ *
+ * @return array
+ */
+ protected function getCoveredTags(array $tags)
+ {
+ // Finds for actual tag names
+ $matched = [];
+ foreach ($this->tagCoverageList as $original) {
+ $actual = $this->tagSearch($original, $tags);
+ if (null !== $actual) {
+ $matched[] = $actual;
+ }
+ }
+
+ // Whitelist
+ if ('whitelist' === $this->tagCoverageStrategy) {
+ return $matched;
+ }
+
+ // Blacklist
+ return array_values(array_diff($tags, $matched));
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Processor/TagNameCaseProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/TagNameCaseProcessor.php
new file mode 100644
index 0000000..ad6dda8
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Processor/TagNameCaseProcessor.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Processor;
+
+/**
+ * Change the case of all tag names.
+ */
+class TagNameCaseProcessor
+{
+ /** @var int */
+ private $case;
+
+ /**
+ * @param int $case
+ */
+ public function __construct($case)
+ {
+ $this->case = $case;
+ }
+
+ /**
+ * @return array
+ */
+ public function __invoke(array $entry)
+ {
+ return array_change_key_case($entry, $this->case);
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Processor/TagSearchTrait.php b/vendor/renanbr/bibtex-parser/src/Processor/TagSearchTrait.php
new file mode 100644
index 0000000..99ab10e
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Processor/TagSearchTrait.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Processor;
+
+trait TagSearchTrait
+{
+ /**
+ * Searches for the actual name of a tag.
+ *
+ * The search performed is case-insensitive.
+ *
+ * @param string $needle
+ *
+ * @return string|null
+ */
+ protected function tagSearch($needle, array $haystack)
+ {
+ foreach ($haystack as $actual) {
+ if (0 === strcasecmp($needle, $actual)) {
+ return $actual;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Processor/TrimProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/TrimProcessor.php
new file mode 100644
index 0000000..bc77595
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Processor/TrimProcessor.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Processor;
+
+/**
+ * @author Florent DESPIERRES
+ */
+class TrimProcessor
+{
+ use TagCoverageTrait;
+
+ public function __construct(array $fields = null)
+ {
+ if ($fields) {
+ $this->setTagCoverage($fields);
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function __invoke(array $entry)
+ {
+ $covered = $this->getCoveredTags(array_keys($entry));
+ foreach ($covered as $tag) {
+ $entry[$tag] = $this->trim($entry[$tag]);
+ }
+
+ return $entry;
+ }
+
+ private function trim($value)
+ {
+ if (\is_array($value)) {
+ $trimmed = [];
+ foreach ($value as $key => $subValue) {
+ $trimmed[$key] = $this->trim($subValue);
+ }
+
+ return $trimmed;
+ }
+
+ if (\is_string($value)) {
+ return trim($value);
+ }
+
+ return $value;
+ }
+}
diff --git a/vendor/renanbr/bibtex-parser/src/Processor/UrlFromDoiProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/UrlFromDoiProcessor.php
new file mode 100644
index 0000000..7309a6a
--- /dev/null
+++ b/vendor/renanbr/bibtex-parser/src/Processor/UrlFromDoiProcessor.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RenanBr\BibTexParser\Processor;
+
+class UrlFromDoiProcessor
+{
+ use TagSearchTrait;
+
+ const FORMAT = 'https://doi.org/%s';
+
+ /**
+ * @var string
+ */
+ private $urlFormat;
+
+ /**
+ * @param string $urlFormat
+ */
+ public function __construct($urlFormat = null)
+ {
+ $this->urlFormat = $urlFormat ?: self::FORMAT;
+ }
+
+ /**
+ * @return array
+ */
+ public function __invoke(array $entry)
+ {
+ $doiTag = $this->tagSearch('doi', array_keys($entry));
+ $urlTag = $this->tagSearch('url', array_keys($entry));
+ if (null === $urlTag && null !== $doiTag) {
+ $doiValue = $entry[$doiTag];
+ if (\is_string($doiValue) && '' !== $doiValue) {
+ $entry['url'] = sprintf($this->urlFormat, $doiValue);
+ }
+ }
+
+ return $entry;
+ }
+}