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]));
303 if (authors.size() >= 2)
304 retval = bformat(from_ascii("%1$s et al."),
305 familyName(authors[0]));
307 return convertLaTeXCommands(retval);
311 docstring const BibTeXInfo::getAbbreviatedAuthor(Buffer const & buf, bool jurabib_style) const
313 docstring const author = getAbbreviatedAuthor(jurabib_style);
316 vector<docstring> const authors = getVectorFromString(author, from_ascii(" and "));
317 if (authors.size() == 2)
318 return bformat(buf.B_("%1$s and %2$s"), authors[0], authors[1]);
319 docstring::size_type const idx = author.rfind(from_ascii(" et al."));
320 if (idx != docstring::npos)
321 return bformat(buf.B_("%1$s et al."), author.substr(0, idx));
326 docstring const BibTeXInfo::getYear() const
329 return operator[]("year");
331 docstring const opt = label();
336 docstring tmp = split(opt, authors, '(');
338 // we don't have author (year)
341 tmp = split(tmp, year, ')');
346 docstring const BibTeXInfo::getXRef() const
350 return operator[]("crossref");
356 docstring parseOptions(docstring const & format, string & optkey,
357 docstring & ifpart, docstring & elsepart);
359 // Calls parseOptions to deal with an embedded option, such as:
360 // {%number%[[, no.~%number%]]}
361 // which must appear at the start of format. ifelsepart gets the
362 // whole of the option, and we return what's left after the option.
363 // we return format if there is an error.
364 docstring parseEmbeddedOption(docstring const & format, docstring & ifelsepart)
366 LASSERT(format[0] == '{' && format[1] == '%', return format);
370 docstring const rest = parseOptions(format, optkey, ifpart, elsepart);
371 if (format == rest) { // parse error
372 LYXERR0("ERROR! Couldn't parse `" << format <<"'.");
375 LASSERT(rest.size() <= format.size(),
376 { ifelsepart = docstring(); return format; });
377 ifelsepart = format.substr(0, format.size() - rest.size());
382 // Gets a "clause" from a format string, where the clause is
383 // delimited by '[[' and ']]'. Returns what is left after the
384 // clause is removed, and returns format if there is an error.
385 docstring getClause(docstring const & format, docstring & clause)
387 docstring fmt = format;
390 // we'll remove characters from the front of fmt as we
392 while (!fmt.empty()) {
393 if (fmt[0] == ']' && fmt.size() > 1 && fmt[1] == ']') {
398 // check for an embedded option
399 if (fmt[0] == '{' && fmt.size() > 1 && fmt[1] == '%') {
401 docstring const rest = parseEmbeddedOption(fmt, part);
403 LYXERR0("ERROR! Couldn't parse embedded option in `" << format <<"'.");
408 } else { // it's just a normal character
417 // parse an options string, which must appear at the start of the
418 // format parameter. puts the parsed bits in optkey, ifpart, and
419 // elsepart and returns what's left after the option is removed.
420 // if there's an error, it returns format itself.
421 docstring parseOptions(docstring const & format, string & optkey,
422 docstring & ifpart, docstring & elsepart)
424 LASSERT(format[0] == '{' && format[1] == '%', return format);
426 docstring fmt = format.substr(2);
427 size_t pos = fmt.find('%'); // end of key
428 if (pos == string::npos) {
429 LYXERR0("Error parsing `" << format <<"'. Can't find end of key.");
432 optkey = to_utf8(fmt.substr(0, pos));
433 fmt = fmt.substr(pos + 1);
434 // [[format]] should be next
435 if (fmt[0] != '[' || fmt[1] != '[') {
436 LYXERR0("Error parsing `" << format <<"'. Can't find '[[' after key.");
440 docstring curfmt = fmt;
441 fmt = getClause(curfmt, ifpart);
443 LYXERR0("Error parsing `" << format <<"'. Couldn't get if clause.");
447 if (fmt[0] == '}') // we're done, no else clause
448 return fmt.substr(1);
450 // else part should follow
451 if (fmt[0] != '[' || fmt[1] != '[') {
452 LYXERR0("Error parsing `" << format <<"'. Can't find else clause.");
457 fmt = getClause(curfmt, elsepart);
459 if (fmt == curfmt || fmt[0] != '}') {
460 LYXERR0("Error parsing `" << format <<"'. Can't find end of option.");
463 return fmt.substr(1);
470 Bug #9131 revealed an oddity in how we are generating citation information
471 when more than one key is given. We end up building a longer and longer format
472 string as we go, which we then have to re-parse, over and over and over again,
473 rather than generating the information for the individual keys and then putting
474 all of that together. We do that to deal with the way separators work, from what
475 I can tell, but it still feels like a hack. Fixing this would require quite a
476 bit of work, however.
478 docstring BibTeXInfo::expandFormat(docstring const & format,
479 BibTeXInfo const * const xref, int & counter, Buffer const & buf,
480 docstring before, docstring after, docstring dialog, bool next) const
482 // incorrect use of macros could put us in an infinite loop
483 static int const max_passes = 5000;
484 // the use of overly large keys can lead to performance problems, due
485 // to eventual attempts to convert LaTeX macros to unicode. See bug
486 // #8944. This is perhaps not the best solution, but it will have to
488 static size_t const max_keysize = 128;
489 odocstringstream ret; // return value
491 bool scanning_key = false;
492 bool scanning_rich = false;
494 CiteEngineType const engine_type = buf.params().citeEngineType();
495 docstring fmt = format;
496 // we'll remove characters from the front of fmt as we
498 while (!fmt.empty()) {
499 if (counter > max_passes) {
500 LYXERR0("Recursion limit reached while parsing `"
505 char_type thischar = fmt[0];
506 if (thischar == '%') {
507 // beginning or end of key
510 scanning_key = false;
511 // so we replace the key with its value, which may be empty
515 buf.params().documentClass().getCiteMacro(engine_type, key);
516 fmt = from_utf8(val) + fmt.substr(1);
519 } else if (key[0] == '_') {
520 // a translatable bit
522 buf.params().documentClass().getCiteMacro(engine_type, key);
523 docstring const trans =
524 translateIfPossible(from_utf8(val), buf.params().language->code());
527 docstring const val =
528 getValueForKey(key, buf, before, after, dialog, xref, max_keysize);
530 ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
533 ret << from_ascii("{!</span>!}");
541 else if (thischar == '{') {
542 // beginning of option?
544 LYXERR0("ERROR: Found `{' when scanning key in `" << format << "'.");
547 if (fmt.size() > 1) {
549 // it is the beginning of an optional format
553 docstring const newfmt =
554 parseOptions(fmt, optkey, ifpart, elsepart);
555 if (newfmt == fmt) // parse error
558 docstring const val =
559 getValueForKey(optkey, buf, before, after, dialog, xref);
560 if (optkey == "next" && next)
561 ret << ifpart; // without expansion
562 else if (!val.empty()) {
564 ret << expandFormat(ifpart, xref, newcounter, buf,
565 before, after, dialog, next);
566 } else if (!elsepart.empty()) {
568 ret << expandFormat(elsepart, xref, newcounter, buf,
569 before, after, dialog, next);
571 // fmt will have been shortened for us already
575 // beginning of rich text
576 scanning_rich = true;
578 ret << from_ascii("{!");
582 // we are here if '{' was not followed by % or !.
583 // So it's just a character.
586 else if (scanning_rich && thischar == '!'
587 && fmt.size() > 1 && fmt[1] == '}') {
589 scanning_rich = false;
591 ret << from_ascii("!}");
594 else if (scanning_key)
595 key += char(thischar);
599 } catch (EncodingException & /* e */) {
600 LYXERR0("Uncodable character '" << docstring(1, thischar) << " in citation label!");
606 LYXERR0("Never found end of key in `" << format << "'!");
610 LYXERR0("Never found end of rich text in `" << format << "'!");
617 docstring const & BibTeXInfo::getInfo(BibTeXInfo const * const xref,
618 Buffer const & buf, bool richtext) const
620 if (!richtext && !info_.empty())
622 if (richtext && !info_richtext_.empty())
623 return info_richtext_;
626 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
631 CiteEngineType const engine_type = buf.params().citeEngineType();
632 DocumentClass const & dc = buf.params().documentClass();
633 docstring const & format =
634 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
636 info_ = expandFormat(format, xref, counter, buf,
637 docstring(), docstring(), docstring(), false);
640 // this probably shouldn't happen
645 info_richtext_ = convertLaTeXCommands(processRichtext(info_, true));
646 return info_richtext_;
649 info_ = convertLaTeXCommands(processRichtext(info_, false));
654 docstring const BibTeXInfo::getLabel(BibTeXInfo const * const xref,
655 Buffer const & buf, docstring const & format, bool richtext,
656 docstring const & before, docstring const & after,
657 docstring const & dialog, bool next) const
662 loclabel = expandFormat(format, xref, counter, buf,
663 before, after, dialog, next);
665 if (!loclabel.empty() && !next) {
666 loclabel = processRichtext(loclabel, richtext);
667 loclabel = convertLaTeXCommands(loclabel);
674 docstring const & BibTeXInfo::operator[](docstring const & field) const
676 BibTeXInfo::const_iterator it = find(field);
679 static docstring const empty_value = docstring();
684 docstring const & BibTeXInfo::operator[](string const & field) const
686 return operator[](from_ascii(field));
690 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
691 docstring const & before, docstring const & after, docstring const & dialog,
692 BibTeXInfo const * const xref, size_t maxsize) const
694 // anything less is pointless
695 LASSERT(maxsize >= 16, maxsize = 16);
697 bool cleanit = false;
698 if (prefixIs(oldkey, "clean:")) {
699 key = oldkey.substr(6);
703 docstring ret = operator[](key);
704 if (ret.empty() && xref)
708 // FIXME: dialog, textbefore and textafter have nothing to do with this
711 else if (key == "entrytype")
713 else if (key == "key")
715 else if (key == "label")
717 else if (key == "modifier" && modifier_ != 0)
719 else if (key == "numericallabel")
721 else if (key == "abbrvauthor")
722 // Special key to provide abbreviated author names.
723 ret = getAbbreviatedAuthor(buf, false);
724 else if (key == "shortauthor")
725 // When shortauthor is not defined, jurabib automatically
726 // provides jurabib-style abbreviated author names. We do
728 ret = getAbbreviatedAuthor(buf, true);
729 else if (key == "shorttitle") {
730 // When shorttitle is not defined, jurabib uses for `article'
731 // and `periodical' entries the form `journal volume [year]'
732 // and for other types of entries it uses the `title' field.
733 if (entry_type_ == "article" || entry_type_ == "periodical")
734 ret = operator[]("journal") + " " + operator[]("volume")
735 + " [" + operator[]("year") + "]";
737 ret = operator[]("title");
738 } else if (key == "bibentry") {
739 // Special key to provide the full bibliography entry: see getInfo()
740 CiteEngineType const engine_type = buf.params().citeEngineType();
741 DocumentClass const & dc = buf.params().documentClass();
742 docstring const & format =
743 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
745 ret = expandFormat(format, xref, counter, buf,
746 docstring(), docstring(), docstring(), false);
747 } else if (key == "textbefore")
749 else if (key == "textafter")
751 else if (key == "year")
756 ret = html::cleanAttr(ret);
758 // make sure it is not too big
759 support::truncateWithEllipsis(ret, maxsize);
764 //////////////////////////////////////////////////////////////////////
768 //////////////////////////////////////////////////////////////////////
772 // A functor for use with sort, leading to case insensitive sorting
773 class compareNoCase: public binary_function<docstring, docstring, bool>
776 bool operator()(docstring const & s1, docstring const & s2) const {
777 return compare_no_case(s1, s2) < 0;
784 vector<docstring> const BiblioInfo::getKeys() const
786 vector<docstring> bibkeys;
787 BiblioInfo::const_iterator it = begin();
788 for (; it != end(); ++it)
789 bibkeys.push_back(it->first);
790 sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
795 vector<docstring> const BiblioInfo::getFields() const
797 vector<docstring> bibfields;
798 set<docstring>::const_iterator it = field_names_.begin();
799 set<docstring>::const_iterator end = field_names_.end();
800 for (; it != end; ++it)
801 bibfields.push_back(*it);
802 sort(bibfields.begin(), bibfields.end());
807 vector<docstring> const BiblioInfo::getEntries() const
809 vector<docstring> bibentries;
810 set<docstring>::const_iterator it = entry_types_.begin();
811 set<docstring>::const_iterator end = entry_types_.end();
812 for (; it != end; ++it)
813 bibentries.push_back(*it);
814 sort(bibentries.begin(), bibentries.end());
819 docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key, Buffer const & buf) const
821 BiblioInfo::const_iterator it = find(key);
824 BibTeXInfo const & data = it->second;
825 return data.getAbbreviatedAuthor(buf, false);
829 docstring const BiblioInfo::getCiteNumber(docstring const & key) const
831 BiblioInfo::const_iterator it = find(key);
834 BibTeXInfo const & data = it->second;
835 return data.citeNumber();
839 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
841 BiblioInfo::const_iterator it = find(key);
844 BibTeXInfo const & data = it->second;
845 docstring year = data.getYear();
847 // let's try the crossref
848 docstring const xref = data.getXRef();
852 BiblioInfo::const_iterator const xrefit = find(xref);
856 BibTeXInfo const & xref_data = xrefit->second;
857 year = xref_data.getYear();
859 if (use_modifier && data.modifier() != 0)
860 year += data.modifier();
865 docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, bool use_modifier) const
867 docstring const year = getYear(key, use_modifier);
869 return buf.B_("No year");
874 docstring const BiblioInfo::getInfo(docstring const & key,
875 Buffer const & buf, bool richtext) const
877 BiblioInfo::const_iterator it = find(key);
879 return docstring(_("Bibliography entry not found!"));
880 BibTeXInfo const & data = it->second;
881 BibTeXInfo const * xrefptr = 0;
882 docstring const xref = data.getXRef();
884 BiblioInfo::const_iterator const xrefit = find(xref);
886 xrefptr = &(xrefit->second);
888 return data.getInfo(xrefptr, buf, richtext);
892 docstring const BiblioInfo::getLabel(vector<docstring> keys,
893 Buffer const & buf, string const & style, bool for_xhtml,
894 size_t max_size, docstring const & before, docstring const & after,
895 docstring const & dialog) const
897 // shorter makes no sense
898 LASSERT(max_size >= 16, max_size = 16);
900 // we can't display more than 10 of these, anyway
901 bool const too_many_keys = keys.size() > 10;
905 CiteEngineType const engine_type = buf.params().citeEngineType();
906 DocumentClass const & dc = buf.params().documentClass();
907 docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
908 docstring ret = format;
909 vector<docstring>::const_iterator key = keys.begin();
910 vector<docstring>::const_iterator ken = keys.end();
911 for (; key != ken; ++key) {
912 BiblioInfo::const_iterator it = find(*key);
913 BibTeXInfo empty_data;
914 empty_data.key(*key);
915 BibTeXInfo & data = empty_data;
916 BibTeXInfo const * xrefptr = 0;
919 docstring const xref = data.getXRef();
921 BiblioInfo::const_iterator const xrefit = find(xref);
923 xrefptr = &(xrefit->second);
926 ret = data.getLabel(xrefptr, buf, ret, for_xhtml,
927 before, after, dialog, key + 1 != ken);
931 ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
932 support::truncateWithEllipsis(ret, max_size);
937 bool BiblioInfo::isBibtex(docstring const & key) const
940 split(key, key1, ',');
941 BiblioInfo::const_iterator it = find(key1);
944 return it->second.isBibTeX();
948 vector<docstring> const BiblioInfo::getCiteStrings(
949 vector<docstring> const & keys, vector<CitationStyle> const & styles,
950 Buffer const & buf, docstring const & before,
951 docstring const & after, docstring const & dialog, size_t max_size) const
954 return vector<docstring>();
957 vector<docstring> vec(styles.size());
958 for (size_t i = 0; i != vec.size(); ++i) {
959 style = styles[i].cmd;
960 vec[i] = getLabel(keys, buf, style, false, max_size, before, after, dialog);
967 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
969 bimap_.insert(info.begin(), info.end());
970 field_names_.insert(info.field_names_.begin(), info.field_names_.end());
971 entry_types_.insert(info.entry_types_.begin(), info.entry_types_.end());
977 // used in xhtml to sort a list of BibTeXInfo objects
978 bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
980 docstring const lauth = lhs->getAbbreviatedAuthor();
981 docstring const rauth = rhs->getAbbreviatedAuthor();
982 docstring const lyear = lhs->getYear();
983 docstring const ryear = rhs->getYear();
984 docstring const ltitl = lhs->operator[]("title");
985 docstring const rtitl = rhs->operator[]("title");
986 return (lauth < rauth)
987 || (lauth == rauth && lyear < ryear)
988 || (lauth == rauth && lyear == ryear && ltitl < rtitl);
994 void BiblioInfo::collectCitedEntries(Buffer const & buf)
996 cited_entries_.clear();
997 // We are going to collect all the citation keys used in the document,
998 // getting them from the TOC.
999 // FIXME We may want to collect these differently, in the first case,
1000 // so that we might have them in order of appearance.
1001 set<docstring> citekeys;
1002 shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
1003 Toc::const_iterator it = toc->begin();
1004 Toc::const_iterator const en = toc->end();
1005 for (; it != en; ++it) {
1006 if (it->str().empty())
1008 vector<docstring> const keys = getVectorFromString(it->str());
1009 citekeys.insert(keys.begin(), keys.end());
1011 if (citekeys.empty())
1014 // We have a set of the keys used in this document.
1015 // We will now convert it to a list of the BibTeXInfo objects used in
1017 vector<BibTeXInfo const *> bi;
1018 set<docstring>::const_iterator cit = citekeys.begin();
1019 set<docstring>::const_iterator const cen = citekeys.end();
1020 for (; cit != cen; ++cit) {
1021 BiblioInfo::const_iterator const bt = find(*cit);
1022 if (bt == end() || !bt->second.isBibTeX())
1024 bi.push_back(&(bt->second));
1027 sort(bi.begin(), bi.end(), lSorter);
1029 // Now we can write the sorted keys
1030 vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
1031 vector<BibTeXInfo const *>::const_iterator ben = bi.end();
1032 for (; bit != ben; ++bit)
1033 cited_entries_.push_back((*bit)->key());
1037 void BiblioInfo::makeCitationLabels(Buffer const & buf)
1039 collectCitedEntries(buf);
1040 CiteEngineType const engine_type = buf.params().citeEngineType();
1041 bool const numbers = (engine_type & ENGINE_TYPE_NUMERICAL);
1045 // used to remember the last one we saw
1046 // we'll be comparing entries to see if we need to add
1047 // modifiers, like "1984a"
1048 map<docstring, BibTeXInfo>::iterator last;
1050 vector<docstring>::const_iterator it = cited_entries_.begin();
1051 vector<docstring>::const_iterator const en = cited_entries_.end();
1052 for (; it != en; ++it) {
1053 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1054 // this shouldn't happen, but...
1055 if (biit == bimap_.end())
1056 // ...fail gracefully, anyway.
1058 BibTeXInfo & entry = biit->second;
1060 docstring const num = convert<docstring>(++keynumber);
1061 entry.setCiteNumber(num);
1063 if (it != cited_entries_.begin()
1064 && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
1065 // we access the year via getYear() so as to get it from the xref,
1066 // if we need to do so
1067 && getYear(entry.key()) == getYear(last->second.key())) {
1068 if (modifier == 0) {
1069 // so the last one should have been 'a'
1070 last->second.setModifier('a');
1072 } else if (modifier == 'z')
1079 entry.setModifier(modifier);
1080 // remember the last one
1085 it = cited_entries_.begin();
1086 for (; it != en; ++it) {
1087 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1088 // this shouldn't happen, but...
1089 if (biit == bimap_.end())
1090 // ...fail gracefully, anyway.
1092 BibTeXInfo & entry = biit->second;
1094 entry.label(entry.citeNumber());
1096 docstring const auth = entry.getAbbreviatedAuthor(buf, false);
1097 // we do it this way so as to access the xref, if necessary
1098 // note that this also gives us the modifier
1099 docstring const year = getYear(*it, buf, true);
1100 if (!auth.empty() && !year.empty())
1101 entry.label(auth + ' ' + year);
1103 entry.label(entry.key());
1109 //////////////////////////////////////////////////////////////////////
1113 //////////////////////////////////////////////////////////////////////
1116 CitationStyle citationStyleFromString(string const & command)
1119 if (command.empty())
1122 string cmd = command;
1123 if (cmd[0] == 'C') {
1124 cs.forceUpperCase = true;
1128 size_t const n = cmd.size() - 1;
1129 if (cmd[n] == '*') {
1130 cs.fullAuthorList = true;
1131 cmd = cmd.substr(0, n);
1139 string citationStyleToString(const CitationStyle & cs)
1141 string cmd = cs.cmd;
1142 if (cs.forceUpperCase)
1144 if (cs.fullAuthorList)