3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Angus Leeming
11 * Full author contact details are available in file CREDITS.
16 #include "BiblioInfo.h"
18 #include "BufferParams.h"
19 #include "buffer_funcs.h"
22 #include "InsetIterator.h"
24 #include "output_xhtml.h"
25 #include "Paragraph.h"
26 #include "TextClass.h"
27 #include "TocBackend.h"
29 #include "support/convert.h"
30 #include "support/debug.h"
31 #include "support/docstream.h"
32 #include "support/gettext.h"
33 #include "support/lassert.h"
34 #include "support/lstrings.h"
35 #include "support/regex.h"
36 #include "support/textutils.h"
41 using namespace lyx::support;
48 // gets the "family name" from an author-type string
49 docstring familyName(docstring const & name)
54 // first we look for a comma, and take the last name to be everything
55 // preceding the right-most one, so that we also get the "jr" part.
56 docstring::size_type idx = name.rfind(',');
57 if (idx != docstring::npos)
58 return ltrim(name.substr(0, idx));
60 // OK, so now we want to look for the last name. We're going to
61 // include the "von" part. This isn't perfect.
62 // Split on spaces, to get various tokens.
63 vector<docstring> pieces = getVectorFromString(name, from_ascii(" "));
64 // If we only get two, assume the last one is the last name
65 if (pieces.size() <= 2)
68 // Now we look for the first token that begins with a lower case letter.
69 vector<docstring>::const_iterator it = pieces.begin();
70 vector<docstring>::const_iterator en = pieces.end();
71 for (; it != en; ++it) {
74 char_type const c = (*it)[0];
79 if (it == en) // we never found a "von"
82 // reconstruct what we need to return
85 for (; it != en; ++it) {
96 // converts a string containing LaTeX commands into unicode
98 docstring convertLaTeXCommands(docstring const & str)
103 bool scanning_cmd = false;
104 bool scanning_math = false;
105 bool escaped = false; // used to catch \$, etc.
106 while (!val.empty()) {
107 char_type const ch = val[0];
109 // if we're scanning math, we output everything until we
110 // find an unescaped $, at which point we break out.
117 scanning_math = false;
123 // if we're scanning a command name, then we just
124 // discard characters until we hit something that
127 if (isAlphaASCII(ch)) {
132 // so we're done with this command.
133 // now we fall through and check this character.
134 scanning_cmd = false;
137 // was the last character a \? If so, then this is something like:
138 // \\ or \$, so we'll just output it. That's probably not always right...
140 // exception: output \, as THIN SPACE
142 ret.push_back(0x2009);
153 scanning_math = true;
157 // we just ignore braces
158 if (ch == '{' || ch == '}') {
163 // we're going to check things that look like commands, so if
164 // this doesn't, just output it.
171 // ok, could be a command of some sort
172 // let's see if it corresponds to some unicode
173 // unicodesymbols has things in the form: \"{u},
174 // whereas we may see things like: \"u. So we'll
175 // look for that and change it, if necessary.
176 // FIXME: This is a sort of mini-tex2lyx.
177 // Use the real tex2lyx instead!
178 static lyx::regex const reg("^\\\\\\W\\w");
179 if (lyx::regex_search(to_utf8(val), reg)) {
180 val.insert(3, from_ascii("}"));
181 val.insert(2, from_ascii("{"));
185 docstring const cnvtd = Encodings::fromLaTeXCommand(val,
186 Encodings::TEXT_CMD, termination, rem);
187 if (!cnvtd.empty()) {
188 // it did, so we'll take that bit and proceed with what's left
193 // it's a command of some sort
202 // Escape '<' and '>' and remove richtext markers (e.g. {!this is richtext!}) from a string.
203 docstring processRichtext(docstring const & str, bool richtext)
208 bool scanning_rich = false;
209 while (!val.empty()) {
210 char_type const ch = val[0];
211 if (ch == '{' && val.size() > 1 && val[1] == '!') {
212 // beginning of rich text
213 scanning_rich = true;
217 if (scanning_rich && ch == '!' && val.size() > 1 && val[1] == '}') {
219 scanning_rich = false;
227 // we need to escape '<' and '>'
235 } else if (!scanning_rich /* && !richtext */)
237 // else the character is discarded, which will happen only if
238 // richtext == false and we are scanning rich text
247 //////////////////////////////////////////////////////////////////////
251 //////////////////////////////////////////////////////////////////////
253 BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
254 : is_bibtex_(true), bib_key_(key), entry_type_(type), info_(),
259 docstring const BibTeXInfo::getAuthorList(
260 Buffer const * buf, bool full, bool forceshort) const
262 // Maxnames treshold depend on engine
263 size_t maxnames = buf ?
264 buf->params().documentClass().max_citenames() : 2;
267 docstring const opt = label();
272 docstring const remainder = trim(split(opt, authors, '('));
273 if (remainder.empty())
274 // in this case, we didn't find a "(",
275 // so we don't have author (year)
280 docstring author = operator[]("author");
281 if (author.empty()) {
282 author = operator[]("editor");
287 // FIXME Move this to a separate routine that can
288 // be called from elsewhere.
290 // OK, we've got some names. Let's format them.
291 // Try to split the author list on " and "
292 vector<docstring> const authors =
293 getVectorFromString(author, from_ascii(" and "));
297 CiteEngineType const engine_type = buf ? buf->params().citeEngineType()
298 : ENGINE_TYPE_DEFAULT;
300 // These are defined in the styles
302 buf ? buf->params().documentClass().getCiteMacro(engine_type, "_etal")
304 string const namesep =
305 buf ? buf->params().documentClass().getCiteMacro(engine_type, "_namesep")
307 string const lastnamesep =
308 buf ? buf->params().documentClass().getCiteMacro(engine_type, "_lastnamesep")
310 string const pairnamesep =
311 buf ? buf->params().documentClass().getCiteMacro(engine_type, "_pairnamesep")
314 // Shorten the list (with et al.) if forceshort is set
315 // and the list can actually be shorten, else if maxcitenames
316 // is passed and full is not set.
317 bool shorten = forceshort && authors.size() > 1;
318 vector<docstring>::const_iterator it = authors.begin();
319 vector<docstring>::const_iterator en = authors.end();
320 for (size_t i = 0; it != en; ++it, ++i) {
321 if (i >= maxnames && !full) {
325 if (*it == "others") {
326 retval += buf ? buf->B_(etal) : from_ascii(etal);
329 if (i > 0 && i == authors.size() - 1) {
330 if (authors.size() == 2)
331 retval += buf ? buf->B_(pairnamesep) : from_ascii(pairnamesep);
333 retval += buf ? buf->B_(lastnamesep) : from_ascii(lastnamesep);
335 retval += buf ? buf->B_(namesep) : from_ascii(namesep);
336 retval += familyName(*it);
339 retval = familyName(authors[0]) + (buf ? buf->B_(etal) : from_ascii(etal));
341 return convertLaTeXCommands(retval);
345 docstring const BibTeXInfo::getYear() const
348 // first try legacy year field
349 docstring year = operator[]("year");
352 // now try biblatex's date field
353 year = operator[]("date");
354 // Format is [-]YYYY-MM-DD*/[-]YYYY-MM-DD*
355 // We only want the years.
356 static regex const yreg("[-]?([\\d]{4}).*");
357 static regex const ereg(".*/[-]?([\\d]{4}).*");
359 string const date = to_utf8(year);
360 regex_match(date, sm, yreg);
361 year = from_ascii(sm[1]);
362 // check for an endyear
363 if (regex_match(date, sm, ereg))
364 year += char_type(0x2013) + from_ascii(sm[1]);
368 docstring const opt = label();
373 docstring tmp = split(opt, authors, '(');
375 // we don't have author (year)
378 tmp = split(tmp, year, ')');
385 docstring parseOptions(docstring const & format, string & optkey,
386 docstring & ifpart, docstring & elsepart);
388 // Calls parseOptions to deal with an embedded option, such as:
389 // {%number%[[, no.~%number%]]}
390 // which must appear at the start of format. ifelsepart gets the
391 // whole of the option, and we return what's left after the option.
392 // we return format if there is an error.
393 docstring parseEmbeddedOption(docstring const & format, docstring & ifelsepart)
395 LASSERT(format[0] == '{' && format[1] == '%', return format);
399 docstring const rest = parseOptions(format, optkey, ifpart, elsepart);
400 if (format == rest) { // parse error
401 LYXERR0("ERROR! Couldn't parse `" << format <<"'.");
404 LASSERT(rest.size() <= format.size(),
405 { ifelsepart = docstring(); return format; });
406 ifelsepart = format.substr(0, format.size() - rest.size());
411 // Gets a "clause" from a format string, where the clause is
412 // delimited by '[[' and ']]'. Returns what is left after the
413 // clause is removed, and returns format if there is an error.
414 docstring getClause(docstring const & format, docstring & clause)
416 docstring fmt = format;
419 // we'll remove characters from the front of fmt as we
421 while (!fmt.empty()) {
422 if (fmt[0] == ']' && fmt.size() > 1 && fmt[1] == ']') {
427 // check for an embedded option
428 if (fmt[0] == '{' && fmt.size() > 1 && fmt[1] == '%') {
430 docstring const rest = parseEmbeddedOption(fmt, part);
432 LYXERR0("ERROR! Couldn't parse embedded option in `" << format <<"'.");
437 } else { // it's just a normal character
446 // parse an options string, which must appear at the start of the
447 // format parameter. puts the parsed bits in optkey, ifpart, and
448 // elsepart and returns what's left after the option is removed.
449 // if there's an error, it returns format itself.
450 docstring parseOptions(docstring const & format, string & optkey,
451 docstring & ifpart, docstring & elsepart)
453 LASSERT(format[0] == '{' && format[1] == '%', return format);
455 docstring fmt = format.substr(2);
456 size_t pos = fmt.find('%'); // end of key
457 if (pos == string::npos) {
458 LYXERR0("Error parsing `" << format <<"'. Can't find end of key.");
461 optkey = to_utf8(fmt.substr(0, pos));
462 fmt = fmt.substr(pos + 1);
463 // [[format]] should be next
464 if (fmt[0] != '[' || fmt[1] != '[') {
465 LYXERR0("Error parsing `" << format <<"'. Can't find '[[' after key.");
469 docstring curfmt = fmt;
470 fmt = getClause(curfmt, ifpart);
472 LYXERR0("Error parsing `" << format <<"'. Couldn't get if clause.");
476 if (fmt[0] == '}') // we're done, no else clause
477 return fmt.substr(1);
479 // else part should follow
480 if (fmt[0] != '[' || fmt[1] != '[') {
481 LYXERR0("Error parsing `" << format <<"'. Can't find else clause.");
486 fmt = getClause(curfmt, elsepart);
488 if (fmt == curfmt || fmt[0] != '}') {
489 LYXERR0("Error parsing `" << format <<"'. Can't find end of option.");
492 return fmt.substr(1);
499 Bug #9131 revealed an oddity in how we are generating citation information
500 when more than one key is given. We end up building a longer and longer format
501 string as we go, which we then have to re-parse, over and over and over again,
502 rather than generating the information for the individual keys and then putting
503 all of that together. We do that to deal with the way separators work, from what
504 I can tell, but it still feels like a hack. Fixing this would require quite a
505 bit of work, however.
507 docstring BibTeXInfo::expandFormat(docstring const & format,
508 BibTeXInfoList const xrefs, int & counter, Buffer const & buf,
509 CiteItem const & ci, bool next, bool second) const
511 // incorrect use of macros could put us in an infinite loop
512 static int const max_passes = 5000;
513 // the use of overly large keys can lead to performance problems, due
514 // to eventual attempts to convert LaTeX macros to unicode. See bug
515 // #8944. This is perhaps not the best solution, but it will have to
517 static size_t const max_keysize = 128;
518 odocstringstream ret; // return value
520 bool scanning_key = false;
521 bool scanning_rich = false;
523 CiteEngineType const engine_type = buf.params().citeEngineType();
524 docstring fmt = format;
525 // we'll remove characters from the front of fmt as we
527 while (!fmt.empty()) {
528 if (counter > max_passes) {
529 LYXERR0("Recursion limit reached while parsing `"
534 char_type thischar = fmt[0];
535 if (thischar == '%') {
536 // beginning or end of key
539 scanning_key = false;
540 // so we replace the key with its value, which may be empty
544 buf.params().documentClass().getCiteMacro(engine_type, key);
545 fmt = from_utf8(val) + fmt.substr(1);
548 } else if (key[0] == '_') {
549 // a translatable bit
551 buf.params().documentClass().getCiteMacro(engine_type, key);
552 docstring const trans =
553 translateIfPossible(from_utf8(val), buf.params().language->code());
556 docstring const val =
557 getValueForKey(key, buf, ci, xrefs, max_keysize);
559 ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
562 ret << from_ascii("{!</span>!}");
570 else if (thischar == '{') {
571 // beginning of option?
573 LYXERR0("ERROR: Found `{' when scanning key in `" << format << "'.");
576 if (fmt.size() > 1) {
578 // it is the beginning of an optional format
582 docstring const newfmt =
583 parseOptions(fmt, optkey, ifpart, elsepart);
584 if (newfmt == fmt) // parse error
587 docstring const val =
588 getValueForKey(optkey, buf, ci, xrefs);
589 if (optkey == "next" && next)
590 ret << ifpart; // without expansion
591 else if (!val.empty()) {
593 ret << expandFormat(ifpart, xrefs, newcounter, buf,
595 } else if (!elsepart.empty()) {
597 ret << expandFormat(elsepart, xrefs, newcounter, buf,
600 // fmt will have been shortened for us already
604 // beginning of rich text
605 scanning_rich = true;
607 ret << from_ascii("{!");
611 // we are here if '{' was not followed by % or !.
612 // So it's just a character.
615 else if (scanning_rich && thischar == '!'
616 && fmt.size() > 1 && fmt[1] == '}') {
618 scanning_rich = false;
620 ret << from_ascii("!}");
623 else if (scanning_key)
624 key += char(thischar);
628 } catch (EncodingException & /* e */) {
629 LYXERR0("Uncodable character '" << docstring(1, thischar) << " in citation label!");
635 LYXERR0("Never found end of key in `" << format << "'!");
639 LYXERR0("Never found end of rich text in `" << format << "'!");
646 docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
647 Buffer const & buf, CiteItem const & ci) const
649 bool const richtext = ci.richtext;
651 if (!richtext && !info_.empty())
653 if (richtext && !info_richtext_.empty())
654 return info_richtext_;
657 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
662 CiteEngineType const engine_type = buf.params().citeEngineType();
663 DocumentClass const & dc = buf.params().documentClass();
664 docstring const & format =
665 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
667 info_ = expandFormat(format, xrefs, counter, buf,
671 // this probably shouldn't happen
676 info_richtext_ = convertLaTeXCommands(processRichtext(info_, true));
677 return info_richtext_;
680 info_ = convertLaTeXCommands(processRichtext(info_, false));
685 docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
686 Buffer const & buf, docstring const & format,
687 CiteItem const & ci, bool next, bool second) const
692 loclabel = expandFormat(format, xrefs, counter, buf, ci, next, second);
694 if (!loclabel.empty() && !next) {
695 loclabel = processRichtext(loclabel, ci.richtext);
696 loclabel = convertLaTeXCommands(loclabel);
703 docstring const & BibTeXInfo::operator[](docstring const & field) const
705 BibTeXInfo::const_iterator it = find(field);
708 static docstring const empty_value = docstring();
713 docstring const & BibTeXInfo::operator[](string const & field) const
715 return operator[](from_ascii(field));
719 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
720 CiteItem const & ci, BibTeXInfoList const xrefs, size_t maxsize) const
722 // anything less is pointless
723 LASSERT(maxsize >= 16, maxsize = 16);
725 bool cleanit = false;
726 if (prefixIs(oldkey, "clean:")) {
727 key = oldkey.substr(6);
731 docstring ret = operator[](key);
732 if (ret.empty() && !xrefs.empty()) {
733 vector<BibTeXInfo const *>::const_iterator it = xrefs.begin();
734 vector<BibTeXInfo const *>::const_iterator en = xrefs.end();
735 for (; it != en; ++it) {
736 if (*it && !(**it)[key].empty()) {
744 // FIXME: dialog, textbefore and textafter have nothing to do with this
745 if (key == "dialog" && ci.context == CiteItem::Dialog)
746 ret = from_ascii("x"); // any non-empty string will do
747 else if (key == "ifstar" && ci.Starred)
748 ret = from_ascii("x"); // any non-empty string will do
749 else if (key == "entrytype")
751 else if (key == "key")
753 else if (key == "label")
755 else if (key == "modifier" && modifier_ != 0)
757 else if (key == "numericallabel")
759 else if (key == "abbrvauthor") {
760 // Special key to provide abbreviated author names,
761 // with respect to maxcitenames.
762 ret = getAuthorList(&buf, false, false);
763 if (ci.forceUpperCase && isLowerCase(ret[0]))
764 ret[0] = uppercase(ret[0]);
765 } else if (key == "fullauthor") {
766 // Return a full author list
767 ret = getAuthorList(&buf, true, false);
768 if (ci.forceUpperCase && isLowerCase(ret[0]))
769 ret[0] = uppercase(ret[0]);
770 } else if (key == "forceabbrvauthor") {
771 // Special key to provide abbreviated author names,
772 // irrespective of maxcitenames.
773 ret = getAuthorList(&buf, false, true);
774 if (ci.forceUpperCase && isLowerCase(ret[0]))
775 ret[0] = uppercase(ret[0]);
776 } else if (key == "bibentry") {
777 // Special key to provide the full bibliography entry: see getInfo()
778 CiteEngineType const engine_type = buf.params().citeEngineType();
779 DocumentClass const & dc = buf.params().documentClass();
780 docstring const & format =
781 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
783 ret = expandFormat(format, xrefs, counter, buf, ci, false, false);
784 } else if (key == "textbefore")
786 else if (key == "textafter")
788 else if (key == "year")
793 ret = html::cleanAttr(ret);
795 // make sure it is not too big
796 support::truncateWithEllipsis(ret, maxsize);
801 //////////////////////////////////////////////////////////////////////
805 //////////////////////////////////////////////////////////////////////
809 // A functor for use with sort, leading to case insensitive sorting
810 class compareNoCase: public binary_function<docstring, docstring, bool>
813 bool operator()(docstring const & s1, docstring const & s2) const {
814 return compare_no_case(s1, s2) < 0;
821 vector<docstring> const BiblioInfo::getXRefs(BibTeXInfo const & data, bool const nested) const
823 vector<docstring> result;
824 if (!data.isBibTeX())
826 // Legacy crossref field. This is not nestable.
827 if (!nested && !data["crossref"].empty()) {
828 docstring const xrefkey = data["crossref"];
829 result.push_back(xrefkey);
830 // However, check for nested xdatas
831 BiblioInfo::const_iterator it = find(xrefkey);
833 BibTeXInfo const & xref = it->second;
834 vector<docstring> const nxdata = getXRefs(xref, true);
836 result.insert(result.end(), nxdata.begin(), nxdata.end());
839 // Biblatex's xdata field. Infinitely nestable.
840 // XData field can consist of a comma-separated list of keys
841 vector<docstring> const xdatakeys = getVectorFromString(data["xdata"]);
842 if (!xdatakeys.empty()) {
843 vector<docstring>::const_iterator xit = xdatakeys.begin();
844 vector<docstring>::const_iterator xen = xdatakeys.end();
845 for (; xit != xen; ++xit) {
846 docstring const xdatakey = *xit;
847 result.push_back(xdatakey);
848 BiblioInfo::const_iterator it = find(xdatakey);
850 BibTeXInfo const & xdata = it->second;
851 vector<docstring> const nxdata = getXRefs(xdata, true);
853 result.insert(result.end(), nxdata.begin(), nxdata.end());
861 vector<docstring> const BiblioInfo::getKeys() const
863 vector<docstring> bibkeys;
864 BiblioInfo::const_iterator it = begin();
865 for (; it != end(); ++it)
866 bibkeys.push_back(it->first);
867 sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
872 vector<docstring> const BiblioInfo::getFields() const
874 vector<docstring> bibfields;
875 set<docstring>::const_iterator it = field_names_.begin();
876 set<docstring>::const_iterator end = field_names_.end();
877 for (; it != end; ++it)
878 bibfields.push_back(*it);
879 sort(bibfields.begin(), bibfields.end());
884 vector<docstring> const BiblioInfo::getEntries() const
886 vector<docstring> bibentries;
887 set<docstring>::const_iterator it = entry_types_.begin();
888 set<docstring>::const_iterator end = entry_types_.end();
889 for (; it != end; ++it)
890 bibentries.push_back(*it);
891 sort(bibentries.begin(), bibentries.end());
896 docstring const BiblioInfo::getAuthorList(docstring const & key, Buffer const & buf) const
898 BiblioInfo::const_iterator it = find(key);
901 BibTeXInfo const & data = it->second;
902 return data.getAuthorList(&buf, false);
906 docstring const BiblioInfo::getCiteNumber(docstring const & key) const
908 BiblioInfo::const_iterator it = find(key);
911 BibTeXInfo const & data = it->second;
912 return data.citeNumber();
916 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
918 BiblioInfo::const_iterator it = find(key);
921 BibTeXInfo const & data = it->second;
922 docstring year = data.getYear();
924 // let's try the crossrefs
925 vector<docstring> const xrefs = getXRefs(data);
929 vector<docstring>::const_iterator it = xrefs.begin();
930 vector<docstring>::const_iterator en = xrefs.end();
931 for (; it != en; ++it) {
932 BiblioInfo::const_iterator const xrefit = find(*it);
935 BibTeXInfo const & xref_data = xrefit->second;
936 year = xref_data.getYear();
942 if (use_modifier && data.modifier() != 0)
943 year += data.modifier();
948 docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, bool use_modifier) const
950 docstring const year = getYear(key, use_modifier);
952 return buf.B_("No year");
957 docstring const BiblioInfo::getInfo(docstring const & key,
958 Buffer const & buf, CiteItem const & ci) const
960 BiblioInfo::const_iterator it = find(key);
962 return docstring(_("Bibliography entry not found!"));
963 BibTeXInfo const & data = it->second;
964 BibTeXInfoList xrefptrs;
965 vector<docstring> const xrefs = getXRefs(data);
966 if (!xrefs.empty()) {
967 vector<docstring>::const_iterator it = xrefs.begin();
968 vector<docstring>::const_iterator en = xrefs.end();
969 for (; it != en; ++it) {
970 BiblioInfo::const_iterator const xrefit = find(*it);
972 xrefptrs.push_back(&(xrefit->second));
975 return data.getInfo(xrefptrs, buf, ci);
979 docstring const BiblioInfo::getLabel(vector<docstring> keys,
980 Buffer const & buf, string const & style, CiteItem const & ci) const
982 size_t max_size = ci.max_size;
983 // shorter makes no sense
984 LASSERT(max_size >= 16, max_size = 16);
986 // we can't display more than 10 of these, anyway
987 bool const too_many_keys = keys.size() > 10;
991 CiteEngineType const engine_type = buf.params().citeEngineType();
992 DocumentClass const & dc = buf.params().documentClass();
993 docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
994 docstring ret = format;
995 vector<docstring>::const_iterator key = keys.begin();
996 vector<docstring>::const_iterator ken = keys.end();
997 for (; key != ken; ++key) {
998 BiblioInfo::const_iterator it = find(*key);
999 BibTeXInfo empty_data;
1000 empty_data.key(*key);
1001 BibTeXInfo & data = empty_data;
1002 vector<BibTeXInfo const *> xrefptrs;
1005 vector<docstring> const xrefs = getXRefs(data);
1006 if (!xrefs.empty()) {
1007 vector<docstring>::const_iterator it = xrefs.begin();
1008 vector<docstring>::const_iterator en = xrefs.end();
1009 for (; it != en; ++it) {
1010 BiblioInfo::const_iterator const xrefit = find(*it);
1011 if (xrefit != end())
1012 xrefptrs.push_back(&(xrefit->second));
1016 ret = data.getLabel(xrefptrs, buf, ret, ci, key + 1 != ken, i == 1);
1020 ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
1021 support::truncateWithEllipsis(ret, max_size);
1026 bool BiblioInfo::isBibtex(docstring const & key) const
1029 split(key, key1, ',');
1030 BiblioInfo::const_iterator it = find(key1);
1033 return it->second.isBibTeX();
1037 vector<docstring> const BiblioInfo::getCiteStrings(
1038 vector<docstring> const & keys, vector<CitationStyle> const & styles,
1039 Buffer const & buf, CiteItem const & ci) const
1042 return vector<docstring>();
1045 vector<docstring> vec(styles.size());
1046 for (size_t i = 0; i != vec.size(); ++i) {
1047 style = styles[i].name;
1048 vec[i] = getLabel(keys, buf, style, ci);
1055 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
1057 bimap_.insert(info.begin(), info.end());
1058 field_names_.insert(info.field_names_.begin(), info.field_names_.end());
1059 entry_types_.insert(info.entry_types_.begin(), info.entry_types_.end());
1065 // used in xhtml to sort a list of BibTeXInfo objects
1066 bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
1068 docstring const lauth = lhs->getAuthorList();
1069 docstring const rauth = rhs->getAuthorList();
1070 docstring const lyear = lhs->getYear();
1071 docstring const ryear = rhs->getYear();
1072 docstring const ltitl = lhs->operator[]("title");
1073 docstring const rtitl = rhs->operator[]("title");
1074 return (lauth < rauth)
1075 || (lauth == rauth && lyear < ryear)
1076 || (lauth == rauth && lyear == ryear && ltitl < rtitl);
1082 void BiblioInfo::collectCitedEntries(Buffer const & buf)
1084 cited_entries_.clear();
1085 // We are going to collect all the citation keys used in the document,
1086 // getting them from the TOC.
1087 // FIXME We may want to collect these differently, in the first case,
1088 // so that we might have them in order of appearance.
1089 set<docstring> citekeys;
1090 shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
1091 Toc::const_iterator it = toc->begin();
1092 Toc::const_iterator const en = toc->end();
1093 for (; it != en; ++it) {
1094 if (it->str().empty())
1096 vector<docstring> const keys = getVectorFromString(it->str());
1097 citekeys.insert(keys.begin(), keys.end());
1099 if (citekeys.empty())
1102 // We have a set of the keys used in this document.
1103 // We will now convert it to a list of the BibTeXInfo objects used in
1105 vector<BibTeXInfo const *> bi;
1106 set<docstring>::const_iterator cit = citekeys.begin();
1107 set<docstring>::const_iterator const cen = citekeys.end();
1108 for (; cit != cen; ++cit) {
1109 BiblioInfo::const_iterator const bt = find(*cit);
1110 if (bt == end() || !bt->second.isBibTeX())
1112 bi.push_back(&(bt->second));
1115 sort(bi.begin(), bi.end(), lSorter);
1117 // Now we can write the sorted keys
1118 vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
1119 vector<BibTeXInfo const *>::const_iterator ben = bi.end();
1120 for (; bit != ben; ++bit)
1121 cited_entries_.push_back((*bit)->key());
1125 void BiblioInfo::makeCitationLabels(Buffer const & buf)
1127 collectCitedEntries(buf);
1128 CiteEngineType const engine_type = buf.params().citeEngineType();
1129 bool const numbers = (engine_type & ENGINE_TYPE_NUMERICAL);
1133 // used to remember the last one we saw
1134 // we'll be comparing entries to see if we need to add
1135 // modifiers, like "1984a"
1136 map<docstring, BibTeXInfo>::iterator last;
1138 vector<docstring>::const_iterator it = cited_entries_.begin();
1139 vector<docstring>::const_iterator const en = cited_entries_.end();
1140 for (; it != en; ++it) {
1141 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1142 // this shouldn't happen, but...
1143 if (biit == bimap_.end())
1144 // ...fail gracefully, anyway.
1146 BibTeXInfo & entry = biit->second;
1148 docstring const num = convert<docstring>(++keynumber);
1149 entry.setCiteNumber(num);
1151 // coverity complains about our derefercing the iterator last,
1152 // which was not initialized above. but it does get initialized
1153 // after the first time through the loop, which is the point of
1155 // coverity[FORWARD_NULL]
1156 if (it != cited_entries_.begin()
1157 && entry.getAuthorList() == last->second.getAuthorList()
1158 // we access the year via getYear() so as to get it from the xref,
1159 // if we need to do so
1160 && getYear(entry.key()) == getYear(last->second.key())) {
1161 if (modifier == 0) {
1162 // so the last one should have been 'a'
1163 last->second.setModifier('a');
1165 } else if (modifier == 'z')
1172 entry.setModifier(modifier);
1173 // remember the last one
1178 it = cited_entries_.begin();
1179 for (; it != en; ++it) {
1180 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1181 // this shouldn't happen, but...
1182 if (biit == bimap_.end())
1183 // ...fail gracefully, anyway.
1185 BibTeXInfo & entry = biit->second;
1187 entry.label(entry.citeNumber());
1189 docstring const auth = entry.getAuthorList(&buf, false);
1190 // we do it this way so as to access the xref, if necessary
1191 // note that this also gives us the modifier
1192 docstring const year = getYear(*it, buf, true);
1193 if (!auth.empty() && !year.empty())
1194 entry.label(auth + ' ' + year);
1196 entry.label(entry.key());
1202 //////////////////////////////////////////////////////////////////////
1206 //////////////////////////////////////////////////////////////////////
1209 CitationStyle citationStyleFromString(string const & command,
1210 BufferParams const & params)
1213 if (command.empty())
1216 string const alias = params.getCiteAlias(command);
1217 string cmd = alias.empty() ? command : alias;
1218 if (isUpperCase(command[0])) {
1219 cs.forceUpperCase = true;
1220 cmd[0] = lowercase(cmd[0]);
1223 size_t const n = command.size() - 1;
1224 if (command[n] == '*') {
1225 cs.hasStarredVersion = true;
1226 if (suffixIs(cmd, '*'))
1227 cmd = cmd.substr(0, cmd.size() - 1);
1235 string citationStyleToString(const CitationStyle & cs, bool const latex)
1237 string cmd = latex ? cs.cmd : cs.name;
1238 if (cs.forceUpperCase)
1239 cmd[0] = uppercase(cmd[0]);
1240 if (cs.hasStarredVersion)