File indexing completed on 2024-06-16 07:42:27

0001 /*
0002     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 
0008 
0009 #include "k3bprocess.h"
0010 #include "k3bexternalbinmanager.h"
0011 
0012 #include <QByteArray>
0013 #include <QDebug>
0014 #include <QStringList>
0015 
0016 
0017 namespace {
0018     QStringList splitOutput( const QByteArray& data,
0019                              QString& unfinishedLine,
0020                              bool suppressEmptyLines )
0021     {
0022         //
0023         // The stderr splitting is mainly used for parsing of messages
0024         // That's why we simplify the data before proceeding
0025         //
0026         int len = data.length();
0027 
0028         QByteArray buffer;
0029         for( int i = 0; i < len; i++ ) {
0030             if( data[i] == '\b' ) {
0031                 while( i < len &&
0032                        data[i] == '\b' )  // we replace multiple backspaces with a single line feed
0033                     i++;
0034                 buffer += '\n';
0035             }
0036             if ( i < len ) {
0037                 if( data[i] == '\r' )
0038                     buffer += '\n';
0039                 else if( data[i] == '\t' )  // replace tabs with a single space
0040                     buffer += ' ';
0041                 else
0042                     buffer += data[i];
0043             }
0044         }
0045 
0046         QStringList lines = QString::fromLocal8Bit( buffer ).split( '\n', suppressEmptyLines ? Qt::SkipEmptyParts : Qt::KeepEmptyParts );
0047 
0048         // in case we suppress empty lines we need to handle the following separately
0049         // to make sure we join unfinished lines correctly
0050         if( suppressEmptyLines && buffer.startsWith( '\n' ) )
0051             lines.prepend( QString() );
0052 
0053         if( !unfinishedLine.isEmpty() ) {
0054             lines.first().prepend( unfinishedLine );
0055             unfinishedLine.truncate(0);
0056 
0057             qDebug() << "(K3b::Process)           joined line: '" << (lines.first()) << "'";
0058         }
0059 
0060         QStringList::iterator it;
0061 
0062         // check if line ends with a newline
0063         // if not save the last line because it is not finished
0064         if ( !buffer.isEmpty() ) {
0065             const auto c = buffer[buffer.length()-1];
0066             bool hasUnfinishedLine = ( c != '\n' && c != '\r' && QChar( c ) != QChar(46) );  // What is unicode 46?? It is printed as a point
0067             if( hasUnfinishedLine ) {
0068                 qDebug() << "(K3b::Process) found unfinished line: '" << lines.last() << "'";
0069                 qDebug() << "(K3b::Process)             last char: '" << buffer.right(1) << "'";
0070                 unfinishedLine = lines.takeLast();
0071             }
0072         }
0073 
0074         return lines;
0075     }
0076 }
0077 
0078 
0079 class K3b::Process::Private
0080 {
0081 public:
0082     QString unfinishedStdoutLine;
0083     QString unfinishedStderrLine;
0084 
0085     bool suppressEmptyLines;
0086 
0087     bool bSplitStdout;
0088 };
0089 
0090 
0091 K3b::Process::Process( QObject* parent )
0092     : K3bKProcess( parent ),
0093       d( new Private() )
0094 {
0095     setNextOpenMode( ReadWrite|Unbuffered );
0096     d->suppressEmptyLines = true;
0097     d->bSplitStdout = false;
0098 
0099     connect( this, SIGNAL(readyReadStandardError()),
0100              this, SLOT(slotReadyReadStandardError()) );
0101     connect( this, SIGNAL(readyReadStandardOutput()),
0102              this, SLOT(slotReadyReadStandardOutput()) );
0103 }
0104 
0105 
0106 K3b::Process::~Process()
0107 {
0108     delete d;
0109 }
0110 
0111 
0112 K3b::Process& K3b::Process::operator<<( const K3b::ExternalBin* bin )
0113 {
0114     return static_cast<Process&>( K3bKProcess::operator<<( bin->path() ) );
0115 }
0116 
0117 
0118 K3b::Process& K3b::Process::operator<<( const char* arg )
0119 {
0120     return static_cast<Process&>( K3bKProcess::operator<<( QLatin1String( arg ) ) );
0121 }
0122 
0123 
0124 K3b::Process& K3b::Process::operator<<( const QByteArray& arg )
0125 {
0126     return static_cast<Process&>( K3bKProcess::operator<<( QLatin1String( arg ) ) );
0127 }
0128 
0129 
0130 K3b::Process& K3b::Process::operator<<( const QLatin1String& arg )
0131 {
0132     return static_cast<Process&>( K3bKProcess::operator<<( arg ) );
0133 }
0134 
0135 
0136 void K3b::Process::setSplitStdout( bool b )
0137 {
0138     d->bSplitStdout = b;
0139 }
0140 
0141 
0142 void K3b::Process::slotReadyReadStandardOutput()
0143 {
0144     if( d->bSplitStdout ) {
0145         QStringList lines = splitOutput( readAllStandardOutput(), d->unfinishedStdoutLine, d->suppressEmptyLines );
0146 
0147         for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
0148             QString& str = *it;
0149 
0150             // just to be sure since something in splitOutput does not do this right
0151             if( d->suppressEmptyLines && str.isEmpty() )
0152                 continue;
0153 
0154             emit stdoutLine( str );
0155         }
0156     }
0157 }
0158 
0159 
0160 void K3b::Process::slotReadyReadStandardError()
0161 {
0162     QStringList lines = splitOutput( readAllStandardError(), d->unfinishedStderrLine, d->suppressEmptyLines );
0163 
0164     for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
0165         QString& str = *it;
0166 
0167         // just to be sure since something in splitOutput does not do this right
0168         if( d->suppressEmptyLines && str.isEmpty() )
0169             continue;
0170 
0171         emit stderrLine( str );
0172     }
0173 }
0174 
0175 
0176 void K3b::Process::setSuppressEmptyLines( bool b )
0177 {
0178     d->suppressEmptyLines = b;
0179 }
0180 
0181 
0182 QString K3b::Process::joinedArgs()
0183 {
0184     return program().join( " " );
0185 }
0186 
0187 
0188 void K3b::Process::close()
0189 {
0190     qDebug();
0191     closeWriteChannel();
0192     closeReadChannel( QProcess::StandardOutput );
0193 }
0194 
0195 
0196 bool K3b::Process::start( KProcess::OutputChannelMode mode )
0197 {
0198     qDebug();
0199     setOutputChannelMode( mode );
0200     K3bKProcess::start();
0201     qDebug() << "started";
0202     return K3bQProcess::waitForStarted();
0203 }
0204 
0205 #include "moc_k3bprocess.cpp"