summaryrefslogtreecommitdiff
path: root/rtemstoolkit/rld-process.cpp
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2014-09-13 12:09:16 +1000
committerChris Johns <chrisj@rtems.org>2014-09-13 12:09:16 +1000
commit87e0e76be5b17d1dd27274d58ac9b58cdf71c0ca (patch)
tree0d44623ad165fa69cd0946f2baab79339b3e6215 /rtemstoolkit/rld-process.cpp
parent749ddf1ed53c3903823eed031649441a59edd264 (diff)
Refactor code into the RTEMS Toolkit.
Diffstat (limited to 'rtemstoolkit/rld-process.cpp')
-rw-r--r--rtemstoolkit/rld-process.cpp561
1 files changed, 561 insertions, 0 deletions
diff --git a/rtemstoolkit/rld-process.cpp b/rtemstoolkit/rld-process.cpp
new file mode 100644
index 0000000..bfd6734
--- /dev/null
+++ b/rtemstoolkit/rld-process.cpp
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2011, Chris Johns <chrisj@rtems.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifndef WIFEXITED
+#define WIFEXITED(S) (((S) & 0xff) == 0)
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(S) (((S) & 0xff00) >> 8)
+#endif
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(S) (((S) & 0xff) != 0 && ((S) & 0xff) != 0x7f)
+#endif
+#ifndef WTERMSIG
+#define WTERMSIG(S) ((S) & 0x7f)
+#endif
+#ifndef WIFSTOPPED
+#define WIFSTOPPED WIFEXITED
+#endif
+#ifndef WSTOPSIG
+#define WSTOPSIG WEXITSTATUS
+#endif
+
+#if __WIN32__
+#define CREATE_MODE (S_IRUSR | S_IWUSR)
+#define OPEN_FLAGS (O_BINARY)
+#else
+#define CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
+#define OPEN_FLAGS (0)
+#endif
+
+#include <iostream>
+
+#include "rld.h"
+#include "rld-process.h"
+
+#include <libiberty.h>
+
+namespace rld
+{
+ namespace process
+ {
+ /**
+ * Global keep of temporary files if true. Used to help debug a system.
+ */
+ bool keep_temporary_files = false;
+
+ /**
+ * The temporary files.
+ */
+ temporary_files temporaries;
+
+ temporary_files::temporary_files ()
+ {
+ }
+
+ temporary_files::~temporary_files ()
+ {
+ clean_up ();
+ }
+
+ const std::string
+ temporary_files::get (const std::string& suffix, bool keep)
+ {
+ char* temp = ::make_temp_file (suffix.c_str ());
+
+ if (!temp)
+ throw rld::error ("bad temp name", "temp-file");
+
+ const std::string name = rld::find_replace (temp,
+ RLD_PATH_SEPARATOR_STR RLD_PATH_SEPARATOR_STR,
+ RLD_PATH_SEPARATOR_STR);
+ tempfile_ref ref (name, keep);
+ tempfiles.push_back (ref);
+ return name;
+ }
+
+ void
+ temporary_files::unlink (const tempfile_ref& ref)
+ {
+ if (!keep_temporary_files && !ref.keep)
+ rld::path::unlink (ref.name);
+ }
+
+ void
+ temporary_files::erase (const std::string& name)
+ {
+ for (tempfile_container::iterator tfi = tempfiles.begin ();
+ tfi != tempfiles.end ();
+ ++tfi)
+ {
+ if ((*tfi).name == name)
+ {
+ unlink (*tfi);
+ tempfiles.erase (tfi);
+ break;
+ }
+ }
+ }
+
+ void
+ temporary_files::keep (const std::string& name)
+ {
+ for (tempfile_container::iterator tfi = tempfiles.begin ();
+ tfi != tempfiles.end ();
+ ++tfi)
+ {
+ if ((*tfi).name == name)
+ {
+ (*tfi).keep = true;
+ break;
+ }
+ }
+ }
+
+ void
+ temporary_files::clean_up ()
+ {
+ for (tempfile_container::iterator tfi = tempfiles.begin ();
+ tfi != tempfiles.end ();
+ ++tfi)
+ {
+ unlink (*tfi);
+ }
+ }
+
+ tempfile::tempfile (const std::string& suffix, bool _keep)
+ : suffix (suffix),
+ overridden (false),
+ fd (-1),
+ level (0)
+ {
+ _name = temporaries.get (suffix, _keep);
+ }
+
+ tempfile::~tempfile ()
+ {
+ close ();
+ temporaries.erase (_name);
+ }
+
+ void
+ tempfile::open (bool writable)
+ {
+ if (fd < 0)
+ {
+ bool ok = rld::path::check_file (_name);
+ int flags = writable ? O_RDWR : O_RDONLY;
+ int mode = writable ? CREATE_MODE : 0;
+
+ if (writable && overridden)
+ {
+ flags |= O_CREAT | O_TRUNC | O_APPEND;
+ }
+ else
+ {
+ if (!ok)
+ throw rld::error ("Not found.", "tempfile open:" + _name);
+ }
+
+ level = 0;
+ fd = ::open (_name.c_str (), flags, mode);
+ if (fd < 0)
+ throw rld::error (::strerror (errno), "tempfile open:" + _name);
+ }
+ }
+
+ void
+ tempfile::close ()
+ {
+ if (fd != -1)
+ {
+ ::close (fd);
+ fd = -1;
+ level = 0;
+ }
+ }
+
+ void
+ tempfile::override (const std::string& name_)
+ {
+ if (fd >= 0)
+ throw rld::error ("Already open", "tempfile override");
+ rld::path::unlink (_name);
+ overridden = true;
+ _name = name_ + suffix;
+ }
+
+ void
+ tempfile::keep ()
+ {
+ temporaries.keep (_name);
+ }
+
+ const std::string&
+ tempfile::name () const
+ {
+ return _name;
+ }
+
+ size_t
+ tempfile::size ()
+ {
+ if (fd < 0)
+ return 0;
+
+ struct stat sb;
+ if (::stat (_name.c_str (), &sb) == 0)
+ return sb.st_size;
+
+ return 0;
+ }
+
+ void
+ tempfile::read (std::string& all)
+ {
+ all.clear ();
+ if (fd != -1)
+ {
+ if (level)
+ all.append (buf, level);
+ level = 0;
+ while (true)
+ {
+ int read = ::read (fd, buf, sizeof (buf) );
+ if (read < 0)
+ throw rld::error (::strerror (errno), "tempfile get read:" + _name);
+ else if (read == 0)
+ break;
+ else
+ all.append (buf, read);
+ }
+ }
+ }
+
+ void
+ tempfile::read_line (std::string& line)
+ {
+ line.clear ();
+ if (fd != -1)
+ {
+ bool reading = true;
+ while (reading)
+ {
+ if (level < (sizeof (buf) - 1))
+ {
+ memset (buf + level, 0, sizeof (buf) - level);
+ int read = ::read (fd, buf + level, sizeof (buf) - level - 1);
+ if (read < 0)
+ throw rld::error (::strerror (errno), "tempfile read:" + _name);
+ else if (read == 0)
+ reading = false;
+ else
+ level += read;
+ }
+ if (level)
+ {
+ char* lf = ::strchr (buf, '\n');
+ int len = level;
+ if (lf)
+ len = lf - &buf[0] + 1;
+ if (lf || !reading)
+ {
+ line.append (buf, len);
+ level -= len;
+ }
+ if (level)
+ ::memmove (buf, &buf[len], level + 1);
+ reading = false;
+ }
+ }
+ }
+ }
+
+ void
+ tempfile::write (const std::string& s)
+ {
+ const char* p = s.c_str ();
+ size_t l = s.length ();
+ while (l)
+ {
+ int written = ::write (fd, p, l);
+ if (written < 0)
+ throw rld::error (::strerror (errno), "tempfile write:" + _name);
+ if (written == 0)
+ break;
+ l -= written;
+ }
+ }
+
+ void
+ tempfile::write_line (const std::string& s)
+ {
+ write (s);
+ write (RLD_LINE_SEPARATOR);
+ }
+
+ void
+ tempfile::write_lines (const rld::strings& ss)
+ {
+ for (rld::strings::const_iterator ssi = ss.begin ();
+ ssi != ss.end ();
+ ++ssi)
+ {
+ write_line (*ssi);
+ }
+ }
+
+ void
+ tempfile::output (std::ostream& out)
+ {
+ std::string prefix;
+ output (prefix, out);
+ }
+
+ void
+ tempfile::output (const std::string& prefix,
+ std::ostream& out,
+ bool line_numbers)
+ {
+ if (fd == -1)
+ {
+ std::string line;
+ int lc = 0;
+ open ();
+ while (true)
+ {
+ read_line (line);
+ ++lc;
+ if (line.empty ())
+ break;
+ if (!prefix.empty ())
+ out << prefix << ": ";
+ if (line_numbers)
+ out << lc << ": ";
+ out << line << std::flush;
+ }
+ close ();
+ }
+ }
+
+ void
+ set_keep_temporary_files ()
+ {
+ keep_temporary_files = true;
+ }
+
+ void
+ temporaries_clean_up ()
+ {
+ temporaries.clean_up ();
+ }
+
+ void
+ args_append (arg_container& args, const std::string& str)
+ {
+ rld::strings ss;
+ rld::split (ss, str);
+ for (rld::strings::iterator ssi = ss.begin ();
+ ssi != ss.end ();
+ ++ssi)
+ {
+ args.push_back (*ssi);
+ }
+ }
+
+ status
+ execute (const std::string& pname,
+ const std::string& command,
+ const std::string& outname,
+ const std::string& errname)
+ {
+ arg_container args;
+ parse_command_line (command, args);
+ return execute (pname, args, outname, errname);
+ }
+
+ status
+ execute (const std::string& pname,
+ const arg_container& args,
+ const std::string& outname,
+ const std::string& errname)
+ {
+ if (rld::verbose (RLD_VERBOSE_TRACE))
+ {
+ std::cout << "execute: ";
+ for (size_t a = 0; a < args.size (); ++a)
+ std::cout << args[a] << ' ';
+ std::cout << std::endl;
+ }
+
+ const char** cargs = new const char* [args.size () + 1];
+
+ for (size_t a = 0; a < args.size (); ++a)
+ cargs[a] = args[a].c_str ();
+ cargs[args.size ()] = 0;
+
+ int err = 0;
+ int s = 0;
+
+ const char* serr = pex_one (PEX_LAST | PEX_SEARCH,
+ args[0].c_str (),
+ (char* const*) cargs,
+ pname.c_str (),
+ outname.c_str (),
+ errname.c_str (),
+ &s,
+ &err);
+
+ delete [] cargs;
+
+ if (serr)
+ throw rld::error ("execute: " + args[0], serr);
+ else if (err)
+ throw rld::error ("execute: " + args[0], ::strerror (err));
+
+ status _status;
+
+ if (rld::verbose (RLD_VERBOSE_TRACE))
+ std::cout << "execute: status: ";
+
+ if (WIFEXITED (s))
+ {
+ _status.type = status::normal;
+ _status.code = WEXITSTATUS (s);
+ if (rld::verbose (RLD_VERBOSE_TRACE))
+ std::cout << _status.code << std::endl;
+ }
+ else if (WIFSIGNALED (s))
+ {
+ _status.type = status::signal;
+ _status.code = WTERMSIG (s);
+ if (rld::verbose (RLD_VERBOSE_TRACE))
+ std::cout << "signal: " << _status.code << std::endl;
+ }
+ else if (WIFSTOPPED (s))
+ {
+ _status.type = status::stopped;
+ _status.code = WSTOPSIG (s);
+ if (rld::verbose (RLD_VERBOSE_TRACE))
+ std::cout << "stopped: " << _status.code << std::endl;
+ }
+ else
+ throw rld::error ("execute: " + args[0], "unknown status returned");
+
+ return _status;
+ }
+
+ /*
+ * The code is based on this C file:
+ * http://cybertiggyr.com/pcm/src/parse.c
+ */
+ void
+ parse_command_line (const std::string& command, arg_container& args)
+ {
+ enum pstate
+ {
+ pstate_discard_space,
+ pstate_accumulate_quoted,
+ pstate_accumulate_raw
+ };
+
+ args.clear ();
+
+ const char quote = '"';
+ const char escape = '\\';
+ pstate state = pstate_discard_space;
+ size_t start = 0;
+ size_t i = 0;
+
+ while (i < command.size ())
+ {
+ switch (state)
+ {
+ case pstate_discard_space:
+ if (command[i] == quote)
+ {
+ ++i;
+ start = i;
+ state = pstate_accumulate_quoted;
+ }
+ else if (::isspace (command[i]))
+ {
+ ++i;
+ }
+ else /* includes escape */
+ {
+ start = i;
+ state = pstate_accumulate_raw;
+ }
+ break;
+
+ case pstate_accumulate_quoted:
+ if (command[i] == quote)
+ {
+ args.push_back (command.substr (start, i - 1));
+ ++i;
+ state = pstate_discard_space;
+ }
+ else if ((command[i] == escape) && (command[i + 1] == quote))
+ {
+ i += 2;
+ }
+ else /* includes space */
+ {
+ ++i;
+ }
+ break;
+
+ case pstate_accumulate_raw:
+ if (command[i] == quote)
+ {
+ throw rld::error ("quote in token", "command parse");
+ }
+ else if ((command[i] == escape) && (command[i + 1] == quote))
+ {
+ i += 2;
+ }
+ else if (::isspace (command[i]))
+ {
+ args.push_back (command.substr (start, i - 1));
+ ++i;
+ state = pstate_discard_space;
+ }
+ else
+ {
+ ++i;
+ }
+ break;
+ }
+ }
+ }
+ }
+}