Angus Leeming wrote:
> If you're happy with this, I'll make the change, commit it to the 1.3.x
> tree and forward-port it to 1.4.x. AFAIK, that's all squashable bugs
> squashed for 1.3.x. If you know different, please keep it to yourself ;-)

Patch attached. I also implemented Paul Rubin's suggestion of popping up a
warning the first time that the user exports a file to LaTeX that
references a BibTeX database whose path contains spaces.

-- 
Angus
Index: src/insets/insetbib.C
===================================================================
RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/insets/Attic/insetbib.C,v
retrieving revision 1.97.2.4
diff -u -p -r1.97.2.4 insetbib.C
--- src/insets/insetbib.C	18 Apr 2005 17:44:04 -0000	1.97.2.4
+++ src/insets/insetbib.C	14 Jul 2005 09:09:59 -0000
@@ -10,26 +10,31 @@
 #include <config.h>
 
 #include "insetbib.h"
+
 #include "buffer.h"
-#include "debug.h"
 #include "BufferView.h"
+#include "debug.h"
 #include "gettext.h"
-#include "lyxtext.h"
-#include "lyxrc.h"
 #include "lyxlex.h"
-#include "frontends/font_metrics.h"
-#include "frontends/LyXView.h"
+#include "lyxrc.h"
+#include "lyxtext.h"
 
+#include "frontends/Alert.h"
 #include "frontends/Dialogs.h"
+#include "frontends/font_metrics.h"
+#include "frontends/LyXView.h"
 
 #include "support/filetools.h"
-#include "support/path.h"
-#include "support/os.h"
-#include "support/lstrings.h"
 #include "support/LAssert.h"
+#include "support/lstrings.h"
+#include "support/os.h"
+#include "support/path.h"
+
+#include <boost/tokenizer.hpp>
 
-#include <fstream>
 #include <cstdlib>
+#include <fstream>
+#include "Lsstream.h"
 
 using std::ostream;
 using std::ifstream;
@@ -150,12 +155,12 @@ string normalize_name(Buffer const * buf
 	string const fname = MakeAbsPath(name, buffer->filePath());
 	if (AbsolutePath(name) || !IsFileReadable(fname + ext))
 		return name;
-	else if (!buffer->niceFile) 
+	else if (!buffer->niceFile)
 		return fname;
-	else 
+	else
 		return MakeRelPath(fname, buffer->getMasterBuffer()->filePath());
 }
-	
+
 }
 
 
@@ -209,21 +214,70 @@ int InsetBibtex::latex(Buffer const * bu
 	}
 
 	// database
-	// If we generate in a temp dir, we might need to give an
-	// absolute path there. This is a bit complicated since we can
-	// have a comma-separated list of bibliographies
-	string adb, db_out;
-	string db_in = getContents();
-	db_in = split(db_in, adb, ',');
-	while (!adb.empty()) {
-		db_out += latex_path(normalize_name(buffer, adb, ".bib"));
-		db_out += ',';
-		db_in= split(db_in, adb,',');
+	// If we are processing the LaTeX file in a temp directory then
+	// copy the .bib databases to this temp directory, mangling their
+	// names in the process. Store this mangled name in the list of
+	// all databases.
+	// (We need to do all this because BibTeX *really*, *really*
+	// can't handle "files with spaces" and Windows users tend to
+	// use such filenames.)
+	// Otherwise, store the (maybe absolute) path to the original,
+	// unmangled database name.
+	typedef boost::char_separator<char> Separator;
+	typedef boost::tokenizer<Separator> Tokenizer;
+
+	Separator const separator(",");
+	Tokenizer const tokens(getContents(), separator);
+	Tokenizer::const_iterator const begin = tokens.begin();
+	Tokenizer::const_iterator const end = tokens.end();
+
+	ostringstream dbs;
+	for (Tokenizer::const_iterator it = begin; it != end; ++it) {
+		string const input = trim(*it);
+		string database = normalize_name(buffer, input, ".bib");
+		string const in_file = database + ".bib";
+
+		if (!buffer->niceFile &&
+		    lyxrc.use_tempdir &&
+		    IsFileReadable(in_file)) {
+
+			database = mangled_filename(database);
+			string const out_file = MakeAbsPath(database + ".bib",
+							    buffer->tmppath);
+
+			bool const success = lyx::copy(in_file, out_file);
+			if (!success) {
+				lyxerr << "Failed to copy '" << in_file
+				       << "' to '" << out_file << "'"
+				       << endl;
+			}
+		}
+
+		if (it != begin)
+			dbs << ',';
+		dbs << latex_path(database);
+	}
+	string const dbs_str = dbs.str();
+
+	// Post this warning only once.
+	static bool warned_about_spaces = false;
+	if (!warned_about_spaces &&
+	    buffer->niceFile && dbs_str.find(' ') != string::npos) {
+		warned_about_spaces = true;
+
+		Alert::alert(_("LyX Warning!"),
+			     _("There are spaces in the paths to your BibTeX databases."),
+			     _("BibTeX will be unable to find them."));
+
+	}
+
+	int nnewlines = 1;
+	if (!dbs_str.empty()) {
+		++nnewlines;
+		os << "\\bibliography{" << dbs_str << "}\n";
 	}
 
-	db_out = rtrim(db_out, ",");
-	os   << "\\bibliography{" << db_out << "}\n";
-	return 2;
+	return nnewlines;
 }
 
 
Index: src/insets/insetgraphics.C
===================================================================
RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/insets/insetgraphics.C,v
retrieving revision 1.146.2.12
diff -u -p -r1.146.2.12 insetgraphics.C
--- src/insets/insetgraphics.C	4 Jul 2005 11:26:41 -0000	1.146.2.12
+++ src/insets/insetgraphics.C	14 Jul 2005 09:10:01 -0000
@@ -619,37 +619,24 @@ string const InsetGraphics::prepareFile(
 		<< "\tthe orig file is: " << orig_file_with_path << endl;
 
 	if (lyxrc.use_tempdir) {
-		string const ext_tmp = GetExtension(orig_file_with_path);
-		// without ext and /
-		temp_file = subst(
-			ChangeExtension(orig_file_with_path, string()), "/", "_");
-		// Replace ' ' in the file name with '_'
-		temp_file = subst(temp_file, " ", "_");
-		// without dots and again with ext
-		temp_file = ChangeExtension(
-			subst(temp_file, ".", "_"), ext_tmp);
-
-#if defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(_WIN32)
-		// Mangle the drive letter in a Windows-style path.
-		if (temp_file.size() >= 2 && temp_file[1] == ':')
-			temp_file[1] = '_';
-#endif
-
-		// now we have any_dir_file.ext
+		temp_file = mangled_filename(orig_file_with_path, buf->tmppath);
 		temp_file = MakeAbsPath(temp_file, buf->tmppath);
+
 		lyxerr[Debug::GRAPHICS]
 			<< "\tchanged to: " << temp_file << endl;
 
-		// if the file doen't exists, copy it into the tempdir
+		// if the file doesn't exist, copy it into the tempdir
 		if (file_has_changed || !IsFileReadable(temp_file)) {
 			bool const success = lyx::copy(orig_file_with_path, temp_file);
 			lyxerr[Debug::GRAPHICS]
-				<< "\tcopying from " << orig_file_with_path << " to "
+				<< "\tcopying from "
+				<< orig_file_with_path << " to "
 				<< temp_file
 				<< (success ? " succeeded\n" : " failed\n");
 			if (!success) {
-				Alert::alert(_("Cannot copy file"), orig_file_with_path,
-					_("into tempdir"));
+				Alert::alert(_("Cannot copy file"),
+					     orig_file_with_path,
+					     _("into tempdir"));
 				return orig_file;
 			}
 		}
Index: src/support/filetools.C
===================================================================
RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/support/filetools.C,v
retrieving revision 1.146.2.19
diff -u -p -r1.146.2.19 filetools.C
--- src/support/filetools.C	5 Jul 2005 07:45:44 -0000	1.146.2.19
+++ src/support/filetools.C	14 Jul 2005 09:10:03 -0000
@@ -41,8 +41,9 @@
 #include <fcntl.h>
 #include <cerrno>
 
-#include <utility>
 #include <fstream>
+#include <map>
+#include <utility>
 
 
 // Which part of this is still necessary? (JMarc).
@@ -1505,3 +1506,65 @@ string const readBB_from_PSFile(string c
 	readBB_lyxerrMessage(file_, zipped, "no bb found");
 	return string();
 }
+
+
+string const mangled_filename(string const & filename, string const & dir)
+{
+	// We need to make sure that every FileName instance for a given
+	// filename returns the same mangled name.
+	typedef std::map<string, string> MangledMap;
+	static MangledMap mangledNames;
+	MangledMap::const_iterator const it = mangledNames.find(filename);
+	if (it != mangledNames.end())
+		return (*it).second;
+
+	// Now the real work
+	string mname = os::internal_path(filename);
+	// Remove the extension.
+	mname = ChangeExtension(filename, string());
+	// Replace '/' in the file name with '_'
+	mname = subst(mname, "/", "_");
+	// Replace '.' in the file name with '_'
+	mname = subst(mname, ".", "_");
+	// Replace ' ' in the file name with '_'
+	mname = subst(mname, " ", "_");
+	// Replace ':' in the file name with '_'
+	mname = subst(mname, ":", "_");
+	// Add the extension back on
+	mname = ChangeExtension(mname, GetExtension(filename));
+
+	// Prepend a counter to the filename. This is necessary to make
+	// the mangled name unique.
+	static int counter = 0;
+	std::ostringstream s;
+	s << counter++ << mname;
+	mname = STRCONV(s.str());
+
+	// Experiments show that MiKTeX's YAP (version 2.4.1803)
+	// will crash if the string referencing the file name in
+	// the .dvi file is longer than 220 characters.
+	// This string contains about 50 chars-worth of other data,
+	// leaving us, say, 160 characters for the file name itself.
+	// (Erring on the side of caution.)
+	string::size_type max_length = 160;
+	if (dir.size() - 1 < max_length) {
+		// If dir.size() > max_length, all bets are off anyway.
+		// "+ 1" for the directory separator.
+		max_length -= dir.size() + 1;
+
+		// If the mangled file name is too long, hack it to fit.
+		// We know we're guaranteed to have a unique file name because
+		// of the counter.
+		if (mname.size() > max_length) {
+			int const half = (int(max_length) / 2) - 2;
+			if (half > 0) {
+				mname = mname.substr(0, half) + "___" +
+					mname.substr(mname.size() - half);
+			}
+		}
+	}
+
+	mangledNames[filename] = mname;
+	return mname;
+}
+
Index: src/support/filetools.h
===================================================================
RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/support/filetools.h,v
retrieving revision 1.38.2.7
diff -u -p -r1.38.2.7 filetools.h
--- src/support/filetools.h	4 Jul 2005 11:26:42 -0000	1.38.2.7
+++ src/support/filetools.h	14 Jul 2005 09:10:03 -0000
@@ -247,8 +247,21 @@ void removeAutosaveFile(string const & f
 /// read the BoundingBox entry from a ps/eps/pdf-file
 string const readBB_from_PSFile(string const & file);
 
-typedef std::pair<int, string> cmd_ret;
+/** @param filename the file path that is to be mangled.
+ *  @param dir the directory that will contain this file with
+ *  its mangled name. This information is used by the mangling
+ *  algorithm when determining the maximum allowable length of
+ *  the mangled name.
+ *  @returns a mangled representation of the input @c filename.
+ *  Eg C:/foo bar/baz.eps -> 0C__foo_bar_baz.eps
+ *  The integer prefix is used to guarantee that the file name
+ *  is unique.
+ *  Only the file name is returned. It is not prepended with @c dir.
+ */
+string const mangled_filename(string const & filename,
+                              string const & dir = string());
 
+typedef std::pair<int, string> cmd_ret;
 cmd_ret const RunCommand(string const & cmd);
 
 #endif

Reply via email to