Browse Source

Merge b88da5c996 into 249f26eb52

pull/81/merge
Chris 8 years ago
committed by GitHub
parent
commit
12561b4abc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .gitignore
  2. 4
      .travis.yml
  3. 0
      bin/bibtexbrowser-cli.php
  4. 14
      composer.json
  5. 203
      composer.lock
  6. 0
      public/bibacid-utf8.bib
  7. 25
      public/embed.php
  8. 16
      public/index.php
  9. 161
      src/BibDBBuilder.php
  10. 26
      src/Bibliography.php
  11. 195
      src/Definitions.php
  12. 27
      src/ParserDelegate.php
  13. 235
      src/StateBasedBibtexParser.php
  14. 19
      src/StringEntry.php
  15. 40
      src/XMLPrettyPrinter.php
  16. 231
      src/bibacid-utf8.bib
  17. 494
      src/bibtexbrowser.php
  18. 4
      tests/bibtexbrowser-test.php
  19. 0
      wiki/bibtexbrowser-documentation.wiki
  20. 0
      wiki/bibtexbrowser-users.wiki

2
.gitignore

@ -0,0 +1,2 @@
*.dat
vendor

4
.travis.yml

@ -10,6 +10,8 @@ php:
script:
- curl -L -o reflectivedoc.php https://www.monperrus.net/martin/reflectivedoc.php.txt
- curl -L -o gakowiki-syntax.php https://www.monperrus.net/martin/gakowiki-syntax.php.txt
- phpunit bibtexbrowser-test.php
- cp ./src/bibtexbrowser.php ./tests/
- cp ./src/bibacid-utf8.bib ./tests/
- phpunit tests/bibtexbrowser-test.php
sudo: false

0
bibtexbrowser-cli.php → bin/bibtexbrowser-cli.php

14
composer.json

@ -11,6 +11,18 @@
}
],
"require": {
"php": ">=4"
"php": ">=5.3.0"
},
"autoload": {
"psr-4": {
"Monperrus\\BibtexBrowser\\": "src/"
}
},
"scripts": {
"serve": "@php -S localhost:29896 -t public/"
},
"require-dev": {
"monolog/monolog": "^1.23",
"kint-php/kint": "^2.2"
}
}

203
composer.lock

@ -0,0 +1,203 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "fa3a6762d454ebe3dce634e74511aae2",
"packages": [],
"packages-dev": [
{
"name": "kint-php/kint",
"version": "2.2",
"source": {
"type": "git",
"url": "https://github.com/kint-php/kint.git",
"reference": "b091715eadaf6e1a7ef927f3e81d1004611d2aea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kint-php/kint/zipball/b091715eadaf6e1a7ef927f3e81d1004611d2aea",
"reference": "b091715eadaf6e1a7ef927f3e81d1004611d2aea",
"shasum": ""
},
"require": {
"php": ">=5.1.2"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.0",
"phpunit/phpunit": "^4.0",
"symfony/finder": "^2.6"
},
"type": "library",
"autoload": {
"files": [
"init.php"
],
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rokas Šleinius",
"homepage": "https://github.com/raveren"
},
{
"name": "Jonathan Vollebregt",
"homepage": "https://github.com/jnvsor"
},
{
"name": "Contributors",
"homepage": "https://github.com/kint-php/kint/graphs/contributors"
}
],
"description": "Kint - debugging tool for PHP developers",
"homepage": "https://kint-php.github.io/kint/",
"keywords": [
"debug",
"kint",
"php"
],
"time": "2017-09-06T17:46:03+00:00"
},
{
"name": "monolog/monolog",
"version": "1.23.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
"reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/log": "~1.0"
},
"provide": {
"psr/log-implementation": "1.0.0"
},
"require-dev": {
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
"doctrine/couchdb": "~1.0@dev",
"graylog2/gelf-php": "~1.0",
"jakub-onderka/php-parallel-lint": "0.9",
"php-amqplib/php-amqplib": "~2.4",
"php-console/php-console": "^3.1.3",
"phpunit/phpunit": "~4.5",
"phpunit/phpunit-mock-objects": "2.3.0",
"ruflin/elastica": ">=0.90 <3.0",
"sentry/sentry": "^0.13",
"swiftmailer/swiftmailer": "^5.3|^6.0"
},
"suggest": {
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
"ext-mongo": "Allow sending log messages to a MongoDB server",
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
"mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
"php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
"php-console/php-console": "Allow sending log messages to Google Chrome",
"rollbar/rollbar": "Allow sending log messages to Rollbar",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server",
"sentry/sentry": "Allow sending log messages to a Sentry server"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Monolog\\": "src/Monolog"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
"homepage": "http://github.com/Seldaek/monolog",
"keywords": [
"log",
"logging",
"psr-3"
],
"time": "2017-06-19T01:22:40+00:00"
},
{
"name": "psr/log",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2016-10-10T12:19:37+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=4"
},
"platform-dev": []
}

0
bibacid-utf8.bib → public/bibacid-utf8.bib

25
public/embed.php

@ -0,0 +1,25 @@
<?PHP
print <<<HTML
<!doctype html>
<html>
<head></head>
<body>
HTML;
$_GET['library']=1;
require_once('../src/bibtexbrowser.php');
$db = new BibDataBase();
$db->load('bibacid-utf8.bib');
$query = array('year'=>'1997');
$entries=$db->multisearch($query);
uasort($entries, 'compare_bib_entries');
foreach ($entries as $bibentry) {
print $bibentry->toHTML()."<br/>";
}
print <<<HTML
</body>
</html>
HTML;
exit;

16
public/index.php

@ -0,0 +1,16 @@
<?PHP
$_GET['bib'] = "bibacid-utf8.bib";
require_once "../vendor/autoload.php";
use Monperrus\BibtexBrowser\Bibliography;
$config = array("bib" => "bibacid-utf8.bib",
"all" => 1,
"author" => "",
"academic" => 1
);
$browser = new Bibliography($config);
$browser->print();

161
src/BibDBBuilder.php

@ -0,0 +1,161 @@
<?PHP
namespace Monperrus\BibtexBrowser;
/** builds arrays of BibEntry objects from a bibtex file.
usage:
<pre>
$empty_array = array();
$db = new BibDBBuilder(); // see also factory method createBibDBBuilder
$db->build('bibacid-utf8.bib'); // parses bib file
print_r($db->builtdb);// an associated array key -> BibEntry objects
print_r($db->stringdb);// an associated array key -> strings representing @string
</pre>
notes:
method build can be used several times, bibtex entries are accumulated in the builder
*/
class BibDBBuilder extends ParserDelegate {
/** A hashtable from keys to bib entries (BibEntry). */
var $builtdb = array();
/** A hashtable of constant strings */
var $stringdb = array();
var $filename;
var $currentEntry;
function build($bibfilename, $handle = NULL) {
$this->filename = $bibfilename;
if ($handle == NULL) {
$handle = fopen($bibfilename, "r");
}
if (!$handle) die ('cannot open '.$bibfilename);
$parser = new StateBasedBibtexParser($this);
$parser->parse($handle);
fclose($handle);
//print_r(array_keys($this->builtdb));
//print_r($this->builtdb);
}
function getBuiltDb() {
//print_r($this->builtdb);
return $this->builtdb;
}
function beginFile() {
}
function endFile() {
// resolving crossrefs
// we are careful with PHP 4 semantics
foreach (array_keys($this->builtdb) as $key) {
$bib = $this->builtdb[$key];
if ($bib->hasField('crossref')) {
if (isset($this->builtdb[$bib->getField('crossref')])) {
$crossrefEntry = $this->builtdb[$bib->getField('crossref')];
$bib->crossref = $crossrefEntry;
foreach($crossrefEntry->getFields() as $k => $v) {
// copying the fields of the cross ref
// only if they don't exist yet
if (!$bib->hasField($k)) {
$bib->setField($k,$v);
}
}
}
}
}
//print_r($this->builtdb);
}
function setEntryField($fieldkey,$entryvalue) {
$fieldkey=trim($fieldkey);
// support for Bibtex concatenation
// see http://newton.ex.ac.uk/tex/pack/bibtex/btxdoc/node3.html
// (?<! is a negative look-behind assertion, see http://www.php.net/manual/en/regexp.reference.assertions.php
$entryvalue_array=preg_split('/(?<!\\\\)#/', $entryvalue);
foreach ($entryvalue_array as $k=>$v) {
// spaces are allowed when using # and they are not taken into account
// however # is not itself replaced by a space
// warning: @strings are not case sensitive
// see http://newton.ex.ac.uk/tex/pack/bibtex/btxdoc/node3.html
$stringKey=strtolower(trim($v));
if (isset($this->stringdb[$stringKey]))
{
// this field will be formated later by xtrim and latex2html
$entryvalue_array[$k]=$this->stringdb[$stringKey]->value;
// we keep a trace of this replacement
// so as to produce correct bibtex snippets
$this->currentEntry->constants[$stringKey]=$this->stringdb[$stringKey]->value;
}
}
$entryvalue=implode('',$entryvalue_array);
$this->currentEntry->setField($fieldkey,$entryvalue);
}
function setEntryType($entrytype) {
$this->currentEntry->setType($entrytype);
}
function setEntryKey($entrykey) {
//echo "new entry:".$entrykey."\n";
$this->currentEntry->setKey($entrykey);
}
function beginEntry() {
$this->currentEntry = createBibEntry();
$this->currentEntry->setFile($this->filename);
}
function endEntry($entrysource) {
// we add a timestamp
$this->currentEntry->timestamp();
// we add a key if there is no key
if (!$this->currentEntry->hasField(Q_KEY) && $this->currentEntry->getType()!='string') {
$this->currentEntry->setField(Q_KEY,md5($entrysource));
}
// we set the fulltext
$this->currentEntry->text = $entrysource;
// we format the author names in a special field
// to enable search
if ($this->currentEntry->hasField('author')) {
$this->currentEntry->setField(Q_INNER_AUTHOR,$this->currentEntry->getFormattedAuthorsString());
foreach($this->currentEntry->getCanonicalAuthors() as $author) {
$homepage_key = $this->currentEntry->getHomePageKey($author);
if (isset($this->stringdb[$homepage_key])) {
$this->currentEntry->homepages[$homepage_key] = $this->stringdb[$homepage_key]->value;
}
}
}
// ignoring jabref comments
if (($this->currentEntry->getType()=='comment')) {
/* do nothing for jabref comments */
}
// we add it to the string database
else if ($this->currentEntry->getType()=='string') {
foreach($this->currentEntry->fields as $k => $v) {
$k!=Q_INNER_TYPE and $this->stringdb[$k] = new StringEntry($k,$v,$this->filename);
}
}
// we add it to the database
else {
$this->builtdb[$this->currentEntry->getKey()] = $this->currentEntry;
}
}
} // end class BibDBBuilder

26
src/Bibliography.php

@ -0,0 +1,26 @@
<?PHP
namespace Monperrus\BibtexBrowser;
use Monperrus\BibtexBrowser\Definitions;
class Bibliography
{
private $defaultConfig = array("bib" => null,
"all" => null,
"author" => null,
"library" => null,
"academic" => null);
public function __construct($userConfig = array())
{
// (PHP 5 >= 5.3.0, PHP 7)
// $userConfig precedent over defaultConfig; $_GET over $userConfig
$_GET = array_replace($this->defaultConfig, $userConfig, $_GET);
}
public function print()
{
require_once "bibtexbrowser.php";
}
}

195
src/Definitions.php

@ -0,0 +1,195 @@
<?PHP
namespace Monperrus\BibtexBrowser;
class Definitions
{
public function __construct()
{
// the encoding of your bibtex file
@define('BIBTEX_INPUT_ENCODING','UTF-8');//@define('BIBTEX_INPUT_ENCODING','iso-8859-1');//define('BIBTEX_INPUT_ENCODING','windows-1252');
// the encoding of the HTML output
@define('OUTPUT_ENCODING','UTF-8');
// print a warning if deprecated variable is used
if (defined('ENCODING')) {
echo 'ENCODING has been replaced by BIBTEX_INPUT_ENCODING and OUTPUT_ENCODING';
}
// number of bib items per page
// we use the same parameter 'num' as Google
@define('PAGE_SIZE',isset($_GET['num'])?(preg_match('/^\d+$/',$_GET['num'])?$_GET['num']:10000):14);
// bibtexbrowser uses a small piece of Javascript to improve the user experience
// see http://en.wikipedia.org/wiki/Progressive_enhancement
// if you don't like it, you can be disable it by adding in bibtexbrowser.local.php
// @define('BIBTEXBROWSER_USE_PROGRESSIVE_ENHANCEMENT',false);
@define('BIBTEXBROWSER_USE_PROGRESSIVE_ENHANCEMENT',true);
@define('BIBLIOGRAPHYSTYLE','DefaultBibliographyStyle');// this is the name of a function
@define('BIBLIOGRAPHYSECTIONS','DefaultBibliographySections');// this is the name of a function
@define('BIBLIOGRAPHYTITLE','DefaultBibliographyTitle');// this is the name of a function
// shall we load MathJax to render math in $…$ in HTML?
@define('BIBTEXBROWSER_RENDER_MATH', true);
@define('MATHJAX_URI', '//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML.js');
// the default jquery URI
@define('JQUERY_URI', '//code.jquery.com/jquery-1.5.1.min.js');
// can we load bibtex files on external servers?
@define('BIBTEXBROWSER_LOCAL_BIB_ONLY', true);
// the default view in {SimpleDisplay,AcademicDisplay,RSSDisplay,BibtexDisplay}
@define('BIBTEXBROWSER_DEFAULT_DISPLAY','SimpleDisplay');
// the default template
@define('BIBTEXBROWSER_DEFAULT_TEMPLATE','HTMLTemplate');
// the target frame of menu links
@define('BIBTEXBROWSER_MENU_TARGET','main'); // might be define('BIBTEXBROWSER_MENU_TARGET','_self'); in bibtexbrowser.local.php
@define('ABBRV_TYPE','index');// may be year/x-abbrv/key/none/index/keys-index
// are robots allowed to crawl and index bibtexbrowser generated pages?
@define('BIBTEXBROWSER_ROBOTS_NOINDEX',false);
//the default view in the "main" (right hand side) frame
@define('BIBTEXBROWSER_DEFAULT_FRAME','year=latest'); // year=latest,all and all valid bibtexbrowser queries
// Wrapper to use when we are included by another script
@define('BIBTEXBROWSER_EMBEDDED_WRAPPER', 'NoWrapper');
// Main class to use
@define('BIBTEXBROWSER_MAIN', 'Dispatcher');
// default order functions
// Contract Returns < 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal.
// can be @define('ORDER_FUNCTION','compare_bib_entry_by_title');
// can be @define('ORDER_FUNCTION','compare_bib_entry_by_bibtex_order');
@define('ORDER_FUNCTION','compare_bib_entry_by_year');
@define('ORDER_FUNCTION_FINE','compare_bib_entry_by_month');
// only displaying the n newest entries
@define('BIBTEXBROWSER_NEWEST',5);
@define('BIBTEXBROWSER_NO_DEFAULT', false);
// BIBTEXBROWSER_LINK_STYLE defines which function to use to display the links of a bibtex entry
@define('BIBTEXBROWSER_LINK_STYLE','bib2links_default'); // can be 'nothing' (a function that does nothing)
// do we add [bibtex] links ?
@define('BIBTEXBROWSER_BIBTEX_LINKS',true);
// do we add [pdf] links ?
@define('BIBTEXBROWSER_PDF_LINKS',true);
// do we add [doi] links ?
@define('BIBTEXBROWSER_DOI_LINKS',true);
// do we add [gsid] links (Google Scholar)?
@define('BIBTEXBROWSER_GSID_LINKS',true);
// should pdf, doi, url, gsid links be opened in a new window?
@define('BIBTEXBROWSER_LINKS_TARGET','_self');// can be _blank (new window), _top (with frames)
// should authors be linked to [none/homepage/resultpage]
// none: nothing
// their homepage if defined as @strings
// their publication lists according to this bibtex
@define('BIBTEXBROWSER_AUTHOR_LINKS','homepage');
// BIBTEXBROWSER_LAYOUT defines the HTML rendering layout of the produced HTML
// may be table/list/ordered_list/definition/none (for <table>, <ol>, <dl>, nothing resp.).
// for list/ordered_list, the abbrevations are not taken into account (see ABBRV_TYPE)
// for ordered_list, the index is given by HTML directly (in increasing order)
@define('BIBTEXBROWSER_LAYOUT','table');
// should the original bibtex be displayed or a reconstructed one with filtering
// values: original/reconstructed
// warning, with reconstructed, the latex markup for accents/diacritics is lost
@define('BIBTEXBROWSER_BIBTEX_VIEW','original');
// a list of fields that will not be shown in the bibtex view if BIBTEXBROWSER_BIBTEX_VIEW=reconstructed
@define('BIBTEXBROWSER_BIBTEX_VIEW_FILTEREDOUT','comment|note|file');
// should Latex macros be executed (e.g. \'e -> é
@define('BIBTEXBROWSER_USE_LATEX2HTML',true);
// Which is the first html <hN> level that should be used in embedded mode?
@define('BIBTEXBROWSER_HTMLHEADINGLEVEL', 2);
@define('BIBTEXBROWSER_ACADEMIC_TOC', false);
@define('BIBTEXBROWSER_DEBUG',false);
// how to print authors names?
// default => as in the bibtex file
// USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT = true => "Meyer, Herbert"
// USE_INITIALS_FOR_NAMES = true => "Meyer H"
// USE_FIRST_THEN_LAST => Herbert Meyer
@define('USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT',false);// output authors in a comma separated form, e.g. "Meyer, H"?
@define('USE_INITIALS_FOR_NAMES',false); // use only initials for all first names?
@define('USE_FIRST_THEN_LAST',false); // use only initials for all first names?
@define('FORCE_NAMELIST_SEPARATOR', ''); // if non-empty, use this to separate multiple names regardless of USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT
@define('LAST_AUTHOR_SEPARATOR',' and ');
@define('TYPES_SIZE',10); // number of entry types per table
@define('YEAR_SIZE',20); // number of years per table
@define('AUTHORS_SIZE',30); // number of authors per table
@define('TAGS_SIZE',30); // number of keywords per table
@define('READLINE_LIMIT',1024);
@define('Q_YEAR', 'year');
@define('Q_YEAR_PAGE', 'year_page');
@define('Q_YEAR_INPRESS', 'in press');
@define('Q_YEAR_ACCEPTED', 'accepted');
@define('Q_YEAR_SUBMITTED', 'submitted');
@define('Q_FILE', 'bib');
@define('Q_AUTHOR', 'author');
@define('Q_AUTHOR_PAGE', 'author_page');
@define('Q_TAG', 'keywords');
@define('Q_TAG_PAGE', 'keywords_page');
@define('Q_TYPE', 'type');// used for queries
@define('Q_TYPE_PAGE', 'type_page');
@define('Q_ALL', 'all');
@define('Q_ENTRY', 'entry');
@define('Q_KEY', 'key');
@define('Q_KEYS', 'keys'); // filter entries using a url-encoded, JSON-encoded array of bibtex keys
@define('Q_SEARCH', 'search');
@define('Q_EXCLUDE', 'exclude');
@define('Q_RESULT', 'result');
@define('Q_ACADEMIC', 'academic');
@define('Q_DB', 'bibdb');
@define('Q_LATEST', 'latest');
@define('Q_RANGE', 'range');
@define('AUTHOR', 'author');
@define('EDITOR', 'editor');
@define('SCHOOL', 'school');
@define('TITLE', 'title');
@define('BOOKTITLE', 'booktitle');
@define('YEAR', 'year');
@define('BUFFERSIZE',100000);
@define('MULTIPLE_BIB_SEPARATOR',';');
@define('METADATA_GS',true);
@define('METADATA_DC',true);
@define('METADATA_OPENGRAPH',true);
@define('METADATA_EPRINTS',false);
// define sort order for special values in 'year' field
// highest number is sorted first
// don't exceed 0 though, since the values are added to PHP_INT_MAX
@define('ORDER_YEAR_INPRESS', -0);
@define('ORDER_YEAR_ACCEPTED', -1);
@define('ORDER_YEAR_SUBMITTED', -2);
@define('ORDER_YEAR_OTHERNONINT', -3);
// in embedded mode, we still need a URL for displaying bibtex entries alone
// this is usually resolved to bibtexbrowser.php
// but can be overridden in bibtexbrowser.local.php
// for instance with @define('BIBTEXBROWSER_URL',''); // links to the current page with ?
//@define('BIBTEXBROWSER_URL',basename(__FILE__));
@define('BIBTEXBROWSER_URL',parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
// *************** END CONFIGURATION
define('Q_INNER_AUTHOR', '_author');// internally used for representing the author
define('Q_INNER_TYPE', 'x-bibtex-type');// used for representing the type of the bibtex entry internally
@define('Q_INNER_KEYS_INDEX', '_keys-index');// used for storing indices in $_GET[Q_KEYS] array
}
}

27
src/ParserDelegate.php

@ -0,0 +1,27 @@
<?PHP
namespace Monperrus\BibtexBrowser;
/** a default empty implementation of a delegate for StateBasedBibtexParser */
class ParserDelegate {
function beginFile() {}
function endFile() {}
function setEntryField($finalkey,$entryvalue) {}
function setEntryType($entrytype) {}
function setEntryKey($entrykey) {}
function beginEntry() {}
function endEntry($entrysource) {}
/** called for each sub parts of type {part} of a field value
* for now, only CURLYTOP and CURLYONE events
*/
function entryValuePart($key, $value, $type) {}
} // end class ParserDelegate

235
src/StateBasedBibtexParser.php

@ -0,0 +1,235 @@
<?PHP
namespace Monperrus\BibtexBrowser;
class StateBasedBibtexParser
{
var $delegate;
public function __construct($delegate) {
$this->delegate = $delegate;
}
public function parse($handle) {
if (gettype($handle) == 'string') { throw new Exception('oops'); }
$delegate = $this->delegate;
// STATE DEFINITIONS
@define('NOTHING',1);
@define('GETTYPE',2);
@define('GETKEY',3);
@define('GETVALUE',4);
@define('GETVALUEDELIMITEDBYQUOTES',5);
@define('GETVALUEDELIMITEDBYQUOTES_ESCAPED',6);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS',7);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_ESCAPED',8);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL',9);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL_ESCAPED',10);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL',11);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL_ESCAPED',12);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL',13);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL_ESCAPED',14);
$state=NOTHING;
$entrytype='';
$entrykey='';
$entryvalue='';
$fieldvaluepart='';
$finalkey='';
$entrysource='';
// metastate
$isinentry = false;
$delegate->beginFile();
// if you encounter this error "Allowed memory size of xxxxx bytes exhausted"
// then decrease the size of the temp buffer below
$bufsize=BUFFERSIZE;
while (!feof($handle)) {
$sread=fread($handle,$bufsize);
//foreach(str_split($sread) as $s) {
for ( $i=0; $i < strlen( $sread ); $i++) { $s=$sread[$i];
if ($isinentry) $entrysource.=$s;
if ($state==NOTHING) {
// this is the beginning of an entry
if ($s=='@') {
$delegate->beginEntry();
$state = GETTYPE;
$isinentry = true;
$entrysource='@';
}
}
else if ($state==GETTYPE) {
// this is the beginning of a key
if ($s=='{') {
$state = GETKEY;
$delegate->setEntryType($entrytype);
$entrytype='';
}
else $entrytype=$entrytype.$s;
}
else if ($state==GETKEY) {
// now we get the value
if ($s=='=') {
$state = GETVALUE;
$fieldvaluepart='';
$finalkey=$entrykey;
$entrykey='';
}
// oups we only have the key :-) anyway
else if ($s=='}') {
$state = NOTHING;$isinentry = false;$delegate->endEntry($entrysource);
$entrykey='';
}
// OK now we look for values
else if ($s==',') {
$state=GETKEY;
$delegate->setEntryKey($entrykey);
$entrykey='';}
else { $entrykey=$entrykey.$s; }
}
// we just got a =, we can now receive the value, but we don't now whether the value
// is delimited by curly brackets, double quotes or nothing
else if ($state==GETVALUE) {
// the value is delimited by double quotes
if ($s=='"') {
$state = GETVALUEDELIMITEDBYQUOTES;
}
// the value is delimited by curly brackets
else if ($s=='{') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS;
}
// the end of the key and no value found: it is the bibtex key e.g. \cite{Descartes1637}
else if ($s==',') {
$state = GETKEY;
$delegate->setEntryField($finalkey,$entryvalue);
$entryvalue=''; // resetting the value buffer
}
// this is the end of the value AND of the entry
else if ($s=='}') {
$state = NOTHING;
$delegate->setEntryField($finalkey,$entryvalue);
$isinentry = false;$delegate->endEntry($entrysource);
$entryvalue=''; // resetting the value buffer
}
else if ($s==' ' || $s=="\t" || $s=="\n" || $s=="\r" ) {
// blank characters are not taken into account when values are not in quotes or curly brackets
}
else {
$entryvalue=$entryvalue.$s;
}
}
/* GETVALUEDELIMITEDBYCURLYBRACKETS* handle entries delimited by curly brackets and the possible nested curly brackets */
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS) {
if ($s=='\\') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_ESCAPED;
$entryvalue=$entryvalue.$s;}
else if ($s=='{') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL;
$entryvalue=$entryvalue.$s;
$delegate->entryValuePart($finalkey,$fieldvaluepart,'CURLYTOP');
$fieldvaluepart='';
}
else if ($s=='}') { // end entry
$state = GETVALUE;
$delegate->entryValuePart($finalkey,$fieldvaluepart,'CURLYTOP');
}
else {
$entryvalue=$entryvalue.$s;
$fieldvaluepart=$fieldvaluepart.$s;
}
}
// handle anti-slashed brackets
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_ESCAPED) {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS;
$entryvalue=$entryvalue.$s;
}
// in first level of curly bracket
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL) {
if ($s=='\\') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL_ESCAPED;
$entryvalue=$entryvalue.$s;}
else if ($s=='{') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL;$entryvalue=$entryvalue.$s;}
else if ($s=='}') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS;
$delegate->entryValuePart($finalkey,$fieldvaluepart,'CURLYONE');
$fieldvaluepart='';
$entryvalue=$entryvalue.$s;
}
else {
$entryvalue=$entryvalue.$s;
$fieldvaluepart=$fieldvaluepart.$s;
}
}
// handle anti-slashed brackets
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL_ESCAPED) {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL;
$entryvalue=$entryvalue.$s;
}
// in second level of curly bracket
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL) {
if ($s=='\\') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL_ESCAPED;
$entryvalue=$entryvalue.$s;}
else if ($s=='{') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL;$entryvalue=$entryvalue.$s;}
else if ($s=='}') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL;$entryvalue=$entryvalue.$s;}
else { $entryvalue=$entryvalue.$s;}
}
// handle anti-slashed brackets
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL_ESCAPED) {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL;
$entryvalue=$entryvalue.$s;
}
// in third level of curly bracket
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL) {
if ($s=='\\') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL_ESCAPED;
$entryvalue=$entryvalue.$s;}
else if ($s=='}') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL;$entryvalue=$entryvalue.$s;}
else { $entryvalue=$entryvalue.$s;}
}
// handle anti-slashed brackets
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL_ESCAPED) {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL;
$entryvalue=$entryvalue.$s;
}
/* handles entries delimited by double quotes */
else if ($state==GETVALUEDELIMITEDBYQUOTES) {
if ($s=='\\') {
$state = GETVALUEDELIMITEDBYQUOTES_ESCAPED;
$entryvalue=$entryvalue.$s;}
else if ($s=='"') {
$state = GETVALUE;
}
else { $entryvalue=$entryvalue.$s;}
}
// handle anti-double quotes
else if ($state==GETVALUEDELIMITEDBYQUOTES_ESCAPED) {
$state = GETVALUEDELIMITEDBYQUOTES;
$entryvalue=$entryvalue.$s;
}
} // end for
} // end while
$delegate->endFile();
//$d = $this->delegate;print_r($d);
} // end function
} // end class

19
src/StringEntry.php

@ -0,0 +1,19 @@
<?PHP
namespace Monperrus\BibtexBrowser;
/** represents @string{k=v} */
class StringEntry
{
public function __construct($key, $value, $filename)
{
$this->name=$key;
$this->value=$value;
$this->filename=$filename;
}
public function toString()
{
return '@string{'.$this->name.'={'.$this->value.'}}';
}
} // end class StringEntry

40
src/XMLPrettyPrinter.php

@ -0,0 +1,40 @@
<?PHP
namespace Monperrus\BibtexBrowser;
/** is a possible delegate for StateBasedBibParser.
usage:
see snippet of [[#StateBasedBibParser]]
*/
class XMLPrettyPrinter extends ParserDelegate
{
public function beginFile() {
header('Content-type: text/xml;');
print '<?xml version="1.0" encoding="'.OUTPUT_ENCODING.'"?>';
print '<bibfile>';
}
public function endFile() {
print '</bibfile>';
}
public function setEntryField($finalkey,$entryvalue) {
print "<data>\n<key>".$finalkey."</key>\n<value>".$entryvalue."</value>\n</data>\n";
}
public function setEntryType($entrytype) {
print '<type>'.$entrytype.'</type>';
}
public function setEntryKey($entrykey) {
print '<keyonly>'.$entrykey.'</keyonly>';
}
public function beginEntry() {
print "<entry>\n";
}
public function endEntry($entrysource) {
print "</entry>\n";
}
} // end class XMLPrettyPrinter

231
src/bibacid-utf8.bib

@ -0,0 +1,231 @@
@string{foo="Foo"}
@string{bar=foo#" Bar"}
%% test the home page feature
@string{hp_J.Abounader="http://www.google.com"}
@inproceedings{classical,
author = {J. Abounader and D. Tooj},
title = bar,
institution = {Queen's University, Kingston, ON.},
year = {1997}
}
%% testing the abstract
@inproceedings{with_abstract,
author = {J. Abounader and D. Tooj},
title = {This is with abstract},
institution = {Queen's University, Kingston, ON.},
year = {1997},
abstract = {This is an abstract
on several lines}
}
%% using double quotes
%% also Took test the advanced search
@TECHREPORT{dquotes,
author = "J. Abounader and Tooj, D.",
title = "using double quotes ",
institution = "Queen's University, Kingston, ON.",
year = "1997"
}
%% using single value (year)
@TECHREPORT{singlevalue,
author = {The },
title = {The year},
institution = {Queen's University, Kingston, ON.},
year = 1997
}
%% no new line
@TECHREPORT{Abounader1997e, author = {J. Abounader and D. Lamb}, title = "using double quotes", institution = {Queen's University, Kingston, ON.}, year = 1997 }
%% intersting: bibtex does not allow \} in brackets enclosed entries and \" in quotes enclosed entries
%% escaped quotes
@TECHREPORT{notallowed1,
author = {J. Abounader and D. Lamb},
title = "escaped \"quotes\"",
institution = {Queen's University, Kingston, ON.},
year = {1997}
}
%% escaped braces
@TECHREPORT{notallowed2,
author = {J. Abounader \} and D. Lamb},
title = "escaped braces",
institution = {Queen's University, Kingston, ON.},
year = {1997}
}
@article{testsearch1,
author = {Jacob G{\"o}del},
title = {using Banach Space},
year = {1997}
}
@article{testsearch2,
author = {Jacob {G}{\"{o}}del},
title = {using banach space},
year = {1997}
}
@article{testsearch3,
author = {Jacob {G\"{o}del}},
title = {using {Banach} Space},
year = {1997}
}
@article{testsearch4,
author = {Jacob Gödel},
title = {using {B}anach Space},
year = {1997}
}
@article{testsearch5,
author = {Jacob Gödel},
title = {using {Banach Space}},
year = {1997}
}
%% bug from Jakob Kellner
%% copied from ams.org
@article {MR1841330,
AUTHOR = {Lejay, Antoine},
TITLE = {Homogenization of divergence-form operators with lower-order
terms in random media},
JOURNAL = {Probab. Theory Related Fields},
FJOURNAL = {Probability Theory and Related Fields},
VOLUME = {120},
YEAR = {2001},
NUMBER = {2},
PAGES = {255--276},
ISSN = {0178-8051},
CODEN = {PTRFEU},
MRCLASS = {35B27 (31C25 35R60 60H30 60J60)},
MRNUMBER = {MR1841330 (2002g:35023)},
MRREVIEWER = {Jean-Fran{\c{c}}ois Clouet},
DOI = {10.1007/PL00008783},
URL = {http://dx.doi.org/10.1007/PL00008783},
}
@article{serge,
author = "Serge Bug",
title = "curly {braces} in quote",
year = 2010
}
@article{cediltest,
author = "Serge Bug",
title = "\ccurly \c{c}urly {\c{c}}urly ",
year = 2010
}
%%%%% now testing the string concatenation
@string{str1="toto"}
@string{str2="titi"}
@book{stgringconcat:test1,
author = {J. Abounader and D. Lamb},
title = str1#str2,
institution = {Queen's University, Kingston, ON.},
year = {1997}
}
% with space
@bookchapter{stgringconcat:test2,
author = {J. Abounader and D. Lamb},
title = str1 # str2,
institution = {Queen's University, Kingston, ON.},
year = {1997}
}
% with another value
@inbook{stgringconcat:test3,
author = {J. Abounader and D. Lamb},
title = str1 # {3344},
institution = {Queen's University, Kingston, ON.},
year = {1997}
}
%% Saturday, October 09 2010
%% taken from http://en.wikipedia.org/wiki/BibTeX
@PROCEEDINGS {conference:06,
editor = {First Editor and Second Editor},
title = {Proceedings of the Xth Conference on XYZ},
booktitle = {Proceedings of the Xth Conference on XYZ},
year = {2006},
month = oct,
}
@INPROCEEDINGS {author:06,
title = {Some publication title},
author = {First Author and Second Author},
crossref = {conference:06},
pages = {330?331},
}
%% Thursday, October 28 2010
%% two test cases for special key
@INPROCEEDINGS {author+06,
title = {Some publication title},
author = {First Author and Second Author},
year = {2011},
}
@INPROCEEDINGS { author06 ,
title = {Some publication title},
author = {First Author and Second Author},
year = {2011},
}
%% testing the accented i and j
@INPROCEEDINGS {paper000,
title = {Some publication title},
author = {F\`\irst A\`{\i}thor and S{\`\i}cond Author\`\I},
year = {2011},
}
%% bug reported by Mark Hereld
@misc{mark,
title = {Bug in Urls},
author = {Mark Hereld},
howpublished = {\url{http://foo.com/under_score.html}},
year = {2011},
}
%% handling of percentage
@misc{mark,
title = {Percentage \%},
abstract = {Percentage \%},
author = {MM},
year = {2015},
}
% bug https://github.com/monperrus/bibtexbrowser/issues/40
@Article{Baldwin2014Quantum,
Doi = {10.1103/PhysRevA.90.012110},
Url = {http://link.aps.org/doi/10.1103/PhysRevA.90.012110}
}
@article{croatiantest,
author = "Strabi{\'{c}} Strabi\'{c}",
title = "Fancy accents bug",
year = 2015
}
@article{kjsdf 8,
title = "Entry with + in the key",
author = "Foo Bar",
year = 2017
}

494
bibtexbrowser.php → src/bibtexbrowser.php

@ -15,6 +15,12 @@ License, or (at your option) any later version.
*/
use Monperrus\BibtexBrowser\StateBasedBibtexParser;
use Monperrus\BibtexBrowser\ParserDelegate;
use Monperrus\BibtexBrowser\XMLPrettyPrinter;
use Monperrus\BibtexBrowser\StringEntry;
use Monperrus\BibtexBrowser\BibDBBuilder;
// it is be possible to include( 'bibtexbrowser.php' ); several times in the same script
// added on Wednesday, June 01 2011, bug found by Carlos Bras
if (!defined('BIBTEXBROWSER')) {
@ -226,7 +232,8 @@ if (defined('ENCODING')) {
// this is usually resolved to bibtexbrowser.php
// but can be overridden in bibtexbrowser.local.php
// for instance with @define('BIBTEXBROWSER_URL',''); // links to the current page with ?
@define('BIBTEXBROWSER_URL',basename(__FILE__));
//@define('BIBTEXBROWSER_URL',basename(__FILE__));
@define('BIBTEXBROWSER_URL','');
// *************** END CONFIGURATION
@ -243,6 +250,8 @@ define('Q_INNER_TYPE', 'x-bibtex-type');// used for representing the type of the
function nothing() {}
function config_value($key) {
global $CONFIGURATION;
if (isset($CONFIGURATION[$key])) { return $CONFIGURATION[$key]; }
@ -325,7 +334,7 @@ function _zetDB($bibtex_filenames) {
// to automate dectection of faulty links with tools such as webcheck
header('HTTP/1.1 404 Not found');
// escape $bib to prevent XSS
$escapedBib = htmlEntities($bib, ENT_QUOTES);
htmlEntities($bib, ENT_QUOTES);
die('<b>the bib file '.$escapedBib.' does not exist !</b>');
}
} // end for each
@ -467,7 +476,6 @@ if (!function_exists('createMenuManager')) {
function createMenuManager() { $x = new MenuManager(); return $x;}
}
////////////////////////////////////////////////////////
/** is a generic parser of bibtex files.
@ -481,472 +489,11 @@ notes:
- It has no dependencies, it can be used outside of bibtexbrowser
- The delegate is expected to have some methods, see classes BibDBBuilder and XMLPrettyPrinter
*/
class StateBasedBibtexParser {
var $delegate;
function __construct($delegate) {
$this->delegate = $delegate;
}
function parse($handle) {
if (gettype($handle) == 'string') { throw new Exception('oops'); }
$delegate = $this->delegate;
// STATE DEFINITIONS
@define('NOTHING',1);
@define('GETTYPE',2);
@define('GETKEY',3);
@define('GETVALUE',4);
@define('GETVALUEDELIMITEDBYQUOTES',5);
@define('GETVALUEDELIMITEDBYQUOTES_ESCAPED',6);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS',7);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_ESCAPED',8);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL',9);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL_ESCAPED',10);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL',11);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL_ESCAPED',12);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL',13);
@define('GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL_ESCAPED',14);
$state=NOTHING;
$entrytype='';
$entrykey='';
$entryvalue='';
$fieldvaluepart='';
$finalkey='';
$entrysource='';
// metastate
$isinentry = false;
$delegate->beginFile();
// if you encounter this error "Allowed memory size of xxxxx bytes exhausted"
// then decrease the size of the temp buffer below
$bufsize=BUFFERSIZE;
while (!feof($handle)) {
$sread=fread($handle,$bufsize);
//foreach(str_split($sread) as $s) {
for ( $i=0; $i < strlen( $sread ); $i++) { $s=$sread[$i];
if ($isinentry) $entrysource.=$s;
if ($state==NOTHING) {
// this is the beginning of an entry
if ($s=='@') {
$delegate->beginEntry();
$state = GETTYPE;
$isinentry = true;
$entrysource='@';
}
}
else if ($state==GETTYPE) {
// this is the beginning of a key
if ($s=='{') {
$state = GETKEY;
$delegate->setEntryType($entrytype);
$entrytype='';
}
else $entrytype=$entrytype.$s;
}
else if ($state==GETKEY) {
// now we get the value
if ($s=='=') {
$state = GETVALUE;
$fieldvaluepart='';
$finalkey=$entrykey;
$entrykey='';
}
// oups we only have the key :-) anyway
else if ($s=='}') {
$state = NOTHING;$isinentry = false;$delegate->endEntry($entrysource);
$entrykey='';
}
// OK now we look for values
else if ($s==',') {
$state=GETKEY;
$delegate->setEntryKey($entrykey);
$entrykey='';}
else { $entrykey=$entrykey.$s; }
}
// we just got a =, we can now receive the value, but we don't now whether the value
// is delimited by curly brackets, double quotes or nothing
else if ($state==GETVALUE) {
// the value is delimited by double quotes
if ($s=='"') {
$state = GETVALUEDELIMITEDBYQUOTES;
}
// the value is delimited by curly brackets
else if ($s=='{') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS;
}
// the end of the key and no value found: it is the bibtex key e.g. \cite{Descartes1637}
else if ($s==',') {
$state = GETKEY;
$delegate->setEntryField($finalkey,$entryvalue);
$entryvalue=''; // resetting the value buffer
}
// this is the end of the value AND of the entry
else if ($s=='}') {
$state = NOTHING;
$delegate->setEntryField($finalkey,$entryvalue);
$isinentry = false;$delegate->endEntry($entrysource);
$entryvalue=''; // resetting the value buffer
}
else if ($s==' ' || $s=="\t" || $s=="\n" || $s=="\r" ) {
// blank characters are not taken into account when values are not in quotes or curly brackets
}
else {
$entryvalue=$entryvalue.$s;
}
}
/* GETVALUEDELIMITEDBYCURLYBRACKETS* handle entries delimited by curly brackets and the possible nested curly brackets */
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS) {
if ($s=='\\') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_ESCAPED;
$entryvalue=$entryvalue.$s;}
else if ($s=='{') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL;
$entryvalue=$entryvalue.$s;
$delegate->entryValuePart($finalkey,$fieldvaluepart,'CURLYTOP');
$fieldvaluepart='';
}
else if ($s=='}') { // end entry
$state = GETVALUE;
$delegate->entryValuePart($finalkey,$fieldvaluepart,'CURLYTOP');
}
else {
$entryvalue=$entryvalue.$s;
$fieldvaluepart=$fieldvaluepart.$s;
}
}
// handle anti-slashed brackets
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_ESCAPED) {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS;
$entryvalue=$entryvalue.$s;
}
// in first level of curly bracket
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL) {
if ($s=='\\') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL_ESCAPED;
$entryvalue=$entryvalue.$s;}
else if ($s=='{') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL;$entryvalue=$entryvalue.$s;}
else if ($s=='}') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS;
$delegate->entryValuePart($finalkey,$fieldvaluepart,'CURLYONE');
$fieldvaluepart='';
$entryvalue=$entryvalue.$s;
}
else {
$entryvalue=$entryvalue.$s;
$fieldvaluepart=$fieldvaluepart.$s;
}
}
// handle anti-slashed brackets
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL_ESCAPED) {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL;
$entryvalue=$entryvalue.$s;
}
// in second level of curly bracket
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL) {
if ($s=='\\') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL_ESCAPED;
$entryvalue=$entryvalue.$s;}
else if ($s=='{') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL;$entryvalue=$entryvalue.$s;}
else if ($s=='}') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL;$entryvalue=$entryvalue.$s;}
else { $entryvalue=$entryvalue.$s;}
}
// handle anti-slashed brackets
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL_ESCAPED) {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL;
$entryvalue=$entryvalue.$s;
}
// in third level of curly bracket
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL) {
if ($s=='\\') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL_ESCAPED;
$entryvalue=$entryvalue.$s;}
else if ($s=='}') {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL;$entryvalue=$entryvalue.$s;}
else { $entryvalue=$entryvalue.$s;}
}
// handle anti-slashed brackets
else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL_ESCAPED) {
$state = GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL;
$entryvalue=$entryvalue.$s;
}
/* handles entries delimited by double quotes */
else if ($state==GETVALUEDELIMITEDBYQUOTES) {
if ($s=='\\') {
$state = GETVALUEDELIMITEDBYQUOTES_ESCAPED;
$entryvalue=$entryvalue.$s;}
else if ($s=='"') {
$state = GETVALUE;
}
else { $entryvalue=$entryvalue.$s;}
}
// handle anti-double quotes
else if ($state==GETVALUEDELIMITEDBYQUOTES_ESCAPED) {
$state = GETVALUEDELIMITEDBYQUOTES;
$entryvalue=$entryvalue.$s;
}
} // end for
} // end while
$delegate->endFile();
//$d = $this->delegate;print_r($d);
} // end function
} // end class
/** a default empty implementation of a delegate for StateBasedBibtexParser */
class ParserDelegate {
function beginFile() {}
function endFile() {}
function setEntryField($finalkey,$entryvalue) {}
function setEntryType($entrytype) {}
function setEntryKey($entrykey) {}
function beginEntry() {}
function endEntry($entrysource) {}
/** called for each sub parts of type {part} of a field value
* for now, only CURLYTOP and CURLYONE events
*/
function entryValuePart($key, $value, $type) {}
} // end class ParserDelegate
/** is a possible delegate for StateBasedBibParser.
usage:
see snippet of [[#StateBasedBibParser]]
*/
class XMLPrettyPrinter extends ParserDelegate {
function beginFile() {
header('Content-type: text/xml;');
print '<?xml version="1.0" encoding="'.OUTPUT_ENCODING.'"?>';
print '<bibfile>';
}
function endFile() {
print '</bibfile>';
}
function setEntryField($finalkey,$entryvalue) {
print "<data>\n<key>".$finalkey."</key>\n<value>".$entryvalue."</value>\n</data>\n";
}
function setEntryType($entrytype) {
print '<type>'.$entrytype.'</type>';
}
function setEntryKey($entrykey) {
print '<keyonly>'.$entrykey.'</keyonly>';
}
function beginEntry() {
print "<entry>\n";
}
function endEntry($entrysource) {
print "</entry>\n";
}
} // end class XMLPrettyPrinter
/** represents @string{k=v} */
class StringEntry {
function __construct($k, $v, $filename) {
$this->name=$k;
$this->value=$v;
$this->filename=$filename;
}
function toString() {
return '@string{'.$this->name.'={'.$this->value.'}}';
}
} // end class StringEntry
/** builds arrays of BibEntry objects from a bibtex file.
usage:
<pre>
$empty_array = array();
$db = new BibDBBuilder(); // see also factory method createBibDBBuilder
$db->build('bibacid-utf8.bib'); // parses bib file
print_r($db->builtdb);// an associated array key -> BibEntry objects
print_r($db->stringdb);// an associated array key -> strings representing @string
</pre>
notes:
method build can be used several times, bibtex entries are accumulated in the builder
*/
class BibDBBuilder extends ParserDelegate {
/** A hashtable from keys to bib entries (BibEntry). */
var $builtdb = array();
/** A hashtable of constant strings */
var $stringdb = array();
var $filename;
var $currentEntry;
function build($bibfilename, $handle = NULL) {
$this->filename = $bibfilename;
if ($handle == NULL) {
$handle = fopen($bibfilename, "r");
}
if (!$handle) die ('cannot open '.$bibfilename);
$parser = new StateBasedBibtexParser($this);
$parser->parse($handle);
fclose($handle);
//print_r(array_keys($this->builtdb));
//print_r($this->builtdb);
}
function getBuiltDb() {
//print_r($this->builtdb);
return $this->builtdb;
}
function beginFile() {
}
function endFile() {
// resolving crossrefs
// we are careful with PHP 4 semantics
foreach (array_keys($this->builtdb) as $key) {
$bib = $this->builtdb[$key];
if ($bib->hasField('crossref')) {
if (isset($this->builtdb[$bib->getField('crossref')])) {
$crossrefEntry = $this->builtdb[$bib->getField('crossref')];
$bib->crossref = $crossrefEntry;
foreach($crossrefEntry->getFields() as $k => $v) {
// copying the fields of the cross ref
// only if they don't exist yet
if (!$bib->hasField($k)) {
$bib->setField($k,$v);
}
}
}
}
}
//print_r($this->builtdb);
}
function setEntryField($fieldkey,$entryvalue) {
$fieldkey=trim($fieldkey);
// support for Bibtex concatenation
// see http://newton.ex.ac.uk/tex/pack/bibtex/btxdoc/node3.html
// (?<! is a negative look-behind assertion, see http://www.php.net/manual/en/regexp.reference.assertions.php
$entryvalue_array=preg_split('/(?<!\\\\)#/', $entryvalue);
foreach ($entryvalue_array as $k=>$v) {
// spaces are allowed when using # and they are not taken into account
// however # is not itself replaced by a space
// warning: @strings are not case sensitive
// see http://newton.ex.ac.uk/tex/pack/bibtex/btxdoc/node3.html
$stringKey=strtolower(trim($v));
if (isset($this->stringdb[$stringKey]))
{
// this field will be formated later by xtrim and latex2html
$entryvalue_array[$k]=$this->stringdb[$stringKey]->value;
// we keep a trace of this replacement
// so as to produce correct bibtex snippets
$this->currentEntry->constants[$stringKey]=$this->stringdb[$stringKey]->value;
}
}
$entryvalue=implode('',$entryvalue_array);
$this->currentEntry->setField($fieldkey,$entryvalue);
}
function setEntryType($entrytype) {
$this->currentEntry->setType($entrytype);
}
function setEntryKey($entrykey) {
//echo "new entry:".$entrykey."\n";
$this->currentEntry->setKey($entrykey);
}
function beginEntry() {
$this->currentEntry = createBibEntry();
$this->currentEntry->setFile($this->filename);
}
function endEntry($entrysource) {
// we add a timestamp
$this->currentEntry->timestamp();
// we add a key if there is no key
if (!$this->currentEntry->hasField(Q_KEY) && $this->currentEntry->getType()!='string') {
$this->currentEntry->setField(Q_KEY,md5($entrysource));
}
// we set the fulltext
$this->currentEntry->text = $entrysource;
// we format the author names in a special field
// to enable search
if ($this->currentEntry->hasField('author')) {
$this->currentEntry->setField(Q_INNER_AUTHOR,$this->currentEntry->getFormattedAuthorsString());
foreach($this->currentEntry->getCanonicalAuthors() as $author) {
$homepage_key = $this->currentEntry->getHomePageKey($author);
if (isset($this->stringdb[$homepage_key])) {
$this->currentEntry->homepages[$homepage_key] = $this->stringdb[$homepage_key]->value;
}
}
}
// ignoring jabref comments
if (($this->currentEntry->getType()=='comment')) {
/* do nothing for jabref comments */
}
// we add it to the string database
else if ($this->currentEntry->getType()=='string') {
foreach($this->currentEntry->fields as $k => $v) {
$k!=Q_INNER_TYPE and $this->stringdb[$k] = new StringEntry($k,$v,$this->filename);
}
}
// we add it to the database
else {
$this->builtdb[$this->currentEntry->getKey()] = $this->currentEntry;
}
}
} // end class BibDBBuilder
@ -1228,7 +775,7 @@ class BibEntry {
// we assume that "comment" is never latex code
// but instead could contain HTML code (with links using the character "~" for example)
// so "comment" is not transformed too
if ($name!='url' && $name!='comment'
if ($name!='url' && $name!='comment'
&& !preg_match('/^hp_/',$name) // homepage links should not be transformed with latex2html
) {
$value = $this->transformValue($value);
@ -4089,7 +3636,6 @@ dd {
<?php
} // end function bibtexbrowserDefaultCSS
/** encapsulates the content of a delegate into full-fledged HTML (&lt;HTML>&lt;BODY> and TITLE)
usage:
<pre>
@ -4103,6 +3649,7 @@ usage:
getTitle()
* $title: title of the page
*/
function HTMLTemplate($content) {
// when we load a page with AJAX
@ -4139,20 +3686,20 @@ if (method_exists($content, 'getTitle')) {
// now the CSS
echo '<style type="text/css"><!-- '."\n";
echo bibtexbrowserDefaultCSS();
if (method_exists($content, 'getCSS')) {
echo $content->getCSS();
// echo $content->getCSS();
} else if (is_readable(dirname(__FILE__).'/bibtexbrowser.css')) {
readfile(dirname(__FILE__).'/bibtexbrowser.css');
}
else { bibtexbrowserDefaultCSS(); }
echo "\n".' --></style>';
?>
</head>
<body>
<?php
<?php
// configuration point to add a banner
echo bibtexbrowser_top_banner();
?>
@ -4529,7 +4076,7 @@ class Dispatcher {
}
// should call method display() on $x
$fun = $this->wrapper;
$fun = BIBTEXBROWSER_DEFAULT_TEMPLATE;//$this->wrapper;
$fun($x);
$this->clearQuery();
@ -4539,7 +4086,7 @@ class Dispatcher {
// if some contents have already been sent, for instance if we are included
// this means doing nothing
if ( headers_sent() == false ) { /* to avoid sending an unnecessary frameset */
header("Location: ".$_SERVER['SCRIPT_NAME']."?frameset&bib=".$_GET[Q_FILE]);
header("Location: ".$_SERVER['SCRIPT_NAME']."?frameset&bib=".$_GET[Q_FILE]);
}
}
}
@ -4666,7 +4213,8 @@ class Dispatcher {
function menu() {
$menu = createMenuManager();
$menu->setDB($this->getDB());
$fun = $this->wrapper;
// why does //$this->wrapper; = no wrapper?
$fun = BIBTEXBROWSER_DEFAULT_TEMPLATE;
$fun($menu);
return 'END_DISPATCH';
}
@ -4757,7 +4305,7 @@ class Dispatcher {
</html>
<?php
return 'END_DISPATCH';
return 'END_DISPATCH';
}
} // end class Dispatcher

4
bibtexbrowser-test.php → tests/bibtexbrowser-test.php

@ -137,7 +137,7 @@ class BTBTest extends PHPUnit_Framework_TestCase {
// listing the CSS classes
$css_classes_before = $this->extract_css_classes($first_entry->toHTML());
// IEEE style
bibtexbrowser_configure('BIBLIOGRAPHYSTYLE','JanosBibliographyStyle');
$this->assertEquals("Foo Bar and Jane Doe, \"An Article\", In New Results, vol. 5, pp. 1-2, 2009.\n ",strip_tags($first_entry->toHTML()));
@ -646,7 +646,7 @@ class BTBTest extends PHPUnit_Framework_TestCase {
ob_get_clean();
$this->assertEquals("[2]", $btb->getEntryByKey('aKey')->getAbbrv());
$this->assertEquals("[1]", $btb->getEntryByKey('aKey-withSlash')->getAbbrv());
}
function test_identity() {

0
bibtexbrowser-documentation.wiki → wiki/bibtexbrowser-documentation.wiki

0
bibtexbrowser-users.wiki → wiki/bibtexbrowser-users.wiki

Loading…
Cancel
Save