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, ')');
357 docstring parseOptions(docstring const & format, string & optkey,
358 docstring & ifpart, docstring & elsepart);
360 // Calls parseOptions to deal with an embedded option, such as:
361 // {%number%[[, no.~%number%]]}
362 // which must appear at the start of format. ifelsepart gets the
363 // whole of the option, and we return what's left after the option.
364 // we return format if there is an error.
365 docstring parseEmbeddedOption(docstring const & format, docstring & ifelsepart)
367 LASSERT(format[0] == '{' && format[1] == '%', return format);
371 docstring const rest = parseOptions(format, optkey, ifpart, elsepart);
372 if (format == rest) { // parse error
373 LYXERR0("ERROR! Couldn't parse `" << format <<"'.");
376 LASSERT(rest.size() <= format.size(),
377 { ifelsepart = docstring(); return format; });
378 ifelsepart = format.substr(0, format.size() - rest.size());
383 // Gets a "clause" from a format string, where the clause is
384 // delimited by '[[' and ']]'. Returns what is left after the
385 // clause is removed, and returns format if there is an error.
386 docstring getClause(docstring const & format, docstring & clause)
388 docstring fmt = format;
391 // we'll remove characters from the front of fmt as we
393 while (!fmt.empty()) {
394 if (fmt[0] == ']' && fmt.size() > 1 && fmt[1] == ']') {
399 // check for an embedded option
400 if (fmt[0] == '{' && fmt.size() > 1 && fmt[1] == '%') {
402 docstring const rest = parseEmbeddedOption(fmt, part);
404 LYXERR0("ERROR! Couldn't parse embedded option in `" << format <<"'.");
409 } else { // it's just a normal character
418 // parse an options string, which must appear at the start of the
419 // format parameter. puts the parsed bits in optkey, ifpart, and
420 // elsepart and returns what's left after the option is removed.
421 // if there's an error, it returns format itself.
422 docstring parseOptions(docstring const & format, string & optkey,
423 docstring & ifpart, docstring & elsepart)
425 LASSERT(format[0] == '{' && format[1] == '%', return format);
427 docstring fmt = format.substr(2);
428 size_t pos = fmt.find('%'); // end of key
429 if (pos == string::npos) {
430 LYXERR0("Error parsing `" << format <<"'. Can't find end of key.");
433 optkey = to_utf8(fmt.substr(0, pos));
434 fmt = fmt.substr(pos + 1);
435 // [[format]] should be next
436 if (fmt[0] != '[' || fmt[1] != '[') {
437 LYXERR0("Error parsing `" << format <<"'. Can't find '[[' after key.");
441 docstring curfmt = fmt;
442 fmt = getClause(curfmt, ifpart);
444 LYXERR0("Error parsing `" << format <<"'. Couldn't get if clause.");
448 if (fmt[0] == '}') // we're done, no else clause
449 return fmt.substr(1);
451 // else part should follow
452 if (fmt[0] != '[' || fmt[1] != '[') {
453 LYXERR0("Error parsing `" << format <<"'. Can't find else clause.");
458 fmt = getClause(curfmt, elsepart);
460 if (fmt == curfmt || fmt[0] != '}') {
461 LYXERR0("Error parsing `" << format <<"'. Can't find end of option.");
464 return fmt.substr(1);
471 Bug #9131 revealed an oddity in how we are generating citation information
472 when more than one key is given. We end up building a longer and longer format
473 string as we go, which we then have to re-parse, over and over and over again,
474 rather than generating the information for the individual keys and then putting
475 all of that together. We do that to deal with the way separators work, from what
476 I can tell, but it still feels like a hack. Fixing this would require quite a
477 bit of work, however.
479 docstring BibTeXInfo::expandFormat(docstring const & format,
480 BibTeXInfoList const xrefs, int & counter, Buffer const & buf,
481 docstring before, docstring after, docstring dialog, bool next) const
483 // incorrect use of macros could put us in an infinite loop
484 static int const max_passes = 5000;
485 // the use of overly large keys can lead to performance problems, due
486 // to eventual attempts to convert LaTeX macros to unicode. See bug
487 // #8944. This is perhaps not the best solution, but it will have to
489 static size_t const max_keysize = 128;
490 odocstringstream ret; // return value
492 bool scanning_key = false;
493 bool scanning_rich = false;
495 CiteEngineType const engine_type = buf.params().citeEngineType();
496 docstring fmt = format;
497 // we'll remove characters from the front of fmt as we
499 while (!fmt.empty()) {
500 if (counter > max_passes) {
501 LYXERR0("Recursion limit reached while parsing `"
506 char_type thischar = fmt[0];
507 if (thischar == '%') {
508 // beginning or end of key
511 scanning_key = false;
512 // so we replace the key with its value, which may be empty
516 buf.params().documentClass().getCiteMacro(engine_type, key);
517 fmt = from_utf8(val) + fmt.substr(1);
520 } else if (key[0] == '_') {
521 // a translatable bit
523 buf.params().documentClass().getCiteMacro(engine_type, key);
524 docstring const trans =
525 translateIfPossible(from_utf8(val), buf.params().language->code());
528 docstring const val =
529 getValueForKey(key, buf, before, after, dialog, xrefs, max_keysize);
531 ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
534 ret << from_ascii("{!</span>!}");
542 else if (thischar == '{') {
543 // beginning of option?
545 LYXERR0("ERROR: Found `{' when scanning key in `" << format << "'.");
548 if (fmt.size() > 1) {
550 // it is the beginning of an optional format
554 docstring const newfmt =
555 parseOptions(fmt, optkey, ifpart, elsepart);
556 if (newfmt == fmt) // parse error
559 docstring const val =
560 getValueForKey(optkey, buf, before, after, dialog, xrefs);
561 if (optkey == "next" && next)
562 ret << ifpart; // without expansion
563 else if (!val.empty()) {
565 ret << expandFormat(ifpart, xrefs, newcounter, buf,
566 before, after, dialog, next);
567 } else if (!elsepart.empty()) {
569 ret << expandFormat(elsepart, xrefs, newcounter, buf,
570 before, after, dialog, next);
572 // fmt will have been shortened for us already
576 // beginning of rich text
577 scanning_rich = true;
579 ret << from_ascii("{!");
583 // we are here if '{' was not followed by % or !.
584 // So it's just a character.
587 else if (scanning_rich && thischar == '!'
588 && fmt.size() > 1 && fmt[1] == '}') {
590 scanning_rich = false;
592 ret << from_ascii("!}");
595 else if (scanning_key)
596 key += char(thischar);
600 } catch (EncodingException & /* e */) {
601 LYXERR0("Uncodable character '" << docstring(1, thischar) << " in citation label!");
607 LYXERR0("Never found end of key in `" << format << "'!");
611 LYXERR0("Never found end of rich text in `" << format << "'!");
618 docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
619 Buffer const & buf, bool richtext) const
621 if (!richtext && !info_.empty())
623 if (richtext && !info_richtext_.empty())
624 return info_richtext_;
627 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
632 CiteEngineType const engine_type = buf.params().citeEngineType();
633 DocumentClass const & dc = buf.params().documentClass();
634 docstring const & format =
635 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
637 info_ = expandFormat(format, xrefs, counter, buf,
638 docstring(), docstring(), docstring(), false);
641 // this probably shouldn't happen
646 info_richtext_ = convertLaTeXCommands(processRichtext(info_, true));
647 return info_richtext_;
650 info_ = convertLaTeXCommands(processRichtext(info_, false));
655 docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
656 Buffer const & buf, docstring const & format, bool richtext,
657 docstring const & before, docstring const & after,
658 docstring const & dialog, bool next) const
663 loclabel = expandFormat(format, xrefs, counter, buf,
664 before, after, dialog, next);
666 if (!loclabel.empty() && !next) {
667 loclabel = processRichtext(loclabel, richtext);
668 loclabel = convertLaTeXCommands(loclabel);
675 docstring const & BibTeXInfo::operator[](docstring const & field) const
677 BibTeXInfo::const_iterator it = find(field);
680 static docstring const empty_value = docstring();
685 docstring const & BibTeXInfo::operator[](string const & field) const
687 return operator[](from_ascii(field));
691 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
692 docstring const & before, docstring const & after, docstring const & dialog,
693 BibTeXInfoList const xrefs, size_t maxsize) const
695 // anything less is pointless
696 LASSERT(maxsize >= 16, maxsize = 16);
698 bool cleanit = false;
699 if (prefixIs(oldkey, "clean:")) {
700 key = oldkey.substr(6);
704 docstring ret = operator[](key);
705 if (ret.empty() && !xrefs.empty()) {
706 vector<BibTeXInfo const *>::const_iterator it = xrefs.begin();
707 vector<BibTeXInfo const *>::const_iterator en = xrefs.end();
708 for (; it != en; ++it) {
709 if (*it && !(**it)[key].empty()) {
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, xrefs, 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::getXRefs(BibTeXInfo const & data, bool const nested) const
795 vector<docstring> result;
796 if (!data.isBibTeX())
798 // Legacy crossref field. This is not nestable.
799 if (!nested && !data["crossref"].empty()) {
800 docstring const xrefkey = data["crossref"];
801 result.push_back(xrefkey);
802 // However, check for nested xdatas
803 BiblioInfo::const_iterator it = find(xrefkey);
805 BibTeXInfo const & xref = it->second;
806 vector<docstring> const nxdata = getXRefs(xref, true);
808 result.insert(result.end(), nxdata.begin(), nxdata.end());
811 // Biblatex's xdata field. Infinitely nestable.
812 // XData field can consist of a comma-separated list of keys
813 vector<docstring> const xdatakeys = getVectorFromString(data["xdata"]);
814 if (!xdatakeys.empty()) {
815 vector<docstring>::const_iterator xit = xdatakeys.begin();
816 vector<docstring>::const_iterator xen = xdatakeys.end();
817 for (; xit != xen; ++xit) {
818 docstring const xdatakey = *xit;
819 result.push_back(xdatakey);
820 BiblioInfo::const_iterator it = find(xdatakey);
822 BibTeXInfo const & xdata = it->second;
823 vector<docstring> const nxdata = getXRefs(xdata, true);
825 result.insert(result.end(), nxdata.begin(), nxdata.end());
833 vector<docstring> const BiblioInfo::getKeys() const
835 vector<docstring> bibkeys;
836 BiblioInfo::const_iterator it = begin();
837 for (; it != end(); ++it)
838 bibkeys.push_back(it->first);
839 sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
844 vector<docstring> const BiblioInfo::getFields() const
846 vector<docstring> bibfields;
847 set<docstring>::const_iterator it = field_names_.begin();
848 set<docstring>::const_iterator end = field_names_.end();
849 for (; it != end; ++it)
850 bibfields.push_back(*it);
851 sort(bibfields.begin(), bibfields.end());
856 vector<docstring> const BiblioInfo::getEntries() const
858 vector<docstring> bibentries;
859 set<docstring>::const_iterator it = entry_types_.begin();
860 set<docstring>::const_iterator end = entry_types_.end();
861 for (; it != end; ++it)
862 bibentries.push_back(*it);
863 sort(bibentries.begin(), bibentries.end());
868 docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key, Buffer const & buf) const
870 BiblioInfo::const_iterator it = find(key);
873 BibTeXInfo const & data = it->second;
874 return data.getAbbreviatedAuthor(&buf, false);
878 docstring const BiblioInfo::getCiteNumber(docstring const & key) const
880 BiblioInfo::const_iterator it = find(key);
883 BibTeXInfo const & data = it->second;
884 return data.citeNumber();
888 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
890 BiblioInfo::const_iterator it = find(key);
893 BibTeXInfo const & data = it->second;
894 docstring year = data.getYear();
896 // let's try the crossrefs
897 vector<docstring> const xrefs = getXRefs(data);
901 vector<docstring>::const_iterator it = xrefs.begin();
902 vector<docstring>::const_iterator en = xrefs.end();
903 for (; it != en; ++it) {
904 BiblioInfo::const_iterator const xrefit = find(*it);
907 BibTeXInfo const & xref_data = xrefit->second;
908 year = xref_data.getYear();
914 if (use_modifier && data.modifier() != 0)
915 year += data.modifier();
920 docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, bool use_modifier) const
922 docstring const year = getYear(key, use_modifier);
924 return buf.B_("No year");
929 docstring const BiblioInfo::getInfo(docstring const & key,
930 Buffer const & buf, bool richtext) const
932 BiblioInfo::const_iterator it = find(key);
934 return docstring(_("Bibliography entry not found!"));
935 BibTeXInfo const & data = it->second;
936 BibTeXInfoList xrefptrs;
937 vector<docstring> const xrefs = getXRefs(data);
938 if (!xrefs.empty()) {
939 vector<docstring>::const_iterator it = xrefs.begin();
940 vector<docstring>::const_iterator en = xrefs.end();
941 for (; it != en; ++it) {
942 BiblioInfo::const_iterator const xrefit = find(*it);
944 xrefptrs.push_back(&(xrefit->second));
947 return data.getInfo(xrefptrs, buf, richtext);
951 docstring const BiblioInfo::getLabel(vector<docstring> keys,
952 Buffer const & buf, string const & style, bool for_xhtml,
953 size_t max_size, docstring const & before, docstring const & after,
954 docstring const & dialog) const
956 // shorter makes no sense
957 LASSERT(max_size >= 16, max_size = 16);
959 // we can't display more than 10 of these, anyway
960 bool const too_many_keys = keys.size() > 10;
964 CiteEngineType const engine_type = buf.params().citeEngineType();
965 DocumentClass const & dc = buf.params().documentClass();
966 docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
967 docstring ret = format;
968 vector<docstring>::const_iterator key = keys.begin();
969 vector<docstring>::const_iterator ken = keys.end();
970 for (; key != ken; ++key) {
971 BiblioInfo::const_iterator it = find(*key);
972 BibTeXInfo empty_data;
973 empty_data.key(*key);
974 BibTeXInfo & data = empty_data;
975 vector<BibTeXInfo const *> xrefptrs;
978 vector<docstring> const xrefs = getXRefs(data);
979 if (!xrefs.empty()) {
980 vector<docstring>::const_iterator it = xrefs.begin();
981 vector<docstring>::const_iterator en = xrefs.end();
982 for (; it != en; ++it) {
983 BiblioInfo::const_iterator const xrefit = find(*it);
985 xrefptrs.push_back(&(xrefit->second));
989 ret = data.getLabel(xrefptrs, buf, ret, for_xhtml,
990 before, after, dialog, key + 1 != ken);
994 ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
995 support::truncateWithEllipsis(ret, max_size);
1000 bool BiblioInfo::isBibtex(docstring const & key) const
1003 split(key, key1, ',');
1004 BiblioInfo::const_iterator it = find(key1);
1007 return it->second.isBibTeX();
1011 vector<docstring> const BiblioInfo::getCiteStrings(
1012 vector<docstring> const & keys, vector<CitationStyle> const & styles,
1013 Buffer const & buf, docstring const & before,
1014 docstring const & after, docstring const & dialog, size_t max_size) const
1017 return vector<docstring>();
1020 vector<docstring> vec(styles.size());
1021 for (size_t i = 0; i != vec.size(); ++i) {
1022 style = styles[i].name;
1023 vec[i] = getLabel(keys, buf, style, false, max_size, before, after, dialog);
1030 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
1032 bimap_.insert(info.begin(), info.end());
1033 field_names_.insert(info.field_names_.begin(), info.field_names_.end());
1034 entry_types_.insert(info.entry_types_.begin(), info.entry_types_.end());
1040 // used in xhtml to sort a list of BibTeXInfo objects
1041 bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
1043 docstring const lauth = lhs->getAbbreviatedAuthor();
1044 docstring const rauth = rhs->getAbbreviatedAuthor();
1045 docstring const lyear = lhs->getYear();
1046 docstring const ryear = rhs->getYear();
1047 docstring const ltitl = lhs->operator[]("title");
1048 docstring const rtitl = rhs->operator[]("title");
1049 return (lauth < rauth)
1050 || (lauth == rauth && lyear < ryear)
1051 || (lauth == rauth && lyear == ryear && ltitl < rtitl);
1057 void BiblioInfo::collectCitedEntries(Buffer const & buf)
1059 cited_entries_.clear();
1060 // We are going to collect all the citation keys used in the document,
1061 // getting them from the TOC.
1062 // FIXME We may want to collect these differently, in the first case,
1063 // so that we might have them in order of appearance.
1064 set<docstring> citekeys;
1065 shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
1066 Toc::const_iterator it = toc->begin();
1067 Toc::const_iterator const en = toc->end();
1068 for (; it != en; ++it) {
1069 if (it->str().empty())
1071 vector<docstring> const keys = getVectorFromString(it->str());
1072 citekeys.insert(keys.begin(), keys.end());
1074 if (citekeys.empty())
1077 // We have a set of the keys used in this document.
1078 // We will now convert it to a list of the BibTeXInfo objects used in
1080 vector<BibTeXInfo const *> bi;
1081 set<docstring>::const_iterator cit = citekeys.begin();
1082 set<docstring>::const_iterator const cen = citekeys.end();
1083 for (; cit != cen; ++cit) {
1084 BiblioInfo::const_iterator const bt = find(*cit);
1085 if (bt == end() || !bt->second.isBibTeX())
1087 bi.push_back(&(bt->second));
1090 sort(bi.begin(), bi.end(), lSorter);
1092 // Now we can write the sorted keys
1093 vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
1094 vector<BibTeXInfo const *>::const_iterator ben = bi.end();
1095 for (; bit != ben; ++bit)
1096 cited_entries_.push_back((*bit)->key());
1100 void BiblioInfo::makeCitationLabels(Buffer const & buf)
1102 collectCitedEntries(buf);
1103 CiteEngineType const engine_type = buf.params().citeEngineType();
1104 bool const numbers = (engine_type & ENGINE_TYPE_NUMERICAL);
1108 // used to remember the last one we saw
1109 // we'll be comparing entries to see if we need to add
1110 // modifiers, like "1984a"
1111 map<docstring, BibTeXInfo>::iterator last;
1113 vector<docstring>::const_iterator it = cited_entries_.begin();
1114 vector<docstring>::const_iterator const en = cited_entries_.end();
1115 for (; it != en; ++it) {
1116 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1117 // this shouldn't happen, but...
1118 if (biit == bimap_.end())
1119 // ...fail gracefully, anyway.
1121 BibTeXInfo & entry = biit->second;
1123 docstring const num = convert<docstring>(++keynumber);
1124 entry.setCiteNumber(num);
1126 // coverity complains about our derefercing the iterator last,
1127 // which was not initialized above. but it does get initialized
1128 // after the first time through the loop, which is the point of
1130 // coverity[FORWARD_NULL]
1131 if (it != cited_entries_.begin()
1132 && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
1133 // we access the year via getYear() so as to get it from the xref,
1134 // if we need to do so
1135 && getYear(entry.key()) == getYear(last->second.key())) {
1136 if (modifier == 0) {
1137 // so the last one should have been 'a'
1138 last->second.setModifier('a');
1140 } else if (modifier == 'z')
1147 entry.setModifier(modifier);
1148 // remember the last one
1153 it = cited_entries_.begin();
1154 for (; it != en; ++it) {
1155 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1156 // this shouldn't happen, but...
1157 if (biit == bimap_.end())
1158 // ...fail gracefully, anyway.
1160 BibTeXInfo & entry = biit->second;
1162 entry.label(entry.citeNumber());
1164 docstring const auth = entry.getAbbreviatedAuthor(&buf, false);
1165 // we do it this way so as to access the xref, if necessary
1166 // note that this also gives us the modifier
1167 docstring const year = getYear(*it, buf, true);
1168 if (!auth.empty() && !year.empty())
1169 entry.label(auth + ' ' + year);
1171 entry.label(entry.key());
1177 //////////////////////////////////////////////////////////////////////
1181 //////////////////////////////////////////////////////////////////////
1184 CitationStyle citationStyleFromString(string const & command,
1185 BufferParams const & params)
1188 if (command.empty())
1191 string const alias = params.getCiteAlias(command);
1192 string cmd = alias.empty() ? command : alias;
1193 if (isUpperCase(command[0])) {
1194 cs.forceUpperCase = true;
1195 cmd[0] = lowercase(cmd[0]);
1198 size_t const n = command.size() - 1;
1199 if (command[n] == '*') {
1200 cs.hasStarredVersion = true;
1201 if (suffixIs(cmd, '*'))
1202 cmd = cmd.substr(0, cmd.size() - 1);
1210 string citationStyleToString(const CitationStyle & cs, bool const latex)
1212 string cmd = latex ? cs.cmd : cs.name;
1213 if (cs.forceUpperCase)
1214 cmd[0] = uppercase(cmd[0]);
1215 if (cs.hasStarredVersion)