]> git.lyx.org Git - lyx.git/commitdiff
Add -copyfiles command line option to tex2lyx
authorGeorg Baum <baum@lyx.org>
Wed, 3 Oct 2012 11:23:27 +0000 (13:23 +0200)
committerGeorg Baum <baum@lyx.org>
Wed, 3 Oct 2012 11:23:27 +0000 (13:23 +0200)
If this option is given, included files will be copied to the output directory.
Also -roundtrip is now allowed with given output file.
-copyfiles is useful if you want to ensure that no file (not even an included
one) is overwritten by a subsequent export from LyX. Both changes are needed
for unit tests that do not write to the source directory.

src/tex2lyx/tex2lyx.1in
src/tex2lyx/tex2lyx.cpp
src/tex2lyx/tex2lyx.h
src/tex2lyx/text.cpp

index 1ac5161efbf086090a1156fa385394eb2ffac121..b430909f030931363a23e93b7cec22f286927337 100644 (file)
@@ -21,7 +21,7 @@ options.
 .PP
 \fBtex2lyx\fR [ \fB\-userdir\fR \fIuserdir\fR ] [ \fB\-systemdir\fR \fIsystemdir\fR ]
 [ \fB\-n\fR ] [ \fB\-c\fR \fItextclass\fR ] [\ \fB\-s\fR\ \fIsfile1\fR[,\fIsfile2\fR...]] [
-\fB\-roundtrip\fR ] \fIinputfile\fR [ \fIoutputfile\fR ]
+\fB\-roundtrip\fR ] [ \fB\-copyfiles\fR ] \fIinputfile\fR [ \fIoutputfile\fR ]
 .\" .PP
 .\" \fBtex2lyx\fR [ \fB\-userdir\fR \fIuserdir\fR ] [ \fB\-systemdir\fR \fIsystemdir\fR ]
 .\" [\ \fB\-r\fR\ \fIrenv1\fR[,\fIrenv2\fR...]] [\ \fB\-s\fR\ \fIsfile1\fR[,\fIsfile2\fR...]]
@@ -70,12 +70,21 @@ Specify a user directory. Normally, you shouldn't need this. Your LyX user direc
 chosen. Cf. the section \f(CWFILES\fR for details.
 .TP
 .BI \-roundtrip
-Call LyX to re-export the created output file to LaTeX. The output file name
-is always determined automatically to avoid over-writing the input file by
-accident: If the input file is named \fIfoo.tex\fR the output file will be
+Call LyX to re-export the created output file to LaTeX. If the output file name
+is not given it is determined automatically to avoid over-writing the input file
+by accident: If the input file is named \fIfoo.tex\fR the output file will be
 named \fIfoo.lyx.lyx\fR, and the re-exported file will be named
 \fIfoo.lyx.tex\fR.
 .TP
+.BI \-copyfiles
+Copy all included files below the input directory and that \fBtex2lyx\fR is
+aware of to the output directory if the output file is located in a different
+directory than the input file. This is useful if you want to ensure that no
+included file is overwritten (either in roundtrip mode or by a later export
+from LyX). Please note that the resulting document may be uncompilable. This
+happens if it needs files that \fBtex2lyx\fR does not know about and therefore
+does not copy to the output directory.
+.TP
 .BI \-help
 Help. Print out usage information and quit.
 .TP
index a049aab6f5fdb85d280741e2e785cc87a9ffe581..384c22836d02e6ab4a113a193d60c2000caf20c8 100644 (file)
@@ -446,6 +446,7 @@ void read_syntaxfile(FileName const & file_name)
 string documentclass;
 string default_encoding;
 string syntaxfile;
+bool copy_files = false;
 bool overwrite_files = false;
 int error_code = 0;
 
@@ -458,6 +459,7 @@ int parse_help(string const &, string const &)
        cerr << "Usage: tex2lyx [options] infile.tex [outfile.lyx]\n"
                "Options:\n"
                "\t-c textclass       Declare the textclass.\n"
+               "\t-copyfiles         Copy all included files to the directory of outfile.lyx.\n"
                "\t-e encoding        Set the default encoding (latex name).\n"
                "\t-f                 Force overwrite of .lyx files.\n"
                "\t-help              Print this message and quit.\n"
@@ -571,6 +573,13 @@ int parse_roundtrip(string const &, string const &)
 }
 
 
+int parse_copyfiles(string const &, string const &)
+{
+       copy_files = true;
+       return 0;
+}
+
+
 void easyParse(int & argc, char * argv[])
 {
        map<string, cmd_helper> cmdmap;
@@ -589,6 +598,7 @@ void easyParse(int & argc, char * argv[])
        cmdmap["-sysdir"] = parse_sysdir;
        cmdmap["-userdir"] = parse_userdir;
        cmdmap["-roundtrip"] = parse_roundtrip;
+       cmdmap["-copyfiles"] = parse_copyfiles;
 
        for (int i = 1; i < argc; ++i) {
                map<string, cmd_helper>::const_iterator it
@@ -619,21 +629,42 @@ void easyParse(int & argc, char * argv[])
 
 
 // path of the first parsed file
-string masterFilePath;
+string masterFilePathLyX;
+string masterFilePathTeX;
 // path of the currently parsed file
-string parentFilePath;
+string parentFilePathTeX;
 
 } // anonymous namespace
 
 
-string getMasterFilePath()
+string getMasterFilePath(bool input)
+{
+       return input ? masterFilePathTeX : masterFilePathLyX;
+}
+
+string getParentFilePath(bool input)
+{
+       if (input)
+               return parentFilePathTeX;
+       string const rel = to_utf8(makeRelPath(from_utf8(masterFilePathTeX),
+                                              from_utf8(parentFilePathTeX)));
+       if (rel.substr(0, 3) == "../") {
+               // The parent is not below the master - keep the path
+               return parentFilePathTeX;
+       }
+       return makeAbsPath(rel, masterFilePathLyX).absFileName();
+}
+
+
+bool copyFiles()
 {
-       return masterFilePath;
+       return copy_files;
 }
 
-string getParentFilePath()
+
+bool overwriteFiles()
 {
-       return parentFilePath;
+       return overwrite_files;
 }
 
 
@@ -645,7 +676,7 @@ namespace {
  *  be used more than once for included documents.
  *  Caution: Overwrites the existing preamble settings if the new document
  *  contains a preamble.
- *  You must ensure that \p parentFilePath is properly set before calling
+ *  You must ensure that \p parentFilePathTeX is properly set before calling
  *  this function!
  */
 bool tex2lyx(idocstream & is, ostream & os, string encoding)
@@ -720,10 +751,10 @@ bool tex2lyx(FileName const & infilename, ostream & os, string const & encoding)
                     << "\" for reading." << endl;
                return false;
        }
-       string const oldParentFilePath = parentFilePath;
-       parentFilePath = onlyPath(infilename.absFileName());
+       string const oldParentFilePath = parentFilePathTeX;
+       parentFilePathTeX = onlyPath(infilename.absFileName());
        bool retval = tex2lyx(is, os, encoding);
-       parentFilePath = oldParentFilePath;
+       parentFilePathTeX = oldParentFilePath;
        return retval;
 }
 
@@ -826,20 +857,28 @@ int main(int argc, char * argv[])
        infilename = makeAbsPath(infilename).absFileName();
 
        string outfilename;
-       if (roundtrip) {
-               if (argc > 2) {
-                       // Do not allow a user supplied output filename
-                       // (otherwise it could easily happen that LyX would
-                       // overwrite the original .tex file)
-                       cerr << "Error: output filename must not be given in roundtrip mode."
-                            << endl;
-                       return EXIT_FAILURE;
-               }
-               outfilename = changeExtension(infilename, ".lyx.lyx");
-       } else if (argc > 2) {
+       if (argc > 2) {
                outfilename = internal_path(os::utf8_argv(2));
                if (outfilename != "-")
                        outfilename = makeAbsPath(outfilename).absFileName();
+               if (roundtrip) {
+                       if (outfilename == "-") {
+                               cerr << "Error: Writing to standard output is "
+                                       "not supported in roundtrip mode."
+                                    << endl;
+                               return EXIT_FAILURE;
+                       }
+                       string texfilename = changeExtension(outfilename, ".tex");
+                       if (equivalent(FileName(infilename), FileName(texfilename))) {
+                               cerr << "Error: The input file `" << infilename
+                                    << "´ would be overwritten by the TeX file exported from `"
+                                    << outfilename << "´ in roundtrip mode." << endl;
+                               return EXIT_FAILURE;
+                       }
+               }
+       } else if (roundtrip) {
+               // avoid overwriting the input file
+               outfilename = changeExtension(infilename, ".lyx.lyx");
        } else
                outfilename = changeExtension(infilename, ".lyx");
 
@@ -876,17 +915,22 @@ int main(int argc, char * argv[])
        theModuleList.read();
 
        // The real work now.
-       masterFilePath = onlyPath(infilename);
-       parentFilePath = masterFilePath;
+       masterFilePathTeX = onlyPath(infilename);
+       parentFilePathTeX = masterFilePathTeX;
        if (outfilename == "-") {
+               // assume same directory as input file
+               masterFilePathLyX = masterFilePathTeX;
                if (tex2lyx(FileName(infilename), cout, default_encoding))
                        return EXIT_SUCCESS;
-       } else if (roundtrip) {
-               if (tex2tex(infilename, FileName(outfilename), default_encoding))
-                       return EXIT_SUCCESS;
        } else {
-               if (tex2lyx(infilename, FileName(outfilename), default_encoding))
-                       return EXIT_SUCCESS;
+               masterFilePathLyX = onlyPath(outfilename);
+               if (roundtrip) {
+                       if (tex2tex(infilename, FileName(outfilename), default_encoding))
+                               return EXIT_SUCCESS;
+               } else {
+                       if (tex2lyx(infilename, FileName(outfilename), default_encoding))
+                               return EXIT_SUCCESS;
+               }
        }
        return EXIT_FAILURE;
 }
index 66a60fd12e497fd77ecb4d802cc047eafe00e482..a471a0f3f447627ef133ddb15dd100366d109661 100644 (file)
@@ -173,10 +173,14 @@ extern bool is_nonCJKJapanese;
 /// LyX format that is created by tex2lyx
 extern int const LYX_FORMAT;
 
-/// path of the master .tex file
-extern std::string getMasterFilePath();
-/// path of the currently processed .tex file
-extern std::string getParentFilePath();
+/// Absolute path of the master .lyx or .tex file
+extern std::string getMasterFilePath(bool input);
+/// Absolute path of the currently processed .lyx or .tex file
+extern std::string getParentFilePath(bool input);
+/// Is it allowed to overwrite existing files?
+extern bool overwriteFiles();
+/// Do we need to copy included files to the output directory?
+extern bool copyFiles();
 
 
 /*!
index 53836804b276aaad5fff8175f8c21e76ecb91a98..db9f9b5adc7be4b10020a6b7453a2bbdcc40c6d7 100644 (file)
@@ -1834,15 +1834,88 @@ string const normalize_filename(string const & name)
 
 /// Convert \p name from TeX convention (relative to master file) to LyX
 /// convention (relative to .lyx file) if it is relative
-void fix_relative_filename(string & name)
+void fix_child_filename(string & name)
 {
-       if (FileName::isAbsolute(name))
-               return;
+       string const absMasterTeX = getMasterFilePath(true);
+       bool const isabs = FileName::isAbsolute(name);
+       // convert from "relative to .tex master" to absolute original path
+       if (!isabs)
+               name = makeAbsPath(name, absMasterTeX).absFileName();
+       bool copyfile = copyFiles();
+       // convert from absolute original path to "relative to master file"
+       string const rel = to_utf8(makeRelPath(from_utf8(name),
+                                              from_utf8(absMasterTeX)));
+       // Do not copy if the file is not in or below the directory of the
+       // master, since in this case the new path might be impossible to
+       // create. Example:
+       // absMasterTeX = "/foo/bar/"
+       // absMasterLyX = "/bar/"
+       // name = "/baz.eps" => new absolute name would be "/../baz.eps"
+       if (copyfile && rel.substr(0, 3) == "../")
+               copyfile = false;
+       string const absParentLyX = getParentFilePath(false);
+       if (copyfile) {
+               // re-interpret "relative to .tex file" as "relative to .lyx file"
+               // (is different if the master .lyx file resides in a
+               // different path than the master .tex file)
+               string const absMasterLyX = getMasterFilePath(false);
+               name = makeAbsPath(rel, absMasterLyX).absFileName();
+               if (!isabs) {
+                       // convert from absolute original path to
+                       // "relative to .lyx file"
+                       name = to_utf8(makeRelPath(from_utf8(name),
+                                                  from_utf8(absParentLyX)));
+               }
+       }
+       else if (!isabs) {
+               // convert from absolute original path to "relative to .lyx file"
+               name = to_utf8(makeRelPath(from_utf8(name),
+                                          from_utf8(absParentLyX)));
+       }
+}
+
 
-       string const absMaster = makeAbsPath(getMasterFilePath()).absFileName();
-       string const absParent = makeAbsPath(getParentFilePath()).absFileName();
-       string const abs = makeAbsPath(name, absMaster).absFileName();
-       name = to_utf8(makeRelPath(from_utf8(abs), from_utf8(absParent)));
+void copy_file(FileName const & src, string dstname)
+{
+       if (!copyFiles())
+               return;
+       string const absParent = getParentFilePath(false);
+       FileName dst;
+       if (FileName::isAbsolute(dstname))
+               dst = FileName(dstname);
+       else
+               dst = makeAbsPath(dstname, absParent);
+       string const absMaster = getMasterFilePath(false);
+       string const rel = to_utf8(makeRelPath(from_utf8(dst.absFileName()),
+                                              from_utf8(absMaster)));
+       // Do not copy if the file is not in or below the directory of the
+       // master (see above)
+       if (rel.substr(0, 3) == "../")
+               return;
+       FileName const srcpath = src.onlyPath();
+       FileName const dstpath = dst.onlyPath();
+       if (equivalent(srcpath, dstpath))
+               return;
+       if (!dstpath.isDirectory()) {
+               if (!dstpath.createPath()) {
+                       cerr << "Warning: Could not create directory for file `"
+                            << dst.absFileName() << "´." << endl;
+                       return;
+               }
+       }
+       if (dst.isReadableFile()) {
+               if (overwriteFiles())
+                       cerr << "Warning: Overwriting existing file `"
+                            << dst.absFileName() << "´." << endl;
+               else {
+                       cerr << "Warning: Not overwriting existing file `"
+                            << dst.absFileName() << "´." << endl;
+                       return;
+               }
+       }
+       if (!src.copyTo(dst))
+               cerr << "Warning: Could not copy file `" << src.absFileName()
+                    << "´ to `" << dst.absFileName() << "´." << endl;
 }
 
 
@@ -2533,7 +2606,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                                skip_braces(p);
                                                p.get_token();
                                                string name = normalize_filename(p.verbatim_item());
-                                               string const path = makeAbsPath(getMasterFilePath()).absFileName();
+                                               string const path = getMasterFilePath(true);
                                                // We want to preserve relative / absolute filenames,
                                                // therefore path is only used for testing
                                                // The file extension is in every case ".tex".
@@ -2548,9 +2621,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                                        if (!Gnumeric_name.empty())
                                                                name = Gnumeric_name;
                                                }
-                                               if (makeAbsPath(name, path).exists())
-                                                       fix_relative_filename(name);
-                                               else
+                                               FileName const absname = makeAbsPath(name, path);
+                                               if (absname.exists()) {
+                                                       fix_child_filename(name);
+                                                       copy_file(absname, name);
+                                               } else
                                                        cerr << "Warning: Could not find file '"
                                                             << name << "'." << endl;
                                                context.check_layout(os);
@@ -2560,7 +2635,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                                end_inset(os);
                                                context.check_layout(os);
                                                macro = false;
-                                               // register the packages that are automatically reloaded
+                                               // register the packages that are automatically loaded
                                                // by the Gnumeric template
                                                registerExternalTemplatePackages("GnumericSpreadsheet");
                                        }
@@ -2760,7 +2835,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                opts["clip"] = string();
                        string name = normalize_filename(p.verbatim_item());
 
-                       string const path = makeAbsPath(getMasterFilePath()).absFileName();
+                       string const path = getMasterFilePath(true);
                        // We want to preserve relative / absolute filenames,
                        // therefore path is only used for testing
                        if (!makeAbsPath(name, path).exists()) {
@@ -2795,9 +2870,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                }
                        }
 
-                       if (makeAbsPath(name, path).exists())
-                               fix_relative_filename(name);
-                       else
+                       FileName const absname = makeAbsPath(name, path);
+                       if (absname.exists()) {
+                               fix_child_filename(name);
+                               copy_file(absname, name);
+                       } else
                                cerr << "Warning: Could not find graphics file '"
                                     << name << "'." << endl;
 
@@ -3725,7 +3802,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                name += p.get_token().asInput();
                        context.check_layout(os);
                        string filename(normalize_filename(p.getArg('{', '}')));
-                       string const path = makeAbsPath(getMasterFilePath()).absFileName();
+                       string const path = getMasterFilePath(true);
                        // We want to preserve relative / absolute filenames,
                        // therefore path is only used for testing
                        if ((t.cs() == "include" || t.cs() == "input") &&
@@ -3743,13 +3820,13 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        if (makeAbsPath(filename, path).exists()) {
                                string const abstexname =
                                        makeAbsPath(filename, path).absFileName();
-                               string const abslyxname =
-                                       changeExtension(abstexname, ".lyx");
                                string const absfigname =
                                        changeExtension(abstexname, ".fig");
-                               fix_relative_filename(filename);
+                               fix_child_filename(filename);
                                string const lyxname =
                                        changeExtension(filename, ".lyx");
+                               string const abslyxname = makeAbsPath(
+                                       lyxname, getParentFilePath(false)).absFileName();
                                bool xfig = false;
                                external = FileName(absfigname).exists();
                                if (t.cs() == "input") {
@@ -3795,16 +3872,24 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                }
                                if (external) {
                                        outname = changeExtension(filename, ".fig");
+                                       FileName abssrc(changeExtension(abstexname, ".fig"));
+                                       copy_file(abssrc, outname);
                                } else if (xfig) {
                                        // Don't try to convert, the result
                                        // would be full of ERT.
                                        outname = filename;
+                                       FileName abssrc(abstexname);
+                                       copy_file(abssrc, outname);
                                } else if (t.cs() != "verbatiminput" &&
                                    tex2lyx(abstexname, FileName(abslyxname),
                                            p.getEncoding())) {
                                        outname = lyxname;
+                                       // no need to call copy_file
+                                       // tex2lyx creates the file
                                } else {
                                        outname = filename;
+                                       FileName abssrc(abstexname);
+                                       copy_file(abssrc, outname);
                                }
                        } else {
                                cerr << "Warning: Could not find included file '"
@@ -4185,7 +4270,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        vector<string> keys;
                        split_map(arg, opts, keys);
                        string name = normalize_filename(p.verbatim_item());
-                       string const path = makeAbsPath(getMasterFilePath()).absFileName();
+                       string const path = getMasterFilePath(true);
                        // We want to preserve relative / absolute filenames,
                        // therefore path is only used for testing
                        if (!makeAbsPath(name, path).exists()) {
@@ -4199,9 +4284,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                        pdflatex = true;
                                }
                        }
-                       if (makeAbsPath(name, path).exists())
-                               fix_relative_filename(name);
-                       else
+                       FileName const absname = makeAbsPath(name, path);
+                       if (absname.exists())
+                       {
+                               fix_child_filename(name);
+                               copy_file(absname, name);
+                       } else
                                cerr << "Warning: Could not find file '"
                                     << name << "'." << endl;
                        // write output
@@ -4250,7 +4338,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                else if (t.cs() == "loadgame") {
                        p.skip_spaces();
                        string name = normalize_filename(p.verbatim_item());
-                       string const path = makeAbsPath(getMasterFilePath()).absFileName();
+                       string const path = getMasterFilePath(true);
                        // We want to preserve relative / absolute filenames,
                        // therefore path is only used for testing
                        if (!makeAbsPath(name, path).exists()) {
@@ -4262,9 +4350,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                if (!lyxskak_name.empty())
                                        name = lyxskak_name;
                        }
-                       if (makeAbsPath(name, path).exists())
-                               fix_relative_filename(name);
-                       else
+                       FileName const absname = makeAbsPath(name, path);
+                       if (absname.exists())
+                       {
+                               fix_child_filename(name);
+                               copy_file(absname, name);
+                       } else
                                cerr << "Warning: Could not find file '"
                                     << name << "'." << endl;
                        context.check_layout(os);