]> git.lyx.org Git - lyx.git/blob - src/insets/InsetBibtex.cpp
Find: Renamend for_searchAdv to for_serach in src/OutputParams.h
[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 Kimberly 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 "PDFOptions.h"
32 #include "texstream.h"
33 #include "TextClass.h"
34 #include "TocBackend.h"
35
36 #include "frontends/alert.h"
37
38 #include "support/convert.h"
39 #include "support/debug.h"
40 #include "support/docstream.h"
41 #include "support/docstring_list.h"
42 #include "support/ExceptionMessage.h"
43 #include "support/FileNameList.h"
44 #include "support/filetools.h"
45 #include "support/gettext.h"
46 #include "support/lstrings.h"
47 #include "support/os.h"
48 #include "support/PathChanger.h"
49 #include "support/textutils.h"
50
51 #include <limits>
52 #include <map>
53 #include <regex>
54 #include <utility>
55
56 #include <iostream>
57
58 using namespace std;
59 using namespace lyx::support;
60
61 namespace lyx {
62
63 namespace Alert = frontend::Alert;
64 namespace os = support::os;
65
66
67 InsetBibtex::InsetBibtex(Buffer * buf, InsetCommandParams const & p)
68         : InsetCommand(buf, p)
69 {}
70
71
72 ParamInfo const & InsetBibtex::findInfo(string const & /* cmdName */)
73 {
74         static ParamInfo param_info_;
75         if (param_info_.empty()) {
76                 param_info_.add("btprint", ParamInfo::LATEX_OPTIONAL);
77                 param_info_.add("bibfiles", ParamInfo::LATEX_REQUIRED);
78                 param_info_.add("options", ParamInfo::LYX_INTERNAL);
79                 param_info_.add("encoding", ParamInfo::LYX_INTERNAL);
80                 param_info_.add("file_encodings", ParamInfo::LYX_INTERNAL);
81                 param_info_.add("biblatexopts", ParamInfo::LATEX_OPTIONAL);
82         }
83         return param_info_;
84 }
85
86
87 void InsetBibtex::doDispatch(Cursor & cur, FuncRequest & cmd)
88 {
89         switch (cmd.action()) {
90
91         case LFUN_INSET_EDIT:
92                 editDatabases(cmd.argument());
93                 break;
94
95         case LFUN_INSET_MODIFY: {
96                 InsetCommandParams p(BIBTEX_CODE);
97                 try {
98                         if (!InsetCommand::string2params(to_utf8(cmd.argument()), p)) {
99                                 cur.noScreenUpdate();
100                                 break;
101                         }
102                 } catch (ExceptionMessage const & message) {
103                         if (message.type_ == WarningException) {
104                                 Alert::warning(message.title_, message.details_);
105                                 cur.noScreenUpdate();
106                         } else
107                                 throw;
108                         break;
109                 }
110
111                 cur.recordUndo();
112                 setParams(p);
113                 cur.buffer()->clearBibFileCache();
114                 cur.forceBufferUpdate();
115                 break;
116         }
117
118         default:
119                 InsetCommand::doDispatch(cur, cmd);
120                 break;
121         }
122 }
123
124
125 bool InsetBibtex::getStatus(Cursor & cur, FuncRequest const & cmd,
126                 FuncStatus & flag) const
127 {
128         switch (cmd.action()) {
129         case LFUN_INSET_EDIT:
130                 flag.setEnabled(true);
131                 return true;
132
133         default:
134                 return InsetCommand::getStatus(cur, cmd, flag);
135         }
136 }
137
138
139 void InsetBibtex::editDatabases(docstring const & db) const
140 {
141         vector<docstring> bibfilelist = getVectorFromString(getParam("bibfiles"));
142
143         if (bibfilelist.empty())
144                 return;
145
146         size_t nr_databases = bibfilelist.size();
147         if (nr_databases > 1 && db.empty()) {
148                         docstring const engine = usingBiblatex() ? _("Biblatex") : _("BibTeX");
149                         docstring message = bformat(_("The %1$s[[BibTeX/Biblatex]] inset includes %2$s databases.\n"
150                                                        "If you proceed, all of them will be opened."),
151                                                         engine, convert<docstring>(nr_databases));
152                         int const ret = Alert::prompt(_("Open Databases?"),
153                                 message, 0, 1, _("&Cancel"), _("&Proceed"));
154
155                         if (ret == 0)
156                                 return;
157         }
158
159         vector<docstring>::const_iterator it = bibfilelist.begin();
160         vector<docstring>::const_iterator en = bibfilelist.end();
161         for (; it != en; ++it) {
162                 if (!db.empty() && db != *it)
163                         continue;
164                 FileName const bibfile = buffer().getBibfilePath(*it);
165                 theFormats().edit(buffer(), bibfile,
166                      theFormats().getFormatFromFile(bibfile));
167         }
168 }
169
170
171 bool InsetBibtex::usingBiblatex() const
172 {
173         return buffer().masterParams().useBiblatex();
174 }
175
176
177 docstring InsetBibtex::screenLabel() const
178 {
179         return usingBiblatex() ? _("Biblatex Generated Bibliography")
180                                : _("BibTeX Generated Bibliography");
181 }
182
183
184 docstring InsetBibtex::toolTip(BufferView const & /*bv*/, int /*x*/, int /*y*/) const
185 {
186         docstring tip = _("Databases:");
187         vector<docstring> bibfilelist = getVectorFromString(getParam("bibfiles"));
188
189         tip += "<ul>";
190         if (bibfilelist.empty())
191                 tip += "<li>" + _("none") + "</li>";
192         else
193                 for (docstring const & bibfile : bibfilelist)
194                         tip += "<li>" + bibfile + "</li>";
195         tip += "</ul>";
196
197         // Style-Options
198         bool toc = false;
199         docstring style = getParam("options"); // maybe empty! and with bibtotoc
200         docstring bibtotoc = from_ascii("bibtotoc");
201         if (prefixIs(style, bibtotoc)) {
202                 toc = true;
203                 if (contains(style, char_type(',')))
204                         style = split(style, bibtotoc, char_type(','));
205         }
206
207         docstring const btprint = getParam("btprint");
208         if (!usingBiblatex()) {
209                 tip += _("Style File:");
210                 tip += "<ul><li>" + (style.empty() ? _("none") : style) + "</li></ul>";
211
212                 tip += _("Lists:") + " ";
213                 if (btprint == "btPrintAll")
214                         tip += _("all references");
215                 else if (btprint == "btPrintNotCited")
216                         tip += _("all uncited references");
217                 else
218                         tip += _("all cited references");
219                 if (toc) {
220                         tip += ", ";
221                         tip += _("included in TOC");
222                 }
223                 if (!buffer().parent()
224                     && buffer().params().multibib == "child") {
225                         tip += "<br />";
226                         tip += _("Note: This bibliography is not output, since bibliographies in the master file "
227                                  "are not allowed with the setting 'Multiple bibliographies per child document'");
228                 }
229         } else {
230                 tip += _("Lists:") + " ";
231                 if (btprint == "bibbysection")
232                         tip += _("all reference units");
233                 else if (btprint == "btPrintAll")
234                         tip += _("all references");
235                 else
236                         tip += _("all cited references");
237                 if (toc) {
238                         tip += ", ";
239                         tip += _("included in TOC");
240                 }
241                 if (!getParam("biblatexopts").empty()) {
242                         tip += "<br />";
243                         tip += _("Options: ") + getParam("biblatexopts");
244                 }
245         }
246
247         return tip;
248 }
249
250
251 void InsetBibtex::latex(otexstream & os, OutputParams const & runparams) const
252 {
253         // The sequence of the commands:
254         // With normal BibTeX:
255         // 1. \bibliographystyle{style}
256         // 2. \addcontentsline{...} - if option bibtotoc set
257         // 3. \bibliography{database}
258         // With bibtopic:
259         // 1. \bibliographystyle{style}
260         // 2. \begin{btSect}{database}
261         // 3. \btPrint{Cited|NotCited|All}
262         // 4. \end{btSect}
263         // With Biblatex:
264         // \printbibliography[biblatexopts]
265         // or
266         // \bibbysection[biblatexopts] - if btprint is "bibbysection"
267
268         // chapterbib does not allow bibliographies in the master
269         if (!usingBiblatex() && !runparams.is_child
270             && buffer().params().multibib == "child")
271                 return;
272
273         if (runparams.inDeletedInset) {
274                 // We cannot strike-out bibligraphies,
275                 // so we just output a note.
276                 os << "\\textbf{"
277                    << buffer().B_("[BIBLIOGRAPHY DELETED!]")
278                    << "}";
279                 return;
280         }
281
282         string style = to_utf8(getParam("options")); // maybe empty! and with bibtotoc
283         string bibtotoc;
284         if (prefixIs(style, "bibtotoc")) {
285                 bibtotoc = "bibtotoc";
286                 if (contains(style, ','))
287                         style = split(style, bibtotoc, ',');
288         }
289
290         if (usingBiblatex()) {
291                 // Options
292                 string opts = to_utf8(getParam("biblatexopts"));
293                 // bibtotoc-Option
294                 if (!bibtotoc.empty())
295                         opts = opts.empty() ? "heading=bibintoc" : "heading=bibintoc," + opts;
296                 // The bibliography command
297                 docstring btprint = getParam("btprint");
298                 if (btprint == "btPrintAll")
299                         os << "\\nocite{*}\n";
300                 if (btprint == "bibbysection" && !buffer().masterParams().multibib.empty())
301                         os << "\\bibbysection";
302                 else
303                         os << "\\printbibliography";
304                 if (!opts.empty())
305                         os << "[" << opts << "]";
306                 os << "\n";
307         } else {// using BibTeX
308                 // Database(s)
309                 vector<pair<docstring, string>> const dbs =
310                         buffer().prepareBibFilePaths(runparams, getBibFiles(), false);
311                 vector<docstring> db_out;
312                 db_out.reserve(dbs.size());
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 == Flavor::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 != OutputParams::NoSearch) {
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         // Check that the bibliography is not empty, to ensure that the document is valid.
1092         if (cites.empty()) {
1093                 xs << XMLStream::ESCAPE_NONE << "<!-- The bibliography is empty! -->";
1094                 xs << xml::CR();
1095                 return;
1096         }
1097
1098         // Tell BiblioInfo our purpose (i.e. generate HTML rich text).
1099         CiteItem ci;
1100         ci.context = CiteItem::Export;
1101         ci.richtext = true;
1102         ci.max_key_size = UINT_MAX;
1103
1104         // Header for bibliography (title required).
1105         xs << xml::StartTag("bibliography");
1106         xs << xml::CR();
1107         xs << xml::StartTag("title");
1108         xs << reflabel;
1109         xs << xml::EndTag("title");
1110         xs << xml::CR();
1111
1112         // Translation between keys in each entry and DocBook tags.
1113         // IDs for publications; list: http://tdg.docbook.org/tdg/5.2/biblioid.html.
1114         vector<pair<string, string>> biblioId = { // <bibtex, docbook>
1115                 make_pair("doi", "doi"),
1116                 make_pair("isbn", "isbn"),
1117                 make_pair("issn", "issn"),
1118                 make_pair("isrn", "isrn"),
1119                 make_pair("istc", "istc"),
1120                 make_pair("lccn", "libraryofcongress"),
1121                 make_pair("number", "pubsnumber"),
1122                 make_pair("url", "uri")
1123         };
1124         // Relations between documents.
1125         vector<pair<string, string>> relations = { // <bibtex, docbook biblioset relation>
1126                 make_pair("journal", "journal"),
1127                 make_pair("booktitle", "book"),
1128                 make_pair("series", "series")
1129         };
1130         // Various things that do not fit DocBook.
1131         vector<string> misc = { "language", "school", "note" };
1132
1133         // Store the mapping between BibTeX and DocBook.
1134         map<string, string> toDocBookTag;
1135         toDocBookTag["fullnames:author"] = "SPECIFIC"; // No direct translation to DocBook: <authorgroup>.
1136         toDocBookTag["publisher"] = "SPECIFIC"; // No direct translation to DocBook: <publisher>.
1137         toDocBookTag["address"] = "SPECIFIC"; // No direct translation to DocBook: <publisher>.
1138         toDocBookTag["editor"] = "editor";
1139         toDocBookTag["institution"] = "SPECIFIC"; // No direct translation to DocBook: <org>.
1140
1141         toDocBookTag["title"] = "title";
1142         toDocBookTag["volume"] = "volumenum";
1143         toDocBookTag["edition"] = "edition";
1144         toDocBookTag["pages"] = "artpagenums";
1145
1146         toDocBookTag["abstract"] = "SPECIFIC"; // No direct translation to DocBook: <abstract>.
1147         toDocBookTag["keywords"] = "SPECIFIC"; // No direct translation to DocBook: <keywordset>.
1148         toDocBookTag["year"] = "SPECIFIC"; // No direct translation to DocBook: <pubdate>.
1149         toDocBookTag["month"] = "SPECIFIC"; // No direct translation to DocBook: <pubdate>.
1150
1151         toDocBookTag["journal"] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
1152         toDocBookTag["booktitle"] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
1153         toDocBookTag["series"] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
1154
1155         for (auto const & id: biblioId)
1156             toDocBookTag[id.first] = "SPECIFIC"; // No direct translation to DocBook: <biblioid>.
1157         for (auto const & id: relations)
1158             toDocBookTag[id.first] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
1159         for (auto const & id: misc)
1160             toDocBookTag[id] = "SPECIFIC"; // No direct translation to DocBook: <bibliomisc>.
1161
1162         // Loop over the entries. If there are no entries, add a comment to say so.
1163         auto vit = cites.begin();
1164         auto ven = cites.end();
1165
1166         for (; vit != ven; ++vit) {
1167                 auto const biit = bibinfo.find(*vit);
1168                 if (biit == bibinfo.end())
1169                         continue;
1170
1171                 BibTeXInfo const & entry = biit->second;
1172                 string const attr = "xml:id=\"" + to_utf8(xml::cleanID(entry.key())) + "\"";
1173                 xs << xml::StartTag("biblioentry", attr);
1174                 xs << xml::CR();
1175
1176                 // FIXME Right now, we are calling BibInfo::getInfo on the key,
1177                 // which will give us all the cross-referenced info. But for every
1178                 // entry, so there's a lot of repetition. This should be fixed.
1179
1180                 // Parse the results of getInfo and emit the corresponding DocBook tags. Interesting pieces have the form
1181                 // "<span class="bib-STH">STH</span>", the rest of the text may be discarded.
1182                 // Could have written a DocBook version of expandFormat (that parses a citation into HTML), but it implements
1183                 // some kind of recursion. Still, a (static) conversion step between the citation format and DocBook would have
1184                 // been required. All in all, both codes approaches would have been similar, but this parsing allows relying
1185                 // on existing building blocks.
1186
1187                 string html = to_utf8(bibinfo.getInfo(entry.key(), buffer(), ci));
1188                 regex tagRegex("<span class=\"bib-([^\"]*)\">([^<]*)</span>");
1189                 smatch match;
1190                 auto tagIt = sregex_iterator(html.cbegin(), html.cend(), tagRegex, regex_constants::match_default);
1191                 auto tagEnd = sregex_iterator();
1192                 map<string, string> delayedTags;
1193
1194                 // Read all tags from HTML and convert those that have a 1:1 matching.
1195                 while (tagIt != tagEnd) {
1196                         string tag = tagIt->str(); // regex_match cannot work with temporary strings.
1197                         ++tagIt;
1198
1199                         if (regex_match(tag, match, tagRegex)) {
1200                                 if (toDocBookTag[match[1]] == "SPECIFIC") {
1201                                         delayedTags[match[1]] = match[2];
1202                                 } else {
1203                                         xs << xml::StartTag(toDocBookTag[match[1]]);
1204                                         xs << from_utf8(match[2].str());
1205                                         xs << xml::EndTag(toDocBookTag[match[1]]);
1206                                         xs << xml::CR();
1207                                 }
1208                         } else {
1209                                 LYXERR0("The BibTeX field " << match[1].str() << " is unknown.");
1210                                 xs << XMLStream::ESCAPE_NONE << from_utf8("<!-- Output Error: The BibTeX field " + match[1].str() + " is unknown -->\n");
1211                         }
1212                 }
1213
1214                 // Type of document (book, journal paper, etc.).
1215                 xs << xml::StartTag("bibliomisc", "role=\"type\"");
1216                 xs << entry.entryType();
1217                 xs << xml::EndTag("bibliomisc");
1218                 xs << xml::CR();
1219
1220                 // Handle tags that have complex transformations.
1221                 if (! delayedTags.empty()) {
1222                         unsigned long remainingTags = delayedTags.size(); // Used as a workaround. With GCC 7, when erasing all
1223                         // elements one by one, some elements may still pop in later on (even though they were deleted previously).
1224                         auto hasTag = [&delayedTags](const string & key) { return delayedTags.find(key) != delayedTags.end(); };
1225                         auto getTag = [&delayedTags](const string & key) { return from_utf8(delayedTags[key]); };
1226                         auto eraseTag = [&delayedTags, &remainingTags](const string & key) {
1227                                 remainingTags -= 1;
1228                                 delayedTags.erase(key);
1229                         };
1230
1231                         // Notes on order of checks.
1232                         // - address goes with publisher if there is one, so check this first. Otherwise, the address goes with
1233                         //   the entry without other details.
1234
1235                         // <publisher>
1236                         if (hasTag("publisher")) {
1237                                 xs << xml::StartTag("publisher");
1238                                 xs << xml::CR();
1239                                 xs << xml::StartTag("publishername");
1240                                 xs << getTag("publisher");
1241                                 xs << xml::EndTag("publishername");
1242                                 xs << xml::CR();
1243
1244                                 if (hasTag("address")) {
1245                                         xs << xml::StartTag("address");
1246                                         xs << getTag("address");
1247                                         xs << xml::EndTag("address");
1248                                         eraseTag("address");
1249                                 }
1250
1251                                 xs << xml::EndTag("publisher");
1252                                 xs << xml::CR();
1253                                 eraseTag("publisher");
1254                         }
1255
1256                         if (hasTag("address")) {
1257                                 xs << xml::StartTag("address");
1258                                 xs << getTag("address");
1259                                 xs << xml::EndTag("address");
1260                                 eraseTag("address");
1261                         }
1262
1263                         // <keywordset>
1264                         if (hasTag("keywords")) {
1265                                 // Split the keywords on comma.
1266                                 docstring keywordSet = getTag("keywords");
1267                                 vector<docstring> keywords;
1268                                 if (keywordSet.find(from_utf8(",")) == string::npos) {
1269                                         keywords = { keywordSet };
1270                                 } else {
1271                                         size_t pos = 0;
1272                                         while ((pos = keywordSet.find(from_utf8(","))) != string::npos) {
1273                                                 keywords.push_back(keywordSet.substr(0, pos));
1274                                                 keywordSet.erase(0, pos + 1);
1275                                         }
1276                                         keywords.push_back(keywordSet);
1277                                 }
1278
1279                                 xs << xml::StartTag("keywordset") << xml::CR();
1280                                 for (auto & kw: keywords) {
1281                                         kw.erase(kw.begin(), std::find_if(kw.begin(), kw.end(),
1282                                                                           [](char_type c) {return !lyx::isSpace(c);}));
1283                                         xs << xml::StartTag("keyword");
1284                                         xs << kw;
1285                                         xs << xml::EndTag("keyword");
1286                                         xs << xml::CR();
1287                                 }
1288                                 xs << xml::EndTag("keywordset") << xml::CR();
1289                                 eraseTag("keywords");
1290                         }
1291
1292                         // <copyright>
1293                         // Example: http://tdg.docbook.org/tdg/5.1/biblioset.html
1294                         if (hasTag("year")) {
1295                                 docstring value = getTag("year");
1296                                 eraseTag("year");
1297
1298                                 // Follow xsd:gYearMonth format (http://books.xmlschemata.org/relaxng/ch19-77135.html).
1299                                 if (hasTag("month")) {
1300                                         value += "-" + getTag("month");
1301                                         eraseTag("month");
1302                                 }
1303
1304                                 xs << xml::StartTag("pubdate");
1305                                 xs << value;
1306                                 xs << xml::EndTag("pubdate");
1307                                 xs << xml::CR();
1308                         }
1309
1310                         // <institution>
1311                         if (hasTag("institution")) {
1312                                 xs << xml::StartTag("org");
1313                                 xs << xml::CR();
1314                                 xs << xml::StartTag("orgname");
1315                                 xs << getTag("institution");
1316                                 xs << xml::EndTag("orgname");
1317                                 xs << xml::CR();
1318                                 xs << xml::EndTag("org");
1319                                 xs << xml::CR();
1320                                 eraseTag("institution");
1321                         }
1322
1323                         // <biblioset>
1324                         // Example: http://tdg.docbook.org/tdg/5.1/biblioset.html
1325                         for (auto const & id: relations) {
1326                                 if (hasTag(id.first)) {
1327                                         xs << xml::StartTag("biblioset", "relation=\"" + id.second + "\"");
1328                                         xs << xml::CR();
1329                                         xs << xml::StartTag("title");
1330                                         xs << getTag(id.first);
1331                                         xs << xml::EndTag("title");
1332                                         xs << xml::CR();
1333                                         xs << xml::EndTag("biblioset");
1334                                         xs << xml::CR();
1335                                         eraseTag(id.first);
1336                                 }
1337                         }
1338
1339                         // <authorgroup>
1340                         // Example: http://tdg.docbook.org/tdg/5.1/authorgroup.html
1341                         if (hasTag("fullnames:author")) {
1342                                 // Perform full parsing of the BibTeX string, dealing with the many corner cases that might
1343                                 // be encountered.
1344                                 authorsToDocBookAuthorGroup(getTag("fullnames:author"), xs, buffer());
1345                                 eraseTag("fullnames:author");
1346                         }
1347
1348                         // <abstract>
1349                         if (hasTag("abstract")) {
1350                                 // Split the paragraphs on new line.
1351                                 docstring abstract = getTag("abstract");
1352                                 vector<docstring> paragraphs;
1353                                 if (abstract.find(from_utf8("\n")) == string::npos) {
1354                                         paragraphs = { abstract };
1355                                 } else {
1356                                         size_t pos = 0;
1357                                         while ((pos = abstract.find(from_utf8(","))) != string::npos) {
1358                                                 paragraphs.push_back(abstract.substr(0, pos));
1359                                                 abstract.erase(0, pos + 1);
1360                                         }
1361                                         paragraphs.push_back(abstract);
1362                                 }
1363
1364                                 xs << xml::StartTag("abstract");
1365                                 xs << xml::CR();
1366                                 for (auto const & para: paragraphs) {
1367                                         if (para.empty())
1368                                                 continue;
1369                                         xs << xml::StartTag("para");
1370                                         xs << para;
1371                                         xs << xml::EndTag("para");
1372                                 }
1373                                 xs << xml::CR();
1374                                 xs << xml::EndTag("abstract");
1375                                 xs << xml::CR();
1376                                 eraseTag("abstract");
1377                         }
1378
1379                         // <biblioid>
1380                         for (auto const & id: biblioId) {
1381                                 if (hasTag(id.first)) {
1382                                         xs << xml::StartTag("biblioid", "class=\"" + id.second + "\"");
1383                                         xs << getTag(id.first);
1384                                         xs << xml::EndTag("biblioid");
1385                                         xs << xml::CR();
1386                                         eraseTag(id.first);
1387                                 }
1388                         }
1389
1390                         // <bibliomisc>
1391                         for (auto const & id: misc) {
1392                                 if (hasTag(id)) {
1393                                         xs << xml::StartTag("bibliomisc", "role=\"" + id + "\"");
1394                                         xs << getTag(id);
1395                                         xs << xml::EndTag("bibliomisc");
1396                                         xs << xml::CR();
1397                                         eraseTag(id);
1398                                 }
1399                         }
1400
1401                         // After all tags are processed, check for errors.
1402                         if (remainingTags > 0) {
1403                                 LYXERR0("Still delayed tags not yet handled.");
1404                                 xs << XMLStream::ESCAPE_NONE << from_utf8("<!-- Output Error: still delayed tags not yet handled.\n");
1405                                 for (auto const & item: delayedTags) {
1406                                         xs << from_utf8(" " + item.first + ": " + item.second + "\n");
1407                                 }
1408                                 xs << XMLStream::ESCAPE_NONE << from_utf8(" -->\n");
1409                         }
1410                 }
1411
1412                 xs << xml::EndTag("biblioentry");
1413                 xs << xml::CR();
1414         }
1415
1416         // Footer for bibliography.
1417         xs << xml::EndTag("bibliography");
1418         xs << xml::CR();
1419 }
1420
1421
1422 void InsetBibtex::write(ostream & os) const
1423 {
1424         params().Write(os, &buffer());
1425 }
1426
1427
1428 string InsetBibtex::contextMenuName() const
1429 {
1430         return "context-bibtex";
1431 }
1432
1433
1434 } // namespace lyx