3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alejandro Aguilar Sierra
8 * Full author contact details are available in file CREDITS.
13 #include "insetbibtex.h"
16 #include "bufferparams.h"
17 #include "dispatchresult.h"
19 #include "funcrequest.h"
21 #include "LaTeXFeatures.h"
22 #include "metricsinfo.h"
23 #include "outputparams.h"
25 #include "frontends/Alert.h"
27 #include "support/filetools.h"
28 #include "support/lstrings.h"
29 #include "support/lyxlib.h"
30 #include "support/os.h"
31 #include "support/path.h"
33 #include <boost/tokenizer.hpp>
41 using support::absolutePath;
42 using support::ascii_lowercase;
43 using support::changeExtension;
44 using support::contains;
46 using support::DocFileName;
47 using support::FileName;
48 using support::findtexfile;
49 using support::isFileReadable;
50 using support::latex_path;
52 using support::makeAbsPath;
53 using support::makeRelPath;
55 using support::prefixIs;
56 using support::removeExtension;
60 using support::tokenPos;
63 namespace Alert = frontend::Alert;
64 namespace os = support::os;
75 InsetBibtex::InsetBibtex(InsetCommandParams const & p)
76 : InsetCommand(p, "bibtex")
80 std::auto_ptr<InsetBase> InsetBibtex::doClone() const
82 return std::auto_ptr<InsetBase>(new InsetBibtex(*this));
86 void InsetBibtex::doDispatch(LCursor & cur, FuncRequest & cmd)
90 case LFUN_INSET_MODIFY: {
91 InsetCommandParams p("bibtex");
92 InsetCommandMailer::string2params("bibtex", to_utf8(cmd.argument()), p);
93 if (!p.getCmdName().empty()) {
95 cur.buffer().updateBibfilesCache();
102 InsetCommand::doDispatch(cur, cmd);
108 docstring const InsetBibtex::getScreenLabel(Buffer const &) const
110 return _("BibTeX Generated Bibliography");
116 string normalize_name(Buffer const & buffer, OutputParams const & runparams,
117 string const & name, string const & ext)
119 string const fname = makeAbsPath(name, buffer.filePath());
120 if (absolutePath(name) || !isFileReadable(FileName(fname + ext)))
122 else if (!runparams.nice)
125 return makeRelPath(fname, buffer.getMasterBuffer()->filePath());
131 int InsetBibtex::latex(Buffer const & buffer, odocstream & os,
132 OutputParams const & runparams) const
134 // the sequence of the commands:
135 // 1. \bibliographystyle{style}
136 // 2. \addcontentsline{...} - if option bibtotoc set
137 // 3. \bibliography{database}
138 // and with bibtopic:
139 // 1. \bibliographystyle{style}
140 // 2. \begin{btSect}{database}
141 // 3. \btPrint{Cited|NotCited|All}
145 // If we are processing the LaTeX file in a temp directory then
146 // copy the .bib databases to this temp directory, mangling their
147 // names in the process. Store this mangled name in the list of
149 // (We need to do all this because BibTeX *really*, *really*
150 // can't handle "files with spaces" and Windows users tend to
151 // use such filenames.)
152 // Otherwise, store the (maybe absolute) path to the original,
153 // unmangled database name.
154 typedef boost::char_separator<char_type> Separator;
155 typedef boost::tokenizer<Separator, docstring::const_iterator, docstring> Tokenizer;
157 Separator const separator(from_ascii(",").c_str());
158 // The tokenizer must not be called with temporary strings, since
159 // it does not make a copy and uses iterators of the string further
160 // down. getParam returns a reference, so this is OK.
161 Tokenizer const tokens(getParam("bibfiles"), separator);
162 Tokenizer::const_iterator const begin = tokens.begin();
163 Tokenizer::const_iterator const end = tokens.end();
165 odocstringstream dbs;
166 for (Tokenizer::const_iterator it = begin; it != end; ++it) {
167 docstring const input = trim(*it);
169 string utf8input(to_utf8(input));
171 normalize_name(buffer, runparams, utf8input, ".bib");
172 string const try_in_file = makeAbsPath(database + ".bib", buffer.filePath());
173 bool const not_from_texmf = isFileReadable(FileName(try_in_file));
175 if (!runparams.inComment && !runparams.dryrun && !runparams.nice &&
178 // mangledFilename() needs the extension
179 DocFileName const in_file = DocFileName(try_in_file);
180 database = removeExtension(in_file.mangledFilename());
181 FileName const out_file = FileName(makeAbsPath(database + ".bib",
182 buffer.getMasterBuffer()->temppath()));
184 bool const success = copy(in_file, out_file);
186 lyxerr << "Failed to copy '" << in_file
187 << "' to '" << out_file << "'"
195 dbs << from_utf8(latex_path(database));
197 docstring const db_out = dbs.str();
199 // Post this warning only once.
200 static bool warned_about_spaces = false;
201 if (!warned_about_spaces &&
202 runparams.nice && db_out.find(' ') != docstring::npos) {
203 warned_about_spaces = true;
205 Alert::warning(_("Export Warning!"),
206 _("There are spaces in the paths to your BibTeX databases.\n"
207 "BibTeX will be unable to find them."));
212 string style = to_utf8(getParam("options")); // maybe empty! and with bibtotoc
214 if (prefixIs(style, "bibtotoc")) {
215 bibtotoc = "bibtotoc";
216 if (contains(style, ',')) {
217 style = split(style, bibtotoc, ',');
224 if (!style.empty()) {
226 normalize_name(buffer, runparams, style, ".bst");
227 string const try_in_file = makeAbsPath(base + ".bst", buffer.filePath());
228 bool const not_from_texmf = isFileReadable(FileName(try_in_file));
229 // If this style does not come from texmf and we are not
230 // exporting to .tex copy it to the tmp directory.
231 // This prevents problems with spaces and 8bit charcaters
233 if (!runparams.inComment && !runparams.dryrun && !runparams.nice &&
235 // use new style name
236 DocFileName const in_file = DocFileName(try_in_file);
237 base = removeExtension(in_file.mangledFilename());
238 FileName const out_file = FileName(makeAbsPath(base + ".bst",
239 buffer.getMasterBuffer()->temppath()));
240 bool const success = copy(in_file, out_file);
242 lyxerr << "Failed to copy '" << in_file
243 << "' to '" << out_file << "'"
248 os << "\\bibliographystyle{"
249 << from_utf8(latex_path(normalize_name(buffer, runparams, base, ".bst")))
254 // Post this warning only once.
255 static bool warned_about_bst_spaces = false;
256 if (!warned_about_bst_spaces && runparams.nice && contains(style, ' ')) {
257 warned_about_bst_spaces = true;
258 Alert::warning(_("Export Warning!"),
259 _("There are spaces in the path to your BibTeX style file.\n"
260 "BibTeX will be unable to find it."));
263 if (!db_out.empty() && buffer.params().use_bibtopic){
264 os << "\\begin{btSect}{" << db_out << "}\n";
265 docstring btprint = getParam("btprint");
268 btprint = from_ascii("btPrintCited");
269 os << "\\" << btprint << "\n"
270 << "\\end{btSect}\n";
275 if (!bibtotoc.empty() && !buffer.params().use_bibtopic) {
276 // maybe a problem when a textclass has no "art" as
277 // part of its name, because it's than book.
278 // For the "official" lyx-layouts it's no problem to support
280 if (!contains(buffer.params().getLyXTextClass().name(),
282 if (buffer.params().sides == LyXTextClass::OneSide) {
287 os << "\\cleardoublepage";
291 os << "\\addcontentsline{toc}{chapter}{\\bibname}";
295 os << "\\addcontentsline{toc}{section}{\\refname}";
299 if (!db_out.empty() && !buffer.params().use_bibtopic){
300 os << "\\bibliography{" << db_out << "}\n";
308 vector<FileName> const InsetBibtex::getFiles(Buffer const & buffer) const
310 Path p(buffer.filePath());
312 vector<FileName> vec;
316 string bibfiles = to_utf8(getParam("bibfiles"));
317 bibfiles = split(bibfiles, tmp, ',');
318 while (!tmp.empty()) {
319 FileName const file = findtexfile(changeExtension(tmp, "bib"), "bib");
320 lyxerr[Debug::LATEX] << "Bibfile: " << file << endl;
322 // If we didn't find a matching file name just fail silently
326 // Get next file name
327 bibfiles = split(bibfiles, tmp, ',');
334 // This method returns a comma separated list of Bibtex entries
335 void InsetBibtex::fillWithBibKeys(Buffer const & buffer,
336 std::vector<std::pair<string, string> > & keys) const
338 vector<FileName> const files = getFiles(buffer);
339 for (vector<FileName>::const_iterator it = files.begin();
340 it != files.end(); ++ it) {
341 // This is a _very_ simple parser for Bibtex database
342 // files. All it does is to look for lines starting
343 // in @ and not being @preamble and @string entries.
344 // It does NOT do any syntax checking!
345 ifstream ifs(it->toFilesystemEncoding().c_str());
347 while (getline(ifs, linebuf0)) {
348 string linebuf = trim(linebuf0);
349 if (linebuf.empty()) continue;
350 if (prefixIs(linebuf, "@")) {
351 linebuf = subst(linebuf, '{', '(');
353 linebuf = split(linebuf, tmp, '(');
354 tmp = ascii_lowercase(tmp);
355 if (!prefixIs(tmp, "@string")
356 && !prefixIs(tmp, "@preamble")) {
357 linebuf = split(linebuf, tmp, ',');
358 tmp = ltrim(tmp, " \t");
360 keys.push_back(pair<string,string>(tmp,string()));
363 } else if (!keys.empty()) {
364 keys.back().second += linebuf + "\n";
371 bool InsetBibtex::addDatabase(string const & db)
374 string bibfiles(to_utf8(getParam("bibfiles")));
375 if (tokenPos(bibfiles, ',', db) == -1) {
376 if (!bibfiles.empty())
378 setParam("bibfiles", from_utf8(bibfiles + db));
385 bool InsetBibtex::delDatabase(string const & db)
388 string bibfiles(to_utf8(getParam("bibfiles")));
389 if (contains(bibfiles, db)) {
390 int const n = tokenPos(bibfiles, ',', db);
393 // this is not the first database
394 string tmp = ',' + bd;
395 setParam("bibfiles", from_utf8(subst(bibfiles, tmp, string())));
397 // this is the first (or only) database
398 setParam("bibfiles", from_utf8(split(bibfiles, bd, ',')));
406 void InsetBibtex::validate(LaTeXFeatures & features) const
408 if (features.bufferParams().use_bibtopic)
409 features.require("bibtopic");