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"
22 #include "InsetIterator.h"
24 #include "output_xhtml.h"
25 #include "Paragraph.h"
26 #include "TextClass.h"
27 #include "TocBackend.h"
29 #include "support/convert.h"
30 #include "support/debug.h"
31 #include "support/docstream.h"
32 #include "support/gettext.h"
33 #include "support/lassert.h"
34 #include "support/lstrings.h"
35 #include "support/regex.h"
36 #include "support/textutils.h"
41 using namespace lyx::support;
48 // gets the "family name" from an author-type string
49 docstring familyName(docstring const & name)
54 // first we look for a comma, and take the last name to be everything
55 // preceding the right-most one, so that we also get the "jr" part.
56 docstring::size_type idx = name.rfind(',');
57 if (idx != docstring::npos)
58 return ltrim(name.substr(0, idx));
60 // OK, so now we want to look for the last name. We're going to
61 // include the "von" part. This isn't perfect.
62 // Split on spaces, to get various tokens.
63 vector<docstring> pieces = getVectorFromString(name, from_ascii(" "));
64 // If we only get two, assume the last one is the last name
65 if (pieces.size() <= 2)
68 // Now we look for the first token that begins with a lower case letter.
69 vector<docstring>::const_iterator it = pieces.begin();
70 vector<docstring>::const_iterator en = pieces.end();
71 for (; it != en; ++it) {
74 char_type const c = (*it)[0];
79 if (it == en) // we never found a "von"
82 // reconstruct what we need to return
85 for (; it != en; ++it) {
96 // converts a string containing LaTeX commands into unicode
98 docstring convertLaTeXCommands(docstring const & str)
103 bool scanning_cmd = false;
104 bool scanning_math = false;
105 bool escaped = false; // used to catch \$, etc.
106 while (!val.empty()) {
107 char_type const ch = val[0];
109 // if we're scanning math, we output everything until we
110 // find an unescaped $, at which point we break out.
117 scanning_math = false;
123 // if we're scanning a command name, then we just
124 // discard characters until we hit something that
127 if (isAlphaASCII(ch)) {
132 // so we're done with this command.
133 // now we fall through and check this character.
134 scanning_cmd = false;
137 // was the last character a \? If so, then this is something like:
138 // \\ or \$, so we'll just output it. That's probably not always right...
140 // exception: output \, as THIN SPACE
142 ret.push_back(0x2009);
153 scanning_math = true;
157 // we just ignore braces
158 if (ch == '{' || ch == '}') {
163 // we're going to check things that look like commands, so if
164 // this doesn't, just output it.
171 // ok, could be a command of some sort
172 // let's see if it corresponds to some unicode
173 // unicodesymbols has things in the form: \"{u},
174 // whereas we may see things like: \"u. So we'll
175 // look for that and change it, if necessary.
176 // FIXME: This is a sort of mini-tex2lyx.
177 // Use the real tex2lyx instead!
178 static lyx::regex const reg("^\\\\\\W\\w");
179 if (lyx::regex_search(to_utf8(val), reg)) {
180 val.insert(3, from_ascii("}"));
181 val.insert(2, from_ascii("{"));
185 docstring const cnvtd = Encodings::fromLaTeXCommand(val,
186 Encodings::TEXT_CMD, termination, rem);
187 if (!cnvtd.empty()) {
188 // it did, so we'll take that bit and proceed with what's left
193 // it's a command of some sort
202 // Escape '<' and '>' and remove richtext markers (e.g. {!this is richtext!}) from a string.
203 docstring processRichtext(docstring const & str, bool richtext)
208 bool scanning_rich = false;
209 while (!val.empty()) {
210 char_type const ch = val[0];
211 if (ch == '{' && val.size() > 1 && val[1] == '!') {
212 // beginning of rich text
213 scanning_rich = true;
217 if (scanning_rich && ch == '!' && val.size() > 1 && val[1] == '}') {
219 scanning_rich = false;
227 // we need to escape '<' and '>'
235 } else if (!scanning_rich /* && !richtext */)
237 // else the character is discarded, which will happen only if
238 // richtext == false and we are scanning rich text
247 //////////////////////////////////////////////////////////////////////
251 //////////////////////////////////////////////////////////////////////
253 BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
254 : is_bibtex_(true), bib_key_(key), entry_type_(type), info_(),
259 docstring const BibTeXInfo::getAbbreviatedAuthor(
260 Buffer const * buf, bool jurabib_style) const
263 docstring const opt = label();
268 docstring const remainder = trim(split(opt, authors, '('));
269 if (remainder.empty())
270 // in this case, we didn't find a "(",
271 // so we don't have author (year)
276 docstring author = operator[]("author");
277 if (author.empty()) {
278 author = operator[]("editor");
283 // FIXME Move this to a separate routine that can
284 // be called from elsewhere.
286 // OK, we've got some names. Let's format them.
287 // Try to split the author list on " and "
288 vector<docstring> const authors =
289 getVectorFromString(author, from_ascii(" and "));
291 if (jurabib_style && (authors.size() == 2 || authors.size() == 3)) {
292 docstring shortauthor = familyName(authors[0])
293 + "/" + familyName(authors[1]);
294 if (authors.size() == 3)
295 shortauthor += "/" + familyName(authors[2]);
296 return convertLaTeXCommands(shortauthor);
299 docstring retval = familyName(authors[0]);
301 if (authors.size() == 2 && authors[1] != "others") {
302 docstring const dformat = buf ?
303 buf->B_("%1$s and %2$s") : from_ascii("%1$s and %2$s");
304 retval = bformat(dformat, familyName(authors[0]), familyName(authors[1]));
305 } else if (authors.size() >= 2) {
306 // we get here either if the author list is longer than two names
307 // or if the second 'name' is "others". we do the same thing either
309 docstring const dformat = buf ?
310 buf->B_("%1$s et al.") : from_ascii("%1$s et al.");
311 retval = bformat(dformat, familyName(authors[0]));
314 return convertLaTeXCommands(retval);
318 docstring const BibTeXInfo::getYear() const
321 // first try legacy year field
322 docstring year = operator[]("year");
325 // now try biblatex's date field
326 year = operator[]("date");
327 // Format is [-]YYYY-MM-DD*/[-]YYYY-MM-DD*
328 // We only want the years.
329 static regex const yreg("[-]?([\\d]{4}).*");
330 static regex const ereg(".*/[-]?([\\d]{4}).*");
332 string const date = to_utf8(year);
333 regex_match(date, sm, yreg);
334 year = from_ascii(sm[1]);
335 // check for an endyear
336 if (regex_match(date, sm, ereg))
337 year += char_type(0x2013) + from_ascii(sm[1]);
341 docstring const opt = label();
346 docstring tmp = split(opt, authors, '(');
348 // we don't have author (year)
351 tmp = split(tmp, year, ')');
358 docstring parseOptions(docstring const & format, string & optkey,
359 docstring & ifpart, docstring & elsepart);
361 // Calls parseOptions to deal with an embedded option, such as:
362 // {%number%[[, no.~%number%]]}
363 // which must appear at the start of format. ifelsepart gets the
364 // whole of the option, and we return what's left after the option.
365 // we return format if there is an error.
366 docstring parseEmbeddedOption(docstring const & format, docstring & ifelsepart)
368 LASSERT(format[0] == '{' && format[1] == '%', return format);
372 docstring const rest = parseOptions(format, optkey, ifpart, elsepart);
373 if (format == rest) { // parse error
374 LYXERR0("ERROR! Couldn't parse `" << format <<"'.");
377 LASSERT(rest.size() <= format.size(),
378 { ifelsepart = docstring(); return format; });
379 ifelsepart = format.substr(0, format.size() - rest.size());
384 // Gets a "clause" from a format string, where the clause is
385 // delimited by '[[' and ']]'. Returns what is left after the
386 // clause is removed, and returns format if there is an error.
387 docstring getClause(docstring const & format, docstring & clause)
389 docstring fmt = format;
392 // we'll remove characters from the front of fmt as we
394 while (!fmt.empty()) {
395 if (fmt[0] == ']' && fmt.size() > 1 && fmt[1] == ']') {
400 // check for an embedded option
401 if (fmt[0] == '{' && fmt.size() > 1 && fmt[1] == '%') {
403 docstring const rest = parseEmbeddedOption(fmt, part);
405 LYXERR0("ERROR! Couldn't parse embedded option in `" << format <<"'.");
410 } else { // it's just a normal character
419 // parse an options string, which must appear at the start of the
420 // format parameter. puts the parsed bits in optkey, ifpart, and
421 // elsepart and returns what's left after the option is removed.
422 // if there's an error, it returns format itself.
423 docstring parseOptions(docstring const & format, string & optkey,
424 docstring & ifpart, docstring & elsepart)
426 LASSERT(format[0] == '{' && format[1] == '%', return format);
428 docstring fmt = format.substr(2);
429 size_t pos = fmt.find('%'); // end of key
430 if (pos == string::npos) {
431 LYXERR0("Error parsing `" << format <<"'. Can't find end of key.");
434 optkey = to_utf8(fmt.substr(0, pos));
435 fmt = fmt.substr(pos + 1);
436 // [[format]] should be next
437 if (fmt[0] != '[' || fmt[1] != '[') {
438 LYXERR0("Error parsing `" << format <<"'. Can't find '[[' after key.");
442 docstring curfmt = fmt;
443 fmt = getClause(curfmt, ifpart);
445 LYXERR0("Error parsing `" << format <<"'. Couldn't get if clause.");
449 if (fmt[0] == '}') // we're done, no else clause
450 return fmt.substr(1);
452 // else part should follow
453 if (fmt[0] != '[' || fmt[1] != '[') {
454 LYXERR0("Error parsing `" << format <<"'. Can't find else clause.");
459 fmt = getClause(curfmt, elsepart);
461 if (fmt == curfmt || fmt[0] != '}') {
462 LYXERR0("Error parsing `" << format <<"'. Can't find end of option.");
465 return fmt.substr(1);
472 Bug #9131 revealed an oddity in how we are generating citation information
473 when more than one key is given. We end up building a longer and longer format
474 string as we go, which we then have to re-parse, over and over and over again,
475 rather than generating the information for the individual keys and then putting
476 all of that together. We do that to deal with the way separators work, from what
477 I can tell, but it still feels like a hack. Fixing this would require quite a
478 bit of work, however.
480 docstring BibTeXInfo::expandFormat(docstring const & format,
481 BibTeXInfoList const xrefs, int & counter, Buffer const & buf,
482 CiteItem const & ci, bool next, bool second) const
484 // incorrect use of macros could put us in an infinite loop
485 static int const max_passes = 5000;
486 // the use of overly large keys can lead to performance problems, due
487 // to eventual attempts to convert LaTeX macros to unicode. See bug
488 // #8944. This is perhaps not the best solution, but it will have to
490 static size_t const max_keysize = 128;
491 odocstringstream ret; // return value
493 bool scanning_key = false;
494 bool scanning_rich = false;
496 CiteEngineType const engine_type = buf.params().citeEngineType();
497 docstring fmt = format;
498 // we'll remove characters from the front of fmt as we
500 while (!fmt.empty()) {
501 if (counter > max_passes) {
502 LYXERR0("Recursion limit reached while parsing `"
507 char_type thischar = fmt[0];
508 if (thischar == '%') {
509 // beginning or end of key
512 scanning_key = false;
513 // so we replace the key with its value, which may be empty
517 buf.params().documentClass().getCiteMacro(engine_type, key);
518 fmt = from_utf8(val) + fmt.substr(1);
521 } else if (key[0] == '_') {
522 // a translatable bit
524 buf.params().documentClass().getCiteMacro(engine_type, key);
525 docstring const trans =
526 translateIfPossible(from_utf8(val), buf.params().language->code());
529 docstring const val =
530 getValueForKey(key, buf, ci, xrefs, max_keysize);
532 ret << from_ascii("{!<span class=\"bib-" + key + "\">!}");
535 ret << from_ascii("{!</span>!}");
543 else if (thischar == '{') {
544 // beginning of option?
546 LYXERR0("ERROR: Found `{' when scanning key in `" << format << "'.");
549 if (fmt.size() > 1) {
551 // it is the beginning of an optional format
555 docstring const newfmt =
556 parseOptions(fmt, optkey, ifpart, elsepart);
557 if (newfmt == fmt) // parse error
560 docstring const val =
561 getValueForKey(optkey, buf, ci, xrefs);
562 if (optkey == "next" && next)
563 ret << ifpart; // without expansion
564 else if (!val.empty()) {
566 ret << expandFormat(ifpart, xrefs, newcounter, buf,
568 } else if (!elsepart.empty()) {
570 ret << expandFormat(elsepart, xrefs, newcounter, buf,
573 // fmt will have been shortened for us already
577 // beginning of rich text
578 scanning_rich = true;
580 ret << from_ascii("{!");
584 // we are here if '{' was not followed by % or !.
585 // So it's just a character.
588 else if (scanning_rich && thischar == '!'
589 && fmt.size() > 1 && fmt[1] == '}') {
591 scanning_rich = false;
593 ret << from_ascii("!}");
596 else if (scanning_key)
597 key += char(thischar);
601 } catch (EncodingException & /* e */) {
602 LYXERR0("Uncodable character '" << docstring(1, thischar) << " in citation label!");
608 LYXERR0("Never found end of key in `" << format << "'!");
612 LYXERR0("Never found end of rich text in `" << format << "'!");
619 docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
620 Buffer const & buf, CiteItem const & ci) const
622 bool const richtext = ci.richtext;
624 if (!richtext && !info_.empty())
626 if (richtext && !info_richtext_.empty())
627 return info_richtext_;
630 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
635 CiteEngineType const engine_type = buf.params().citeEngineType();
636 DocumentClass const & dc = buf.params().documentClass();
637 docstring const & format =
638 from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
640 info_ = expandFormat(format, xrefs, counter, buf,
644 // this probably shouldn't happen
649 info_richtext_ = convertLaTeXCommands(processRichtext(info_, true));
650 return info_richtext_;
653 info_ = convertLaTeXCommands(processRichtext(info_, false));
658 docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
659 Buffer const & buf, docstring const & format,
660 CiteItem const & ci, bool next, bool second) const
665 loclabel = expandFormat(format, xrefs, counter, buf, ci, next, second);
667 if (!loclabel.empty() && !next) {
668 loclabel = processRichtext(loclabel, ci.richtext);
669 loclabel = convertLaTeXCommands(loclabel);
676 docstring const & BibTeXInfo::operator[](docstring const & field) const
678 BibTeXInfo::const_iterator it = find(field);
681 static docstring const empty_value = docstring();
686 docstring const & BibTeXInfo::operator[](string const & field) const
688 return operator[](from_ascii(field));
692 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
693 CiteItem const & ci, 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
718 if (key == "dialog" && ci.context == CiteItem::Dialog)
719 ret = from_ascii("x"); // any non-empty string will do
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, ci, false, false);
755 } else if (key == "textbefore")
757 else if (key == "textafter")
759 else if (key == "year")
764 ret = html::cleanAttr(ret);
766 // make sure it is not too big
767 support::truncateWithEllipsis(ret, maxsize);
772 //////////////////////////////////////////////////////////////////////
776 //////////////////////////////////////////////////////////////////////
780 // A functor for use with sort, leading to case insensitive sorting
781 class compareNoCase: public binary_function<docstring, docstring, bool>
784 bool operator()(docstring const & s1, docstring const & s2) const {
785 return compare_no_case(s1, s2) < 0;
792 vector<docstring> const BiblioInfo::getXRefs(BibTeXInfo const & data, bool const nested) const
794 vector<docstring> result;
795 if (!data.isBibTeX())
797 // Legacy crossref field. This is not nestable.
798 if (!nested && !data["crossref"].empty()) {
799 docstring const xrefkey = data["crossref"];
800 result.push_back(xrefkey);
801 // However, check for nested xdatas
802 BiblioInfo::const_iterator it = find(xrefkey);
804 BibTeXInfo const & xref = it->second;
805 vector<docstring> const nxdata = getXRefs(xref, true);
807 result.insert(result.end(), nxdata.begin(), nxdata.end());
810 // Biblatex's xdata field. Infinitely nestable.
811 // XData field can consist of a comma-separated list of keys
812 vector<docstring> const xdatakeys = getVectorFromString(data["xdata"]);
813 if (!xdatakeys.empty()) {
814 vector<docstring>::const_iterator xit = xdatakeys.begin();
815 vector<docstring>::const_iterator xen = xdatakeys.end();
816 for (; xit != xen; ++xit) {
817 docstring const xdatakey = *xit;
818 result.push_back(xdatakey);
819 BiblioInfo::const_iterator it = find(xdatakey);
821 BibTeXInfo const & xdata = it->second;
822 vector<docstring> const nxdata = getXRefs(xdata, true);
824 result.insert(result.end(), nxdata.begin(), nxdata.end());
832 vector<docstring> const BiblioInfo::getKeys() const
834 vector<docstring> bibkeys;
835 BiblioInfo::const_iterator it = begin();
836 for (; it != end(); ++it)
837 bibkeys.push_back(it->first);
838 sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
843 vector<docstring> const BiblioInfo::getFields() const
845 vector<docstring> bibfields;
846 set<docstring>::const_iterator it = field_names_.begin();
847 set<docstring>::const_iterator end = field_names_.end();
848 for (; it != end; ++it)
849 bibfields.push_back(*it);
850 sort(bibfields.begin(), bibfields.end());
855 vector<docstring> const BiblioInfo::getEntries() const
857 vector<docstring> bibentries;
858 set<docstring>::const_iterator it = entry_types_.begin();
859 set<docstring>::const_iterator end = entry_types_.end();
860 for (; it != end; ++it)
861 bibentries.push_back(*it);
862 sort(bibentries.begin(), bibentries.end());
867 docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key, Buffer const & buf) const
869 BiblioInfo::const_iterator it = find(key);
872 BibTeXInfo const & data = it->second;
873 return data.getAbbreviatedAuthor(&buf, false);
877 docstring const BiblioInfo::getCiteNumber(docstring const & key) const
879 BiblioInfo::const_iterator it = find(key);
882 BibTeXInfo const & data = it->second;
883 return data.citeNumber();
887 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
889 BiblioInfo::const_iterator it = find(key);
892 BibTeXInfo const & data = it->second;
893 docstring year = data.getYear();
895 // let's try the crossrefs
896 vector<docstring> const xrefs = getXRefs(data);
900 vector<docstring>::const_iterator it = xrefs.begin();
901 vector<docstring>::const_iterator en = xrefs.end();
902 for (; it != en; ++it) {
903 BiblioInfo::const_iterator const xrefit = find(*it);
906 BibTeXInfo const & xref_data = xrefit->second;
907 year = xref_data.getYear();
913 if (use_modifier && data.modifier() != 0)
914 year += data.modifier();
919 docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, bool use_modifier) const
921 docstring const year = getYear(key, use_modifier);
923 return buf.B_("No year");
928 docstring const BiblioInfo::getInfo(docstring const & key,
929 Buffer const & buf, CiteItem const & ci) const
931 BiblioInfo::const_iterator it = find(key);
933 return docstring(_("Bibliography entry not found!"));
934 BibTeXInfo const & data = it->second;
935 BibTeXInfoList xrefptrs;
936 vector<docstring> const xrefs = getXRefs(data);
937 if (!xrefs.empty()) {
938 vector<docstring>::const_iterator it = xrefs.begin();
939 vector<docstring>::const_iterator en = xrefs.end();
940 for (; it != en; ++it) {
941 BiblioInfo::const_iterator const xrefit = find(*it);
943 xrefptrs.push_back(&(xrefit->second));
946 return data.getInfo(xrefptrs, buf, ci);
950 docstring const BiblioInfo::getLabel(vector<docstring> keys,
951 Buffer const & buf, string const & style, CiteItem const & ci) const
953 size_t max_size = ci.max_size;
954 // shorter makes no sense
955 LASSERT(max_size >= 16, max_size = 16);
957 // we can't display more than 10 of these, anyway
958 bool const too_many_keys = keys.size() > 10;
962 CiteEngineType const engine_type = buf.params().citeEngineType();
963 DocumentClass const & dc = buf.params().documentClass();
964 docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite"));
965 docstring ret = format;
966 vector<docstring>::const_iterator key = keys.begin();
967 vector<docstring>::const_iterator ken = keys.end();
968 for (; key != ken; ++key) {
969 BiblioInfo::const_iterator it = find(*key);
970 BibTeXInfo empty_data;
971 empty_data.key(*key);
972 BibTeXInfo & data = empty_data;
973 vector<BibTeXInfo const *> xrefptrs;
976 vector<docstring> const xrefs = getXRefs(data);
977 if (!xrefs.empty()) {
978 vector<docstring>::const_iterator it = xrefs.begin();
979 vector<docstring>::const_iterator en = xrefs.end();
980 for (; it != en; ++it) {
981 BiblioInfo::const_iterator const xrefit = find(*it);
983 xrefptrs.push_back(&(xrefit->second));
987 ret = data.getLabel(xrefptrs, buf, ret, ci, key + 1 != ken, i == 1);
991 ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
992 support::truncateWithEllipsis(ret, max_size);
997 bool BiblioInfo::isBibtex(docstring const & key) const
1000 split(key, key1, ',');
1001 BiblioInfo::const_iterator it = find(key1);
1004 return it->second.isBibTeX();
1008 vector<docstring> const BiblioInfo::getCiteStrings(
1009 vector<docstring> const & keys, vector<CitationStyle> const & styles,
1010 Buffer const & buf, CiteItem const & ci) const
1013 return vector<docstring>();
1016 vector<docstring> vec(styles.size());
1017 for (size_t i = 0; i != vec.size(); ++i) {
1018 style = styles[i].name;
1019 vec[i] = getLabel(keys, buf, style, ci);
1026 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
1028 bimap_.insert(info.begin(), info.end());
1029 field_names_.insert(info.field_names_.begin(), info.field_names_.end());
1030 entry_types_.insert(info.entry_types_.begin(), info.entry_types_.end());
1036 // used in xhtml to sort a list of BibTeXInfo objects
1037 bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
1039 docstring const lauth = lhs->getAbbreviatedAuthor();
1040 docstring const rauth = rhs->getAbbreviatedAuthor();
1041 docstring const lyear = lhs->getYear();
1042 docstring const ryear = rhs->getYear();
1043 docstring const ltitl = lhs->operator[]("title");
1044 docstring const rtitl = rhs->operator[]("title");
1045 return (lauth < rauth)
1046 || (lauth == rauth && lyear < ryear)
1047 || (lauth == rauth && lyear == ryear && ltitl < rtitl);
1053 void BiblioInfo::collectCitedEntries(Buffer const & buf)
1055 cited_entries_.clear();
1056 // We are going to collect all the citation keys used in the document,
1057 // getting them from the TOC.
1058 // FIXME We may want to collect these differently, in the first case,
1059 // so that we might have them in order of appearance.
1060 set<docstring> citekeys;
1061 shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
1062 Toc::const_iterator it = toc->begin();
1063 Toc::const_iterator const en = toc->end();
1064 for (; it != en; ++it) {
1065 if (it->str().empty())
1067 vector<docstring> const keys = getVectorFromString(it->str());
1068 citekeys.insert(keys.begin(), keys.end());
1070 if (citekeys.empty())
1073 // We have a set of the keys used in this document.
1074 // We will now convert it to a list of the BibTeXInfo objects used in
1076 vector<BibTeXInfo const *> bi;
1077 set<docstring>::const_iterator cit = citekeys.begin();
1078 set<docstring>::const_iterator const cen = citekeys.end();
1079 for (; cit != cen; ++cit) {
1080 BiblioInfo::const_iterator const bt = find(*cit);
1081 if (bt == end() || !bt->second.isBibTeX())
1083 bi.push_back(&(bt->second));
1086 sort(bi.begin(), bi.end(), lSorter);
1088 // Now we can write the sorted keys
1089 vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
1090 vector<BibTeXInfo const *>::const_iterator ben = bi.end();
1091 for (; bit != ben; ++bit)
1092 cited_entries_.push_back((*bit)->key());
1096 void BiblioInfo::makeCitationLabels(Buffer const & buf)
1098 collectCitedEntries(buf);
1099 CiteEngineType const engine_type = buf.params().citeEngineType();
1100 bool const numbers = (engine_type & ENGINE_TYPE_NUMERICAL);
1104 // used to remember the last one we saw
1105 // we'll be comparing entries to see if we need to add
1106 // modifiers, like "1984a"
1107 map<docstring, BibTeXInfo>::iterator last;
1109 vector<docstring>::const_iterator it = cited_entries_.begin();
1110 vector<docstring>::const_iterator const en = cited_entries_.end();
1111 for (; it != en; ++it) {
1112 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1113 // this shouldn't happen, but...
1114 if (biit == bimap_.end())
1115 // ...fail gracefully, anyway.
1117 BibTeXInfo & entry = biit->second;
1119 docstring const num = convert<docstring>(++keynumber);
1120 entry.setCiteNumber(num);
1122 // coverity complains about our derefercing the iterator last,
1123 // which was not initialized above. but it does get initialized
1124 // after the first time through the loop, which is the point of
1126 // coverity[FORWARD_NULL]
1127 if (it != cited_entries_.begin()
1128 && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
1129 // we access the year via getYear() so as to get it from the xref,
1130 // if we need to do so
1131 && getYear(entry.key()) == getYear(last->second.key())) {
1132 if (modifier == 0) {
1133 // so the last one should have been 'a'
1134 last->second.setModifier('a');
1136 } else if (modifier == 'z')
1143 entry.setModifier(modifier);
1144 // remember the last one
1149 it = cited_entries_.begin();
1150 for (; it != en; ++it) {
1151 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
1152 // this shouldn't happen, but...
1153 if (biit == bimap_.end())
1154 // ...fail gracefully, anyway.
1156 BibTeXInfo & entry = biit->second;
1158 entry.label(entry.citeNumber());
1160 docstring const auth = entry.getAbbreviatedAuthor(&buf, false);
1161 // we do it this way so as to access the xref, if necessary
1162 // note that this also gives us the modifier
1163 docstring const year = getYear(*it, buf, true);
1164 if (!auth.empty() && !year.empty())
1165 entry.label(auth + ' ' + year);
1167 entry.label(entry.key());
1173 //////////////////////////////////////////////////////////////////////
1177 //////////////////////////////////////////////////////////////////////
1180 CitationStyle citationStyleFromString(string const & command,
1181 BufferParams const & params)
1184 if (command.empty())
1187 string const alias = params.getCiteAlias(command);
1188 string cmd = alias.empty() ? command : alias;
1189 if (isUpperCase(command[0])) {
1190 cs.forceUpperCase = true;
1191 cmd[0] = lowercase(cmd[0]);
1194 size_t const n = command.size() - 1;
1195 if (command[n] == '*') {
1196 cs.hasStarredVersion = true;
1197 if (suffixIs(cmd, '*'))
1198 cmd = cmd.substr(0, cmd.size() - 1);
1206 string citationStyleToString(const CitationStyle & cs, bool const latex)
1208 string cmd = latex ? cs.cmd : cs.name;
1209 if (cs.forceUpperCase)
1210 cmd[0] = uppercase(cmd[0]);
1211 if (cs.hasStarredVersion)