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 return operator[]("year");
322 docstring const opt = label();
327 docstring tmp = split(opt, authors, '(');
329 // we don't have author (year)
332 tmp = split(tmp, year, ')');
337 docstring const BibTeXInfo::getXRef() const
341 return operator[]("crossref");
347 docstring parseOptions(docstring const & format, string & optkey,
348 docstring & ifpart, docstring & elsepart);
350 // Calls parseOptions to deal with an embedded option, such as:
351 // {%number%[[, no.~%number%]]}
352 // which must appear at the start of format. ifelsepart gets the
353 // whole of the option, and we return what's left after the option.
354 // we return format if there is an error.
355 docstring parseEmbeddedOption(docstring const & format, docstring & ifelsepart)
357 LASSERT(format[0] == '{' && format[1] == '%', return format);
361 docstring const rest = parseOptions(format, optkey, ifpart, elsepart);
362 if (format == rest) { // parse error
363 LYXERR0("ERROR! Couldn't parse `" << format <<"'.");
366 LASSERT(rest.size() <= format.size(),
367 { ifelsepart = docstring(); return format; });
368 ifelsepart = format.substr(0, format.size() - rest.size());
373 // Gets a "clause" from a format string, where the clause is
374 // delimited by '[[' and ']]'. Returns what is left after the
375 // clause is removed, and returns format if there is an error.
376 docstring getClause(docstring const & format, docstring & clause)
378 docstring fmt = format;
381 // we'll remove characters from the front of fmt as we
383 while (!fmt.empty()) {
384 if (fmt[0] == ']' && fmt.size() > 1 && fmt[1] == ']') {
389 // check for an embedded option
390 if (fmt[0] == '{' && fmt.size() > 1 && fmt[1] == '%') {
392 docstring const rest = parseEmbeddedOption(fmt, part);
394 LYXERR0("ERROR! Couldn't parse embedded option in `" << format <<"'.");
399 } else { // it's just a normal character
408 // parse an options string, which must appear at the start of the
409 // format parameter. puts the parsed bits in optkey, ifpart, and
410 // elsepart and returns what's left after the option is removed.
411 // if there's an error, it returns format itself.
412 docstring parseOptions(docstring const & format, string & optkey,
413 docstring & ifpart, docstring & elsepart)
415 LASSERT(format[0] == '{' && format[1] == '%', return format);
417 docstring fmt = format.substr(2);
418 size_t pos = fmt.find('%'); // end of key
419 if (pos == string::npos) {
420 LYXERR0("Error parsing `" << format <<"'. Can't find end of key.");
423 optkey = to_utf8(fmt.substr(0, pos));
424 fmt = fmt.substr(pos + 1);
425 // [[format]] should be next
426 if (fmt[0] != '[' || fmt[1] != '[') {
427 LYXERR0("Error parsing `" << format <<"'. Can't find '[[' after key.");
431 docstring curfmt = fmt;
432 fmt = getClause(curfmt, ifpart);
434 LYXERR0("Error parsing `" << format <<"'. Couldn't get if clause.");
438 if (fmt[0] == '}') // we're done, no else clause
439 return fmt.substr(1);
441 // else part should follow
442 if (fmt[0] != '[' || fmt[1] != '[') {
443 LYXERR0("Error parsing `" << format <<"'. Can't find else clause.");
448 fmt = getClause(curfmt, elsepart);
450 if (fmt == curfmt || fmt[0] != '}') {
451 LYXERR0("Error parsing `" << format <<"'. Can't find end of option.");
454 return fmt.substr(1);
461 Bug #9131 revealed an oddity in how we are generating citation information
462 when more than one key is given. We end up building a longer and longer format
463 string as we go, which we then have to re-parse, over and over and over again,
464 rather than generating the information for the individual keys and then putting
465 all of that together. We do that to deal with the way separators work, from what
466 I can tell, but it still feels like a hack. Fixing this would require quite a
467 bit of work, however.
469 docstring BibTeXInfo::expandFormat(docstring const & format,
470 BibTeXInfo const * const xref, int & counter, Buffer const & buf,
471 docstring before, docstring after, docstring dialog, bool next) const
473 // incorrect use of macros could put us in an infinite loop
474 static int const max_passes = 5000;
475 // the use of overly large keys can lead to performance problems, due
476 // to eventual attempts to convert LaTeX macros to unicode. See bug
477 // #8944. This is perhaps not the best solution, but it will have to
479 static size_t const max_keysize = 128;
480 odocstringstream ret; // return value
482 bool scanning_key = false;
483 bool scanning_rich = false;
485 CiteEngineType const engine_type = buf.params().citeEngineType();
486 docstring fmt = format;
487 // we'll remove characters from the front of fmt as we
489 while (!fmt.empty()) {
490 if (counter > max_passes) {
491 LYXERR0("Recursion limit reached while parsing `"
496 char_type thischar = fmt[0];
497 if (thischar == '%') {
498 // beginning or end of key
501 scanning_key = false;
502 // so we replace the key with its value, which may be empty
506 buf.params().documentClass().getCiteMacro(engine_type, key);
507 fmt = from_utf8(val) + fmt.substr(1);
510 } else if (key[0] == '_') {
511 // a translatable bit
513 buf.params().documentClass().getCiteMacro(engine_type, key);
514 docstring const trans =
515 translateIfPossible(from_utf8(val), buf.params().language->code());
518 docstring const val =
519 getValueForKey(key, buf, before, after, dialog, xref, max_keysize);
521 ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
524 ret << from_ascii("{!</span>!}");
532 else if (thischar == '{') {
533 // beginning of option?
535 LYXERR0("ERROR: Found `{' when scanning key in `" << format << "'.");
538 if (fmt.size() > 1) {
540 // it is the beginning of an optional format
544 docstring const newfmt =
545 parseOptions(fmt, optkey, ifpart, elsepart);
546 if (newfmt == fmt) // parse error
549 docstring const val =
550 getValueForKey(optkey, buf, before, after, dialog, xref);
551 if (optkey == "next" && next)
552 ret << ifpart; // without expansion
553 else if (!val.empty()) {
555 ret << expandFormat(ifpart, xref, newcounter, buf,
556 before, after, dialog, next);
557 } else if (!elsepart.empty()) {
559 ret << expandFormat(elsepart, xref, newcounter, buf,
560 before, after, dialog, next);
562 // fmt will have been shortened for us already
566 // beginning of rich text
567 scanning_rich = true;
569 ret << from_ascii("{!");
573 // we are here if '{' was not followed by % or !.
574 // So it's just a character.
577 else if (scanning_rich && thischar == '!'
578 && fmt.size() > 1 && fmt[1] == '}') {
580 scanning_rich = false;
582 ret << from_ascii("!}");
585 else if (scanning_key)
586 key += char(thischar);
590 } catch (EncodingException & /* e */) {
591 LYXERR0("Uncodable character '" << docstring(1, thischar) << " in citation label!");
597 LYXERR0("Never found end of key in `" << format << "'!");
601 LYXERR0("Never found end of rich text in `" << format << "'!");
608 docstring const & BibTeXInfo::getInfo(BibTeXInfo const * const xref,
609 Buffer const & buf, bool richtext) const
611 if (!richtext && !info_.empty())
613 if (richtext && !info_richtext_.empty())
614 return info_richtext_;
617 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
622 CiteEngineType const engine_type = buf.params().citeEngineType();
623 DocumentClass const & dc = buf.params().documentClass();
624 docstring const & format =
625 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
627 info_ = expandFormat(format, xref, counter, buf,
628 docstring(), docstring(), docstring(), false);
631 // this probably shouldn't happen
636 info_richtext_ = convertLaTeXCommands(processRichtext(info_, true));
637 return info_richtext_;
640 info_ = convertLaTeXCommands(processRichtext(info_, false));
645 docstring const BibTeXInfo::getLabel(BibTeXInfo const * const xref,
646 Buffer const & buf, docstring const & format, bool richtext,
647 docstring const & before, docstring const & after,
648 docstring const & dialog, bool next) const
653 loclabel = expandFormat(format, xref, counter, buf,
654 before, after, dialog, next);
656 if (!loclabel.empty() && !next) {
657 loclabel = processRichtext(loclabel, richtext);
658 loclabel = convertLaTeXCommands(loclabel);
665 docstring const & BibTeXInfo::operator[](docstring const & field) const
667 BibTeXInfo::const_iterator it = find(field);
670 static docstring const empty_value = docstring();
675 docstring const & BibTeXInfo::operator[](string const & field) const
677 return operator[](from_ascii(field));
681 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
682 docstring const & before, docstring const & after, docstring const & dialog,
683 BibTeXInfo const * const xref, size_t maxsize) const
685 // anything less is pointless
686 LASSERT(maxsize >= 16, maxsize = 16);
688 bool cleanit = false;
689 if (prefixIs(oldkey, "clean:")) {
690 key = oldkey.substr(6);
694 docstring ret = operator[](key);
695 if (ret.empty() && xref)
699 // FIXME: dialog, textbefore and textafter have nothing to do with this
702 else if (key == "entrytype")
704 else if (key == "key")
706 else if (key == "label")
708 else if (key == "modifier" && modifier_ != 0)
710 else if (key == "numericallabel")
712 else if (key == "abbrvauthor")
713 // Special key to provide abbreviated author names.
714 ret = getAbbreviatedAuthor(&buf, false);
715 else if (key == "shortauthor")
716 // When shortauthor is not defined, jurabib automatically
717 // provides jurabib-style abbreviated author names. We do
719 ret = getAbbreviatedAuthor(&buf, true);
720 else if (key == "shorttitle") {
721 // When shorttitle is not defined, jurabib uses for `article'
722 // and `periodical' entries the form `journal volume [year]'
723 // and for other types of entries it uses the `title' field.
724 if (entry_type_ == "article" || entry_type_ == "periodical")
725 ret = operator[]("journal") + " " + operator[]("volume")
726 + " [" + operator[]("year") + "]";
728 ret = operator[]("title");
729 } else if (key == "bibentry") {
730 // Special key to provide the full bibliography entry: see getInfo()
731 CiteEngineType const engine_type = buf.params().citeEngineType();
732 DocumentClass const & dc = buf.params().documentClass();
733 docstring const & format =
734 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
736 ret = expandFormat(format, xref, counter, buf,
737 docstring(), docstring(), docstring(), false);
738 } else if (key == "textbefore")
740 else if (key == "textafter")
742 else if (key == "year")
747 ret = html::cleanAttr(ret);
749 // make sure it is not too big
750 support::truncateWithEllipsis(ret, maxsize);
755 //////////////////////////////////////////////////////////////////////
759 //////////////////////////////////////////////////////////////////////
763 // A functor for use with sort, leading to case insensitive sorting
764 class compareNoCase: public binary_function<docstring, docstring, bool>
767 bool operator()(docstring const & s1, docstring const & s2) const {
768 return compare_no_case(s1, s2) < 0;
775 vector<docstring> const BiblioInfo::getKeys() const
777 vector<docstring> bibkeys;
778 BiblioInfo::const_iterator it = begin();
779 for (; it != end(); ++it)
780 bibkeys.push_back(it->first);
781 sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
786 vector<docstring> const BiblioInfo::getFields() const
788 vector<docstring> bibfields;
789 set<docstring>::const_iterator it = field_names_.begin();
790 set<docstring>::const_iterator end = field_names_.end();
791 for (; it != end; ++it)
792 bibfields.push_back(*it);
793 sort(bibfields.begin(), bibfields.end());
798 vector<docstring> const BiblioInfo::getEntries() const
800 vector<docstring> bibentries;
801 set<docstring>::const_iterator it = entry_types_.begin();
802 set<docstring>::const_iterator end = entry_types_.end();
803 for (; it != end; ++it)
804 bibentries.push_back(*it);
805 sort(bibentries.begin(), bibentries.end());
810 docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key, Buffer const & buf) const
812 BiblioInfo::const_iterator it = find(key);
815 BibTeXInfo const & data = it->second;
816 return data.getAbbreviatedAuthor(&buf, false);
820 docstring const BiblioInfo::getCiteNumber(docstring const & key) const
822 BiblioInfo::const_iterator it = find(key);
825 BibTeXInfo const & data = it->second;
826 return data.citeNumber();
830 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
832 BiblioInfo::const_iterator it = find(key);
835 BibTeXInfo const & data = it->second;
836 docstring year = data.getYear();
838 // let's try the crossref
839 docstring const xref = data.getXRef();
843 BiblioInfo::const_iterator const xrefit = find(xref);
847 BibTeXInfo const & xref_data = xrefit->second;
848 year = xref_data.getYear();
850 if (use_modifier && data.modifier() != 0)
851 year += data.modifier();
856 docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, bool use_modifier) const
858 docstring const year = getYear(key, use_modifier);
860 return buf.B_("No year");
865 docstring const BiblioInfo::getInfo(docstring const & key,
866 Buffer const & buf, bool richtext) const
868 BiblioInfo::const_iterator it = find(key);
870 return docstring(_("Bibliography entry not found!"));
871 BibTeXInfo const & data = it->second;
872 BibTeXInfo const * xrefptr = 0;
873 docstring const xref = data.getXRef();
875 BiblioInfo::const_iterator const xrefit = find(xref);
877 xrefptr = &(xrefit->second);
879 return data.getInfo(xrefptr, buf, richtext);
883 docstring const BiblioInfo::getLabel(vector<docstring> keys,
884 Buffer const & buf, string const & style, bool for_xhtml,
885 size_t max_size, docstring const & before, docstring const & after,
886 docstring const & dialog) const
888 // shorter makes no sense
889 LASSERT(max_size >= 16, max_size = 16);
891 // we can't display more than 10 of these, anyway
892 bool const too_many_keys = keys.size() > 10;
896 CiteEngineType const engine_type = buf.params().citeEngineType();
897 DocumentClass const & dc = buf.params().documentClass();
898 docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
899 docstring ret = format;
900 vector<docstring>::const_iterator key = keys.begin();
901 vector<docstring>::const_iterator ken = keys.end();
902 for (; key != ken; ++key) {
903 BiblioInfo::const_iterator it = find(*key);
904 BibTeXInfo empty_data;
905 empty_data.key(*key);
906 BibTeXInfo & data = empty_data;
907 BibTeXInfo const * xrefptr = 0;
910 docstring const xref = data.getXRef();
912 BiblioInfo::const_iterator const xrefit = find(xref);
914 xrefptr = &(xrefit->second);
917 ret = data.getLabel(xrefptr, buf, ret, for_xhtml,
918 before, after, dialog, key + 1 != ken);
922 ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
923 support::truncateWithEllipsis(ret, max_size);
928 bool BiblioInfo::isBibtex(docstring const & key) const
931 split(key, key1, ',');
932 BiblioInfo::const_iterator it = find(key1);
935 return it->second.isBibTeX();
939 vector<docstring> const BiblioInfo::getCiteStrings(
940 vector<docstring> const & keys, vector<CitationStyle> const & styles,
941 Buffer const & buf, docstring const & before,
942 docstring const & after, docstring const & dialog, size_t max_size) const
945 return vector<docstring>();
948 vector<docstring> vec(styles.size());
949 for (size_t i = 0; i != vec.size(); ++i) {
950 style = styles[i].cmd;
951 vec[i] = getLabel(keys, buf, style, false, max_size, before, after, dialog);
958 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
960 bimap_.insert(info.begin(), info.end());
961 field_names_.insert(info.field_names_.begin(), info.field_names_.end());
962 entry_types_.insert(info.entry_types_.begin(), info.entry_types_.end());
968 // used in xhtml to sort a list of BibTeXInfo objects
969 bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
971 docstring const lauth = lhs->getAbbreviatedAuthor();
972 docstring const rauth = rhs->getAbbreviatedAuthor();
973 docstring const lyear = lhs->getYear();
974 docstring const ryear = rhs->getYear();
975 docstring const ltitl = lhs->operator[]("title");
976 docstring const rtitl = rhs->operator[]("title");
977 return (lauth < rauth)
978 || (lauth == rauth && lyear < ryear)
979 || (lauth == rauth && lyear == ryear && ltitl < rtitl);
985 void BiblioInfo::collectCitedEntries(Buffer const & buf)
987 cited_entries_.clear();
988 // We are going to collect all the citation keys used in the document,
989 // getting them from the TOC.
990 // FIXME We may want to collect these differently, in the first case,
991 // so that we might have them in order of appearance.
992 set<docstring> citekeys;
993 shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
994 Toc::const_iterator it = toc->begin();
995 Toc::const_iterator const en = toc->end();
996 for (; it != en; ++it) {
997 if (it->str().empty())
999 vector<docstring> const keys = getVectorFromString(it->str());
1000 citekeys.insert(keys.begin(), keys.end());
1002 if (citekeys.empty())
1005 // We have a set of the keys used in this document.
1006 // We will now convert it to a list of the BibTeXInfo objects used in
1008 vector<BibTeXInfo const *> bi;
1009 set<docstring>::const_iterator cit = citekeys.begin();
1010 set<docstring>::const_iterator const cen = citekeys.end();
1011 for (; cit != cen; ++cit) {
1012 BiblioInfo::const_iterator const bt = find(*cit);
1013 if (bt == end() || !bt->second.isBibTeX())
1015 bi.push_back(&(bt->second));
1018 sort(bi.begin(), bi.end(), lSorter);
1020 // Now we can write the sorted keys
1021 vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
1022 vector<BibTeXInfo const *>::const_iterator ben = bi.end();
1023 for (; bit != ben; ++bit)
1024 cited_entries_.push_back((*bit)->key());
1028 void BiblioInfo::makeCitationLabels(Buffer const & buf)
1030 collectCitedEntries(buf);
1031 CiteEngineType const engine_type = buf.params().citeEngineType();
1032 bool const numbers = (engine_type & ENGINE_TYPE_NUMERICAL);
1036 // used to remember the last one we saw
1037 // we'll be comparing entries to see if we need to add
1038 // modifiers, like "1984a"
1039 map<docstring, BibTeXInfo>::iterator last;
1041 vector<docstring>::const_iterator it = cited_entries_.begin();
1042 vector<docstring>::const_iterator const en = cited_entries_.end();
1043 for (; it != en; ++it) {
1044 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1045 // this shouldn't happen, but...
1046 if (biit == bimap_.end())
1047 // ...fail gracefully, anyway.
1049 BibTeXInfo & entry = biit->second;
1051 docstring const num = convert<docstring>(++keynumber);
1052 entry.setCiteNumber(num);
1054 // coverity complains about our derefercing the iterator last,
1055 // which was not initialized above. but it does get initialized
1056 // after the first time through the loop, which is the point of
1058 // coverity[FORWARD_NULL]
1059 if (it != cited_entries_.begin()
1060 && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
1061 // we access the year via getYear() so as to get it from the xref,
1062 // if we need to do so
1063 && getYear(entry.key()) == getYear(last->second.key())) {
1064 if (modifier == 0) {
1065 // so the last one should have been 'a'
1066 last->second.setModifier('a');
1068 } else if (modifier == 'z')
1075 entry.setModifier(modifier);
1076 // remember the last one
1081 it = cited_entries_.begin();
1082 for (; it != en; ++it) {
1083 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1084 // this shouldn't happen, but...
1085 if (biit == bimap_.end())
1086 // ...fail gracefully, anyway.
1088 BibTeXInfo & entry = biit->second;
1090 entry.label(entry.citeNumber());
1092 docstring const auth = entry.getAbbreviatedAuthor(&buf, false);
1093 // we do it this way so as to access the xref, if necessary
1094 // note that this also gives us the modifier
1095 docstring const year = getYear(*it, buf, true);
1096 if (!auth.empty() && !year.empty())
1097 entry.label(auth + ' ' + year);
1099 entry.label(entry.key());
1105 //////////////////////////////////////////////////////////////////////
1109 //////////////////////////////////////////////////////////////////////
1112 CitationStyle citationStyleFromString(string const & command)
1115 if (command.empty())
1118 string cmd = command;
1119 if (cmd[0] == 'C') {
1120 cs.forceUpperCase = true;
1124 size_t const n = cmd.size() - 1;
1125 if (cmd[n] == '*') {
1126 cs.fullAuthorList = true;
1127 cmd = cmd.substr(0, n);
1135 string citationStyleToString(const CitationStyle & cs)
1137 string cmd = cs.cmd;
1138 if (cs.forceUpperCase)
1139 cmd[0] = uppercase(cmd[0]);
1140 if (cs.fullAuthorList)