File indexing completed on 2025-03-16 04:29:31
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 #include "k3breadcdreader.h" 0009 0010 #include "k3bcore.h" 0011 #include "k3bexternalbinmanager.h" 0012 #include "k3bdevice.h" 0013 #include "k3bdevicemanager.h" 0014 #include "k3bprocess.h" 0015 #include "k3bmsf.h" 0016 #include "k3bglobals.h" 0017 #include "k3b_i18n.h" 0018 0019 #include <KConfig> 0020 0021 #include <QDebug> 0022 #include <QRegularExpression> 0023 #include <QStringList> 0024 #include <QList> 0025 #include <QIODevice> 0026 0027 0028 class K3b::ReadcdReader::Private 0029 { 0030 public: 0031 Private() 0032 : process(0), 0033 ioDevToWriteTo(0), 0034 canceled(false) { 0035 } 0036 0037 K3b::Msf firstSector, lastSector; 0038 0039 K3b::Process* process; 0040 const K3b::ExternalBin* readcdBinObject; 0041 0042 QIODevice* ioDevToWriteTo; 0043 bool canceled; 0044 0045 long blocksToRead; 0046 int unreadableBlocks; 0047 0048 int lastProgress; 0049 int lastProcessedSize; 0050 }; 0051 0052 0053 0054 K3b::ReadcdReader::ReadcdReader( K3b::JobHandler* jh, QObject* parent ) 0055 : K3b::Job( jh, parent ), 0056 m_noCorr(false), 0057 m_clone(false), 0058 m_noError(false), 0059 m_c2Scan(false), 0060 m_speed(0), 0061 m_retries(128) 0062 { 0063 d = new Private(); 0064 } 0065 0066 0067 K3b::ReadcdReader::~ReadcdReader() 0068 { 0069 delete d->process; 0070 delete d; 0071 } 0072 0073 0074 bool K3b::ReadcdReader::active() const 0075 { 0076 return (d->process ? d->process->isRunning() : false); 0077 } 0078 0079 0080 void K3b::ReadcdReader::writeTo( QIODevice* dev ) 0081 { 0082 d->ioDevToWriteTo = dev; 0083 } 0084 0085 0086 void K3b::ReadcdReader::start() 0087 { 0088 jobStarted(); 0089 0090 d->blocksToRead = 1; 0091 d->unreadableBlocks = 0; 0092 d->lastProgress = 0; 0093 d->lastProcessedSize = 0; 0094 0095 // the first thing to do is to check for readcd 0096 d->readcdBinObject = k3bcore->externalBinManager()->binObject( "readcd" ); 0097 if( !d->readcdBinObject ) { 0098 emit infoMessage( i18n("Could not find %1 executable.",QString("readcd")), MessageError ); 0099 jobFinished(false); 0100 return; 0101 } 0102 0103 // check if we have clone support if we need it 0104 if( m_clone ) { 0105 bool foundCloneSupport = false; 0106 0107 if( !d->readcdBinObject->hasFeature( "clone" ) ) { 0108 // search all readcd installations 0109 K3b::ExternalProgram* readcdProgram = k3bcore->externalBinManager()->program( "readcd" ); 0110 QList<const K3b::ExternalBin*> readcdBins = readcdProgram->bins(); 0111 for( QList<const K3b::ExternalBin*>::const_iterator it = readcdBins.constBegin(); it != readcdBins.constEnd(); ++it ) { 0112 const K3b::ExternalBin* bin = *it; 0113 if( bin->hasFeature( "clone" ) ) { 0114 d->readcdBinObject = bin; 0115 emit infoMessage( i18n("Using readcd %1 instead of default version for clone support.", d->readcdBinObject->version()), MessageInfo ); 0116 foundCloneSupport = true; 0117 break; 0118 } 0119 } 0120 0121 if( !foundCloneSupport ) { 0122 emit infoMessage( i18n("Could not find readcd executable with cloning support."), MessageError ); 0123 jobFinished(false); 0124 return; 0125 } 0126 } 0127 } 0128 0129 0130 // create the commandline 0131 delete d->process; 0132 d->process = new K3b::Process(); 0133 connect( d->process, SIGNAL(stderrLine(QString)), this, SLOT(slotStderrLine(QString)) ); 0134 connect( d->process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotProcessExited(int,QProcess::ExitStatus)) ); 0135 0136 0137 *d->process << d->readcdBinObject; 0138 0139 // display progress 0140 *d->process << "-v"; 0141 0142 // Again we assume the device to be set! 0143 *d->process << QString("dev=%1").arg(K3b::externalBinDeviceParameter(m_readDevice, 0144 d->readcdBinObject)); 0145 if( m_speed > 0 ) 0146 *d->process << QString("speed=%1").arg(m_speed); 0147 0148 0149 // output 0150 if( d->ioDevToWriteTo ) { 0151 *d->process << "f=-"; 0152 connect( d->process, SIGNAL(readyReadStandardOutput()), this, SLOT(slotReadyRead()) ); 0153 } 0154 else { 0155 emit newTask( i18n("Writing image to %1.", m_imagePath) ); 0156 emit infoMessage( i18n("Writing image to %1.", m_imagePath), MessageInfo ); 0157 *d->process << "f=" + m_imagePath; 0158 } 0159 0160 0161 if( m_noError ) 0162 *d->process << "-noerror"; 0163 if( m_clone ) { 0164 *d->process << "-clone"; 0165 // noCorr can only be used with cloning 0166 if( m_noCorr ) 0167 *d->process << "-nocorr"; 0168 } 0169 if( m_c2Scan ) 0170 *d->process << "-c2scan"; 0171 0172 *d->process << QString("retries=%1").arg(m_retries); 0173 0174 // readcd does not read the last sector specified 0175 if( d->firstSector < d->lastSector ) 0176 *d->process << QString("sectors=%1-%2").arg(d->firstSector.lba()).arg(d->lastSector.lba()+1); 0177 0178 // Joerg sais it is a Linux kernel bug, anyway, with the default value it does not work 0179 *d->process << "ts=128k"; 0180 0181 // additional user parameters from config 0182 const QStringList& params = d->readcdBinObject->userParameters(); 0183 for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it ) 0184 *d->process << *it; 0185 0186 0187 qDebug() << "***** readcd parameters:\n"; 0188 QString s = d->process->joinedArgs(); 0189 qDebug() << s << Qt::endl << Qt::flush; 0190 emit debuggingOutput("readcd command:", s); 0191 0192 d->canceled = false; 0193 0194 if( !d->process->start( KProcess::SeparateChannels ) ) { 0195 // something went wrong when starting the program 0196 // it "should" be the executable 0197 qCritical() << "(K3b::ReadcdReader) could not start readcd" << Qt::endl; 0198 emit infoMessage( i18n("Could not start readcd."), K3b::Job::MessageError ); 0199 jobFinished( false ); 0200 } 0201 } 0202 0203 0204 void K3b::ReadcdReader::cancel() 0205 { 0206 if( d->process ) { 0207 if( d->process->isRunning() ) { 0208 d->canceled = true; 0209 d->process->kill(); 0210 } 0211 } 0212 } 0213 0214 0215 void K3b::ReadcdReader::slotStderrLine( const QString& line ) 0216 { 0217 emit debuggingOutput( "readcd", line ); 0218 0219 int pos = -1; 0220 0221 if( line.startsWith( "end:" ) ) { 0222 bool ok; 0223 d->blocksToRead = line.mid(4).toInt(&ok); 0224 if( d->firstSector < d->lastSector ) 0225 d->blocksToRead -= d->firstSector.lba(); 0226 if( !ok ) 0227 qCritical() << "(K3b::ReadcdReader) blocksToRead parsing error in line: " 0228 << line.mid(4) << Qt::endl; 0229 } 0230 0231 else if( line.startsWith( "addr:" ) ) { 0232 bool ok; 0233 long currentReadBlock = line.mid( 6, line.indexOf("cnt")-7 ).toInt(&ok); 0234 if( d->firstSector < d->lastSector ) 0235 currentReadBlock -= d->firstSector.lba(); 0236 if( ok ) { 0237 int p = (int)(100.0 * (double)currentReadBlock / (double)d->blocksToRead); 0238 if( p > d->lastProgress ) { 0239 emit percent( p ); 0240 d->lastProgress = p; 0241 } 0242 int ps = currentReadBlock*2/1024; 0243 if( ps > d->lastProcessedSize ) { 0244 emit processedSize( ps, d->blocksToRead*2/1024 ); 0245 d->lastProcessedSize = ps; 0246 } 0247 } 0248 else 0249 qCritical() << "(K3b::ReadcdReader) currentReadBlock parsing error in line: " 0250 << line.mid( 6, line.indexOf("cnt")-7 ) << Qt::endl; 0251 } 0252 0253 else if( line.contains("Cannot read source disk") ) { 0254 emit infoMessage( i18n("Cannot read source disk."), MessageError ); 0255 } 0256 0257 else if( (pos = line.indexOf("Retrying from sector")) >= 0 ) { 0258 // parse the sector 0259 pos += 21; 0260 bool ok; 0261 static const QRegularExpression rx("\\D"); 0262 int problemSector = line.mid( pos, line.indexOf( rx, pos )-pos ).toInt(&ok); 0263 if( !ok ) { 0264 qCritical() << "(K3b::ReadcdReader) problemSector parsing error in line: " 0265 << line.mid( pos, line.indexOf( rx, pos )-pos ) << Qt::endl; 0266 } 0267 emit infoMessage( i18n("Retrying from sector %1.",problemSector), MessageInfo ); 0268 } 0269 0270 else if( (pos = line.indexOf("Error on sector")) >= 0 ) { 0271 d->unreadableBlocks++; 0272 0273 pos += 16; 0274 bool ok; 0275 static const QRegularExpression rx("\\D"); 0276 int problemSector = line.mid( pos, line.indexOf( rx, pos )-pos ).toInt(&ok); 0277 if( !ok ) { 0278 qCritical() << "(K3b::ReadcdReader) problemSector parsing error in line: " 0279 << line.mid( pos, line.indexOf( rx, pos )-pos ) << Qt::endl; 0280 } 0281 0282 if( line.contains( "not corrected") ) { 0283 emit infoMessage( i18n("Uncorrected error in sector %1",problemSector), MessageError ); 0284 } 0285 else { 0286 emit infoMessage( i18n("Corrected error in sector %1",problemSector), MessageError ); 0287 } 0288 } 0289 0290 else { 0291 qDebug() << "(readcd) " << line; 0292 } 0293 } 0294 0295 void K3b::ReadcdReader::slotProcessExited( int exitCode, QProcess::ExitStatus exitStatus ) 0296 { 0297 if( d->canceled ) { 0298 emit canceled(); 0299 jobFinished(false); 0300 } 0301 else if( exitStatus == QProcess::NormalExit ) { 0302 if( exitCode == 0 ) { 0303 jobFinished( true ); 0304 } 0305 else { 0306 emit infoMessage( i18n("%1 returned error: %2",QString("Readcd"), exitCode ), MessageError ); 0307 jobFinished( false ); 0308 } 0309 } 0310 else { 0311 emit infoMessage( i18n("Readcd exited abnormally."), MessageError ); 0312 jobFinished( false ); 0313 } 0314 } 0315 0316 0317 void K3b::ReadcdReader::setSectorRange( const K3b::Msf& first, const K3b::Msf& last ) 0318 { 0319 d->firstSector = first; 0320 d->lastSector = last; 0321 } 0322 0323 0324 void K3b::ReadcdReader::slotReadyRead() 0325 { 0326 // FIXME: error handling! 0327 if ( d->ioDevToWriteTo ) { 0328 d->ioDevToWriteTo->write( d->process->readAllStandardOutput() ); 0329 } 0330 } 0331 0332 #include "moc_k3breadcdreader.cpp"