X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2FBiblioInfo.cpp;h=253fb3759cbb7d50a30d9d6c701dd62330c18530;hb=26ba2a65838731ce639a09539f617cb0f0be3b22;hp=cefee85ba9ae154b2390d103ed895f2c9166bf2a;hpb=196d9caeb0b9f74d02750f774de1ca63a483803f;p=lyx.git diff --git a/src/BiblioInfo.cpp b/src/BiblioInfo.cpp index cefee85ba9..253fb3759c 100644 --- a/src/BiblioInfo.cpp +++ b/src/BiblioInfo.cpp @@ -5,7 +5,7 @@ * * \author Angus Leeming * \author Herbert Voß - * \author Richard Heck + * \author Richard Kimberly Heck * \author Julien Rioux * \author Jürgen Spitzmüller * @@ -15,14 +15,15 @@ #include #include "BiblioInfo.h" + #include "Buffer.h" #include "BufferParams.h" #include "Citation.h" #include "Encoding.h" #include "Language.h" -#include "xml.h" #include "TextClass.h" #include "TocBackend.h" +#include "xml.h" #include "support/convert.h" #include "support/debug.h" @@ -31,10 +32,10 @@ #include "support/gettext.h" #include "support/lassert.h" #include "support/lstrings.h" -#include "support/regex.h" #include "support/textutils.h" #include +#include #include using namespace std; @@ -276,8 +277,14 @@ vector const getAuthors(docstring const & author) // in author names, but can happen (consider cases such as "C \& A Corp."). docstring iname = subst(author, from_ascii("&"), from_ascii("$$amp!")); // Then, we temporarily make all " and " strings to ampersands in order - // to handle them later on a per-char level. - iname = subst(iname, from_ascii(" and "), from_ascii(" & ")); + // to handle them later on a per-char level. Note that arbitrary casing + // ("And", "AND", "aNd", ...) is allowed in bibtex (#10465). + static regex const and_reg("(.* )([aA][nN][dD])( .*)"); + smatch sub; + string res = to_utf8(iname); + while (regex_match(res, sub, and_reg)) + res = sub.str(1) + "&" + sub.str(3); + iname = from_utf8(res); // Now we traverse through the string and replace the "&" by the proper // output in- and outside groups docstring name; @@ -326,6 +333,7 @@ docstring convertLaTeXCommands(docstring const & str) bool scanning_cmd = false; bool scanning_math = false; + bool is_section = false; bool escaped = false; // used to catch \$, etc. while (!val.empty()) { char_type const ch = val[0]; @@ -348,13 +356,24 @@ docstring convertLaTeXCommands(docstring const & str) // discard characters until we hit something that // isn't alpha. if (scanning_cmd) { + if (!is_section && ch == 'S') { + is_section = true; + val = val.substr(1); + continue; + } if (isAlphaASCII(ch)) { + is_section = false; val = val.substr(1); escaped = false; continue; + } else if (is_section) { + ret.push_back(0x00a7); + is_section = false; + continue; } // so we're done with this command. // now we fall through and check this character. + is_section = false; scanning_cmd = false; } @@ -371,6 +390,12 @@ docstring convertLaTeXCommands(docstring const & str) continue; } + if (ch == '~') { + ret += char_type(0x00a0); + val = val.substr(1); + continue; + } + if (ch == '$') { ret += ch; val = val.substr(1); @@ -382,8 +407,8 @@ docstring convertLaTeXCommands(docstring const & str) // {\v a} to \v{a} (see #9340). // FIXME: This is a sort of mini-tex2lyx. // Use the real tex2lyx instead! - static lyx::regex const tma_reg("^\\{\\\\[bcCdfGhHkrtuUv]\\s\\w\\}"); - if (lyx::regex_search(to_utf8(val), tma_reg)) { + static regex const tma_reg("^\\{\\\\[bcCdfGhHkrtuUv]\\s\\w\\}"); + if (regex_search(to_utf8(val), tma_reg)) { val = val.substr(1); val.replace(2, 1, from_ascii("{")); continue; @@ -410,8 +435,8 @@ docstring convertLaTeXCommands(docstring const & str) // look for that and change it, if necessary. // FIXME: This is a sort of mini-tex2lyx. // Use the real tex2lyx instead! - static lyx::regex const reg("^\\\\\\W\\w"); - if (lyx::regex_search(to_utf8(val), reg)) { + static regex const reg("^\\\\\\W\\w"); + if (regex_search(to_utf8(val), reg)) { val.insert(3, from_ascii("}")); val.insert(2, from_ascii("{")); } @@ -661,32 +686,54 @@ void BibTeXInfo::getLocators(docstring & doi, docstring & url, docstring & file) // get "file" entry from citation record file = operator[]("file"); - // Jabref case, field has a format: + // Jabref case, "file" field has a format (depending on exporter): // Description:Location:Filetype;Description:Location:Filetype... - // We will grab only first pdf + // or simply: + // Location;Location;... + // We will strip out the locations and return an \n-separated list if (!file.empty()) { - docstring ret, filedest, tmp; - ret = split(file, tmp, ':'); - tmp = split(ret, filedest, ':'); - //TODO howto deal with relative directories? - FileName f(to_utf8(filedest)); - if (f.exists()) - file = "file:///" + filedest; + docstring filelist; + vector files = getVectorFromString(file, from_ascii(";")); + for (auto const & f : files) { + // first try if we have Description:Location:Filetype + docstring ret, filedest, tmp; + ret = split(f, tmp, ':'); + tmp = split(ret, filedest, ':'); + if (filedest.empty()) + // we haven't, so use the whole string + filedest = f; + // TODO howto deal with relative directories? + FileName fn(to_utf8(filedest)); + if (fn.exists()) { + if (!filelist.empty()) + filelist += '\n'; + filelist += "file:///" + filedest; + } + } + if (!filelist.empty()) + file = filelist; } - // kbibtex case, format: + // kbibtex case, "localfile" field with format: // file1.pdf;file2.pdf - // We will grab only first pdf + // We will strip out the locations and return an \n-separated list docstring kfile; if (file.empty()) kfile = operator[]("localfile"); if (!kfile.empty()) { - docstring filedest, tmp; - tmp = split(kfile, filedest, ';'); - //TODO howto deal with relative directories? - FileName f(to_utf8(filedest)); - if (f.exists()) - file = "file:///" + filedest; + docstring filelist; + vector files = getVectorFromString(kfile, from_ascii(";")); + for (auto const & f : files) { + // TODO howto deal with relative directories? + FileName fn(to_utf8(f)); + if (fn.exists()) { + if (!filelist.empty()) + filelist += '\n'; + filelist = "file:///" + f; + } + } + if (!filelist.empty()) + file = filelist; } if (!url.empty()) @@ -1090,16 +1137,10 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, } docstring ret = operator[](key); - if (ret.empty() && !xrefs.empty()) { - // xr is a (reference to a) BibTeXInfo const * - for (auto const & xr : xrefs) { - if (xr && !(*xr)[key].empty()) { - ret = (*xr)[key]; - break; - } - } - } if (ret.empty()) { + docstring subtype; + if (contains(key, ':')) + subtype = from_ascii(token(key, ':', 1)); // some special keys // FIXME: dialog, textbefore and textafter have nothing to do with this if (key == "dialog" && ci.context == CiteItem::Dialog) @@ -1125,7 +1166,7 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, ret = cite_number_; else if (prefixIs(key, "ifmultiple:")) { // Return whether we have multiple authors - docstring const kind = operator[](from_ascii(key.substr(11))); + docstring const kind = operator[](subtype); if (multipleAuthors(kind)) ret = from_ascii("x"); // any non-empty string will do } @@ -1133,14 +1174,14 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, // Special key to provide abbreviated name list, // with respect to maxcitenames. Suitable for Bibliography // beginnings. - docstring const kind = operator[](from_ascii(key.substr(11))); + docstring const kind = operator[](subtype); ret = getAuthorList(&buf, kind, false, false, true); if (ci.forceUpperCase && isLowerCase(ret[0])) ret[0] = uppercase(ret[0]); } else if (prefixIs(key, "fullnames:")) { // Return a full name list. Suitable for Bibliography // beginnings. - docstring const kind = operator[](from_ascii(key.substr(10))); + docstring const kind = operator[](subtype); ret = getAuthorList(&buf, kind, true, false, true); if (ci.forceUpperCase && isLowerCase(ret[0])) ret[0] = uppercase(ret[0]); @@ -1148,7 +1189,7 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, // Special key to provide abbreviated name lists, // irrespective of maxcitenames. Suitable for Bibliography // beginnings. - docstring const kind = operator[](from_ascii(key.substr(15))); + docstring const kind = operator[](subtype); ret = getAuthorList(&buf, kind, false, true, true); if (ci.forceUpperCase && isLowerCase(ret[0])) ret[0] = uppercase(ret[0]); @@ -1156,14 +1197,14 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, // Special key to provide abbreviated name list, // with respect to maxcitenames. Suitable for further names inside a // bibliography item // (such as "ed. by ...") - docstring const kind = operator[](from_ascii(key.substr(11))); + docstring const kind = operator[](subtype); ret = getAuthorList(&buf, kind, false, false, true, false); if (ci.forceUpperCase && isLowerCase(ret[0])) ret[0] = uppercase(ret[0]); } else if (prefixIs(key, "fullbynames:")) { // Return a full name list. Suitable for further names inside a // bibliography item // (such as "ed. by ...") - docstring const kind = operator[](from_ascii(key.substr(10))); + docstring const kind = operator[](subtype); ret = getAuthorList(&buf, kind, true, false, true, false); if (ci.forceUpperCase && isLowerCase(ret[0])) ret[0] = uppercase(ret[0]); @@ -1171,7 +1212,7 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, // Special key to provide abbreviated name lists, // irrespective of maxcitenames. Suitable for further names inside a // bibliography item // (such as "ed. by ...") - docstring const kind = operator[](from_ascii(key.substr(15))); + docstring const kind = operator[](subtype); ret = getAuthorList(&buf, kind, false, true, true, false); if (ci.forceUpperCase && isLowerCase(ret[0])) ret[0] = uppercase(ret[0]); @@ -1236,6 +1277,34 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf, ret = getYear(); } + // If we have no result, check in the cross-ref'ed entries + if (ret.empty() && !xrefs.empty()) { + bool const biblatex = + buf.params().documentClass().citeFramework() == "biblatex"; + // xr is a (reference to a) BibTeXInfo const * + for (auto const & xr : xrefs) { + if (!xr) + continue; + // use empty BibTeXInfoList to avoid loops + BibTeXInfoList xr_dummy; + ret = xr->getValueForKey(oldkey, buf, ci, xr_dummy, maxsize); + if (!ret.empty()) + // success! + break; + // in biblatex, cross-ref'ed titles are mapped + // to booktitle. Same for subtitle etc. + if (biblatex && prefixIs(key, "book")) + ret = (*xr)[key.substr(4)]; + // likewise, author is maped onto bookauthor + else if (biblatex && contains(key, ":bookauthor")) + ret = xr->getValueForKey(subst(key, "bookauthor", "author"), + buf, ci, xr_dummy, maxsize); + if (!ret.empty()) + // success! + break; + } + } + if (cleanit) ret = xml::cleanAttr(ret); @@ -1401,7 +1470,7 @@ docstring const BiblioInfo::getInfo(docstring const & key, { BiblioInfo::const_iterator it = find(key); if (it == end()) - return docstring(_("Bibliography entry not found!")); + return _("Bibliography entry not found!"); BibTeXInfo const & data = it->second; BibTeXInfoList xrefptrs; for (docstring const & xref : getXRefs(data)) { @@ -1679,15 +1748,21 @@ string citationStyleToString(const CitationStyle & cs, bool const latex) } -docstring authorsToDocBookAuthorGroup(docstring const & authorsString, XMLStream & xs, Buffer const & buf) +void authorsToDocBookAuthorGroup(docstring const & authorsString, XMLStream & xs, Buffer const & buf, + const std::string type) { // This function closely mimics getAuthorList, but produces DocBook instead of text. // It has been greatly simplified, as the complete list of authors is always produced. No separators are required, // as the output has a database-like shape. // constructName has also been merged within, as it becomes really simple and leads to no copy-paste. + if (! type.empty() && (type != "author" && type != "book")) { + LYXERR0("ERROR! Unexpected author contribution `" << type <<"'."); + return; + } + if (authorsString.empty()) { - return docstring(); + return; } // Split the input list of authors into individual authors. @@ -1703,13 +1778,17 @@ docstring authorsToDocBookAuthorGroup(docstring const & authorsString, XMLStream auto it = authors.cbegin(); auto en = authors.cend(); for (size_t i = 0; it != en; ++it, ++i) { - xs << xml::StartTag("author"); + const std::string tag = (type.empty() || type == "author") ? "author" : "othercredit"; + const std::string attr = (type == "book") ? R"(class="other" otherclass="bookauthor")" : ""; + + xs << xml::StartTag(tag, attr); xs << xml::CR(); xs << xml::StartTag("personname"); xs << xml::CR(); - docstring name = *it; + const docstring name = *it; - // All authors go in a . If more structure is known, use it; otherwise (just "et al."), print it as such. + // All authors go in a . If more structure is known, use it; otherwise (just "et al."), + // print it as such. if (name == "others") { xs << buf.B_(etal); } else { @@ -1742,15 +1821,13 @@ docstring authorsToDocBookAuthorGroup(docstring const & authorsString, XMLStream xs << xml::EndTag("personname"); xs << xml::CR(); - xs << xml::EndTag("author"); + xs << xml::EndTag(tag); xs << xml::CR(); // Could add an affiliation after , but not stored in BibTeX. } xs << xml::EndTag("authorgroup"); xs << xml::CR(); - - return docstring(); } } // namespace lyx