]> git.lyx.org Git - lyx.git/blobdiff - src/BiblioInfo.cpp
Move <QTimer> from TocWidget.h
[lyx.git] / src / BiblioInfo.cpp
index a9f48e44577ed28eea1a66389b0699ad0b7c63d2..15705ce14488f8dc1ac8143e91d13ecc91163e16 100644 (file)
@@ -22,7 +22,7 @@
 #include "Encoding.h"
 #include "InsetIterator.h"
 #include "Language.h"
-#include "output_xhtml.h"
+#include "xml.h"
 #include "Paragraph.h"
 #include "TextClass.h"
 #include "TocBackend.h"
@@ -223,7 +223,7 @@ name_parts nameParts(docstring const & iname)
 }
 
 
-docstring constructName(docstring const & name, string const scheme)
+docstring constructName(docstring const & name, string const scheme)
 {
        // re-constructs a name from name parts according
        // to a given scheme
@@ -236,6 +236,8 @@ docstring constructName(docstring const & name, string const scheme)
        static regex const reg2("(.*)(\\{%suffix%\\[\\[)([^\\]]+)(\\]\\]\\})(.*)");
        static regex const reg3("(.*)(\\{%prefix%\\[\\[)([^\\]]+)(\\]\\]\\})(.*)");
        smatch sub;
+       // Changing the first parameter of regex_match() may corrupt the
+       // second one. In this case we use the temporary string tmp.
        if (regex_match(scheme, sub, reg1)) {
                res = sub.str(1);
                if (!prename.empty())
@@ -243,16 +245,16 @@ docstring constructName(docstring const & name, string const scheme)
                res += sub.str(5);
        }
        if (regex_match(res, sub, reg2)) {
-               res = sub.str(1);
+               string tmp = sub.str(1);
                if (!suffix.empty())
-                       res += sub.str(3);
-               res += sub.str(5);
+                       tmp += sub.str(3);
+               res = tmp + sub.str(5);
        }
        if (regex_match(res, sub, reg3)) {
-               res = sub.str(1);
+               string tmp = sub.str(1);
                if (!prefix.empty())
-                       res += sub.str(3);
-               res += sub.str(5);
+                       tmp += sub.str(3);
+               res = tmp + sub.str(5);
        }
        docstring result = from_ascii(res);
        result = subst(result, from_ascii("%prename%"), prename);
@@ -311,7 +313,7 @@ vector<docstring> const getAuthors(docstring const & author)
 }
 
 
-bool multipleAuthors(docstring const author)
+bool multipleAuthors(docstring const author)
 {
        return getAuthors(author).size() > 1;
 }
@@ -476,7 +478,7 @@ docstring processRichtext(docstring const & str, bool richtext)
        return ret;
 }
 
-} // anon namespace
+} // namespace
 
 
 //////////////////////////////////////////////////////////////////////
@@ -486,8 +488,8 @@ docstring processRichtext(docstring const & str, bool richtext)
 //////////////////////////////////////////////////////////////////////
 
 BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
-       : is_bibtex_(true), bib_key_(key), entry_type_(type), info_(),
-         modifier_(0)
+       : is_bibtex_(true), bib_key_(key), num_bib_key_(0), entry_type_(type),
+         info_(), format_(), modifier_(0)
 {}
 
 
@@ -503,8 +505,8 @@ docstring const BibTeXInfo::getAuthorOrEditorList(Buffer const * buf,
 }
 
 
-docstring const BibTeXInfo::getAuthorList(Buffer const * buf, 
-               docstring const & author, bool const full, bool const forceshort, 
+docstring const BibTeXInfo::getAuthorList(Buffer const * buf,
+               docstring const & author, bool const full, bool const forceshort,
                bool const allnames, bool const beginning) const
 {
        // Maxnames treshold depend on engine
@@ -522,6 +524,12 @@ docstring const BibTeXInfo::getAuthorList(Buffer const * buf,
                        // in this case, we didn't find a "(",
                        // so we don't have author (year)
                        return docstring();
+               if (full) {
+                       // Natbib syntax is "Jones et al.(1990)Jones, Baker, and Williams"
+                       docstring const fullauthors = trim(rsplit(remainder, ')'));
+                       if (!fullauthors.empty())
+                               return fullauthors;
+               }
                return authors;
        }
 
@@ -539,16 +547,16 @@ docstring const BibTeXInfo::getAuthorList(Buffer const * buf,
 
        // These are defined in the styles
        string const etal =
-               buf ? buf->params().documentClass().getCiteMacro(engine_type, "_etal")
+               buf ? buf->params().documentClass().getCiteMacro(engine_type, "B_etal")
                    : " et al.";
        string const namesep =
-               buf ? buf->params().documentClass().getCiteMacro(engine_type, "_namesep")
+               buf ? buf->params().documentClass().getCiteMacro(engine_type, "B_namesep")
                   : ", ";
        string const lastnamesep =
-               buf ? buf->params().documentClass().getCiteMacro(engine_type, "_lastnamesep")
+               buf ? buf->params().documentClass().getCiteMacro(engine_type, "B_lastnamesep")
                    : ", and ";
        string const pairnamesep =
-               buf ? buf->params().documentClass().getCiteMacro(engine_type, "_pairnamesep")
+               buf ? buf->params().documentClass().getCiteMacro(engine_type, "B_pairnamesep")
                     : " and ";
        string firstnameform =
                        buf ? buf->params().documentClass().getCiteMacro(engine_type, "!firstnameform")
@@ -643,6 +651,75 @@ docstring const BibTeXInfo::getYear() const
 }
 
 
+void BibTeXInfo::getLocators(docstring & doi, docstring & url, docstring & file) const
+{
+       if (is_bibtex_) {
+               // get "doi" entry from citation record
+               doi = operator[]("doi");
+               if (!doi.empty() && !prefixIs(doi,from_ascii("http")))
+                       doi = "https://doi.org/" + doi;
+               // get "url" entry from citation record
+               url = operator[]("url");
+               // get "file" entry from citation record
+               file = operator[]("file");
+
+               // Jabref case, field has a format:
+               // Description:Location:Filetype;Description:Location:Filetype...
+               // We will grab only first pdf
+               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;
+               }
+
+               // kbibtex case, format:
+               // file1.pdf;file2.pdf
+               // We will grab only first pdf
+               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;
+               }
+
+               if (!url.empty())
+                       return;
+
+               // try biblatex specific fields, see its manual
+               // 3.13.7 "Electronic Publishing Informationl"
+               docstring eprinttype = operator[]("eprinttype");
+               docstring eprint = operator[]("eprint");
+               if (eprint.empty())
+                       return;
+
+               if (eprinttype == "arxiv")
+                       url = "https://arxiv.org/abs/" + eprint;
+               if (eprinttype == "jstor")
+                       url = "https://www.jstor.org/stable/" + eprint;
+               if (eprinttype == "pubmed")
+                       url = "http://www.ncbi.nlm.nih.gov/pubmed/" + eprint;
+               if (eprinttype == "hdl")
+                       url = "https://hdl.handle.net/" + eprint;
+               if (eprinttype == "googlebooks")
+                       url = "http://books.google.com/books?id=" + eprint;
+
+               return;
+       }
+
+       // Here can be handled the bibliography environment. All one could do
+       // here is let LyX scan the entry for URL or HRef insets.
+}
+
+
 namespace {
 
 docstring parseOptions(docstring const & format, string & optkey,
@@ -756,11 +833,11 @@ docstring parseOptions(docstring const & format, string & optkey,
 }
 
 
-} // anon namespace
+} // 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 
+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
@@ -768,7 +845,7 @@ 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,
-               BibTeXInfoList const xrefs, int & counter, Buffer const & buf,
+               BibTeXInfoList const xrefs, int & counter, Buffer const & buf,
                CiteItem const & ci, bool next, bool second) const
 {
        // incorrect use of macros could put us in an infinite loop
@@ -809,13 +886,20 @@ docstring BibTeXInfo::expandFormat(docstring const & format,
                                        fmt = from_utf8(val) + fmt.substr(1);
                                        counter += 1;
                                        continue;
-                               } else if (key[0] == '_') {
-                                       // a translatable bit
+                               } else if (prefixIs(key, "B_")) {
+                                       // a translatable bit (to the Buffer language)
                                        string const val =
                                                buf.params().documentClass().getCiteMacro(engine_type, key);
                                        docstring const trans =
                                                translateIfPossible(from_utf8(val), buf.params().language->code());
                                        ret << trans;
+                               } else if (key[0] == '_') {
+                                       // a translatable bit (to the GUI language)
+                                       string const val =
+                                               buf.params().documentClass().getCiteMacro(engine_type, key);
+                                       docstring const trans =
+                                               translateIfPossible(from_utf8(val));
+                                       ret << trans;
                                } else {
                                        docstring const val =
                                                getValueForKey(key, buf, ci, xrefs, max_keysize);
@@ -911,13 +995,28 @@ docstring BibTeXInfo::expandFormat(docstring const & format,
 }
 
 
-docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
-       Buffer const & buf, CiteItem const & ci) const
+docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
+       Buffer const & buf, CiteItem const & ci, docstring const & format_in) const
 {
        bool const richtext = ci.richtext;
 
-       if (!richtext && !info_.empty())
+       CiteEngineType const engine_type = buf.params().citeEngineType();
+       DocumentClass const & dc = buf.params().documentClass();
+       docstring const & format = format_in.empty()? 
+                               from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)))
+                             : format_in;
+
+       if (format != format_) {
+               // clear caches since format changed
+               info_.clear();
+               info_richtext_.clear();
+               format_ = format;
+       }
+
+       if (!richtext && !info_.empty()) {
+               info_ = convertLaTeXCommands(processRichtext(info_, false));
                return info_;
+       }
        if (richtext && !info_richtext_.empty())
                return info_richtext_;
 
@@ -927,10 +1026,6 @@ docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
                return info_;
        }
 
-       CiteEngineType const engine_type = buf.params().citeEngineType();
-       DocumentClass const & dc = buf.params().documentClass();
-       docstring const & format =
-               from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
        int counter = 0;
        info_ = expandFormat(format, xrefs, counter, buf,
                ci, false, false);
@@ -950,7 +1045,7 @@ docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
 }
 
 
-docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
+docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
        Buffer const & buf, docstring const & format,
        CiteItem const & ci, bool next, bool second) const
 {
@@ -985,7 +1080,7 @@ docstring const & BibTeXInfo::operator[](string const & field) const
 
 
 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
-       CiteItem const & ci, BibTeXInfoList const xrefs, size_t maxsize) const
+       CiteItem const & ci, BibTeXInfoList const xrefs, size_t maxsize) const
 {
        // anything less is pointless
        LASSERT(maxsize >= 16, maxsize = 16);
@@ -998,11 +1093,10 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
 
        docstring ret = operator[](key);
        if (ret.empty() && !xrefs.empty()) {
-               vector<BibTeXInfo const *>::const_iterator it = xrefs.begin();
-               vector<BibTeXInfo const *>::const_iterator en = xrefs.end();
-               for (; it != en; ++it) {
-                       if (*it && !(**it)[key].empty()) {
-                               ret = (**it)[key];
+               // xr is a (reference to a) BibTeXInfo const *
+               for (auto const & xr : xrefs) {
+                       if (xr && !(*xr)[key].empty()) {
+                               ret = (*xr)[key];
                                break;
                        }
                }
@@ -1114,16 +1208,38 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
                        ret = ci.textBefore;
                else if (key == "textafter")
                        ret = ci.textAfter;
-               else if (key == "curpretext")
-                       ret = ci.getPretexts()[bib_key_];
-               else if (key == "curposttext")
-                       ret = ci.getPosttexts()[bib_key_];
-               else if (key == "year")
+               else if (key == "curpretext") {
+                       vector<pair<docstring, docstring>> pres = ci.getPretexts();
+                       vector<pair<docstring, docstring>>::iterator it = pres.begin();
+                       int numkey = 1;
+                       for (; it != pres.end() ; ++it) {
+                               if ((*it).first == bib_key_ && numkey == num_bib_key_) {
+                                       ret = (*it).second;
+                                       pres.erase(it);
+                                       break;
+                               }
+                               if ((*it).first == bib_key_)
+                                       ++numkey;
+                       }
+               } else if (key == "curposttext") {
+                       vector<pair<docstring, docstring>> posts = ci.getPosttexts();
+                       vector<pair<docstring, docstring>>::iterator it = posts.begin();
+                       int numkey = 1;
+                       for (; it != posts.end() ; ++it) {
+                               if ((*it).first == bib_key_ && numkey == num_bib_key_) {
+                                       ret = (*it).second;
+                                       posts.erase(it);
+                                       break;
+                               }
+                               if ((*it).first == bib_key_)
+                                       ++numkey;
+                       }
+               } else if (key == "year")
                        ret = getYear();
        }
 
        if (cleanit)
-               ret = html::cleanAttr(ret);
+               ret = xml::cleanAttr(ret);
 
        // make sure it is not too big
        support::truncateWithEllipsis(ret, maxsize);
@@ -1140,15 +1256,11 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
 namespace {
 
 // A functor for use with sort, leading to case insensitive sorting
-class compareNoCase: public binary_function<docstring, docstring, bool>
-{
-public:
-       bool operator()(docstring const & s1, docstring const & s2) const {
-               return compare_no_case(s1, s2) < 0;
-       }
-};
+bool compareNoCase(const docstring & a, const docstring & b) {
+       return compare_no_case(a, b) < 0;
+}
 
-} // namespace anon
+} // namespace
 
 
 vector<docstring> const BiblioInfo::getXRefs(BibTeXInfo const & data, bool const nested) const
@@ -1173,10 +1285,7 @@ vector<docstring> const BiblioInfo::getXRefs(BibTeXInfo const & data, bool const
        // XData field can consist of a comma-separated list of keys
        vector<docstring> const xdatakeys = getVectorFromString(data["xdata"]);
        if (!xdatakeys.empty()) {
-               vector<docstring>::const_iterator xit = xdatakeys.begin();
-               vector<docstring>::const_iterator xen = xdatakeys.end();
-               for (; xit != xen; ++xit) {
-                       docstring const xdatakey = *xit;
+               for (auto const & xdatakey : xdatakeys) {
                        result.push_back(xdatakey);
                        BiblioInfo::const_iterator it = find(xdatakey);
                        if (it != end()) {
@@ -1194,10 +1303,9 @@ vector<docstring> const BiblioInfo::getXRefs(BibTeXInfo const & data, bool const
 vector<docstring> const BiblioInfo::getKeys() const
 {
        vector<docstring> bibkeys;
-       BiblioInfo::const_iterator it  = begin();
-       for (; it != end(); ++it)
-               bibkeys.push_back(it->first);
-       sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
+       for (auto const & bi : *this)
+               bibkeys.push_back(bi.first);
+       sort(bibkeys.begin(), bibkeys.end(), &compareNoCase);
        return bibkeys;
 }
 
@@ -1205,10 +1313,8 @@ vector<docstring> const BiblioInfo::getKeys() const
 vector<docstring> const BiblioInfo::getFields() const
 {
        vector<docstring> bibfields;
-       set<docstring>::const_iterator it = field_names_.begin();
-       set<docstring>::const_iterator end = field_names_.end();
-       for (; it != end; ++it)
-               bibfields.push_back(*it);
+       for (auto const & fn : field_names_)
+               bibfields.push_back(fn);
        sort(bibfields.begin(), bibfields.end());
        return bibfields;
 }
@@ -1217,10 +1323,8 @@ vector<docstring> const BiblioInfo::getFields() const
 vector<docstring> const BiblioInfo::getEntries() const
 {
        vector<docstring> bibentries;
-       set<docstring>::const_iterator it = entry_types_.begin();
-       set<docstring>::const_iterator end = entry_types_.end();
-       for (; it != end; ++it)
-               bibentries.push_back(*it);
+       for (auto const & et : entry_types_)
+               bibentries.push_back(et);
        sort(bibentries.begin(), bibentries.end());
        return bibentries;
 }
@@ -1245,6 +1349,15 @@ docstring const BiblioInfo::getCiteNumber(docstring const & key) const
        return data.citeNumber();
 }
 
+void BiblioInfo::getLocators(docstring const & key, docstring & doi, docstring & url, docstring & file) const
+{
+       BiblioInfo::const_iterator it = find(key);
+        if (it == end())
+               return;
+       BibTeXInfo const & data = it->second;
+       data.getLocators(doi,url,file);
+}
+
 
 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
 {
@@ -1259,10 +1372,8 @@ docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) co
                if (xrefs.empty())
                        // no luck
                        return docstring();
-               vector<docstring>::const_iterator it = xrefs.begin();
-               vector<docstring>::const_iterator en = xrefs.end();
-               for (; it != en; ++it) {
-                       BiblioInfo::const_iterator const xrefit = find(*it);
+               for (docstring const & xref : xrefs) {
+                       BiblioInfo::const_iterator const xrefit = find(xref);
                        if (xrefit == end())
                                continue;
                        BibTeXInfo const & xref_data = xrefit->second;
@@ -1288,24 +1399,19 @@ docstring const BiblioInfo::getYear(docstring const & key, Buffer const & buf, b
 
 
 docstring const BiblioInfo::getInfo(docstring const & key,
-       Buffer const & buf, CiteItem const & ci) const
+       Buffer const & buf, CiteItem const & ci, docstring const & format) const
 {
        BiblioInfo::const_iterator it = find(key);
        if (it == end())
                return docstring(_("Bibliography entry not found!"));
        BibTeXInfo const & data = it->second;
        BibTeXInfoList xrefptrs;
-       vector<docstring> const xrefs = getXRefs(data);
-       if (!xrefs.empty()) {
-               vector<docstring>::const_iterator it = xrefs.begin();
-               vector<docstring>::const_iterator en = xrefs.end();
-               for (; it != en; ++it) {
-                       BiblioInfo::const_iterator const xrefit = find(*it);
-                       if (xrefit != end())
-                               xrefptrs.push_back(&(xrefit->second));
-               }
+       for (docstring const & xref : getXRefs(data)) {
+               BiblioInfo::const_iterator const xrefit = find(xref);
+               if (xrefit != end())
+                       xrefptrs.push_back(&(xrefit->second));
        }
-       return data.getInfo(xrefptrs, buf, ci);
+       return data.getInfo(xrefptrs, buf, ci, format);
 }
 
 
@@ -1317,9 +1423,15 @@ docstring const BiblioInfo::getLabel(vector<docstring> keys,
        LASSERT(max_size >= 16, max_size = 16);
 
        // we can't display more than 10 of these, anyway
+       // but since we truncate in the middle,
+       // we need to split into two halfs.
        bool const too_many_keys = keys.size() > 10;
-       if (too_many_keys)
-               keys.resize(10);
+       vector<docstring> lkeys;
+       if (too_many_keys) {
+               lkeys.insert(lkeys.end(), keys.end() - 5, keys.end());
+               keys.resize(5);
+               keys.insert(keys.end(), lkeys.begin(), lkeys.end());
+       }
 
        CiteEngineType const engine_type = buf.params().citeEngineType();
        DocumentClass const & dc = buf.params().documentClass();
@@ -1327,7 +1439,14 @@ docstring const BiblioInfo::getLabel(vector<docstring> keys,
        docstring ret = format;
        vector<docstring>::const_iterator key = keys.begin();
        vector<docstring>::const_iterator ken = keys.end();
+       vector<docstring> handled_keys;
        for (int i = 0; key != ken; ++key, ++i) {
+               handled_keys.push_back(*key);
+               int n = 0;
+               for (auto const & k : handled_keys) {
+                       if (k == *key)
+                               ++n;
+               }
                BiblioInfo::const_iterator it = find(*key);
                BibTeXInfo empty_data;
                empty_data.key(*key);
@@ -1335,23 +1454,18 @@ docstring const BiblioInfo::getLabel(vector<docstring> keys,
                vector<BibTeXInfo const *> xrefptrs;
                if (it != end()) {
                        data = it->second;
-                       vector<docstring> const xrefs = getXRefs(data);
-                       if (!xrefs.empty()) {
-                               vector<docstring>::const_iterator it = xrefs.begin();
-                               vector<docstring>::const_iterator en = xrefs.end();
-                               for (; it != en; ++it) {
-                                       BiblioInfo::const_iterator const xrefit = find(*it);
-                                       if (xrefit != end())
-                                               xrefptrs.push_back(&(xrefit->second));
-                               }
+                       for (docstring const & xref : getXRefs(data)) {
+                               BiblioInfo::const_iterator const xrefit = find(xref);
+                               if (xrefit != end())
+                                       xrefptrs.push_back(&(xrefit->second));
                        }
                }
+               data.numKey(n);
                ret = data.getLabel(xrefptrs, buf, ret, ci, key + 1 != ken, i == 1);
        }
 
-       if (too_many_keys)
-               ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
-       support::truncateWithEllipsis(ret, max_size);
+       support::truncateWithEllipsis(ret, max_size, true);
+
        return ret;
 }
 
@@ -1409,7 +1523,7 @@ bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
                || (lauth == rauth && lyear == ryear && ltitl < rtitl);
 }
 
-}
+} // namespace
 
 
 void BiblioInfo::collectCitedEntries(Buffer const & buf)
@@ -1420,13 +1534,11 @@ 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<docstring> citekeys;
-       shared_ptr<Toc const> 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())
+       Toc const & toc = *buf.tocBackend().toc("citation");
+       for (auto const & t : toc) {
+               if (t.str().empty())
                        continue;
-               vector<docstring> const keys = getVectorFromString(it->str());
+               vector<docstring> const keys = getVectorFromString(t.str());
                citekeys.insert(keys.begin(), keys.end());
        }
        if (citekeys.empty())
@@ -1436,10 +1548,8 @@ void BiblioInfo::collectCitedEntries(Buffer const & buf)
        // We will now convert it to a list of the BibTeXInfo objects used in
        // this document...
        vector<BibTeXInfo const *> bi;
-       set<docstring>::const_iterator cit = citekeys.begin();
-       set<docstring>::const_iterator const cen = citekeys.end();
-       for (; cit != cen; ++cit) {
-               BiblioInfo::const_iterator const bt = find(*cit);
+       for (auto const & ck : citekeys) {
+               BiblioInfo::const_iterator const bt = find(ck);
                if (bt == end() || !bt->second.isBibTeX())
                        continue;
                bi.push_back(&(bt->second));
@@ -1448,10 +1558,9 @@ void BiblioInfo::collectCitedEntries(Buffer const & buf)
        sort(bi.begin(), bi.end(), lSorter);
 
        // Now we can write the sorted keys
-       vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
-       vector<BibTeXInfo const *>::const_iterator ben = bi.end();
-       for (; bit != ben; ++bit)
-               cited_entries_.push_back((*bit)->key());
+       // b is a BibTeXInfo const *
+       for (auto const & b : bi)
+               cited_entries_.push_back(b->key());
 }
 
 
@@ -1468,10 +1577,9 @@ void BiblioInfo::makeCitationLabels(Buffer const & buf)
        // modifiers, like "1984a"
        map<docstring, BibTeXInfo>::iterator last = bimap_.end();
 
-       vector<docstring>::const_iterator it = cited_entries_.begin();
-       vector<docstring>::const_iterator const en = cited_entries_.end();
-       for (; it != en; ++it) {
-               map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
+       // add letters to years
+       for (auto const & ce : cited_entries_) {
+               map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(ce);
                // this shouldn't happen, but...
                if (biit == bimap_.end())
                        // ...fail gracefully, anyway.
@@ -1506,9 +1614,8 @@ void BiblioInfo::makeCitationLabels(Buffer const & buf)
                }
        }
        // Set the labels
-       it = cited_entries_.begin();
-       for (; it != en; ++it) {
-               map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
+       for (auto const & ce : cited_entries_) {
+               map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(ce);
                // this shouldn't happen, but...
                if (biit == bimap_.end())
                        // ...fail gracefully, anyway.
@@ -1520,7 +1627,7 @@ void BiblioInfo::makeCitationLabels(Buffer const & buf)
                        docstring const auth = entry.getAuthorOrEditorList(&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);
+                       docstring const year = getYear(ce, buf, true);
                        if (!auth.empty() && !year.empty())
                                entry.label(auth + ' ' + year);
                        else
@@ -1573,4 +1680,79 @@ string citationStyleToString(const CitationStyle & cs, bool const latex)
        return cmd;
 }
 
+
+docstring authorsToDocBookAuthorGroup(docstring const & authorsString, XMLStream & xs, Buffer const & buf)
+{
+       // 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 (authorsString.empty()) {
+               return docstring();
+       }
+
+       // Split the input list of authors into individual authors.
+       vector<docstring> const authors = getAuthors(authorsString);
+
+       // Retrieve the "et al." variation.
+       string const etal = buf.params().documentClass().getCiteMacro(buf.params().citeEngineType(), "_etal");
+
+       // Output the list of authors.
+       xs << xml::StartTag("authorgroup");
+       xs << xml::CR();
+
+       auto it = authors.cbegin();
+       auto en = authors.cend();
+       for (size_t i = 0; it != en; ++it, ++i) {
+               xs << xml::StartTag("author");
+               xs << xml::CR();
+               xs << xml::StartTag("personname");
+               xs << xml::CR();
+               docstring name = *it;
+
+               // All authors go in a <personname>. If more structure is known, use it; otherwise (just "et al."), print it as such.
+               if (name == "others") {
+                       xs << buf.B_(etal);
+               } else {
+                       name_parts parts = nameParts(name);
+                       if (! parts.prefix.empty()) {
+                               xs << xml::StartTag("honorific");
+                               xs << parts.prefix;
+                               xs << xml::EndTag("honorific");
+                               xs << xml::CR();
+                       }
+                       if (! parts.prename.empty()) {
+                               xs << xml::StartTag("firstname");
+                               xs << parts.prename;
+                               xs << xml::EndTag("firstname");
+                               xs << xml::CR();
+                       }
+                       if (! parts.surname.empty()) {
+                               xs << xml::StartTag("surname");
+                               xs << parts.surname;
+                               xs << xml::EndTag("surname");
+                               xs << xml::CR();
+                       }
+                       if (! parts.suffix.empty()) {
+                               xs << xml::StartTag("othername", "role=\"suffix\"");
+                               xs << parts.suffix;
+                               xs << xml::EndTag("othername");
+                               xs << xml::CR();
+                       }
+               }
+
+               xs << xml::EndTag("personname");
+               xs << xml::CR();
+               xs << xml::EndTag("author");
+               xs << xml::CR();
+
+               // Could add an affiliation after <personname>, but not stored in BibTeX.
+       }
+       xs << xml::EndTag("authorgroup");
+       xs << xml::CR();
+
+       return docstring();
+}
+
 } // namespace lyx