mirror of
https://git.rtems.org/rtems-tools/
synced 2025-06-14 06:10:50 +08:00
438 lines
10 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|