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

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"