* \author Herbert Voß
* \author Richard Heck
* \author Julien Rioux
+ * \author Jürgen Spitzmüller
*
* Full author contact details are available in file CREDITS.
*/
#include "Buffer.h"
#include "BufferParams.h"
#include "buffer_funcs.h"
+#include "Citation.h"
#include "Encoding.h"
#include "InsetIterator.h"
#include "Language.h"
namespace {
-// gets the "family name" from an author-type string
-docstring familyName(docstring const & name)
+// gets the "prename" and "family name" from an author-type string
+pair<docstring, docstring> nameParts(docstring const & name)
{
if (name.empty())
- return docstring();
+ return make_pair(docstring(), docstring());
// first we look for a comma, and take the last name to be everything
// preceding the right-most one, so that we also get the "jr" part.
- docstring::size_type idx = name.rfind(',');
- if (idx != docstring::npos)
- return ltrim(name.substr(0, idx));
+ vector<docstring> pieces = getVectorFromString(name);
+ if (pieces.size() > 1)
+ // whether we have a jr. part or not, it's always
+ // the first and last item (reversed)
+ return make_pair(pieces.back(), pieces.front());
// OK, so now we want to look for the last name. We're going to
// include the "von" part. This isn't perfect.
// Split on spaces, to get various tokens.
- vector<docstring> pieces = getVectorFromString(name, from_ascii(" "));
+ pieces = getVectorFromString(name, from_ascii(" "));
// If we only get two, assume the last one is the last name
if (pieces.size() <= 2)
- return pieces.back();
+ return make_pair(pieces.front(), pieces.back());
- // Now we look for the first token that begins with a lower case letter.
+ // Now we look for the first token that begins with
+ // a lower case letter or an opening group {.
+ docstring prename;
vector<docstring>::const_iterator it = pieces.begin();
vector<docstring>::const_iterator en = pieces.end();
+ bool first = true;
for (; it != en; ++it) {
if ((*it).empty())
continue;
char_type const c = (*it)[0];
- if (isLower(c))
+ if (isLower(c) || c == '{')
break;
+ if (!first)
+ prename += " ";
+ else
+ first = false;
+ prename += *it;
}
- if (it == en) // we never found a "von"
- return pieces.back();
+ if (it == en) // we never found a "von" or group
+ return make_pair(prename, pieces.back());
- // reconstruct what we need to return
- docstring retval;
- bool first = true;
+ // reconstruct the family name
+ docstring surname;
+ first = true;
for (; it != en; ++it) {
if (!first)
- retval += " ";
+ surname += " ";
else
first = false;
- retval += *it;
+ surname += *it;
}
- return retval;
+ return make_pair(prename, surname);
+}
+
+
+docstring constructName(docstring const & name, string const scheme)
+{
+ // re-constructs a name from name parts according
+ // to a given scheme
+ docstring const prename = nameParts(name).first;
+ docstring const surname = nameParts(name).second;
+ docstring result = from_ascii(scheme);
+ result = subst(result, from_ascii("%prename%"), prename);
+ result = subst(result, from_ascii("%surname%"), surname);
+ return result;
+}
+
+
+bool multipleAuthors(docstring const author)
+{
+ vector<docstring> const authors =
+ getVectorFromString(author, from_ascii(" and "));
+ return authors.size() > 1;
}
{}
-docstring const BibTeXInfo::getAbbreviatedAuthor(
- Buffer const * buf, bool jurabib_style) const
+docstring const BibTeXInfo::getAuthorOrEditorList(Buffer const * buf,
+ bool full, bool forceshort) const
+{
+ docstring author = operator[]("author");
+ if (author.empty())
+ author = operator[]("editor");
+
+ return getAuthorList(buf, author, full, forceshort);
+}
+
+
+docstring const BibTeXInfo::getAuthorList(Buffer const * buf, docstring author,
+ bool full, bool forceshort, bool allnames,
+ bool beginning) const
{
+ // Maxnames treshold depend on engine
+ size_t maxnames = buf ?
+ buf->params().documentClass().max_citenames() : 2;
+
if (!is_bibtex_) {
docstring const opt = label();
if (opt.empty())
return authors;
}
- docstring author = operator[]("author");
- if (author.empty()) {
- author = operator[]("editor");
- if (author.empty())
- return author;
- }
+ if (author.empty())
+ return author;
// FIXME Move this to a separate routine that can
// be called from elsewhere.
vector<docstring> const authors =
getVectorFromString(author, from_ascii(" and "));
- if (jurabib_style && (authors.size() == 2 || authors.size() == 3)) {
- docstring shortauthor = familyName(authors[0])
- + "/" + familyName(authors[1]);
- if (authors.size() == 3)
- shortauthor += "/" + familyName(authors[2]);
- return convertLaTeXCommands(shortauthor);
- }
+ docstring retval;
- docstring retval = familyName(authors[0]);
-
- if (authors.size() == 2 && authors[1] != "others") {
- docstring const dformat = buf ?
- buf->B_("%1$s and %2$s") : from_ascii("%1$s and %2$s");
- retval = bformat(dformat, familyName(authors[0]), familyName(authors[1]));
- } else if (authors.size() >= 2) {
- // we get here either if the author list is longer than two names
- // or if the second 'name' is "others". we do the same thing either
- // way.
- docstring const dformat = buf ?
- buf->B_("%1$s et al.") : from_ascii("%1$s et al.");
- retval = bformat(dformat, familyName(authors[0]));
+ CiteEngineType const engine_type = buf ? buf->params().citeEngineType()
+ : ENGINE_TYPE_DEFAULT;
+
+ // These are defined in the styles
+ string const etal =
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "_etal")
+ : " et al.";
+ string const namesep =
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "_namesep")
+ : ", ";
+ string const lastnamesep =
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "_lastnamesep")
+ : ", and ";
+ string const pairnamesep =
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "_pairnamesep")
+ : " and ";
+ string firstnameform =
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "!firstnameform")
+ : "%surname%, %prename%";
+ if (!beginning)
+ firstnameform = buf ? buf->params().documentClass().getCiteMacro(engine_type, "!firstbynameform")
+ : "%prename% %surname%";
+ string othernameform = buf ? buf->params().documentClass().getCiteMacro(engine_type, "!othernameform")
+ : "%surname%, %prename%";
+ if (!beginning)
+ othernameform = buf ? buf->params().documentClass().getCiteMacro(engine_type, "!otherbynameform")
+ : "%prename% %surname%";
+
+ // Shorten the list (with et al.) if forceshort is set
+ // and the list can actually be shorten, else if maxcitenames
+ // is passed and full is not set.
+ bool shorten = forceshort && authors.size() > 1;
+ vector<docstring>::const_iterator it = authors.begin();
+ vector<docstring>::const_iterator en = authors.end();
+ for (size_t i = 0; it != en; ++it, ++i) {
+ if (i >= maxnames && !full) {
+ shorten = true;
+ break;
+ }
+ if (*it == "others") {
+ retval += buf ? buf->B_(etal) : from_ascii(etal);
+ break;
+ }
+ if (i > 0 && i == authors.size() - 1) {
+ if (authors.size() == 2)
+ retval += buf ? buf->B_(pairnamesep) : from_ascii(pairnamesep);
+ else
+ retval += buf ? buf->B_(lastnamesep) : from_ascii(lastnamesep);
+ } else if (i > 0)
+ retval += buf ? buf->B_(namesep) : from_ascii(namesep);
+ if (allnames)
+ retval += (i == 0) ? constructName(*it, firstnameform)
+ : constructName(*it, othernameform);
+ else
+ retval += nameParts(*it).second;
+ }
+ if (shorten) {
+ if (allnames)
+ retval = constructName(authors[0], firstnameform) + (buf ? buf->B_(etal) : from_ascii(etal));
+ else
+ retval = nameParts(authors[0]).second + (buf ? buf->B_(etal) : from_ascii(etal));
}
return convertLaTeXCommands(retval);
*/
docstring BibTeXInfo::expandFormat(docstring const & format,
BibTeXInfoList const xrefs, int & counter, Buffer const & buf,
- docstring before, docstring after, docstring dialog, bool next) const
+ CiteItem const & ci, bool next, bool second) const
{
// incorrect use of macros could put us in an infinite loop
static int const max_passes = 5000;
// the use of overly large keys can lead to performance problems, due
// to eventual attempts to convert LaTeX macros to unicode. See bug
- // #8944. This is perhaps not the best solution, but it will have to
- // do for now.
- static size_t const max_keysize = 128;
+ // #8944. By default, the size is limited to 128 (in CiteItem), but
+ // for specific purposes (such as XHTML export), it needs to be enlarged
+ // This is perhaps not the best solution, but it will have to do for now.
+ size_t const max_keysize = ci.max_key_size;
odocstringstream ret; // return value
string key;
bool scanning_key = false;
ret << trans;
} else {
docstring const val =
- getValueForKey(key, buf, before, after, dialog, xrefs, max_keysize);
+ getValueForKey(key, buf, ci, xrefs, max_keysize);
if (!scanning_rich)
ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
ret << val;
return _("ERROR!");
fmt = newfmt;
docstring const val =
- getValueForKey(optkey, buf, before, after, dialog, xrefs);
+ getValueForKey(optkey, buf, ci, xrefs);
if (optkey == "next" && next)
ret << ifpart; // without expansion
- else if (!val.empty()) {
+ else if (optkey == "second" && second) {
int newcounter = 0;
ret << expandFormat(ifpart, xrefs, newcounter, buf,
- before, after, dialog, next);
+ ci, next);
+ } else if (!val.empty()) {
+ int newcounter = 0;
+ ret << expandFormat(ifpart, xrefs, newcounter, buf,
+ ci, next);
} else if (!elsepart.empty()) {
int newcounter = 0;
ret << expandFormat(elsepart, xrefs, newcounter, buf,
- before, after, dialog, next);
+ ci, next);
}
// fmt will have been shortened for us already
continue;
docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
- Buffer const & buf, bool richtext) const
+ Buffer const & buf, CiteItem const & ci) const
{
+ bool const richtext = ci.richtext;
+
if (!richtext && !info_.empty())
return info_;
if (richtext && !info_richtext_.empty())
from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
int counter = 0;
info_ = expandFormat(format, xrefs, counter, buf,
- docstring(), docstring(), docstring(), false);
+ ci, false, false);
if (info_.empty()) {
// this probably shouldn't happen
docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
- Buffer const & buf, docstring const & format, bool richtext,
- docstring const & before, docstring const & after,
- docstring const & dialog, bool next) const
+ Buffer const & buf, docstring const & format,
+ CiteItem const & ci, bool next, bool second) const
{
docstring loclabel;
int counter = 0;
- loclabel = expandFormat(format, xrefs, counter, buf,
- before, after, dialog, next);
+ loclabel = expandFormat(format, xrefs, counter, buf, ci, next, second);
if (!loclabel.empty() && !next) {
- loclabel = processRichtext(loclabel, richtext);
+ loclabel = processRichtext(loclabel, ci.richtext);
loclabel = convertLaTeXCommands(loclabel);
}
docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
- docstring const & before, docstring const & after, docstring const & dialog,
- BibTeXInfoList const xrefs, size_t maxsize) const
+ CiteItem const & ci, BibTeXInfoList const xrefs, size_t maxsize) const
{
// anything less is pointless
LASSERT(maxsize >= 16, maxsize = 16);
if (ret.empty()) {
// some special keys
// FIXME: dialog, textbefore and textafter have nothing to do with this
- if (key == "dialog")
- ret = dialog;
+ if (key == "dialog" && ci.context == CiteItem::Dialog)
+ ret = from_ascii("x"); // any non-empty string will do
+ else if (key == "export" && ci.context == CiteItem::Export)
+ ret = from_ascii("x"); // any non-empty string will do
+ else if (key == "ifstar" && ci.Starred)
+ ret = from_ascii("x"); // any non-empty string will do
else if (key == "entrytype")
ret = entry_type_;
+ else if (prefixIs(key, "ifentrytype:")
+ && from_ascii(key.substr(12)) == entry_type_)
+ ret = from_ascii("x"); // any non-empty string will do
else if (key == "key")
ret = bib_key_;
else if (key == "label")
ret = modifier_;
else if (key == "numericallabel")
ret = cite_number_;
- else if (key == "abbrvauthor")
- // Special key to provide abbreviated author names.
- ret = getAbbreviatedAuthor(&buf, false);
- else if (key == "shortauthor")
- // When shortauthor is not defined, jurabib automatically
- // provides jurabib-style abbreviated author names. We do
- // this as well.
- ret = getAbbreviatedAuthor(&buf, true);
- else if (key == "shorttitle") {
- // When shorttitle is not defined, jurabib uses for `article'
- // and `periodical' entries the form `journal volume [year]'
- // and for other types of entries it uses the `title' field.
- if (entry_type_ == "article" || entry_type_ == "periodical")
- ret = operator[]("journal") + " " + operator[]("volume")
- + " [" + operator[]("year") + "]";
- else
- ret = operator[]("title");
+ else if (prefixIs(key, "ifmultiple:")) {
+ // Return whether we have multiple authors
+ docstring const kind = operator[](from_ascii(key.substr(11)));
+ if (multipleAuthors(kind))
+ ret = from_ascii("x"); // any non-empty string will do
+ }
+ else if (prefixIs(key, "abbrvnames:")) {
+ // Special key to provide abbreviated name list,
+ // with respect to maxcitenames. Suitable for Bibliography
+ // beginnings.
+ docstring const kind = operator[](from_ascii(key.substr(11)));
+ ret = getAuthorList(&buf, kind, false, false, true);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (prefixIs(key, "fullnames:")) {
+ // Return a full name list. Suitable for Bibliography
+ // beginnings.
+ docstring const kind = operator[](from_ascii(key.substr(10)));
+ ret = getAuthorList(&buf, kind, true, false, true);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (prefixIs(key, "forceabbrvnames:")) {
+ // Special key to provide abbreviated name lists,
+ // irrespective of maxcitenames. Suitable for Bibliography
+ // beginnings.
+ docstring const kind = operator[](from_ascii(key.substr(15)));
+ ret = getAuthorList(&buf, kind, false, true, true);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (prefixIs(key, "abbrvbynames:")) {
+ // Special key to provide abbreviated name list,
+ // with respect to maxcitenames. Suitable for further names inside a
+ // bibliography item // (such as "ed. by ...")
+ docstring const kind = operator[](from_ascii(key.substr(11)));
+ ret = getAuthorList(&buf, kind, false, false, true, false);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (prefixIs(key, "fullbynames:")) {
+ // Return a full name list. Suitable for further names inside a
+ // bibliography item // (such as "ed. by ...")
+ docstring const kind = operator[](from_ascii(key.substr(10)));
+ ret = getAuthorList(&buf, kind, true, false, true, false);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (prefixIs(key, "forceabbrvbynames:")) {
+ // Special key to provide abbreviated name lists,
+ // irrespective of maxcitenames. Suitable for further names inside a
+ // bibliography item // (such as "ed. by ...")
+ docstring const kind = operator[](from_ascii(key.substr(15)));
+ ret = getAuthorList(&buf, kind, false, true, true, false);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (key == "abbrvciteauthor") {
+ // Special key to provide abbreviated author or
+ // editor names (suitable for citation labels),
+ // with respect to maxcitenames.
+ ret = getAuthorOrEditorList(&buf, false, false);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (key == "fullciteauthor") {
+ // Return a full author or editor list (for citation labels)
+ ret = getAuthorOrEditorList(&buf, true, false);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
+ } else if (key == "forceabbrvciteauthor") {
+ // Special key to provide abbreviated author or
+ // editor names (suitable for citation labels),
+ // irrespective of maxcitenames.
+ ret = getAuthorOrEditorList(&buf, false, true);
+ if (ci.forceUpperCase && isLowerCase(ret[0]))
+ ret[0] = uppercase(ret[0]);
} else if (key == "bibentry") {
// Special key to provide the full bibliography entry: see getInfo()
CiteEngineType const engine_type = buf.params().citeEngineType();
DocumentClass const & dc = buf.params().documentClass();
docstring const & format =
- from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
+ from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_), false));
int counter = 0;
- ret = expandFormat(format, xrefs, counter, buf,
- docstring(), docstring(), docstring(), false);
+ ret = expandFormat(format, xrefs, counter, buf, ci, false, false);
} else if (key == "textbefore")
- ret = before;
+ ret = ci.textBefore;
else if (key == "textafter")
- ret = after;
+ ret = ci.textAfter;
else if (key == "year")
ret = getYear();
}
}
-docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key, Buffer const & buf) const
+docstring const BiblioInfo::getAuthorOrEditorList(docstring const & key, Buffer const & buf) const
{
BiblioInfo::const_iterator it = find(key);
if (it == end())
return docstring();
BibTeXInfo const & data = it->second;
- return data.getAbbreviatedAuthor(&buf, false);
+ return data.getAuthorOrEditorList(&buf, false);
}
docstring const BiblioInfo::getInfo(docstring const & key,
- Buffer const & buf, bool richtext) const
+ Buffer const & buf, CiteItem const & ci) const
{
BiblioInfo::const_iterator it = find(key);
if (it == end())
xrefptrs.push_back(&(xrefit->second));
}
}
- return data.getInfo(xrefptrs, buf, richtext);
+ return data.getInfo(xrefptrs, buf, ci);
}
docstring const BiblioInfo::getLabel(vector<docstring> keys,
- Buffer const & buf, string const & style, bool for_xhtml,
- size_t max_size, docstring const & before, docstring const & after,
- docstring const & dialog) const
+ Buffer const & buf, string const & style, CiteItem const & ci) const
{
+ size_t max_size = ci.max_size;
// shorter makes no sense
LASSERT(max_size >= 16, max_size = 16);
CiteEngineType const engine_type = buf.params().citeEngineType();
DocumentClass const & dc = buf.params().documentClass();
- docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
+ docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, false, "cite"));
docstring ret = format;
vector<docstring>::const_iterator key = keys.begin();
vector<docstring>::const_iterator ken = keys.end();
- for (; key != ken; ++key) {
+ for (int i = 0; key != ken; ++key, ++i) {
BiblioInfo::const_iterator it = find(*key);
BibTeXInfo empty_data;
empty_data.key(*key);
}
}
}
- ret = data.getLabel(xrefptrs, buf, ret, for_xhtml,
- before, after, dialog, key + 1 != ken);
+ ret = data.getLabel(xrefptrs, buf, ret, ci, key + 1 != ken, i == 1);
}
if (too_many_keys)
vector<docstring> const BiblioInfo::getCiteStrings(
vector<docstring> const & keys, vector<CitationStyle> const & styles,
- Buffer const & buf, docstring const & before,
- docstring const & after, docstring const & dialog, size_t max_size) const
+ Buffer const & buf, CiteItem const & ci) const
{
if (empty())
return vector<docstring>();
vector<docstring> vec(styles.size());
for (size_t i = 0; i != vec.size(); ++i) {
style = styles[i].name;
- vec[i] = getLabel(keys, buf, style, false, max_size, before, after, dialog);
+ vec[i] = getLabel(keys, buf, style, ci);
}
return vec;
// used in xhtml to sort a list of BibTeXInfo objects
bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
{
- docstring const lauth = lhs->getAbbreviatedAuthor();
- docstring const rauth = rhs->getAbbreviatedAuthor();
+ docstring const lauth = lhs->getAuthorOrEditorList();
+ docstring const rauth = rhs->getAuthorOrEditorList();
docstring const lyear = lhs->getYear();
docstring const ryear = rhs->getYear();
docstring const ltitl = lhs->operator[]("title");
// the first test.
// coverity[FORWARD_NULL]
if (it != cited_entries_.begin()
- && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
+ && entry.getAuthorOrEditorList() == last->second.getAuthorOrEditorList()
// we access the year via getYear() so as to get it from the xref,
// if we need to do so
&& getYear(entry.key()) == getYear(last->second.key())) {
if (numbers) {
entry.label(entry.citeNumber());
} else {
- docstring const auth = entry.getAbbreviatedAuthor(&buf, false);
+ docstring const auth = entry.getAuthorOrEditorList(&buf, false);
// we do it this way so as to access the xref, if necessary
// note that this also gives us the modifier
docstring const year = getYear(*it, buf, true);