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"
21 #include "InsetIterator.h"
23 #include "output_xhtml.h"
24 #include "Paragraph.h"
25 #include "TextClass.h"
26 #include "TocBackend.h"
28 #include "support/convert.h"
29 #include "support/debug.h"
30 #include "support/docstream.h"
31 #include "support/gettext.h"
32 #include "support/lassert.h"
33 #include "support/lstrings.h"
34 #include "support/regex.h"
35 #include "support/textutils.h"
40 using namespace lyx::support;
47 // gets the "family name" from an author-type string
48 docstring familyName(docstring const & name)
53 // first we look for a comma, and take the last name to be everything
54 // preceding the right-most one, so that we also get the "jr" part.
55 docstring::size_type idx = name.rfind(',');
56 if (idx != docstring::npos)
57 return ltrim(name.substr(0, idx));
59 // OK, so now we want to look for the last name. We're going to
60 // include the "von" part. This isn't perfect.
61 // Split on spaces, to get various tokens.
62 vector<docstring> pieces = getVectorFromString(name, from_ascii(" "));
63 // If we only get two, assume the last one is the last name
64 if (pieces.size() <= 2)
67 // Now we look for the first token that begins with a lower case letter.
68 vector<docstring>::const_iterator it = pieces.begin();
69 vector<docstring>::const_iterator en = pieces.end();
70 for (; it != en; ++it) {
73 char_type const c = (*it)[0];
78 if (it == en) // we never found a "von"
81 // reconstruct what we need to return
84 for (; it != en; ++it) {
95 // converts a string containing LaTeX commands into unicode
97 docstring convertLaTeXCommands(docstring const & str)
102 bool scanning_cmd = false;
103 bool scanning_math = false;
104 bool escaped = false; // used to catch \$, etc.
105 while (!val.empty()) {
106 char_type const ch = val[0];
108 // if we're scanning math, we output everything until we
109 // find an unescaped $, at which point we break out.
116 scanning_math = false;
122 // if we're scanning a command name, then we just
123 // discard characters until we hit something that
126 if (isAlphaASCII(ch)) {
131 // so we're done with this command.
132 // now we fall through and check this character.
133 scanning_cmd = false;
136 // was the last character a \? If so, then this is something like:
137 // \\ or \$, so we'll just output it. That's probably not always right...
139 // exception: output \, as THIN SPACE
141 ret.push_back(0x2009);
152 scanning_math = true;
156 // we just ignore braces
157 if (ch == '{' || ch == '}') {
162 // we're going to check things that look like commands, so if
163 // this doesn't, just output it.
170 // ok, could be a command of some sort
171 // let's see if it corresponds to some unicode
172 // unicodesymbols has things in the form: \"{u},
173 // whereas we may see things like: \"u. So we'll
174 // look for that and change it, if necessary.
175 // FIXME: This is a sort of mini-tex2lyx.
176 // Use the real tex2lyx instead!
177 static lyx::regex const reg("^\\\\\\W\\w");
178 if (lyx::regex_search(to_utf8(val), reg)) {
179 val.insert(3, from_ascii("}"));
180 val.insert(2, from_ascii("{"));
184 docstring const cnvtd = Encodings::fromLaTeXCommand(val,
185 Encodings::TEXT_CMD, termination, rem);
186 if (!cnvtd.empty()) {
187 // it did, so we'll take that bit and proceed with what's left
192 // it's a command of some sort
201 // Escape '<' and '>' and remove richtext markers (e.g. {!this is richtext!}) from a string.
202 docstring processRichtext(docstring const & str, bool richtext)
207 bool scanning_rich = false;
208 while (!val.empty()) {
209 char_type const ch = val[0];
210 if (ch == '{' && val.size() > 1 && val[1] == '!') {
211 // beginning of rich text
212 scanning_rich = true;
216 if (scanning_rich && ch == '!' && val.size() > 1 && val[1] == '}') {
218 scanning_rich = false;
226 // we need to escape '<' and '>'
234 } else if (!scanning_rich /* && !richtext */)
236 // else the character is discarded, which will happen only if
237 // richtext == false and we are scanning rich text
246 //////////////////////////////////////////////////////////////////////
250 //////////////////////////////////////////////////////////////////////
252 BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
253 : is_bibtex_(true), bib_key_(key), entry_type_(type), info_(),
258 docstring const BibTeXInfo::getAbbreviatedAuthor(bool jurabib_style) const
261 docstring const opt = label();
266 docstring const remainder = trim(split(opt, authors, '('));
267 if (remainder.empty())
268 // in this case, we didn't find a "(",
269 // so we don't have author (year)
274 docstring author = operator[]("author");
275 if (author.empty()) {
276 author = operator[]("editor");
281 // FIXME Move this to a separate routine that can
282 // be called from elsewhere.
284 // OK, we've got some names. Let's format them.
285 // Try to split the author list on " and "
286 vector<docstring> const authors =
287 getVectorFromString(author, from_ascii(" and "));
289 if (jurabib_style && (authors.size() == 2 || authors.size() == 3)) {
290 docstring shortauthor = familyName(authors[0])
291 + "/" + familyName(authors[1]);
292 if (authors.size() == 3)
293 shortauthor += "/" + familyName(authors[2]);
294 return convertLaTeXCommands(shortauthor);
297 docstring retval = familyName(authors[0]);
299 if (authors.size() == 2 && authors[1] != "others")
300 retval = bformat(from_ascii("%1$s and %2$s"),
301 familyName(authors[0]), familyName(authors[1]));
302 else if (authors.size() >= 2)
303 retval = bformat(from_ascii("%1$s et al."),
304 familyName(authors[0]));
306 return convertLaTeXCommands(retval);
310 docstring const BibTeXInfo::getAbbreviatedAuthor(Buffer const & buf, bool jurabib_style) const
312 docstring const author = getAbbreviatedAuthor(jurabib_style);
315 vector<docstring> const authors = getVectorFromString(author, from_ascii(" and "));
316 if (authors.size() == 2)
317 return bformat(buf.B_("%1$s and %2$s"), authors[0], authors[1]);
318 docstring::size_type const idx = author.rfind(from_ascii(" et al."));
319 if (idx != docstring::npos)
320 return bformat(buf.B_("%1$s et al."), author.substr(0, idx));
325 docstring const BibTeXInfo::getYear() const
328 return operator[]("year");
330 docstring const opt = label();
335 docstring tmp = split(opt, authors, '(');
337 // we don't have author (year)
340 tmp = split(tmp, year, ')');
345 docstring const BibTeXInfo::getXRef() const
349 return operator[]("crossref");
355 docstring parseOptions(docstring const & format, string & optkey,
356 docstring & ifpart, docstring & elsepart);
358 // Calls parseOptions to deal with an embedded option, such as:
359 // {%number%[[, no.~%number%]]}
360 // which must appear at the start of format. ifelsepart gets the
361 // whole of the option, and we return what's left after the option.
362 // we return format if there is an error.
363 docstring parseEmbeddedOption(docstring const & format, docstring & ifelsepart)
365 LASSERT(format[0] == '{' && format[1] == '%', return format);
369 docstring const rest = parseOptions(format, optkey, ifpart, elsepart);
370 if (format == rest) { // parse error
371 LYXERR0("ERROR! Couldn't parse `" << format <<"'.");
374 LASSERT(rest.size() <= format.size(),
375 { ifelsepart = docstring(); return format; });
376 ifelsepart = format.substr(0, format.size() - rest.size());
381 // Gets a "clause" from a format string, where the clause is
382 // delimited by '[[' and ']]'. Returns what is left after the
383 // clause is removed, and returns format if there is an error.
384 docstring getClause(docstring const & format, docstring & clause)
386 docstring fmt = format;
389 // we'll remove characters from the front of fmt as we
391 while (!fmt.empty()) {
392 if (fmt[0] == ']' && fmt.size() > 1 && fmt[1] == ']') {
397 // check for an embedded option
398 if (fmt[0] == '{' && fmt.size() > 1 && fmt[1] == '%') {
400 docstring const rest = parseEmbeddedOption(fmt, part);
402 LYXERR0("ERROR! Couldn't parse embedded option in `" << format <<"'.");
407 } else { // it's just a normal character
416 // parse an options string, which must appear at the start of the
417 // format parameter. puts the parsed bits in optkey, ifpart, and
418 // elsepart and returns what's left after the option is removed.
419 // if there's an error, it returns format itself.
420 docstring parseOptions(docstring const & format, string & optkey,
421 docstring & ifpart, docstring & elsepart)
423 LASSERT(format[0] == '{' && format[1] == '%', return format);
425 docstring fmt = format.substr(2);
426 size_t pos = fmt.find('%'); // end of key
427 if (pos == string::npos) {
428 LYXERR0("Error parsing `" << format <<"'. Can't find end of key.");
431 optkey = to_utf8(fmt.substr(0, pos));
432 fmt = fmt.substr(pos + 1);
433 // [[format]] should be next
434 if (fmt[0] != '[' || fmt[1] != '[') {
435 LYXERR0("Error parsing `" << format <<"'. Can't find '[[' after key.");
439 docstring curfmt = fmt;
440 fmt = getClause(curfmt, ifpart);
442 LYXERR0("Error parsing `" << format <<"'. Couldn't get if clause.");
446 if (fmt[0] == '}') // we're done, no else clause
447 return fmt.substr(1);
449 // else part should follow
450 if (fmt[0] != '[' || fmt[1] != '[') {
451 LYXERR0("Error parsing `" << format <<"'. Can't find else clause.");
456 fmt = getClause(curfmt, elsepart);
458 if (fmt == curfmt || fmt[0] != '}') {
459 LYXERR0("Error parsing `" << format <<"'. Can't find end of option.");
462 return fmt.substr(1);
469 Bug #9131 revealed an oddity in how we are generating citation information
470 when more than one key is given. We end up building a longer and longer format
471 string as we go, which we then have to re-parse, over and over and over again,
472 rather than generating the information for the individual keys and then putting
473 all of that together. We do that to deal with the way separators work, from what
474 I can tell, but it still feels like a hack. Fixing this would require quite a
475 bit of work, however.
477 docstring BibTeXInfo::expandFormat(docstring const & format,
478 BibTeXInfo const * const xref, int & counter, Buffer const & buf,
479 docstring before, docstring after, docstring dialog, bool next) const
481 // incorrect use of macros could put us in an infinite loop
482 static int const max_passes = 5000;
483 // the use of overly large keys can lead to performance problems, due
484 // to eventual attempts to convert LaTeX macros to unicode. See bug
485 // #8944. This is perhaps not the best solution, but it will have to
487 static size_t const max_keysize = 128;
488 odocstringstream ret; // return value
490 bool scanning_key = false;
491 bool scanning_rich = false;
493 CiteEngineType const engine_type = buf.params().citeEngineType();
494 docstring fmt = format;
495 // we'll remove characters from the front of fmt as we
497 while (!fmt.empty()) {
498 if (counter > max_passes) {
499 LYXERR0("Recursion limit reached while parsing `"
504 char_type thischar = fmt[0];
505 if (thischar == '%') {
506 // beginning or end of key
509 scanning_key = false;
510 // so we replace the key with its value, which may be empty
514 buf.params().documentClass().getCiteMacro(engine_type, key);
515 fmt = from_utf8(val) + fmt.substr(1);
518 } else if (key[0] == '_') {
519 // a translatable bit
521 buf.params().documentClass().getCiteMacro(engine_type, key);
522 docstring const trans =
523 translateIfPossible(from_utf8(val), buf.params().language->code());
526 docstring const val =
527 getValueForKey(key, buf, before, after, dialog, xref, max_keysize);
529 ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
532 ret << from_ascii("{!</span>!}");
540 else if (thischar == '{') {
541 // beginning of option?
543 LYXERR0("ERROR: Found `{' when scanning key in `" << format << "'.");
546 if (fmt.size() > 1) {
548 // it is the beginning of an optional format
552 docstring const newfmt =
553 parseOptions(fmt, optkey, ifpart, elsepart);
554 if (newfmt == fmt) // parse error
557 docstring const val =
558 getValueForKey(optkey, buf, before, after, dialog, xref);
559 if (optkey == "next" && next)
560 ret << ifpart; // without expansion
561 else if (!val.empty()) {
563 ret << expandFormat(ifpart, xref, newcounter, buf,
564 before, after, dialog, next);
565 } else if (!elsepart.empty()) {
567 ret << expandFormat(elsepart, xref, newcounter, buf,
568 before, after, dialog, next);
570 // fmt will have been shortened for us already
574 // beginning of rich text
575 scanning_rich = true;
577 ret << from_ascii("{!");
581 // we are here if '{' was not followed by % or !.
582 // So it's just a character.
585 else if (scanning_rich && thischar == '!'
586 && fmt.size() > 1 && fmt[1] == '}') {
588 scanning_rich = false;
590 ret << from_ascii("!}");
593 else if (scanning_key)
594 key += char(thischar);
598 } catch (EncodingException & /* e */) {
599 LYXERR0("Uncodable character '" << docstring(1, thischar) << " in citation label!");
605 LYXERR0("Never found end of key in `" << format << "'!");
609 LYXERR0("Never found end of rich text in `" << format << "'!");
616 docstring const & BibTeXInfo::getInfo(BibTeXInfo const * const xref,
617 Buffer const & buf, bool richtext) const
619 if (!richtext && !info_.empty())
621 if (richtext && !info_richtext_.empty())
622 return info_richtext_;
625 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
630 CiteEngineType const engine_type = buf.params().citeEngineType();
631 DocumentClass const & dc = buf.params().documentClass();
632 docstring const & format =
633 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
635 info_ = expandFormat(format, xref, counter, buf,
636 docstring(), docstring(), docstring(), false);
639 // this probably shouldn't happen
644 info_richtext_ = convertLaTeXCommands(processRichtext(info_, true));
645 return info_richtext_;
648 info_ = convertLaTeXCommands(processRichtext(info_, false));
653 docstring const BibTeXInfo::getLabel(BibTeXInfo const * const xref,
654 Buffer const & buf, docstring const & format, bool richtext,
655 docstring const & before, docstring const & after,
656 docstring const & dialog, bool next) const
661 loclabel = expandFormat(format, xref, counter, buf,
662 before, after, dialog, next);
664 if (!loclabel.empty() && !next) {
665 loclabel = processRichtext(loclabel, richtext);
666 loclabel = convertLaTeXCommands(loclabel);
673 docstring const & BibTeXInfo::operator[](docstring const & field) const
675 BibTeXInfo::const_iterator it = find(field);
678 static docstring const empty_value = docstring();
683 docstring const & BibTeXInfo::operator[](string const & field) const
685 return operator[](from_ascii(field));
689 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
690 docstring const & before, docstring const & after, docstring const & dialog,
691 BibTeXInfo const * const xref, size_t maxsize) const
693 // anything less is pointless
694 LASSERT(maxsize >= 16, maxsize = 16);
696 bool cleanit = false;
697 if (prefixIs(oldkey, "clean:")) {
698 key = oldkey.substr(6);
702 docstring ret = operator[](key);
703 if (ret.empty() && xref)
707 // FIXME: dialog, textbefore and textafter have nothing to do with this
710 else if (key == "entrytype")
712 else if (key == "key")
714 else if (key == "label")
716 else if (key == "modifier" && modifier_ != 0)
718 else if (key == "numericallabel")
720 else if (key == "abbrvauthor")
721 // Special key to provide abbreviated author names.
722 ret = getAbbreviatedAuthor(buf, false);
723 else if (key == "shortauthor")
724 // When shortauthor is not defined, jurabib automatically
725 // provides jurabib-style abbreviated author names. We do
727 ret = getAbbreviatedAuthor(buf, true);
728 else if (key == "shorttitle") {
729 // When shorttitle is not defined, jurabib uses for `article'
730 // and `periodical' entries the form `journal volume [year]'
731 // and for other types of entries it uses the `title' field.
732 if (entry_type_ == "article" || entry_type_ == "periodical")
733 ret = operator[]("journal") + " " + operator[]("volume")
734 + " [" + operator[]("year") + "]";
736 ret = operator[]("title");
737 } else if (key == "bibentry") {
738 // Special key to provide the full bibliography entry: see getInfo()
739 CiteEngineType const engine_type = buf.params().citeEngineType();
740 DocumentClass const & dc = buf.params().documentClass();
741 docstring const & format =
742 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
744 ret = expandFormat(format, xref, counter, buf,
745 docstring(), docstring(), docstring(), false);
746 } else if (key == "textbefore")
748 else if (key == "textafter")
750 else if (key == "year")
755 ret = html::cleanAttr(ret);
757 // make sure it is not too big
758 support::truncateWithEllipsis(ret, maxsize);
763 //////////////////////////////////////////////////////////////////////
767 //////////////////////////////////////////////////////////////////////
771 // A functor for use with sort, leading to case insensitive sorting
772 class compareNoCase: public binary_function<docstring, docstring, bool>
775 bool operator()(docstring const & s1, docstring const & s2) const {
776 return compare_no_case(s1, s2) < 0;
783 vector<docstring> const BiblioInfo::getKeys() const
785 vector<docstring> bibkeys;
786 BiblioInfo::const_iterator it = begin();
787 for (; it != end(); ++it)
788 bibkeys.push_back(it->first);
789 sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
794 vector<docstring> const BiblioInfo::getFields() const
796 vector<docstring> bibfields;
797 set<docstring>::const_iterator it = field_names_.begin();
798 set<docstring>::const_iterator end = field_names_.end();
799 for (; it != end; ++it)
800 bibfields.push_back(*it);
801 sort(bibfields.begin(), bibfields.end());
806 vector<docstring> const BiblioInfo::getEntries() const
808 vector<docstring> bibentries;
809 set<docstring>::const_iterator it = entry_types_.begin();
810 set<docstring>::const_iterator end = entry_types_.end();
811 for (; it != end; ++it)
812 bibentries.push_back(*it);
813 sort(bibentries.begin(), bibentries.end());
818 docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key, Buffer const & buf) const
820 BiblioInfo::const_iterator it = find(key);
823 BibTeXInfo const & data = it->second;
824 return data.getAbbreviatedAuthor(buf, false);
828 docstring const BiblioInfo::getCiteNumber(docstring const & key) const
830 BiblioInfo::const_iterator it = find(key);
833 BibTeXInfo const & data = it->second;
834 return data.citeNumber();
838 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
840 BiblioInfo::const_iterator it = find(key);
843 BibTeXInfo const & data = it->second;
844 docstring year = data.getYear();
846 // let's try the crossref
847 docstring const xref = data.getXRef();
851 BiblioInfo::const_iterator const xrefit = find(xref);
855 BibTeXInfo const & xref_data = xrefit->second;
856 year = xref_data.getYear();
858 if (use_modifier && data.modifier() != 0)
859 year += data.modifier();
864 docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, bool use_modifier) const
866 docstring const year = getYear(key, use_modifier);
868 return buf.B_("No year");
873 docstring const BiblioInfo::getInfo(docstring const & key,
874 Buffer const & buf, bool richtext) const
876 BiblioInfo::const_iterator it = find(key);
878 return docstring(_("Bibliography entry not found!"));
879 BibTeXInfo const & data = it->second;
880 BibTeXInfo const * xrefptr = 0;
881 docstring const xref = data.getXRef();
883 BiblioInfo::const_iterator const xrefit = find(xref);
885 xrefptr = &(xrefit->second);
887 return data.getInfo(xrefptr, buf, richtext);
891 docstring const BiblioInfo::getLabel(vector<docstring> keys,
892 Buffer const & buf, string const & style, bool for_xhtml,
893 size_t max_size, docstring const & before, docstring const & after,
894 docstring const & dialog) const
896 // shorter makes no sense
897 LASSERT(max_size >= 16, max_size = 16);
899 // we can't display more than 10 of these, anyway
900 bool const too_many_keys = keys.size() > 10;
904 CiteEngineType const engine_type = buf.params().citeEngineType();
905 DocumentClass const & dc = buf.params().documentClass();
906 docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
907 docstring ret = format;
908 vector<docstring>::const_iterator key = keys.begin();
909 vector<docstring>::const_iterator ken = keys.end();
910 for (; key != ken; ++key) {
911 BiblioInfo::const_iterator it = find(*key);
912 BibTeXInfo empty_data;
913 empty_data.key(*key);
914 BibTeXInfo & data = empty_data;
915 BibTeXInfo const * xrefptr = 0;
918 docstring const xref = data.getXRef();
920 BiblioInfo::const_iterator const xrefit = find(xref);
922 xrefptr = &(xrefit->second);
925 ret = data.getLabel(xrefptr, buf, ret, for_xhtml,
926 before, after, dialog, key + 1 != ken);
930 ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
931 support::truncateWithEllipsis(ret, max_size);
936 bool BiblioInfo::isBibtex(docstring const & key) const
939 split(key, key1, ',');
940 BiblioInfo::const_iterator it = find(key1);
943 return it->second.isBibTeX();
947 vector<docstring> const BiblioInfo::getCiteStrings(
948 vector<docstring> const & keys, vector<CitationStyle> const & styles,
949 Buffer const & buf, docstring const & before,
950 docstring const & after, docstring const & dialog, size_t max_size) const
953 return vector<docstring>();
956 vector<docstring> vec(styles.size());
957 for (size_t i = 0; i != vec.size(); ++i) {
958 style = styles[i].cmd;
959 vec[i] = getLabel(keys, buf, style, false, max_size, before, after, dialog);
966 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
968 bimap_.insert(info.begin(), info.end());
969 field_names_.insert(info.field_names_.begin(), info.field_names_.end());
970 entry_types_.insert(info.entry_types_.begin(), info.entry_types_.end());
976 // used in xhtml to sort a list of BibTeXInfo objects
977 bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
979 docstring const lauth = lhs->getAbbreviatedAuthor();
980 docstring const rauth = rhs->getAbbreviatedAuthor();
981 docstring const lyear = lhs->getYear();
982 docstring const ryear = rhs->getYear();
983 docstring const ltitl = lhs->operator[]("title");
984 docstring const rtitl = rhs->operator[]("title");
985 return (lauth < rauth)
986 || (lauth == rauth && lyear < ryear)
987 || (lauth == rauth && lyear == ryear && ltitl < rtitl);
993 void BiblioInfo::collectCitedEntries(Buffer const & buf)
995 cited_entries_.clear();
996 // We are going to collect all the citation keys used in the document,
997 // getting them from the TOC.
998 // FIXME We may want to collect these differently, in the first case,
999 // so that we might have them in order of appearance.
1000 set<docstring> citekeys;
1001 shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
1002 Toc::const_iterator it = toc->begin();
1003 Toc::const_iterator const en = toc->end();
1004 for (; it != en; ++it) {
1005 if (it->str().empty())
1007 vector<docstring> const keys = getVectorFromString(it->str());
1008 citekeys.insert(keys.begin(), keys.end());
1010 if (citekeys.empty())
1013 // We have a set of the keys used in this document.
1014 // We will now convert it to a list of the BibTeXInfo objects used in
1016 vector<BibTeXInfo const *> bi;
1017 set<docstring>::const_iterator cit = citekeys.begin();
1018 set<docstring>::const_iterator const cen = citekeys.end();
1019 for (; cit != cen; ++cit) {
1020 BiblioInfo::const_iterator const bt = find(*cit);
1021 if (bt == end() || !bt->second.isBibTeX())
1023 bi.push_back(&(bt->second));
1026 sort(bi.begin(), bi.end(), lSorter);
1028 // Now we can write the sorted keys
1029 vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
1030 vector<BibTeXInfo const *>::const_iterator ben = bi.end();
1031 for (; bit != ben; ++bit)
1032 cited_entries_.push_back((*bit)->key());
1036 void BiblioInfo::makeCitationLabels(Buffer const & buf)
1038 collectCitedEntries(buf);
1039 CiteEngineType const engine_type = buf.params().citeEngineType();
1040 bool const numbers = (engine_type & ENGINE_TYPE_NUMERICAL);
1044 // used to remember the last one we saw
1045 // we'll be comparing entries to see if we need to add
1046 // modifiers, like "1984a"
1047 map<docstring, BibTeXInfo>::iterator last;
1049 vector<docstring>::const_iterator it = cited_entries_.begin();
1050 vector<docstring>::const_iterator const en = cited_entries_.end();
1051 for (; it != en; ++it) {
1052 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1053 // this shouldn't happen, but...
1054 if (biit == bimap_.end())
1055 // ...fail gracefully, anyway.
1057 BibTeXInfo & entry = biit->second;
1059 docstring const num = convert<docstring>(++keynumber);
1060 entry.setCiteNumber(num);
1062 // coverity complains about our derefercing the iterator last,
1063 // which was not initialized above. but it does get initialized
1064 // after the first time through the loop, which is the point of
1066 // coverity[FORWARD_NULL]
1067 if (it != cited_entries_.begin()
1068 && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
1069 // we access the year via getYear() so as to get it from the xref,
1070 // if we need to do so
1071 && getYear(entry.key()) == getYear(last->second.key())) {
1072 if (modifier == 0) {
1073 // so the last one should have been 'a'
1074 last->second.setModifier('a');
1076 } else if (modifier == 'z')
1083 entry.setModifier(modifier);
1084 // remember the last one
1089 it = cited_entries_.begin();
1090 for (; it != en; ++it) {
1091 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1092 // this shouldn't happen, but...
1093 if (biit == bimap_.end())
1094 // ...fail gracefully, anyway.
1096 BibTeXInfo & entry = biit->second;
1098 entry.label(entry.citeNumber());
1100 docstring const auth = entry.getAbbreviatedAuthor(buf, false);
1101 // we do it this way so as to access the xref, if necessary
1102 // note that this also gives us the modifier
1103 docstring const year = getYear(*it, buf, true);
1104 if (!auth.empty() && !year.empty())
1105 entry.label(auth + ' ' + year);
1107 entry.label(entry.key());
1113 //////////////////////////////////////////////////////////////////////
1117 //////////////////////////////////////////////////////////////////////
1120 CitationStyle citationStyleFromString(string const & command)
1123 if (command.empty())
1126 string cmd = command;
1127 if (cmd[0] == 'C') {
1128 cs.forceUpperCase = true;
1132 size_t const n = cmd.size() - 1;
1133 if (cmd[n] == '*') {
1134 cs.fullAuthorList = true;
1135 cmd = cmd.substr(0, n);
1143 string citationStyleToString(const CitationStyle & cs)
1145 string cmd = cs.cmd;
1146 if (cs.forceUpperCase)
1147 cmd[0] = uppercase(cmd[0]);
1148 if (cs.fullAuthorList)