#include <errno.h>
#include <unistd.h>
#include <Autolock.h>
#include <String.h>
#include <TestShell.h>
#include <TestUtils.h>
#include <cppunit/TestAssert.h>
#include "PipedAppRunner.h"
PipedAppRunner::PipedAppRunner()
: fOutputLock(),
fPipe(NULL),
fOutput(),
fReader(-1)
{
}
PipedAppRunner::~PipedAppRunner()
{
if (fReader >= 0) {
_ClosePipe();
int32 result;
wait_for_thread(fReader, &result);
}
}
status_t
PipedAppRunner::Run(const char *command, const char *args, bool findCommand)
{
status_t error = (HasQuitted() ? B_OK : B_ERROR);
BString appPath;
if (findCommand) {
appPath = BTestShell::GlobalTestDir();
appPath.CharacterEscape(" \t\n!\"'`$&()?*+{}[]<>|", '\\');
appPath += "/";
appPath += command;
#ifdef TEST_R5
appPath += "_r5";
#endif
command = appPath.String();
}
BString cmdLine(command);
if (args) {
cmdLine += " ";
cmdLine += args;
}
if (error == B_OK) {
fPipe = popen(cmdLine.String(), "r");
if (!fPipe)
error = errno;
}
if (error == B_OK) {
fReader = spawn_thread(&_ReaderEntry, "PipedAppRunner reader",
B_NORMAL_PRIORITY, (void*)this);
if (fReader >= 0)
error = resume_thread(fReader);
else
error = fReader;
}
if (error != B_OK) {
if (fReader >= 0) {
kill_thread(fReader);
fReader = -1;
}
if (fPipe) {
pclose(fPipe);
fPipe = NULL;
}
}
return error;
}
bool
PipedAppRunner::HasQuitted()
{
BAutolock locker(fOutputLock);
return !fPipe;
}
void
PipedAppRunner::WaitFor()
{
while (!HasQuitted())
snooze(10000);
}
status_t
PipedAppRunner::GetOutput(BString *buffer)
{
status_t error = (buffer ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
BAutolock locker(fOutputLock);
size_t size = fOutput.BufferLength();
const void *output = fOutput.Buffer();
if (size > 0)
buffer->SetTo((const char*)output, size);
else
*buffer = "";
}
return error;
}
ssize_t
PipedAppRunner::ReadOutput(void *buffer, size_t size)
{
BAutolock locker(fOutputLock);
return fOutput.Read(buffer, size);
}
ssize_t
PipedAppRunner::ReadOutputAt(off_t position, void *buffer, size_t size)
{
BAutolock locker(fOutputLock);
return fOutput.ReadAt(position, buffer, size);
}
int32
PipedAppRunner::_ReaderEntry(void *data)
{
int32 result = 0;
if (PipedAppRunner *me = (PipedAppRunner*)data)
result = me->_ReaderLoop();
return result;
}
int32
PipedAppRunner::_ReaderLoop()
{
char buffer[10240];
fOutputLock.Lock();
FILE *pipe = fPipe;
fOutputLock.Unlock();
while (!feof(pipe)) {
size_t bytes = fread(buffer, 1, sizeof(buffer), pipe);
if (bytes > 0) {
BAutolock locker(fOutputLock);
off_t oldPosition = fOutput.Seek(0, SEEK_END);
fOutput.Write(buffer, bytes);
fOutput.Seek(oldPosition, SEEK_SET);
}
}
_ClosePipe();
return 0;
}
void
PipedAppRunner::_ClosePipe()
{
if (fOutputLock.Lock()) {
if (fPipe) {
pclose(fPipe);
fPipe = NULL;
}
fOutputLock.Unlock();
}
}