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(
259 Buffer const * buf, bool jurabib_style) const
262 docstring const opt = label();
267 docstring const remainder = trim(split(opt, authors, '('));
268 if (remainder.empty())
269 // in this case, we didn't find a "(",
270 // so we don't have author (year)
275 docstring author = operator[]("author");
276 if (author.empty()) {
277 author = operator[]("editor");
282 // FIXME Move this to a separate routine that can
283 // be called from elsewhere.
285 // OK, we've got some names. Let's format them.
286 // Try to split the author list on " and "
287 vector<docstring> const authors =
288 getVectorFromString(author, from_ascii(" and "));
290 if (jurabib_style && (authors.size() == 2 || authors.size() == 3)) {
291 docstring shortauthor = familyName(authors[0])
292 + "/" + familyName(authors[1]);
293 if (authors.size() == 3)
294 shortauthor += "/" + familyName(authors[2]);
295 return convertLaTeXCommands(shortauthor);
298 docstring retval = familyName(authors[0]);
300 if (authors.size() == 2 && authors[1] != "others") {
301 docstring const dformat = buf ?
302 buf->B_("%1$s and %2$s") : from_ascii("%1$s and %2$s");
303 retval = bformat(dformat, familyName(authors[0]), familyName(authors[1]));
304 } else if (authors.size() >= 2) {
305 // we get here either if the author list is longer than two names
306 // or if the second 'name' is "others". we do the same thing either
308 docstring const dformat = buf ?
309 buf->B_("%1$s et al.") : from_ascii("%1$s et al.");
310 retval = bformat(dformat, familyName(authors[0]));
313 return convertLaTeXCommands(retval);
317 docstring const BibTeXInfo::getYear() const
320 // first try legacy year field
321 docstring year = operator[]("year");
324 // now try biblatex's date field
325 year = operator[]("date");
326 // Format is [-]YYYY-MM-DD*/[-]YYYY-MM-DD*
327 // We only want the years.
328 static regex const yreg("[-]?([\\d]{4}).*");
329 static regex const ereg(".*/([\\d]{4}).*");
331 string const date = to_utf8(year);
332 regex_match(date, sm, yreg);
333 year = from_ascii(sm[1]);
334 // check for an endyear
335 if (regex_match(date, sm, ereg))
336 year += char_type(0x2013) + from_ascii(sm[1]);
340 docstring const opt = label();
345 docstring tmp = split(opt, authors, '(');
347 // we don't have author (year)
350 tmp = split(tmp, year, ')');
355 docstring const BibTeXInfo::getXRef() const
359 return operator[]("crossref");
365 docstring parseOptions(docstring const & format, string & optkey,
366 docstring & ifpart, docstring & elsepart);
368 // Calls parseOptions to deal with an embedded option, such as:
369 // {%number%[[, no.~%number%]]}
370 // which must appear at the start of format. ifelsepart gets the
371 // whole of the option, and we return what's left after the option.
372 // we return format if there is an error.
373 docstring parseEmbeddedOption(docstring const & format, docstring & ifelsepart)
375 LASSERT(format[0] == '{' && format[1] == '%', return format);
379 docstring const rest = parseOptions(format, optkey, ifpart, elsepart);
380 if (format == rest) { // parse error
381 LYXERR0("ERROR! Couldn't parse `" << format <<"'.");
384 LASSERT(rest.size() <= format.size(),
385 { ifelsepart = docstring(); return format; });
386 ifelsepart = format.substr(0, format.size() - rest.size());
391 // Gets a "clause" from a format string, where the clause is
392 // delimited by '[[' and ']]'. Returns what is left after the
393 // clause is removed, and returns format if there is an error.
394 docstring getClause(docstring const & format, docstring & clause)
396 docstring fmt = format;
399 // we'll remove characters from the front of fmt as we
401 while (!fmt.empty()) {
402 if (fmt[0] == ']' && fmt.size() > 1 && fmt[1] == ']') {
407 // check for an embedded option
408 if (fmt[0] == '{' && fmt.size() > 1 && fmt[1] == '%') {
410 docstring const rest = parseEmbeddedOption(fmt, part);
412 LYXERR0("ERROR! Couldn't parse embedded option in `" << format <<"'.");
417 } else { // it's just a normal character
426 // parse an options string, which must appear at the start of the
427 // format parameter. puts the parsed bits in optkey, ifpart, and
428 // elsepart and returns what's left after the option is removed.
429 // if there's an error, it returns format itself.
430 docstring parseOptions(docstring const & format, string & optkey,
431 docstring & ifpart, docstring & elsepart)
433 LASSERT(format[0] == '{' && format[1] == '%', return format);
435 docstring fmt = format.substr(2);
436 size_t pos = fmt.find('%'); // end of key
437 if (pos == string::npos) {
438 LYXERR0("Error parsing `" << format <<"'. Can't find end of key.");
441 optkey = to_utf8(fmt.substr(0, pos));
442 fmt = fmt.substr(pos + 1);
443 // [[format]] should be next
444 if (fmt[0] != '[' || fmt[1] != '[') {
445 LYXERR0("Error parsing `" << format <<"'. Can't find '[[' after key.");
449 docstring curfmt = fmt;
450 fmt = getClause(curfmt, ifpart);
452 LYXERR0("Error parsing `" << format <<"'. Couldn't get if clause.");
456 if (fmt[0] == '}') // we're done, no else clause
457 return fmt.substr(1);
459 // else part should follow
460 if (fmt[0] != '[' || fmt[1] != '[') {
461 LYXERR0("Error parsing `" << format <<"'. Can't find else clause.");
466 fmt = getClause(curfmt, elsepart);
468 if (fmt == curfmt || fmt[0] != '}') {
469 LYXERR0("Error parsing `" << format <<"'. Can't find end of option.");
472 return fmt.substr(1);
479 Bug #9131 revealed an oddity in how we are generating citation information
480 when more than one key is given. We end up building a longer and longer format
481 string as we go, which we then have to re-parse, over and over and over again,
482 rather than generating the information for the individual keys and then putting
483 all of that together. We do that to deal with the way separators work, from what
484 I can tell, but it still feels like a hack. Fixing this would require quite a
485 bit of work, however.
487 docstring BibTeXInfo::expandFormat(docstring const & format,
488 BibTeXInfo const * const xref, int & counter, Buffer const & buf,
489 docstring before, docstring after, docstring dialog, bool next) const
491 // incorrect use of macros could put us in an infinite loop
492 static int const max_passes = 5000;
493 // the use of overly large keys can lead to performance problems, due
494 // to eventual attempts to convert LaTeX macros to unicode. See bug
495 // #8944. This is perhaps not the best solution, but it will have to
497 static size_t const max_keysize = 128;
498 odocstringstream ret; // return value
500 bool scanning_key = false;
501 bool scanning_rich = false;
503 CiteEngineType const engine_type = buf.params().citeEngineType();
504 docstring fmt = format;
505 // we'll remove characters from the front of fmt as we
507 while (!fmt.empty()) {
508 if (counter > max_passes) {
509 LYXERR0("Recursion limit reached while parsing `"
514 char_type thischar = fmt[0];
515 if (thischar == '%') {
516 // beginning or end of key
519 scanning_key = false;
520 // so we replace the key with its value, which may be empty
524 buf.params().documentClass().getCiteMacro(engine_type, key);
525 fmt = from_utf8(val) + fmt.substr(1);
528 } else if (key[0] == '_') {
529 // a translatable bit
531 buf.params().documentClass().getCiteMacro(engine_type, key);
532 docstring const trans =
533 translateIfPossible(from_utf8(val), buf.params().language->code());
536 docstring const val =
537 getValueForKey(key, buf, before, after, dialog, xref, max_keysize);
539 ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
542 ret << from_ascii("{!</span>!}");
550 else if (thischar == '{') {
551 // beginning of option?
553 LYXERR0("ERROR: Found `{' when scanning key in `" << format << "'.");
556 if (fmt.size() > 1) {
558 // it is the beginning of an optional format
562 docstring const newfmt =
563 parseOptions(fmt, optkey, ifpart, elsepart);
564 if (newfmt == fmt) // parse error
567 docstring const val =
568 getValueForKey(optkey, buf, before, after, dialog, xref);
569 if (optkey == "next" && next)
570 ret << ifpart; // without expansion
571 else if (!val.empty()) {
573 ret << expandFormat(ifpart, xref, newcounter, buf,
574 before, after, dialog, next);
575 } else if (!elsepart.empty()) {
577 ret << expandFormat(elsepart, xref, newcounter, buf,
578 before, after, dialog, next);
580 // fmt will have been shortened for us already
584 // beginning of rich text
585 scanning_rich = true;
587 ret << from_ascii("{!");
591 // we are here if '{' was not followed by % or !.
592 // So it's just a character.
595 else if (scanning_rich && thischar == '!'
596 && fmt.size() > 1 && fmt[1] == '}') {
598 scanning_rich = false;
600 ret << from_ascii("!}");
603 else if (scanning_key)
604 key += char(thischar);
608 } catch (EncodingException & /* e */) {
609 LYXERR0("Uncodable character '" << docstring(1, thischar) << " in citation label!");
615 LYXERR0("Never found end of key in `" << format << "'!");
619 LYXERR0("Never found end of rich text in `" << format << "'!");
626 docstring const & BibTeXInfo::getInfo(BibTeXInfo const * const xref,
627 Buffer const & buf, bool richtext) const
629 if (!richtext && !info_.empty())
631 if (richtext && !info_richtext_.empty())
632 return info_richtext_;
635 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
640 CiteEngineType const engine_type = buf.params().citeEngineType();
641 DocumentClass const & dc = buf.params().documentClass();
642 docstring const & format =
643 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
645 info_ = expandFormat(format, xref, counter, buf,
646 docstring(), docstring(), docstring(), false);
649 // this probably shouldn't happen
654 info_richtext_ = convertLaTeXCommands(processRichtext(info_, true));
655 return info_richtext_;
658 info_ = convertLaTeXCommands(processRichtext(info_, false));
663 docstring const BibTeXInfo::getLabel(BibTeXInfo const * const xref,
664 Buffer const & buf, docstring const & format, bool richtext,
665 docstring const & before, docstring const & after,
666 docstring const & dialog, bool next) const
671 loclabel = expandFormat(format, xref, counter, buf,
672 before, after, dialog, next);
674 if (!loclabel.empty() && !next) {
675 loclabel = processRichtext(loclabel, richtext);
676 loclabel = convertLaTeXCommands(loclabel);
683 docstring const & BibTeXInfo::operator[](docstring const & field) const
685 BibTeXInfo::const_iterator it = find(field);
688 static docstring const empty_value = docstring();
693 docstring const & BibTeXInfo::operator[](string const & field) const
695 return operator[](from_ascii(field));
699 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
700 docstring const & before, docstring const & after, docstring const & dialog,
701 BibTeXInfo const * const xref, size_t maxsize) const
703 // anything less is pointless
704 LASSERT(maxsize >= 16, maxsize = 16);
706 bool cleanit = false;
707 if (prefixIs(oldkey, "clean:")) {
708 key = oldkey.substr(6);
712 docstring ret = operator[](key);
713 if (ret.empty() && xref)
717 // FIXME: dialog, textbefore and textafter have nothing to do with this
720 else if (key == "entrytype")
722 else if (key == "key")
724 else if (key == "label")
726 else if (key == "modifier" && modifier_ != 0)
728 else if (key == "numericallabel")
730 else if (key == "abbrvauthor")
731 // Special key to provide abbreviated author names.
732 ret = getAbbreviatedAuthor(&buf, false);
733 else if (key == "shortauthor")
734 // When shortauthor is not defined, jurabib automatically
735 // provides jurabib-style abbreviated author names. We do
737 ret = getAbbreviatedAuthor(&buf, true);
738 else if (key == "shorttitle") {
739 // When shorttitle is not defined, jurabib uses for `article'
740 // and `periodical' entries the form `journal volume [year]'
741 // and for other types of entries it uses the `title' field.
742 if (entry_type_ == "article" || entry_type_ == "periodical")
743 ret = operator[]("journal") + " " + operator[]("volume")
744 + " [" + operator[]("year") + "]";
746 ret = operator[]("title");
747 } else if (key == "bibentry") {
748 // Special key to provide the full bibliography entry: see getInfo()
749 CiteEngineType const engine_type = buf.params().citeEngineType();
750 DocumentClass const & dc = buf.params().documentClass();
751 docstring const & format =
752 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
754 ret = expandFormat(format, xref, counter, buf,
755 docstring(), docstring(), docstring(), false);
756 } else if (key == "textbefore")
758 else if (key == "textafter")
760 else if (key == "year")
765 ret = html::cleanAttr(ret);
767 // make sure it is not too big
768 support::truncateWithEllipsis(ret, maxsize);
773 //////////////////////////////////////////////////////////////////////
777 //////////////////////////////////////////////////////////////////////
781 // A functor for use with sort, leading to case insensitive sorting
782 class compareNoCase: public binary_function<docstring, docstring, bool>
785 bool operator()(docstring const & s1, docstring const & s2) const {
786 return compare_no_case(s1, s2) < 0;
793 vector<docstring> const BiblioInfo::getKeys() const
795 vector<docstring> bibkeys;
796 BiblioInfo::const_iterator it = begin();
797 for (; it != end(); ++it)
798 bibkeys.push_back(it->first);
799 sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
804 vector<docstring> const BiblioInfo::getFields() const
806 vector<docstring> bibfields;
807 set<docstring>::const_iterator it = field_names_.begin();
808 set<docstring>::const_iterator end = field_names_.end();
809 for (; it != end; ++it)
810 bibfields.push_back(*it);
811 sort(bibfields.begin(), bibfields.end());
816 vector<docstring> const BiblioInfo::getEntries() const
818 vector<docstring> bibentries;
819 set<docstring>::const_iterator it = entry_types_.begin();
820 set<docstring>::const_iterator end = entry_types_.end();
821 for (; it != end; ++it)
822 bibentries.push_back(*it);
823 sort(bibentries.begin(), bibentries.end());
828 docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key, Buffer const & buf) const
830 BiblioInfo::const_iterator it = find(key);
833 BibTeXInfo const & data = it->second;
834 return data.getAbbreviatedAuthor(&buf, false);
838 docstring const BiblioInfo::getCiteNumber(docstring const & key) const
840 BiblioInfo::const_iterator it = find(key);
843 BibTeXInfo const & data = it->second;
844 return data.citeNumber();
848 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
850 BiblioInfo::const_iterator it = find(key);
853 BibTeXInfo const & data = it->second;
854 docstring year = data.getYear();
856 // let's try the crossref
857 docstring const xref = data.getXRef();
861 BiblioInfo::const_iterator const xrefit = find(xref);
865 BibTeXInfo const & xref_data = xrefit->second;
866 year = xref_data.getYear();
868 if (use_modifier && data.modifier() != 0)
869 year += data.modifier();
874 docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, bool use_modifier) const
876 docstring const year = getYear(key, use_modifier);
878 return buf.B_("No year");
883 docstring const BiblioInfo::getInfo(docstring const & key,
884 Buffer const & buf, bool richtext) const
886 BiblioInfo::const_iterator it = find(key);
888 return docstring(_("Bibliography entry not found!"));
889 BibTeXInfo const & data = it->second;
890 BibTeXInfo const * xrefptr = 0;
891 docstring const xref = data.getXRef();
893 BiblioInfo::const_iterator const xrefit = find(xref);
895 xrefptr = &(xrefit->second);
897 return data.getInfo(xrefptr, buf, richtext);
901 docstring const BiblioInfo::getLabel(vector<docstring> keys,
902 Buffer const & buf, string const & style, bool for_xhtml,
903 size_t max_size, docstring const & before, docstring const & after,
904 docstring const & dialog) const
906 // shorter makes no sense
907 LASSERT(max_size >= 16, max_size = 16);
909 // we can't display more than 10 of these, anyway
910 bool const too_many_keys = keys.size() > 10;
914 CiteEngineType const engine_type = buf.params().citeEngineType();
915 DocumentClass const & dc = buf.params().documentClass();
916 docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
917 docstring ret = format;
918 vector<docstring>::const_iterator key = keys.begin();
919 vector<docstring>::const_iterator ken = keys.end();
920 for (; key != ken; ++key) {
921 BiblioInfo::const_iterator it = find(*key);
922 BibTeXInfo empty_data;
923 empty_data.key(*key);
924 BibTeXInfo & data = empty_data;
925 BibTeXInfo const * xrefptr = 0;
928 docstring const xref = data.getXRef();
930 BiblioInfo::const_iterator const xrefit = find(xref);
932 xrefptr = &(xrefit->second);
935 ret = data.getLabel(xrefptr, buf, ret, for_xhtml,
936 before, after, dialog, key + 1 != ken);
940 ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
941 support::truncateWithEllipsis(ret, max_size);
946 bool BiblioInfo::isBibtex(docstring const & key) const
949 split(key, key1, ',');
950 BiblioInfo::const_iterator it = find(key1);
953 return it->second.isBibTeX();
957 vector<docstring> const BiblioInfo::getCiteStrings(
958 vector<docstring> const & keys, vector<CitationStyle> const & styles,
959 Buffer const & buf, docstring const & before,
960 docstring const & after, docstring const & dialog, size_t max_size) const
963 return vector<docstring>();
966 vector<docstring> vec(styles.size());
967 for (size_t i = 0; i != vec.size(); ++i) {
968 style = styles[i].cmd;
969 vec[i] = getLabel(keys, buf, style, false, max_size, before, after, dialog);
976 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
978 bimap_.insert(info.begin(), info.end());
979 field_names_.insert(info.field_names_.begin(), info.field_names_.end());
980 entry_types_.insert(info.entry_types_.begin(), info.entry_types_.end());
986 // used in xhtml to sort a list of BibTeXInfo objects
987 bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
989 docstring const lauth = lhs->getAbbreviatedAuthor();
990 docstring const rauth = rhs->getAbbreviatedAuthor();
991 docstring const lyear = lhs->getYear();
992 docstring const ryear = rhs->getYear();
993 docstring const ltitl = lhs->operator[]("title");
994 docstring const rtitl = rhs->operator[]("title");
995 return (lauth < rauth)
996 || (lauth == rauth && lyear < ryear)
997 || (lauth == rauth && lyear == ryear && ltitl < rtitl);
1003 void BiblioInfo::collectCitedEntries(Buffer const & buf)
1005 cited_entries_.clear();
1006 // We are going to collect all the citation keys used in the document,
1007 // getting them from the TOC.
1008 // FIXME We may want to collect these differently, in the first case,
1009 // so that we might have them in order of appearance.
1010 set<docstring> citekeys;
1011 shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
1012 Toc::const_iterator it = toc->begin();
1013 Toc::const_iterator const en = toc->end();
1014 for (; it != en; ++it) {
1015 if (it->str().empty())
1017 vector<docstring> const keys = getVectorFromString(it->str());
1018 citekeys.insert(keys.begin(), keys.end());
1020 if (citekeys.empty())
1023 // We have a set of the keys used in this document.
1024 // We will now convert it to a list of the BibTeXInfo objects used in
1026 vector<BibTeXInfo const *> bi;
1027 set<docstring>::const_iterator cit = citekeys.begin();
1028 set<docstring>::const_iterator const cen = citekeys.end();
1029 for (; cit != cen; ++cit) {
1030 BiblioInfo::const_iterator const bt = find(*cit);
1031 if (bt == end() || !bt->second.isBibTeX())
1033 bi.push_back(&(bt->second));
1036 sort(bi.begin(), bi.end(), lSorter);
1038 // Now we can write the sorted keys
1039 vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
1040 vector<BibTeXInfo const *>::const_iterator ben = bi.end();
1041 for (; bit != ben; ++bit)
1042 cited_entries_.push_back((*bit)->key());
1046 void BiblioInfo::makeCitationLabels(Buffer const & buf)
1048 collectCitedEntries(buf);
1049 CiteEngineType const engine_type = buf.params().citeEngineType();
1050 bool const numbers = (engine_type & ENGINE_TYPE_NUMERICAL);
1054 // used to remember the last one we saw
1055 // we'll be comparing entries to see if we need to add
1056 // modifiers, like "1984a"
1057 map<docstring, BibTeXInfo>::iterator last;
1059 vector<docstring>::const_iterator it = cited_entries_.begin();
1060 vector<docstring>::const_iterator const en = cited_entries_.end();
1061 for (; it != en; ++it) {
1062 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1063 // this shouldn't happen, but...
1064 if (biit == bimap_.end())
1065 // ...fail gracefully, anyway.
1067 BibTeXInfo & entry = biit->second;
1069 docstring const num = convert<docstring>(++keynumber);
1070 entry.setCiteNumber(num);
1072 // coverity complains about our derefercing the iterator last,
1073 // which was not initialized above. but it does get initialized
1074 // after the first time through the loop, which is the point of
1076 // coverity[FORWARD_NULL]
1077 if (it != cited_entries_.begin()
1078 && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
1079 // we access the year via getYear() so as to get it from the xref,
1080 // if we need to do so
1081 && getYear(entry.key()) == getYear(last->second.key())) {
1082 if (modifier == 0) {
1083 // so the last one should have been 'a'
1084 last->second.setModifier('a');
1086 } else if (modifier == 'z')
1093 entry.setModifier(modifier);
1094 // remember the last one
1099 it = cited_entries_.begin();
1100 for (; it != en; ++it) {
1101 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1102 // this shouldn't happen, but...
1103 if (biit == bimap_.end())
1104 // ...fail gracefully, anyway.
1106 BibTeXInfo & entry = biit->second;
1108 entry.label(entry.citeNumber());
1110 docstring const auth = entry.getAbbreviatedAuthor(&buf, false);
1111 // we do it this way so as to access the xref, if necessary
1112 // note that this also gives us the modifier
1113 docstring const year = getYear(*it, buf, true);
1114 if (!auth.empty() && !year.empty())
1115 entry.label(auth + ' ' + year);
1117 entry.label(entry.key());
1123 //////////////////////////////////////////////////////////////////////
1127 //////////////////////////////////////////////////////////////////////
1130 CitationStyle citationStyleFromString(string const & command)
1133 if (command.empty())
1136 string cmd = command;
1137 if (cmd[0] == 'C') {
1138 cs.forceUpperCase = true;
1142 size_t const n = cmd.size() - 1;
1143 if (cmd[n] == '*') {
1144 cs.fullAuthorList = true;
1145 cmd = cmd.substr(0, n);
1153 string citationStyleToString(const CitationStyle & cs)
1155 string cmd = cs.cmd;
1156 if (cs.forceUpperCase)
1157 cmd[0] = uppercase(cmd[0]);
1158 if (cs.fullAuthorList)