Paul A. Rubin wrote:
Just to clarify things a bit: the (approximately) 220 character limit I think I found in yap applies not to the path/filename but to the argument string to the specials command. That argument string includes the literal "PSFile=", the path/filename, bounding box information and assorted spaces and quotation marks. IIRC, the path/filename itself accounted for somewhat over half the string. So the likelihood of encountering it in practice may be greater than it would appear at first blush.

Thanks for the detailed report, Paul. I find that I can reproduce the problem exactly as you describe it.

The strategy I've adopted is to ensure that the mangled file name is no more than 165 characters long:

    // 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, 165 characters for the file name itself.
    int const max_filelength = 165 - (int(buf->tmppath.size()) + 1);
    temp_file = mangled_filename(orig_file_with_path, max_filelength);
    temp_file = MakeAbsPath(temp_file, buf->tmppath);

This means that Uwe's test file

C:\Documents and Settings\Angus\Desktop\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaatiger.eps

is copied to the temp directory as

C:\Documents and Settings\Angus\Local Settings\Temp\lyx_tmpdir3652a01440\lyx_tmpbuf0\0C__Documents_and_Settings_Angus_Deskt___aaaaaaaaaaaaaaaaaaaaaaaaaaaaatiger.eps

I ensure that the file name remains unique by using the same counter prefix strategy as is used in the 1.4 series. (Here the counter is 0.)

I've put the resulting installer here:
http://www.lyx.org/~leeming/lyx_setup_136_paul.exe

If you can confirm that it fixes your problems then I'll move it onto the wiki.

Is this problem unique to 1.3.6? Someone mentioned relative paths in 1.4. Assuming plausible lengths for file names, will that cure the problem?

The problem will be less severe in LyX 1.4 because the .dvi file will store only the mangled file name not the absolute tempdir path to it. That's a difference of about 80 characters here.

Angus
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 -a -u -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  8 Jul 2005 22:45:02 -0000
@@ -91,6 +91,7 @@
 #include "BoostFormat.h"
 
 #include <algorithm> // For the std::max
+#include <map>
 
 using std::ostream;
 using std::endl;
@@ -523,6 +524,64 @@
 }
 
 
+namespace {
+
+// Create a human-readable filename version of the input absolute path.
+// Eg /home/angus/foo\ bar/image.eps -> ID_home_angus_foo_bar_image.eps
+// where ID is a unique integer ID.
+string const mangled_filename(string const & input, int const max_length)
+{
+       // 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(input);
+       if (it != mangledNames.end())
+               return (*it).second;
+
+       // Now the real work
+       string mname = os::internal_path(input);
+       // Remove the extension.
+       mname = ChangeExtension(input, 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, " ", "_");
+       // Add the extension back on
+       mname = ChangeExtension(mname, GetExtension(input));
+
+#if defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(_WIN32)
+       // Mangle the drive letter in a Windows-style path.
+       if (mname.size() >= 2 && mname[1] == ':')
+               mname[1] = '_';
+#endif
+
+       // 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());
+
+       // 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 (int(mname.size()) > max_length) {
+               int const half = (max_length / 2) - 2;
+               if (half > 0) {
+                       mname = mname.substr(0, half) + "___" +
+                               mname.substr(mname.size() - half);
+               }
+       }
+       mangledNames[input] = mname;
+       return mname;
+}
+
+}
+
+
 string const InsetGraphics::prepareFile(Buffer const * buf) const
 {
        // LaTeX can cope if the graphics file doesn't exist, so just
@@ -619,37 +678,31 @@
                << "\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
+               // 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, 165 characters for the file name itself.
+               int const max_filelength = 165 - (int(buf->tmppath.size()) + 1);
+               temp_file = mangled_filename(orig_file_with_path, 
max_filelength);
 
                // now we have any_dir_file.ext
                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;
                        }
                }

Reply via email to