rtems-tools/linkers/rld-process.cpp
2012-05-07 08:47:11 +10:00

438 lines
10 KiB
C++

/*
* 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>
#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
#include <iostream>
#include "rld.h"
#include "rld-process.h"
#include <libiberty.h>
namespace rld
{
namespace process
{
temporary_files::temporary_files ()
{
}
temporary_files::~temporary_files ()
{
clean_up ();
}
const std::string
temporary_files::get ()
{
char* temp = ::make_temp_file ("rldXXXXXX");
if (!temp)
throw rld::error ("bad temp name", "pex");
std::string name = temp;
tempfiles.push_back (name);
return name;
}
void
temporary_files::unlink (const std::string& name)
{
struct stat sb;
if ((::stat (name.c_str (), &sb) >= 0) && S_ISREG (sb.st_mode))
{
if (::unlink (name.c_str ()) < 0)
{
std::cerr << "error: unlinking temp file: " << name << std::endl;
::exit (100);
}
}
}
void
temporary_files::erase (const std::string& name)
{
for (tempfile_container::iterator tfi = tempfiles.begin ();
tfi != tempfiles.end ();
++tfi)
{
if (*tfi == name)
{
unlink (name);
tempfiles.erase (tfi);
break;
}
}
}
void
temporary_files::clean_up ()
{
for (tempfile_container::iterator tfi = tempfiles.begin ();
tfi != tempfiles.end ();
++tfi)
{
unlink (*tfi);
}
}
tempfile::tempfile ()
: fd (-1),
level (0)
{
_name = temporaries.get ();
}
tempfile::~tempfile ()
{
close ();
temporaries.erase (_name);
}
void
tempfile::open ()
{
if ((fd < 0) && rld::files::check_file (_name))
{
level = 0;
fd = ::open (_name.c_str (), O_RDONLY);
if (fd < 0)
throw rld::error (::strerror (errno), "tempfile open:" + _name);
}
}
void
tempfile::close ()
{
if (fd != -1)
{
::close (fd);
fd = -1;
level = 0;
}
}
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::get (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::getline (std::string& line)
{
line.clear ();
if (fd != -1)
{
if (level)
line.append (buf, level);
level = 0;
while (true)
{
int read = ::read (fd, buf, sizeof (buf));
if (read < 0)
throw rld::error (::strerror (errno), "tempfile read:" + _name);
else if (read == 0)
break;
else
{
char* lf = ::strchr (buf, '\n');
if (lf)
{
int len = lf - &buf[0] + 1;
line.append (buf, len);
level = read - len;
if (level)
::memmove (buf, &buf[len], level);
break;
}
line.append (buf, read);
}
}
}
}
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)
{
getline (line);
++lc;
if (line.empty ())
break;
if (!prefix.empty ())
out << prefix << ':';
if (line_numbers)
out << lc << ':';
out << line;
}
close ();
}
}
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;
}
}
}
}
}