Add -copyfiles command line option to tex2lyx

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.
This commit is contained in:
Georg Baum 2012-10-03 13:23:27 +02:00
parent 02c73cd721
commit 24181cf28e
4 changed files with 212 additions and 64 deletions

View File

@ -21,7 +21,7 @@ options.
.PP .PP
\fBtex2lyx\fR [ \fB\-userdir\fR \fIuserdir\fR ] [ \fB\-systemdir\fR \fIsystemdir\fR ] \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\-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 .\" .PP
.\" \fBtex2lyx\fR [ \fB\-userdir\fR \fIuserdir\fR ] [ \fB\-systemdir\fR \fIsystemdir\fR ] .\" \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...]] .\" [\ \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. chosen. Cf. the section \f(CWFILES\fR for details.
.TP .TP
.BI \-roundtrip .BI \-roundtrip
Call LyX to re-export the created output file to LaTeX. The output file name Call LyX to re-export the created output file to LaTeX. If the output file name
is always determined automatically to avoid over-writing the input file by is not given it is determined automatically to avoid over-writing the input file
accident: If the input file is named \fIfoo.tex\fR the output file will be 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 named \fIfoo.lyx.lyx\fR, and the re-exported file will be named
\fIfoo.lyx.tex\fR. \fIfoo.lyx.tex\fR.
.TP .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 .BI \-help
Help. Print out usage information and quit. Help. Print out usage information and quit.
.TP .TP

View File

@ -446,6 +446,7 @@ void read_syntaxfile(FileName const & file_name)
string documentclass; string documentclass;
string default_encoding; string default_encoding;
string syntaxfile; string syntaxfile;
bool copy_files = false;
bool overwrite_files = false; bool overwrite_files = false;
int error_code = 0; int error_code = 0;
@ -458,6 +459,7 @@ int parse_help(string const &, string const &)
cerr << "Usage: tex2lyx [options] infile.tex [outfile.lyx]\n" cerr << "Usage: tex2lyx [options] infile.tex [outfile.lyx]\n"
"Options:\n" "Options:\n"
"\t-c textclass Declare the textclass.\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-e encoding Set the default encoding (latex name).\n"
"\t-f Force overwrite of .lyx files.\n" "\t-f Force overwrite of .lyx files.\n"
"\t-help Print this message and quit.\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[]) void easyParse(int & argc, char * argv[])
{ {
map<string, cmd_helper> cmdmap; map<string, cmd_helper> cmdmap;
@ -589,6 +598,7 @@ void easyParse(int & argc, char * argv[])
cmdmap["-sysdir"] = parse_sysdir; cmdmap["-sysdir"] = parse_sysdir;
cmdmap["-userdir"] = parse_userdir; cmdmap["-userdir"] = parse_userdir;
cmdmap["-roundtrip"] = parse_roundtrip; cmdmap["-roundtrip"] = parse_roundtrip;
cmdmap["-copyfiles"] = parse_copyfiles;
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
map<string, cmd_helper>::const_iterator it map<string, cmd_helper>::const_iterator it
@ -619,21 +629,42 @@ void easyParse(int & argc, char * argv[])
// path of the first parsed file // path of the first parsed file
string masterFilePath; string masterFilePathLyX;
string masterFilePathTeX;
// path of the currently parsed file // path of the currently parsed file
string parentFilePath; string parentFilePathTeX;
} // anonymous namespace } // 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. * be used more than once for included documents.
* Caution: Overwrites the existing preamble settings if the new document * Caution: Overwrites the existing preamble settings if the new document
* contains a preamble. * 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! * this function!
*/ */
bool tex2lyx(idocstream & is, ostream & os, string encoding) 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; << "\" for reading." << endl;
return false; return false;
} }
string const oldParentFilePath = parentFilePath; string const oldParentFilePath = parentFilePathTeX;
parentFilePath = onlyPath(infilename.absFileName()); parentFilePathTeX = onlyPath(infilename.absFileName());
bool retval = tex2lyx(is, os, encoding); bool retval = tex2lyx(is, os, encoding);
parentFilePath = oldParentFilePath; parentFilePathTeX = oldParentFilePath;
return retval; return retval;
} }
@ -826,20 +857,28 @@ int main(int argc, char * argv[])
infilename = makeAbsPath(infilename).absFileName(); infilename = makeAbsPath(infilename).absFileName();
string outfilename; string outfilename;
if (roundtrip) { if (argc > 2) {
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) {
outfilename = internal_path(os::utf8_argv(2)); outfilename = internal_path(os::utf8_argv(2));
if (outfilename != "-") if (outfilename != "-")
outfilename = makeAbsPath(outfilename).absFileName(); 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 } else
outfilename = changeExtension(infilename, ".lyx"); outfilename = changeExtension(infilename, ".lyx");
@ -876,17 +915,22 @@ int main(int argc, char * argv[])
theModuleList.read(); theModuleList.read();
// The real work now. // The real work now.
masterFilePath = onlyPath(infilename); masterFilePathTeX = onlyPath(infilename);
parentFilePath = masterFilePath; parentFilePathTeX = masterFilePathTeX;
if (outfilename == "-") { if (outfilename == "-") {
// assume same directory as input file
masterFilePathLyX = masterFilePathTeX;
if (tex2lyx(FileName(infilename), cout, default_encoding)) if (tex2lyx(FileName(infilename), cout, default_encoding))
return EXIT_SUCCESS; return EXIT_SUCCESS;
} else if (roundtrip) {
if (tex2tex(infilename, FileName(outfilename), default_encoding))
return EXIT_SUCCESS;
} else { } else {
if (tex2lyx(infilename, FileName(outfilename), default_encoding)) masterFilePathLyX = onlyPath(outfilename);
return EXIT_SUCCESS; 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; return EXIT_FAILURE;
} }

View File

@ -173,10 +173,14 @@ extern bool is_nonCJKJapanese;
/// LyX format that is created by tex2lyx /// LyX format that is created by tex2lyx
extern int const LYX_FORMAT; extern int const LYX_FORMAT;
/// path of the master .tex file /// Absolute path of the master .lyx or .tex file
extern std::string getMasterFilePath(); extern std::string getMasterFilePath(bool input);
/// path of the currently processed .tex file /// Absolute path of the currently processed .lyx or .tex file
extern std::string getParentFilePath(); 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();
/*! /*!

View File

@ -1834,15 +1834,88 @@ string const normalize_filename(string const & name)
/// Convert \p name from TeX convention (relative to master file) to LyX /// Convert \p name from TeX convention (relative to master file) to LyX
/// convention (relative to .lyx file) if it is relative /// 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)) string const absMasterTeX = getMasterFilePath(true);
return; 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(); void copy_file(FileName const & src, string dstname)
string const abs = makeAbsPath(name, absMaster).absFileName(); {
name = to_utf8(makeRelPath(from_utf8(abs), from_utf8(absParent))); 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); skip_braces(p);
p.get_token(); p.get_token();
string name = normalize_filename(p.verbatim_item()); 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, // We want to preserve relative / absolute filenames,
// therefore path is only used for testing // therefore path is only used for testing
// The file extension is in every case ".tex". // 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()) if (!Gnumeric_name.empty())
name = Gnumeric_name; name = Gnumeric_name;
} }
if (makeAbsPath(name, path).exists()) FileName const absname = makeAbsPath(name, path);
fix_relative_filename(name); if (absname.exists()) {
else fix_child_filename(name);
copy_file(absname, name);
} else
cerr << "Warning: Could not find file '" cerr << "Warning: Could not find file '"
<< name << "'." << endl; << name << "'." << endl;
context.check_layout(os); context.check_layout(os);
@ -2560,7 +2635,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
end_inset(os); end_inset(os);
context.check_layout(os); context.check_layout(os);
macro = false; macro = false;
// register the packages that are automatically reloaded // register the packages that are automatically loaded
// by the Gnumeric template // by the Gnumeric template
registerExternalTemplatePackages("GnumericSpreadsheet"); registerExternalTemplatePackages("GnumericSpreadsheet");
} }
@ -2760,7 +2835,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
opts["clip"] = string(); opts["clip"] = string();
string name = normalize_filename(p.verbatim_item()); 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, // We want to preserve relative / absolute filenames,
// therefore path is only used for testing // therefore path is only used for testing
if (!makeAbsPath(name, path).exists()) { 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()) FileName const absname = makeAbsPath(name, path);
fix_relative_filename(name); if (absname.exists()) {
else fix_child_filename(name);
copy_file(absname, name);
} else
cerr << "Warning: Could not find graphics file '" cerr << "Warning: Could not find graphics file '"
<< name << "'." << endl; << name << "'." << endl;
@ -3725,7 +3802,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
name += p.get_token().asInput(); name += p.get_token().asInput();
context.check_layout(os); context.check_layout(os);
string filename(normalize_filename(p.getArg('{', '}'))); string filename(normalize_filename(p.getArg('{', '}')));
string const path = makeAbsPath(getMasterFilePath()).absFileName(); string const path = getMasterFilePath(true);
// We want to preserve relative / absolute filenames, // We want to preserve relative / absolute filenames,
// therefore path is only used for testing // therefore path is only used for testing
if ((t.cs() == "include" || t.cs() == "input") && 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()) { if (makeAbsPath(filename, path).exists()) {
string const abstexname = string const abstexname =
makeAbsPath(filename, path).absFileName(); makeAbsPath(filename, path).absFileName();
string const abslyxname =
changeExtension(abstexname, ".lyx");
string const absfigname = string const absfigname =
changeExtension(abstexname, ".fig"); changeExtension(abstexname, ".fig");
fix_relative_filename(filename); fix_child_filename(filename);
string const lyxname = string const lyxname =
changeExtension(filename, ".lyx"); changeExtension(filename, ".lyx");
string const abslyxname = makeAbsPath(
lyxname, getParentFilePath(false)).absFileName();
bool xfig = false; bool xfig = false;
external = FileName(absfigname).exists(); external = FileName(absfigname).exists();
if (t.cs() == "input") { if (t.cs() == "input") {
@ -3795,16 +3872,24 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
} }
if (external) { if (external) {
outname = changeExtension(filename, ".fig"); outname = changeExtension(filename, ".fig");
FileName abssrc(changeExtension(abstexname, ".fig"));
copy_file(abssrc, outname);
} else if (xfig) { } else if (xfig) {
// Don't try to convert, the result // Don't try to convert, the result
// would be full of ERT. // would be full of ERT.
outname = filename; outname = filename;
FileName abssrc(abstexname);
copy_file(abssrc, outname);
} else if (t.cs() != "verbatiminput" && } else if (t.cs() != "verbatiminput" &&
tex2lyx(abstexname, FileName(abslyxname), tex2lyx(abstexname, FileName(abslyxname),
p.getEncoding())) { p.getEncoding())) {
outname = lyxname; outname = lyxname;
// no need to call copy_file
// tex2lyx creates the file
} else { } else {
outname = filename; outname = filename;
FileName abssrc(abstexname);
copy_file(abssrc, outname);
} }
} else { } else {
cerr << "Warning: Could not find included file '" 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; vector<string> keys;
split_map(arg, opts, keys); split_map(arg, opts, keys);
string name = normalize_filename(p.verbatim_item()); 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, // We want to preserve relative / absolute filenames,
// therefore path is only used for testing // therefore path is only used for testing
if (!makeAbsPath(name, path).exists()) { if (!makeAbsPath(name, path).exists()) {
@ -4199,9 +4284,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
pdflatex = true; pdflatex = true;
} }
} }
if (makeAbsPath(name, path).exists()) FileName const absname = makeAbsPath(name, path);
fix_relative_filename(name); if (absname.exists())
else {
fix_child_filename(name);
copy_file(absname, name);
} else
cerr << "Warning: Could not find file '" cerr << "Warning: Could not find file '"
<< name << "'." << endl; << name << "'." << endl;
// write output // write output
@ -4250,7 +4338,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
else if (t.cs() == "loadgame") { else if (t.cs() == "loadgame") {
p.skip_spaces(); p.skip_spaces();
string name = normalize_filename(p.verbatim_item()); 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, // We want to preserve relative / absolute filenames,
// therefore path is only used for testing // therefore path is only used for testing
if (!makeAbsPath(name, path).exists()) { if (!makeAbsPath(name, path).exists()) {
@ -4262,9 +4350,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
if (!lyxskak_name.empty()) if (!lyxskak_name.empty())
name = lyxskak_name; name = lyxskak_name;
} }
if (makeAbsPath(name, path).exists()) FileName const absname = makeAbsPath(name, path);
fix_relative_filename(name); if (absname.exists())
else {
fix_child_filename(name);
copy_file(absname, name);
} else
cerr << "Warning: Could not find file '" cerr << "Warning: Could not find file '"
<< name << "'." << endl; << name << "'." << endl;
context.check_layout(os); context.check_layout(os);