File indexing completed on 2025-04-20 04:26:55
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 }