File indexing completed on 2025-02-16 07:39:53
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"