]> git.lyx.org Git - features.git/blob - src/insets/InsetBibtex.cpp
Support for multiple bibliographies
[features.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_xhtml.h"
30 #include "OutputParams.h"
31 #include "PDFOptions.h"
32 #include "texstream.h"
33 #include "TextClass.h"
34
35 #include "frontends/alert.h"
36
37 #include "support/convert.h"
38 #include "support/debug.h"
39 #include "support/docstream.h"
40 #include "support/ExceptionMessage.h"
41 #include "support/FileNameList.h"
42 #include "support/filetools.h"
43 #include "support/gettext.h"
44 #include "support/lstrings.h"
45 #include "support/os.h"
46 #include "support/PathChanger.h"
47 #include "support/textutils.h"
48
49 #include <limits>
50
51 using namespace std;
52 using namespace lyx::support;
53
54 namespace lyx {
55
56 namespace Alert = frontend::Alert;
57 namespace os = support::os;
58
59
60 InsetBibtex::InsetBibtex(Buffer * buf, InsetCommandParams const & p)
61         : InsetCommand(buf, p)
62 {
63         buffer().invalidateBibfileCache();
64         buffer().removeBiblioTempFiles();
65 }
66
67
68 InsetBibtex::~InsetBibtex()
69 {
70         if (isBufferLoaded()) {
71                 buffer().invalidateBibfileCache();
72                 buffer().removeBiblioTempFiles();
73         }
74 }
75
76
77 ParamInfo const & InsetBibtex::findInfo(string const & /* cmdName */)
78 {
79         static ParamInfo param_info_;
80         if (param_info_.empty()) {
81                 param_info_.add("btprint", ParamInfo::LATEX_OPTIONAL);
82                 param_info_.add("bibfiles", ParamInfo::LATEX_REQUIRED);
83                 param_info_.add("options", ParamInfo::LYX_INTERNAL);
84                 param_info_.add("biblatexopts", ParamInfo::LATEX_OPTIONAL);
85         }
86         return param_info_;
87 }
88
89
90 void InsetBibtex::doDispatch(Cursor & cur, FuncRequest & cmd)
91 {
92         switch (cmd.action()) {
93
94         case LFUN_INSET_EDIT:
95                 editDatabases();
96                 break;
97
98         case LFUN_INSET_MODIFY: {
99                 InsetCommandParams p(BIBTEX_CODE);
100                 try {
101                         if (!InsetCommand::string2params(to_utf8(cmd.argument()), p)) {
102                                 cur.noScreenUpdate();
103                                 break;
104                         }
105                 } catch (ExceptionMessage const & message) {
106                         if (message.type_ == WarningException) {
107                                 Alert::warning(message.title_, message.details_);
108                                 cur.noScreenUpdate();
109                         } else
110                                 throw;
111                         break;
112                 }
113
114                 cur.recordUndo();
115                 setParams(p);
116                 buffer().invalidateBibfileCache();
117                 buffer().removeBiblioTempFiles();
118                 cur.forceBufferUpdate();
119                 break;
120         }
121
122         default:
123                 InsetCommand::doDispatch(cur, cmd);
124                 break;
125         }
126 }
127
128
129 bool InsetBibtex::getStatus(Cursor & cur, FuncRequest const & cmd,
130                 FuncStatus & flag) const
131 {
132         switch (cmd.action()) {
133         case LFUN_INSET_EDIT:
134                 flag.setEnabled(true);
135                 return true;
136
137         default:
138                 return InsetCommand::getStatus(cur, cmd, flag);
139         }
140 }
141
142
143 void InsetBibtex::editDatabases() const
144 {
145         vector<docstring> bibfilelist = getVectorFromString(getParam("bibfiles"));
146
147         if (bibfilelist.empty())
148                 return;
149
150         int nr_databases = bibfilelist.size();
151         if (nr_databases > 1) {
152                         docstring const engine = usingBiblatex() ? _("Biblatex") : _("BibTeX");
153                         docstring message = bformat(_("The %1$s[[BibTeX/Biblatex]] inset includes %2$s databases.\n"
154                                                        "If you proceed, all of them will be opened."),
155                                                         engine, convert<docstring>(nr_databases));
156                         int const ret = Alert::prompt(_("Open Databases?"),
157                                 message, 0, 1, _("&Cancel"), _("&Proceed"));
158
159                         if (ret == 0)
160                                 return;
161         }
162
163         vector<docstring>::const_iterator it = bibfilelist.begin();
164         vector<docstring>::const_iterator en = bibfilelist.end();
165         for (; it != en; ++it) {
166                 FileName const bibfile = getBibTeXPath(*it, buffer());
167                 formats.edit(buffer(), bibfile,
168                      formats.getFormatFromFile(bibfile));
169         }
170 }
171
172
173 bool InsetBibtex::usingBiblatex() const
174 {
175         return buffer().masterParams().useBiblatex();
176 }
177
178
179 docstring InsetBibtex::screenLabel() const
180 {
181         return usingBiblatex() ? _("Biblatex Generated Bibliography")
182                                : _("BibTeX Generated Bibliography");
183 }
184
185
186 docstring InsetBibtex::toolTip(BufferView const & /*bv*/, int /*x*/, int /*y*/) const
187 {
188         docstring tip = _("Databases:");
189         vector<docstring> bibfilelist = getVectorFromString(getParam("bibfiles"));
190
191         tip += "<ul>";
192         if (bibfilelist.empty())
193                 tip += "<li>" + _("none") + "</li>";
194         else
195                 for (docstring const & bibfile : bibfilelist)
196                         tip += "<li>" + bibfile + "</li>";
197         tip += "</ul>";
198
199         // Style-Options
200         bool toc = false;
201         docstring style = getParam("options"); // maybe empty! and with bibtotoc
202         docstring bibtotoc = from_ascii("bibtotoc");
203         if (prefixIs(style, bibtotoc)) {
204                 toc = true;
205                 if (contains(style, char_type(',')))
206                         style = split(style, bibtotoc, char_type(','));
207         }
208
209         docstring const btprint = getParam("btprint");
210         if (!usingBiblatex()) {
211                 tip += _("Style File:");
212                 tip += "<ul><li>" + (style.empty() ? _("none") : style) + "</li></ul>";
213
214                 tip += _("Lists:") + " ";
215                 if (btprint == "btPrintAll")
216                         tip += _("all references");
217                 else if (btprint == "btPrintNotCited")
218                         tip += _("all uncited references");
219                 else
220                         tip += _("all cited references");
221                 if (toc) {
222                         tip += ", ";
223                         tip += _("included in TOC");
224                 }
225         } else {
226                 tip += _("Lists:") + " ";
227                 if (btprint == "bibbysection")
228                         tip += _("all reference units");
229                 else if (btprint == "btPrintAll")
230                         tip += _("all references");
231                 else
232                         tip += _("all cited references");
233                 if (toc) {
234                         tip += ", ";
235                         tip += _("included in TOC");
236                 }
237                 if (!getParam("biblatexopts").empty()) {
238                         if (toc)
239                                 tip += "<br />";
240                         tip += _("Options: ") + getParam("biblatexopts");
241                 }
242         }
243
244         return tip;
245 }
246
247
248 void InsetBibtex::latex(otexstream & os, OutputParams const & runparams) const
249 {
250         // The sequence of the commands:
251         // With normal BibTeX:
252         // 1. \bibliographystyle{style}
253         // 2. \addcontentsline{...} - if option bibtotoc set
254         // 3. \bibliography{database}
255         // With bibtopic:
256         // 1. \bibliographystyle{style}
257         // 2. \begin{btSect}{database}
258         // 3. \btPrint{Cited|NotCited|All}
259         // 4. \end{btSect}
260         // With Biblatex:
261         // \printbibliography[biblatexopts]
262         // or
263         // \bibbysection[biblatexopts] - if btprint is "bibbysection"
264
265         string style = to_utf8(getParam("options")); // maybe empty! and with bibtotoc
266         string bibtotoc;
267         if (prefixIs(style, "bibtotoc")) {
268                 bibtotoc = "bibtotoc";
269                 if (contains(style, ','))
270                         style = split(style, bibtotoc, ',');
271         }
272
273         if (usingBiblatex()) {
274                 // Options
275                 string opts = to_utf8(getParam("biblatexopts"));
276                 // bibtotoc-Option
277                 if (!bibtotoc.empty())
278                         opts = opts.empty() ? "heading=bibintoc" : "heading=bibintoc," + opts;
279                 // The bibliography command
280                 docstring btprint = getParam("btprint");
281                 if (btprint == "btPrintAll")
282                         os << "\\nocite{*}\n";
283                 if (btprint == "bibbysection" && !buffer().masterParams().multibib.empty())
284                         os << "\\bibbysection";
285                 else
286                         os << "\\printbibliography";
287                 if (!opts.empty())
288                         os << "[" << opts << "]";
289                 os << "\n";
290         } else {// using BibTeX
291                 // Database(s)
292                 vector<docstring> const db_out =
293                         buffer().prepareBibFilePaths(runparams, getBibFiles(), false);
294                 // Style options
295                 if (style == "default")
296                         style = buffer().masterParams().defaultBiblioStyle();
297                 if (!style.empty() && !buffer().masterParams().useBibtopic()) {
298                         string base = buffer().masterBuffer()->prepareFileNameForLaTeX(style, ".bst", runparams.nice);
299                         FileName const try_in_file =
300                                 makeAbsPath(base + ".bst", buffer().filePath());
301                         bool const not_from_texmf = try_in_file.isReadableFile();
302                         // If this style does not come from texmf and we are not
303                         // exporting to .tex copy it to the tmp directory.
304                         // This prevents problems with spaces and 8bit characters
305                         // in the file name.
306                         if (!runparams.inComment && !runparams.dryrun && !runparams.nice &&
307                             not_from_texmf) {
308                                 // use new style name
309                                 DocFileName const in_file = DocFileName(try_in_file);
310                                 base = removeExtension(in_file.mangledFileName());
311                                 FileName const out_file = makeAbsPath(base + ".bst",
312                                                 buffer().masterBuffer()->temppath());
313                                 bool const success = in_file.copyTo(out_file);
314                                 if (!success) {
315                                         LYXERR0("Failed to copy '" << in_file
316                                                << "' to '" << out_file << "'");
317                                 }
318                         }
319                         // FIXME UNICODE
320                         os << "\\bibliographystyle{"
321                            << from_utf8(latex_path(buffer().prepareFileNameForLaTeX(base, ".bst", runparams.nice)))
322                            << "}\n";
323                 }
324                 // Warn about spaces in bst path. Warn only once.
325                 static bool warned_about_bst_spaces = false;
326                 if (!warned_about_bst_spaces && runparams.nice && contains(style, ' ')) {
327                         warned_about_bst_spaces = true;
328                         Alert::warning(_("Export Warning!"),
329                                        _("There are spaces in the path to your BibTeX style file.\n"
330                                                       "BibTeX will be unable to find it."));
331                 }
332                 // Handle the bibtopic case
333                 if (!db_out.empty() && buffer().masterParams().useBibtopic()) {
334                         os << "\\begin{btSect}";
335                         if (!style.empty())
336                                 os << "[" << style << "]";
337                         os << "{" << getStringFromVector(db_out) << "}\n";
338                         docstring btprint = getParam("btprint");
339                         if (btprint.empty())
340                                 // default
341                                 btprint = from_ascii("btPrintCited");
342                         os << "\\" << btprint << "\n"
343                            << "\\end{btSect}\n";
344                 }
345                 // bibtotoc option
346                 if (!bibtotoc.empty() && !buffer().masterParams().useBibtopic()) {
347                         // set label for hyperref, see http://www.lyx.org/trac/ticket/6470
348                         if (buffer().masterParams().pdfoptions().use_hyperref)
349                                         os << "\\phantomsection";
350                         if (buffer().masterParams().documentClass().hasLaTeXLayout("chapter"))
351                                 os << "\\addcontentsline{toc}{chapter}{\\bibname}";
352                         else if (buffer().masterParams().documentClass().hasLaTeXLayout("section"))
353                                 os << "\\addcontentsline{toc}{section}{\\refname}";
354                 }
355                 // The bibliography command
356                 if (!db_out.empty() && !buffer().masterParams().useBibtopic()) {
357                         docstring btprint = getParam("btprint");
358                         if (btprint == "btPrintAll") {
359                                 os << "\\nocite{*}\n";
360                         }
361                         os << "\\bibliography{" << getStringFromVector(db_out) << "}\n";
362                 }
363         }
364 }
365
366
367 support::FileNamePairList InsetBibtex::getBibFiles() const
368 {
369         FileName path(buffer().filePath());
370         support::PathChanger p(path);
371
372         // We need to store both the real FileName and the way it is entered
373         // (with full path, rel path or as a single file name).
374         // The latter is needed for biblatex's central bibfile macro.
375         support::FileNamePairList vec;
376
377         vector<docstring> bibfilelist = getVectorFromString(getParam("bibfiles"));
378         vector<docstring>::const_iterator it = bibfilelist.begin();
379         vector<docstring>::const_iterator en = bibfilelist.end();
380         for (; it != en; ++it) {
381                 FileName const file = getBibTeXPath(*it, buffer());
382
383                 if (!file.empty())
384                         vec.push_back(make_pair(*it, file));
385                 else
386                         LYXERR0("Couldn't find " + to_utf8(*it) + " in InsetBibtex::getBibFiles()!");
387         }
388
389         return vec;
390
391 }
392
393 namespace {
394
395         // methods for parsing bibtex files
396
397         typedef map<docstring, docstring> VarMap;
398
399         /// remove whitespace characters, optionally a single comma,
400         /// and further whitespace characters from the stream.
401         /// @return true if a comma was found, false otherwise
402         ///
403         bool removeWSAndComma(ifdocstream & ifs) {
404                 char_type ch;
405
406                 if (!ifs)
407                         return false;
408
409                 // skip whitespace
410                 do {
411                         ifs.get(ch);
412                 } while (ifs && isSpace(ch));
413
414                 if (!ifs)
415                         return false;
416
417                 if (ch != ',') {
418                         ifs.putback(ch);
419                         return false;
420                 }
421
422                 // skip whitespace
423                 do {
424                         ifs.get(ch);
425                 } while (ifs && isSpace(ch));
426
427                 if (ifs) {
428                         ifs.putback(ch);
429                 }
430
431                 return true;
432         }
433
434
435         enum charCase {
436                 makeLowerCase,
437                 keepCase
438         };
439
440         /// remove whitespace characters, read characer sequence
441         /// not containing whitespace characters or characters in
442         /// delimChars, and remove further whitespace characters.
443         ///
444         /// @return true if a string of length > 0 could be read.
445         ///
446         bool readTypeOrKey(docstring & val, ifdocstream & ifs,
447                 docstring const & delimChars, docstring const & illegalChars,
448                 charCase chCase) {
449
450                 char_type ch;
451
452                 val.clear();
453
454                 if (!ifs)
455                         return false;
456
457                 // skip whitespace
458                 do {
459                         ifs.get(ch);
460                 } while (ifs && isSpace(ch));
461
462                 if (!ifs)
463                         return false;
464
465                 // read value
466                 bool legalChar = true;
467                 while (ifs && !isSpace(ch) &&
468                                                  delimChars.find(ch) == docstring::npos &&
469                                                  (legalChar = (illegalChars.find(ch) == docstring::npos))
470                                         )
471                 {
472                         if (chCase == makeLowerCase)
473                                 val += lowercase(ch);
474                         else
475                                 val += ch;
476                         ifs.get(ch);
477                 }
478
479                 if (!legalChar) {
480                         ifs.putback(ch);
481                         return false;
482                 }
483
484                 // skip whitespace
485                 while (ifs && isSpace(ch)) {
486                         ifs.get(ch);
487                 }
488
489                 if (ifs) {
490                         ifs.putback(ch);
491                 }
492
493                 return val.length() > 0;
494         }
495
496         /// read subsequent bibtex values that are delimited with a #-character.
497         /// Concatenate all parts and replace names with the associated string in
498         /// the variable strings.
499         /// @return true if reading was successfull (all single parts were delimited
500         /// correctly)
501         bool readValue(docstring & val, ifdocstream & ifs, const VarMap & strings) {
502
503                 char_type ch;
504
505                 val.clear();
506
507                 if (!ifs)
508                         return false;
509
510                 do {
511                         // skip whitespace
512                         do {
513                                 ifs.get(ch);
514                         } while (ifs && isSpace(ch));
515
516                         if (!ifs)
517                                 return false;
518
519                         // check for field type
520                         if (isDigitASCII(ch)) {
521
522                                 // read integer value
523                                 do {
524                                         val += ch;
525                                         ifs.get(ch);
526                                 } while (ifs && isDigitASCII(ch));
527
528                                 if (!ifs)
529                                         return false;
530
531                         } else if (ch == '"' || ch == '{') {
532                                 // set end delimiter
533                                 char_type delim = ch == '"' ? '"': '}';
534
535                                 // Skip whitespace
536                                 do {
537                                         ifs.get(ch);
538                                 } while (ifs && isSpace(ch));
539
540                                 if (!ifs)
541                                         return false;
542
543                                 // We now have the first non-whitespace character
544                                 // We'll collapse adjacent whitespace.
545                                 bool lastWasWhiteSpace = false;
546
547                                 // inside this delimited text braces must match.
548                                 // Thus we can have a closing delimiter only
549                                 // when nestLevel == 0
550                                 int nestLevel = 0;
551
552                                 while (ifs && (nestLevel > 0 || ch != delim)) {
553                                         if (isSpace(ch)) {
554                                                 lastWasWhiteSpace = true;
555                                                 ifs.get(ch);
556                                                 continue;
557                                         }
558                                         // We output the space only after we stop getting
559                                         // whitespace so as not to output any whitespace
560                                         // at the end of the value.
561                                         if (lastWasWhiteSpace) {
562                                                 lastWasWhiteSpace = false;
563                                                 val += ' ';
564                                         }
565
566                                         val += ch;
567
568                                         // update nesting level
569                                         switch (ch) {
570                                                 case '{':
571                                                         ++nestLevel;
572                                                         break;
573                                                 case '}':
574                                                         --nestLevel;
575                                                         if (nestLevel < 0)
576                                                                 return false;
577                                                         break;
578                                         }
579
580                                         if (ifs)
581                                                 ifs.get(ch);
582                                 }
583
584                                 if (!ifs)
585                                         return false;
586
587                                 // FIXME Why is this here?
588                                 ifs.get(ch);
589
590                                 if (!ifs)
591                                         return false;
592
593                         } else {
594
595                                 // reading a string name
596                                 docstring strName;
597
598                                 while (ifs && !isSpace(ch) && ch != '#' && ch != ',' && ch != '}' && ch != ')') {
599                                         strName += lowercase(ch);
600                                         ifs.get(ch);
601                                 }
602
603                                 if (!ifs)
604                                         return false;
605
606                                 // replace the string with its assigned value or
607                                 // discard it if it's not assigned
608                                 if (strName.length()) {
609                                         VarMap::const_iterator pos = strings.find(strName);
610                                         if (pos != strings.end()) {
611                                                 val += pos->second;
612                                         }
613                                 }
614                         }
615
616                         // skip WS
617                         while (ifs && isSpace(ch)) {
618                                 ifs.get(ch);
619                         }
620
621                         if (!ifs)
622                                 return false;
623
624                         // continue reading next value on concatenate with '#'
625                 } while (ch == '#');
626
627                 ifs.putback(ch);
628
629                 return true;
630         }
631 }
632
633
634 void InsetBibtex::collectBibKeys(InsetIterator const & /*di*/) const
635 {
636         parseBibTeXFiles();
637 }
638
639
640 void InsetBibtex::parseBibTeXFiles() const
641 {
642         // This bibtex parser is a first step to parse bibtex files
643         // more precisely.
644         //
645         // - it reads the whole bibtex entry and does a syntax check
646         //   (matching delimiters, missing commas,...
647         // - it recovers from errors starting with the next @-character
648         // - it reads @string definitions and replaces them in the
649         //   field values.
650         // - it accepts more characters in keys or value names than
651         //   bibtex does.
652         //
653         // Officially bibtex does only support ASCII, but in practice
654         // you can use the encoding of the main document as long as
655         // some elements like keys and names are pure ASCII. Therefore
656         // we convert the file from the buffer encoding.
657         // We don't restrict keys to ASCII in LyX, since our own
658         // InsetBibitem can generate non-ASCII keys, and nonstandard
659         // 8bit clean bibtex forks exist.
660
661         BiblioInfo keylist;
662
663         support::FileNamePairList const files = getBibFiles();
664         support::FileNamePairList::const_iterator it = files.begin();
665         support::FileNamePairList::const_iterator en = files.end();
666         for (; it != en; ++ it) {
667                 ifdocstream ifs(it->second.toFilesystemEncoding().c_str(),
668                         ios_base::in, buffer().masterParams().encoding().iconvName());
669
670                 char_type ch;
671                 VarMap strings;
672
673                 while (ifs) {
674
675                         ifs.get(ch);
676                         if (!ifs)
677                                 break;
678
679                         if (ch != '@')
680                                 continue;
681
682                         docstring entryType;
683
684                         if (!readTypeOrKey(entryType, ifs, from_ascii("{("), docstring(), makeLowerCase)) {
685                                 lyxerr << "BibTeX Parser: Error reading entry type." << std::endl;
686                                 continue;
687                         }
688
689                         if (!ifs) {
690                                 lyxerr << "BibTeX Parser: Unexpected end of file." << std::endl;
691                                 continue;
692                         }
693
694                         if (entryType == from_ascii("comment")) {
695                                 ifs.ignore(numeric_limits<int>::max(), '\n');
696                                 continue;
697                         }
698
699                         ifs.get(ch);
700                         if (!ifs) {
701                                 lyxerr << "BibTeX Parser: Unexpected end of file." << std::endl;
702                                 break;
703                         }
704
705                         if ((ch != '(') && (ch != '{')) {
706                                 lyxerr << "BibTeX Parser: Invalid entry delimiter." << std::endl;
707                                 ifs.putback(ch);
708                                 continue;
709                         }
710
711                         // process the entry
712                         if (entryType == from_ascii("string")) {
713
714                                 // read string and add it to the strings map
715                                 // (or replace it's old value)
716                                 docstring name;
717                                 docstring value;
718
719                                 if (!readTypeOrKey(name, ifs, from_ascii("="), from_ascii("#{}(),"), makeLowerCase)) {
720                                         lyxerr << "BibTeX Parser: Error reading string name." << std::endl;
721                                         continue;
722                                 }
723
724                                 if (!ifs) {
725                                         lyxerr << "BibTeX Parser: Unexpected end of file." << std::endl;
726                                         continue;
727                                 }
728
729                                 // next char must be an equal sign
730                                 ifs.get(ch);
731                                 if (!ifs || ch != '=') {
732                                         lyxerr << "BibTeX Parser: No `=' after string name: " <<
733                                                         name << "." << std::endl;
734                                         continue;
735                                 }
736
737                                 if (!readValue(value, ifs, strings)) {
738                                         lyxerr << "BibTeX Parser: Unable to read value for string: " <<
739                                                         name << "." << std::endl;
740                                         continue;
741                                 }
742
743                                 strings[name] = value;
744
745                         } else if (entryType == from_ascii("preamble")) {
746
747                                 // preamble definitions are discarded.
748                                 // can they be of any use in lyx?
749                                 docstring value;
750
751                                 if (!readValue(value, ifs, strings)) {
752                                         lyxerr << "BibTeX Parser: Unable to read preamble value." << std::endl;
753                                         continue;
754                                 }
755
756                         } else {
757
758                                 // Citation entry. Try to read the key.
759                                 docstring key;
760
761                                 if (!readTypeOrKey(key, ifs, from_ascii(","), from_ascii("}"), keepCase)) {
762                                         lyxerr << "BibTeX Parser: Unable to read key for entry type:" <<
763                                                         entryType << "." << std::endl;
764                                         continue;
765                                 }
766
767                                 if (!ifs) {
768                                         lyxerr << "BibTeX Parser: Unexpected end of file." << std::endl;
769                                         continue;
770                                 }
771
772                                 /////////////////////////////////////////////
773                                 // now we have a key, so we will add an entry
774                                 // (even if it's empty, as bibtex does)
775                                 //
776                                 // we now read the field = value pairs.
777                                 // all items must be separated by a comma. If
778                                 // it is missing the scanning of this entry is
779                                 // stopped and the next is searched.
780                                 docstring name;
781                                 docstring value;
782                                 docstring data;
783                                 BibTeXInfo keyvalmap(key, entryType);
784
785                                 bool readNext = removeWSAndComma(ifs);
786
787                                 while (ifs && readNext) {
788
789                                         // read field name
790                                         if (!readTypeOrKey(name, ifs, from_ascii("="),
791                                                            from_ascii("{}(),"), makeLowerCase) || !ifs)
792                                                 break;
793
794                                         // next char must be an equal sign
795                                         // FIXME Whitespace??
796                                         ifs.get(ch);
797                                         if (!ifs) {
798                                                 lyxerr << "BibTeX Parser: Unexpected end of file." << std::endl;
799                                                 break;
800                                         }
801                                         if (ch != '=') {
802                                                 lyxerr << "BibTeX Parser: Missing `=' after field name: " <<
803                                                                 name << ", for key: " << key << "." << std::endl;
804                                                 ifs.putback(ch);
805                                                 break;
806                                         }
807
808                                         // read field value
809                                         if (!readValue(value, ifs, strings)) {
810                                                 lyxerr << "BibTeX Parser: Unable to read value for field: " <<
811                                                                 name << ", for key: " << key << "." << std::endl;
812                                                 break;
813                                         }
814
815                                         keyvalmap[name] = value;
816                                         data += "\n\n" + value;
817                                         keylist.addFieldName(name);
818                                         readNext = removeWSAndComma(ifs);
819                                 }
820
821                                 // add the new entry
822                                 keylist.addEntryType(entryType);
823                                 keyvalmap.setAllData(data);
824                                 keylist[key] = keyvalmap;
825                         } //< else (citation entry)
826                 } //< searching '@'
827         } //< for loop over files
828
829         buffer().addBiblioInfo(keylist);
830 }
831
832
833 FileName InsetBibtex::getBibTeXPath(docstring const & filename, Buffer const & buf)
834 {
835         string texfile = changeExtension(to_utf8(filename), "bib");
836         // note that, if the filename can be found directly from the path,
837         // findtexfile will just return a FileName object for that path.
838         FileName file(findtexfile(texfile, "bib"));
839         if (file.empty())
840                 file = FileName(makeAbsPath(texfile, buf.filePath()));
841         return file;
842 }
843
844
845 bool InsetBibtex::addDatabase(docstring const & db)
846 {
847         docstring bibfiles = getParam("bibfiles");
848         if (tokenPos(bibfiles, ',', db) != -1)
849                 return false;
850         if (!bibfiles.empty())
851                 bibfiles += ',';
852         setParam("bibfiles", bibfiles + db);
853         return true;
854 }
855
856
857 bool InsetBibtex::delDatabase(docstring const & db)
858 {
859         docstring bibfiles = getParam("bibfiles");
860         if (contains(bibfiles, db)) {
861                 int const n = tokenPos(bibfiles, ',', db);
862                 docstring bd = db;
863                 if (n > 0) {
864                         // this is not the first database
865                         docstring tmp = ',' + bd;
866                         setParam("bibfiles", subst(bibfiles, tmp, docstring()));
867                 } else if (n == 0)
868                         // this is the first (or only) database
869                         setParam("bibfiles", split(bibfiles, bd, ','));
870                 else
871                         return false;
872         }
873         return true;
874 }
875
876
877 void InsetBibtex::validate(LaTeXFeatures & features) const
878 {
879         if (features.buffer().masterParams().useBibtopic())
880                 features.require("bibtopic");
881         // FIXME XHTML
882         // It'd be better to be able to get this from an InsetLayout, but at present
883         // InsetLayouts do not seem really to work for things that aren't InsetTexts.
884         if (features.runparams().flavor == OutputParams::HTML)
885                 features.addCSSSnippet("div.bibtexentry { margin-left: 2em; text-indent: -2em; }\n"
886                         "span.bibtexlabel:before{ content: \"[\"; }\n"
887                         "span.bibtexlabel:after{ content: \"] \"; }");
888 }
889
890
891 int InsetBibtex::plaintext(odocstringstream & os,
892        OutputParams const & op, size_t max_length) const
893 {
894         docstring const reflabel = buffer().B_("References");
895
896         // We could output more information here, e.g., what databases are included
897         // and information about options. But I don't necessarily see any reason to
898         // do this right now.
899         if (op.for_tooltip || op.for_toc || op.for_search) {
900                 os << '[' << reflabel << ']' << '\n';
901                 return PLAINTEXT_NEWLINE;
902         }
903
904         BiblioInfo bibinfo = buffer().masterBibInfo();
905         bibinfo.makeCitationLabels(buffer());
906         vector<docstring> const & cites = bibinfo.citedEntries();
907
908         size_t start_size = os.str().size();
909         docstring refoutput;
910         refoutput += reflabel + "\n\n";
911
912         // Tell BiblioInfo our purpose
913         CiteItem ci;
914         ci.context = CiteItem::Export;
915
916         // Now we loop over the entries
917         vector<docstring>::const_iterator vit = cites.begin();
918         vector<docstring>::const_iterator const ven = cites.end();
919         for (; vit != ven; ++vit) {
920                 if (start_size + refoutput.size() >= max_length)
921                         break;
922                 BiblioInfo::const_iterator const biit = bibinfo.find(*vit);
923                 if (biit == bibinfo.end())
924                         continue;
925                 BibTeXInfo const & entry = biit->second;
926                 refoutput += "[" + entry.label() + "] ";
927                 // FIXME Right now, we are calling BibInfo::getInfo on the key,
928                 // which will give us all the cross-referenced info. But for every
929                 // entry, so there's a lot of repitition. This should be fixed.
930                 refoutput += bibinfo.getInfo(entry.key(), buffer(), ci) + "\n\n";
931         }
932         os << refoutput;
933         return refoutput.size();
934 }
935
936
937 // FIXME
938 // docstring InsetBibtex::entriesAsXHTML(vector<docstring> const & entries)
939 // And then here just: entriesAsXHTML(buffer().masterBibInfo().citedEntries())
940 docstring InsetBibtex::xhtml(XHTMLStream & xs, OutputParams const &) const
941 {
942         BiblioInfo const & bibinfo = buffer().masterBibInfo();
943         bool const all_entries = getParam("btprint") == "btPrintAll";
944         vector<docstring> const & cites = 
945             all_entries ? bibinfo.getKeys() : bibinfo.citedEntries();
946
947         docstring const reflabel = buffer().B_("References");
948
949         // tell BiblioInfo our purpose
950         CiteItem ci;
951         ci.context = CiteItem::Export;
952         ci.richtext = true;
953         ci.max_key_size = UINT_MAX;
954
955         xs << html::StartTag("h2", "class='bibtex'")
956                 << reflabel
957                 << html::EndTag("h2")
958                 << html::StartTag("div", "class='bibtex'");
959
960         // Now we loop over the entries
961         vector<docstring>::const_iterator vit = cites.begin();
962         vector<docstring>::const_iterator const ven = cites.end();
963         for (; vit != ven; ++vit) {
964                 BiblioInfo::const_iterator const biit = bibinfo.find(*vit);
965                 if (biit == bibinfo.end())
966                         continue;
967
968                 BibTeXInfo const & entry = biit->second;
969                 string const attr = "class='bibtexentry' id='LyXCite-" 
970                     + to_utf8(html::cleanAttr(entry.key())) + "'";
971                 xs << html::StartTag("div", attr);
972                 
973                 // don't print labels if we're outputting all entries
974                 if (!all_entries) {
975                         xs << html::StartTag("span", "class='bibtexlabel'")
976                                 << entry.label()
977                                 << html::EndTag("span");
978                 }
979                 
980                 // FIXME Right now, we are calling BibInfo::getInfo on the key,
981                 // which will give us all the cross-referenced info. But for every
982                 // entry, so there's a lot of repitition. This should be fixed.
983                 xs << html::StartTag("span", "class='bibtexinfo'")
984                    << XHTMLStream::ESCAPE_AND
985                    << bibinfo.getInfo(entry.key(), buffer(), ci)
986                    << html::EndTag("span")
987                    << html::EndTag("div")
988                    << html::CR();
989         }
990         xs << html::EndTag("div");
991         return docstring();
992 }
993
994
995 void InsetBibtex::write(ostream & os) const
996 {
997         params().Write(os, &buffer());
998 }
999
1000
1001 string InsetBibtex::contextMenuName() const
1002 {
1003         return "context-bibtex";
1004 }
1005
1006
1007 } // namespace lyx