File indexing completed on 2024-06-16 04:33:37

0001 /*
0002     SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl>
0003     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 
0009 #include "k3bdefaultexternalprograms.h"
0010 #include "k3bexternalbinmanager.h"
0011 #include "k3bglobals.h"
0012 #include "k3bprocess.h"
0013 
0014 #include <KProcess>
0015 
0016 #include <QDebug>
0017 #include <QDir>
0018 #include <QFile>
0019 #include <QFileInfo>
0020 #include <QStringList>
0021 #include <QTextStream>
0022 
0023 
0024 void K3b::addDefaultPrograms( K3b::ExternalBinManager* m )
0025 {
0026     m->addProgram( new K3b::CdrecordProgram() );
0027     m->addProgram( new K3b::MkisofsProgram() );
0028     m->addProgram( new K3b::ReadcdProgram() );
0029     m->addProgram( new K3b::CdrdaoProgram() );
0030     m->addProgram( new K3b::GrowisofsProgram() );
0031     m->addProgram( new K3b::DvdformatProgram() );
0032     m->addProgram(new K3b::CdrskinProgram());
0033     //  m->addProgram( new K3b::DvdBooktypeProgram() );
0034 }
0035 
0036 // TODO: addFFmpegPrograms
0037 // https://bugs.kde.org/show_bug.cgi?id=381131
0038 void K3b::addTranscodePrograms(K3b::ExternalBinManager* m)
0039 {
0040     Q_UNUSED(m);
0041     /* Deprecated transcode
0042     static const char* const transcodeTools[] =  {"transcode",
0043                                              0, // K3b 1.0 only uses the transcode binary
0044                                              "tcprobe",
0045                                              "tccat",
0046                                              "tcscan",
0047                                              "tcextract",
0048                                              "tcdecode",
0049                                              0};
0050 
0051     for (int i = 0; transcodeTools[i]; ++i)
0052         m->addProgram(new K3b::TranscodeProgram(transcodeTools[i]));
0053     */
0054 }
0055 
0056 
0057 void K3b::addVcdimagerPrograms( K3b::ExternalBinManager* m )
0058 {
0059     // don't know if we need more vcdTools in the future (vcdxrip)
0060     static const char* const vcdTools[] =  { "vcdxbuild",
0061                                        "vcdxminfo",
0062                                        "vcdxrip",
0063                                        0 };
0064 
0065     for( int i = 0; vcdTools[i]; ++i )
0066         m->addProgram( new K3b::VcdbuilderProgram( vcdTools[i] ) );
0067 }
0068 
0069 
0070 class K3b::AbstractCdrtoolsProgram::Private
0071 {
0072 public:
0073     Private( const QString& cdrkitAlternative ) : cdrkitAlt( cdrkitAlternative ) {}
0074     QString cdrkitAlt;
0075 };
0076 
0077 
0078 K3b::AbstractCdrtoolsProgram::AbstractCdrtoolsProgram( const QString& program, const QString& cdrkitAlternative )
0079     : SimpleExternalProgram( program ),
0080       d( new Private( cdrkitAlternative ) )
0081 {
0082 }
0083 
0084 
0085 K3b::AbstractCdrtoolsProgram::~AbstractCdrtoolsProgram()
0086 {
0087     delete d;
0088 }
0089 
0090 
0091 bool K3b::AbstractCdrtoolsProgram::usingCdrkit( const ExternalBin& bin ) const
0092 {
0093     return QFileInfo( bin.path() ).baseName() == d->cdrkitAlt;
0094 }
0095 
0096 
0097 
0098 #ifndef Q_OS_WIN32
0099 //
0100 // This is a hack for Debian based systems which use
0101 // a wrapper cdrecord script to call cdrecord.mmap or cdrecord.shm
0102 // depending on the kernel version.
0103 // For 2.0.x and 2.2.x kernels the shm version is used. In all
0104 // other cases it's the mmap version.
0105 //
0106 // But since it may be that someone manually installed cdrecord
0107 // replacing the wrapper we check if cdrecord is a script.
0108 //
0109 static QString& debianWeirdnessHack( QString& path )
0110 {
0111     if( QFile::exists( path + ".mmap" ) ) {
0112         qDebug() << "checking for Debian cdrtools wrapper script.";
0113         if( QFileInfo( path ).size() < 1024 ) {
0114             qDebug() << "Debian Wrapper script size fits. Checking file.";
0115             QFile f( path );
0116             f.open( QIODevice::ReadOnly );
0117             QString s = QTextStream( &f ).readAll();
0118             if( s.contains( "cdrecord.mmap" ) && s.contains( "cdrecord.shm" ) ) {
0119                 qDebug() << "Found Debian Wrapper script.";
0120                 QString ext;
0121                 if( K3b::kernelVersion().versionString().left(3) > "2.2" )
0122                     ext = ".mmap";
0123                 else
0124                     ext = ".shm";
0125 
0126                 qDebug() << "Using crtools" << ext;
0127 
0128                 path += ext;
0129             }
0130         }
0131     }
0132 
0133     return path;
0134 }
0135 #endif
0136 
0137 
0138 QString K3b::AbstractCdrtoolsProgram::getProgramPath( const QString& dir ) const
0139 {
0140     QString cdrtoolsPath = ExternalProgram::buildProgramPath( dir, name() );
0141     QString cdrkitPath = ExternalProgram::buildProgramPath( dir, d->cdrkitAlt );
0142 
0143     QString path;
0144     if( QFile::exists( cdrtoolsPath ) &&
0145         QFileInfo(K3b::resolveLink( cdrtoolsPath )).baseName() != d->cdrkitAlt ) {
0146         path = cdrtoolsPath;
0147     }
0148     else if( QFile::exists( cdrkitPath ) ) {
0149         path = cdrkitPath;
0150     }
0151 
0152 #ifndef Q_OS_WIN32
0153     if ( !path.isEmpty() && name() == QLatin1String( "cdrecord" ) ) {
0154         debianWeirdnessHack( path );
0155     }
0156 #endif
0157 
0158     return path;
0159 }
0160 
0161 
0162 QString K3b::AbstractCdrtoolsProgram::versionIdentifier( const ExternalBin& bin ) const
0163 {
0164     if( usingCdrkit( bin ) )
0165         return d->cdrkitAlt;
0166     else
0167         return name();
0168 }
0169 
0170 
0171 K3b::CdrecordProgram::CdrecordProgram()
0172     : K3b::AbstractCdrtoolsProgram( QLatin1String( "cdrecord" ), QLatin1String( "wodim" ) )
0173 {
0174 }
0175 
0176 
0177 void K3b::CdrecordProgram::parseFeatures( const QString& output, ExternalBin& bin ) const
0178 {
0179     if( usingCdrkit( bin ) )
0180         bin.addFeature( "wodim" );
0181 
0182     if( bin.version().suffix().endsWith( "-dvd" ) ) {
0183         bin.addFeature( "dvd-patch" );
0184         bin.setVersion( QString(bin.version().versionString()).remove("-dvd") );
0185     }
0186 
0187     if( output.contains( "gracetime" ) )
0188         bin.addFeature( "gracetime" );
0189     if( output.contains( "-overburn" ) )
0190         bin.addFeature( "overburn" );
0191     if( output.contains( "-text" ) )
0192         bin.addFeature( "cdtext" );
0193     if( output.contains( "-clone" ) )
0194         bin.addFeature( "clone" );
0195     if( output.contains( "-tao" ) )
0196         bin.addFeature( "tao" );
0197     if( output.contains( "cuefile=" ) &&
0198         ( usingCdrkit( bin ) || bin.version() > K3b::Version( 2, 1, -1, "a14") ) ) // cuefile handling was still buggy in a14
0199         bin.addFeature( "cuefile" );
0200 
0201     // new mode 2 options since cdrecord 2.01a12
0202     // we use both checks here since the help was not updated in 2.01a12 yet (well, I
0203     // just double-checked and the help page is proper but there is no harm in having
0204     // two checks)
0205     // and the version check does not handle versions like 2.01-dvd properly
0206     if( output.contains( "-xamix" ) ||
0207         bin.version() >= K3b::Version( 2, 1, -1, "a12" ) ||
0208         usingCdrkit( bin ) )
0209         bin.addFeature( "xamix" );
0210 
0211     if( bin.version() < K3b::Version( 2, 0 ) && !usingCdrkit( bin ) )
0212         bin.addFeature( "outdated" );
0213 
0214     // FIXME: are these version correct?
0215     if( bin.version() >= K3b::Version("1.11a38") || usingCdrkit( bin ) )
0216         bin.addFeature( "plain-atapi" );
0217     if( bin.version() > K3b::Version("1.11a17") || usingCdrkit( bin ) )
0218         bin.addFeature( "hacked-atapi" );
0219 
0220     if( bin.version() >= K3b::Version( 2, 1, 1, "a02" ) || usingCdrkit( bin ) )
0221         bin.addFeature( "short-track-raw" );
0222 
0223     if( bin.version() >= K3b::Version( 2, 1, -1, "a13" ) || usingCdrkit( bin ) )
0224         bin.addFeature( "audio-stdin" );
0225 
0226     if( bin.version() >= K3b::Version( "1.11a02" ) || usingCdrkit( bin ) )
0227         bin.addFeature( "burnfree" );
0228     else
0229         bin.addFeature( "burnproof" );
0230 
0231     if ( bin.version() >= K3b::Version( 2, 1, 1, "a29" ) && !usingCdrkit( bin ) )
0232         bin.addFeature( "blu-ray" );
0233 
0234     // FIXME: when did cdrecord introduce free dvd support?
0235     bin.addFeature( "dvd" );
0236 }
0237 
0238 
0239 
0240 K3b::MkisofsProgram::MkisofsProgram()
0241     : K3b::AbstractCdrtoolsProgram( QLatin1String( "mkisofs" ), QLatin1String( "genisoimage" ) )
0242 {
0243 }
0244 
0245 
0246 void K3b::MkisofsProgram::parseFeatures( const QString& output, ExternalBin& bin ) const
0247 {
0248     if( usingCdrkit( bin ) )
0249         bin.addFeature( "genisoimage" );
0250 
0251     if( output.contains( "-udf" ) )
0252         bin.addFeature( "udf" );
0253     if( output.contains( "-dvd-video" ) )
0254         bin.addFeature( "dvd-video" );
0255     if( output.contains( "-joliet-long" ) )
0256         bin.addFeature( "joliet-long" );
0257     if( output.contains( "-xa" ) )
0258         bin.addFeature( "xa" );
0259     if( output.contains( "-sectype" ) )
0260         bin.addFeature( "sectype" );
0261 
0262     if( bin.version() < K3b::Version( 1, 14) && !usingCdrkit( bin ) )
0263         bin.addFeature( "outdated" );
0264 
0265     if( bin.version() >= K3b::Version( 1, 15, -1, "a40" ) || usingCdrkit( bin ) )
0266         bin.addFeature( "backslashed_filenames" );
0267 
0268     if ( usingCdrkit( bin ) && bin.version() >= K3b::Version( 1, 1, 4 ) )
0269         bin.addFeature( "no-4gb-limit" );
0270 
0271     if ( !usingCdrkit( bin ) && bin.version() >= K3b::Version( 2, 1, 1, "a32" ) )
0272         bin.addFeature( "no-4gb-limit" );
0273 }
0274 
0275 
0276 K3b::ReadcdProgram::ReadcdProgram()
0277     : K3b::AbstractCdrtoolsProgram( QLatin1String( "readcd" ), QLatin1String( "readom" ) )
0278 {
0279 }
0280 
0281 
0282 void K3b::ReadcdProgram::parseFeatures( const QString& output, ExternalBin& bin ) const
0283 {
0284     if( usingCdrkit( bin ) )
0285         bin.addFeature( "readom" );
0286 
0287     if( output.contains( "-clone" ) )
0288         bin.addFeature( "clone" );
0289 
0290     // FIXME: are these version correct?
0291     if( bin.version() >= K3b::Version("1.11a38") || usingCdrkit( bin ) )
0292         bin.addFeature( "plain-atapi" );
0293     if( bin.version() > K3b::Version("1.11a17") || usingCdrkit( bin ) )
0294         bin.addFeature( "hacked-atapi" );
0295 }
0296 
0297 
0298 K3b::Cdda2wavProgram::Cdda2wavProgram()
0299     : K3b::AbstractCdrtoolsProgram( QLatin1String( "cdda2wav" ), QLatin1String( "icedax" ) )
0300 {
0301 }
0302 
0303 
0304 void K3b::Cdda2wavProgram::parseFeatures( const QString& output, ExternalBin& bin ) const
0305 {
0306     // features (we do this since the cdda2wav help says that the short
0307     //           options will disappear soon)
0308     if( output.indexOf( "-info-only" ) )
0309         bin.addFeature( "info-only" ); // otherwise use the -J option
0310     if( output.indexOf( "-no-infofile" ) )
0311         bin.addFeature( "no-infofile" ); // otherwise use the -H option
0312     if( output.indexOf( "-gui" ) )
0313         bin.addFeature( "gui" ); // otherwise use the -g option
0314     if( output.indexOf( "-bulk" ) )
0315         bin.addFeature( "bulk" ); // otherwise use the -B option
0316     if( output.indexOf( "dev=" ) )
0317         bin.addFeature( "dev" ); // otherwise use the -B option
0318 }
0319 
0320 
0321 K3b::CdrdaoProgram::CdrdaoProgram()
0322     : K3b::SimpleExternalProgram( "cdrdao" )
0323 {
0324 }
0325 
0326 
0327 QString K3b::CdrdaoProgram::versionIdentifier( const ExternalBin& /*bin*/ ) const
0328 {
0329     return QLatin1String( "Cdrdao version" );
0330 }
0331 
0332 
0333 bool K3b::CdrdaoProgram::scanFeatures( ExternalBin& bin ) const
0334 {
0335     // probe features
0336     KProcess fp;
0337     fp.setOutputChannelMode( KProcess::MergedChannels );
0338     fp << bin.path() << "write" << "-h";
0339 
0340     if( fp.execute() >= 0 ) {
0341         QByteArray out = fp.readAll();
0342         if( out.contains( "--overburn" ) )
0343             bin.addFeature( "overburn" );
0344         if( out.contains( "--multi" ) )
0345             bin.addFeature( "multisession" );
0346 
0347         if( out.contains( "--buffer-under-run-protection" ) )
0348             bin.addFeature( "disable-burnproof" );
0349 
0350         // SuSE 9.0 ships with a patched cdrdao 1.1.7 which contains an updated libschily
0351         // Gentoo ships with a patched cdrdao 1.1.7 which contains scglib support
0352         if( bin.version() > K3b::Version( 1, 1, 7 ) ||
0353             bin.version() == K3b::Version( 1, 1, 7, "-gentoo" ) ||
0354             bin.version() == K3b::Version( 1, 1, 7, "-suse" ) ) {
0355             //    bin.addFeature( "plain-atapi" );
0356             bin.addFeature( "hacked-atapi" );
0357         }
0358 
0359         if( bin.version() >= K3b::Version( 1, 1, 8 ) )
0360             bin.addFeature( "plain-atapi" );
0361 
0362         return SimpleExternalProgram::scanFeatures( bin );
0363     }
0364     else {
0365         qDebug() << "could not start " << bin.path();
0366         return false;
0367     }
0368 }
0369 
0370 
0371 K3b::TranscodeProgram::TranscodeProgram( const QString& transcodeProgram )
0372     : K3b::SimpleExternalProgram( transcodeProgram )
0373 {
0374 }
0375 
0376 
0377 QString K3b::TranscodeProgram::versionIdentifier( const ExternalBin& /*bin*/ ) const
0378 {
0379     return QLatin1String( "transcode v" );
0380 }
0381 
0382 
0383 bool K3b::TranscodeProgram::scanFeatures( ExternalBin& bin ) const
0384 {
0385     //
0386     // Check features
0387     //
0388     QString modInfoBin = buildProgramPath( QFileInfo( bin.path() ).absolutePath(), QLatin1String( "tcmodinfo" ) );
0389     Process modp;
0390     modp.setOutputChannelMode( KProcess::MergedChannels );
0391     modp << modInfoBin << "-p";
0392 
0393     if( !modp.execute() ) {
0394         QString modPath = QString::fromLocal8Bit( modp.readAll() ).simplified();
0395         QDir modDir( modPath );
0396         if( !modDir.entryList( QStringList() << "*export_xvid*", QDir::Files ).isEmpty() )
0397             bin.addFeature( "xvid" );
0398         if( !modDir.entryList( QStringList() << "*export_lame*", QDir::Files ).isEmpty() )
0399             bin.addFeature( "lame" );
0400         if( !modDir.entryList( QStringList() << "*export_ffmpeg*", QDir::Files ).isEmpty() )
0401             bin.addFeature( "ffmpeg" );
0402         if( !modDir.entryList( QStringList() << "*export_ac3*", QDir::Files ).isEmpty() )
0403             bin.addFeature( "ac3" );
0404 
0405         return true;
0406     }
0407     else {
0408         qDebug() << "Failed to start" << modp.program();
0409         return false;
0410     }
0411 }
0412 
0413 
0414 K3b::VcdbuilderProgram::VcdbuilderProgram( const QString& p )
0415     : K3b::SimpleExternalProgram( p )
0416 {
0417 }
0418 
0419 
0420 QString K3b::VcdbuilderProgram::versionIdentifier( const ExternalBin& /*bin*/ ) const
0421 {
0422     return QLatin1String( "GNU VCDImager" );
0423 }
0424 
0425 
0426 K3b::NormalizeProgram::NormalizeProgram()
0427     : K3b::SimpleExternalProgram( "normalize" )
0428 {
0429 }
0430 
0431 
0432 K3b::GrowisofsProgram::GrowisofsProgram()
0433     : K3b::SimpleExternalProgram( "growisofs" )
0434 {
0435 }
0436 
0437 
0438 bool K3b::GrowisofsProgram::scanFeatures( ExternalBin& bin ) const
0439 {
0440     // fixed Copyright:
0441     bin.setCopyright( "Andy Polyakov <appro@fy.chalmers.se>" );
0442 
0443     if ( bin.version() >= K3b::Version( 5, 20 ) )
0444         bin.addFeature( "dual-layer" );
0445 
0446     if ( bin.version() > K3b::Version( 5, 17 ) )
0447         bin.addFeature( "tracksize" );
0448 
0449     if ( bin.version() >= K3b::Version( 5, 15 ) )
0450         bin.addFeature( "daosize" );
0451 
0452     if ( bin.version() >= K3b::Version( 6, 0 ) )
0453         bin.addFeature( "buffer" );
0454 
0455     if ( bin.version() >= K3b::Version( 7, 0 ) )
0456         bin.addFeature( "blu-ray" );
0457 
0458     return SimpleExternalProgram::scanFeatures( bin );
0459 }
0460 
0461 
0462 K3b::DvdformatProgram::DvdformatProgram()
0463     : K3b::SimpleExternalProgram( "dvd+rw-format" )
0464 {
0465 }
0466 
0467 
0468 K3b::Version K3b::DvdformatProgram::parseVersion( const QString& output, const ExternalBin& /*bin*/ ) const
0469 {
0470     QString version;
0471     // extract fields
0472     QStringList outputList = output.split( ' ' );
0473 
0474     for (int i = 0; i < outputList.size(); ++i) {
0475         if ( outputList.at( i ) == "version" ) {
0476             // extract version number
0477             version = outputList.at( i + 1 );
0478             // remove dot and spaces found at the end of the version
0479             version = version.left( version.size() - 3 );
0480             return version;
0481         }
0482     }
0483 
0484     return Version();
0485 }
0486 
0487 
0488 QString K3b::DvdformatProgram::parseCopyright( const QString& output, const ExternalBin& /*bin*/ ) const
0489 {
0490     QString copyright = "Andy Polyakov ";
0491     // extract fields
0492     QStringList outputList = output.split( ' ' );
0493 
0494     for (int i = 0; i < outputList.size(); ++i) {
0495         if ( outputList.at( i ) == "by" ) {
0496             // add missing author name
0497             copyright.append( outputList.at( i + 1 ) );
0498             // remove dot found at the end of the version
0499             copyright = copyright.left( copyright.size() - 1 );
0500         }
0501     }
0502 
0503     return copyright;
0504 }
0505 
0506 
0507 K3b::DvdBooktypeProgram::DvdBooktypeProgram()
0508     : K3b::SimpleExternalProgram( "dvd+rw-booktype" )
0509 {
0510 }
0511 
0512 
0513 K3b::Version K3b::DvdBooktypeProgram::parseVersion( const QString& output, const ExternalBin& /*bin*/ ) const
0514 {
0515     int pos = output.indexOf( "dvd+rw-booktype" );
0516     if( pos < 0 )
0517         return Version();
0518 
0519     // dvd+rw-booktype does not have version information
0520     // create dummy version based on latest release (05-Mar-2008)
0521     return K3b::Version( 7, 1 );
0522 }
0523 
0524 
0525 QString K3b::DvdBooktypeProgram::parseCopyright( const QString& /*output*/, const ExternalBin& /*bin*/ ) const
0526 {
0527     // fixed Copyright:
0528     return QLatin1String( "Andy Polyakov <appro@fy.chalmers.se>" );
0529 }
0530 
0531 K3b::CdrskinProgram::CdrskinProgram()
0532     : K3b::SimpleExternalProgram(QLatin1String("cdrskin"))
0533 {
0534 }
0535 
0536 bool K3b::CdrskinProgram::scanFeatures(ExternalBin& bin) const
0537 {
0538     KProcess fp;
0539     fp.setOutputChannelMode(KProcess::MergedChannels);
0540     fp << bin.path() << "-help";
0541 
0542     if (fp.execute() >= 0) {
0543         QByteArray output = fp.readAll();
0544 
0545         if (output.contains("gracetime"))
0546             bin.addFeature("gracetime");
0547         if (output.contains("-overburn"))
0548             bin.addFeature("overburn");
0549         if (output.contains("-text"))
0550             bin.addFeature("cdtext");
0551         if (output.contains("-clone"))
0552             bin.addFeature("clone");
0553         if (output.contains("-tao"))
0554             bin.addFeature("tao");
0555     }
0556 
0557 #ifdef K3B_DEBUG
0558     qDebug() << "DEBUG:" << __PRETTY_FUNCTION__ << bin.version(); // 1.4.6 for example
0559 #endif
0560     // TODO: cdrskin -help | grep XXX
0561     // Hello Thomas, please help me: the other version number dependent features 
0562     // need to be checked what they mean and whether cdrskin supports them.
0563     if (bin.version().suffix().endsWith("-dvd")) {
0564         bin.addFeature("dvd-patch");
0565         bin.setVersion(QString(bin.version().versionString()).remove("-dvd"));
0566     }
0567 
0568     // TODO: find . -name "*.cpp" | xargs grep plain-atapi
0569     // In src/k3bsystemproblemdialog.cpp there is Check for we have atapi support 
0570     // in some way in the kernel
0571     bin.addFeature("plain-atapi");
0572     // The same story
0573     bin.addFeature("hacked-atapi");
0574 
0575     bin.addFeature("burnfree");
0576 
0577     if (bin.version() >= K3b::Version(0, 6, 2)) {
0578 #ifdef K3B_DEBUG
0579         qDebug() << "DEBUG:" << __PRETTY_FUNCTION__ << "Blu-ray support was complete in cdrskin-0.6.2, 20 Feb 2009";
0580 #endif
0581         bin.addFeature("blu-ray");
0582     }
0583 
0584     bin.addFeature("dvd");
0585 #ifdef K3B_DEBUG
0586     qDebug() << "DEBUG:" << __PRETTY_FUNCTION__ << bin.features();
0587 #endif
0588 
0589     return SimpleExternalProgram::scanFeatures(bin);
0590 }