You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
k3b/libk3b/core/k3bprocess.cpp

453 lines
9.5 KiB

/*
*
* $Id: k3bprocess.cpp 619556 2007-01-03 17:38:12Z trueg $
* Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
*
* This file is part of the K3b project.
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* See the file "COPYING" for the exact licensing terms.
*/
#include "k3bprocess.h"
#include "k3bexternalbinmanager.h"
#include <qstringlist.h>
#include <qsocketnotifier.h>
#include <qptrqueue.h>
#include <qapplication.h>
#include <kdebug.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
class K3bProcess::Data
{
public:
QString unfinishedStdoutLine;
QString unfinishedStderrLine;
int dupStdoutFd;
int dupStdinFd;
bool rawStdin;
bool rawStdout;
int in[2];
int out[2];
bool suppressEmptyLines;
};
K3bProcess::K3bProcess()
: KProcess(),
m_bSplitStdout(false)
{
d = new Data();
d->dupStdinFd = d->dupStdoutFd = -1;
d->rawStdout = d->rawStdin = false;
d->in[0] = d->in[1] = -1;
d->out[0] = d->out[1] = -1;
d->suppressEmptyLines = true;
}
K3bProcess::~K3bProcess()
{
delete d;
}
K3bProcess& K3bProcess::operator<<( const K3bExternalBin* bin )
{
return this->operator<<( bin->path );
}
K3bProcess& K3bProcess::operator<<( const QString& arg )
{
static_cast<KProcess*>(this)->operator<<( arg );
return *this;
}
K3bProcess& K3bProcess::operator<<( const char* arg )
{
static_cast<KProcess*>(this)->operator<<( arg );
return *this;
}
K3bProcess& K3bProcess::operator<<( const QCString& arg )
{
static_cast<KProcess*>(this)->operator<<( arg );
return *this;
}
K3bProcess& K3bProcess::operator<<( const QStringList& args )
{
static_cast<KProcess*>(this)->operator<<( args );
return *this;
}
bool K3bProcess::start( RunMode run, Communication com )
{
if( com & Stderr ) {
connect( this, SIGNAL(receivedStderr(KProcess*, char*, int)),
this, SLOT(slotSplitStderr(KProcess*, char*, int)) );
}
if( com & Stdout ) {
connect( this, SIGNAL(receivedStdout(KProcess*, char*, int)),
this, SLOT(slotSplitStdout(KProcess*, char*, int)) );
}
return KProcess::start( run, com );
}
void K3bProcess::slotSplitStdout( KProcess*, char* data, int len )
{
if( m_bSplitStdout ) {
QStringList lines = splitOutput( data, len, d->unfinishedStdoutLine, d->suppressEmptyLines );
for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
QString& str = *it;
// just to be sure since something in splitOutput does not do this right
if( d->suppressEmptyLines && str.isEmpty() )
continue;
emit stdoutLine( str );
}
}
}
void K3bProcess::slotSplitStderr( KProcess*, char* data, int len )
{
QStringList lines = splitOutput( data, len, d->unfinishedStderrLine, d->suppressEmptyLines );
for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
QString& str = *it;
// just to be sure since something in splitOutput does not do this right
if( d->suppressEmptyLines && str.isEmpty() )
continue;
emit stderrLine( str );
}
}
QStringList K3bProcess::splitOutput( char* data, int len,
QString& unfinishedLine, bool suppressEmptyLines )
{
//
// The stderr splitting is mainly used for parsing of messages
// That's why we simplify the data before proceeding
//
QString buffer;
for( int i = 0; i < len; i++ ) {
if( data[i] == '\b' ) {
while( data[i] == '\b' ) // we replace multiple backspaces with a single line feed
i++;
buffer += '\n';
}
if( data[i] == '\r' )
buffer += '\n';
else if( data[i] == '\t' ) // replace tabs with a single space
buffer += " ";
else
buffer += data[i];
}
QStringList lines = QStringList::split( '\n', buffer, !suppressEmptyLines );
// in case we suppress empty lines we need to handle the following separately
// to make sure we join unfinished lines correctly
if( suppressEmptyLines && buffer[0] == '\n' )
lines.prepend( QString::null );
if( !unfinishedLine.isEmpty() ) {
lines.first().prepend( unfinishedLine );
unfinishedLine.truncate(0);
kdDebug() << "(K3bProcess) joined line: '" << (lines.first()) << "'" << endl;
}
QStringList::iterator it;
// check if line ends with a newline
// if not save the last line because it is not finished
QChar c = buffer.right(1).at(0);
bool hasUnfinishedLine = ( c != '\n' && c != '\r' && c != QChar(46) ); // What is unicode 46?? It is printed as a point
if( hasUnfinishedLine ) {
kdDebug() << "(K3bProcess) found unfinished line: '" << lines.last() << "'" << endl;
kdDebug() << "(K3bProcess) last char: '" << buffer.right(1) << "'" << endl;
unfinishedLine = lines.last();
it = lines.end();
--it;
lines.remove(it);
}
return lines;
}
int K3bProcess::setupCommunication( Communication comm )
{
if( KProcess::setupCommunication( comm ) ) {
//
// Setup our own socketpair
//
if( d->rawStdin ) {
if( socketpair(AF_UNIX, SOCK_STREAM, 0, d->in) == 0 ) {
fcntl(d->in[0], F_SETFD, FD_CLOEXEC);
fcntl(d->in[1], F_SETFD, FD_CLOEXEC);
}
else
return 0;
}
if( d->rawStdout ) {
if( socketpair(AF_UNIX, SOCK_STREAM, 0, d->out) == 0 ) {
fcntl(d->out[0], F_SETFD, FD_CLOEXEC);
fcntl(d->out[1], F_SETFD, FD_CLOEXEC);
}
else {
if( d->rawStdin || d->dupStdinFd ) {
close(d->in[0]);
close(d->in[1]);
}
return 0;
}
}
return 1;
}
else
return 0;
}
void K3bProcess::commClose()
{
if( d->rawStdin ) {
close(d->in[1]);
d->in[1] = -1;
}
if( d->rawStdout ) {
close(d->out[0]);
d->out[0] = -1;
}
KProcess::commClose();
}
int K3bProcess::commSetupDoneP()
{
int ok = KProcess::commSetupDoneP();
if( d->rawStdin )
close(d->in[0]);
if( d->rawStdout )
close(d->out[1]);
d->in[0] = d->out[1] = -1;
return ok;
}
int K3bProcess::commSetupDoneC()
{
int ok = KProcess::commSetupDoneC();
if( d->dupStdoutFd != -1 ) {
//
// make STDOUT_FILENO a duplicate of d->dupStdoutFd such that writes to STDOUT_FILENO are "redirected"
// to d->dupStdoutFd
//
if( ::dup2( d->dupStdoutFd, STDOUT_FILENO ) < 0 ) {
kdDebug() << "(K3bProcess) Error while dup( " << d->dupStdoutFd << ", " << STDOUT_FILENO << endl;
ok = 0;
}
}
else if( d->rawStdout ) {
if( ::dup2( d->out[1], STDOUT_FILENO ) < 0 ) {
kdDebug() << "(K3bProcess) Error while dup( " << d->out[1] << ", " << STDOUT_FILENO << endl;
ok = 0;
}
}
if( d->dupStdinFd != -1 ) {
if( ::dup2( d->dupStdinFd, STDIN_FILENO ) < 0 ) {
kdDebug() << "(K3bProcess) Error while dup( " << d->dupStdinFd << ", " << STDIN_FILENO << endl;
ok = 0;
}
}
else if( d->rawStdin ) {
if( ::dup2( d->in[0], STDIN_FILENO ) < 0 ) {
kdDebug() << "(K3bProcess) Error while dup( " << d->in[0] << ", " << STDIN_FILENO << endl;
ok = 0;
}
}
return ok;
}
int K3bProcess::stdinFd() const
{
if( d->rawStdin )
return d->in[1];
else if( d->dupStdinFd != -1 )
return d->dupStdinFd;
else
return -1;
}
int K3bProcess::stdoutFd() const
{
if( d->rawStdout )
return d->out[0];
else if( d->dupStdoutFd != -1 )
return d->dupStdoutFd;
else
return -1;
}
void K3bProcess::dupStdout( int fd )
{
writeToFd( fd );
}
void K3bProcess::dupStdin( int fd )
{
readFromFd( fd );
}
void K3bProcess::writeToFd( int fd )
{
d->dupStdoutFd = fd;
if( fd != -1 )
d->rawStdout = false;
}
void K3bProcess::readFromFd( int fd )
{
d->dupStdinFd = fd;
if( fd != -1 )
d->rawStdin = false;
}
void K3bProcess::setRawStdin(bool b)
{
if( b ) {
d->rawStdin = true;
d->dupStdinFd = -1;
}
else
d->rawStdin = false;
}
void K3bProcess::setRawStdout(bool b)
{
if( b ) {
d->rawStdout = true;
d->dupStdoutFd = -1;
}
else
d->rawStdout = false;
}
void K3bProcess::setSuppressEmptyLines( bool b )
{
d->suppressEmptyLines = b;
}
bool K3bProcess::closeStdin()
{
if( d->rawStdin ) {
close(d->in[1]);
d->in[1] = -1;
return true;
}
else
return KProcess::closeStdin();
}
bool K3bProcess::closeStdout()
{
if( d->rawStdout ) {
close(d->out[0]);
d->out[0] = -1;
return true;
}
else
return KProcess::closeStdout();
}
K3bProcessOutputCollector::K3bProcessOutputCollector( KProcess* p )
: m_process(0)
{
setProcess( p );
}
void K3bProcessOutputCollector::setProcess( KProcess* p )
{
if( m_process )
m_process->disconnect( this );
m_process = p;
if( p ) {
connect( p, SIGNAL(receivedStdout(KProcess*, char*, int)),
this, SLOT(slotGatherStdout(KProcess*, char*, int)) );
connect( p, SIGNAL(receivedStderr(KProcess*, char*, int)),
this, SLOT(slotGatherStderr(KProcess*, char*, int)) );
}
m_gatheredOutput.truncate( 0 );
m_stderrOutput.truncate( 0 );
m_stdoutOutput.truncate( 0 );
}
void K3bProcessOutputCollector::slotGatherStderr( KProcess*, char* data, int len )
{
m_gatheredOutput.append( QString::fromLocal8Bit( data, len ) );
m_stderrOutput.append( QString::fromLocal8Bit( data, len ) );
}
void K3bProcessOutputCollector::slotGatherStdout( KProcess*, char* data, int len )
{
m_gatheredOutput.append( QString::fromLocal8Bit( data, len ) );
m_stdoutOutput.append( QString::fromLocal8Bit( data, len ) );
}
#include "k3bprocess.moc"