summaryrefslogtreecommitdiffstats
path: root/libk3b/core/k3bprocess.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libk3b/core/k3bprocess.cpp')
-rw-r--r--libk3b/core/k3bprocess.cpp452
1 files changed, 452 insertions, 0 deletions
diff --git a/libk3b/core/k3bprocess.cpp b/libk3b/core/k3bprocess.cpp
new file mode 100644
index 0000000..35ddff4
--- /dev/null
+++ b/libk3b/core/k3bprocess.cpp
@@ -0,0 +1,452 @@
+/*
+ *
+ * $Id: k3bprocess.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <[email protected]>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]>
+ *
+ * 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"