The attached patch solves the problem about relative pathnames and BibTeX files...at least, it solves most of the problem. You can at least use relative pathnames now, though they won't always work correctly with the embedding feature (though no worse than before).

I've also fixed up some of the Unicode issues along the way. The only remaining one concerns EmbeddedFiles::inzip_name_, which is currently a std::string. Shouldn't this be docstring? It is written to the LyX file.

Comments welcome. And see the long FIXME about remaining issues.

Richard

Index: src/EmbeddedFiles.h
===================================================================
--- src/EmbeddedFiles.h	(revision 23974)
+++ src/EmbeddedFiles.h	(working copy)
@@ -94,8 +94,11 @@
 class EmbeddedFile : public support::DocFileName
 {
 public:
+	///
 	EmbeddedFile(std::string const & file = std::string(),
 		std::string const & buffer_path = std::string());
+	///
+	EmbeddedFile(FileName const &);
 	
 	/// set filename and inzipName.
 	/**
Index: src/EmbeddedFiles.cpp
===================================================================
--- src/EmbeddedFiles.cpp	(revision 23974)
+++ src/EmbeddedFiles.cpp	(working copy)
@@ -54,6 +54,11 @@
 }
 
 
+EmbeddedFile::EmbeddedFile(FileName const & fn)
+	: DocFileName(fn, false), embedded_(false), inset_list_()
+{}
+
+
 void EmbeddedFile::set(std::string const & filename, std::string const & buffer_path)
 {
 	DocFileName::set(filename, buffer_path);
@@ -372,7 +377,7 @@
 {
 	string inzipName = to_utf8(makeRelPath(from_utf8(absFilename()),
 			from_utf8(buffer_path)));
-	
+
 	if (FileName(inzipName).isAbsolute())
 		inzipName = absDirName + '/' + inzipName;
 
Index: src/insets/InsetBibtex.h
===================================================================
--- src/insets/InsetBibtex.h	(revision 23974)
+++ src/insets/InsetBibtex.h	(working copy)
@@ -12,14 +12,16 @@
 #ifndef INSET_BIBTEX_H
 #define INSET_BIBTEX_H
 
-#include <map>
-#include "InsetCommand.h"
 #include "BiblioInfo.h"
 #include "EmbeddedFiles.h"
+#include "InsetCommand.h"
 
-namespace lyx {
+#include "support/docstring.h"
 
+#include <map>
 
+namespace lyx {
+
 /** Used to insert BibTeX's information
   */
 class InsetBibtex : public InsetCommand {
@@ -41,11 +43,11 @@
 	///
 	void fillWithBibKeys(BiblioInfo &, InsetIterator const &) const;
 	///
-	EmbeddedFileList embeddedFiles() const;
+	EmbeddedFileList const & embeddedFiles() const;
 	///
-	bool addDatabase(std::string const &);
+	bool addDatabase(docstring const &);
 	///
-	bool delDatabase(std::string const &);
+	bool delDatabase(docstring const &);
 	///
 	void validate(LaTeXFeatures &) const;
 	///
@@ -55,15 +57,9 @@
 	///
 	static bool isCompatibleCommand(std::string const & s) 
 		{ return s == "bibtex"; }
-	/// create bibfiles_ from params bibfiles and embed
-	/**
-		\param bibfiles comma separated bib files
-		\param embed comma separated embed status
-	*/
-	void createBibFiles(docstring const & bibfiles, docstring const & embed) const;
-	/// update bibfiles and embed from bibfiles_
-	void updateParam();
 private:
+	/// create bibfiles_ from params
+	void createBibFiles() const;
 	///
 	void registerEmbeddedFiles(EmbeddedFileList &) const;
 	///
Index: src/insets/InsetBibtex.cpp
===================================================================
--- src/insets/InsetBibtex.cpp	(revision 23974)
+++ src/insets/InsetBibtex.cpp	(working copy)
@@ -105,9 +105,8 @@
 			break;
 		}
 		//
-		createBibFiles(p["bibfiles"], p["embed"]);
-		updateParam();
-		setParam("options", p["options"]);
+		setParams(p);
+		createBibFiles();
 		buffer().updateBibfilesCache();
 		break;
 	}
@@ -305,7 +304,7 @@
 }
 
 
-EmbeddedFileList InsetBibtex::embeddedFiles() const
+EmbeddedFileList const & InsetBibtex::embeddedFiles() const
 {
 	return bibfiles_;
 }
@@ -552,28 +551,28 @@
 void InsetBibtex::fillWithBibKeys(BiblioInfo & keylist,
 	InsetIterator const & /*di*/) const
 {
-	EmbeddedFileList const files = embeddedFiles();
-	for (vector<EmbeddedFile>::const_iterator it = files.begin();
-	     it != files.end(); ++ it) {
-		// This bibtex parser is a first step to parse bibtex files
-		// more precisely.
-		//
-		// - it reads the whole bibtex entry and does a syntax check
-		//   (matching delimiters, missing commas,...
-		// - it recovers from errors starting with the next @-character
-		// - it reads @string definitions and replaces them in the
-		//   field values.
-		// - it accepts more characters in keys or value names than
-		//   bibtex does.
-		//
-		// Officially bibtex does only support ASCII, but in practice
-		// you can use the encoding of the main document as long as
-		// some elements like keys and names are pure ASCII. Therefore
-		// we convert the file from the buffer encoding.
-		// We don't restrict keys to ASCII in LyX, since our own
-		// InsetBibitem can generate non-ASCII keys, and nonstandard
-		// 8bit clean bibtex forks exist.
-		
+	// This bibtex parser is a first step to parse bibtex files
+	// more precisely.
+	//
+	// - it reads the whole bibtex entry and does a syntax check
+	//   (matching delimiters, missing commas,...
+	// - it recovers from errors starting with the next @-character
+	// - it reads @string definitions and replaces them in the
+	//   field values.
+	// - it accepts more characters in keys or value names than
+	//   bibtex does.
+	//
+	// Officially bibtex does only support ASCII, but in practice
+	// you can use the encoding of the main document as long as
+	// some elements like keys and names are pure ASCII. Therefore
+	// we convert the file from the buffer encoding.
+	// We don't restrict keys to ASCII in LyX, since our own
+	// InsetBibitem can generate non-ASCII keys, and nonstandard
+	// 8bit clean bibtex forks exist.
+	
+	vector<EmbeddedFile>::const_iterator it = bibfiles_.begin();
+	vector<EmbeddedFile>::const_iterator en = bibfiles_.end();
+	for (;it != en; ++ it) {
 		idocfstream ifs(it->availableFile().toFilesystemEncoding().c_str(),
 			ios_base::in, buffer().params().encoding().iconvName());
 
@@ -704,35 +703,88 @@
 }
 
 
+namespace {
+	// look up the path to the file using TeX
+	FileName getTeXPath(docstring const & filename, Buffer const & buf)
+		{
+			string texfile = changeExtension(to_utf8(filename), "bib");
+			// note that, if the filename can be found directly from the path, 
+			// findtexfile will just return a FileName object for that path.
+			FileName file(findtexfile(texfile, "bib"));
+			if (file.empty())
+				file = FileName(makeAbsPath(texfile, buf.filePath()));
+			return file;
+		}
+}
 
-bool InsetBibtex::addDatabase(string const & db)
+
+bool InsetBibtex::addDatabase(docstring const & db)
 {
-	EmbeddedFile file(changeExtension(db, "bib"), buffer().filePath());
-	
-	// only compare filename
-	EmbeddedFileList::iterator it = bibfiles_.begin();
-	EmbeddedFileList::iterator it_end = bibfiles_.end();
-	for (; it != it_end; ++it)
-		if (it->absFilename() == file.absFilename())
-			return false;
-	
+	docstring bibfiles(getParam("bibfiles"));
+	if (tokenPos(bibfiles, ',', db) != -1)
+		return false;
+
+	EmbeddedFile file(getTeXPath(db, buffer()));
 	bibfiles_.push_back(file);
-	updateParam();
+
+	docstring embed(getParam("embed"));
+	if (!bibfiles.empty()) {
+		embed += ',';
+		bibfiles += ',';
+	}
+	setParam("bibfiles", bibfiles + db);
+	setParam("embed", embed + from_utf8(bibfiles_.back().inzipName()));
+
 	return true;
 }
 
 
-bool InsetBibtex::delDatabase(string const & db)
+bool InsetBibtex::delDatabase(docstring const & db)
 {
-	EmbeddedFile file(changeExtension(db, "bib"), buffer().filePath());
+	// look for the item and update status
+	docstring bibfiles(getParam("bibfiles"));
+	docstring embfiles(getParam("embed"));
+	docstring bibfile;
+	docstring embfile;
+	docstring newembfiles;
+	docstring newbibfiles;
+
+	bool found = false;
+	bool first = true;
+
+	char_type comma(',');
+
+	bibfiles = split(bibfiles, bibfile, comma);
+	embfiles = split(embfiles, embfile, comma);
+	while (!bibfile.empty()) {
+		// copy over the information from the old params for the ones
+		// we don't need to delete
+		if (bibfile != db) {
+			if (!first) {
+				newbibfiles = newbibfiles + comma;
+				newembfiles = newembfiles + comma;
+			}
+			newembfiles = newembfiles + embfile;
+			newbibfiles = newbibfiles + bibfile;
+			first = false;
+		} else
+			found = true;
+		bibfiles = split(bibfiles, bibfile, comma);
+	}
 	
+	if (!found)
+		return false;
+
+	EmbeddedFile file(getTeXPath(db, buffer()));
+	
 	// only compare filename
 	EmbeddedFileList::iterator it = bibfiles_.begin();
 	EmbeddedFileList::iterator it_end = bibfiles_.end();
 	for (; it != it_end; ++it)
 		if (it->absFilename() == file.absFilename()) {
 			bibfiles_.erase(it);
-			updateParam();
+			setParam("embed", newembfiles);
+			setParam("bibfiles", newbibfiles);
 			return true;
 		}
 	return false;
@@ -746,65 +798,48 @@
 }
 
 
-void InsetBibtex::createBibFiles(docstring const & bibParam,
-	docstring const & embedParam) const
+void InsetBibtex::createBibFiles() const
 {
 	bibfiles_.clear();
 	
-	string tmp;
-	string emb;
+	docstring bib;
+	docstring emb;
 	
-	string bibfiles = to_utf8(bibParam);
-	string embedStatus = to_utf8(embedParam);
+	docstring bibfiles(getParam("bibfiles"));
+	docstring embfiles(getParam("embed"));
 	
 	LYXERR(Debug::FILES, "Create bib files from parameters "
-		<< bibfiles << " and " << embedStatus);
+		<< bibfiles << " and " << embfiles);
 
-	bibfiles = split(bibfiles, tmp, ',');
-	embedStatus = split(embedStatus, emb, ',');
+	bibfiles = split(bibfiles, bib, ',');
+	embfiles = split(embfiles, emb, ',');
 	
-	while (!tmp.empty()) {
-		EmbeddedFile file(changeExtension(tmp, "bib"), buffer().filePath());
-		
-		file.setInzipName(emb);
+	while (!bib.empty()) {
+		// FIXME This is still wrong for relative pathnames like:
+		//         ../../bibtex/bibfile.bib
+		// because getTeXPath will return an absolute version of that, which then
+		// will appear to EmbeddedFiles::calcInzipName() as if it was absolute.
+		// The only solution I can see is to hand EmbeddedFile either the original
+		// pathname, which it can then check, or a flag as to whether the original
+		// pathname was absolute. (And note that DocFileName::save_abs_path_ doesn't
+		// help here at all.)
+			EmbeddedFile file(getTeXPath(bib, buffer()));
+
+		file.setInzipName(to_utf8(emb));
 		file.setEmbed(!emb.empty());
 		file.enable(buffer().embedded(), &buffer(), false);
 		bibfiles_.push_back(file);
 		// Get next file name
-		bibfiles = split(bibfiles, tmp, ',');
-		embedStatus = split(embedStatus, emb, ',');
+		bibfiles = split(bibfiles, bib, ',');
+		embfiles = split(embfiles, emb, ',');
 	}
 }
 
 
-void InsetBibtex::updateParam()
-{
-	docstring bibfiles;
-	docstring embed;
-
-	bool first = true;
-
-	EmbeddedFileList::iterator it = bibfiles_.begin();
-	EmbeddedFileList::iterator en = bibfiles_.end();
-	for (; it != en; ++it) {
-		if (!first) {
-			bibfiles += ',';
-			embed += ',';
-		} else
-			first = false;
-		bibfiles += from_utf8(it->outputFilename(buffer().filePath()));
-		if (it->embedded())
-			embed += from_utf8(it->inzipName());
-	}
-	setParam("bibfiles", bibfiles);
-	setParam("embed", embed);
-}
-
-
 void InsetBibtex::registerEmbeddedFiles(EmbeddedFileList & files) const
 {
 	if (bibfiles_.empty())
-		createBibFiles(params()["bibfiles"], params()["embed"]);
+		createBibFiles();
 
 	EmbeddedFileList::const_iterator it = bibfiles_.begin();
 	EmbeddedFileList::const_iterator it_end = bibfiles_.end();
@@ -816,12 +851,37 @@
 void InsetBibtex::updateEmbeddedFile(EmbeddedFile const & file)
 {
 	// look for the item and update status
-	for (EmbeddedFileList::iterator it = bibfiles_.begin();
-		it != bibfiles_.end(); ++it)
-		if (it->absFilename() == file.absFilename())
+	docstring bibfiles(getParam("bibfiles"));
+	docstring embfiles(getParam("embed"));
+	docstring bibfile;
+	docstring embfile;
+	docstring newembfiles;
+
+	bool found = false;
+	bool first = true;
+
+	char_type comma(',');
+
+	EmbeddedFileList::iterator it = bibfiles_.begin();
+	EmbeddedFileList::iterator en = bibfiles_.end();
+	for (; it != en; ++it) {
+		if (!first)
+			newembfiles = newembfiles + comma;
+		bibfiles = split(bibfiles, bibfile, comma);
+		embfiles = split(embfiles, embfile, comma);
+
+		string pathToBib(getTeXPath(bibfile, buffer()).absFilename());
+		if (!found && pathToBib == it->absFilename()) {
+			//found it, we'll suppose
+			newembfiles = newembfiles + from_utf8(it->inzipName());
 			*it = file;
-	
-	updateParam();
+			found = true;
+		} else // just copy the old value
+			newembfiles = newembfiles + embfile;
+		first = false;
+	}
+
+	setParam("embed", newembfiles);
 }
 
 
Index: src/support/lstrings.h
===================================================================
--- src/support/lstrings.h	(revision 23974)
+++ src/support/lstrings.h	(working copy)
@@ -153,6 +153,7 @@
 */
 int tokenPos(std::string const & a, char delim, std::string const & tok);
 
+int tokenPos(docstring const & a, char_type delim, docstring const & tok);
 
 /// Substitute all \a oldchar with \a newchar
 std::string const subst(std::string const & a, char oldchar, char newchar);
Index: src/support/lstrings.cpp
===================================================================
--- src/support/lstrings.cpp	(revision 23974)
+++ src/support/lstrings.cpp	(working copy)
@@ -592,6 +592,22 @@
 }
 
 
+int tokenPos(docstring const & a, char_type delim, docstring const & tok)
+{
+	int i = 0;
+	docstring str = a;
+	docstring tmptok;
+
+	while (!str.empty()) {
+		str = split(str, tmptok, delim);
+		if (tok == tmptok)
+			return i;
+		++i;
+	}
+	return -1;
+}
+
+
 namespace {
 
 /// Substitute all \a oldchar with \a newchar
Index: src/Buffer.cpp
===================================================================
--- src/Buffer.cpp	(revision 23974)
+++ src/Buffer.cpp	(working copy)
@@ -1405,7 +1405,7 @@
 		if (it->lyxCode() == BIBTEX_CODE) {
 			InsetBibtex const & inset =
 				static_cast<InsetBibtex const &>(*it);
-			EmbeddedFileList const bibfiles = inset.embeddedFiles();
+			EmbeddedFileList const & bibfiles = inset.embeddedFiles();
 			d->bibfilesCache_.insert(d->bibfilesCache_.end(),
 				bibfiles.begin(),
 				bibfiles.end());
Index: src/BufferView.cpp
===================================================================
--- src/BufferView.cpp	(revision 23974)
+++ src/BufferView.cpp	(working copy)
@@ -1240,7 +1240,7 @@
 		InsetBibtex * inset = getInsetByCode<InsetBibtex>(tmpcur,
 						BIBTEX_CODE);
 		if (inset) {
-			if (inset->addDatabase(to_utf8(cmd.argument())))
+			if (inset->addDatabase(cmd.argument()))
 				buffer_.updateBibfilesCache();
 		}
 		break;
@@ -1252,7 +1252,7 @@
 		InsetBibtex * inset = getInsetByCode<InsetBibtex>(tmpcur,
 						BIBTEX_CODE);
 		if (inset) {
-			if (inset->delDatabase(to_utf8(cmd.argument())))
+			if (inset->delDatabase(cmd.argument()))
 				buffer_.updateBibfilesCache();
 		}
 		break;

Reply via email to