X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBiblioInfo.cpp;h=243f78c59f6595fb7d24ac0cede957e04e2db87c;hb=925f933a19eb15c49467a60bbd2e4bf30adde294;hp=f6f0e8ba8f5370ab53f58e839dc6bda56f63b805;hpb=ba17d842b41521d58664a6aa5cdfb4cef53768b0;p=lyx.git diff --git a/src/BiblioInfo.cpp b/src/BiblioInfo.cpp index f6f0e8ba8f..243f78c59f 100644 --- a/src/BiblioInfo.cpp +++ b/src/BiblioInfo.cpp @@ -255,7 +255,8 @@ BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type) {} -docstring const BibTeXInfo::getAbbreviatedAuthor(bool jurabib_style) const +docstring const BibTeXInfo::getAbbreviatedAuthor( + Buffer const * buf, bool jurabib_style) const { if (!is_bibtex_) { docstring const opt = label(); @@ -296,37 +297,45 @@ docstring const BibTeXInfo::getAbbreviatedAuthor(bool jurabib_style) const docstring retval = familyName(authors[0]); - if (authors.size() == 2 && authors[1] != "others") - retval = bformat(from_ascii("%1$s and %2$s"), - familyName(authors[0]), familyName(authors[1])); - - if (authors.size() >= 2) - retval = bformat(from_ascii("%1$s et al."), - familyName(authors[0])); + if (authors.size() == 2 && authors[1] != "others") { + docstring const dformat = buf ? + buf->B_("%1$s and %2$s") : from_ascii("%1$s and %2$s"); + retval = bformat(dformat, familyName(authors[0]), familyName(authors[1])); + } else if (authors.size() >= 2) { + // we get here either if the author list is longer than two names + // or if the second 'name' is "others". we do the same thing either + // way. + docstring const dformat = buf ? + buf->B_("%1$s et al.") : from_ascii("%1$s et al."); + retval = bformat(dformat, familyName(authors[0])); + } return convertLaTeXCommands(retval); } -docstring const BibTeXInfo::getAbbreviatedAuthor(Buffer const & buf, bool jurabib_style) const -{ - docstring const author = getAbbreviatedAuthor(jurabib_style); - if (!is_bibtex_) - return author; - vector const authors = getVectorFromString(author, from_ascii(" and ")); - if (authors.size() == 2) - return bformat(buf.B_("%1$s and %2$s"), authors[0], authors[1]); - docstring::size_type const idx = author.rfind(from_ascii(" et al.")); - if (idx != docstring::npos) - return bformat(buf.B_("%1$s et al."), author.substr(0, idx)); - return author; -} - - docstring const BibTeXInfo::getYear() const { - if (is_bibtex_) - return operator[]("year"); + if (is_bibtex_) { + // first try legacy year field + docstring year = operator[]("year"); + if (!year.empty()) + return year; + // now try biblatex's date field + year = operator[]("date"); + // Format is [-]YYYY-MM-DD*/[-]YYYY-MM-DD* + // We only want the years. + static regex const yreg("[-]?([\\d]{4}).*"); + static regex const ereg(".*/[-]?([\\d]{4}).*"); + smatch sm; + string const date = to_utf8(year); + regex_match(date, sm, yreg); + year = from_ascii(sm[1]); + // check for an endyear + if (regex_match(date, sm, ereg)) + year += char_type(0x2013) + from_ascii(sm[1]); + return year; + } docstring const opt = label(); if (opt.empty()) @@ -343,14 +352,6 @@ docstring const BibTeXInfo::getYear() const } -docstring const BibTeXInfo::getXRef() const -{ - if (!is_bibtex_) - return docstring(); - return operator[]("crossref"); -} - - namespace { docstring parseOptions(docstring const & format, string & optkey, @@ -466,9 +467,17 @@ docstring parseOptions(docstring const & format, string & optkey, } // anon namespace - +/* FIXME +Bug #9131 revealed an oddity in how we are generating citation information +when more than one key is given. We end up building a longer and longer format +string as we go, which we then have to re-parse, over and over and over again, +rather than generating the information for the individual keys and then putting +all of that together. We do that to deal with the way separators work, from what +I can tell, but it still feels like a hack. Fixing this would require quite a +bit of work, however. +*/ docstring BibTeXInfo::expandFormat(docstring const & format, - BibTeXInfo const * const xref, int & counter, Buffer const & buf, + BibTeXInfoList const xrefs, int & counter, Buffer const & buf, docstring before, docstring after, docstring dialog, bool next) const { // incorrect use of macros could put us in an infinite loop @@ -488,7 +497,7 @@ docstring BibTeXInfo::expandFormat(docstring const & format, // we'll remove characters from the front of fmt as we // deal with them while (!fmt.empty()) { - if (counter++ > max_passes) { + if (counter > max_passes) { LYXERR0("Recursion limit reached while parsing `" << format << "'."); return _("ERROR!"); @@ -506,6 +515,7 @@ docstring BibTeXInfo::expandFormat(docstring const & format, string const val = buf.params().documentClass().getCiteMacro(engine_type, key); fmt = from_utf8(val) + fmt.substr(1); + counter += 1; continue; } else if (key[0] == '_') { // a translatable bit @@ -516,7 +526,7 @@ docstring BibTeXInfo::expandFormat(docstring const & format, ret << trans; } else { docstring const val = - getValueForKey(key, buf, before, after, dialog, xref, max_keysize); + getValueForKey(key, buf, before, after, dialog, xrefs, max_keysize); if (!scanning_rich) ret << from_ascii("{!!}"); ret << val; @@ -547,15 +557,18 @@ docstring BibTeXInfo::expandFormat(docstring const & format, return _("ERROR!"); fmt = newfmt; docstring const val = - getValueForKey(optkey, buf, before, after, dialog, xref); + getValueForKey(optkey, buf, before, after, dialog, xrefs); if (optkey == "next" && next) ret << ifpart; // without expansion - else if (!val.empty()) - ret << expandFormat(ifpart, xref, counter, buf, + else if (!val.empty()) { + int newcounter = 0; + ret << expandFormat(ifpart, xrefs, newcounter, buf, before, after, dialog, next); - else if (!elsepart.empty()) - ret << expandFormat(elsepart, xref, counter, buf, + } else if (!elsepart.empty()) { + int newcounter = 0; + ret << expandFormat(elsepart, xrefs, newcounter, buf, before, after, dialog, next); + } // fmt will have been shortened for us already continue; } @@ -602,7 +615,7 @@ docstring BibTeXInfo::expandFormat(docstring const & format, } -docstring const & BibTeXInfo::getInfo(BibTeXInfo const * const xref, +docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs, Buffer const & buf, bool richtext) const { if (!richtext && !info_.empty()) @@ -621,7 +634,7 @@ docstring const & BibTeXInfo::getInfo(BibTeXInfo const * const xref, docstring const & format = from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_))); int counter = 0; - info_ = expandFormat(format, xref, counter, buf, + info_ = expandFormat(format, xrefs, counter, buf, docstring(), docstring(), docstring(), false); if (info_.empty()) { @@ -639,7 +652,7 @@ docstring const & BibTeXInfo::getInfo(BibTeXInfo const * const xref, } -docstring const BibTeXInfo::getLabel(BibTeXInfo const * const xref, +docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs, Buffer const & buf, docstring const & format, bool richtext, docstring const & before, docstring const & after, docstring const & dialog, bool next) const @@ -647,7 +660,7 @@ docstring const BibTeXInfo::getLabel(BibTeXInfo const * const xref, docstring loclabel; int counter = 0; - loclabel = expandFormat(format, xref, counter, buf, + loclabel = expandFormat(format, xrefs, counter, buf, before, after, dialog, next); if (!loclabel.empty() && !next) { @@ -677,7 +690,7 @@ docstring const & BibTeXInfo::operator[](string const & field) const docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, docstring const & before, docstring const & after, docstring const & dialog, - BibTeXInfo const * const xref, size_t maxsize) const + BibTeXInfoList const xrefs, size_t maxsize) const { // anything less is pointless LASSERT(maxsize >= 16, maxsize = 16); @@ -689,8 +702,16 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, } docstring ret = operator[](key); - if (ret.empty() && xref) - ret = (*xref)[key]; + if (ret.empty() && !xrefs.empty()) { + vector::const_iterator it = xrefs.begin(); + vector::const_iterator en = xrefs.end(); + for (; it != en; ++it) { + if (*it && !(**it)[key].empty()) { + ret = (**it)[key]; + break; + } + } + } if (ret.empty()) { // some special keys // FIXME: dialog, textbefore and textafter have nothing to do with this @@ -708,12 +729,12 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, ret = cite_number_; else if (key == "abbrvauthor") // Special key to provide abbreviated author names. - ret = getAbbreviatedAuthor(buf, false); + ret = getAbbreviatedAuthor(&buf, false); else if (key == "shortauthor") // When shortauthor is not defined, jurabib automatically // provides jurabib-style abbreviated author names. We do // this as well. - ret = getAbbreviatedAuthor(buf, true); + ret = getAbbreviatedAuthor(&buf, true); else if (key == "shorttitle") { // When shorttitle is not defined, jurabib uses for `article' // and `periodical' entries the form `journal volume [year]' @@ -730,7 +751,7 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, docstring const & format = from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_))); int counter = 0; - ret = expandFormat(format, xref, counter, buf, + ret = expandFormat(format, xrefs, counter, buf, docstring(), docstring(), docstring(), false); } else if (key == "textbefore") ret = before; @@ -744,8 +765,7 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, ret = html::cleanAttr(ret); // make sure it is not too big - if (ret.size() > maxsize) - ret = ret.substr(0, maxsize - 3) + from_ascii("..."); + support::truncateWithEllipsis(ret, maxsize); return ret; } @@ -770,6 +790,46 @@ public: } // namespace anon +vector const BiblioInfo::getXRefs(BibTeXInfo const & data, bool const nested) const +{ + vector result; + if (!data.isBibTeX()) + return result; + // Legacy crossref field. This is not nestable. + if (!nested && !data["crossref"].empty()) { + docstring const xrefkey = data["crossref"]; + result.push_back(xrefkey); + // However, check for nested xdatas + BiblioInfo::const_iterator it = find(xrefkey); + if (it != end()) { + BibTeXInfo const & xref = it->second; + vector const nxdata = getXRefs(xref, true); + if (!nxdata.empty()) + result.insert(result.end(), nxdata.begin(), nxdata.end()); + } + } + // Biblatex's xdata field. Infinitely nestable. + // XData field can consist of a comma-separated list of keys + vector const xdatakeys = getVectorFromString(data["xdata"]); + if (!xdatakeys.empty()) { + vector::const_iterator xit = xdatakeys.begin(); + vector::const_iterator xen = xdatakeys.end(); + for (; xit != xen; ++xit) { + docstring const xdatakey = *xit; + result.push_back(xdatakey); + BiblioInfo::const_iterator it = find(xdatakey); + if (it != end()) { + BibTeXInfo const & xdata = it->second; + vector const nxdata = getXRefs(xdata, true); + if (!nxdata.empty()) + result.insert(result.end(), nxdata.begin(), nxdata.end()); + } + } + } + return result; +} + + vector const BiblioInfo::getKeys() const { vector bibkeys; @@ -811,7 +871,7 @@ docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key, Buffer c if (it == end()) return docstring(); BibTeXInfo const & data = it->second; - return data.getAbbreviatedAuthor(buf, false); + return data.getAbbreviatedAuthor(&buf, false); } @@ -833,17 +893,23 @@ docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) co BibTeXInfo const & data = it->second; docstring year = data.getYear(); if (year.empty()) { - // let's try the crossref - docstring const xref = data.getXRef(); - if (xref.empty()) + // let's try the crossrefs + vector const xrefs = getXRefs(data); + if (xrefs.empty()) // no luck return docstring(); - BiblioInfo::const_iterator const xrefit = find(xref); - if (xrefit == end()) - // no luck again - return docstring(); - BibTeXInfo const & xref_data = xrefit->second; - year = xref_data.getYear(); + vector::const_iterator it = xrefs.begin(); + vector::const_iterator en = xrefs.end(); + for (; it != en; ++it) { + BiblioInfo::const_iterator const xrefit = find(*it); + if (xrefit == end()) + continue; + BibTeXInfo const & xref_data = xrefit->second; + year = xref_data.getYear(); + if (!year.empty()) + // success! + break; + } } if (use_modifier && data.modifier() != 0) year += data.modifier(); @@ -867,21 +933,34 @@ docstring const BiblioInfo::getInfo(docstring const & key, if (it == end()) return docstring(_("Bibliography entry not found!")); BibTeXInfo const & data = it->second; - BibTeXInfo const * xrefptr = 0; - docstring const xref = data.getXRef(); - if (!xref.empty()) { - BiblioInfo::const_iterator const xrefit = find(xref); - if (xrefit != end()) - xrefptr = &(xrefit->second); + BibTeXInfoList xrefptrs; + vector const xrefs = getXRefs(data); + if (!xrefs.empty()) { + vector::const_iterator it = xrefs.begin(); + vector::const_iterator en = xrefs.end(); + for (; it != en; ++it) { + BiblioInfo::const_iterator const xrefit = find(*it); + if (xrefit != end()) + xrefptrs.push_back(&(xrefit->second)); + } } - return data.getInfo(xrefptr, buf, richtext); + return data.getInfo(xrefptrs, buf, richtext); } -docstring const BiblioInfo::getLabel(vector const & keys, - Buffer const & buf, string const & style, bool richtext, - docstring const & before, docstring const & after, docstring const & dialog) const +docstring const BiblioInfo::getLabel(vector keys, + Buffer const & buf, string const & style, bool for_xhtml, + size_t max_size, docstring const & before, docstring const & after, + docstring const & dialog) const { + // shorter makes no sense + LASSERT(max_size >= 16, max_size = 16); + + // we can't display more than 10 of these, anyway + bool const too_many_keys = keys.size() > 10; + if (too_many_keys) + keys.resize(10); + CiteEngineType const engine_type = buf.params().citeEngineType(); DocumentClass const & dc = buf.params().documentClass(); docstring const & format = from_utf8(dc.getCiteFormat(engine_type, style, "cite")); @@ -893,26 +972,36 @@ docstring const BiblioInfo::getLabel(vector const & keys, BibTeXInfo empty_data; empty_data.key(*key); BibTeXInfo & data = empty_data; - BibTeXInfo const * xrefptr = 0; + vector xrefptrs; if (it != end()) { data = it->second; - docstring const xref = data.getXRef(); - if (!xref.empty()) { - BiblioInfo::const_iterator const xrefit = find(xref); - if (xrefit != end()) - xrefptr = &(xrefit->second); + vector const xrefs = getXRefs(data); + if (!xrefs.empty()) { + vector::const_iterator it = xrefs.begin(); + vector::const_iterator en = xrefs.end(); + for (; it != en; ++it) { + BiblioInfo::const_iterator const xrefit = find(*it); + if (xrefit != end()) + xrefptrs.push_back(&(xrefit->second)); + } } } - ret = data.getLabel(xrefptr, buf, ret, richtext, - before, after, dialog, key+1 != ken); + ret = data.getLabel(xrefptrs, buf, ret, for_xhtml, + before, after, dialog, key + 1 != ken); } + + if (too_many_keys) + ret.push_back(0x2026);//HORIZONTAL ELLIPSIS + support::truncateWithEllipsis(ret, max_size); return ret; } bool BiblioInfo::isBibtex(docstring const & key) const { - BiblioInfo::const_iterator it = find(key); + docstring key1; + split(key, key1, ','); + BiblioInfo::const_iterator it = find(key1); if (it == end()) return false; return it->second.isBibTeX(); @@ -921,8 +1010,8 @@ bool BiblioInfo::isBibtex(docstring const & key) const vector const BiblioInfo::getCiteStrings( vector const & keys, vector const & styles, - Buffer const & buf, bool richtext, docstring const & before, - docstring const & after, docstring const & dialog) const + Buffer const & buf, docstring const & before, + docstring const & after, docstring const & dialog, size_t max_size) const { if (empty()) return vector(); @@ -931,7 +1020,7 @@ vector const BiblioInfo::getCiteStrings( vector vec(styles.size()); for (size_t i = 0; i != vec.size(); ++i) { style = styles[i].cmd; - vec[i] = getLabel(keys, buf, style, richtext, before, after, dialog); + vec[i] = getLabel(keys, buf, style, false, max_size, before, after, dialog); } return vec; @@ -973,9 +1062,9 @@ void BiblioInfo::collectCitedEntries(Buffer const & buf) // FIXME We may want to collect these differently, in the first case, // so that we might have them in order of appearance. set citekeys; - Toc const & toc = buf.tocBackend().toc("citation"); - Toc::const_iterator it = toc.begin(); - Toc::const_iterator const en = toc.end(); + shared_ptr toc = buf.tocBackend().toc("citation"); + Toc::const_iterator it = toc->begin(); + Toc::const_iterator const en = toc->end(); for (; it != en; ++it) { if (it->str().empty()) continue; @@ -1034,6 +1123,11 @@ void BiblioInfo::makeCitationLabels(Buffer const & buf) docstring const num = convert(++keynumber); entry.setCiteNumber(num); } else { + // coverity complains about our derefercing the iterator last, + // which was not initialized above. but it does get initialized + // after the first time through the loop, which is the point of + // the first test. + // coverity[FORWARD_NULL] if (it != cited_entries_.begin() && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor() // we access the year via getYear() so as to get it from the xref, @@ -1067,7 +1161,7 @@ void BiblioInfo::makeCitationLabels(Buffer const & buf) if (numbers) { entry.label(entry.citeNumber()); } else { - docstring const auth = entry.getAbbreviatedAuthor(buf, false); + docstring const auth = entry.getAbbreviatedAuthor(&buf, false); // we do it this way so as to access the xref, if necessary // note that this also gives us the modifier docstring const year = getYear(*it, buf, true); @@ -1114,7 +1208,7 @@ string citationStyleToString(const CitationStyle & cs) { string cmd = cs.cmd; if (cs.forceUpperCase) - cmd[0] = 'C'; + cmd[0] = uppercase(cmd[0]); if (cs.fullAuthorList) cmd += '*'; return cmd;