diff --git a/src/tex2lyx/tex2lyx.1in b/src/tex2lyx/tex2lyx.1in index 1ac5161efb..b430909f03 100644 --- a/src/tex2lyx/tex2lyx.1in +++ b/src/tex2lyx/tex2lyx.1in @@ -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 diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp index a049aab6f5..384c22836d 100644 --- a/src/tex2lyx/tex2lyx.cpp +++ b/src/tex2lyx/tex2lyx.cpp @@ -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 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::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 masterFilePath; + return input ? masterFilePathTeX : masterFilePathLyX; } -string getParentFilePath() +string getParentFilePath(bool input) { - return parentFilePath; + 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 copy_files; +} + + +bool overwriteFiles() +{ + 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; } diff --git a/src/tex2lyx/tex2lyx.h b/src/tex2lyx/tex2lyx.h index 66a60fd12e..a471a0f3f4 100644 --- a/src/tex2lyx/tex2lyx.h +++ b/src/tex2lyx/tex2lyx.h @@ -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(); /*! diff --git a/src/tex2lyx/text.cpp b/src/tex2lyx/text.cpp index 53836804b2..db9f9b5adc 100644 --- a/src/tex2lyx/text.cpp +++ b/src/tex2lyx/text.cpp @@ -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 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);