1 /**
2  * \file Buffer.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Stefan Schimanski
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11 
12 #include <config.h>
13 
14 #include "Buffer.h"
15 
16 #include "Author.h"
17 #include "LayoutFile.h"
18 #include "BiblioInfo.h"
19 #include "BranchList.h"
20 #include "buffer_funcs.h"
21 #include "BufferList.h"
22 #include "BufferParams.h"
23 #include "Bullet.h"
24 #include "Chktex.h"
25 #include "Converter.h"
26 #include "Counters.h"
27 #include "Cursor.h"
28 #include "CutAndPaste.h"
29 #include "DispatchResult.h"
30 #include "DocIterator.h"
31 #include "BufferEncodings.h"
32 #include "ErrorList.h"
33 #include "Exporter.h"
34 #include "Format.h"
35 #include "FuncRequest.h"
36 #include "FuncStatus.h"
37 #include "IndicesList.h"
38 #include "InsetIterator.h"
39 #include "InsetList.h"
40 #include "Language.h"
41 #include "LaTeXFeatures.h"
42 #include "LaTeX.h"
43 #include "Layout.h"
44 #include "Lexer.h"
45 #include "LyXAction.h"
46 #include "LyX.h"
47 #include "LyXRC.h"
48 #include "LyXVC.h"
49 #include "output_docbook.h"
50 #include "output.h"
51 #include "output_latex.h"
52 #include "output_xhtml.h"
53 #include "output_plaintext.h"
54 #include "Paragraph.h"
55 #include "ParagraphParameters.h"
56 #include "ParIterator.h"
57 #include "PDFOptions.h"
58 #include "Session.h"
59 #include "SpellChecker.h"
60 #include "sgml.h"
61 #include "texstream.h"
62 #include "TexRow.h"
63 #include "Text.h"
64 #include "TextClass.h"
65 #include "TocBackend.h"
66 #include "Undo.h"
67 #include "VCBackend.h"
68 #include "version.h"
69 #include "WordLangTuple.h"
70 #include "WordList.h"
71 
72 #include "insets/InsetBibtex.h"
73 #include "insets/InsetBranch.h"
74 #include "insets/InsetInclude.h"
75 #include "insets/InsetTabular.h"
76 #include "insets/InsetText.h"
77 
78 #include "mathed/InsetMathHull.h"
79 #include "mathed/MacroTable.h"
80 #include "mathed/InsetMathMacroTemplate.h"
81 #include "mathed/MathSupport.h"
82 
83 #include "graphics/GraphicsCache.h"
84 #include "graphics/PreviewLoader.h"
85 
86 #include "frontends/alert.h"
87 #include "frontends/Delegates.h"
88 #include "frontends/WorkAreaManager.h"
89 
90 #include "support/lassert.h"
91 #include "support/convert.h"
92 #include "support/debug.h"
93 #include "support/docstring_list.h"
94 #include "support/ExceptionMessage.h"
95 #include "support/FileMonitor.h"
96 #include "support/FileName.h"
97 #include "support/FileNameList.h"
98 #include "support/filetools.h"
99 #include "support/ForkedCalls.h"
100 #include "support/gettext.h"
101 #include "support/gzstream.h"
102 #include "support/lstrings.h"
103 #include "support/lyxalgo.h"
104 #include "support/mutex.h"
105 #include "support/os.h"
106 #include "support/Package.h"
107 #include "support/PathChanger.h"
108 #include "support/Systemcall.h"
109 #include "support/TempFile.h"
110 #include "support/textutils.h"
111 #include "support/types.h"
112 
113 #include "support/bind.h"
114 
115 #include <algorithm>
116 #include <fstream>
117 #include <iomanip>
118 #include <map>
119 #include <memory>
120 #include <set>
121 #include <sstream>
122 #include <vector>
123 
124 using namespace std;
125 using namespace lyx::support;
126 using namespace lyx::graphics;
127 
128 namespace lyx {
129 
130 namespace Alert = frontend::Alert;
131 namespace os = support::os;
132 
133 namespace {
134 
135 int const LYX_FORMAT = LYX_FORMAT_LYX;
136 
137 typedef map<string, bool> DepClean;
138 
139 // Information about labels and their associated refs
140 struct LabelInfo {
141 	/// label string
142 	docstring label;
143 	/// label inset
144 	InsetLabel const * inset;
145 	/// associated references cache
146 	Buffer::References references;
147 	/// whether this label is active (i.e., not deleted)
148 	bool active;
149 };
150 
151 typedef vector<LabelInfo> LabelCache;
152 
153 typedef map<docstring, Buffer::References> RefCache;
154 
155 } // namespace
156 
157 
158 // A storehouse for the cloned buffers.
159 list<CloneList *> cloned_buffers;
160 
161 
162 class Buffer::Impl
163 {
164 public:
165 	Impl(Buffer * owner, FileName const & file, bool readonly, Buffer const * cloned_buffer);
166 
~Impl()167 	~Impl()
168 	{
169 		delete preview_loader_;
170 		if (wa_) {
171 			wa_->closeAll();
172 			delete wa_;
173 		}
174 		delete inset;
175 	}
176 
177 	/// search for macro in local (buffer) table or in children
178 	MacroData const * getBufferMacro(docstring const & name,
179 		DocIterator const & pos) const;
180 
181 	/// Update macro table starting with position of it \param it in some
182 	/// text inset.
183 	void updateMacros(DocIterator & it, DocIterator & scope);
184 	///
185 	void setLabel(ParIterator & it, UpdateType utype) const;
186 
187 	/** If we have branches that use the file suffix
188 	    feature, return the file name with suffix appended.
189 	*/
190 	support::FileName exportFileName() const;
191 
192 	Buffer * owner_;
193 
194 	BufferParams params;
195 	LyXVC lyxvc;
196 	FileName temppath;
197 	mutable TexRow texrow;
198 
199 	/// need to regenerate .tex?
200 	DepClean dep_clean;
201 
202 	/// is save needed?
203 	mutable bool lyx_clean;
204 
205 	/// is autosave needed?
206 	mutable bool bak_clean;
207 
208 	/// is this an unnamed file (New...)?
209 	bool unnamed;
210 
211 	/// is this an internal bufffer?
212 	bool internal_buffer;
213 
214 	/// buffer is r/o
215 	bool read_only;
216 
217 	/// name of the file the buffer is associated with.
218 	FileName filename;
219 
220 	/** Set to true only when the file is fully loaded.
221 	 *  Used to prevent the premature generation of previews
222 	 *  and by the citation inset.
223 	 */
224 	bool file_fully_loaded;
225 
226 	/// original format of loaded file
227 	int file_format;
228 
229 	/// if the file was originally loaded from an older format, do
230 	/// we need to back it up still?
231 	bool need_format_backup;
232 
233 	/// Ignore the parent (e.g. when exporting a child standalone)?
234 	bool ignore_parent;
235 
236 	///
237 	mutable TocBackend toc_backend;
238 
239 	/// macro tables
240 	struct ScopeMacro {
ScopeMacrolyx::Buffer::Impl::ScopeMacro241 		ScopeMacro() {}
ScopeMacrolyx::Buffer::Impl::ScopeMacro242 		ScopeMacro(DocIterator const & s, MacroData const & m)
243 			: scope(s), macro(m) {}
244 		DocIterator scope;
245 		MacroData macro;
246 	};
247 	typedef map<DocIterator, ScopeMacro> PositionScopeMacroMap;
248 	typedef map<docstring, PositionScopeMacroMap> NamePositionScopeMacroMap;
249 	/// map from the macro name to the position map,
250 	/// which maps the macro definition position to the scope and the MacroData.
251 	NamePositionScopeMacroMap macros;
252 	/// This seem to change the way Buffer::getMacro() works
253 	mutable bool macro_lock;
254 
255 	/// positions of child buffers in the buffer
256 	typedef map<Buffer const * const, DocIterator> BufferPositionMap;
257 	struct ScopeBuffer {
ScopeBufferlyx::Buffer::Impl::ScopeBuffer258 		ScopeBuffer() : buffer(0) {}
ScopeBufferlyx::Buffer::Impl::ScopeBuffer259 		ScopeBuffer(DocIterator const & s, Buffer const * b)
260 			: scope(s), buffer(b) {}
261 		DocIterator scope;
262 		Buffer const * buffer;
263 	};
264 	typedef map<DocIterator, ScopeBuffer> PositionScopeBufferMap;
265 	/// position of children buffers in this buffer
266 	BufferPositionMap children_positions;
267 	/// map from children inclusion positions to their scope and their buffer
268 	PositionScopeBufferMap position_to_children;
269 
270 	/// Contains the old buffer filePath() while saving-as, or the
271 	/// directory where the document was last saved while loading.
272 	string old_position;
273 
274 	/** Keeps track of the path of local layout files.
275 	 *  If possible, it is always relative to the buffer path.
276 	 *  Empty for layouts in system or user directory.
277 	 */
278 	string layout_position;
279 
280 	/// Container for all sort of Buffer dependant errors.
281 	map<string, ErrorList> errorLists;
282 
283 	/// checksum used to test if the file has been externally modified.  Used to
284 	/// double check whether the file had been externally modified when saving.
285 	unsigned long checksum_;
286 
287 	///
288 	frontend::WorkAreaManager * wa_;
289 	///
290 	frontend::GuiBufferDelegate * gui_;
291 
292 	///
293 	Undo undo_;
294 
295 	/// A cache for the bibfiles (including bibfiles of loaded child
296 	/// documents), needed for appropriate update of natbib labels.
297 	mutable docstring_list bibfiles_cache_;
298 
299 	// FIXME The caching mechanism could be improved. At present, we have a
300 	// cache for each Buffer, that caches all the bibliography info for that
301 	// Buffer. A more efficient solution would be to have a global cache per
302 	// file, and then to construct the Buffer's bibinfo from that.
303 	/// A cache for bibliography info
304 	mutable BiblioInfo bibinfo_;
305 	/// whether the bibinfo cache is valid
306 	mutable bool bibinfo_cache_valid_;
307 	/// whether the bibfile cache is valid
308 	mutable bool bibfile_cache_valid_;
309 	/// Cache of timestamps of .bib files
310 	map<FileName, time_t> bibfile_status_;
311 	/// Indicates whether the bibinfo has changed since the last time
312 	/// we ran updateBuffer(), i.e., whether citation labels may need
313 	/// to be updated.
314 	mutable bool cite_labels_valid_;
315 	/// Do we have a bibliography environment?
316 	mutable bool have_bibitems_;
317 
318 	/// These two hold the file name and format, written to by
319 	/// Buffer::preview and read from by LFUN_BUFFER_VIEW_CACHE.
320 	FileName preview_file_;
321 	string preview_format_;
322 	/// If there was an error when previewing, on the next preview we do
323 	/// a fresh compile (e.g. in case the user installed a package that
324 	/// was missing).
325 	bool preview_error_;
326 
327 	/// Cache the references associated to a label and their positions
328 	/// in the buffer.
329 	mutable RefCache ref_cache_;
330 	/// Cache the label insets and their activity status.
331 	mutable LabelCache label_cache_;
332 
333 	/// our Text that should be wrapped in an InsetText
334 	InsetText * inset;
335 
336 	///
337 	PreviewLoader * preview_loader_;
338 
339 	/// This is here to force the test to be done whenever parent_buffer
340 	/// is accessed.
parent() const341 	Buffer const * parent() const
342 	{
343 		// ignore_parent temporarily "orphans" a buffer
344 		// (e.g. if a child is compiled standalone)
345 		if (ignore_parent)
346 			return 0;
347 		// if parent_buffer is not loaded, then it has been unloaded,
348 		// which means that parent_buffer is an invalid pointer. So we
349 		// set it to null in that case.
350 		// however, the BufferList doesn't know about cloned buffers, so
351 		// they will always be regarded as unloaded. in that case, we hope
352 		// for the best.
353 		if (!cloned_buffer_ && !theBufferList().isLoaded(parent_buffer))
354 			parent_buffer = 0;
355 		return parent_buffer;
356 	}
357 
358 	///
setParent(Buffer const * pb)359 	void setParent(Buffer const * pb)
360 	{
361 		if (parent_buffer == pb)
362 			// nothing to do
363 			return;
364 		if (!cloned_buffer_ && parent_buffer && pb)
365 			LYXERR0("Warning: a buffer should not have two parents!");
366 		parent_buffer = pb;
367 		if (!cloned_buffer_ && parent_buffer) {
368 			parent_buffer->invalidateBibinfoCache();
369 		}
370 	}
371 
372 	/// If non zero, this buffer is a clone of existing buffer \p cloned_buffer_
373 	/// This one is useful for preview detached in a thread.
374 	Buffer const * cloned_buffer_;
375 	///
376 	CloneList * clone_list_;
377 	/// are we in the process of exporting this buffer?
378 	mutable bool doing_export;
379 
380 	/// compute statistics
381 	/// \p from initial position
382 	/// \p to points to the end position
383 	void updateStatistics(DocIterator & from, DocIterator & to,
384 			      bool skipNoOutput = true);
385 	/// statistics accessor functions
wordCount() const386 	int wordCount() const
387 	{
388 		return word_count_;
389 	}
charCount(bool with_blanks) const390 	int charCount(bool with_blanks) const
391 	{
392 		return char_count_
393 		+ (with_blanks ? blank_count_ : 0);
394 	}
395 
396 	// does the buffer contain tracked changes? (if so, we automatically
397 	// display the review toolbar, for instance)
398 	mutable bool tracked_changes_present_;
399 
400 	// Make sure the file monitor monitors the good file.
401 	void refreshFileMonitor();
402 
403 	/// Notify or clear of external modification
404 	void fileExternallyModified(bool exists);
405 
406 	/// has been externally modified? Can be reset by the user.
407 	mutable bool externally_modified_;
408 
409 private:
410 	/// So we can force access via the accessors.
411 	mutable Buffer const * parent_buffer;
412 
413 	int word_count_;
414 	int char_count_;
415 	int blank_count_;
416 
417 	FileMonitorPtr file_monitor_;
418 };
419 
420 
421 /// Creates the per buffer temporary directory
createBufferTmpDir()422 static FileName createBufferTmpDir()
423 {
424 	// FIXME This would be the ideal application for a TempDir class (like
425 	//       TempFile but for directories)
426 	string counter;
427 	{
428 		static int count;
429 		static Mutex mutex;
430 		Mutex::Locker locker(&mutex);
431 		counter = convert<string>(count++);
432 	}
433 	// We are in our own directory.  Why bother to mangle name?
434 	// In fact I wrote this code to circumvent a problematic behaviour
435 	// (bug?) of EMX mkstemp().
436 	FileName tmpfl(package().temp_dir().absFileName() + "/lyx_tmpbuf" +
437 		counter);
438 
439 	if (!tmpfl.createDirectory(0777)) {
440 		throw ExceptionMessage(WarningException, _("Disk Error: "), bformat(
441 			_("LyX could not create the temporary directory '%1$s' (Disk is full maybe?)"),
442 			from_utf8(tmpfl.absFileName())));
443 	}
444 	return tmpfl;
445 }
446 
447 
Impl(Buffer * owner,FileName const & file,bool readonly_,Buffer const * cloned_buffer)448 Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_,
449 	Buffer const * cloned_buffer)
450 	: owner_(owner), lyx_clean(true), bak_clean(true), unnamed(false),
451 	  internal_buffer(false), read_only(readonly_), filename(file),
452 	  file_fully_loaded(false), file_format(LYX_FORMAT), need_format_backup(false),
453 	  ignore_parent(false),  toc_backend(owner), macro_lock(false),
454 	  checksum_(0), wa_(0),  gui_(0), undo_(*owner), bibinfo_cache_valid_(false),
455 	  bibfile_cache_valid_(false), cite_labels_valid_(false), have_bibitems_(false),
456 	  preview_error_(false), inset(0), preview_loader_(0), cloned_buffer_(cloned_buffer),
457 	  clone_list_(0), doing_export(false),
458 	  tracked_changes_present_(0), externally_modified_(false), parent_buffer(0),
459 	  word_count_(0), char_count_(0), blank_count_(0)
460 {
461 	refreshFileMonitor();
462 	if (!cloned_buffer_) {
463 		temppath = createBufferTmpDir();
464 		lyxvc.setBuffer(owner_);
465 		if (use_gui)
466 			wa_ = new frontend::WorkAreaManager;
467 		return;
468 	}
469 	temppath = cloned_buffer_->d->temppath;
470 	file_fully_loaded = true;
471 	params = cloned_buffer_->d->params;
472 	bibfiles_cache_ = cloned_buffer_->d->bibfiles_cache_;
473 	bibinfo_ = cloned_buffer_->d->bibinfo_;
474 	bibinfo_cache_valid_ = cloned_buffer_->d->bibinfo_cache_valid_;
475 	bibfile_cache_valid_ = cloned_buffer_->d->bibfile_cache_valid_;
476 	bibfile_status_ = cloned_buffer_->d->bibfile_status_;
477 	cite_labels_valid_ = cloned_buffer_->d->cite_labels_valid_;
478 	have_bibitems_ = cloned_buffer_->d->have_bibitems_;
479 	unnamed = cloned_buffer_->d->unnamed;
480 	internal_buffer = cloned_buffer_->d->internal_buffer;
481 	layout_position = cloned_buffer_->d->layout_position;
482 	preview_file_ = cloned_buffer_->d->preview_file_;
483 	preview_format_ = cloned_buffer_->d->preview_format_;
484 	preview_error_ = cloned_buffer_->d->preview_error_;
485 	tracked_changes_present_ = cloned_buffer_->d->tracked_changes_present_;
486 }
487 
488 
Buffer(string const & file,bool readonly,Buffer const * cloned_buffer)489 Buffer::Buffer(string const & file, bool readonly, Buffer const * cloned_buffer)
490 	: d(new Impl(this, FileName(file), readonly, cloned_buffer))
491 {
492 	LYXERR(Debug::INFO, "Buffer::Buffer()");
493 	if (cloned_buffer) {
494 		d->inset = new InsetText(*cloned_buffer->d->inset);
495 		d->inset->setBuffer(*this);
496 		// FIXME: optimize this loop somewhat, maybe by creating a new
497 		// general recursive Inset::setId().
498 		DocIterator it = doc_iterator_begin(this);
499 		DocIterator cloned_it = doc_iterator_begin(cloned_buffer);
500 		for (; !it.atEnd(); it.forwardPar(), cloned_it.forwardPar())
501 			it.paragraph().setId(cloned_it.paragraph().id());
502 	} else
503 		d->inset = new InsetText(this);
504 	d->inset->getText(0)->setMacrocontextPosition(par_iterator_begin());
505 }
506 
507 
~Buffer()508 Buffer::~Buffer()
509 {
510 	LYXERR(Debug::INFO, "Buffer::~Buffer()");
511 	// here the buffer should take care that it is
512 	// saved properly, before it goes into the void.
513 
514 	// GuiView already destroyed
515 	d->gui_ = 0;
516 
517 	if (isInternal()) {
518 		// No need to do additional cleanups for internal buffer.
519 		delete d;
520 		return;
521 	}
522 
523 	if (isClone()) {
524 		// this is in case of recursive includes: we won't try to delete
525 		// ourselves as a child.
526 		d->clone_list_->erase(this);
527 		// loop over children
528 		Impl::BufferPositionMap::iterator it = d->children_positions.begin();
529 		Impl::BufferPositionMap::iterator end = d->children_positions.end();
530 		for (; it != end; ++it) {
531 			Buffer * child = const_cast<Buffer *>(it->first);
532 				if (d->clone_list_->erase(child))
533 					delete child;
534 		}
535 		// if we're the master buffer, then we should get rid of the list
536 		// of clones
537 		if (!parent()) {
538 			// If this is not empty, we have leaked something. Worse, one of the
539 			// children still has a reference to this list. But we will try to
540 			// continue, rather than shut down.
541 			LATTEST(d->clone_list_->empty());
542 			list<CloneList *>::iterator it =
543 				find(cloned_buffers.begin(), cloned_buffers.end(), d->clone_list_);
544 			if (it == cloned_buffers.end()) {
545 				// We will leak in this case, but it is safe to continue.
546 				LATTEST(false);
547 			} else
548 				cloned_buffers.erase(it);
549 			delete d->clone_list_;
550 		}
551 		// FIXME Do we really need to do this right before we delete d?
552 		// clear references to children in macro tables
553 		d->children_positions.clear();
554 		d->position_to_children.clear();
555 	} else {
556 		// loop over children
557 		Impl::BufferPositionMap::iterator it = d->children_positions.begin();
558 		Impl::BufferPositionMap::iterator end = d->children_positions.end();
559 		for (; it != end; ++it) {
560 			Buffer * child = const_cast<Buffer *>(it->first);
561 			if (theBufferList().isLoaded(child)) {
562 				if (theBufferList().isOthersChild(this, child))
563 					child->setParent(0);
564 				else
565 					theBufferList().release(child);
566 			}
567 		}
568 
569 		if (!isClean()) {
570 			docstring msg = _("LyX attempted to close a document that had unsaved changes!\n");
571 			try {
572 				msg += emergencyWrite();
573 			} catch (...) {
574 				msg += "  " + _("Save failed! Document is lost.");
575 			}
576 			Alert::warning(_("Attempting to close changed document!"), msg);
577 		}
578 
579 		// FIXME Do we really need to do this right before we delete d?
580 		// clear references to children in macro tables
581 		d->children_positions.clear();
582 		d->position_to_children.clear();
583 
584 		if (!d->temppath.destroyDirectory()) {
585 			LYXERR0(bformat(_("Could not remove the temporary directory %1$s"),
586 				from_utf8(d->temppath.absFileName())));
587 		}
588 		removePreviews();
589 	}
590 
591 	delete d;
592 }
593 
594 
cloneWithChildren() const595 Buffer * Buffer::cloneWithChildren() const
596 {
597 	BufferMap bufmap;
598 	cloned_buffers.push_back(new CloneList);
599 	CloneList * clones = cloned_buffers.back();
600 
601 	cloneWithChildren(bufmap, clones);
602 
603 	// make sure we got cloned
604 	BufferMap::const_iterator bit = bufmap.find(this);
605 	LASSERT(bit != bufmap.end(), return 0);
606 	Buffer * cloned_buffer = bit->second;
607 
608 	return cloned_buffer;
609 }
610 
611 
cloneWithChildren(BufferMap & bufmap,CloneList * clones) const612 void Buffer::cloneWithChildren(BufferMap & bufmap, CloneList * clones) const
613 {
614 	// have we already been cloned?
615 	if (bufmap.find(this) != bufmap.end())
616 		return;
617 
618 	Buffer * buffer_clone = new Buffer(fileName().absFileName(), false, this);
619 
620 	// The clone needs its own DocumentClass, since running updateBuffer() will
621 	// modify it, and we would otherwise be sharing it with the original Buffer.
622 	buffer_clone->params().makeDocumentClass(true);
623 	ErrorList el;
624 	cap::switchBetweenClasses(
625 			params().documentClassPtr(), buffer_clone->params().documentClassPtr(),
626 			static_cast<InsetText &>(buffer_clone->inset()), el);
627 
628 	bufmap[this] = buffer_clone;
629 	clones->insert(buffer_clone);
630 	buffer_clone->d->clone_list_ = clones;
631 	buffer_clone->d->macro_lock = true;
632 	buffer_clone->d->children_positions.clear();
633 
634 	// FIXME (Abdel 09/01/2010): this is too complicated. The whole children_positions and
635 	// math macro caches need to be rethought and simplified.
636 	// I am not sure wether we should handle Buffer cloning here or in BufferList.
637 	// Right now BufferList knows nothing about buffer clones.
638 	Impl::PositionScopeBufferMap::iterator it = d->position_to_children.begin();
639 	Impl::PositionScopeBufferMap::iterator end = d->position_to_children.end();
640 	for (; it != end; ++it) {
641 		DocIterator dit = it->first.clone(buffer_clone);
642 		dit.setBuffer(buffer_clone);
643 		Buffer * child = const_cast<Buffer *>(it->second.buffer);
644 
645 		child->cloneWithChildren(bufmap, clones);
646 		BufferMap::iterator const bit = bufmap.find(child);
647 		LASSERT(bit != bufmap.end(), continue);
648 		Buffer * child_clone = bit->second;
649 
650 		Inset * inset = dit.nextInset();
651 		LASSERT(inset && inset->lyxCode() == INCLUDE_CODE, continue);
652 		InsetInclude * inset_inc = static_cast<InsetInclude *>(inset);
653 		inset_inc->setChildBuffer(child_clone);
654 		child_clone->d->setParent(buffer_clone);
655 		// FIXME Do we need to do this now, or can we wait until we run updateMacros()?
656 		buffer_clone->setChild(dit, child_clone);
657 	}
658 	buffer_clone->d->macro_lock = false;
659 	return;
660 }
661 
662 
cloneBufferOnly() const663 Buffer * Buffer::cloneBufferOnly() const {
664 	cloned_buffers.push_back(new CloneList);
665 	CloneList * clones = cloned_buffers.back();
666 	Buffer * buffer_clone = new Buffer(fileName().absFileName(), false, this);
667 
668 	// The clone needs its own DocumentClass, since running updateBuffer() will
669 	// modify it, and we would otherwise be sharing it with the original Buffer.
670 	buffer_clone->params().makeDocumentClass(true);
671 	ErrorList el;
672 	cap::switchBetweenClasses(
673 			params().documentClassPtr(), buffer_clone->params().documentClassPtr(),
674 			static_cast<InsetText &>(buffer_clone->inset()), el);
675 
676 	clones->insert(buffer_clone);
677 	buffer_clone->d->clone_list_ = clones;
678 
679 	// we won't be cloning the children
680 	buffer_clone->d->children_positions.clear();
681 	return buffer_clone;
682 }
683 
684 
isClone() const685 bool Buffer::isClone() const
686 {
687 	return d->cloned_buffer_;
688 }
689 
690 
changed(bool update_metrics) const691 void Buffer::changed(bool update_metrics) const
692 {
693 	if (d->wa_)
694 		d->wa_->redrawAll(update_metrics);
695 }
696 
697 
workAreaManager() const698 frontend::WorkAreaManager & Buffer::workAreaManager() const
699 {
700 	LBUFERR(d->wa_);
701 	return *d->wa_;
702 }
703 
704 
text() const705 Text & Buffer::text() const
706 {
707 	return d->inset->text();
708 }
709 
710 
inset() const711 Inset & Buffer::inset() const
712 {
713 	return *d->inset;
714 }
715 
716 
params()717 BufferParams & Buffer::params()
718 {
719 	return d->params;
720 }
721 
722 
params() const723 BufferParams const & Buffer::params() const
724 {
725 	return d->params;
726 }
727 
728 
masterParams() const729 BufferParams const & Buffer::masterParams() const
730 {
731 	if (masterBuffer() == this)
732 		return params();
733 
734 	BufferParams & mparams = const_cast<Buffer *>(masterBuffer())->params();
735 	// Copy child authors to the params. We need those pointers.
736 	AuthorList const & child_authors = params().authors();
737 	AuthorList::Authors::const_iterator it = child_authors.begin();
738 	for (; it != child_authors.end(); ++it)
739 		mparams.authors().record(*it);
740 	return mparams;
741 }
742 
743 
fontScalingFactor() const744 double Buffer::fontScalingFactor() const
745 {
746 	return isExporting() ? 75.0 * params().html_math_img_scale
747 		: 0.01 * lyxrc.dpi * lyxrc.currentZoom * lyxrc.preview_scale_factor * params().display_pixel_ratio;
748 }
749 
750 
paragraphs()751 ParagraphList & Buffer::paragraphs()
752 {
753 	return text().paragraphs();
754 }
755 
756 
paragraphs() const757 ParagraphList const & Buffer::paragraphs() const
758 {
759 	return text().paragraphs();
760 }
761 
762 
lyxvc()763 LyXVC & Buffer::lyxvc()
764 {
765 	return d->lyxvc;
766 }
767 
768 
lyxvc() const769 LyXVC const & Buffer::lyxvc() const
770 {
771 	return d->lyxvc;
772 }
773 
774 
temppath() const775 string const Buffer::temppath() const
776 {
777 	return d->temppath.absFileName();
778 }
779 
780 
texrow()781 TexRow & Buffer::texrow()
782 {
783 	return d->texrow;
784 }
785 
786 
texrow() const787 TexRow const & Buffer::texrow() const
788 {
789 	return d->texrow;
790 }
791 
792 
tocBackend() const793 TocBackend & Buffer::tocBackend() const
794 {
795 	return d->toc_backend;
796 }
797 
798 
undo()799 Undo & Buffer::undo()
800 {
801 	return d->undo_;
802 }
803 
804 
setChild(DocIterator const & dit,Buffer * child)805 void Buffer::setChild(DocIterator const & dit, Buffer * child)
806 {
807 	d->children_positions[child] = dit;
808 }
809 
810 
latexName(bool const no_path) const811 string Buffer::latexName(bool const no_path) const
812 {
813 	FileName latex_name =
814 		makeLatexName(d->exportFileName());
815 	return no_path ? latex_name.onlyFileName()
816 		: latex_name.absFileName();
817 }
818 
819 
exportFileName() const820 FileName Buffer::Impl::exportFileName() const
821 {
822 	docstring const branch_suffix =
823 		params.branchlist().getFileNameSuffix();
824 	if (branch_suffix.empty())
825 		return filename;
826 
827 	string const name = addExtension(filename.onlyFileNameWithoutExt()
828 			+ to_utf8(branch_suffix), filename.extension());
829 	FileName res(filename.onlyPath().absFileName() + "/" + name);
830 
831 	return res;
832 }
833 
834 
logName(LogType * type) const835 string Buffer::logName(LogType * type) const
836 {
837 	string const filename = latexName(false);
838 
839 	if (filename.empty()) {
840 		if (type)
841 			*type = latexlog;
842 		return string();
843 	}
844 
845 	string const path = temppath();
846 
847 	FileName const fname(addName(temppath(),
848 				     onlyFileName(changeExtension(filename,
849 								  ".log"))));
850 
851 	// FIXME: how do we know this is the name of the build log?
852 	FileName const bname(
853 		addName(path, onlyFileName(
854 			changeExtension(filename,
855 					theFormats().extension(params().bufferFormat()) + ".out"))));
856 
857 	// Also consider the master buffer log file
858 	FileName masterfname = fname;
859 	LogType mtype = latexlog;
860 	if (masterBuffer() != this) {
861 		string const mlogfile = masterBuffer()->logName(&mtype);
862 		masterfname = FileName(mlogfile);
863 	}
864 
865 	// If no Latex log or Build log is newer, show Build log
866 	if (bname.exists() &&
867 	    ((!fname.exists() && !masterfname.exists())
868 	     || (fname.lastModified() < bname.lastModified()
869 	         && masterfname.lastModified() < bname.lastModified()))) {
870 		LYXERR(Debug::FILES, "Log name calculated as: " << bname);
871 		if (type)
872 			*type = buildlog;
873 		return bname.absFileName();
874 	// If we have a newer master file log or only a master log, show this
875 	} else if (fname != masterfname
876 		   && (!fname.exists() && (masterfname.exists()
877 		   || fname.lastModified() < masterfname.lastModified()))) {
878 		LYXERR(Debug::FILES, "Log name calculated as: " << masterfname);
879 		if (type)
880 			*type = mtype;
881 		return masterfname.absFileName();
882 	}
883 	LYXERR(Debug::FILES, "Log name calculated as: " << fname);
884 	if (type)
885 			*type = latexlog;
886 	return fname.absFileName();
887 }
888 
889 
setReadonly(bool const flag)890 void Buffer::setReadonly(bool const flag)
891 {
892 	if (d->read_only != flag) {
893 		d->read_only = flag;
894 		changed(false);
895 	}
896 }
897 
898 
setFileName(FileName const & fname)899 void Buffer::setFileName(FileName const & fname)
900 {
901 	bool const changed = fname != d->filename;
902 	d->filename = fname;
903 	d->refreshFileMonitor();
904 	if (changed)
905 		lyxvc().file_found_hook(fname);
906 	setReadonly(d->filename.isReadOnly());
907 	saveCheckSum();
908 	updateTitles();
909 }
910 
911 
readHeader(Lexer & lex)912 int Buffer::readHeader(Lexer & lex)
913 {
914 	int unknown_tokens = 0;
915 	int line = -1;
916 	int begin_header_line = -1;
917 
918 	// Initialize parameters that may be/go lacking in header:
919 	params().branchlist().clear();
920 	params().preamble.erase();
921 	params().options.erase();
922 	params().master.erase();
923 	params().float_placement.erase();
924 	params().paperwidth.erase();
925 	params().paperheight.erase();
926 	params().leftmargin.erase();
927 	params().rightmargin.erase();
928 	params().topmargin.erase();
929 	params().bottommargin.erase();
930 	params().headheight.erase();
931 	params().headsep.erase();
932 	params().footskip.erase();
933 	params().columnsep.erase();
934 	params().fonts_cjk.erase();
935 	params().listings_params.clear();
936 	params().clearLayoutModules();
937 	params().clearRemovedModules();
938 	params().clearIncludedChildren();
939 	params().pdfoptions().clear();
940 	params().indiceslist().clear();
941 	params().backgroundcolor = lyx::rgbFromHexName("#ffffff");
942 	params().isbackgroundcolor = false;
943 	params().fontcolor = RGBColor(0, 0, 0);
944 	params().isfontcolor = false;
945 	params().notefontcolor = RGBColor(0xCC, 0xCC, 0xCC);
946 	params().boxbgcolor = RGBColor(0xFF, 0, 0);
947 	params().html_latex_start.clear();
948 	params().html_latex_end.clear();
949 	params().html_math_img_scale = 1.0;
950 	params().output_sync_macro.erase();
951 	params().setLocalLayout(docstring(), false);
952 	params().setLocalLayout(docstring(), true);
953 	params().biblio_opts.erase();
954 	params().biblatex_bibstyle.erase();
955 	params().biblatex_citestyle.erase();
956 	params().multibib.erase();
957 
958 	for (int i = 0; i < 4; ++i) {
959 		params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
960 		params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
961 	}
962 
963 	ErrorList & errorList = d->errorLists["Parse"];
964 
965 	while (lex.isOK()) {
966 		string token;
967 		lex >> token;
968 
969 		if (token.empty())
970 			continue;
971 
972 		if (token == "\\end_header")
973 			break;
974 
975 		++line;
976 		if (token == "\\begin_header") {
977 			begin_header_line = line;
978 			continue;
979 		}
980 
981 		LYXERR(Debug::PARSER, "Handling document header token: `"
982 				      << token << '\'');
983 
984 		string const result =
985 			params().readToken(lex, token, d->filename.onlyPath());
986 		if (!result.empty()) {
987 			if (token == "\\textclass") {
988 				d->layout_position = result;
989 			} else {
990 				++unknown_tokens;
991 				docstring const s = bformat(_("Unknown token: "
992 									"%1$s %2$s\n"),
993 							 from_utf8(token),
994 							 lex.getDocString());
995 				errorList.push_back(ErrorItem(_("Document header error"), s));
996 			}
997 		}
998 	}
999 	if (begin_header_line) {
1000 		docstring const s = _("\\begin_header is missing");
1001 		errorList.push_back(ErrorItem(_("Document header error"), s));
1002 	}
1003 
1004 	params().shell_escape = theSession().shellescapeFiles().find(absFileName());
1005 
1006 	params().makeDocumentClass();
1007 
1008 	return unknown_tokens;
1009 }
1010 
1011 
1012 // Uwe C. Schroeder
1013 // changed to be public and have one parameter
1014 // Returns true if "\end_document" is not read (Asger)
readDocument(Lexer & lex)1015 bool Buffer::readDocument(Lexer & lex)
1016 {
1017 	ErrorList & errorList = d->errorLists["Parse"];
1018 	errorList.clear();
1019 
1020 	// remove dummy empty par
1021 	paragraphs().clear();
1022 
1023 	if (!lex.checkFor("\\begin_document")) {
1024 		docstring const s = _("\\begin_document is missing");
1025 		errorList.push_back(ErrorItem(_("Document header error"), s));
1026 	}
1027 
1028 	readHeader(lex);
1029 
1030 	if (params().output_changes) {
1031 		bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
1032 		bool xcolorulem = LaTeXFeatures::isAvailable("ulem") &&
1033 				  LaTeXFeatures::isAvailable("xcolor");
1034 
1035 		if (!dvipost && !xcolorulem) {
1036 			Alert::warning(_("Changes not shown in LaTeX output"),
1037 				       _("Changes will not be highlighted in LaTeX output, "
1038 					 "because neither dvipost nor xcolor/ulem are installed.\n"
1039 					 "Please install these packages or redefine "
1040 					 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
1041 		} else if (!xcolorulem) {
1042 			Alert::warning(_("Changes not shown in LaTeX output"),
1043 				       _("Changes will not be highlighted in LaTeX output "
1044 					 "when using pdflatex, because xcolor and ulem are not installed.\n"
1045 					 "Please install both packages or redefine "
1046 					 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
1047 		}
1048 	}
1049 
1050 	if (!parent() && !params().master.empty()) {
1051 		FileName const master_file = makeAbsPath(params().master,
1052 			   onlyPath(absFileName()));
1053 		if (isLyXFileName(master_file.absFileName())) {
1054 			Buffer * master =
1055 				checkAndLoadLyXFile(master_file, true);
1056 			if (master) {
1057 				// necessary e.g. after a reload
1058 				// to re-register the child (bug 5873)
1059 				// FIXME: clean up updateMacros (here, only
1060 				// child registering is needed).
1061 				master->updateMacros();
1062 				// set master as master buffer, but only
1063 				// if we are a real child
1064 				if (master->isChild(this))
1065 					setParent(master);
1066 				// if the master is not fully loaded
1067 				// it is probably just loading this
1068 				// child. No warning needed then.
1069 				else if (master->isFullyLoaded())
1070 					LYXERR0("The master '"
1071 						<< params().master
1072 						<< "' assigned to this document ("
1073 						<< absFileName()
1074 						<< ") does not include "
1075 						"this document. Ignoring the master assignment.");
1076 				// If the master has just been created, un-hide it (#11162)
1077 				if (!master->fileName().exists())
1078 					lyx::dispatch(FuncRequest(LFUN_BUFFER_SWITCH,
1079 								  master->absFileName()));
1080 			}
1081 		}
1082 	}
1083 
1084 	// assure we have a default index
1085 	params().indiceslist().addDefault(B_("Index"));
1086 
1087 	// read main text
1088 	if (FileName::isAbsolute(params().origin))
1089 		d->old_position = params().origin;
1090 	else
1091 		d->old_position = filePath();
1092 	bool const res = text().read(lex, errorList, d->inset);
1093 	d->old_position.clear();
1094 
1095 	// inform parent buffer about local macros
1096 	if (parent()) {
1097 		Buffer const * pbuf = parent();
1098 		UserMacroSet::const_iterator cit = usermacros.begin();
1099 		UserMacroSet::const_iterator end = usermacros.end();
1100 		for (; cit != end; ++cit)
1101 			pbuf->usermacros.insert(*cit);
1102 	}
1103 	usermacros.clear();
1104 	updateMacros();
1105 	updateMacroInstances(InternalUpdate);
1106 	return res;
1107 }
1108 
1109 
importString(string const & format,docstring const & contents,ErrorList & errorList)1110 bool Buffer::importString(string const & format, docstring const & contents, ErrorList & errorList)
1111 {
1112 	Format const * fmt = theFormats().getFormat(format);
1113 	if (!fmt)
1114 		return false;
1115 	// It is important to use the correct extension here, since some
1116 	// converters create a wrong output file otherwise (e.g. html2latex)
1117 	FileName const name = tempFileName("Buffer_importStringXXXXXX." + fmt->extension());
1118 	ofdocstream os(name.toFilesystemEncoding().c_str());
1119 	// Do not convert os implicitly to bool, since that is forbidden in C++11.
1120 	bool const success = !(os << contents).fail();
1121 	os.close();
1122 
1123 	bool converted = false;
1124 	if (success) {
1125 		params().compressed = false;
1126 
1127 		// remove dummy empty par
1128 		paragraphs().clear();
1129 
1130 		converted = importFile(format, name, errorList);
1131 	}
1132 
1133 	removeTempFile(name);
1134 	return converted;
1135 }
1136 
1137 
importFile(string const & format,FileName const & name,ErrorList & errorList)1138 bool Buffer::importFile(string const & format, FileName const & name, ErrorList & errorList)
1139 {
1140 	if (!theConverters().isReachable(format, "lyx"))
1141 		return false;
1142 
1143 	FileName const lyx = tempFileName("Buffer_importFileXXXXXX.lyx");
1144 	if (theConverters().convert(0, name, lyx, name, format, "lyx", errorList)) {
1145 		bool const success = readFile(lyx) == ReadSuccess;
1146 		removeTempFile(lyx);
1147 		return success;
1148 	}
1149 
1150 	return false;
1151 }
1152 
1153 
readString(string const & s)1154 bool Buffer::readString(string const & s)
1155 {
1156 	params().compressed = false;
1157 
1158 	Lexer lex;
1159 	istringstream is(s);
1160 	lex.setStream(is);
1161 	TempFile tempfile("Buffer_readStringXXXXXX.lyx");
1162 	FileName const fn = tempfile.name();
1163 
1164 	int file_format;
1165 	bool success = parseLyXFormat(lex, fn, file_format) == ReadSuccess;
1166 
1167 	if (success && file_format != LYX_FORMAT) {
1168 		// We need to call lyx2lyx, so write the input to a file
1169 		ofstream os(fn.toFilesystemEncoding().c_str());
1170 		os << s;
1171 		os.close();
1172 		// lyxvc in readFile
1173 		if (readFile(fn) != ReadSuccess)
1174 			success = false;
1175 	}
1176 	else if (success)
1177 		if (readDocument(lex))
1178 			success = false;
1179 	return success;
1180 }
1181 
1182 
readFile(FileName const & fn)1183 Buffer::ReadStatus Buffer::readFile(FileName const & fn)
1184 {
1185 	FileName fname(fn);
1186 	Lexer lex;
1187 	if (!lex.setFile(fname)) {
1188 		Alert::error(_("File Not Found"),
1189 			bformat(_("Unable to open file `%1$s'."),
1190 			        from_utf8(fn.absFileName())));
1191 		return ReadFileNotFound;
1192 	}
1193 
1194 	int file_format;
1195 	ReadStatus const ret_plf = parseLyXFormat(lex, fn, file_format);
1196 	if (ret_plf != ReadSuccess)
1197 		return ret_plf;
1198 
1199 	if (file_format != LYX_FORMAT) {
1200 		FileName tmpFile;
1201 		ReadStatus ret_clf = convertLyXFormat(fn, tmpFile, file_format);
1202 		if (ret_clf != ReadSuccess)
1203 			return ret_clf;
1204 		ret_clf = readFile(tmpFile);
1205 		if (ret_clf == ReadSuccess) {
1206 			d->file_format = file_format;
1207 			d->need_format_backup = true;
1208 		}
1209 		return ret_clf;
1210 	}
1211 
1212 	// FIXME: InsetInfo needs to know whether the file is under VCS
1213 	// during the parse process, so this has to be done before.
1214 	lyxvc().file_found_hook(d->filename);
1215 
1216 	if (readDocument(lex)) {
1217 		Alert::error(_("Document format failure"),
1218 			bformat(_("%1$s ended unexpectedly, which means"
1219 				" that it is probably corrupted."),
1220 					from_utf8(fn.absFileName())));
1221 		return ReadDocumentFailure;
1222 	}
1223 
1224 	d->file_fully_loaded = true;
1225 	d->read_only = !d->filename.isWritable();
1226 	params().compressed = theFormats().isZippedFile(d->filename);
1227 	saveCheckSum();
1228 	return ReadSuccess;
1229 }
1230 
1231 
isFullyLoaded() const1232 bool Buffer::isFullyLoaded() const
1233 {
1234 	return d->file_fully_loaded;
1235 }
1236 
1237 
setFullyLoaded(bool value)1238 void Buffer::setFullyLoaded(bool value)
1239 {
1240 	d->file_fully_loaded = value;
1241 }
1242 
1243 
lastPreviewError() const1244 bool Buffer::lastPreviewError() const
1245 {
1246 	return d->preview_error_;
1247 }
1248 
1249 
loader() const1250 PreviewLoader * Buffer::loader() const
1251 {
1252 	if (!isExporting() && lyxrc.preview == LyXRC::PREVIEW_OFF)
1253 		return 0;
1254 	if (!d->preview_loader_)
1255 		d->preview_loader_ = new PreviewLoader(*this);
1256 	return d->preview_loader_;
1257 }
1258 
1259 
removePreviews() const1260 void Buffer::removePreviews() const
1261 {
1262 	delete d->preview_loader_;
1263 	d->preview_loader_ = 0;
1264 }
1265 
1266 
updatePreviews() const1267 void Buffer::updatePreviews() const
1268 {
1269 	PreviewLoader * ploader = loader();
1270 	if (!ploader)
1271 		return;
1272 
1273 	InsetIterator it = inset_iterator_begin(*d->inset);
1274 	InsetIterator const end = inset_iterator_end(*d->inset);
1275 	for (; it != end; ++it)
1276 		it->addPreview(it, *ploader);
1277 
1278 	ploader->startLoading();
1279 }
1280 
1281 
parseLyXFormat(Lexer & lex,FileName const & fn,int & file_format) const1282 Buffer::ReadStatus Buffer::parseLyXFormat(Lexer & lex,
1283 	FileName const & fn, int & file_format) const
1284 {
1285 	if(!lex.checkFor("\\lyxformat")) {
1286 		Alert::error(_("Document format failure"),
1287 			bformat(_("%1$s is not a readable LyX document."),
1288 				from_utf8(fn.absFileName())));
1289 		return ReadNoLyXFormat;
1290 	}
1291 
1292 	string tmp_format;
1293 	lex >> tmp_format;
1294 
1295 	// LyX formats 217 and earlier were written as 2.17. This corresponds
1296 	// to files from LyX versions < 1.1.6.3. We just remove the dot in
1297 	// these cases. See also: www.lyx.org/trac/changeset/1313.
1298 	size_t dot = tmp_format.find_first_of(".,");
1299 	if (dot != string::npos)
1300 		tmp_format.erase(dot, 1);
1301 
1302 	file_format = convert<int>(tmp_format);
1303 	return ReadSuccess;
1304 }
1305 
1306 
convertLyXFormat(FileName const & fn,FileName & tmpfile,int from_format)1307 Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn,
1308 	FileName & tmpfile, int from_format)
1309 {
1310 	TempFile tempfile("Buffer_convertLyXFormatXXXXXX.lyx");
1311 	tempfile.setAutoRemove(false);
1312 	tmpfile = tempfile.name();
1313 	if(tmpfile.empty()) {
1314 		Alert::error(_("Conversion failed"),
1315 			bformat(_("%1$s is from a different"
1316 				" version of LyX, but a temporary"
1317 				" file for converting it could"
1318 				" not be created."),
1319 				from_utf8(fn.absFileName())));
1320 		return LyX2LyXNoTempFile;
1321 	}
1322 
1323 	FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
1324 	if (lyx2lyx.empty()) {
1325 		Alert::error(_("Conversion script not found"),
1326 		     bformat(_("%1$s is from a different"
1327 			       " version of LyX, but the"
1328 			       " conversion script lyx2lyx"
1329 			       " could not be found."),
1330 			       from_utf8(fn.absFileName())));
1331 		return LyX2LyXNotFound;
1332 	}
1333 
1334 	// Run lyx2lyx:
1335 	//   $python$ "$lyx2lyx$" -t $LYX_FORMAT$ -o "$tempfile$" "$filetoread$"
1336 	ostringstream command;
1337 	command << os::python()
1338 		<< ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
1339 		<< " -t " << convert<string>(LYX_FORMAT)
1340 		<< " -o " << quoteName(tmpfile.toSafeFilesystemEncoding())
1341 		<< ' ' << quoteName(fn.toSafeFilesystemEncoding());
1342 	string const command_str = command.str();
1343 
1344 	LYXERR(Debug::INFO, "Running '" << command_str << '\'');
1345 
1346 	cmd_ret const ret = runCommand(command_str);
1347 	if (ret.first != 0) {
1348 		if (from_format < LYX_FORMAT) {
1349 			Alert::error(_("Conversion script failed"),
1350 				bformat(_("%1$s is from an older version"
1351 					" of LyX and the lyx2lyx script"
1352 					" failed to convert it."),
1353 					from_utf8(fn.absFileName())));
1354 			return LyX2LyXOlderFormat;
1355 		} else {
1356 			Alert::error(_("Conversion script failed"),
1357 				bformat(_("%1$s is from a newer version"
1358 					" of LyX and the lyx2lyx script"
1359 					" failed to convert it."),
1360 					from_utf8(fn.absFileName())));
1361 			return LyX2LyXNewerFormat;
1362 		}
1363 	}
1364 	return ReadSuccess;
1365 }
1366 
1367 
getBackupName() const1368 FileName Buffer::getBackupName() const {
1369 	FileName const & fn = fileName();
1370 	string const fname = fn.onlyFileNameWithoutExt();
1371 	string const fext  = fn.extension() + "~";
1372 	string const fpath = lyxrc.backupdir_path.empty() ?
1373 		fn.onlyPath().absFileName() :
1374 		lyxrc.backupdir_path;
1375 	string const fform = convert<string>(d->file_format);
1376 	string const backname = fname + "-lyxformat-" + fform;
1377 	FileName backup(addName(fpath, addExtension(backname, fext)));
1378 
1379 	// limit recursion, just in case
1380 	int v = 1;
1381 	unsigned long orig_checksum = 0;
1382 	while (backup.exists() && v < 100) {
1383 		if (orig_checksum == 0)
1384 			orig_checksum = fn.checksum();
1385 		unsigned long new_checksum = backup.checksum();
1386 		if (orig_checksum == new_checksum) {
1387 			LYXERR(Debug::FILES, "Not backing up " << fn <<
1388 			       "since " << backup << "has the same checksum.");
1389 			// a bit of a hack, but we have to check this anyway
1390 			// below, and setting this is simpler than introducing
1391 			// a special boolean for this purpose.
1392 			v = 1000;
1393 			break;
1394 		}
1395 		string const newbackname = backname + "-" + convert<string>(v);
1396 		backup.set(addName(fpath, addExtension(newbackname, fext)));
1397 		v++;
1398 	}
1399 	return v < 100 ? backup : FileName();
1400 }
1401 
1402 
1403 // Should probably be moved to somewhere else: BufferView? GuiView?
save() const1404 bool Buffer::save() const
1405 {
1406 	docstring const file = makeDisplayPath(absFileName(), 20);
1407 	d->filename.refresh();
1408 
1409 	// check the read-only status before moving the file as a backup
1410 	if (d->filename.exists()) {
1411 		bool const read_only = !d->filename.isWritable();
1412 		if (read_only) {
1413 			Alert::warning(_("File is read-only"),
1414 				bformat(_("The file %1$s cannot be written because it "
1415 				"is marked as read-only."), file));
1416 			return false;
1417 		}
1418 	}
1419 
1420 	// ask if the disk file has been externally modified (use checksum method)
1421 	if (fileName().exists() && isChecksumModified()) {
1422 		docstring text =
1423 			bformat(_("Document %1$s has been externally modified. "
1424 				"Are you sure you want to overwrite this file?"), file);
1425 		int const ret = Alert::prompt(_("Overwrite modified file?"),
1426 			text, 1, 1, _("&Overwrite"), _("&Cancel"));
1427 		if (ret == 1)
1428 			return false;
1429 	}
1430 
1431 	// We don't need autosaves in the immediate future. (Asger)
1432 	resetAutosaveTimers();
1433 
1434 	// if the file does not yet exist, none of the backup activity
1435 	// that follows is necessary
1436 	if (!fileName().exists()) {
1437 		if (!writeFile(fileName()))
1438 			return false;
1439 		markClean();
1440 		return true;
1441 	}
1442 
1443 	// we first write the file to a new name, then move it to its
1444 	// proper location once that has been done successfully. that
1445 	// way we preserve the original file if something goes wrong.
1446 	string const justname = fileName().onlyFileNameWithoutExt();
1447 	auto tempfile = make_unique<TempFile>(fileName().onlyPath(),
1448 	                                      justname + "-XXXXXX.lyx");
1449 	bool const symlink = fileName().isSymLink();
1450 	if (!symlink)
1451 		tempfile->setAutoRemove(false);
1452 
1453 	FileName savefile(tempfile->name());
1454 	LYXERR(Debug::FILES, "Saving to " << savefile.absFileName());
1455 	if (!savefile.clonePermissions(fileName()))
1456 		LYXERR0("Failed to clone the permission from " << fileName().absFileName() << " to " << savefile.absFileName());
1457 
1458 	if (!writeFile(savefile))
1459 		return false;
1460 
1461 	// we will set this to false if we fail
1462 	bool made_backup = true;
1463 
1464 	FileName backupName;
1465 	bool const needBackup = lyxrc.make_backup || d->need_format_backup;
1466 	if (needBackup) {
1467 		if (d->need_format_backup)
1468 			backupName = getBackupName();
1469 
1470 		// If we for some reason failed to find a backup name in case of
1471 		// a format change, this will still set one. It's the best we can
1472 		// do in this case.
1473 		if (backupName.empty()) {
1474 			backupName.set(fileName().absFileName() + "~");
1475 			if (!lyxrc.backupdir_path.empty()) {
1476 				string const mangledName =
1477 					subst(subst(backupName.absFileName(), '/', '!'), ':', '!');
1478 				backupName.set(addName(lyxrc.backupdir_path, mangledName));
1479 			}
1480 		}
1481 
1482 		LYXERR(Debug::FILES, "Backing up original file to " <<
1483 				backupName.absFileName());
1484 		// Except file is symlink do not copy because of #6587.
1485 		// Hard links have bad luck.
1486 		made_backup = symlink ?
1487 			fileName().copyTo(backupName):
1488 			fileName().moveTo(backupName);
1489 
1490 		if (!made_backup) {
1491 			Alert::error(_("Backup failure"),
1492 				     bformat(_("Cannot create backup file %1$s.\n"
1493 					       "Please check whether the directory exists and is writable."),
1494 					     from_utf8(backupName.absFileName())));
1495 			//LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
1496 		} else if (d->need_format_backup) {
1497 			// the original file has been backed up successfully, so we
1498 			// will not need to do that again
1499 			d->need_format_backup = false;
1500 		}
1501 	}
1502 
1503 	// Destroy tempfile since it keeps the file locked on windows (bug 9234)
1504 	// Only do this if tempfile is not in autoremove mode
1505 	if (!symlink)
1506 		tempfile.reset();
1507 	// If we have no symlink, we can simply rename the temp file.
1508 	// Otherwise, we need to copy it so the symlink stays intact.
1509 	if (made_backup && symlink ? savefile.copyTo(fileName(), true) :
1510 		                           savefile.moveTo(fileName()))
1511 	{
1512 		// saveCheckSum() was already called by writeFile(), but the
1513 		// time stamp is invalidated by copying/moving
1514 		saveCheckSum();
1515 		markClean();
1516 		if (d->file_format != LYX_FORMAT)
1517 			// the file associated with this buffer is now in the current format
1518 			d->file_format = LYX_FORMAT;
1519 		return true;
1520 	}
1521 	// else we saved the file, but failed to move it to the right location.
1522 
1523 	if (needBackup && made_backup && !symlink) {
1524 		// the original file was moved to some new location, so it will look
1525 		// to the user as if it was deleted. (see bug #9234.) we could try
1526 		// to restore it, but that would basically mean trying to do again
1527 		// what we just failed to do. better to leave things as they are.
1528 		Alert::error(_("Write failure"),
1529 		             bformat(_("The file has successfully been saved as:\n  %1$s.\n"
1530 		                       "But LyX could not move it to:\n  %2$s.\n"
1531 		                       "Your original file has been backed up to:\n  %3$s"),
1532 		                     from_utf8(savefile.absFileName()),
1533 		                     from_utf8(fileName().absFileName()),
1534 		                     from_utf8(backupName.absFileName())));
1535 	} else {
1536 		// either we did not try to make a backup, or else we tried and failed,
1537 		// or else the original file was a symlink, in which case it was copied,
1538 		// not moved. so the original file is intact.
1539 		Alert::error(_("Write failure"),
1540 			     bformat(_("Cannot move saved file to:\n  %1$s.\n"
1541 				       "But the file has successfully been saved as:\n  %2$s."),
1542 				     from_utf8(fileName().absFileName()),
1543 		         from_utf8(savefile.absFileName())));
1544 	}
1545 	return false;
1546 }
1547 
1548 
writeFile(FileName const & fname) const1549 bool Buffer::writeFile(FileName const & fname) const
1550 {
1551 	if (d->read_only && fname == d->filename)
1552 		return false;
1553 
1554 	bool retval = false;
1555 
1556 	docstring const str = bformat(_("Saving document %1$s..."),
1557 		makeDisplayPath(fname.absFileName()));
1558 	message(str);
1559 
1560 	string const encoded_fname = fname.toSafeFilesystemEncoding(os::CREATE);
1561 
1562 	if (params().compressed) {
1563 		gz::ogzstream ofs(encoded_fname.c_str(), ios::out|ios::trunc);
1564 		retval = ofs && write(ofs);
1565 	} else {
1566 		ofstream ofs(encoded_fname.c_str(), ios::out|ios::trunc);
1567 		retval = ofs && write(ofs);
1568 	}
1569 
1570 	if (!retval) {
1571 		message(str + _(" could not write file!"));
1572 		return false;
1573 	}
1574 
1575 	// see bug 6587
1576 	// removeAutosaveFile();
1577 
1578 	saveCheckSum();
1579 	message(str + _(" done."));
1580 
1581 	return true;
1582 }
1583 
1584 
emergencyWrite()1585 docstring Buffer::emergencyWrite()
1586 {
1587 	// No need to save if the buffer has not changed.
1588 	if (isClean())
1589 		return docstring();
1590 
1591 	string const doc = isUnnamed() ? onlyFileName(absFileName()) : absFileName();
1592 
1593 	docstring user_message = bformat(
1594 		_("LyX: Attempting to save document %1$s\n"), from_utf8(doc));
1595 
1596 	// We try to save three places:
1597 	// 1) Same place as document. Unless it is an unnamed doc.
1598 	if (!isUnnamed()) {
1599 		string s = absFileName();
1600 		s += ".emergency";
1601 		LYXERR0("  " << s);
1602 		if (writeFile(FileName(s))) {
1603 			markClean();
1604 			user_message += "  " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s));
1605 			return user_message;
1606 		} else {
1607 			user_message += "  " + _("Save failed! Trying again...\n");
1608 		}
1609 	}
1610 
1611 	// 2) In HOME directory.
1612 	string s = addName(Package::get_home_dir().absFileName(), absFileName());
1613 	s += ".emergency";
1614 	lyxerr << ' ' << s << endl;
1615 	if (writeFile(FileName(s))) {
1616 		markClean();
1617 		user_message += "  " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s));
1618 		return user_message;
1619 	}
1620 
1621 	user_message += "  " + _("Save failed! Trying yet again...\n");
1622 
1623 	// 3) In "/tmp" directory.
1624 	// MakeAbsPath to prepend the current
1625 	// drive letter on OS/2
1626 	s = addName(package().temp_dir().absFileName(), absFileName());
1627 	s += ".emergency";
1628 	lyxerr << ' ' << s << endl;
1629 	if (writeFile(FileName(s))) {
1630 		markClean();
1631 		user_message += "  " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s));
1632 		return user_message;
1633 	}
1634 
1635 	user_message += "  " + _("Save failed! Document is lost.");
1636 	// Don't try again.
1637 	markClean();
1638 	return user_message;
1639 }
1640 
1641 
write(ostream & ofs) const1642 bool Buffer::write(ostream & ofs) const
1643 {
1644 #ifdef HAVE_LOCALE
1645 	// Use the standard "C" locale for file output.
1646 	ofs.imbue(locale::classic());
1647 #endif
1648 
1649 	// The top of the file should not be written by params().
1650 
1651 	// write out a comment in the top of the file
1652 	// Important: Keep the version formatting in sync with lyx2lyx and
1653 	//            tex2lyx (bug 7951)
1654 	ofs << "#LyX " << lyx_version_major << "." << lyx_version_minor
1655 	    << " created this file. For more info see http://www.lyx.org/\n"
1656 	    << "\\lyxformat " << LYX_FORMAT << "\n"
1657 	    << "\\begin_document\n";
1658 
1659 	/// For each author, set 'used' to true if there is a change
1660 	/// by this author in the document; otherwise set it to 'false'.
1661 	AuthorList::Authors::const_iterator a_it = params().authors().begin();
1662 	AuthorList::Authors::const_iterator a_end = params().authors().end();
1663 	for (; a_it != a_end; ++a_it)
1664 		a_it->setUsed(false);
1665 
1666 	ParIterator const end = const_cast<Buffer *>(this)->par_iterator_end();
1667 	ParIterator it = const_cast<Buffer *>(this)->par_iterator_begin();
1668 	for ( ; it != end; ++it)
1669 		it->checkAuthors(params().authors());
1670 
1671 	// now write out the buffer parameters.
1672 	ofs << "\\begin_header\n";
1673 	params().writeFile(ofs, this);
1674 	ofs << "\\end_header\n";
1675 
1676 	// write the text
1677 	ofs << "\n\\begin_body\n";
1678 	text().write(ofs);
1679 	ofs << "\n\\end_body\n";
1680 
1681 	// Write marker that shows file is complete
1682 	ofs << "\\end_document" << endl;
1683 
1684 	// Shouldn't really be needed....
1685 	//ofs.close();
1686 
1687 	// how to check if close went ok?
1688 	// Following is an attempt... (BE 20001011)
1689 
1690 	// good() returns false if any error occurred, including some
1691 	//        formatting error.
1692 	// bad()  returns true if something bad happened in the buffer,
1693 	//        which should include file system full errors.
1694 
1695 	bool status = true;
1696 	if (!ofs) {
1697 		status = false;
1698 		lyxerr << "File was not closed properly." << endl;
1699 	}
1700 
1701 	return status;
1702 }
1703 
1704 
makeLaTeXFile(FileName const & fname,string const & original_path,OutputParams const & runparams_in,OutputWhat output) const1705 bool Buffer::makeLaTeXFile(FileName const & fname,
1706 			   string const & original_path,
1707 			   OutputParams const & runparams_in,
1708 			   OutputWhat output) const
1709 {
1710 	OutputParams runparams = runparams_in;
1711 
1712 	// XeTeX with TeX fonts is only safe with ASCII encoding (see also #9740),
1713 	// Check here, because the "flavor" is not known in BufferParams::encoding()
1714 	// (power users can override this safety measure selecting "utf8-plain").
1715 	if (!params().useNonTeXFonts && (runparams.flavor == OutputParams::XETEX)
1716 	    && (runparams.encoding->name() != "utf8-plain"))
1717 		runparams.encoding = encodings.fromLyXName("ascii");
1718 
1719 	string const encoding = runparams.encoding->iconvName();
1720 	LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << ", fname=" << fname.realPath());
1721 
1722 	ofdocstream ofs;
1723 	try { ofs.reset(encoding); }
1724 	catch (iconv_codecvt_facet_exception const & e) {
1725 		lyxerr << "Caught iconv exception: " << e.what() << endl;
1726 		Alert::error(_("Iconv software exception Detected"), bformat(_("Please "
1727 			"verify that the support software for your encoding (%1$s) is "
1728 			"properly installed"), from_ascii(encoding)));
1729 		return false;
1730 	}
1731 	if (!openFileWrite(ofs, fname))
1732 		return false;
1733 
1734 	ErrorList & errorList = d->errorLists["Export"];
1735 	errorList.clear();
1736 	bool failed_export = false;
1737 	otexstream os(ofs);
1738 
1739 	// make sure we are ready to export
1740 	// this needs to be done before we validate
1741 	// FIXME Do we need to do this all the time? I.e., in children
1742 	// of a master we are exporting?
1743 	updateBuffer();
1744 	updateMacroInstances(OutputUpdate);
1745 
1746 	try {
1747 		writeLaTeXSource(os, original_path, runparams, output);
1748 	}
1749 	catch (EncodingException const & e) {
1750 		docstring const failed(1, e.failed_char);
1751 		ostringstream oss;
1752 		oss << "0x" << hex << e.failed_char << dec;
1753 		docstring msg = bformat(_("Could not find LaTeX command for character '%1$s'"
1754 					  " (code point %2$s)"),
1755 					  failed, from_utf8(oss.str()));
1756 		errorList.push_back(ErrorItem(msg, _("Some characters of your document are probably not "
1757 				"representable in the chosen encoding.\n"
1758 				"Changing the document encoding to utf8 could help."),
1759 		                              {e.par_id, e.pos}, {e.par_id, e.pos + 1}));
1760 		failed_export = true;
1761 	}
1762 	catch (iconv_codecvt_facet_exception const & e) {
1763 		errorList.push_back(ErrorItem(_("iconv conversion failed"),
1764 		                              _(e.what())));
1765 		failed_export = true;
1766 	}
1767 	catch (exception const & e) {
1768 		errorList.push_back(ErrorItem(_("conversion failed"),
1769 		                              _(e.what())));
1770 		lyxerr << e.what() << endl;
1771 		failed_export = true;
1772 	}
1773 	catch (...) {
1774 		lyxerr << "Caught some really weird exception..." << endl;
1775 		lyx_exit(1);
1776 	}
1777 
1778 	d->texrow = move(os.texrow());
1779 
1780 	ofs.close();
1781 	if (ofs.fail()) {
1782 		failed_export = true;
1783 		lyxerr << "File '" << fname << "' was not closed properly." << endl;
1784 	}
1785 
1786 	if (runparams_in.silent)
1787 		errorList.clear();
1788 	else
1789 		errors("Export");
1790 	return !failed_export;
1791 }
1792 
1793 
writeLaTeXSource(otexstream & os,string const & original_path,OutputParams const & runparams_in,OutputWhat output) const1794 void Buffer::writeLaTeXSource(otexstream & os,
1795 			   string const & original_path,
1796 			   OutputParams const & runparams_in,
1797 			   OutputWhat output) const
1798 {
1799 	// The child documents, if any, shall be already loaded at this point.
1800 
1801 	OutputParams runparams = runparams_in;
1802 
1803 	// XeTeX with TeX fonts is only safe with ASCII encoding,
1804 	// Check here, because the "flavor" is not known in BufferParams::encoding()
1805 	// (power users can override this safety measure selecting "utf8-plain").
1806 	if (!params().useNonTeXFonts && (runparams.flavor == OutputParams::XETEX)
1807 	    && (runparams.encoding->name() != "utf8-plain"))
1808 		runparams.encoding = encodings.fromLyXName("ascii");
1809 	// FIXME: when only the current paragraph is shown, this is ignored
1810 	//        (or not reached) and characters encodable in the current
1811 	//        encoding are not converted to ASCII-representation.
1812 
1813 	// If we are compiling a file standalone, even if this is the
1814 	// child of some other buffer, let's cut the link here, so the
1815 	// file is really independent and no concurring settings from
1816 	// the master (e.g. branch state) interfere (see #8100).
1817 	if (!runparams.is_child)
1818 		d->ignore_parent = true;
1819 
1820 	// Classify the unicode characters appearing in math insets
1821 	BufferEncodings::initUnicodeMath(*this);
1822 
1823 	// validate the buffer.
1824 	LYXERR(Debug::LATEX, "  Validating buffer...");
1825 	LaTeXFeatures features(*this, params(), runparams);
1826 	validate(features);
1827 	// This is only set once per document (in master)
1828 	if (!runparams.is_child)
1829 		runparams.use_polyglossia = features.usePolyglossia();
1830 	LYXERR(Debug::LATEX, "  Buffer validation done.");
1831 
1832 	bool const output_preamble =
1833 		output == FullSource || output == OnlyPreamble;
1834 	bool const output_body =
1835 		output == FullSource || output == OnlyBody;
1836 
1837 	// The starting paragraph of the coming rows is the
1838 	// first paragraph of the document. (Asger)
1839 	if (output_preamble && runparams.nice) {
1840 		os << "%% LyX " << lyx_version << " created this file.  "
1841 			"For more info, see http://www.lyx.org/.\n"
1842 			"%% Do not edit unless you really know what "
1843 			"you are doing.\n";
1844 	}
1845 	LYXERR(Debug::INFO, "lyx document header finished");
1846 
1847 	// There are a few differences between nice LaTeX and usual files:
1848 	// usual files have \batchmode and special input@path to allow
1849 	// inclusion of figures specified by an explicitly relative path
1850 	// (i.e., a path starting with './' or '../') with either \input or
1851 	// \includegraphics, as the TEXINPUTS method doesn't work in this case.
1852 	// input@path is set when the actual parameter original_path is set.
1853 	// This is done for usual tex-file, but not for nice-latex-file.
1854 	// (Matthias 250696)
1855 	// Note that input@path is only needed for something the user does
1856 	// in the preamble, included .tex files or ERT, files included by
1857 	// LyX work without it.
1858 	if (output_preamble) {
1859 		if (!runparams.nice) {
1860 			// code for usual, NOT nice-latex-file
1861 			os << "\\batchmode\n"; // changed from \nonstopmode
1862 		}
1863 		if (!original_path.empty()) {
1864 			// FIXME UNICODE
1865 			// We don't know the encoding of inputpath
1866 			docstring const inputpath = from_utf8(original_path);
1867 			docstring uncodable_glyphs;
1868 			Encoding const * const enc = runparams.encoding;
1869 			if (enc) {
1870 				for (size_t n = 0; n < inputpath.size(); ++n) {
1871 					if (!enc->encodable(inputpath[n])) {
1872 						docstring const glyph(1, inputpath[n]);
1873 						LYXERR0("Uncodable character '"
1874 							<< glyph
1875 							<< "' in input path!");
1876 						uncodable_glyphs += glyph;
1877 					}
1878 				}
1879 			}
1880 
1881 			// warn user if we found uncodable glyphs.
1882 			if (!uncodable_glyphs.empty()) {
1883 				frontend::Alert::warning(
1884 					_("Uncodable character in file path"),
1885 					support::bformat(
1886 					  _("The path of your document\n"
1887 					    "(%1$s)\n"
1888 					    "contains glyphs that are unknown "
1889 					    "in the current document encoding "
1890 					    "(namely %2$s). This may result in "
1891 					    "incomplete output, unless "
1892 					    "TEXINPUTS contains the document "
1893 					    "directory and you don't use "
1894 					    "explicitly relative paths (i.e., "
1895 					    "paths starting with './' or "
1896 					    "'../') in the preamble or in ERT."
1897 					    "\n\nIn case of problems, choose "
1898 					    "an appropriate document encoding\n"
1899 					    "(such as utf8) or change the "
1900 					    "file path name."),
1901 					  inputpath, uncodable_glyphs));
1902 			} else {
1903 				string docdir = os::latex_path(original_path);
1904 				if (contains(docdir, '#')) {
1905 					docdir = subst(docdir, "#", "\\#");
1906 					os << "\\catcode`\\#=11"
1907 					      "\\def\\#{#}\\catcode`\\#=6\n";
1908 				}
1909 				if (contains(docdir, '%')) {
1910 					docdir = subst(docdir, "%", "\\%");
1911 					os << "\\catcode`\\%=11"
1912 					      "\\def\\%{%}\\catcode`\\%=14\n";
1913 				}
1914 				bool const detokenize = !isAscii(from_utf8(docdir))
1915 						|| contains(docdir, '~');
1916 				bool const quote = contains(docdir, ' ');
1917 				os << "\\makeatletter\n"
1918 				   << "\\def\\input@path{{";
1919 				if (detokenize)
1920 					os << "\\detokenize{";
1921 				if (quote)
1922 					os << "\"";
1923 				os << docdir;
1924 				if (quote)
1925 					os << "\"";
1926 				if (detokenize)
1927 					os << "}";
1928 				os << "}}\n"
1929 				   << "\\makeatother\n";
1930 			}
1931 		}
1932 
1933 		// get parent macros (if this buffer has a parent) which will be
1934 		// written at the document begin further down.
1935 		MacroSet parentMacros;
1936 		listParentMacros(parentMacros, features);
1937 
1938 		// Write the preamble
1939 		runparams.use_babel = params().writeLaTeX(os, features,
1940 							  d->filename.onlyPath());
1941 
1942 		// Biblatex bibliographies are loaded here
1943 		if (params().useBiblatex()) {
1944 			vector<docstring> const bibfiles =
1945 			    prepareBibFilePaths(runparams, getBibfiles(), true);
1946 			for (docstring const & file: bibfiles)
1947 				os << "\\addbibresource{" << file << "}\n";
1948 		}
1949 
1950 		if (!runparams.dryrun && features.hasPolyglossiaExclusiveLanguages()
1951 		    && !features.hasOnlyPolyglossiaLanguages()) {
1952 			docstring blangs;
1953 			docstring plangs;
1954 			vector<string> bll = features.getBabelExclusiveLanguages();
1955 			vector<string> pll = features.getPolyglossiaExclusiveLanguages();
1956 			if (!bll.empty()) {
1957 				docstring langs;
1958 				for (vector<string>::const_iterator it = bll.begin(); it != bll.end(); ++it) {
1959 					if (!langs.empty())
1960 						langs += ", ";
1961 					langs += _(*it);
1962 				}
1963 				blangs = bll.size() > 1 ?
1964 					    support::bformat(_("The languages %1$s are only supported by Babel."), langs)
1965 					  : support::bformat(_("The language %1$s is only supported by Babel."), langs);
1966 			}
1967 			if (!pll.empty()) {
1968 				docstring langs;
1969 				for (vector<string>::const_iterator it = pll.begin(); it != pll.end(); ++it) {
1970 					if (!langs.empty())
1971 						langs += ", ";
1972 					langs += _(*it);
1973 				}
1974 				plangs = pll.size() > 1 ?
1975 					    support::bformat(_("The languages %1$s are only supported by Polyglossia."), langs)
1976 					  : support::bformat(_("The language %1$s is only supported by Polyglossia."), langs);
1977 				if (!blangs.empty())
1978 					plangs += "\n";
1979 			}
1980 
1981 			frontend::Alert::warning(
1982 				_("Incompatible Languages!"),
1983 				support::bformat(
1984 				  _("You cannot use the following languages "
1985 				    "together in one LaTeX document because "
1986 				    "they require conflicting language packages:\n"
1987 				    "%1$s%2$s"),
1988 				  plangs, blangs));
1989 		}
1990 
1991 		// Japanese might be required only in some children of a document,
1992 		// but once required, we must keep use_japanese true.
1993 		runparams.use_japanese |= features.isRequired("japanese");
1994 
1995 		if (!output_body) {
1996 			// Restore the parenthood if needed
1997 			if (!runparams.is_child)
1998 				d->ignore_parent = false;
1999 			return;
2000 		}
2001 
2002 		// make the body.
2003 		// mark the beginning of the body to separate it from InPreamble insets
2004 		os.texrow().start(TexRow::beginDocument());
2005 		os << "\\begin{document}\n";
2006 
2007 		// mark the start of a new paragraph by simulating a newline,
2008 		// so that os.afterParbreak() returns true at document start
2009 		os.lastChar('\n');
2010 
2011 		// output the parent macros
2012 		MacroSet::iterator it = parentMacros.begin();
2013 		MacroSet::iterator end = parentMacros.end();
2014 		for (; it != end; ++it) {
2015 			int num_lines = (*it)->write(os.os(), true);
2016 			os.texrow().newlines(num_lines);
2017 		}
2018 
2019 	} // output_preamble
2020 
2021 	LYXERR(Debug::INFO, "preamble finished, now the body.");
2022 
2023 	// the real stuff
2024 	latexParagraphs(*this, text(), os, runparams);
2025 
2026 	// Restore the parenthood if needed
2027 	if (!runparams.is_child)
2028 		d->ignore_parent = false;
2029 
2030 	// add this just in case after all the paragraphs
2031 	os << endl;
2032 
2033 	if (output_preamble) {
2034 		os << "\\end{document}\n";
2035 		LYXERR(Debug::LATEX, "makeLaTeXFile...done");
2036 	} else {
2037 		LYXERR(Debug::LATEX, "LaTeXFile for inclusion made.");
2038 	}
2039 	runparams_in.encoding = runparams.encoding;
2040 
2041 	LYXERR(Debug::INFO, "Finished making LaTeX file.");
2042 	LYXERR(Debug::INFO, "Row count was " << os.texrow().rows() - 1 << '.');
2043 }
2044 
2045 
makeDocBookFile(FileName const & fname,OutputParams const & runparams,OutputWhat output) const2046 void Buffer::makeDocBookFile(FileName const & fname,
2047 			      OutputParams const & runparams,
2048 			      OutputWhat output) const
2049 {
2050 	LYXERR(Debug::LATEX, "makeDocBookFile...");
2051 
2052 	ofdocstream ofs;
2053 	if (!openFileWrite(ofs, fname))
2054 		return;
2055 
2056 	// make sure we are ready to export
2057 	// this needs to be done before we validate
2058 	updateBuffer();
2059 	updateMacroInstances(OutputUpdate);
2060 
2061 	writeDocBookSource(ofs, fname.absFileName(), runparams, output);
2062 
2063 	ofs.close();
2064 	if (ofs.fail())
2065 		lyxerr << "File '" << fname << "' was not closed properly." << endl;
2066 }
2067 
2068 
writeDocBookSource(odocstream & os,string const & fname,OutputParams const & runparams,OutputWhat output) const2069 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
2070 			     OutputParams const & runparams,
2071 			     OutputWhat output) const
2072 {
2073 	LaTeXFeatures features(*this, params(), runparams);
2074 	validate(features);
2075 
2076 	d->texrow.reset();
2077 
2078 	DocumentClass const & tclass = params().documentClass();
2079 	string const & top_element = tclass.latexname();
2080 
2081 	bool const output_preamble =
2082 		output == FullSource || output == OnlyPreamble;
2083 	bool const output_body =
2084 	  output == FullSource || output == OnlyBody;
2085 
2086 	if (output_preamble) {
2087 		if (runparams.flavor == OutputParams::XML)
2088 			os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2089 
2090 		// FIXME UNICODE
2091 		os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
2092 
2093 		// FIXME UNICODE
2094 		if (! tclass.class_header().empty())
2095 			os << from_ascii(tclass.class_header());
2096 		else if (runparams.flavor == OutputParams::XML)
2097 			os << "PUBLIC \"-//OASIS//DTD DocBook XML V4.2//EN\" "
2098 			    << "\"https://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
2099 		else
2100 			os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
2101 
2102 		docstring preamble = params().preamble;
2103 		if (runparams.flavor != OutputParams::XML ) {
2104 			preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
2105 			preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
2106 			preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
2107 			preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
2108 		}
2109 
2110 		string const name = runparams.nice
2111 			? changeExtension(absFileName(), ".sgml") : fname;
2112 		preamble += features.getIncludedFiles(name);
2113 		preamble += features.getLyXSGMLEntities();
2114 
2115 		if (!preamble.empty()) {
2116 			os << "\n [ " << preamble << " ]";
2117 		}
2118 		os << ">\n\n";
2119 	}
2120 
2121 	if (output_body) {
2122 		string top = top_element;
2123 		top += " lang=\"";
2124 		if (runparams.flavor == OutputParams::XML)
2125 			top += params().language->code();
2126 		else
2127 			top += params().language->code().substr(0, 2);
2128 		top += '"';
2129 
2130 		if (!params().options.empty()) {
2131 			top += ' ';
2132 			top += params().options;
2133 		}
2134 
2135 		os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
2136 				<< " file was created by LyX " << lyx_version
2137 				<< "\n  See http://www.lyx.org/ for more information -->\n";
2138 
2139 		params().documentClass().counters().reset();
2140 
2141 		sgml::openTag(os, top);
2142 		os << '\n';
2143 		docbookParagraphs(text(), *this, os, runparams);
2144 		sgml::closeTag(os, top_element);
2145 	}
2146 }
2147 
2148 
makeLyXHTMLFile(FileName const & fname,OutputParams const & runparams) const2149 void Buffer::makeLyXHTMLFile(FileName const & fname,
2150 			      OutputParams const & runparams) const
2151 {
2152 	LYXERR(Debug::LATEX, "makeLyXHTMLFile...");
2153 
2154 	ofdocstream ofs;
2155 	if (!openFileWrite(ofs, fname))
2156 		return;
2157 
2158 	// make sure we are ready to export
2159 	// this has to be done before we validate
2160 	updateBuffer(UpdateMaster, OutputUpdate);
2161 	updateMacroInstances(OutputUpdate);
2162 
2163 	writeLyXHTMLSource(ofs, runparams, FullSource);
2164 
2165 	ofs.close();
2166 	if (ofs.fail())
2167 		lyxerr << "File '" << fname << "' was not closed properly." << endl;
2168 }
2169 
2170 
writeLyXHTMLSource(odocstream & os,OutputParams const & runparams,OutputWhat output) const2171 void Buffer::writeLyXHTMLSource(odocstream & os,
2172 			     OutputParams const & runparams,
2173 			     OutputWhat output) const
2174 {
2175 	LaTeXFeatures features(*this, params(), runparams);
2176 	validate(features);
2177 	d->bibinfo_.makeCitationLabels(*this);
2178 
2179 	bool const output_preamble =
2180 		output == FullSource || output == OnlyPreamble;
2181 	bool const output_body =
2182 	  output == FullSource || output == OnlyBody || output == IncludedFile;
2183 
2184 	if (output_preamble) {
2185 		os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2186 		   << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\" \"http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd\">\n"
2187 		   // FIXME Language should be set properly.
2188 		   << "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
2189 		   << "<head>\n"
2190 		   << "<meta name=\"GENERATOR\" content=\"" << PACKAGE_STRING << "\" />\n"
2191 		   // FIXME Presumably need to set this right
2192 		   << "<meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />\n";
2193 
2194 		docstring const & doctitle = features.htmlTitle();
2195 		os << "<title>"
2196 		   << (doctitle.empty() ?
2197 		         from_ascii("LyX Document") :
2198 		         html::htmlize(doctitle, XHTMLStream::ESCAPE_ALL))
2199 		   << "</title>\n";
2200 
2201 		docstring styles = features.getTClassHTMLPreamble();
2202 		if (!styles.empty())
2203 			os << "\n<!-- Text Class Preamble -->\n" << styles << '\n';
2204 
2205 		styles = features.getPreambleSnippets().str;
2206 		if (!styles.empty())
2207 			os << "\n<!-- Preamble Snippets -->\n" << styles << '\n';
2208 
2209 		// we will collect CSS information in a stream, and then output it
2210 		// either here, as part of the header, or else in a separate file.
2211 		odocstringstream css;
2212 		styles = features.getCSSSnippets();
2213 		if (!styles.empty())
2214 			css << "/* LyX Provided Styles */\n" << styles << '\n';
2215 
2216 		styles = features.getTClassHTMLStyles();
2217 		if (!styles.empty())
2218 			css << "/* Layout-provided Styles */\n" << styles << '\n';
2219 
2220 		bool const needfg = params().fontcolor != RGBColor(0, 0, 0);
2221 		bool const needbg = params().backgroundcolor != RGBColor(0xFF, 0xFF, 0xFF);
2222 		if (needfg || needbg) {
2223 				css << "\nbody {\n";
2224 				if (needfg)
2225 				   css << "  color: "
2226 					    << from_ascii(X11hexname(params().fontcolor))
2227 					    << ";\n";
2228 				if (needbg)
2229 				   css << "  background-color: "
2230 					    << from_ascii(X11hexname(params().backgroundcolor))
2231 					    << ";\n";
2232 				css << "}\n";
2233 		}
2234 
2235 		docstring const dstyles = css.str();
2236 		if (!dstyles.empty()) {
2237 			bool written = false;
2238 			if (params().html_css_as_file) {
2239 				// open a file for CSS info
2240 				ofdocstream ocss;
2241 				string const fcssname = addName(temppath(), "docstyle.css");
2242 				FileName const fcssfile = FileName(fcssname);
2243 				if (openFileWrite(ocss, fcssfile)) {
2244 					ocss << dstyles;
2245 					ocss.close();
2246 					written = true;
2247 					// write link to header
2248 					os << "<link rel='stylesheet' href='docstyle.css' type='text/css' />\n";
2249 					// register file
2250 					runparams.exportdata->addExternalFile("xhtml", fcssfile);
2251 				}
2252 			}
2253 			// we are here if the CSS is supposed to be written to the header
2254 			// or if we failed to write it to an external file.
2255 			if (!written) {
2256 				os << "<style type='text/css'>\n"
2257 					 << dstyles
2258 					 << "\n</style>\n";
2259 			}
2260 		}
2261 		os << "</head>\n";
2262 	}
2263 
2264 	if (output_body) {
2265 		bool const output_body_tag = (output != IncludedFile);
2266 		if (output_body_tag)
2267 			os << "<body dir=\"auto\">\n";
2268 		XHTMLStream xs(os);
2269 		if (output != IncludedFile)
2270 			// if we're an included file, the counters are in the master.
2271 			params().documentClass().counters().reset();
2272 		xhtmlParagraphs(text(), *this, xs, runparams);
2273 		if (output_body_tag)
2274 			os << "</body>\n";
2275 	}
2276 
2277 	if (output_preamble)
2278 		os << "</html>\n";
2279 }
2280 
2281 
2282 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
2283 // Other flags: -wall -v0 -x
runChktex()2284 int Buffer::runChktex()
2285 {
2286 	setBusy(true);
2287 
2288 	// get LaTeX-Filename
2289 	FileName const path(temppath());
2290 	string const name = addName(path.absFileName(), latexName());
2291 	string const org_path = filePath();
2292 
2293 	PathChanger p(path); // path to LaTeX file
2294 	message(_("Running chktex..."));
2295 
2296 	// Generate the LaTeX file if neccessary
2297 	OutputParams runparams(&params().encoding());
2298 	runparams.flavor = OutputParams::LATEX;
2299 	runparams.nice = false;
2300 	runparams.linelen = lyxrc.plaintext_linelen;
2301 	makeLaTeXFile(FileName(name), org_path, runparams);
2302 
2303 	TeXErrors terr;
2304 	Chktex chktex(lyxrc.chktex_command, onlyFileName(name), filePath());
2305 	int const res = chktex.run(terr); // run chktex
2306 
2307 	if (res == -1) {
2308 		Alert::error(_("chktex failure"),
2309 			     _("Could not run chktex successfully."));
2310 	} else {
2311 		ErrorList & errlist = d->errorLists["ChkTeX"];
2312 		errlist.clear();
2313 		bufferErrors(terr, errlist);
2314 	}
2315 
2316 	setBusy(false);
2317 
2318 	if (runparams.silent)
2319 		d->errorLists["ChkTeX"].clear();
2320 	else
2321 		errors("ChkTeX");
2322 
2323 	return res;
2324 }
2325 
2326 
validate(LaTeXFeatures & features) const2327 void Buffer::validate(LaTeXFeatures & features) const
2328 {
2329 	// Validate the buffer params, but not for included
2330 	// files, since they also use the parent buffer's
2331 	// params (# 5941)
2332 	if (!features.runparams().is_child)
2333 		params().validate(features);
2334 
2335 	for (Paragraph const & p : paragraphs())
2336 		p.validate(features);
2337 
2338 	if (lyxerr.debugging(Debug::LATEX)) {
2339 		features.showStruct();
2340 	}
2341 }
2342 
2343 
getLabelList(vector<docstring> & list) const2344 void Buffer::getLabelList(vector<docstring> & list) const
2345 {
2346 	// If this is a child document, use the master's list instead.
2347 	if (parent()) {
2348 		masterBuffer()->getLabelList(list);
2349 		return;
2350 	}
2351 
2352 	list.clear();
2353 	shared_ptr<Toc> toc = d->toc_backend.toc("label");
2354 	Toc::const_iterator toc_it = toc->begin();
2355 	Toc::const_iterator end = toc->end();
2356 	for (; toc_it != end; ++toc_it) {
2357 		if (toc_it->depth() == 0)
2358 			list.push_back(toc_it->str());
2359 	}
2360 }
2361 
2362 
2363 // This is not in master, so it has been removed at 2126d5a3, on
2364 // 14 December 2018, so as to test whether it's needed. If there
2365 // aren't any complaints, it can be fully removed.
2366 #if 0
2367 void Buffer::updateBibfilesCache(UpdateScope scope) const
2368 {
2369 	// FIXME This is probably unnecssary, given where we call this.
2370 	// If this is a child document, use the parent's cache instead.
2371 	if (parent() && scope != UpdateChildOnly) {
2372 		masterBuffer()->updateBibfilesCache();
2373 		return;
2374 	}
2375 
2376 	docstring_list old_cache = d->bibfiles_cache_;
2377 	d->bibfiles_cache_.clear();
2378 	for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2379 		if (it->lyxCode() == BIBTEX_CODE) {
2380 			InsetBibtex const & inset = static_cast<InsetBibtex const &>(*it);
2381 			docstring_list const bibfiles = inset.getBibFiles();
2382 			d->bibfiles_cache_.insert(d->bibfiles_cache_.end(),
2383 				bibfiles.begin(),
2384 				bibfiles.end());
2385 		} else if (it->lyxCode() == INCLUDE_CODE) {
2386 			InsetInclude & inset = static_cast<InsetInclude &>(*it);
2387 			Buffer const * const incbuf = inset.getChildBuffer();
2388 			if (!incbuf)
2389 				continue;
2390 			docstring_list const & bibfiles =
2391 			        incbuf->getBibfiles(UpdateChildOnly);
2392 			if (!bibfiles.empty()) {
2393 				d->bibfiles_cache_.insert(d->bibfiles_cache_.end(),
2394 					bibfiles.begin(),
2395 					bibfiles.end());
2396 			}
2397 		}
2398 	}
2399 	d->bibfile_cache_valid_ = true;
2400 	d->cite_labels_valid_ = false;
2401 	if (d->bibfiles_cache_ != old_cache)
2402 		d->bibinfo_cache_valid_ = false;
2403 }
2404 #endif
2405 
2406 
invalidateBibinfoCache() const2407 void Buffer::invalidateBibinfoCache() const
2408 {
2409 	d->bibinfo_cache_valid_ = false;
2410 	d->cite_labels_valid_ = false;
2411 	removeBiblioTempFiles();
2412 	// also invalidate the cache for the parent buffer
2413 	Buffer const * const pbuf = d->parent();
2414 	if (pbuf)
2415 		pbuf->invalidateBibinfoCache();
2416 }
2417 
2418 
getBibfiles(UpdateScope scope) const2419 docstring_list const & Buffer::getBibfiles(UpdateScope scope) const
2420 {
2421 	// FIXME This is probably unnecessary, given where we call this.
2422 	// If this is a child document, use the master's cache instead.
2423 	Buffer const * const pbuf = masterBuffer();
2424 	if (pbuf != this && scope != UpdateChildOnly)
2425 		return pbuf->getBibfiles();
2426 
2427 	return d->bibfiles_cache_;
2428 }
2429 
2430 
masterBibInfo() const2431 BiblioInfo const & Buffer::masterBibInfo() const
2432 {
2433 	Buffer const * const tmp = masterBuffer();
2434 	if (tmp != this)
2435 		return tmp->masterBibInfo();
2436 	return d->bibinfo_;
2437 }
2438 
2439 
bibInfo() const2440 BiblioInfo const & Buffer::bibInfo() const
2441 {
2442 	return d->bibinfo_;
2443 }
2444 
2445 
registerBibfiles(const docstring_list & bf) const2446 void Buffer::registerBibfiles(const docstring_list & bf) const
2447 {
2448 	// We register the bib files in the master buffer,
2449 	// if there is one, but also in every single buffer,
2450 	// in case a child is compiled alone.
2451 	Buffer const * const tmp = masterBuffer();
2452 	if (tmp != this)
2453 		tmp->registerBibfiles(bf);
2454 
2455 	for (auto const & p : bf) {
2456 		docstring_list::const_iterator temp =
2457 			find(d->bibfiles_cache_.begin(), d->bibfiles_cache_.end(), p);
2458 		if (temp == d->bibfiles_cache_.end())
2459 			d->bibfiles_cache_.push_back(p);
2460 	}
2461 }
2462 
2463 
2464 static map<docstring, FileName> bibfileCache;
2465 
getBibfilePath(docstring const & bibid) const2466 FileName Buffer::getBibfilePath(docstring const & bibid) const
2467 {
2468 	map<docstring, FileName>::const_iterator it =
2469 		bibfileCache.find(bibid);
2470 	if (it != bibfileCache.end()) {
2471 		// i.e., return bibfileCache[bibid];
2472 		return it->second;
2473 	}
2474 
2475 	LYXERR(Debug::FILES, "Reading file location for " << bibid);
2476 	string const texfile = changeExtension(to_utf8(bibid), "bib");
2477 	// we need to check first if this file exists where it's said to be.
2478 	// there's a weird bug that occurs otherwise: if the file is in the
2479 	// Buffer's directory but has the same name as some file that would be
2480 	// found by kpsewhich, then we find the latter, not the former.
2481 	FileName const local_file = makeAbsPath(texfile, filePath());
2482 	FileName file = local_file;
2483 	if (!file.exists()) {
2484 		// there's no need now to check whether the file can be found
2485 		// locally
2486 		file = findtexfile(texfile, "bib", true);
2487 		if (file.empty())
2488 			file = local_file;
2489 	}
2490 	LYXERR(Debug::FILES, "Found at: " << file);
2491 
2492 	bibfileCache[bibid] = file;
2493 	return bibfileCache[bibid];
2494 }
2495 
2496 
checkIfBibInfoCacheIsValid() const2497 void Buffer::checkIfBibInfoCacheIsValid() const
2498 {
2499 	// use the master's cache
2500 	Buffer const * const tmp = masterBuffer();
2501 	if (tmp != this) {
2502 		tmp->checkIfBibInfoCacheIsValid();
2503 		return;
2504 	}
2505 
2506 	// If we already know the cache is invalid, stop here.
2507 	// This is important in the case when the bibliography
2508 	// environment (rather than Bib[la]TeX) is used.
2509 	// In that case, the timestamp check below gives no
2510 	// sensible result. Rather than that, the cache will
2511 	// be invalidated explicitly via invalidateBibInfoCache()
2512 	// by the Bibitem inset.
2513 	// Same applies for bib encoding changes, which trigger
2514 	// invalidateBibInfoCache() by InsetBibtex.
2515 	if (!d->bibinfo_cache_valid_)
2516 		return;
2517 
2518 	if (d->have_bibitems_) {
2519 		// We have a bibliography environment.
2520 		// Invalidate the bibinfo cache unconditionally.
2521 		// Cite labels will get invalidated by the inset if needed.
2522 		d->bibinfo_cache_valid_ = false;
2523 		return;
2524 	}
2525 
2526 	// OK. This is with Bib(la)tex. We'll assume the cache
2527 	// is valid and change this if we find changes in the bibs.
2528 	d->bibinfo_cache_valid_ = true;
2529 	d->cite_labels_valid_ = true;
2530 	// compare the cached timestamps with the actual ones.
2531 	docstring_list const & bibfiles_cache = getBibfiles();
2532 	for (auto const & bf : bibfiles_cache) {
2533 		FileName const fn = getBibfilePath(bf);
2534 		time_t lastw = fn.lastModified();
2535 		time_t prevw = d->bibfile_status_[fn];
2536 		if (lastw != prevw) {
2537 			d->bibinfo_cache_valid_ = false;
2538 			d->cite_labels_valid_ = false;
2539 			d->bibfile_status_[fn] = lastw;
2540 		}
2541 	}
2542 }
2543 
2544 
clearBibFileCache() const2545 void Buffer::clearBibFileCache() const
2546 {
2547 	bibfileCache.clear();
2548 }
2549 
2550 
reloadBibInfoCache() const2551 void Buffer::reloadBibInfoCache() const
2552 {
2553 	if (isInternal())
2554 		return;
2555 
2556 	// use the master's cache
2557 	Buffer const * const tmp = masterBuffer();
2558 	if (tmp != this) {
2559 		tmp->reloadBibInfoCache();
2560 		return;
2561 	}
2562 
2563 	checkIfBibInfoCacheIsValid();
2564 	if (d->bibinfo_cache_valid_)
2565 		return;
2566 	LYXERR(Debug::FILES, "Bibinfo cache was invalid.");
2567 
2568 	// re-read file locations when this info changes
2569 	// FIXME Is this sufficient? Or should we also force that
2570 	// in some other cases? If so, then it is easy enough to
2571 	// add the following line in some other places.
2572 	clearBibFileCache();
2573 	d->bibinfo_.clear();
2574 	FileNameList checkedFiles;
2575 	d->have_bibitems_ = false;
2576 	collectBibKeys(checkedFiles);
2577 	d->bibinfo_cache_valid_ = true;
2578 }
2579 
2580 
collectBibKeys(FileNameList & checkedFiles) const2581 void Buffer::collectBibKeys(FileNameList & checkedFiles) const
2582 {
2583 	for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2584 		it->collectBibKeys(it, checkedFiles);
2585 		if (it->lyxCode() == BIBITEM_CODE) {
2586 			if (parent() != 0)
2587 				parent()->d->have_bibitems_ = true;
2588 			else
2589 				d->have_bibitems_ = true;
2590 		}
2591 	}
2592 }
2593 
2594 
addBiblioInfo(BiblioInfo const & bin) const2595 void Buffer::addBiblioInfo(BiblioInfo const & bin) const
2596 {
2597 	// We add the biblio info to the master buffer,
2598 	// if there is one, but also to every single buffer,
2599 	// in case a child is compiled alone.
2600 	BiblioInfo & bi = d->bibinfo_;
2601 	bi.mergeBiblioInfo(bin);
2602 
2603 	if (parent() != 0) {
2604 		BiblioInfo & masterbi = parent()->d->bibinfo_;
2605 		masterbi.mergeBiblioInfo(bin);
2606 	}
2607 }
2608 
2609 
addBibTeXInfo(docstring const & key,BibTeXInfo const & bin) const2610 void Buffer::addBibTeXInfo(docstring const & key, BibTeXInfo const & bin) const
2611 {
2612 	// We add the bibtex info to the master buffer,
2613 	// if there is one, but also to every single buffer,
2614 	// in case a child is compiled alone.
2615 	BiblioInfo & bi = d->bibinfo_;
2616 	bi[key] = bin;
2617 
2618 	if (parent() != 0) {
2619 		BiblioInfo & masterbi = parent()->d->bibinfo_;
2620 		masterbi[key] = bin;
2621 	}
2622 }
2623 
2624 
makeCitationLabels() const2625 void Buffer::makeCitationLabels() const
2626 {
2627 	Buffer const * const master = masterBuffer();
2628 	return d->bibinfo_.makeCitationLabels(*master);
2629 }
2630 
2631 
invalidateCiteLabels() const2632 void Buffer::invalidateCiteLabels() const
2633 {
2634 	masterBuffer()->d->cite_labels_valid_ = false;
2635 }
2636 
citeLabelsValid() const2637 bool Buffer::citeLabelsValid() const
2638 {
2639 	return masterBuffer()->d->cite_labels_valid_;
2640 }
2641 
2642 
removeBiblioTempFiles() const2643 void Buffer::removeBiblioTempFiles() const
2644 {
2645 	// We remove files that contain LaTeX commands specific to the
2646 	// particular bibliographic style being used, in order to avoid
2647 	// LaTeX errors when we switch style.
2648 	FileName const aux_file(addName(temppath(), changeExtension(latexName(),".aux")));
2649 	FileName const bbl_file(addName(temppath(), changeExtension(latexName(),".bbl")));
2650 	LYXERR(Debug::FILES, "Removing the .aux file " << aux_file);
2651 	aux_file.removeFile();
2652 	LYXERR(Debug::FILES, "Removing the .bbl file " << bbl_file);
2653 	bbl_file.removeFile();
2654 	// Also for the parent buffer
2655 	Buffer const * const pbuf = parent();
2656 	if (pbuf)
2657 		pbuf->removeBiblioTempFiles();
2658 }
2659 
2660 
isDepClean(string const & name) const2661 bool Buffer::isDepClean(string const & name) const
2662 {
2663 	DepClean::const_iterator const it = d->dep_clean.find(name);
2664 	if (it == d->dep_clean.end())
2665 		return true;
2666 	return it->second;
2667 }
2668 
2669 
markDepClean(string const & name)2670 void Buffer::markDepClean(string const & name)
2671 {
2672 	d->dep_clean[name] = true;
2673 }
2674 
2675 
getStatus(FuncRequest const & cmd,FuncStatus & flag)2676 bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag)
2677 {
2678 	if (isInternal()) {
2679 		// FIXME? if there is an Buffer LFUN that can be dispatched even
2680 		// if internal, put a switch '(cmd.action)' here.
2681 		return false;
2682 	}
2683 
2684 	bool enable = true;
2685 
2686 	switch (cmd.action()) {
2687 
2688 	case LFUN_BUFFER_TOGGLE_READ_ONLY:
2689 		flag.setOnOff(hasReadonlyFlag());
2690 		break;
2691 
2692 		// FIXME: There is need for a command-line import.
2693 		//case LFUN_BUFFER_IMPORT:
2694 
2695 	case LFUN_BUFFER_AUTO_SAVE:
2696 		break;
2697 
2698 	case LFUN_BUFFER_EXPORT_CUSTOM:
2699 		// FIXME: Nothing to check here?
2700 		break;
2701 
2702 	case LFUN_BUFFER_EXPORT: {
2703 		docstring const arg = cmd.argument();
2704 		if (arg == "custom") {
2705 			enable = true;
2706 			break;
2707 		}
2708 		string format = (arg.empty() || arg == "default") ?
2709 			params().getDefaultOutputFormat() : to_utf8(arg);
2710 		size_t pos = format.find(' ');
2711 		if (pos != string::npos)
2712 			format = format.substr(0, pos);
2713 		enable = params().isExportable(format, false);
2714 		if (!enable)
2715 			flag.message(bformat(
2716 					     _("Don't know how to export to format: %1$s"), arg));
2717 		break;
2718 	}
2719 
2720 	case LFUN_BUILD_PROGRAM:
2721 		enable = params().isExportable("program", false);
2722 		break;
2723 
2724 	case LFUN_BRANCH_ACTIVATE:
2725 	case LFUN_BRANCH_DEACTIVATE:
2726 	case LFUN_BRANCH_MASTER_ACTIVATE:
2727 	case LFUN_BRANCH_MASTER_DEACTIVATE: {
2728 		bool const master = (cmd.action() == LFUN_BRANCH_MASTER_ACTIVATE
2729 				     || cmd.action() == LFUN_BRANCH_MASTER_DEACTIVATE);
2730 		BranchList const & branchList = master ? masterBuffer()->params().branchlist()
2731 			: params().branchlist();
2732 		docstring const branchName = cmd.argument();
2733 		flag.setEnabled(!branchName.empty() && branchList.find(branchName));
2734 		break;
2735 	}
2736 
2737 	case LFUN_BRANCH_ADD:
2738 	case LFUN_BRANCHES_RENAME:
2739 		// if no Buffer is present, then of course we won't be called!
2740 		break;
2741 
2742 	case LFUN_BUFFER_LANGUAGE:
2743 		enable = !isReadonly();
2744 		break;
2745 
2746 	case LFUN_BUFFER_VIEW_CACHE:
2747 		(d->preview_file_).refresh();
2748 		enable = (d->preview_file_).exists() && !(d->preview_file_).isFileEmpty();
2749 		break;
2750 
2751 	case LFUN_CHANGES_TRACK:
2752 		flag.setEnabled(true);
2753 		flag.setOnOff(params().track_changes);
2754 		break;
2755 
2756 	case LFUN_CHANGES_OUTPUT:
2757 		flag.setEnabled(true);
2758 		flag.setOnOff(params().output_changes);
2759 		break;
2760 
2761 	case LFUN_BUFFER_TOGGLE_COMPRESSION:
2762 		flag.setOnOff(params().compressed);
2763 		break;
2764 
2765 	case LFUN_BUFFER_TOGGLE_OUTPUT_SYNC:
2766 		flag.setOnOff(params().output_sync);
2767 		break;
2768 
2769 	case LFUN_BUFFER_ANONYMIZE:
2770 		break;
2771 
2772 	default:
2773 		return false;
2774 	}
2775 	flag.setEnabled(enable);
2776 	return true;
2777 }
2778 
2779 
dispatch(string const & command,DispatchResult & result)2780 void Buffer::dispatch(string const & command, DispatchResult & result)
2781 {
2782 	return dispatch(lyxaction.lookupFunc(command), result);
2783 }
2784 
2785 
2786 // NOTE We can end up here even if we have no GUI, because we are called
2787 // by LyX::exec to handled command-line requests. So we may need to check
2788 // whether we have a GUI or not. The boolean use_gui holds this information.
dispatch(FuncRequest const & func,DispatchResult & dr)2789 void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
2790 {
2791 	if (isInternal()) {
2792 		// FIXME? if there is an Buffer LFUN that can be dispatched even
2793 		// if internal, put a switch '(cmd.action())' here.
2794 		dr.dispatched(false);
2795 		return;
2796 	}
2797 	string const argument = to_utf8(func.argument());
2798 	// We'll set this back to false if need be.
2799 	bool dispatched = true;
2800 	// This handles undo groups automagically
2801 	UndoGroupHelper ugh(this);
2802 
2803 	switch (func.action()) {
2804 	case LFUN_BUFFER_TOGGLE_READ_ONLY:
2805 		if (lyxvc().inUse()) {
2806 			string log = lyxvc().toggleReadOnly();
2807 			if (!log.empty())
2808 				dr.setMessage(log);
2809 		}
2810 		else
2811 			setReadonly(!hasReadonlyFlag());
2812 		break;
2813 
2814 	case LFUN_BUFFER_EXPORT: {
2815 		string const format = (argument.empty() || argument == "default") ?
2816 			params().getDefaultOutputFormat() : argument;
2817 		ExportStatus const status = doExport(format, false);
2818 		dr.setError(status != ExportSuccess);
2819 		if (status != ExportSuccess)
2820 			dr.setMessage(bformat(_("Error exporting to format: %1$s."),
2821 			                      from_utf8(format)));
2822 		break;
2823 	}
2824 
2825 	case LFUN_BUILD_PROGRAM: {
2826 		ExportStatus const status = doExport("program", true);
2827 		dr.setError(status != ExportSuccess);
2828 		if (status != ExportSuccess)
2829 			dr.setMessage(_("Error generating literate programming code."));
2830 		break;
2831 	}
2832 
2833 	case LFUN_BUFFER_EXPORT_CUSTOM: {
2834 		string format_name;
2835 		string command = split(argument, format_name, ' ');
2836 		Format const * format = theFormats().getFormat(format_name);
2837 		if (!format) {
2838 			lyxerr << "Format \"" << format_name
2839 				<< "\" not recognized!"
2840 				<< endl;
2841 			break;
2842 		}
2843 
2844 		// The name of the file created by the conversion process
2845 		string filename;
2846 
2847 		// Output to filename
2848 		if (format->name() == "lyx") {
2849 			string const latexname = latexName(false);
2850 			filename = changeExtension(latexname,
2851 				format->extension());
2852 			filename = addName(temppath(), filename);
2853 
2854 			if (!writeFile(FileName(filename)))
2855 				break;
2856 
2857 		} else {
2858 			doExport(format_name, true, filename);
2859 		}
2860 
2861 		// Substitute $$FName for filename
2862 		if (!contains(command, "$$FName"))
2863 			command = "( " + command + " ) < $$FName";
2864 		command = subst(command, "$$FName", filename);
2865 
2866 		// Execute the command in the background
2867 		Systemcall call;
2868 		call.startscript(Systemcall::DontWait, command,
2869 		                 filePath(), layoutPos());
2870 		break;
2871 	}
2872 
2873 	// FIXME: There is need for a command-line import.
2874 	/*
2875 	case LFUN_BUFFER_IMPORT:
2876 		doImport(argument);
2877 		break;
2878 	*/
2879 
2880 	case LFUN_BUFFER_AUTO_SAVE:
2881 		autoSave();
2882 		resetAutosaveTimers();
2883 		break;
2884 
2885 	case LFUN_BRANCH_ACTIVATE:
2886 	case LFUN_BRANCH_DEACTIVATE:
2887 	case LFUN_BRANCH_MASTER_ACTIVATE:
2888 	case LFUN_BRANCH_MASTER_DEACTIVATE: {
2889 		bool const master = (func.action() == LFUN_BRANCH_MASTER_ACTIVATE
2890 				     || func.action() == LFUN_BRANCH_MASTER_DEACTIVATE);
2891 		Buffer * buf = master ? const_cast<Buffer *>(masterBuffer())
2892 				      : this;
2893 
2894 		docstring const branch_name = func.argument();
2895 		// the case without a branch name is handled elsewhere
2896 		if (branch_name.empty()) {
2897 			dispatched = false;
2898 			break;
2899 		}
2900 		Branch * branch = buf->params().branchlist().find(branch_name);
2901 		if (!branch) {
2902 			LYXERR0("Branch " << branch_name << " does not exist.");
2903 			dr.setError(true);
2904 			docstring const msg =
2905 				bformat(_("Branch \"%1$s\" does not exist."), branch_name);
2906 			dr.setMessage(msg);
2907 			break;
2908 		}
2909 		bool const activate = (func.action() == LFUN_BRANCH_ACTIVATE
2910 				       || func.action() == LFUN_BRANCH_MASTER_ACTIVATE);
2911 		if (branch->isSelected() != activate) {
2912 			buf->undo().recordUndoBufferParams(CursorData());
2913 			branch->setSelected(activate);
2914 			dr.setError(false);
2915 			dr.screenUpdate(Update::Force);
2916 			dr.forceBufferUpdate();
2917 		}
2918 		break;
2919 	}
2920 
2921 	case LFUN_BRANCH_ADD: {
2922 		docstring branch_name = func.argument();
2923 		if (branch_name.empty()) {
2924 			dispatched = false;
2925 			break;
2926 		}
2927 		BranchList & branch_list = params().branchlist();
2928 		vector<docstring> const branches =
2929 			getVectorFromString(branch_name, branch_list.separator());
2930 		docstring msg;
2931 		for (vector<docstring>::const_iterator it = branches.begin();
2932 		     it != branches.end(); ++it) {
2933 			branch_name = *it;
2934 			Branch * branch = branch_list.find(branch_name);
2935 			if (branch) {
2936 				LYXERR0("Branch " << branch_name << " already exists.");
2937 				dr.setError(true);
2938 				if (!msg.empty())
2939 					msg += ("\n");
2940 				msg += bformat(_("Branch \"%1$s\" already exists."), branch_name);
2941 			} else {
2942 				undo().recordUndoBufferParams(CursorData());
2943 				branch_list.add(branch_name);
2944 				branch = branch_list.find(branch_name);
2945 				string const x11hexname = X11hexname(branch->color());
2946 				docstring const str = branch_name + ' ' + from_ascii(x11hexname);
2947 				lyx::dispatch(FuncRequest(LFUN_SET_COLOR, str));
2948 				dr.setError(false);
2949 				dr.screenUpdate(Update::Force);
2950 			}
2951 		}
2952 		if (!msg.empty())
2953 			dr.setMessage(msg);
2954 		break;
2955 	}
2956 
2957 	case LFUN_BRANCHES_RENAME: {
2958 		if (func.argument().empty())
2959 			break;
2960 
2961 		docstring const oldname = from_utf8(func.getArg(0));
2962 		docstring const newname = from_utf8(func.getArg(1));
2963 		InsetIterator it  = inset_iterator_begin(inset());
2964 		InsetIterator const end = inset_iterator_end(inset());
2965 		bool success = false;
2966 		for (; it != end; ++it) {
2967 			if (it->lyxCode() == BRANCH_CODE) {
2968 				InsetBranch & ins = static_cast<InsetBranch &>(*it);
2969 				if (ins.branch() == oldname) {
2970 					undo().recordUndo(CursorData(it));
2971 					ins.rename(newname);
2972 					success = true;
2973 					continue;
2974 				}
2975 			}
2976 			if (it->lyxCode() == INCLUDE_CODE) {
2977 				// get buffer of external file
2978 				InsetInclude const & ins =
2979 					static_cast<InsetInclude const &>(*it);
2980 				Buffer * child = ins.getChildBuffer();
2981 				if (!child)
2982 					continue;
2983 				child->dispatch(func, dr);
2984 			}
2985 		}
2986 
2987 		if (success) {
2988 			dr.screenUpdate(Update::Force);
2989 			dr.forceBufferUpdate();
2990 		}
2991 		break;
2992 	}
2993 
2994 	case LFUN_BUFFER_VIEW_CACHE:
2995 		if (!theFormats().view(*this, d->preview_file_,
2996 				  d->preview_format_))
2997 			dr.setMessage(_("Error viewing the output file."));
2998 		break;
2999 
3000 	case LFUN_CHANGES_TRACK:
3001 		if (params().save_transient_properties)
3002 			undo().recordUndoBufferParams(CursorData());
3003 		params().track_changes = !params().track_changes;
3004 		if (!params().track_changes)
3005 			dr.forceChangesUpdate();
3006 		break;
3007 
3008 	case LFUN_CHANGES_OUTPUT:
3009 		if (params().save_transient_properties)
3010 			undo().recordUndoBufferParams(CursorData());
3011 		params().output_changes = !params().output_changes;
3012 		if (params().output_changes) {
3013 			bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
3014 			bool xcolorulem = LaTeXFeatures::isAvailable("ulem") &&
3015 					  LaTeXFeatures::isAvailable("xcolor");
3016 
3017 			if (!dvipost && !xcolorulem) {
3018 				Alert::warning(_("Changes not shown in LaTeX output"),
3019 					       _("Changes will not be highlighted in LaTeX output, "
3020 						 "because neither dvipost nor xcolor/ulem are installed.\n"
3021 						 "Please install these packages or redefine "
3022 						 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
3023 			} else if (!xcolorulem) {
3024 				Alert::warning(_("Changes not shown in LaTeX output"),
3025 					       _("Changes will not be highlighted in LaTeX output "
3026 						 "when using pdflatex, because xcolor and ulem are not installed.\n"
3027 						 "Please install both packages or redefine "
3028 						 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
3029 			}
3030 		}
3031 		break;
3032 
3033 	case LFUN_BUFFER_TOGGLE_COMPRESSION:
3034 		// turn compression on/off
3035 		undo().recordUndoBufferParams(CursorData());
3036 		params().compressed = !params().compressed;
3037 		break;
3038 
3039 	case LFUN_BUFFER_TOGGLE_OUTPUT_SYNC:
3040 		undo().recordUndoBufferParams(CursorData());
3041 		params().output_sync = !params().output_sync;
3042 		break;
3043 
3044 	case LFUN_BUFFER_ANONYMIZE: {
3045 		undo().recordUndoFullBuffer(CursorData());
3046 		CursorData cur(doc_iterator_begin(this));
3047 		for ( ; cur ; cur.forwardPar())
3048 			cur.paragraph().anonymize();
3049 		dr.forceBufferUpdate();
3050 		dr.screenUpdate(Update::Force);
3051 		break;
3052 	}
3053 
3054 	default:
3055 		dispatched = false;
3056 		break;
3057 	}
3058 	dr.dispatched(dispatched);
3059 }
3060 
3061 
changeLanguage(Language const * from,Language const * to)3062 void Buffer::changeLanguage(Language const * from, Language const * to)
3063 {
3064 	LASSERT(from, return);
3065 	LASSERT(to, return);
3066 
3067 	for_each(par_iterator_begin(),
3068 		 par_iterator_end(),
3069 		 bind(&Paragraph::changeLanguage, _1, params(), from, to));
3070 }
3071 
3072 
isMultiLingual() const3073 bool Buffer::isMultiLingual() const
3074 {
3075 	ParConstIterator end = par_iterator_end();
3076 	for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
3077 		if (it->isMultiLingual(params()))
3078 			return true;
3079 
3080 	return false;
3081 }
3082 
3083 
getLanguages() const3084 std::set<Language const *> Buffer::getLanguages() const
3085 {
3086 	std::set<Language const *> languages;
3087 	getLanguages(languages);
3088 	return languages;
3089 }
3090 
3091 
getLanguages(std::set<Language const * > & languages) const3092 void Buffer::getLanguages(std::set<Language const *> & languages) const
3093 {
3094 	ParConstIterator end = par_iterator_end();
3095 	// add the buffer language, even if it's not actively used
3096 	languages.insert(language());
3097 	// iterate over the paragraphs
3098 	for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
3099 		it->getLanguages(languages);
3100 	// also children
3101 	ListOfBuffers clist = getDescendents();
3102 	ListOfBuffers::const_iterator cit = clist.begin();
3103 	ListOfBuffers::const_iterator const cen = clist.end();
3104 	for (; cit != cen; ++cit)
3105 		(*cit)->getLanguages(languages);
3106 }
3107 
3108 
getParFromID(int const id) const3109 DocIterator Buffer::getParFromID(int const id) const
3110 {
3111 	Buffer * buf = const_cast<Buffer *>(this);
3112 	if (id < 0)
3113 		// This means non-existent
3114 		return doc_iterator_end(buf);
3115 
3116 	for (DocIterator it = doc_iterator_begin(buf); !it.atEnd(); it.forwardPar())
3117 		if (it.paragraph().id() == id)
3118 			return it;
3119 
3120 	return doc_iterator_end(buf);
3121 }
3122 
3123 
hasParWithID(int const id) const3124 bool Buffer::hasParWithID(int const id) const
3125 {
3126 	return !getParFromID(id).atEnd();
3127 }
3128 
3129 
par_iterator_begin()3130 ParIterator Buffer::par_iterator_begin()
3131 {
3132 	return ParIterator(doc_iterator_begin(this));
3133 }
3134 
3135 
par_iterator_end()3136 ParIterator Buffer::par_iterator_end()
3137 {
3138 	return ParIterator(doc_iterator_end(this));
3139 }
3140 
3141 
par_iterator_begin() const3142 ParConstIterator Buffer::par_iterator_begin() const
3143 {
3144 	return ParConstIterator(doc_iterator_begin(this));
3145 }
3146 
3147 
par_iterator_end() const3148 ParConstIterator Buffer::par_iterator_end() const
3149 {
3150 	return ParConstIterator(doc_iterator_end(this));
3151 }
3152 
3153 
language() const3154 Language const * Buffer::language() const
3155 {
3156 	return params().language;
3157 }
3158 
3159 
B_(string const & l10n) const3160 docstring const Buffer::B_(string const & l10n) const
3161 {
3162 	return params().B_(l10n);
3163 }
3164 
3165 
isClean() const3166 bool Buffer::isClean() const
3167 {
3168 	return d->lyx_clean;
3169 }
3170 
3171 
isChecksumModified() const3172 bool Buffer::isChecksumModified() const
3173 {
3174 	LASSERT(d->filename.exists(), return false);
3175 	return d->checksum_ != d->filename.checksum();
3176 }
3177 
3178 
saveCheckSum() const3179 void Buffer::saveCheckSum() const
3180 {
3181 	FileName const & file = d->filename;
3182 	file.refresh();
3183 	d->checksum_ = file.exists() ? file.checksum()
3184 		: 0; // in the case of save to a new file.
3185 }
3186 
3187 
markClean() const3188 void Buffer::markClean() const
3189 {
3190 	if (!d->lyx_clean) {
3191 		d->lyx_clean = true;
3192 		updateTitles();
3193 	}
3194 	// if the .lyx file has been saved, we don't need an
3195 	// autosave
3196 	d->bak_clean = true;
3197 	d->undo_.markDirty();
3198 	clearExternalModification();
3199 }
3200 
3201 
setUnnamed(bool flag)3202 void Buffer::setUnnamed(bool flag)
3203 {
3204 	d->unnamed = flag;
3205 }
3206 
3207 
isUnnamed() const3208 bool Buffer::isUnnamed() const
3209 {
3210 	return d->unnamed;
3211 }
3212 
3213 
3214 /// \note
3215 /// Don't check unnamed, here: isInternal() is used in
3216 /// newBuffer(), where the unnamed flag has not been set by anyone
3217 /// yet. Also, for an internal buffer, there should be no need for
3218 /// retrieving fileName() nor for checking if it is unnamed or not.
isInternal() const3219 bool Buffer::isInternal() const
3220 {
3221 	return d->internal_buffer;
3222 }
3223 
3224 
setInternal(bool flag)3225 void Buffer::setInternal(bool flag)
3226 {
3227 	d->internal_buffer = flag;
3228 }
3229 
3230 
markDirty()3231 void Buffer::markDirty()
3232 {
3233 	if (d->lyx_clean) {
3234 		d->lyx_clean = false;
3235 		updateTitles();
3236 	}
3237 	d->bak_clean = false;
3238 
3239 	DepClean::iterator it = d->dep_clean.begin();
3240 	DepClean::const_iterator const end = d->dep_clean.end();
3241 
3242 	for (; it != end; ++it)
3243 		it->second = false;
3244 }
3245 
3246 
fileName() const3247 FileName Buffer::fileName() const
3248 {
3249 	return d->filename;
3250 }
3251 
3252 
absFileName() const3253 string Buffer::absFileName() const
3254 {
3255 	return d->filename.absFileName();
3256 }
3257 
3258 
filePath() const3259 string Buffer::filePath() const
3260 {
3261 	string const abs = d->filename.onlyPath().absFileName();
3262 	if (abs.empty())
3263 		return abs;
3264 	int last = abs.length() - 1;
3265 
3266 	return abs[last] == '/' ? abs : abs + '/';
3267 }
3268 
3269 
getReferencedFileName(string const & fn) const3270 DocFileName Buffer::getReferencedFileName(string const & fn) const
3271 {
3272 	DocFileName result;
3273 	if (FileName::isAbsolute(fn) || !FileName::isAbsolute(params().origin))
3274 		result.set(fn, filePath());
3275 	else {
3276 		// filePath() ends with a path separator
3277 		FileName const test(filePath() + fn);
3278 		if (test.exists())
3279 			result.set(fn, filePath());
3280 		else
3281 			result.set(fn, params().origin);
3282 	}
3283 
3284 	return result;
3285 }
3286 
3287 
prepareFileNameForLaTeX(string const & name,string const & ext,bool nice) const3288 string const Buffer::prepareFileNameForLaTeX(string const & name,
3289 					     string const & ext, bool nice) const
3290 {
3291 	string const fname = makeAbsPath(name, filePath()).absFileName();
3292 	if (FileName::isAbsolute(name) || !FileName(fname + ext).isReadableFile())
3293 		return name;
3294 	if (!nice)
3295 		return fname;
3296 
3297 	// FIXME UNICODE
3298 	return to_utf8(makeRelPath(from_utf8(fname),
3299 		from_utf8(masterBuffer()->filePath())));
3300 }
3301 
3302 
prepareBibFilePaths(OutputParams const & runparams,docstring_list const & bibfilelist,bool const add_extension) const3303 vector<docstring> const Buffer::prepareBibFilePaths(OutputParams const & runparams,
3304 						docstring_list const & bibfilelist,
3305 						bool const add_extension) const
3306 {
3307 	// If we are processing the LaTeX file in a temp directory then
3308 	// copy the .bib databases to this temp directory, mangling their
3309 	// names in the process. Store this mangled name in the list of
3310 	// all databases.
3311 	// (We need to do all this because BibTeX *really*, *really*
3312 	// can't handle "files with spaces" and Windows users tend to
3313 	// use such filenames.)
3314 	// Otherwise, store the (maybe absolute) path to the original,
3315 	// unmangled database name.
3316 
3317 	vector<docstring> res;
3318 
3319 	// determine the export format
3320 	string const tex_format = flavor2format(runparams.flavor);
3321 
3322 	// check for spaces in paths
3323 	bool found_space = false;
3324 
3325 	for (auto const & bit : bibfilelist) {
3326 		string utf8input = to_utf8(bit);
3327 		string database =
3328 			prepareFileNameForLaTeX(utf8input, ".bib", runparams.nice);
3329 		FileName try_in_file =
3330 			makeAbsPath(database + ".bib", filePath());
3331 		bool not_from_texmf = try_in_file.isReadableFile();
3332 		// If the file has not been found, try with the real file name
3333 		// (it might come from a child in a sub-directory)
3334 		if (!not_from_texmf) {
3335 			try_in_file = getBibfilePath(bit);
3336 			if (try_in_file.isReadableFile()) {
3337 				// Check if the file is in texmf
3338 				FileName kpsefile(findtexfile(changeExtension(utf8input, "bib"), "bib", true));
3339 				not_from_texmf = kpsefile.empty()
3340 						|| kpsefile.absFileName() != try_in_file.absFileName();
3341 				if (not_from_texmf)
3342 					// If this exists, make path relative to the master
3343 					// FIXME Unicode
3344 					database = removeExtension(
3345 							prepareFileNameForLaTeX(to_utf8(makeRelPath(from_utf8(try_in_file.absFileName()),
3346 												    from_utf8(filePath()))),
3347 										".bib", runparams.nice));
3348 			}
3349 		}
3350 
3351 		if (!runparams.inComment && !runparams.dryrun && !runparams.nice &&
3352 		    not_from_texmf) {
3353 			// mangledFileName() needs the extension
3354 			DocFileName const in_file = DocFileName(try_in_file);
3355 			database = removeExtension(in_file.mangledFileName());
3356 			FileName const out_file = makeAbsPath(database + ".bib",
3357 					masterBuffer()->temppath());
3358 			bool const success = in_file.copyTo(out_file);
3359 			if (!success) {
3360 				LYXERR0("Failed to copy '" << in_file
3361 				       << "' to '" << out_file << "'");
3362 			}
3363 		} else if (!runparams.inComment && runparams.nice && not_from_texmf) {
3364 			runparams.exportdata->addExternalFile(tex_format, try_in_file, database + ".bib");
3365 			if (!isValidLaTeXFileName(database)) {
3366 				frontend::Alert::warning(_("Invalid filename"),
3367 					 _("The following filename will cause troubles "
3368 					       "when running the exported file through LaTeX: ") +
3369 					     from_utf8(database));
3370 			}
3371 			if (!isValidDVIFileName(database)) {
3372 				frontend::Alert::warning(_("Problematic filename for DVI"),
3373 					 _("The following filename can cause troubles "
3374 					       "when running the exported file through LaTeX "
3375 						   "and opening the resulting DVI: ") +
3376 					     from_utf8(database), true);
3377 			}
3378 		}
3379 
3380 		if (add_extension)
3381 			database += ".bib";
3382 
3383 		// FIXME UNICODE
3384 		docstring const path = from_utf8(latex_path(database));
3385 
3386 		if (contains(path, ' '))
3387 			found_space = true;
3388 
3389 		if (find(res.begin(), res.end(), path) == res.end())
3390 			res.push_back(path);
3391 	}
3392 
3393 	// Check if there are spaces in the path and warn BibTeX users, if so.
3394 	// (biber can cope with such paths)
3395 	if (!prefixIs(runparams.bibtex_command, "biber")) {
3396 		// Post this warning only once.
3397 		static bool warned_about_spaces = false;
3398 		if (!warned_about_spaces &&
3399 		    runparams.nice && found_space) {
3400 			warned_about_spaces = true;
3401 			Alert::warning(_("Export Warning!"),
3402 				       _("There are spaces in the paths to your BibTeX databases.\n"
3403 						      "BibTeX will be unable to find them."));
3404 		}
3405 	}
3406 
3407 	return res;
3408 }
3409 
3410 
3411 
layoutPos() const3412 string Buffer::layoutPos() const
3413 {
3414 	return d->layout_position;
3415 }
3416 
3417 
setLayoutPos(string const & path)3418 void Buffer::setLayoutPos(string const & path)
3419 {
3420 	if (path.empty()) {
3421 		d->layout_position.clear();
3422 		return;
3423 	}
3424 
3425 	LATTEST(FileName::isAbsolute(path));
3426 
3427 	d->layout_position =
3428 		to_utf8(makeRelPath(from_utf8(path), from_utf8(filePath())));
3429 
3430 	if (d->layout_position.empty())
3431 		d->layout_position = ".";
3432 }
3433 
3434 
hasReadonlyFlag() const3435 bool Buffer::hasReadonlyFlag() const
3436 {
3437 	return d->read_only;
3438 }
3439 
3440 
isReadonly() const3441 bool Buffer::isReadonly() const
3442 {
3443 	return hasReadonlyFlag() || notifiesExternalModification();
3444 }
3445 
3446 
setParent(Buffer const * buffer)3447 void Buffer::setParent(Buffer const * buffer)
3448 {
3449 	// Avoids recursive include.
3450 	d->setParent(buffer == this ? 0 : buffer);
3451 	updateMacros();
3452 }
3453 
3454 
parent() const3455 Buffer const * Buffer::parent() const
3456 {
3457 	return d->parent();
3458 }
3459 
3460 
allRelatives() const3461 ListOfBuffers Buffer::allRelatives() const
3462 {
3463 	ListOfBuffers lb = masterBuffer()->getDescendents();
3464 	lb.push_front(const_cast<Buffer *>(masterBuffer()));
3465 	return lb;
3466 }
3467 
3468 
masterBuffer() const3469 Buffer const * Buffer::masterBuffer() const
3470 {
3471 	// FIXME Should be make sure we are not in some kind
3472 	// of recursive include? A -> B -> A will crash this.
3473 	Buffer const * const pbuf = d->parent();
3474 	if (!pbuf)
3475 		return this;
3476 
3477 	return pbuf->masterBuffer();
3478 }
3479 
3480 
isChild(Buffer * child) const3481 bool Buffer::isChild(Buffer * child) const
3482 {
3483 	return d->children_positions.find(child) != d->children_positions.end();
3484 }
3485 
3486 
firstChildPosition(Buffer const * child)3487 DocIterator Buffer::firstChildPosition(Buffer const * child)
3488 {
3489 	Impl::BufferPositionMap::iterator it;
3490 	it = d->children_positions.find(child);
3491 	if (it == d->children_positions.end())
3492 		return DocIterator(this);
3493 	return it->second;
3494 }
3495 
3496 
hasChildren() const3497 bool Buffer::hasChildren() const
3498 {
3499 	return !d->children_positions.empty();
3500 }
3501 
3502 
collectChildren(ListOfBuffers & clist,bool grand_children) const3503 void Buffer::collectChildren(ListOfBuffers & clist, bool grand_children) const
3504 {
3505 	// loop over children
3506 	Impl::BufferPositionMap::iterator it = d->children_positions.begin();
3507 	Impl::BufferPositionMap::iterator end = d->children_positions.end();
3508 	for (; it != end; ++it) {
3509 		Buffer * child = const_cast<Buffer *>(it->first);
3510 		// No duplicates
3511 		ListOfBuffers::const_iterator bit = find(clist.begin(), clist.end(), child);
3512 		if (bit != clist.end())
3513 			continue;
3514 		clist.push_back(child);
3515 		if (grand_children)
3516 			// there might be grandchildren
3517 			child->collectChildren(clist, true);
3518 	}
3519 }
3520 
3521 
getChildren() const3522 ListOfBuffers Buffer::getChildren() const
3523 {
3524 	ListOfBuffers v;
3525 	collectChildren(v, false);
3526 	// Make sure we have not included ourselves.
3527 	ListOfBuffers::iterator bit = find(v.begin(), v.end(), this);
3528 	if (bit != v.end()) {
3529 		LYXERR0("Recursive include detected in `" << fileName() << "'.");
3530 		v.erase(bit);
3531 	}
3532 	return v;
3533 }
3534 
3535 
getDescendents() const3536 ListOfBuffers Buffer::getDescendents() const
3537 {
3538 	ListOfBuffers v;
3539 	collectChildren(v, true);
3540 	// Make sure we have not included ourselves.
3541 	ListOfBuffers::iterator bit = find(v.begin(), v.end(), this);
3542 	if (bit != v.end()) {
3543 		LYXERR0("Recursive include detected in `" << fileName() << "'.");
3544 		v.erase(bit);
3545 	}
3546 	return v;
3547 }
3548 
3549 
3550 template<typename M>
greatest_below(M & m,typename M::key_type const & x)3551 typename M::const_iterator greatest_below(M & m, typename M::key_type const & x)
3552 {
3553 	if (m.empty())
3554 		return m.end();
3555 
3556 	typename M::const_iterator it = m.lower_bound(x);
3557 	if (it == m.begin())
3558 		return m.end();
3559 
3560 	it--;
3561 	return it;
3562 }
3563 
3564 
getBufferMacro(docstring const & name,DocIterator const & pos) const3565 MacroData const * Buffer::Impl::getBufferMacro(docstring const & name,
3566 					 DocIterator const & pos) const
3567 {
3568 	LYXERR(Debug::MACROS, "Searching for " << to_ascii(name) << " at " << pos);
3569 
3570 	// if paragraphs have no macro context set, pos will be empty
3571 	if (pos.empty())
3572 		return 0;
3573 
3574 	// we haven't found anything yet
3575 	DocIterator bestPos = owner_->par_iterator_begin();
3576 	MacroData const * bestData = 0;
3577 
3578 	// find macro definitions for name
3579 	NamePositionScopeMacroMap::const_iterator nameIt = macros.find(name);
3580 	if (nameIt != macros.end()) {
3581 		// find last definition in front of pos or at pos itself
3582 		PositionScopeMacroMap::const_iterator it
3583 			= greatest_below(nameIt->second, pos);
3584 		if (it != nameIt->second.end()) {
3585 			while (true) {
3586 				// scope ends behind pos?
3587 				if (pos < it->second.scope) {
3588 					// Looks good, remember this. If there
3589 					// is no external macro behind this,
3590 					// we found the right one already.
3591 					bestPos = it->first;
3592 					bestData = &it->second.macro;
3593 					break;
3594 				}
3595 
3596 				// try previous macro if there is one
3597 				if (it == nameIt->second.begin())
3598 					break;
3599 				--it;
3600 			}
3601 		}
3602 	}
3603 
3604 	// find macros in included files
3605 	PositionScopeBufferMap::const_iterator it
3606 		= greatest_below(position_to_children, pos);
3607 	if (it == position_to_children.end())
3608 		// no children before
3609 		return bestData;
3610 
3611 	while (true) {
3612 		// do we know something better (i.e. later) already?
3613 		if (it->first < bestPos )
3614 			break;
3615 
3616 		// scope ends behind pos?
3617 		if (pos < it->second.scope
3618 			&& (cloned_buffer_ ||
3619 			    theBufferList().isLoaded(it->second.buffer))) {
3620 			// look for macro in external file
3621 			macro_lock = true;
3622 			MacroData const * data
3623 				= it->second.buffer->getMacro(name, false);
3624 			macro_lock = false;
3625 			if (data) {
3626 				bestPos = it->first;
3627 				bestData = data;
3628 				break;
3629 			}
3630 		}
3631 
3632 		// try previous file if there is one
3633 		if (it == position_to_children.begin())
3634 			break;
3635 		--it;
3636 	}
3637 
3638 	// return the best macro we have found
3639 	return bestData;
3640 }
3641 
3642 
getMacro(docstring const & name,DocIterator const & pos,bool global) const3643 MacroData const * Buffer::getMacro(docstring const & name,
3644 	DocIterator const & pos, bool global) const
3645 {
3646 	if (d->macro_lock)
3647 		return 0;
3648 
3649 	// query buffer macros
3650 	MacroData const * data = d->getBufferMacro(name, pos);
3651 	if (data != 0)
3652 		return data;
3653 
3654 	// If there is a master buffer, query that
3655 	Buffer const * const pbuf = d->parent();
3656 	if (pbuf) {
3657 		d->macro_lock = true;
3658 		MacroData const * macro	= pbuf->getMacro(
3659 			name, *this, false);
3660 		d->macro_lock = false;
3661 		if (macro)
3662 			return macro;
3663 	}
3664 
3665 	if (global) {
3666 		data = MacroTable::globalMacros().get(name);
3667 		if (data != 0)
3668 			return data;
3669 	}
3670 
3671 	return 0;
3672 }
3673 
3674 
getMacro(docstring const & name,bool global) const3675 MacroData const * Buffer::getMacro(docstring const & name, bool global) const
3676 {
3677 	// set scope end behind the last paragraph
3678 	DocIterator scope = par_iterator_begin();
3679 	scope.pit() = scope.lastpit() + 1;
3680 
3681 	return getMacro(name, scope, global);
3682 }
3683 
3684 
getMacro(docstring const & name,Buffer const & child,bool global) const3685 MacroData const * Buffer::getMacro(docstring const & name,
3686 	Buffer const & child, bool global) const
3687 {
3688 	// look where the child buffer is included first
3689 	Impl::BufferPositionMap::iterator it = d->children_positions.find(&child);
3690 	if (it == d->children_positions.end())
3691 		return 0;
3692 
3693 	// check for macros at the inclusion position
3694 	return getMacro(name, it->second, global);
3695 }
3696 
3697 
updateMacros(DocIterator & it,DocIterator & scope)3698 void Buffer::Impl::updateMacros(DocIterator & it, DocIterator & scope)
3699 {
3700 	pit_type const lastpit = it.lastpit();
3701 
3702 	// look for macros in each paragraph
3703 	while (it.pit() <= lastpit) {
3704 		Paragraph & par = it.paragraph();
3705 
3706 		// iterate over the insets of the current paragraph
3707 		InsetList const & insets = par.insetList();
3708 		InsetList::const_iterator iit = insets.begin();
3709 		InsetList::const_iterator end = insets.end();
3710 		for (; iit != end; ++iit) {
3711 			it.pos() = iit->pos;
3712 
3713 			// is it a nested text inset?
3714 			if (iit->inset->asInsetText()) {
3715 				// Inset needs its own scope?
3716 				InsetText const * itext = iit->inset->asInsetText();
3717 				bool newScope = itext->isMacroScope();
3718 
3719 				// scope which ends just behind the inset
3720 				DocIterator insetScope = it;
3721 				++insetScope.pos();
3722 
3723 				// collect macros in inset
3724 				it.push_back(CursorSlice(*iit->inset));
3725 				updateMacros(it, newScope ? insetScope : scope);
3726 				it.pop_back();
3727 				continue;
3728 			}
3729 
3730 			if (iit->inset->asInsetTabular()) {
3731 				CursorSlice slice(*iit->inset);
3732 				size_t const numcells = slice.nargs();
3733 				for (; slice.idx() < numcells; slice.forwardIdx()) {
3734 					it.push_back(slice);
3735 					updateMacros(it, scope);
3736 					it.pop_back();
3737 				}
3738 				continue;
3739 			}
3740 
3741 			// is it an external file?
3742 			if (iit->inset->lyxCode() == INCLUDE_CODE) {
3743 				// get buffer of external file
3744 				InsetInclude const & inset =
3745 					static_cast<InsetInclude const &>(*iit->inset);
3746 				macro_lock = true;
3747 				Buffer * child = inset.getChildBuffer();
3748 				macro_lock = false;
3749 				if (!child)
3750 					continue;
3751 
3752 				// register its position, but only when it is
3753 				// included first in the buffer
3754 				if (children_positions.find(child) ==
3755 					children_positions.end())
3756 						children_positions[child] = it;
3757 
3758 				// register child with its scope
3759 				position_to_children[it] = Impl::ScopeBuffer(scope, child);
3760 				continue;
3761 			}
3762 
3763 			InsetMath * im = iit->inset->asInsetMath();
3764 			if (doing_export && im)  {
3765 				InsetMathHull * hull = im->asHullInset();
3766 				if (hull)
3767 					hull->recordLocation(it);
3768 			}
3769 
3770 			if (iit->inset->lyxCode() != MATHMACRO_CODE)
3771 				continue;
3772 
3773 			// get macro data
3774 			InsetMathMacroTemplate & macroTemplate =
3775 				*iit->inset->asInsetMath()->asMacroTemplate();
3776 			MacroContext mc(owner_, it);
3777 			macroTemplate.updateToContext(mc);
3778 
3779 			// valid?
3780 			bool valid = macroTemplate.validMacro();
3781 			// FIXME: Should be fixNameAndCheckIfValid() in fact,
3782 			// then the BufferView's cursor will be invalid in
3783 			// some cases which leads to crashes.
3784 			if (!valid)
3785 				continue;
3786 
3787 			// register macro
3788 			// FIXME (Abdel), I don't understand why we pass 'it' here
3789 			// instead of 'macroTemplate' defined above... is this correct?
3790 			macros[macroTemplate.name()][it] =
3791 				Impl::ScopeMacro(scope, MacroData(const_cast<Buffer *>(owner_), it));
3792 		}
3793 
3794 		// next paragraph
3795 		it.pit()++;
3796 		it.pos() = 0;
3797 	}
3798 }
3799 
3800 
updateMacros() const3801 void Buffer::updateMacros() const
3802 {
3803 	if (d->macro_lock)
3804 		return;
3805 
3806 	LYXERR(Debug::MACROS, "updateMacro of " << d->filename.onlyFileName());
3807 
3808 	// start with empty table
3809 	d->macros.clear();
3810 	d->children_positions.clear();
3811 	d->position_to_children.clear();
3812 
3813 	// Iterate over buffer, starting with first paragraph
3814 	// The scope must be bigger than any lookup DocIterator
3815 	// later. For the global lookup, lastpit+1 is used, hence
3816 	// we use lastpit+2 here.
3817 	DocIterator it = par_iterator_begin();
3818 	DocIterator outerScope = it;
3819 	outerScope.pit() = outerScope.lastpit() + 2;
3820 	d->updateMacros(it, outerScope);
3821 }
3822 
3823 
getUsedBranches(std::list<docstring> & result,bool const from_master) const3824 void Buffer::getUsedBranches(std::list<docstring> & result, bool const from_master) const
3825 {
3826 	InsetIterator it  = inset_iterator_begin(inset());
3827 	InsetIterator const end = inset_iterator_end(inset());
3828 	for (; it != end; ++it) {
3829 		if (it->lyxCode() == BRANCH_CODE) {
3830 			InsetBranch & br = static_cast<InsetBranch &>(*it);
3831 			docstring const name = br.branch();
3832 			if (!from_master && !params().branchlist().find(name))
3833 				result.push_back(name);
3834 			else if (from_master && !masterBuffer()->params().branchlist().find(name))
3835 				result.push_back(name);
3836 			continue;
3837 		}
3838 		if (it->lyxCode() == INCLUDE_CODE) {
3839 			// get buffer of external file
3840 			InsetInclude const & ins =
3841 				static_cast<InsetInclude const &>(*it);
3842 			Buffer * child = ins.getChildBuffer();
3843 			if (!child)
3844 				continue;
3845 			child->getUsedBranches(result, true);
3846 		}
3847 	}
3848 	// remove duplicates
3849 	result.unique();
3850 }
3851 
3852 
updateMacroInstances(UpdateType utype) const3853 void Buffer::updateMacroInstances(UpdateType utype) const
3854 {
3855 	LYXERR(Debug::MACROS, "updateMacroInstances for "
3856 		<< d->filename.onlyFileName());
3857 	DocIterator it = doc_iterator_begin(this);
3858 	it.forwardInset();
3859 	DocIterator const end = doc_iterator_end(this);
3860 	for (; it != end; it.forwardInset()) {
3861  		// look for MathData cells in InsetMathNest insets
3862 		InsetMath * minset = it.nextInset()->asInsetMath();
3863 		if (!minset)
3864 			continue;
3865 
3866 		// update macro in all cells of the InsetMathNest
3867 		DocIterator::idx_type n = minset->nargs();
3868 		MacroContext mc = MacroContext(this, it);
3869 		for (DocIterator::idx_type i = 0; i < n; ++i) {
3870 			MathData & data = minset->cell(i);
3871 			data.updateMacros(0, mc, utype, 0);
3872 		}
3873 	}
3874 }
3875 
3876 
listMacroNames(MacroNameSet & macros) const3877 void Buffer::listMacroNames(MacroNameSet & macros) const
3878 {
3879 	if (d->macro_lock)
3880 		return;
3881 
3882 	d->macro_lock = true;
3883 
3884 	// loop over macro names
3885 	Impl::NamePositionScopeMacroMap::iterator nameIt = d->macros.begin();
3886 	Impl::NamePositionScopeMacroMap::iterator nameEnd = d->macros.end();
3887 	for (; nameIt != nameEnd; ++nameIt)
3888 		macros.insert(nameIt->first);
3889 
3890 	// loop over children
3891 	Impl::BufferPositionMap::iterator it = d->children_positions.begin();
3892 	Impl::BufferPositionMap::iterator end = d->children_positions.end();
3893 	for (; it != end; ++it) {
3894 		Buffer * child = const_cast<Buffer *>(it->first);
3895 		// The buffer might have been closed (see #10766).
3896 		if (theBufferList().isLoaded(child))
3897 			child->listMacroNames(macros);
3898 	}
3899 
3900 	// call parent
3901 	Buffer const * const pbuf = d->parent();
3902 	if (pbuf)
3903 		pbuf->listMacroNames(macros);
3904 
3905 	d->macro_lock = false;
3906 }
3907 
3908 
listParentMacros(MacroSet & macros,LaTeXFeatures & features) const3909 void Buffer::listParentMacros(MacroSet & macros, LaTeXFeatures & features) const
3910 {
3911 	Buffer const * const pbuf = d->parent();
3912 	if (!pbuf)
3913 		return;
3914 
3915 	MacroNameSet names;
3916 	pbuf->listMacroNames(names);
3917 
3918 	// resolve macros
3919 	MacroNameSet::iterator it = names.begin();
3920 	MacroNameSet::iterator end = names.end();
3921 	for (; it != end; ++it) {
3922 		// defined?
3923 		MacroData const * data =
3924 		pbuf->getMacro(*it, *this, false);
3925 		if (data) {
3926 			macros.insert(data);
3927 
3928 			// we cannot access the original InsetMathMacroTemplate anymore
3929 			// here to calls validate method. So we do its work here manually.
3930 			// FIXME: somehow make the template accessible here.
3931 			if (data->optionals() > 0)
3932 				features.require("xargs");
3933 		}
3934 	}
3935 }
3936 
3937 
getReferenceCache(docstring const & label)3938 Buffer::References & Buffer::getReferenceCache(docstring const & label)
3939 {
3940 	if (d->parent())
3941 		return const_cast<Buffer *>(masterBuffer())->getReferenceCache(label);
3942 
3943 	RefCache::iterator it = d->ref_cache_.find(label);
3944 	if (it != d->ref_cache_.end())
3945 		return it->second;
3946 
3947 	static References const dummy_refs = References();
3948 	it = d->ref_cache_.insert(
3949 		make_pair(label, dummy_refs)).first;
3950 	return it->second;
3951 }
3952 
3953 
references(docstring const & label) const3954 Buffer::References const & Buffer::references(docstring const & label) const
3955 {
3956 	return const_cast<Buffer *>(this)->getReferenceCache(label);
3957 }
3958 
3959 
addReference(docstring const & label,Inset * inset,ParIterator it)3960 void Buffer::addReference(docstring const & label, Inset * inset, ParIterator it)
3961 {
3962 	References & refs = getReferenceCache(label);
3963 	refs.push_back(make_pair(inset, it));
3964 }
3965 
3966 
setInsetLabel(docstring const & label,InsetLabel const * il,bool const active)3967 void Buffer::setInsetLabel(docstring const & label, InsetLabel const * il,
3968 			   bool const active)
3969 {
3970 	LabelInfo linfo;
3971 	linfo.label = label;
3972 	linfo.inset = il;
3973 	linfo.active = active;
3974 	masterBuffer()->d->label_cache_.push_back(linfo);
3975 }
3976 
3977 
insetLabel(docstring const & label,bool const active) const3978 InsetLabel const * Buffer::insetLabel(docstring const & label,
3979 				      bool const active) const
3980 {
3981 	for (auto & rc : masterBuffer()->d->label_cache_) {
3982 		if (rc.label == label && (rc.active || !active))
3983 			return rc.inset;
3984 	}
3985 	return nullptr;
3986 }
3987 
3988 
activeLabel(docstring const & label) const3989 bool Buffer::activeLabel(docstring const & label) const
3990 {
3991 	if (!insetLabel(label, true))
3992 		return false;
3993 
3994 	return true;
3995 }
3996 
3997 
clearReferenceCache() const3998 void Buffer::clearReferenceCache() const
3999 {
4000 	if (!d->parent()) {
4001 		d->ref_cache_.clear();
4002 		d->label_cache_.clear();
4003 	}
4004 }
4005 
4006 
changeRefsIfUnique(docstring const & from,docstring const & to)4007 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to)
4008 {
4009 	//FIXME: This does not work for child documents yet.
4010 	reloadBibInfoCache();
4011 
4012 	// Check if the label 'from' appears more than once
4013 	BiblioInfo const & keys = masterBibInfo();
4014 	BiblioInfo::const_iterator bit  = keys.begin();
4015 	BiblioInfo::const_iterator bend = keys.end();
4016 	vector<docstring> labels;
4017 
4018 	for (; bit != bend; ++bit)
4019 		// FIXME UNICODE
4020 		labels.push_back(bit->first);
4021 
4022 	if (count(labels.begin(), labels.end(), from) > 1)
4023 		return;
4024 
4025 	string const paramName = "key";
4026 	for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
4027 		if (it->lyxCode() != CITE_CODE)
4028 			continue;
4029 		InsetCommand * inset = it->asInsetCommand();
4030 		docstring const oldValue = inset->getParam(paramName);
4031 		if (oldValue == from)
4032 			inset->setParam(paramName, to);
4033 	}
4034 }
4035 
4036 // returns NULL if id-to-row conversion is unsupported
getSourceCode(odocstream & os,string const & format,pit_type par_begin,pit_type par_end,OutputWhat output,bool master) const4037 unique_ptr<TexRow> Buffer::getSourceCode(odocstream & os, string const & format,
4038                                          pit_type par_begin, pit_type par_end,
4039                                          OutputWhat output, bool master) const
4040 {
4041 	unique_ptr<TexRow> texrow;
4042 	OutputParams runparams(&params().encoding());
4043 	runparams.nice = true;
4044 	runparams.flavor = params().getOutputFlavor(format);
4045 	runparams.linelen = lyxrc.plaintext_linelen;
4046 	// No side effect of file copying and image conversion
4047 	runparams.dryrun = true;
4048 
4049 	if (output == CurrentParagraph) {
4050 		runparams.par_begin = par_begin;
4051 		runparams.par_end = par_end;
4052 		if (par_begin + 1 == par_end) {
4053 			os << "% "
4054 			   << bformat(_("Preview source code for paragraph %1$d"), par_begin)
4055 			   << "\n\n";
4056 		} else {
4057 			os << "% "
4058 			   << bformat(_("Preview source code from paragraph %1$s to %2$s"),
4059 					convert<docstring>(par_begin),
4060 					convert<docstring>(par_end - 1))
4061 			   << "\n\n";
4062 		}
4063 		// output paragraphs
4064 		if (runparams.flavor == OutputParams::LYX) {
4065 			Paragraph const & par = text().paragraphs()[par_begin];
4066 			ostringstream ods;
4067 			depth_type dt = par.getDepth();
4068 			par.write(ods, params(), dt);
4069 			os << from_utf8(ods.str());
4070 		} else if (runparams.flavor == OutputParams::HTML) {
4071 			XHTMLStream xs(os);
4072 			setMathFlavor(runparams);
4073 			xhtmlParagraphs(text(), *this, xs, runparams);
4074 		} else if (runparams.flavor == OutputParams::TEXT) {
4075 			bool dummy = false;
4076 			// FIXME Handles only one paragraph, unlike the others.
4077 			// Probably should have some routine with a signature like them.
4078 			writePlaintextParagraph(*this,
4079 				text().paragraphs()[par_begin], os, runparams, dummy);
4080 		} else if (params().isDocBook()) {
4081 			docbookParagraphs(text(), *this, os, runparams);
4082 		} else {
4083 			// If we are previewing a paragraph, even if this is the
4084 			// child of some other buffer, let's cut the link here,
4085 			// so that no concurring settings from the master
4086 			// (e.g. branch state) interfere (see #8101).
4087 			if (!master)
4088 				d->ignore_parent = true;
4089 			// We need to validate the Buffer params' features here
4090 			// in order to know if we should output polyglossia
4091 			// macros (instead of babel macros)
4092 			LaTeXFeatures features(*this, params(), runparams);
4093 			validate(features);
4094 			runparams.use_polyglossia = features.usePolyglossia();
4095 			// latex or literate
4096 			otexstream ots(os);
4097 			// output above
4098 			ots.texrow().newlines(2);
4099 			// the real stuff
4100 			latexParagraphs(*this, text(), ots, runparams);
4101 			texrow = ots.releaseTexRow();
4102 
4103 			// Restore the parenthood
4104 			if (!master)
4105 				d->ignore_parent = false;
4106 		}
4107 	} else {
4108 		os << "% ";
4109 		if (output == FullSource)
4110 			os << _("Preview source code");
4111 		else if (output == OnlyPreamble)
4112 			os << _("Preview preamble");
4113 		else if (output == OnlyBody)
4114 			os << _("Preview body");
4115 		os << "\n\n";
4116 		if (runparams.flavor == OutputParams::LYX) {
4117 			ostringstream ods;
4118 			if (output == FullSource)
4119 				write(ods);
4120 			else if (output == OnlyPreamble)
4121 				params().writeFile(ods, this);
4122 			else if (output == OnlyBody)
4123 				text().write(ods);
4124 			os << from_utf8(ods.str());
4125 		} else if (runparams.flavor == OutputParams::HTML) {
4126 			writeLyXHTMLSource(os, runparams, output);
4127 		} else if (runparams.flavor == OutputParams::TEXT) {
4128 			if (output == OnlyPreamble) {
4129 				os << "% "<< _("Plain text does not have a preamble.");
4130 			} else
4131 				writePlaintextFile(*this, os, runparams);
4132 		} else if (params().isDocBook()) {
4133 				writeDocBookSource(os, absFileName(), runparams, output);
4134 		} else {
4135 			// latex or literate
4136 			otexstream ots(os);
4137 			// output above
4138 			ots.texrow().newlines(2);
4139 			if (master)
4140 				runparams.is_child = true;
4141 			updateBuffer();
4142 			writeLaTeXSource(ots, string(), runparams, output);
4143 			texrow = ots.releaseTexRow();
4144 		}
4145 	}
4146 	return texrow;
4147 }
4148 
4149 
errorList(string const & type) const4150 ErrorList & Buffer::errorList(string const & type) const
4151 {
4152 	return d->errorLists[type];
4153 }
4154 
4155 
updateTocItem(std::string const & type,DocIterator const & dit) const4156 void Buffer::updateTocItem(std::string const & type,
4157 	DocIterator const & dit) const
4158 {
4159 	if (d->gui_)
4160 		d->gui_->updateTocItem(type, dit);
4161 }
4162 
4163 
structureChanged() const4164 void Buffer::structureChanged() const
4165 {
4166 	if (d->gui_)
4167 		d->gui_->structureChanged();
4168 }
4169 
4170 
errors(string const & err,bool from_master) const4171 void Buffer::errors(string const & err, bool from_master) const
4172 {
4173 	if (d->gui_)
4174 		d->gui_->errors(err, from_master);
4175 }
4176 
4177 
message(docstring const & msg) const4178 void Buffer::message(docstring const & msg) const
4179 {
4180 	if (d->gui_)
4181 		d->gui_->message(msg);
4182 }
4183 
4184 
setBusy(bool on) const4185 void Buffer::setBusy(bool on) const
4186 {
4187 	if (d->gui_)
4188 		d->gui_->setBusy(on);
4189 }
4190 
4191 
updateTitles() const4192 void Buffer::updateTitles() const
4193 {
4194 	if (d->wa_)
4195 		d->wa_->updateTitles();
4196 }
4197 
4198 
resetAutosaveTimers() const4199 void Buffer::resetAutosaveTimers() const
4200 {
4201 	if (d->gui_)
4202 		d->gui_->resetAutosaveTimers();
4203 }
4204 
4205 
hasGuiDelegate() const4206 bool Buffer::hasGuiDelegate() const
4207 {
4208 	return d->gui_;
4209 }
4210 
4211 
setGuiDelegate(frontend::GuiBufferDelegate * gui)4212 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
4213 {
4214 	d->gui_ = gui;
4215 }
4216 
4217 
4218 
4219 namespace {
4220 
4221 class AutoSaveBuffer : public ForkedProcess {
4222 public:
4223 	///
AutoSaveBuffer(Buffer const & buffer,FileName const & fname)4224 	AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
4225 		: buffer_(buffer), fname_(fname) {}
4226 	///
clone() const4227 	virtual shared_ptr<ForkedProcess> clone() const
4228 	{
4229 		return make_shared<AutoSaveBuffer>(*this);
4230 	}
4231 	///
start()4232 	int start()
4233 	{
4234 		command_ = to_utf8(bformat(_("Auto-saving %1$s"),
4235 						 from_utf8(fname_.absFileName())));
4236 		return run(DontWait);
4237 	}
4238 private:
4239 	///
4240 	virtual int generateChild();
4241 	///
4242 	Buffer const & buffer_;
4243 	FileName fname_;
4244 };
4245 
4246 
generateChild()4247 int AutoSaveBuffer::generateChild()
4248 {
4249 #if defined(__APPLE__)
4250 	/* FIXME fork() is not usable for autosave on Mac OS X 10.6 (snow leopard)
4251 	 *   We should use something else like threads.
4252 	 *
4253 	 * Since I do not know how to determine at run time what is the OS X
4254 	 * version, I just disable forking altogether for now (JMarc)
4255 	 */
4256 	pid_t const pid = -1;
4257 #else
4258 	// tmp_ret will be located (usually) in /tmp
4259 	// will that be a problem?
4260 	// Note that this calls ForkedCalls::fork(), so it's
4261 	// ok cross-platform.
4262 	pid_t const pid = fork();
4263 	// If you want to debug the autosave
4264 	// you should set pid to -1, and comment out the fork.
4265 	if (pid != 0 && pid != -1)
4266 		return pid;
4267 #endif
4268 
4269 	// pid = -1 signifies that lyx was unable
4270 	// to fork. But we will do the save
4271 	// anyway.
4272 	bool failed = false;
4273 	TempFile tempfile("lyxautoXXXXXX.lyx");
4274 	tempfile.setAutoRemove(false);
4275 	FileName const tmp_ret = tempfile.name();
4276 	if (!tmp_ret.empty()) {
4277 		if (!buffer_.writeFile(tmp_ret))
4278 			failed = true;
4279 		else if (!tmp_ret.moveTo(fname_))
4280 			failed = true;
4281 	} else
4282 		failed = true;
4283 
4284 	if (failed) {
4285 		// failed to write/rename tmp_ret so try writing direct
4286 		if (!buffer_.writeFile(fname_)) {
4287 			// It is dangerous to do this in the child,
4288 			// but safe in the parent, so...
4289 			if (pid == -1) // emit message signal.
4290 				buffer_.message(_("Autosave failed!"));
4291 		}
4292 	}
4293 
4294 	if (pid == 0) // we are the child so...
4295 		_exit(0);
4296 
4297 	return pid;
4298 }
4299 
4300 } // namespace
4301 
4302 
getEmergencyFileName() const4303 FileName Buffer::getEmergencyFileName() const
4304 {
4305 	return FileName(d->filename.absFileName() + ".emergency");
4306 }
4307 
4308 
getAutosaveFileName() const4309 FileName Buffer::getAutosaveFileName() const
4310 {
4311 	// if the document is unnamed try to save in the backup dir, else
4312 	// in the default document path, and as a last try in the filePath,
4313 	// which will most often be the temporary directory
4314 	string fpath;
4315 	if (isUnnamed())
4316 		fpath = lyxrc.backupdir_path.empty() ? lyxrc.document_path
4317 			: lyxrc.backupdir_path;
4318 	if (!isUnnamed() || fpath.empty() || !FileName(fpath).exists())
4319 		fpath = filePath();
4320 
4321 	string const fname = "#" + d->filename.onlyFileName() + "#";
4322 
4323 	return makeAbsPath(fname, fpath);
4324 }
4325 
4326 
removeAutosaveFile() const4327 void Buffer::removeAutosaveFile() const
4328 {
4329 	FileName const f = getAutosaveFileName();
4330 	if (f.exists())
4331 		f.removeFile();
4332 }
4333 
4334 
moveAutosaveFile(support::FileName const & oldauto) const4335 void Buffer::moveAutosaveFile(support::FileName const & oldauto) const
4336 {
4337 	FileName const newauto = getAutosaveFileName();
4338 	oldauto.refresh();
4339 	if (newauto != oldauto && oldauto.exists())
4340 		if (!oldauto.moveTo(newauto))
4341 			LYXERR0("Unable to move autosave file `" << oldauto << "'!");
4342 }
4343 
4344 
autoSave() const4345 bool Buffer::autoSave() const
4346 {
4347 	Buffer const * buf = d->cloned_buffer_ ? d->cloned_buffer_ : this;
4348 	if (buf->d->bak_clean || hasReadonlyFlag())
4349 		return true;
4350 
4351 	message(_("Autosaving current document..."));
4352 	buf->d->bak_clean = true;
4353 
4354 	FileName const fname = getAutosaveFileName();
4355 	LASSERT(d->cloned_buffer_, return false);
4356 
4357 	// If this buffer is cloned, we assume that
4358 	// we are running in a separate thread already.
4359 	TempFile tempfile("lyxautoXXXXXX.lyx");
4360 	tempfile.setAutoRemove(false);
4361 	FileName const tmp_ret = tempfile.name();
4362 	if (!tmp_ret.empty()) {
4363 		writeFile(tmp_ret);
4364 		// assume successful write of tmp_ret
4365 		if (tmp_ret.moveTo(fname))
4366 			return true;
4367 	}
4368 	// failed to write/rename tmp_ret so try writing direct
4369 	return writeFile(fname);
4370 }
4371 
4372 
setExportStatus(bool e) const4373 void Buffer::setExportStatus(bool e) const
4374 {
4375 	d->doing_export = e;
4376 	ListOfBuffers clist = getDescendents();
4377 	ListOfBuffers::const_iterator cit = clist.begin();
4378 	ListOfBuffers::const_iterator const cen = clist.end();
4379 	for (; cit != cen; ++cit)
4380 		(*cit)->d->doing_export = e;
4381 }
4382 
4383 
isExporting() const4384 bool Buffer::isExporting() const
4385 {
4386 	return d->doing_export;
4387 }
4388 
4389 
doExport(string const & target,bool put_in_tempdir) const4390 Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir)
4391 	const
4392 {
4393 	string result_file;
4394 	return doExport(target, put_in_tempdir, result_file);
4395 }
4396 
doExport(string const & target,bool put_in_tempdir,string & result_file) const4397 Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir,
4398 	string & result_file) const
4399 {
4400 	bool const update_unincluded =
4401 			params().maintain_unincluded_children
4402 			&& !params().getIncludedChildren().empty();
4403 
4404 	// (1) export with all included children (omit \includeonly)
4405 	if (update_unincluded) {
4406 		ExportStatus const status =
4407 			doExport(target, put_in_tempdir, true, result_file);
4408 		if (status != ExportSuccess)
4409 			return status;
4410 	}
4411 	// (2) export with included children only
4412 	return doExport(target, put_in_tempdir, false, result_file);
4413 }
4414 
4415 
setMathFlavor(OutputParams & op) const4416 void Buffer::setMathFlavor(OutputParams & op) const
4417 {
4418 	switch (params().html_math_output) {
4419 	case BufferParams::MathML:
4420 		op.math_flavor = OutputParams::MathAsMathML;
4421 		break;
4422 	case BufferParams::HTML:
4423 		op.math_flavor = OutputParams::MathAsHTML;
4424 		break;
4425 	case BufferParams::Images:
4426 		op.math_flavor = OutputParams::MathAsImages;
4427 		break;
4428 	case BufferParams::LaTeX:
4429 		op.math_flavor = OutputParams::MathAsLaTeX;
4430 		break;
4431 	}
4432 }
4433 
4434 
doExport(string const & target,bool put_in_tempdir,bool includeall,string & result_file) const4435 Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir,
4436 	bool includeall, string & result_file) const
4437 {
4438 	LYXERR(Debug::FILES, "target=" << target);
4439 	OutputParams runparams(&params().encoding());
4440 	string format = target;
4441 	string dest_filename;
4442 	size_t pos = target.find(' ');
4443 	if (pos != string::npos) {
4444 		dest_filename = target.substr(pos + 1, target.length() - pos - 1);
4445 		format = target.substr(0, pos);
4446 		if (format == "default")
4447 			format = params().getDefaultOutputFormat();
4448 		runparams.export_folder = FileName(dest_filename).onlyPath().realPath();
4449 		FileName(dest_filename).onlyPath().createPath();
4450 		LYXERR(Debug::FILES, "format=" << format << ", dest_filename=" << dest_filename << ", export_folder=" << runparams.export_folder);
4451 	}
4452 	MarkAsExporting exporting(this);
4453 	string backend_format;
4454 	runparams.flavor = OutputParams::LATEX;
4455 	runparams.linelen = lyxrc.plaintext_linelen;
4456 	runparams.includeall = includeall;
4457 	vector<string> backs = params().backends();
4458 	Converters converters = theConverters();
4459 	bool need_nice_file = false;
4460 	if (find(backs.begin(), backs.end(), format) == backs.end()) {
4461 		// Get shortest path to format
4462 		converters.buildGraph();
4463 		Graph::EdgePath path;
4464 		for (vector<string>::const_iterator it = backs.begin();
4465 		     it != backs.end(); ++it) {
4466 			Graph::EdgePath p = converters.getPath(*it, format);
4467 			if (!p.empty() && (path.empty() || p.size() < path.size())) {
4468 				backend_format = *it;
4469 				path = p;
4470 			}
4471 		}
4472 		if (path.empty()) {
4473 			if (!put_in_tempdir) {
4474 				// Only show this alert if this is an export to a non-temporary
4475 				// file (not for previewing).
4476 				Alert::error(_("Couldn't export file"), bformat(
4477 					_("No information for exporting the format %1$s."),
4478 					theFormats().prettyName(format)));
4479 			}
4480 			return ExportNoPathToFormat;
4481 		}
4482 		runparams.flavor = converters.getFlavor(path, this);
4483 		runparams.hyperref_driver = converters.getHyperrefDriver(path);
4484 		Graph::EdgePath::const_iterator it = path.begin();
4485 		Graph::EdgePath::const_iterator en = path.end();
4486 		for (; it != en; ++it)
4487 			if (theConverters().get(*it).nice()) {
4488 				need_nice_file = true;
4489 				break;
4490 			}
4491 
4492 	} else {
4493 		backend_format = format;
4494 		LYXERR(Debug::FILES, "backend_format=" << backend_format);
4495 		// FIXME: Don't hardcode format names here, but use a flag
4496 		if (backend_format == "pdflatex")
4497 			runparams.flavor = OutputParams::PDFLATEX;
4498 		else if (backend_format == "luatex")
4499 			runparams.flavor = OutputParams::LUATEX;
4500 		else if (backend_format == "dviluatex")
4501 			runparams.flavor = OutputParams::DVILUATEX;
4502 		else if (backend_format == "xetex")
4503 			runparams.flavor = OutputParams::XETEX;
4504 	}
4505 
4506 	string filename = latexName(false);
4507 	filename = addName(temppath(), filename);
4508 	filename = changeExtension(filename,
4509 				   theFormats().extension(backend_format));
4510 	LYXERR(Debug::FILES, "filename=" << filename);
4511 
4512 	// Plain text backend
4513 	if (backend_format == "text") {
4514 		runparams.flavor = OutputParams::TEXT;
4515 		writePlaintextFile(*this, FileName(filename), runparams);
4516 	}
4517 	// HTML backend
4518 	else if (backend_format == "xhtml") {
4519 		runparams.flavor = OutputParams::HTML;
4520 		setMathFlavor(runparams);
4521 		makeLyXHTMLFile(FileName(filename), runparams);
4522 	} else if (backend_format == "lyx")
4523 		writeFile(FileName(filename));
4524 	// Docbook backend
4525 	else if (params().isDocBook()) {
4526 		runparams.nice = !put_in_tempdir;
4527 		makeDocBookFile(FileName(filename), runparams);
4528 	}
4529 	// LaTeX backend
4530 	else if (backend_format == format || need_nice_file) {
4531 		runparams.nice = true;
4532 		bool const success = makeLaTeXFile(FileName(filename), string(), runparams);
4533 		if (d->cloned_buffer_)
4534 			d->cloned_buffer_->d->errorLists["Export"] = d->errorLists["Export"];
4535 		if (!success)
4536 			return ExportError;
4537 	} else if (!lyxrc.tex_allows_spaces
4538 		   && contains(filePath(), ' ')) {
4539 		Alert::error(_("File name error"),
4540 			bformat(_("The directory path to the document\n%1$s\n"
4541 			    "contains spaces, but your TeX installation does "
4542 			    "not allow them. You should save the file to a directory "
4543 					"whose name does not contain spaces."), from_utf8(filePath())));
4544 		return ExportTexPathHasSpaces;
4545 	} else {
4546 		runparams.nice = false;
4547 		bool const success = makeLaTeXFile(
4548 			FileName(filename), filePath(), runparams);
4549 		if (d->cloned_buffer_)
4550 			d->cloned_buffer_->d->errorLists["Export"] = d->errorLists["Export"];
4551 		if (!success)
4552 			return ExportError;
4553 	}
4554 
4555 	string const error_type = (format == "program")
4556 		? "Build" : params().bufferFormat();
4557 	ErrorList & error_list = d->errorLists[error_type];
4558 	string const ext = theFormats().extension(format);
4559 	FileName const tmp_result_file(changeExtension(filename, ext));
4560 	bool const success = converters.convert(this, FileName(filename),
4561 		tmp_result_file, FileName(absFileName()), backend_format, format,
4562 		error_list);
4563 
4564 	// Emit the signal to show the error list or copy it back to the
4565 	// cloned Buffer so that it can be emitted afterwards.
4566 	if (format != backend_format) {
4567 		if (runparams.silent)
4568 			error_list.clear();
4569 		else if (d->cloned_buffer_)
4570 			d->cloned_buffer_->d->errorLists[error_type] =
4571 				d->errorLists[error_type];
4572 		else
4573 			errors(error_type);
4574 		// also to the children, in case of master-buffer-view
4575 		ListOfBuffers clist = getDescendents();
4576 		ListOfBuffers::const_iterator cit = clist.begin();
4577 		ListOfBuffers::const_iterator const cen = clist.end();
4578 		for (; cit != cen; ++cit) {
4579 			if (runparams.silent)
4580 				(*cit)->d->errorLists[error_type].clear();
4581 			else if (d->cloned_buffer_) {
4582 				// Enable reverse search by copying back the
4583 				// texrow object to the cloned buffer.
4584 				// FIXME: this is not thread safe.
4585 				(*cit)->d->cloned_buffer_->d->texrow = (*cit)->d->texrow;
4586 				(*cit)->d->cloned_buffer_->d->errorLists[error_type] =
4587 					(*cit)->d->errorLists[error_type];
4588 			} else
4589 				(*cit)->errors(error_type, true);
4590 		}
4591 	}
4592 
4593 	if (d->cloned_buffer_) {
4594 		// Enable reverse dvi or pdf to work by copying back the texrow
4595 		// object to the cloned buffer.
4596 		// FIXME: There is a possibility of concurrent access to texrow
4597 		// here from the main GUI thread that should be securized.
4598 		d->cloned_buffer_->d->texrow = d->texrow;
4599 		string const error_type = params().bufferFormat();
4600 		d->cloned_buffer_->d->errorLists[error_type] = d->errorLists[error_type];
4601 	}
4602 
4603 
4604 	if (put_in_tempdir) {
4605 		result_file = tmp_result_file.absFileName();
4606 		return success ? ExportSuccess : ExportConverterError;
4607 	}
4608 
4609 	if (dest_filename.empty())
4610 		result_file = changeExtension(d->exportFileName().absFileName(), ext);
4611 	else
4612 		result_file = dest_filename;
4613 	// We need to copy referenced files (e. g. included graphics
4614 	// if format == "dvi") to the result dir.
4615 	vector<ExportedFile> const files =
4616 		runparams.exportdata->externalFiles(format);
4617 	string const dest = runparams.export_folder.empty() ?
4618 		onlyPath(result_file) : runparams.export_folder;
4619 	bool use_force = use_gui ? lyxrc.export_overwrite == ALL_FILES
4620 				 : force_overwrite == ALL_FILES;
4621 	CopyStatus status = use_force ? FORCE : SUCCESS;
4622 
4623 	vector<ExportedFile>::const_iterator it = files.begin();
4624 	vector<ExportedFile>::const_iterator const en = files.end();
4625 	for (; it != en && status != CANCEL; ++it) {
4626 		string const fmt = theFormats().getFormatFromFile(it->sourceName);
4627 		string fixedName = it->exportName;
4628 		if (!runparams.export_folder.empty()) {
4629 			// Relative pathnames starting with ../ will be sanitized
4630 			// if exporting to a different folder
4631 			while (fixedName.substr(0, 3) == "../")
4632 				fixedName = fixedName.substr(3, fixedName.length() - 3);
4633 		}
4634 		FileName fixedFileName = makeAbsPath(fixedName, dest);
4635 		fixedFileName.onlyPath().createPath();
4636 		status = copyFile(fmt, it->sourceName,
4637 			fixedFileName,
4638 			it->exportName, status == FORCE,
4639 			runparams.export_folder.empty());
4640 	}
4641 
4642 	if (status == CANCEL) {
4643 		message(_("Document export cancelled."));
4644 		return ExportCancel;
4645 	}
4646 
4647 	if (tmp_result_file.exists()) {
4648 		// Finally copy the main file
4649 		use_force = use_gui ? lyxrc.export_overwrite != NO_FILES
4650 				    : force_overwrite != NO_FILES;
4651 		if (status == SUCCESS && use_force)
4652 			status = FORCE;
4653 		status = copyFile(format, tmp_result_file,
4654 			FileName(result_file), result_file,
4655 			status == FORCE);
4656 		if (status == CANCEL) {
4657 			message(_("Document export cancelled."));
4658 			return ExportCancel;
4659 		} else {
4660 			message(bformat(_("Document exported as %1$s "
4661 				"to file `%2$s'"),
4662 				theFormats().prettyName(format),
4663 				makeDisplayPath(result_file)));
4664 		}
4665 	} else {
4666 		// This must be a dummy converter like fax (bug 1888)
4667 		message(bformat(_("Document exported as %1$s"),
4668 			theFormats().prettyName(format)));
4669 	}
4670 
4671 	return success ? ExportSuccess : ExportConverterError;
4672 }
4673 
4674 
preview(string const & format) const4675 Buffer::ExportStatus Buffer::preview(string const & format) const
4676 {
4677 	bool const update_unincluded =
4678 			params().maintain_unincluded_children
4679 			&& !params().getIncludedChildren().empty();
4680 	return preview(format, update_unincluded);
4681 }
4682 
4683 
preview(string const & format,bool includeall) const4684 Buffer::ExportStatus Buffer::preview(string const & format, bool includeall) const
4685 {
4686 	MarkAsExporting exporting(this);
4687 	string result_file;
4688 	// (1) export with all included children (omit \includeonly)
4689 	if (includeall) {
4690 		ExportStatus const status = doExport(format, true, true, result_file);
4691 		if (status != ExportSuccess)
4692 			return status;
4693 	}
4694 	// (2) export with included children only
4695 	ExportStatus const status = doExport(format, true, false, result_file);
4696 	FileName const previewFile(result_file);
4697 
4698 	Impl * theimpl = isClone() ? d->cloned_buffer_->d : d;
4699 	theimpl->preview_file_ = previewFile;
4700 	theimpl->preview_format_ = format;
4701 	theimpl->preview_error_ = (status != ExportSuccess);
4702 
4703 	if (status != ExportSuccess)
4704 		return status;
4705 
4706 	if (previewFile.exists())
4707 		return theFormats().view(*this, previewFile, format) ?
4708 			PreviewSuccess : PreviewError;
4709 
4710 	// Successful export but no output file?
4711 	// Probably a bug in error detection.
4712 	LATTEST(status != ExportSuccess);
4713 	return status;
4714 }
4715 
4716 
extractFromVC()4717 Buffer::ReadStatus Buffer::extractFromVC()
4718 {
4719 	bool const found = LyXVC::file_not_found_hook(d->filename);
4720 	if (!found)
4721 		return ReadFileNotFound;
4722 	if (!d->filename.isReadableFile())
4723 		return ReadVCError;
4724 	return ReadSuccess;
4725 }
4726 
4727 
loadEmergency()4728 Buffer::ReadStatus Buffer::loadEmergency()
4729 {
4730 	FileName const emergencyFile = getEmergencyFileName();
4731 	if (!emergencyFile.exists()
4732 		  || emergencyFile.lastModified() <= d->filename.lastModified())
4733 		return ReadFileNotFound;
4734 
4735 	docstring const file = makeDisplayPath(d->filename.absFileName(), 20);
4736 	docstring const text = bformat(_("An emergency save of the document "
4737 		"%1$s exists.\n\nRecover emergency save?"), file);
4738 
4739 	int const load_emerg = Alert::prompt(_("Load emergency save?"), text,
4740 		0, 2, _("&Recover"), _("&Load Original"), _("&Cancel"));
4741 
4742 	switch (load_emerg)
4743 	{
4744 	case 0: {
4745 		docstring str;
4746 		ReadStatus const ret_llf = loadThisLyXFile(emergencyFile);
4747 		bool const success = (ret_llf == ReadSuccess);
4748 		if (success) {
4749 			if (hasReadonlyFlag()) {
4750 				Alert::warning(_("File is read-only"),
4751 					bformat(_("An emergency file is successfully loaded, "
4752 					"but the original file %1$s is marked read-only. "
4753 					"Please make sure to save the document as a different "
4754 					"file."), from_utf8(d->filename.absFileName())));
4755 			}
4756 			markDirty();
4757 			lyxvc().file_found_hook(d->filename);
4758 			str = _("Document was successfully recovered.");
4759 		} else
4760 			str = _("Document was NOT successfully recovered.");
4761 		str += "\n\n" + bformat(_("Remove emergency file now?\n(%1$s)"),
4762 			makeDisplayPath(emergencyFile.absFileName()));
4763 
4764 		int const del_emerg =
4765 			Alert::prompt(_("Delete emergency file?"), str, 1, 1,
4766 				_("&Remove"), _("&Keep"));
4767 		if (del_emerg == 0) {
4768 			emergencyFile.removeFile();
4769 			if (success)
4770 				Alert::warning(_("Emergency file deleted"),
4771 					_("Do not forget to save your file now!"), true);
4772 			}
4773 		return success ? ReadSuccess : ReadEmergencyFailure;
4774 	}
4775 	case 1: {
4776 		int const del_emerg =
4777 			Alert::prompt(_("Delete emergency file?"),
4778 				_("Remove emergency file now?"), 1, 1,
4779 				_("&Remove"), _("&Keep"));
4780 		if (del_emerg == 0)
4781 			emergencyFile.removeFile();
4782 		else {
4783 			// See bug #11464
4784 			FileName newname;
4785 			string const ename = emergencyFile.absFileName();
4786 			bool noname = true;
4787 			// Surely we can find one in 100 tries?
4788 			for (int i = 1; i < 100; ++i) {
4789 				newname.set(ename + to_string(i) + ".lyx");
4790 				if (!newname.exists()) {
4791 					noname = false;
4792 					break;
4793 				}
4794 			}
4795 			if (!noname) {
4796 				// renameTo returns true on success. So inverting that
4797 				// will give us true if we fail.
4798 				noname = !emergencyFile.renameTo(newname);
4799 			}
4800 			if (noname) {
4801 				Alert::warning(_("Can't rename emergency file!"),
4802 					_("LyX was unable to rename the emergency file. "
4803 					  "You should do so manually. Otherwise, you will be"
4804 					  "asked about it again the next time you try to load"
4805 					  "this file, and may over-write your own work."));
4806 			}
4807 		}
4808 		return ReadOriginal;
4809 	}
4810 
4811 	default:
4812 		break;
4813 	}
4814 	return ReadCancel;
4815 }
4816 
4817 
loadAutosave()4818 Buffer::ReadStatus Buffer::loadAutosave()
4819 {
4820 	// Now check if autosave file is newer.
4821 	FileName const autosaveFile = getAutosaveFileName();
4822 	if (!autosaveFile.exists()
4823 		  || autosaveFile.lastModified() <= d->filename.lastModified())
4824 		return ReadFileNotFound;
4825 
4826 	docstring const file = makeDisplayPath(d->filename.absFileName(), 20);
4827 	docstring const text = bformat(_("The backup of the document %1$s "
4828 		"is newer.\n\nLoad the backup instead?"), file);
4829 	int const ret = Alert::prompt(_("Load backup?"), text, 0, 2,
4830 		_("&Load backup"), _("Load &original"),	_("&Cancel"));
4831 
4832 	switch (ret)
4833 	{
4834 	case 0: {
4835 		ReadStatus const ret_llf = loadThisLyXFile(autosaveFile);
4836 		// the file is not saved if we load the autosave file.
4837 		if (ret_llf == ReadSuccess) {
4838 			if (hasReadonlyFlag()) {
4839 				Alert::warning(_("File is read-only"),
4840 					bformat(_("A backup file is successfully loaded, "
4841 					"but the original file %1$s is marked read-only. "
4842 					"Please make sure to save the document as a "
4843 					"different file."),
4844 					from_utf8(d->filename.absFileName())));
4845 			}
4846 			markDirty();
4847 			lyxvc().file_found_hook(d->filename);
4848 			return ReadSuccess;
4849 		}
4850 		return ReadAutosaveFailure;
4851 	}
4852 	case 1:
4853 		// Here we delete the autosave
4854 		autosaveFile.removeFile();
4855 		return ReadOriginal;
4856 	default:
4857 		break;
4858 	}
4859 	return ReadCancel;
4860 }
4861 
4862 
loadLyXFile()4863 Buffer::ReadStatus Buffer::loadLyXFile()
4864 {
4865 	if (!d->filename.isReadableFile()) {
4866 		ReadStatus const ret_rvc = extractFromVC();
4867 		if (ret_rvc != ReadSuccess)
4868 			return ret_rvc;
4869 	}
4870 
4871 	ReadStatus const ret_re = loadEmergency();
4872 	if (ret_re == ReadSuccess || ret_re == ReadCancel)
4873 		return ret_re;
4874 
4875 	ReadStatus const ret_ra = loadAutosave();
4876 	if (ret_ra == ReadSuccess || ret_ra == ReadCancel)
4877 		return ret_ra;
4878 
4879 	return loadThisLyXFile(d->filename);
4880 }
4881 
4882 
loadThisLyXFile(FileName const & fn)4883 Buffer::ReadStatus Buffer::loadThisLyXFile(FileName const & fn)
4884 {
4885 	return readFile(fn);
4886 }
4887 
4888 
bufferErrors(TeXErrors const & terr,ErrorList & errorList) const4889 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
4890 {
4891 	for (auto const & err : terr) {
4892 		TexRow::TextEntry start = TexRow::text_none, end = TexRow::text_none;
4893 		int errorRow = err.error_in_line;
4894 		Buffer const * buf = 0;
4895 		Impl const * p = d;
4896 		if (err.child_name.empty())
4897 			tie(start, end) = p->texrow.getEntriesFromRow(errorRow);
4898 		else {
4899 			// The error occurred in a child
4900 			for (Buffer const * child : getDescendents()) {
4901 				string const child_name =
4902 					DocFileName(changeExtension(child->absFileName(), "tex")).
4903 					mangledFileName();
4904 				if (err.child_name != child_name)
4905 					continue;
4906 				tie(start, end) = child->d->texrow.getEntriesFromRow(errorRow);
4907 				if (!TexRow::isNone(start)) {
4908 					buf = d->cloned_buffer_
4909 						? child->d->cloned_buffer_->d->owner_
4910 						: child->d->owner_;
4911 					p = child->d;
4912 					break;
4913 				}
4914 			}
4915 		}
4916 		errorList.push_back(ErrorItem(err.error_desc, err.error_text,
4917 		                              start, end, buf));
4918 	}
4919 }
4920 
4921 
setBuffersForInsets() const4922 void Buffer::setBuffersForInsets() const
4923 {
4924 	inset().setBuffer(const_cast<Buffer &>(*this));
4925 }
4926 
4927 
updateBuffer(UpdateScope scope,UpdateType utype) const4928 void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const
4929 {
4930 	LBUFERR(!text().paragraphs().empty());
4931 
4932 	// Use the master text class also for child documents
4933 	Buffer const * const master = masterBuffer();
4934 	DocumentClass const & textclass = master->params().documentClass();
4935 
4936 	docstring_list old_bibfiles;
4937 	// Do this only if we are the top-level Buffer. We also need to account
4938 	// for the case of a previewed child with ignored parent here.
4939 	if (master == this && !d->ignore_parent) {
4940 		textclass.counters().reset(from_ascii("bibitem"));
4941 		reloadBibInfoCache();
4942 		// we will re-read this cache as we go through, but we need
4943 		// to know whether it's changed to know whether we need to
4944 		// update the bibinfo cache.
4945 		old_bibfiles = d->bibfiles_cache_;
4946 		d->bibfiles_cache_.clear();
4947 	}
4948 
4949 	// keep the buffers to be children in this set. If the call from the
4950 	// master comes back we can see which of them were actually seen (i.e.
4951 	// via an InsetInclude). The remaining ones in the set need still be updated.
4952 	static std::set<Buffer const *> bufToUpdate;
4953 	if (scope == UpdateMaster) {
4954 		// If this is a child document start with the master
4955 		if (master != this) {
4956 			bufToUpdate.insert(this);
4957 			master->updateBuffer(UpdateMaster, utype);
4958 			// If the master buffer has no gui associated with it, then the TocModel is
4959 			// not updated during the updateBuffer call and TocModel::toc_ is invalid
4960 			// (bug 5699). The same happens if the master buffer is open in a different
4961 			// window. This test catches both possibilities.
4962 			// See: https://marc.info/?l=lyx-devel&m=138590578911716&w=2
4963 			// There remains a problem here: If there is another child open in yet a third
4964 			// window, that TOC is not updated. So some more general solution is needed at
4965 			// some point.
4966 			if (master->d->gui_ != d->gui_)
4967 				structureChanged();
4968 
4969 			// was buf referenced from the master (i.e. not in bufToUpdate anymore)?
4970 			if (bufToUpdate.find(this) == bufToUpdate.end())
4971 				return;
4972 		}
4973 
4974 		// start over the counters in the master
4975 		textclass.counters().reset();
4976 	}
4977 
4978 	// update will be done below for this buffer
4979 	bufToUpdate.erase(this);
4980 
4981 	// update all caches
4982 	clearReferenceCache();
4983 	updateMacros();
4984 	setChangesPresent(false);
4985 
4986 	Buffer & cbuf = const_cast<Buffer &>(*this);
4987 
4988 	// do the real work
4989 	ParIterator parit = cbuf.par_iterator_begin();
4990 	updateBuffer(parit, utype);
4991 
4992 	if (master != this)
4993 		// If this document has siblings, then update the TocBackend later. The
4994 		// reason is to ensure that later siblings are up to date when e.g. the
4995 		// broken or not status of references is computed. The update is called
4996 		// in InsetInclude::addToToc.
4997 		return;
4998 
4999 	// if the bibfiles changed, the cache of bibinfo is invalid
5000 	docstring_list new_bibfiles = d->bibfiles_cache_;
5001 	// this is a trick to determine whether the two vectors have
5002 	// the same elements.
5003 	sort(new_bibfiles.begin(), new_bibfiles.end());
5004 	sort(old_bibfiles.begin(), old_bibfiles.end());
5005 	if (old_bibfiles != new_bibfiles) {
5006 		LYXERR(Debug::FILES, "Reloading bibinfo cache.");
5007 		invalidateBibinfoCache();
5008 		reloadBibInfoCache();
5009 		// We relied upon the bibinfo cache when recalculating labels. But that
5010 		// cache was invalid, although we didn't find that out until now. So we
5011 		// have to do it all again.
5012 		// That said, the only thing we really need to do is update the citation
5013 		// labels. Nothing else will have changed. So we could create a new
5014 		// UpdateType that would signal that fact, if we needed to do so.
5015 		parit = cbuf.par_iterator_begin();
5016 		// we will be re-doing the counters and references and such.
5017 		textclass.counters().reset();
5018 		clearReferenceCache();
5019 		// we should not need to do this again?
5020 		// updateMacros();
5021 		setChangesPresent(false);
5022 		updateBuffer(parit, utype);
5023 		// this will already have been done by reloadBibInfoCache();
5024 		// d->bibinfo_cache_valid_ = true;
5025 	}
5026 	else {
5027 		LYXERR(Debug::FILES, "Bibfiles unchanged.");
5028 		// this is also set to true on the other path, by reloadBibInfoCache.
5029 		d->bibinfo_cache_valid_ = true;
5030 	}
5031 	d->cite_labels_valid_ = true;
5032 	/// FIXME: Perf
5033 	cbuf.tocBackend().update(true, utype);
5034 	if (scope == UpdateMaster)
5035 		cbuf.structureChanged();
5036 }
5037 
5038 
getDepth(DocIterator const & it)5039 static depth_type getDepth(DocIterator const & it)
5040 {
5041 	depth_type depth = 0;
5042 	for (size_t i = 0 ; i < it.depth() ; ++i)
5043 		if (!it[i].inset().inMathed())
5044 			depth += it[i].paragraph().getDepth() + 1;
5045 	// remove 1 since the outer inset does not count
5046 	// we should have at least one non-math inset, so
5047 	// depth should nevery be 0. but maybe it is worth
5048 	// marking this, just in case.
5049 	LATTEST(depth > 0);
5050 	// coverity[INTEGER_OVERFLOW]
5051 	return depth - 1;
5052 }
5053 
getItemDepth(ParIterator const & it)5054 static depth_type getItemDepth(ParIterator const & it)
5055 {
5056 	Paragraph const & par = *it;
5057 	LabelType const labeltype = par.layout().labeltype;
5058 
5059 	if (labeltype != LABEL_ENUMERATE && labeltype != LABEL_ITEMIZE)
5060 		return 0;
5061 
5062 	// this will hold the lowest depth encountered up to now.
5063 	depth_type min_depth = getDepth(it);
5064 	ParIterator prev_it = it;
5065 	while (true) {
5066 		if (prev_it.pit())
5067 			--prev_it.top().pit();
5068 		else {
5069 			// start of nested inset: go to outer par
5070 			prev_it.pop_back();
5071 			if (prev_it.empty()) {
5072 				// start of document: nothing to do
5073 				return 0;
5074 			}
5075 		}
5076 
5077 		// We search for the first paragraph with same label
5078 		// that is not more deeply nested.
5079 		Paragraph & prev_par = *prev_it;
5080 		depth_type const prev_depth = getDepth(prev_it);
5081 		if (labeltype == prev_par.layout().labeltype) {
5082 			if (prev_depth < min_depth)
5083 				return prev_par.itemdepth + 1;
5084 			if (prev_depth == min_depth)
5085 				return prev_par.itemdepth;
5086 		}
5087 		min_depth = min(min_depth, prev_depth);
5088 		// small optimization: if we are at depth 0, we won't
5089 		// find anything else
5090 		if (prev_depth == 0)
5091 			return 0;
5092 	}
5093 }
5094 
5095 
needEnumCounterReset(ParIterator const & it)5096 static bool needEnumCounterReset(ParIterator const & it)
5097 {
5098 	Paragraph const & par = *it;
5099 	LASSERT(par.layout().labeltype == LABEL_ENUMERATE, return false);
5100 	depth_type const cur_depth = par.getDepth();
5101 	ParIterator prev_it = it;
5102 	while (prev_it.pit()) {
5103 		--prev_it.top().pit();
5104 		Paragraph const & prev_par = *prev_it;
5105 		if (prev_par.getDepth() <= cur_depth)
5106 			return prev_par.layout().name() != par.layout().name();
5107 	}
5108 	// start of nested inset: reset
5109 	return true;
5110 }
5111 
5112 
5113 // set the label of a paragraph. This includes the counters.
setLabel(ParIterator & it,UpdateType utype) const5114 void Buffer::Impl::setLabel(ParIterator & it, UpdateType utype) const
5115 {
5116 	BufferParams const & bp = owner_->masterBuffer()->params();
5117 	DocumentClass const & textclass = bp.documentClass();
5118 	Paragraph & par = it.paragraph();
5119 	Layout const & layout = par.layout();
5120 	Counters & counters = textclass.counters();
5121 
5122 	if (par.params().startOfAppendix()) {
5123 		// We want to reset the counter corresponding to toplevel sectioning
5124 		Layout const & lay = textclass.getTOCLayout();
5125 		docstring const cnt = lay.counter;
5126 		if (!cnt.empty())
5127 			counters.reset(cnt);
5128 		counters.appendix(true);
5129 	}
5130 	par.params().appendix(counters.appendix());
5131 
5132 	// Compute the item depth of the paragraph
5133 	par.itemdepth = getItemDepth(it);
5134 
5135 	if (layout.margintype == MARGIN_MANUAL) {
5136 		if (par.params().labelWidthString().empty())
5137 			par.params().labelWidthString(par.expandLabel(layout, bp));
5138 	} else if (layout.latextype == LATEX_BIB_ENVIRONMENT) {
5139 		// we do not need to do anything here, since the empty case is
5140 		// handled during export.
5141 	} else {
5142 		par.params().labelWidthString(docstring());
5143 	}
5144 
5145 	switch(layout.labeltype) {
5146 	case LABEL_ITEMIZE: {
5147 		// At some point of time we should do something more
5148 		// clever here, like:
5149 		//   par.params().labelString(
5150 		//    bp.user_defined_bullet(par.itemdepth).getText());
5151 		// for now, use a simple hardcoded label
5152 		docstring itemlabel;
5153 		switch (par.itemdepth) {
5154 		case 0:
5155 			itemlabel = char_type(0x2022);
5156 			break;
5157 		case 1:
5158 			itemlabel = char_type(0x2013);
5159 			break;
5160 		case 2:
5161 			itemlabel = char_type(0x2217);
5162 			break;
5163 		case 3:
5164 			itemlabel = char_type(0x2219); // or 0x00b7
5165 			break;
5166 		}
5167 		par.params().labelString(itemlabel);
5168 		break;
5169 	}
5170 
5171 	case LABEL_ENUMERATE: {
5172 		docstring enumcounter = layout.counter.empty() ? from_ascii("enum") : layout.counter;
5173 
5174 		switch (par.itemdepth) {
5175 		case 2:
5176 			enumcounter += 'i';
5177 			// fall through
5178 		case 1:
5179 			enumcounter += 'i';
5180 			// fall through
5181 		case 0:
5182 			enumcounter += 'i';
5183 			break;
5184 		case 3:
5185 			enumcounter += "iv";
5186 			break;
5187 		default:
5188 			// not a valid enumdepth...
5189 			break;
5190 		}
5191 
5192 		// Increase the master counter?
5193 		if (layout.stepmastercounter && needEnumCounterReset(it))
5194 			counters.stepMaster(enumcounter, utype);
5195 
5196 		// Maybe we have to reset the enumeration counter.
5197 		if (!layout.resumecounter && needEnumCounterReset(it))
5198 			counters.reset(enumcounter);
5199 		counters.step(enumcounter, utype);
5200 
5201 		string const & lang = par.getParLanguage(bp)->code();
5202 		par.params().labelString(counters.theCounter(enumcounter, lang));
5203 
5204 		break;
5205 	}
5206 
5207 	case LABEL_SENSITIVE: {
5208 		string const & type = counters.current_float();
5209 		docstring full_label;
5210 		if (type.empty())
5211 			full_label = owner_->B_("Senseless!!! ");
5212 		else {
5213 			docstring name = owner_->B_(textclass.floats().getType(type).name());
5214 			if (counters.hasCounter(from_utf8(type))) {
5215 				string const & lang = par.getParLanguage(bp)->code();
5216 				counters.step(from_utf8(type), utype);
5217 				full_label = bformat(from_ascii("%1$s %2$s:"),
5218 						     name,
5219 						     counters.theCounter(from_utf8(type), lang));
5220 			} else
5221 				full_label = bformat(from_ascii("%1$s #:"), name);
5222 		}
5223 		par.params().labelString(full_label);
5224 		break;
5225 	}
5226 
5227 	case LABEL_NO_LABEL:
5228 		par.params().labelString(docstring());
5229 		break;
5230 
5231 	case LABEL_ABOVE:
5232 	case LABEL_CENTERED:
5233 	case LABEL_STATIC: {
5234 		docstring const & lcounter = layout.counter;
5235 		if (!lcounter.empty()) {
5236 			if (layout.toclevel <= bp.secnumdepth
5237 						&& (layout.latextype != LATEX_ENVIRONMENT
5238 					|| it.text()->isFirstInSequence(it.pit()))) {
5239 				if (counters.hasCounter(lcounter))
5240 					counters.step(lcounter, utype);
5241 				par.params().labelString(par.expandLabel(layout, bp));
5242 			} else
5243 				par.params().labelString(docstring());
5244 		} else
5245 			par.params().labelString(par.expandLabel(layout, bp));
5246 		break;
5247 	}
5248 
5249 	case LABEL_MANUAL:
5250 	case LABEL_BIBLIO:
5251 		par.params().labelString(par.expandLabel(layout, bp));
5252 	}
5253 }
5254 
5255 
updateBuffer(ParIterator & parit,UpdateType utype) const5256 void Buffer::updateBuffer(ParIterator & parit, UpdateType utype) const
5257 {
5258 	// LASSERT: Is it safe to continue here, or should we just return?
5259 	LASSERT(parit.pit() == 0, /**/);
5260 
5261 	// Set the position of the text in the buffer to be able
5262 	// to resolve macros in it.
5263 	parit.text()->setMacrocontextPosition(parit);
5264 
5265 	// Reset bibitem counter in master (#8499)
5266 	Buffer const * const master = masterBuffer();
5267 	if (master == this && !d->ignore_parent)
5268 		master->params().documentClass().counters().reset(from_ascii("bibitem"));
5269 
5270 	depth_type maxdepth = 0;
5271 	pit_type const lastpit = parit.lastpit();
5272 	for ( ; parit.pit() <= lastpit ; ++parit.pit()) {
5273 		// reduce depth if necessary
5274 		if (parit->params().depth() > maxdepth) {
5275 			/** FIXME: this function is const, but
5276 			 * nevertheless it modifies the buffer. To be
5277 			 * cleaner, one should modify the buffer in
5278 			 * another function, which is actually
5279 			 * non-const. This would however be costly in
5280 			 * terms of code duplication.
5281 			 */
5282 			const_cast<Buffer *>(this)->undo().recordUndo(CursorData(parit));
5283 			parit->params().depth(maxdepth);
5284 		}
5285 		maxdepth = parit->getMaxDepthAfter();
5286 
5287 		if (utype == OutputUpdate) {
5288 			// track the active counters
5289 			// we have to do this for the master buffer, since the local
5290 			// buffer isn't tracking anything.
5291 			masterBuffer()->params().documentClass().counters().
5292 					setActiveLayout(parit->layout());
5293 		}
5294 
5295 		// set the counter for this paragraph
5296 		d->setLabel(parit, utype);
5297 
5298 		// update change-tracking flag
5299 		parit->addChangesToBuffer(*this);
5300 
5301 		// now the insets
5302 		InsetList::const_iterator iit = parit->insetList().begin();
5303 		InsetList::const_iterator end = parit->insetList().end();
5304 		for (; iit != end; ++iit) {
5305 			parit.pos() = iit->pos;
5306 			iit->inset->updateBuffer(parit, utype);
5307 		}
5308 	}
5309 }
5310 
5311 
spellCheck(DocIterator & from,DocIterator & to,WordLangTuple & word_lang,docstring_list & suggestions) const5312 int Buffer::spellCheck(DocIterator & from, DocIterator & to,
5313 	WordLangTuple & word_lang, docstring_list & suggestions) const
5314 {
5315 	int progress = 0;
5316 	WordLangTuple wl;
5317 	suggestions.clear();
5318 	word_lang = WordLangTuple();
5319 	bool const to_end = to.empty();
5320 	DocIterator const end = to_end ? doc_iterator_end(this) : to;
5321 	// OK, we start from here.
5322 	for (; from != end; from.forwardPos()) {
5323 		// This skips all insets with spell check disabled.
5324 		while (!from.allowSpellCheck()) {
5325 			from.pop_back();
5326 			from.pos()++;
5327 		}
5328 		// If from is at the end of the document (which is possible
5329 		// when "from" was changed above) LyX will crash later otherwise.
5330 		if (from.atEnd() || (!to_end && from >= end))
5331 			break;
5332 		to = from;
5333 		from.paragraph().spellCheck();
5334 		SpellChecker::Result res = from.paragraph().spellCheck(from.pos(), to.pos(), wl, suggestions);
5335 		if (SpellChecker::misspelled(res)) {
5336 			word_lang = wl;
5337 			break;
5338 		}
5339 		// Do not increase progress when from == to, otherwise the word
5340 		// count will be wrong.
5341 		if (from != to) {
5342 			from = to;
5343 			++progress;
5344 		}
5345 	}
5346 	return progress;
5347 }
5348 
5349 
updateStatistics(DocIterator & from,DocIterator & to,bool skipNoOutput)5350 void Buffer::Impl::updateStatistics(DocIterator & from, DocIterator & to, bool skipNoOutput)
5351 {
5352 	bool inword = false;
5353 	word_count_ = 0;
5354 	char_count_ = 0;
5355 	blank_count_ = 0;
5356 
5357 	for (DocIterator dit = from ; dit != to && !dit.atEnd(); ) {
5358 		if (!dit.inTexted()) {
5359 			dit.forwardPos();
5360 			continue;
5361 		}
5362 
5363 		Paragraph const & par = dit.paragraph();
5364 		pos_type const pos = dit.pos();
5365 
5366 		// Copied and adapted from isWordSeparator() in Paragraph
5367 		if (pos == dit.lastpos()) {
5368 			inword = false;
5369 		} else {
5370 			Inset const * ins = par.getInset(pos);
5371 			if (ins && skipNoOutput && !ins->producesOutput()) {
5372 				// skip this inset
5373 				++dit.top().pos();
5374 				// stop if end of range was skipped
5375 				if (!to.atEnd() && dit >= to)
5376 					break;
5377 				continue;
5378 			} else if (!par.isDeleted(pos)) {
5379 				if (par.isWordSeparator(pos))
5380 					inword = false;
5381 				else if (!inword) {
5382 					++word_count_;
5383 					inword = true;
5384 				}
5385 				if (ins && ins->isLetter())
5386 					++char_count_;
5387 				else if (ins && ins->isSpace())
5388 					++blank_count_;
5389 				else {
5390 					char_type const c = par.getChar(pos);
5391 					if (isPrintableNonspace(c))
5392 						++char_count_;
5393 					else if (isSpace(c))
5394 						++blank_count_;
5395 				}
5396 			}
5397 		}
5398 		dit.forwardPos();
5399 	}
5400 }
5401 
5402 
updateStatistics(DocIterator & from,DocIterator & to,bool skipNoOutput) const5403 void Buffer::updateStatistics(DocIterator & from, DocIterator & to, bool skipNoOutput) const
5404 {
5405 	d->updateStatistics(from, to, skipNoOutput);
5406 }
5407 
5408 
wordCount() const5409 int Buffer::wordCount() const
5410 {
5411 	return d->wordCount();
5412 }
5413 
5414 
charCount(bool with_blanks) const5415 int Buffer::charCount(bool with_blanks) const
5416 {
5417 	return d->charCount(with_blanks);
5418 }
5419 
5420 
reload()5421 Buffer::ReadStatus Buffer::reload()
5422 {
5423 	setBusy(true);
5424 	// c.f. bug https://www.lyx.org/trac/ticket/6587
5425 	removeAutosaveFile();
5426 	// e.g., read-only status could have changed due to version control
5427 	d->filename.refresh();
5428 	docstring const disp_fn = makeDisplayPath(d->filename.absFileName());
5429 
5430 	// clear parent. this will get reset if need be.
5431 	d->setParent(0);
5432 	ReadStatus const status = loadLyXFile();
5433 	if (status == ReadSuccess) {
5434 		updateBuffer();
5435 		changed(true);
5436 		updateTitles();
5437 		markClean();
5438 		message(bformat(_("Document %1$s reloaded."), disp_fn));
5439 		d->undo_.clear();
5440 	} else {
5441 		message(bformat(_("Could not reload document %1$s."), disp_fn));
5442 	}
5443 	setBusy(false);
5444 	removePreviews();
5445 	updatePreviews();
5446 	errors("Parse");
5447 	return status;
5448 }
5449 
5450 
saveAs(FileName const & fn)5451 bool Buffer::saveAs(FileName const & fn)
5452 {
5453 	FileName const old_name = fileName();
5454 	FileName const old_auto = getAutosaveFileName();
5455 	bool const old_unnamed = isUnnamed();
5456 	bool success = true;
5457 	d->old_position = filePath();
5458 
5459 	setFileName(fn);
5460 	markDirty();
5461 	setUnnamed(false);
5462 
5463 	if (save()) {
5464 		// bring the autosave file with us, just in case.
5465 		moveAutosaveFile(old_auto);
5466 		// validate version control data and
5467 		// correct buffer title
5468 		lyxvc().file_found_hook(fileName());
5469 		updateTitles();
5470 		// the file has now been saved to the new location.
5471 		// we need to check that the locations of child buffers
5472 		// are still valid.
5473 		checkChildBuffers();
5474 		checkMasterBuffer();
5475 	} else {
5476 		// save failed
5477 		// reset the old filename and unnamed state
5478 		setFileName(old_name);
5479 		setUnnamed(old_unnamed);
5480 		success = false;
5481 	}
5482 
5483 	d->old_position.clear();
5484 	return success;
5485 }
5486 
5487 
checkChildBuffers()5488 void Buffer::checkChildBuffers()
5489 {
5490 	Impl::BufferPositionMap::iterator it = d->children_positions.begin();
5491 	Impl::BufferPositionMap::iterator const en = d->children_positions.end();
5492 	for (; it != en; ++it) {
5493 		DocIterator dit = it->second;
5494 		Buffer * cbuf = const_cast<Buffer *>(it->first);
5495 		if (!cbuf || !theBufferList().isLoaded(cbuf))
5496 			continue;
5497 		Inset * inset = dit.nextInset();
5498 		LASSERT(inset && inset->lyxCode() == INCLUDE_CODE, continue);
5499 		InsetInclude * inset_inc = static_cast<InsetInclude *>(inset);
5500 		docstring const & incfile = inset_inc->getParam("filename");
5501 		string oldloc = cbuf->absFileName();
5502 		string newloc = makeAbsPath(to_utf8(incfile),
5503 				onlyPath(absFileName())).absFileName();
5504 		if (oldloc == newloc)
5505 			continue;
5506 		// the location of the child file is incorrect.
5507 		cbuf->setParent(0);
5508 		inset_inc->setChildBuffer(0);
5509 	}
5510 	// invalidate cache of children
5511 	d->children_positions.clear();
5512 	d->position_to_children.clear();
5513 }
5514 
5515 
5516 // If a child has been saved under a different name/path, it might have been
5517 // orphaned. Therefore the master needs to be reset (bug 8161).
checkMasterBuffer()5518 void Buffer::checkMasterBuffer()
5519 {
5520 	Buffer const * const master = masterBuffer();
5521 	if (master == this)
5522 		return;
5523 
5524 	// necessary to re-register the child (bug 5873)
5525 	// FIXME: clean up updateMacros (here, only
5526 	// child registering is needed).
5527 	master->updateMacros();
5528 	// (re)set master as master buffer, but only
5529 	// if we are a real child
5530 	if (master->isChild(this))
5531 		setParent(master);
5532 	else
5533 		setParent(0);
5534 }
5535 
5536 
includedFilePath(string const & name,string const & ext) const5537 string Buffer::includedFilePath(string const & name, string const & ext) const
5538 {
5539 	if (d->old_position.empty() ||
5540 	    equivalent(FileName(d->old_position), FileName(filePath())))
5541 		return name;
5542 
5543 	bool isabsolute = FileName::isAbsolute(name);
5544 	// both old_position and filePath() end with a path separator
5545 	string absname = isabsolute ? name : d->old_position + name;
5546 
5547 	// if old_position is set to origin, we need to do the equivalent of
5548 	// getReferencedFileName() (see readDocument())
5549 	if (!isabsolute && d->old_position == params().origin) {
5550 		FileName const test(addExtension(filePath() + name, ext));
5551 		if (test.exists())
5552 			absname = filePath() + name;
5553 	}
5554 
5555 	if (!FileName(addExtension(absname, ext)).exists())
5556 		return name;
5557 
5558 	if (isabsolute)
5559 		return to_utf8(makeRelPath(from_utf8(name), from_utf8(filePath())));
5560 
5561 	return to_utf8(makeRelPath(from_utf8(FileName(absname).realPath()),
5562 	                           from_utf8(filePath())));
5563 }
5564 
5565 
setChangesPresent(bool b) const5566 void Buffer::setChangesPresent(bool b) const
5567 {
5568 	d->tracked_changes_present_ = b;
5569 }
5570 
5571 
areChangesPresent() const5572 bool Buffer::areChangesPresent() const
5573 {
5574 	return d->tracked_changes_present_;
5575 }
5576 
5577 
updateChangesPresent() const5578 void Buffer::updateChangesPresent() const
5579 {
5580 	LYXERR(Debug::CHANGES, "Buffer::updateChangesPresent");
5581 	setChangesPresent(false);
5582 	ParConstIterator it = par_iterator_begin();
5583 	ParConstIterator const end = par_iterator_end();
5584 	for (; !areChangesPresent() && it != end; ++it)
5585 		it->addChangesToBuffer(*this);
5586 }
5587 
5588 
refreshFileMonitor()5589 void Buffer::Impl::refreshFileMonitor()
5590 {
5591 	if (file_monitor_ && file_monitor_->filename() == filename.absFileName()) {
5592 		file_monitor_->refresh();
5593 		return;
5594 	}
5595 
5596 	// The previous file monitor is invalid
5597 	// This also destroys the previous file monitor and all its connections
5598 	file_monitor_ = FileSystemWatcher::monitor(filename);
5599 	// file_monitor_ will be destroyed with *this, so it is not going to call a
5600 	// destroyed object method.
5601 	file_monitor_->connect([this](bool exists) {
5602 			fileExternallyModified(exists);
5603 		});
5604 }
5605 
5606 
fileExternallyModified(bool const exists)5607 void Buffer::Impl::fileExternallyModified(bool const exists)
5608 {
5609 	// ignore notifications after our own saving operations
5610 	if (checksum_ == filename.checksum()) {
5611 		LYXERR(Debug::FILES, "External modification but "
5612 		       "checksum unchanged: " << filename);
5613 		return;
5614 	}
5615 	// If the file has been deleted, only mark the file as dirty since it is
5616 	// pointless to prompt for reloading. If later a file is moved into this
5617 	// location, then the externally modified warning will appear then.
5618 	if (exists)
5619 			externally_modified_ = true;
5620 	// Update external modification notification.
5621 	// Dirty buffers must be visible at all times.
5622 	if (wa_ && wa_->unhide(owner_))
5623 		wa_->updateTitles();
5624 	else
5625 		// Unable to unhide the buffer (e.g. no GUI or not current View)
5626 		lyx_clean = true;
5627 }
5628 
5629 
notifiesExternalModification() const5630 bool Buffer::notifiesExternalModification() const
5631 {
5632 	return d->externally_modified_;
5633 }
5634 
5635 
clearExternalModification() const5636 void Buffer::clearExternalModification() const
5637 {
5638 	d->externally_modified_ = false;
5639 	if (d->wa_)
5640 		d->wa_->updateTitles();
5641 }
5642 
5643 
5644 } // namespace lyx
5645