]> git.lyx.org Git - lyx.git/blob - src/insets/InsetBibtex.cpp
Fix use of std::regex_match
[lyx.git] / src / insets / InsetBibtex.cpp
1 /**
2  * \file InsetBibtex.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author Richard Heck (BibTeX parser improvements)
8  * \author Jürgen Spitzmüller
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "InsetBibtex.h"
16
17 #include "BiblioInfo.h"
18 #include "Buffer.h"
19 #include "BufferParams.h"
20 #include "CiteEnginesList.h"
21 #include "Cursor.h"
22 #include "DispatchResult.h"
23 #include "Encoding.h"
24 #include "Exporter.h"
25 #include "Format.h"
26 #include "FuncRequest.h"
27 #include "FuncStatus.h"
28 #include "LaTeXFeatures.h"
29 #include "output_latex.h"
30 #include "xml.h"
31 #include "OutputParams.h"
32 #include "PDFOptions.h"
33 #include "texstream.h"
34 #include "TextClass.h"
35 #include "TocBackend.h"
36
37 #include "frontends/alert.h"
38
39 #include "support/convert.h"
40 #include "support/debug.h"
41 #include "support/docstream.h"
42 #include "support/docstring_list.h"
43 #include "support/ExceptionMessage.h"
44 #include "support/FileNameList.h"
45 #include "support/filetools.h"
46 #include "support/regex.h"
47 #include "support/gettext.h"
48 #include "support/lstrings.h"
49 #include "support/os.h"
50 #include "support/PathChanger.h"
51 #include "support/textutils.h"
52
53 #include <limits>
54 #include <map>
55 #include <utility>
56
57 #include <iostream>
58
59 using namespace std;
60 using namespace lyx::support;
61
62 namespace lyx {
63
64 namespace Alert = frontend::Alert;
65 namespace os = support::os;
66
67
68 InsetBibtex::InsetBibtex(Buffer * buf, InsetCommandParams const & p)
69         : InsetCommand(buf, p)
70 {}
71
72
73 ParamInfo const & InsetBibtex::findInfo(string const & /* cmdName */)
74 {
75         static ParamInfo param_info_;
76         if (param_info_.empty()) {
77                 param_info_.add("btprint", ParamInfo::LATEX_OPTIONAL);
78                 param_info_.add("bibfiles", ParamInfo::LATEX_REQUIRED);
79                 param_info_.add("options", ParamInfo::LYX_INTERNAL);
80                 param_info_.add("encoding", ParamInfo::LYX_INTERNAL);
81                 param_info_.add("file_encodings", ParamInfo::LYX_INTERNAL);
82                 param_info_.add("biblatexopts", ParamInfo::LATEX_OPTIONAL);
83         }
84         return param_info_;
85 }
86
87
88 void InsetBibtex::doDispatch(Cursor & cur, FuncRequest & cmd)
89 {
90         switch (cmd.action()) {
91
92         case LFUN_INSET_EDIT:
93                 editDatabases(cmd.argument());
94                 break;
95
96         case LFUN_INSET_MODIFY: {
97                 InsetCommandParams p(BIBTEX_CODE);
98                 try {
99                         if (!InsetCommand::string2params(to_utf8(cmd.argument()), p)) {
100                                 cur.noScreenUpdate();
101                                 break;
102                         }
103                 } catch (ExceptionMessage const & message) {
104                         if (message.type_ == WarningException) {
105                                 Alert::warning(message.title_, message.details_);
106                                 cur.noScreenUpdate();
107                         } else
108                                 throw;
109                         break;
110                 }
111
112                 cur.recordUndo();
113                 setParams(p);
114                 cur.buffer()->clearBibFileCache();
115                 cur.forceBufferUpdate();
116                 break;
117         }
118
119         default:
120                 InsetCommand::doDispatch(cur, cmd);
121                 break;
122         }
123 }
124
125
126 bool InsetBibtex::getStatus(Cursor & cur, FuncRequest const & cmd,
127                 FuncStatus & flag) const
128 {
129         switch (cmd.action()) {
130         case LFUN_INSET_EDIT:
131                 flag.setEnabled(true);
132                 return true;
133
134         default:
135                 return InsetCommand::getStatus(cur, cmd, flag);
136         }
137 }
138
139
140 void InsetBibtex::editDatabases(docstring const & db) const
141 {
142         vector<docstring> bibfilelist = getVectorFromString(getParam("bibfiles"));
143
144         if (bibfilelist.empty())
145                 return;
146
147         size_t nr_databases = bibfilelist.size();
148         if (nr_databases > 1 && db.empty()) {
149                         docstring const engine = usingBiblatex() ? _("Biblatex") : _("BibTeX");
150                         docstring message = bformat(_("The %1$s[[BibTeX/Biblatex]] inset includes %2$s databases.\n"
151                                                        "If you proceed, all of them will be opened."),
152                                                         engine, convert<docstring>(nr_databases));
153                         int const ret = Alert::prompt(_("Open Databases?"),
154                                 message, 0, 1, _("&Cancel"), _("&Proceed"));
155
156                         if (ret == 0)
157                                 return;
158         }
159
160         vector<docstring>::const_iterator it = bibfilelist.begin();
161         vector<docstring>::const_iterator en = bibfilelist.end();
162         for (; it != en; ++it) {
163                 if (!db.empty() && db != *it)
164                         continue;
165                 FileName const bibfile = buffer().getBibfilePath(*it);
166                 theFormats().edit(buffer(), bibfile,
167                      theFormats().getFormatFromFile(bibfile));
168         }
169 }
170
171
172 bool InsetBibtex::usingBiblatex() const
173 {
174         return buffer().masterParams().useBiblatex();
175 }
176
177
178 docstring InsetBibtex::screenLabel() const
179 {
180         return usingBiblatex() ? _("Biblatex Generated Bibliography")
181                                : _("BibTeX Generated Bibliography");
182 }
183
184
185 docstring InsetBibtex::toolTip(BufferView const & /*bv*/, int /*x*/, int /*y*/) const
186 {
187         docstring tip = _("Databases:");
188         vector<docstring> bibfilelist = getVectorFromString(getParam("bibfiles"));
189
190         tip += "<ul>";
191         if (bibfilelist.empty())
192                 tip += "<li>" + _("none") + "</li>";
193         else
194                 for (docstring const & bibfile : bibfilelist)
195                         tip += "<li>" + bibfile + "</li>";
196         tip += "</ul>";
197
198         // Style-Options
199         bool toc = false;
200         docstring style = getParam("options"); // maybe empty! and with bibtotoc
201         docstring bibtotoc = from_ascii("bibtotoc");
202         if (prefixIs(style, bibtotoc)) {
203                 toc = true;
204                 if (contains(style, char_type(',')))
205                         style = split(style, bibtotoc, char_type(','));
206         }
207
208         docstring const btprint = getParam("btprint");
209         if (!usingBiblatex()) {
210                 tip += _("Style File:");
211                 tip += "<ul><li>" + (style.empty() ? _("none") : style) + "</li></ul>";
212
213                 tip += _("Lists:") + " ";
214                 if (btprint == "btPrintAll")
215                         tip += _("all references");
216                 else if (btprint == "btPrintNotCited")
217                         tip += _("all uncited references");
218                 else
219                         tip += _("all cited references");
220                 if (toc) {
221                         tip += ", ";
222                         tip += _("included in TOC");
223                 }
224                 if (!buffer().parent()
225                     && buffer().params().multibib == "child") {
226                         tip += "<br />";
227                         tip += _("Note: This bibliography is not output, since bibliographies in the master file "
228                                  "are not allowed with the setting 'Multiple bibliographies per child document'");
229                 }
230         } else {
231                 tip += _("Lists:") + " ";
232                 if (btprint == "bibbysection")
233                         tip += _("all reference units");
234                 else if (btprint == "btPrintAll")
235                         tip += _("all references");
236                 else
237                         tip += _("all cited references");
238                 if (toc) {
239                         tip += ", ";
240                         tip += _("included in TOC");
241                 }
242                 if (!getParam("biblatexopts").empty()) {
243                         tip += "<br />";
244                         tip += _("Options: ") + getParam("biblatexopts");
245                 }
246         }
247
248         return tip;
249 }
250
251
252 void InsetBibtex::latex(otexstream & os, OutputParams const & runparams) const
253 {
254         // The sequence of the commands:
255         // With normal BibTeX:
256         // 1. \bibliographystyle{style}
257         // 2. \addcontentsline{...} - if option bibtotoc set
258         // 3. \bibliography{database}
259         // With bibtopic:
260         // 1. \bibliographystyle{style}
261         // 2. \begin{btSect}{database}
262         // 3. \btPrint{Cited|NotCited|All}
263         // 4. \end{btSect}
264         // With Biblatex:
265         // \printbibliography[biblatexopts]
266         // or
267         // \bibbysection[biblatexopts] - if btprint is "bibbysection"
268
269         // chapterbib does not allow bibliographies in the master
270         if (!usingBiblatex() && !runparams.is_child
271             && buffer().params().multibib == "child")
272                 return;
273
274         if (runparams.inDeletedInset) {
275                 // We cannot strike-out bibligraphies,
276                 // so we just output a note.
277                 os << "\\textbf{"
278                    << buffer().B_("[BIBLIOGRAPHY DELETED!]")
279                    << "}";
280                 return;
281         }
282
283         string style = to_utf8(getParam("options")); // maybe empty! and with bibtotoc
284         string bibtotoc;
285         if (prefixIs(style, "bibtotoc")) {
286                 bibtotoc = "bibtotoc";
287                 if (contains(style, ','))
288                         style = split(style, bibtotoc, ',');
289         }
290
291         if (usingBiblatex()) {
292                 // Options
293                 string opts = to_utf8(getParam("biblatexopts"));
294                 // bibtotoc-Option
295                 if (!bibtotoc.empty())
296                         opts = opts.empty() ? "heading=bibintoc" : "heading=bibintoc," + opts;
297                 // The bibliography command
298                 docstring btprint = getParam("btprint");
299                 if (btprint == "btPrintAll")
300                         os << "\\nocite{*}\n";
301                 if (btprint == "bibbysection" && !buffer().masterParams().multibib.empty())
302                         os << "\\bibbysection";
303                 else
304                         os << "\\printbibliography";
305                 if (!opts.empty())
306                         os << "[" << opts << "]";
307                 os << "\n";
308         } else {// using BibTeX
309                 // Database(s)
310                 vector<pair<docstring, string>> const dbs =
311                         buffer().prepareBibFilePaths(runparams, getBibFiles(), false);
312                 vector<docstring> db_out;
313                 for (pair<docstring, string> const & db : dbs)
314                         db_out.push_back(db.first);
315                 // Style options
316                 if (style == "default")
317                         style = buffer().masterParams().defaultBiblioStyle();
318                 if (!style.empty() && !buffer().masterParams().useBibtopic()) {
319                         string base = buffer().masterBuffer()->prepareFileNameForLaTeX(style, ".bst", runparams.nice);
320                         FileName const try_in_file =
321                                 makeAbsPath(base + ".bst", buffer().filePath());
322                         bool const not_from_texmf = try_in_file.isReadableFile();
323                         // If this style does not come from texmf and we are not
324                         // exporting to .tex copy it to the tmp directory.
325                         // This prevents problems with spaces and 8bit characters
326                         // in the file name.
327                         if (!runparams.inComment && !runparams.dryrun && !runparams.nice &&
328                             not_from_texmf) {
329                                 // use new style name
330                                 DocFileName const in_file = DocFileName(try_in_file);
331                                 base = removeExtension(in_file.mangledFileName());
332                                 FileName const out_file = makeAbsPath(base + ".bst",
333                                                 buffer().masterBuffer()->temppath());
334                                 bool const success = in_file.copyTo(out_file);
335                                 if (!success) {
336                                         LYXERR0("Failed to copy '" << in_file
337                                                << "' to '" << out_file << "'");
338                                 }
339                         }
340                         // FIXME UNICODE
341                         os << "\\bibliographystyle{"
342                            << from_utf8(latex_path(buffer().prepareFileNameForLaTeX(base, ".bst", runparams.nice)))
343                            << "}\n";
344                 }
345                 // Warn about spaces in bst path. Warn only once.
346                 static bool warned_about_bst_spaces = false;
347                 if (!warned_about_bst_spaces && runparams.nice && contains(style, ' ')) {
348                         warned_about_bst_spaces = true;
349                         Alert::warning(_("Export Warning!"),
350                                        _("There are spaces in the path to your BibTeX style file.\n"
351                                                       "BibTeX will be unable to find it."));
352                 }
353                 // Encoding
354                 bool encoding_switched = false;
355                 Encoding const * const save_enc = runparams.encoding;
356                 docstring const encoding = getParam("encoding");
357                 if (!encoding.empty() && encoding != from_ascii("default")) {
358                         Encoding const * const enc = encodings.fromLyXName(to_ascii(encoding));
359                         if (enc != runparams.encoding) {
360                                 os << "\\bgroup";
361                                 switchEncoding(os.os(), buffer().params(), runparams, *enc, true);
362                                 runparams.encoding = enc;
363                                 encoding_switched = true;
364                         }
365                 }
366                 // Handle the bibtopic case
367                 if (!db_out.empty() && buffer().masterParams().useBibtopic()) {
368                         os << "\\begin{btSect}";
369                         if (!style.empty())
370                                 os << "[" << style << "]";
371                         os << "{" << getStringFromVector(db_out) << "}\n";
372                         docstring btprint = getParam("btprint");
373                         if (btprint.empty())
374                                 // default
375                                 btprint = from_ascii("btPrintCited");
376                         os << "\\" << btprint << "\n"
377                            << "\\end{btSect}\n";
378                 }
379                 // bibtotoc option
380                 if (!bibtotoc.empty() && !buffer().masterParams().useBibtopic()
381                     && !buffer().masterParams().documentClass().bibInToc()) {
382                         // set label for hyperref, see http://www.lyx.org/trac/ticket/6470
383                         if (buffer().masterParams().pdfoptions().use_hyperref)
384                                         os << "\\phantomsection";
385                         if (buffer().masterParams().documentClass().hasLaTeXLayout("chapter"))
386                                 os << "\\addcontentsline{toc}{chapter}{\\bibname}";
387                         else if (buffer().masterParams().documentClass().hasLaTeXLayout("section"))
388                                 os << "\\addcontentsline{toc}{section}{\\refname}";
389                 }
390                 // The bibliography command
391                 if (!db_out.empty() && !buffer().masterParams().useBibtopic()) {
392                         docstring btprint = getParam("btprint");
393                         if (btprint == "btPrintAll") {
394                                 os << "\\nocite{*}\n";
395                         }
396                         os << "\\bibliography{" << getStringFromVector(db_out) << "}\n";
397                 }
398                 if (encoding_switched){
399                         // Switch back
400                         switchEncoding(os.os(), buffer().params(),
401                                        runparams, *save_enc, true, true);
402                         os << "\\egroup" << breakln;
403                         runparams.encoding = save_enc;
404                 }
405         }
406 }
407
408
409 docstring_list InsetBibtex::getBibFiles() const
410 {
411         return getVectorFromString(getParam("bibfiles"));
412 }
413
414 namespace {
415
416         // methods for parsing bibtex files
417
418         typedef map<docstring, docstring> VarMap;
419
420         /// remove whitespace characters, optionally a single comma,
421         /// and further whitespace characters from the stream.
422         /// @return true if a comma was found, false otherwise
423         ///
424         bool removeWSAndComma(ifdocstream & ifs) {
425                 char_type ch;
426
427                 if (!ifs)
428                         return false;
429
430                 // skip whitespace
431                 do {
432                         ifs.get(ch);
433                 } while (ifs && isSpace(ch));
434
435                 if (!ifs)
436                         return false;
437
438                 if (ch != ',') {
439                         ifs.putback(ch);
440                         return false;
441                 }
442
443                 // skip whitespace
444                 do {
445                         ifs.get(ch);
446                 } while (ifs && isSpace(ch));
447
448                 if (ifs) {
449                         ifs.putback(ch);
450                 }
451
452                 return true;
453         }
454
455
456         enum charCase {
457                 makeLowerCase,
458                 keepCase
459         };
460
461         /// remove whitespace characters, read character sequence
462         /// not containing whitespace characters or characters in
463         /// delimChars, and remove further whitespace characters.
464         ///
465         /// @return true if a string of length > 0 could be read.
466         ///
467         bool readTypeOrKey(docstring & val, ifdocstream & ifs,
468                 docstring const & delimChars, docstring const & illegalChars,
469                 charCase chCase) {
470
471                 char_type ch;
472
473                 val.clear();
474
475                 if (!ifs)
476                         return false;
477
478                 // skip whitespace
479                 do {
480                         ifs.get(ch);
481                 } while (ifs && isSpace(ch));
482
483                 if (!ifs)
484                         return false;
485
486                 // read value
487                 while (ifs && !isSpace(ch) &&
488                        delimChars.find(ch) == docstring::npos &&
489                        illegalChars.find(ch) == docstring::npos)
490                 {
491                         if (chCase == makeLowerCase)
492                                 val += lowercase(ch);
493                         else
494                                 val += ch;
495                         ifs.get(ch);
496                 }
497
498                 if (illegalChars.find(ch) != docstring::npos) {
499                         ifs.putback(ch);
500                         return false;
501                 }
502
503                 // skip whitespace
504                 while (ifs && isSpace(ch)) {
505                         ifs.get(ch);
506                 }
507
508                 if (ifs) {
509                         ifs.putback(ch);
510                 }
511
512                 return val.length() > 0;
513         }
514
515         /// read subsequent bibtex values that are delimited with a #-character.
516         /// Concatenate all parts and replace names with the associated string in
517         /// the variable strings.
518         /// @return true if reading was successful (all single parts were delimited
519         /// correctly)
520         bool readValue(docstring & val, ifdocstream & ifs, const VarMap & strings) {
521
522                 char_type ch;
523
524                 val.clear();
525
526                 if (!ifs)
527                         return false;
528
529                 do {
530                         // skip whitespace
531                         do {
532                                 ifs.get(ch);
533                         } while (ifs && isSpace(ch));
534
535                         if (!ifs)
536                                 return false;
537
538                         // check for field type
539                         if (isDigitASCII(ch)) {
540
541                                 // read integer value
542                                 do {
543                                         val += ch;
544                                         ifs.get(ch);
545                                 } while (ifs && isDigitASCII(ch));
546
547                                 if (!ifs)
548                                         return false;
549
550                         } else if (ch == '"' || ch == '{') {
551                                 // set end delimiter
552                                 char_type delim = ch == '"' ? '"': '}';
553
554                                 // Skip whitespace
555                                 do {
556                                         ifs.get(ch);
557                                 } while (ifs && isSpace(ch));
558
559                                 if (!ifs)
560                                         return false;
561
562                                 // We now have the first non-whitespace character
563                                 // We'll collapse adjacent whitespace.
564                                 bool lastWasWhiteSpace = false;
565
566                                 // inside this delimited text braces must match.
567                                 // Thus we can have a closing delimiter only
568                                 // when nestLevel == 0
569                                 int nestLevel = 0;
570
571                                 while (ifs && (nestLevel > 0 || ch != delim)) {
572                                         if (isSpace(ch)) {
573                                                 lastWasWhiteSpace = true;
574                                                 ifs.get(ch);
575                                                 continue;
576                                         }
577                                         // We output the space only after we stop getting
578                                         // whitespace so as not to output any whitespace
579                                         // at the end of the value.
580                                         if (lastWasWhiteSpace) {
581                                                 lastWasWhiteSpace = false;
582                                                 val += ' ';
583                                         }
584
585                                         val += ch;
586
587                                         // update nesting level
588                                         switch (ch) {
589                                                 case '{':
590                                                         ++nestLevel;
591                                                         break;
592                                                 case '}':
593                                                         --nestLevel;
594                                                         if (nestLevel < 0)
595                                                                 return false;
596                                                         break;
597                                         }
598
599                                         if (ifs)
600                                                 ifs.get(ch);
601                                 }
602
603                                 if (!ifs)
604                                         return false;
605
606                                 // FIXME Why is this here?
607                                 ifs.get(ch);
608
609                                 if (!ifs)
610                                         return false;
611
612                         } else {
613
614                                 // reading a string name
615                                 docstring strName;
616
617                                 while (ifs && !isSpace(ch) && ch != '#' && ch != ',' && ch != '}' && ch != ')') {
618                                         strName += lowercase(ch);
619                                         ifs.get(ch);
620                                 }
621
622                                 if (!ifs)
623                                         return false;
624
625                                 // replace the string with its assigned value or
626                                 // discard it if it's not assigned
627                                 if (strName.length()) {
628                                         VarMap::const_iterator pos = strings.find(strName);
629                                         if (pos != strings.end()) {
630                                                 val += pos->second;
631                                         }
632                                 }
633                         }
634
635                         // skip WS
636                         while (ifs && isSpace(ch)) {
637                                 ifs.get(ch);
638                         }
639
640                         if (!ifs)
641                                 return false;
642
643                         // continue reading next value on concatenate with '#'
644                 } while (ch == '#');
645
646                 ifs.putback(ch);
647
648                 return true;
649         }
650 } // namespace
651
652
653 void InsetBibtex::collectBibKeys(InsetIterator const & /*di*/, FileNameList & checkedFiles) const
654 {
655         parseBibTeXFiles(checkedFiles);
656 }
657
658
659 void InsetBibtex::parseBibTeXFiles(FileNameList & checkedFiles) const
660 {
661         // This bibtex parser is a first step to parse bibtex files
662         // more precisely.
663         //
664         // - it reads the whole bibtex entry and does a syntax check
665         //   (matching delimiters, missing commas,...
666         // - it recovers from errors starting with the next @-character
667         // - it reads @string definitions and replaces them in the
668         //   field values.
669         // - it accepts more characters in keys or value names than
670         //   bibtex does.
671         //
672         // Officially bibtex does only support ASCII, but in practice
673         // you can use any encoding as long as some elements like keys
674         // and names are pure ASCII. We support specifying an encoding,
675         // and we convert the file from that (default is buffer encoding).
676         // We don't restrict keys to ASCII in LyX, since our own
677         // InsetBibitem can generate non-ASCII keys, and nonstandard
678         // 8bit clean bibtex forks exist.
679
680         BiblioInfo keylist;
681
682         docstring_list const files = getBibFiles();
683         for (auto const & bf : files) {
684                 FileName const bibfile = buffer().getBibfilePath(bf);
685                 if (bibfile.empty()) {
686                         LYXERR0("Unable to find path for " << bf << "!");
687                         continue;
688                 }
689                 if (find(checkedFiles.begin(), checkedFiles.end(), bibfile) != checkedFiles.end())
690                         // already checked this one. Skip.
691                         continue;
692                 else
693                         // record that we check this.
694                         checkedFiles.push_back(bibfile);
695                 string encoding = buffer().masterParams().encoding().iconvName();
696                 string ienc = buffer().masterParams().bibFileEncoding(to_utf8(bf));
697                 if (ienc.empty() || ienc == "general")
698                         ienc = to_ascii(params()["encoding"]);
699
700                 if (!ienc.empty() && ienc != "auto-legacy-plain" && ienc != "auto-legacy" && encodings.fromLyXName(ienc))
701                         encoding = encodings.fromLyXName(ienc)->iconvName();
702                 ifdocstream ifs(bibfile.toFilesystemEncoding().c_str(),
703                         ios_base::in, encoding);
704
705                 char_type ch;
706                 VarMap strings;
707
708                 while (ifs) {
709                         ifs.get(ch);
710                         if (!ifs)
711                                 break;
712
713                         if (ch != '@')
714                                 continue;
715
716                         docstring entryType;
717
718                         if (!readTypeOrKey(entryType, ifs, from_ascii("{("), docstring(), makeLowerCase)) {
719                                 lyxerr << "BibTeX Parser: Error reading entry type." << std::endl;
720                                 continue;
721                         }
722
723                         if (!ifs) {
724                                 lyxerr << "BibTeX Parser: Unexpected end of file." << std::endl;
725                                 continue;
726                         }
727
728                         if (entryType == from_ascii("comment")) {
729                                 ifs.ignore(numeric_limits<int>::max(), '\n');
730                                 continue;
731                         }
732
733                         ifs.get(ch);
734                         if (!ifs) {
735                                 lyxerr << "BibTeX Parser: Unexpected end of file." << std::endl;
736                                 break;
737                         }
738
739                         if ((ch != '(') && (ch != '{')) {
740                                 lyxerr << "BibTeX Parser: Invalid entry delimiter." << std::endl;
741                                 ifs.putback(ch);
742                                 continue;
743                         }
744
745                         // process the entry
746                         if (entryType == from_ascii("string")) {
747
748                                 // read string and add it to the strings map
749                                 // (or replace it's old value)
750                                 docstring name;
751                                 docstring value;
752
753                                 if (!readTypeOrKey(name, ifs, from_ascii("="), from_ascii("#{}(),"), makeLowerCase)) {
754                                         lyxerr << "BibTeX Parser: Error reading string name." << std::endl;
755                                         continue;
756                                 }
757
758                                 if (!ifs) {
759                                         lyxerr << "BibTeX Parser: Unexpected end of file." << std::endl;
760                                         continue;
761                                 }
762
763                                 // next char must be an equal sign
764                                 ifs.get(ch);
765                                 if (!ifs || ch != '=') {
766                                         lyxerr << "BibTeX Parser: No `=' after string name: " <<
767                                                         name << "." << std::endl;
768                                         continue;
769                                 }
770
771                                 if (!readValue(value, ifs, strings)) {
772                                         lyxerr << "BibTeX Parser: Unable to read value for string: " <<
773                                                         name << "." << std::endl;
774                                         continue;
775                                 }
776
777                                 strings[name] = value;
778
779                         } else if (entryType == from_ascii("preamble")) {
780
781                                 // preamble definitions are discarded.
782                                 // can they be of any use in lyx?
783                                 docstring value;
784
785                                 if (!readValue(value, ifs, strings)) {
786                                         lyxerr << "BibTeX Parser: Unable to read preamble value." << std::endl;
787                                         continue;
788                                 }
789
790                         } else {
791
792                                 // Citation entry. Try to read the key.
793                                 docstring key;
794
795                                 if (!readTypeOrKey(key, ifs, from_ascii(","), from_ascii("}"), keepCase)) {
796                                         lyxerr << "BibTeX Parser: Unable to read key for entry type:" <<
797                                                         entryType << "." << std::endl;
798                                         continue;
799                                 }
800
801                                 if (!ifs) {
802                                         lyxerr << "BibTeX Parser: Unexpected end of file." << std::endl;
803                                         continue;
804                                 }
805
806                                 /////////////////////////////////////////////
807                                 // now we have a key, so we will add an entry
808                                 // (even if it's empty, as bibtex does)
809                                 //
810                                 // we now read the field = value pairs.
811                                 // all items must be separated by a comma. If
812                                 // it is missing the scanning of this entry is
813                                 // stopped and the next is searched.
814                                 docstring name;
815                                 docstring value;
816                                 docstring data;
817                                 BibTeXInfo keyvalmap(key, entryType);
818
819                                 bool readNext = removeWSAndComma(ifs);
820
821                                 while (ifs && readNext) {
822
823                                         // read field name
824                                         if (!readTypeOrKey(name, ifs, from_ascii("="),
825                                                            from_ascii("{}(),"), makeLowerCase) || !ifs)
826                                                 break;
827
828                                         // next char must be an equal sign
829                                         // FIXME Whitespace??
830                                         ifs.get(ch);
831                                         if (!ifs) {
832                                                 lyxerr << "BibTeX Parser: Unexpected end of file." << std::endl;
833                                                 break;
834                                         }
835                                         if (ch != '=') {
836                                                 lyxerr << "BibTeX Parser: Missing `=' after field name: " <<
837                                                                 name << ", for key: " << key << "." << std::endl;
838                                                 ifs.putback(ch);
839                                                 break;
840                                         }
841
842                                         // read field value
843                                         if (!readValue(value, ifs, strings)) {
844                                                 lyxerr << "BibTeX Parser: Unable to read value for field: " <<
845                                                                 name << ", for key: " << key << "." << std::endl;
846                                                 break;
847                                         }
848
849                                         keyvalmap[name] = value;
850                                         data += "\n\n" + value;
851                                         keylist.addFieldName(name);
852                                         readNext = removeWSAndComma(ifs);
853                                 }
854
855                                 // add the new entry
856                                 keylist.addEntryType(entryType);
857                                 keyvalmap.setAllData(data);
858                                 keylist[key] = keyvalmap;
859                         } //< else (citation entry)
860                 } //< searching '@'
861         } //< for loop over files
862
863         buffer().addBiblioInfo(keylist);
864 }
865
866
867 bool InsetBibtex::addDatabase(docstring const & db)
868 {
869         docstring bibfiles = getParam("bibfiles");
870         if (tokenPos(bibfiles, ',', db) != -1)
871                 return false;
872         if (!bibfiles.empty())
873                 bibfiles += ',';
874         setParam("bibfiles", bibfiles + db);
875         return true;
876 }
877
878
879 bool InsetBibtex::delDatabase(docstring const & db)
880 {
881         docstring bibfiles = getParam("bibfiles");
882         if (contains(bibfiles, db)) {
883                 int const n = tokenPos(bibfiles, ',', db);
884                 docstring bd = db;
885                 if (n > 0) {
886                         // this is not the first database
887                         docstring tmp = ',' + bd;
888                         setParam("bibfiles", subst(bibfiles, tmp, docstring()));
889                 } else if (n == 0)
890                         // this is the first (or only) database
891                         setParam("bibfiles", split(bibfiles, bd, ','));
892                 else
893                         return false;
894         }
895         return true;
896 }
897
898
899 void InsetBibtex::validate(LaTeXFeatures & features) const
900 {
901         BufferParams const & mparams = features.buffer().masterParams();
902         if (mparams.useBibtopic())
903                 features.require("bibtopic");
904         else if (!mparams.useBiblatex() && mparams.multibib == "child")
905                 features.require("chapterbib");
906         // FIXME XHTML
907         // It'd be better to be able to get this from an InsetLayout, but at present
908         // InsetLayouts do not seem really to work for things that aren't InsetTexts.
909         if (features.runparams().flavor == OutputParams::HTML)
910                 features.addCSSSnippet("div.bibtexentry { margin-left: 2em; text-indent: -2em; }\n"
911                         "span.bibtexlabel:before{ content: \"[\"; }\n"
912                         "span.bibtexlabel:after{ content: \"] \"; }");
913 }
914
915
916 void InsetBibtex::updateBuffer(ParIterator const &, UpdateType, bool const /*deleted*/)
917 {
918         buffer().registerBibfiles(getBibFiles());
919         // record encoding of bib files for biblatex
920         string const enc = (params()["encoding"] == from_ascii("default")) ?
921                                 string() : to_ascii(params()["encoding"]);
922         bool invalidate = false;
923         if (buffer().params().bibEncoding() != enc) {
924                 buffer().params().setBibEncoding(enc);
925                 invalidate = true;
926         }
927         map<string, string> encs = getFileEncodings();
928         map<string, string>::const_iterator it = encs.begin();
929         for (; it != encs.end(); ++it) {
930                 if (buffer().params().bibFileEncoding(it->first) != it->second) {
931                         buffer().params().setBibFileEncoding(it->first, it->second);
932                         invalidate = true;
933                 }
934         }
935         if (invalidate)
936                 buffer().invalidateBibinfoCache();
937 }
938
939
940 map<string, string> InsetBibtex::getFileEncodings() const
941 {
942         vector<string> ps =
943                 getVectorFromString(to_utf8(getParam("file_encodings")), "\t");
944         std::map<string, string> res;
945         for (string const & s: ps) {
946                 string key;
947                 string val = split(s, key, ' ');
948                 res[key] = val;
949         }
950         return res;
951 }
952
953
954 docstring InsetBibtex::getRefLabel() const
955 {
956         if (buffer().masterParams().documentClass().hasLaTeXLayout("chapter"))
957                 return buffer().B_("Bibliography");
958         return buffer().B_("References");
959 }
960
961
962 void InsetBibtex::addToToc(DocIterator const & cpit, bool output_active,
963                            UpdateType, TocBackend & backend) const
964 {
965         if (!prefixIs(to_utf8(getParam("options")), "bibtotoc"))
966                 return;
967
968         docstring const str = getRefLabel();
969         shared_ptr<Toc> toc = backend.toc("tableofcontents");
970         // Assign to appropriate level
971         int const item_depth =
972                 (buffer().masterParams().documentClass().hasLaTeXLayout("chapter")) 
973                         ? 1 : 2;
974         toc->push_back(TocItem(cpit, item_depth, str, output_active));
975 }
976
977
978 int InsetBibtex::plaintext(odocstringstream & os,
979        OutputParams const & op, size_t max_length) const
980 {
981         docstring const reflabel = getRefLabel();
982
983         // We could output more information here, e.g., what databases are included
984         // and information about options. But I don't necessarily see any reason to
985         // do this right now.
986         if (op.for_tooltip || op.for_toc || op.for_search) {
987                 os << '[' << reflabel << ']' << '\n';
988                 return PLAINTEXT_NEWLINE;
989         }
990
991         BiblioInfo bibinfo = buffer().masterBibInfo();
992         bibinfo.makeCitationLabels(buffer());
993         vector<docstring> const & cites = bibinfo.citedEntries();
994
995         size_t start_size = os.str().size();
996         docstring refoutput;
997         refoutput += reflabel + "\n\n";
998
999         // Tell BiblioInfo our purpose
1000         CiteItem ci;
1001         ci.context = CiteItem::Export;
1002
1003         // Now we loop over the entries
1004         vector<docstring>::const_iterator vit = cites.begin();
1005         vector<docstring>::const_iterator const ven = cites.end();
1006         for (; vit != ven; ++vit) {
1007                 if (start_size + refoutput.size() >= max_length)
1008                         break;
1009                 BiblioInfo::const_iterator const biit = bibinfo.find(*vit);
1010                 if (biit == bibinfo.end())
1011                         continue;
1012                 BibTeXInfo const & entry = biit->second;
1013                 refoutput += "[" + entry.label() + "] ";
1014                 // FIXME Right now, we are calling BibInfo::getInfo on the key,
1015                 // which will give us all the cross-referenced info. But for every
1016                 // entry, so there's a lot of repetition. This should be fixed.
1017                 refoutput += bibinfo.getInfo(entry.key(), buffer(), ci) + "\n\n";
1018         }
1019         os << refoutput;
1020         return int(refoutput.size());
1021 }
1022
1023
1024 // FIXME
1025 // docstring InsetBibtex::entriesAsXHTML(vector<docstring> const & entries)
1026 // And then here just: entriesAsXHTML(buffer().masterBibInfo().citedEntries())
1027 docstring InsetBibtex::xhtml(XMLStream & xs, OutputParams const &) const
1028 {
1029         BiblioInfo const & bibinfo = buffer().masterBibInfo();
1030         bool const all_entries = getParam("btprint") == "btPrintAll";
1031         vector<docstring> const & cites =
1032             all_entries ? bibinfo.getKeys() : bibinfo.citedEntries();
1033
1034         docstring const reflabel = buffer().B_("References");
1035
1036         // tell BiblioInfo our purpose
1037         CiteItem ci;
1038         ci.context = CiteItem::Export;
1039         ci.richtext = true;
1040         ci.max_key_size = UINT_MAX;
1041
1042         xs << xml::StartTag("h2", "class='bibtex'")
1043                 << reflabel
1044                 << xml::EndTag("h2")
1045                 << xml::StartTag("div", "class='bibtex'");
1046
1047         // Now we loop over the entries
1048         vector<docstring>::const_iterator vit = cites.begin();
1049         vector<docstring>::const_iterator const ven = cites.end();
1050         for (; vit != ven; ++vit) {
1051                 BiblioInfo::const_iterator const biit = bibinfo.find(*vit);
1052                 if (biit == bibinfo.end())
1053                         continue;
1054
1055                 BibTeXInfo const & entry = biit->second;
1056                 string const attr = "class='bibtexentry' id='LyXCite-"
1057                     + to_utf8(xml::cleanAttr(entry.key())) + "'";
1058                 xs << xml::StartTag("div", attr);
1059
1060                 // don't print labels if we're outputting all entries
1061                 if (!all_entries) {
1062                         xs << xml::StartTag("span", "class='bibtexlabel'")
1063                                 << entry.label()
1064                                 << xml::EndTag("span");
1065                 }
1066
1067                 // FIXME Right now, we are calling BibInfo::getInfo on the key,
1068                 // which will give us all the cross-referenced info. But for every
1069                 // entry, so there's a lot of repetition. This should be fixed.
1070                 xs << xml::StartTag("span", "class='bibtexinfo'")
1071                    << XMLStream::ESCAPE_AND
1072                    << bibinfo.getInfo(entry.key(), buffer(), ci)
1073                    << xml::EndTag("span")
1074                    << xml::EndTag("div")
1075                    << xml::CR();
1076         }
1077         xs << xml::EndTag("div");
1078         return docstring();
1079 }
1080
1081
1082 void InsetBibtex::docbook(XMLStream & xs, OutputParams const &) const
1083 {
1084         BiblioInfo const & bibinfo = buffer().masterBibInfo();
1085         bool const all_entries = getParam("btprint") == "btPrintAll";
1086         vector<docstring> const & cites =
1087                         all_entries ? bibinfo.getKeys() : bibinfo.citedEntries();
1088
1089         docstring const reflabel = buffer().B_("References");
1090
1091         // Tell BiblioInfo our purpose (i.e. generate HTML rich text).
1092         CiteItem ci;
1093         ci.context = CiteItem::Export;
1094         ci.richtext = true;
1095         ci.max_key_size = UINT_MAX;
1096
1097         // Header for bibliography (title required).
1098         xs << xml::StartTag("bibliography");
1099         xs << xml::CR();
1100         xs << xml::StartTag("title");
1101         xs << reflabel;
1102         xs << xml::EndTag("title") << xml::CR();
1103
1104         // Translation between keys in each entry and DocBook tags.
1105         // IDs for publications; list: http://tdg.docbook.org/tdg/5.2/biblioid.html.
1106         vector<pair<string, string>> biblioId = { // <bibtex, docbook>
1107                 make_pair("doi", "doi"),
1108                 make_pair("isbn", "isbn"),
1109                 make_pair("issn", "issn"),
1110                 make_pair("isrn", "isrn"),
1111                 make_pair("istc", "istc"),
1112                 make_pair("lccn", "libraryofcongress"),
1113                 make_pair("number", "pubsnumber"),
1114                 make_pair("url", "uri")
1115         };
1116         // Relations between documents.
1117         vector<pair<string, string>> relations = { // <bibtex, docbook biblioset relation>
1118                 make_pair("journal", "journal"),
1119                 make_pair("booktitle", "book"),
1120                 make_pair("series", "series")
1121         };
1122         // Various things that do not fit DocBook.
1123         vector<string> misc = { "language", "school", "note" };
1124
1125         // Store the mapping between BibTeX and DocBook.
1126         map<string, string> toDocBookTag;
1127         toDocBookTag["fullnames:author"] = "SPECIFIC"; // No direct translation to DocBook: <authorgroup>.
1128         toDocBookTag["publisher"] = "SPECIFIC"; // No direct translation to DocBook: <publisher>.
1129         toDocBookTag["address"] = "SPECIFIC"; // No direct translation to DocBook: <publisher>.
1130         toDocBookTag["editor"] = "editor";
1131         toDocBookTag["institution"] = "SPECIFIC"; // No direct translation to DocBook: <org>.
1132
1133         toDocBookTag["title"] = "title";
1134         toDocBookTag["volume"] = "volumenum";
1135         toDocBookTag["edition"] = "edition";
1136         toDocBookTag["pages"] = "artpagenums";
1137
1138         toDocBookTag["abstract"] = "SPECIFIC"; // No direct translation to DocBook: <abstract>.
1139         toDocBookTag["keywords"] = "SPECIFIC"; // No direct translation to DocBook: <keywordset>.
1140         toDocBookTag["year"] = "SPECIFIC"; // No direct translation to DocBook: <pubdate>.
1141         toDocBookTag["month"] = "SPECIFIC"; // No direct translation to DocBook: <pubdate>.
1142
1143         toDocBookTag["journal"] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
1144         toDocBookTag["booktitle"] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
1145         toDocBookTag["series"] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
1146
1147         for (auto const & id: biblioId)
1148             toDocBookTag[id.first] = "SPECIFIC"; // No direct translation to DocBook: <biblioid>.
1149         for (auto const & id: relations)
1150             toDocBookTag[id.first] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
1151         for (auto const & id: misc)
1152             toDocBookTag[id] = "SPECIFIC"; // No direct translation to DocBook: <bibliomisc>.
1153
1154         // Loop over the entries. If there are no entries, add a comment to say so.
1155         auto vit = cites.begin();
1156         auto ven = cites.end();
1157
1158         if (vit == ven) {
1159                 xs << XMLStream::ESCAPE_NONE << "<!-- No entry in the bibliography. -->";
1160         }
1161
1162         for (; vit != ven; ++vit) {
1163                 BiblioInfo::const_iterator const biit = bibinfo.find(*vit);
1164                 if (biit == bibinfo.end())
1165                         continue;
1166
1167                 BibTeXInfo const & entry = biit->second;
1168                 string const attr = "xml:id=\"" + to_utf8(xml::cleanID(entry.key())) + "\"";
1169                 xs << xml::StartTag("biblioentry", attr);
1170                 xs << xml::CR();
1171
1172                 // FIXME Right now, we are calling BibInfo::getInfo on the key,
1173                 // which will give us all the cross-referenced info. But for every
1174                 // entry, so there's a lot of repetition. This should be fixed.
1175
1176                 // Parse the results of getInfo and emit the corresponding DocBook tags. Interesting pieces have the form
1177                 // "<span class="bib-STH">STH</span>", the rest of the text may be discarded.
1178                 // Could have written a DocBook version of expandFormat (that parses a citation into HTML), but it implements
1179                 // some kind of recursion. Still, a (static) conversion step between the citation format and DocBook would have
1180                 // been required. All in all, both codes approaches would have been similar, but this parsing allows relying
1181                 // on existing building blocks.
1182
1183                 string html = to_utf8(bibinfo.getInfo(entry.key(), buffer(), ci));
1184                 regex tagRegex("<span class=\"bib-([^\"]*)\">([^<]*)</span>");
1185                 smatch match;
1186                 auto tagIt = std::sregex_iterator(html.cbegin(), html.cend(), tagRegex, regex_constants::match_default);
1187                 auto tagEnd = std::sregex_iterator();
1188                 map<string, string> delayedTags;
1189
1190                 // Read all tags from HTML and convert those that have a 1:1 matching.
1191                 while (tagIt != tagEnd) {
1192                         string tag = tagIt->str(); // regex_match cannot work with temporary strings.
1193                         ++tagIt;
1194                         regex_match(tag, match, tagRegex);
1195
1196                         if (regex_match(tag, match, tagRegex)) {
1197                                 if (toDocBookTag[match[1]] == "SPECIFIC") {
1198                                         delayedTags[match[1]] = match[2];
1199                                 } else {
1200                                         xs << xml::StartTag(toDocBookTag[match[1]]);
1201                                         xs << from_utf8(match[2].str());
1202                                         xs << xml::EndTag(toDocBookTag[match[1]]);
1203                                 }
1204                         } else {
1205                                 LYXERR0("The BibTeX field " << match[1].str() << " is unknown.");
1206                                 xs << XMLStream::ESCAPE_NONE << from_utf8("<!-- Output Error: The BibTeX field " + match[1].str() + " is unknown -->\n");
1207                         }
1208                 }
1209
1210                 // Type of document (book, journal paper, etc.).
1211                 xs << xml::StartTag("bibliomisc", "role=\"type\"");
1212                 xs << entry.entryType();
1213                 xs << xml::EndTag("bibliomisc");
1214                 xs << xml::CR();
1215
1216                 // Handle tags that have complex transformations.
1217                 if (! delayedTags.empty()) {
1218                         unsigned long remainingTags = delayedTags.size(); // Used as a workaround. With GCC 7, when erasing all
1219                         // elements one by one, some elements may still pop in later on (even though they were deleted previously).
1220                         auto hasTag = [&delayedTags](string key) { return delayedTags.find(key) != delayedTags.end(); };
1221                         auto getTag = [&delayedTags](string key) { return from_utf8(delayedTags[key]); };
1222                         auto eraseTag = [&delayedTags, &remainingTags](string key) {
1223                                 remainingTags -= 1;
1224                                 delayedTags.erase(key);
1225                         };
1226
1227                         // Notes on order of checks.
1228                         // - address goes with publisher if there is one, so check this first. Otherwise, the address goes with
1229                         //   the entry without other details.
1230
1231                         // <publisher>
1232                         if (hasTag("publisher")) {
1233                                 xs << xml::StartTag("publisher");
1234                                 xs << xml::CR();
1235                                 xs << xml::StartTag("publishername");
1236                                 xs << getTag("publisher");
1237                                 xs << xml::EndTag("publishername");
1238                                 xs << xml::CR();
1239
1240                                 if (hasTag("address")) {
1241                                         xs << xml::StartTag("address");
1242                                         xs << getTag("address");
1243                                         xs << xml::EndTag("address");
1244                                         eraseTag("address");
1245                                 }
1246
1247                                 xs << xml::EndTag("publisher");
1248                                 xs << xml::CR();
1249                                 eraseTag("publisher");
1250                         }
1251
1252                         if (hasTag("address")) {
1253                                 xs << xml::StartTag("address");
1254                                 xs << getTag("address");
1255                                 xs << xml::EndTag("address");
1256                                 eraseTag("address");
1257                         }
1258
1259                         // <keywordset>
1260                         if (hasTag("keywords")) {
1261                                 // Split the keywords on comma.
1262                                 docstring keywordSet = getTag("keywords");
1263                                 vector<docstring> keywords;
1264                                 if (keywordSet.find(from_utf8(",")) == string::npos) {
1265                                         keywords = { keywordSet };
1266                                 } else {
1267                                         size_t pos = 0;
1268                                         while ((pos = keywordSet.find(from_utf8(","))) != string::npos) {
1269                                                 keywords.push_back(keywordSet.substr(0, pos));
1270                                                 keywordSet.erase(0, pos + 1);
1271                                         }
1272                                         keywords.push_back(keywordSet);
1273                                 }
1274
1275                                 xs << xml::StartTag("keywordset") << xml::CR();
1276                                 for (auto & kw: keywords) {
1277                                         kw.erase(kw.begin(), std::find_if(kw.begin(), kw.end(),
1278                                                                           [](int c) {return !std::isspace(c);}));
1279                                         xs << xml::StartTag("keyword");
1280                                         xs << kw;
1281                                         xs << xml::EndTag("keyword");
1282                                         xs << xml::CR();
1283                                 }
1284                                 xs << xml::EndTag("keywordset") << xml::CR();
1285                                 eraseTag("keywords");
1286                         }
1287
1288                         // <copyright>
1289                         // Example: http://tdg.docbook.org/tdg/5.1/biblioset.html
1290                         if (hasTag("year")) {
1291                                 docstring value = getTag("year");
1292                                 eraseTag("year");
1293
1294                                 // Follow xsd:gYearMonth format (http://books.xmlschemata.org/relaxng/ch19-77135.html).
1295                                 if (hasTag("month")) {
1296                                         value += "-" + getTag("month");
1297                                         eraseTag("month");
1298                                 }
1299
1300                                 xs << xml::StartTag("pubdate");
1301                                 xs << value;
1302                                 xs << xml::EndTag("pubdate");
1303                                 xs << xml::CR();
1304                         }
1305
1306                         // <institution>
1307                         if (hasTag("institution")) {
1308                                 xs << xml::StartTag("org");
1309                                 xs << xml::CR();
1310                                 xs << xml::StartTag("orgname");
1311                                 xs << getTag("institution");
1312                                 xs << xml::EndTag("orgname");
1313                                 xs << xml::CR();
1314                                 xs << xml::EndTag("org");
1315                                 xs << xml::CR();
1316                                 eraseTag("institution");
1317                         }
1318
1319                         // <biblioset>
1320                         // Example: http://tdg.docbook.org/tdg/5.1/biblioset.html
1321                         for (auto const & id: relations) {
1322                                 if (hasTag(id.first)) {
1323                                         xs << xml::StartTag("biblioset", "relation=\"" + id.second + "\"");
1324                                         xs << xml::CR();
1325                                         xs << xml::StartTag("title");
1326                                         xs << getTag(id.first);
1327                                         xs << xml::EndTag("title");
1328                                         xs << xml::CR();
1329                                         xs << xml::EndTag("biblioset");
1330                                         xs << xml::CR();
1331                                         eraseTag(id.first);
1332                                 }
1333                         }
1334
1335                         // <authorgroup>
1336                         // Example: http://tdg.docbook.org/tdg/5.1/authorgroup.html
1337                         if (hasTag("fullnames:author")) {
1338                                 // Perform full parsing of the BibTeX string, dealing with the many corner cases that might
1339                                 // be encountered.
1340                                 authorsToDocBookAuthorGroup(getTag("fullnames:author"), xs, buffer());
1341                                 eraseTag("fullnames:author");
1342                         }
1343
1344                         // <abstract>
1345                         if (hasTag("abstract")) {
1346                                 // Split the paragraphs on new line.
1347                                 docstring abstract = getTag("abstract");
1348                                 vector<docstring> paragraphs;
1349                                 if (abstract.find(from_utf8("\n")) == string::npos) {
1350                                         paragraphs = { abstract };
1351                                 } else {
1352                                         size_t pos = 0;
1353                                         while ((pos = abstract.find(from_utf8(","))) != string::npos) {
1354                                                 paragraphs.push_back(abstract.substr(0, pos));
1355                                                 abstract.erase(0, pos + 1);
1356                                         }
1357                                         paragraphs.push_back(abstract);
1358                                 }
1359
1360                                 xs << xml::StartTag("abstract");
1361                                 xs << xml::CR();
1362                                 for (auto const & para: paragraphs) {
1363                                         if (para.empty())
1364                                                 continue;
1365                                         xs << xml::StartTag("para");
1366                                         xs << para;
1367                                         xs << xml::EndTag("para");
1368                                 }
1369                                 xs << xml::CR();
1370                                 xs << xml::EndTag("abstract");
1371                                 xs << xml::CR();
1372                                 eraseTag("abstract");
1373                         }
1374
1375                         // <biblioid>
1376                         for (auto const & id: biblioId) {
1377                                 if (hasTag(id.first)) {
1378                                         xs << xml::StartTag("biblioid", "class=\"" + id.second + "\"");
1379                                         xs << getTag(id.first);
1380                                         xs << xml::EndTag("biblioid");
1381                                         xs << xml::CR();
1382                                         eraseTag(id.first);
1383                                 }
1384                         }
1385
1386                         // <bibliomisc>
1387                         for (auto const & id: misc) {
1388                                 if (hasTag(id)) {
1389                                         xs << xml::StartTag("bibliomisc", "role=\"" + id + "\"");
1390                                         xs << getTag(id);
1391                                         xs << xml::EndTag("bibliomisc");
1392                                         xs << xml::CR();
1393                                         eraseTag(id);
1394                                 }
1395                         }
1396
1397                         // After all tags are processed, check for errors.
1398                         if (remainingTags > 0) {
1399                                 LYXERR0("Still delayed tags not yet handled.");
1400                                 xs << XMLStream::ESCAPE_NONE << from_utf8("<!-- Output Error: still delayed tags not yet handled.\n");
1401                                 for (auto const & item: delayedTags) {
1402                                         xs << from_utf8(" " + item.first + ": " + item.second + "\n");
1403                                 }
1404                                 xs << XMLStream::ESCAPE_NONE << from_utf8(" -->\n");
1405                         }
1406                 }
1407
1408                 xs << xml::EndTag("biblioentry");
1409                 xs << xml::CR();
1410         }
1411
1412         // Footer for bibliography.
1413         xs << xml::EndTag("bibliography");
1414 }
1415
1416
1417 void InsetBibtex::write(ostream & os) const
1418 {
1419         params().Write(os, &buffer());
1420 }
1421
1422
1423 string InsetBibtex::contextMenuName() const
1424 {
1425         return "context-bibtex";
1426 }
1427
1428
1429 } // namespace lyx