mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-09 18:31:04 +00:00
Fix export of graphics images when compiling latex file.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@7649 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
d80565c905
commit
a4fc784124
@ -1,3 +1,9 @@
|
||||
2003-09-03 Angus Leeming <leeming@lyx.org>
|
||||
|
||||
* insetexternal.C (updateExternal):
|
||||
* insetgraphics.C (prepareFile): Fix the conversion when exporting to
|
||||
latex.
|
||||
|
||||
2003-09-03 Angus Leeming <leeming@lyx.org>
|
||||
|
||||
* insetexternal.C: don't bring namespace lyx::support into the global
|
||||
|
@ -518,28 +518,33 @@ void InsetExternal::updateExternal(string const & format,
|
||||
|
||||
if (external_in_tmpdir && !from_file.empty()) {
|
||||
// We are running stuff through LaTeX
|
||||
from_file = support::copyFileToDir(buf.tmppath, from_file);
|
||||
if (from_file.empty())
|
||||
string const temp_file =
|
||||
support::MakeAbsPath(params_.filename.mangledFilename(),
|
||||
buf.tmppath);
|
||||
unsigned long const from_checksum = support::sum(from_file);
|
||||
unsigned long const temp_checksum = support::sum(temp_file);
|
||||
|
||||
// Nothing to do...
|
||||
if (from_checksum == temp_checksum)
|
||||
return;
|
||||
|
||||
// Cannot proceed...
|
||||
if (!support::copy(from_file, temp_file))
|
||||
return;
|
||||
from_file = temp_file;
|
||||
}
|
||||
|
||||
string const to_file = doSubstitution(params_, buf,
|
||||
outputFormat.updateResult);
|
||||
string const abs_to_file = support::MakeAbsPath(to_file, buf.filePath());
|
||||
|
||||
support::FileInfo fi(from_file);
|
||||
string abs_to_file = to_file;
|
||||
if (!support::AbsolutePath(to_file))
|
||||
abs_to_file = support::MakeAbsPath(to_file,
|
||||
support::OnlyPath(from_file));
|
||||
support::FileInfo fi2(abs_to_file);
|
||||
if (fi2.exist() && fi.exist() &&
|
||||
difftime(fi2.getModificationTime(),
|
||||
fi.getModificationTime()) >= 0) {
|
||||
} else {
|
||||
string const to_filebase = support::ChangeExtension(to_file, string());
|
||||
converters.convert(&buf, from_file, to_filebase,
|
||||
from_format, to_format);
|
||||
}
|
||||
// Do we need to perform the conversion?
|
||||
// Yes if to_file does not exist or if from_file is newer than to_file
|
||||
if (support::compare_timestamps(from_file, abs_to_file) < 0)
|
||||
return;
|
||||
|
||||
string const to_filebase = support::ChangeExtension(to_file, string());
|
||||
converters.convert(&buf, from_file, to_filebase, from_format, to_format);
|
||||
}
|
||||
|
||||
|
||||
|
@ -74,6 +74,7 @@ TODO
|
||||
#include "frontends/Alert.h"
|
||||
#include "frontends/Dialogs.h"
|
||||
|
||||
#include "support/LAssert.h"
|
||||
#include "support/filetools.h"
|
||||
#include "support/lyxalgo.h" // lyx::count
|
||||
#include "support/lyxlib.h" // float_equal
|
||||
@ -81,11 +82,13 @@ TODO
|
||||
#include "support/systemcall.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
#include <algorithm> // For the std::max
|
||||
|
||||
// set by Exporters
|
||||
|
||||
namespace support = lyx::support;
|
||||
using namespace lyx::support;
|
||||
|
||||
using std::ostream;
|
||||
@ -328,14 +331,79 @@ string const InsetGraphics::createLatexOptions() const
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
enum CopyStatus {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
IDENTICAL_PATHS,
|
||||
IDENTICAL_CONTENTS
|
||||
};
|
||||
|
||||
|
||||
std::pair<CopyStatus, string> const
|
||||
copyToDirIfNeeded(string const & file_in, string const & dir)
|
||||
{
|
||||
using support::rtrim;
|
||||
|
||||
support::Assert(AbsolutePath(file_in));
|
||||
|
||||
string const only_path = support::OnlyPath(file_in);
|
||||
if (rtrim(support::OnlyPath(file_in) , "/") == rtrim(dir, "/"))
|
||||
return std::make_pair(IDENTICAL_PATHS, file_in);
|
||||
|
||||
string mangled;
|
||||
if (support::zippedFile(file_in)) {
|
||||
string const ext = GetExtension(file_in);
|
||||
string const unzipped = support::unzippedFileName(file_in);
|
||||
mangled = FileName(unzipped).mangledFilename();
|
||||
mangled += "." + ext;
|
||||
} else
|
||||
mangled = FileName(file_in).mangledFilename();
|
||||
|
||||
string const file_out = support::MakeAbsPath(mangled, dir);
|
||||
|
||||
unsigned long const checksum_in = support::sum(file_in);
|
||||
unsigned long const checksum_out = support::sum(file_out);
|
||||
|
||||
if (checksum_in == checksum_out)
|
||||
// Nothing to do...
|
||||
return std::make_pair(IDENTICAL_CONTENTS, file_out);
|
||||
|
||||
bool const success = support::copy(file_in, file_out);
|
||||
if (!success) {
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< support::bformat(_("Could not copy the file\n%1$s\n"
|
||||
"into the temporary directory."),
|
||||
file_in)
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
CopyStatus status = success ? SUCCESS : FAILURE;
|
||||
return std::make_pair(status, file_out);
|
||||
}
|
||||
|
||||
|
||||
string const stripExtensionIfPossible(string const & file, string const & to)
|
||||
{
|
||||
// No conversion is needed. LaTeX can handle the graphic file as is.
|
||||
// This is true even if the orig_file is compressed.
|
||||
if (formats.getFormat(to)->extension() == GetExtension(file))
|
||||
return RemoveExtension(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
} // namespace anon
|
||||
|
||||
|
||||
string const InsetGraphics::prepareFile(Buffer const & buf,
|
||||
LatexRunParams const & runparams) const
|
||||
{
|
||||
// LaTeX can cope if the graphics file doesn't exist, so just return the
|
||||
// filename.
|
||||
string orig_file = params().filename.absFilename();
|
||||
string const rel_file = params().filename.relFilename(buf.filePath());
|
||||
|
||||
// LaTeX can cope if the graphics file doesn't exist, so just return the
|
||||
// filename.
|
||||
if (!IsFileReadable(orig_file)) {
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "InsetGraphics::prepareFile\n"
|
||||
@ -343,11 +411,11 @@ string const InsetGraphics::prepareFile(Buffer const & buf,
|
||||
return rel_file;
|
||||
}
|
||||
|
||||
bool const zipped = zippedFile(orig_file);
|
||||
|
||||
// If the file is compressed and we have specified that it
|
||||
// should not be uncompressed, then just return its name and
|
||||
// let LaTeX do the rest!
|
||||
bool const zipped = params().filename.isZipped();
|
||||
|
||||
if (zipped && params().noUnzip) {
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "\tpass zipped file to LaTeX but with full path.\n";
|
||||
@ -361,95 +429,85 @@ string const InsetGraphics::prepareFile(Buffer const & buf,
|
||||
string temp_file = orig_file;
|
||||
|
||||
if (zipped) {
|
||||
// Uncompress the file if necessary.
|
||||
// If it has been uncompressed in a previous call to
|
||||
// prepareFile, do nothing.
|
||||
temp_file = MakeAbsPath(OnlyFilename(temp_file), buf.tmppath);
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "\ttemp_file: " << temp_file << endl;
|
||||
if (graphic_->hasFileChanged() || !IsFileReadable(temp_file)) {
|
||||
bool const success = copy(orig_file, temp_file);
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "\tCopying zipped file from "
|
||||
<< orig_file << " to " << temp_file
|
||||
<< (success ? " succeeded\n" : " failed\n");
|
||||
} else
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "\tzipped file " << temp_file
|
||||
<< " exists! Maybe no tempdir ...\n";
|
||||
orig_file = unzipFile(temp_file);
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "\tunzipped to " << orig_file << endl;
|
||||
}
|
||||
CopyStatus status;
|
||||
boost::tie(status, temp_file) =
|
||||
copyToDirIfNeeded(orig_file, buf.tmppath);
|
||||
|
||||
if (status == FAILURE)
|
||||
return orig_file;
|
||||
|
||||
orig_file = unzippedFileName(temp_file);
|
||||
if (!IsFileReadable(orig_file)) {
|
||||
unzipFile(temp_file);
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "\tunzipped to " << orig_file << endl;
|
||||
}
|
||||
}
|
||||
|
||||
string const from = getExtFromContents(orig_file);
|
||||
string const to = findTargetFormat(from, runparams);
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "\t we have: from " << from << " to " << to << '\n';
|
||||
|
||||
if (from == to && !lyxrc.use_tempdir) {
|
||||
// No conversion is needed. LaTeX can handle the
|
||||
// graphic file as is.
|
||||
// This is true even if the orig_file is compressed.
|
||||
if (formats.getFormat(to)->extension() == GetExtension(orig_file))
|
||||
return RemoveExtension(orig_file);
|
||||
return orig_file;
|
||||
}
|
||||
if (from == to && !lyxrc.use_tempdir)
|
||||
return stripExtensionIfPossible(orig_file, to);
|
||||
|
||||
// We're going to be running the exported buffer through the LaTeX
|
||||
// compiler, so must ensure that LaTeX can cope with the graphics
|
||||
// file format.
|
||||
|
||||
// Perform all these manipulations on a temporary file if possible.
|
||||
// If we are not using a temp dir, then temp_file contains the
|
||||
// original file.
|
||||
// to allow files with the same name in different dirs
|
||||
// we manipulate the original file "any.dir/file.ext"
|
||||
// to "any_dir_file.ext"! changing the dots in the
|
||||
// dirname is important for the use of ChangeExtension
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "\tthe orig file is: " << orig_file << endl;
|
||||
|
||||
bool conversion_needed = true;
|
||||
if (lyxrc.use_tempdir) {
|
||||
temp_file = copyFileToDir(buf.tmppath, orig_file);
|
||||
if (temp_file.empty()) {
|
||||
string str = bformat(_("Could not copy the file\n%1$s\n"
|
||||
"into the temporary directory."),
|
||||
orig_file);
|
||||
Alert::error(_("Graphics display failed"), str);
|
||||
return orig_file;
|
||||
}
|
||||
CopyStatus status;
|
||||
boost::tie(status, temp_file) =
|
||||
copyToDirIfNeeded(orig_file, buf.tmppath);
|
||||
|
||||
if (from == to) {
|
||||
// No conversion is needed. LaTeX can handle the
|
||||
// graphic file as is.
|
||||
if (formats.getFormat(to)->extension() == GetExtension(orig_file))
|
||||
return RemoveExtension(temp_file);
|
||||
return temp_file;
|
||||
}
|
||||
if (status == FAILURE)
|
||||
return orig_file;
|
||||
else if (status == IDENTICAL_CONTENTS)
|
||||
conversion_needed = false;
|
||||
}
|
||||
|
||||
if (from == to)
|
||||
return stripExtensionIfPossible(temp_file, to);
|
||||
|
||||
string const to_file_base = RemoveExtension(temp_file);
|
||||
string const to_file = ChangeExtension(to_file_base, to);
|
||||
|
||||
// Do we need to perform the conversion?
|
||||
// Yes if to_file does not exist or if temp_file is newer than to_file
|
||||
if (!conversion_needed ||
|
||||
support::compare_timestamps(temp_file, to_file) < 0) {
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< bformat(_("No conversion of %1$s is needed after all"),
|
||||
rel_file)
|
||||
<< std::endl;
|
||||
return to_file_base;
|
||||
}
|
||||
|
||||
string const outfile_base = RemoveExtension(temp_file);
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "\tThe original file is " << orig_file << "\n"
|
||||
<< "\tA copy has been made and convert is to be called with:\n"
|
||||
<< "\tfile to convert = " << temp_file << '\n'
|
||||
<< "\toutfile_base = " << outfile_base << '\n'
|
||||
<< "\tto_file_base = " << to_file_base << '\n'
|
||||
<< "\t from " << from << " to " << to << '\n';
|
||||
|
||||
// if no special converter defined, than we take the default one
|
||||
// from ImageMagic: convert from:inname.from to:outname.to
|
||||
if (!converters.convert(&buf, temp_file, outfile_base, from, to)) {
|
||||
if (!converters.convert(&buf, temp_file, to_file_base, from, to)) {
|
||||
string const command =
|
||||
"sh " + LibFileSearch("scripts", "convertDefault.sh") +
|
||||
' ' + from + ':' + temp_file + ' ' +
|
||||
to + ':' + outfile_base + '.' + to;
|
||||
to + ':' + to_file_base + '.' + to;
|
||||
lyxerr[Debug::GRAPHICS]
|
||||
<< "No converter defined! I use convertDefault.sh:\n\t"
|
||||
<< command << endl;
|
||||
Systemcall one;
|
||||
one.startscript(Systemcall::Wait, command);
|
||||
if (!IsFileReadable(ChangeExtension(outfile_base, to))) {
|
||||
if (!IsFileReadable(ChangeExtension(to_file_base, to))) {
|
||||
string str = bformat(_("No information for converting %1$s "
|
||||
"format files to %2$s.\n"
|
||||
"Try defining a convertor in the preferences."), from, to);
|
||||
@ -457,7 +515,7 @@ string const InsetGraphics::prepareFile(Buffer const & buf,
|
||||
}
|
||||
}
|
||||
|
||||
return RemoveExtension(temp_file);
|
||||
return to_file_base;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,3 +1,13 @@
|
||||
2003-09-03 Angus Leeming <leeming@lyx.org>
|
||||
|
||||
* filename.[Ch] (FileName): new c-tor takes abs_filename arg.
|
||||
(mangledFilename): new function, returning a mangled version of the
|
||||
absolute file name, suitable for use in the temp dir when, for example,
|
||||
converting an image file to another format.
|
||||
|
||||
* filetools.[Ch] (copyFileToDir): removed.
|
||||
(compare_timestamps): new function.
|
||||
|
||||
2003-09-03 Angus Leeming <leeming@lyx.org>
|
||||
|
||||
* translator.h: Assert is in namespace lyx::support...
|
||||
@ -22,7 +32,7 @@
|
||||
|
||||
2003-07-29 Lars Gullik Bjønnes <larsbj@gullik.net>
|
||||
|
||||
* Makefile.am: contidionalize USE_COMPRESSION
|
||||
* Makefile.am: conditionalize USE_COMPRESSION.
|
||||
|
||||
2003-07-28 Lars Gullik Bjønnes <larsbj@gullik.net>
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include "filename.h"
|
||||
#include "support/filetools.h"
|
||||
#include "lstrings.h"
|
||||
#include "LAssert.h"
|
||||
|
||||
|
||||
namespace lyx {
|
||||
@ -24,6 +26,13 @@ FileName::FileName()
|
||||
{}
|
||||
|
||||
|
||||
FileName::FileName(string const & abs_filename, bool save_abs)
|
||||
: name_(abs_filename), save_abs_path_(save_abs)
|
||||
{
|
||||
Assert(AbsolutePath(name_));
|
||||
}
|
||||
|
||||
|
||||
void FileName::set(string const & name, string const & buffer_path)
|
||||
{
|
||||
save_abs_path_ = AbsolutePath(name);
|
||||
@ -49,6 +58,32 @@ string const FileName::outputFilename(string const & path) const
|
||||
}
|
||||
|
||||
|
||||
string const FileName::mangledFilename() const
|
||||
{
|
||||
string mname = os::slashify_path(name_);
|
||||
// Remove the extension.
|
||||
mname = ChangeExtension(name_, string());
|
||||
// Replace '/' in the file name with '_'
|
||||
mname = subst(mname, "/", "_");
|
||||
// Replace '.' in the file name with '_'
|
||||
mname = subst(mname, ".", "_");
|
||||
// Add the extension back on
|
||||
return ChangeExtension(mname, GetExtension(name_));
|
||||
}
|
||||
|
||||
|
||||
bool FileName::isZipped() const
|
||||
{
|
||||
return zippedFile(name_);
|
||||
}
|
||||
|
||||
|
||||
string const FileName::unzippedFilename() const
|
||||
{
|
||||
return unzippedFileName(name_);
|
||||
}
|
||||
|
||||
|
||||
bool operator==(FileName const & lhs, FileName const & rhs)
|
||||
{
|
||||
return lhs.absFilename() == rhs.absFilename() &&
|
||||
@ -61,5 +96,5 @@ bool operator!=(FileName const & lhs, FileName const & rhs)
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} //namespace support
|
||||
} // namespace support
|
||||
} // namespace lyx
|
||||
|
@ -22,11 +22,15 @@ namespace support {
|
||||
class FileName {
|
||||
public:
|
||||
FileName();
|
||||
/** \param filename the file in question. Must have an absolute path.
|
||||
* \param save_abs_path how is the file to be output to file?
|
||||
*/
|
||||
FileName(string const & abs_filename, bool save_abs_path = true);
|
||||
|
||||
/** \param filename the file in question. May have either a relative
|
||||
or an absolute path.
|
||||
\param buffer_path if \c filename has a relative path, generate
|
||||
the absolute path using this.
|
||||
* or an absolute path.
|
||||
* \param buffer_path if \c filename has a relative path, generate
|
||||
* the absolute path using this.
|
||||
*/
|
||||
void set(string const & filename, string const & buffer_path);
|
||||
|
||||
@ -39,6 +43,16 @@ public:
|
||||
string const relFilename(string const & buffer_path = string()) const;
|
||||
/// \param buf_path if empty, uses `pwd`
|
||||
string const outputFilename(string const & buf_path = string()) const;
|
||||
/** \return a mangled version of the absolute file name,
|
||||
* suitable for use in the temp dir when, for example, converting
|
||||
* an image file to another format.
|
||||
*/
|
||||
string const mangledFilename() const;
|
||||
|
||||
/// \return true if the file is compressed.
|
||||
bool isZipped() const;
|
||||
/// \return the absolute file name without its .gz, .z, .Z extension
|
||||
string const unzippedFilename() const;
|
||||
|
||||
private:
|
||||
string name_;
|
||||
|
@ -1134,10 +1134,18 @@ bool zippedFile(string const & name)
|
||||
}
|
||||
|
||||
|
||||
string const unzippedFileName(string const & zipped_file)
|
||||
{
|
||||
string const ext = GetExtension(zipped_file);
|
||||
if (ext == "gz" || ext == "z" || ext == "Z")
|
||||
return ChangeExtension(zipped_file, string());
|
||||
return "unzipped_" + zipped_file;
|
||||
}
|
||||
|
||||
|
||||
string const unzipFile(string const & zipped_file)
|
||||
{
|
||||
string const file = ChangeExtension(zipped_file, string());
|
||||
string const tempfile = tempName(string(), file);
|
||||
string const tempfile = unzippedFileName(zipped_file);
|
||||
// Run gunzip
|
||||
string const command = "gunzip -c " + zipped_file + " > " + tempfile;
|
||||
Systemcall one;
|
||||
@ -1333,43 +1341,29 @@ string const readBB_from_PSFile(string const & file)
|
||||
}
|
||||
|
||||
|
||||
string const copyFileToDir(string const & path, string const & file_in)
|
||||
int compare_timestamps(string const & file1, string const & file2)
|
||||
{
|
||||
Assert(AbsolutePath(path));
|
||||
|
||||
// First, make the file path relative to path.
|
||||
string file_out = MakeRelPath(path, NormalizePath(file_in));
|
||||
file_out = os::slashify_path(file_out);
|
||||
|
||||
// Now generate a unique filename.
|
||||
// Remove the extension.
|
||||
file_out = ChangeExtension(file_out, string());
|
||||
// Replace '/' in the file name with '_'
|
||||
file_out = subst(file_out, "/", "_");
|
||||
// Replace '.' in the file name with '_'
|
||||
file_out = subst(file_out, ".", "_");
|
||||
// Append a unique ID
|
||||
// static int id;
|
||||
// file_out += '_' + tostr(id++);
|
||||
// Add the extension back on
|
||||
file_out = ChangeExtension(file_out, GetExtension(file_in));
|
||||
// Put this file in the buffer's temp dir
|
||||
file_out = MakeAbsPath(file_out, path);
|
||||
Assert(AbsolutePath(file1) && AbsolutePath(file2));
|
||||
|
||||
// If the original is newer than the copy, then copy the original
|
||||
// to the new directory.
|
||||
FileInfo fi(file_in);
|
||||
FileInfo fi2(file_out);
|
||||
FileInfo f1(file1);
|
||||
FileInfo f2(file2);
|
||||
|
||||
bool success = true;
|
||||
if (fi.exist()) {
|
||||
if (!fi2.exist() ||
|
||||
difftime(fi.getModificationTime(),
|
||||
fi2.getModificationTime()) >= 0)
|
||||
success = copy(file_in, file_out);
|
||||
int cmp = 0;
|
||||
if (f1.exist() && f2.exist()) {
|
||||
double const tmp = difftime(f1.getModificationTime(),
|
||||
f2.getModificationTime());
|
||||
if (tmp != 0)
|
||||
cmp = tmp > 0 ? 1 : -1;
|
||||
|
||||
} else if (f1.exist()) {
|
||||
cmp = 1;
|
||||
} else if (f2.exist()) {
|
||||
cmp = -1;
|
||||
}
|
||||
|
||||
return success ? file_out : string();
|
||||
return cmp;
|
||||
}
|
||||
|
||||
} //namespace support
|
||||
|
@ -143,6 +143,9 @@ string const getExtFromContents(string const & name);
|
||||
/// check for zipped file
|
||||
bool zippedFile(string const & name);
|
||||
|
||||
/// \return the name that LyX will give to the unzipped file.
|
||||
string const unzippedFileName(string const & zipped_file);
|
||||
|
||||
/// unzip a file
|
||||
string const unzipFile(string const & zipped_file);
|
||||
|
||||
@ -210,14 +213,14 @@ void removeAutosaveFile(string const & filename);
|
||||
/// read the BoundingBox entry from a ps/eps/pdf-file
|
||||
string const readBB_from_PSFile(string const & file);
|
||||
|
||||
/** Copy \c file to directory \c path. The file name is manipulated
|
||||
so that eg some/path/to/file becomes some_path_to_file.
|
||||
\param path where to put the file
|
||||
\param file the file that is copied
|
||||
\returns this file name if the file is copied successfully, else
|
||||
\returns an empty string.
|
||||
/** \param file1, file2 the two files to be compared. Must have absolute paths.
|
||||
* \returns 1 if \c file1 has a more recent timestamp than \c file2,
|
||||
* 0 if their timestamps are the same,
|
||||
* -1 if \c file2 has a more recent timestamp than \c file1.
|
||||
* If one of the files does not exist, the return value indicates the file
|
||||
* which does exist. Eg, if \c file1 exists but \c file2 does not, return 1.
|
||||
*/
|
||||
string const copyFileToDir(string const & path, string const & file);
|
||||
int compare_timestamps(string const & file1, string const & file2);
|
||||
|
||||
typedef std::pair<int, string> cmd_ret;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user