summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael Espindola <rafael.espindola@gmail.com>2013-06-12 20:58:35 +0000
committerRafael Espindola <rafael.espindola@gmail.com>2013-06-12 20:58:35 +0000
commit9f1d9fd1964d82f3e801efb71518144492cdf290 (patch)
treefb6a689c0275a2c77086b695db3b5065ce454bf2
parent7e17024400941889b6fe1b178e5374f75c34d9ab (diff)
downloadllvm-9f1d9fd1964d82f3e801efb71518144492cdf290.tar.gz
llvm-9f1d9fd1964d82f3e801efb71518144492cdf290.tar.bz2
llvm-9f1d9fd1964d82f3e801efb71518144492cdf290.tar.xz
Remove the program class.
It was only used to implement ExecuteAndWait and ExecuteNoWait. Expose just those two functions and make Execute and Wait implementations details. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@183864 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/llvm/Support/Program.h159
-rw-r--r--lib/Support/DataStream.cpp2
-rw-r--r--lib/Support/GraphWriter.cpp4
-rw-r--r--lib/Support/MemoryBuffer.cpp2
-rw-r--r--lib/Support/Program.cpp38
-rw-r--r--lib/Support/Unix/Program.inc38
-rw-r--r--lib/Support/raw_ostream.cpp2
-rw-r--r--tools/bugpoint/OptimizerDriver.cpp10
-rw-r--r--tools/bugpoint/ToolRunner.cpp15
-rw-r--r--tools/llvm-nm/llvm-nm.cpp2
-rw-r--r--unittests/Support/ProgramTest.cpp6
-rw-r--r--utils/not/not.cpp5
12 files changed, 99 insertions, 184 deletions
diff --git a/include/llvm/Support/Program.h b/include/llvm/Support/Program.h
index db12a153f6..dac377aaa7 100644
--- a/include/llvm/Support/Program.h
+++ b/include/llvm/Support/Program.h
@@ -22,49 +22,42 @@
namespace llvm {
class error_code;
namespace sys {
-
- // TODO: Add operations to communicate with the process, redirect its I/O,
- // etc.
-
- /// This class provides an abstraction for programs that are executable by the
- /// operating system. It provides a platform generic way to find executable
- /// programs from the path and to execute them in various ways. The sys::Path
- /// class is used to specify the location of the Program.
- /// @since 1.4
- /// @brief An abstraction for finding and executing programs.
- class Program {
- /// Opaque handle for target specific data.
- void *Data_;
-
- // Noncopyable.
- Program(const Program& other) LLVM_DELETED_FUNCTION;
- Program& operator=(const Program& other) LLVM_DELETED_FUNCTION;
-
- /// @name Methods
- /// @{
-
- Program();
- ~Program();
-
- /// This function executes the program using the \p arguments provided. The
- /// invoked program will inherit the stdin, stdout, and stderr file
- /// descriptors, the environment and other configuration settings of the
- /// invoking program. If Path::executable() does not return true when this
- /// function is called then a std::string is thrown.
- /// @returns false in case of error, true otherwise.
- /// @see FindProgramByName
- /// @brief Executes the program with the given set of \p args.
- bool Execute
- ( const Path& path, ///< sys::Path object providing the path of the
+ /// This static constructor (factory) will attempt to locate a program in
+ /// the operating system's file system using some pre-determined set of
+ /// locations to search (e.g. the PATH on Unix). Paths with slashes are
+ /// returned unmodified.
+ /// @returns A Path object initialized to the path of the program or a
+ /// Path object that is empty (invalid) if the program could not be found.
+ /// @brief Construct a Program by finding it by name.
+ Path FindProgramByName(const std::string& name);
+
+ // These functions change the specified standard stream (stdin, stdout, or
+ // stderr) to binary mode. They return errc::success if the specified stream
+ // was changed. Otherwise a platform dependent error is returned.
+ error_code ChangeStdinToBinary();
+ error_code ChangeStdoutToBinary();
+ error_code ChangeStderrToBinary();
+
+ /// This function executes the program using the arguments provided. The
+ /// invoked program will inherit the stdin, stdout, and stderr file
+ /// descriptors, the environment and other configuration settings of the
+ /// invoking program.
+ /// This function waits the program to finish.
+ /// @returns an integer result code indicating the status of the program.
+ /// A zero or positive value indicates the result code of the program.
+ /// -1 indicates failure to execute
+ /// -2 indicates a crash during execution or timeout
+ int ExecuteAndWait(
+ const Path &path, ///< sys::Path object providing the path of the
///< program to be executed. It is presumed this is the result of
///< the FindProgramByName method.
- const char** args, ///< A vector of strings that are passed to the
+ const char **args, ///< A vector of strings that are passed to the
///< program. The first element should be the name of the program.
///< The list *must* be terminated by a null char* entry.
- const char ** env = 0, ///< An optional vector of strings to use for
+ const char **env = 0, ///< An optional vector of strings to use for
///< the program's environment. If not provided, the current program's
///< environment will be used.
- const sys::Path** redirects = 0, ///< An optional array of pointers to
+ const sys::Path **redirects = 0, ///< An optional array of pointers to
///< Paths. If the array is null, no redirection is done. The array
///< should have a size of at least three. If the pointer in the array
///< are not null, then the inferior process's stdin(0), stdout(1),
@@ -72,95 +65,25 @@ namespace sys {
///< When an empty Path is passed in, the corresponding file
///< descriptor will be disconnected (ie, /dev/null'd) in a portable
///< way.
+ unsigned secondsToWait = 0, ///< If non-zero, this specifies the amount
+ ///< of time to wait for the child process to exit. If the time
+ ///< expires, the child is killed and this call returns. If zero,
+ ///< this function will wait until the child finishes or forever if
+ ///< it doesn't.
unsigned memoryLimit = 0, ///< If non-zero, this specifies max. amount
///< of memory can be allocated by process. If memory usage will be
///< higher limit, the child is killed and this call returns. If zero
///< - no memory limit.
- std::string* ErrMsg = 0 ///< If non-zero, provides a pointer to a string
+ std::string *ErrMsg = 0, ///< If non-zero, provides a pointer to a string
///< instance in which error messages will be returned. If the string
///< is non-empty upon return an error occurred while invoking the
///< program.
- );
-
- /// This function waits for the program to exit. This function will block
- /// the current program until the invoked program exits.
- /// @returns an integer result code indicating the status of the program.
- /// A zero or positive value indicates the result code of the program.
- /// -1 indicates failure to execute
- /// -2 indicates a crash during execution or timeout
- /// @see Execute
- /// @brief Waits for the program to exit.
- int Wait
- ( const Path& path, ///< The path to the child process executable.
- unsigned secondsToWait, ///< If non-zero, this specifies the amount
- ///< of time to wait for the child process to exit. If the time
- ///< expires, the child is killed and this call returns. If zero,
- ///< this function will wait until the child finishes or forever if
- ///< it doesn't.
- std::string* ErrMsg ///< If non-zero, provides a pointer to a string
- ///< instance in which error messages will be returned. If the string
- ///< is non-empty upon return an error occurred while waiting.
- );
-
- public:
- /// This static constructor (factory) will attempt to locate a program in
- /// the operating system's file system using some pre-determined set of
- /// locations to search (e.g. the PATH on Unix). Paths with slashes are
- /// returned unmodified.
- /// @returns A Path object initialized to the path of the program or a
- /// Path object that is empty (invalid) if the program could not be found.
- /// @brief Construct a Program by finding it by name.
- static Path FindProgramByName(const std::string& name);
-
- // These methods change the specified standard stream (stdin, stdout, or
- // stderr) to binary mode. They return errc::success if the specified stream
- // was changed. Otherwise a platform dependent error is returned.
- static error_code ChangeStdinToBinary();
- static error_code ChangeStdoutToBinary();
- static error_code ChangeStderrToBinary();
-
- /// A convenience function equivalent to Program prg; prg.Execute(..);
- /// prg.Wait(..);
- /// @see Execute, Wait
- static int ExecuteAndWait(const Path& path,
- const char** args,
- const char ** env = 0,
- const sys::Path** redirects = 0,
- unsigned secondsToWait = 0,
- unsigned memoryLimit = 0,
- std::string* ErrMsg = 0,
- bool *ExecutionFailed = 0);
-
- /// A convenience function equivalent to Program prg; prg.Execute(..);
- /// @see Execute
- static void ExecuteNoWait(const Path& path,
- const char** args,
- const char ** env = 0,
- const sys::Path** redirects = 0,
- unsigned memoryLimit = 0,
- std::string* ErrMsg = 0);
-
- /// @}
-
- };
-
- inline int ExecuteAndWait(const Path &path, const char **args,
- const char **env = 0,
- const sys::Path **redirects = 0,
- unsigned secondsToWait = 0,
- unsigned memoryLimit = 0, std::string *ErrMsg = 0,
- bool *ExecutionFailed = 0) {
- return Program::ExecuteAndWait(path, args, env, redirects, secondsToWait,
- memoryLimit, ErrMsg, ExecutionFailed);
- }
-
- inline Path FindProgramByName(const std::string& name) {
- return Program::FindProgramByName(name);
- }
+ bool *ExecutionFailed = 0);
- inline error_code ChangeStdoutToBinary() {
- return Program::ChangeStdoutToBinary();
- }
+ /// Similar to ExecuteAndWait, but return immediately.
+ void ExecuteNoWait(const Path &path, const char **args, const char **env = 0,
+ const sys::Path **redirects = 0, unsigned memoryLimit = 0,
+ std::string *ErrMsg = 0);
// Return true if the given arguments fit within system-specific
// argument length limits.
diff --git a/lib/Support/DataStream.cpp b/lib/Support/DataStream.cpp
index 0a02281c25..7815d08092 100644
--- a/lib/Support/DataStream.cpp
+++ b/lib/Support/DataStream.cpp
@@ -66,7 +66,7 @@ public:
error_code OpenFile(const std::string &Filename) {
if (Filename == "-") {
Fd = 0;
- sys::Program::ChangeStdinToBinary();
+ sys::ChangeStdinToBinary();
return error_code::success();
}
diff --git a/lib/Support/GraphWriter.cpp b/lib/Support/GraphWriter.cpp
index bff182f30e..41af06c239 100644
--- a/lib/Support/GraphWriter.cpp
+++ b/lib/Support/GraphWriter.cpp
@@ -69,7 +69,7 @@ static bool LLVM_ATTRIBUTE_UNUSED
ExecGraphViewer(const sys::Path &ExecPath, std::vector<const char*> &args,
const sys::Path &Filename, bool wait, std::string &ErrMsg) {
if (wait) {
- if (sys::Program::ExecuteAndWait(ExecPath, &args[0],0,0,0,0,&ErrMsg)) {
+ if (sys::ExecuteAndWait(ExecPath, &args[0],0,0,0,0,&ErrMsg)) {
errs() << "Error: " << ErrMsg << "\n";
return false;
}
@@ -77,7 +77,7 @@ ExecGraphViewer(const sys::Path &ExecPath, std::vector<const char*> &args,
errs() << " done. \n";
}
else {
- sys::Program::ExecuteNoWait(ExecPath, &args[0],0,0,0,&ErrMsg);
+ sys::ExecuteNoWait(ExecPath, &args[0],0,0,0,&ErrMsg);
errs() << "Remember to erase graph file: " << Filename.str() << "\n";
}
return true;
diff --git a/lib/Support/MemoryBuffer.cpp b/lib/Support/MemoryBuffer.cpp
index 82812c0aed..e875d11ec3 100644
--- a/lib/Support/MemoryBuffer.cpp
+++ b/lib/Support/MemoryBuffer.cpp
@@ -419,7 +419,7 @@ error_code MemoryBuffer::getSTDIN(OwningPtr<MemoryBuffer> &result) {
//
// FIXME: That isn't necessarily true, we should try to mmap stdin and
// fallback if it fails.
- sys::Program::ChangeStdinToBinary();
+ sys::ChangeStdinToBinary();
return getMemoryBufferForStream(0, "<stdin>", result);
}
diff --git a/lib/Support/Program.cpp b/lib/Support/Program.cpp
index 201d5c0d30..ae7e291e2a 100644
--- a/lib/Support/Program.cpp
+++ b/lib/Support/Program.cpp
@@ -22,33 +22,31 @@ using namespace sys;
//=== independent code.
//===----------------------------------------------------------------------===//
-int
-Program::ExecuteAndWait(const Path& path,
- const char** args,
- const char** envp,
- const Path** redirects,
- unsigned secondsToWait,
- unsigned memoryLimit,
- std::string* ErrMsg,
+static bool Execute(void *&Data, const Path &path, const char **args,
+ const char **env, const sys::Path **redirects,
+ unsigned memoryLimit, std::string *ErrMsg);
+
+static int Wait(void *&Data, const Path &path, unsigned secondsToWait,
+ std::string *ErrMsg);
+
+int sys::ExecuteAndWait(const Path &path, const char **args, const char **envp,
+ const Path **redirects, unsigned secondsToWait,
+ unsigned memoryLimit, std::string *ErrMsg,
bool *ExecutionFailed) {
- Program prg;
- if (prg.Execute(path, args, envp, redirects, memoryLimit, ErrMsg)) {
+ void *Data;
+ if (Execute(Data, path, args, envp, redirects, memoryLimit, ErrMsg)) {
if (ExecutionFailed) *ExecutionFailed = false;
- return prg.Wait(path, secondsToWait, ErrMsg);
+ return Wait(Data, path, secondsToWait, ErrMsg);
}
if (ExecutionFailed) *ExecutionFailed = true;
return -1;
}
-void
-Program::ExecuteNoWait(const Path& path,
- const char** args,
- const char** envp,
- const Path** redirects,
- unsigned memoryLimit,
- std::string* ErrMsg) {
- Program prg;
- prg.Execute(path, args, envp, redirects, memoryLimit, ErrMsg);
+void sys::ExecuteNoWait(const Path &path, const char **args, const char **envp,
+ const Path **redirects, unsigned memoryLimit,
+ std::string *ErrMsg) {
+ void *Data;
+ Execute(Data, path, args, envp, redirects, memoryLimit, ErrMsg);
}
// Include the platform-specific parts of this class.
diff --git a/lib/Support/Unix/Program.inc b/lib/Support/Unix/Program.inc
index aa03d48438..0d6543a8a8 100644
--- a/lib/Support/Unix/Program.inc
+++ b/lib/Support/Unix/Program.inc
@@ -47,13 +47,9 @@
namespace llvm {
using namespace sys;
-Program::Program() : Data_(0) {}
-
-Program::~Program() {}
-
// This function just uses the PATH environment variable to find the program.
Path
-Program::FindProgramByName(const std::string& progName) {
+sys::FindProgramByName(const std::string& progName) {
// Check some degenerate cases
if (progName.length() == 0) // no program
@@ -180,10 +176,11 @@ static void SetMemoryLimits (unsigned size)
#endif
}
-bool
-Program::Execute(const Path &path, const char **args, const char **envp,
- const Path **redirects, unsigned memoryLimit,
- std::string *ErrMsg) {
+}
+
+static bool Execute(void *&Data, const Path &path, const char **args,
+ const char **envp, const Path **redirects,
+ unsigned memoryLimit, std::string *ErrMsg) {
// If this OS has posix_spawn and there is no memory limit being implied, use
// posix_spawn. It is more efficient than fork/exec.
#ifdef HAVE_POSIX_SPAWN
@@ -231,7 +228,7 @@ Program::Execute(const Path &path, const char **args, const char **envp,
if (Err)
return !MakeErrMsg(ErrMsg, "posix_spawn failed", Err);
- Data_ = reinterpret_cast<void*>(PID);
+ Data = reinterpret_cast<void*>(PID);
return true;
}
#endif
@@ -293,20 +290,17 @@ Program::Execute(const Path &path, const char **args, const char **envp,
break;
}
- Data_ = reinterpret_cast<void*>(child);
+ Data = reinterpret_cast<void*>(child);
return true;
}
-int
-Program::Wait(const sys::Path &path,
- unsigned secondsToWait,
- std::string* ErrMsg)
-{
+static int Wait(void *&Data, const sys::Path &path, unsigned secondsToWait,
+ std::string *ErrMsg) {
#ifdef HAVE_SYS_WAIT_H
struct sigaction Act, Old;
- if (Data_ == 0) {
+ if (Data == 0) {
MakeErrMsg(ErrMsg, "Process not started!");
return -1;
}
@@ -324,7 +318,7 @@ Program::Wait(const sys::Path &path,
// Parent process: Wait for the child process to terminate.
int status;
- uint64_t pid = reinterpret_cast<uint64_t>(Data_);
+ uint64_t pid = reinterpret_cast<uint64_t>(Data);
pid_t child = static_cast<pid_t>(pid);
while (waitpid(pid, &status, 0) != child)
if (secondsToWait && errno == EINTR) {
@@ -397,17 +391,19 @@ Program::Wait(const sys::Path &path,
#endif
}
-error_code Program::ChangeStdinToBinary(){
+namespace llvm {
+
+error_code sys::ChangeStdinToBinary(){
// Do nothing, as Unix doesn't differentiate between text and binary.
return make_error_code(errc::success);
}
-error_code Program::ChangeStdoutToBinary(){
+error_code sys::ChangeStdoutToBinary(){
// Do nothing, as Unix doesn't differentiate between text and binary.
return make_error_code(errc::success);
}
-error_code Program::ChangeStderrToBinary(){
+error_code sys::ChangeStderrToBinary(){
// Do nothing, as Unix doesn't differentiate between text and binary.
return make_error_code(errc::success);
}
diff --git a/lib/Support/raw_ostream.cpp b/lib/Support/raw_ostream.cpp
index a433088b19..9262945358 100644
--- a/lib/Support/raw_ostream.cpp
+++ b/lib/Support/raw_ostream.cpp
@@ -442,7 +442,7 @@ raw_fd_ostream::raw_fd_ostream(const char *Filename, std::string &ErrorInfo,
// If user requested binary then put stdout into binary mode if
// possible.
if (Flags & F_Binary)
- sys::Program::ChangeStdoutToBinary();
+ sys::ChangeStdoutToBinary();
// Close stdout when we're done, to detect any output errors.
ShouldClose = true;
return;
diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp
index 87dc9f332c..6c491ff0f9 100644
--- a/tools/bugpoint/OptimizerDriver.cpp
+++ b/tools/bugpoint/OptimizerDriver.cpp
@@ -148,7 +148,7 @@ bool BugDriver::runPasses(Module *Program,
return 1;
}
- sys::Path tool = sys::Program::FindProgramByName("opt");
+ sys::Path tool = sys::FindProgramByName("opt");
if (tool.empty()) {
errs() << "Cannot find `opt' in PATH!\n";
return 1;
@@ -196,7 +196,7 @@ bool BugDriver::runPasses(Module *Program,
sys::Path prog;
if (UseValgrind)
- prog = sys::Program::FindProgramByName("valgrind");
+ prog = sys::FindProgramByName("valgrind");
else
prog = tool;
@@ -204,9 +204,9 @@ bool BugDriver::runPasses(Module *Program,
sys::Path Nowhere;
const sys::Path *Redirects[3] = {0, &Nowhere, &Nowhere};
- int result = sys::Program::ExecuteAndWait(prog, Args.data(), 0,
- (SilencePasses ? Redirects : 0),
- Timeout, MemoryLimit, &ErrMsg);
+ int result =
+ sys::ExecuteAndWait(prog, Args.data(), 0, (SilencePasses ? Redirects : 0),
+ Timeout, MemoryLimit, &ErrMsg);
// If we are supposed to delete the bitcode file or if the passes crashed,
// remove it now. This may fail if the file was never created, but that's ok.
diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp
index 735061d8bc..54a48111f8 100644
--- a/tools/bugpoint/ToolRunner.cpp
+++ b/tools/bugpoint/ToolRunner.cpp
@@ -75,9 +75,8 @@ static int RunProgramWithTimeout(const sys::Path &ProgramPath,
}
#endif
- return
- sys::Program::ExecuteAndWait(ProgramPath, Args, 0, redirects,
- NumSeconds, MemoryLimit, ErrMsg);
+ return sys::ExecuteAndWait(ProgramPath, Args, 0, redirects, NumSeconds,
+ MemoryLimit, ErrMsg);
}
/// RunProgramRemotelyWithTimeout - This function runs the given program
@@ -108,8 +107,8 @@ static int RunProgramRemotelyWithTimeout(const sys::Path &RemoteClientPath,
#endif
// Run the program remotely with the remote client
- int ReturnCode = sys::Program::ExecuteAndWait(RemoteClientPath, Args,
- 0, redirects, NumSeconds, MemoryLimit);
+ int ReturnCode = sys::ExecuteAndWait(RemoteClientPath, Args, 0, redirects,
+ NumSeconds, MemoryLimit);
// Has the remote client fail?
if (255 == ReturnCode) {
@@ -398,7 +397,7 @@ static void lexCommand(std::string &Message, const std::string &CommandLine,
pos = CommandLine.find_first_of(delimiters, lastPos);
}
- CmdPath = sys::Program::FindProgramByName(Command).str();
+ CmdPath = sys::FindProgramByName(Command).str();
if (CmdPath.empty()) {
Message =
std::string("Cannot find '") + Command +
@@ -875,7 +874,7 @@ int GCC::MakeSharedObject(const std::string &InputFile, FileType fileType,
GCC *GCC::create(std::string &Message,
const std::string &GCCBinary,
const std::vector<std::string> *Args) {
- sys::Path GCCPath = sys::Program::FindProgramByName(GCCBinary);
+ sys::Path GCCPath = sys::FindProgramByName(GCCBinary);
if (GCCPath.isEmpty()) {
Message = "Cannot find `"+ GCCBinary +"' in PATH!\n";
return 0;
@@ -883,7 +882,7 @@ GCC *GCC::create(std::string &Message,
sys::Path RemoteClientPath;
if (!RemoteClient.empty())
- RemoteClientPath = sys::Program::FindProgramByName(RemoteClient);
+ RemoteClientPath = sys::FindProgramByName(RemoteClient);
Message = "Found gcc: " + GCCPath.str() + "\n";
return new GCC(GCCPath, RemoteClientPath, Args);
diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp
index a24aae6061..683f99d342 100644
--- a/tools/llvm-nm/llvm-nm.cpp
+++ b/tools/llvm-nm/llvm-nm.cpp
@@ -425,7 +425,7 @@ int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv, "llvm symbol table dumper\n");
// llvm-nm only reads binary files.
- if (error(sys::Program::ChangeStdinToBinary()))
+ if (error(sys::ChangeStdinToBinary()))
return 1;
ToolName = argv[0];
diff --git a/unittests/Support/ProgramTest.cpp b/unittests/Support/ProgramTest.cpp
index 6cbb05454f..f132c03304 100644
--- a/unittests/Support/ProgramTest.cpp
+++ b/unittests/Support/ProgramTest.cpp
@@ -79,9 +79,9 @@ TEST(ProgramTest, CreateProcessTrailingSlash) {
Path nul("/dev/null");
#endif
const Path *redirects[] = { &nul, &nul, 0 };
- int rc = Program::ExecuteAndWait(my_exe, argv, &envp[0], redirects,
- /*secondsToWait=*/10, /*memoryLimit=*/0,
- &error, &ExecutionFailed);
+ int rc =
+ ExecuteAndWait(my_exe, argv, &envp[0], redirects, /*secondsToWait=*/ 10,
+ /*memoryLimit=*/ 0, &error, &ExecutionFailed);
EXPECT_FALSE(ExecutionFailed) << error;
EXPECT_EQ(0, rc);
}
diff --git a/utils/not/not.cpp b/utils/not/not.cpp
index 9a924b56a7..bb2d87bb44 100644
--- a/utils/not/not.cpp
+++ b/utils/not/not.cpp
@@ -13,11 +13,10 @@
using namespace llvm;
int main(int argc, const char **argv) {
- sys::Path Program = sys::Program::FindProgramByName(argv[1]);
+ sys::Path Program = sys::FindProgramByName(argv[1]);
std::string ErrMsg;
- int Result = sys::Program::ExecuteAndWait(Program, argv + 1, 0, 0, 0, 0,
- &ErrMsg);
+ int Result = sys::ExecuteAndWait(Program, argv + 1, 0, 0, 0, 0, &ErrMsg);
if (Result < 0) {
errs() << "Error: " << ErrMsg << "\n";
return 1;